5854059457
The bochs driver (and virtual hardware) requires buffer objects to reside in video ram to display them to the screen. So it can not display the framebuffer console because the respective buffer object is permanently pinned in system memory. Using a shadow buffer for the console solves this problem. The console emulation will pin the buffer object only during updates from the shadow buffer. Otherwise, the bochs driver can freely relocated the buffer between system memory and video ram. v2: * select shadow FB via struct drm_mode_config.prefer_shadow_fbdev Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de> Acked-by: Noralf Trønnes <noralf@tronnes.org> Link: https://patchwork.freedesktop.org/patch/315833/ Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
218 lines
6.0 KiB
C
218 lines
6.0 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
*/
|
|
|
|
#include "bochs.h"
|
|
#include <drm/drm_atomic_helper.h>
|
|
#include <drm/drm_plane_helper.h>
|
|
#include <drm/drm_atomic_uapi.h>
|
|
#include <drm/drm_gem_framebuffer_helper.h>
|
|
#include <drm/drm_probe_helper.h>
|
|
|
|
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 const uint32_t bochs_formats[] = {
|
|
DRM_FORMAT_XRGB8888,
|
|
DRM_FORMAT_BGRX8888,
|
|
};
|
|
|
|
static void bochs_plane_update(struct bochs_device *bochs,
|
|
struct drm_plane_state *state)
|
|
{
|
|
struct drm_gem_vram_object *gbo;
|
|
|
|
if (!state->fb || !bochs->stride)
|
|
return;
|
|
|
|
gbo = drm_gem_vram_of_gem(state->fb->obj[0]);
|
|
bochs_hw_setbase(bochs,
|
|
state->crtc_x,
|
|
state->crtc_y,
|
|
state->fb->pitches[0],
|
|
state->fb->offsets[0] + gbo->bo.offset);
|
|
bochs_hw_setformat(bochs, state->fb->format);
|
|
}
|
|
|
|
static void bochs_pipe_enable(struct drm_simple_display_pipe *pipe,
|
|
struct drm_crtc_state *crtc_state,
|
|
struct drm_plane_state *plane_state)
|
|
{
|
|
struct bochs_device *bochs = pipe->crtc.dev->dev_private;
|
|
|
|
bochs_hw_setmode(bochs, &crtc_state->mode);
|
|
bochs_plane_update(bochs, plane_state);
|
|
}
|
|
|
|
static void bochs_pipe_update(struct drm_simple_display_pipe *pipe,
|
|
struct drm_plane_state *old_state)
|
|
{
|
|
struct bochs_device *bochs = pipe->crtc.dev->dev_private;
|
|
struct drm_crtc *crtc = &pipe->crtc;
|
|
|
|
bochs_plane_update(bochs, pipe->plane.state);
|
|
|
|
if (crtc->state->event) {
|
|
spin_lock_irq(&crtc->dev->event_lock);
|
|
drm_crtc_send_vblank_event(crtc, crtc->state->event);
|
|
crtc->state->event = NULL;
|
|
spin_unlock_irq(&crtc->dev->event_lock);
|
|
}
|
|
}
|
|
|
|
static int bochs_pipe_prepare_fb(struct drm_simple_display_pipe *pipe,
|
|
struct drm_plane_state *new_state)
|
|
{
|
|
struct drm_gem_vram_object *gbo;
|
|
|
|
if (!new_state->fb)
|
|
return 0;
|
|
gbo = drm_gem_vram_of_gem(new_state->fb->obj[0]);
|
|
return drm_gem_vram_pin(gbo, DRM_GEM_VRAM_PL_FLAG_VRAM);
|
|
}
|
|
|
|
static void bochs_pipe_cleanup_fb(struct drm_simple_display_pipe *pipe,
|
|
struct drm_plane_state *old_state)
|
|
{
|
|
struct drm_gem_vram_object *gbo;
|
|
|
|
if (!old_state->fb)
|
|
return;
|
|
gbo = drm_gem_vram_of_gem(old_state->fb->obj[0]);
|
|
drm_gem_vram_unpin(gbo);
|
|
}
|
|
|
|
static const struct drm_simple_display_pipe_funcs bochs_pipe_funcs = {
|
|
.enable = bochs_pipe_enable,
|
|
.update = bochs_pipe_update,
|
|
.prepare_fb = bochs_pipe_prepare_fb,
|
|
.cleanup_fb = bochs_pipe_cleanup_fb,
|
|
};
|
|
|
|
static int bochs_connector_get_modes(struct drm_connector *connector)
|
|
{
|
|
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);
|
|
|
|
if (!count) {
|
|
count = drm_add_modes_noedid(connector, 8192, 8192);
|
|
drm_set_preferred_mode(connector, defx, defy);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static enum drm_mode_status bochs_connector_mode_valid(struct drm_connector *connector,
|
|
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 const struct drm_connector_helper_funcs bochs_connector_connector_helper_funcs = {
|
|
.get_modes = bochs_connector_get_modes,
|
|
.mode_valid = bochs_connector_mode_valid,
|
|
};
|
|
|
|
static const struct drm_connector_funcs bochs_connector_connector_funcs = {
|
|
.dpms = drm_helper_connector_dpms,
|
|
.fill_modes = drm_helper_probe_single_connector_modes,
|
|
.destroy = drm_connector_cleanup,
|
|
.reset = drm_atomic_helper_connector_reset,
|
|
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
|
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
|
};
|
|
|
|
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);
|
|
drm_connector_register(connector);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
static struct drm_framebuffer *
|
|
bochs_gem_fb_create(struct drm_device *dev, struct drm_file *file,
|
|
const struct drm_mode_fb_cmd2 *mode_cmd)
|
|
{
|
|
if (mode_cmd->pixel_format != DRM_FORMAT_XRGB8888 &&
|
|
mode_cmd->pixel_format != DRM_FORMAT_BGRX8888)
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
return drm_gem_fb_create(dev, file, mode_cmd);
|
|
}
|
|
|
|
const struct drm_mode_config_funcs bochs_mode_funcs = {
|
|
.fb_create = bochs_gem_fb_create,
|
|
.atomic_check = drm_atomic_helper_check,
|
|
.atomic_commit = drm_atomic_helper_commit,
|
|
};
|
|
|
|
int bochs_kms_init(struct bochs_device *bochs)
|
|
{
|
|
drm_mode_config_init(bochs->dev);
|
|
|
|
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;
|
|
bochs->dev->mode_config.prefer_shadow_fbdev = 1;
|
|
bochs->dev->mode_config.quirk_addfb_prefer_host_byte_order = true;
|
|
|
|
bochs->dev->mode_config.funcs = &bochs_mode_funcs;
|
|
|
|
bochs_connector_init(bochs->dev);
|
|
drm_simple_display_pipe_init(bochs->dev,
|
|
&bochs->pipe,
|
|
&bochs_pipe_funcs,
|
|
bochs_formats,
|
|
ARRAY_SIZE(bochs_formats),
|
|
NULL,
|
|
&bochs->connector);
|
|
|
|
drm_mode_config_reset(bochs->dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void bochs_kms_fini(struct bochs_device *bochs)
|
|
{
|
|
drm_atomic_helper_shutdown(bochs->dev);
|
|
drm_mode_config_cleanup(bochs->dev);
|
|
}
|