44da2d71f9e1472f1136b50548d259ff2ed8ddd2
[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/platform_device.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 platform_device  *pdev;
37         struct fb_info          *info;
38
39         struct resource         *fb_mem;
40
41         int                     xres;
42         int                     yres;
43
44         atomic_t                in_use;
45 };
46
47 /* palette array used by the fbcon */
48 static u32 pseudo_palette[16];
49
50 static int tegra_fb_open(struct fb_info *info, int user)
51 {
52         struct tegra_fb_info *tegra_fb = info->par;
53
54         if (atomic_xchg(&tegra_fb->in_use, 1))
55                 return -EBUSY;
56
57         return 0;
58 }
59
60 static int tegra_fb_release(struct fb_info *info, int user)
61 {
62         struct tegra_fb_info *tegra_fb = info->par;
63
64         WARN_ON(!atomic_xchg(&tegra_fb->in_use, 0));
65
66         return 0;
67 }
68
69 static int tegra_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
70 {
71         if ((var->xres != info->var.xres) ||
72             (var->yres != info->var.yres) ||
73             (var->xres_virtual != info->var.xres_virtual) ||
74             (var->yres_virtual != info->var.yres_virtual) ||
75             (var->grayscale != info->var.grayscale))
76                 return -EINVAL;
77         return 0;
78 }
79
80 static int tegra_fb_set_par(struct fb_info *info)
81 {
82         struct tegra_fb_info *tegra_fb = info->par;
83         struct fb_var_screeninfo *var = &info->var;
84
85         /* we only support RGB ordering for now */
86         switch (var->bits_per_pixel) {
87         case 32:
88         case 24:
89                 var->red.offset = 0;
90                 var->red.length = 8;
91                 var->green.offset = 8;
92                 var->green.length = 8;
93                 var->blue.offset = 16;
94                 var->blue.length = 8;
95                 tegra_fb->win->fmt = TEGRA_WIN_FMT_R8G8B8A8;
96                 break;
97         case 16:
98                 var->red.offset = 11;
99                 var->red.length = 5;
100                 var->green.offset = 5;
101                 var->green.length = 6;
102                 var->blue.offset = 0;
103                 var->blue.length = 5;
104                 tegra_fb->win->fmt = TEGRA_WIN_FMT_B5G6R5;
105                 break;
106         default:
107                 return -EINVAL;
108         }
109         info->fix.line_length = var->xres * var->bits_per_pixel / 8;
110
111         tegra_dc_update_windows(&tegra_fb->win, 1);
112
113         return 0;
114 }
115
116 static int tegra_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
117         unsigned blue, unsigned transp, struct fb_info *info)
118 {
119         struct fb_var_screeninfo *var = &info->var;
120
121         if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
122             info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
123                 u32 v;
124
125                 if (regno >= 16)
126                         return -EINVAL;
127
128                 v = (red << var->red.offset) |
129                         (green << var->green.offset) |
130                         (blue << var->blue.offset);
131
132                 ((u32 *)info->pseudo_palette)[regno] = v;
133         }
134
135         return 0;
136 }
137
138 static int tegra_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
139 {
140         struct tegra_fb_info *tegra_fb = info->par;
141         char __iomem *flush_start;
142         char __iomem *flush_end;
143         u32 addr;
144
145         flush_start = info->screen_base + (var->yoffset * info->fix.line_length);
146         flush_end = flush_start + (var->yres * info->fix.line_length);
147         /* do we need to dma flush here? */
148
149         info->var.xoffset = var->xoffset;
150         info->var.yoffset = var->yoffset;
151
152         addr = info->fix.smem_start + (var->yoffset * info->fix.line_length) +
153                 (var->xoffset * (var->bits_per_pixel/8));
154
155         tegra_fb->win->phys_addr = addr;
156         /* TODO: update virt_addr */
157
158         tegra_dc_update_windows(&tegra_fb->win, 1);
159         tegra_dc_sync_windows(&tegra_fb->win, 1);
160
161         return 0;
162 }
163
164 static void tegra_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
165 {
166         cfb_fillrect(info, rect);
167 }
168
169 static void tegra_fb_copyarea(struct fb_info *info, const struct fb_copyarea *region)
170 {
171         cfb_copyarea(info, region);
172 }
173
174 static void tegra_fb_imageblit(struct fb_info *info, const struct fb_image *image)
175 {
176         cfb_imageblit(info, image);
177 }
178
179 /* TODO: implement ALLOC, FREE, BLANK ioctls */
180 /* TODO: implement private window ioctls to set overlay x,y */
181
182 static struct fb_ops tegra_fb_ops = {
183         .owner = THIS_MODULE,
184         .fb_open = tegra_fb_open,
185         .fb_release = tegra_fb_release,
186         .fb_check_var = tegra_fb_check_var,
187         .fb_set_par = tegra_fb_set_par,
188         .fb_setcolreg = tegra_fb_setcolreg,
189         .fb_pan_display = tegra_fb_pan_display,
190         .fb_fillrect = tegra_fb_fillrect,
191         .fb_copyarea = tegra_fb_copyarea,
192         .fb_imageblit = tegra_fb_imageblit,
193 };
194
195 struct tegra_fb_info *tegra_fb_register(struct platform_device *pdev, struct tegra_dc *dc,
196                                         struct tegra_fb_data *fb_data, struct resource *fb_mem)
197 {
198         struct tegra_dc_win *win;
199         struct fb_info *info;
200         struct tegra_fb_info *tegra_fb;
201         void __iomem *fb_base;
202         unsigned long fb_size;
203         unsigned long fb_phys;
204         int ret = 0;
205
206         win = tegra_dc_get_window(dc, fb_data->win);
207         if (!win) {
208                 dev_err(&pdev->dev, "dc does not have a window at index %d\n", fb_data->win);
209                 return ERR_PTR(-ENOENT);
210         }
211
212         info = framebuffer_alloc(sizeof(struct tegra_fb_info), &pdev->dev);
213         if (!info) {
214                 ret = -ENOMEM;
215                 goto err;
216         }
217
218         fb_size = resource_size(fb_mem);
219         fb_phys = fb_mem->start;
220         fb_base = ioremap_nocache(fb_phys, fb_size);
221         if (!fb_base) {
222                 dev_err(&pdev->dev, "fb can't be mapped\n");
223                 ret = -EBUSY;
224                 goto err_free;
225         }
226
227         tegra_fb = info->par;
228         tegra_fb->win = win;
229         tegra_fb->pdev = pdev;
230         tegra_fb->fb_mem = fb_mem;
231         tegra_fb->xres = fb_data->xres;
232         tegra_fb->yres = fb_data->yres;
233         atomic_set(&tegra_fb->in_use, 0);
234
235         info->fbops = &tegra_fb_ops;
236         info->pseudo_palette = pseudo_palette;
237         info->screen_base = fb_base;
238         info->screen_size = fb_size;
239
240         strlcpy(info->fix.id, "tegra_fb", sizeof(info->fix.id));
241         info->fix.type          = FB_TYPE_PACKED_PIXELS;
242         info->fix.visual        = FB_VISUAL_TRUECOLOR;
243         info->fix.xpanstep      = 1;
244         info->fix.ypanstep      = 1;
245         info->fix.accel         = FB_ACCEL_NONE;
246         info->fix.smem_start    = fb_phys;
247         info->fix.smem_len      = fb_size;
248
249         info->var.xres                  = fb_data->xres;
250         info->var.yres                  = fb_data->yres;
251         info->var.xres_virtual          = fb_data->xres;
252         info->var.yres_virtual          = fb_data->yres*2;
253         info->var.bits_per_pixel        = fb_data->bits_per_pixel;
254         info->var.activate              = FB_ACTIVATE_VBL;
255         /* TODO: fill in the following by querying the DC */
256         info->var.height                = -1;
257         info->var.width                 = -1;
258         info->var.pixclock              = 24500;
259         info->var.left_margin           = 0;
260         info->var.right_margin          = 0;
261         info->var.upper_margin          = 0;
262         info->var.lower_margin          = 0;
263         info->var.hsync_len             = 0;
264         info->var.vsync_len             = 0;
265         info->var.vmode                 = FB_VMODE_NONINTERLACED;
266
267         win->x = 0;
268         win->y = 0;
269         win->w = fb_data->xres;
270         win->h = fb_data->yres;
271         /* TODO: set to output res dc */
272         win->out_w = fb_data->xres;
273         win->out_h = fb_data->yres;
274         win->phys_addr = fb_phys;
275         win->virt_addr = fb_base;
276         win->flags = TEGRA_WIN_FLAG_ENABLED | TEGRA_WIN_FLAG_COLOR_EXPAND;
277
278         tegra_fb_set_par(info);
279
280         if (register_framebuffer(info)) {
281                 dev_err(&pdev->dev, "failed to register framebuffer\n");
282                 ret = -ENODEV;
283                 goto err_iounmap_fb;
284         }
285
286         tegra_fb->info = info;
287
288         dev_info(&pdev->dev, "probed\n");
289
290         return tegra_fb;
291
292 err_iounmap_fb:
293         iounmap(fb_base);
294 err_free:
295         framebuffer_release(info);
296 err:
297         return ERR_PTR(ret);
298 }
299
300 void tegra_fb_unregister(struct tegra_fb_info *fb_info)
301 {
302         struct fb_info *info = fb_info->info;
303
304         unregister_framebuffer(info);
305         iounmap(info->screen_base);
306         framebuffer_release(info);
307 }