Merge branch 'stable-3.2' into pandora-3.2
[pandora-kernel.git] / drivers / staging / omapdrm / omap_fbdev.c
1 /*
2  * drivers/staging/omapdrm/omap_fbdev.c
3  *
4  * Copyright (C) 2011 Texas Instruments
5  * Author: Rob Clark <rob@ti.com>
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License version 2 as published by
9  * the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "omap_drv.h"
21
22 #include "drm_crtc.h"
23 #include "drm_fb_helper.h"
24
25 /*
26  * fbdev funcs, to implement legacy fbdev interface on top of drm driver
27  */
28
29 #define to_omap_fbdev(x) container_of(x, struct omap_fbdev, base)
30
31 struct omap_fbdev {
32         struct drm_fb_helper base;
33         struct drm_framebuffer *fb;
34 };
35
36 static void omap_fbdev_flush(struct fb_info *fbi, int x, int y, int w, int h);
37
38 static ssize_t omap_fbdev_write(struct fb_info *fbi, const char __user *buf,
39                 size_t count, loff_t *ppos)
40 {
41         ssize_t res;
42
43         res = fb_sys_write(fbi, buf, count, ppos);
44         omap_fbdev_flush(fbi, 0, 0, fbi->var.xres, fbi->var.yres);
45
46         return res;
47 }
48
49 static void omap_fbdev_fillrect(struct fb_info *fbi,
50                 const struct fb_fillrect *rect)
51 {
52         sys_fillrect(fbi, rect);
53         omap_fbdev_flush(fbi, rect->dx, rect->dy, rect->width, rect->height);
54 }
55
56 static void omap_fbdev_copyarea(struct fb_info *fbi,
57                 const struct fb_copyarea *area)
58 {
59         sys_copyarea(fbi, area);
60         omap_fbdev_flush(fbi, area->dx, area->dy, area->width, area->height);
61 }
62
63 static void omap_fbdev_imageblit(struct fb_info *fbi,
64                 const struct fb_image *image)
65 {
66         sys_imageblit(fbi, image);
67         omap_fbdev_flush(fbi, image->dx, image->dy,
68                                 image->width, image->height);
69 }
70
71 static struct fb_ops omap_fb_ops = {
72         .owner = THIS_MODULE,
73
74         /* Note: to properly handle manual update displays, we wrap the
75          * basic fbdev ops which write to the framebuffer
76          */
77         .fb_read = fb_sys_read,
78         .fb_write = omap_fbdev_write,
79         .fb_fillrect = omap_fbdev_fillrect,
80         .fb_copyarea = omap_fbdev_copyarea,
81         .fb_imageblit = omap_fbdev_imageblit,
82
83         .fb_check_var = drm_fb_helper_check_var,
84         .fb_set_par = drm_fb_helper_set_par,
85         .fb_pan_display = drm_fb_helper_pan_display,
86         .fb_blank = drm_fb_helper_blank,
87         .fb_setcmap = drm_fb_helper_setcmap,
88
89         .fb_debug_enter = drm_fb_helper_debug_enter,
90         .fb_debug_leave = drm_fb_helper_debug_leave,
91 };
92
93 static int omap_fbdev_create(struct drm_fb_helper *helper,
94                 struct drm_fb_helper_surface_size *sizes)
95 {
96         struct omap_fbdev *fbdev = to_omap_fbdev(helper);
97         struct drm_device *dev = helper->dev;
98         struct drm_framebuffer *fb = NULL;
99         struct fb_info *fbi = NULL;
100         struct drm_mode_fb_cmd mode_cmd = {0};
101         dma_addr_t paddr;
102         void __iomem *vaddr;
103         int size, screen_width;
104         int ret;
105
106         /* only doing ARGB32 since this is what is needed to alpha-blend
107          * with video overlays:
108          */
109         sizes->surface_bpp = 32;
110         sizes->surface_depth = 32;
111
112         DBG("create fbdev: %dx%d@%d", sizes->surface_width,
113                         sizes->surface_height, sizes->surface_bpp);
114
115         mode_cmd.width = sizes->surface_width;
116         mode_cmd.height = sizes->surface_height;
117
118         mode_cmd.bpp = sizes->surface_bpp;
119         mode_cmd.depth = sizes->surface_depth;
120
121         fb = omap_framebuffer_init(dev, &mode_cmd, NULL);
122         if (!fb) {
123                 dev_err(dev->dev, "failed to allocate fb\n");
124                 ret = -ENOMEM;
125                 goto fail;
126         }
127
128         mutex_lock(&dev->struct_mutex);
129
130         fbi = framebuffer_alloc(0, dev->dev);
131         if (!fbi) {
132                 dev_err(dev->dev, "failed to allocate fb info\n");
133                 ret = -ENOMEM;
134                 goto fail_unlock;
135         }
136
137         DBG("fbi=%p, dev=%p", fbi, dev);
138
139         fbdev->fb = fb;
140         helper->fb = fb;
141         helper->fbdev = fbi;
142
143         fbi->par = helper;
144         fbi->flags = FBINFO_DEFAULT;
145         fbi->fbops = &omap_fb_ops;
146
147         strcpy(fbi->fix.id, MODULE_NAME);
148
149         ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
150         if (ret) {
151                 ret = -ENOMEM;
152                 goto fail_unlock;
153         }
154
155         drm_fb_helper_fill_fix(fbi, fb->pitch, fb->depth);
156         drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
157
158         size = omap_framebuffer_get_buffer(fb, 0, 0,
159                         &vaddr, &paddr, &screen_width);
160
161         dev->mode_config.fb_base = paddr;
162
163         fbi->screen_base = vaddr;
164         fbi->screen_size = size;
165         fbi->fix.smem_start = paddr;
166         fbi->fix.smem_len = size;
167
168         DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres);
169         DBG("allocated %dx%d fb", fbdev->fb->width, fbdev->fb->height);
170
171         mutex_unlock(&dev->struct_mutex);
172
173         return 0;
174
175 fail_unlock:
176         mutex_unlock(&dev->struct_mutex);
177 fail:
178
179         if (ret) {
180                 if (fbi)
181                         framebuffer_release(fbi);
182                 if (fb)
183                         fb->funcs->destroy(fb);
184         }
185
186         return ret;
187 }
188
189 static void omap_crtc_fb_gamma_set(struct drm_crtc *crtc,
190                 u16 red, u16 green, u16 blue, int regno)
191 {
192         DBG("fbdev: set gamma");
193 }
194
195 static void omap_crtc_fb_gamma_get(struct drm_crtc *crtc,
196                 u16 *red, u16 *green, u16 *blue, int regno)
197 {
198         DBG("fbdev: get gamma");
199 }
200
201 static int omap_fbdev_probe(struct drm_fb_helper *helper,
202                 struct drm_fb_helper_surface_size *sizes)
203 {
204         int new_fb = 0;
205         int ret;
206
207         if (!helper->fb) {
208                 ret = omap_fbdev_create(helper, sizes);
209                 if (ret)
210                         return ret;
211                 new_fb = 1;
212         }
213         return new_fb;
214 }
215
216 static struct drm_fb_helper_funcs omap_fb_helper_funcs = {
217         .gamma_set = omap_crtc_fb_gamma_set,
218         .gamma_get = omap_crtc_fb_gamma_get,
219         .fb_probe = omap_fbdev_probe,
220 };
221
222 static struct drm_fb_helper *get_fb(struct fb_info *fbi)
223 {
224         if (!fbi || strcmp(fbi->fix.id, MODULE_NAME)) {
225                 /* these are not the fb's you're looking for */
226                 return NULL;
227         }
228         return fbi->par;
229 }
230
231 /* flush an area of the framebuffer (in case of manual update display that
232  * is not automatically flushed)
233  */
234 static void omap_fbdev_flush(struct fb_info *fbi, int x, int y, int w, int h)
235 {
236         struct drm_fb_helper *helper = get_fb(fbi);
237
238         if (!helper)
239                 return;
240
241         VERB("flush fbdev: %d,%d %dx%d, fbi=%p", x, y, w, h, fbi);
242
243         omap_framebuffer_flush(helper->fb, x, y, w, h);
244 }
245
246 /* initialize fbdev helper */
247 struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev)
248 {
249         struct omap_drm_private *priv = dev->dev_private;
250         struct omap_fbdev *fbdev = NULL;
251         struct drm_fb_helper *helper;
252         int ret = 0;
253
254         fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
255         if (!fbdev) {
256                 dev_err(dev->dev, "could not allocate fbdev\n");
257                 goto fail;
258         }
259
260         helper = &fbdev->base;
261
262         helper->funcs = &omap_fb_helper_funcs;
263
264         ret = drm_fb_helper_init(dev, helper,
265                         priv->num_crtcs, priv->num_connectors);
266         if (ret) {
267                 dev_err(dev->dev, "could not init fbdev: ret=%d\n", ret);
268                 goto fail;
269         }
270
271         drm_fb_helper_single_add_all_connectors(helper);
272         drm_fb_helper_initial_config(helper, 32);
273
274         priv->fbdev = helper;
275
276         return helper;
277
278 fail:
279         kfree(fbdev);
280         return NULL;
281 }
282
283 void omap_fbdev_free(struct drm_device *dev)
284 {
285         struct omap_drm_private *priv = dev->dev_private;
286         struct drm_fb_helper *helper = priv->fbdev;
287         struct omap_fbdev *fbdev;
288         struct fb_info *fbi;
289
290         DBG();
291
292         fbi = helper->fbdev;
293
294         unregister_framebuffer(fbi);
295         framebuffer_release(fbi);
296
297         drm_fb_helper_fini(helper);
298
299         fbdev = to_omap_fbdev(priv->fbdev);
300
301         kfree(fbdev);
302
303         priv->fbdev = NULL;
304 }