From 2dc570561e7e85b0e75707619c7d4fca53742740 Mon Sep 17 00:00:00 2001 From: Jannik Vogel Date: Wed, 26 Jun 2019 08:07:24 +0200 Subject: [PATCH] nv2a: Fix VGA get_bpp for X1R5G5B5 --- hw/xbox/nv2a/nv2a.c | 33 ++++++++++++++++++++++++++++++--- hw/xbox/nv2a/nv2a_int.h | 1 + hw/xbox/nv2a/nv2a_pramdac.c | 6 ++++++ hw/xbox/nv2a/nv2a_regs.h | 3 ++- 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/hw/xbox/nv2a/nv2a.c b/hw/xbox/nv2a/nv2a.c index 5592480590..c564ca2b8b 100644 --- a/hw/xbox/nv2a/nv2a.c +++ b/hw/xbox/nv2a/nv2a.c @@ -317,10 +317,37 @@ static void nv2a_overlay_draw_line(VGACommonState *vga, uint8_t *line, int y) static int nv2a_get_bpp(VGACommonState *s) { - if ((s->cr[0x28] & 3) == 3) { - return 32; + NV2AState *d = container_of(s, NV2AState, vga); + + int depth = s->cr[0x28] & 3; + + int bpp; + switch (depth) { + case 0: + /* FIXME: This case is sometimes hit during early Xbox startup. + * Presumably a race-condition where VGA isn't initialized, yet. + * `bpp = 0` mimics old code that did `bpp = depth * 8;`. + * This works around the issue of this mode being unhandled. + * However, QEMU VGA uses a 4bpp mode if `bpp = 0`. + * We don't know if Xbox hardware would do the same. */ + bpp = 0; + break; + case 2: + bpp = d->pramdac.general_control & + NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL ? 16 : 15; + break; + case 3: + bpp = 32; + break; + default: + /* This is only a fallback path */ + bpp = depth * 8; + fprintf(stderr, "Unknown VGA depth: %d\n", depth); + assert(false); + break; } - return (s->cr[0x28] & 3) * 8; + + return bpp; } static void nv2a_get_offsets(VGACommonState *s, diff --git a/hw/xbox/nv2a/nv2a_int.h b/hw/xbox/nv2a/nv2a_int.h index 6a07377a5e..446d62ba46 100644 --- a/hw/xbox/nv2a/nv2a_int.h +++ b/hw/xbox/nv2a/nv2a_int.h @@ -330,6 +330,7 @@ typedef struct NV2AState { uint64_t core_clock_freq; uint32_t memory_clock_coeff; uint32_t video_clock_coeff; + uint32_t general_control; } pramdac; } NV2AState; diff --git a/hw/xbox/nv2a/nv2a_pramdac.c b/hw/xbox/nv2a/nv2a_pramdac.c index bfd09a5fb2..d7f7fd75e8 100644 --- a/hw/xbox/nv2a/nv2a_pramdac.c +++ b/hw/xbox/nv2a/nv2a_pramdac.c @@ -41,6 +41,9 @@ uint64_t pramdac_read(void *opaque, hwaddr addr, unsigned int size) | NV_PRAMDAC_PLL_TEST_COUNTER_MPLL_LOCK | NV_PRAMDAC_PLL_TEST_COUNTER_VPLL_LOCK; break; + case NV_PRAMDAC_GENERAL_CONTROL: + r = d->pramdac.general_control; + break; default: break; } @@ -81,6 +84,9 @@ void pramdac_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size) case NV_PRAMDAC_VPLL_COEFF: d->pramdac.video_clock_coeff = val; break; + case NV_PRAMDAC_GENERAL_CONTROL: + d->pramdac.general_control = val; + break; default: break; } diff --git a/hw/xbox/nv2a/nv2a_regs.h b/hw/xbox/nv2a/nv2a_regs.h index 84e3005793..e27e43aa82 100644 --- a/hw/xbox/nv2a/nv2a_regs.h +++ b/hw/xbox/nv2a/nv2a_regs.h @@ -678,7 +678,8 @@ # define NV_PRAMDAC_PLL_TEST_COUNTER_NVPLL_LOCK (1 << 29) # define NV_PRAMDAC_PLL_TEST_COUNTER_MPLL_LOCK (1 << 30) # define NV_PRAMDAC_PLL_TEST_COUNTER_VPLL_LOCK (1 << 31) - +#define NV_PRAMDAC_GENERAL_CONTROL 0x00000600 +# define NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL (1 << 12) #define NV_USER_DMA_PUT 0x40 #define NV_USER_DMA_GET 0x44