Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2015 Red Hat, Inc.
3 : : * All Rights Reserved.
4 : : *
5 : : * Authors:
6 : : * Dave Airlie
7 : : * Alon Levy
8 : : *
9 : : * Permission is hereby granted, free of charge, to any person obtaining a
10 : : * copy of this software and associated documentation files (the "Software"),
11 : : * to deal in the Software without restriction, including without limitation
12 : : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 : : * and/or sell copies of the Software, and to permit persons to whom the
14 : : * Software is furnished to do so, subject to the following conditions:
15 : : *
16 : : * The above copyright notice and this permission notice shall be included in
17 : : * all copies or substantial portions of the Software.
18 : : *
19 : : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 : : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 : : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 : : * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
23 : : * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24 : : * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 : : * OTHER DEALINGS IN THE SOFTWARE.
26 : : */
27 : :
28 : : #include <drm/drm_atomic_helper.h>
29 : : #include <drm/drm_damage_helper.h>
30 : : #include <drm/drm_fourcc.h>
31 : : #include <drm/drm_gem_framebuffer_helper.h>
32 : : #include <drm/drm_probe_helper.h>
33 : : #include <drm/drm_vblank.h>
34 : :
35 : : #include "virtgpu_drv.h"
36 : :
37 : : #define XRES_MIN 32
38 : : #define YRES_MIN 32
39 : :
40 : : #define XRES_DEF 1024
41 : : #define YRES_DEF 768
42 : :
43 : : #define XRES_MAX 8192
44 : : #define YRES_MAX 8192
45 : :
46 : : #define drm_connector_to_virtio_gpu_output(x) \
47 : : container_of(x, struct virtio_gpu_output, conn)
48 : :
49 : : static const struct drm_crtc_funcs virtio_gpu_crtc_funcs = {
50 : : .set_config = drm_atomic_helper_set_config,
51 : : .destroy = drm_crtc_cleanup,
52 : :
53 : : .page_flip = drm_atomic_helper_page_flip,
54 : : .reset = drm_atomic_helper_crtc_reset,
55 : : .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
56 : : .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
57 : : };
58 : :
59 : : static const struct drm_framebuffer_funcs virtio_gpu_fb_funcs = {
60 : : .create_handle = drm_gem_fb_create_handle,
61 : : .destroy = drm_gem_fb_destroy,
62 : : .dirty = drm_atomic_helper_dirtyfb,
63 : : };
64 : :
65 : : static int
66 : 0 : virtio_gpu_framebuffer_init(struct drm_device *dev,
67 : : struct virtio_gpu_framebuffer *vgfb,
68 : : const struct drm_mode_fb_cmd2 *mode_cmd,
69 : : struct drm_gem_object *obj)
70 : : {
71 : 0 : int ret;
72 : :
73 : 0 : vgfb->base.obj[0] = obj;
74 : :
75 : 0 : drm_helper_mode_fill_fb_struct(dev, &vgfb->base, mode_cmd);
76 : :
77 : 0 : ret = drm_framebuffer_init(dev, &vgfb->base, &virtio_gpu_fb_funcs);
78 [ # # ]: 0 : if (ret) {
79 : 0 : vgfb->base.obj[0] = NULL;
80 : 0 : return ret;
81 : : }
82 : : return 0;
83 : : }
84 : :
85 : 0 : static void virtio_gpu_crtc_mode_set_nofb(struct drm_crtc *crtc)
86 : : {
87 : 0 : struct drm_device *dev = crtc->dev;
88 : 0 : struct virtio_gpu_device *vgdev = dev->dev_private;
89 : 0 : struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc);
90 : :
91 : 0 : virtio_gpu_cmd_set_scanout(vgdev, output->index, 0,
92 : 0 : crtc->mode.hdisplay,
93 : 0 : crtc->mode.vdisplay, 0, 0);
94 : 0 : }
95 : :
96 : 0 : static void virtio_gpu_crtc_atomic_enable(struct drm_crtc *crtc,
97 : : struct drm_crtc_state *old_state)
98 : : {
99 : 0 : struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc);
100 : :
101 : 0 : output->enabled = true;
102 : 0 : }
103 : :
104 : 0 : static void virtio_gpu_crtc_atomic_disable(struct drm_crtc *crtc,
105 : : struct drm_crtc_state *old_state)
106 : : {
107 : 0 : struct drm_device *dev = crtc->dev;
108 : 0 : struct virtio_gpu_device *vgdev = dev->dev_private;
109 : 0 : struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc);
110 : :
111 : 0 : virtio_gpu_cmd_set_scanout(vgdev, output->index, 0, 0, 0, 0, 0);
112 : 0 : output->enabled = false;
113 : 0 : }
114 : :
115 : 0 : static int virtio_gpu_crtc_atomic_check(struct drm_crtc *crtc,
116 : : struct drm_crtc_state *state)
117 : : {
118 : 0 : return 0;
119 : : }
120 : :
121 : 0 : static void virtio_gpu_crtc_atomic_flush(struct drm_crtc *crtc,
122 : : struct drm_crtc_state *old_state)
123 : : {
124 : 0 : unsigned long flags;
125 : :
126 : 0 : spin_lock_irqsave(&crtc->dev->event_lock, flags);
127 [ # # ]: 0 : if (crtc->state->event)
128 : 0 : drm_crtc_send_vblank_event(crtc, crtc->state->event);
129 : 0 : crtc->state->event = NULL;
130 : 0 : spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
131 : 0 : }
132 : :
133 : : static const struct drm_crtc_helper_funcs virtio_gpu_crtc_helper_funcs = {
134 : : .mode_set_nofb = virtio_gpu_crtc_mode_set_nofb,
135 : : .atomic_check = virtio_gpu_crtc_atomic_check,
136 : : .atomic_flush = virtio_gpu_crtc_atomic_flush,
137 : : .atomic_enable = virtio_gpu_crtc_atomic_enable,
138 : : .atomic_disable = virtio_gpu_crtc_atomic_disable,
139 : : };
140 : :
141 : 0 : static void virtio_gpu_enc_mode_set(struct drm_encoder *encoder,
142 : : struct drm_display_mode *mode,
143 : : struct drm_display_mode *adjusted_mode)
144 : : {
145 : 0 : }
146 : :
147 : 0 : static void virtio_gpu_enc_enable(struct drm_encoder *encoder)
148 : : {
149 : 0 : }
150 : :
151 : 0 : static void virtio_gpu_enc_disable(struct drm_encoder *encoder)
152 : : {
153 : 0 : }
154 : :
155 : 0 : static int virtio_gpu_conn_get_modes(struct drm_connector *connector)
156 : : {
157 : 0 : struct virtio_gpu_output *output =
158 : 0 : drm_connector_to_virtio_gpu_output(connector);
159 : 0 : struct drm_display_mode *mode = NULL;
160 : 0 : int count, width, height;
161 : :
162 [ # # ]: 0 : if (output->edid) {
163 : 0 : count = drm_add_edid_modes(connector, output->edid);
164 [ # # ]: 0 : if (count)
165 : : return count;
166 : : }
167 : :
168 : 0 : width = le32_to_cpu(output->info.r.width);
169 : 0 : height = le32_to_cpu(output->info.r.height);
170 : 0 : count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX);
171 : :
172 [ # # ]: 0 : if (width == 0 || height == 0) {
173 : 0 : width = XRES_DEF;
174 : 0 : height = YRES_DEF;
175 : 0 : drm_set_preferred_mode(connector, XRES_DEF, YRES_DEF);
176 : : } else {
177 : 0 : DRM_DEBUG("add mode: %dx%d\n", width, height);
178 : 0 : mode = drm_cvt_mode(connector->dev, width, height, 60,
179 : : false, false, false);
180 : 0 : mode->type |= DRM_MODE_TYPE_PREFERRED;
181 : 0 : drm_mode_probed_add(connector, mode);
182 : 0 : count++;
183 : : }
184 : :
185 : : return count;
186 : : }
187 : :
188 : 0 : static enum drm_mode_status virtio_gpu_conn_mode_valid(struct drm_connector *connector,
189 : : struct drm_display_mode *mode)
190 : : {
191 : 0 : struct virtio_gpu_output *output =
192 : 0 : drm_connector_to_virtio_gpu_output(connector);
193 : 0 : int width, height;
194 : :
195 : 0 : width = le32_to_cpu(output->info.r.width);
196 : 0 : height = le32_to_cpu(output->info.r.height);
197 : :
198 [ # # ]: 0 : if (!(mode->type & DRM_MODE_TYPE_PREFERRED))
199 : : return MODE_OK;
200 [ # # # # ]: 0 : if (mode->hdisplay == XRES_DEF && mode->vdisplay == YRES_DEF)
201 : : return MODE_OK;
202 [ # # # # ]: 0 : if (mode->hdisplay <= width && mode->hdisplay >= width - 16 &&
203 [ # # # # ]: 0 : mode->vdisplay <= height && mode->vdisplay >= height - 16)
204 : : return MODE_OK;
205 : :
206 : 0 : DRM_DEBUG("del mode: %dx%d\n", mode->hdisplay, mode->vdisplay);
207 : 0 : return MODE_BAD;
208 : : }
209 : :
210 : : static const struct drm_encoder_helper_funcs virtio_gpu_enc_helper_funcs = {
211 : : .mode_set = virtio_gpu_enc_mode_set,
212 : : .enable = virtio_gpu_enc_enable,
213 : : .disable = virtio_gpu_enc_disable,
214 : : };
215 : :
216 : : static const struct drm_connector_helper_funcs virtio_gpu_conn_helper_funcs = {
217 : : .get_modes = virtio_gpu_conn_get_modes,
218 : : .mode_valid = virtio_gpu_conn_mode_valid,
219 : : };
220 : :
221 : 0 : static enum drm_connector_status virtio_gpu_conn_detect(
222 : : struct drm_connector *connector,
223 : : bool force)
224 : : {
225 : 0 : struct virtio_gpu_output *output =
226 : 0 : drm_connector_to_virtio_gpu_output(connector);
227 : :
228 [ # # ]: 0 : if (output->info.enabled)
229 : : return connector_status_connected;
230 : : else
231 : 0 : return connector_status_disconnected;
232 : : }
233 : :
234 : 0 : static void virtio_gpu_conn_destroy(struct drm_connector *connector)
235 : : {
236 : 0 : drm_connector_unregister(connector);
237 : 0 : drm_connector_cleanup(connector);
238 : 0 : }
239 : :
240 : : static const struct drm_connector_funcs virtio_gpu_connector_funcs = {
241 : : .detect = virtio_gpu_conn_detect,
242 : : .fill_modes = drm_helper_probe_single_connector_modes,
243 : : .destroy = virtio_gpu_conn_destroy,
244 : : .reset = drm_atomic_helper_connector_reset,
245 : : .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
246 : : .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
247 : : };
248 : :
249 : : static const struct drm_encoder_funcs virtio_gpu_enc_funcs = {
250 : : .destroy = drm_encoder_cleanup,
251 : : };
252 : :
253 : 0 : static int vgdev_output_init(struct virtio_gpu_device *vgdev, int index)
254 : : {
255 : 0 : struct drm_device *dev = vgdev->ddev;
256 : 0 : struct virtio_gpu_output *output = vgdev->outputs + index;
257 : 0 : struct drm_connector *connector = &output->conn;
258 : 0 : struct drm_encoder *encoder = &output->enc;
259 : 0 : struct drm_crtc *crtc = &output->crtc;
260 : 0 : struct drm_plane *primary, *cursor;
261 : :
262 : 0 : output->index = index;
263 [ # # ]: 0 : if (index == 0) {
264 : 0 : output->info.enabled = cpu_to_le32(true);
265 : 0 : output->info.r.width = cpu_to_le32(XRES_DEF);
266 : 0 : output->info.r.height = cpu_to_le32(YRES_DEF);
267 : : }
268 : :
269 : 0 : primary = virtio_gpu_plane_init(vgdev, DRM_PLANE_TYPE_PRIMARY, index);
270 [ # # ]: 0 : if (IS_ERR(primary))
271 : 0 : return PTR_ERR(primary);
272 : 0 : cursor = virtio_gpu_plane_init(vgdev, DRM_PLANE_TYPE_CURSOR, index);
273 [ # # ]: 0 : if (IS_ERR(cursor))
274 : 0 : return PTR_ERR(cursor);
275 : 0 : drm_crtc_init_with_planes(dev, crtc, primary, cursor,
276 : : &virtio_gpu_crtc_funcs, NULL);
277 : 0 : drm_crtc_helper_add(crtc, &virtio_gpu_crtc_helper_funcs);
278 : :
279 : 0 : drm_connector_init(dev, connector, &virtio_gpu_connector_funcs,
280 : : DRM_MODE_CONNECTOR_VIRTUAL);
281 [ # # ]: 0 : drm_connector_helper_add(connector, &virtio_gpu_conn_helper_funcs);
282 [ # # ]: 0 : if (vgdev->has_edid)
283 : 0 : drm_connector_attach_edid_property(connector);
284 : :
285 : 0 : drm_encoder_init(dev, encoder, &virtio_gpu_enc_funcs,
286 : : DRM_MODE_ENCODER_VIRTUAL, NULL);
287 : 0 : drm_encoder_helper_add(encoder, &virtio_gpu_enc_helper_funcs);
288 : 0 : encoder->possible_crtcs = 1 << index;
289 : :
290 : 0 : drm_connector_attach_encoder(connector, encoder);
291 : 0 : drm_connector_register(connector);
292 : 0 : return 0;
293 : : }
294 : :
295 : : static struct drm_framebuffer *
296 : 0 : virtio_gpu_user_framebuffer_create(struct drm_device *dev,
297 : : struct drm_file *file_priv,
298 : : const struct drm_mode_fb_cmd2 *mode_cmd)
299 : : {
300 : 0 : struct drm_gem_object *obj = NULL;
301 : 0 : struct virtio_gpu_framebuffer *virtio_gpu_fb;
302 : 0 : int ret;
303 : :
304 [ # # ]: 0 : if (mode_cmd->pixel_format != DRM_FORMAT_HOST_XRGB8888 &&
305 : : mode_cmd->pixel_format != DRM_FORMAT_HOST_ARGB8888)
306 : : return ERR_PTR(-ENOENT);
307 : :
308 : : /* lookup object associated with res handle */
309 : 0 : obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[0]);
310 [ # # ]: 0 : if (!obj)
311 : : return ERR_PTR(-EINVAL);
312 : :
313 : 0 : virtio_gpu_fb = kzalloc(sizeof(*virtio_gpu_fb), GFP_KERNEL);
314 [ # # ]: 0 : if (virtio_gpu_fb == NULL)
315 : : return ERR_PTR(-ENOMEM);
316 : :
317 : 0 : ret = virtio_gpu_framebuffer_init(dev, virtio_gpu_fb, mode_cmd, obj);
318 [ # # ]: 0 : if (ret) {
319 : 0 : kfree(virtio_gpu_fb);
320 : 0 : drm_gem_object_put_unlocked(obj);
321 : 0 : return NULL;
322 : : }
323 : :
324 : 0 : return &virtio_gpu_fb->base;
325 : : }
326 : :
327 : 0 : static void vgdev_atomic_commit_tail(struct drm_atomic_state *state)
328 : : {
329 : 0 : struct drm_device *dev = state->dev;
330 : :
331 : 0 : drm_atomic_helper_commit_modeset_disables(dev, state);
332 : 0 : drm_atomic_helper_commit_modeset_enables(dev, state);
333 : 0 : drm_atomic_helper_commit_planes(dev, state, 0);
334 : :
335 : 0 : drm_atomic_helper_commit_hw_done(state);
336 : :
337 : 0 : drm_atomic_helper_wait_for_vblanks(dev, state);
338 : 0 : drm_atomic_helper_cleanup_planes(dev, state);
339 : 0 : }
340 : :
341 : : static const struct drm_mode_config_helper_funcs virtio_mode_config_helpers = {
342 : : .atomic_commit_tail = vgdev_atomic_commit_tail,
343 : : };
344 : :
345 : : static const struct drm_mode_config_funcs virtio_gpu_mode_funcs = {
346 : : .fb_create = virtio_gpu_user_framebuffer_create,
347 : : .atomic_check = drm_atomic_helper_check,
348 : : .atomic_commit = drm_atomic_helper_commit,
349 : : };
350 : :
351 : 0 : void virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev)
352 : : {
353 : 0 : int i;
354 : :
355 : 0 : drm_mode_config_init(vgdev->ddev);
356 : 0 : vgdev->ddev->mode_config.quirk_addfb_prefer_host_byte_order = true;
357 : 0 : vgdev->ddev->mode_config.funcs = &virtio_gpu_mode_funcs;
358 : 0 : vgdev->ddev->mode_config.helper_private = &virtio_mode_config_helpers;
359 : :
360 : : /* modes will be validated against the framebuffer size */
361 : 0 : vgdev->ddev->mode_config.min_width = XRES_MIN;
362 : 0 : vgdev->ddev->mode_config.min_height = YRES_MIN;
363 : 0 : vgdev->ddev->mode_config.max_width = XRES_MAX;
364 : 0 : vgdev->ddev->mode_config.max_height = YRES_MAX;
365 : :
366 [ # # ]: 0 : for (i = 0 ; i < vgdev->num_scanouts; ++i)
367 : 0 : vgdev_output_init(vgdev, i);
368 : :
369 : 0 : drm_mode_config_reset(vgdev->ddev);
370 : 0 : }
371 : :
372 : 0 : void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev)
373 : : {
374 : 0 : int i;
375 : :
376 [ # # ]: 0 : for (i = 0 ; i < vgdev->num_scanouts; ++i)
377 : 0 : kfree(vgdev->outputs[i].edid);
378 : 0 : drm_atomic_helper_shutdown(vgdev->ddev);
379 : 0 : drm_mode_config_cleanup(vgdev->ddev);
380 : 0 : }
|