162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2013-2017 Oracle Corporation
462306a36Sopenharmony_ci * This file is based on ast_mode.c
562306a36Sopenharmony_ci * Copyright 2012 Red Hat Inc.
662306a36Sopenharmony_ci * Parts based on xf86-video-ast
762306a36Sopenharmony_ci * Copyright (c) 2005 ASPEED Technology Inc.
862306a36Sopenharmony_ci * Authors: Dave Airlie <airlied@redhat.com>
962306a36Sopenharmony_ci *          Michael Thayer <michael.thayer@oracle.com,
1062306a36Sopenharmony_ci *          Hans de Goede <hdegoede@redhat.com>
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/iosys-map.h>
1462306a36Sopenharmony_ci#include <linux/export.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <drm/drm_atomic.h>
1762306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
1862306a36Sopenharmony_ci#include <drm/drm_edid.h>
1962306a36Sopenharmony_ci#include <drm/drm_fb_helper.h>
2062306a36Sopenharmony_ci#include <drm/drm_fourcc.h>
2162306a36Sopenharmony_ci#include <drm/drm_framebuffer.h>
2262306a36Sopenharmony_ci#include <drm/drm_gem_atomic_helper.h>
2362306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h>
2462306a36Sopenharmony_ci#include <drm/drm_plane_helper.h>
2562306a36Sopenharmony_ci#include <drm/drm_probe_helper.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include "hgsmi_channels.h"
2862306a36Sopenharmony_ci#include "vbox_drv.h"
2962306a36Sopenharmony_ci#include "vboxvideo.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/*
3262306a36Sopenharmony_ci * Set a graphics mode.  Poke any required values into registers, do an HGSMI
3362306a36Sopenharmony_ci * mode set and tell the host we support advanced graphics functions.
3462306a36Sopenharmony_ci */
3562306a36Sopenharmony_cistatic void vbox_do_modeset(struct drm_crtc *crtc)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct drm_framebuffer *fb = crtc->primary->state->fb;
3862306a36Sopenharmony_ci	struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
3962306a36Sopenharmony_ci	struct vbox_private *vbox;
4062306a36Sopenharmony_ci	int width, height, bpp, pitch;
4162306a36Sopenharmony_ci	u16 flags;
4262306a36Sopenharmony_ci	s32 x_offset, y_offset;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	vbox = to_vbox_dev(crtc->dev);
4562306a36Sopenharmony_ci	width = vbox_crtc->width ? vbox_crtc->width : 640;
4662306a36Sopenharmony_ci	height = vbox_crtc->height ? vbox_crtc->height : 480;
4762306a36Sopenharmony_ci	bpp = fb ? fb->format->cpp[0] * 8 : 32;
4862306a36Sopenharmony_ci	pitch = fb ? fb->pitches[0] : width * bpp / 8;
4962306a36Sopenharmony_ci	x_offset = vbox->single_framebuffer ? vbox_crtc->x : vbox_crtc->x_hint;
5062306a36Sopenharmony_ci	y_offset = vbox->single_framebuffer ? vbox_crtc->y : vbox_crtc->y_hint;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/*
5362306a36Sopenharmony_ci	 * This is the old way of setting graphics modes.  It assumed one screen
5462306a36Sopenharmony_ci	 * and a frame-buffer at the start of video RAM.  On older versions of
5562306a36Sopenharmony_ci	 * VirtualBox, certain parts of the code still assume that the first
5662306a36Sopenharmony_ci	 * screen is programmed this way, so try to fake it.
5762306a36Sopenharmony_ci	 */
5862306a36Sopenharmony_ci	if (vbox_crtc->crtc_id == 0 && fb &&
5962306a36Sopenharmony_ci	    vbox_crtc->fb_offset / pitch < 0xffff - crtc->y &&
6062306a36Sopenharmony_ci	    vbox_crtc->fb_offset % (bpp / 8) == 0) {
6162306a36Sopenharmony_ci		vbox_write_ioport(VBE_DISPI_INDEX_XRES, width);
6262306a36Sopenharmony_ci		vbox_write_ioport(VBE_DISPI_INDEX_YRES, height);
6362306a36Sopenharmony_ci		vbox_write_ioport(VBE_DISPI_INDEX_VIRT_WIDTH, pitch * 8 / bpp);
6462306a36Sopenharmony_ci		vbox_write_ioport(VBE_DISPI_INDEX_BPP, bpp);
6562306a36Sopenharmony_ci		vbox_write_ioport(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED);
6662306a36Sopenharmony_ci		vbox_write_ioport(VBE_DISPI_INDEX_X_OFFSET,
6762306a36Sopenharmony_ci			vbox_crtc->fb_offset % pitch / bpp * 8 + vbox_crtc->x);
6862306a36Sopenharmony_ci		vbox_write_ioport(VBE_DISPI_INDEX_Y_OFFSET,
6962306a36Sopenharmony_ci				  vbox_crtc->fb_offset / pitch + vbox_crtc->y);
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	flags = VBVA_SCREEN_F_ACTIVE;
7362306a36Sopenharmony_ci	flags |= (fb && crtc->state->enable) ? 0 : VBVA_SCREEN_F_BLANK;
7462306a36Sopenharmony_ci	flags |= vbox_crtc->disconnected ? VBVA_SCREEN_F_DISABLED : 0;
7562306a36Sopenharmony_ci	hgsmi_process_display_info(vbox->guest_pool, vbox_crtc->crtc_id,
7662306a36Sopenharmony_ci				   x_offset, y_offset,
7762306a36Sopenharmony_ci				   vbox_crtc->x * bpp / 8 +
7862306a36Sopenharmony_ci							vbox_crtc->y * pitch,
7962306a36Sopenharmony_ci				   pitch, width, height, bpp, flags);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic int vbox_set_view(struct drm_crtc *crtc)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
8562306a36Sopenharmony_ci	struct vbox_private *vbox = to_vbox_dev(crtc->dev);
8662306a36Sopenharmony_ci	struct vbva_infoview *p;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/*
8962306a36Sopenharmony_ci	 * Tell the host about the view.  This design originally targeted the
9062306a36Sopenharmony_ci	 * Windows XP driver architecture and assumed that each screen would
9162306a36Sopenharmony_ci	 * have a dedicated frame buffer with the command buffer following it,
9262306a36Sopenharmony_ci	 * the whole being a "view".  The host works out which screen a command
9362306a36Sopenharmony_ci	 * buffer belongs to by checking whether it is in the first view, then
9462306a36Sopenharmony_ci	 * whether it is in the second and so on.  The first match wins.  We
9562306a36Sopenharmony_ci	 * cheat around this by making the first view be the managed memory
9662306a36Sopenharmony_ci	 * plus the first command buffer, the second the same plus the second
9762306a36Sopenharmony_ci	 * buffer and so on.
9862306a36Sopenharmony_ci	 */
9962306a36Sopenharmony_ci	p = hgsmi_buffer_alloc(vbox->guest_pool, sizeof(*p),
10062306a36Sopenharmony_ci			       HGSMI_CH_VBVA, VBVA_INFO_VIEW);
10162306a36Sopenharmony_ci	if (!p)
10262306a36Sopenharmony_ci		return -ENOMEM;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	p->view_index = vbox_crtc->crtc_id;
10562306a36Sopenharmony_ci	p->view_offset = vbox_crtc->fb_offset;
10662306a36Sopenharmony_ci	p->view_size = vbox->available_vram_size - vbox_crtc->fb_offset +
10762306a36Sopenharmony_ci		       vbox_crtc->crtc_id * VBVA_MIN_BUFFER_SIZE;
10862306a36Sopenharmony_ci	p->max_screen_size = vbox->available_vram_size - vbox_crtc->fb_offset;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	hgsmi_buffer_submit(vbox->guest_pool, p);
11162306a36Sopenharmony_ci	hgsmi_buffer_free(vbox->guest_pool, p);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return 0;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/*
11762306a36Sopenharmony_ci * Try to map the layout of virtual screens to the range of the input device.
11862306a36Sopenharmony_ci * Return true if we need to re-set the crtc modes due to screen offset
11962306a36Sopenharmony_ci * changes.
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_cistatic bool vbox_set_up_input_mapping(struct vbox_private *vbox)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct drm_crtc *crtci;
12462306a36Sopenharmony_ci	struct drm_connector *connectori;
12562306a36Sopenharmony_ci	struct drm_framebuffer *fb, *fb1 = NULL;
12662306a36Sopenharmony_ci	bool single_framebuffer = true;
12762306a36Sopenharmony_ci	bool old_single_framebuffer = vbox->single_framebuffer;
12862306a36Sopenharmony_ci	u16 width = 0, height = 0;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/*
13162306a36Sopenharmony_ci	 * Are we using an X.Org-style single large frame-buffer for all crtcs?
13262306a36Sopenharmony_ci	 * If so then screen layout can be deduced from the crtc offsets.
13362306a36Sopenharmony_ci	 * Same fall-back if this is the fbdev frame-buffer.
13462306a36Sopenharmony_ci	 */
13562306a36Sopenharmony_ci	list_for_each_entry(crtci, &vbox->ddev.mode_config.crtc_list, head) {
13662306a36Sopenharmony_ci		fb = crtci->primary->state->fb;
13762306a36Sopenharmony_ci		if (!fb)
13862306a36Sopenharmony_ci			continue;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci		if (!fb1) {
14162306a36Sopenharmony_ci			fb1 = fb;
14262306a36Sopenharmony_ci			if (fb1 == vbox->ddev.fb_helper->fb)
14362306a36Sopenharmony_ci				break;
14462306a36Sopenharmony_ci		} else if (fb != fb1) {
14562306a36Sopenharmony_ci			single_framebuffer = false;
14662306a36Sopenharmony_ci		}
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci	if (!fb1)
14962306a36Sopenharmony_ci		return false;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (single_framebuffer) {
15262306a36Sopenharmony_ci		vbox->single_framebuffer = true;
15362306a36Sopenharmony_ci		vbox->input_mapping_width = fb1->width;
15462306a36Sopenharmony_ci		vbox->input_mapping_height = fb1->height;
15562306a36Sopenharmony_ci		return old_single_framebuffer != vbox->single_framebuffer;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci	/* Otherwise calculate the total span of all screens. */
15862306a36Sopenharmony_ci	list_for_each_entry(connectori, &vbox->ddev.mode_config.connector_list,
15962306a36Sopenharmony_ci			    head) {
16062306a36Sopenharmony_ci		struct vbox_connector *vbox_connector =
16162306a36Sopenharmony_ci		    to_vbox_connector(connectori);
16262306a36Sopenharmony_ci		struct vbox_crtc *vbox_crtc = vbox_connector->vbox_crtc;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci		width = max_t(u16, width, vbox_crtc->x_hint +
16562306a36Sopenharmony_ci					  vbox_connector->mode_hint.width);
16662306a36Sopenharmony_ci		height = max_t(u16, height, vbox_crtc->y_hint +
16762306a36Sopenharmony_ci					    vbox_connector->mode_hint.height);
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	vbox->single_framebuffer = false;
17162306a36Sopenharmony_ci	vbox->input_mapping_width = width;
17262306a36Sopenharmony_ci	vbox->input_mapping_height = height;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return old_single_framebuffer != vbox->single_framebuffer;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void vbox_crtc_set_base_and_mode(struct drm_crtc *crtc,
17862306a36Sopenharmony_ci					struct drm_framebuffer *fb,
17962306a36Sopenharmony_ci					int x, int y)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(fb->obj[0]);
18262306a36Sopenharmony_ci	struct vbox_private *vbox = to_vbox_dev(crtc->dev);
18362306a36Sopenharmony_ci	struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
18462306a36Sopenharmony_ci	bool needs_modeset = drm_atomic_crtc_needs_modeset(crtc->state);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	mutex_lock(&vbox->hw_mutex);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (crtc->state->enable) {
18962306a36Sopenharmony_ci		vbox_crtc->width = crtc->state->mode.hdisplay;
19062306a36Sopenharmony_ci		vbox_crtc->height = crtc->state->mode.vdisplay;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	vbox_crtc->x = x;
19462306a36Sopenharmony_ci	vbox_crtc->y = y;
19562306a36Sopenharmony_ci	vbox_crtc->fb_offset = drm_gem_vram_offset(gbo);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	/* vbox_do_modeset() checks vbox->single_framebuffer so update it now */
19862306a36Sopenharmony_ci	if (needs_modeset && vbox_set_up_input_mapping(vbox)) {
19962306a36Sopenharmony_ci		struct drm_crtc *crtci;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci		list_for_each_entry(crtci, &vbox->ddev.mode_config.crtc_list,
20262306a36Sopenharmony_ci				    head) {
20362306a36Sopenharmony_ci			if (crtci == crtc)
20462306a36Sopenharmony_ci				continue;
20562306a36Sopenharmony_ci			vbox_do_modeset(crtci);
20662306a36Sopenharmony_ci		}
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	vbox_set_view(crtc);
21062306a36Sopenharmony_ci	vbox_do_modeset(crtc);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (needs_modeset)
21362306a36Sopenharmony_ci		hgsmi_update_input_mapping(vbox->guest_pool, 0, 0,
21462306a36Sopenharmony_ci					   vbox->input_mapping_width,
21562306a36Sopenharmony_ci					   vbox->input_mapping_height);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	mutex_unlock(&vbox->hw_mutex);
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic void vbox_crtc_atomic_enable(struct drm_crtc *crtc,
22162306a36Sopenharmony_ci				    struct drm_atomic_state *state)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic void vbox_crtc_atomic_disable(struct drm_crtc *crtc,
22662306a36Sopenharmony_ci				     struct drm_atomic_state *state)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic void vbox_crtc_atomic_flush(struct drm_crtc *crtc,
23162306a36Sopenharmony_ci				   struct drm_atomic_state *state)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs vbox_crtc_helper_funcs = {
23662306a36Sopenharmony_ci	.atomic_enable = vbox_crtc_atomic_enable,
23762306a36Sopenharmony_ci	.atomic_disable = vbox_crtc_atomic_disable,
23862306a36Sopenharmony_ci	.atomic_flush = vbox_crtc_atomic_flush,
23962306a36Sopenharmony_ci};
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic void vbox_crtc_destroy(struct drm_crtc *crtc)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	drm_crtc_cleanup(crtc);
24462306a36Sopenharmony_ci	kfree(crtc);
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic const struct drm_crtc_funcs vbox_crtc_funcs = {
24862306a36Sopenharmony_ci	.set_config = drm_atomic_helper_set_config,
24962306a36Sopenharmony_ci	.page_flip = drm_atomic_helper_page_flip,
25062306a36Sopenharmony_ci	/* .gamma_set = vbox_crtc_gamma_set, */
25162306a36Sopenharmony_ci	.destroy = vbox_crtc_destroy,
25262306a36Sopenharmony_ci	.reset = drm_atomic_helper_crtc_reset,
25362306a36Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
25462306a36Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
25562306a36Sopenharmony_ci};
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic int vbox_primary_atomic_check(struct drm_plane *plane,
25862306a36Sopenharmony_ci				     struct drm_atomic_state *state)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
26162306a36Sopenharmony_ci									   plane);
26262306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state = NULL;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (new_state->crtc) {
26562306a36Sopenharmony_ci		crtc_state = drm_atomic_get_existing_crtc_state(state,
26662306a36Sopenharmony_ci								new_state->crtc);
26762306a36Sopenharmony_ci		if (WARN_ON(!crtc_state))
26862306a36Sopenharmony_ci			return -EINVAL;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	return drm_atomic_helper_check_plane_state(new_state, crtc_state,
27262306a36Sopenharmony_ci						   DRM_PLANE_NO_SCALING,
27362306a36Sopenharmony_ci						   DRM_PLANE_NO_SCALING,
27462306a36Sopenharmony_ci						   false, true);
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic void vbox_primary_atomic_update(struct drm_plane *plane,
27862306a36Sopenharmony_ci				       struct drm_atomic_state *state)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
28162306a36Sopenharmony_ci									   plane);
28262306a36Sopenharmony_ci	struct drm_crtc *crtc = new_state->crtc;
28362306a36Sopenharmony_ci	struct drm_framebuffer *fb = new_state->fb;
28462306a36Sopenharmony_ci	struct vbox_private *vbox = to_vbox_dev(fb->dev);
28562306a36Sopenharmony_ci	struct drm_mode_rect *clips;
28662306a36Sopenharmony_ci	uint32_t num_clips, i;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	vbox_crtc_set_base_and_mode(crtc, fb,
28962306a36Sopenharmony_ci				    new_state->src_x >> 16,
29062306a36Sopenharmony_ci				    new_state->src_y >> 16);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/* Send information about dirty rectangles to VBVA. */
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	clips = drm_plane_get_damage_clips(new_state);
29562306a36Sopenharmony_ci	num_clips = drm_plane_get_damage_clips_count(new_state);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	if (!num_clips)
29862306a36Sopenharmony_ci		return;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	mutex_lock(&vbox->hw_mutex);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	for (i = 0; i < num_clips; ++i, ++clips) {
30362306a36Sopenharmony_ci		struct vbva_cmd_hdr cmd_hdr;
30462306a36Sopenharmony_ci		unsigned int crtc_id = to_vbox_crtc(crtc)->crtc_id;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci		cmd_hdr.x = (s16)clips->x1;
30762306a36Sopenharmony_ci		cmd_hdr.y = (s16)clips->y1;
30862306a36Sopenharmony_ci		cmd_hdr.w = (u16)clips->x2 - clips->x1;
30962306a36Sopenharmony_ci		cmd_hdr.h = (u16)clips->y2 - clips->y1;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci		if (!vbva_buffer_begin_update(&vbox->vbva_info[crtc_id],
31262306a36Sopenharmony_ci					      vbox->guest_pool))
31362306a36Sopenharmony_ci			continue;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci		vbva_write(&vbox->vbva_info[crtc_id], vbox->guest_pool,
31662306a36Sopenharmony_ci			   &cmd_hdr, sizeof(cmd_hdr));
31762306a36Sopenharmony_ci		vbva_buffer_end_update(&vbox->vbva_info[crtc_id]);
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	mutex_unlock(&vbox->hw_mutex);
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic void vbox_primary_atomic_disable(struct drm_plane *plane,
32462306a36Sopenharmony_ci					struct drm_atomic_state *state)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
32762306a36Sopenharmony_ci									   plane);
32862306a36Sopenharmony_ci	struct drm_crtc *crtc = old_state->crtc;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/* vbox_do_modeset checks plane->state->fb and will disable if NULL */
33162306a36Sopenharmony_ci	vbox_crtc_set_base_and_mode(crtc, old_state->fb,
33262306a36Sopenharmony_ci				    old_state->src_x >> 16,
33362306a36Sopenharmony_ci				    old_state->src_y >> 16);
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic int vbox_cursor_atomic_check(struct drm_plane *plane,
33762306a36Sopenharmony_ci				    struct drm_atomic_state *state)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
34062306a36Sopenharmony_ci									   plane);
34162306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state = NULL;
34262306a36Sopenharmony_ci	u32 width = new_state->crtc_w;
34362306a36Sopenharmony_ci	u32 height = new_state->crtc_h;
34462306a36Sopenharmony_ci	int ret;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	if (new_state->crtc) {
34762306a36Sopenharmony_ci		crtc_state = drm_atomic_get_existing_crtc_state(state,
34862306a36Sopenharmony_ci								new_state->crtc);
34962306a36Sopenharmony_ci		if (WARN_ON(!crtc_state))
35062306a36Sopenharmony_ci			return -EINVAL;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	ret = drm_atomic_helper_check_plane_state(new_state, crtc_state,
35462306a36Sopenharmony_ci						  DRM_PLANE_NO_SCALING,
35562306a36Sopenharmony_ci						  DRM_PLANE_NO_SCALING,
35662306a36Sopenharmony_ci						  true, true);
35762306a36Sopenharmony_ci	if (ret)
35862306a36Sopenharmony_ci		return ret;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	if (!new_state->fb)
36162306a36Sopenharmony_ci		return 0;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	if (width > VBOX_MAX_CURSOR_WIDTH || height > VBOX_MAX_CURSOR_HEIGHT ||
36462306a36Sopenharmony_ci	    width == 0 || height == 0)
36562306a36Sopenharmony_ci		return -EINVAL;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	return 0;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci/*
37162306a36Sopenharmony_ci * Copy the ARGB image and generate the mask, which is needed in case the host
37262306a36Sopenharmony_ci * does not support ARGB cursors.  The mask is a 1BPP bitmap with the bit set
37362306a36Sopenharmony_ci * if the corresponding alpha value in the ARGB image is greater than 0xF0.
37462306a36Sopenharmony_ci */
37562306a36Sopenharmony_cistatic void copy_cursor_image(u8 *src, u8 *dst, u32 width, u32 height,
37662306a36Sopenharmony_ci			      size_t mask_size)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	size_t line_size = (width + 7) / 8;
37962306a36Sopenharmony_ci	u32 i, j;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	memcpy(dst + mask_size, src, width * height * 4);
38262306a36Sopenharmony_ci	for (i = 0; i < height; ++i)
38362306a36Sopenharmony_ci		for (j = 0; j < width; ++j)
38462306a36Sopenharmony_ci			if (((u32 *)src)[i * width + j] > 0xf0000000)
38562306a36Sopenharmony_ci				dst[i * line_size + j / 8] |= (0x80 >> (j % 8));
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic void vbox_cursor_atomic_update(struct drm_plane *plane,
38962306a36Sopenharmony_ci				      struct drm_atomic_state *state)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
39262306a36Sopenharmony_ci									   plane);
39362306a36Sopenharmony_ci	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
39462306a36Sopenharmony_ci									   plane);
39562306a36Sopenharmony_ci	struct vbox_private *vbox =
39662306a36Sopenharmony_ci		container_of(plane->dev, struct vbox_private, ddev);
39762306a36Sopenharmony_ci	struct vbox_crtc *vbox_crtc = to_vbox_crtc(new_state->crtc);
39862306a36Sopenharmony_ci	struct drm_framebuffer *fb = new_state->fb;
39962306a36Sopenharmony_ci	u32 width = new_state->crtc_w;
40062306a36Sopenharmony_ci	u32 height = new_state->crtc_h;
40162306a36Sopenharmony_ci	struct drm_shadow_plane_state *shadow_plane_state =
40262306a36Sopenharmony_ci		to_drm_shadow_plane_state(new_state);
40362306a36Sopenharmony_ci	struct iosys_map map = shadow_plane_state->data[0];
40462306a36Sopenharmony_ci	u8 *src = map.vaddr; /* TODO: Use mapping abstraction properly */
40562306a36Sopenharmony_ci	size_t data_size, mask_size;
40662306a36Sopenharmony_ci	u32 flags;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	/*
40962306a36Sopenharmony_ci	 * VirtualBox uses the host windowing system to draw the cursor so
41062306a36Sopenharmony_ci	 * moves are a no-op, we only need to upload new cursor sprites.
41162306a36Sopenharmony_ci	 */
41262306a36Sopenharmony_ci	if (fb == old_state->fb)
41362306a36Sopenharmony_ci		return;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	mutex_lock(&vbox->hw_mutex);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	vbox_crtc->cursor_enabled = true;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	/*
42062306a36Sopenharmony_ci	 * The mask must be calculated based on the alpha
42162306a36Sopenharmony_ci	 * channel, one bit per ARGB word, and must be 32-bit
42262306a36Sopenharmony_ci	 * padded.
42362306a36Sopenharmony_ci	 */
42462306a36Sopenharmony_ci	mask_size = ((width + 7) / 8 * height + 3) & ~3;
42562306a36Sopenharmony_ci	data_size = width * height * 4 + mask_size;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	copy_cursor_image(src, vbox->cursor_data, width, height, mask_size);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	flags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE |
43062306a36Sopenharmony_ci		VBOX_MOUSE_POINTER_ALPHA;
43162306a36Sopenharmony_ci	hgsmi_update_pointer_shape(vbox->guest_pool, flags,
43262306a36Sopenharmony_ci				   min_t(u32, max(fb->hot_x, 0), width),
43362306a36Sopenharmony_ci				   min_t(u32, max(fb->hot_y, 0), height),
43462306a36Sopenharmony_ci				   width, height, vbox->cursor_data, data_size);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	mutex_unlock(&vbox->hw_mutex);
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic void vbox_cursor_atomic_disable(struct drm_plane *plane,
44062306a36Sopenharmony_ci				       struct drm_atomic_state *state)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
44362306a36Sopenharmony_ci									   plane);
44462306a36Sopenharmony_ci	struct vbox_private *vbox =
44562306a36Sopenharmony_ci		container_of(plane->dev, struct vbox_private, ddev);
44662306a36Sopenharmony_ci	struct vbox_crtc *vbox_crtc = to_vbox_crtc(old_state->crtc);
44762306a36Sopenharmony_ci	bool cursor_enabled = false;
44862306a36Sopenharmony_ci	struct drm_crtc *crtci;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	mutex_lock(&vbox->hw_mutex);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	vbox_crtc->cursor_enabled = false;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	list_for_each_entry(crtci, &vbox->ddev.mode_config.crtc_list, head) {
45562306a36Sopenharmony_ci		if (to_vbox_crtc(crtci)->cursor_enabled)
45662306a36Sopenharmony_ci			cursor_enabled = true;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	if (!cursor_enabled)
46062306a36Sopenharmony_ci		hgsmi_update_pointer_shape(vbox->guest_pool, 0, 0, 0,
46162306a36Sopenharmony_ci					   0, 0, NULL, 0);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	mutex_unlock(&vbox->hw_mutex);
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic const u32 vbox_cursor_plane_formats[] = {
46762306a36Sopenharmony_ci	DRM_FORMAT_ARGB8888,
46862306a36Sopenharmony_ci};
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs vbox_cursor_helper_funcs = {
47162306a36Sopenharmony_ci	.atomic_check	= vbox_cursor_atomic_check,
47262306a36Sopenharmony_ci	.atomic_update	= vbox_cursor_atomic_update,
47362306a36Sopenharmony_ci	.atomic_disable	= vbox_cursor_atomic_disable,
47462306a36Sopenharmony_ci	DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
47562306a36Sopenharmony_ci};
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic const struct drm_plane_funcs vbox_cursor_plane_funcs = {
47862306a36Sopenharmony_ci	.update_plane	= drm_atomic_helper_update_plane,
47962306a36Sopenharmony_ci	.disable_plane	= drm_atomic_helper_disable_plane,
48062306a36Sopenharmony_ci	.destroy	= drm_plane_helper_destroy,
48162306a36Sopenharmony_ci	DRM_GEM_SHADOW_PLANE_FUNCS,
48262306a36Sopenharmony_ci};
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic const u32 vbox_primary_plane_formats[] = {
48562306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
48662306a36Sopenharmony_ci	DRM_FORMAT_ARGB8888,
48762306a36Sopenharmony_ci};
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs vbox_primary_helper_funcs = {
49062306a36Sopenharmony_ci	.atomic_check = vbox_primary_atomic_check,
49162306a36Sopenharmony_ci	.atomic_update = vbox_primary_atomic_update,
49262306a36Sopenharmony_ci	.atomic_disable = vbox_primary_atomic_disable,
49362306a36Sopenharmony_ci	DRM_GEM_VRAM_PLANE_HELPER_FUNCS,
49462306a36Sopenharmony_ci};
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_cistatic const struct drm_plane_funcs vbox_primary_plane_funcs = {
49762306a36Sopenharmony_ci	.update_plane	= drm_atomic_helper_update_plane,
49862306a36Sopenharmony_ci	.disable_plane	= drm_atomic_helper_disable_plane,
49962306a36Sopenharmony_ci	.destroy	= drm_plane_helper_destroy,
50062306a36Sopenharmony_ci	.reset		= drm_atomic_helper_plane_reset,
50162306a36Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
50262306a36Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
50362306a36Sopenharmony_ci};
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic struct drm_plane *vbox_create_plane(struct vbox_private *vbox,
50662306a36Sopenharmony_ci					   unsigned int possible_crtcs,
50762306a36Sopenharmony_ci					   enum drm_plane_type type)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	const struct drm_plane_helper_funcs *helper_funcs = NULL;
51062306a36Sopenharmony_ci	const struct drm_plane_funcs *funcs;
51162306a36Sopenharmony_ci	struct drm_plane *plane;
51262306a36Sopenharmony_ci	const u32 *formats;
51362306a36Sopenharmony_ci	int num_formats;
51462306a36Sopenharmony_ci	int err;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if (type == DRM_PLANE_TYPE_PRIMARY) {
51762306a36Sopenharmony_ci		funcs = &vbox_primary_plane_funcs;
51862306a36Sopenharmony_ci		formats = vbox_primary_plane_formats;
51962306a36Sopenharmony_ci		helper_funcs = &vbox_primary_helper_funcs;
52062306a36Sopenharmony_ci		num_formats = ARRAY_SIZE(vbox_primary_plane_formats);
52162306a36Sopenharmony_ci	} else if (type == DRM_PLANE_TYPE_CURSOR) {
52262306a36Sopenharmony_ci		funcs = &vbox_cursor_plane_funcs;
52362306a36Sopenharmony_ci		formats = vbox_cursor_plane_formats;
52462306a36Sopenharmony_ci		helper_funcs = &vbox_cursor_helper_funcs;
52562306a36Sopenharmony_ci		num_formats = ARRAY_SIZE(vbox_cursor_plane_formats);
52662306a36Sopenharmony_ci	} else {
52762306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
53162306a36Sopenharmony_ci	if (!plane)
53262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	err = drm_universal_plane_init(&vbox->ddev, plane, possible_crtcs,
53562306a36Sopenharmony_ci				       funcs, formats, num_formats,
53662306a36Sopenharmony_ci				       NULL, type, NULL);
53762306a36Sopenharmony_ci	if (err)
53862306a36Sopenharmony_ci		goto free_plane;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	drm_plane_helper_add(plane, helper_funcs);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	return plane;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cifree_plane:
54562306a36Sopenharmony_ci	kfree(plane);
54662306a36Sopenharmony_ci	return ERR_PTR(-EINVAL);
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic struct vbox_crtc *vbox_crtc_init(struct drm_device *dev, unsigned int i)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	struct vbox_private *vbox =
55262306a36Sopenharmony_ci		container_of(dev, struct vbox_private, ddev);
55362306a36Sopenharmony_ci	struct drm_plane *cursor = NULL;
55462306a36Sopenharmony_ci	struct vbox_crtc *vbox_crtc;
55562306a36Sopenharmony_ci	struct drm_plane *primary;
55662306a36Sopenharmony_ci	u32 caps = 0;
55762306a36Sopenharmony_ci	int ret;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	ret = hgsmi_query_conf(vbox->guest_pool,
56062306a36Sopenharmony_ci			       VBOX_VBVA_CONF32_CURSOR_CAPABILITIES, &caps);
56162306a36Sopenharmony_ci	if (ret)
56262306a36Sopenharmony_ci		return ERR_PTR(ret);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	vbox_crtc = kzalloc(sizeof(*vbox_crtc), GFP_KERNEL);
56562306a36Sopenharmony_ci	if (!vbox_crtc)
56662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	primary = vbox_create_plane(vbox, 1 << i, DRM_PLANE_TYPE_PRIMARY);
56962306a36Sopenharmony_ci	if (IS_ERR(primary)) {
57062306a36Sopenharmony_ci		ret = PTR_ERR(primary);
57162306a36Sopenharmony_ci		goto free_mem;
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	if ((caps & VBOX_VBVA_CURSOR_CAPABILITY_HARDWARE)) {
57562306a36Sopenharmony_ci		cursor = vbox_create_plane(vbox, 1 << i, DRM_PLANE_TYPE_CURSOR);
57662306a36Sopenharmony_ci		if (IS_ERR(cursor)) {
57762306a36Sopenharmony_ci			ret = PTR_ERR(cursor);
57862306a36Sopenharmony_ci			goto clean_primary;
57962306a36Sopenharmony_ci		}
58062306a36Sopenharmony_ci	} else {
58162306a36Sopenharmony_ci		DRM_WARN("VirtualBox host is too old, no cursor support\n");
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	vbox_crtc->crtc_id = i;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	ret = drm_crtc_init_with_planes(dev, &vbox_crtc->base, primary, cursor,
58762306a36Sopenharmony_ci					&vbox_crtc_funcs, NULL);
58862306a36Sopenharmony_ci	if (ret)
58962306a36Sopenharmony_ci		goto clean_cursor;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	drm_mode_crtc_set_gamma_size(&vbox_crtc->base, 256);
59262306a36Sopenharmony_ci	drm_crtc_helper_add(&vbox_crtc->base, &vbox_crtc_helper_funcs);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	return vbox_crtc;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ciclean_cursor:
59762306a36Sopenharmony_ci	if (cursor) {
59862306a36Sopenharmony_ci		drm_plane_cleanup(cursor);
59962306a36Sopenharmony_ci		kfree(cursor);
60062306a36Sopenharmony_ci	}
60162306a36Sopenharmony_ciclean_primary:
60262306a36Sopenharmony_ci	drm_plane_cleanup(primary);
60362306a36Sopenharmony_ci	kfree(primary);
60462306a36Sopenharmony_cifree_mem:
60562306a36Sopenharmony_ci	kfree(vbox_crtc);
60662306a36Sopenharmony_ci	return ERR_PTR(ret);
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic void vbox_encoder_destroy(struct drm_encoder *encoder)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	drm_encoder_cleanup(encoder);
61262306a36Sopenharmony_ci	kfree(encoder);
61362306a36Sopenharmony_ci}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_cistatic const struct drm_encoder_funcs vbox_enc_funcs = {
61662306a36Sopenharmony_ci	.destroy = vbox_encoder_destroy,
61762306a36Sopenharmony_ci};
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cistatic struct drm_encoder *vbox_encoder_init(struct drm_device *dev,
62062306a36Sopenharmony_ci					     unsigned int i)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	struct vbox_encoder *vbox_encoder;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	vbox_encoder = kzalloc(sizeof(*vbox_encoder), GFP_KERNEL);
62562306a36Sopenharmony_ci	if (!vbox_encoder)
62662306a36Sopenharmony_ci		return NULL;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	drm_encoder_init(dev, &vbox_encoder->base, &vbox_enc_funcs,
62962306a36Sopenharmony_ci			 DRM_MODE_ENCODER_DAC, NULL);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	vbox_encoder->base.possible_crtcs = 1 << i;
63262306a36Sopenharmony_ci	return &vbox_encoder->base;
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci/*
63662306a36Sopenharmony_ci * Generate EDID data with a mode-unique serial number for the virtual
63762306a36Sopenharmony_ci * monitor to try to persuade Unity that different modes correspond to
63862306a36Sopenharmony_ci * different monitors and it should not try to force the same resolution on
63962306a36Sopenharmony_ci * them.
64062306a36Sopenharmony_ci */
64162306a36Sopenharmony_cistatic void vbox_set_edid(struct drm_connector *connector, int width,
64262306a36Sopenharmony_ci			  int height)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	enum { EDID_SIZE = 128 };
64562306a36Sopenharmony_ci	unsigned char edid[EDID_SIZE] = {
64662306a36Sopenharmony_ci		0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,	/* header */
64762306a36Sopenharmony_ci		0x58, 0x58,	/* manufacturer (VBX) */
64862306a36Sopenharmony_ci		0x00, 0x00,	/* product code */
64962306a36Sopenharmony_ci		0x00, 0x00, 0x00, 0x00,	/* serial number goes here */
65062306a36Sopenharmony_ci		0x01,		/* week of manufacture */
65162306a36Sopenharmony_ci		0x00,		/* year of manufacture */
65262306a36Sopenharmony_ci		0x01, 0x03,	/* EDID version */
65362306a36Sopenharmony_ci		0x80,		/* capabilities - digital */
65462306a36Sopenharmony_ci		0x00,		/* horiz. res in cm, zero for projectors */
65562306a36Sopenharmony_ci		0x00,		/* vert. res in cm */
65662306a36Sopenharmony_ci		0x78,		/* display gamma (120 == 2.2). */
65762306a36Sopenharmony_ci		0xEE,		/* features (standby, suspend, off, RGB, std */
65862306a36Sopenharmony_ci				/* colour space, preferred timing mode) */
65962306a36Sopenharmony_ci		0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54,
66062306a36Sopenharmony_ci		/* chromaticity for standard colour space. */
66162306a36Sopenharmony_ci		0x00, 0x00, 0x00,	/* no default timings */
66262306a36Sopenharmony_ci		0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
66362306a36Sopenharmony_ci		    0x01, 0x01,
66462306a36Sopenharmony_ci		0x01, 0x01, 0x01, 0x01,	/* no standard timings */
66562306a36Sopenharmony_ci		0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x02, 0x02,
66662306a36Sopenharmony_ci		    0x02, 0x02,
66762306a36Sopenharmony_ci		/* descriptor block 1 goes below */
66862306a36Sopenharmony_ci		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
66962306a36Sopenharmony_ci		/* descriptor block 2, monitor ranges */
67062306a36Sopenharmony_ci		0x00, 0x00, 0x00, 0xFD, 0x00,
67162306a36Sopenharmony_ci		0x00, 0xC8, 0x00, 0xC8, 0x64, 0x00, 0x0A, 0x20, 0x20, 0x20,
67262306a36Sopenharmony_ci		    0x20, 0x20,
67362306a36Sopenharmony_ci		/* 0-200Hz vertical, 0-200KHz horizontal, 1000MHz pixel clock */
67462306a36Sopenharmony_ci		0x20,
67562306a36Sopenharmony_ci		/* descriptor block 3, monitor name */
67662306a36Sopenharmony_ci		0x00, 0x00, 0x00, 0xFC, 0x00,
67762306a36Sopenharmony_ci		'V', 'B', 'O', 'X', ' ', 'm', 'o', 'n', 'i', 't', 'o', 'r',
67862306a36Sopenharmony_ci		'\n',
67962306a36Sopenharmony_ci		/* descriptor block 4: dummy data */
68062306a36Sopenharmony_ci		0x00, 0x00, 0x00, 0x10, 0x00,
68162306a36Sopenharmony_ci		0x0A, 0x20, 0x20, 0x20, 0x20, 0x20,
68262306a36Sopenharmony_ci		0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
68362306a36Sopenharmony_ci		0x20,
68462306a36Sopenharmony_ci		0x00,		/* number of extensions */
68562306a36Sopenharmony_ci		0x00		/* checksum goes here */
68662306a36Sopenharmony_ci	};
68762306a36Sopenharmony_ci	int clock = (width + 6) * (height + 6) * 60 / 10000;
68862306a36Sopenharmony_ci	unsigned int i, sum = 0;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	edid[12] = width & 0xff;
69162306a36Sopenharmony_ci	edid[13] = width >> 8;
69262306a36Sopenharmony_ci	edid[14] = height & 0xff;
69362306a36Sopenharmony_ci	edid[15] = height >> 8;
69462306a36Sopenharmony_ci	edid[54] = clock & 0xff;
69562306a36Sopenharmony_ci	edid[55] = clock >> 8;
69662306a36Sopenharmony_ci	edid[56] = width & 0xff;
69762306a36Sopenharmony_ci	edid[58] = (width >> 4) & 0xf0;
69862306a36Sopenharmony_ci	edid[59] = height & 0xff;
69962306a36Sopenharmony_ci	edid[61] = (height >> 4) & 0xf0;
70062306a36Sopenharmony_ci	for (i = 0; i < EDID_SIZE - 1; ++i)
70162306a36Sopenharmony_ci		sum += edid[i];
70262306a36Sopenharmony_ci	edid[EDID_SIZE - 1] = (0x100 - (sum & 0xFF)) & 0xFF;
70362306a36Sopenharmony_ci	drm_connector_update_edid_property(connector, (struct edid *)edid);
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic int vbox_get_modes(struct drm_connector *connector)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	struct vbox_connector *vbox_connector = NULL;
70962306a36Sopenharmony_ci	struct drm_display_mode *mode = NULL;
71062306a36Sopenharmony_ci	struct vbox_private *vbox = NULL;
71162306a36Sopenharmony_ci	unsigned int num_modes = 0;
71262306a36Sopenharmony_ci	int preferred_width, preferred_height;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	vbox_connector = to_vbox_connector(connector);
71562306a36Sopenharmony_ci	vbox = to_vbox_dev(connector->dev);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	hgsmi_report_flags_location(vbox->guest_pool, GUEST_HEAP_OFFSET(vbox) +
71862306a36Sopenharmony_ci				    HOST_FLAGS_OFFSET);
71962306a36Sopenharmony_ci	if (vbox_connector->vbox_crtc->crtc_id == 0)
72062306a36Sopenharmony_ci		vbox_report_caps(vbox);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	num_modes = drm_add_modes_noedid(connector, 2560, 1600);
72362306a36Sopenharmony_ci	preferred_width = vbox_connector->mode_hint.width ?
72462306a36Sopenharmony_ci			  vbox_connector->mode_hint.width : 1024;
72562306a36Sopenharmony_ci	preferred_height = vbox_connector->mode_hint.height ?
72662306a36Sopenharmony_ci			   vbox_connector->mode_hint.height : 768;
72762306a36Sopenharmony_ci	mode = drm_cvt_mode(connector->dev, preferred_width, preferred_height,
72862306a36Sopenharmony_ci			    60, false, false, false);
72962306a36Sopenharmony_ci	if (mode) {
73062306a36Sopenharmony_ci		mode->type |= DRM_MODE_TYPE_PREFERRED;
73162306a36Sopenharmony_ci		drm_mode_probed_add(connector, mode);
73262306a36Sopenharmony_ci		++num_modes;
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci	vbox_set_edid(connector, preferred_width, preferred_height);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	if (vbox_connector->vbox_crtc->x_hint != -1)
73762306a36Sopenharmony_ci		drm_object_property_set_value(&connector->base,
73862306a36Sopenharmony_ci			vbox->ddev.mode_config.suggested_x_property,
73962306a36Sopenharmony_ci			vbox_connector->vbox_crtc->x_hint);
74062306a36Sopenharmony_ci	else
74162306a36Sopenharmony_ci		drm_object_property_set_value(&connector->base,
74262306a36Sopenharmony_ci			vbox->ddev.mode_config.suggested_x_property, 0);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	if (vbox_connector->vbox_crtc->y_hint != -1)
74562306a36Sopenharmony_ci		drm_object_property_set_value(&connector->base,
74662306a36Sopenharmony_ci			vbox->ddev.mode_config.suggested_y_property,
74762306a36Sopenharmony_ci			vbox_connector->vbox_crtc->y_hint);
74862306a36Sopenharmony_ci	else
74962306a36Sopenharmony_ci		drm_object_property_set_value(&connector->base,
75062306a36Sopenharmony_ci			vbox->ddev.mode_config.suggested_y_property, 0);
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	return num_modes;
75362306a36Sopenharmony_ci}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_cistatic void vbox_connector_destroy(struct drm_connector *connector)
75662306a36Sopenharmony_ci{
75762306a36Sopenharmony_ci	drm_connector_unregister(connector);
75862306a36Sopenharmony_ci	drm_connector_cleanup(connector);
75962306a36Sopenharmony_ci	kfree(connector);
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic enum drm_connector_status
76362306a36Sopenharmony_civbox_connector_detect(struct drm_connector *connector, bool force)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	struct vbox_connector *vbox_connector;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	vbox_connector = to_vbox_connector(connector);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	return vbox_connector->mode_hint.disconnected ?
77062306a36Sopenharmony_ci	    connector_status_disconnected : connector_status_connected;
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cistatic int vbox_fill_modes(struct drm_connector *connector, u32 max_x,
77462306a36Sopenharmony_ci			   u32 max_y)
77562306a36Sopenharmony_ci{
77662306a36Sopenharmony_ci	struct vbox_connector *vbox_connector;
77762306a36Sopenharmony_ci	struct drm_device *dev;
77862306a36Sopenharmony_ci	struct drm_display_mode *mode, *iterator;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	vbox_connector = to_vbox_connector(connector);
78162306a36Sopenharmony_ci	dev = vbox_connector->base.dev;
78262306a36Sopenharmony_ci	list_for_each_entry_safe(mode, iterator, &connector->modes, head) {
78362306a36Sopenharmony_ci		list_del(&mode->head);
78462306a36Sopenharmony_ci		drm_mode_destroy(dev, mode);
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	return drm_helper_probe_single_connector_modes(connector, max_x, max_y);
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_cistatic const struct drm_connector_helper_funcs vbox_connector_helper_funcs = {
79162306a36Sopenharmony_ci	.get_modes = vbox_get_modes,
79262306a36Sopenharmony_ci};
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_cistatic const struct drm_connector_funcs vbox_connector_funcs = {
79562306a36Sopenharmony_ci	.detect = vbox_connector_detect,
79662306a36Sopenharmony_ci	.fill_modes = vbox_fill_modes,
79762306a36Sopenharmony_ci	.destroy = vbox_connector_destroy,
79862306a36Sopenharmony_ci	.reset = drm_atomic_helper_connector_reset,
79962306a36Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
80062306a36Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
80162306a36Sopenharmony_ci};
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic int vbox_connector_init(struct drm_device *dev,
80462306a36Sopenharmony_ci			       struct vbox_crtc *vbox_crtc,
80562306a36Sopenharmony_ci			       struct drm_encoder *encoder)
80662306a36Sopenharmony_ci{
80762306a36Sopenharmony_ci	struct vbox_connector *vbox_connector;
80862306a36Sopenharmony_ci	struct drm_connector *connector;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	vbox_connector = kzalloc(sizeof(*vbox_connector), GFP_KERNEL);
81162306a36Sopenharmony_ci	if (!vbox_connector)
81262306a36Sopenharmony_ci		return -ENOMEM;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	connector = &vbox_connector->base;
81562306a36Sopenharmony_ci	vbox_connector->vbox_crtc = vbox_crtc;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	drm_connector_init(dev, connector, &vbox_connector_funcs,
81862306a36Sopenharmony_ci			   DRM_MODE_CONNECTOR_VGA);
81962306a36Sopenharmony_ci	drm_connector_helper_add(connector, &vbox_connector_helper_funcs);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	connector->interlace_allowed = 0;
82262306a36Sopenharmony_ci	connector->doublescan_allowed = 0;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	drm_mode_create_suggested_offset_properties(dev);
82562306a36Sopenharmony_ci	drm_object_attach_property(&connector->base,
82662306a36Sopenharmony_ci				   dev->mode_config.suggested_x_property, 0);
82762306a36Sopenharmony_ci	drm_object_attach_property(&connector->base,
82862306a36Sopenharmony_ci				   dev->mode_config.suggested_y_property, 0);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	drm_connector_attach_encoder(connector, encoder);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	return 0;
83362306a36Sopenharmony_ci}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_cistatic const struct drm_mode_config_funcs vbox_mode_funcs = {
83662306a36Sopenharmony_ci	.fb_create = drm_gem_fb_create_with_dirty,
83762306a36Sopenharmony_ci	.mode_valid = drm_vram_helper_mode_valid,
83862306a36Sopenharmony_ci	.atomic_check = drm_atomic_helper_check,
83962306a36Sopenharmony_ci	.atomic_commit = drm_atomic_helper_commit,
84062306a36Sopenharmony_ci};
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ciint vbox_mode_init(struct vbox_private *vbox)
84362306a36Sopenharmony_ci{
84462306a36Sopenharmony_ci	struct drm_device *dev = &vbox->ddev;
84562306a36Sopenharmony_ci	struct drm_encoder *encoder;
84662306a36Sopenharmony_ci	struct vbox_crtc *vbox_crtc;
84762306a36Sopenharmony_ci	unsigned int i;
84862306a36Sopenharmony_ci	int ret;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	drm_mode_config_init(dev);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	dev->mode_config.funcs = (void *)&vbox_mode_funcs;
85362306a36Sopenharmony_ci	dev->mode_config.min_width = 0;
85462306a36Sopenharmony_ci	dev->mode_config.min_height = 0;
85562306a36Sopenharmony_ci	dev->mode_config.preferred_depth = 24;
85662306a36Sopenharmony_ci	dev->mode_config.max_width = VBE_DISPI_MAX_XRES;
85762306a36Sopenharmony_ci	dev->mode_config.max_height = VBE_DISPI_MAX_YRES;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	for (i = 0; i < vbox->num_crtcs; ++i) {
86062306a36Sopenharmony_ci		vbox_crtc = vbox_crtc_init(dev, i);
86162306a36Sopenharmony_ci		if (IS_ERR(vbox_crtc)) {
86262306a36Sopenharmony_ci			ret = PTR_ERR(vbox_crtc);
86362306a36Sopenharmony_ci			goto err_drm_mode_cleanup;
86462306a36Sopenharmony_ci		}
86562306a36Sopenharmony_ci		encoder = vbox_encoder_init(dev, i);
86662306a36Sopenharmony_ci		if (!encoder) {
86762306a36Sopenharmony_ci			ret = -ENOMEM;
86862306a36Sopenharmony_ci			goto err_drm_mode_cleanup;
86962306a36Sopenharmony_ci		}
87062306a36Sopenharmony_ci		ret = vbox_connector_init(dev, vbox_crtc, encoder);
87162306a36Sopenharmony_ci		if (ret)
87262306a36Sopenharmony_ci			goto err_drm_mode_cleanup;
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	drm_mode_config_reset(dev);
87662306a36Sopenharmony_ci	return 0;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_cierr_drm_mode_cleanup:
87962306a36Sopenharmony_ci	drm_mode_config_cleanup(dev);
88062306a36Sopenharmony_ci	return ret;
88162306a36Sopenharmony_ci}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_civoid vbox_mode_fini(struct vbox_private *vbox)
88462306a36Sopenharmony_ci{
88562306a36Sopenharmony_ci	drm_mode_config_cleanup(&vbox->ddev);
88662306a36Sopenharmony_ci}
887