aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/omapdrm/omap_fbdev.c
diff options
context:
space:
mode:
authorRob Clark <robdclark@gmail.com>2013-02-11 12:43:09 -0500
committerRob Clark <robdclark@gmail.com>2013-02-16 17:38:06 -0500
commit8bb0daffb0b8e45188066255b4203446eae181f1 (patch)
treec1a324b863df57becdfab54c9325231bbb853b56 /drivers/gpu/drm/omapdrm/omap_fbdev.c
parenta4462f246c8821f625f45bce52c7ca7e0207dffe (diff)
drm/omap: move out of staging
Now that the omapdss interface has been reworked so that omapdrm can use dispc directly, we have been able to fix the remaining functional kms issues with omapdrm. And in the mean time the PM sequencing and many other of that open issues have been solved. So I think it makes sense to finally move omapdrm out of staging. Signed-off-by: Rob Clark <robdclark@gmail.com>
Diffstat (limited to 'drivers/gpu/drm/omapdrm/omap_fbdev.c')
-rw-r--r--drivers/gpu/drm/omapdrm/omap_fbdev.c402
1 files changed, 402 insertions, 0 deletions
diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
new file mode 100644
index 000000000000..11eed30efe06
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
@@ -0,0 +1,402 @@
1/*
2 * drivers/gpu/drm/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
25MODULE_PARM_DESC(ywrap, "Enable ywrap scrolling (omap44xx and later, default 'y')");
26static bool ywrap_enabled = true;
27module_param_named(ywrap, ywrap_enabled, bool, 0644);
28
29/*
30 * fbdev funcs, to implement legacy fbdev interface on top of drm driver
31 */
32
33#define to_omap_fbdev(x) container_of(x, struct omap_fbdev, base)
34
35struct omap_fbdev {
36 struct drm_fb_helper base;
37 struct drm_framebuffer *fb;
38 struct drm_gem_object *bo;
39 bool ywrap_enabled;
40
41 /* for deferred dmm roll when getting called in atomic ctx */
42 struct work_struct work;
43};
44
45static void omap_fbdev_flush(struct fb_info *fbi, int x, int y, int w, int h);
46static struct drm_fb_helper *get_fb(struct fb_info *fbi);
47
48static ssize_t omap_fbdev_write(struct fb_info *fbi, const char __user *buf,
49 size_t count, loff_t *ppos)
50{
51 ssize_t res;
52
53 res = fb_sys_write(fbi, buf, count, ppos);
54 omap_fbdev_flush(fbi, 0, 0, fbi->var.xres, fbi->var.yres);
55
56 return res;
57}
58
59static void omap_fbdev_fillrect(struct fb_info *fbi,
60 const struct fb_fillrect *rect)
61{
62 sys_fillrect(fbi, rect);
63 omap_fbdev_flush(fbi, rect->dx, rect->dy, rect->width, rect->height);
64}
65
66static void omap_fbdev_copyarea(struct fb_info *fbi,
67 const struct fb_copyarea *area)
68{
69 sys_copyarea(fbi, area);
70 omap_fbdev_flush(fbi, area->dx, area->dy, area->width, area->height);
71}
72
73static void omap_fbdev_imageblit(struct fb_info *fbi,
74 const struct fb_image *image)
75{
76 sys_imageblit(fbi, image);
77 omap_fbdev_flush(fbi, image->dx, image->dy,
78 image->width, image->height);
79}
80
81static void pan_worker(struct work_struct *work)
82{
83 struct omap_fbdev *fbdev = container_of(work, struct omap_fbdev, work);
84 struct fb_info *fbi = fbdev->base.fbdev;
85 int npages;
86
87 /* DMM roll shifts in 4K pages: */
88 npages = fbi->fix.line_length >> PAGE_SHIFT;
89 omap_gem_roll(fbdev->bo, fbi->var.yoffset * npages);
90}
91
92static int omap_fbdev_pan_display(struct fb_var_screeninfo *var,
93 struct fb_info *fbi)
94{
95 struct drm_fb_helper *helper = get_fb(fbi);
96 struct omap_fbdev *fbdev = to_omap_fbdev(helper);
97
98 if (!helper)
99 goto fallback;
100
101 if (!fbdev->ywrap_enabled)
102 goto fallback;
103
104 if (drm_can_sleep()) {
105 pan_worker(&fbdev->work);
106 } else {
107 struct omap_drm_private *priv = helper->dev->dev_private;
108 queue_work(priv->wq, &fbdev->work);
109 }
110
111 return 0;
112
113fallback:
114 return drm_fb_helper_pan_display(var, fbi);
115}
116
117static struct fb_ops omap_fb_ops = {
118 .owner = THIS_MODULE,
119
120 /* Note: to properly handle manual update displays, we wrap the
121 * basic fbdev ops which write to the framebuffer
122 */
123 .fb_read = fb_sys_read,
124 .fb_write = omap_fbdev_write,
125 .fb_fillrect = omap_fbdev_fillrect,
126 .fb_copyarea = omap_fbdev_copyarea,
127 .fb_imageblit = omap_fbdev_imageblit,
128
129 .fb_check_var = drm_fb_helper_check_var,
130 .fb_set_par = drm_fb_helper_set_par,
131 .fb_pan_display = omap_fbdev_pan_display,
132 .fb_blank = drm_fb_helper_blank,
133 .fb_setcmap = drm_fb_helper_setcmap,
134
135 .fb_debug_enter = drm_fb_helper_debug_enter,
136 .fb_debug_leave = drm_fb_helper_debug_leave,
137};
138
139static int omap_fbdev_create(struct drm_fb_helper *helper,
140 struct drm_fb_helper_surface_size *sizes)
141{
142 struct omap_fbdev *fbdev = to_omap_fbdev(helper);
143 struct drm_device *dev = helper->dev;
144 struct omap_drm_private *priv = dev->dev_private;
145 struct drm_framebuffer *fb = NULL;
146 union omap_gem_size gsize;
147 struct fb_info *fbi = NULL;
148 struct drm_mode_fb_cmd2 mode_cmd = {0};
149 dma_addr_t paddr;
150 int ret;
151
152 /* only doing ARGB32 since this is what is needed to alpha-blend
153 * with video overlays:
154 */
155 sizes->surface_bpp = 32;
156 sizes->surface_depth = 32;
157
158 DBG("create fbdev: %dx%d@%d (%dx%d)", sizes->surface_width,
159 sizes->surface_height, sizes->surface_bpp,
160 sizes->fb_width, sizes->fb_height);
161
162 mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
163 sizes->surface_depth);
164
165 mode_cmd.width = sizes->surface_width;
166 mode_cmd.height = sizes->surface_height;
167
168 mode_cmd.pitches[0] = align_pitch(
169 mode_cmd.width * ((sizes->surface_bpp + 7) / 8),
170 mode_cmd.width, sizes->surface_bpp);
171
172 fbdev->ywrap_enabled = priv->has_dmm && ywrap_enabled;
173 if (fbdev->ywrap_enabled) {
174 /* need to align pitch to page size if using DMM scrolling */
175 mode_cmd.pitches[0] = ALIGN(mode_cmd.pitches[0], PAGE_SIZE);
176 }
177
178 /* allocate backing bo */
179 gsize = (union omap_gem_size){
180 .bytes = PAGE_ALIGN(mode_cmd.pitches[0] * mode_cmd.height),
181 };
182 DBG("allocating %d bytes for fb %d", gsize.bytes, dev->primary->index);
183 fbdev->bo = omap_gem_new(dev, gsize, OMAP_BO_SCANOUT | OMAP_BO_WC);
184 if (!fbdev->bo) {
185 dev_err(dev->dev, "failed to allocate buffer object\n");
186 ret = -ENOMEM;
187 goto fail;
188 }
189
190 fb = omap_framebuffer_init(dev, &mode_cmd, &fbdev->bo);
191 if (IS_ERR(fb)) {
192 dev_err(dev->dev, "failed to allocate fb\n");
193 /* note: if fb creation failed, we can't rely on fb destroy
194 * to unref the bo:
195 */
196 drm_gem_object_unreference(fbdev->bo);
197 ret = PTR_ERR(fb);
198 goto fail;
199 }
200
201 /* note: this keeps the bo pinned.. which is perhaps not ideal,
202 * but is needed as long as we use fb_mmap() to mmap to userspace
203 * (since this happens using fix.smem_start). Possibly we could
204 * implement our own mmap using GEM mmap support to avoid this
205 * (non-tiled buffer doesn't need to be pinned for fbcon to write
206 * to it). Then we just need to be sure that we are able to re-
207 * pin it in case of an opps.
208 */
209 ret = omap_gem_get_paddr(fbdev->bo, &paddr, true);
210 if (ret) {
211 dev_err(dev->dev,
212 "could not map (paddr)! Skipping framebuffer alloc\n");
213 ret = -ENOMEM;
214 goto fail;
215 }
216
217 mutex_lock(&dev->struct_mutex);
218
219 fbi = framebuffer_alloc(0, dev->dev);
220 if (!fbi) {
221 dev_err(dev->dev, "failed to allocate fb info\n");
222 ret = -ENOMEM;
223 goto fail_unlock;
224 }
225
226 DBG("fbi=%p, dev=%p", fbi, dev);
227
228 fbdev->fb = fb;
229 helper->fb = fb;
230 helper->fbdev = fbi;
231
232 fbi->par = helper;
233 fbi->flags = FBINFO_DEFAULT;
234 fbi->fbops = &omap_fb_ops;
235
236 strcpy(fbi->fix.id, MODULE_NAME);
237
238 ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
239 if (ret) {
240 ret = -ENOMEM;
241 goto fail_unlock;
242 }
243
244 drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
245 drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height);
246
247 dev->mode_config.fb_base = paddr;
248
249 fbi->screen_base = omap_gem_vaddr(fbdev->bo);
250 fbi->screen_size = fbdev->bo->size;
251 fbi->fix.smem_start = paddr;
252 fbi->fix.smem_len = fbdev->bo->size;
253
254 /* if we have DMM, then we can use it for scrolling by just
255 * shuffling pages around in DMM rather than doing sw blit.
256 */
257 if (fbdev->ywrap_enabled) {
258 DRM_INFO("Enabling DMM ywrap scrolling\n");
259 fbi->flags |= FBINFO_HWACCEL_YWRAP | FBINFO_READS_FAST;
260 fbi->fix.ywrapstep = 1;
261 }
262
263
264 DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres);
265 DBG("allocated %dx%d fb", fbdev->fb->width, fbdev->fb->height);
266
267 mutex_unlock(&dev->struct_mutex);
268
269 return 0;
270
271fail_unlock:
272 mutex_unlock(&dev->struct_mutex);
273fail:
274
275 if (ret) {
276 if (fbi)
277 framebuffer_release(fbi);
278 if (fb) {
279 drm_framebuffer_unregister_private(fb);
280 drm_framebuffer_remove(fb);
281 }
282 }
283
284 return ret;
285}
286
287static void omap_crtc_fb_gamma_set(struct drm_crtc *crtc,
288 u16 red, u16 green, u16 blue, int regno)
289{
290 DBG("fbdev: set gamma");
291}
292
293static void omap_crtc_fb_gamma_get(struct drm_crtc *crtc,
294 u16 *red, u16 *green, u16 *blue, int regno)
295{
296 DBG("fbdev: get gamma");
297}
298
299static struct drm_fb_helper_funcs omap_fb_helper_funcs = {
300 .gamma_set = omap_crtc_fb_gamma_set,
301 .gamma_get = omap_crtc_fb_gamma_get,
302 .fb_probe = omap_fbdev_create,
303};
304
305static struct drm_fb_helper *get_fb(struct fb_info *fbi)
306{
307 if (!fbi || strcmp(fbi->fix.id, MODULE_NAME)) {
308 /* these are not the fb's you're looking for */
309 return NULL;
310 }
311 return fbi->par;
312}
313
314/* flush an area of the framebuffer (in case of manual update display that
315 * is not automatically flushed)
316 */
317static void omap_fbdev_flush(struct fb_info *fbi, int x, int y, int w, int h)
318{
319 struct drm_fb_helper *helper = get_fb(fbi);
320
321 if (!helper)
322 return;
323
324 VERB("flush fbdev: %d,%d %dx%d, fbi=%p", x, y, w, h, fbi);
325
326 omap_framebuffer_flush(helper->fb, x, y, w, h);
327}
328
329/* initialize fbdev helper */
330struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev)
331{
332 struct omap_drm_private *priv = dev->dev_private;
333 struct omap_fbdev *fbdev = NULL;
334 struct drm_fb_helper *helper;
335 int ret = 0;
336
337 fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
338 if (!fbdev) {
339 dev_err(dev->dev, "could not allocate fbdev\n");
340 goto fail;
341 }
342
343 INIT_WORK(&fbdev->work, pan_worker);
344
345 helper = &fbdev->base;
346
347 helper->funcs = &omap_fb_helper_funcs;
348
349 ret = drm_fb_helper_init(dev, helper,
350 priv->num_crtcs, priv->num_connectors);
351 if (ret) {
352 dev_err(dev->dev, "could not init fbdev: ret=%d\n", ret);
353 goto fail;
354 }
355
356 drm_fb_helper_single_add_all_connectors(helper);
357
358 /* disable all the possible outputs/crtcs before entering KMS mode */
359 drm_helper_disable_unused_functions(dev);
360
361 drm_fb_helper_initial_config(helper, 32);
362
363 priv->fbdev = helper;
364
365 return helper;
366
367fail:
368 kfree(fbdev);
369 return NULL;
370}
371
372void omap_fbdev_free(struct drm_device *dev)
373{
374 struct omap_drm_private *priv = dev->dev_private;
375 struct drm_fb_helper *helper = priv->fbdev;
376 struct omap_fbdev *fbdev;
377 struct fb_info *fbi;
378
379 DBG();
380
381 fbi = helper->fbdev;
382
383 /* only cleanup framebuffer if it is present */
384 if (fbi) {
385 unregister_framebuffer(fbi);
386 framebuffer_release(fbi);
387 }
388
389 drm_fb_helper_fini(helper);
390
391 fbdev = to_omap_fbdev(priv->fbdev);
392
393 /* this will free the backing object */
394 if (fbdev->fb) {
395 drm_framebuffer_unregister_private(fbdev->fb);
396 drm_framebuffer_remove(fbdev->fb);
397 }
398
399 kfree(fbdev);
400
401 priv->fbdev = NULL;
402}