xref: /freebsd/sys/arm/nvidia/drm2/tegra_fb.c (revision 685dc743)
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
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
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
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
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
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 *
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 *
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
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
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
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