95c7c489b513fc3d8adc2870c402eef7ea88f2d6
[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  * This software is licensed under the terms of the GNU General Public
10  * License version 2, as published by the Free Software Foundation, and
11  * may be copied, distributed, and modified under those terms.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  */
19
20 #include <linux/fb.h>
21 #include <linux/module.h>
22 #include <linux/kernel.h>
23 #include <linux/errno.h>
24 #include <linux/string.h>
25 #include <linux/mm.h>
26 #include <linux/slab.h>
27 #include <linux/nvhost_bus.h>
28
29 #include <asm/atomic.h>
30
31 #include <mach/dc.h>
32 #include <mach/fb.h>
33
34 struct tegra_fb_info {
35         struct tegra_dc_win     *win;
36         struct nvhost_device    *ndev;
37         struct fb_info          *info;
38         bool                    valid;
39
40         struct resource         *fb_mem;
41
42         int                     xres;
43         int                     yres;
44
45         atomic_t                in_use;
46 };
47
48 /* palette array used by the fbcon */
49 static u32 pseudo_palette[16];
50
51 static int tegra_fb_open(struct fb_info *info, int user)
52 {
53         struct tegra_fb_info *tegra_fb = info->par;
54
55         if (atomic_xchg(&tegra_fb->in_use, 1))
56                 return -EBUSY;
57
58         return 0;
59 }
60
61 static int tegra_fb_release(struct fb_info *info, int user)
62 {
63         struct tegra_fb_info *tegra_fb = info->par;
64
65         WARN_ON(!atomic_xchg(&tegra_fb->in_use, 0));
66
67         return 0;
68 }
69
70 static int tegra_fb_check_var(struct fb_var_screeninfo *var,
71                               struct fb_info *info)
72 {
73         if ((var->yres * var->xres * var->bits_per_pixel / 8 * 2) >
74             info->screen_size)
75                 return -EINVAL;
76
77         /* double yres_virtual to allow double buffering through pan_display */
78         var->yres_virtual = var->yres * 2;
79
80         return 0;
81 }
82
83 static int tegra_fb_set_par(struct fb_info *info)
84 {
85         struct tegra_fb_info *tegra_fb = info->par;
86         struct fb_var_screeninfo *var = &info->var;
87
88         /* we only support RGB ordering for now */
89         switch (var->bits_per_pixel) {
90         case 32:
91         case 24:
92                 var->red.offset = 0;
93                 var->red.length = 8;
94                 var->green.offset = 8;
95                 var->green.length = 8;
96                 var->blue.offset = 16;
97                 var->blue.length = 8;
98                 tegra_fb->win->fmt = TEGRA_WIN_FMT_R8G8B8A8;
99                 break;
100         case 16:
101                 var->red.offset = 11;
102                 var->red.length = 5;
103                 var->green.offset = 5;
104                 var->green.length = 6;
105                 var->blue.offset = 0;
106                 var->blue.length = 5;
107                 tegra_fb->win->fmt = TEGRA_WIN_FMT_B5G6R5;
108                 break;
109
110         case 0:
111                 break;
112
113         default:
114                 return -EINVAL;
115         }
116         info->fix.line_length = var->xres * var->bits_per_pixel / 8;
117
118         if (var->pixclock) {
119                 struct tegra_dc_mode mode;
120
121                 info->mode = (struct fb_videomode *)
122                         fb_find_best_mode(var, &info->modelist);
123                 if (!info->mode) {
124                         dev_warn(&tegra_fb->ndev->dev, "can't match video mode\n");
125                         return -EINVAL;
126                 }
127
128                 mode.pclk = PICOS2KHZ(info->mode->pixclock) * 1000;
129                 mode.h_ref_to_sync = 1;
130                 mode.v_ref_to_sync = 1;
131                 mode.h_sync_width = info->mode->hsync_len;
132                 mode.v_sync_width = info->mode->vsync_len;
133                 mode.h_back_porch = info->mode->left_margin;
134                 mode.v_back_porch = info->mode->upper_margin;
135                 mode.h_active = info->mode->xres;
136                 mode.v_active = info->mode->yres;
137                 mode.h_front_porch = info->mode->right_margin;
138                 mode.v_front_porch = info->mode->lower_margin;
139
140                 tegra_dc_set_mode(tegra_fb->win->dc, &mode);
141         }
142         return 0;
143 }
144
145 static int tegra_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
146         unsigned blue, unsigned transp, struct fb_info *info)
147 {
148         struct fb_var_screeninfo *var = &info->var;
149
150         if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
151             info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
152                 u32 v;
153
154                 if (regno >= 16)
155                         return -EINVAL;
156
157                 v = (red << var->red.offset) |
158                         (green << var->green.offset) |
159                         (blue << var->blue.offset);
160
161                 ((u32 *)info->pseudo_palette)[regno] = v;
162         }
163
164         return 0;
165 }
166
167 static int tegra_fb_blank(int blank, struct fb_info *info)
168 {
169         struct tegra_fb_info *tegra_fb = info->par;
170
171         switch (blank) {
172         case FB_BLANK_UNBLANK:
173                 dev_dbg(&tegra_fb->ndev->dev, "unblank\n");
174                 tegra_dc_enable(tegra_fb->win->dc);
175                 return 0;
176
177         case FB_BLANK_POWERDOWN:
178                 dev_dbg(&tegra_fb->ndev->dev, "blank\n");
179                 tegra_dc_disable(tegra_fb->win->dc);
180                 return 0;
181
182         default:
183                 return -ENOTTY;
184         }
185 }
186
187 static int tegra_fb_pan_display(struct fb_var_screeninfo *var,
188                                 struct fb_info *info)
189 {
190         struct tegra_fb_info *tegra_fb = info->par;
191         char __iomem *flush_start;
192         char __iomem *flush_end;
193         u32 addr;
194
195         flush_start = info->screen_base + (var->yoffset * info->fix.line_length);
196         flush_end = flush_start + (var->yres * info->fix.line_length);
197
198         info->var.xoffset = var->xoffset;
199         info->var.yoffset = var->yoffset;
200
201         addr = info->fix.smem_start + (var->yoffset * info->fix.line_length) +
202                 (var->xoffset * (var->bits_per_pixel/8));
203
204         tegra_fb->win->phys_addr = addr;
205         /* TODO: update virt_addr */
206
207         tegra_dc_update_windows(&tegra_fb->win, 1);
208         tegra_dc_sync_windows(&tegra_fb->win, 1);
209
210         return 0;
211 }
212
213 static void tegra_fb_fillrect(struct fb_info *info,
214                               const struct fb_fillrect *rect)
215 {
216         cfb_fillrect(info, rect);
217 }
218
219 static void tegra_fb_copyarea(struct fb_info *info,
220                               const struct fb_copyarea *region)
221 {
222         cfb_copyarea(info, region);
223 }
224
225 static void tegra_fb_imageblit(struct fb_info *info,
226                                const struct fb_image *image)
227 {
228         cfb_imageblit(info, image);
229 }
230
231 static struct fb_ops tegra_fb_ops = {
232         .owner = THIS_MODULE,
233         .fb_open = tegra_fb_open,
234         .fb_release = tegra_fb_release,
235         .fb_check_var = tegra_fb_check_var,
236         .fb_set_par = tegra_fb_set_par,
237         .fb_setcolreg = tegra_fb_setcolreg,
238         .fb_blank = tegra_fb_blank,
239         .fb_pan_display = tegra_fb_pan_display,
240         .fb_fillrect = tegra_fb_fillrect,
241         .fb_copyarea = tegra_fb_copyarea,
242         .fb_imageblit = tegra_fb_imageblit,
243 };
244
245 void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info,
246                               struct fb_monspecs *specs,
247                               bool (*mode_filter)(struct fb_videomode *mode))
248 {
249         struct fb_event event;
250         int i;
251
252         mutex_lock(&fb_info->info->lock);
253         fb_destroy_modedb(fb_info->info->monspecs.modedb);
254
255         memcpy(&fb_info->info->monspecs, specs,
256                sizeof(fb_info->info->monspecs));
257
258         fb_destroy_modelist(&fb_info->info->modelist);
259
260         for (i = 0; i < specs->modedb_len; i++) {
261                 if (mode_filter) {
262                         if (mode_filter(&specs->modedb[i]))
263                                 fb_add_videomode(&specs->modedb[i],
264                                                  &fb_info->info->modelist);
265                 } else {
266                         fb_add_videomode(&specs->modedb[i],
267                                          &fb_info->info->modelist);
268                 }
269         }
270
271         fb_info->info->mode = (struct fb_videomode *)
272                 fb_find_best_display(specs, &fb_info->info->modelist);
273
274         event.info = fb_info->info;
275         fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event);
276         mutex_unlock(&fb_info->info->lock);
277 }
278
279 struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
280                                         struct tegra_dc *dc,
281                                         struct tegra_fb_data *fb_data,
282                                         struct resource *fb_mem)
283 {
284         struct tegra_dc_win *win;
285         struct fb_info *info;
286         struct tegra_fb_info *tegra_fb;
287         void __iomem *fb_base = NULL;
288         unsigned long fb_size = 0;      unsigned long fb_phys = 0;
289         int ret = 0;
290
291         win = tegra_dc_get_window(dc, fb_data->win);
292         if (!win) {
293                 dev_err(&ndev->dev, "dc does not have a window at index %d\n",
294                         fb_data->win);
295                 return ERR_PTR(-ENOENT);
296         }
297
298         info = framebuffer_alloc(sizeof(struct tegra_fb_info), &ndev->dev);
299         if (!info) {
300                 ret = -ENOMEM;
301                 goto err;
302         }
303
304         tegra_fb = info->par;
305         tegra_fb->win = win;
306         tegra_fb->ndev = ndev;
307         tegra_fb->fb_mem = fb_mem;
308         tegra_fb->xres = fb_data->xres;
309         tegra_fb->yres = fb_data->yres;
310         atomic_set(&tegra_fb->in_use, 0);
311
312         if (fb_mem) {
313                 fb_size = resource_size(fb_mem);
314                 fb_phys = fb_mem->start;
315                 fb_base = ioremap_nocache(fb_phys, fb_size);
316                 if (!fb_base) {
317                         dev_err(&ndev->dev, "fb can't be mapped\n");
318                         ret = -EBUSY;
319                         goto err_free;
320                 }
321                 tegra_fb->valid = true;
322         }
323
324         info->fbops = &tegra_fb_ops;
325         info->pseudo_palette = pseudo_palette;
326         info->screen_base = fb_base;
327         info->screen_size = fb_size;
328
329         strlcpy(info->fix.id, "tegra_fb", sizeof(info->fix.id));
330         info->fix.type          = FB_TYPE_PACKED_PIXELS;
331         info->fix.visual        = FB_VISUAL_TRUECOLOR;
332         info->fix.xpanstep      = 1;
333         info->fix.ypanstep      = 1;
334         info->fix.accel         = FB_ACCEL_NONE;
335         info->fix.smem_start    = fb_phys;
336         info->fix.smem_len      = fb_size;
337
338         info->var.xres                  = fb_data->xres;
339         info->var.yres                  = fb_data->yres;
340         info->var.xres_virtual          = fb_data->xres;
341         info->var.yres_virtual          = fb_data->yres * 2;
342         info->var.bits_per_pixel        = fb_data->bits_per_pixel;
343         info->var.activate              = FB_ACTIVATE_VBL;
344         /* TODO: fill in the following by querying the DC */
345         info->var.height                = -1;
346         info->var.width                 = -1;
347         info->var.pixclock              = 0;
348         info->var.left_margin           = 0;
349         info->var.right_margin          = 0;
350         info->var.upper_margin          = 0;
351         info->var.lower_margin          = 0;
352         info->var.hsync_len             = 0;
353         info->var.vsync_len             = 0;
354         info->var.vmode                 = FB_VMODE_NONINTERLACED;
355
356         win->x = 0;
357         win->y = 0;
358         win->w = fb_data->xres;
359         win->h = fb_data->yres;
360         /* TODO: set to output res dc */
361         win->out_w = fb_data->xres;
362         win->out_h = fb_data->yres;
363         win->phys_addr = fb_phys;
364         win->virt_addr = fb_base;
365         win->flags = TEGRA_WIN_FLAG_ENABLED | TEGRA_WIN_FLAG_COLOR_EXPAND;
366
367         if (fb_mem)
368                 tegra_fb_set_par(info);
369
370         if (register_framebuffer(info)) {
371                 dev_err(&ndev->dev, "failed to register framebuffer\n");
372                 ret = -ENODEV;
373                 goto err_iounmap_fb;
374         }
375
376         tegra_fb->info = info;
377
378         dev_info(&ndev->dev, "probed\n");
379
380         return tegra_fb;
381
382 err_iounmap_fb:
383         iounmap(fb_base);
384 err_free:
385         framebuffer_release(info);
386 err:
387         return ERR_PTR(ret);
388 }
389
390 void tegra_fb_unregister(struct tegra_fb_info *fb_info)
391 {
392         struct fb_info *info = fb_info->info;
393
394         unregister_framebuffer(info);
395         iounmap(info->screen_base);
396         framebuffer_release(info);
397 }