2 * Copyright 2013 Red Hat Inc.
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
22 * Authors: Dave Airlie
26 #include <linux/crc32.h>
27 #include <drm/drm_crtc_helper.h>
28 #include <drm/drm_plane_helper.h>
29 #include <drm/drm_atomic_helper.h>
30 #include <drm/drm_atomic.h>
33 #include "qxl_object.h"
35 static bool qxl_head_enabled(struct qxl_head
*head
)
37 return head
->width
&& head
->height
;
40 static void qxl_alloc_client_monitors_config(struct qxl_device
*qdev
, unsigned count
)
42 if (qdev
->client_monitors_config
&&
43 count
> qdev
->client_monitors_config
->count
) {
44 kfree(qdev
->client_monitors_config
);
45 qdev
->client_monitors_config
= NULL
;
47 if (!qdev
->client_monitors_config
) {
48 qdev
->client_monitors_config
= kzalloc(
49 sizeof(struct qxl_monitors_config
) +
50 sizeof(struct qxl_head
) * count
, GFP_KERNEL
);
51 if (!qdev
->client_monitors_config
) {
53 "%s: allocation failure for %u heads\n",
58 qdev
->client_monitors_config
->count
= count
;
62 MONITORS_CONFIG_MODIFIED
,
63 MONITORS_CONFIG_UNCHANGED
,
64 MONITORS_CONFIG_BAD_CRC
,
67 static int qxl_display_copy_rom_client_monitors_config(struct qxl_device
*qdev
)
72 int status
= MONITORS_CONFIG_UNCHANGED
;
74 num_monitors
= qdev
->rom
->client_monitors_config
.count
;
75 crc
= crc32(0, (const uint8_t *)&qdev
->rom
->client_monitors_config
,
76 sizeof(qdev
->rom
->client_monitors_config
));
77 if (crc
!= qdev
->rom
->client_monitors_config_crc
) {
78 qxl_io_log(qdev
, "crc mismatch: have %X (%zd) != %X\n", crc
,
79 sizeof(qdev
->rom
->client_monitors_config
),
80 qdev
->rom
->client_monitors_config_crc
);
81 return MONITORS_CONFIG_BAD_CRC
;
84 DRM_DEBUG_KMS("no client monitors configured\n");
87 if (num_monitors
> qdev
->monitors_config
->max_allowed
) {
88 DRM_DEBUG_KMS("client monitors list will be truncated: %d < %d\n",
89 qdev
->monitors_config
->max_allowed
, num_monitors
);
90 num_monitors
= qdev
->monitors_config
->max_allowed
;
92 num_monitors
= qdev
->rom
->client_monitors_config
.count
;
94 if (qdev
->client_monitors_config
95 && (num_monitors
!= qdev
->client_monitors_config
->count
)) {
96 status
= MONITORS_CONFIG_MODIFIED
;
98 qxl_alloc_client_monitors_config(qdev
, num_monitors
);
99 /* we copy max from the client but it isn't used */
100 qdev
->client_monitors_config
->max_allowed
=
101 qdev
->monitors_config
->max_allowed
;
102 for (i
= 0 ; i
< qdev
->client_monitors_config
->count
; ++i
) {
103 struct qxl_urect
*c_rect
=
104 &qdev
->rom
->client_monitors_config
.heads
[i
];
105 struct qxl_head
*client_head
=
106 &qdev
->client_monitors_config
->heads
[i
];
107 if (client_head
->x
!= c_rect
->left
) {
108 client_head
->x
= c_rect
->left
;
109 status
= MONITORS_CONFIG_MODIFIED
;
111 if (client_head
->y
!= c_rect
->top
) {
112 client_head
->y
= c_rect
->top
;
113 status
= MONITORS_CONFIG_MODIFIED
;
115 if (client_head
->width
!= c_rect
->right
- c_rect
->left
) {
116 client_head
->width
= c_rect
->right
- c_rect
->left
;
117 status
= MONITORS_CONFIG_MODIFIED
;
119 if (client_head
->height
!= c_rect
->bottom
- c_rect
->top
) {
120 client_head
->height
= c_rect
->bottom
- c_rect
->top
;
121 status
= MONITORS_CONFIG_MODIFIED
;
123 if (client_head
->surface_id
!= 0) {
124 client_head
->surface_id
= 0;
125 status
= MONITORS_CONFIG_MODIFIED
;
127 if (client_head
->id
!= i
) {
129 status
= MONITORS_CONFIG_MODIFIED
;
131 if (client_head
->flags
!= 0) {
132 client_head
->flags
= 0;
133 status
= MONITORS_CONFIG_MODIFIED
;
135 DRM_DEBUG_KMS("read %dx%d+%d+%d\n", client_head
->width
, client_head
->height
,
136 client_head
->x
, client_head
->y
);
142 static void qxl_update_offset_props(struct qxl_device
*qdev
)
144 struct drm_device
*dev
= &qdev
->ddev
;
145 struct drm_connector
*connector
;
146 struct qxl_output
*output
;
147 struct qxl_head
*head
;
149 list_for_each_entry(connector
, &dev
->mode_config
.connector_list
, head
) {
150 output
= drm_connector_to_qxl_output(connector
);
152 head
= &qdev
->client_monitors_config
->heads
[output
->index
];
154 drm_object_property_set_value(&connector
->base
,
155 dev
->mode_config
.suggested_x_property
, head
->x
);
156 drm_object_property_set_value(&connector
->base
,
157 dev
->mode_config
.suggested_y_property
, head
->y
);
161 void qxl_display_read_client_monitors_config(struct qxl_device
*qdev
)
163 struct drm_device
*dev
= &qdev
->ddev
;
166 for (retries
= 0; retries
< 10; retries
++) {
167 status
= qxl_display_copy_rom_client_monitors_config(qdev
);
168 if (status
!= MONITORS_CONFIG_BAD_CRC
)
172 if (status
== MONITORS_CONFIG_BAD_CRC
) {
173 qxl_io_log(qdev
, "config: bad crc\n");
174 DRM_DEBUG_KMS("ignoring client monitors config: bad crc");
177 if (status
== MONITORS_CONFIG_UNCHANGED
) {
178 qxl_io_log(qdev
, "config: unchanged\n");
179 DRM_DEBUG_KMS("ignoring client monitors config: unchanged");
183 drm_modeset_lock_all(dev
);
184 qxl_update_offset_props(qdev
);
185 drm_modeset_unlock_all(dev
);
186 if (!drm_helper_hpd_irq_event(dev
)) {
187 /* notify that the monitor configuration changed, to
188 adjust at the arbitrary resolution */
189 drm_kms_helper_hotplug_event(dev
);
193 static int qxl_add_monitors_config_modes(struct drm_connector
*connector
,
197 struct drm_device
*dev
= connector
->dev
;
198 struct qxl_device
*qdev
= dev
->dev_private
;
199 struct qxl_output
*output
= drm_connector_to_qxl_output(connector
);
200 int h
= output
->index
;
201 struct drm_display_mode
*mode
= NULL
;
202 struct qxl_head
*head
;
204 if (!qdev
->monitors_config
)
206 if (h
>= qdev
->monitors_config
->max_allowed
)
208 if (!qdev
->client_monitors_config
)
210 if (h
>= qdev
->client_monitors_config
->count
)
213 head
= &qdev
->client_monitors_config
->heads
[h
];
214 DRM_DEBUG_KMS("head %d is %dx%d\n", h
, head
->width
, head
->height
);
216 mode
= drm_cvt_mode(dev
, head
->width
, head
->height
, 60, false, false,
218 mode
->type
|= DRM_MODE_TYPE_PREFERRED
;
219 mode
->hdisplay
= head
->width
;
220 mode
->vdisplay
= head
->height
;
221 drm_mode_set_name(mode
);
222 *pwidth
= head
->width
;
223 *pheight
= head
->height
;
224 drm_mode_probed_add(connector
, mode
);
225 /* remember the last custom size for mode validation */
226 qdev
->monitors_config_width
= mode
->hdisplay
;
227 qdev
->monitors_config_height
= mode
->vdisplay
;
231 static struct mode_size
{
254 static int qxl_add_common_modes(struct drm_connector
*connector
,
258 struct drm_device
*dev
= connector
->dev
;
259 struct drm_display_mode
*mode
= NULL
;
261 for (i
= 0; i
< ARRAY_SIZE(common_modes
); i
++) {
262 mode
= drm_cvt_mode(dev
, common_modes
[i
].w
, common_modes
[i
].h
,
263 60, false, false, false);
264 if (common_modes
[i
].w
== pwidth
&& common_modes
[i
].h
== pheight
)
265 mode
->type
|= DRM_MODE_TYPE_PREFERRED
;
266 drm_mode_probed_add(connector
, mode
);
271 static void qxl_crtc_atomic_flush(struct drm_crtc
*crtc
,
272 struct drm_crtc_state
*old_crtc_state
)
274 struct drm_device
*dev
= crtc
->dev
;
275 struct drm_pending_vblank_event
*event
;
278 if (crtc
->state
&& crtc
->state
->event
) {
279 event
= crtc
->state
->event
;
280 crtc
->state
->event
= NULL
;
282 spin_lock_irqsave(&dev
->event_lock
, flags
);
283 drm_crtc_send_vblank_event(crtc
, event
);
284 spin_unlock_irqrestore(&dev
->event_lock
, flags
);
288 static void qxl_crtc_destroy(struct drm_crtc
*crtc
)
290 struct qxl_crtc
*qxl_crtc
= to_qxl_crtc(crtc
);
292 drm_crtc_cleanup(crtc
);
296 static const struct drm_crtc_funcs qxl_crtc_funcs
= {
297 .set_config
= drm_atomic_helper_set_config
,
298 .destroy
= qxl_crtc_destroy
,
299 .page_flip
= drm_atomic_helper_page_flip
,
300 .reset
= drm_atomic_helper_crtc_reset
,
301 .atomic_duplicate_state
= drm_atomic_helper_crtc_duplicate_state
,
302 .atomic_destroy_state
= drm_atomic_helper_crtc_destroy_state
,
305 void qxl_user_framebuffer_destroy(struct drm_framebuffer
*fb
)
307 struct qxl_framebuffer
*qxl_fb
= to_qxl_framebuffer(fb
);
309 drm_gem_object_unreference_unlocked(qxl_fb
->obj
);
310 drm_framebuffer_cleanup(fb
);
314 static int qxl_framebuffer_surface_dirty(struct drm_framebuffer
*fb
,
315 struct drm_file
*file_priv
,
316 unsigned flags
, unsigned color
,
317 struct drm_clip_rect
*clips
,
320 /* TODO: vmwgfx where this was cribbed from had locking. Why? */
321 struct qxl_framebuffer
*qxl_fb
= to_qxl_framebuffer(fb
);
322 struct qxl_device
*qdev
= qxl_fb
->base
.dev
->dev_private
;
323 struct drm_clip_rect norect
;
327 drm_modeset_lock_all(fb
->dev
);
329 qobj
= gem_to_qxl_bo(qxl_fb
->obj
);
330 /* if we aren't primary surface ignore this */
331 if (!qobj
->is_primary
) {
332 drm_modeset_unlock_all(fb
->dev
);
339 norect
.x1
= norect
.y1
= 0;
340 norect
.x2
= fb
->width
;
341 norect
.y2
= fb
->height
;
342 } else if (flags
& DRM_MODE_FB_DIRTY_ANNOTATE_COPY
) {
344 inc
= 2; /* skip source rects */
347 qxl_draw_dirty_fb(qdev
, qxl_fb
, qobj
, flags
, color
,
348 clips
, num_clips
, inc
);
350 drm_modeset_unlock_all(fb
->dev
);
355 static const struct drm_framebuffer_funcs qxl_fb_funcs
= {
356 .destroy
= qxl_user_framebuffer_destroy
,
357 .dirty
= qxl_framebuffer_surface_dirty
,
359 * .create_handle = qxl_user_framebuffer_create_handle, */
363 qxl_framebuffer_init(struct drm_device
*dev
,
364 struct qxl_framebuffer
*qfb
,
365 const struct drm_mode_fb_cmd2
*mode_cmd
,
366 struct drm_gem_object
*obj
,
367 const struct drm_framebuffer_funcs
*funcs
)
372 drm_helper_mode_fill_fb_struct(dev
, &qfb
->base
, mode_cmd
);
373 ret
= drm_framebuffer_init(dev
, &qfb
->base
, funcs
);
381 static void qxl_crtc_dpms(struct drm_crtc
*crtc
, int mode
)
385 static bool qxl_crtc_mode_fixup(struct drm_crtc
*crtc
,
386 const struct drm_display_mode
*mode
,
387 struct drm_display_mode
*adjusted_mode
)
389 struct drm_device
*dev
= crtc
->dev
;
390 struct qxl_device
*qdev
= dev
->dev_private
;
392 qxl_io_log(qdev
, "%s: (%d,%d) => (%d,%d)\n",
394 mode
->hdisplay
, mode
->vdisplay
,
395 adjusted_mode
->hdisplay
,
396 adjusted_mode
->vdisplay
);
401 qxl_send_monitors_config(struct qxl_device
*qdev
)
405 BUG_ON(!qdev
->ram_header
->monitors_config
);
407 if (qdev
->monitors_config
->count
== 0) {
408 qxl_io_log(qdev
, "%s: 0 monitors??\n", __func__
);
411 for (i
= 0 ; i
< qdev
->monitors_config
->count
; ++i
) {
412 struct qxl_head
*head
= &qdev
->monitors_config
->heads
[i
];
414 if (head
->y
> 8192 || head
->x
> 8192 ||
415 head
->width
> 8192 || head
->height
> 8192) {
416 DRM_ERROR("head %d wrong: %dx%d+%d+%d\n",
417 i
, head
->width
, head
->height
,
422 qxl_io_monitors_config(qdev
);
425 static void qxl_monitors_config_set(struct qxl_device
*qdev
,
427 unsigned x
, unsigned y
,
428 unsigned width
, unsigned height
,
431 DRM_DEBUG_KMS("%d:%dx%d+%d+%d\n", index
, width
, height
, x
, y
);
432 qdev
->monitors_config
->heads
[index
].x
= x
;
433 qdev
->monitors_config
->heads
[index
].y
= y
;
434 qdev
->monitors_config
->heads
[index
].width
= width
;
435 qdev
->monitors_config
->heads
[index
].height
= height
;
436 qdev
->monitors_config
->heads
[index
].surface_id
= surf_id
;
440 void qxl_mode_set_nofb(struct drm_crtc
*crtc
)
442 struct qxl_device
*qdev
= crtc
->dev
->dev_private
;
443 struct qxl_crtc
*qcrtc
= to_qxl_crtc(crtc
);
444 struct drm_display_mode
*mode
= &crtc
->mode
;
446 DRM_DEBUG("Mode set (%d,%d)\n",
447 mode
->hdisplay
, mode
->vdisplay
);
449 qxl_monitors_config_set(qdev
, qcrtc
->index
, 0, 0,
450 mode
->hdisplay
, mode
->vdisplay
, 0);
454 static void qxl_crtc_commit(struct drm_crtc
*crtc
)
459 static void qxl_crtc_disable(struct drm_crtc
*crtc
)
461 struct qxl_crtc
*qcrtc
= to_qxl_crtc(crtc
);
462 struct qxl_device
*qdev
= crtc
->dev
->dev_private
;
464 qxl_monitors_config_set(qdev
, qcrtc
->index
, 0, 0, 0, 0, 0);
466 qxl_send_monitors_config(qdev
);
469 static const struct drm_crtc_helper_funcs qxl_crtc_helper_funcs
= {
470 .dpms
= qxl_crtc_dpms
,
471 .disable
= qxl_crtc_disable
,
472 .mode_fixup
= qxl_crtc_mode_fixup
,
473 .mode_set_nofb
= qxl_mode_set_nofb
,
474 .commit
= qxl_crtc_commit
,
475 .atomic_flush
= qxl_crtc_atomic_flush
,
478 int qxl_primary_atomic_check(struct drm_plane
*plane
,
479 struct drm_plane_state
*state
)
481 struct qxl_device
*qdev
= plane
->dev
->dev_private
;
482 struct qxl_framebuffer
*qfb
;
485 if (!state
->crtc
|| !state
->fb
)
488 qfb
= to_qxl_framebuffer(state
->fb
);
489 bo
= gem_to_qxl_bo(qfb
->obj
);
491 if (bo
->surf
.stride
* bo
->surf
.height
> qdev
->vram_size
) {
492 DRM_ERROR("Mode doesn't fit in vram size (vgamem)");
499 static void qxl_primary_atomic_update(struct drm_plane
*plane
,
500 struct drm_plane_state
*old_state
)
502 struct qxl_device
*qdev
= plane
->dev
->dev_private
;
503 struct qxl_framebuffer
*qfb
=
504 to_qxl_framebuffer(plane
->state
->fb
);
505 struct qxl_framebuffer
*qfb_old
;
506 struct qxl_bo
*bo
= gem_to_qxl_bo(qfb
->obj
);
507 struct qxl_bo
*bo_old
;
508 struct drm_clip_rect norect
= {
511 .x2
= qfb
->base
.width
,
512 .y2
= qfb
->base
.height
515 if (!old_state
->fb
) {
517 "create primary fb: %dx%d,%d,%d\n",
518 bo
->surf
.width
, bo
->surf
.height
,
519 bo
->surf
.stride
, bo
->surf
.format
);
521 qxl_io_create_primary(qdev
, 0, bo
);
522 bo
->is_primary
= true;
526 qfb_old
= to_qxl_framebuffer(old_state
->fb
);
527 bo_old
= gem_to_qxl_bo(qfb_old
->obj
);
528 bo_old
->is_primary
= false;
531 bo
->is_primary
= true;
532 qxl_draw_dirty_fb(qdev
, qfb
, bo
, 0, 0, &norect
, 1, 1);
535 static void qxl_primary_atomic_disable(struct drm_plane
*plane
,
536 struct drm_plane_state
*old_state
)
538 struct qxl_device
*qdev
= plane
->dev
->dev_private
;
541 { struct qxl_framebuffer
*qfb
=
542 to_qxl_framebuffer(old_state
->fb
);
543 struct qxl_bo
*bo
= gem_to_qxl_bo(qfb
->obj
);
545 qxl_io_destroy_primary(qdev
);
546 bo
->is_primary
= false;
550 int qxl_plane_atomic_check(struct drm_plane
*plane
,
551 struct drm_plane_state
*state
)
556 static void qxl_cursor_atomic_update(struct drm_plane
*plane
,
557 struct drm_plane_state
*old_state
)
559 struct drm_device
*dev
= plane
->dev
;
560 struct qxl_device
*qdev
= dev
->dev_private
;
561 struct drm_framebuffer
*fb
= plane
->state
->fb
;
562 struct qxl_release
*release
;
563 struct qxl_cursor_cmd
*cmd
;
564 struct qxl_cursor
*cursor
;
565 struct drm_gem_object
*obj
;
566 struct qxl_bo
*cursor_bo
, *user_bo
= NULL
;
571 ret
= qxl_alloc_release_reserved(qdev
, sizeof(*cmd
),
572 QXL_RELEASE_CURSOR_CMD
,
577 if (fb
!= old_state
->fb
) {
578 obj
= to_qxl_framebuffer(fb
)->obj
;
579 user_bo
= gem_to_qxl_bo(obj
);
581 /* pinning is done in the prepare/cleanup framevbuffer */
582 ret
= qxl_bo_kmap(user_bo
, &user_ptr
);
584 goto out_free_release
;
586 ret
= qxl_alloc_bo_reserved(qdev
, release
,
587 sizeof(struct qxl_cursor
) + size
,
592 ret
= qxl_release_reserve_list(release
, true);
596 ret
= qxl_bo_kmap(cursor_bo
, (void **)&cursor
);
600 cursor
->header
.unique
= 0;
601 cursor
->header
.type
= SPICE_CURSOR_TYPE_ALPHA
;
602 cursor
->header
.width
= 64;
603 cursor
->header
.height
= 64;
604 cursor
->header
.hot_spot_x
= fb
->hot_x
;
605 cursor
->header
.hot_spot_y
= fb
->hot_y
;
606 cursor
->data_size
= size
;
607 cursor
->chunk
.next_chunk
= 0;
608 cursor
->chunk
.prev_chunk
= 0;
609 cursor
->chunk
.data_size
= size
;
610 memcpy(cursor
->chunk
.data
, user_ptr
, size
);
611 qxl_bo_kunmap(cursor_bo
);
612 qxl_bo_kunmap(user_bo
);
614 cmd
= (struct qxl_cursor_cmd
*) qxl_release_map(qdev
, release
);
615 cmd
->u
.set
.visible
= 1;
616 cmd
->u
.set
.shape
= qxl_bo_physical_address(qdev
,
618 cmd
->type
= QXL_CURSOR_SET
;
621 ret
= qxl_release_reserve_list(release
, true);
623 goto out_free_release
;
625 cmd
= (struct qxl_cursor_cmd
*) qxl_release_map(qdev
, release
);
626 cmd
->type
= QXL_CURSOR_MOVE
;
629 cmd
->u
.position
.x
= plane
->state
->crtc_x
+ fb
->hot_x
;
630 cmd
->u
.position
.y
= plane
->state
->crtc_y
+ fb
->hot_y
;
632 qxl_release_unmap(qdev
, release
, &cmd
->release_info
);
633 qxl_push_cursor_ring_release(qdev
, release
, QXL_CMD_CURSOR
, false);
634 qxl_release_fence_buffer_objects(release
);
639 qxl_release_backoff_reserve_list(release
);
641 qxl_bo_unref(&cursor_bo
);
643 qxl_bo_kunmap(user_bo
);
645 qxl_release_free(qdev
, release
);
650 void qxl_cursor_atomic_disable(struct drm_plane
*plane
,
651 struct drm_plane_state
*old_state
)
653 struct qxl_device
*qdev
= plane
->dev
->dev_private
;
654 struct qxl_release
*release
;
655 struct qxl_cursor_cmd
*cmd
;
658 ret
= qxl_alloc_release_reserved(qdev
, sizeof(*cmd
),
659 QXL_RELEASE_CURSOR_CMD
,
664 ret
= qxl_release_reserve_list(release
, true);
666 qxl_release_free(qdev
, release
);
670 cmd
= (struct qxl_cursor_cmd
*)qxl_release_map(qdev
, release
);
671 cmd
->type
= QXL_CURSOR_HIDE
;
672 qxl_release_unmap(qdev
, release
, &cmd
->release_info
);
674 qxl_push_cursor_ring_release(qdev
, release
, QXL_CMD_CURSOR
, false);
675 qxl_release_fence_buffer_objects(release
);
678 int qxl_plane_prepare_fb(struct drm_plane
*plane
,
679 struct drm_plane_state
*new_state
)
681 struct drm_gem_object
*obj
;
682 struct qxl_bo
*user_bo
;
688 obj
= to_qxl_framebuffer(new_state
->fb
)->obj
;
689 user_bo
= gem_to_qxl_bo(obj
);
691 ret
= qxl_bo_pin(user_bo
, QXL_GEM_DOMAIN_CPU
, NULL
);
698 static void qxl_plane_cleanup_fb(struct drm_plane
*plane
,
699 struct drm_plane_state
*old_state
)
701 struct drm_gem_object
*obj
;
702 struct qxl_bo
*user_bo
;
704 if (!plane
->state
->fb
) {
705 /* we never executed prepare_fb, so there's nothing to
711 obj
= to_qxl_framebuffer(plane
->state
->fb
)->obj
;
712 user_bo
= gem_to_qxl_bo(obj
);
713 qxl_bo_unpin(user_bo
);
716 static const uint32_t qxl_cursor_plane_formats
[] = {
720 static const struct drm_plane_helper_funcs qxl_cursor_helper_funcs
= {
721 .atomic_check
= qxl_plane_atomic_check
,
722 .atomic_update
= qxl_cursor_atomic_update
,
723 .atomic_disable
= qxl_cursor_atomic_disable
,
724 .prepare_fb
= qxl_plane_prepare_fb
,
725 .cleanup_fb
= qxl_plane_cleanup_fb
,
728 static const struct drm_plane_funcs qxl_cursor_plane_funcs
= {
729 .update_plane
= drm_atomic_helper_update_plane
,
730 .disable_plane
= drm_atomic_helper_disable_plane
,
731 .destroy
= drm_primary_helper_destroy
,
732 .reset
= drm_atomic_helper_plane_reset
,
733 .atomic_duplicate_state
= drm_atomic_helper_plane_duplicate_state
,
734 .atomic_destroy_state
= drm_atomic_helper_plane_destroy_state
,
737 static const uint32_t qxl_primary_plane_formats
[] = {
742 static const struct drm_plane_helper_funcs primary_helper_funcs
= {
743 .atomic_check
= qxl_primary_atomic_check
,
744 .atomic_update
= qxl_primary_atomic_update
,
745 .atomic_disable
= qxl_primary_atomic_disable
,
746 .prepare_fb
= qxl_plane_prepare_fb
,
747 .cleanup_fb
= qxl_plane_cleanup_fb
,
750 static const struct drm_plane_funcs qxl_primary_plane_funcs
= {
751 .update_plane
= drm_atomic_helper_update_plane
,
752 .disable_plane
= drm_atomic_helper_disable_plane
,
753 .destroy
= drm_primary_helper_destroy
,
754 .reset
= drm_atomic_helper_plane_reset
,
755 .atomic_duplicate_state
= drm_atomic_helper_plane_duplicate_state
,
756 .atomic_destroy_state
= drm_atomic_helper_plane_destroy_state
,
759 static struct drm_plane
*qxl_create_plane(struct qxl_device
*qdev
,
760 unsigned int possible_crtcs
,
761 enum drm_plane_type type
)
763 const struct drm_plane_helper_funcs
*helper_funcs
= NULL
;
764 struct drm_plane
*plane
;
765 const struct drm_plane_funcs
*funcs
;
766 const uint32_t *formats
;
770 if (type
== DRM_PLANE_TYPE_PRIMARY
) {
771 funcs
= &qxl_primary_plane_funcs
;
772 formats
= qxl_primary_plane_formats
;
773 num_formats
= ARRAY_SIZE(qxl_primary_plane_formats
);
774 helper_funcs
= &primary_helper_funcs
;
775 } else if (type
== DRM_PLANE_TYPE_CURSOR
) {
776 funcs
= &qxl_cursor_plane_funcs
;
777 formats
= qxl_cursor_plane_formats
;
778 helper_funcs
= &qxl_cursor_helper_funcs
;
779 num_formats
= ARRAY_SIZE(qxl_cursor_plane_formats
);
781 return ERR_PTR(-EINVAL
);
784 plane
= kzalloc(sizeof(*plane
), GFP_KERNEL
);
786 return ERR_PTR(-ENOMEM
);
788 err
= drm_universal_plane_init(&qdev
->ddev
, plane
, possible_crtcs
,
789 funcs
, formats
, num_formats
,
794 drm_plane_helper_add(plane
, helper_funcs
);
800 return ERR_PTR(-EINVAL
);
803 static int qdev_crtc_init(struct drm_device
*dev
, int crtc_id
)
805 struct qxl_crtc
*qxl_crtc
;
806 struct drm_plane
*primary
, *cursor
;
807 struct qxl_device
*qdev
= dev
->dev_private
;
810 qxl_crtc
= kzalloc(sizeof(struct qxl_crtc
), GFP_KERNEL
);
814 primary
= qxl_create_plane(qdev
, 1 << crtc_id
, DRM_PLANE_TYPE_PRIMARY
);
815 if (IS_ERR(primary
)) {
820 cursor
= qxl_create_plane(qdev
, 1 << crtc_id
, DRM_PLANE_TYPE_CURSOR
);
821 if (IS_ERR(cursor
)) {
826 r
= drm_crtc_init_with_planes(dev
, &qxl_crtc
->base
, primary
, cursor
,
827 &qxl_crtc_funcs
, NULL
);
831 qxl_crtc
->index
= crtc_id
;
832 drm_crtc_helper_add(&qxl_crtc
->base
, &qxl_crtc_helper_funcs
);
836 drm_plane_cleanup(cursor
);
839 drm_plane_cleanup(primary
);
846 static void qxl_enc_dpms(struct drm_encoder
*encoder
, int mode
)
851 static void qxl_enc_prepare(struct drm_encoder
*encoder
)
856 static void qxl_write_monitors_config_for_encoder(struct qxl_device
*qdev
,
857 struct drm_encoder
*encoder
)
860 struct qxl_output
*output
= drm_encoder_to_qxl_output(encoder
);
861 struct qxl_head
*head
;
862 struct drm_display_mode
*mode
;
865 /* TODO: ugly, do better */
867 if (!qdev
->monitors_config
||
868 qdev
->monitors_config
->max_allowed
<= i
) {
870 "head number too large or missing monitors config: %p, %d",
871 qdev
->monitors_config
,
872 qdev
->monitors_config
?
873 qdev
->monitors_config
->max_allowed
: -1);
876 if (!encoder
->crtc
) {
877 DRM_ERROR("missing crtc on encoder %p\n", encoder
);
881 DRM_DEBUG("missing for multiple monitors: no head holes\n");
882 head
= &qdev
->monitors_config
->heads
[i
];
884 if (encoder
->crtc
->enabled
) {
885 mode
= &encoder
->crtc
->mode
;
886 head
->width
= mode
->hdisplay
;
887 head
->height
= mode
->vdisplay
;
888 head
->x
= encoder
->crtc
->x
;
889 head
->y
= encoder
->crtc
->y
;
890 if (qdev
->monitors_config
->count
< i
+ 1)
891 qdev
->monitors_config
->count
= i
+ 1;
898 DRM_DEBUG_KMS("setting head %d to +%d+%d %dx%d out of %d\n",
899 i
, head
->x
, head
->y
, head
->width
, head
->height
, qdev
->monitors_config
->count
);
901 /* TODO - somewhere else to call this for multiple monitors
902 * (config_commit?) */
903 qxl_send_monitors_config(qdev
);
906 static void qxl_enc_commit(struct drm_encoder
*encoder
)
908 struct qxl_device
*qdev
= encoder
->dev
->dev_private
;
910 qxl_write_monitors_config_for_encoder(qdev
, encoder
);
914 static void qxl_enc_mode_set(struct drm_encoder
*encoder
,
915 struct drm_display_mode
*mode
,
916 struct drm_display_mode
*adjusted_mode
)
921 static int qxl_conn_get_modes(struct drm_connector
*connector
)
923 unsigned pwidth
= 1024;
924 unsigned pheight
= 768;
927 ret
= qxl_add_monitors_config_modes(connector
, &pwidth
, &pheight
);
930 ret
+= qxl_add_common_modes(connector
, pwidth
, pheight
);
934 static int qxl_conn_mode_valid(struct drm_connector
*connector
,
935 struct drm_display_mode
*mode
)
937 struct drm_device
*ddev
= connector
->dev
;
938 struct qxl_device
*qdev
= ddev
->dev_private
;
941 /* TODO: is this called for user defined modes? (xrandr --add-mode)
942 * TODO: check that the mode fits in the framebuffer */
944 if(qdev
->monitors_config_width
== mode
->hdisplay
&&
945 qdev
->monitors_config_height
== mode
->vdisplay
)
948 for (i
= 0; i
< ARRAY_SIZE(common_modes
); i
++) {
949 if (common_modes
[i
].w
== mode
->hdisplay
&& common_modes
[i
].h
== mode
->vdisplay
)
955 static struct drm_encoder
*qxl_best_encoder(struct drm_connector
*connector
)
957 struct qxl_output
*qxl_output
=
958 drm_connector_to_qxl_output(connector
);
961 return &qxl_output
->enc
;
965 static const struct drm_encoder_helper_funcs qxl_enc_helper_funcs
= {
966 .dpms
= qxl_enc_dpms
,
967 .prepare
= qxl_enc_prepare
,
968 .mode_set
= qxl_enc_mode_set
,
969 .commit
= qxl_enc_commit
,
972 static const struct drm_connector_helper_funcs qxl_connector_helper_funcs
= {
973 .get_modes
= qxl_conn_get_modes
,
974 .mode_valid
= qxl_conn_mode_valid
,
975 .best_encoder
= qxl_best_encoder
,
978 static enum drm_connector_status
qxl_conn_detect(
979 struct drm_connector
*connector
,
982 struct qxl_output
*output
=
983 drm_connector_to_qxl_output(connector
);
984 struct drm_device
*ddev
= connector
->dev
;
985 struct qxl_device
*qdev
= ddev
->dev_private
;
986 bool connected
= false;
988 /* The first monitor is always connected */
989 if (!qdev
->client_monitors_config
) {
990 if (output
->index
== 0)
993 connected
= qdev
->client_monitors_config
->count
> output
->index
&&
994 qxl_head_enabled(&qdev
->client_monitors_config
->heads
[output
->index
]);
996 DRM_DEBUG("#%d connected: %d\n", output
->index
, connected
);
998 qxl_monitors_config_set(qdev
, output
->index
, 0, 0, 0, 0, 0);
1000 return connected
? connector_status_connected
1001 : connector_status_disconnected
;
1004 static int qxl_conn_set_property(struct drm_connector
*connector
,
1005 struct drm_property
*property
,
1012 static void qxl_conn_destroy(struct drm_connector
*connector
)
1014 struct qxl_output
*qxl_output
=
1015 drm_connector_to_qxl_output(connector
);
1017 drm_connector_unregister(connector
);
1018 drm_connector_cleanup(connector
);
1022 static const struct drm_connector_funcs qxl_connector_funcs
= {
1023 .dpms
= drm_helper_connector_dpms
,
1024 .detect
= qxl_conn_detect
,
1025 .fill_modes
= drm_helper_probe_single_connector_modes
,
1026 .set_property
= qxl_conn_set_property
,
1027 .destroy
= qxl_conn_destroy
,
1028 .reset
= drm_atomic_helper_connector_reset
,
1029 .atomic_duplicate_state
= drm_atomic_helper_connector_duplicate_state
,
1030 .atomic_destroy_state
= drm_atomic_helper_connector_destroy_state
,
1033 static void qxl_enc_destroy(struct drm_encoder
*encoder
)
1035 drm_encoder_cleanup(encoder
);
1038 static const struct drm_encoder_funcs qxl_enc_funcs
= {
1039 .destroy
= qxl_enc_destroy
,
1042 static int qxl_mode_create_hotplug_mode_update_property(struct qxl_device
*qdev
)
1044 if (qdev
->hotplug_mode_update_property
)
1047 qdev
->hotplug_mode_update_property
=
1048 drm_property_create_range(&qdev
->ddev
, DRM_MODE_PROP_IMMUTABLE
,
1049 "hotplug_mode_update", 0, 1);
1054 static int qdev_output_init(struct drm_device
*dev
, int num_output
)
1056 struct qxl_device
*qdev
= dev
->dev_private
;
1057 struct qxl_output
*qxl_output
;
1058 struct drm_connector
*connector
;
1059 struct drm_encoder
*encoder
;
1061 qxl_output
= kzalloc(sizeof(struct qxl_output
), GFP_KERNEL
);
1065 qxl_output
->index
= num_output
;
1067 connector
= &qxl_output
->base
;
1068 encoder
= &qxl_output
->enc
;
1069 drm_connector_init(dev
, &qxl_output
->base
,
1070 &qxl_connector_funcs
, DRM_MODE_CONNECTOR_VIRTUAL
);
1072 drm_encoder_init(dev
, &qxl_output
->enc
, &qxl_enc_funcs
,
1073 DRM_MODE_ENCODER_VIRTUAL
, NULL
);
1075 /* we get HPD via client monitors config */
1076 connector
->polled
= DRM_CONNECTOR_POLL_HPD
;
1077 encoder
->possible_crtcs
= 1 << num_output
;
1078 drm_mode_connector_attach_encoder(&qxl_output
->base
,
1080 drm_encoder_helper_add(encoder
, &qxl_enc_helper_funcs
);
1081 drm_connector_helper_add(connector
, &qxl_connector_helper_funcs
);
1083 drm_object_attach_property(&connector
->base
,
1084 qdev
->hotplug_mode_update_property
, 0);
1085 drm_object_attach_property(&connector
->base
,
1086 dev
->mode_config
.suggested_x_property
, 0);
1087 drm_object_attach_property(&connector
->base
,
1088 dev
->mode_config
.suggested_y_property
, 0);
1092 static struct drm_framebuffer
*
1093 qxl_user_framebuffer_create(struct drm_device
*dev
,
1094 struct drm_file
*file_priv
,
1095 const struct drm_mode_fb_cmd2
*mode_cmd
)
1097 struct drm_gem_object
*obj
;
1098 struct qxl_framebuffer
*qxl_fb
;
1101 obj
= drm_gem_object_lookup(file_priv
, mode_cmd
->handles
[0]);
1105 qxl_fb
= kzalloc(sizeof(*qxl_fb
), GFP_KERNEL
);
1109 ret
= qxl_framebuffer_init(dev
, qxl_fb
, mode_cmd
, obj
, &qxl_fb_funcs
);
1112 drm_gem_object_unreference_unlocked(obj
);
1116 return &qxl_fb
->base
;
1119 static const struct drm_mode_config_funcs qxl_mode_funcs
= {
1120 .fb_create
= qxl_user_framebuffer_create
,
1121 .atomic_check
= drm_atomic_helper_check
,
1122 .atomic_commit
= drm_atomic_helper_commit
,
1125 int qxl_create_monitors_object(struct qxl_device
*qdev
)
1128 struct drm_gem_object
*gobj
;
1129 int max_allowed
= qxl_num_crtc
;
1130 int monitors_config_size
= sizeof(struct qxl_monitors_config
) +
1131 max_allowed
* sizeof(struct qxl_head
);
1133 ret
= qxl_gem_object_create(qdev
, monitors_config_size
, 0,
1134 QXL_GEM_DOMAIN_VRAM
,
1135 false, false, NULL
, &gobj
);
1137 DRM_ERROR("%s: failed to create gem ret=%d\n", __func__
, ret
);
1140 qdev
->monitors_config_bo
= gem_to_qxl_bo(gobj
);
1142 ret
= qxl_bo_pin(qdev
->monitors_config_bo
, QXL_GEM_DOMAIN_VRAM
, NULL
);
1146 qxl_bo_kmap(qdev
->monitors_config_bo
, NULL
);
1148 qdev
->monitors_config
= qdev
->monitors_config_bo
->kptr
;
1149 qdev
->ram_header
->monitors_config
=
1150 qxl_bo_physical_address(qdev
, qdev
->monitors_config_bo
, 0);
1152 memset(qdev
->monitors_config
, 0, monitors_config_size
);
1153 qdev
->monitors_config
->max_allowed
= max_allowed
;
1157 int qxl_destroy_monitors_object(struct qxl_device
*qdev
)
1161 qdev
->monitors_config
= NULL
;
1162 qdev
->ram_header
->monitors_config
= 0;
1164 qxl_bo_kunmap(qdev
->monitors_config_bo
);
1165 ret
= qxl_bo_unpin(qdev
->monitors_config_bo
);
1169 qxl_bo_unref(&qdev
->monitors_config_bo
);
1173 int qxl_modeset_init(struct qxl_device
*qdev
)
1178 drm_mode_config_init(&qdev
->ddev
);
1180 ret
= qxl_create_monitors_object(qdev
);
1184 qdev
->ddev
.mode_config
.funcs
= (void *)&qxl_mode_funcs
;
1186 /* modes will be validated against the framebuffer size */
1187 qdev
->ddev
.mode_config
.min_width
= 0;
1188 qdev
->ddev
.mode_config
.min_height
= 0;
1189 qdev
->ddev
.mode_config
.max_width
= 8192;
1190 qdev
->ddev
.mode_config
.max_height
= 8192;
1192 qdev
->ddev
.mode_config
.fb_base
= qdev
->vram_base
;
1194 drm_mode_create_suggested_offset_properties(&qdev
->ddev
);
1195 qxl_mode_create_hotplug_mode_update_property(qdev
);
1197 for (i
= 0 ; i
< qxl_num_crtc
; ++i
) {
1198 qdev_crtc_init(&qdev
->ddev
, i
);
1199 qdev_output_init(&qdev
->ddev
, i
);
1202 qxl_display_read_client_monitors_config(qdev
);
1203 qdev
->mode_info
.mode_config_initialized
= true;
1205 drm_mode_config_reset(&qdev
->ddev
);
1207 /* primary surface must be created by this point, to allow
1208 * issuing command queue commands and having them read by
1210 qxl_fbdev_init(qdev
);
1214 void qxl_modeset_fini(struct qxl_device
*qdev
)
1216 qxl_fbdev_fini(qdev
);
1218 qxl_destroy_monitors_object(qdev
);
1219 if (qdev
->mode_info
.mode_config_initialized
) {
1220 drm_mode_config_cleanup(&qdev
->ddev
);
1221 qdev
->mode_info
.mode_config_initialized
= false;