video: tegra: fb: remove warning unused var 'mode'
[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                 tegra_fb->win->stride = round_up(info->fix.line_length,
112                                                 TEGRA_LINEAR_PITCH_ALIGNMENT);
113                 tegra_fb->win->stride_uv = 0;
114                 tegra_fb->win->offset_u = 0;
115                 tegra_fb->win->offset_v = 0;
116         }
117
118         if (var->pixclock) {
119                 bool stereo;
120                 struct fb_videomode m;
121
122                 fb_var_to_videomode(&m, var);
123
124                 info->mode = (struct fb_videomode *)
125                         fb_find_nearest_mode(&m, &info->modelist);
126                 if (!info->mode) {
127                         dev_warn(&tegra_fb->ndev->dev, "can't match video mode\n");
128                         return -EINVAL;
129                 }
130
131                 /*
132                  * only enable stereo if the mode supports it and
133                  * client requests it
134                  */
135                 stereo = !!(var->vmode & info->mode->vmode &
136                                         FB_VMODE_STEREO_FRAME_PACK);
137
138                 tegra_dc_set_fb_mode(tegra_fb->win->dc, info->mode, stereo);
139
140                 tegra_fb->win->w.full = dfixed_const(info->mode->xres);
141                 tegra_fb->win->h.full = dfixed_const(info->mode->yres);
142                 tegra_fb->win->out_w = info->mode->xres;
143                 tegra_fb->win->out_h = info->mode->yres;
144         }
145         return 0;
146 }
147
148 static int tegra_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
149         unsigned blue, unsigned transp, struct fb_info *info)
150 {
151         struct fb_var_screeninfo *var = &info->var;
152
153         if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
154             info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
155                 u32 v;
156
157                 if (regno >= 16)
158                         return -EINVAL;
159
160                 red = (red >> (16 - info->var.red.length));
161                 green = (green >> (16 - info->var.green.length));
162                 blue = (blue >> (16 - info->var.blue.length));
163
164                 v = (red << var->red.offset) |
165                         (green << var->green.offset) |
166                         (blue << var->blue.offset);
167
168                 ((u32 *)info->pseudo_palette)[regno] = v;
169         }
170
171         return 0;
172 }
173
174 static int tegra_fb_blank(int blank, struct fb_info *info)
175 {
176         struct tegra_fb_info *tegra_fb = info->par;
177
178         switch (blank) {
179         case FB_BLANK_UNBLANK:
180                 dev_dbg(&tegra_fb->ndev->dev, "unblank\n");
181                 tegra_dc_enable(tegra_fb->win->dc);
182                 return 0;
183
184         case FB_BLANK_NORMAL:
185         case FB_BLANK_VSYNC_SUSPEND:
186         case FB_BLANK_HSYNC_SUSPEND:
187         case FB_BLANK_POWERDOWN:
188                 dev_dbg(&tegra_fb->ndev->dev, "blank\n");
189                 tegra_dc_disable(tegra_fb->win->dc);
190                 return 0;
191
192         default:
193                 return -ENOTTY;
194         }
195 }
196
197 static int tegra_fb_pan_display(struct fb_var_screeninfo *var,
198                                 struct fb_info *info)
199 {
200         struct tegra_fb_info *tegra_fb = info->par;
201         char __iomem *flush_start;
202         char __iomem *flush_end;
203         u32 addr;
204
205         if (!tegra_fb->win->cur_handle) {
206                 flush_start = info->screen_base + (var->yoffset * info->fix.line_length);
207                 flush_end = flush_start + (var->yres * info->fix.line_length);
208
209                 info->var.xoffset = var->xoffset;
210                 info->var.yoffset = var->yoffset;
211
212                 addr = info->fix.smem_start + (var->yoffset * info->fix.line_length) +
213                         (var->xoffset * (var->bits_per_pixel/8));
214
215                 tegra_fb->win->phys_addr = addr;
216                 /* TODO: update virt_addr */
217
218                 tegra_dc_update_windows(&tegra_fb->win, 1);
219                 tegra_dc_sync_windows(&tegra_fb->win, 1);
220         }
221
222         return 0;
223 }
224
225 static void tegra_fb_fillrect(struct fb_info *info,
226                               const struct fb_fillrect *rect)
227 {
228         cfb_fillrect(info, rect);
229 }
230
231 static void tegra_fb_copyarea(struct fb_info *info,
232                               const struct fb_copyarea *region)
233 {
234         cfb_copyarea(info, region);
235 }
236
237 static void tegra_fb_imageblit(struct fb_info *info,
238                                const struct fb_image *image)
239 {
240         cfb_imageblit(info, image);
241 }
242
243 static int tegra_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
244 {
245         struct tegra_fb_modedb modedb;
246         struct fb_modelist *modelist;
247         int i;
248
249         switch (cmd) {
250         case FBIO_TEGRA_GET_MODEDB:
251                 if (copy_from_user(&modedb, (void __user *)arg, sizeof(modedb)))
252                         return -EFAULT;
253
254                 i = 0;
255                 list_for_each_entry(modelist, &info->modelist, list) {
256                         struct fb_var_screeninfo var;
257
258                         if (i >= modedb.modedb_len)
259                                 break;
260
261                         /* fb_videomode_to_var doesn't fill out all the members
262                            of fb_var_screeninfo */
263                         memset(&var, 0x0, sizeof(var));
264
265                         fb_videomode_to_var(&var, &modelist->mode);
266
267                         if (copy_to_user((void __user *)&modedb.modedb[i],
268                                          &var, sizeof(var)))
269                                 return -EFAULT;
270                         i++;
271
272                         if (var.vmode & FB_VMODE_STEREO_MASK) {
273                                 if (i >= modedb.modedb_len)
274                                         break;
275                                 var.vmode &= ~FB_VMODE_STEREO_MASK;
276                                 if (copy_to_user(
277                                         (void __user *)&modedb.modedb[i],
278                                          &var, sizeof(var)))
279                                         return -EFAULT;
280                                 i++;
281                         }
282                 }
283                 modedb.modedb_len = i;
284
285                 if (copy_to_user((void __user *)arg, &modedb, sizeof(modedb)))
286                         return -EFAULT;
287                 break;
288
289         default:
290                 return -ENOTTY;
291         }
292
293         return 0;
294 }
295
296 static struct fb_ops tegra_fb_ops = {
297         .owner = THIS_MODULE,
298         .fb_check_var = tegra_fb_check_var,
299         .fb_set_par = tegra_fb_set_par,
300         .fb_setcolreg = tegra_fb_setcolreg,
301         .fb_blank = tegra_fb_blank,
302         .fb_pan_display = tegra_fb_pan_display,
303         .fb_fillrect = tegra_fb_fillrect,
304         .fb_copyarea = tegra_fb_copyarea,
305         .fb_imageblit = tegra_fb_imageblit,
306         .fb_ioctl = tegra_fb_ioctl,
307 };
308
309 void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info,
310                               struct fb_monspecs *specs,
311                               bool (*mode_filter)(const struct tegra_dc *dc,
312                                                   struct fb_videomode *mode))
313 {
314         struct fb_event event;
315         struct fb_modelist *m;
316         int i;
317
318         mutex_lock(&fb_info->info->lock);
319         fb_destroy_modedb(fb_info->info->monspecs.modedb);
320
321         fb_destroy_modelist(&fb_info->info->modelist);
322
323         if (specs == NULL) {
324                 struct tegra_dc_mode mode;
325                 memset(&fb_info->info->monspecs, 0x0,
326                        sizeof(fb_info->info->monspecs));
327                 memset(&mode, 0x0, sizeof(mode));
328                 tegra_dc_set_mode(fb_info->win->dc, &mode);
329                 mutex_unlock(&fb_info->info->lock);
330                 return;
331         }
332
333         memcpy(&fb_info->info->monspecs, specs,
334                sizeof(fb_info->info->monspecs));
335
336         for (i = 0; i < specs->modedb_len; i++) {
337                 if (mode_filter) {
338                         if (mode_filter(fb_info->win->dc, &specs->modedb[i]))
339                                 fb_add_videomode(&specs->modedb[i],
340                                                  &fb_info->info->modelist);
341                 } else {
342                         fb_add_videomode(&specs->modedb[i],
343                                          &fb_info->info->modelist);
344                 }
345         }
346
347         if (list_empty(&fb_info->info->modelist)) {
348                 struct tegra_dc_mode mode;
349                 memset(&fb_info->info->var, 0x0, sizeof(fb_info->info->var));
350                 memset(&mode, 0x0, sizeof(mode));
351                 tegra_dc_set_mode(fb_info->win->dc, &mode);
352         } else {
353                 /* in case the first mode was not matched */
354                 m = list_first_entry(&fb_info->info->modelist, struct fb_modelist, list);
355                 m->mode.flag |= FB_MODE_IS_FIRST;
356                 fb_info->info->mode = (struct fb_videomode *)
357                         fb_find_best_display(specs, &fb_info->info->modelist);
358
359                 /* fb_videomode_to_var doesn't fill out all the members
360                    of fb_var_screeninfo */
361                 memset(&fb_info->info->var, 0x0, sizeof(fb_info->info->var));
362
363                 fb_videomode_to_var(&fb_info->info->var, fb_info->info->mode);
364                 tegra_fb_set_par(fb_info->info);
365         }
366
367         event.info = fb_info->info;
368         fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event);
369         mutex_unlock(&fb_info->info->lock);
370 }
371
372 struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
373                                         struct tegra_dc *dc,
374                                         struct tegra_fb_data *fb_data,
375                                         struct resource *fb_mem)
376 {
377         struct tegra_dc_win *win;
378         struct fb_info *info;
379         struct tegra_fb_info *tegra_fb;
380         void __iomem *fb_base = NULL;
381         unsigned long fb_size = 0;
382         unsigned long fb_phys = 0;
383         int ret = 0;
384
385         win = tegra_dc_get_window(dc, fb_data->win);
386         if (!win) {
387                 dev_err(&ndev->dev, "dc does not have a window at index %d\n",
388                         fb_data->win);
389                 return ERR_PTR(-ENOENT);
390         }
391
392         info = framebuffer_alloc(sizeof(struct tegra_fb_info), &ndev->dev);
393         if (!info) {
394                 ret = -ENOMEM;
395                 goto err;
396         }
397
398         tegra_fb = info->par;
399         tegra_fb->win = win;
400         tegra_fb->ndev = ndev;
401         tegra_fb->fb_mem = fb_mem;
402         tegra_fb->xres = fb_data->xres;
403         tegra_fb->yres = fb_data->yres;
404
405         if (fb_mem) {
406                 fb_size = resource_size(fb_mem);
407                 fb_phys = fb_mem->start;
408                 fb_base = ioremap_nocache(fb_phys, fb_size);
409                 if (!fb_base) {
410                         dev_err(&ndev->dev, "fb can't be mapped\n");
411                         ret = -EBUSY;
412                         goto err_free;
413                 }
414                 tegra_fb->valid = true;
415         }
416
417         info->fbops = &tegra_fb_ops;
418         info->pseudo_palette = pseudo_palette;
419         info->screen_base = fb_base;
420         info->screen_size = fb_size;
421
422         strlcpy(info->fix.id, "tegra_fb", sizeof(info->fix.id));
423         info->fix.type          = FB_TYPE_PACKED_PIXELS;
424         info->fix.visual        = FB_VISUAL_TRUECOLOR;
425         info->fix.xpanstep      = 1;
426         info->fix.ypanstep      = 1;
427         info->fix.accel         = FB_ACCEL_NONE;
428         info->fix.smem_start    = fb_phys;
429         info->fix.smem_len      = fb_size;
430
431         info->var.xres                  = fb_data->xres;
432         info->var.yres                  = fb_data->yres;
433         info->var.xres_virtual          = fb_data->xres;
434         info->var.yres_virtual          = fb_data->yres * 2;
435         info->var.bits_per_pixel        = fb_data->bits_per_pixel;
436         info->var.activate              = FB_ACTIVATE_VBL;
437         info->var.height                = tegra_dc_get_out_height(dc);
438         info->var.width                 = tegra_dc_get_out_width(dc);
439         info->var.pixclock              = 0;
440         info->var.left_margin           = 0;
441         info->var.right_margin          = 0;
442         info->var.upper_margin          = 0;
443         info->var.lower_margin          = 0;
444         info->var.hsync_len             = 0;
445         info->var.vsync_len             = 0;
446         info->var.vmode                 = FB_VMODE_NONINTERLACED;
447
448         win->x.full = dfixed_const(0);
449         win->y.full = dfixed_const(0);
450         win->w.full = dfixed_const(fb_data->xres);
451         win->h.full = dfixed_const(fb_data->yres);
452         /* TODO: set to output res dc */
453         win->out_x = 0;
454         win->out_y = 0;
455         win->out_w = fb_data->xres;
456         win->out_h = fb_data->yres;
457         win->z = 0;
458         win->phys_addr = fb_phys;
459         win->virt_addr = fb_base;
460         win->offset_u = 0;
461         win->offset_v = 0;
462         win->stride = fb_data->xres * fb_data->bits_per_pixel / 8;
463         /* Pad the stride to 16-byte boundary. */
464         win->stride = round_up(win->stride, TEGRA_LINEAR_PITCH_ALIGNMENT);
465         win->stride_uv = 0;
466         win->flags = TEGRA_WIN_FLAG_ENABLED;
467
468         if (fb_mem)
469                 tegra_fb_set_par(info);
470
471         if (register_framebuffer(info)) {
472                 dev_err(&ndev->dev, "failed to register framebuffer\n");
473                 ret = -ENODEV;
474                 goto err_iounmap_fb;
475         }
476
477         tegra_fb->info = info;
478
479         dev_info(&ndev->dev, "probed\n");
480
481         if (fb_data->flags & TEGRA_FB_FLIP_ON_PROBE) {
482                 tegra_dc_update_windows(&tegra_fb->win, 1);
483                 tegra_dc_sync_windows(&tegra_fb->win, 1);
484         }
485
486         return tegra_fb;
487
488 err_iounmap_fb:
489         if (fb_base)
490                 iounmap(fb_base);
491 err_free:
492         framebuffer_release(info);
493 err:
494         return ERR_PTR(ret);
495 }
496
497 void tegra_fb_unregister(struct tegra_fb_info *fb_info)
498 {
499         struct fb_info *info = fb_info->info;
500
501         unregister_framebuffer(info);
502
503         iounmap(info->screen_base);
504         framebuffer_release(info);
505 }