162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2013-2017 Oracle Corporation
462306a36Sopenharmony_ci * This file is based on ast_main.c
562306a36Sopenharmony_ci * Copyright 2012 Red Hat Inc.
662306a36Sopenharmony_ci * Authors: Dave Airlie <airlied@redhat.com>,
762306a36Sopenharmony_ci *          Michael Thayer <michael.thayer@oracle.com,
862306a36Sopenharmony_ci *          Hans de Goede <hdegoede@redhat.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/pci.h>
1262306a36Sopenharmony_ci#include <linux/vbox_err.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <drm/drm_damage_helper.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "vbox_drv.h"
1762306a36Sopenharmony_ci#include "vboxvideo_guest.h"
1862306a36Sopenharmony_ci#include "vboxvideo_vbe.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_civoid vbox_report_caps(struct vbox_private *vbox)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	u32 caps = VBVACAPS_DISABLE_CURSOR_INTEGRATION |
2362306a36Sopenharmony_ci		   VBVACAPS_IRQ | VBVACAPS_USE_VBVA_ONLY;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	/* The host only accepts VIDEO_MODE_HINTS if it is send separately. */
2662306a36Sopenharmony_ci	hgsmi_send_caps_info(vbox->guest_pool, caps);
2762306a36Sopenharmony_ci	caps |= VBVACAPS_VIDEO_MODE_HINTS;
2862306a36Sopenharmony_ci	hgsmi_send_caps_info(vbox->guest_pool, caps);
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic int vbox_accel_init(struct vbox_private *vbox)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(vbox->ddev.dev);
3462306a36Sopenharmony_ci	struct vbva_buffer *vbva;
3562306a36Sopenharmony_ci	unsigned int i;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	vbox->vbva_info = devm_kcalloc(vbox->ddev.dev, vbox->num_crtcs,
3862306a36Sopenharmony_ci				       sizeof(*vbox->vbva_info), GFP_KERNEL);
3962306a36Sopenharmony_ci	if (!vbox->vbva_info)
4062306a36Sopenharmony_ci		return -ENOMEM;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	/* Take a command buffer for each screen from the end of usable VRAM. */
4362306a36Sopenharmony_ci	vbox->available_vram_size -= vbox->num_crtcs * VBVA_MIN_BUFFER_SIZE;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	vbox->vbva_buffers = pci_iomap_range(pdev, 0,
4662306a36Sopenharmony_ci					     vbox->available_vram_size,
4762306a36Sopenharmony_ci					     vbox->num_crtcs *
4862306a36Sopenharmony_ci					     VBVA_MIN_BUFFER_SIZE);
4962306a36Sopenharmony_ci	if (!vbox->vbva_buffers)
5062306a36Sopenharmony_ci		return -ENOMEM;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	for (i = 0; i < vbox->num_crtcs; ++i) {
5362306a36Sopenharmony_ci		vbva_setup_buffer_context(&vbox->vbva_info[i],
5462306a36Sopenharmony_ci					  vbox->available_vram_size +
5562306a36Sopenharmony_ci					  i * VBVA_MIN_BUFFER_SIZE,
5662306a36Sopenharmony_ci					  VBVA_MIN_BUFFER_SIZE);
5762306a36Sopenharmony_ci		vbva = (void __force *)vbox->vbva_buffers +
5862306a36Sopenharmony_ci			i * VBVA_MIN_BUFFER_SIZE;
5962306a36Sopenharmony_ci		if (!vbva_enable(&vbox->vbva_info[i],
6062306a36Sopenharmony_ci				 vbox->guest_pool, vbva, i)) {
6162306a36Sopenharmony_ci			/* very old host or driver error. */
6262306a36Sopenharmony_ci			DRM_ERROR("vboxvideo: vbva_enable failed\n");
6362306a36Sopenharmony_ci		}
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return 0;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic void vbox_accel_fini(struct vbox_private *vbox)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	unsigned int i;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	for (i = 0; i < vbox->num_crtcs; ++i)
7462306a36Sopenharmony_ci		vbva_disable(&vbox->vbva_info[i], vbox->guest_pool, i);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/* Do we support the 4.3 plus mode hint reporting interface? */
7862306a36Sopenharmony_cistatic bool have_hgsmi_mode_hints(struct vbox_private *vbox)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	u32 have_hints, have_cursor;
8162306a36Sopenharmony_ci	int ret;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	ret = hgsmi_query_conf(vbox->guest_pool,
8462306a36Sopenharmony_ci			       VBOX_VBVA_CONF32_MODE_HINT_REPORTING,
8562306a36Sopenharmony_ci			       &have_hints);
8662306a36Sopenharmony_ci	if (ret)
8762306a36Sopenharmony_ci		return false;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	ret = hgsmi_query_conf(vbox->guest_pool,
9062306a36Sopenharmony_ci			       VBOX_VBVA_CONF32_GUEST_CURSOR_REPORTING,
9162306a36Sopenharmony_ci			       &have_cursor);
9262306a36Sopenharmony_ci	if (ret)
9362306a36Sopenharmony_ci		return false;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return have_hints == VINF_SUCCESS && have_cursor == VINF_SUCCESS;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cibool vbox_check_supported(u16 id)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	u16 dispi_id;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	vbox_write_ioport(VBE_DISPI_INDEX_ID, id);
10362306a36Sopenharmony_ci	dispi_id = inw(VBE_DISPI_IOPORT_DATA);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	return dispi_id == id;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ciint vbox_hw_init(struct vbox_private *vbox)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(vbox->ddev.dev);
11162306a36Sopenharmony_ci	int ret = -ENOMEM;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	vbox->full_vram_size = inl(VBE_DISPI_IOPORT_DATA);
11462306a36Sopenharmony_ci	vbox->any_pitch = vbox_check_supported(VBE_DISPI_ID_ANYX);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	DRM_INFO("VRAM %08x\n", vbox->full_vram_size);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/* Map guest-heap at end of vram */
11962306a36Sopenharmony_ci	vbox->guest_heap =
12062306a36Sopenharmony_ci	    pci_iomap_range(pdev, 0, GUEST_HEAP_OFFSET(vbox),
12162306a36Sopenharmony_ci			    GUEST_HEAP_SIZE);
12262306a36Sopenharmony_ci	if (!vbox->guest_heap)
12362306a36Sopenharmony_ci		return -ENOMEM;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* Create guest-heap mem-pool use 2^4 = 16 byte chunks */
12662306a36Sopenharmony_ci	vbox->guest_pool = devm_gen_pool_create(vbox->ddev.dev, 4, -1,
12762306a36Sopenharmony_ci						"vboxvideo-accel");
12862306a36Sopenharmony_ci	if (IS_ERR(vbox->guest_pool))
12962306a36Sopenharmony_ci		return PTR_ERR(vbox->guest_pool);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	ret = gen_pool_add_virt(vbox->guest_pool,
13262306a36Sopenharmony_ci				(unsigned long)vbox->guest_heap,
13362306a36Sopenharmony_ci				GUEST_HEAP_OFFSET(vbox),
13462306a36Sopenharmony_ci				GUEST_HEAP_USABLE_SIZE, -1);
13562306a36Sopenharmony_ci	if (ret)
13662306a36Sopenharmony_ci		return ret;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	ret = hgsmi_test_query_conf(vbox->guest_pool);
13962306a36Sopenharmony_ci	if (ret) {
14062306a36Sopenharmony_ci		DRM_ERROR("vboxvideo: hgsmi_test_query_conf failed\n");
14162306a36Sopenharmony_ci		return ret;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* Reduce available VRAM size to reflect the guest heap. */
14562306a36Sopenharmony_ci	vbox->available_vram_size = GUEST_HEAP_OFFSET(vbox);
14662306a36Sopenharmony_ci	/* Linux drm represents monitors as a 32-bit array. */
14762306a36Sopenharmony_ci	hgsmi_query_conf(vbox->guest_pool, VBOX_VBVA_CONF32_MONITOR_COUNT,
14862306a36Sopenharmony_ci			 &vbox->num_crtcs);
14962306a36Sopenharmony_ci	vbox->num_crtcs = clamp_t(u32, vbox->num_crtcs, 1, VBOX_MAX_SCREENS);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (!have_hgsmi_mode_hints(vbox)) {
15262306a36Sopenharmony_ci		ret = -ENOTSUPP;
15362306a36Sopenharmony_ci		return ret;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	vbox->last_mode_hints = devm_kcalloc(vbox->ddev.dev, vbox->num_crtcs,
15762306a36Sopenharmony_ci					     sizeof(struct vbva_modehint),
15862306a36Sopenharmony_ci					     GFP_KERNEL);
15962306a36Sopenharmony_ci	if (!vbox->last_mode_hints)
16062306a36Sopenharmony_ci		return -ENOMEM;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	ret = vbox_accel_init(vbox);
16362306a36Sopenharmony_ci	if (ret)
16462306a36Sopenharmony_ci		return ret;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return 0;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_civoid vbox_hw_fini(struct vbox_private *vbox)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	vbox_accel_fini(vbox);
17262306a36Sopenharmony_ci}
173