]>
Commit | Line | Data |
---|---|---|
d1667b86 RZ |
1 | /* Hisilicon Hibmc SoC drm driver |
2 | * | |
3 | * Based on the bochs drm driver. | |
4 | * | |
5 | * Copyright (c) 2016 Huawei Limited. | |
6 | * | |
7 | * Author: | |
8 | * Rongrong Zou <zourongrong@huawei.com> | |
9 | * Rongrong Zou <zourongrong@gmail.com> | |
10 | * Jianhua Li <lijianhua@huawei.com> | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or modify | |
13 | * it under the terms of the GNU General Public License as published by | |
14 | * the Free Software Foundation; either version 2 of the License, or | |
15 | * (at your option) any later version. | |
16 | * | |
17 | */ | |
18 | ||
19 | #include <drm/drm_crtc.h> | |
20 | #include <drm/drm_crtc_helper.h> | |
21 | #include <drm/drm_fb_helper.h> | |
22 | ||
23 | #include "hibmc_drm_drv.h" | |
24 | ||
25 | static int hibmcfb_create_object( | |
26 | struct hibmc_drm_private *priv, | |
27 | const struct drm_mode_fb_cmd2 *mode_cmd, | |
28 | struct drm_gem_object **gobj_p) | |
29 | { | |
30 | struct drm_gem_object *gobj; | |
31 | struct drm_device *dev = priv->dev; | |
32 | u32 size; | |
33 | int ret = 0; | |
34 | ||
35 | size = mode_cmd->pitches[0] * mode_cmd->height; | |
36 | ret = hibmc_gem_create(dev, size, true, &gobj); | |
37 | if (ret) | |
38 | return ret; | |
39 | ||
40 | *gobj_p = gobj; | |
41 | return ret; | |
42 | } | |
43 | ||
44 | static struct fb_ops hibmc_drm_fb_ops = { | |
45 | .owner = THIS_MODULE, | |
46 | .fb_check_var = drm_fb_helper_check_var, | |
47 | .fb_set_par = drm_fb_helper_set_par, | |
48 | .fb_fillrect = drm_fb_helper_sys_fillrect, | |
49 | .fb_copyarea = drm_fb_helper_sys_copyarea, | |
50 | .fb_imageblit = drm_fb_helper_sys_imageblit, | |
51 | .fb_pan_display = drm_fb_helper_pan_display, | |
52 | .fb_blank = drm_fb_helper_blank, | |
53 | .fb_setcmap = drm_fb_helper_setcmap, | |
54 | }; | |
55 | ||
56 | static int hibmc_drm_fb_create(struct drm_fb_helper *helper, | |
57 | struct drm_fb_helper_surface_size *sizes) | |
58 | { | |
59 | struct hibmc_fbdev *hi_fbdev = | |
60 | container_of(helper, struct hibmc_fbdev, helper); | |
61 | struct hibmc_drm_private *priv = helper->dev->dev_private; | |
62 | struct fb_info *info; | |
63 | struct drm_mode_fb_cmd2 mode_cmd; | |
64 | struct drm_gem_object *gobj = NULL; | |
65 | int ret = 0; | |
66 | int ret1; | |
67 | size_t size; | |
68 | unsigned int bytes_per_pixel; | |
69 | struct hibmc_bo *bo = NULL; | |
70 | ||
71 | DRM_DEBUG_DRIVER("surface width(%d), height(%d) and bpp(%d)\n", | |
72 | sizes->surface_width, sizes->surface_height, | |
73 | sizes->surface_bpp); | |
74 | sizes->surface_depth = 32; | |
75 | ||
76 | bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8); | |
77 | ||
78 | mode_cmd.width = sizes->surface_width; | |
79 | mode_cmd.height = sizes->surface_height; | |
80 | mode_cmd.pitches[0] = mode_cmd.width * bytes_per_pixel; | |
81 | mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, | |
82 | sizes->surface_depth); | |
83 | ||
84 | size = PAGE_ALIGN(mode_cmd.pitches[0] * mode_cmd.height); | |
85 | ||
86 | ret = hibmcfb_create_object(priv, &mode_cmd, &gobj); | |
87 | if (ret) { | |
88 | DRM_ERROR("failed to create fbcon backing object: %d\n", ret); | |
89 | return -ENOMEM; | |
90 | } | |
91 | ||
92 | bo = gem_to_hibmc_bo(gobj); | |
93 | ||
94 | ret = ttm_bo_reserve(&bo->bo, true, false, NULL); | |
95 | if (ret) { | |
96 | DRM_ERROR("failed to reserve ttm_bo: %d\n", ret); | |
97 | goto out_unref_gem; | |
98 | } | |
99 | ||
100 | ret = hibmc_bo_pin(bo, TTM_PL_FLAG_VRAM, NULL); | |
101 | if (ret) { | |
102 | DRM_ERROR("failed to pin fbcon: %d\n", ret); | |
103 | goto out_unreserve_ttm_bo; | |
104 | } | |
105 | ||
106 | ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); | |
107 | if (ret) { | |
108 | DRM_ERROR("failed to kmap fbcon: %d\n", ret); | |
109 | goto out_unpin_bo; | |
110 | } | |
111 | ttm_bo_unreserve(&bo->bo); | |
112 | ||
113 | info = drm_fb_helper_alloc_fbi(helper); | |
114 | if (IS_ERR(info)) { | |
115 | ret = PTR_ERR(info); | |
116 | DRM_ERROR("failed to allocate fbi: %d\n", ret); | |
117 | goto out_release_fbi; | |
118 | } | |
119 | ||
120 | info->par = hi_fbdev; | |
121 | ||
122 | hi_fbdev->fb = hibmc_framebuffer_init(priv->dev, &mode_cmd, gobj); | |
123 | if (IS_ERR(hi_fbdev->fb)) { | |
124 | ret = PTR_ERR(info); | |
125 | DRM_ERROR("failed to initialize framebuffer: %d\n", ret); | |
126 | goto out_release_fbi; | |
127 | } | |
128 | ||
129 | priv->fbdev->size = size; | |
130 | hi_fbdev->helper.fb = &hi_fbdev->fb->fb; | |
131 | ||
132 | strcpy(info->fix.id, "hibmcdrmfb"); | |
133 | ||
134 | info->flags = FBINFO_DEFAULT; | |
135 | info->fbops = &hibmc_drm_fb_ops; | |
136 | ||
137 | drm_fb_helper_fill_fix(info, hi_fbdev->fb->fb.pitches[0], | |
138 | hi_fbdev->fb->fb.depth); | |
139 | drm_fb_helper_fill_var(info, &priv->fbdev->helper, sizes->fb_width, | |
140 | sizes->fb_height); | |
141 | ||
142 | info->screen_base = bo->kmap.virtual; | |
143 | info->screen_size = size; | |
144 | ||
145 | info->fix.smem_start = bo->bo.mem.bus.offset + bo->bo.mem.bus.base; | |
146 | info->fix.smem_len = size; | |
147 | return 0; | |
148 | ||
149 | out_release_fbi: | |
150 | drm_fb_helper_release_fbi(helper); | |
151 | ret1 = ttm_bo_reserve(&bo->bo, true, false, NULL); | |
152 | if (ret1) { | |
153 | DRM_ERROR("failed to rsv ttm_bo when release fbi: %d\n", ret1); | |
154 | goto out_unref_gem; | |
155 | } | |
156 | ttm_bo_kunmap(&bo->kmap); | |
157 | out_unpin_bo: | |
158 | hibmc_bo_unpin(bo); | |
159 | out_unreserve_ttm_bo: | |
160 | ttm_bo_unreserve(&bo->bo); | |
161 | out_unref_gem: | |
162 | drm_gem_object_unreference_unlocked(gobj); | |
163 | ||
164 | return ret; | |
165 | } | |
166 | ||
167 | static void hibmc_fbdev_destroy(struct hibmc_fbdev *fbdev) | |
168 | { | |
169 | struct hibmc_framebuffer *gfb = fbdev->fb; | |
170 | struct drm_fb_helper *fbh = &fbdev->helper; | |
171 | ||
172 | drm_fb_helper_unregister_fbi(fbh); | |
173 | drm_fb_helper_release_fbi(fbh); | |
174 | ||
175 | drm_fb_helper_fini(fbh); | |
176 | ||
177 | if (gfb) | |
178 | drm_framebuffer_unreference(&gfb->fb); | |
179 | } | |
180 | ||
181 | static const struct drm_fb_helper_funcs hibmc_fbdev_helper_funcs = { | |
182 | .fb_probe = hibmc_drm_fb_create, | |
183 | }; | |
184 | ||
185 | int hibmc_fbdev_init(struct hibmc_drm_private *priv) | |
186 | { | |
187 | int ret; | |
188 | struct fb_var_screeninfo *var; | |
189 | struct fb_fix_screeninfo *fix; | |
190 | struct hibmc_fbdev *hifbdev; | |
191 | ||
192 | hifbdev = devm_kzalloc(priv->dev->dev, sizeof(*hifbdev), GFP_KERNEL); | |
193 | if (!hifbdev) { | |
194 | DRM_ERROR("failed to allocate hibmc_fbdev\n"); | |
195 | return -ENOMEM; | |
196 | } | |
197 | ||
198 | priv->fbdev = hifbdev; | |
199 | drm_fb_helper_prepare(priv->dev, &hifbdev->helper, | |
200 | &hibmc_fbdev_helper_funcs); | |
201 | ||
202 | /* Now just one crtc and one channel */ | |
203 | ret = drm_fb_helper_init(priv->dev, | |
204 | &hifbdev->helper, 1, 1); | |
205 | if (ret) { | |
206 | DRM_ERROR("failed to initialize fb helper: %d\n", ret); | |
207 | return ret; | |
208 | } | |
209 | ||
210 | ret = drm_fb_helper_single_add_all_connectors(&hifbdev->helper); | |
211 | if (ret) { | |
212 | DRM_ERROR("failed to add all connectors: %d\n", ret); | |
213 | goto fini; | |
214 | } | |
215 | ||
216 | ret = drm_fb_helper_initial_config(&hifbdev->helper, 16); | |
217 | if (ret) { | |
218 | DRM_ERROR("failed to setup initial conn config: %d\n", ret); | |
219 | goto fini; | |
220 | } | |
221 | ||
222 | var = &hifbdev->helper.fbdev->var; | |
223 | fix = &hifbdev->helper.fbdev->fix; | |
224 | ||
225 | DRM_DEBUG_DRIVER("Member of info->var is :\n" | |
226 | "xres=%d\n" | |
227 | "yres=%d\n" | |
228 | "xres_virtual=%d\n" | |
229 | "yres_virtual=%d\n" | |
230 | "xoffset=%d\n" | |
231 | "yoffset=%d\n" | |
232 | "bits_per_pixel=%d\n" | |
233 | "...\n", var->xres, var->yres, var->xres_virtual, | |
234 | var->yres_virtual, var->xoffset, var->yoffset, | |
235 | var->bits_per_pixel); | |
236 | DRM_DEBUG_DRIVER("Member of info->fix is :\n" | |
237 | "smem_start=%lx\n" | |
238 | "smem_len=%d\n" | |
239 | "type=%d\n" | |
240 | "type_aux=%d\n" | |
241 | "visual=%d\n" | |
242 | "xpanstep=%d\n" | |
243 | "ypanstep=%d\n" | |
244 | "ywrapstep=%d\n" | |
245 | "line_length=%d\n" | |
246 | "accel=%d\n" | |
247 | "capabilities=%d\n" | |
248 | "...\n", fix->smem_start, fix->smem_len, fix->type, | |
249 | fix->type_aux, fix->visual, fix->xpanstep, | |
250 | fix->ypanstep, fix->ywrapstep, fix->line_length, | |
251 | fix->accel, fix->capabilities); | |
252 | ||
253 | return 0; | |
254 | ||
255 | fini: | |
256 | drm_fb_helper_fini(&hifbdev->helper); | |
257 | return ret; | |
258 | } | |
259 | ||
260 | void hibmc_fbdev_fini(struct hibmc_drm_private *priv) | |
261 | { | |
262 | if (!priv->fbdev) | |
263 | return; | |
264 | ||
265 | hibmc_fbdev_destroy(priv->fbdev); | |
266 | priv->fbdev = NULL; | |
267 | } |