Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
msm_fb.c
Go to the documentation of this file.
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>
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...) \
51 do { \
52  if (msmfb_debug_mask & mask) \
53  printk(KERN_INFO "msmfb: "fmt, ##args); \
54 } while (0)
55 
56 static int msmfb_debug_mask;
57 module_param_named(msmfb_debug_mask, msmfb_debug_mask, int,
58  S_IRUGO | S_IWUSR | S_IWGRP);
59 
60 struct mdp_device *mdp;
61 
62 struct msmfb_info {
63  struct fb_info *fb;
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 
89 };
90 
91 static int msmfb_open(struct fb_info *info, int user)
92 {
93  return 0;
94 }
95 
96 static int msmfb_release(struct fb_info *info, int user)
97 {
98  return 0;
99 }
100 
101 /* Called from dma interrupt handler, must not sleep */
102 static 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 
119 static int msmfb_start_dma(struct msmfb_info *msmfb)
120 {
121  uint32_t x, y, w, h;
122  unsigned addr;
123  unsigned long irq_flags;
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;
169 error:
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 */
179 static void msmfb_handle_vsync_interrupt(struct msmfb_callback *callback)
180 {
181  struct msmfb_info *msmfb = container_of(callback, struct msmfb_info,
183  msmfb_start_dma(msmfb);
184 }
185 
186 static 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 
194 static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top,
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);
206 restart:
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)
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);
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),
290  }
291  }
292 }
293 
294 static 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 
300 static 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  }
320 error:
321  mutex_unlock(&msmfb->panel_init_lock);
322 }
323 
324 
325 static 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 
338 int 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 
357 static 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 
364 static 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 
371 static 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 
379 static 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 
403 DEFINE_MUTEX(mdp_ppp_lock);
404 
405 static 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 
426 static 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 
438 static unsigned PP[16];
439 
440 
441 
442 #define BITS_PER_PIXEL 16
443 
444 static 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 
508 static 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 
538 static 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;
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 
595 error_register_framebuffer:
596  iounmap(fb->screen_base);
597 error_setup_fbmem:
598  framebuffer_release(msmfb->fb);
599  return ret;
600 }
601 
602 static struct platform_driver msm_panel_driver = {
603  /* need to write remove */
604  .probe = msmfb_probe,
605  .driver = {.name = "msm_panel"},
606 };
607 
608 
609 static 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 
619 static 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 
629 static struct class_interface msm_fb_interface = {
630  .add_dev = &msmfb_add_mdp_device,
631  .remove_dev = &msmfb_remove_mdp_device,
632 };
633 
634 static int __init msmfb_init(void)
635 {
636  return register_mdp_client(&msm_fb_interface);
637 }
638 
639 module_init(msmfb_init);