2 * Copyright (C) 2015 Red Hat, Inc.
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:
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
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.
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_simple_kms_helper.h>
35 #include "virtgpu_drv.h"
46 #define drm_connector_to_virtio_gpu_output(x) \
47 container_of(x, struct virtio_gpu_output, conn)
49 static const struct drm_crtc_funcs virtio_gpu_crtc_funcs
= {
50 .set_config
= drm_atomic_helper_set_config
,
51 .destroy
= drm_crtc_cleanup
,
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
,
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
,
66 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
)
73 vgfb
->base
.obj
[0] = obj
;
75 drm_helper_mode_fill_fb_struct(dev
, &vgfb
->base
, mode_cmd
);
77 ret
= drm_framebuffer_init(dev
, &vgfb
->base
, &virtio_gpu_fb_funcs
);
79 vgfb
->base
.obj
[0] = NULL
;
85 static void virtio_gpu_crtc_mode_set_nofb(struct drm_crtc
*crtc
)
87 struct drm_device
*dev
= crtc
->dev
;
88 struct virtio_gpu_device
*vgdev
= dev
->dev_private
;
89 struct virtio_gpu_output
*output
= drm_crtc_to_virtio_gpu_output(crtc
);
91 virtio_gpu_cmd_set_scanout(vgdev
, output
->index
, 0,
93 crtc
->mode
.vdisplay
, 0, 0);
94 virtio_gpu_notify(vgdev
);
97 static void virtio_gpu_crtc_atomic_enable(struct drm_crtc
*crtc
,
98 struct drm_atomic_state
*state
)
102 static void virtio_gpu_crtc_atomic_disable(struct drm_crtc
*crtc
,
103 struct drm_atomic_state
*state
)
105 struct drm_device
*dev
= crtc
->dev
;
106 struct virtio_gpu_device
*vgdev
= dev
->dev_private
;
107 struct virtio_gpu_output
*output
= drm_crtc_to_virtio_gpu_output(crtc
);
109 virtio_gpu_cmd_set_scanout(vgdev
, output
->index
, 0, 0, 0, 0, 0);
110 virtio_gpu_notify(vgdev
);
113 static int virtio_gpu_crtc_atomic_check(struct drm_crtc
*crtc
,
114 struct drm_atomic_state
*state
)
119 static void virtio_gpu_crtc_atomic_flush(struct drm_crtc
*crtc
,
120 struct drm_atomic_state
*state
)
122 struct drm_crtc_state
*crtc_state
= drm_atomic_get_new_crtc_state(state
,
124 struct virtio_gpu_output
*output
= drm_crtc_to_virtio_gpu_output(crtc
);
127 * virtio-gpu can't do modeset and plane update operations
128 * independent from each other. So the actual modeset happens
129 * in the plane update callback, and here we just check
130 * whenever we must force the modeset.
132 if (drm_atomic_crtc_needs_modeset(crtc_state
)) {
133 output
->needs_modeset
= true;
137 static const struct drm_crtc_helper_funcs virtio_gpu_crtc_helper_funcs
= {
138 .mode_set_nofb
= virtio_gpu_crtc_mode_set_nofb
,
139 .atomic_check
= virtio_gpu_crtc_atomic_check
,
140 .atomic_flush
= virtio_gpu_crtc_atomic_flush
,
141 .atomic_enable
= virtio_gpu_crtc_atomic_enable
,
142 .atomic_disable
= virtio_gpu_crtc_atomic_disable
,
145 static void virtio_gpu_enc_mode_set(struct drm_encoder
*encoder
,
146 struct drm_display_mode
*mode
,
147 struct drm_display_mode
*adjusted_mode
)
151 static void virtio_gpu_enc_enable(struct drm_encoder
*encoder
)
155 static void virtio_gpu_enc_disable(struct drm_encoder
*encoder
)
159 static int virtio_gpu_conn_get_modes(struct drm_connector
*connector
)
161 struct virtio_gpu_output
*output
=
162 drm_connector_to_virtio_gpu_output(connector
);
163 struct drm_display_mode
*mode
= NULL
;
164 int count
, width
, height
;
167 count
= drm_add_edid_modes(connector
, output
->edid
);
172 width
= le32_to_cpu(output
->info
.r
.width
);
173 height
= le32_to_cpu(output
->info
.r
.height
);
174 count
= drm_add_modes_noedid(connector
, XRES_MAX
, YRES_MAX
);
176 if (width
== 0 || height
== 0) {
177 drm_set_preferred_mode(connector
, XRES_DEF
, YRES_DEF
);
179 DRM_DEBUG("add mode: %dx%d\n", width
, height
);
180 mode
= drm_cvt_mode(connector
->dev
, width
, height
, 60,
181 false, false, false);
182 mode
->type
|= DRM_MODE_TYPE_PREFERRED
;
183 drm_mode_probed_add(connector
, mode
);
190 static enum drm_mode_status
virtio_gpu_conn_mode_valid(struct drm_connector
*connector
,
191 struct drm_display_mode
*mode
)
193 struct virtio_gpu_output
*output
=
194 drm_connector_to_virtio_gpu_output(connector
);
197 width
= le32_to_cpu(output
->info
.r
.width
);
198 height
= le32_to_cpu(output
->info
.r
.height
);
200 if (!(mode
->type
& DRM_MODE_TYPE_PREFERRED
))
202 if (mode
->hdisplay
== XRES_DEF
&& mode
->vdisplay
== YRES_DEF
)
204 if (mode
->hdisplay
<= width
&& mode
->hdisplay
>= width
- 16 &&
205 mode
->vdisplay
<= height
&& mode
->vdisplay
>= height
- 16)
208 DRM_DEBUG("del mode: %dx%d\n", mode
->hdisplay
, mode
->vdisplay
);
212 static const struct drm_encoder_helper_funcs virtio_gpu_enc_helper_funcs
= {
213 .mode_set
= virtio_gpu_enc_mode_set
,
214 .enable
= virtio_gpu_enc_enable
,
215 .disable
= virtio_gpu_enc_disable
,
218 static const struct drm_connector_helper_funcs virtio_gpu_conn_helper_funcs
= {
219 .get_modes
= virtio_gpu_conn_get_modes
,
220 .mode_valid
= virtio_gpu_conn_mode_valid
,
223 static enum drm_connector_status
virtio_gpu_conn_detect(
224 struct drm_connector
*connector
,
227 struct virtio_gpu_output
*output
=
228 drm_connector_to_virtio_gpu_output(connector
);
230 if (output
->info
.enabled
)
231 return connector_status_connected
;
233 return connector_status_disconnected
;
236 static void virtio_gpu_conn_destroy(struct drm_connector
*connector
)
238 drm_connector_unregister(connector
);
239 drm_connector_cleanup(connector
);
242 static const struct drm_connector_funcs virtio_gpu_connector_funcs
= {
243 .detect
= virtio_gpu_conn_detect
,
244 .fill_modes
= drm_helper_probe_single_connector_modes
,
245 .destroy
= virtio_gpu_conn_destroy
,
246 .reset
= drm_atomic_helper_connector_reset
,
247 .atomic_duplicate_state
= drm_atomic_helper_connector_duplicate_state
,
248 .atomic_destroy_state
= drm_atomic_helper_connector_destroy_state
,
251 static int vgdev_output_init(struct virtio_gpu_device
*vgdev
, int index
)
253 struct drm_device
*dev
= vgdev
->ddev
;
254 struct virtio_gpu_output
*output
= vgdev
->outputs
+ index
;
255 struct drm_connector
*connector
= &output
->conn
;
256 struct drm_encoder
*encoder
= &output
->enc
;
257 struct drm_crtc
*crtc
= &output
->crtc
;
258 struct drm_plane
*primary
, *cursor
;
260 output
->index
= index
;
262 output
->info
.enabled
= cpu_to_le32(true);
263 output
->info
.r
.width
= cpu_to_le32(XRES_DEF
);
264 output
->info
.r
.height
= cpu_to_le32(YRES_DEF
);
267 primary
= virtio_gpu_plane_init(vgdev
, DRM_PLANE_TYPE_PRIMARY
, index
);
269 return PTR_ERR(primary
);
270 cursor
= virtio_gpu_plane_init(vgdev
, DRM_PLANE_TYPE_CURSOR
, index
);
272 return PTR_ERR(cursor
);
273 drm_crtc_init_with_planes(dev
, crtc
, primary
, cursor
,
274 &virtio_gpu_crtc_funcs
, NULL
);
275 drm_crtc_helper_add(crtc
, &virtio_gpu_crtc_helper_funcs
);
277 drm_connector_init(dev
, connector
, &virtio_gpu_connector_funcs
,
278 DRM_MODE_CONNECTOR_VIRTUAL
);
279 drm_connector_helper_add(connector
, &virtio_gpu_conn_helper_funcs
);
281 drm_connector_attach_edid_property(connector
);
283 drm_simple_encoder_init(dev
, encoder
, DRM_MODE_ENCODER_VIRTUAL
);
284 drm_encoder_helper_add(encoder
, &virtio_gpu_enc_helper_funcs
);
285 encoder
->possible_crtcs
= 1 << index
;
287 drm_connector_attach_encoder(connector
, encoder
);
288 drm_connector_register(connector
);
292 static struct drm_framebuffer
*
293 virtio_gpu_user_framebuffer_create(struct drm_device
*dev
,
294 struct drm_file
*file_priv
,
295 const struct drm_mode_fb_cmd2
*mode_cmd
)
297 struct drm_gem_object
*obj
= NULL
;
298 struct virtio_gpu_framebuffer
*virtio_gpu_fb
;
301 if (mode_cmd
->pixel_format
!= DRM_FORMAT_HOST_XRGB8888
&&
302 mode_cmd
->pixel_format
!= DRM_FORMAT_HOST_ARGB8888
)
303 return ERR_PTR(-ENOENT
);
305 /* lookup object associated with res handle */
306 obj
= drm_gem_object_lookup(file_priv
, mode_cmd
->handles
[0]);
308 return ERR_PTR(-EINVAL
);
310 virtio_gpu_fb
= kzalloc(sizeof(*virtio_gpu_fb
), GFP_KERNEL
);
311 if (virtio_gpu_fb
== NULL
)
312 return ERR_PTR(-ENOMEM
);
314 ret
= virtio_gpu_framebuffer_init(dev
, virtio_gpu_fb
, mode_cmd
, obj
);
316 kfree(virtio_gpu_fb
);
317 drm_gem_object_put(obj
);
321 return &virtio_gpu_fb
->base
;
324 static const struct drm_mode_config_funcs virtio_gpu_mode_funcs
= {
325 .fb_create
= virtio_gpu_user_framebuffer_create
,
326 .atomic_check
= drm_atomic_helper_check
,
327 .atomic_commit
= drm_atomic_helper_commit
,
330 int virtio_gpu_modeset_init(struct virtio_gpu_device
*vgdev
)
334 ret
= drmm_mode_config_init(vgdev
->ddev
);
338 vgdev
->ddev
->mode_config
.quirk_addfb_prefer_host_byte_order
= true;
339 vgdev
->ddev
->mode_config
.funcs
= &virtio_gpu_mode_funcs
;
341 /* modes will be validated against the framebuffer size */
342 vgdev
->ddev
->mode_config
.min_width
= XRES_MIN
;
343 vgdev
->ddev
->mode_config
.min_height
= YRES_MIN
;
344 vgdev
->ddev
->mode_config
.max_width
= XRES_MAX
;
345 vgdev
->ddev
->mode_config
.max_height
= YRES_MAX
;
347 for (i
= 0 ; i
< vgdev
->num_scanouts
; ++i
)
348 vgdev_output_init(vgdev
, i
);
350 drm_mode_config_reset(vgdev
->ddev
);
354 void virtio_gpu_modeset_fini(struct virtio_gpu_device
*vgdev
)
358 for (i
= 0 ; i
< vgdev
->num_scanouts
; ++i
)
359 kfree(vgdev
->outputs
[i
].edid
);