fbdev: framebuffer support for HTC Dream
[pandora-kernel.git] / drivers / video / msm / msm_fb.c
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/delay.h>
21
22 #include <linux/freezer.h>
23 #include <linux/wait.h>
24 #include <linux/msm_mdp.h>
25 #include <linux/io.h>
26 #include <linux/uaccess.h>
27 #include <mach/msm_fb.h>
28 #include <mach/board.h>
29 #include <linux/workqueue.h>
30 #include <linux/clk.h>
31 #include <linux/debugfs.h>
32 #include <linux/dma-mapping.h>
33
34 #define PRINT_FPS 0
35 #define PRINT_BLIT_TIME 0
36
37 #define SLEEPING 0x4
38 #define UPDATING 0x3
39 #define FULL_UPDATE_DONE 0x2
40 #define WAKING 0x1
41 #define AWAKE 0x0
42
43 #define NONE 0
44 #define SUSPEND_RESUME 0x1
45 #define FPS 0x2
46 #define BLIT_TIME 0x4
47 #define SHOW_UPDATES 0x8
48
49 #define DLOG(mask, fmt, args...) \
50 do { \
51         if (msmfb_debug_mask & mask) \
52                 printk(KERN_INFO "msmfb: "fmt, ##args); \
53 } while (0)
54
55 static int msmfb_debug_mask;
56 module_param_named(msmfb_debug_mask, msmfb_debug_mask, int,
57                    S_IRUGO | S_IWUSR | S_IWGRP);
58
59 struct mdp_device *mdp;
60
61 struct msmfb_info {
62         struct fb_info *fb;
63         struct msm_panel_data *panel;
64         int xres;
65         int yres;
66         unsigned output_format;
67         unsigned yoffset;
68         unsigned frame_requested;
69         unsigned frame_done;
70         int sleeping;
71         unsigned update_frame;
72         struct {
73                 int left;
74                 int top;
75                 int eright; /* exclusive */
76                 int ebottom; /* exclusive */
77         } update_info;
78         char *black;
79
80         spinlock_t update_lock;
81         struct mutex panel_init_lock;
82         wait_queue_head_t frame_wq;
83         struct workqueue_struct *resume_workqueue;
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
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                 queue_work(msmfb->resume_workqueue, &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;
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;
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,
182                                                vsync_callback);
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,
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);
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)
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
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                 fb_info->var.reserved[0] = 0x54445055;
473                 fb_info->var.reserved[1] = 0;
474                 fb_info->var.reserved[2] = (uint16_t)msmfb->xres |
475                                            ((uint32_t)msmfb->yres << 16);
476         }
477
478         fb_info->var.red.offset = 11;
479         fb_info->var.red.length = 5;
480         fb_info->var.red.msb_right = 0;
481         fb_info->var.green.offset = 5;
482         fb_info->var.green.length = 6;
483         fb_info->var.green.msb_right = 0;
484         fb_info->var.blue.offset = 0;
485         fb_info->var.blue.length = 5;
486         fb_info->var.blue.msb_right = 0;
487
488         r = fb_alloc_cmap(&fb_info->cmap, 16, 0);
489         fb_info->pseudo_palette = PP;
490
491         PP[0] = 0;
492         for (r = 1; r < 16; r++)
493                 PP[r] = 0xffffffff;
494 }
495
496 static int setup_fbmem(struct msmfb_info *msmfb, struct platform_device *pdev)
497 {
498         struct fb_info *fb = msmfb->fb;
499         struct resource *resource;
500         unsigned long size = msmfb->xres * msmfb->yres *
501                              (BITS_PER_PIXEL >> 3) * 2;
502         unsigned char *fbram;
503
504         /* board file might have attached a resource describing an fb */
505         resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
506         if (!resource)
507                 return -EINVAL;
508
509         /* check the resource is large enough to fit the fb */
510         if (resource->end - resource->start < size) {
511                 printk(KERN_ERR "allocated resource is too small for "
512                                 "fb\n");
513                 return -ENOMEM;
514         }
515         fb->fix.smem_start = resource->start;
516         fb->fix.smem_len = resource->end - resource->start;
517         fbram = ioremap(resource->start,
518                         resource->end - resource->start);
519         if (fbram == 0) {
520                 printk(KERN_ERR "msmfb: cannot allocate fbram!\n");
521                 return -ENOMEM;
522         }
523         fb->screen_base = fbram;
524         return 0;
525 }
526
527 static int msmfb_probe(struct platform_device *pdev)
528 {
529         struct fb_info *fb;
530         struct msmfb_info *msmfb;
531         struct msm_panel_data *panel = pdev->dev.platform_data;
532         int ret;
533
534         if (!panel) {
535                 pr_err("msmfb_probe: no platform data\n");
536                 return -EINVAL;
537         }
538         if (!panel->fb_data) {
539                 pr_err("msmfb_probe: no fb_data\n");
540                 return -EINVAL;
541         }
542
543         fb = framebuffer_alloc(sizeof(struct msmfb_info), &pdev->dev);
544         if (!fb)
545                 return -ENOMEM;
546         msmfb = fb->par;
547         msmfb->fb = fb;
548         msmfb->panel = panel;
549         msmfb->xres = panel->fb_data->xres;
550         msmfb->yres = panel->fb_data->yres;
551
552         ret = setup_fbmem(msmfb, pdev);
553         if (ret)
554                 goto error_setup_fbmem;
555
556         setup_fb_info(msmfb);
557
558         spin_lock_init(&msmfb->update_lock);
559         mutex_init(&msmfb->panel_init_lock);
560         init_waitqueue_head(&msmfb->frame_wq);
561         msmfb->resume_workqueue = create_workqueue("panel_on");
562         if (msmfb->resume_workqueue == NULL) {
563                 printk(KERN_ERR "failed to create panel_on workqueue\n");
564                 ret = -ENOMEM;
565                 goto error_create_workqueue;
566         }
567         INIT_WORK(&msmfb->resume_work, power_on_panel);
568         msmfb->black = kzalloc(msmfb->fb->var.bits_per_pixel*msmfb->xres,
569                                GFP_KERNEL);
570
571         printk(KERN_INFO "msmfb_probe() installing %d x %d panel\n",
572                msmfb->xres, msmfb->yres);
573
574         msmfb->dma_callback.func = msmfb_handle_dma_interrupt;
575         msmfb->vsync_callback.func = msmfb_handle_vsync_interrupt;
576         hrtimer_init(&msmfb->fake_vsync, CLOCK_MONOTONIC,
577                      HRTIMER_MODE_REL);
578
579
580         msmfb->fake_vsync.function = msmfb_fake_vsync;
581
582         ret = register_framebuffer(fb);
583         if (ret)
584                 goto error_register_framebuffer;
585
586         msmfb->sleeping = WAKING;
587
588         return 0;
589
590 error_register_framebuffer:
591         destroy_workqueue(msmfb->resume_workqueue);
592 error_create_workqueue:
593         iounmap(fb->screen_base);
594 error_setup_fbmem:
595         framebuffer_release(msmfb->fb);
596         return ret;
597 }
598
599 static struct platform_driver msm_panel_driver = {
600         /* need to write remove */
601         .probe = msmfb_probe,
602         .driver = {.name = "msm_panel"},
603 };
604
605
606 static int msmfb_add_mdp_device(struct device *dev,
607                                 struct class_interface *class_intf)
608 {
609         /* might need locking if mulitple mdp devices */
610         if (mdp)
611                 return 0;
612         mdp = container_of(dev, struct mdp_device, dev);
613         return platform_driver_register(&msm_panel_driver);
614 }
615
616 static void msmfb_remove_mdp_device(struct device *dev,
617                                 struct class_interface *class_intf)
618 {
619         /* might need locking if mulitple mdp devices */
620         if (dev != &mdp->dev)
621                 return;
622         platform_driver_unregister(&msm_panel_driver);
623         mdp = NULL;
624 }
625
626 static struct class_interface msm_fb_interface = {
627         .add_dev = &msmfb_add_mdp_device,
628         .remove_dev = &msmfb_remove_mdp_device,
629 };
630
631 static int __init msmfb_init(void)
632 {
633         return register_mdp_client(&msm_fb_interface);
634 }
635
636 module_init(msmfb_init);