1 /*-
2 * Copyright (c) 2016 Michal Meloun
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/bus.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.h>
33
34 #include <machine/bus.h>
35
36 #include <dev/extres/clk/clk.h>
37 #include <dev/drm2/drmP.h>
38 #include <dev/drm2/drm_crtc_helper.h>
39 #include <dev/drm2/drm_fb_helper.h>
40
41 #include <arm/nvidia/drm2/tegra_drm.h>
42
43 static void
fb_destroy(struct drm_framebuffer * drm_fb)44 fb_destroy(struct drm_framebuffer *drm_fb)
45 {
46 struct tegra_fb *fb;
47 struct tegra_bo *bo;
48 unsigned int i;
49
50 fb = container_of(drm_fb, struct tegra_fb, drm_fb);
51 for (i = 0; i < fb->nplanes; i++) {
52 bo = fb->planes[i];
53 if (bo != NULL)
54 drm_gem_object_unreference_unlocked(&bo->gem_obj);
55 }
56
57 drm_framebuffer_cleanup(drm_fb);
58 free(fb->planes, DRM_MEM_DRIVER);
59 }
60
61 static int
fb_create_handle(struct drm_framebuffer * drm_fb,struct drm_file * file,unsigned int * handle)62 fb_create_handle(struct drm_framebuffer *drm_fb, struct drm_file *file,
63 unsigned int *handle)
64 {
65 struct tegra_fb *fb;
66 int rv;
67
68 fb = container_of(drm_fb, struct tegra_fb, drm_fb);
69 rv = drm_gem_handle_create(file, &fb->planes[0]->gem_obj, handle);
70 return (rv);
71 }
72
73 /* XXX Probably not needed */
74 static int
fb_dirty(struct drm_framebuffer * fb,struct drm_file * file_priv,unsigned flags,unsigned color,struct drm_clip_rect * clips,unsigned num_clips)75 fb_dirty(struct drm_framebuffer *fb, struct drm_file *file_priv,
76 unsigned flags, unsigned color, struct drm_clip_rect *clips, unsigned num_clips)
77 {
78
79 return (0);
80 }
81
82 static const struct drm_framebuffer_funcs fb_funcs = {
83 .destroy = fb_destroy,
84 .create_handle = fb_create_handle,
85 .dirty = fb_dirty,
86 };
87
88 static int
fb_alloc(struct drm_device * drm,struct drm_mode_fb_cmd2 * mode_cmd,struct tegra_bo ** planes,int num_planes,struct tegra_fb ** res_fb)89 fb_alloc(struct drm_device *drm, struct drm_mode_fb_cmd2 *mode_cmd,
90 struct tegra_bo **planes, int num_planes, struct tegra_fb **res_fb)
91 {
92 struct tegra_fb *fb;
93 int i;
94 int rv;
95
96 fb = malloc(sizeof(*fb), DRM_MEM_DRIVER, M_WAITOK | M_ZERO);
97 fb->planes = malloc(num_planes * sizeof(*fb->planes), DRM_MEM_DRIVER,
98 M_WAITOK | M_ZERO);
99 fb->nplanes = num_planes;
100
101 drm_helper_mode_fill_fb_struct(&fb->drm_fb, mode_cmd);
102 for (i = 0; i < fb->nplanes; i++)
103 fb->planes[i] = planes[i];
104 rv = drm_framebuffer_init(drm, &fb->drm_fb, &fb_funcs);
105 if (rv < 0) {
106 device_printf(drm->dev,
107 "Cannot initialize frame buffer %d\n", rv);
108 free(fb->planes, DRM_MEM_DRIVER);
109 return (rv);
110 }
111 *res_fb = fb;
112 return (0);
113 }
114
115 static int
tegra_fb_probe(struct drm_fb_helper * helper,struct drm_fb_helper_surface_size * sizes)116 tegra_fb_probe(struct drm_fb_helper *helper,
117 struct drm_fb_helper_surface_size *sizes)
118 {
119 u_int bpp, size;
120 struct tegra_drm *drm;
121 struct tegra_fb *fb;
122 struct fb_info *info;
123 struct tegra_bo *bo;
124 struct drm_mode_fb_cmd2 mode_cmd;
125 struct drm_device *drm_dev;
126 int rv;
127
128 if (helper->fb != NULL)
129 return (0);
130
131 DRM_DEBUG_KMS("surface: %d x %d (bpp: %d)\n", sizes->surface_width,
132 sizes->surface_height, sizes->surface_bpp);
133
134 drm_dev = helper->dev;
135 fb = container_of(helper, struct tegra_fb, fb_helper);
136 drm = container_of(drm_dev, struct tegra_drm, drm_dev);
137 bpp = (sizes->surface_bpp + 7) / 8;
138
139 /* Create mode_cmd */
140 memset(&mode_cmd, 0, sizeof(mode_cmd));
141 mode_cmd.width = sizes->surface_width;
142 mode_cmd.height = sizes->surface_height;
143 mode_cmd.pitches[0] = roundup(sizes->surface_width * bpp,
144 drm->pitch_align);
145 mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
146 sizes->surface_depth);
147 size = mode_cmd.pitches[0] * mode_cmd.height;
148
149 DRM_LOCK(drm_dev);
150 rv = tegra_bo_create(drm_dev, size, &bo);
151 DRM_UNLOCK(drm_dev);
152 if (rv != 0)
153 return (rv);
154
155 info = framebuffer_alloc();
156 if (info == NULL) {
157 device_printf(drm_dev->dev,
158 "Cannot allocate DRM framebuffer info.\n");
159 rv = -ENOMEM;
160 goto err_object;
161 }
162
163 rv = fb_alloc(drm_dev, &mode_cmd, &bo, 1, &fb);
164 if (rv != 0) {
165 device_printf(drm_dev->dev,
166 "Cannot allocate DRM framebuffer.\n");
167 goto err_fb;
168 }
169 helper->fb = &fb->drm_fb;
170 helper->fbdev = info;
171
172 /* Fill FB info */
173 info->fb_vbase = bo->vbase;
174 info->fb_pbase = bo->pbase;
175 info->fb_size = size;
176 info->fb_bpp = sizes->surface_bpp;
177 drm_fb_helper_fill_fix(info, fb->drm_fb.pitches[0], fb->drm_fb.depth);
178 drm_fb_helper_fill_var(info, helper, fb->drm_fb.width,
179 fb->drm_fb.height);
180
181 DRM_DEBUG_KMS("allocated %dx%d (s %dbits) fb size: %d, bo %p\n",
182 fb->drm_fb.width, fb->drm_fb.height, fb->drm_fb.depth,
183 size, bo);
184 return (1);
185 err_fb:
186 drm_gem_object_unreference_unlocked(&bo->gem_obj);
187 framebuffer_release(info);
188 err_object:
189 drm_gem_object_release(&bo->gem_obj);
190 return (rv);
191 }
192
193 static struct drm_fb_helper_funcs fb_helper_funcs = {
194 .fb_probe = tegra_fb_probe,
195 };
196
197 /*
198 * Exported functions
199 */
200 struct fb_info *
tegra_drm_fb_getinfo(struct drm_device * drm_dev)201 tegra_drm_fb_getinfo(struct drm_device *drm_dev)
202 {
203 struct tegra_fb *fb;
204 struct tegra_drm *drm;
205
206 drm = container_of(drm_dev, struct tegra_drm, drm_dev);
207 fb = drm->fb;
208 if (fb == NULL)
209 return (NULL);
210 return (fb->fb_helper.fbdev);
211 }
212
213 struct tegra_bo *
tegra_fb_get_plane(struct tegra_fb * fb,int idx)214 tegra_fb_get_plane(struct tegra_fb *fb, int idx)
215 {
216
217 if (idx >= drm_format_num_planes(fb->drm_fb.pixel_format))
218 return (NULL);
219 if (idx >= fb->nplanes)
220 return (NULL);
221 return (fb->planes[idx]);
222 }
223
224 int
tegra_drm_fb_init(struct drm_device * drm_dev)225 tegra_drm_fb_init(struct drm_device *drm_dev)
226 {
227 struct tegra_fb *fb;
228 struct tegra_drm *drm;
229 int rv;
230
231 drm = drm_dev->dev_private;
232 drm = container_of(drm_dev, struct tegra_drm, drm_dev);
233 fb = malloc(sizeof(*fb), DRM_MEM_DRIVER, M_WAITOK | M_ZERO);
234 drm->fb = fb;
235
236 fb->fb_helper.funcs = &fb_helper_funcs;
237 rv = drm_fb_helper_init(drm_dev, &fb->fb_helper,
238 drm_dev->mode_config.num_crtc, drm_dev->mode_config.num_connector);
239 if (rv != 0) {
240 device_printf(drm_dev->dev,
241 "Cannot initialize frame buffer %d\n", rv);
242 return (rv);
243 }
244
245 rv = drm_fb_helper_single_add_all_connectors(&fb->fb_helper);
246 if (rv != 0) {
247 device_printf(drm_dev->dev, "Cannot add all connectors: %d\n",
248 rv);
249 goto err_fini;
250 }
251
252 rv = drm_fb_helper_initial_config(&fb->fb_helper, 32);
253 if (rv != 0) {
254 device_printf(drm_dev->dev,
255 "Cannot set initial config: %d\n", rv);
256 goto err_fini;
257 }
258 /* XXXX Setup initial mode for FB */
259 /* drm_fb_helper_set_par(fb->fb_helper.fbdev); */
260 return 0;
261
262 err_fini:
263 drm_fb_helper_fini(&fb->fb_helper);
264 return (rv);
265 }
266
267 int
tegra_drm_fb_create(struct drm_device * drm,struct drm_file * file,struct drm_mode_fb_cmd2 * cmd,struct drm_framebuffer ** fb_res)268 tegra_drm_fb_create(struct drm_device *drm, struct drm_file *file,
269 struct drm_mode_fb_cmd2 *cmd, struct drm_framebuffer **fb_res)
270 {
271 int hsub, vsub, i;
272 int width, height, size, bpp;
273 struct tegra_bo *planes[4];
274 struct drm_gem_object *gem_obj;
275 struct tegra_fb *fb;
276 int rv, nplanes;
277
278 hsub = drm_format_horz_chroma_subsampling(cmd->pixel_format);
279 vsub = drm_format_vert_chroma_subsampling(cmd->pixel_format);
280
281 nplanes = drm_format_num_planes(cmd->pixel_format);
282 for (i = 0; i < nplanes; i++) {
283 width = cmd->width;
284 height = cmd->height;
285 if (i != 0) {
286 width /= hsub;
287 height /= vsub;
288 }
289 gem_obj = drm_gem_object_lookup(drm, file, cmd->handles[i]);
290 if (gem_obj == NULL) {
291 rv = -ENXIO;
292 goto fail;
293 }
294
295 bpp = drm_format_plane_cpp(cmd->pixel_format, i);
296 size = (height - 1) * cmd->pitches[i] +
297 width * bpp + cmd->offsets[i];
298 if (gem_obj->size < size) {
299 rv = -EINVAL;
300 goto fail;
301 }
302 planes[i] = container_of(gem_obj, struct tegra_bo, gem_obj);
303 }
304
305 rv = fb_alloc(drm, cmd, planes, nplanes, &fb);
306 if (rv != 0)
307 goto fail;
308
309 *fb_res = &fb->drm_fb;
310 return (0);
311
312 fail:
313 while (i--)
314 drm_gem_object_unreference_unlocked(&planes[i]->gem_obj);
315 return (rv);
316 }
317
318 void
tegra_drm_fb_destroy(struct drm_device * drm_dev)319 tegra_drm_fb_destroy(struct drm_device *drm_dev)
320 {
321 struct fb_info *info;
322 struct tegra_fb *fb;
323 struct tegra_drm *drm;
324
325 drm = container_of(drm_dev, struct tegra_drm, drm_dev);
326 fb = drm->fb;
327 if (fb == NULL)
328 return;
329 info = fb->fb_helper.fbdev;
330 drm_framebuffer_remove(&fb->drm_fb);
331 framebuffer_release(info);
332 drm_fb_helper_fini(&fb->fb_helper);
333 drm_framebuffer_cleanup(&fb->drm_fb);
334 free(fb, DRM_MEM_DRIVER);
335 drm->fb = NULL;
336 }
337