blob: 5809c6c6e7b7f81ecd2c9fc520319d6d2dd7748a [file] [log] [blame]
Dave Airlief64122c2013-02-25 14:47:55 +10001/*
2 * Copyright 2013 Red Hat Inc.
3 *
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:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
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.
21 *
22 * Authors: Dave Airlie
23 * Alon Levy
24 */
25
Randy Dunlapc5416d662013-12-20 10:58:15 -080026#include <linux/crc32.h>
Masahiro Yamadaedaf4922017-04-24 13:50:30 +090027#include <drm/drm_crtc_helper.h>
Daniel Vetter3cb9ae42014-10-29 10:03:57 +010028#include <drm/drm_plane_helper.h>
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -030029#include <drm/drm_atomic_helper.h>
Gabriel Krisman Bertazi10a0bd82017-02-27 17:43:24 -030030#include <drm/drm_atomic.h>
Dave Airlief64122c2013-02-25 14:47:55 +100031
Masahiro Yamadaedaf4922017-04-24 13:50:30 +090032#include "qxl_drv.h"
33#include "qxl_object.h"
34
Dave Airlie07f8d9b2013-07-02 06:37:13 +010035static bool qxl_head_enabled(struct qxl_head *head)
36{
37 return head->width && head->height;
38}
39
Christophe Fergeaue4a76442016-11-08 10:12:03 +010040static void qxl_alloc_client_monitors_config(struct qxl_device *qdev, unsigned count)
Dave Airlief64122c2013-02-25 14:47:55 +100041{
42 if (qdev->client_monitors_config &&
43 count > qdev->client_monitors_config->count) {
44 kfree(qdev->client_monitors_config);
Dave Airlie62c8ba72013-04-16 13:36:00 +100045 qdev->client_monitors_config = NULL;
Dave Airlief64122c2013-02-25 14:47:55 +100046 }
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);
Gerd Hoffmann735581a2018-04-20 09:19:01 +020051 if (!qdev->client_monitors_config)
Dave Airlief64122c2013-02-25 14:47:55 +100052 return;
Dave Airlief64122c2013-02-25 14:47:55 +100053 }
54 qdev->client_monitors_config->count = count;
55}
56
Christophe Fergeau9e3b3172016-11-08 10:12:08 +010057enum {
58 MONITORS_CONFIG_MODIFIED,
59 MONITORS_CONFIG_UNCHANGED,
60 MONITORS_CONFIG_BAD_CRC,
61};
62
Dave Airlief64122c2013-02-25 14:47:55 +100063static int qxl_display_copy_rom_client_monitors_config(struct qxl_device *qdev)
64{
65 int i;
66 int num_monitors;
67 uint32_t crc;
Christophe Fergeau9e3b3172016-11-08 10:12:08 +010068 int status = MONITORS_CONFIG_UNCHANGED;
Dave Airlief64122c2013-02-25 14:47:55 +100069
Dave Airlief64122c2013-02-25 14:47:55 +100070 num_monitors = qdev->rom->client_monitors_config.count;
71 crc = crc32(0, (const uint8_t *)&qdev->rom->client_monitors_config,
72 sizeof(qdev->rom->client_monitors_config));
Gerd Hoffmann735581a2018-04-20 09:19:01 +020073 if (crc != qdev->rom->client_monitors_config_crc)
Christophe Fergeau9e3b3172016-11-08 10:12:08 +010074 return MONITORS_CONFIG_BAD_CRC;
Gerd Hoffmannc50fad82017-03-01 11:12:33 +010075 if (!num_monitors) {
76 DRM_DEBUG_KMS("no client monitors configured\n");
77 return status;
78 }
Dave Airlief64122c2013-02-25 14:47:55 +100079 if (num_monitors > qdev->monitors_config->max_allowed) {
Dave Airlie5b8788c2013-07-01 14:14:38 +100080 DRM_DEBUG_KMS("client monitors list will be truncated: %d < %d\n",
81 qdev->monitors_config->max_allowed, num_monitors);
Dave Airlief64122c2013-02-25 14:47:55 +100082 num_monitors = qdev->monitors_config->max_allowed;
83 } else {
84 num_monitors = qdev->rom->client_monitors_config.count;
85 }
Christophe Fergeau9e3b3172016-11-08 10:12:08 +010086 if (qdev->client_monitors_config
87 && (num_monitors != qdev->client_monitors_config->count)) {
88 status = MONITORS_CONFIG_MODIFIED;
89 }
Dave Airlief64122c2013-02-25 14:47:55 +100090 qxl_alloc_client_monitors_config(qdev, num_monitors);
91 /* we copy max from the client but it isn't used */
92 qdev->client_monitors_config->max_allowed =
93 qdev->monitors_config->max_allowed;
94 for (i = 0 ; i < qdev->client_monitors_config->count ; ++i) {
95 struct qxl_urect *c_rect =
96 &qdev->rom->client_monitors_config.heads[i];
97 struct qxl_head *client_head =
98 &qdev->client_monitors_config->heads[i];
Christophe Fergeau9e3b3172016-11-08 10:12:08 +010099 if (client_head->x != c_rect->left) {
100 client_head->x = c_rect->left;
101 status = MONITORS_CONFIG_MODIFIED;
102 }
103 if (client_head->y != c_rect->top) {
104 client_head->y = c_rect->top;
105 status = MONITORS_CONFIG_MODIFIED;
106 }
107 if (client_head->width != c_rect->right - c_rect->left) {
108 client_head->width = c_rect->right - c_rect->left;
109 status = MONITORS_CONFIG_MODIFIED;
110 }
111 if (client_head->height != c_rect->bottom - c_rect->top) {
112 client_head->height = c_rect->bottom - c_rect->top;
113 status = MONITORS_CONFIG_MODIFIED;
114 }
115 if (client_head->surface_id != 0) {
116 client_head->surface_id = 0;
117 status = MONITORS_CONFIG_MODIFIED;
118 }
119 if (client_head->id != i) {
120 client_head->id = i;
121 status = MONITORS_CONFIG_MODIFIED;
122 }
123 if (client_head->flags != 0) {
124 client_head->flags = 0;
125 status = MONITORS_CONFIG_MODIFIED;
126 }
Dave Airlie07f8d9b2013-07-02 06:37:13 +0100127 DRM_DEBUG_KMS("read %dx%d+%d+%d\n", client_head->width, client_head->height,
128 client_head->x, client_head->y);
Dave Airlief64122c2013-02-25 14:47:55 +1000129 }
Christophe Fergeau9e3b3172016-11-08 10:12:08 +0100130
131 return status;
Dave Airlief64122c2013-02-25 14:47:55 +1000132}
133
Dave Airlie7dea0942014-10-28 11:28:44 +1000134static void qxl_update_offset_props(struct qxl_device *qdev)
135{
Gabriel Krisman Bertazicbdded72017-01-26 23:05:48 -0200136 struct drm_device *dev = &qdev->ddev;
Dave Airlie7dea0942014-10-28 11:28:44 +1000137 struct drm_connector *connector;
138 struct qxl_output *output;
139 struct qxl_head *head;
140
141 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
142 output = drm_connector_to_qxl_output(connector);
143
144 head = &qdev->client_monitors_config->heads[output->index];
145
146 drm_object_property_set_value(&connector->base,
147 dev->mode_config.suggested_x_property, head->x);
148 drm_object_property_set_value(&connector->base,
149 dev->mode_config.suggested_y_property, head->y);
150 }
151}
152
Dave Airlief64122c2013-02-25 14:47:55 +1000153void qxl_display_read_client_monitors_config(struct qxl_device *qdev)
154{
Gabriel Krisman Bertazicbdded72017-01-26 23:05:48 -0200155 struct drm_device *dev = &qdev->ddev;
Gerd Hoffmann90621552017-03-01 11:12:32 +0100156 int status, retries;
Christophe Fergeau9e3b3172016-11-08 10:12:08 +0100157
Gerd Hoffmann90621552017-03-01 11:12:32 +0100158 for (retries = 0; retries < 10; retries++) {
Christophe Fergeau9e3b3172016-11-08 10:12:08 +0100159 status = qxl_display_copy_rom_client_monitors_config(qdev);
Gerd Hoffmann90621552017-03-01 11:12:32 +0100160 if (status != MONITORS_CONFIG_BAD_CRC)
161 break;
162 udelay(5);
163 }
164 if (status == MONITORS_CONFIG_BAD_CRC) {
Gerd Hoffmann90621552017-03-01 11:12:32 +0100165 DRM_DEBUG_KMS("ignoring client monitors config: bad crc");
166 return;
Christophe Fergeau9e3b3172016-11-08 10:12:08 +0100167 }
168 if (status == MONITORS_CONFIG_UNCHANGED) {
Gerd Hoffmann90621552017-03-01 11:12:32 +0100169 DRM_DEBUG_KMS("ignoring client monitors config: unchanged");
Christophe Fergeau9e3b3172016-11-08 10:12:08 +0100170 return;
Dave Airlief64122c2013-02-25 14:47:55 +1000171 }
Marc-André Lureau4fdb0862013-10-18 16:11:29 +0200172
Dave Airlie7dea0942014-10-28 11:28:44 +1000173 drm_modeset_lock_all(dev);
174 qxl_update_offset_props(qdev);
175 drm_modeset_unlock_all(dev);
Gabriel Krisman Bertazicbdded72017-01-26 23:05:48 -0200176 if (!drm_helper_hpd_irq_event(dev)) {
Marc-André Lureau4fdb0862013-10-18 16:11:29 +0200177 /* notify that the monitor configuration changed, to
178 adjust at the arbitrary resolution */
Gabriel Krisman Bertazicbdded72017-01-26 23:05:48 -0200179 drm_kms_helper_hotplug_event(dev);
Marc-André Lureau4fdb0862013-10-18 16:11:29 +0200180 }
Dave Airlief64122c2013-02-25 14:47:55 +1000181}
182
Marc-André Lureaub0807422013-10-18 16:11:31 +0200183static int qxl_add_monitors_config_modes(struct drm_connector *connector,
184 unsigned *pwidth,
185 unsigned *pheight)
Dave Airlief64122c2013-02-25 14:47:55 +1000186{
187 struct drm_device *dev = connector->dev;
188 struct qxl_device *qdev = dev->dev_private;
189 struct qxl_output *output = drm_connector_to_qxl_output(connector);
190 int h = output->index;
191 struct drm_display_mode *mode = NULL;
192 struct qxl_head *head;
193
Gerd Hoffmann2d856f942017-03-01 11:12:34 +0100194 if (!qdev->monitors_config)
195 return 0;
196 if (h >= qdev->monitors_config->max_allowed)
197 return 0;
Dave Airlie07f8d9b2013-07-02 06:37:13 +0100198 if (!qdev->client_monitors_config)
Dave Airlief64122c2013-02-25 14:47:55 +1000199 return 0;
Gerd Hoffmann2d856f942017-03-01 11:12:34 +0100200 if (h >= qdev->client_monitors_config->count)
201 return 0;
202
Dave Airlie07f8d9b2013-07-02 06:37:13 +0100203 head = &qdev->client_monitors_config->heads[h];
Gerd Hoffmann2d856f942017-03-01 11:12:34 +0100204 DRM_DEBUG_KMS("head %d is %dx%d\n", h, head->width, head->height);
Dave Airlief64122c2013-02-25 14:47:55 +1000205
206 mode = drm_cvt_mode(dev, head->width, head->height, 60, false, false,
207 false);
208 mode->type |= DRM_MODE_TYPE_PREFERRED;
Christophe Fergeauff996e72016-11-08 10:12:09 +0100209 mode->hdisplay = head->width;
210 mode->vdisplay = head->height;
211 drm_mode_set_name(mode);
Marc-André Lureaub0807422013-10-18 16:11:31 +0200212 *pwidth = head->width;
213 *pheight = head->height;
Dave Airlief64122c2013-02-25 14:47:55 +1000214 drm_mode_probed_add(connector, mode);
Jonathon Jongsmabd3e1c72015-08-20 14:04:32 -0500215 /* remember the last custom size for mode validation */
216 qdev->monitors_config_width = mode->hdisplay;
217 qdev->monitors_config_height = mode->vdisplay;
Dave Airlief64122c2013-02-25 14:47:55 +1000218 return 1;
219}
220
Jonathon Jongsmabd3e1c72015-08-20 14:04:32 -0500221static struct mode_size {
222 int w;
223 int h;
224} common_modes[] = {
225 { 640, 480},
226 { 720, 480},
227 { 800, 600},
228 { 848, 480},
229 {1024, 768},
230 {1152, 768},
231 {1280, 720},
232 {1280, 800},
233 {1280, 854},
234 {1280, 960},
235 {1280, 1024},
236 {1440, 900},
237 {1400, 1050},
238 {1680, 1050},
239 {1600, 1200},
240 {1920, 1080},
241 {1920, 1200}
242};
243
Marc-André Lureaub0807422013-10-18 16:11:31 +0200244static int qxl_add_common_modes(struct drm_connector *connector,
245 unsigned pwidth,
246 unsigned pheight)
Dave Airlief64122c2013-02-25 14:47:55 +1000247{
248 struct drm_device *dev = connector->dev;
249 struct drm_display_mode *mode = NULL;
250 int i;
Dave Airlief64122c2013-02-25 14:47:55 +1000251 for (i = 0; i < ARRAY_SIZE(common_modes); i++) {
Dave Airlief64122c2013-02-25 14:47:55 +1000252 mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h,
253 60, false, false, false);
Marc-André Lureaub0807422013-10-18 16:11:31 +0200254 if (common_modes[i].w == pwidth && common_modes[i].h == pheight)
Dave Airlief64122c2013-02-25 14:47:55 +1000255 mode->type |= DRM_MODE_TYPE_PREFERRED;
256 drm_mode_probed_add(connector, mode);
257 }
258 return i - 1;
259}
260
Gabriel Krisman Bertazic2ff6632017-02-27 17:43:20 -0300261static void qxl_crtc_atomic_flush(struct drm_crtc *crtc,
262 struct drm_crtc_state *old_crtc_state)
263{
264 struct drm_device *dev = crtc->dev;
265 struct drm_pending_vblank_event *event;
266 unsigned long flags;
267
268 if (crtc->state && crtc->state->event) {
269 event = crtc->state->event;
270 crtc->state->event = NULL;
271
272 spin_lock_irqsave(&dev->event_lock, flags);
273 drm_crtc_send_vblank_event(crtc, event);
274 spin_unlock_irqrestore(&dev->event_lock, flags);
275 }
276}
277
Dave Airlief64122c2013-02-25 14:47:55 +1000278static void qxl_crtc_destroy(struct drm_crtc *crtc)
279{
280 struct qxl_crtc *qxl_crtc = to_qxl_crtc(crtc);
281
Ray Strode94280882017-11-27 16:50:10 -0500282 qxl_bo_unref(&qxl_crtc->cursor_bo);
Dave Airlief64122c2013-02-25 14:47:55 +1000283 drm_crtc_cleanup(crtc);
284 kfree(qxl_crtc);
285}
286
Dave Airlief64122c2013-02-25 14:47:55 +1000287static const struct drm_crtc_funcs qxl_crtc_funcs = {
Gabriel Krisman Bertazibc8a00d2017-02-27 17:43:26 -0300288 .set_config = drm_atomic_helper_set_config,
Dave Airlief64122c2013-02-25 14:47:55 +1000289 .destroy = qxl_crtc_destroy,
Gabriel Krisman Bertazi9973c872017-02-27 17:43:27 -0300290 .page_flip = drm_atomic_helper_page_flip,
Gabriel Krisman Bertazi9ade8b92017-02-27 17:43:23 -0300291 .reset = drm_atomic_helper_crtc_reset,
292 .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
293 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
Dave Airlief64122c2013-02-25 14:47:55 +1000294};
295
Noralf Trønnes6819c3c2016-04-28 17:18:36 +0200296void qxl_user_framebuffer_destroy(struct drm_framebuffer *fb)
Dave Airlief64122c2013-02-25 14:47:55 +1000297{
298 struct qxl_framebuffer *qxl_fb = to_qxl_framebuffer(fb);
Gerd Hoffmann62676d12017-10-19 08:21:50 +0200299 struct qxl_bo *bo = gem_to_qxl_bo(qxl_fb->obj);
Dave Airlief64122c2013-02-25 14:47:55 +1000300
Gerd Hoffmann62676d12017-10-19 08:21:50 +0200301 WARN_ON(bo->shadow);
Santha Meena Ramamoorthy2793c1d2018-03-20 11:29:27 -0700302 drm_gem_object_put_unlocked(qxl_fb->obj);
Dave Airlief64122c2013-02-25 14:47:55 +1000303 drm_framebuffer_cleanup(fb);
304 kfree(qxl_fb);
305}
306
Dave Airlie6d01f1f2013-04-16 13:24:25 +1000307static int qxl_framebuffer_surface_dirty(struct drm_framebuffer *fb,
308 struct drm_file *file_priv,
309 unsigned flags, unsigned color,
310 struct drm_clip_rect *clips,
311 unsigned num_clips)
Dave Airlief64122c2013-02-25 14:47:55 +1000312{
313 /* TODO: vmwgfx where this was cribbed from had locking. Why? */
314 struct qxl_framebuffer *qxl_fb = to_qxl_framebuffer(fb);
315 struct qxl_device *qdev = qxl_fb->base.dev->dev_private;
316 struct drm_clip_rect norect;
317 struct qxl_bo *qobj;
318 int inc = 1;
319
Ville Syrjälä73e9efd2013-12-04 14:13:58 +0200320 drm_modeset_lock_all(fb->dev);
321
Dave Airlief64122c2013-02-25 14:47:55 +1000322 qobj = gem_to_qxl_bo(qxl_fb->obj);
Dave Airlieb2b44652013-05-13 12:48:40 +1000323 /* if we aren't primary surface ignore this */
Ville Syrjälä73e9efd2013-12-04 14:13:58 +0200324 if (!qobj->is_primary) {
325 drm_modeset_unlock_all(fb->dev);
Dave Airlieb2b44652013-05-13 12:48:40 +1000326 return 0;
Ville Syrjälä73e9efd2013-12-04 14:13:58 +0200327 }
Dave Airlieb2b44652013-05-13 12:48:40 +1000328
Dave Airlief64122c2013-02-25 14:47:55 +1000329 if (!num_clips) {
330 num_clips = 1;
331 clips = &norect;
332 norect.x1 = norect.y1 = 0;
333 norect.x2 = fb->width;
334 norect.y2 = fb->height;
335 } else if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) {
336 num_clips /= 2;
337 inc = 2; /* skip source rects */
338 }
339
340 qxl_draw_dirty_fb(qdev, qxl_fb, qobj, flags, color,
341 clips, num_clips, inc);
Ville Syrjälä73e9efd2013-12-04 14:13:58 +0200342
343 drm_modeset_unlock_all(fb->dev);
344
Dave Airlief64122c2013-02-25 14:47:55 +1000345 return 0;
346}
347
348static const struct drm_framebuffer_funcs qxl_fb_funcs = {
349 .destroy = qxl_user_framebuffer_destroy,
350 .dirty = qxl_framebuffer_surface_dirty,
351/* TODO?
352 * .create_handle = qxl_user_framebuffer_create_handle, */
353};
354
355int
356qxl_framebuffer_init(struct drm_device *dev,
357 struct qxl_framebuffer *qfb,
Ville Syrjälä1eb83452015-11-11 19:11:29 +0200358 const struct drm_mode_fb_cmd2 *mode_cmd,
Noralf Trønnes6819c3c2016-04-28 17:18:36 +0200359 struct drm_gem_object *obj,
360 const struct drm_framebuffer_funcs *funcs)
Dave Airlief64122c2013-02-25 14:47:55 +1000361{
362 int ret;
363
364 qfb->obj = obj;
Ville Syrjälä53609432016-11-18 21:52:51 +0200365 drm_helper_mode_fill_fb_struct(dev, &qfb->base, mode_cmd);
Noralf Trønnes6819c3c2016-04-28 17:18:36 +0200366 ret = drm_framebuffer_init(dev, &qfb->base, funcs);
Dave Airlief64122c2013-02-25 14:47:55 +1000367 if (ret) {
368 qfb->obj = NULL;
369 return ret;
370 }
Dave Airlief64122c2013-02-25 14:47:55 +1000371 return 0;
372}
373
Dave Airlief64122c2013-02-25 14:47:55 +1000374static bool qxl_crtc_mode_fixup(struct drm_crtc *crtc,
375 const struct drm_display_mode *mode,
376 struct drm_display_mode *adjusted_mode)
377{
Dave Airlief64122c2013-02-25 14:47:55 +1000378 return true;
379}
380
Christophe Fergeaue4a76442016-11-08 10:12:03 +0100381static void
Dave Airlief64122c2013-02-25 14:47:55 +1000382qxl_send_monitors_config(struct qxl_device *qdev)
383{
384 int i;
385
386 BUG_ON(!qdev->ram_header->monitors_config);
387
Gerd Hoffmann735581a2018-04-20 09:19:01 +0200388 if (qdev->monitors_config->count == 0)
Dave Airlief64122c2013-02-25 14:47:55 +1000389 return;
Gerd Hoffmann735581a2018-04-20 09:19:01 +0200390
Dave Airlief64122c2013-02-25 14:47:55 +1000391 for (i = 0 ; i < qdev->monitors_config->count ; ++i) {
392 struct qxl_head *head = &qdev->monitors_config->heads[i];
393
Dave Airlie07f8d9b2013-07-02 06:37:13 +0100394 if (head->y > 8192 || head->x > 8192 ||
Dave Airlief64122c2013-02-25 14:47:55 +1000395 head->width > 8192 || head->height > 8192) {
396 DRM_ERROR("head %d wrong: %dx%d+%d+%d\n",
397 i, head->width, head->height,
398 head->x, head->y);
399 return;
400 }
401 }
402 qxl_io_monitors_config(qdev);
403}
404
Dave Airlie07f8d9b2013-07-02 06:37:13 +0100405static void qxl_monitors_config_set(struct qxl_device *qdev,
406 int index,
407 unsigned x, unsigned y,
408 unsigned width, unsigned height,
409 unsigned surf_id)
Dave Airlief64122c2013-02-25 14:47:55 +1000410{
Dave Airlie07f8d9b2013-07-02 06:37:13 +0100411 DRM_DEBUG_KMS("%d:%dx%d+%d+%d\n", index, width, height, x, y);
412 qdev->monitors_config->heads[index].x = x;
413 qdev->monitors_config->heads[index].y = y;
414 qdev->monitors_config->heads[index].width = width;
415 qdev->monitors_config->heads[index].height = height;
416 qdev->monitors_config->heads[index].surface_id = surf_id;
417
Dave Airlief64122c2013-02-25 14:47:55 +1000418}
419
Gerd Hoffmann45dfe572017-06-20 13:39:15 +0200420static void qxl_mode_set_nofb(struct drm_crtc *crtc)
Gabriel Krisman Bertazi3538e802017-02-27 17:43:21 -0300421{
422 struct qxl_device *qdev = crtc->dev->dev_private;
423 struct qxl_crtc *qcrtc = to_qxl_crtc(crtc);
424 struct drm_display_mode *mode = &crtc->mode;
425
426 DRM_DEBUG("Mode set (%d,%d)\n",
427 mode->hdisplay, mode->vdisplay);
428
429 qxl_monitors_config_set(qdev, qcrtc->index, 0, 0,
430 mode->hdisplay, mode->vdisplay, 0);
431
432}
433
Laurent Pinchart0b20a0f2017-06-30 12:36:44 +0300434static void qxl_crtc_atomic_enable(struct drm_crtc *crtc,
435 struct drm_crtc_state *old_state)
Dave Airlief64122c2013-02-25 14:47:55 +1000436{
437 DRM_DEBUG("\n");
438}
439
Laurent Pinchart64581712017-06-30 12:36:45 +0300440static void qxl_crtc_atomic_disable(struct drm_crtc *crtc,
441 struct drm_crtc_state *old_state)
Dave Airlie07f8d9b2013-07-02 06:37:13 +0100442{
443 struct qxl_crtc *qcrtc = to_qxl_crtc(crtc);
Gabriel Krisman Bertazi37235452017-02-27 17:43:22 -0300444 struct qxl_device *qdev = crtc->dev->dev_private;
Dave Airlie07f8d9b2013-07-02 06:37:13 +0100445
446 qxl_monitors_config_set(qdev, qcrtc->index, 0, 0, 0, 0, 0);
447
448 qxl_send_monitors_config(qdev);
449}
450
Dave Airlief64122c2013-02-25 14:47:55 +1000451static const struct drm_crtc_helper_funcs qxl_crtc_helper_funcs = {
Dave Airlief64122c2013-02-25 14:47:55 +1000452 .mode_fixup = qxl_crtc_mode_fixup,
Gabriel Krisman Bertazi3538e802017-02-27 17:43:21 -0300453 .mode_set_nofb = qxl_mode_set_nofb,
Gabriel Krisman Bertazic2ff6632017-02-27 17:43:20 -0300454 .atomic_flush = qxl_crtc_atomic_flush,
Laurent Pinchart0b20a0f2017-06-30 12:36:44 +0300455 .atomic_enable = qxl_crtc_atomic_enable,
Laurent Pinchart64581712017-06-30 12:36:45 +0300456 .atomic_disable = qxl_crtc_atomic_disable,
Dave Airlief64122c2013-02-25 14:47:55 +1000457};
458
Gerd Hoffmann45dfe572017-06-20 13:39:15 +0200459static int qxl_primary_atomic_check(struct drm_plane *plane,
460 struct drm_plane_state *state)
Gabriel Krisman Bertazic2ff6632017-02-27 17:43:20 -0300461{
462 struct qxl_device *qdev = plane->dev->dev_private;
463 struct qxl_framebuffer *qfb;
464 struct qxl_bo *bo;
465
466 if (!state->crtc || !state->fb)
467 return 0;
468
469 qfb = to_qxl_framebuffer(state->fb);
470 bo = gem_to_qxl_bo(qfb->obj);
471
472 if (bo->surf.stride * bo->surf.height > qdev->vram_size) {
473 DRM_ERROR("Mode doesn't fit in vram size (vgamem)");
474 return -EINVAL;
475 }
476
477 return 0;
478}
479
Ray Strode94280882017-11-27 16:50:10 -0500480static int qxl_primary_apply_cursor(struct drm_plane *plane)
481{
482 struct drm_device *dev = plane->dev;
483 struct qxl_device *qdev = dev->dev_private;
484 struct drm_framebuffer *fb = plane->state->fb;
485 struct qxl_crtc *qcrtc = to_qxl_crtc(plane->state->crtc);
486 struct qxl_cursor_cmd *cmd;
487 struct qxl_release *release;
488 int ret = 0;
489
490 if (!qcrtc->cursor_bo)
491 return 0;
492
493 ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd),
494 QXL_RELEASE_CURSOR_CMD,
495 &release, NULL);
496 if (ret)
497 return ret;
498
499 ret = qxl_release_list_add(release, qcrtc->cursor_bo);
500 if (ret)
501 goto out_free_release;
502
503 ret = qxl_release_reserve_list(release, false);
504 if (ret)
505 goto out_free_release;
506
507 cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release);
508 cmd->type = QXL_CURSOR_SET;
509 cmd->u.set.position.x = plane->state->crtc_x + fb->hot_x;
510 cmd->u.set.position.y = plane->state->crtc_y + fb->hot_y;
511
512 cmd->u.set.shape = qxl_bo_physical_address(qdev, qcrtc->cursor_bo, 0);
513
514 cmd->u.set.visible = 1;
515 qxl_release_unmap(qdev, release, &cmd->release_info);
516
517 qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
518 qxl_release_fence_buffer_objects(release);
519
520 return ret;
521
522out_free_release:
523 qxl_release_free(qdev, release);
524 return ret;
525}
526
Gabriel Krisman Bertazic2ff6632017-02-27 17:43:20 -0300527static void qxl_primary_atomic_update(struct drm_plane *plane,
528 struct drm_plane_state *old_state)
529{
530 struct qxl_device *qdev = plane->dev->dev_private;
531 struct qxl_framebuffer *qfb =
532 to_qxl_framebuffer(plane->state->fb);
533 struct qxl_framebuffer *qfb_old;
534 struct qxl_bo *bo = gem_to_qxl_bo(qfb->obj);
535 struct qxl_bo *bo_old;
536 struct drm_clip_rect norect = {
537 .x1 = 0,
538 .y1 = 0,
539 .x2 = qfb->base.width,
540 .y2 = qfb->base.height
541 };
Ray Strode94280882017-11-27 16:50:10 -0500542 int ret;
Gerd Hoffmann62676d12017-10-19 08:21:50 +0200543 bool same_shadow = false;
Gabriel Krisman Bertazic2ff6632017-02-27 17:43:20 -0300544
Gerd Hoffmannb0e07da2017-09-11 11:39:50 +0200545 if (old_state->fb) {
Gabriel Krisman Bertazic2ff6632017-02-27 17:43:20 -0300546 qfb_old = to_qxl_framebuffer(old_state->fb);
547 bo_old = gem_to_qxl_bo(qfb_old->obj);
Gerd Hoffmannb0e07da2017-09-11 11:39:50 +0200548 } else {
549 bo_old = NULL;
550 }
551
552 if (bo == bo_old)
553 return;
554
Gerd Hoffmann62676d12017-10-19 08:21:50 +0200555 if (bo_old && bo_old->shadow && bo->shadow &&
556 bo_old->shadow == bo->shadow) {
557 same_shadow = true;
558 }
559
Gerd Hoffmannb0e07da2017-09-11 11:39:50 +0200560 if (bo_old && bo_old->is_primary) {
Gerd Hoffmann62676d12017-10-19 08:21:50 +0200561 if (!same_shadow)
562 qxl_io_destroy_primary(qdev);
Gabriel Krisman Bertazic2ff6632017-02-27 17:43:20 -0300563 bo_old->is_primary = false;
Ray Strode94280882017-11-27 16:50:10 -0500564
565 ret = qxl_primary_apply_cursor(plane);
566 if (ret)
567 DRM_ERROR(
568 "could not set cursor after creating primary");
Gabriel Krisman Bertazic2ff6632017-02-27 17:43:20 -0300569 }
570
Gerd Hoffmannb0e07da2017-09-11 11:39:50 +0200571 if (!bo->is_primary) {
Gerd Hoffmann62676d12017-10-19 08:21:50 +0200572 if (!same_shadow)
573 qxl_io_create_primary(qdev, 0, bo);
Gerd Hoffmannb0e07da2017-09-11 11:39:50 +0200574 bo->is_primary = true;
575 }
Gerd Hoffmann62676d12017-10-19 08:21:50 +0200576
Gabriel Krisman Bertazic2ff6632017-02-27 17:43:20 -0300577 qxl_draw_dirty_fb(qdev, qfb, bo, 0, 0, &norect, 1, 1);
578}
579
580static void qxl_primary_atomic_disable(struct drm_plane *plane,
581 struct drm_plane_state *old_state)
582{
583 struct qxl_device *qdev = plane->dev->dev_private;
584
Gerd Hoffmannb0e07da2017-09-11 11:39:50 +0200585 if (old_state->fb) {
586 struct qxl_framebuffer *qfb =
Gabriel Krisman Bertazic2ff6632017-02-27 17:43:20 -0300587 to_qxl_framebuffer(old_state->fb);
588 struct qxl_bo *bo = gem_to_qxl_bo(qfb->obj);
589
Gerd Hoffmannb0e07da2017-09-11 11:39:50 +0200590 if (bo->is_primary) {
591 qxl_io_destroy_primary(qdev);
592 bo->is_primary = false;
593 }
Gabriel Krisman Bertazic2ff6632017-02-27 17:43:20 -0300594 }
595}
596
Gerd Hoffmann45dfe572017-06-20 13:39:15 +0200597static int qxl_plane_atomic_check(struct drm_plane *plane,
598 struct drm_plane_state *state)
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300599{
600 return 0;
601}
602
603static void qxl_cursor_atomic_update(struct drm_plane *plane,
604 struct drm_plane_state *old_state)
605{
606 struct drm_device *dev = plane->dev;
607 struct qxl_device *qdev = dev->dev_private;
608 struct drm_framebuffer *fb = plane->state->fb;
Ray Strode94280882017-11-27 16:50:10 -0500609 struct qxl_crtc *qcrtc = to_qxl_crtc(plane->state->crtc);
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300610 struct qxl_release *release;
611 struct qxl_cursor_cmd *cmd;
612 struct qxl_cursor *cursor;
613 struct drm_gem_object *obj;
Ray Strode16c6db32017-11-27 16:50:09 -0500614 struct qxl_bo *cursor_bo = NULL, *user_bo = NULL;
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300615 int ret;
616 void *user_ptr;
617 int size = 64*64*4;
618
619 ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd),
620 QXL_RELEASE_CURSOR_CMD,
621 &release, NULL);
Dan Carpenteree5cb7c2017-03-14 10:54:10 +0300622 if (ret)
623 return;
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300624
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300625 if (fb != old_state->fb) {
626 obj = to_qxl_framebuffer(fb)->obj;
627 user_bo = gem_to_qxl_bo(obj);
628
629 /* pinning is done in the prepare/cleanup framevbuffer */
630 ret = qxl_bo_kmap(user_bo, &user_ptr);
631 if (ret)
632 goto out_free_release;
633
634 ret = qxl_alloc_bo_reserved(qdev, release,
635 sizeof(struct qxl_cursor) + size,
636 &cursor_bo);
637 if (ret)
638 goto out_kunmap;
639
640 ret = qxl_release_reserve_list(release, true);
641 if (ret)
642 goto out_free_bo;
643
644 ret = qxl_bo_kmap(cursor_bo, (void **)&cursor);
645 if (ret)
646 goto out_backoff;
647
648 cursor->header.unique = 0;
649 cursor->header.type = SPICE_CURSOR_TYPE_ALPHA;
650 cursor->header.width = 64;
651 cursor->header.height = 64;
652 cursor->header.hot_spot_x = fb->hot_x;
653 cursor->header.hot_spot_y = fb->hot_y;
654 cursor->data_size = size;
655 cursor->chunk.next_chunk = 0;
656 cursor->chunk.prev_chunk = 0;
657 cursor->chunk.data_size = size;
658 memcpy(cursor->chunk.data, user_ptr, size);
659 qxl_bo_kunmap(cursor_bo);
660 qxl_bo_kunmap(user_bo);
661
Gabriel Krisman Bertazi429030b2017-05-19 14:58:19 -0300662 cmd = (struct qxl_cursor_cmd *) qxl_release_map(qdev, release);
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300663 cmd->u.set.visible = 1;
664 cmd->u.set.shape = qxl_bo_physical_address(qdev,
665 cursor_bo, 0);
666 cmd->type = QXL_CURSOR_SET;
Ray Strode94280882017-11-27 16:50:10 -0500667
668 qxl_bo_unref(&qcrtc->cursor_bo);
669 qcrtc->cursor_bo = cursor_bo;
670 cursor_bo = NULL;
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300671 } else {
672
673 ret = qxl_release_reserve_list(release, true);
674 if (ret)
675 goto out_free_release;
676
Gabriel Krisman Bertazi429030b2017-05-19 14:58:19 -0300677 cmd = (struct qxl_cursor_cmd *) qxl_release_map(qdev, release);
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300678 cmd->type = QXL_CURSOR_MOVE;
679 }
680
681 cmd->u.position.x = plane->state->crtc_x + fb->hot_x;
682 cmd->u.position.y = plane->state->crtc_y + fb->hot_y;
683
684 qxl_release_unmap(qdev, release, &cmd->release_info);
685 qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
686 qxl_release_fence_buffer_objects(release);
687
Ray Strode16c6db32017-11-27 16:50:09 -0500688 qxl_bo_unref(&cursor_bo);
689
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300690 return;
691
692out_backoff:
693 qxl_release_backoff_reserve_list(release);
694out_free_bo:
695 qxl_bo_unref(&cursor_bo);
696out_kunmap:
697 qxl_bo_kunmap(user_bo);
698out_free_release:
699 qxl_release_free(qdev, release);
700 return;
701
702}
703
Gerd Hoffmann45dfe572017-06-20 13:39:15 +0200704static void qxl_cursor_atomic_disable(struct drm_plane *plane,
705 struct drm_plane_state *old_state)
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300706{
707 struct qxl_device *qdev = plane->dev->dev_private;
708 struct qxl_release *release;
709 struct qxl_cursor_cmd *cmd;
710 int ret;
711
712 ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd),
713 QXL_RELEASE_CURSOR_CMD,
714 &release, NULL);
715 if (ret)
716 return;
717
718 ret = qxl_release_reserve_list(release, true);
719 if (ret) {
720 qxl_release_free(qdev, release);
721 return;
722 }
723
724 cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release);
725 cmd->type = QXL_CURSOR_HIDE;
726 qxl_release_unmap(qdev, release, &cmd->release_info);
727
728 qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
729 qxl_release_fence_buffer_objects(release);
730}
731
Gerd Hoffmann45dfe572017-06-20 13:39:15 +0200732static int qxl_plane_prepare_fb(struct drm_plane *plane,
733 struct drm_plane_state *new_state)
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300734{
Gerd Hoffmann62676d12017-10-19 08:21:50 +0200735 struct qxl_device *qdev = plane->dev->dev_private;
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300736 struct drm_gem_object *obj;
Gerd Hoffmann62676d12017-10-19 08:21:50 +0200737 struct qxl_bo *user_bo, *old_bo = NULL;
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300738 int ret;
739
740 if (!new_state->fb)
741 return 0;
742
743 obj = to_qxl_framebuffer(new_state->fb)->obj;
744 user_bo = gem_to_qxl_bo(obj);
745
Gerd Hoffmann62676d12017-10-19 08:21:50 +0200746 if (plane->type == DRM_PLANE_TYPE_PRIMARY &&
747 user_bo->is_dumb && !user_bo->shadow) {
748 if (plane->state->fb) {
749 obj = to_qxl_framebuffer(plane->state->fb)->obj;
750 old_bo = gem_to_qxl_bo(obj);
751 }
752 if (old_bo && old_bo->shadow &&
753 user_bo->gem_base.size == old_bo->gem_base.size &&
754 plane->state->crtc == new_state->crtc &&
755 plane->state->crtc_w == new_state->crtc_w &&
756 plane->state->crtc_h == new_state->crtc_h &&
757 plane->state->src_x == new_state->src_x &&
758 plane->state->src_y == new_state->src_y &&
759 plane->state->src_w == new_state->src_w &&
760 plane->state->src_h == new_state->src_h &&
761 plane->state->rotation == new_state->rotation &&
762 plane->state->zpos == new_state->zpos) {
763 drm_gem_object_get(&old_bo->shadow->gem_base);
764 user_bo->shadow = old_bo->shadow;
765 } else {
766 qxl_bo_create(qdev, user_bo->gem_base.size,
767 true, true, QXL_GEM_DOMAIN_VRAM, NULL,
768 &user_bo->shadow);
769 }
770 }
771
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300772 ret = qxl_bo_pin(user_bo, QXL_GEM_DOMAIN_CPU, NULL);
773 if (ret)
774 return ret;
775
776 return 0;
777}
778
779static void qxl_plane_cleanup_fb(struct drm_plane *plane,
780 struct drm_plane_state *old_state)
781{
782 struct drm_gem_object *obj;
783 struct qxl_bo *user_bo;
784
Gerd Hoffmann5f3d8622017-09-18 09:41:45 +0200785 if (!old_state->fb) {
786 /*
787 * we never executed prepare_fb, so there's nothing to
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300788 * unpin.
789 */
790 return;
791 }
792
Gerd Hoffmann5f3d8622017-09-18 09:41:45 +0200793 obj = to_qxl_framebuffer(old_state->fb)->obj;
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300794 user_bo = gem_to_qxl_bo(obj);
795 qxl_bo_unpin(user_bo);
Gerd Hoffmann62676d12017-10-19 08:21:50 +0200796
797 if (user_bo->shadow && !user_bo->is_primary) {
798 drm_gem_object_put_unlocked(&user_bo->shadow->gem_base);
799 user_bo->shadow = NULL;
800 }
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300801}
802
803static const uint32_t qxl_cursor_plane_formats[] = {
804 DRM_FORMAT_ARGB8888,
805};
806
807static const struct drm_plane_helper_funcs qxl_cursor_helper_funcs = {
808 .atomic_check = qxl_plane_atomic_check,
809 .atomic_update = qxl_cursor_atomic_update,
810 .atomic_disable = qxl_cursor_atomic_disable,
811 .prepare_fb = qxl_plane_prepare_fb,
812 .cleanup_fb = qxl_plane_cleanup_fb,
813};
814
815static const struct drm_plane_funcs qxl_cursor_plane_funcs = {
Gabriel Krisman Bertazi472e6d42017-02-27 17:43:25 -0300816 .update_plane = drm_atomic_helper_update_plane,
817 .disable_plane = drm_atomic_helper_disable_plane,
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300818 .destroy = drm_primary_helper_destroy,
Gabriel Krisman Bertazi9ade8b92017-02-27 17:43:23 -0300819 .reset = drm_atomic_helper_plane_reset,
820 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
821 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300822};
823
Gabriel Krisman Bertazid3e7e422017-02-27 17:43:18 -0300824static const uint32_t qxl_primary_plane_formats[] = {
825 DRM_FORMAT_XRGB8888,
826 DRM_FORMAT_ARGB8888,
827};
828
Gabriel Krisman Bertazic2ff6632017-02-27 17:43:20 -0300829static const struct drm_plane_helper_funcs primary_helper_funcs = {
830 .atomic_check = qxl_primary_atomic_check,
831 .atomic_update = qxl_primary_atomic_update,
832 .atomic_disable = qxl_primary_atomic_disable,
833 .prepare_fb = qxl_plane_prepare_fb,
834 .cleanup_fb = qxl_plane_cleanup_fb,
835};
836
Gabriel Krisman Bertazid3e7e422017-02-27 17:43:18 -0300837static const struct drm_plane_funcs qxl_primary_plane_funcs = {
Gabriel Krisman Bertazi472e6d42017-02-27 17:43:25 -0300838 .update_plane = drm_atomic_helper_update_plane,
839 .disable_plane = drm_atomic_helper_disable_plane,
Gabriel Krisman Bertazid3e7e422017-02-27 17:43:18 -0300840 .destroy = drm_primary_helper_destroy,
Gabriel Krisman Bertazi9ade8b92017-02-27 17:43:23 -0300841 .reset = drm_atomic_helper_plane_reset,
842 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
843 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
Gabriel Krisman Bertazid3e7e422017-02-27 17:43:18 -0300844};
845
846static struct drm_plane *qxl_create_plane(struct qxl_device *qdev,
847 unsigned int possible_crtcs,
848 enum drm_plane_type type)
849{
850 const struct drm_plane_helper_funcs *helper_funcs = NULL;
851 struct drm_plane *plane;
852 const struct drm_plane_funcs *funcs;
853 const uint32_t *formats;
854 int num_formats;
855 int err;
856
857 if (type == DRM_PLANE_TYPE_PRIMARY) {
858 funcs = &qxl_primary_plane_funcs;
859 formats = qxl_primary_plane_formats;
860 num_formats = ARRAY_SIZE(qxl_primary_plane_formats);
Gabriel Krisman Bertazic2ff6632017-02-27 17:43:20 -0300861 helper_funcs = &primary_helper_funcs;
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300862 } else if (type == DRM_PLANE_TYPE_CURSOR) {
863 funcs = &qxl_cursor_plane_funcs;
864 formats = qxl_cursor_plane_formats;
865 helper_funcs = &qxl_cursor_helper_funcs;
866 num_formats = ARRAY_SIZE(qxl_cursor_plane_formats);
Gabriel Krisman Bertazid3e7e422017-02-27 17:43:18 -0300867 } else {
868 return ERR_PTR(-EINVAL);
869 }
870
871 plane = kzalloc(sizeof(*plane), GFP_KERNEL);
872 if (!plane)
873 return ERR_PTR(-ENOMEM);
874
875 err = drm_universal_plane_init(&qdev->ddev, plane, possible_crtcs,
876 funcs, formats, num_formats,
Ben Widawskye6fc3b62017-07-23 20:46:38 -0700877 NULL, type, NULL);
Gabriel Krisman Bertazid3e7e422017-02-27 17:43:18 -0300878 if (err)
879 goto free_plane;
880
881 drm_plane_helper_add(plane, helper_funcs);
882
883 return plane;
884
885free_plane:
886 kfree(plane);
887 return ERR_PTR(-EINVAL);
888}
889
Dave Airlie07f8d9b2013-07-02 06:37:13 +0100890static int qdev_crtc_init(struct drm_device *dev, int crtc_id)
Dave Airlief64122c2013-02-25 14:47:55 +1000891{
892 struct qxl_crtc *qxl_crtc;
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300893 struct drm_plane *primary, *cursor;
Gabriel Krisman Bertazid3e7e422017-02-27 17:43:18 -0300894 struct qxl_device *qdev = dev->dev_private;
895 int r;
Dave Airlief64122c2013-02-25 14:47:55 +1000896
897 qxl_crtc = kzalloc(sizeof(struct qxl_crtc), GFP_KERNEL);
898 if (!qxl_crtc)
899 return -ENOMEM;
900
Gabriel Krisman Bertazid3e7e422017-02-27 17:43:18 -0300901 primary = qxl_create_plane(qdev, 1 << crtc_id, DRM_PLANE_TYPE_PRIMARY);
902 if (IS_ERR(primary)) {
903 r = -ENOMEM;
904 goto free_mem;
905 }
906
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300907 cursor = qxl_create_plane(qdev, 1 << crtc_id, DRM_PLANE_TYPE_CURSOR);
908 if (IS_ERR(cursor)) {
909 r = -ENOMEM;
910 goto clean_primary;
911 }
912
913 r = drm_crtc_init_with_planes(dev, &qxl_crtc->base, primary, cursor,
Gabriel Krisman Bertazid3e7e422017-02-27 17:43:18 -0300914 &qxl_crtc_funcs, NULL);
915 if (r)
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300916 goto clean_cursor;
Gabriel Krisman Bertazid3e7e422017-02-27 17:43:18 -0300917
Dave Airlie07f8d9b2013-07-02 06:37:13 +0100918 qxl_crtc->index = crtc_id;
Dave Airlief64122c2013-02-25 14:47:55 +1000919 drm_crtc_helper_add(&qxl_crtc->base, &qxl_crtc_helper_funcs);
920 return 0;
Gabriel Krisman Bertazid3e7e422017-02-27 17:43:18 -0300921
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -0300922clean_cursor:
923 drm_plane_cleanup(cursor);
924 kfree(cursor);
Gabriel Krisman Bertazid3e7e422017-02-27 17:43:18 -0300925clean_primary:
926 drm_plane_cleanup(primary);
927 kfree(primary);
928free_mem:
929 kfree(qxl_crtc);
930 return r;
Dave Airlief64122c2013-02-25 14:47:55 +1000931}
932
933static void qxl_enc_dpms(struct drm_encoder *encoder, int mode)
934{
935 DRM_DEBUG("\n");
936}
937
Dave Airlief64122c2013-02-25 14:47:55 +1000938static void qxl_enc_prepare(struct drm_encoder *encoder)
939{
940 DRM_DEBUG("\n");
941}
942
943static void qxl_write_monitors_config_for_encoder(struct qxl_device *qdev,
944 struct drm_encoder *encoder)
945{
946 int i;
Dave Airlie07f8d9b2013-07-02 06:37:13 +0100947 struct qxl_output *output = drm_encoder_to_qxl_output(encoder);
Dave Airlief64122c2013-02-25 14:47:55 +1000948 struct qxl_head *head;
949 struct drm_display_mode *mode;
950
951 BUG_ON(!encoder);
952 /* TODO: ugly, do better */
Dave Airlie07f8d9b2013-07-02 06:37:13 +0100953 i = output->index;
Dave Airlief64122c2013-02-25 14:47:55 +1000954 if (!qdev->monitors_config ||
955 qdev->monitors_config->max_allowed <= i) {
956 DRM_ERROR(
957 "head number too large or missing monitors config: %p, %d",
958 qdev->monitors_config,
959 qdev->monitors_config ?
960 qdev->monitors_config->max_allowed : -1);
961 return;
962 }
963 if (!encoder->crtc) {
964 DRM_ERROR("missing crtc on encoder %p\n", encoder);
965 return;
966 }
967 if (i != 0)
968 DRM_DEBUG("missing for multiple monitors: no head holes\n");
969 head = &qdev->monitors_config->heads[i];
970 head->id = i;
Dave Airlief64122c2013-02-25 14:47:55 +1000971 if (encoder->crtc->enabled) {
972 mode = &encoder->crtc->mode;
973 head->width = mode->hdisplay;
974 head->height = mode->vdisplay;
975 head->x = encoder->crtc->x;
976 head->y = encoder->crtc->y;
977 if (qdev->monitors_config->count < i + 1)
978 qdev->monitors_config->count = i + 1;
979 } else {
980 head->width = 0;
981 head->height = 0;
982 head->x = 0;
983 head->y = 0;
984 }
Dave Airlie07f8d9b2013-07-02 06:37:13 +0100985 DRM_DEBUG_KMS("setting head %d to +%d+%d %dx%d out of %d\n",
986 i, head->x, head->y, head->width, head->height, qdev->monitors_config->count);
Dave Airlief64122c2013-02-25 14:47:55 +1000987 head->flags = 0;
988 /* TODO - somewhere else to call this for multiple monitors
989 * (config_commit?) */
990 qxl_send_monitors_config(qdev);
991}
992
993static void qxl_enc_commit(struct drm_encoder *encoder)
994{
995 struct qxl_device *qdev = encoder->dev->dev_private;
996
997 qxl_write_monitors_config_for_encoder(qdev, encoder);
998 DRM_DEBUG("\n");
999}
1000
1001static void qxl_enc_mode_set(struct drm_encoder *encoder,
1002 struct drm_display_mode *mode,
1003 struct drm_display_mode *adjusted_mode)
1004{
1005 DRM_DEBUG("\n");
1006}
1007
1008static int qxl_conn_get_modes(struct drm_connector *connector)
1009{
Marc-André Lureaub0807422013-10-18 16:11:31 +02001010 unsigned pwidth = 1024;
1011 unsigned pheight = 768;
Gerd Hoffmann2d856f942017-03-01 11:12:34 +01001012 int ret = 0;
Dave Airlief64122c2013-02-25 14:47:55 +10001013
Gerd Hoffmann2d856f942017-03-01 11:12:34 +01001014 ret = qxl_add_monitors_config_modes(connector, &pwidth, &pheight);
1015 if (ret < 0)
1016 return ret;
Marc-André Lureaub0807422013-10-18 16:11:31 +02001017 ret += qxl_add_common_modes(connector, pwidth, pheight);
Dave Airlief64122c2013-02-25 14:47:55 +10001018 return ret;
1019}
1020
Luc Van Oostenrycke0d92e12018-04-24 15:15:15 +02001021static enum drm_mode_status qxl_conn_mode_valid(struct drm_connector *connector,
Dave Airlief64122c2013-02-25 14:47:55 +10001022 struct drm_display_mode *mode)
1023{
Jonathon Jongsmabd3e1c72015-08-20 14:04:32 -05001024 struct drm_device *ddev = connector->dev;
1025 struct qxl_device *qdev = ddev->dev_private;
1026 int i;
1027
Dave Airlief64122c2013-02-25 14:47:55 +10001028 /* TODO: is this called for user defined modes? (xrandr --add-mode)
1029 * TODO: check that the mode fits in the framebuffer */
Jonathon Jongsmabd3e1c72015-08-20 14:04:32 -05001030
1031 if(qdev->monitors_config_width == mode->hdisplay &&
1032 qdev->monitors_config_height == mode->vdisplay)
1033 return MODE_OK;
1034
1035 for (i = 0; i < ARRAY_SIZE(common_modes); i++) {
1036 if (common_modes[i].w == mode->hdisplay && common_modes[i].h == mode->vdisplay)
1037 return MODE_OK;
1038 }
1039 return MODE_BAD;
Dave Airlief64122c2013-02-25 14:47:55 +10001040}
1041
Dave Airlie6d01f1f2013-04-16 13:24:25 +10001042static struct drm_encoder *qxl_best_encoder(struct drm_connector *connector)
Dave Airlief64122c2013-02-25 14:47:55 +10001043{
1044 struct qxl_output *qxl_output =
1045 drm_connector_to_qxl_output(connector);
1046
1047 DRM_DEBUG("\n");
1048 return &qxl_output->enc;
1049}
1050
1051
1052static const struct drm_encoder_helper_funcs qxl_enc_helper_funcs = {
1053 .dpms = qxl_enc_dpms,
Dave Airlief64122c2013-02-25 14:47:55 +10001054 .prepare = qxl_enc_prepare,
1055 .mode_set = qxl_enc_mode_set,
1056 .commit = qxl_enc_commit,
1057};
1058
1059static const struct drm_connector_helper_funcs qxl_connector_helper_funcs = {
1060 .get_modes = qxl_conn_get_modes,
1061 .mode_valid = qxl_conn_mode_valid,
1062 .best_encoder = qxl_best_encoder,
1063};
1064
Dave Airlief64122c2013-02-25 14:47:55 +10001065static enum drm_connector_status qxl_conn_detect(
1066 struct drm_connector *connector,
1067 bool force)
1068{
1069 struct qxl_output *output =
1070 drm_connector_to_qxl_output(connector);
1071 struct drm_device *ddev = connector->dev;
1072 struct qxl_device *qdev = ddev->dev_private;
Dave Airlie69e5d3f2015-09-14 10:28:34 +10001073 bool connected = false;
Dave Airlief64122c2013-02-25 14:47:55 +10001074
1075 /* The first monitor is always connected */
Dave Airlie69e5d3f2015-09-14 10:28:34 +10001076 if (!qdev->client_monitors_config) {
1077 if (output->index == 0)
1078 connected = true;
1079 } else
1080 connected = qdev->client_monitors_config->count > output->index &&
1081 qxl_head_enabled(&qdev->client_monitors_config->heads[output->index]);
Dave Airlief64122c2013-02-25 14:47:55 +10001082
Marc-André Lureau5cab51c2013-10-18 16:11:33 +02001083 DRM_DEBUG("#%d connected: %d\n", output->index, connected);
1084 if (!connected)
1085 qxl_monitors_config_set(qdev, output->index, 0, 0, 0, 0, 0);
1086
Dave Airlief64122c2013-02-25 14:47:55 +10001087 return connected ? connector_status_connected
1088 : connector_status_disconnected;
1089}
1090
1091static int qxl_conn_set_property(struct drm_connector *connector,
1092 struct drm_property *property,
1093 uint64_t value)
1094{
1095 DRM_DEBUG("\n");
1096 return 0;
1097}
1098
1099static void qxl_conn_destroy(struct drm_connector *connector)
1100{
1101 struct qxl_output *qxl_output =
1102 drm_connector_to_qxl_output(connector);
1103
Thomas Wood34ea3d32014-05-29 16:57:41 +01001104 drm_connector_unregister(connector);
Dave Airlief64122c2013-02-25 14:47:55 +10001105 drm_connector_cleanup(connector);
1106 kfree(qxl_output);
1107}
1108
1109static const struct drm_connector_funcs qxl_connector_funcs = {
1110 .dpms = drm_helper_connector_dpms,
Dave Airlief64122c2013-02-25 14:47:55 +10001111 .detect = qxl_conn_detect,
Ville Syrjälä6af3e652015-12-03 23:14:14 +02001112 .fill_modes = drm_helper_probe_single_connector_modes,
Dave Airlief64122c2013-02-25 14:47:55 +10001113 .set_property = qxl_conn_set_property,
1114 .destroy = qxl_conn_destroy,
Gabriel Krisman Bertazi9ade8b92017-02-27 17:43:23 -03001115 .reset = drm_atomic_helper_connector_reset,
1116 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
1117 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
Dave Airlief64122c2013-02-25 14:47:55 +10001118};
1119
1120static void qxl_enc_destroy(struct drm_encoder *encoder)
1121{
1122 drm_encoder_cleanup(encoder);
1123}
1124
1125static const struct drm_encoder_funcs qxl_enc_funcs = {
1126 .destroy = qxl_enc_destroy,
1127};
1128
Dave Airlie4695b032013-10-11 11:05:00 +10001129static int qxl_mode_create_hotplug_mode_update_property(struct qxl_device *qdev)
1130{
1131 if (qdev->hotplug_mode_update_property)
1132 return 0;
1133
1134 qdev->hotplug_mode_update_property =
Gabriel Krisman Bertazicbdded72017-01-26 23:05:48 -02001135 drm_property_create_range(&qdev->ddev, DRM_MODE_PROP_IMMUTABLE,
Dave Airlie4695b032013-10-11 11:05:00 +10001136 "hotplug_mode_update", 0, 1);
1137
1138 return 0;
1139}
1140
Dave Airlie6d01f1f2013-04-16 13:24:25 +10001141static int qdev_output_init(struct drm_device *dev, int num_output)
Dave Airlief64122c2013-02-25 14:47:55 +10001142{
Dave Airlie4695b032013-10-11 11:05:00 +10001143 struct qxl_device *qdev = dev->dev_private;
Dave Airlief64122c2013-02-25 14:47:55 +10001144 struct qxl_output *qxl_output;
1145 struct drm_connector *connector;
1146 struct drm_encoder *encoder;
1147
1148 qxl_output = kzalloc(sizeof(struct qxl_output), GFP_KERNEL);
1149 if (!qxl_output)
1150 return -ENOMEM;
1151
1152 qxl_output->index = num_output;
1153
1154 connector = &qxl_output->base;
1155 encoder = &qxl_output->enc;
1156 drm_connector_init(dev, &qxl_output->base,
1157 &qxl_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL);
1158
1159 drm_encoder_init(dev, &qxl_output->enc, &qxl_enc_funcs,
Ville Syrjälä13a3d912015-12-09 16:20:18 +02001160 DRM_MODE_ENCODER_VIRTUAL, NULL);
Dave Airlief64122c2013-02-25 14:47:55 +10001161
Dave Airlie5ff91e42013-07-05 10:20:33 +10001162 /* we get HPD via client monitors config */
1163 connector->polled = DRM_CONNECTOR_POLL_HPD;
Dave Airlief64122c2013-02-25 14:47:55 +10001164 encoder->possible_crtcs = 1 << num_output;
1165 drm_mode_connector_attach_encoder(&qxl_output->base,
1166 &qxl_output->enc);
1167 drm_encoder_helper_add(encoder, &qxl_enc_helper_funcs);
1168 drm_connector_helper_add(connector, &qxl_connector_helper_funcs);
1169
Dave Airlie4695b032013-10-11 11:05:00 +10001170 drm_object_attach_property(&connector->base,
1171 qdev->hotplug_mode_update_property, 0);
Dave Airlie7dea0942014-10-28 11:28:44 +10001172 drm_object_attach_property(&connector->base,
1173 dev->mode_config.suggested_x_property, 0);
1174 drm_object_attach_property(&connector->base,
1175 dev->mode_config.suggested_y_property, 0);
Dave Airlief64122c2013-02-25 14:47:55 +10001176 return 0;
1177}
1178
1179static struct drm_framebuffer *
1180qxl_user_framebuffer_create(struct drm_device *dev,
1181 struct drm_file *file_priv,
Ville Syrjälä1eb83452015-11-11 19:11:29 +02001182 const struct drm_mode_fb_cmd2 *mode_cmd)
Dave Airlief64122c2013-02-25 14:47:55 +10001183{
1184 struct drm_gem_object *obj;
1185 struct qxl_framebuffer *qxl_fb;
Dave Airlief64122c2013-02-25 14:47:55 +10001186 int ret;
1187
Chris Wilsona8ad0bd2016-05-09 11:04:54 +01001188 obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[0]);
1189 if (!obj)
1190 return NULL;
Dave Airlief64122c2013-02-25 14:47:55 +10001191
1192 qxl_fb = kzalloc(sizeof(*qxl_fb), GFP_KERNEL);
1193 if (qxl_fb == NULL)
1194 return NULL;
1195
Noralf Trønnes6819c3c2016-04-28 17:18:36 +02001196 ret = qxl_framebuffer_init(dev, qxl_fb, mode_cmd, obj, &qxl_fb_funcs);
Dave Airlief64122c2013-02-25 14:47:55 +10001197 if (ret) {
1198 kfree(qxl_fb);
Santha Meena Ramamoorthy2793c1d2018-03-20 11:29:27 -07001199 drm_gem_object_put_unlocked(obj);
Dave Airlief64122c2013-02-25 14:47:55 +10001200 return NULL;
1201 }
1202
Dave Airlief64122c2013-02-25 14:47:55 +10001203 return &qxl_fb->base;
1204}
1205
1206static const struct drm_mode_config_funcs qxl_mode_funcs = {
1207 .fb_create = qxl_user_framebuffer_create,
Gabriel Krisman Bertazi472e6d42017-02-27 17:43:25 -03001208 .atomic_check = drm_atomic_helper_check,
1209 .atomic_commit = drm_atomic_helper_commit,
Dave Airlief64122c2013-02-25 14:47:55 +10001210};
1211
Dave Airlie2bd6ce82013-07-04 14:46:46 +10001212int qxl_create_monitors_object(struct qxl_device *qdev)
Dave Airlief64122c2013-02-25 14:47:55 +10001213{
Dave Airlief64122c2013-02-25 14:47:55 +10001214 int ret;
1215 struct drm_gem_object *gobj;
Dave Airlie07f8d9b2013-07-02 06:37:13 +01001216 int max_allowed = qxl_num_crtc;
Dave Airlief64122c2013-02-25 14:47:55 +10001217 int monitors_config_size = sizeof(struct qxl_monitors_config) +
Dave Airlie2bd6ce82013-07-04 14:46:46 +10001218 max_allowed * sizeof(struct qxl_head);
Dave Airlief64122c2013-02-25 14:47:55 +10001219
Dave Airlief64122c2013-02-25 14:47:55 +10001220 ret = qxl_gem_object_create(qdev, monitors_config_size, 0,
1221 QXL_GEM_DOMAIN_VRAM,
1222 false, false, NULL, &gobj);
1223 if (ret) {
1224 DRM_ERROR("%s: failed to create gem ret=%d\n", __func__, ret);
1225 return -ENOMEM;
1226 }
1227 qdev->monitors_config_bo = gem_to_qxl_bo(gobj);
Dave Airlie2bd6ce82013-07-04 14:46:46 +10001228
Gabriel Krisman Bertazi715a11f2017-02-27 17:43:16 -03001229 ret = qxl_bo_pin(qdev->monitors_config_bo, QXL_GEM_DOMAIN_VRAM, NULL);
Dave Airlie2bd6ce82013-07-04 14:46:46 +10001230 if (ret)
1231 return ret;
1232
Dave Airlief64122c2013-02-25 14:47:55 +10001233 qxl_bo_kmap(qdev->monitors_config_bo, NULL);
Dave Airlie2bd6ce82013-07-04 14:46:46 +10001234
Dave Airlief64122c2013-02-25 14:47:55 +10001235 qdev->monitors_config = qdev->monitors_config_bo->kptr;
1236 qdev->ram_header->monitors_config =
1237 qxl_bo_physical_address(qdev, qdev->monitors_config_bo, 0);
1238
1239 memset(qdev->monitors_config, 0, monitors_config_size);
1240 qdev->monitors_config->max_allowed = max_allowed;
Dave Airlie2bd6ce82013-07-04 14:46:46 +10001241 return 0;
1242}
1243
1244int qxl_destroy_monitors_object(struct qxl_device *qdev)
1245{
1246 int ret;
1247
1248 qdev->monitors_config = NULL;
1249 qdev->ram_header->monitors_config = 0;
1250
1251 qxl_bo_kunmap(qdev->monitors_config_bo);
Gabriel Krisman Bertazi715a11f2017-02-27 17:43:16 -03001252 ret = qxl_bo_unpin(qdev->monitors_config_bo);
Dave Airlie2bd6ce82013-07-04 14:46:46 +10001253 if (ret)
1254 return ret;
1255
Dave Airlie2bd6ce82013-07-04 14:46:46 +10001256 qxl_bo_unref(&qdev->monitors_config_bo);
1257 return 0;
1258}
1259
1260int qxl_modeset_init(struct qxl_device *qdev)
1261{
1262 int i;
1263 int ret;
1264
Gabriel Krisman Bertazicbdded72017-01-26 23:05:48 -02001265 drm_mode_config_init(&qdev->ddev);
Dave Airlie2bd6ce82013-07-04 14:46:46 +10001266
1267 ret = qxl_create_monitors_object(qdev);
1268 if (ret)
1269 return ret;
Dave Airlief64122c2013-02-25 14:47:55 +10001270
Gabriel Krisman Bertazicbdded72017-01-26 23:05:48 -02001271 qdev->ddev.mode_config.funcs = (void *)&qxl_mode_funcs;
Dave Airlief64122c2013-02-25 14:47:55 +10001272
1273 /* modes will be validated against the framebuffer size */
Gabriel Krisman Bertazi1277eed2017-02-27 17:43:19 -03001274 qdev->ddev.mode_config.min_width = 0;
1275 qdev->ddev.mode_config.min_height = 0;
Gabriel Krisman Bertazicbdded72017-01-26 23:05:48 -02001276 qdev->ddev.mode_config.max_width = 8192;
1277 qdev->ddev.mode_config.max_height = 8192;
Dave Airlief64122c2013-02-25 14:47:55 +10001278
Gabriel Krisman Bertazicbdded72017-01-26 23:05:48 -02001279 qdev->ddev.mode_config.fb_base = qdev->vram_base;
Dave Airlie4695b032013-10-11 11:05:00 +10001280
Gabriel Krisman Bertazicbdded72017-01-26 23:05:48 -02001281 drm_mode_create_suggested_offset_properties(&qdev->ddev);
Dave Airlie4695b032013-10-11 11:05:00 +10001282 qxl_mode_create_hotplug_mode_update_property(qdev);
1283
Dave Airlie07f8d9b2013-07-02 06:37:13 +01001284 for (i = 0 ; i < qxl_num_crtc; ++i) {
Gabriel Krisman Bertazicbdded72017-01-26 23:05:48 -02001285 qdev_crtc_init(&qdev->ddev, i);
1286 qdev_output_init(&qdev->ddev, i);
Dave Airlief64122c2013-02-25 14:47:55 +10001287 }
1288
Gerd Hoffmannc50fad82017-03-01 11:12:33 +01001289 qxl_display_read_client_monitors_config(qdev);
Dave Airlief64122c2013-02-25 14:47:55 +10001290 qdev->mode_info.mode_config_initialized = true;
1291
Gabriel Krisman Bertazi9ade8b92017-02-27 17:43:23 -03001292 drm_mode_config_reset(&qdev->ddev);
1293
Dave Airlief64122c2013-02-25 14:47:55 +10001294 /* primary surface must be created by this point, to allow
1295 * issuing command queue commands and having them read by
1296 * spice server. */
1297 qxl_fbdev_init(qdev);
1298 return 0;
1299}
1300
1301void qxl_modeset_fini(struct qxl_device *qdev)
1302{
1303 qxl_fbdev_fini(qdev);
Dave Airlie2bd6ce82013-07-04 14:46:46 +10001304
1305 qxl_destroy_monitors_object(qdev);
Dave Airlief64122c2013-02-25 14:47:55 +10001306 if (qdev->mode_info.mode_config_initialized) {
Gabriel Krisman Bertazicbdded72017-01-26 23:05:48 -02001307 drm_mode_config_cleanup(&qdev->ddev);
Dave Airlief64122c2013-02-25 14:47:55 +10001308 qdev->mode_info.mode_config_initialized = false;
1309 }
1310}