2 * Copyright 2012 Red Hat Inc.
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sub license, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
12 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
15 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
16 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
17 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
18 * USE OR OTHER DEALINGS IN THE SOFTWARE.
20 * The above copyright notice and this permission notice (including the
21 * next paragraph) shall be included in all copies or substantial portions
26 * Authors: Dave Airlie <airlied@redhat.com>
28 #include <linux/module.h>
29 #include <linux/kernel.h>
30 #include <linux/errno.h>
31 #include <linux/string.h>
33 #include <linux/tty.h>
34 #include <linux/sysrq.h>
35 #include <linux/delay.h>
36 #include <linux/init.h>
40 #include <drm/drm_crtc.h>
41 #include <drm/drm_fb_helper.h>
42 #include <drm/drm_crtc_helper.h>
45 static void ast_dirty_update(struct ast_fbdev
*afbdev
,
46 int x
, int y
, int width
, int height
)
49 struct drm_gem_object
*obj
;
51 int src_offset
, dst_offset
;
52 int bpp
= afbdev
->afb
.base
.format
->cpp
[0];
55 bool store_for_later
= false;
59 obj
= afbdev
->afb
.obj
;
60 bo
= gem_to_ast_bo(obj
);
63 * try and reserve the BO, if we fail with busy
64 * then the BO is being moved and we should
65 * store up the damage until later.
68 ret
= ast_bo_reserve(bo
, true);
73 store_for_later
= true;
78 spin_lock_irqsave(&afbdev
->dirty_lock
, flags
);
89 if (store_for_later
) {
94 spin_unlock_irqrestore(&afbdev
->dirty_lock
, flags
);
98 afbdev
->x1
= afbdev
->y1
= INT_MAX
;
99 afbdev
->x2
= afbdev
->y2
= 0;
100 spin_unlock_irqrestore(&afbdev
->dirty_lock
, flags
);
102 if (!bo
->kmap
.virtual) {
103 ret
= ttm_bo_kmap(&bo
->bo
, 0, bo
->bo
.num_pages
, &bo
->kmap
);
105 DRM_ERROR("failed to kmap fb updates\n");
106 ast_bo_unreserve(bo
);
111 for (i
= y
; i
<= y2
; i
++) {
112 /* assume equal stride for now */
113 src_offset
= dst_offset
= i
* afbdev
->afb
.base
.pitches
[0] + (x
* bpp
);
114 memcpy_toio(bo
->kmap
.virtual + src_offset
, afbdev
->sysram
+ src_offset
, (x2
- x
+ 1) * bpp
);
118 ttm_bo_kunmap(&bo
->kmap
);
120 ast_bo_unreserve(bo
);
123 static void ast_fillrect(struct fb_info
*info
,
124 const struct fb_fillrect
*rect
)
126 struct ast_fbdev
*afbdev
= info
->par
;
127 drm_fb_helper_sys_fillrect(info
, rect
);
128 ast_dirty_update(afbdev
, rect
->dx
, rect
->dy
, rect
->width
,
132 static void ast_copyarea(struct fb_info
*info
,
133 const struct fb_copyarea
*area
)
135 struct ast_fbdev
*afbdev
= info
->par
;
136 drm_fb_helper_sys_copyarea(info
, area
);
137 ast_dirty_update(afbdev
, area
->dx
, area
->dy
, area
->width
,
141 static void ast_imageblit(struct fb_info
*info
,
142 const struct fb_image
*image
)
144 struct ast_fbdev
*afbdev
= info
->par
;
145 drm_fb_helper_sys_imageblit(info
, image
);
146 ast_dirty_update(afbdev
, image
->dx
, image
->dy
, image
->width
,
150 static struct fb_ops astfb_ops
= {
151 .owner
= THIS_MODULE
,
152 .fb_check_var
= drm_fb_helper_check_var
,
153 .fb_set_par
= drm_fb_helper_set_par
,
154 .fb_fillrect
= ast_fillrect
,
155 .fb_copyarea
= ast_copyarea
,
156 .fb_imageblit
= ast_imageblit
,
157 .fb_pan_display
= drm_fb_helper_pan_display
,
158 .fb_blank
= drm_fb_helper_blank
,
159 .fb_setcmap
= drm_fb_helper_setcmap
,
160 .fb_debug_enter
= drm_fb_helper_debug_enter
,
161 .fb_debug_leave
= drm_fb_helper_debug_leave
,
164 static int astfb_create_object(struct ast_fbdev
*afbdev
,
165 const struct drm_mode_fb_cmd2
*mode_cmd
,
166 struct drm_gem_object
**gobj_p
)
168 struct drm_device
*dev
= afbdev
->helper
.dev
;
170 struct drm_gem_object
*gobj
;
173 size
= mode_cmd
->pitches
[0] * mode_cmd
->height
;
174 ret
= ast_gem_create(dev
, size
, true, &gobj
);
182 static int astfb_create(struct drm_fb_helper
*helper
,
183 struct drm_fb_helper_surface_size
*sizes
)
185 struct ast_fbdev
*afbdev
=
186 container_of(helper
, struct ast_fbdev
, helper
);
187 struct drm_device
*dev
= afbdev
->helper
.dev
;
188 struct drm_mode_fb_cmd2 mode_cmd
;
189 struct drm_framebuffer
*fb
;
190 struct fb_info
*info
;
193 struct drm_gem_object
*gobj
= NULL
;
194 struct ast_bo
*bo
= NULL
;
195 mode_cmd
.width
= sizes
->surface_width
;
196 mode_cmd
.height
= sizes
->surface_height
;
197 mode_cmd
.pitches
[0] = mode_cmd
.width
* ((sizes
->surface_bpp
+ 7)/8);
199 mode_cmd
.pixel_format
= drm_mode_legacy_fb_format(sizes
->surface_bpp
,
200 sizes
->surface_depth
);
202 size
= mode_cmd
.pitches
[0] * mode_cmd
.height
;
204 ret
= astfb_create_object(afbdev
, &mode_cmd
, &gobj
);
206 DRM_ERROR("failed to create fbcon backing object %d\n", ret
);
209 bo
= gem_to_ast_bo(gobj
);
211 sysram
= vmalloc(size
);
215 info
= drm_fb_helper_alloc_fbi(helper
);
222 ret
= ast_framebuffer_init(dev
, &afbdev
->afb
, &mode_cmd
, gobj
);
226 afbdev
->sysram
= sysram
;
229 fb
= &afbdev
->afb
.base
;
230 afbdev
->helper
.fb
= fb
;
232 strcpy(info
->fix
.id
, "astdrmfb");
234 info
->fbops
= &astfb_ops
;
236 info
->apertures
->ranges
[0].base
= pci_resource_start(dev
->pdev
, 0);
237 info
->apertures
->ranges
[0].size
= pci_resource_len(dev
->pdev
, 0);
239 drm_fb_helper_fill_fix(info
, fb
->pitches
[0], fb
->format
->depth
);
240 drm_fb_helper_fill_var(info
, &afbdev
->helper
, sizes
->fb_width
, sizes
->fb_height
);
242 info
->screen_base
= sysram
;
243 info
->screen_size
= size
;
245 info
->pixmap
.flags
= FB_PIXMAP_SYSTEM
;
247 DRM_DEBUG_KMS("allocated %dx%d\n",
248 fb
->width
, fb
->height
);
257 static const struct drm_fb_helper_funcs ast_fb_helper_funcs
= {
258 .fb_probe
= astfb_create
,
261 static void ast_fbdev_destroy(struct drm_device
*dev
,
262 struct ast_fbdev
*afbdev
)
264 struct ast_framebuffer
*afb
= &afbdev
->afb
;
266 drm_crtc_force_disable_all(dev
);
267 drm_fb_helper_unregister_fbi(&afbdev
->helper
);
270 drm_gem_object_put_unlocked(afb
->obj
);
273 drm_fb_helper_fini(&afbdev
->helper
);
275 vfree(afbdev
->sysram
);
276 drm_framebuffer_unregister_private(&afb
->base
);
277 drm_framebuffer_cleanup(&afb
->base
);
280 int ast_fbdev_init(struct drm_device
*dev
)
282 struct ast_private
*ast
= dev
->dev_private
;
283 struct ast_fbdev
*afbdev
;
286 afbdev
= kzalloc(sizeof(struct ast_fbdev
), GFP_KERNEL
);
291 spin_lock_init(&afbdev
->dirty_lock
);
293 drm_fb_helper_prepare(dev
, &afbdev
->helper
, &ast_fb_helper_funcs
);
295 ret
= drm_fb_helper_init(dev
, &afbdev
->helper
, 1);
299 ret
= drm_fb_helper_single_add_all_connectors(&afbdev
->helper
);
303 /* disable all the possible outputs/crtcs before entering KMS mode */
304 drm_helper_disable_unused_functions(dev
);
306 ret
= drm_fb_helper_initial_config(&afbdev
->helper
, 32);
313 drm_fb_helper_fini(&afbdev
->helper
);
319 void ast_fbdev_fini(struct drm_device
*dev
)
321 struct ast_private
*ast
= dev
->dev_private
;
326 ast_fbdev_destroy(dev
, ast
->fbdev
);
331 void ast_fbdev_set_suspend(struct drm_device
*dev
, int state
)
333 struct ast_private
*ast
= dev
->dev_private
;
338 drm_fb_helper_set_suspend(&ast
->fbdev
->helper
, state
);
341 void ast_fbdev_set_base(struct ast_private
*ast
, unsigned long gpu_addr
)
343 ast
->fbdev
->helper
.fbdev
->fix
.smem_start
=
344 ast
->fbdev
->helper
.fbdev
->apertures
->ranges
[0].base
+ gpu_addr
;
345 ast
->fbdev
->helper
.fbdev
->fix
.smem_len
= ast
->vram_size
- gpu_addr
;