linux/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
Linus Torvalds 4b99990cdf drm-next for 7.2-rc1
core:
 - add docbook for DRM_IOCTL_SYNCOBJ_EVENTFD
 - change signature of drm_connector_attach_hdr_output_metadata_property
 - dedup counter and timestamp retrieval in vblank code
 - parse AMD VSDB v3 in CTA extension blocks
 - add P230, Y7, XYYY2101010, T430, XVUY210101010 formats
 - don't call drop master on file close if not master
 - use drm_printf_indent in atomic / bridge
 - fix 32b format descriptions
 - docs: fix toctree
 - hdmi: add common TMDS character rates
 - fix drm_syncobj_find_fence leak
 
 rust:
 - introduce Higher-Ranked lifetime types
 - replace drvdata with scoped registration data
 - add GPUVM immediate mode abstraction for rust GPU drivers
 - introduce DeviceContext type state for drm::Device
 
 bridge:
 - clarify drm_bridge_get/put
 - create drm_get_bridge_by_endpoint and use it
 - analogix_dp: add panel probing
 - ite-it6211 - use drm audio hdmi helpers
 
 buddy:
 - add lockdep annotations
 
 dp:
 - add PR and VRR updates
 - mst: fix buffer overflows
 - add Adaptive Sync SDP decoding support
 - fix OOB reads in dp-mst
 
 ttm:
 - bump fpfn/lpfn to 64-bit
 
 scheduler:
 - change default to fair scheduler
 - map runqueue 1:1 with scheduler
 
 dma-buf:
 - port selftests to kunit
 - convert dma-buf system/heap allocators to module
 - add separate DMABUF_HEAPS_SYSTEM_CC_SHARED Kconfig
 
 udmabuf:
 - revert hugetlb support
 - fix error with CONFIG_DMA_API_DEBUG
 
 dma-fence:
 - fix tracepoints lifetime
 - remove unused signal on any support
 
 ras:
 - add clear error counter netlink command to drm ras
 
 gpusvm:
 - reject VMAs with VM_IO or VM_PFNMAP when creating SVM ranges
 - use IOVA allocations
 
 pagemap:
 - use IOVA allocations
 
 panels:
 - update to use ref counts
 - add support for CSW PNB601LS1-2, LGD LP116WHA-SPB1
 - add support for waveshare panels
 - CMN N116BCN-EA1, CMN N140HCA-EEK, IVO M140NWFQ R5,
 - IVO, R140NWFW R0, BOE NT140*, BOE NV133FHM-N4F,
 - AUO B140*, AUO B133HAN06.6 and AUO B116XTN02.3 eDP panels
 - Surface Pro 12 Panel
 
 xe:
 - add CRI PCI-IDs
 - debugfs add multi-lrc info
 - engine init cleanup
 - PF fair scheduling auto provisioning
 - system controller support for CRI/Xe3p
 - PXP state machine fixes
 - Reset/wedge/unload corner case fixes
 - Wedge path memory allocation fixes
 - PAT type cleanups
 - Reject unsafe PAT for CPU cached memory
 - OA improvements for CRI device memory
 - kernel doc syntax in xe headers
 - xe_drm.h documentation fixes
 - include guard cleanups
 - VF CCS memory pool
 - i915/xe step unification
 - Xe3p GT tuning fixes
 - forcewake cleanup in GT and GuC
 - admin-only PF mode
 - enable hwmon energy attributes for CRI
 - enable GT_MI_USER_INTERRUPT
 - refactor emit functions
 - oa workarounds
 - multi_queue: allow QUEUE_TIMESTAMP register
 - convert stolen memory to ttm range manager
 - use xe2 style blitter as a feature flag
 - make drm_driver const
 - add/use IRQ page to HW engine definition
 - fix oops when display disabled
 
 i915:
 - enable PIPEDMC_ERROR interrupt
 - more common display code refactoring
 - restructure DP/HDMI sink format handling
 - eliminate FB usage from lowlevel pinning code
 - panel replay bw optimization
 - integrate sharpness filter into the scaler
 - new fb_pin abstraction for xe/i915 fb transparent handling
 - skip inactive MST connectors on HDCP
 - start switching to display specific registers
 - use polling when irq unavailable
 - Adaptive-sync SDP prep
 
 amdgpu:
 - use drm_display_info for AMD VSDB data
 - Initial HDMI 2.1 FRL support
 - Initial DCN 4.2.1 support
 - GART fixes for non-4k pages
 - GC 11.5.6/SDMA 6.4.0/and other new IPs
 - GFX9/DCE6/Hawaii/SDMA4/GART/Userq fixes
 - Finish support for using multiple SDMA queues for TTM operations
 - SWSMU updates
 - GC 12.1 updates
 - SMU 15.0.8 updates
 - DCN 4.2 updates
 - DC type conversion fixes
 - Enable DC power module
 - Replay/PSR updates
 - SMU 13.x updates
 - Compute queue quantum MQD updates
 - ASPM fix
 - Align VKMS with common implementation
 - DC analog support fixes
 - UVD 3 fixes
 - TCC harvesting fixes for SI
 - GC 11 APU module reload fix
 - NBIO 6.3.2 support
 - IH 7.1 updates
 - DC cursor fixes
 - VCN/JPEG user fence fixes
 - DC support for connectors without DDC
 - Prefer ROM BAR for default VGA device
 - DC bandwidth fixes
 - Add PTL support for profiler
 - Introduce dc_plane_cm and migrate surface update color path
 - Add FRL registers for HDMI 2.1
 - Restructure VM state machine
 - Auxless ALPM support
 - GEM_OP locking/warning fixes
 - switch to system_dfl_wq
 
 amdkfd:
 - GPUVM TLB flush fix
 - Hotplug fix
 - Boundary check fixes
 - SVM fixes
 - CRIU fixes
 - add profiler API
 - MES 12.1 updates
 
 msm:
 - core:
   - fix shrinker documentation
   - IFPC enabled for gen8
   - PERFCNTR_CONFIG ioctl support
 - GPU:
   - reworked UBWC handling
   - a810 support
 - MDSS:
   - add support for Milos platform
   - reworked UBWC handling
 - DisplayPort:
   - reworked HPD handling as prep for MST
 - DPU:
   - Milos platform support
   - reworked UBWC handling
 - DSI:
   - Milos platform support
 
 nova:
 - Hopper/Blackwell enablement (GH100/GB100/GB202)
   - FSP support
   - 32-bit firmware support
   - HAL functions
 - refactor GSP boot/unload
 - GA100 support
 - VBIOS hardening/refactoring
 - Adopt higher order lifetime types
 
 tyr:
 - define register blocks
 - add shmem backed GEM objects
 - adopt higher order lifetime types
 - move clock cleanup into Drop
 
 radeon:
 - Hawaii SMU fixes
 - CS parser fix
 - use struct drm_edid instead of edid
 
 amdxdna:
 - export per-client BO memory via fdinfo
 - AIE4 device support
 - support medium/lower power modes
 - expandable device heap support
 - revert read-only user-pointer BO mappings
 
 ivpu:
 - support frequency limiting
 
 panthor:
 - enable GEM shrinker support
 - add eviction and reclaim info to fdinfo
 
 v3d:
 - enable runtime PM
 
 mgag200:
 - support XRGB1555 + C8
 
 ast:
 - support XRGB1555 + C8
 - use constants for lots of registers
 - fix register handling
 
 imagination:
 - fence handling refactoring
 
 nouveau:
 - fix sched double call
 - expose VBIOS on GSP-RM systems
 - add GA100 support
 
 virtio:
 - add VIRTIO_GPU_F_BLOB_ALIGNMENT flag
 - add deferred mapping support
 
 gud:
 - add RCade Display Adapter
 
 hibmc:
 - fix no connectors usage
 
 mediatek:
 - hdmi: convert error handling
 - simplify mtk_crtc allocation
 
 exynos:
 - move fbdev emulation to drm client buffers
 - use drm format helpers for geometry/size
 - adopt core DMA tracking
 - fix framebuffer offset handling
 
 renesas:
 - add RZ/T2H SOC support
 
 versilicon:
 - add cursor plane support
 
 tegra:
 - use drm client for framebuffer
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEEKbZHaGwW9KfbeusDHTzWXnEhr4FAmoyOXkACgkQDHTzWXnE
 hr5Pxg//VwqDYgf8CwWtLwXqHYBhGDwj1PDA/zLoosm32fXABKhzPMA5XcaPHBKh
 Y7Wyo7988cxXFQ0BRltKP1S3d04ck5ZbBA/qpwwX9Pz0u8HhNMicwLd9ASlWqqkP
 ogFIj3J8PsjlTMVI2cnzQ7s0TMQoQorUaNl13vQnITDPSNncCAunrKixpzVagHeK
 /ARlIg4kqSbvlKAhkvps7FbJUG3l/g901HdAcspp0bn6d3+4zz8Yg+yg/b4Xngdh
 Sn6UGDxwrNeBSHP2Spj5/rGSOuo4L8Fr1rS0+KEwRnEqrg+qdq9mo9l0xQsJV9/e
 4ql6nssEMC/CIHwHomsgCdxdOsj2DiXLUucJKBwavHHSWNHFgdLhNSdaUcwrkui3
 sLmahOEXFwFIR3K1X++547Wux9DhAWDcvq/SnxzN0dQBpN0AmpE0/o/3Vb7Vw4dO
 /cfvI4Y1WGviJCs58p36tqYGx8dDPuMh9tiUtQvBXBkkS87MqBX8wM49qHSlzEOV
 EkQfGV1cLMBJpZZkMezrqCNyIS7ghzXJ+K/IruAPrgvN7/kCY7hRclUT+Z/1Mqvc
 mw3Japbcy7P+BMKnlNfCZsDXg04kYZC5MOaMeiJOLg6qfHBHITD8gP1zct+RikIS
 d+43kYwt43dPj53jUUXpREoAezSoF2ktncFChB2tdeaMlb6oJIM=
 =86oS
 -----END PGP SIGNATURE-----

Merge tag 'drm-next-2026-06-17' of https://gitlab.freedesktop.org/drm/kernel

Pull drm updates from Dave Airlie:
 "Highlights:
   - xe: add initial CRI platform support
   - amdgpu: initial HDMI 2.1 FRL support
   - rust: add some new type concepts for device lifetimes
   - scheduler: moves to a fair algorithm and lots of cleanups

  But it's mostly the usual mountain of changes across the board.

  core:
   - add docbook for DRM_IOCTL_SYNCOBJ_EVENTFD
   - change signature of drm_connector_attach_hdr_output_metadata_property
   - dedup counter and timestamp retrieval in vblank code
   - parse AMD VSDB v3 in CTA extension blocks
   - add P230, Y7, XYYY2101010, T430, XVUY210101010 formats
   - don't call drop master on file close if not master
   - use drm_printf_indent in atomic / bridge
   - fix 32b format descriptions
   - docs: fix toctree
   - hdmi: add common TMDS character rates
   - fix drm_syncobj_find_fence leak

  rust:
   - introduce Higher-Ranked lifetime types
   - replace drvdata with scoped registration data
   - add GPUVM immediate mode abstraction for rust GPU drivers
   - introduce DeviceContext type state for drm::Device

  bridge:
   - clarify drm_bridge_get/put
   - create drm_get_bridge_by_endpoint and use it
   - analogix_dp: add panel probing
   - ite-it6211 - use drm audio hdmi helpers

  buddy:
   - add lockdep annotations

  dp:
   - add PR and VRR updates
   - mst: fix buffer overflows
   - add Adaptive Sync SDP decoding support
   - fix OOB reads in dp-mst

  ttm:
   - bump fpfn/lpfn to 64-bit

  scheduler:
   - change default to fair scheduler
   - map runqueue 1:1 with scheduler

  dma-buf:
   - port selftests to kunit
   - convert dma-buf system/heap allocators to module
   - add separate DMABUF_HEAPS_SYSTEM_CC_SHARED Kconfig

  udmabuf:
   - revert hugetlb support
   - fix error with CONFIG_DMA_API_DEBUG

  dma-fence:
   - fix tracepoints lifetime
   - remove unused signal on any support

  ras:
   - add clear error counter netlink command to drm ras

  gpusvm:
   - reject VMAs with VM_IO or VM_PFNMAP when creating SVM ranges
   - use IOVA allocations

  pagemap:
   - use IOVA allocations

  panels:
   - update to use ref counts
   - add support for CSW PNB601LS1-2, LGD LP116WHA-SPB1
   - add support for waveshare panels
   - CMN N116BCN-EA1, CMN N140HCA-EEK, IVO M140NWFQ R5,
   - IVO, R140NWFW R0, BOE NT140*, BOE NV133FHM-N4F,
   - AUO B140*, AUO B133HAN06.6 and AUO B116XTN02.3 eDP panels
   - Surface Pro 12 Panel

  xe:
   - add CRI PCI-IDs
   - debugfs add multi-lrc info
   - engine init cleanup
   - PF fair scheduling auto provisioning
   - system controller support for CRI/Xe3p
   - PXP state machine fixes
   - Reset/wedge/unload corner case fixes
   - Wedge path memory allocation fixes
   - PAT type cleanups
   - Reject unsafe PAT for CPU cached memory
   - OA improvements for CRI device memory
   - kernel doc syntax in xe headers
   - xe_drm.h documentation fixes
   - include guard cleanups
   - VF CCS memory pool
   - i915/xe step unification
   - Xe3p GT tuning fixes
   - forcewake cleanup in GT and GuC
   - admin-only PF mode
   - enable hwmon energy attributes for CRI
   - enable GT_MI_USER_INTERRUPT
   - refactor emit functions
   - oa workarounds
   - multi_queue: allow QUEUE_TIMESTAMP register
   - convert stolen memory to ttm range manager
   - use xe2 style blitter as a feature flag
   - make drm_driver const
   - add/use IRQ page to HW engine definition
   - fix oops when display disabled

  i915:
   - enable PIPEDMC_ERROR interrupt
   - more common display code refactoring
   - restructure DP/HDMI sink format handling
   - eliminate FB usage from lowlevel pinning code
   - panel replay bw optimization
   - integrate sharpness filter into the scaler
   - new fb_pin abstraction for xe/i915 fb transparent handling
   - skip inactive MST connectors on HDCP
   - start switching to display specific registers
   - use polling when irq unavailable
   - Adaptive-sync SDP prep

  amdgpu:
   - use drm_display_info for AMD VSDB data
   - Initial HDMI 2.1 FRL support
   - Initial DCN 4.2.1 support
   - GART fixes for non-4k pages
   - GC 11.5.6/SDMA 6.4.0/and other new IPs
   - GFX9/DCE6/Hawaii/SDMA4/GART/Userq fixes
   - Finish support for using multiple SDMA queues for TTM operations
   - SWSMU updates
   - GC 12.1 updates
   - SMU 15.0.8 updates
   - DCN 4.2 updates
   - DC type conversion fixes
   - Enable DC power module
   - Replay/PSR updates
   - SMU 13.x updates
   - Compute queue quantum MQD updates
   - ASPM fix
   - Align VKMS with common implementation
   - DC analog support fixes
   - UVD 3 fixes
   - TCC harvesting fixes for SI
   - GC 11 APU module reload fix
   - NBIO 6.3.2 support
   - IH 7.1 updates
   - DC cursor fixes
   - VCN/JPEG user fence fixes
   - DC support for connectors without DDC
   - Prefer ROM BAR for default VGA device
   - DC bandwidth fixes
   - Add PTL support for profiler
   - Introduce dc_plane_cm and migrate surface update color path
   - Add FRL registers for HDMI 2.1
   - Restructure VM state machine
   - Auxless ALPM support
   - GEM_OP locking/warning fixes
   - switch to system_dfl_wq

  amdkfd:
   - GPUVM TLB flush fix
   - Hotplug fix
   - Boundary check fixes
   - SVM fixes
   - CRIU fixes
   - add profiler API
   - MES 12.1 updates

  msm:
   - core:
     - fix shrinker documentation
     - IFPC enabled for gen8
     - PERFCNTR_CONFIG ioctl support
   - GPU:
     - reworked UBWC handling
     - a810 support
   - MDSS:
     - add support for Milos platform
     - reworked UBWC handling
   - DisplayPort:
     - reworked HPD handling as prep for MST
   - DPU:
     - Milos platform support
     - reworked UBWC handling
   - DSI:
     - Milos platform support

  nova:
   - Hopper/Blackwell enablement (GH100/GB100/GB202)
     - FSP support
     - 32-bit firmware support
     - HAL functions
   - refactor GSP boot/unload
   - GA100 support
   - VBIOS hardening/refactoring
   - Adopt higher order lifetime types

  tyr:
   - define register blocks
   - add shmem backed GEM objects
   - adopt higher order lifetime types
   - move clock cleanup into Drop

  radeon:
   - Hawaii SMU fixes
   - CS parser fix
   - use struct drm_edid instead of edid

  amdxdna:
   - export per-client BO memory via fdinfo
   - AIE4 device support
   - support medium/lower power modes
   - expandable device heap support
   - revert read-only user-pointer BO mappings

  ivpu:
   - support frequency limiting

  panthor:
   - enable GEM shrinker support
   - add eviction and reclaim info to fdinfo

  v3d:
   - enable runtime PM

  mgag200:
   - support XRGB1555 + C8

  ast:
   - support XRGB1555 + C8
   - use constants for lots of registers
   - fix register handling

  imagination:
   - fence handling refactoring

  nouveau:
   - fix sched double call
   - expose VBIOS on GSP-RM systems
   - add GA100 support

  virtio:
   - add VIRTIO_GPU_F_BLOB_ALIGNMENT flag
   - add deferred mapping support

  gud:
   - add RCade Display Adapter

  hibmc:
   - fix no connectors usage

  mediatek:
   - hdmi: convert error handling
   - simplify mtk_crtc allocation

  exynos:
   - move fbdev emulation to drm client buffers
   - use drm format helpers for geometry/size
   - adopt core DMA tracking
   - fix framebuffer offset handling

  renesas:
   - add RZ/T2H SOC support

  versilicon:
   - add cursor plane support

  tegra:
   - use drm client for framebuffer"

* tag 'drm-next-2026-06-17' of https://gitlab.freedesktop.org/drm/kernel: (1731 commits)
  dma-buf: move system_cc_shared heap under separate Kconfig
  accel/amdxdna: Clear sva pointer after unbind
  agp/amd64: Fix broken error propagation in agp_amd64_probe()
  accel/amdxdna: Require carveout when PASID and force_iova are disabled
  drm/amdkfd: always resume_all after suspend_all
  drm/amdgpu/gfx: move fault and EOP IRQ get/put to hw_init/hw_fini
  drm/amd/display: Consult MCCS FreeSync cap only if requested & supported
  drm/amd/pm: Use strscpy in profile mode parsing
  drm/amdkfd: Fix infinite loop parsing CRAT with zero subtype length
  drm/amdkfd: fix sysfs topology prop length on buffer truncation
  drm/amdgpu: drop retry loop in amdgpu_hmm_range_get_pages
  drm/amd/pm: bound OD parameter parsing to stack array size
  drm/amd/pm: Stop pp_od_clk_voltage emit at PAGE_SIZE
  drm/amdkfd: Unwind debug trap enable on copy_to_user failure
  drm/amdgpu: validate the mes firmware version for gfx12.1
  drm/amdgpu: validate the mes firmware version for gfx12
  drm/amdgpu: compare MES firmware version ucode for gfx11
  drm/amdkfd: Add bounds check for AMDKFD_IOC_WAIT_EVENTS
  drm/amdgpu: restart the CS if some parts of the VM are still invalidated
  drm/amd/display: use unsigned types for local pipe and REG_GET counters
  ...
2026-06-17 10:21:00 +01:00

605 lines
16 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2021 Microsoft
*/
#include <linux/hyperv.h>
#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>
#include "hyperv_drm.h"
#define VMBUS_RING_BUFSIZE VMBUS_RING_SIZE(256 * 1024)
#define VMBUS_VSP_TIMEOUT (10 * HZ)
#define SYNTHVID_VERSION(major, minor) ((minor) << 16 | (major))
#define SYNTHVID_VER_GET_MAJOR(ver) (ver & 0x0000ffff)
#define SYNTHVID_VER_GET_MINOR(ver) ((ver & 0xffff0000) >> 16)
/* Support for VERSION_WIN7 is removed. #define is retained for reference. */
#define SYNTHVID_VERSION_WIN7 SYNTHVID_VERSION(3, 0)
#define SYNTHVID_VERSION_WIN8 SYNTHVID_VERSION(3, 2)
#define SYNTHVID_VERSION_WIN10 SYNTHVID_VERSION(3, 5)
#define SYNTHVID_DEPTH_WIN8 32
#define SYNTHVID_WIDTH_WIN8 1600
#define SYNTHVID_HEIGHT_WIN8 1200
#define SYNTHVID_FB_SIZE_WIN8 (8 * 1024 * 1024)
enum pipe_msg_type {
PIPE_MSG_INVALID,
PIPE_MSG_DATA,
PIPE_MSG_MAX
};
enum synthvid_msg_type {
SYNTHVID_ERROR = 0,
SYNTHVID_VERSION_REQUEST = 1,
SYNTHVID_VERSION_RESPONSE = 2,
SYNTHVID_VRAM_LOCATION = 3,
SYNTHVID_VRAM_LOCATION_ACK = 4,
SYNTHVID_SITUATION_UPDATE = 5,
SYNTHVID_SITUATION_UPDATE_ACK = 6,
SYNTHVID_POINTER_POSITION = 7,
SYNTHVID_POINTER_SHAPE = 8,
SYNTHVID_FEATURE_CHANGE = 9,
SYNTHVID_DIRT = 10,
SYNTHVID_RESOLUTION_REQUEST = 13,
SYNTHVID_RESOLUTION_RESPONSE = 14,
SYNTHVID_MAX = 15
};
struct pipe_msg_hdr {
u32 type;
u32 size; /* size of message after this field */
} __packed;
struct hvd_screen_info {
u16 width;
u16 height;
} __packed;
struct synthvid_msg_hdr {
u32 type;
u32 size; /* size of this header + payload after this field */
} __packed;
struct synthvid_version_req {
u32 version;
} __packed;
struct synthvid_version_resp {
u32 version;
u8 is_accepted;
u8 max_video_outputs;
} __packed;
struct synthvid_vram_location {
u64 user_ctx;
u8 is_vram_gpa_specified;
u64 vram_gpa;
} __packed;
struct synthvid_vram_location_ack {
u64 user_ctx;
} __packed;
struct video_output_situation {
u8 active;
u32 vram_offset;
u8 depth_bits;
u32 width_pixels;
u32 height_pixels;
u32 pitch_bytes;
} __packed;
struct synthvid_situation_update {
u64 user_ctx;
u8 video_output_count;
struct video_output_situation video_output[1];
} __packed;
struct synthvid_situation_update_ack {
u64 user_ctx;
} __packed;
struct synthvid_pointer_position {
u8 is_visible;
u8 video_output;
s32 image_x;
s32 image_y;
} __packed;
#define SYNTHVID_CURSOR_MAX_X 96
#define SYNTHVID_CURSOR_MAX_Y 96
#define SYNTHVID_CURSOR_ARGB_PIXEL_SIZE 4
#define SYNTHVID_CURSOR_MAX_SIZE (SYNTHVID_CURSOR_MAX_X * \
SYNTHVID_CURSOR_MAX_Y * SYNTHVID_CURSOR_ARGB_PIXEL_SIZE)
#define SYNTHVID_CURSOR_COMPLETE (-1)
struct synthvid_pointer_shape {
u8 part_idx;
u8 is_argb;
u32 width; /* SYNTHVID_CURSOR_MAX_X at most */
u32 height; /* SYNTHVID_CURSOR_MAX_Y at most */
u32 hot_x; /* hotspot relative to upper-left of pointer image */
u32 hot_y;
u8 data[4];
} __packed;
struct synthvid_feature_change {
u8 is_dirt_needed;
u8 is_ptr_pos_needed;
u8 is_ptr_shape_needed;
u8 is_situ_needed;
} __packed;
struct rect {
s32 x1, y1; /* top left corner */
s32 x2, y2; /* bottom right corner, exclusive */
} __packed;
struct synthvid_dirt {
u8 video_output;
u8 dirt_count;
struct rect rect[1];
} __packed;
#define SYNTHVID_EDID_BLOCK_SIZE 128
#define SYNTHVID_MAX_RESOLUTION_COUNT 64
struct synthvid_supported_resolution_req {
u8 maximum_resolution_count;
} __packed;
struct synthvid_supported_resolution_resp {
u8 edid_block[SYNTHVID_EDID_BLOCK_SIZE];
u8 resolution_count;
u8 default_resolution_index;
u8 is_standard;
struct hvd_screen_info supported_resolution[SYNTHVID_MAX_RESOLUTION_COUNT];
} __packed;
struct synthvid_msg {
struct pipe_msg_hdr pipe_hdr;
struct synthvid_msg_hdr vid_hdr;
union {
struct synthvid_version_req ver_req;
struct synthvid_version_resp ver_resp;
struct synthvid_vram_location vram;
struct synthvid_vram_location_ack vram_ack;
struct synthvid_situation_update situ;
struct synthvid_situation_update_ack situ_ack;
struct synthvid_pointer_position ptr_pos;
struct synthvid_pointer_shape ptr_shape;
struct synthvid_feature_change feature_chg;
struct synthvid_dirt dirt;
struct synthvid_supported_resolution_req resolution_req;
struct synthvid_supported_resolution_resp resolution_resp;
};
} __packed;
static inline bool hyperv_version_ge(u32 ver1, u32 ver2)
{
if (SYNTHVID_VER_GET_MAJOR(ver1) > SYNTHVID_VER_GET_MAJOR(ver2) ||
(SYNTHVID_VER_GET_MAJOR(ver1) == SYNTHVID_VER_GET_MAJOR(ver2) &&
SYNTHVID_VER_GET_MINOR(ver1) >= SYNTHVID_VER_GET_MINOR(ver2)))
return true;
return false;
}
static inline int hyperv_sendpacket(struct hv_device *hdev, struct synthvid_msg *msg)
{
static atomic64_t request_id = ATOMIC64_INIT(0);
struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
int ret;
msg->pipe_hdr.type = PIPE_MSG_DATA;
msg->pipe_hdr.size = msg->vid_hdr.size;
ret = vmbus_sendpacket(hdev->channel, msg,
msg->vid_hdr.size + sizeof(struct pipe_msg_hdr),
atomic64_inc_return(&request_id),
VM_PKT_DATA_INBAND, 0);
if (ret)
drm_err_ratelimited(&hv->dev, "Unable to send packet via vmbus; error %d\n", ret);
return ret;
}
static int hyperv_negotiate_version(struct hv_device *hdev, u32 ver)
{
struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf;
struct drm_device *dev = &hv->dev;
unsigned long t;
memset(msg, 0, sizeof(struct synthvid_msg));
msg->vid_hdr.type = SYNTHVID_VERSION_REQUEST;
msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
sizeof(struct synthvid_version_req);
msg->ver_req.version = ver;
hyperv_sendpacket(hdev, msg);
t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT);
if (!t) {
drm_err(dev, "Time out on waiting version response\n");
return -ETIMEDOUT;
}
if (!msg->ver_resp.is_accepted) {
drm_err(dev, "Version request not accepted\n");
return -ENODEV;
}
hv->synthvid_version = ver;
drm_info(dev, "Synthvid Version major %d, minor %d\n",
SYNTHVID_VER_GET_MAJOR(ver), SYNTHVID_VER_GET_MINOR(ver));
return 0;
}
int hyperv_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp)
{
struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf;
struct drm_device *dev = &hv->dev;
unsigned long t;
memset(msg, 0, sizeof(struct synthvid_msg));
msg->vid_hdr.type = SYNTHVID_VRAM_LOCATION;
msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
sizeof(struct synthvid_vram_location);
msg->vram.user_ctx = vram_pp;
msg->vram.vram_gpa = vram_pp;
msg->vram.is_vram_gpa_specified = 1;
hyperv_sendpacket(hdev, msg);
t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT);
if (!t) {
drm_err(dev, "Time out on waiting vram location ack\n");
return -ETIMEDOUT;
}
if (msg->vram_ack.user_ctx != vram_pp) {
drm_err(dev, "Unable to set VRAM location\n");
return -ENODEV;
}
return 0;
}
int hyperv_update_situation(struct hv_device *hdev, u8 active, u32 bpp,
u32 w, u32 h, u32 pitch)
{
struct synthvid_msg msg;
memset(&msg, 0, sizeof(struct synthvid_msg));
msg.vid_hdr.type = SYNTHVID_SITUATION_UPDATE;
msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
sizeof(struct synthvid_situation_update);
msg.situ.user_ctx = 0;
msg.situ.video_output_count = 1;
msg.situ.video_output[0].active = active;
/* vram_offset should always be 0 */
msg.situ.video_output[0].vram_offset = 0;
msg.situ.video_output[0].depth_bits = bpp;
msg.situ.video_output[0].width_pixels = w;
msg.situ.video_output[0].height_pixels = h;
msg.situ.video_output[0].pitch_bytes = pitch;
hyperv_sendpacket(hdev, &msg);
return 0;
}
/*
* Hyper-V supports a hardware cursor feature. It's not used by Linux VM,
* but the Hyper-V host still draws a point as an extra mouse pointer,
* which is unwanted, especially when Xorg is running.
*
* Hide the unwanted pointer, by setting msg.ptr_pos.is_visible = 1 and setting
* the msg.ptr_shape.data. Note: setting msg.ptr_pos.is_visible to 0 doesn't
* work in tests.
*
* The hyperv_hide_hw_ptr() is also called in the handler of the
* SYNTHVID_FEATURE_CHANGE event, otherwise the host still draws an extra
* unwanted mouse pointer after the VM Connection window is closed and reopened.
*/
int hyperv_hide_hw_ptr(struct hv_device *hdev)
{
struct synthvid_msg msg;
memset(&msg, 0, sizeof(struct synthvid_msg));
msg.vid_hdr.type = SYNTHVID_POINTER_POSITION;
msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
sizeof(struct synthvid_pointer_position);
msg.ptr_pos.is_visible = 1;
msg.ptr_pos.video_output = 0;
msg.ptr_pos.image_x = 0;
msg.ptr_pos.image_y = 0;
hyperv_sendpacket(hdev, &msg);
memset(&msg, 0, sizeof(struct synthvid_msg));
msg.vid_hdr.type = SYNTHVID_POINTER_SHAPE;
msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
sizeof(struct synthvid_pointer_shape);
msg.ptr_shape.part_idx = SYNTHVID_CURSOR_COMPLETE;
msg.ptr_shape.is_argb = 1;
msg.ptr_shape.width = 1;
msg.ptr_shape.height = 1;
msg.ptr_shape.hot_x = 0;
msg.ptr_shape.hot_y = 0;
msg.ptr_shape.data[0] = 0;
msg.ptr_shape.data[1] = 1;
msg.ptr_shape.data[2] = 1;
msg.ptr_shape.data[3] = 1;
hyperv_sendpacket(hdev, &msg);
return 0;
}
int hyperv_update_dirt(struct hv_device *hdev, struct drm_rect *rect)
{
struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
struct synthvid_msg msg;
if (!hv->dirt_needed)
return 0;
memset(&msg, 0, sizeof(struct synthvid_msg));
msg.vid_hdr.type = SYNTHVID_DIRT;
msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
sizeof(struct synthvid_dirt);
msg.dirt.video_output = 0;
msg.dirt.dirt_count = 1;
msg.dirt.rect[0].x1 = rect->x1;
msg.dirt.rect[0].y1 = rect->y1;
msg.dirt.rect[0].x2 = rect->x2;
msg.dirt.rect[0].y2 = rect->y2;
hyperv_sendpacket(hdev, &msg);
return 0;
}
static int hyperv_get_supported_resolution(struct hv_device *hdev)
{
struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf;
struct drm_device *dev = &hv->dev;
unsigned long t;
u8 index;
int i;
memset(msg, 0, sizeof(struct synthvid_msg));
msg->vid_hdr.type = SYNTHVID_RESOLUTION_REQUEST;
msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) +
sizeof(struct synthvid_supported_resolution_req);
msg->resolution_req.maximum_resolution_count =
SYNTHVID_MAX_RESOLUTION_COUNT;
hyperv_sendpacket(hdev, msg);
t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT);
if (!t) {
drm_err(dev, "Time out on waiting resolution response\n");
return -ETIMEDOUT;
}
if (msg->resolution_resp.resolution_count == 0 ||
msg->resolution_resp.resolution_count >
SYNTHVID_MAX_RESOLUTION_COUNT) {
drm_err(dev, "Invalid resolution count: %d\n",
msg->resolution_resp.resolution_count);
return -ENODEV;
}
index = msg->resolution_resp.default_resolution_index;
if (index >= msg->resolution_resp.resolution_count) {
drm_err(dev, "Invalid resolution index: %d\n", index);
return -ENODEV;
}
for (i = 0; i < msg->resolution_resp.resolution_count; i++) {
hv->screen_width_max = max_t(u32, hv->screen_width_max,
msg->resolution_resp.supported_resolution[i].width);
hv->screen_height_max = max_t(u32, hv->screen_height_max,
msg->resolution_resp.supported_resolution[i].height);
}
hv->preferred_width =
msg->resolution_resp.supported_resolution[index].width;
hv->preferred_height =
msg->resolution_resp.supported_resolution[index].height;
return 0;
}
static void hyperv_receive_sub(struct hv_device *hdev, u32 bytes_recvd)
{
struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
struct synthvid_msg *msg;
size_t hdr_size;
size_t need;
if (!hv)
return;
hdr_size = sizeof(struct pipe_msg_hdr) +
sizeof(struct synthvid_msg_hdr);
if (bytes_recvd < hdr_size) {
drm_err_ratelimited(&hv->dev,
"synthvid packet too small for header: %u\n",
bytes_recvd);
return;
}
msg = (struct synthvid_msg *)hv->recv_buf;
need = hdr_size;
switch (msg->vid_hdr.type) {
case SYNTHVID_VERSION_RESPONSE:
need += sizeof(struct synthvid_version_resp);
break;
case SYNTHVID_RESOLUTION_RESPONSE:
/*
* The resolution response is variable length: the host
* fills resolution_count entries, not the full
* SYNTHVID_MAX_RESOLUTION_COUNT array. Require the fixed
* prefix first so resolution_count can be read, then
* demand exactly the count-sized array.
*/
need += offsetof(struct synthvid_supported_resolution_resp,
supported_resolution);
if (bytes_recvd < need)
break;
if (msg->resolution_resp.resolution_count >
SYNTHVID_MAX_RESOLUTION_COUNT) {
drm_err_ratelimited(&hv->dev,
"synthvid resolution count too large: %u\n",
msg->resolution_resp.resolution_count);
return;
}
need += msg->resolution_resp.resolution_count *
sizeof(struct hvd_screen_info);
break;
case SYNTHVID_VRAM_LOCATION_ACK:
need += sizeof(struct synthvid_vram_location_ack);
break;
case SYNTHVID_FEATURE_CHANGE:
/*
* Not a completion-driving message: validate its own payload
* and consume it here rather than falling through to the
* memcpy/complete shared by the wait-event responses.
*/
if (bytes_recvd < need +
sizeof(struct synthvid_feature_change)) {
drm_err_ratelimited(&hv->dev,
"synthvid feature change packet too small: %u\n",
bytes_recvd);
return;
}
hv->dirt_needed = msg->feature_chg.is_dirt_needed;
if (hv->dirt_needed)
hyperv_hide_hw_ptr(hv->hdev);
return;
default:
return;
}
/*
* Shared completion path for the wait-event responses
* (VERSION_RESPONSE, RESOLUTION_RESPONSE, VRAM_LOCATION_ACK):
* require the type-specific payload before handing the buffer to
* the waiter.
*/
if (bytes_recvd < need) {
drm_err_ratelimited(&hv->dev,
"synthvid packet too small for type %u: %u < %zu\n",
msg->vid_hdr.type, bytes_recvd, need);
return;
}
memcpy(hv->init_buf, msg, bytes_recvd);
complete(&hv->wait);
}
static void hyperv_receive(void *ctx)
{
struct hv_device *hdev = ctx;
struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
struct synthvid_msg *recv_buf;
u32 bytes_recvd;
u64 req_id;
int ret;
if (!hv)
return;
recv_buf = (struct synthvid_msg *)hv->recv_buf;
do {
ret = vmbus_recvpacket(hdev->channel, recv_buf,
VMBUS_MAX_PACKET_SIZE,
&bytes_recvd, &req_id);
if (ret) {
/*
* A nonzero return (e.g. -ENOBUFS for an oversized
* packet) is itself a malformed message: bytes_recvd
* then reports the required length rather than a copied
* payload, so it must not be forwarded to the
* sub-handler. Channel recovery is not attempted.
*/
drm_err_ratelimited(&hv->dev,
"vmbus_recvpacket failed: %d (need %u)\n",
ret, bytes_recvd);
} else if (bytes_recvd > 0 &&
recv_buf->pipe_hdr.type == PIPE_MSG_DATA) {
hyperv_receive_sub(hdev, bytes_recvd);
}
} while (bytes_recvd > 0 && ret == 0);
}
int hyperv_connect_vsp(struct hv_device *hdev)
{
struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
struct drm_device *dev = &hv->dev;
int ret;
ret = vmbus_open(hdev->channel, VMBUS_RING_BUFSIZE, VMBUS_RING_BUFSIZE,
NULL, 0, hyperv_receive, hdev);
if (ret) {
drm_err(dev, "Unable to open vmbus channel\n");
return ret;
}
/* Negotiate the protocol version with host */
switch (vmbus_proto_version) {
case VERSION_WIN10:
case VERSION_WIN10_V5:
ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN10);
if (!ret)
break;
fallthrough;
case VERSION_WIN8:
case VERSION_WIN8_1:
ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN8);
break;
default:
ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN10);
break;
}
if (ret) {
drm_err(dev, "Synthetic video device version not accepted %d\n", ret);
goto error;
}
hv->screen_depth = SYNTHVID_DEPTH_WIN8;
if (hyperv_version_ge(hv->synthvid_version, SYNTHVID_VERSION_WIN10)) {
ret = hyperv_get_supported_resolution(hdev);
if (ret)
drm_err(dev, "Failed to get supported resolution from host, use default\n");
}
if (!hv->screen_width_max) {
hv->screen_width_max = SYNTHVID_WIDTH_WIN8;
hv->screen_height_max = SYNTHVID_HEIGHT_WIN8;
hv->preferred_width = SYNTHVID_WIDTH_WIN8;
hv->preferred_height = SYNTHVID_HEIGHT_WIN8;
}
hv->mmio_megabytes = hdev->channel->offermsg.offer.mmio_megabytes;
return 0;
error:
vmbus_close(hdev->channel);
return ret;
}