video: tegra: fb: Enable window state while pan
[linux-2.6.git] / drivers / video / tegra / fb.c
1 /*
2  * drivers/video/tegra/fb.c
3  *
4  * Copyright (C) 2010 Google, Inc.
5  * Author: Erik Gilling <konkers@android.com>
6  *         Colin Cross <ccross@android.com>
7  *         Travis Geiselbrecht <travis@palm.com>
8  *
9  * Copyright (C) 2010-2011 NVIDIA Corporation
10  *
11  * This software is licensed under the terms of the GNU General Public
12  * License version 2, as published by the Free Software Foundation, and
13  * may be copied, distributed, and modified under those terms.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  */
21
22 #include <linux/fb.h>
23 #include <linux/module.h>
24 #include <linux/kernel.h>
25 #include <linux/errno.h>
26 #include <linux/string.h>
27 #include <linux/mm.h>
28 #include <linux/uaccess.h>
29 #include <linux/slab.h>
30 #include <linux/file.h>
31 #include <linux/workqueue.h>
32
33 #include <asm/atomic.h>
34
35 #include <video/tegrafb.h>
36
37 #include <mach/dc.h>
38 #include <mach/fb.h>
39 #include <linux/nvhost.h>
40 #include <mach/nvmap.h>
41
42 #include "host/dev.h"
43 #include "nvmap/nvmap.h"
44 #include "dc/dc_priv.h"
45
46 /* Pad pitch to 16-byte boundary. */
47 #define TEGRA_LINEAR_PITCH_ALIGNMENT 16
48
49 struct tegra_fb_info {
50         struct tegra_dc_win     *win;
51         struct nvhost_device    *ndev;
52         struct fb_info          *info;
53         bool                    valid;
54
55         struct resource         *fb_mem;
56
57         int                     xres;
58         int                     yres;
59 };
60
61 /* palette array used by the fbcon */
62 static u32 pseudo_palette[16];
63
64 static int tegra_fb_check_var(struct fb_var_screeninfo *var,
65                               struct fb_info *info)
66 {
67         if ((var->yres * var->xres * var->bits_per_pixel / 8 * 2) >
68             info->screen_size)
69                 return -EINVAL;
70
71         /* double yres_virtual to allow double buffering through pan_display */
72         var->yres_virtual = var->yres * 2;
73
74         return 0;
75 }
76
77 static int tegra_fb_set_par(struct fb_info *info)
78 {
79         struct tegra_fb_info *tegra_fb = info->par;
80         struct fb_var_screeninfo *var = &info->var;
81
82         if (var->bits_per_pixel) {
83                 /* we only support RGB ordering for now */
84                 switch (var->bits_per_pixel) {
85                 case 32:
86                         var->red.offset = 0;
87                         var->red.length = 8;
88                         var->green.offset = 8;
89                         var->green.length = 8;
90                         var->blue.offset = 16;
91                         var->blue.length = 8;
92                         var->transp.offset = 24;
93                         var->transp.length = 8;
94                         tegra_fb->win->fmt = TEGRA_WIN_FMT_R8G8B8A8;
95                         break;
96                 case 16:
97                         var->red.offset = 11;
98                         var->red.length = 5;
99                         var->green.offset = 5;
100                         var->green.length = 6;
101                         var->blue.offset = 0;
102                         var->blue.length = 5;
103                         tegra_fb->win->fmt = TEGRA_WIN_FMT_B5G6R5;
104                         break;
105
106                 default:
107                         return -EINVAL;
108                 }
109                 info->fix.line_length = var->xres * var->bits_per_pixel / 8;
110                 /* Pad the stride to 16-byte boundary. */
111                 info->fix.line_length = round_up(info->fix.line_length,
112                                                 TEGRA_LINEAR_PITCH_ALIGNMENT);
113                 tegra_fb->win->stride = info->fix.line_length;
114                 tegra_fb->win->stride_uv = 0;
115                 tegra_fb->win->phys_addr_u = 0;
116                 tegra_fb->win->phys_addr_v = 0;
117         }
118
119         if (var->pixclock) {
120                 bool stereo;
121                 struct fb_videomode m;
122
123                 fb_var_to_videomode(&m, var);
124
125                 info->mode = (struct fb_videomode *)
126                         fb_find_nearest_mode(&m, &info->modelist);
127                 if (!info->mode) {
128                         dev_warn(&tegra_fb->ndev->dev, "can't match video mode\n");
129                         return -EINVAL;
130                 }
131
132                 /*
133                  * only enable stereo if the mode supports it and
134                  * client requests it
135                  */
136                 stereo = !!(var->vmode & info->mode->vmode &
137 #ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT
138                                         FB_VMODE_STEREO_FRAME_PACK);
139 #else
140                                         FB_VMODE_STEREO_LEFT_RIGHT);
141 #endif
142
143                 tegra_dc_set_fb_mode(tegra_fb->win->dc, info->mode, stereo);
144
145                 tegra_fb->win->w.full = dfixed_const(info->mode->xres);
146                 tegra_fb->win->h.full = dfixed_const(info->mode->yres);
147                 tegra_fb->win->out_w = info->mode->xres;
148                 tegra_fb->win->out_h = info->mode->yres;
149         }
150         return 0;
151 }
152
153 static int tegra_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
154         unsigned blue, unsigned transp, struct fb_info *info)
155 {
156         struct fb_var_screeninfo *var = &info->var;
157
158         if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
159             info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
160                 u32 v;
161
162                 if (regno >= 16)
163                         return -EINVAL;
164
165                 red = (red >> (16 - info->var.red.length));
166                 green = (green >> (16 - info->var.green.length));
167                 blue = (blue >> (16 - info->var.blue.length));
168
169                 v = (red << var->red.offset) |
170                         (green << var->green.offset) |
171                         (blue << var->blue.offset);
172
173                 ((u32 *)info->pseudo_palette)[regno] = v;
174         }
175
176         return 0;
177 }
178
179
180 static int tegra_fb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
181 {
182         struct tegra_fb_info *tegra_fb = info->par;
183         struct tegra_dc *dc = tegra_fb->win->dc;
184         int i;
185         u16 *red = cmap->red;
186         u16 *green = cmap->green;
187         u16 *blue = cmap->blue;
188         int start = cmap->start;
189
190         if (((unsigned)start > 255) || ((start + cmap->len) > 256))
191                 return -EINVAL;
192
193         if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
194                 info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
195                 /*
196                  * For now we are considering color schemes with
197                  * cmap->len <=16 as special case of basic color
198                  * scheme to support fbconsole.But for DirectColor
199                  * visuals(like the one we actually have, that include
200                  * a HW LUT),the way it's intended to work is that the
201                  * actual LUT HW is programmed to the intended values,
202                  * even for small color maps like those with 16 or fewer
203                  * entries. The pseudo_palette is then programmed to the
204                  * identity transform.
205                  */
206                 if (cmap->len <= 16) {
207                         /* Low-color schemes like fbconsole*/
208                         u16 *transp = cmap->transp;
209                         u_int vtransp = 0xffff;
210
211                         for (i = 0; i < cmap->len; i++) {
212                                 if (transp)
213                                         vtransp = *transp++;
214                                 if (tegra_fb_setcolreg(start++, *red++,
215                                         *green++, *blue++,
216                                         vtransp, info))
217                                                 return -EINVAL;
218                         }
219                 } else {
220                         /* High-color schemes*/
221                         for (i = 0; i < cmap->len; i++) {
222                                 dc->fb_lut.r[start+i] = *red++ >> 8;
223                                 dc->fb_lut.g[start+i] = *green++ >> 8;
224                                 dc->fb_lut.b[start+i] = *blue++ >> 8;
225                         }
226                         tegra_dc_update_lut(dc, -1, -1);
227                 }
228         }
229         return 0;
230 }
231
232 #if defined(CONFIG_FRAMEBUFFER_CONSOLE)
233 static void tegra_fb_flip_win(struct tegra_fb_info *tegra_fb)
234 {
235         struct tegra_dc_win *win = tegra_fb->win;
236         struct fb_info *info = tegra_fb->info;
237
238         win->x.full = dfixed_const(0);
239         win->y.full = dfixed_const(0);
240         win->w.full = dfixed_const(tegra_fb->xres);
241         win->h.full = dfixed_const(tegra_fb->yres);
242
243         /* TODO: set to output res dc */
244         win->out_x = 0;
245         win->out_y = 0;
246         win->out_w = tegra_fb->xres;
247         win->out_h = tegra_fb->yres;
248         win->z = 0;
249         win->phys_addr = info->fix.smem_start +
250                 (info->var.yoffset * info->fix.line_length) +
251                 (info->var.xoffset * (info->var.bits_per_pixel / 8));
252         win->virt_addr = info->screen_base;
253
254         win->phys_addr_u = 0;
255         win->phys_addr_v = 0;
256         win->stride = info->fix.line_length;
257         win->stride_uv = 0;
258
259         switch (info->var.bits_per_pixel) {
260         default:
261                 WARN_ON(1);
262                 /* fall through */
263         case 32:
264                 tegra_fb->win->fmt = TEGRA_WIN_FMT_R8G8B8A8;
265                 break;
266         case 16:
267                 tegra_fb->win->fmt = TEGRA_WIN_FMT_B5G6R5;
268                 break;
269         }
270         win->flags = TEGRA_WIN_FLAG_ENABLED;
271
272         tegra_dc_update_windows(&tegra_fb->win, 1);
273         tegra_dc_sync_windows(&tegra_fb->win, 1);
274 }
275 #endif
276
277 static int tegra_fb_blank(int blank, struct fb_info *info)
278 {
279         struct tegra_fb_info *tegra_fb = info->par;
280
281         switch (blank) {
282         case FB_BLANK_UNBLANK:
283                 dev_dbg(&tegra_fb->ndev->dev, "unblank\n");
284                 tegra_fb->win->flags = TEGRA_WIN_FLAG_ENABLED;
285                 tegra_dc_enable(tegra_fb->win->dc);
286                 return 0;
287
288         case FB_BLANK_NORMAL:
289                 dev_dbg(&tegra_fb->ndev->dev, "blank - normal\n");
290                 tegra_dc_blank(tegra_fb->win->dc);
291                 return 0;
292
293         case FB_BLANK_VSYNC_SUSPEND:
294         case FB_BLANK_HSYNC_SUSPEND:
295         case FB_BLANK_POWERDOWN:
296                 dev_dbg(&tegra_fb->ndev->dev, "blank - powerdown\n");
297                 tegra_dc_disable(tegra_fb->win->dc);
298                 return 0;
299
300         default:
301                 return -ENOTTY;
302         }
303 }
304
305 static int tegra_fb_pan_display(struct fb_var_screeninfo *var,
306                                 struct fb_info *info)
307 {
308         struct tegra_fb_info *tegra_fb = info->par;
309         char __iomem *flush_start;
310         char __iomem *flush_end;
311         u32 addr;
312
313         if (!tegra_fb->win->cur_handle) {
314                 flush_start = info->screen_base + (var->yoffset * info->fix.line_length);
315                 flush_end = flush_start + (var->yres * info->fix.line_length);
316
317                 info->var.xoffset = var->xoffset;
318                 info->var.yoffset = var->yoffset;
319
320                 addr = info->fix.smem_start + (var->yoffset * info->fix.line_length) +
321                         (var->xoffset * (var->bits_per_pixel/8));
322
323                 tegra_fb->win->phys_addr = addr;
324                 tegra_fb->win->flags = TEGRA_WIN_FLAG_ENABLED;
325                 tegra_fb->win->virt_addr = info->screen_base;
326
327                 tegra_dc_update_windows(&tegra_fb->win, 1);
328                 tegra_dc_sync_windows(&tegra_fb->win, 1);
329         }
330
331         return 0;
332 }
333
334 static void tegra_fb_fillrect(struct fb_info *info,
335                               const struct fb_fillrect *rect)
336 {
337         cfb_fillrect(info, rect);
338 }
339
340 static void tegra_fb_copyarea(struct fb_info *info,
341                               const struct fb_copyarea *region)
342 {
343         cfb_copyarea(info, region);
344 }
345
346 static void tegra_fb_imageblit(struct fb_info *info,
347                                const struct fb_image *image)
348 {
349         cfb_imageblit(info, image);
350 }
351
352 static int tegra_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
353 {
354         struct tegra_fb_modedb modedb;
355         struct fb_modelist *modelist;
356         int i;
357
358         switch (cmd) {
359         case FBIO_TEGRA_GET_MODEDB:
360                 if (copy_from_user(&modedb, (void __user *)arg, sizeof(modedb)))
361                         return -EFAULT;
362
363                 i = 0;
364                 list_for_each_entry(modelist, &info->modelist, list) {
365                         struct fb_var_screeninfo var;
366
367                         if (i >= modedb.modedb_len)
368                                 break;
369
370                         /* fb_videomode_to_var doesn't fill out all the members
371                            of fb_var_screeninfo */
372                         memset(&var, 0x0, sizeof(var));
373
374                         fb_videomode_to_var(&var, &modelist->mode);
375
376                         if (copy_to_user((void __user *)&modedb.modedb[i],
377                                          &var, sizeof(var)))
378                                 return -EFAULT;
379                         i++;
380
381                         if (var.vmode & FB_VMODE_STEREO_MASK) {
382                                 if (i >= modedb.modedb_len)
383                                         break;
384                                 var.vmode &= ~FB_VMODE_STEREO_MASK;
385                                 if (copy_to_user(
386                                         (void __user *)&modedb.modedb[i],
387                                          &var, sizeof(var)))
388                                         return -EFAULT;
389                                 i++;
390                         }
391                 }
392                 modedb.modedb_len = i;
393
394                 if (copy_to_user((void __user *)arg, &modedb, sizeof(modedb)))
395                         return -EFAULT;
396                 break;
397
398         default:
399                 return -ENOTTY;
400         }
401
402         return 0;
403 }
404
405 int tegra_fb_get_mode(struct tegra_dc *dc) {
406         return dc->fb->info->mode->refresh;
407 }
408
409 int tegra_fb_set_mode(struct tegra_dc *dc, int fps) {
410         size_t stereo;
411         struct list_head *pos;
412         struct fb_videomode *best_mode = NULL;
413         int curr_diff = INT_MAX; /* difference of best_mode refresh rate */
414         struct fb_modelist *modelist;
415         struct fb_info *info = dc->fb->info;
416
417         list_for_each(pos, &info->modelist) {
418                 struct fb_videomode *mode;
419
420                 modelist = list_entry(pos, struct fb_modelist, list);
421                 mode = &modelist->mode;
422                 if (fps <= mode->refresh && curr_diff > (mode->refresh - fps)) {
423                         curr_diff = mode->refresh - fps;
424                         best_mode = mode;
425                 }
426         }
427         if (best_mode) {
428                 info->mode = best_mode;
429                 stereo = !!(info->var.vmode & info->mode->vmode &
430 #ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT
431                                 FB_VMODE_STEREO_FRAME_PACK);
432 #else
433                                 FB_VMODE_STEREO_LEFT_RIGHT);
434 #endif
435                 return tegra_dc_set_fb_mode(dc, best_mode, stereo);
436         }
437         return -EIO;
438 }
439
440 static struct fb_ops tegra_fb_ops = {
441         .owner = THIS_MODULE,
442         .fb_check_var = tegra_fb_check_var,
443         .fb_set_par = tegra_fb_set_par,
444         .fb_setcmap = tegra_fb_setcmap,
445         .fb_blank = tegra_fb_blank,
446         .fb_pan_display = tegra_fb_pan_display,
447         .fb_fillrect = tegra_fb_fillrect,
448         .fb_copyarea = tegra_fb_copyarea,
449         .fb_imageblit = tegra_fb_imageblit,
450         .fb_ioctl = tegra_fb_ioctl,
451 };
452
453 void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info,
454                               struct fb_monspecs *specs,
455                               bool (*mode_filter)(const struct tegra_dc *dc,
456                                                   struct fb_videomode *mode))
457 {
458         struct fb_event event;
459         int i;
460
461         mutex_lock(&fb_info->info->lock);
462         fb_destroy_modedb(fb_info->info->monspecs.modedb);
463
464         fb_destroy_modelist(&fb_info->info->modelist);
465
466         if (specs == NULL) {
467                 struct tegra_dc_mode mode;
468                 memset(&fb_info->info->monspecs, 0x0,
469                        sizeof(fb_info->info->monspecs));
470                 memset(&mode, 0x0, sizeof(mode));
471
472                 /*
473                  * reset video mode properties to prevent garbage being displayed on 'mode' device.
474                  */
475                 fb_info->info->mode = (struct fb_videomode*) NULL;
476
477                 tegra_dc_set_mode(fb_info->win->dc, &mode);
478                 mutex_unlock(&fb_info->info->lock);
479                 return;
480         }
481
482         memcpy(&fb_info->info->monspecs, specs,
483                sizeof(fb_info->info->monspecs));
484         fb_info->info->mode = specs->modedb;
485
486         for (i = 0; i < specs->modedb_len; i++) {
487                 if (mode_filter) {
488                         if (mode_filter(fb_info->win->dc, &specs->modedb[i]))
489                                 fb_add_videomode(&specs->modedb[i],
490                                                  &fb_info->info->modelist);
491                 } else {
492                         fb_add_videomode(&specs->modedb[i],
493                                          &fb_info->info->modelist);
494                 }
495         }
496
497         event.info = fb_info->info;
498         fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event);
499         mutex_unlock(&fb_info->info->lock);
500 }
501
502 struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
503                                         struct tegra_dc *dc,
504                                         struct tegra_fb_data *fb_data,
505                                         struct resource *fb_mem)
506 {
507         struct tegra_dc_win *win;
508         struct fb_info *info;
509         struct tegra_fb_info *tegra_fb;
510         void __iomem *fb_base = NULL;
511         unsigned long fb_size = 0;
512         unsigned long fb_phys = 0;
513         int ret = 0;
514
515         win = tegra_dc_get_window(dc, fb_data->win);
516         if (!win) {
517                 dev_err(&ndev->dev, "dc does not have a window at index %d\n",
518                         fb_data->win);
519                 return ERR_PTR(-ENOENT);
520         }
521
522         info = framebuffer_alloc(sizeof(struct tegra_fb_info), &ndev->dev);
523         if (!info) {
524                 ret = -ENOMEM;
525                 goto err;
526         }
527
528         tegra_fb = info->par;
529         tegra_fb->win = win;
530         tegra_fb->ndev = ndev;
531         tegra_fb->fb_mem = fb_mem;
532         tegra_fb->xres = fb_data->xres;
533         tegra_fb->yres = fb_data->yres;
534
535         if (fb_mem) {
536                 fb_size = resource_size(fb_mem);
537                 fb_phys = fb_mem->start;
538                 fb_base = ioremap_nocache(fb_phys, fb_size);
539                 if (!fb_base) {
540                         dev_err(&ndev->dev, "fb can't be mapped\n");
541                         ret = -EBUSY;
542                         goto err_free;
543                 }
544                 tegra_fb->valid = true;
545         }
546
547         info->fbops = &tegra_fb_ops;
548         info->pseudo_palette = pseudo_palette;
549         info->screen_base = fb_base;
550         info->screen_size = fb_size;
551
552         strlcpy(info->fix.id, "tegra_fb", sizeof(info->fix.id));
553         info->fix.type          = FB_TYPE_PACKED_PIXELS;
554         info->fix.visual        = FB_VISUAL_TRUECOLOR;
555         info->fix.xpanstep      = 1;
556         info->fix.ypanstep      = 1;
557         info->fix.accel         = FB_ACCEL_NONE;
558         info->fix.smem_start    = fb_phys;
559         info->fix.smem_len      = fb_size;
560         info->fix.line_length = fb_data->xres * fb_data->bits_per_pixel / 8;
561         /* Pad the stride to 16-byte boundary. */
562         info->fix.line_length = round_up(info->fix.line_length,
563                                         TEGRA_LINEAR_PITCH_ALIGNMENT);
564
565         info->var.xres                  = fb_data->xres;
566         info->var.yres                  = fb_data->yres;
567         info->var.xres_virtual          = fb_data->xres;
568         info->var.yres_virtual          = fb_data->yres * 2;
569         info->var.bits_per_pixel        = fb_data->bits_per_pixel;
570         info->var.activate              = FB_ACTIVATE_VBL;
571         info->var.height                = tegra_dc_get_out_height(dc);
572         info->var.width                 = tegra_dc_get_out_width(dc);
573         info->var.pixclock              = 0;
574         info->var.left_margin           = 0;
575         info->var.right_margin          = 0;
576         info->var.upper_margin          = 0;
577         info->var.lower_margin          = 0;
578         info->var.hsync_len             = 0;
579         info->var.vsync_len             = 0;
580         info->var.vmode                 = FB_VMODE_NONINTERLACED;
581
582         win->x.full = dfixed_const(0);
583         win->y.full = dfixed_const(0);
584         win->w.full = dfixed_const(fb_data->xres);
585         win->h.full = dfixed_const(fb_data->yres);
586         /* TODO: set to output res dc */
587         win->out_x = 0;
588         win->out_y = 0;
589         win->out_w = fb_data->xres;
590         win->out_h = fb_data->yres;
591         win->z = 0;
592         win->phys_addr = fb_phys;
593         win->virt_addr = fb_base;
594         win->phys_addr_u = 0;
595         win->phys_addr_v = 0;
596         win->stride = info->fix.line_length;
597         win->stride_uv = 0;
598         win->flags = TEGRA_WIN_FLAG_ENABLED;
599
600         if (fb_mem)
601                 tegra_fb_set_par(info);
602
603         if (register_framebuffer(info)) {
604                 dev_err(&ndev->dev, "failed to register framebuffer\n");
605                 ret = -ENODEV;
606                 goto err_iounmap_fb;
607         }
608
609         tegra_fb->info = info;
610
611         dev_info(&ndev->dev, "probed\n");
612
613         if (fb_data->flags & TEGRA_FB_FLIP_ON_PROBE) {
614                 tegra_dc_update_windows(&tegra_fb->win, 1);
615                 tegra_dc_sync_windows(&tegra_fb->win, 1);
616         }
617
618         if (dc->mode.pclk > 1000) {
619                 struct tegra_dc_mode *mode = &dc->mode;
620
621                 if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
622                         info->var.pixclock = KHZ2PICOS(mode->rated_pclk / 1000);
623                 else
624                         info->var.pixclock = KHZ2PICOS(mode->pclk / 1000);
625                 info->var.left_margin = mode->h_back_porch;
626                 info->var.right_margin = mode->h_front_porch;
627                 info->var.upper_margin = mode->v_back_porch;
628                 info->var.lower_margin = mode->v_front_porch;
629                 info->var.hsync_len = mode->h_sync_width;
630                 info->var.vsync_len = mode->v_sync_width;
631         }
632
633         return tegra_fb;
634
635 err_iounmap_fb:
636         if (fb_base)
637                 iounmap(fb_base);
638 err_free:
639         framebuffer_release(info);
640 err:
641         return ERR_PTR(ret);
642 }
643
644 void tegra_fb_unregister(struct tegra_fb_info *fb_info)
645 {
646         struct fb_info *info = fb_info->info;
647
648         unregister_framebuffer(info);
649
650         iounmap(info->screen_base);
651         framebuffer_release(info);
652 }