bochs_kms.c 10.8 KB
Newer Older
G
Gerd Hoffmann 已提交
1 2 3 4 5 6 7 8
/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#include "bochs.h"
9
#include <drm/drm_atomic_helper.h>
10
#include <drm/drm_plane_helper.h>
11
#include <drm/drm_atomic_uapi.h>
G
Gerd Hoffmann 已提交
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

static int defx = 1024;
static int defy = 768;

module_param(defx, int, 0444);
module_param(defy, int, 0444);
MODULE_PARM_DESC(defx, "default x resolution");
MODULE_PARM_DESC(defy, "default y resolution");

/* ---------------------------------------------------------------------- */

static void bochs_crtc_dpms(struct drm_crtc *crtc, int mode)
{
	switch (mode) {
	case DRM_MODE_DPMS_ON:
	case DRM_MODE_DPMS_STANDBY:
	case DRM_MODE_DPMS_SUSPEND:
	case DRM_MODE_DPMS_OFF:
	default:
		return;
	}
}

static int bochs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
				    struct drm_framebuffer *old_fb)
{
	struct bochs_device *bochs =
		container_of(crtc, struct bochs_device, crtc);
	struct bochs_bo *bo;
	u64 gpu_addr = 0;
	int ret;

	if (old_fb) {
45
		bo = gem_to_bochs_bo(old_fb->obj[0]);
46
		ret = ttm_bo_reserve(&bo->bo, true, false, NULL);
G
Gerd Hoffmann 已提交
47 48 49 50 51 52 53 54
		if (ret) {
			DRM_ERROR("failed to reserve old_fb bo\n");
		} else {
			bochs_bo_unpin(bo);
			ttm_bo_unreserve(&bo->bo);
		}
	}

55
	if (WARN_ON(crtc->primary->fb == NULL))
G
Gerd Hoffmann 已提交
56 57
		return -EINVAL;

58
	bo = gem_to_bochs_bo(crtc->primary->fb->obj[0]);
59
	ret = ttm_bo_reserve(&bo->bo, true, false, NULL);
G
Gerd Hoffmann 已提交
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
	if (ret)
		return ret;

	ret = bochs_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr);
	if (ret) {
		ttm_bo_unreserve(&bo->bo);
		return ret;
	}

	ttm_bo_unreserve(&bo->bo);
	bochs_hw_setbase(bochs, x, y, gpu_addr);
	return 0;
}

static int bochs_crtc_mode_set(struct drm_crtc *crtc,
			       struct drm_display_mode *mode,
			       struct drm_display_mode *adjusted_mode,
			       int x, int y, struct drm_framebuffer *old_fb)
{
	struct bochs_device *bochs =
		container_of(crtc, struct bochs_device, crtc);

82 83 84
	if (WARN_ON(crtc->primary->fb == NULL))
		return -EINVAL;

85 86
	bochs_hw_setmode(bochs, mode);
	bochs_hw_setformat(bochs, crtc->primary->fb->format);
G
Gerd Hoffmann 已提交
87 88 89 90
	bochs_crtc_mode_set_base(crtc, x, y, old_fb);
	return 0;
}

91 92 93 94 95 96 97 98
static void bochs_crtc_mode_set_nofb(struct drm_crtc *crtc)
{
	struct bochs_device *bochs =
		container_of(crtc, struct bochs_device, crtc);

	bochs_hw_setmode(bochs, &crtc->mode);
}

G
Gerd Hoffmann 已提交
99 100 101 102 103 104 105 106
static void bochs_crtc_prepare(struct drm_crtc *crtc)
{
}

static void bochs_crtc_commit(struct drm_crtc *crtc)
{
}

G
Gerd Hoffmann 已提交
107 108 109
static int bochs_crtc_page_flip(struct drm_crtc *crtc,
				struct drm_framebuffer *fb,
				struct drm_pending_vblank_event *event,
110 111
				uint32_t page_flip_flags,
				struct drm_modeset_acquire_ctx *ctx)
G
Gerd Hoffmann 已提交
112 113 114 115 116 117
{
	struct bochs_device *bochs =
		container_of(crtc, struct bochs_device, crtc);
	struct drm_framebuffer *old_fb = crtc->primary->fb;
	unsigned long irqflags;

118
	drm_atomic_set_fb_for_plane(crtc->primary->state, fb);
G
Gerd Hoffmann 已提交
119 120 121
	bochs_crtc_mode_set_base(crtc, 0, 0, old_fb);
	if (event) {
		spin_lock_irqsave(&bochs->dev->event_lock, irqflags);
122
		drm_crtc_send_vblank_event(crtc, event);
G
Gerd Hoffmann 已提交
123 124 125 126 127
		spin_unlock_irqrestore(&bochs->dev->event_lock, irqflags);
	}
	return 0;
}

128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
static void bochs_crtc_atomic_enable(struct drm_crtc *crtc,
				     struct drm_crtc_state *old_crtc_state)
{
}

static void bochs_crtc_atomic_flush(struct drm_crtc *crtc,
				    struct drm_crtc_state *old_crtc_state)
{
	struct drm_device *dev = crtc->dev;
	struct drm_pending_vblank_event *event;

	if (crtc->state && crtc->state->event) {
		unsigned long irqflags;

		spin_lock_irqsave(&dev->event_lock, irqflags);
		event = crtc->state->event;
		crtc->state->event = NULL;
		drm_crtc_send_vblank_event(crtc, event);
		spin_unlock_irqrestore(&dev->event_lock, irqflags);
	}
}


G
Gerd Hoffmann 已提交
151 152
/* These provide the minimum set of functions required to handle a CRTC */
static const struct drm_crtc_funcs bochs_crtc_funcs = {
153
	.set_config = drm_atomic_helper_set_config,
G
Gerd Hoffmann 已提交
154
	.destroy = drm_crtc_cleanup,
G
Gerd Hoffmann 已提交
155
	.page_flip = bochs_crtc_page_flip,
156 157 158
	.reset = drm_atomic_helper_crtc_reset,
	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
G
Gerd Hoffmann 已提交
159 160 161 162 163 164
};

static const struct drm_crtc_helper_funcs bochs_helper_funcs = {
	.dpms = bochs_crtc_dpms,
	.mode_set = bochs_crtc_mode_set,
	.mode_set_base = bochs_crtc_mode_set_base,
165
	.mode_set_nofb = bochs_crtc_mode_set_nofb,
G
Gerd Hoffmann 已提交
166 167
	.prepare = bochs_crtc_prepare,
	.commit = bochs_crtc_commit,
168 169
	.atomic_enable = bochs_crtc_atomic_enable,
	.atomic_flush = bochs_crtc_atomic_flush,
G
Gerd Hoffmann 已提交
170 171
};

172
static const uint32_t bochs_formats[] = {
173 174
	DRM_FORMAT_XRGB8888,
	DRM_FORMAT_BGRX8888,
175 176
};

177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
static void bochs_plane_atomic_update(struct drm_plane *plane,
				      struct drm_plane_state *old_state)
{
	struct bochs_device *bochs = plane->dev->dev_private;
	struct bochs_bo *bo;

	if (!plane->state->fb)
		return;
	bo = gem_to_bochs_bo(plane->state->fb->obj[0]);
	bochs_hw_setbase(bochs,
			 plane->state->crtc_x,
			 plane->state->crtc_y,
			 bo->bo.offset);
	bochs_hw_setformat(bochs, plane->state->fb->format);
}

static int bochs_plane_prepare_fb(struct drm_plane *plane,
				struct drm_plane_state *new_state)
{
	struct bochs_bo *bo;
	int ret;

	if (!new_state->fb)
		return 0;
	bo = gem_to_bochs_bo(new_state->fb->obj[0]);

	ret = ttm_bo_reserve(&bo->bo, true, false, NULL);
	if (ret)
		return ret;
	ret = bochs_bo_pin(bo, TTM_PL_FLAG_VRAM, NULL);
	ttm_bo_unreserve(&bo->bo);
	return ret;
}

static void bochs_plane_cleanup_fb(struct drm_plane *plane,
				   struct drm_plane_state *old_state)
{
	struct bochs_bo *bo;
	int ret;

	if (!old_state->fb)
		return;
	bo = gem_to_bochs_bo(old_state->fb->obj[0]);
	ret = ttm_bo_reserve(&bo->bo, true, false, NULL);
	if (ret)
		return;
	bochs_bo_unpin(bo);
	ttm_bo_unreserve(&bo->bo);
}

static const struct drm_plane_helper_funcs bochs_plane_helper_funcs = {
	.atomic_update = bochs_plane_atomic_update,
	.prepare_fb = bochs_plane_prepare_fb,
	.cleanup_fb = bochs_plane_cleanup_fb,
};

static const struct drm_plane_funcs bochs_plane_funcs = {
       .update_plane   = drm_atomic_helper_update_plane,
       .disable_plane  = drm_atomic_helper_disable_plane,
       .destroy        = drm_primary_helper_destroy,
       .reset          = drm_atomic_helper_plane_reset,
       .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
       .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};

242 243 244 245 246 247 248 249 250 251 252 253
static struct drm_plane *bochs_primary_plane(struct drm_device *dev)
{
	struct drm_plane *primary;
	int ret;

	primary = kzalloc(sizeof(*primary), GFP_KERNEL);
	if (primary == NULL) {
		DRM_DEBUG_KMS("Failed to allocate primary plane\n");
		return NULL;
	}

	ret = drm_universal_plane_init(dev, primary, 0,
254
				       &bochs_plane_funcs,
255 256 257 258 259 260
				       bochs_formats,
				       ARRAY_SIZE(bochs_formats),
				       NULL,
				       DRM_PLANE_TYPE_PRIMARY, NULL);
	if (ret) {
		kfree(primary);
261
		return NULL;
262 263
	}

264
	drm_plane_helper_add(primary, &bochs_plane_helper_funcs);
265 266 267
	return primary;
}

G
Gerd Hoffmann 已提交
268 269 270 271
static void bochs_crtc_init(struct drm_device *dev)
{
	struct bochs_device *bochs = dev->dev_private;
	struct drm_crtc *crtc = &bochs->crtc;
272
	struct drm_plane *primary = bochs_primary_plane(dev);
G
Gerd Hoffmann 已提交
273

274 275
	drm_crtc_init_with_planes(dev, crtc, primary, NULL,
				  &bochs_crtc_funcs, NULL);
G
Gerd Hoffmann 已提交
276 277 278 279 280 281 282 283 284 285 286 287 288 289
	drm_crtc_helper_add(crtc, &bochs_helper_funcs);
}

static const struct drm_encoder_funcs bochs_encoder_encoder_funcs = {
	.destroy = drm_encoder_cleanup,
};

static void bochs_encoder_init(struct drm_device *dev)
{
	struct bochs_device *bochs = dev->dev_private;
	struct drm_encoder *encoder = &bochs->encoder;

	encoder->possible_crtcs = 0x1;
	drm_encoder_init(dev, encoder, &bochs_encoder_encoder_funcs,
290
			 DRM_MODE_ENCODER_DAC, NULL);
G
Gerd Hoffmann 已提交
291 292 293
}


294
static int bochs_connector_get_modes(struct drm_connector *connector)
G
Gerd Hoffmann 已提交
295
{
G
Gerd Hoffmann 已提交
296 297 298 299 300 301
	struct bochs_device *bochs =
		container_of(connector, struct bochs_device, connector);
	int count = 0;

	if (bochs->edid)
		count = drm_add_edid_modes(connector, bochs->edid);
G
Gerd Hoffmann 已提交
302

G
Gerd Hoffmann 已提交
303 304 305 306
	if (!count) {
		count = drm_add_modes_noedid(connector, 8192, 8192);
		drm_set_preferred_mode(connector, defx, defy);
	}
G
Gerd Hoffmann 已提交
307 308 309
	return count;
}

310
static enum drm_mode_status bochs_connector_mode_valid(struct drm_connector *connector,
G
Gerd Hoffmann 已提交
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
				      struct drm_display_mode *mode)
{
	struct bochs_device *bochs =
		container_of(connector, struct bochs_device, connector);
	unsigned long size = mode->hdisplay * mode->vdisplay * 4;

	/*
	 * Make sure we can fit two framebuffers into video memory.
	 * This allows up to 1600x1200 with 16 MB (default size).
	 * If you want more try this:
	 *     'qemu -vga std -global VGA.vgamem_mb=32 $otherargs'
	 */
	if (size * 2 > bochs->fb_size)
		return MODE_BAD;

	return MODE_OK;
}

static struct drm_encoder *
bochs_connector_best_encoder(struct drm_connector *connector)
{
	int enc_id = connector->encoder_ids[0];
	/* pick the encoder ids */
R
Rob Clark 已提交
334
	if (enc_id)
335
		return drm_encoder_find(connector->dev, NULL, enc_id);
G
Gerd Hoffmann 已提交
336 337 338
	return NULL;
}

339
static const struct drm_connector_helper_funcs bochs_connector_connector_helper_funcs = {
G
Gerd Hoffmann 已提交
340 341 342 343 344
	.get_modes = bochs_connector_get_modes,
	.mode_valid = bochs_connector_mode_valid,
	.best_encoder = bochs_connector_best_encoder,
};

345
static const struct drm_connector_funcs bochs_connector_connector_funcs = {
G
Gerd Hoffmann 已提交
346 347 348
	.dpms = drm_helper_connector_dpms,
	.fill_modes = drm_helper_probe_single_connector_modes,
	.destroy = drm_connector_cleanup,
349 350 351
	.reset = drm_atomic_helper_connector_reset,
	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
G
Gerd Hoffmann 已提交
352 353 354 355 356 357 358 359 360 361 362
};

static void bochs_connector_init(struct drm_device *dev)
{
	struct bochs_device *bochs = dev->dev_private;
	struct drm_connector *connector = &bochs->connector;

	drm_connector_init(dev, connector, &bochs_connector_connector_funcs,
			   DRM_MODE_CONNECTOR_VIRTUAL);
	drm_connector_helper_add(connector,
				 &bochs_connector_connector_helper_funcs);
363
	drm_connector_register(connector);
G
Gerd Hoffmann 已提交
364 365 366 367 368 369 370

	bochs_hw_load_edid(bochs);
	if (bochs->edid) {
		DRM_INFO("Found EDID data blob.\n");
		drm_connector_attach_edid_property(connector);
		drm_connector_update_edid_property(connector, bochs->edid);
	}
G
Gerd Hoffmann 已提交
371 372 373 374 375 376 377 378 379 380 381 382 383 384
}


int bochs_kms_init(struct bochs_device *bochs)
{
	drm_mode_config_init(bochs->dev);
	bochs->mode_config_initialized = true;

	bochs->dev->mode_config.max_width = 8192;
	bochs->dev->mode_config.max_height = 8192;

	bochs->dev->mode_config.fb_base = bochs->fb_base;
	bochs->dev->mode_config.preferred_depth = 24;
	bochs->dev->mode_config.prefer_shadow = 0;
385
	bochs->dev->mode_config.quirk_addfb_prefer_host_byte_order = true;
G
Gerd Hoffmann 已提交
386

387
	bochs->dev->mode_config.funcs = &bochs_mode_funcs;
G
Gerd Hoffmann 已提交
388 389 390 391

	bochs_crtc_init(bochs->dev);
	bochs_encoder_init(bochs->dev);
	bochs_connector_init(bochs->dev);
392
	drm_connector_attach_encoder(&bochs->connector,
G
Gerd Hoffmann 已提交
393 394
					  &bochs->encoder);

395 396
	drm_mode_config_reset(bochs->dev);

G
Gerd Hoffmann 已提交
397 398 399 400 401 402 403 404 405 406
	return 0;
}

void bochs_kms_fini(struct bochs_device *bochs)
{
	if (bochs->mode_config_initialized) {
		drm_mode_config_cleanup(bochs->dev);
		bochs->mode_config_initialized = false;
	}
}