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