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.8-rc3 478 lines 13 kB view raw
1/* 2 * WonderMedia WM8505 Frame Buffer device driver 3 * 4 * Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com> 5 * Based on vt8500lcdfb.c 6 * 7 * This software is licensed under the terms of the GNU General Public 8 * License version 2, as published by the Free Software Foundation, and 9 * may be copied, distributed, and modified under those terms. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 */ 16 17#include <linux/module.h> 18#include <linux/kernel.h> 19#include <linux/errno.h> 20#include <linux/string.h> 21#include <linux/mm.h> 22#include <linux/slab.h> 23#include <linux/delay.h> 24#include <linux/fb.h> 25#include <linux/init.h> 26#include <linux/interrupt.h> 27#include <linux/io.h> 28#include <linux/dma-mapping.h> 29#include <linux/platform_device.h> 30#include <linux/wait.h> 31#include <linux/of.h> 32#include <linux/of_fdt.h> 33#include <linux/memblock.h> 34 35#include <linux/platform_data/video-vt8500lcdfb.h> 36 37#include "wm8505fb_regs.h" 38#include "wmt_ge_rops.h" 39 40#define DRIVER_NAME "wm8505-fb" 41 42#define to_wm8505fb_info(__info) container_of(__info, \ 43 struct wm8505fb_info, fb) 44struct wm8505fb_info { 45 struct fb_info fb; 46 void __iomem *regbase; 47 unsigned int contrast; 48}; 49 50 51static int wm8505fb_init_hw(struct fb_info *info) 52{ 53 struct wm8505fb_info *fbi = to_wm8505fb_info(info); 54 55 int i; 56 57 /* I know the purpose only of few registers, so clear unknown */ 58 for (i = 0; i < 0x200; i += 4) 59 writel(0, fbi->regbase + i); 60 61 /* Set frame buffer address */ 62 writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR); 63 writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR1); 64 65 /* 66 * Set in-memory picture format to RGB 67 * 0x31C sets the correct color mode (RGB565) for WM8650 68 * Bit 8+9 (0x300) are ignored on WM8505 as reserved 69 */ 70 writel(0x31c, fbi->regbase + WMT_GOVR_COLORSPACE); 71 writel(1, fbi->regbase + WMT_GOVR_COLORSPACE1); 72 73 /* Virtual buffer size */ 74 writel(info->var.xres, fbi->regbase + WMT_GOVR_XRES); 75 writel(info->var.xres_virtual, fbi->regbase + WMT_GOVR_XRES_VIRTUAL); 76 77 /* black magic ;) */ 78 writel(0xf, fbi->regbase + WMT_GOVR_FHI); 79 writel(4, fbi->regbase + WMT_GOVR_DVO_SET); 80 writel(1, fbi->regbase + WMT_GOVR_MIF_ENABLE); 81 writel(1, fbi->regbase + WMT_GOVR_REG_UPDATE); 82 83 return 0; 84} 85 86static int wm8505fb_set_timing(struct fb_info *info) 87{ 88 struct wm8505fb_info *fbi = to_wm8505fb_info(info); 89 90 int h_start = info->var.left_margin; 91 int h_end = h_start + info->var.xres; 92 int h_all = h_end + info->var.right_margin; 93 int h_sync = info->var.hsync_len; 94 95 int v_start = info->var.upper_margin; 96 int v_end = v_start + info->var.yres; 97 int v_all = v_end + info->var.lower_margin; 98 int v_sync = info->var.vsync_len; 99 100 writel(0, fbi->regbase + WMT_GOVR_TG); 101 102 writel(h_start, fbi->regbase + WMT_GOVR_TIMING_H_START); 103 writel(h_end, fbi->regbase + WMT_GOVR_TIMING_H_END); 104 writel(h_all, fbi->regbase + WMT_GOVR_TIMING_H_ALL); 105 writel(h_sync, fbi->regbase + WMT_GOVR_TIMING_H_SYNC); 106 107 writel(v_start, fbi->regbase + WMT_GOVR_TIMING_V_START); 108 writel(v_end, fbi->regbase + WMT_GOVR_TIMING_V_END); 109 writel(v_all, fbi->regbase + WMT_GOVR_TIMING_V_ALL); 110 writel(v_sync, fbi->regbase + WMT_GOVR_TIMING_V_SYNC); 111 112 writel(1, fbi->regbase + WMT_GOVR_TG); 113 114 return 0; 115} 116 117 118static int wm8505fb_set_par(struct fb_info *info) 119{ 120 struct wm8505fb_info *fbi = to_wm8505fb_info(info); 121 122 if (!fbi) 123 return -EINVAL; 124 125 if (info->var.bits_per_pixel == 32) { 126 info->var.red.offset = 16; 127 info->var.red.length = 8; 128 info->var.red.msb_right = 0; 129 info->var.green.offset = 8; 130 info->var.green.length = 8; 131 info->var.green.msb_right = 0; 132 info->var.blue.offset = 0; 133 info->var.blue.length = 8; 134 info->var.blue.msb_right = 0; 135 info->fix.visual = FB_VISUAL_TRUECOLOR; 136 info->fix.line_length = info->var.xres_virtual << 2; 137 } else if (info->var.bits_per_pixel == 16) { 138 info->var.red.offset = 11; 139 info->var.red.length = 5; 140 info->var.red.msb_right = 0; 141 info->var.green.offset = 5; 142 info->var.green.length = 6; 143 info->var.green.msb_right = 0; 144 info->var.blue.offset = 0; 145 info->var.blue.length = 5; 146 info->var.blue.msb_right = 0; 147 info->fix.visual = FB_VISUAL_TRUECOLOR; 148 info->fix.line_length = info->var.xres_virtual << 1; 149 } 150 151 wm8505fb_set_timing(info); 152 153 writel(fbi->contrast<<16 | fbi->contrast<<8 | fbi->contrast, 154 fbi->regbase + WMT_GOVR_CONTRAST); 155 156 return 0; 157} 158 159static ssize_t contrast_show(struct device *dev, 160 struct device_attribute *attr, char *buf) 161{ 162 struct fb_info *info = dev_get_drvdata(dev); 163 struct wm8505fb_info *fbi = to_wm8505fb_info(info); 164 165 return sprintf(buf, "%d\n", fbi->contrast); 166} 167 168static ssize_t contrast_store(struct device *dev, 169 struct device_attribute *attr, 170 const char *buf, size_t count) 171{ 172 struct fb_info *info = dev_get_drvdata(dev); 173 struct wm8505fb_info *fbi = to_wm8505fb_info(info); 174 unsigned long tmp; 175 176 if (strict_strtoul(buf, 10, &tmp) || (tmp > 0xff)) 177 return -EINVAL; 178 fbi->contrast = tmp; 179 180 wm8505fb_set_par(info); 181 182 return count; 183} 184 185static DEVICE_ATTR(contrast, 0644, contrast_show, contrast_store); 186 187static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) 188{ 189 chan &= 0xffff; 190 chan >>= 16 - bf->length; 191 return chan << bf->offset; 192} 193 194static int wm8505fb_setcolreg(unsigned regno, unsigned red, unsigned green, 195 unsigned blue, unsigned transp, 196 struct fb_info *info) { 197 struct wm8505fb_info *fbi = to_wm8505fb_info(info); 198 int ret = 1; 199 unsigned int val; 200 if (regno >= 256) 201 return -EINVAL; 202 203 if (info->var.grayscale) 204 red = green = blue = 205 (19595 * red + 38470 * green + 7471 * blue) >> 16; 206 207 switch (fbi->fb.fix.visual) { 208 case FB_VISUAL_TRUECOLOR: 209 if (regno < 16) { 210 u32 *pal = info->pseudo_palette; 211 212 val = chan_to_field(red, &fbi->fb.var.red); 213 val |= chan_to_field(green, &fbi->fb.var.green); 214 val |= chan_to_field(blue, &fbi->fb.var.blue); 215 216 pal[regno] = val; 217 ret = 0; 218 } 219 break; 220 } 221 222 return ret; 223} 224 225static int wm8505fb_pan_display(struct fb_var_screeninfo *var, 226 struct fb_info *info) 227{ 228 struct wm8505fb_info *fbi = to_wm8505fb_info(info); 229 230 writel(var->xoffset, fbi->regbase + WMT_GOVR_XPAN); 231 writel(var->yoffset, fbi->regbase + WMT_GOVR_YPAN); 232 return 0; 233} 234 235static int wm8505fb_blank(int blank, struct fb_info *info) 236{ 237 struct wm8505fb_info *fbi = to_wm8505fb_info(info); 238 239 switch (blank) { 240 case FB_BLANK_UNBLANK: 241 wm8505fb_set_timing(info); 242 break; 243 default: 244 writel(0, fbi->regbase + WMT_GOVR_TIMING_V_SYNC); 245 break; 246 } 247 248 return 0; 249} 250 251static struct fb_ops wm8505fb_ops = { 252 .owner = THIS_MODULE, 253 .fb_set_par = wm8505fb_set_par, 254 .fb_setcolreg = wm8505fb_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_pan_display = wm8505fb_pan_display, 260 .fb_blank = wm8505fb_blank, 261}; 262 263static int wm8505fb_probe(struct platform_device *pdev) 264{ 265 struct wm8505fb_info *fbi; 266 struct resource *res; 267 void *addr; 268 int ret; 269 270 struct fb_videomode of_mode; 271 struct device_node *np; 272 u32 bpp; 273 dma_addr_t fb_mem_phys; 274 unsigned long fb_mem_len; 275 void *fb_mem_virt; 276 277 ret = -ENOMEM; 278 fbi = NULL; 279 280 fbi = devm_kzalloc(&pdev->dev, sizeof(struct wm8505fb_info) + 281 sizeof(u32) * 16, 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, DRIVER_NAME); 289 290 fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; 291 fbi->fb.fix.xpanstep = 1; 292 fbi->fb.fix.ypanstep = 1; 293 fbi->fb.fix.ywrapstep = 0; 294 fbi->fb.fix.accel = FB_ACCEL_NONE; 295 296 fbi->fb.fbops = &wm8505fb_ops; 297 fbi->fb.flags = FBINFO_DEFAULT 298 | FBINFO_HWACCEL_COPYAREA 299 | FBINFO_HWACCEL_FILLRECT 300 | FBINFO_HWACCEL_XPAN 301 | FBINFO_HWACCEL_YPAN 302 | FBINFO_VIRTFB 303 | FBINFO_PARTIAL_PAN_OK; 304 fbi->fb.node = -1; 305 306 addr = fbi; 307 addr = addr + sizeof(struct wm8505fb_info); 308 fbi->fb.pseudo_palette = addr; 309 310 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 311 if (res == NULL) { 312 dev_err(&pdev->dev, "no I/O memory resource defined\n"); 313 ret = -ENODEV; 314 goto failed_fbi; 315 } 316 317 res = request_mem_region(res->start, resource_size(res), DRIVER_NAME); 318 if (res == NULL) { 319 dev_err(&pdev->dev, "failed to request I/O memory\n"); 320 ret = -EBUSY; 321 goto failed_fbi; 322 } 323 324 fbi->regbase = ioremap(res->start, resource_size(res)); 325 if (fbi->regbase == NULL) { 326 dev_err(&pdev->dev, "failed to map I/O memory\n"); 327 ret = -EBUSY; 328 goto failed_free_res; 329 } 330 331 np = of_parse_phandle(pdev->dev.of_node, "default-mode", 0); 332 if (!np) { 333 pr_err("%s: No display description in Device Tree\n", __func__); 334 ret = -EINVAL; 335 goto failed_free_res; 336 } 337 338 /* 339 * This code is copied from Sascha Hauer's of_videomode helper 340 * and can be replaced with a call to the helper once mainlined 341 */ 342 ret = 0; 343 ret |= of_property_read_u32(np, "hactive", &of_mode.xres); 344 ret |= of_property_read_u32(np, "vactive", &of_mode.yres); 345 ret |= of_property_read_u32(np, "hback-porch", &of_mode.left_margin); 346 ret |= of_property_read_u32(np, "hfront-porch", &of_mode.right_margin); 347 ret |= of_property_read_u32(np, "hsync-len", &of_mode.hsync_len); 348 ret |= of_property_read_u32(np, "vback-porch", &of_mode.upper_margin); 349 ret |= of_property_read_u32(np, "vfront-porch", &of_mode.lower_margin); 350 ret |= of_property_read_u32(np, "vsync-len", &of_mode.vsync_len); 351 ret |= of_property_read_u32(np, "bpp", &bpp); 352 if (ret) { 353 pr_err("%s: Unable to read display properties\n", __func__); 354 goto failed_free_res; 355 } 356 357 of_mode.vmode = FB_VMODE_NONINTERLACED; 358 fb_videomode_to_var(&fbi->fb.var, &of_mode); 359 360 fbi->fb.var.nonstd = 0; 361 fbi->fb.var.activate = FB_ACTIVATE_NOW; 362 363 fbi->fb.var.height = -1; 364 fbi->fb.var.width = -1; 365 366 /* try allocating the framebuffer */ 367 fb_mem_len = of_mode.xres * of_mode.yres * 2 * (bpp / 8); 368 fb_mem_virt = dma_alloc_coherent(&pdev->dev, fb_mem_len, &fb_mem_phys, 369 GFP_KERNEL); 370 if (!fb_mem_virt) { 371 pr_err("%s: Failed to allocate framebuffer\n", __func__); 372 return -ENOMEM; 373 }; 374 375 fbi->fb.var.xres_virtual = of_mode.xres; 376 fbi->fb.var.yres_virtual = of_mode.yres * 2; 377 fbi->fb.var.bits_per_pixel = bpp; 378 379 fbi->fb.fix.smem_start = fb_mem_phys; 380 fbi->fb.fix.smem_len = fb_mem_len; 381 fbi->fb.screen_base = fb_mem_virt; 382 fbi->fb.screen_size = fb_mem_len; 383 384 if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) { 385 dev_err(&pdev->dev, "Failed to allocate color map\n"); 386 ret = -ENOMEM; 387 goto failed_free_io; 388 } 389 390 wm8505fb_init_hw(&fbi->fb); 391 392 fbi->contrast = 0x80; 393 ret = wm8505fb_set_par(&fbi->fb); 394 if (ret) { 395 dev_err(&pdev->dev, "Failed to set parameters\n"); 396 goto failed_free_cmap; 397 } 398 399 platform_set_drvdata(pdev, fbi); 400 401 ret = register_framebuffer(&fbi->fb); 402 if (ret < 0) { 403 dev_err(&pdev->dev, 404 "Failed to register framebuffer device: %d\n", ret); 405 goto failed_free_cmap; 406 } 407 408 ret = device_create_file(&pdev->dev, &dev_attr_contrast); 409 if (ret < 0) { 410 printk(KERN_WARNING "fb%d: failed to register attributes (%d)\n", 411 fbi->fb.node, ret); 412 } 413 414 printk(KERN_INFO "fb%d: %s frame buffer at 0x%lx-0x%lx\n", 415 fbi->fb.node, fbi->fb.fix.id, fbi->fb.fix.smem_start, 416 fbi->fb.fix.smem_start + fbi->fb.fix.smem_len - 1); 417 418 return 0; 419 420failed_free_cmap: 421 if (fbi->fb.cmap.len) 422 fb_dealloc_cmap(&fbi->fb.cmap); 423failed_free_io: 424 iounmap(fbi->regbase); 425failed_free_res: 426 release_mem_region(res->start, resource_size(res)); 427failed_fbi: 428 platform_set_drvdata(pdev, NULL); 429 kfree(fbi); 430failed: 431 return ret; 432} 433 434static int wm8505fb_remove(struct platform_device *pdev) 435{ 436 struct wm8505fb_info *fbi = platform_get_drvdata(pdev); 437 struct resource *res; 438 439 device_remove_file(&pdev->dev, &dev_attr_contrast); 440 441 unregister_framebuffer(&fbi->fb); 442 443 writel(0, fbi->regbase); 444 445 if (fbi->fb.cmap.len) 446 fb_dealloc_cmap(&fbi->fb.cmap); 447 448 iounmap(fbi->regbase); 449 450 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 451 release_mem_region(res->start, resource_size(res)); 452 453 kfree(fbi); 454 455 return 0; 456} 457 458static const struct of_device_id wmt_dt_ids[] = { 459 { .compatible = "wm,wm8505-fb", }, 460 {} 461}; 462 463static struct platform_driver wm8505fb_driver = { 464 .probe = wm8505fb_probe, 465 .remove = wm8505fb_remove, 466 .driver = { 467 .owner = THIS_MODULE, 468 .name = DRIVER_NAME, 469 .of_match_table = of_match_ptr(wmt_dt_ids), 470 }, 471}; 472 473module_platform_driver(wm8505fb_driver); 474 475MODULE_AUTHOR("Ed Spiridonov <edo.rus@gmail.com>"); 476MODULE_DESCRIPTION("Framebuffer driver for WMT WM8505"); 477MODULE_LICENSE("GPL v2"); 478MODULE_DEVICE_TABLE(of, wmt_dt_ids);