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.11 639 lines 17 kB view raw
1/* drivers/video/msm/msm_fb.c 2 * 3 * Core MSM framebuffer driver. 4 * 5 * Copyright (C) 2007 Google Incorporated 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/platform_device.h> 18#include <linux/module.h> 19#include <linux/fb.h> 20#include <linux/slab.h> 21#include <linux/delay.h> 22 23#include <linux/freezer.h> 24#include <linux/wait.h> 25#include <linux/msm_mdp.h> 26#include <linux/io.h> 27#include <linux/uaccess.h> 28#include <linux/platform_data/video-msm_fb.h> 29#include <mach/board.h> 30#include <linux/workqueue.h> 31#include <linux/clk.h> 32#include <linux/debugfs.h> 33#include <linux/dma-mapping.h> 34 35#define PRINT_FPS 0 36#define PRINT_BLIT_TIME 0 37 38#define SLEEPING 0x4 39#define UPDATING 0x3 40#define FULL_UPDATE_DONE 0x2 41#define WAKING 0x1 42#define AWAKE 0x0 43 44#define NONE 0 45#define SUSPEND_RESUME 0x1 46#define FPS 0x2 47#define BLIT_TIME 0x4 48#define SHOW_UPDATES 0x8 49 50#define DLOG(mask, fmt, args...) \ 51do { \ 52 if (msmfb_debug_mask & mask) \ 53 printk(KERN_INFO "msmfb: "fmt, ##args); \ 54} while (0) 55 56static int msmfb_debug_mask; 57module_param_named(msmfb_debug_mask, msmfb_debug_mask, int, 58 S_IRUGO | S_IWUSR | S_IWGRP); 59 60struct mdp_device *mdp; 61 62struct msmfb_info { 63 struct fb_info *fb; 64 struct msm_panel_data *panel; 65 int xres; 66 int yres; 67 unsigned output_format; 68 unsigned yoffset; 69 unsigned frame_requested; 70 unsigned frame_done; 71 int sleeping; 72 unsigned update_frame; 73 struct { 74 int left; 75 int top; 76 int eright; /* exclusive */ 77 int ebottom; /* exclusive */ 78 } update_info; 79 char *black; 80 81 spinlock_t update_lock; 82 struct mutex panel_init_lock; 83 wait_queue_head_t frame_wq; 84 struct work_struct resume_work; 85 struct msmfb_callback dma_callback; 86 struct msmfb_callback vsync_callback; 87 struct hrtimer fake_vsync; 88 ktime_t vsync_request_time; 89}; 90 91static int msmfb_open(struct fb_info *info, int user) 92{ 93 return 0; 94} 95 96static int msmfb_release(struct fb_info *info, int user) 97{ 98 return 0; 99} 100 101/* Called from dma interrupt handler, must not sleep */ 102static void msmfb_handle_dma_interrupt(struct msmfb_callback *callback) 103{ 104 unsigned long irq_flags; 105 struct msmfb_info *msmfb = container_of(callback, struct msmfb_info, 106 dma_callback); 107 108 spin_lock_irqsave(&msmfb->update_lock, irq_flags); 109 msmfb->frame_done = msmfb->frame_requested; 110 if (msmfb->sleeping == UPDATING && 111 msmfb->frame_done == msmfb->update_frame) { 112 DLOG(SUSPEND_RESUME, "full update completed\n"); 113 schedule_work(&msmfb->resume_work); 114 } 115 spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); 116 wake_up(&msmfb->frame_wq); 117} 118 119static int msmfb_start_dma(struct msmfb_info *msmfb) 120{ 121 uint32_t x, y, w, h; 122 unsigned addr; 123 unsigned long irq_flags; 124 uint32_t yoffset; 125 s64 time_since_request; 126 struct msm_panel_data *panel = msmfb->panel; 127 128 spin_lock_irqsave(&msmfb->update_lock, irq_flags); 129 time_since_request = ktime_to_ns(ktime_sub(ktime_get(), 130 msmfb->vsync_request_time)); 131 if (time_since_request > 20 * NSEC_PER_MSEC) { 132 uint32_t us; 133 us = do_div(time_since_request, NSEC_PER_MSEC) / NSEC_PER_USEC; 134 printk(KERN_WARNING "msmfb_start_dma %lld.%03u ms after vsync " 135 "request\n", time_since_request, us); 136 } 137 if (msmfb->frame_done == msmfb->frame_requested) { 138 spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); 139 return -1; 140 } 141 if (msmfb->sleeping == SLEEPING) { 142 DLOG(SUSPEND_RESUME, "tried to start dma while asleep\n"); 143 spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); 144 return -1; 145 } 146 x = msmfb->update_info.left; 147 y = msmfb->update_info.top; 148 w = msmfb->update_info.eright - x; 149 h = msmfb->update_info.ebottom - y; 150 yoffset = msmfb->yoffset; 151 msmfb->update_info.left = msmfb->xres + 1; 152 msmfb->update_info.top = msmfb->yres + 1; 153 msmfb->update_info.eright = 0; 154 msmfb->update_info.ebottom = 0; 155 if (unlikely(w > msmfb->xres || h > msmfb->yres || 156 w == 0 || h == 0)) { 157 printk(KERN_INFO "invalid update: %d %d %d " 158 "%d\n", x, y, w, h); 159 msmfb->frame_done = msmfb->frame_requested; 160 goto error; 161 } 162 spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); 163 164 addr = ((msmfb->xres * (yoffset + y) + x) * 2); 165 mdp->dma(mdp, addr + msmfb->fb->fix.smem_start, 166 msmfb->xres * 2, w, h, x, y, &msmfb->dma_callback, 167 panel->interface_type); 168 return 0; 169error: 170 spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); 171 /* some clients need to clear their vsync interrupt */ 172 if (panel->clear_vsync) 173 panel->clear_vsync(panel); 174 wake_up(&msmfb->frame_wq); 175 return 0; 176} 177 178/* Called from esync interrupt handler, must not sleep */ 179static void msmfb_handle_vsync_interrupt(struct msmfb_callback *callback) 180{ 181 struct msmfb_info *msmfb = container_of(callback, struct msmfb_info, 182 vsync_callback); 183 msmfb_start_dma(msmfb); 184} 185 186static enum hrtimer_restart msmfb_fake_vsync(struct hrtimer *timer) 187{ 188 struct msmfb_info *msmfb = container_of(timer, struct msmfb_info, 189 fake_vsync); 190 msmfb_start_dma(msmfb); 191 return HRTIMER_NORESTART; 192} 193 194static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, 195 uint32_t eright, uint32_t ebottom, 196 uint32_t yoffset, int pan_display) 197{ 198 struct msmfb_info *msmfb = info->par; 199 struct msm_panel_data *panel = msmfb->panel; 200 unsigned long irq_flags; 201 int sleeping; 202 int retry = 1; 203 204 DLOG(SHOW_UPDATES, "update %d %d %d %d %d %d\n", 205 left, top, eright, ebottom, yoffset, pan_display); 206restart: 207 spin_lock_irqsave(&msmfb->update_lock, irq_flags); 208 209 /* if we are sleeping, on a pan_display wait 10ms (to throttle back 210 * drawing otherwise return */ 211 if (msmfb->sleeping == SLEEPING) { 212 DLOG(SUSPEND_RESUME, "drawing while asleep\n"); 213 spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); 214 if (pan_display) 215 wait_event_interruptible_timeout(msmfb->frame_wq, 216 msmfb->sleeping != SLEEPING, HZ/10); 217 return; 218 } 219 220 sleeping = msmfb->sleeping; 221 /* on a full update, if the last frame has not completed, wait for it */ 222 if ((pan_display && msmfb->frame_requested != msmfb->frame_done) || 223 sleeping == UPDATING) { 224 int ret; 225 spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); 226 ret = wait_event_interruptible_timeout(msmfb->frame_wq, 227 msmfb->frame_done == msmfb->frame_requested && 228 msmfb->sleeping != UPDATING, 5 * HZ); 229 if (ret <= 0 && (msmfb->frame_requested != msmfb->frame_done || 230 msmfb->sleeping == UPDATING)) { 231 if (retry && panel->request_vsync && 232 (sleeping == AWAKE)) { 233 panel->request_vsync(panel, 234 &msmfb->vsync_callback); 235 retry = 0; 236 printk(KERN_WARNING "msmfb_pan_display timeout " 237 "rerequest vsync\n"); 238 } else { 239 printk(KERN_WARNING "msmfb_pan_display timeout " 240 "waiting for frame start, %d %d\n", 241 msmfb->frame_requested, 242 msmfb->frame_done); 243 return; 244 } 245 } 246 goto restart; 247 } 248 249 250 msmfb->frame_requested++; 251 /* if necessary, update the y offset, if this is the 252 * first full update on resume, set the sleeping state */ 253 if (pan_display) { 254 msmfb->yoffset = yoffset; 255 if (left == 0 && top == 0 && eright == info->var.xres && 256 ebottom == info->var.yres) { 257 if (sleeping == WAKING) { 258 msmfb->update_frame = msmfb->frame_requested; 259 DLOG(SUSPEND_RESUME, "full update starting\n"); 260 msmfb->sleeping = UPDATING; 261 } 262 } 263 } 264 265 /* set the update request */ 266 if (left < msmfb->update_info.left) 267 msmfb->update_info.left = left; 268 if (top < msmfb->update_info.top) 269 msmfb->update_info.top = top; 270 if (eright > msmfb->update_info.eright) 271 msmfb->update_info.eright = eright; 272 if (ebottom > msmfb->update_info.ebottom) 273 msmfb->update_info.ebottom = ebottom; 274 DLOG(SHOW_UPDATES, "update queued %d %d %d %d %d\n", 275 msmfb->update_info.left, msmfb->update_info.top, 276 msmfb->update_info.eright, msmfb->update_info.ebottom, 277 msmfb->yoffset); 278 spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); 279 280 /* if the panel is all the way on wait for vsync, otherwise sleep 281 * for 16 ms (long enough for the dma to panel) and then begin dma */ 282 msmfb->vsync_request_time = ktime_get(); 283 if (panel->request_vsync && (sleeping == AWAKE)) { 284 panel->request_vsync(panel, &msmfb->vsync_callback); 285 } else { 286 if (!hrtimer_active(&msmfb->fake_vsync)) { 287 hrtimer_start(&msmfb->fake_vsync, 288 ktime_set(0, NSEC_PER_SEC/60), 289 HRTIMER_MODE_REL); 290 } 291 } 292} 293 294static void msmfb_update(struct fb_info *info, uint32_t left, uint32_t top, 295 uint32_t eright, uint32_t ebottom) 296{ 297 msmfb_pan_update(info, left, top, eright, ebottom, 0, 0); 298} 299 300static void power_on_panel(struct work_struct *work) 301{ 302 struct msmfb_info *msmfb = 303 container_of(work, struct msmfb_info, resume_work); 304 struct msm_panel_data *panel = msmfb->panel; 305 unsigned long irq_flags; 306 307 mutex_lock(&msmfb->panel_init_lock); 308 DLOG(SUSPEND_RESUME, "turning on panel\n"); 309 if (msmfb->sleeping == UPDATING) { 310 if (panel->unblank(panel)) { 311 printk(KERN_INFO "msmfb: panel unblank failed," 312 "not starting drawing\n"); 313 goto error; 314 } 315 spin_lock_irqsave(&msmfb->update_lock, irq_flags); 316 msmfb->sleeping = AWAKE; 317 wake_up(&msmfb->frame_wq); 318 spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); 319 } 320error: 321 mutex_unlock(&msmfb->panel_init_lock); 322} 323 324 325static int msmfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 326{ 327 if ((var->xres != info->var.xres) || 328 (var->yres != info->var.yres) || 329 (var->xres_virtual != info->var.xres_virtual) || 330 (var->yres_virtual != info->var.yres_virtual) || 331 (var->xoffset != info->var.xoffset) || 332 (var->bits_per_pixel != info->var.bits_per_pixel) || 333 (var->grayscale != info->var.grayscale)) 334 return -EINVAL; 335 return 0; 336} 337 338int msmfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) 339{ 340 struct msmfb_info *msmfb = info->par; 341 struct msm_panel_data *panel = msmfb->panel; 342 343 /* "UPDT" */ 344 if ((panel->caps & MSMFB_CAP_PARTIAL_UPDATES) && 345 (var->reserved[0] == 0x54445055)) { 346 msmfb_pan_update(info, var->reserved[1] & 0xffff, 347 var->reserved[1] >> 16, 348 var->reserved[2] & 0xffff, 349 var->reserved[2] >> 16, var->yoffset, 1); 350 } else { 351 msmfb_pan_update(info, 0, 0, info->var.xres, info->var.yres, 352 var->yoffset, 1); 353 } 354 return 0; 355} 356 357static void msmfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) 358{ 359 cfb_fillrect(p, rect); 360 msmfb_update(p, rect->dx, rect->dy, rect->dx + rect->width, 361 rect->dy + rect->height); 362} 363 364static void msmfb_copyarea(struct fb_info *p, const struct fb_copyarea *area) 365{ 366 cfb_copyarea(p, area); 367 msmfb_update(p, area->dx, area->dy, area->dx + area->width, 368 area->dy + area->height); 369} 370 371static void msmfb_imageblit(struct fb_info *p, const struct fb_image *image) 372{ 373 cfb_imageblit(p, image); 374 msmfb_update(p, image->dx, image->dy, image->dx + image->width, 375 image->dy + image->height); 376} 377 378 379static int msmfb_blit(struct fb_info *info, 380 void __user *p) 381{ 382 struct mdp_blit_req req; 383 struct mdp_blit_req_list req_list; 384 int i; 385 int ret; 386 387 if (copy_from_user(&req_list, p, sizeof(req_list))) 388 return -EFAULT; 389 390 for (i = 0; i < req_list.count; i++) { 391 struct mdp_blit_req_list *list = 392 (struct mdp_blit_req_list *)p; 393 if (copy_from_user(&req, &list->req[i], sizeof(req))) 394 return -EFAULT; 395 ret = mdp->blit(mdp, info, &req); 396 if (ret) 397 return ret; 398 } 399 return 0; 400} 401 402 403DEFINE_MUTEX(mdp_ppp_lock); 404 405static int msmfb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg) 406{ 407 void __user *argp = (void __user *)arg; 408 int ret; 409 410 switch (cmd) { 411 case MSMFB_GRP_DISP: 412 mdp->set_grp_disp(mdp, arg); 413 break; 414 case MSMFB_BLIT: 415 ret = msmfb_blit(p, argp); 416 if (ret) 417 return ret; 418 break; 419 default: 420 printk(KERN_INFO "msmfb unknown ioctl: %d\n", cmd); 421 return -EINVAL; 422 } 423 return 0; 424} 425 426static struct fb_ops msmfb_ops = { 427 .owner = THIS_MODULE, 428 .fb_open = msmfb_open, 429 .fb_release = msmfb_release, 430 .fb_check_var = msmfb_check_var, 431 .fb_pan_display = msmfb_pan_display, 432 .fb_fillrect = msmfb_fillrect, 433 .fb_copyarea = msmfb_copyarea, 434 .fb_imageblit = msmfb_imageblit, 435 .fb_ioctl = msmfb_ioctl, 436}; 437 438static unsigned PP[16]; 439 440 441 442#define BITS_PER_PIXEL 16 443 444static void setup_fb_info(struct msmfb_info *msmfb) 445{ 446 struct fb_info *fb_info = msmfb->fb; 447 int r; 448 449 /* finish setting up the fb_info struct */ 450 strncpy(fb_info->fix.id, "msmfb", 16); 451 fb_info->fix.ypanstep = 1; 452 453 fb_info->fbops = &msmfb_ops; 454 fb_info->flags = FBINFO_DEFAULT; 455 456 fb_info->fix.type = FB_TYPE_PACKED_PIXELS; 457 fb_info->fix.visual = FB_VISUAL_TRUECOLOR; 458 fb_info->fix.line_length = msmfb->xres * 2; 459 460 fb_info->var.xres = msmfb->xres; 461 fb_info->var.yres = msmfb->yres; 462 fb_info->var.width = msmfb->panel->fb_data->width; 463 fb_info->var.height = msmfb->panel->fb_data->height; 464 fb_info->var.xres_virtual = msmfb->xres; 465 fb_info->var.yres_virtual = msmfb->yres * 2; 466 fb_info->var.bits_per_pixel = BITS_PER_PIXEL; 467 fb_info->var.accel_flags = 0; 468 469 fb_info->var.yoffset = 0; 470 471 if (msmfb->panel->caps & MSMFB_CAP_PARTIAL_UPDATES) { 472 /* 473 * Set the param in the fixed screen, so userspace can't 474 * change it. This will be used to check for the 475 * capability. 476 */ 477 fb_info->fix.reserved[0] = 0x5444; 478 fb_info->fix.reserved[1] = 0x5055; 479 480 /* 481 * This preloads the value so that if userspace doesn't 482 * change it, it will be a full update 483 */ 484 fb_info->var.reserved[0] = 0x54445055; 485 fb_info->var.reserved[1] = 0; 486 fb_info->var.reserved[2] = (uint16_t)msmfb->xres | 487 ((uint32_t)msmfb->yres << 16); 488 } 489 490 fb_info->var.red.offset = 11; 491 fb_info->var.red.length = 5; 492 fb_info->var.red.msb_right = 0; 493 fb_info->var.green.offset = 5; 494 fb_info->var.green.length = 6; 495 fb_info->var.green.msb_right = 0; 496 fb_info->var.blue.offset = 0; 497 fb_info->var.blue.length = 5; 498 fb_info->var.blue.msb_right = 0; 499 500 r = fb_alloc_cmap(&fb_info->cmap, 16, 0); 501 fb_info->pseudo_palette = PP; 502 503 PP[0] = 0; 504 for (r = 1; r < 16; r++) 505 PP[r] = 0xffffffff; 506} 507 508static int setup_fbmem(struct msmfb_info *msmfb, struct platform_device *pdev) 509{ 510 struct fb_info *fb = msmfb->fb; 511 struct resource *resource; 512 unsigned long size = msmfb->xres * msmfb->yres * 513 (BITS_PER_PIXEL >> 3) * 2; 514 unsigned char *fbram; 515 516 /* board file might have attached a resource describing an fb */ 517 resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); 518 if (!resource) 519 return -EINVAL; 520 521 /* check the resource is large enough to fit the fb */ 522 if (resource->end - resource->start < size) { 523 printk(KERN_ERR "allocated resource is too small for " 524 "fb\n"); 525 return -ENOMEM; 526 } 527 fb->fix.smem_start = resource->start; 528 fb->fix.smem_len = resource_size(resource); 529 fbram = ioremap(resource->start, resource_size(resource)); 530 if (fbram == NULL) { 531 printk(KERN_ERR "msmfb: cannot allocate fbram!\n"); 532 return -ENOMEM; 533 } 534 fb->screen_base = fbram; 535 return 0; 536} 537 538static int msmfb_probe(struct platform_device *pdev) 539{ 540 struct fb_info *fb; 541 struct msmfb_info *msmfb; 542 struct msm_panel_data *panel = pdev->dev.platform_data; 543 int ret; 544 545 if (!panel) { 546 pr_err("msmfb_probe: no platform data\n"); 547 return -EINVAL; 548 } 549 if (!panel->fb_data) { 550 pr_err("msmfb_probe: no fb_data\n"); 551 return -EINVAL; 552 } 553 554 fb = framebuffer_alloc(sizeof(struct msmfb_info), &pdev->dev); 555 if (!fb) 556 return -ENOMEM; 557 msmfb = fb->par; 558 msmfb->fb = fb; 559 msmfb->panel = panel; 560 msmfb->xres = panel->fb_data->xres; 561 msmfb->yres = panel->fb_data->yres; 562 563 ret = setup_fbmem(msmfb, pdev); 564 if (ret) 565 goto error_setup_fbmem; 566 567 setup_fb_info(msmfb); 568 569 spin_lock_init(&msmfb->update_lock); 570 mutex_init(&msmfb->panel_init_lock); 571 init_waitqueue_head(&msmfb->frame_wq); 572 INIT_WORK(&msmfb->resume_work, power_on_panel); 573 msmfb->black = kzalloc(msmfb->fb->var.bits_per_pixel*msmfb->xres, 574 GFP_KERNEL); 575 576 printk(KERN_INFO "msmfb_probe() installing %d x %d panel\n", 577 msmfb->xres, msmfb->yres); 578 579 msmfb->dma_callback.func = msmfb_handle_dma_interrupt; 580 msmfb->vsync_callback.func = msmfb_handle_vsync_interrupt; 581 hrtimer_init(&msmfb->fake_vsync, CLOCK_MONOTONIC, 582 HRTIMER_MODE_REL); 583 584 585 msmfb->fake_vsync.function = msmfb_fake_vsync; 586 587 ret = register_framebuffer(fb); 588 if (ret) 589 goto error_register_framebuffer; 590 591 msmfb->sleeping = WAKING; 592 593 return 0; 594 595error_register_framebuffer: 596 iounmap(fb->screen_base); 597error_setup_fbmem: 598 framebuffer_release(msmfb->fb); 599 return ret; 600} 601 602static struct platform_driver msm_panel_driver = { 603 /* need to write remove */ 604 .probe = msmfb_probe, 605 .driver = {.name = "msm_panel"}, 606}; 607 608 609static int msmfb_add_mdp_device(struct device *dev, 610 struct class_interface *class_intf) 611{ 612 /* might need locking if mulitple mdp devices */ 613 if (mdp) 614 return 0; 615 mdp = container_of(dev, struct mdp_device, dev); 616 return platform_driver_register(&msm_panel_driver); 617} 618 619static void msmfb_remove_mdp_device(struct device *dev, 620 struct class_interface *class_intf) 621{ 622 /* might need locking if mulitple mdp devices */ 623 if (dev != &mdp->dev) 624 return; 625 platform_driver_unregister(&msm_panel_driver); 626 mdp = NULL; 627} 628 629static struct class_interface msm_fb_interface = { 630 .add_dev = &msmfb_add_mdp_device, 631 .remove_dev = &msmfb_remove_mdp_device, 632}; 633 634static int __init msmfb_init(void) 635{ 636 return register_mdp_client(&msm_fb_interface); 637} 638 639module_init(msmfb_init);