18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: MIT 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2013-2017 Oracle Corporation 48c2ecf20Sopenharmony_ci * This file is based on ast_mode.c 58c2ecf20Sopenharmony_ci * Copyright 2012 Red Hat Inc. 68c2ecf20Sopenharmony_ci * Parts based on xf86-video-ast 78c2ecf20Sopenharmony_ci * Copyright (c) 2005 ASPEED Technology Inc. 88c2ecf20Sopenharmony_ci * Authors: Dave Airlie <airlied@redhat.com> 98c2ecf20Sopenharmony_ci * Michael Thayer <michael.thayer@oracle.com, 108c2ecf20Sopenharmony_ci * Hans de Goede <hdegoede@redhat.com> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci#include <linux/export.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h> 158c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 168c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 178c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 188c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 198c2ecf20Sopenharmony_ci#include <drm/drm_plane_helper.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "hgsmi_channels.h" 238c2ecf20Sopenharmony_ci#include "vbox_drv.h" 248c2ecf20Sopenharmony_ci#include "vboxvideo.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * Set a graphics mode. Poke any required values into registers, do an HGSMI 288c2ecf20Sopenharmony_ci * mode set and tell the host we support advanced graphics functions. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_cistatic void vbox_do_modeset(struct drm_crtc *crtc) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = crtc->primary->state->fb; 338c2ecf20Sopenharmony_ci struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc); 348c2ecf20Sopenharmony_ci struct vbox_private *vbox; 358c2ecf20Sopenharmony_ci int width, height, bpp, pitch; 368c2ecf20Sopenharmony_ci u16 flags; 378c2ecf20Sopenharmony_ci s32 x_offset, y_offset; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci vbox = to_vbox_dev(crtc->dev); 408c2ecf20Sopenharmony_ci width = vbox_crtc->width ? vbox_crtc->width : 640; 418c2ecf20Sopenharmony_ci height = vbox_crtc->height ? vbox_crtc->height : 480; 428c2ecf20Sopenharmony_ci bpp = fb ? fb->format->cpp[0] * 8 : 32; 438c2ecf20Sopenharmony_ci pitch = fb ? fb->pitches[0] : width * bpp / 8; 448c2ecf20Sopenharmony_ci x_offset = vbox->single_framebuffer ? vbox_crtc->x : vbox_crtc->x_hint; 458c2ecf20Sopenharmony_ci y_offset = vbox->single_framebuffer ? vbox_crtc->y : vbox_crtc->y_hint; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci /* 488c2ecf20Sopenharmony_ci * This is the old way of setting graphics modes. It assumed one screen 498c2ecf20Sopenharmony_ci * and a frame-buffer at the start of video RAM. On older versions of 508c2ecf20Sopenharmony_ci * VirtualBox, certain parts of the code still assume that the first 518c2ecf20Sopenharmony_ci * screen is programmed this way, so try to fake it. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ci if (vbox_crtc->crtc_id == 0 && fb && 548c2ecf20Sopenharmony_ci vbox_crtc->fb_offset / pitch < 0xffff - crtc->y && 558c2ecf20Sopenharmony_ci vbox_crtc->fb_offset % (bpp / 8) == 0) { 568c2ecf20Sopenharmony_ci vbox_write_ioport(VBE_DISPI_INDEX_XRES, width); 578c2ecf20Sopenharmony_ci vbox_write_ioport(VBE_DISPI_INDEX_YRES, height); 588c2ecf20Sopenharmony_ci vbox_write_ioport(VBE_DISPI_INDEX_VIRT_WIDTH, pitch * 8 / bpp); 598c2ecf20Sopenharmony_ci vbox_write_ioport(VBE_DISPI_INDEX_BPP, bpp); 608c2ecf20Sopenharmony_ci vbox_write_ioport(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED); 618c2ecf20Sopenharmony_ci vbox_write_ioport(VBE_DISPI_INDEX_X_OFFSET, 628c2ecf20Sopenharmony_ci vbox_crtc->fb_offset % pitch / bpp * 8 + vbox_crtc->x); 638c2ecf20Sopenharmony_ci vbox_write_ioport(VBE_DISPI_INDEX_Y_OFFSET, 648c2ecf20Sopenharmony_ci vbox_crtc->fb_offset / pitch + vbox_crtc->y); 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci flags = VBVA_SCREEN_F_ACTIVE; 688c2ecf20Sopenharmony_ci flags |= (fb && crtc->state->enable) ? 0 : VBVA_SCREEN_F_BLANK; 698c2ecf20Sopenharmony_ci flags |= vbox_crtc->disconnected ? VBVA_SCREEN_F_DISABLED : 0; 708c2ecf20Sopenharmony_ci hgsmi_process_display_info(vbox->guest_pool, vbox_crtc->crtc_id, 718c2ecf20Sopenharmony_ci x_offset, y_offset, 728c2ecf20Sopenharmony_ci vbox_crtc->x * bpp / 8 + 738c2ecf20Sopenharmony_ci vbox_crtc->y * pitch, 748c2ecf20Sopenharmony_ci pitch, width, height, bpp, flags); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int vbox_set_view(struct drm_crtc *crtc) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc); 808c2ecf20Sopenharmony_ci struct vbox_private *vbox = to_vbox_dev(crtc->dev); 818c2ecf20Sopenharmony_ci struct vbva_infoview *p; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* 848c2ecf20Sopenharmony_ci * Tell the host about the view. This design originally targeted the 858c2ecf20Sopenharmony_ci * Windows XP driver architecture and assumed that each screen would 868c2ecf20Sopenharmony_ci * have a dedicated frame buffer with the command buffer following it, 878c2ecf20Sopenharmony_ci * the whole being a "view". The host works out which screen a command 888c2ecf20Sopenharmony_ci * buffer belongs to by checking whether it is in the first view, then 898c2ecf20Sopenharmony_ci * whether it is in the second and so on. The first match wins. We 908c2ecf20Sopenharmony_ci * cheat around this by making the first view be the managed memory 918c2ecf20Sopenharmony_ci * plus the first command buffer, the second the same plus the second 928c2ecf20Sopenharmony_ci * buffer and so on. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ci p = hgsmi_buffer_alloc(vbox->guest_pool, sizeof(*p), 958c2ecf20Sopenharmony_ci HGSMI_CH_VBVA, VBVA_INFO_VIEW); 968c2ecf20Sopenharmony_ci if (!p) 978c2ecf20Sopenharmony_ci return -ENOMEM; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci p->view_index = vbox_crtc->crtc_id; 1008c2ecf20Sopenharmony_ci p->view_offset = vbox_crtc->fb_offset; 1018c2ecf20Sopenharmony_ci p->view_size = vbox->available_vram_size - vbox_crtc->fb_offset + 1028c2ecf20Sopenharmony_ci vbox_crtc->crtc_id * VBVA_MIN_BUFFER_SIZE; 1038c2ecf20Sopenharmony_ci p->max_screen_size = vbox->available_vram_size - vbox_crtc->fb_offset; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci hgsmi_buffer_submit(vbox->guest_pool, p); 1068c2ecf20Sopenharmony_ci hgsmi_buffer_free(vbox->guest_pool, p); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return 0; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* 1128c2ecf20Sopenharmony_ci * Try to map the layout of virtual screens to the range of the input device. 1138c2ecf20Sopenharmony_ci * Return true if we need to re-set the crtc modes due to screen offset 1148c2ecf20Sopenharmony_ci * changes. 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_cistatic bool vbox_set_up_input_mapping(struct vbox_private *vbox) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct drm_crtc *crtci; 1198c2ecf20Sopenharmony_ci struct drm_connector *connectori; 1208c2ecf20Sopenharmony_ci struct drm_framebuffer *fb, *fb1 = NULL; 1218c2ecf20Sopenharmony_ci bool single_framebuffer = true; 1228c2ecf20Sopenharmony_ci bool old_single_framebuffer = vbox->single_framebuffer; 1238c2ecf20Sopenharmony_ci u16 width = 0, height = 0; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* 1268c2ecf20Sopenharmony_ci * Are we using an X.Org-style single large frame-buffer for all crtcs? 1278c2ecf20Sopenharmony_ci * If so then screen layout can be deduced from the crtc offsets. 1288c2ecf20Sopenharmony_ci * Same fall-back if this is the fbdev frame-buffer. 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_ci list_for_each_entry(crtci, &vbox->ddev.mode_config.crtc_list, head) { 1318c2ecf20Sopenharmony_ci fb = crtci->primary->state->fb; 1328c2ecf20Sopenharmony_ci if (!fb) 1338c2ecf20Sopenharmony_ci continue; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (!fb1) { 1368c2ecf20Sopenharmony_ci fb1 = fb; 1378c2ecf20Sopenharmony_ci if (fb1 == vbox->ddev.fb_helper->fb) 1388c2ecf20Sopenharmony_ci break; 1398c2ecf20Sopenharmony_ci } else if (fb != fb1) { 1408c2ecf20Sopenharmony_ci single_framebuffer = false; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci if (!fb1) 1448c2ecf20Sopenharmony_ci return false; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (single_framebuffer) { 1478c2ecf20Sopenharmony_ci vbox->single_framebuffer = true; 1488c2ecf20Sopenharmony_ci vbox->input_mapping_width = fb1->width; 1498c2ecf20Sopenharmony_ci vbox->input_mapping_height = fb1->height; 1508c2ecf20Sopenharmony_ci return old_single_framebuffer != vbox->single_framebuffer; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci /* Otherwise calculate the total span of all screens. */ 1538c2ecf20Sopenharmony_ci list_for_each_entry(connectori, &vbox->ddev.mode_config.connector_list, 1548c2ecf20Sopenharmony_ci head) { 1558c2ecf20Sopenharmony_ci struct vbox_connector *vbox_connector = 1568c2ecf20Sopenharmony_ci to_vbox_connector(connectori); 1578c2ecf20Sopenharmony_ci struct vbox_crtc *vbox_crtc = vbox_connector->vbox_crtc; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci width = max_t(u16, width, vbox_crtc->x_hint + 1608c2ecf20Sopenharmony_ci vbox_connector->mode_hint.width); 1618c2ecf20Sopenharmony_ci height = max_t(u16, height, vbox_crtc->y_hint + 1628c2ecf20Sopenharmony_ci vbox_connector->mode_hint.height); 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci vbox->single_framebuffer = false; 1668c2ecf20Sopenharmony_ci vbox->input_mapping_width = width; 1678c2ecf20Sopenharmony_ci vbox->input_mapping_height = height; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return old_single_framebuffer != vbox->single_framebuffer; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic void vbox_crtc_set_base_and_mode(struct drm_crtc *crtc, 1738c2ecf20Sopenharmony_ci struct drm_framebuffer *fb, 1748c2ecf20Sopenharmony_ci int x, int y) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(fb->obj[0]); 1778c2ecf20Sopenharmony_ci struct vbox_private *vbox = to_vbox_dev(crtc->dev); 1788c2ecf20Sopenharmony_ci struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc); 1798c2ecf20Sopenharmony_ci bool needs_modeset = drm_atomic_crtc_needs_modeset(crtc->state); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci mutex_lock(&vbox->hw_mutex); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (crtc->state->enable) { 1848c2ecf20Sopenharmony_ci vbox_crtc->width = crtc->state->mode.hdisplay; 1858c2ecf20Sopenharmony_ci vbox_crtc->height = crtc->state->mode.vdisplay; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci vbox_crtc->x = x; 1898c2ecf20Sopenharmony_ci vbox_crtc->y = y; 1908c2ecf20Sopenharmony_ci vbox_crtc->fb_offset = drm_gem_vram_offset(gbo); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* vbox_do_modeset() checks vbox->single_framebuffer so update it now */ 1938c2ecf20Sopenharmony_ci if (needs_modeset && vbox_set_up_input_mapping(vbox)) { 1948c2ecf20Sopenharmony_ci struct drm_crtc *crtci; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci list_for_each_entry(crtci, &vbox->ddev.mode_config.crtc_list, 1978c2ecf20Sopenharmony_ci head) { 1988c2ecf20Sopenharmony_ci if (crtci == crtc) 1998c2ecf20Sopenharmony_ci continue; 2008c2ecf20Sopenharmony_ci vbox_do_modeset(crtci); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci vbox_set_view(crtc); 2058c2ecf20Sopenharmony_ci vbox_do_modeset(crtc); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (needs_modeset) 2088c2ecf20Sopenharmony_ci hgsmi_update_input_mapping(vbox->guest_pool, 0, 0, 2098c2ecf20Sopenharmony_ci vbox->input_mapping_width, 2108c2ecf20Sopenharmony_ci vbox->input_mapping_height); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci mutex_unlock(&vbox->hw_mutex); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic void vbox_crtc_atomic_enable(struct drm_crtc *crtc, 2168c2ecf20Sopenharmony_ci struct drm_crtc_state *old_crtc_state) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic void vbox_crtc_atomic_disable(struct drm_crtc *crtc, 2218c2ecf20Sopenharmony_ci struct drm_crtc_state *old_crtc_state) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic void vbox_crtc_atomic_flush(struct drm_crtc *crtc, 2268c2ecf20Sopenharmony_ci struct drm_crtc_state *old_crtc_state) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic const struct drm_crtc_helper_funcs vbox_crtc_helper_funcs = { 2318c2ecf20Sopenharmony_ci .atomic_enable = vbox_crtc_atomic_enable, 2328c2ecf20Sopenharmony_ci .atomic_disable = vbox_crtc_atomic_disable, 2338c2ecf20Sopenharmony_ci .atomic_flush = vbox_crtc_atomic_flush, 2348c2ecf20Sopenharmony_ci}; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic void vbox_crtc_destroy(struct drm_crtc *crtc) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci drm_crtc_cleanup(crtc); 2398c2ecf20Sopenharmony_ci kfree(crtc); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic const struct drm_crtc_funcs vbox_crtc_funcs = { 2438c2ecf20Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 2448c2ecf20Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 2458c2ecf20Sopenharmony_ci /* .gamma_set = vbox_crtc_gamma_set, */ 2468c2ecf20Sopenharmony_ci .destroy = vbox_crtc_destroy, 2478c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_crtc_reset, 2488c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 2498c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 2508c2ecf20Sopenharmony_ci}; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic int vbox_primary_atomic_check(struct drm_plane *plane, 2538c2ecf20Sopenharmony_ci struct drm_plane_state *new_state) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state = NULL; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (new_state->crtc) { 2588c2ecf20Sopenharmony_ci crtc_state = drm_atomic_get_existing_crtc_state( 2598c2ecf20Sopenharmony_ci new_state->state, new_state->crtc); 2608c2ecf20Sopenharmony_ci if (WARN_ON(!crtc_state)) 2618c2ecf20Sopenharmony_ci return -EINVAL; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return drm_atomic_helper_check_plane_state(new_state, crtc_state, 2658c2ecf20Sopenharmony_ci DRM_PLANE_HELPER_NO_SCALING, 2668c2ecf20Sopenharmony_ci DRM_PLANE_HELPER_NO_SCALING, 2678c2ecf20Sopenharmony_ci false, true); 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic void vbox_primary_atomic_update(struct drm_plane *plane, 2718c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct drm_crtc *crtc = plane->state->crtc; 2748c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = plane->state->fb; 2758c2ecf20Sopenharmony_ci struct vbox_private *vbox = to_vbox_dev(fb->dev); 2768c2ecf20Sopenharmony_ci struct drm_mode_rect *clips; 2778c2ecf20Sopenharmony_ci uint32_t num_clips, i; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci vbox_crtc_set_base_and_mode(crtc, fb, 2808c2ecf20Sopenharmony_ci plane->state->src_x >> 16, 2818c2ecf20Sopenharmony_ci plane->state->src_y >> 16); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* Send information about dirty rectangles to VBVA. */ 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci clips = drm_plane_get_damage_clips(plane->state); 2868c2ecf20Sopenharmony_ci num_clips = drm_plane_get_damage_clips_count(plane->state); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (!num_clips) 2898c2ecf20Sopenharmony_ci return; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci mutex_lock(&vbox->hw_mutex); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci for (i = 0; i < num_clips; ++i, ++clips) { 2948c2ecf20Sopenharmony_ci struct vbva_cmd_hdr cmd_hdr; 2958c2ecf20Sopenharmony_ci unsigned int crtc_id = to_vbox_crtc(crtc)->crtc_id; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci cmd_hdr.x = (s16)clips->x1; 2988c2ecf20Sopenharmony_ci cmd_hdr.y = (s16)clips->y1; 2998c2ecf20Sopenharmony_ci cmd_hdr.w = (u16)clips->x2 - clips->x1; 3008c2ecf20Sopenharmony_ci cmd_hdr.h = (u16)clips->y2 - clips->y1; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (!vbva_buffer_begin_update(&vbox->vbva_info[crtc_id], 3038c2ecf20Sopenharmony_ci vbox->guest_pool)) 3048c2ecf20Sopenharmony_ci continue; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci vbva_write(&vbox->vbva_info[crtc_id], vbox->guest_pool, 3078c2ecf20Sopenharmony_ci &cmd_hdr, sizeof(cmd_hdr)); 3088c2ecf20Sopenharmony_ci vbva_buffer_end_update(&vbox->vbva_info[crtc_id]); 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci mutex_unlock(&vbox->hw_mutex); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic void vbox_primary_atomic_disable(struct drm_plane *plane, 3158c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct drm_crtc *crtc = old_state->crtc; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* vbox_do_modeset checks plane->state->fb and will disable if NULL */ 3208c2ecf20Sopenharmony_ci vbox_crtc_set_base_and_mode(crtc, old_state->fb, 3218c2ecf20Sopenharmony_ci old_state->src_x >> 16, 3228c2ecf20Sopenharmony_ci old_state->src_y >> 16); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic int vbox_cursor_atomic_check(struct drm_plane *plane, 3268c2ecf20Sopenharmony_ci struct drm_plane_state *new_state) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state = NULL; 3298c2ecf20Sopenharmony_ci u32 width = new_state->crtc_w; 3308c2ecf20Sopenharmony_ci u32 height = new_state->crtc_h; 3318c2ecf20Sopenharmony_ci int ret; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (new_state->crtc) { 3348c2ecf20Sopenharmony_ci crtc_state = drm_atomic_get_existing_crtc_state( 3358c2ecf20Sopenharmony_ci new_state->state, new_state->crtc); 3368c2ecf20Sopenharmony_ci if (WARN_ON(!crtc_state)) 3378c2ecf20Sopenharmony_ci return -EINVAL; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci ret = drm_atomic_helper_check_plane_state(new_state, crtc_state, 3418c2ecf20Sopenharmony_ci DRM_PLANE_HELPER_NO_SCALING, 3428c2ecf20Sopenharmony_ci DRM_PLANE_HELPER_NO_SCALING, 3438c2ecf20Sopenharmony_ci true, true); 3448c2ecf20Sopenharmony_ci if (ret) 3458c2ecf20Sopenharmony_ci return ret; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (!new_state->fb) 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (width > VBOX_MAX_CURSOR_WIDTH || height > VBOX_MAX_CURSOR_HEIGHT || 3518c2ecf20Sopenharmony_ci width == 0 || height == 0) 3528c2ecf20Sopenharmony_ci return -EINVAL; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci return 0; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci/* 3588c2ecf20Sopenharmony_ci * Copy the ARGB image and generate the mask, which is needed in case the host 3598c2ecf20Sopenharmony_ci * does not support ARGB cursors. The mask is a 1BPP bitmap with the bit set 3608c2ecf20Sopenharmony_ci * if the corresponding alpha value in the ARGB image is greater than 0xF0. 3618c2ecf20Sopenharmony_ci */ 3628c2ecf20Sopenharmony_cistatic void copy_cursor_image(u8 *src, u8 *dst, u32 width, u32 height, 3638c2ecf20Sopenharmony_ci size_t mask_size) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci size_t line_size = (width + 7) / 8; 3668c2ecf20Sopenharmony_ci u32 i, j; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci memcpy(dst + mask_size, src, width * height * 4); 3698c2ecf20Sopenharmony_ci for (i = 0; i < height; ++i) 3708c2ecf20Sopenharmony_ci for (j = 0; j < width; ++j) 3718c2ecf20Sopenharmony_ci if (((u32 *)src)[i * width + j] > 0xf0000000) 3728c2ecf20Sopenharmony_ci dst[i * line_size + j / 8] |= (0x80 >> (j % 8)); 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic void vbox_cursor_atomic_update(struct drm_plane *plane, 3768c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci struct vbox_private *vbox = 3798c2ecf20Sopenharmony_ci container_of(plane->dev, struct vbox_private, ddev); 3808c2ecf20Sopenharmony_ci struct vbox_crtc *vbox_crtc = to_vbox_crtc(plane->state->crtc); 3818c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = plane->state->fb; 3828c2ecf20Sopenharmony_ci struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(fb->obj[0]); 3838c2ecf20Sopenharmony_ci u32 width = plane->state->crtc_w; 3848c2ecf20Sopenharmony_ci u32 height = plane->state->crtc_h; 3858c2ecf20Sopenharmony_ci size_t data_size, mask_size; 3868c2ecf20Sopenharmony_ci u32 flags; 3878c2ecf20Sopenharmony_ci u8 *src; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* 3908c2ecf20Sopenharmony_ci * VirtualBox uses the host windowing system to draw the cursor so 3918c2ecf20Sopenharmony_ci * moves are a no-op, we only need to upload new cursor sprites. 3928c2ecf20Sopenharmony_ci */ 3938c2ecf20Sopenharmony_ci if (fb == old_state->fb) 3948c2ecf20Sopenharmony_ci return; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci mutex_lock(&vbox->hw_mutex); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci vbox_crtc->cursor_enabled = true; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci src = drm_gem_vram_vmap(gbo); 4018c2ecf20Sopenharmony_ci if (IS_ERR(src)) { 4028c2ecf20Sopenharmony_ci /* 4038c2ecf20Sopenharmony_ci * BUG: we should have pinned the BO in prepare_fb(). 4048c2ecf20Sopenharmony_ci */ 4058c2ecf20Sopenharmony_ci mutex_unlock(&vbox->hw_mutex); 4068c2ecf20Sopenharmony_ci DRM_WARN("Could not map cursor bo, skipping update\n"); 4078c2ecf20Sopenharmony_ci return; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* 4118c2ecf20Sopenharmony_ci * The mask must be calculated based on the alpha 4128c2ecf20Sopenharmony_ci * channel, one bit per ARGB word, and must be 32-bit 4138c2ecf20Sopenharmony_ci * padded. 4148c2ecf20Sopenharmony_ci */ 4158c2ecf20Sopenharmony_ci mask_size = ((width + 7) / 8 * height + 3) & ~3; 4168c2ecf20Sopenharmony_ci data_size = width * height * 4 + mask_size; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci copy_cursor_image(src, vbox->cursor_data, width, height, mask_size); 4198c2ecf20Sopenharmony_ci drm_gem_vram_vunmap(gbo, src); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci flags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE | 4228c2ecf20Sopenharmony_ci VBOX_MOUSE_POINTER_ALPHA; 4238c2ecf20Sopenharmony_ci hgsmi_update_pointer_shape(vbox->guest_pool, flags, 4248c2ecf20Sopenharmony_ci min_t(u32, max(fb->hot_x, 0), width), 4258c2ecf20Sopenharmony_ci min_t(u32, max(fb->hot_y, 0), height), 4268c2ecf20Sopenharmony_ci width, height, vbox->cursor_data, data_size); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci mutex_unlock(&vbox->hw_mutex); 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic void vbox_cursor_atomic_disable(struct drm_plane *plane, 4328c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci struct vbox_private *vbox = 4358c2ecf20Sopenharmony_ci container_of(plane->dev, struct vbox_private, ddev); 4368c2ecf20Sopenharmony_ci struct vbox_crtc *vbox_crtc = to_vbox_crtc(old_state->crtc); 4378c2ecf20Sopenharmony_ci bool cursor_enabled = false; 4388c2ecf20Sopenharmony_ci struct drm_crtc *crtci; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci mutex_lock(&vbox->hw_mutex); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci vbox_crtc->cursor_enabled = false; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci list_for_each_entry(crtci, &vbox->ddev.mode_config.crtc_list, head) { 4458c2ecf20Sopenharmony_ci if (to_vbox_crtc(crtci)->cursor_enabled) 4468c2ecf20Sopenharmony_ci cursor_enabled = true; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (!cursor_enabled) 4508c2ecf20Sopenharmony_ci hgsmi_update_pointer_shape(vbox->guest_pool, 0, 0, 0, 4518c2ecf20Sopenharmony_ci 0, 0, NULL, 0); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci mutex_unlock(&vbox->hw_mutex); 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic const u32 vbox_cursor_plane_formats[] = { 4578c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB8888, 4588c2ecf20Sopenharmony_ci}; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic const struct drm_plane_helper_funcs vbox_cursor_helper_funcs = { 4618c2ecf20Sopenharmony_ci .atomic_check = vbox_cursor_atomic_check, 4628c2ecf20Sopenharmony_ci .atomic_update = vbox_cursor_atomic_update, 4638c2ecf20Sopenharmony_ci .atomic_disable = vbox_cursor_atomic_disable, 4648c2ecf20Sopenharmony_ci .prepare_fb = drm_gem_vram_plane_helper_prepare_fb, 4658c2ecf20Sopenharmony_ci .cleanup_fb = drm_gem_vram_plane_helper_cleanup_fb, 4668c2ecf20Sopenharmony_ci}; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic const struct drm_plane_funcs vbox_cursor_plane_funcs = { 4698c2ecf20Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 4708c2ecf20Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 4718c2ecf20Sopenharmony_ci .destroy = drm_primary_helper_destroy, 4728c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_plane_reset, 4738c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 4748c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 4758c2ecf20Sopenharmony_ci}; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic const u32 vbox_primary_plane_formats[] = { 4788c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB8888, 4798c2ecf20Sopenharmony_ci DRM_FORMAT_ARGB8888, 4808c2ecf20Sopenharmony_ci}; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic const struct drm_plane_helper_funcs vbox_primary_helper_funcs = { 4838c2ecf20Sopenharmony_ci .atomic_check = vbox_primary_atomic_check, 4848c2ecf20Sopenharmony_ci .atomic_update = vbox_primary_atomic_update, 4858c2ecf20Sopenharmony_ci .atomic_disable = vbox_primary_atomic_disable, 4868c2ecf20Sopenharmony_ci .prepare_fb = drm_gem_vram_plane_helper_prepare_fb, 4878c2ecf20Sopenharmony_ci .cleanup_fb = drm_gem_vram_plane_helper_cleanup_fb, 4888c2ecf20Sopenharmony_ci}; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic const struct drm_plane_funcs vbox_primary_plane_funcs = { 4918c2ecf20Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 4928c2ecf20Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 4938c2ecf20Sopenharmony_ci .destroy = drm_primary_helper_destroy, 4948c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_plane_reset, 4958c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 4968c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 4978c2ecf20Sopenharmony_ci}; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic struct drm_plane *vbox_create_plane(struct vbox_private *vbox, 5008c2ecf20Sopenharmony_ci unsigned int possible_crtcs, 5018c2ecf20Sopenharmony_ci enum drm_plane_type type) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci const struct drm_plane_helper_funcs *helper_funcs = NULL; 5048c2ecf20Sopenharmony_ci const struct drm_plane_funcs *funcs; 5058c2ecf20Sopenharmony_ci struct drm_plane *plane; 5068c2ecf20Sopenharmony_ci const u32 *formats; 5078c2ecf20Sopenharmony_ci int num_formats; 5088c2ecf20Sopenharmony_ci int err; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (type == DRM_PLANE_TYPE_PRIMARY) { 5118c2ecf20Sopenharmony_ci funcs = &vbox_primary_plane_funcs; 5128c2ecf20Sopenharmony_ci formats = vbox_primary_plane_formats; 5138c2ecf20Sopenharmony_ci helper_funcs = &vbox_primary_helper_funcs; 5148c2ecf20Sopenharmony_ci num_formats = ARRAY_SIZE(vbox_primary_plane_formats); 5158c2ecf20Sopenharmony_ci } else if (type == DRM_PLANE_TYPE_CURSOR) { 5168c2ecf20Sopenharmony_ci funcs = &vbox_cursor_plane_funcs; 5178c2ecf20Sopenharmony_ci formats = vbox_cursor_plane_formats; 5188c2ecf20Sopenharmony_ci helper_funcs = &vbox_cursor_helper_funcs; 5198c2ecf20Sopenharmony_ci num_formats = ARRAY_SIZE(vbox_cursor_plane_formats); 5208c2ecf20Sopenharmony_ci } else { 5218c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci plane = kzalloc(sizeof(*plane), GFP_KERNEL); 5258c2ecf20Sopenharmony_ci if (!plane) 5268c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci err = drm_universal_plane_init(&vbox->ddev, plane, possible_crtcs, 5298c2ecf20Sopenharmony_ci funcs, formats, num_formats, 5308c2ecf20Sopenharmony_ci NULL, type, NULL); 5318c2ecf20Sopenharmony_ci if (err) 5328c2ecf20Sopenharmony_ci goto free_plane; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci drm_plane_helper_add(plane, helper_funcs); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci return plane; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cifree_plane: 5398c2ecf20Sopenharmony_ci kfree(plane); 5408c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic struct vbox_crtc *vbox_crtc_init(struct drm_device *dev, unsigned int i) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci struct vbox_private *vbox = 5468c2ecf20Sopenharmony_ci container_of(dev, struct vbox_private, ddev); 5478c2ecf20Sopenharmony_ci struct drm_plane *cursor = NULL; 5488c2ecf20Sopenharmony_ci struct vbox_crtc *vbox_crtc; 5498c2ecf20Sopenharmony_ci struct drm_plane *primary; 5508c2ecf20Sopenharmony_ci u32 caps = 0; 5518c2ecf20Sopenharmony_ci int ret; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci ret = hgsmi_query_conf(vbox->guest_pool, 5548c2ecf20Sopenharmony_ci VBOX_VBVA_CONF32_CURSOR_CAPABILITIES, &caps); 5558c2ecf20Sopenharmony_ci if (ret) 5568c2ecf20Sopenharmony_ci return ERR_PTR(ret); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci vbox_crtc = kzalloc(sizeof(*vbox_crtc), GFP_KERNEL); 5598c2ecf20Sopenharmony_ci if (!vbox_crtc) 5608c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci primary = vbox_create_plane(vbox, 1 << i, DRM_PLANE_TYPE_PRIMARY); 5638c2ecf20Sopenharmony_ci if (IS_ERR(primary)) { 5648c2ecf20Sopenharmony_ci ret = PTR_ERR(primary); 5658c2ecf20Sopenharmony_ci goto free_mem; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if ((caps & VBOX_VBVA_CURSOR_CAPABILITY_HARDWARE)) { 5698c2ecf20Sopenharmony_ci cursor = vbox_create_plane(vbox, 1 << i, DRM_PLANE_TYPE_CURSOR); 5708c2ecf20Sopenharmony_ci if (IS_ERR(cursor)) { 5718c2ecf20Sopenharmony_ci ret = PTR_ERR(cursor); 5728c2ecf20Sopenharmony_ci goto clean_primary; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci } else { 5758c2ecf20Sopenharmony_ci DRM_WARN("VirtualBox host is too old, no cursor support\n"); 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci vbox_crtc->crtc_id = i; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci ret = drm_crtc_init_with_planes(dev, &vbox_crtc->base, primary, cursor, 5818c2ecf20Sopenharmony_ci &vbox_crtc_funcs, NULL); 5828c2ecf20Sopenharmony_ci if (ret) 5838c2ecf20Sopenharmony_ci goto clean_cursor; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci drm_mode_crtc_set_gamma_size(&vbox_crtc->base, 256); 5868c2ecf20Sopenharmony_ci drm_crtc_helper_add(&vbox_crtc->base, &vbox_crtc_helper_funcs); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci return vbox_crtc; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ciclean_cursor: 5918c2ecf20Sopenharmony_ci if (cursor) { 5928c2ecf20Sopenharmony_ci drm_plane_cleanup(cursor); 5938c2ecf20Sopenharmony_ci kfree(cursor); 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ciclean_primary: 5968c2ecf20Sopenharmony_ci drm_plane_cleanup(primary); 5978c2ecf20Sopenharmony_ci kfree(primary); 5988c2ecf20Sopenharmony_cifree_mem: 5998c2ecf20Sopenharmony_ci kfree(vbox_crtc); 6008c2ecf20Sopenharmony_ci return ERR_PTR(ret); 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic void vbox_encoder_destroy(struct drm_encoder *encoder) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci drm_encoder_cleanup(encoder); 6068c2ecf20Sopenharmony_ci kfree(encoder); 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic const struct drm_encoder_funcs vbox_enc_funcs = { 6108c2ecf20Sopenharmony_ci .destroy = vbox_encoder_destroy, 6118c2ecf20Sopenharmony_ci}; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_cistatic struct drm_encoder *vbox_encoder_init(struct drm_device *dev, 6148c2ecf20Sopenharmony_ci unsigned int i) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci struct vbox_encoder *vbox_encoder; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci vbox_encoder = kzalloc(sizeof(*vbox_encoder), GFP_KERNEL); 6198c2ecf20Sopenharmony_ci if (!vbox_encoder) 6208c2ecf20Sopenharmony_ci return NULL; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci drm_encoder_init(dev, &vbox_encoder->base, &vbox_enc_funcs, 6238c2ecf20Sopenharmony_ci DRM_MODE_ENCODER_DAC, NULL); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci vbox_encoder->base.possible_crtcs = 1 << i; 6268c2ecf20Sopenharmony_ci return &vbox_encoder->base; 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci/* 6308c2ecf20Sopenharmony_ci * Generate EDID data with a mode-unique serial number for the virtual 6318c2ecf20Sopenharmony_ci * monitor to try to persuade Unity that different modes correspond to 6328c2ecf20Sopenharmony_ci * different monitors and it should not try to force the same resolution on 6338c2ecf20Sopenharmony_ci * them. 6348c2ecf20Sopenharmony_ci */ 6358c2ecf20Sopenharmony_cistatic void vbox_set_edid(struct drm_connector *connector, int width, 6368c2ecf20Sopenharmony_ci int height) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci enum { EDID_SIZE = 128 }; 6398c2ecf20Sopenharmony_ci unsigned char edid[EDID_SIZE] = { 6408c2ecf20Sopenharmony_ci 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, /* header */ 6418c2ecf20Sopenharmony_ci 0x58, 0x58, /* manufacturer (VBX) */ 6428c2ecf20Sopenharmony_ci 0x00, 0x00, /* product code */ 6438c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, /* serial number goes here */ 6448c2ecf20Sopenharmony_ci 0x01, /* week of manufacture */ 6458c2ecf20Sopenharmony_ci 0x00, /* year of manufacture */ 6468c2ecf20Sopenharmony_ci 0x01, 0x03, /* EDID version */ 6478c2ecf20Sopenharmony_ci 0x80, /* capabilities - digital */ 6488c2ecf20Sopenharmony_ci 0x00, /* horiz. res in cm, zero for projectors */ 6498c2ecf20Sopenharmony_ci 0x00, /* vert. res in cm */ 6508c2ecf20Sopenharmony_ci 0x78, /* display gamma (120 == 2.2). */ 6518c2ecf20Sopenharmony_ci 0xEE, /* features (standby, suspend, off, RGB, std */ 6528c2ecf20Sopenharmony_ci /* colour space, preferred timing mode) */ 6538c2ecf20Sopenharmony_ci 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54, 6548c2ecf20Sopenharmony_ci /* chromaticity for standard colour space. */ 6558c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, /* no default timings */ 6568c2ecf20Sopenharmony_ci 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 6578c2ecf20Sopenharmony_ci 0x01, 0x01, 6588c2ecf20Sopenharmony_ci 0x01, 0x01, 0x01, 0x01, /* no standard timings */ 6598c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x02, 0x02, 6608c2ecf20Sopenharmony_ci 0x02, 0x02, 6618c2ecf20Sopenharmony_ci /* descriptor block 1 goes below */ 6628c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 6638c2ecf20Sopenharmony_ci /* descriptor block 2, monitor ranges */ 6648c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0xFD, 0x00, 6658c2ecf20Sopenharmony_ci 0x00, 0xC8, 0x00, 0xC8, 0x64, 0x00, 0x0A, 0x20, 0x20, 0x20, 6668c2ecf20Sopenharmony_ci 0x20, 0x20, 6678c2ecf20Sopenharmony_ci /* 0-200Hz vertical, 0-200KHz horizontal, 1000MHz pixel clock */ 6688c2ecf20Sopenharmony_ci 0x20, 6698c2ecf20Sopenharmony_ci /* descriptor block 3, monitor name */ 6708c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0xFC, 0x00, 6718c2ecf20Sopenharmony_ci 'V', 'B', 'O', 'X', ' ', 'm', 'o', 'n', 'i', 't', 'o', 'r', 6728c2ecf20Sopenharmony_ci '\n', 6738c2ecf20Sopenharmony_ci /* descriptor block 4: dummy data */ 6748c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x10, 0x00, 6758c2ecf20Sopenharmony_ci 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 6768c2ecf20Sopenharmony_ci 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 6778c2ecf20Sopenharmony_ci 0x20, 6788c2ecf20Sopenharmony_ci 0x00, /* number of extensions */ 6798c2ecf20Sopenharmony_ci 0x00 /* checksum goes here */ 6808c2ecf20Sopenharmony_ci }; 6818c2ecf20Sopenharmony_ci int clock = (width + 6) * (height + 6) * 60 / 10000; 6828c2ecf20Sopenharmony_ci unsigned int i, sum = 0; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci edid[12] = width & 0xff; 6858c2ecf20Sopenharmony_ci edid[13] = width >> 8; 6868c2ecf20Sopenharmony_ci edid[14] = height & 0xff; 6878c2ecf20Sopenharmony_ci edid[15] = height >> 8; 6888c2ecf20Sopenharmony_ci edid[54] = clock & 0xff; 6898c2ecf20Sopenharmony_ci edid[55] = clock >> 8; 6908c2ecf20Sopenharmony_ci edid[56] = width & 0xff; 6918c2ecf20Sopenharmony_ci edid[58] = (width >> 4) & 0xf0; 6928c2ecf20Sopenharmony_ci edid[59] = height & 0xff; 6938c2ecf20Sopenharmony_ci edid[61] = (height >> 4) & 0xf0; 6948c2ecf20Sopenharmony_ci for (i = 0; i < EDID_SIZE - 1; ++i) 6958c2ecf20Sopenharmony_ci sum += edid[i]; 6968c2ecf20Sopenharmony_ci edid[EDID_SIZE - 1] = (0x100 - (sum & 0xFF)) & 0xFF; 6978c2ecf20Sopenharmony_ci drm_connector_update_edid_property(connector, (struct edid *)edid); 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic int vbox_get_modes(struct drm_connector *connector) 7018c2ecf20Sopenharmony_ci{ 7028c2ecf20Sopenharmony_ci struct vbox_connector *vbox_connector = NULL; 7038c2ecf20Sopenharmony_ci struct drm_display_mode *mode = NULL; 7048c2ecf20Sopenharmony_ci struct vbox_private *vbox = NULL; 7058c2ecf20Sopenharmony_ci unsigned int num_modes = 0; 7068c2ecf20Sopenharmony_ci int preferred_width, preferred_height; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci vbox_connector = to_vbox_connector(connector); 7098c2ecf20Sopenharmony_ci vbox = to_vbox_dev(connector->dev); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci hgsmi_report_flags_location(vbox->guest_pool, GUEST_HEAP_OFFSET(vbox) + 7128c2ecf20Sopenharmony_ci HOST_FLAGS_OFFSET); 7138c2ecf20Sopenharmony_ci if (vbox_connector->vbox_crtc->crtc_id == 0) 7148c2ecf20Sopenharmony_ci vbox_report_caps(vbox); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci num_modes = drm_add_modes_noedid(connector, 2560, 1600); 7178c2ecf20Sopenharmony_ci preferred_width = vbox_connector->mode_hint.width ? 7188c2ecf20Sopenharmony_ci vbox_connector->mode_hint.width : 1024; 7198c2ecf20Sopenharmony_ci preferred_height = vbox_connector->mode_hint.height ? 7208c2ecf20Sopenharmony_ci vbox_connector->mode_hint.height : 768; 7218c2ecf20Sopenharmony_ci mode = drm_cvt_mode(connector->dev, preferred_width, preferred_height, 7228c2ecf20Sopenharmony_ci 60, false, false, false); 7238c2ecf20Sopenharmony_ci if (mode) { 7248c2ecf20Sopenharmony_ci mode->type |= DRM_MODE_TYPE_PREFERRED; 7258c2ecf20Sopenharmony_ci drm_mode_probed_add(connector, mode); 7268c2ecf20Sopenharmony_ci ++num_modes; 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci vbox_set_edid(connector, preferred_width, preferred_height); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (vbox_connector->vbox_crtc->x_hint != -1) 7318c2ecf20Sopenharmony_ci drm_object_property_set_value(&connector->base, 7328c2ecf20Sopenharmony_ci vbox->ddev.mode_config.suggested_x_property, 7338c2ecf20Sopenharmony_ci vbox_connector->vbox_crtc->x_hint); 7348c2ecf20Sopenharmony_ci else 7358c2ecf20Sopenharmony_ci drm_object_property_set_value(&connector->base, 7368c2ecf20Sopenharmony_ci vbox->ddev.mode_config.suggested_x_property, 0); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci if (vbox_connector->vbox_crtc->y_hint != -1) 7398c2ecf20Sopenharmony_ci drm_object_property_set_value(&connector->base, 7408c2ecf20Sopenharmony_ci vbox->ddev.mode_config.suggested_y_property, 7418c2ecf20Sopenharmony_ci vbox_connector->vbox_crtc->y_hint); 7428c2ecf20Sopenharmony_ci else 7438c2ecf20Sopenharmony_ci drm_object_property_set_value(&connector->base, 7448c2ecf20Sopenharmony_ci vbox->ddev.mode_config.suggested_y_property, 0); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci return num_modes; 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_cistatic void vbox_connector_destroy(struct drm_connector *connector) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci drm_connector_unregister(connector); 7528c2ecf20Sopenharmony_ci drm_connector_cleanup(connector); 7538c2ecf20Sopenharmony_ci kfree(connector); 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cistatic enum drm_connector_status 7578c2ecf20Sopenharmony_civbox_connector_detect(struct drm_connector *connector, bool force) 7588c2ecf20Sopenharmony_ci{ 7598c2ecf20Sopenharmony_ci struct vbox_connector *vbox_connector; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci vbox_connector = to_vbox_connector(connector); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci return vbox_connector->mode_hint.disconnected ? 7648c2ecf20Sopenharmony_ci connector_status_disconnected : connector_status_connected; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cistatic int vbox_fill_modes(struct drm_connector *connector, u32 max_x, 7688c2ecf20Sopenharmony_ci u32 max_y) 7698c2ecf20Sopenharmony_ci{ 7708c2ecf20Sopenharmony_ci struct vbox_connector *vbox_connector; 7718c2ecf20Sopenharmony_ci struct drm_device *dev; 7728c2ecf20Sopenharmony_ci struct drm_display_mode *mode, *iterator; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci vbox_connector = to_vbox_connector(connector); 7758c2ecf20Sopenharmony_ci dev = vbox_connector->base.dev; 7768c2ecf20Sopenharmony_ci list_for_each_entry_safe(mode, iterator, &connector->modes, head) { 7778c2ecf20Sopenharmony_ci list_del(&mode->head); 7788c2ecf20Sopenharmony_ci drm_mode_destroy(dev, mode); 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci return drm_helper_probe_single_connector_modes(connector, max_x, max_y); 7828c2ecf20Sopenharmony_ci} 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs vbox_connector_helper_funcs = { 7858c2ecf20Sopenharmony_ci .get_modes = vbox_get_modes, 7868c2ecf20Sopenharmony_ci}; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs vbox_connector_funcs = { 7898c2ecf20Sopenharmony_ci .detect = vbox_connector_detect, 7908c2ecf20Sopenharmony_ci .fill_modes = vbox_fill_modes, 7918c2ecf20Sopenharmony_ci .destroy = vbox_connector_destroy, 7928c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 7938c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 7948c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 7958c2ecf20Sopenharmony_ci}; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_cistatic int vbox_connector_init(struct drm_device *dev, 7988c2ecf20Sopenharmony_ci struct vbox_crtc *vbox_crtc, 7998c2ecf20Sopenharmony_ci struct drm_encoder *encoder) 8008c2ecf20Sopenharmony_ci{ 8018c2ecf20Sopenharmony_ci struct vbox_connector *vbox_connector; 8028c2ecf20Sopenharmony_ci struct drm_connector *connector; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci vbox_connector = kzalloc(sizeof(*vbox_connector), GFP_KERNEL); 8058c2ecf20Sopenharmony_ci if (!vbox_connector) 8068c2ecf20Sopenharmony_ci return -ENOMEM; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci connector = &vbox_connector->base; 8098c2ecf20Sopenharmony_ci vbox_connector->vbox_crtc = vbox_crtc; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci drm_connector_init(dev, connector, &vbox_connector_funcs, 8128c2ecf20Sopenharmony_ci DRM_MODE_CONNECTOR_VGA); 8138c2ecf20Sopenharmony_ci drm_connector_helper_add(connector, &vbox_connector_helper_funcs); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci connector->interlace_allowed = 0; 8168c2ecf20Sopenharmony_ci connector->doublescan_allowed = 0; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci drm_mode_create_suggested_offset_properties(dev); 8198c2ecf20Sopenharmony_ci drm_object_attach_property(&connector->base, 8208c2ecf20Sopenharmony_ci dev->mode_config.suggested_x_property, 0); 8218c2ecf20Sopenharmony_ci drm_object_attach_property(&connector->base, 8228c2ecf20Sopenharmony_ci dev->mode_config.suggested_y_property, 0); 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci drm_connector_attach_encoder(connector, encoder); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci return 0; 8278c2ecf20Sopenharmony_ci} 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_cistatic const struct drm_mode_config_funcs vbox_mode_funcs = { 8308c2ecf20Sopenharmony_ci .fb_create = drm_gem_fb_create_with_dirty, 8318c2ecf20Sopenharmony_ci .mode_valid = drm_vram_helper_mode_valid, 8328c2ecf20Sopenharmony_ci .atomic_check = drm_atomic_helper_check, 8338c2ecf20Sopenharmony_ci .atomic_commit = drm_atomic_helper_commit, 8348c2ecf20Sopenharmony_ci}; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ciint vbox_mode_init(struct vbox_private *vbox) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci struct drm_device *dev = &vbox->ddev; 8398c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 8408c2ecf20Sopenharmony_ci struct vbox_crtc *vbox_crtc; 8418c2ecf20Sopenharmony_ci unsigned int i; 8428c2ecf20Sopenharmony_ci int ret; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci drm_mode_config_init(dev); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci dev->mode_config.funcs = (void *)&vbox_mode_funcs; 8478c2ecf20Sopenharmony_ci dev->mode_config.min_width = 0; 8488c2ecf20Sopenharmony_ci dev->mode_config.min_height = 0; 8498c2ecf20Sopenharmony_ci dev->mode_config.preferred_depth = 24; 8508c2ecf20Sopenharmony_ci dev->mode_config.max_width = VBE_DISPI_MAX_XRES; 8518c2ecf20Sopenharmony_ci dev->mode_config.max_height = VBE_DISPI_MAX_YRES; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci for (i = 0; i < vbox->num_crtcs; ++i) { 8548c2ecf20Sopenharmony_ci vbox_crtc = vbox_crtc_init(dev, i); 8558c2ecf20Sopenharmony_ci if (IS_ERR(vbox_crtc)) { 8568c2ecf20Sopenharmony_ci ret = PTR_ERR(vbox_crtc); 8578c2ecf20Sopenharmony_ci goto err_drm_mode_cleanup; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci encoder = vbox_encoder_init(dev, i); 8608c2ecf20Sopenharmony_ci if (!encoder) { 8618c2ecf20Sopenharmony_ci ret = -ENOMEM; 8628c2ecf20Sopenharmony_ci goto err_drm_mode_cleanup; 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci ret = vbox_connector_init(dev, vbox_crtc, encoder); 8658c2ecf20Sopenharmony_ci if (ret) 8668c2ecf20Sopenharmony_ci goto err_drm_mode_cleanup; 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci drm_mode_config_reset(dev); 8708c2ecf20Sopenharmony_ci return 0; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_cierr_drm_mode_cleanup: 8738c2ecf20Sopenharmony_ci drm_mode_config_cleanup(dev); 8748c2ecf20Sopenharmony_ci return ret; 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_civoid vbox_mode_fini(struct vbox_private *vbox) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci drm_mode_config_cleanup(&vbox->ddev); 8808c2ecf20Sopenharmony_ci} 881