• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2008-2011 Kristian Høgsberg
3  * Copyright © 2011 Intel Corporation
4  * Copyright © 2017, 2018 Collabora, Ltd.
5  * Copyright © 2017, 2018 General Electric Company
6  * Copyright (c) 2018 DisplayLink (UK) Ltd.
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining
9  * a copy of this software and associated documentation files (the
10  * "Software"), to deal in the Software without restriction, including
11  * without limitation the rights to use, copy, modify, merge, publish,
12  * distribute, sublicense, and/or sell copies of the Software, and to
13  * permit persons to whom the Software is furnished to do so, subject to
14  * the following conditions:
15  *
16  * The above copyright notice and this permission notice (including the
17  * next paragraph) shall be included in all copies or substantial
18  * portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
24  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
25  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27  * SOFTWARE.
28  */
29 
30 #include "config.h"
31 
32 #include <errno.h>
33 #include <stdint.h>
34 #include <stdlib.h>
35 #include <ctype.h>
36 #include <string.h>
37 
38 #include "drm-internal.h"
39 #include "renderer-gl/gl-renderer.h"
40 
41 /**
42  * Create a drm_plane for virtual output
43  *
44  * Call drm_virtual_plane_destroy to clean up the plane.
45  *
46  * @param b DRM compositor backend
47  * @param output Output to create internal plane for
48  */
49 static struct drm_plane *
drm_virtual_plane_create(struct drm_backend * b,struct drm_output * output)50 drm_virtual_plane_create(struct drm_backend *b, struct drm_output *output)
51 {
52 	struct drm_plane *plane;
53 
54 	/* num of formats is one */
55 	plane = zalloc(sizeof(*plane) + sizeof(plane->formats[0]));
56 	if (!plane) {
57 		weston_log("%s: out of memory\n", __func__);
58 		return NULL;
59 	}
60 
61 	plane->type = WDRM_PLANE_TYPE_PRIMARY;
62 	plane->backend = b;
63 	plane->state_cur = drm_plane_state_alloc(NULL, plane);
64 	plane->state_cur->complete = true;
65 	plane->formats[0].format = output->gbm_format;
66 	plane->count_formats = 1;
67 	if ((output->gbm_bo_flags & GBM_BO_USE_LINEAR) && b->fb_modifiers) {
68 		uint64_t *modifiers = zalloc(sizeof *modifiers);
69 		if (modifiers) {
70 			*modifiers = DRM_FORMAT_MOD_LINEAR;
71 			plane->formats[0].modifiers = modifiers;
72 			plane->formats[0].count_modifiers = 1;
73 		}
74 	}
75 
76 	weston_plane_init(&plane->base, b->compositor, 0, 0);
77 	wl_list_insert(&b->plane_list, &plane->link);
78 
79 	return plane;
80 }
81 
82 /**
83  * Destroy one DRM plane
84  *
85  * @param plane Plane to deallocate (will be freed)
86  */
87 static void
drm_virtual_plane_destroy(struct drm_plane * plane)88 drm_virtual_plane_destroy(struct drm_plane *plane)
89 {
90 	drm_plane_state_free(plane->state_cur, true);
91 	weston_plane_release(&plane->base);
92 	wl_list_remove(&plane->link);
93 	if (plane->formats[0].modifiers)
94 		free(plane->formats[0].modifiers);
95 	free(plane);
96 }
97 
98 static int
drm_virtual_output_start_repaint_loop(struct weston_output * output_base)99 drm_virtual_output_start_repaint_loop(struct weston_output *output_base)
100 {
101 	weston_output_finish_frame(output_base, NULL,
102 				   WP_PRESENTATION_FEEDBACK_INVALID);
103 
104 	return 0;
105 }
106 
107 static int
drm_virtual_output_submit_frame(struct drm_output * output,struct drm_fb * fb)108 drm_virtual_output_submit_frame(struct drm_output *output,
109 				struct drm_fb *fb)
110 {
111 	struct drm_backend *b = to_drm_backend(output->base.compositor);
112 	int fd, ret;
113 
114 	assert(fb->num_planes == 1);
115 	ret = drmPrimeHandleToFD(b->drm.fd, fb->handles[0], DRM_CLOEXEC, &fd);
116 	if (ret) {
117 		weston_log("drmPrimeHandleFD failed, errno=%d\n", errno);
118 		return -1;
119 	}
120 
121 	drm_fb_ref(fb);
122 	ret = output->virtual_submit_frame(&output->base, fd, fb->strides[0],
123 					   fb);
124 	if (ret < 0) {
125 		drm_fb_unref(fb);
126 		close(fd);
127 	}
128 	return ret;
129 }
130 
131 static int
drm_virtual_output_repaint(struct weston_output * output_base,pixman_region32_t * damage,void * repaint_data)132 drm_virtual_output_repaint(struct weston_output *output_base,
133 			   pixman_region32_t *damage,
134 			   void *repaint_data)
135 {
136 	struct drm_pending_state *pending_state = repaint_data;
137 	struct drm_output_state *state = NULL;
138 	struct drm_output *output = to_drm_output(output_base);
139 	struct drm_plane *scanout_plane = output->scanout_plane;
140 	struct drm_plane_state *scanout_state;
141 
142 	assert(output->virtual);
143 
144 	if (output->disable_pending || output->destroy_pending)
145 		goto err;
146 
147 	/* Drop frame if there isn't free buffers */
148 	if (!gbm_surface_has_free_buffers(output->gbm_surface)) {
149 		weston_log("%s: Drop frame!!\n", __func__);
150 		return -1;
151 	}
152 
153 	assert(!output->state_last);
154 
155 	/* If planes have been disabled in the core, we might not have
156 	 * hit assign_planes at all, so might not have valid output state
157 	 * here. */
158 	state = drm_pending_state_get_output(pending_state, output);
159 	if (!state)
160 		state = drm_output_state_duplicate(output->state_cur,
161 						   pending_state,
162 						   DRM_OUTPUT_STATE_CLEAR_PLANES);
163 
164 	drm_output_render(state, damage);
165 	scanout_state = drm_output_state_get_plane(state, scanout_plane);
166 	if (!scanout_state || !scanout_state->fb)
167 		goto err;
168 
169 	if (drm_virtual_output_submit_frame(output, scanout_state->fb) < 0)
170 		goto err;
171 
172 	return 0;
173 
174 err:
175 	drm_output_state_free(state);
176 	return -1;
177 }
178 
179 static void
drm_virtual_output_deinit(struct weston_output * base)180 drm_virtual_output_deinit(struct weston_output *base)
181 {
182 	struct drm_output *output = to_drm_output(base);
183 
184 	drm_output_fini_egl(output);
185 
186 	drm_virtual_plane_destroy(output->scanout_plane);
187 }
188 
189 static void
drm_virtual_output_destroy(struct weston_output * base)190 drm_virtual_output_destroy(struct weston_output *base)
191 {
192 	struct drm_output *output = to_drm_output(base);
193 
194 	assert(output->virtual);
195 
196 	if (output->base.enabled)
197 		drm_virtual_output_deinit(&output->base);
198 
199 	weston_output_release(&output->base);
200 
201 	drm_output_state_free(output->state_cur);
202 
203 	free(output);
204 }
205 
206 static int
drm_virtual_output_enable(struct weston_output * output_base)207 drm_virtual_output_enable(struct weston_output *output_base)
208 {
209 	struct drm_output *output = to_drm_output(output_base);
210 	struct drm_backend *b = to_drm_backend(output_base->compositor);
211 
212 	assert(output->virtual);
213 
214 	if (b->use_pixman) {
215 		weston_log("Not support pixman renderer on Virtual output\n");
216 		goto err;
217 	}
218 
219 	if (!output->virtual_submit_frame) {
220 		weston_log("The virtual_submit_frame hook is not set\n");
221 		goto err;
222 	}
223 
224 	output->scanout_plane = drm_virtual_plane_create(b, output);
225 	if (!output->scanout_plane) {
226 		weston_log("Failed to find primary plane for output %s\n",
227 			   output->base.name);
228 		return -1;
229 	}
230 
231 	if (drm_output_init_egl(output, b) < 0) {
232 		weston_log("Failed to init output gl state\n");
233 		goto err;
234 	}
235 
236 	output->base.start_repaint_loop = drm_virtual_output_start_repaint_loop;
237 	output->base.repaint = drm_virtual_output_repaint;
238 	output->base.assign_planes = drm_assign_planes;
239 	output->base.set_dpms = NULL;
240 	output->base.switch_mode = NULL;
241 	output->base.gamma_size = 0;
242 	output->base.set_gamma = NULL;
243 
244 	weston_compositor_stack_plane(b->compositor,
245 				      &output->scanout_plane->base,
246 				      &b->compositor->primary_plane);
247 
248 	return 0;
249 err:
250 	return -1;
251 }
252 
253 static int
drm_virtual_output_disable(struct weston_output * base)254 drm_virtual_output_disable(struct weston_output *base)
255 {
256 	struct drm_output *output = to_drm_output(base);
257 
258 	assert(output->virtual);
259 
260 	if (output->base.enabled)
261 		drm_virtual_output_deinit(&output->base);
262 
263 	return 0;
264 }
265 
266 static struct weston_output *
drm_virtual_output_create(struct weston_compositor * c,char * name)267 drm_virtual_output_create(struct weston_compositor *c, char *name)
268 {
269 	struct drm_output *output;
270 
271 	output = zalloc(sizeof *output);
272 	if (!output)
273 		return NULL;
274 
275 	output->virtual = true;
276 	output->gbm_bo_flags = GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING;
277 
278 	weston_output_init(&output->base, c, name);
279 
280 	output->base.enable = drm_virtual_output_enable;
281 	output->base.destroy = drm_virtual_output_destroy;
282 	output->base.disable = drm_virtual_output_disable;
283 	output->base.attach_head = NULL;
284 
285 	output->state_cur = drm_output_state_alloc(output, NULL);
286 
287 	weston_compositor_add_pending_output(&output->base, c);
288 
289 	return &output->base;
290 }
291 
292 static uint32_t
drm_virtual_output_set_gbm_format(struct weston_output * base,const char * gbm_format)293 drm_virtual_output_set_gbm_format(struct weston_output *base,
294 				  const char *gbm_format)
295 {
296 	struct drm_output *output = to_drm_output(base);
297 	struct drm_backend *b = to_drm_backend(base->compositor);
298 
299 	if (parse_gbm_format(gbm_format, b->gbm_format, &output->gbm_format) == -1)
300 		output->gbm_format = b->gbm_format;
301 
302 	return output->gbm_format;
303 }
304 
305 static void
drm_virtual_output_set_submit_frame_cb(struct weston_output * output_base,submit_frame_cb cb)306 drm_virtual_output_set_submit_frame_cb(struct weston_output *output_base,
307 				       submit_frame_cb cb)
308 {
309 	struct drm_output *output = to_drm_output(output_base);
310 
311 	output->virtual_submit_frame = cb;
312 }
313 
314 static int
drm_virtual_output_get_fence_fd(struct weston_output * output_base)315 drm_virtual_output_get_fence_fd(struct weston_output *output_base)
316 {
317 	return gl_renderer->create_fence_fd(output_base);
318 }
319 
320 static void
drm_virtual_output_buffer_released(struct drm_fb * fb)321 drm_virtual_output_buffer_released(struct drm_fb *fb)
322 {
323 	drm_fb_unref(fb);
324 }
325 
326 static void
drm_virtual_output_finish_frame(struct weston_output * output_base,struct timespec * stamp,uint32_t presented_flags)327 drm_virtual_output_finish_frame(struct weston_output *output_base,
328 				struct timespec *stamp,
329 				uint32_t presented_flags)
330 {
331 	struct drm_output *output = to_drm_output(output_base);
332 	struct drm_plane_state *ps;
333 
334 	wl_list_for_each(ps, &output->state_cur->plane_list, link)
335 		ps->complete = true;
336 
337 	drm_output_state_free(output->state_last);
338 	output->state_last = NULL;
339 
340 	weston_output_finish_frame(&output->base, stamp, presented_flags);
341 
342 	/* We can't call this from frame_notify, because the output's
343 	 * repaint needed flag is cleared just after that */
344 	if (output->recorder)
345 		weston_output_schedule_repaint(&output->base);
346 }
347 
348 static const struct weston_drm_virtual_output_api virt_api = {
349 	drm_virtual_output_create,
350 	drm_virtual_output_set_gbm_format,
351 	drm_virtual_output_set_submit_frame_cb,
352 	drm_virtual_output_get_fence_fd,
353 	drm_virtual_output_buffer_released,
354 	drm_virtual_output_finish_frame
355 };
356 
drm_backend_init_virtual_output_api(struct weston_compositor * compositor)357 int drm_backend_init_virtual_output_api(struct weston_compositor *compositor)
358 {
359 	return weston_plugin_api_register(compositor,
360 					  WESTON_DRM_VIRTUAL_OUTPUT_API_NAME,
361 					  &virt_api, sizeof(virt_api));
362 }
363