Skip to content

Commit 2ce1d00

Browse files
committed
nv2a: Update Conexant field counter on vblank
DreamWorks: Over the Hedge performs D3D resets before playing FMVs at startup. As a part of this process, an initialization is performed that checks to see if a DirectX frame counter aligns with the counter reported by the TV encoder (at least if Conexant is the encoder, it looks like it is bypassed for other cases). If there is a mismatch, the DirectX frame counter is incremented. This can cause an issue where the vblank interrupt handler misses the expected frame counter, causing it to fail to update the nv2a buffer read index via the NV_PGRAPH_INCREMENT PGRAPH register. In this particular title, this leads to a livelock where the pushbuffer is waiting on a buffer flip that cannot be triggered until the current buffer is marked as read via the register write. This change populates the low nibble of the Conexant device with a simple frame counter. See https://xboxdevwiki.net/Video_Encoder for a link to the datasheet. Unfortunately testing this on HW has proved difficult, as some state is changed during D3D Present calls that causes SMBus reads to stop providing accurate data. Thus, this change is mostly a guess at the actual behavior along with some light testing without using D3D that shows the field value incrementing from 0-3 and wrapping around forever, presumably aligned with vsync. Fixes #1962
1 parent 8c7de72 commit 2ce1d00

File tree

3 files changed

+23
-1
lines changed

3 files changed

+23
-1
lines changed

hw/xbox/nv2a/nv2a.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
*/
2121

2222
#include "hw/xbox/nv2a/nv2a_int.h"
23+
#include "hw/xbox/smbus.h"
2324
#include "qemu/main-loop.h"
2425

2526
void nv2a_update_irq(NV2AState *d)
@@ -198,8 +199,13 @@ static void nv2a_vga_gfx_update(void *opaque)
198199
VGACommonState *vga = opaque;
199200
vga->hw_ops->gfx_update(vga);
200201

202+
201203
NV2AState *d = container_of(vga, NV2AState, vga);
202-
d->pcrtc.pending_interrupts |= NV_PCRTC_INTR_0_VBLANK;
204+
if (!(d->pcrtc.pending_interrupts & NV_PCRTC_INTR_0_VBLANK)) {
205+
smbus_cx25871_notify_vblank();
206+
d->pcrtc.pending_interrupts |= NV_PCRTC_INTR_0_VBLANK;
207+
}
208+
203209
d->pcrtc.raster = 0;
204210

205211
nv2a_update_irq(d);

hw/xbox/smbus.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ void smbus_fs454_init(I2CBus *smbus, int address);
3131
void smbus_xcalibur_init(I2CBus *smbus, int address);
3232
void smbus_adm1032_init(I2CBus *smbus, int address);
3333

34+
/**
35+
* Must be called on every vblank interrupt to update the Conexant field
36+
* counter.
37+
*/
38+
void smbus_cx25871_notify_vblank(void);
39+
3440
bool xbox_smc_avpack_to_reg(const char *avpack, uint8_t *value);
3541
void xbox_smc_append_avpack_hint(Error **errp);
3642
void xbox_smc_append_smc_version_hint(Error **errp);

hw/xbox/smbus_cx25871.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,13 @@ void smbus_cx25871_init(I2CBus *smbus, int address)
117117
qdev_prop_set_uint8(dev, "address", address);
118118
qdev_realize_and_unref(dev, (BusState *)smbus, &error_fatal);
119119
}
120+
121+
void smbus_cx25871_notify_vblank(void)
122+
{
123+
Object *obj = object_resolve_path_type("", TYPE_SMBUS_CX25871, NULL);
124+
if (obj) {
125+
SMBusCX25871Device *cx = SMBUS_CX25871(obj);
126+
cx->registers[0x06] =
127+
(cx->registers[0x06] & 0xF0) | ((cx->registers[0x06] + 1) & 0x03);
128+
}
129+
}

0 commit comments

Comments
 (0)