diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-11-07 17:34:54 -0500 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-11-07 17:34:54 -0500 |
| commit | 22ca5d04f2fdd91500ff82b6e5154a7e5efb50d4 (patch) | |
| tree | 20b5736c4139773f812b0a29b0d6fac0e66b671e | |
| parent | 4df7e1c5abeee92ddd6f2dc32e3497cf64f948cb (diff) | |
| download | open-iov-22ca5d04f2fdd91500ff82b6e5154a7e5efb50d4.tar.gz open-iov-22ca5d04f2fdd91500ff82b6e5154a7e5efb50d4.zip | |
vgsp-rm: report connected DFP-0 + 1920x1080 EDID, accept IMP, route head
- 0x730108/0x730240/0x73028a: display 0 connected, DFP, 600 MHz pclk
- 0x730245 GET_EDID_V2: 128-byte EDID 1.3 (1920x1080@60 + range limits)
- 0xc3720101 IMP: echo request + bIsPossible (byte 1904) so EDID modes validate
- 0x73010b head routing map: displayMask=1, headRoutingMap=0 (display 0->head 0)
- alloc dump limit raised to expose the display channel allocations
The guest boot display init still needs the display engine (core notifier +
vblank/swap-ready) before the mode set can complete; this is an incremental
display-state step, not a boot-fix (evidence: backups/evidence-20261002-victory/r1619/).
| -rw-r--r-- | kernel/gsp_rm_emulation.c | 171 |
1 files changed, 167 insertions, 4 deletions
diff --git a/kernel/gsp_rm_emulation.c b/kernel/gsp_rm_emulation.c index d43333f..48e91dd 100644 --- a/kernel/gsp_rm_emulation.c +++ b/kernel/gsp_rm_emulation.c @@ -2197,11 +2197,19 @@ static void gsp_rm_handle_display_get_ip_version(struct gsp_emu_context *ctx, * displayMask=1|DDC=1, SPECIFIC_OR_GET_INFO (0x73028b) SOR / * SOR_SINGLE_TMDS_A (TMDS avoids the whole DP-AUX lib), SPECIFIC_ * GET_CONNECTOR_DATA (0x730250) one HDMI_A entry, DFP_GET_INFO (0x731140) - * TMDS flags. Still a headless setup: no connect state or EDID is claimed - * (those are modeset-time queries). + * TMDS flags. + * + * r1617: the connector was still reported disconnected. NVKMS + * nvRmGetConnectedDpys() polls SYSTEM_GET_CONNECT_STATE (0x730108) and a zero + * displayMask makes it see no displays; the driver then sets the NoScanout + * NULL mode and the NVKMS deferred display close bumps the shared nvClip + * modeNum past the GL client's mode-switch test (which requires bit0 clear). + * Report display 0 connected, its type as DFP (0x730240) and a 600 MHz pclk + * limit (0x73028a); the SOR caps seed already gives 600 MHz TMDS. The EDID + * itself is served by gsp_rm_handle_display_get_edid() (0x730245). */ static void gsp_rm_handle_display_heads(struct gsp_emu_context *ctx, u32 cmd, - u32 req_size, + u32 req_size, const u8 *req_params, u32 *out_status, u8 *out_params, u32 *out_params_size) { @@ -2234,6 +2242,39 @@ static void gsp_rm_handle_display_heads(struct gsp_emu_context *ctx, u32 cmd, memcpy(out_params + 8, &one, 4); } break; + case 0x730108: /* SYSTEM_GET_CONNECT_STATE: {subDev, flags, + * displayMask, retryTimeMs} - report the requested + * display 0 connected (NVKMS polls this ~126x while + * opening the display; zero made it set NoScanout). */ + if (sz >= 16) { + u32 mask = 0; + + if (req_params) + memcpy(&mask, req_params + 8, 4); + mask &= 1; + if (!mask) + mask = 1; + memcpy(out_params + 8, &mask, 4); + } + break; + case 0x730240: /* SPECIFIC_GET_TYPE: {subDev, displayId, + * displayType} - display 0 is our static DFP. */ + if (sz >= 12) { + v = 2; /* NV0073_CTRL_SPECIFIC_DISPLAY_TYPE_DFP */ + memcpy(out_params + 8, &v, 4); + } + break; + case 0x73028a: /* SPECIFIC_GET_PCLK_LIMIT: {subDev, displayId, + * pclkLimit, orPclkLimit, vbPclkLimit} in kHz. The + * TMDS SOR caps (600 MHz) are the real limit; a sane + * RM value also keeps non-TMDS validation working. */ + if (sz >= 20) { + v = 600000; + memcpy(out_params + 8, &v, 4); + memcpy(out_params + 12, &v, 4); + memcpy(out_params + 16, &v, 4); + } + break; case 0x73028b: /* SPECIFIC_OR_GET_INFO: {subDev, displayId, index, * type, protocol, ditherType, ditherAlgo, location, * rootPortId, dcbIndex, vbiosAddress, bIsLitByVbios, @@ -2261,6 +2302,24 @@ static void gsp_rm_handle_display_heads(struct gsp_emu_context *ctx, u32 cmd, /* data[0].location = 0 */ } break; + case 0x73010b: /* SYSTEM_GET_HEAD_ROUTING_MAP: {subDev, + * displayMask, oldDisplayMask, oldHeadRoutingMap, + * headRoutingMap} - the generic zero reply dropped + * displayMask (0), so the boot display init's head- + * routing step saw no display. Keep display 0 routed + * to head 0 (headRoutingMap=0). */ + if (sz >= 20) { + u32 mask = 0; + + if (req_params) + memcpy(&mask, req_params + 4, 4); + mask &= 1; + if (!mask) + mask = 1; + memcpy(out_params + 4, &mask, 4); + /* headRoutingMap stays 0: display 0 -> head 0 */ + } + break; case 0x731140: /* DFP_GET_INFO: {subDev, displayId, flags, * UHBRSupportedByDfp} - flags signal bits = TMDS(0) */ break; /* zeros are the TMDS signal value */ @@ -2273,6 +2332,66 @@ static void gsp_rm_handle_display_heads(struct gsp_emu_context *ctx, u32 cmd, } /* + * NV0073_CTRL_CMD_SPECIFIC_GET_EDID_V2 (0x730245) + * + * nvidia-modeset's ReadEdidFromResman() reads the display EDID through this + * control, not through the I2C_TRANSACTION (0x402c0105) path. A zero or + * invalid reply leaves the DFP with no modes, so the driver falls back to + * the NoScanout NULL mode and the NVKMS deferred close bumps the shared + * nvClip modeNum. Serve a 128-byte EDID 1.3 base block with a 1920x1080@60 + * detailed timing plus the 640x480/800x600/1024x768 established timings + * (checksum valid, no extension block). + * + * Params (NV0073_CTRL_SPECIFIC_GET_EDID_V2_PARAMS): subDeviceInstance@0, + * displayId@4, bufferSize@8, flags@12, edidBuffer@16[2048]. Echo the whole + * request and patch bufferSize + edidBuffer (the request is zero-initialized + * by the caller; the reply must stay the same serialized length). + */ +static void gsp_rm_handle_display_get_edid(struct gsp_emu_context *ctx, + u32 req_size, + const u8 *req_params, + u32 *out_status, + u8 *out_params, + u32 *out_params_size) +{ + static const u8 edid[128] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x3a, 0xc4, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x22, 0x01, 0x03, 0x80, 0x33, 0x1d, 0x78, + 0x0a, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, + 0x0f, 0x50, 0x54, 0x21, 0x08, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, + 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, + 0x45, 0x00, 0xfd, 0x1e, 0x11, 0x00, 0x00, 0x1a, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, + 0x4b, 0x1e, 0x53, 0x11, 0x00, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, + }; + u32 buffer_size = sizeof(edid); + u32 display_id = 0; + + if (!req_params || req_size < 16) { + *out_params_size = 0; + *out_status = 0x1; /* NV_ERR_INVALID_ARGUMENT */ + return; + } + memcpy(out_params, req_params, req_size); + memcpy(out_params + 8, &buffer_size, 4); + memcpy(out_params + 16, edid, sizeof(edid)); + memcpy(&display_id, out_params + 4, 4); + *out_params_size = req_size; + *out_status = 0; + nv_vgpu_info(ctx->vgpu, + "vgsp-rm: GET_EDID_V2 displayId=%u -> %u-byte EDID (1920x1080@60)", + display_id, buffer_size); +} + +/* * CE_GET_FAULT_METHOD_BUFFER_SIZE (0x20802a08) * * kchangrpInit allocates one fault method buffer per runqueue of this @@ -3283,7 +3402,7 @@ static void gsp_rm_handle_rm_alloc(struct gsp_emu_context *ctx, * classified yet (rate-limited). %phN is capped at 64 bytes per print, * so chunk it. */ if (gsp_sema_dbg && gsp_rm_alloc_interesting(class_id) && - rm->alloc_param_dumps < 60) { + rm->alloc_param_dumps < 500) { u32 shown = min(params_size, (u32)384); u32 off; @@ -4264,14 +4383,58 @@ static void gsp_rm_handle_rm_control(struct gsp_emu_context *ctx, case 0x730287: /* NV0073_CTRL_CMD_SPECIFIC_GET_ALL_HEAD_MASK */ case 0x7302ad: /* NV0073_CTRL_CMD_SPECIFIC_GET_VALID_HEAD_WINDOW_ASSIGNMENT */ case 0x730107: /* NV0073_CTRL_CMD_SYSTEM_GET_SUPPORTED */ + case 0x730108: /* NV0073_CTRL_CMD_SYSTEM_GET_CONNECT_STATE */ + case 0x730240: /* NV0073_CTRL_CMD_SPECIFIC_GET_TYPE */ + case 0x73028a: /* NV0073_CTRL_CMD_SPECIFIC_GET_PCLK_LIMIT */ case 0x73028b: /* NV0073_CTRL_CMD_SPECIFIC_OR_GET_INFO */ case 0x730250: /* NV0073_CTRL_CMD_SPECIFIC_GET_CONNECTOR_DATA */ + case 0x73010b: /* NV0073_CTRL_CMD_SYSTEM_GET_HEAD_ROUTING_MAP */ case 0x731140: /* NV0073_CTRL_CMD_DFP_GET_INFO */ gsp_rm_handle_display_heads(ctx, req.cmd, req.paramsSize, + req_data + GSP_RM_CTRL_HDR_SIZE, &out_status, reply + GSP_RM_CTRL_HDR_SIZE, &out_params_size); break; + case 0x730245: /* NV0073_CTRL_CMD_SPECIFIC_GET_EDID_V2 */ + gsp_rm_handle_display_get_edid(ctx, req.paramsSize, + req_data + GSP_RM_CTRL_HDR_SIZE, + &out_status, + reply + GSP_RM_CTRL_HDR_SIZE, &out_params_size); + break; + + case 0xc3720101: { /* NVC372_CTRL_CMD_IS_MODE_POSSIBLE (IMP) */ + /* + * nvidia-modeset's GPU IMP check: 2048-byte params + * (NVC372_CTRL_IS_MODE_POSSIBLE_PARAMS) with the mode inputs + * and the bIsPossible answer at byte offset 1904 (NvBool is + * one byte). The caller memsets the struct before filling + * the inputs, so echoing the request and flipping + * bIsPossible is coherent. The generic zero reply answered + * "not possible", which made every EDID mode fail with + * "GPU extended capability check failed" before any mode set. + */ + u8 *rp = reply + GSP_RM_CTRL_HDR_SIZE; + const u8 *pp = req_data + GSP_RM_CTRL_HDR_SIZE; + + if (req.paramsSize >= 1905 && + req.paramsSize <= GSPRM_MAX_SINGLE_ELEM_DATA) { + memcpy(rp, pp, req.paramsSize); + rp[1904] = 1; /* bIsPossible */ + out_params_size = req.paramsSize; + } else { + out_params_size = 0; + } + if (gsp_sema_dbg) + nv_vgpu_info(ctx->vgpu, + "vgsp-rm: IS_MODE_POSSIBLE in=%u -> possible (heads=%u windows=%u)", + req.paramsSize, + req.paramsSize > 5 ? pp[4] : 0, + req.paramsSize > 5 ? pp[5] : 0); + out_status = 0; + break; + } + case TU104_CTRL_CE_GET_FAULT_METHOD_BUFFER_SIZE: gsp_rm_handle_ce_get_fault_method_buffer_size(ctx, sequence, &out_status, |
