mirror of https://github.com/xemu-project/xemu.git
2908 lines
95 KiB
C
2908 lines
95 KiB
C
/*
|
|
* QEMU Geforce NV2A implementation
|
|
*
|
|
* Copyright (c) 2012 espes
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 or
|
|
* (at your option) version 3 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "hw.h"
|
|
#include "pc.h"
|
|
#include "console.h"
|
|
#include "pci.h"
|
|
#include "vga.h"
|
|
#include "vga_int.h"
|
|
#include "qemu-queue.h"
|
|
#include "qemu-thread.h"
|
|
#include "qstring.h"
|
|
|
|
#include "nv2a_vsh.h"
|
|
|
|
#ifdef __APPLE__
|
|
#include <OpenGL/gl.h>
|
|
#include <OpenGL/OpenGL.h>
|
|
#include <OpenGL/CGLTypes.h>
|
|
#include <OpenGL/CGLCurrent.h>
|
|
#include <GLUT/glut.h>
|
|
#else
|
|
#include <X11/Xlib.h>
|
|
#include <GL/gl.h>
|
|
#include <GL/glx.h>
|
|
#include <GL/glut.h>
|
|
#endif
|
|
|
|
#include "nv2a.h"
|
|
|
|
//#define DEBUG_NV2A
|
|
#ifdef DEBUG_NV2A
|
|
# define NV2A_DPRINTF(format, ...) printf(format, ## __VA_ARGS__)
|
|
#else
|
|
# define NV2A_DPRINTF(format, ...) do { } while (0)
|
|
#endif
|
|
|
|
|
|
#define NV_NUM_BLOCKS 21
|
|
#define NV_PMC 0 /* card master control */
|
|
#define NV_PBUS 1 /* bus control */
|
|
#define NV_PFIFO 2 /* MMIO and DMA FIFO submission to PGRAPH and VPE */
|
|
#define NV_PFIFO_CACHE 3
|
|
#define NV_PRMA 4 /* access to BAR0/BAR1 from real mode */
|
|
#define NV_PVIDEO 5 /* video overlay */
|
|
#define NV_PTIMER 6 /* time measurement and time-based alarms */
|
|
#define NV_PCOUNTER 7 /* performance monitoring counters */
|
|
#define NV_PVPE 8 /* MPEG2 decoding engine */
|
|
#define NV_PTV 9 /* TV encoder */
|
|
#define NV_PRMFB 10 /* aliases VGA memory window */
|
|
#define NV_PRMVIO 11 /* aliases VGA sequencer and graphics controller registers */
|
|
#define NV_PFB 12 /* memory interface */
|
|
#define NV_PSTRAPS 13 /* straps readout / override */
|
|
#define NV_PGRAPH 14 /* accelerated 2d/3d drawing engine */
|
|
#define NV_PCRTC 15 /* more CRTC controls */
|
|
#define NV_PRMCIO 16 /* aliases VGA CRTC and attribute controller registers */
|
|
#define NV_PRAMDAC 17 /* RAMDAC, cursor, and PLL control */
|
|
#define NV_PRMDIO 18 /* aliases VGA palette registers */
|
|
#define NV_PRAMIN 19 /* RAMIN access */
|
|
#define NV_USER 20 /* PFIFO MMIO and DMA submission area */
|
|
|
|
|
|
|
|
#define NV_PMC_BOOT_0 0x00000000
|
|
#define NV_PMC_INTR_0 0x00000100
|
|
# define NV_PMC_INTR_0_PFIFO (1 << 8)
|
|
# define NV_PMC_INTR_0_PGRAPH (1 << 12)
|
|
# define NV_PMC_INTR_0_PCRTC (1 << 24)
|
|
# define NV_PMC_INTR_0_PBUS (1 << 28)
|
|
# define NV_PMC_INTR_0_SOFTWARE (1 << 31)
|
|
#define NV_PMC_INTR_EN_0 0x00000140
|
|
# define NV_PMC_INTR_EN_0_HARDWARE 1
|
|
# define NV_PMC_INTR_EN_0_SOFTWARE 2
|
|
#define NV_PMC_ENABLE 0x00000200
|
|
# define NV_PMC_ENABLE_PFIFO (1 << 8)
|
|
# define NV_PMC_ENABLE_PGRAPH (1 << 12)
|
|
|
|
|
|
/* These map approximately to the pci registers */
|
|
#define NV_PBUS_PCI_NV_0 0x00000800
|
|
# define NV_PBUS_PCI_NV_0_VENDOR_ID 0x0000FFFF
|
|
# define NV_CONFIG_PCI_NV_0_DEVICE_ID 0xFFFF0000
|
|
#define NV_PBUS_PCI_NV_1 0x00000804
|
|
#define NV_PBUS_PCI_NV_2 0x00000808
|
|
# define NV_PBUS_PCI_NV_2_REVISION_ID 0x000000FF
|
|
# define NV_PBUS_PCI_NV_2_CLASS_CODE 0xFFFFFF00
|
|
|
|
|
|
#define NV_PFIFO_INTR_0 0x00000100
|
|
# define NV_PFIFO_INTR_0_CACHE_ERROR (1 << 0)
|
|
# define NV_PFIFO_INTR_0_RUNOUT (1 << 4)
|
|
# define NV_PFIFO_INTR_0_RUNOUT_OVERFLOW (1 << 8)
|
|
# define NV_PFIFO_INTR_0_DMA_PUSHER (1 << 12)
|
|
# define NV_PFIFO_INTR_0_DMA_PT (1 << 16)
|
|
# define NV_PFIFO_INTR_0_SEMAPHORE (1 << 20)
|
|
# define NV_PFIFO_INTR_0_ACQUIRE_TIMEOUT (1 << 24)
|
|
#define NV_PFIFO_INTR_EN_0 0x00000140
|
|
# define NV_PFIFO_INTR_EN_0_CACHE_ERROR (1 << 0)
|
|
# define NV_PFIFO_INTR_EN_0_RUNOUT (1 << 4)
|
|
# define NV_PFIFO_INTR_EN_0_RUNOUT_OVERFLOW (1 << 8)
|
|
# define NV_PFIFO_INTR_EN_0_DMA_PUSHER (1 << 12)
|
|
# define NV_PFIFO_INTR_EN_0_DMA_PT (1 << 16)
|
|
# define NV_PFIFO_INTR_EN_0_SEMAPHORE (1 << 20)
|
|
# define NV_PFIFO_INTR_EN_0_ACQUIRE_TIMEOUT (1 << 24)
|
|
#define NV_PFIFO_RAMHT 0x00000210
|
|
# define NV_PFIFO_RAMHT_BASE_ADDRESS 0x000001F0
|
|
# define NV_PFIFO_RAMHT_SIZE 0x00030000
|
|
# define NV_PFIFO_RAMHT_SIZE_4K 0x00000000
|
|
# define NV_PFIFO_RAMHT_SIZE_8K 0x00010000
|
|
# define NV_PFIFO_RAMHT_SIZE_16K 0x00020000
|
|
# define NV_PFIFO_RAMHT_SIZE_32K 0x00030000
|
|
# define NV_PFIFO_RAMHT_SEARCH 0x03000000
|
|
# define NV_PFIFO_RAMHT_SEARCH_16 0x00000000
|
|
# define NV_PFIFO_RAMHT_SEARCH_32 0x01000000
|
|
# define NV_PFIFO_RAMHT_SEARCH_64 0x02000000
|
|
# define NV_PFIFO_RAMHT_SEARCH_128 0x03000000
|
|
#define NV_PFIFO_RAMFC 0x00000214
|
|
# define NV_PFIFO_RAMFC_BASE_ADDRESS1 0x000001FC
|
|
# define NV_PFIFO_RAMFC_SIZE 0x00010000
|
|
# define NV_PFIFO_RAMFC_BASE_ADDRESS2 0x00FE0000
|
|
#define NV_PFIFO_RAMRO 0x00000218
|
|
# define NV_PFIFO_RAMRO_BASE_ADDRESS 0x000001FE
|
|
# define NV_PFIFO_RAMRO_SIZE 0x00010000
|
|
#define NV_PFIFO_RUNOUT_STATUS 0x00000400
|
|
# define NV_PFIFO_RUNOUT_STATUS_RANOUT (1 << 0)
|
|
# define NV_PFIFO_RUNOUT_STATUS_LOW_MARK (1 << 4)
|
|
# define NV_PFIFO_RUNOUT_STATUS_HIGH_MARK (1 << 8)
|
|
#define NV_PFIFO_MODE 0x00000504
|
|
#define NV_PFIFO_DMA 0x00000508
|
|
#define NV_PFIFO_CACHE1_PUSH0 0x00001200
|
|
# define NV_PFIFO_CACHE1_PUSH0_ACCESS (1 << 0)
|
|
#define NV_PFIFO_CACHE1_PUSH1 0x00001204
|
|
# define NV_PFIFO_CACHE1_PUSH1_CHID 0x0000001F
|
|
# define NV_PFIFO_CACHE1_PUSH1_MODE 0x00000100
|
|
#define NV_PFIFO_CACHE1_STATUS 0x00001214
|
|
# define NV_PFIFO_CACHE1_STATUS_LOW_MARK (1 << 4)
|
|
# define NV_PFIFO_CACHE1_STATUS_HIGH_MARK (1 << 8)
|
|
#define NV_PFIFO_CACHE1_DMA_PUSH 0x00001220
|
|
# define NV_PFIFO_CACHE1_DMA_PUSH_ACCESS (1 << 0)
|
|
# define NV_PFIFO_CACHE1_DMA_PUSH_STATE (1 << 4)
|
|
# define NV_PFIFO_CACHE1_DMA_PUSH_BUFFER (1 << 8)
|
|
# define NV_PFIFO_CACHE1_DMA_PUSH_STATUS (1 << 12)
|
|
# define NV_PFIFO_CACHE1_DMA_PUSH_ACQUIRE (1 << 16)
|
|
#define NV_PFIFO_CACHE1_DMA_FETCH 0x00001224
|
|
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG 0x000000F8
|
|
# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE 0x0000E000
|
|
# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS 0x001F0000
|
|
#define NV_PFIFO_CACHE1_DMA_STATE 0x00001228
|
|
# define NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE (1 << 0)
|
|
# define NV_PFIFO_CACHE1_DMA_STATE_METHOD 0x00001FFC
|
|
# define NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL 0x0000E000
|
|
# define NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT 0x1FFC0000
|
|
# define NV_PFIFO_CACHE1_DMA_STATE_ERROR 0xE0000000
|
|
# define NV_PFIFO_CACHE1_DMA_STATE_ERROR_NONE 0x00000000
|
|
# define NV_PFIFO_CACHE1_DMA_STATE_ERROR_CALL 0x00000001
|
|
# define NV_PFIFO_CACHE1_DMA_STATE_ERROR_NON_CACHE 0x00000002
|
|
# define NV_PFIFO_CACHE1_DMA_STATE_ERROR_RETURN 0x00000003
|
|
# define NV_PFIFO_CACHE1_DMA_STATE_ERROR_RESERVED_CMD 0x00000004
|
|
# define NV_PFIFO_CACHE1_DMA_STATE_ERROR_PROTECTION 0x00000006
|
|
#define NV_PFIFO_CACHE1_DMA_INSTANCE 0x0000122C
|
|
# define NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS 0x0000FFFF
|
|
#define NV_PFIFO_CACHE1_DMA_PUT 0x00001240
|
|
#define NV_PFIFO_CACHE1_DMA_GET 0x00001244
|
|
#define NV_PFIFO_CACHE1_DMA_SUBROUTINE 0x0000124C
|
|
# define NV_PFIFO_CACHE1_DMA_SUBROUTINE_RETURN_OFFSET 0x1FFFFFFC
|
|
# define NV_PFIFO_CACHE1_DMA_SUBROUTINE_STATE (1 << 0)
|
|
#define NV_PFIFO_CACHE1_PULL0 0x00001250
|
|
# define NV_PFIFO_CACHE1_PULL0_ACCESS (1 << 0)
|
|
#define NV_PFIFO_CACHE1_ENGINE 0x00001280
|
|
#define NV_PFIFO_CACHE1_DMA_DCOUNT 0x000012A0
|
|
# define NV_PFIFO_CACHE1_DMA_DCOUNT_VALUE 0x00001FFC
|
|
#define NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW 0x000012A4
|
|
# define NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW_OFFSET 0x1FFFFFFC
|
|
#define NV_PFIFO_CACHE1_DMA_RSVD_SHADOW 0x000012A8
|
|
#define NV_PFIFO_CACHE1_DMA_DATA_SHADOW 0x000012AC
|
|
|
|
|
|
#define NV_PGRAPH_INTR 0x00000100
|
|
# define NV_PGRAPH_INTR_NOTIFY (1 << 0)
|
|
# define NV_PGRAPH_INTR_MISSING_HW (1 << 4)
|
|
# define NV_PGRAPH_INTR_TLB_PRESENT_DMA_R (1 << 6)
|
|
# define NV_PGRAPH_INTR_TLB_PRESENT_DMA_W (1 << 7)
|
|
# define NV_PGRAPH_INTR_TLB_PRESENT_TEX_A (1 << 8)
|
|
# define NV_PGRAPH_INTR_TLB_PRESENT_TEX_B (1 << 9)
|
|
# define NV_PGRAPH_INTR_TLB_PRESENT_VTX (1 << 10)
|
|
# define NV_PGRAPH_INTR_CONTEXT_SWITCH (1 << 12)
|
|
# define NV_PGRAPH_INTR_STATE3D (1 << 13)
|
|
# define NV_PGRAPH_INTR_BUFFER_NOTIFY (1 << 16)
|
|
# define NV_PGRAPH_INTR_ERROR (1 << 20)
|
|
# define NV_PGRAPH_INTR_SINGLE_STEP (1 << 24)
|
|
#define NV_PGRAPH_INTR_EN 0x00000140
|
|
# define NV_PGRAPH_INTR_EN_NOTIFY (1 << 0)
|
|
# define NV_PGRAPH_INTR_EN_MISSING_HW (1 << 4)
|
|
# define NV_PGRAPH_INTR_EN_TLB_PRESENT_DMA_R (1 << 6)
|
|
# define NV_PGRAPH_INTR_EN_TLB_PRESENT_DMA_W (1 << 7)
|
|
# define NV_PGRAPH_INTR_EN_TLB_PRESENT_TEX_A (1 << 8)
|
|
# define NV_PGRAPH_INTR_EN_TLB_PRESENT_TEX_B (1 << 9)
|
|
# define NV_PGRAPH_INTR_EN_TLB_PRESENT_VTX (1 << 10)
|
|
# define NV_PGRAPH_INTR_EN_CONTEXT_SWITCH (1 << 12)
|
|
# define NV_PGRAPH_INTR_EN_STATE3D (1 << 13)
|
|
# define NV_PGRAPH_INTR_EN_BUFFER_NOTIFY (1 << 16)
|
|
# define NV_PGRAPH_INTR_EN_ERROR (1 << 20)
|
|
# define NV_PGRAPH_INTR_EN_SINGLE_STEP (1 << 24)
|
|
#define NV_PGRAPH_CTX_CONTROL 0x00000144
|
|
# define NV_PGRAPH_CTX_CONTROL_MINIMUM_TIME 0x00000003
|
|
# define NV_PGRAPH_CTX_CONTROL_TIME (1 << 8)
|
|
# define NV_PGRAPH_CTX_CONTROL_CHID (1 << 16)
|
|
# define NV_PGRAPH_CTX_CONTROL_CHANGE (1 << 20)
|
|
# define NV_PGRAPH_CTX_CONTROL_SWITCHING (1 << 24)
|
|
# define NV_PGRAPH_CTX_CONTROL_DEVICE (1 << 28)
|
|
#define NV_PGRAPH_CTX_USER 0x00000148
|
|
# define NV_PGRAPH_CTX_USER_CHANNEL_3D (1 << 0)
|
|
# define NV_PGRAPH_CTX_USER_CHANNEL_3D_VALID (1 << 4)
|
|
# define NV_PGRAPH_CTX_USER_SUBCH 0x0000E000
|
|
# define NV_PGRAPH_CTX_USER_CHID 0x1F000000
|
|
# define NV_PGRAPH_CTX_USER_SINGLE_STEP (1 << 31)
|
|
#define NV_PGRAPH_CTX_SWITCH1 0x0000014C
|
|
# define NV_PGRAPH_CTX_SWITCH1_GRCLASS 0x000000FF
|
|
# define NV_PGRAPH_CTX_SWITCH1_CHROMA_KEY (1 << 12)
|
|
# define NV_PGRAPH_CTX_SWITCH1_SWIZZLE (1 << 14)
|
|
# define NV_PGRAPH_CTX_SWITCH1_PATCH_CONFIG 0x00038000
|
|
# define NV_PGRAPH_CTX_SWITCH1_SYNCHRONIZE (1 << 18)
|
|
# define NV_PGRAPH_CTX_SWITCH1_ENDIAN_MODE (1 << 19)
|
|
# define NV_PGRAPH_CTX_SWITCH1_CLASS_TYPE (1 << 22)
|
|
# define NV_PGRAPH_CTX_SWITCH1_SINGLE_STEP (1 << 23)
|
|
# define NV_PGRAPH_CTX_SWITCH1_PATCH_STATUS (1 << 24)
|
|
# define NV_PGRAPH_CTX_SWITCH1_CONTEXT_SURFACE0 (1 << 25)
|
|
# define NV_PGRAPH_CTX_SWITCH1_CONTEXT_SURFACE1 (1 << 26)
|
|
# define NV_PGRAPH_CTX_SWITCH1_CONTEXT_PATTERN (1 << 27)
|
|
# define NV_PGRAPH_CTX_SWITCH1_CONTEXT_ROP (1 << 28)
|
|
# define NV_PGRAPH_CTX_SWITCH1_CONTEXT_BETA1 (1 << 29)
|
|
# define NV_PGRAPH_CTX_SWITCH1_CONTEXT_BETA4 (1 << 30)
|
|
# define NV_PGRAPH_CTX_SWITCH1_VOLATILE_RESET (1 << 31)
|
|
#define NV_PGRAPH_TRAPPED_ADDR 0x00000704
|
|
# define NV_PGRAPH_TRAPPED_ADDR_MTHD 0x00001FFF
|
|
# define NV_PGRAPH_TRAPPED_ADDR_SUBCH 0x00070000
|
|
# define NV_PGRAPH_TRAPPED_ADDR_CHID 0x01F00000
|
|
# define NV_PGRAPH_TRAPPED_ADDR_DHV 0x10000000
|
|
#define NV_PGRAPH_CHANNEL_CTX_TABLE 0x00000780
|
|
# define NV_PGRAPH_CHANNEL_CTX_TABLE_INST 0x0000FFFF
|
|
#define NV_PGRAPH_CHANNEL_CTX_POINTER 0x00000784
|
|
# define NV_PGRAPH_CHANNEL_CTX_POINTER_INST 0x0000FFFF
|
|
#define NV_PGRAPH_CHANNEL_CTX_TRIGGER 0x00000788
|
|
# define NV_PGRAPH_CHANNEL_CTX_TRIGGER_READ_IN (1 << 0)
|
|
# define NV_PGRAPH_CHANNEL_CTX_TRIGGER_WRITE_OUT (1 << 1)
|
|
|
|
|
|
#define NV_PCRTC_INTR_0 0x00000100
|
|
# define NV_PCRTC_INTR_0_VBLANK (1 << 0)
|
|
#define NV_PCRTC_INTR_EN_0 0x00000140
|
|
# define NV_PCRTC_INTR_EN_0_VBLANK (1 << 0)
|
|
#define NV_PCRTC_START 0x00000800
|
|
#define NV_PCRTC_CONFIG 0x00000804
|
|
|
|
|
|
#define NV_PTIMER_INTR_0 0x00000100
|
|
# define NV_PTIMER_INTR_0_ALARM (1 << 0)
|
|
#define NV_PTIMER_INTR_EN_0 0x00000140
|
|
# define NV_PTIMER_INTR_EN_0_ALARM (1 << 0)
|
|
#define NV_PTIMER_NUMERATOR 0x00000200
|
|
#define NV_PTIMER_DENOMINATOR 0x00000210
|
|
#define NV_PTIMER_TIME_0 0x00000400
|
|
#define NV_PTIMER_TIME_1 0x00000410
|
|
#define NV_PTIMER_ALARM_0 0x00000420
|
|
|
|
|
|
#define NV_PFB_CFG0 0x00000200
|
|
# define NV_PFB_CFG0_PART 0x00000003
|
|
#define NV_PFB_CSTATUS 0x0000020C
|
|
|
|
|
|
#define NV_PRAMDAC_NVPLL_COEFF 0x00000500
|
|
# define NV_PRAMDAC_NVPLL_COEFF_MDIV 0x000000FF
|
|
# define NV_PRAMDAC_NVPLL_COEFF_NDIV 0x0000FF00
|
|
# define NV_PRAMDAC_NVPLL_COEFF_PDIV 0x00070000
|
|
#define NV_PRAMDAC_MPLL_COEFF 0x00000504
|
|
# define NV_PRAMDAC_MPLL_COEFF_MDIV 0x000000FF
|
|
# define NV_PRAMDAC_MPLL_COEFF_NDIV 0x0000FF00
|
|
# define NV_PRAMDAC_MPLL_COEFF_PDIV 0x00070000
|
|
#define NV_PRAMDAC_VPLL_COEFF 0x00000508
|
|
# define NV_PRAMDAC_VPLL_COEFF_MDIV 0x000000FF
|
|
# define NV_PRAMDAC_VPLL_COEFF_NDIV 0x0000FF00
|
|
# define NV_PRAMDAC_VPLL_COEFF_PDIV 0x00070000
|
|
#define NV_PRAMDAC_PLL_TEST_COUNTER 0x00000514
|
|
# define NV_PRAMDAC_PLL_TEST_COUNTER_NOOFIPCLKS 0x000003FF
|
|
# define NV_PRAMDAC_PLL_TEST_COUNTER_VALUE 0x0000FFFF
|
|
# define NV_PRAMDAC_PLL_TEST_COUNTER_ENABLE (1 << 16)
|
|
# define NV_PRAMDAC_PLL_TEST_COUNTER_RESET (1 << 20)
|
|
# define NV_PRAMDAC_PLL_TEST_COUNTER_SOURCE 0x03000000
|
|
# define NV_PRAMDAC_PLL_TEST_COUNTER_VPLL2_LOCK (1 << 27)
|
|
# define NV_PRAMDAC_PLL_TEST_COUNTER_PDIV_RST (1 << 28)
|
|
# 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_USER_DMA_PUT 0x40
|
|
#define NV_USER_DMA_GET 0x44
|
|
#define NV_USER_REF 0x48
|
|
|
|
|
|
|
|
/* DMA objects */
|
|
#define NV_DMA_FROM_MEMORY_CLASS 0x02
|
|
#define NV_DMA_TO_MEMORY_CLASS 0x03
|
|
#define NV_DMA_IN_MEMORY_CLASS 0x3d
|
|
|
|
#define NV_DMA_CLASS 0x00000FFF
|
|
#define NV_DMA_PAGE_TABLE (1 << 12)
|
|
#define NV_DMA_PAGE_ENTRY (1 << 13)
|
|
#define NV_DMA_FLAGS_ACCESS (1 << 14)
|
|
#define NV_DMA_FLAGS_MAPPING_COHERENCY (1 << 15)
|
|
#define NV_DMA_TARGET 0x00030000
|
|
# define NV_DMA_TARGET_NVM 0x00000000
|
|
# define NV_DMA_TARGET_NVM_TILED 0x00010000
|
|
# define NV_DMA_TARGET_PCI 0x00020000
|
|
# define NV_DMA_TARGET_AGP 0x00030000
|
|
|
|
|
|
#define NV_RAMHT_HANDLE 0xFFFFFFFF
|
|
#define NV_RAMHT_INSTANCE 0x0000FFFF
|
|
#define NV_RAMHT_ENGINE 0x00030000
|
|
# define NV_RAMHT_ENGINE_SW 0x00000000
|
|
# define NV_RAMHT_ENGINE_GRAPHICS 0x00010000
|
|
# define NV_RAMHT_ENGINE_DVD 0x00020000
|
|
#define NV_RAMHT_CHID 0x1F000000
|
|
#define NV_RAMHT_STATUS 0x80000000
|
|
|
|
|
|
|
|
/* graphic classes and methods */
|
|
#define NV_SET_OBJECT 0x00000000
|
|
|
|
#define NV_KELVIN_PRIMITIVE 0x00000097
|
|
# define NV097_NO_OPERATION 0x00970100
|
|
# define NV097_WAIT_FOR_IDLE 0x00970110
|
|
# define NV097_SET_CONTEXT_DMA_NOTIFIES 0x00970180
|
|
# define NV097_SET_CONTEXT_DMA_A 0x00970184
|
|
# define NV097_SET_CONTEXT_DMA_B 0x00970188
|
|
# define NV097_SET_CONTEXT_DMA_STATE 0x00970190
|
|
# define NV097_SET_CONTEXT_DMA_VERTEX_A 0x0097019C
|
|
# define NV097_SET_CONTEXT_DMA_VERTEX_B 0x009701A0
|
|
# define NV097_SET_CONTEXT_DMA_SEMAPHORE 0x009701A4
|
|
# define NV097_SET_TRANSFORM_PROGRAM 0x00970B00
|
|
# define NV097_SET_TRANSFORM_CONSTANT 0x00970B80
|
|
# define NV097_SET_VERTEX_DATA_ARRAY_OFFSET 0x00971720
|
|
# define NV097_SET_VERTEX_DATA_ARRAY_FORMAT 0x00971760
|
|
# define NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE 0x0000000F
|
|
# define NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_UB_D3D 0
|
|
# define NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_S1 1
|
|
# define NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_F 2
|
|
# define NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_UB_OGL 3
|
|
# define NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_S32K 5
|
|
# define NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_CMP 6
|
|
# define NV097_SET_VERTEX_DATA_ARRAY_FORMAT_SIZE 0x000000F0
|
|
# define NV097_SET_VERTEX_DATA_ARRAY_FORMAT_STRIDE 0xFFFFFF00
|
|
# define NV097_SET_BEGIN_END 0x009717fC
|
|
# define NV097_SET_BEGIN_END_OP_END 0x00
|
|
# define NV097_SET_BEGIN_END_OP_POINTS 0x01
|
|
# define NV097_SET_BEGIN_END_OP_LINES 0x02
|
|
# define NV097_SET_BEGIN_END_OP_LINE_LOOP 0x03
|
|
# define NV097_SET_BEGIN_END_OP_LINE_STRIP 0x04
|
|
# define NV097_SET_BEGIN_END_OP_TRIANGLES 0x05
|
|
# define NV097_SET_BEGIN_END_OP_TRIANGLE_STRIP 0x06
|
|
# define NV097_SET_BEGIN_END_OP_TRIANGLE_FAN 0x07
|
|
# define NV097_SET_BEGIN_END_OP_QUADS 0x08
|
|
# define NV097_SET_BEGIN_END_OP_QUAD_STRIP 0x09
|
|
# define NV097_SET_BEGIN_END_OP_POLYGON 0x0A
|
|
# define NV097_ARRAY_ELEMENT16 0x00971800
|
|
# define NV097_ARRAY_ELEMENT32 0x00971808
|
|
# define NV097_DRAW_ARRAYS 0x00971810
|
|
# define NV097_INLINE_ARRAY 0x00971818
|
|
# define NV097_SET_SEMAPHORE_OFFSET 0x00971D6C
|
|
# define NV097_BACK_END_WRITE_SEMAPHORE_RELEASE 0x00971D70
|
|
# define NV097_SET_ZSTENCIL_CLEAR_VALUE 0x00971D8C
|
|
# define NV097_SET_COLOR_CLEAR_VALUE 0x00971D90
|
|
# define NV097_CLEAR_SURFACE 0x00971D94
|
|
# define NV097_CLEAR_SURFACE_Z (1 << 0)
|
|
# define NV097_CLEAR_SURFACE_STENCIL (1 << 1)
|
|
# define NV097_CLEAR_SURFACE_R (1 << 4)
|
|
# define NV097_CLEAR_SURFACE_G (1 << 5)
|
|
# define NV097_CLEAR_SURFACE_B (1 << 6)
|
|
# define NV097_CLEAR_SURFACE_A (1 << 7)
|
|
# define NV097_SET_TRANSFORM_EXECUTION_MODE 0x00971E94
|
|
# define NV097_SET_TRANSFORM_PROGRAM_CXT_WRITE_EN 0x00971E98
|
|
# define NV097_SET_TRANSFORM_PROGRAM_LOAD 0x00971E9C
|
|
# define NV097_SET_TRANSFORM_PROGRAM_START 0x00971EA0
|
|
# define NV097_SET_TRANSFORM_CONSTANT_LOAD 0x00971EA4
|
|
|
|
static const GLenum kelvin_primitive_map[] = {
|
|
0,
|
|
GL_POINTS,
|
|
GL_LINES,
|
|
GL_LINE_LOOP,
|
|
GL_LINE_STRIP,
|
|
GL_TRIANGLES,
|
|
GL_TRIANGLE_STRIP,
|
|
GL_TRIANGLE_FAN,
|
|
GL_QUADS,
|
|
GL_QUAD_STRIP,
|
|
GL_POLYGON,
|
|
};
|
|
|
|
|
|
#define NV_MEMORY_TO_MEMORY_FORMAT 0x00000039
|
|
# define NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY 0x00390180
|
|
# define NV_MEMORY_TO_MEMORY_FORMAT_DMA_SOURCE 0x00390184
|
|
|
|
|
|
|
|
|
|
#define NV2A_CRYSTAL_FREQ 13500000
|
|
#define NV2A_NUM_CHANNELS 32
|
|
#define NV2A_NUM_SUBCHANNELS 8
|
|
#define NV2A_MAX_PUSHBUFFER_METHOD 2048
|
|
|
|
#define NV2A_VERTEXSHADER_SLOTS 32 /*???*/
|
|
#define NV2A_MAX_VERTEXSHADER_LENGTH 136
|
|
#define NV2A_VERTEXSHADER_CONSTANTS 192
|
|
#define NV2A_VERTEXSHADER_ATTRIBUTES 16
|
|
|
|
|
|
|
|
enum FifoMode {
|
|
FIFO_PIO = 0,
|
|
FIFO_DMA = 1,
|
|
};
|
|
|
|
enum FIFOEngine {
|
|
ENGINE_SOFTWARE = 0,
|
|
ENGINE_GRAPHICS = 1,
|
|
ENGINE_DVD = 2,
|
|
};
|
|
|
|
|
|
|
|
typedef struct RAMHTEntry {
|
|
uint32_t handle;
|
|
hwaddr instance;
|
|
enum FIFOEngine engine;
|
|
unsigned int channel_id : 5;
|
|
bool valid;
|
|
} RAMHTEntry;
|
|
|
|
|
|
typedef struct DMAObject {
|
|
unsigned int dma_class;
|
|
hwaddr start;
|
|
hwaddr limit;
|
|
} DMAObject;
|
|
|
|
|
|
|
|
|
|
typedef struct VertexAttribute {
|
|
GLenum gl_type;
|
|
GLboolean gl_normalize;
|
|
unsigned int size; /* size of the data type */
|
|
unsigned int count; /* number of components */
|
|
uint32_t stride;
|
|
} VertexAttribute;
|
|
|
|
typedef struct VertexShaderConstant {
|
|
uint32 data[16];
|
|
} VertexShaderConstant;
|
|
|
|
typedef struct VertexShader {
|
|
unsigned int program_length;
|
|
uint32_t program_data[NV2A_MAX_VERTEXSHADER_LENGTH];
|
|
|
|
bool dirty;
|
|
GLuint gl_program;
|
|
} VertexShader;
|
|
|
|
typedef struct KelvinState {
|
|
hwaddr dma_notifies;
|
|
hwaddr dma_a;
|
|
hwaddr dma_b;
|
|
hwaddr dma_state;
|
|
hwaddr dma_vertex_a;
|
|
hwaddr dma_vertex_b;
|
|
hwaddr dma_semaphore;
|
|
unsigned int semaphore_offset;
|
|
|
|
unsigned int vertexshader_start_slot;
|
|
unsigned int vertexshader_load_slot;
|
|
VertexShader vertexshaders[NV2A_VERTEXSHADER_SLOTS];
|
|
|
|
unsigned int constant_load_slot;
|
|
VertexShaderConstant constants[NV2A_VERTEXSHADER_CONSTANTS];
|
|
|
|
|
|
GLenum gl_primitive_mode;
|
|
|
|
VertexAttribute vertex_attributes[NV2A_VERTEXSHADER_ATTRIBUTES];
|
|
|
|
struct {
|
|
uint32_t offset;
|
|
bool dma_select;
|
|
} vertex_attribute_offsets[NV2A_VERTEXSHADER_ATTRIBUTES];
|
|
|
|
unsigned int inline_vertex_data_length;
|
|
uint32_t inline_vertex_data[NV2A_MAX_PUSHBUFFER_METHOD];
|
|
|
|
unsigned int array_batch_length;
|
|
uint32_t array_batch[NV2A_MAX_PUSHBUFFER_METHOD];
|
|
|
|
bool use_vertex_program;
|
|
bool enable_vertex_program_write;
|
|
} KelvinState;
|
|
|
|
typedef struct GraphicsObject {
|
|
uint8_t graphics_class;
|
|
union {
|
|
struct {
|
|
hwaddr dma_notifies;
|
|
} m2mf;
|
|
|
|
KelvinState kelvin;
|
|
} data;
|
|
} GraphicsObject;
|
|
|
|
typedef struct GraphicsSubchannel {
|
|
hwaddr object_instance;
|
|
GraphicsObject object;
|
|
uint32_t object_cache[5];
|
|
} GraphicsSubchannel;
|
|
|
|
typedef struct GraphicsContext {
|
|
bool channel_3d;
|
|
unsigned int subchannel;
|
|
|
|
GraphicsSubchannel subchannel_data[NV2A_NUM_SUBCHANNELS];
|
|
|
|
|
|
|
|
CGLContextObj gl_context;
|
|
|
|
GLuint gl_framebuffer;
|
|
GLuint gl_renderbuffer;
|
|
} GraphicsContext;
|
|
|
|
|
|
|
|
|
|
typedef struct CacheEntry {
|
|
QSIMPLEQ_ENTRY(CacheEntry) entry;
|
|
|
|
unsigned int method : 14;
|
|
unsigned int subchannel : 3;
|
|
bool nonincreasing;
|
|
uint32_t parameter;
|
|
} CacheEntry;
|
|
|
|
typedef struct Cache1State {
|
|
unsigned int channel_id;
|
|
enum FifoMode mode;
|
|
|
|
/* Pusher state */
|
|
bool push_enabled;
|
|
bool dma_push_enabled;
|
|
bool dma_push_suspended;
|
|
hwaddr dma_instance;
|
|
|
|
bool method_nonincreasing;
|
|
unsigned int method : 14;
|
|
unsigned int subchannel : 3;
|
|
unsigned int method_count : 24;
|
|
uint32_t dcount;
|
|
bool subroutine_active;
|
|
hwaddr subroutine_return;
|
|
hwaddr get_jmp_shadow;
|
|
uint32_t rsvd_shadow;
|
|
uint32_t data_shadow;
|
|
uint32_t error;
|
|
|
|
|
|
/* Puller state */
|
|
QemuMutex pull_lock;
|
|
|
|
bool pull_enabled;
|
|
enum FIFOEngine bound_engines[NV2A_NUM_SUBCHANNELS];
|
|
enum FIFOEngine last_engine;
|
|
|
|
/* The actual command queue */
|
|
QemuMutex cache_lock;
|
|
QemuCond cache_cond;
|
|
QSIMPLEQ_HEAD(, CacheEntry) cache;
|
|
} Cache1State;
|
|
|
|
typedef struct ChannelControl {
|
|
hwaddr dma_put;
|
|
hwaddr dma_get;
|
|
uint32_t ref;
|
|
} ChannelControl;
|
|
|
|
|
|
|
|
typedef struct NV2AState {
|
|
PCIDevice dev;
|
|
qemu_irq irq;
|
|
|
|
VGACommonState vga;
|
|
|
|
MemoryRegion vram;
|
|
uint8_t *vram_ptr;
|
|
MemoryRegion ramin;
|
|
uint8_t *ramin_ptr;
|
|
|
|
MemoryRegion mmio;
|
|
|
|
MemoryRegion block_mmio[NV_NUM_BLOCKS];
|
|
|
|
struct {
|
|
uint32_t pending_interrupts;
|
|
uint32_t enabled_interrupts;
|
|
} pmc;
|
|
|
|
struct {
|
|
uint32_t pending_interrupts;
|
|
uint32_t enabled_interrupts;
|
|
|
|
hwaddr ramht_address;
|
|
unsigned int ramht_size;
|
|
uint32_t ramht_search;
|
|
|
|
hwaddr ramfc_address1;
|
|
hwaddr ramfc_address2;
|
|
unsigned int ramfc_size;
|
|
|
|
QemuThread puller_thread;
|
|
|
|
/* Weather the fifo chanels are PIO or DMA */
|
|
uint32_t channel_modes;
|
|
|
|
uint32_t channels_pending_push;
|
|
|
|
Cache1State cache1;
|
|
} pfifo;
|
|
|
|
struct {
|
|
uint32_t pending_interrupts;
|
|
uint32_t enabled_interrupts;
|
|
|
|
uint32_t numerator;
|
|
uint32_t denominator;
|
|
|
|
uint32_t alarm_time;
|
|
} ptimer;
|
|
|
|
struct {
|
|
uint32_t pending_interrupts;
|
|
uint32_t enabled_interrupts;
|
|
|
|
|
|
hwaddr context_table;
|
|
hwaddr context_address;
|
|
|
|
QemuMutex lock;
|
|
QemuCond context_cond;
|
|
|
|
unsigned int trapped_method;
|
|
unsigned int trapped_subchannel;
|
|
unsigned int trapped_channel_id;
|
|
|
|
unsigned int channel_id;
|
|
bool channel_valid;
|
|
GraphicsContext context[NV2A_NUM_CHANNELS];
|
|
} pgraph;
|
|
|
|
struct {
|
|
uint32_t pending_interrupts;
|
|
uint32_t enabled_interrupts;
|
|
|
|
hwaddr start;
|
|
} pcrtc;
|
|
|
|
struct {
|
|
uint32_t core_clock_coeff;
|
|
uint64_t core_clock_freq;
|
|
uint32_t memory_clock_coeff;
|
|
uint32_t video_clock_coeff;
|
|
} pramdac;
|
|
|
|
struct {
|
|
ChannelControl channel_control[NV2A_NUM_CHANNELS];
|
|
} user;
|
|
|
|
} NV2AState;
|
|
|
|
|
|
#define NV2A_DEVICE(obj) \
|
|
OBJECT_CHECK(NV2AState, (obj), "nv2a")
|
|
|
|
|
|
|
|
static void nv2a_update_irq(NV2AState *d)
|
|
{
|
|
/* PFIFO */
|
|
if (d->pfifo.pending_interrupts & d->pfifo.enabled_interrupts) {
|
|
d->pmc.pending_interrupts |= NV_PMC_INTR_0_PFIFO;
|
|
} else {
|
|
d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PFIFO;
|
|
}
|
|
|
|
/* PCRTC */
|
|
if (d->pcrtc.pending_interrupts & d->pcrtc.enabled_interrupts) {
|
|
d->pmc.pending_interrupts |= NV_PMC_INTR_0_PCRTC;
|
|
} else {
|
|
d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PCRTC;
|
|
}
|
|
|
|
/* PGRAPH */
|
|
if (d->pgraph.pending_interrupts & d->pgraph.enabled_interrupts) {
|
|
d->pmc.pending_interrupts |= NV_PMC_INTR_0_PGRAPH;
|
|
} else {
|
|
d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PGRAPH;
|
|
}
|
|
|
|
if (d->pmc.pending_interrupts && d->pmc.enabled_interrupts) {
|
|
qemu_irq_raise(d->irq);
|
|
} else {
|
|
qemu_irq_lower(d->irq);
|
|
}
|
|
}
|
|
|
|
static uint32_t nv2a_ramht_hash(NV2AState *d,
|
|
uint32_t handle)
|
|
{
|
|
uint32_t hash = 0;
|
|
/* XXX: Think this is different to what nouveau calculates... */
|
|
uint32_t bits = ffs(d->pfifo.ramht_size)-2;
|
|
|
|
while (handle) {
|
|
hash ^= (handle & ((1 << bits) - 1));
|
|
handle >>= bits;
|
|
}
|
|
hash ^= d->pfifo.cache1.channel_id << (bits - 4);
|
|
|
|
return hash;
|
|
}
|
|
|
|
|
|
static RAMHTEntry nv2a_lookup_ramht(NV2AState *d, uint32_t handle)
|
|
{
|
|
uint32_t hash;
|
|
uint8_t *entry_ptr;
|
|
uint32_t entry_handle;
|
|
uint32_t entry_context;
|
|
|
|
|
|
hash = nv2a_ramht_hash(d, handle);
|
|
assert(hash * 8 < d->pfifo.ramht_size);
|
|
|
|
entry_ptr = d->ramin_ptr + d->pfifo.ramht_address + hash * 8;
|
|
|
|
entry_handle = le32_to_cpupu((uint32_t*)entry_ptr);
|
|
entry_context = le32_to_cpupu((uint32_t*)(entry_ptr + 4));
|
|
|
|
return (RAMHTEntry){
|
|
.handle = entry_handle,
|
|
.instance = (entry_context & NV_RAMHT_INSTANCE) << 4,
|
|
.engine = (entry_context & NV_RAMHT_ENGINE) >> 16,
|
|
.channel_id = (entry_context & NV_RAMHT_CHID) >> 24,
|
|
.valid = entry_context & NV_RAMHT_STATUS,
|
|
};
|
|
}
|
|
|
|
|
|
static DMAObject nv2a_load_dma_object(NV2AState *d,
|
|
hwaddr address)
|
|
{
|
|
uint8_t *dma_ptr;
|
|
uint32_t flags;
|
|
|
|
dma_ptr = d->ramin_ptr + address;
|
|
flags = le32_to_cpupu((uint32_t*)dma_ptr);
|
|
|
|
return (DMAObject){
|
|
.dma_class = flags & NV_DMA_CLASS,
|
|
|
|
/* XXX: Why is this layout different to nouveau? */
|
|
.limit = le32_to_cpupu((uint32_t*)(dma_ptr + 4)),
|
|
.start = le32_to_cpupu((uint32_t*)(dma_ptr + 8)) & (~3),
|
|
};
|
|
}
|
|
|
|
static GraphicsObject nv2a_load_graphics_object(NV2AState *d,
|
|
hwaddr address)
|
|
{
|
|
int i;
|
|
uint8_t *obj_ptr;
|
|
uint32_t switch1, switch2, switch3;
|
|
|
|
obj_ptr = d->ramin_ptr + address;
|
|
|
|
switch1 = le32_to_cpupu((uint32_t*)obj_ptr);
|
|
switch2 = le32_to_cpupu((uint32_t*)(obj_ptr+4));
|
|
switch3 = le32_to_cpupu((uint32_t*)(obj_ptr+8));
|
|
|
|
GraphicsObject ret;
|
|
ret.graphics_class = switch1 & NV_PGRAPH_CTX_SWITCH1_GRCLASS;
|
|
|
|
/* init graphics object */
|
|
KelvinState *kelvin;
|
|
switch (ret.graphics_class) {
|
|
case NV_KELVIN_PRIMITIVE:
|
|
kelvin = &ret.data.kelvin;
|
|
|
|
/* generate vertex programs */
|
|
for (i=0; i<NV2A_VERTEXSHADER_SLOTS; i++) {
|
|
VertexShader *shader = &kelvin->vertexshaders[i];
|
|
glGenProgramsARB(1, &shader->gl_program);
|
|
}
|
|
assert(glGetError() == GL_NO_ERROR);
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static unsigned int kelvin_bind_inline_vertex_data(KelvinState *kelvin)
|
|
{
|
|
int i;
|
|
unsigned int offset = 0;
|
|
for (i=0; i<NV2A_VERTEXSHADER_ATTRIBUTES; i++) {
|
|
VertexAttribute *attribute = &kelvin->vertex_attributes[i];
|
|
if (attribute->count) {
|
|
|
|
glVertexAttribPointer(i,
|
|
attribute->count,
|
|
attribute->gl_type,
|
|
attribute->gl_normalize,
|
|
attribute->stride,
|
|
kelvin->inline_vertex_data + offset);
|
|
|
|
glEnableVertexAttribArray(i);
|
|
|
|
offset += attribute->size * attribute->count;
|
|
} else {
|
|
glDisableVertexAttribArray(i);
|
|
}
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
static void kelvin_bind_vertex_attribute_offsets(NV2AState *d,
|
|
KelvinState *kelvin)
|
|
{
|
|
int i;
|
|
for (i=0; i<NV2A_VERTEXSHADER_ATTRIBUTES; i++) {
|
|
VertexAttribute *attribute = &kelvin->vertex_attributes[i];
|
|
if (attribute->count) {
|
|
DMAObject vertex_dma;
|
|
|
|
qemu_mutex_unlock(&d->pgraph.lock);
|
|
qemu_mutex_lock_iothread();
|
|
|
|
if (kelvin->vertex_attribute_offsets[i].dma_select) {
|
|
vertex_dma = nv2a_load_dma_object(d, kelvin->dma_vertex_b);
|
|
} else {
|
|
vertex_dma = nv2a_load_dma_object(d, kelvin->dma_vertex_a);
|
|
}
|
|
uint32_t offset = kelvin->vertex_attribute_offsets[i].offset;
|
|
assert(offset < vertex_dma.limit);
|
|
|
|
if (vertex_dma.dma_class == NV_DMA_IN_MEMORY_CLASS) {
|
|
glVertexAttribPointer(i,
|
|
attribute->count,
|
|
attribute->gl_type,
|
|
attribute->gl_normalize,
|
|
attribute->stride,
|
|
d->vram_ptr + vertex_dma.start + offset);
|
|
} else {
|
|
assert(false);
|
|
}
|
|
|
|
qemu_mutex_lock(&d->pgraph.lock);
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
|
|
glEnableVertexAttribArray(i);
|
|
} else {
|
|
glDisableVertexAttribArray(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void kelvin_bind_vertexshader(KelvinState *kelvin)
|
|
{
|
|
int i;
|
|
VertexShader *shader;
|
|
|
|
assert(kelvin->use_vertex_program);
|
|
|
|
/* TODO */
|
|
assert(!kelvin->enable_vertex_program_write);
|
|
|
|
shader = &kelvin->vertexshaders[kelvin->vertexshader_start_slot];
|
|
|
|
glBindProgramARB(GL_VERTEX_PROGRAM_ARB, shader->gl_program);
|
|
|
|
if (shader->dirty) {
|
|
QString *shader_code = vsh_translate(VSH_VERSION_XVS,
|
|
shader->program_data,
|
|
shader->program_length);
|
|
const char* shader_code_str = qstring_get_str(shader_code);
|
|
|
|
NV2A_DPRINTF("nv2a bind shader %d, code:\n%s\n",
|
|
kelvin->vertexshader_start_slot,
|
|
shader_code_str);
|
|
|
|
glProgramStringARB(GL_VERTEX_PROGRAM_ARB,
|
|
GL_PROGRAM_FORMAT_ASCII_ARB,
|
|
strlen(shader_code_str),
|
|
shader_code_str);
|
|
|
|
/* Check it compiled */
|
|
GLint pos;
|
|
glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &pos);
|
|
if (pos != -1) {
|
|
fprintf(stderr, "nv2a: Shader compilation failed:\n"
|
|
" pos %d, %s\n",
|
|
pos, glGetString(GL_PROGRAM_ERROR_STRING_ARB));
|
|
fprintf(stderr, "ucode:\n");
|
|
for (i=0; i<shader->program_length; i++) {
|
|
fprintf(stderr, " 0x%08x,\n", shader->program_data[i]);
|
|
}
|
|
abort();
|
|
}
|
|
|
|
/* Check we're within resource limits */
|
|
GLint native;
|
|
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB,
|
|
GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB,
|
|
&native);
|
|
assert(native);
|
|
|
|
QDECREF(shader_code);
|
|
shader->dirty = false;
|
|
}
|
|
|
|
/* load constants */
|
|
for (i=0; i<NV2A_VERTEXSHADER_CONSTANTS; i++) {
|
|
VertexShaderConstant *constant = &kelvin->constants[i];
|
|
glProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB,
|
|
i,
|
|
(const GLfloat*)constant->data);
|
|
}
|
|
|
|
assert(glGetError() == GL_NO_ERROR);
|
|
}
|
|
|
|
|
|
static void nv2a_pgraph_context_init(GraphicsContext *context)
|
|
{
|
|
/* TODO: context creation on linux */
|
|
CGLPixelFormatAttribute attributes[] = {
|
|
kCGLPFAAccelerated,
|
|
(CGLPixelFormatAttribute)0
|
|
};
|
|
|
|
CGLPixelFormatObj pix;
|
|
GLint num;
|
|
CGLChoosePixelFormat(attributes, &pix, &num);
|
|
CGLCreateContext(pix, NULL, &context->gl_context);
|
|
CGLDestroyPixelFormat(pix);
|
|
|
|
CGLSetCurrentContext(context->gl_context);
|
|
|
|
|
|
/* Check context capabilities */
|
|
const GLubyte *extensions;
|
|
extensions = glGetString (GL_EXTENSIONS);
|
|
|
|
assert(gluCheckExtension((const GLubyte*)"GL_EXT_framebuffer_object",
|
|
extensions));
|
|
|
|
assert(gluCheckExtension((const GLubyte*)"GL_ARB_vertex_program",
|
|
extensions));
|
|
|
|
GLint max_vertex_attributes;
|
|
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_vertex_attributes);
|
|
assert(max_vertex_attributes >= NV2A_VERTEXSHADER_ATTRIBUTES);
|
|
|
|
|
|
|
|
glGenFramebuffersEXT(1, &context->gl_framebuffer);
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, context->gl_framebuffer);
|
|
|
|
glGenRenderbuffersEXT(1, &context->gl_renderbuffer);
|
|
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, context->gl_renderbuffer);
|
|
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8,
|
|
640, 480);
|
|
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
|
|
GL_COLOR_ATTACHMENT0_EXT,
|
|
GL_RENDERBUFFER_EXT,
|
|
context->gl_renderbuffer);
|
|
|
|
assert(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT)
|
|
== GL_FRAMEBUFFER_COMPLETE_EXT);
|
|
|
|
|
|
assert(glGetError() == GL_NO_ERROR);
|
|
|
|
|
|
CGLSetCurrentContext(NULL);
|
|
}
|
|
|
|
static void nv2a_pgraph_context_set_current(GraphicsContext *context)
|
|
{
|
|
if (context) {
|
|
CGLSetCurrentContext(context->gl_context);
|
|
} else {
|
|
CGLSetCurrentContext(NULL);
|
|
}
|
|
}
|
|
|
|
static void nv2a_pgraph_context_destroy(GraphicsContext *context)
|
|
{
|
|
CGLSetCurrentContext(context->gl_context);
|
|
|
|
glDeleteRenderbuffersEXT(1, &context->gl_renderbuffer);
|
|
glDeleteFramebuffersEXT(1, &context->gl_framebuffer);
|
|
|
|
CGLSetCurrentContext(NULL);
|
|
|
|
CGLDestroyContext(context->gl_context);
|
|
}
|
|
|
|
static void nv2a_pgraph_method(NV2AState *d,
|
|
unsigned int subchannel,
|
|
unsigned int method,
|
|
uint32_t parameter)
|
|
{
|
|
GraphicsContext *context;
|
|
GraphicsSubchannel *subchannel_data;
|
|
GraphicsObject *object;
|
|
|
|
KelvinState *kelvin;
|
|
|
|
DMAObject dma_semaphore;
|
|
unsigned int slot;
|
|
VertexAttribute *vertex_attribute;
|
|
VertexShader *vertexshader;
|
|
VertexShaderConstant *constant;
|
|
|
|
qemu_mutex_lock(&d->pgraph.lock);
|
|
|
|
assert(d->pgraph.channel_valid);
|
|
context = &d->pgraph.context[d->pgraph.channel_id];
|
|
subchannel_data = &context->subchannel_data[subchannel];
|
|
object = &subchannel_data->object;
|
|
kelvin = &object->data.kelvin;
|
|
|
|
NV2A_DPRINTF("nv2a pgraph method: 0x%x, 0x%x, 0x%x\n",
|
|
subchannel, method, parameter);
|
|
|
|
|
|
nv2a_pgraph_context_set_current(context);
|
|
|
|
if (method == NV_SET_OBJECT) {
|
|
subchannel_data->object_instance = parameter;
|
|
|
|
qemu_mutex_unlock(&d->pgraph.lock);
|
|
qemu_mutex_lock_iothread();
|
|
*object = nv2a_load_graphics_object(d, parameter);
|
|
qemu_mutex_unlock_iothread();
|
|
return;
|
|
}
|
|
|
|
|
|
uint32_t class_method = (object->graphics_class << 16) | method;
|
|
switch (class_method) {
|
|
case NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY:
|
|
object->data.m2mf.dma_notifies = parameter;
|
|
break;
|
|
|
|
|
|
case NV097_NO_OPERATION:
|
|
break;
|
|
case NV097_WAIT_FOR_IDLE:
|
|
break;
|
|
case NV097_SET_CONTEXT_DMA_NOTIFIES:
|
|
kelvin->dma_notifies = parameter;
|
|
break;
|
|
case NV097_SET_CONTEXT_DMA_A:
|
|
kelvin->dma_a = parameter;
|
|
break;
|
|
case NV097_SET_CONTEXT_DMA_B:
|
|
kelvin->dma_b = parameter;
|
|
break;
|
|
case NV097_SET_CONTEXT_DMA_STATE:
|
|
kelvin->dma_state = parameter;
|
|
break;
|
|
case NV097_SET_CONTEXT_DMA_VERTEX_A:
|
|
kelvin->dma_vertex_a = parameter;
|
|
break;
|
|
case NV097_SET_CONTEXT_DMA_VERTEX_B:
|
|
kelvin->dma_vertex_b = parameter;
|
|
break;
|
|
case NV097_SET_CONTEXT_DMA_SEMAPHORE:
|
|
kelvin->dma_semaphore = parameter;
|
|
break;
|
|
|
|
case NV097_SET_TRANSFORM_PROGRAM ...
|
|
NV097_SET_TRANSFORM_PROGRAM + 0x7c:
|
|
|
|
slot = (class_method - NV097_SET_TRANSFORM_PROGRAM) / 4;
|
|
/* TODO: It should still work using a non-increasing slot??? */
|
|
|
|
vertexshader = &kelvin->vertexshaders[kelvin->vertexshader_load_slot];
|
|
assert(vertexshader->program_length < NV2A_MAX_VERTEXSHADER_LENGTH);
|
|
vertexshader->program_data[
|
|
vertexshader->program_length++] = parameter;
|
|
break;
|
|
|
|
case NV097_SET_TRANSFORM_CONSTANT ...
|
|
NV097_SET_TRANSFORM_CONSTANT + 0x7c:
|
|
|
|
slot = (class_method - NV097_SET_TRANSFORM_CONSTANT) / 4;
|
|
|
|
constant = &kelvin->constants[kelvin->constant_load_slot];
|
|
constant->data[slot] = parameter;
|
|
break;
|
|
|
|
|
|
case NV097_SET_VERTEX_DATA_ARRAY_FORMAT ...
|
|
NV097_SET_VERTEX_DATA_ARRAY_FORMAT + 0x3c:
|
|
|
|
slot = (class_method - NV097_SET_VERTEX_DATA_ARRAY_FORMAT) / 4;
|
|
vertex_attribute = &kelvin->vertex_attributes[slot];
|
|
|
|
switch (parameter & NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE) {
|
|
case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_UB_D3D:
|
|
case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_UB_OGL:
|
|
vertex_attribute->gl_type = GL_UNSIGNED_BYTE;
|
|
vertex_attribute->gl_normalize = GL_TRUE;
|
|
vertex_attribute->size = 1;
|
|
break;
|
|
case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_S1:
|
|
vertex_attribute->gl_type = GL_SHORT;
|
|
vertex_attribute->gl_normalize = GL_FALSE;
|
|
vertex_attribute->size = 2;
|
|
break;
|
|
case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_F:
|
|
vertex_attribute->gl_type = GL_FLOAT;
|
|
vertex_attribute->gl_normalize = GL_FALSE;
|
|
vertex_attribute->size = 4;
|
|
break;
|
|
case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_S32K:
|
|
vertex_attribute->gl_type = GL_UNSIGNED_SHORT;
|
|
vertex_attribute->gl_normalize = GL_FALSE;
|
|
vertex_attribute->size = 2;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
vertex_attribute->count =
|
|
(parameter & NV097_SET_VERTEX_DATA_ARRAY_FORMAT_SIZE) >> 4;
|
|
vertex_attribute->stride =
|
|
(parameter & NV097_SET_VERTEX_DATA_ARRAY_FORMAT_STRIDE) >> 8;
|
|
|
|
break;
|
|
case NV097_SET_VERTEX_DATA_ARRAY_OFFSET ...
|
|
NV097_SET_VERTEX_DATA_ARRAY_OFFSET + 0x3c:
|
|
|
|
slot = (class_method - NV097_SET_VERTEX_DATA_ARRAY_OFFSET) / 4;
|
|
|
|
kelvin->vertex_attribute_offsets[slot].dma_select =
|
|
parameter & 0x80000000;
|
|
kelvin->vertex_attribute_offsets[slot].offset =
|
|
parameter & 0x7fffffff;
|
|
|
|
break;
|
|
|
|
case NV097_SET_BEGIN_END:
|
|
if (parameter == NV097_SET_BEGIN_END_OP_END) {
|
|
if (kelvin->use_vertex_program) {
|
|
glEnable(GL_VERTEX_PROGRAM_ARB);
|
|
kelvin_bind_vertexshader(kelvin);
|
|
} else {
|
|
glDisable(GL_VERTEX_PROGRAM_ARB);
|
|
}
|
|
|
|
if (kelvin->inline_vertex_data_length) {
|
|
unsigned int vertex_size =
|
|
kelvin_bind_inline_vertex_data(kelvin);
|
|
unsigned int vertex_count =
|
|
kelvin->inline_vertex_data_length*4 / vertex_size;
|
|
glDrawArrays(kelvin->gl_primitive_mode,
|
|
0, vertex_count);
|
|
} else if (kelvin->array_batch_length) {
|
|
kelvin_bind_vertex_attribute_offsets(d, kelvin);
|
|
|
|
glDrawElements(kelvin->gl_primitive_mode,
|
|
kelvin->array_batch_length,
|
|
GL_UNSIGNED_INT,
|
|
kelvin->array_batch);
|
|
} else {
|
|
assert(false);
|
|
}
|
|
assert(glGetError() == GL_NO_ERROR);
|
|
} else {
|
|
assert(parameter <= NV097_SET_BEGIN_END_OP_POLYGON);
|
|
|
|
kelvin->gl_primitive_mode = kelvin_primitive_map[parameter];
|
|
|
|
kelvin->array_batch_length = 0;
|
|
kelvin->inline_vertex_data_length = 0;
|
|
}
|
|
break;
|
|
case NV097_ARRAY_ELEMENT16:
|
|
assert(kelvin->array_batch_length < NV2A_MAX_PUSHBUFFER_METHOD);
|
|
kelvin->array_batch[
|
|
kelvin->array_batch_length++] = parameter & 0xFFFF;
|
|
kelvin->array_batch[
|
|
kelvin->array_batch_length++] = parameter >> 16;
|
|
break;
|
|
case NV097_ARRAY_ELEMENT32:
|
|
assert(kelvin->array_batch_length < NV2A_MAX_PUSHBUFFER_METHOD);
|
|
kelvin->array_batch[
|
|
kelvin->array_batch_length++] = parameter;
|
|
break;
|
|
case NV097_INLINE_ARRAY:
|
|
assert(kelvin->inline_vertex_data_length < NV2A_MAX_PUSHBUFFER_METHOD);
|
|
kelvin->inline_vertex_data[
|
|
kelvin->inline_vertex_data_length++] = parameter;
|
|
break;
|
|
|
|
case NV097_SET_SEMAPHORE_OFFSET:
|
|
kelvin->semaphore_offset = parameter;
|
|
break;
|
|
case NV097_BACK_END_WRITE_SEMAPHORE_RELEASE:
|
|
qemu_mutex_unlock(&d->pgraph.lock);
|
|
qemu_mutex_lock_iothread();
|
|
|
|
dma_semaphore = nv2a_load_dma_object(d, kelvin->dma_semaphore);
|
|
assert(kelvin->semaphore_offset < dma_semaphore.limit);
|
|
|
|
stl_le_phys(dma_semaphore.start + kelvin->semaphore_offset,
|
|
parameter);
|
|
|
|
qemu_mutex_lock(&d->pgraph.lock);
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
break;
|
|
|
|
case NV097_CLEAR_SURFACE:
|
|
/* QQQ */
|
|
printf("------------------CLEAR 0x%x---------------\n", parameter);
|
|
glClearColor(1, 0, 0, 1);
|
|
|
|
GLbitfield gl_mask = 0;
|
|
if (parameter & NV097_CLEAR_SURFACE_Z) {
|
|
gl_mask |= GL_DEPTH_BUFFER_BIT;
|
|
}
|
|
if (parameter & NV097_CLEAR_SURFACE_STENCIL) {
|
|
gl_mask |= GL_STENCIL_BUFFER_BIT;
|
|
}
|
|
if (parameter & (
|
|
NV097_CLEAR_SURFACE_R | NV097_CLEAR_SURFACE_G
|
|
| NV097_CLEAR_SURFACE_B | NV097_CLEAR_SURFACE_A)) {
|
|
gl_mask |= GL_COLOR_BUFFER_BIT;
|
|
}
|
|
glClear(gl_mask);
|
|
break;
|
|
|
|
case NV097_SET_TRANSFORM_EXECUTION_MODE:
|
|
kelvin->use_vertex_program = (parameter & 3) == 2;
|
|
break;
|
|
case NV097_SET_TRANSFORM_PROGRAM_CXT_WRITE_EN:
|
|
kelvin->enable_vertex_program_write = parameter;
|
|
break;
|
|
case NV097_SET_TRANSFORM_PROGRAM_LOAD:
|
|
assert(parameter < NV2A_VERTEXSHADER_SLOTS);
|
|
kelvin->vertexshader_load_slot = parameter;
|
|
kelvin->vertexshaders[parameter].program_length = 0; /* ??? */
|
|
kelvin->vertexshaders[parameter].dirty = true;
|
|
break;
|
|
case NV097_SET_TRANSFORM_PROGRAM_START:
|
|
assert(parameter < NV2A_VERTEXSHADER_SLOTS);
|
|
kelvin->vertexshader_start_slot = parameter;
|
|
break;
|
|
case NV097_SET_TRANSFORM_CONSTANT_LOAD:
|
|
assert(parameter < NV2A_VERTEXSHADER_CONSTANTS);
|
|
kelvin->constant_load_slot = parameter;
|
|
printf("load to %d\n", parameter);
|
|
break;
|
|
|
|
default:
|
|
NV2A_DPRINTF(" unhandled (0x%02x 0x%08x)\n",
|
|
object->graphics_class, method);
|
|
break;
|
|
}
|
|
qemu_mutex_unlock(&d->pgraph.lock);
|
|
}
|
|
|
|
|
|
|
|
static void nv2a_pgraph_wait_context_switch(NV2AState *d)
|
|
{
|
|
qemu_mutex_lock_iothread();
|
|
d->pgraph.pending_interrupts |= NV_PGRAPH_INTR_CONTEXT_SWITCH;
|
|
nv2a_update_irq(d);
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
qemu_mutex_lock(&d->pgraph.lock);
|
|
if (!d->pgraph.channel_valid) {
|
|
qemu_cond_wait(&d->pgraph.context_cond, &d->pgraph.lock);
|
|
}
|
|
qemu_mutex_unlock(&d->pgraph.lock);
|
|
}
|
|
|
|
static void nv2a_pgraph_ensure_channel(NV2AState *d, unsigned int channel_id)
|
|
{
|
|
bool valid;
|
|
qemu_mutex_lock(&d->pgraph.lock);
|
|
valid = d->pgraph.channel_valid && d->pgraph.channel_id == channel_id;
|
|
if (!valid) {
|
|
d->pgraph.trapped_channel_id = channel_id;
|
|
}
|
|
qemu_mutex_unlock(&d->pgraph.lock);
|
|
if (!valid) {
|
|
nv2a_pgraph_wait_context_switch(d);
|
|
}
|
|
}
|
|
|
|
static void *nv2a_pfifo_puller_thread(void *arg)
|
|
{
|
|
NV2AState *d = arg;
|
|
Cache1State *state = &d->pfifo.cache1;
|
|
CacheEntry *command;
|
|
RAMHTEntry entry;
|
|
|
|
while (true) {
|
|
qemu_mutex_lock(&state->pull_lock);
|
|
if (!state->pull_enabled) {
|
|
qemu_mutex_unlock(&state->pull_lock);
|
|
break;
|
|
}
|
|
qemu_mutex_unlock(&state->pull_lock);
|
|
|
|
qemu_mutex_lock(&state->cache_lock);
|
|
if (QSIMPLEQ_EMPTY(&state->cache)) {
|
|
qemu_cond_wait(&state->cache_cond, &state->cache_lock);
|
|
|
|
/* we could have been woken up to tell us we should die */
|
|
qemu_mutex_lock(&state->pull_lock);
|
|
if (!state->pull_enabled) {
|
|
qemu_mutex_unlock(&state->pull_lock);
|
|
qemu_mutex_unlock(&state->cache_lock);
|
|
break;
|
|
}
|
|
qemu_mutex_unlock(&state->pull_lock);
|
|
}
|
|
command = QSIMPLEQ_FIRST(&state->cache);
|
|
QSIMPLEQ_REMOVE_HEAD(&state->cache, entry);
|
|
qemu_mutex_unlock(&state->cache_lock);
|
|
|
|
if (command->method == 0) {
|
|
qemu_mutex_lock_iothread();
|
|
entry = nv2a_lookup_ramht(d, command->parameter);
|
|
assert(entry.valid);
|
|
|
|
assert(entry.channel_id == state->channel_id);
|
|
qemu_mutex_unlock_iothread();
|
|
|
|
switch (entry.engine) {
|
|
case ENGINE_SOFTWARE:
|
|
/* TODO */
|
|
assert(false);
|
|
break;
|
|
case ENGINE_GRAPHICS:
|
|
nv2a_pgraph_ensure_channel(d, entry.channel_id);
|
|
nv2a_pgraph_method(d, command->subchannel, 0, entry.instance);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
/* the engine is bound to the subchannel */
|
|
qemu_mutex_lock(&state->pull_lock);
|
|
state->bound_engines[command->subchannel] = entry.engine;
|
|
state->last_engine = entry.engine;
|
|
qemu_mutex_unlock(&state->pull_lock);
|
|
} else if (command->method >= 0x100) {
|
|
/* method passed to engine */
|
|
|
|
uint32_t parameter = command->parameter;
|
|
|
|
/* methods that take objects.
|
|
* TODO: Check this range is correct for the nv2a */
|
|
if (command->method >= 0x180 && command->method < 0x200) {
|
|
qemu_mutex_lock_iothread();
|
|
entry = nv2a_lookup_ramht(d, parameter);
|
|
assert(entry.valid);
|
|
assert(entry.channel_id == state->channel_id);
|
|
parameter = entry.instance;
|
|
qemu_mutex_unlock_iothread();
|
|
}
|
|
|
|
qemu_mutex_lock(&state->pull_lock);
|
|
enum FIFOEngine engine = state->bound_engines[command->subchannel];
|
|
qemu_mutex_unlock(&state->pull_lock);
|
|
|
|
switch (engine) {
|
|
case ENGINE_SOFTWARE:
|
|
assert(false);
|
|
break;
|
|
case ENGINE_GRAPHICS:
|
|
nv2a_pgraph_method(d, command->subchannel,
|
|
command->method, parameter);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
qemu_mutex_lock(&state->pull_lock);
|
|
state->last_engine = state->bound_engines[command->subchannel];
|
|
qemu_mutex_unlock(&state->pull_lock);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* pusher should be fine to run from a mimo handler
|
|
* whenever's it's convenient */
|
|
static void nv2a_pfifo_run_pusher(NV2AState *d) {
|
|
uint8_t channel_id;
|
|
ChannelControl *control;
|
|
Cache1State *state;
|
|
CacheEntry *command;
|
|
DMAObject dma;
|
|
uint32_t word;
|
|
|
|
/* TODO: How is cache1 selected? */
|
|
state = &d->pfifo.cache1;
|
|
channel_id = state->channel_id;
|
|
control = &d->user.channel_control[channel_id];
|
|
|
|
if (!state->push_enabled) return;
|
|
|
|
|
|
/* only handling DMA for now... */
|
|
|
|
/* Channel running DMA */
|
|
assert(d->pfifo.channel_modes & (1 << channel_id));
|
|
assert(state->mode == FIFO_DMA);
|
|
|
|
if (!state->dma_push_enabled) return;
|
|
if (state->dma_push_suspended) return;
|
|
|
|
/* We're running so there should be no pending errors... */
|
|
assert(state->error == NV_PFIFO_CACHE1_DMA_STATE_ERROR_NONE);
|
|
|
|
dma = nv2a_load_dma_object(d, state->dma_instance);
|
|
assert(dma.dma_class == NV_DMA_FROM_MEMORY_CLASS);
|
|
|
|
NV2A_DPRINTF("nv2a DMA pusher: 0x%llx - 0x%llx, 0x%llx - 0x%llx\n",
|
|
dma.start, dma.limit, control->dma_get, control->dma_put);
|
|
|
|
/* based on the convenient pseudocode in envytools */
|
|
while (control->dma_get != control->dma_put) {
|
|
if (control->dma_get >= dma.limit) {
|
|
|
|
state->error = NV_PFIFO_CACHE1_DMA_STATE_ERROR_PROTECTION;
|
|
break;
|
|
}
|
|
|
|
word = ldl_le_phys(dma.start+control->dma_get);
|
|
control->dma_get += 4;
|
|
|
|
if (state->method_count) {
|
|
/* data word of methods command */
|
|
state->data_shadow = word;
|
|
|
|
command = g_malloc0(sizeof(CacheEntry));
|
|
command->method = state->method;
|
|
command->subchannel = state->subchannel;
|
|
command->nonincreasing = state->method_nonincreasing;
|
|
command->parameter = word;
|
|
qemu_mutex_lock(&state->cache_lock);
|
|
QSIMPLEQ_INSERT_TAIL(&state->cache, command, entry);
|
|
qemu_cond_signal(&state->cache_cond);
|
|
qemu_mutex_unlock(&state->cache_lock);
|
|
|
|
if (!state->method_nonincreasing) {
|
|
state->method += 4;
|
|
}
|
|
state->method_count--;
|
|
state->dcount++;
|
|
} else {
|
|
/* no command active - this is the first word of a new one */
|
|
state->rsvd_shadow = word;
|
|
/* match all forms */
|
|
if ((word & 0xe0000003) == 0x20000000) {
|
|
/* old jump */
|
|
state->get_jmp_shadow = control->dma_get;
|
|
control->dma_get = word & 0x1fffffff;
|
|
NV2A_DPRINTF("nv2a pb OLD_JMP 0x%llx\n", control->dma_get);
|
|
} else if ((word & 3) == 1) {
|
|
/* jump */
|
|
state->get_jmp_shadow = control->dma_get;
|
|
control->dma_get = word & 0xfffffffc;
|
|
NV2A_DPRINTF("nv2a pb JMP 0x%llx\n", control->dma_get);
|
|
} else if ((word & 3) == 2) {
|
|
/* call */
|
|
if (state->subroutine_active) {
|
|
state->error = NV_PFIFO_CACHE1_DMA_STATE_ERROR_CALL;
|
|
break;
|
|
}
|
|
state->subroutine_return = control->dma_get;
|
|
state->subroutine_active = true;
|
|
control->dma_get = word & 0xfffffffc;
|
|
NV2A_DPRINTF("nv2a pb CALL 0x%llx\n", control->dma_get);
|
|
} else if (word == 0x00020000) {
|
|
/* return */
|
|
if (!state->subroutine_active) {
|
|
state->error = NV_PFIFO_CACHE1_DMA_STATE_ERROR_RETURN;
|
|
break;
|
|
}
|
|
control->dma_get = state->subroutine_return;
|
|
state->subroutine_active = false;
|
|
NV2A_DPRINTF("nv2a pb RET 0x%llx\n", control->dma_get);
|
|
} else if ((word & 0xe0030003) == 0) {
|
|
/* increasing methods */
|
|
state->method = word & 0x1fff;
|
|
state->subchannel = (word >> 13) & 7;
|
|
state->method_count = (word >> 18) & 0x7ff;
|
|
state->method_nonincreasing = false;
|
|
state->dcount = 0;
|
|
} else if ((word & 0xe0030003) == 0x40000000) {
|
|
/* non-increasing methods */
|
|
state->method = word & 0x1fff;
|
|
state->subchannel = (word >> 13) & 7;
|
|
state->method_count = (word >> 18) & 0x7ff;
|
|
state->method_nonincreasing = true;
|
|
state->dcount = 0;
|
|
} else {
|
|
state->error = NV_PFIFO_CACHE1_DMA_STATE_ERROR_RESERVED_CMD;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (state->error) {
|
|
NV2A_DPRINTF("nv2a pb error: %d\n", state->error);
|
|
state->dma_push_suspended = true;
|
|
|
|
d->pfifo.pending_interrupts |= NV_PFIFO_INTR_0_DMA_PUSHER;
|
|
nv2a_update_irq(d);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* PMC - card master control */
|
|
static uint64_t nv2a_pmc_read(void *opaque,
|
|
hwaddr addr, unsigned int size)
|
|
{
|
|
NV2AState *d = opaque;
|
|
|
|
uint64_t r = 0;
|
|
switch (addr) {
|
|
case NV_PMC_BOOT_0:
|
|
/* chipset and stepping:
|
|
* NV2A, A02, Rev 0 */
|
|
|
|
r = 0x02A000A2;
|
|
break;
|
|
case NV_PMC_INTR_0:
|
|
/* Shows which functional units have pending IRQ */
|
|
r = d->pmc.pending_interrupts;
|
|
break;
|
|
case NV_PMC_INTR_EN_0:
|
|
/* Selects which functional units can cause IRQs */
|
|
r = d->pmc.enabled_interrupts;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
NV2A_DPRINTF("nv2a PMC: read [0x%llx] -> 0x%llx\n", addr, r);
|
|
return r;
|
|
}
|
|
static void nv2a_pmc_write(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned int size)
|
|
{
|
|
NV2AState *d = opaque;
|
|
|
|
NV2A_DPRINTF("nv2a PMC: [0x%llx] = 0x%02llx\n", addr, val);
|
|
|
|
switch (addr) {
|
|
case NV_PMC_INTR_0:
|
|
/* the bits of the interrupts to clear are wrtten */
|
|
d->pmc.pending_interrupts &= ~val;
|
|
nv2a_update_irq(d);
|
|
break;
|
|
case NV_PMC_INTR_EN_0:
|
|
d->pmc.enabled_interrupts = val;
|
|
nv2a_update_irq(d);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* PBUS - bus control */
|
|
static uint64_t nv2a_pbus_read(void *opaque,
|
|
hwaddr addr, unsigned int size)
|
|
{
|
|
NV2AState *d = opaque;
|
|
|
|
uint64_t r = 0;
|
|
switch (addr) {
|
|
case NV_PBUS_PCI_NV_0:
|
|
r = pci_get_long(d->dev.config + PCI_VENDOR_ID);
|
|
break;
|
|
case NV_PBUS_PCI_NV_1:
|
|
r = pci_get_long(d->dev.config + PCI_COMMAND);
|
|
break;
|
|
case NV_PBUS_PCI_NV_2:
|
|
r = pci_get_long(d->dev.config + PCI_CLASS_REVISION);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
NV2A_DPRINTF("nv2a PBUS: read [0x%llx] -> 0x%llx\n", addr, r);
|
|
return r;
|
|
}
|
|
static void nv2a_pbus_write(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned int size)
|
|
{
|
|
NV2AState *d = opaque;
|
|
|
|
NV2A_DPRINTF("nv2a PBUS: [0x%llx] = 0x%02llx\n", addr, val);
|
|
|
|
switch (addr) {
|
|
case NV_PBUS_PCI_NV_1:
|
|
pci_set_long(d->dev.config + PCI_COMMAND, val);
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* PFIFO - MMIO and DMA FIFO submission to PGRAPH and VPE */
|
|
static uint64_t nv2a_pfifo_read(void *opaque,
|
|
hwaddr addr, unsigned int size)
|
|
{
|
|
int i;
|
|
NV2AState *d = opaque;
|
|
|
|
uint64_t r = 0;
|
|
switch (addr) {
|
|
case NV_PFIFO_INTR_0:
|
|
r = d->pfifo.pending_interrupts;
|
|
break;
|
|
case NV_PFIFO_INTR_EN_0:
|
|
r = d->pfifo.enabled_interrupts;
|
|
break;
|
|
case NV_PFIFO_RAMHT:
|
|
r = ( (d->pfifo.ramht_address >> 12) << 4)
|
|
| (d->pfifo.ramht_search << 24);
|
|
|
|
switch (d->pfifo.ramht_size) {
|
|
case 4096:
|
|
r |= NV_PFIFO_RAMHT_SIZE_4K;
|
|
break;
|
|
case 8192:
|
|
r |= NV_PFIFO_RAMHT_SIZE_8K;
|
|
break;
|
|
case 16384:
|
|
r |= NV_PFIFO_RAMHT_SIZE_16K;
|
|
break;
|
|
case 32768:
|
|
r |= NV_PFIFO_RAMHT_SIZE_32K;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
break;
|
|
case NV_PFIFO_RAMFC:
|
|
r = ( (d->pfifo.ramfc_address1 >> 10) << 2)
|
|
| (d->pfifo.ramfc_size << 16)
|
|
| ((d->pfifo.ramfc_address2 >> 10) << 17);
|
|
break;
|
|
case NV_PFIFO_RUNOUT_STATUS:
|
|
r = NV_PFIFO_RUNOUT_STATUS_LOW_MARK; /* low mark empty */
|
|
break;
|
|
case NV_PFIFO_MODE:
|
|
r = d->pfifo.channel_modes;
|
|
break;
|
|
case NV_PFIFO_DMA:
|
|
r = d->pfifo.channels_pending_push;
|
|
break;
|
|
|
|
case NV_PFIFO_CACHE1_PUSH0:
|
|
r = d->pfifo.cache1.push_enabled;
|
|
break;
|
|
case NV_PFIFO_CACHE1_PUSH1:
|
|
r = (d->pfifo.cache1.channel_id & NV_PFIFO_CACHE1_PUSH1_CHID)
|
|
| (d->pfifo.cache1.mode << 8);
|
|
break;
|
|
case NV_PFIFO_CACHE1_STATUS:
|
|
qemu_mutex_lock(&d->pfifo.cache1.cache_lock);
|
|
if (QSIMPLEQ_EMPTY(&d->pfifo.cache1.cache)) {
|
|
r |= NV_PFIFO_CACHE1_STATUS_LOW_MARK; /* low mark empty */
|
|
}
|
|
qemu_mutex_unlock(&d->pfifo.cache1.cache_lock);
|
|
break;
|
|
case NV_PFIFO_CACHE1_DMA_PUSH:
|
|
r = d->pfifo.cache1.dma_push_enabled
|
|
| (1 << 8) /* buffer empty */
|
|
| (d->pfifo.cache1.dma_push_suspended << 12);
|
|
break;
|
|
case NV_PFIFO_CACHE1_DMA_STATE:
|
|
r = d->pfifo.cache1.method_nonincreasing
|
|
| (d->pfifo.cache1.method << 2)
|
|
| (d->pfifo.cache1.subchannel << 13)
|
|
| (d->pfifo.cache1.method_count << 18)
|
|
| (d->pfifo.cache1.error << 29);
|
|
break;
|
|
case NV_PFIFO_CACHE1_DMA_INSTANCE:
|
|
r = (d->pfifo.cache1.dma_instance >> 4)
|
|
& NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS;
|
|
break;
|
|
case NV_PFIFO_CACHE1_DMA_PUT:
|
|
r = d->user.channel_control[d->pfifo.cache1.channel_id].dma_put;
|
|
break;
|
|
case NV_PFIFO_CACHE1_DMA_GET:
|
|
r = d->user.channel_control[d->pfifo.cache1.channel_id].dma_get;
|
|
break;
|
|
case NV_PFIFO_CACHE1_DMA_SUBROUTINE:
|
|
r = d->pfifo.cache1.subroutine_return
|
|
| d->pfifo.cache1.subroutine_active;
|
|
break;
|
|
case NV_PFIFO_CACHE1_PULL0:
|
|
qemu_mutex_lock(&d->pfifo.cache1.pull_lock);
|
|
r = d->pfifo.cache1.pull_enabled;
|
|
qemu_mutex_unlock(&d->pfifo.cache1.pull_lock);
|
|
break;
|
|
case NV_PFIFO_CACHE1_ENGINE:
|
|
qemu_mutex_lock(&d->pfifo.cache1.pull_lock);
|
|
for (i=0; i<NV2A_NUM_SUBCHANNELS; i++) {
|
|
r |= d->pfifo.cache1.bound_engines[i] << (i*2);
|
|
}
|
|
qemu_mutex_unlock(&d->pfifo.cache1.pull_lock);
|
|
break;
|
|
case NV_PFIFO_CACHE1_DMA_DCOUNT:
|
|
r = d->pfifo.cache1.dcount;
|
|
break;
|
|
case NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW:
|
|
r = d->pfifo.cache1.get_jmp_shadow;
|
|
break;
|
|
case NV_PFIFO_CACHE1_DMA_RSVD_SHADOW:
|
|
r = d->pfifo.cache1.rsvd_shadow;
|
|
break;
|
|
case NV_PFIFO_CACHE1_DMA_DATA_SHADOW:
|
|
r = d->pfifo.cache1.data_shadow;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
NV2A_DPRINTF("nv2a PFIFO: read [0x%llx] -> 0x%llx\n", addr, r);
|
|
return r;
|
|
}
|
|
static void nv2a_pfifo_write(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned int size)
|
|
{
|
|
int i;
|
|
NV2AState *d = opaque;
|
|
|
|
NV2A_DPRINTF("nv2a PFIFO: [0x%llx] = 0x%02llx\n", addr, val);
|
|
|
|
switch (addr) {
|
|
case NV_PFIFO_INTR_0:
|
|
d->pfifo.pending_interrupts &= ~val;
|
|
nv2a_update_irq(d);
|
|
break;
|
|
case NV_PFIFO_INTR_EN_0:
|
|
d->pfifo.enabled_interrupts = val;
|
|
nv2a_update_irq(d);
|
|
break;
|
|
case NV_PFIFO_RAMHT:
|
|
d->pfifo.ramht_address =
|
|
((val & NV_PFIFO_RAMHT_BASE_ADDRESS) >> 4) << 12;
|
|
switch (val & NV_PFIFO_RAMHT_SIZE) {
|
|
case NV_PFIFO_RAMHT_SIZE_4K:
|
|
d->pfifo.ramht_size = 4096;
|
|
break;
|
|
case NV_PFIFO_RAMHT_SIZE_8K:
|
|
d->pfifo.ramht_size = 8192;
|
|
break;
|
|
case NV_PFIFO_RAMHT_SIZE_16K:
|
|
d->pfifo.ramht_size = 16384;
|
|
break;
|
|
case NV_PFIFO_RAMHT_SIZE_32K:
|
|
d->pfifo.ramht_size = 32768;
|
|
break;
|
|
default:
|
|
d->pfifo.ramht_size = 0;
|
|
break;
|
|
}
|
|
d->pfifo.ramht_search = (val & NV_PFIFO_RAMHT_SEARCH) >> 24;
|
|
break;
|
|
case NV_PFIFO_RAMFC:
|
|
d->pfifo.ramfc_address1 =
|
|
((val & NV_PFIFO_RAMFC_BASE_ADDRESS1) >> 2) << 10;
|
|
d->pfifo.ramfc_size = (val & NV_PFIFO_RAMFC_SIZE) >> 16;
|
|
d->pfifo.ramfc_address2 =
|
|
((val & NV_PFIFO_RAMFC_BASE_ADDRESS2) >> 17) << 10;
|
|
break;
|
|
case NV_PFIFO_MODE:
|
|
d->pfifo.channel_modes = val;
|
|
break;
|
|
case NV_PFIFO_DMA:
|
|
d->pfifo.channels_pending_push = val;
|
|
break;
|
|
|
|
case NV_PFIFO_CACHE1_PUSH0:
|
|
d->pfifo.cache1.push_enabled = val & NV_PFIFO_CACHE1_PUSH0_ACCESS;
|
|
break;
|
|
case NV_PFIFO_CACHE1_PUSH1:
|
|
d->pfifo.cache1.channel_id = val & NV_PFIFO_CACHE1_PUSH1_CHID;
|
|
d->pfifo.cache1.mode = (val & NV_PFIFO_CACHE1_PUSH1_MODE) >> 8;
|
|
assert(d->pfifo.cache1.channel_id < NV2A_NUM_CHANNELS);
|
|
break;
|
|
case NV_PFIFO_CACHE1_DMA_PUSH:
|
|
d->pfifo.cache1.dma_push_enabled =
|
|
(val & NV_PFIFO_CACHE1_DMA_PUSH_ACCESS);
|
|
if (d->pfifo.cache1.dma_push_suspended
|
|
&& !(val & NV_PFIFO_CACHE1_DMA_PUSH_STATUS)) {
|
|
d->pfifo.cache1.dma_push_suspended = false;
|
|
nv2a_pfifo_run_pusher(d);
|
|
}
|
|
d->pfifo.cache1.dma_push_suspended =
|
|
(val & NV_PFIFO_CACHE1_DMA_PUSH_STATUS);
|
|
break;
|
|
case NV_PFIFO_CACHE1_DMA_STATE:
|
|
d->pfifo.cache1.method_nonincreasing =
|
|
(val & NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE);
|
|
d->pfifo.cache1.method = (val & NV_PFIFO_CACHE1_DMA_STATE_METHOD);
|
|
d->pfifo.cache1.subchannel =
|
|
(val & NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL) >> 13;
|
|
d->pfifo.cache1.method_count =
|
|
(val & NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT) >> 18;
|
|
d->pfifo.cache1.error =
|
|
(val & NV_PFIFO_CACHE1_DMA_STATE_ERROR) >> 29;
|
|
break;
|
|
case NV_PFIFO_CACHE1_DMA_INSTANCE:
|
|
d->pfifo.cache1.dma_instance =
|
|
(val & NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS) << 4;
|
|
break;
|
|
case NV_PFIFO_CACHE1_DMA_PUT:
|
|
d->user.channel_control[d->pfifo.cache1.channel_id].dma_put = val;
|
|
break;
|
|
case NV_PFIFO_CACHE1_DMA_GET:
|
|
d->user.channel_control[d->pfifo.cache1.channel_id].dma_get = val;
|
|
break;
|
|
case NV_PFIFO_CACHE1_DMA_SUBROUTINE:
|
|
d->pfifo.cache1.subroutine_return =
|
|
(val & NV_PFIFO_CACHE1_DMA_SUBROUTINE_RETURN_OFFSET);
|
|
d->pfifo.cache1.subroutine_active =
|
|
(val & NV_PFIFO_CACHE1_DMA_SUBROUTINE_STATE);
|
|
break;
|
|
case NV_PFIFO_CACHE1_PULL0:
|
|
qemu_mutex_lock(&d->pfifo.cache1.pull_lock);
|
|
if ((val & NV_PFIFO_CACHE1_PULL0_ACCESS)
|
|
&& !d->pfifo.cache1.pull_enabled) {
|
|
d->pfifo.cache1.pull_enabled = true;
|
|
|
|
/* fire up puller thread */
|
|
qemu_thread_create(&d->pfifo.puller_thread,
|
|
nv2a_pfifo_puller_thread,
|
|
d, QEMU_THREAD_DETACHED);
|
|
} else if (!(val & NV_PFIFO_CACHE1_PULL0_ACCESS)
|
|
&& d->pfifo.cache1.pull_enabled) {
|
|
d->pfifo.cache1.pull_enabled = false;
|
|
|
|
/* the puller thread should die, wake it up. */
|
|
qemu_cond_broadcast(&d->pfifo.cache1.cache_cond);
|
|
}
|
|
qemu_mutex_unlock(&d->pfifo.cache1.pull_lock);
|
|
break;
|
|
case NV_PFIFO_CACHE1_ENGINE:
|
|
qemu_mutex_lock(&d->pfifo.cache1.pull_lock);
|
|
for (i=0; i<NV2A_NUM_SUBCHANNELS; i++) {
|
|
d->pfifo.cache1.bound_engines[i] = (val >> (i*2)) & 3;
|
|
}
|
|
qemu_mutex_unlock(&d->pfifo.cache1.pull_lock);
|
|
break;
|
|
case NV_PFIFO_CACHE1_DMA_DCOUNT:
|
|
d->pfifo.cache1.dcount =
|
|
(val & NV_PFIFO_CACHE1_DMA_DCOUNT_VALUE);
|
|
break;
|
|
case NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW:
|
|
d->pfifo.cache1.get_jmp_shadow =
|
|
(val & NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW_OFFSET);
|
|
break;
|
|
case NV_PFIFO_CACHE1_DMA_RSVD_SHADOW:
|
|
d->pfifo.cache1.rsvd_shadow = val;
|
|
break;
|
|
case NV_PFIFO_CACHE1_DMA_DATA_SHADOW:
|
|
d->pfifo.cache1.data_shadow = val;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static uint64_t nv2a_prma_read(void *opaque,
|
|
hwaddr addr, unsigned int size)
|
|
{
|
|
NV2A_DPRINTF("nv2a PRMA: read [0x%llx]\n", addr);
|
|
return 0;
|
|
}
|
|
static void nv2a_prma_write(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned int size)
|
|
{
|
|
NV2A_DPRINTF("nv2a PRMA: [0x%llx] = 0x%02llx\n", addr, val);
|
|
}
|
|
|
|
|
|
static uint64_t nv2a_pvideo_read(void *opaque,
|
|
hwaddr addr, unsigned int size)
|
|
{
|
|
NV2A_DPRINTF("nv2a PVIDEO: read [0x%llx]\n", addr);
|
|
return 0;
|
|
}
|
|
static void nv2a_pvideo_write(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned int size)
|
|
{
|
|
NV2A_DPRINTF("nv2a PVIDEO: [0x%llx] = 0x%02llx\n", addr, val);
|
|
}
|
|
|
|
|
|
|
|
|
|
/* PIMTER - time measurement and time-based alarms */
|
|
static uint64_t nv2a_ptimer_get_clock(NV2AState *d)
|
|
{
|
|
return muldiv64(qemu_get_clock_ns(vm_clock),
|
|
d->pramdac.core_clock_freq * d->ptimer.numerator,
|
|
get_ticks_per_sec() * d->ptimer.denominator);
|
|
}
|
|
static uint64_t nv2a_ptimer_read(void *opaque,
|
|
hwaddr addr, unsigned int size)
|
|
{
|
|
NV2AState *d = opaque;
|
|
|
|
uint64_t r = 0;
|
|
switch (addr) {
|
|
case NV_PTIMER_INTR_0:
|
|
r = d->ptimer.pending_interrupts;
|
|
break;
|
|
case NV_PTIMER_INTR_EN_0:
|
|
r = d->ptimer.enabled_interrupts;
|
|
break;
|
|
case NV_PTIMER_NUMERATOR:
|
|
r = d->ptimer.numerator;
|
|
break;
|
|
case NV_PTIMER_DENOMINATOR:
|
|
r = d->ptimer.denominator;
|
|
break;
|
|
case NV_PTIMER_TIME_0:
|
|
r = (nv2a_ptimer_get_clock(d) & 0x7ffffff) << 5;
|
|
break;
|
|
case NV_PTIMER_TIME_1:
|
|
r = (nv2a_ptimer_get_clock(d) >> 27) & 0x1fffffff;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
NV2A_DPRINTF("nv2a PTIMER: read [0x%llx] -> 0x%llx\n", addr, r);
|
|
return r;
|
|
}
|
|
static void nv2a_ptimer_write(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned int size)
|
|
{
|
|
NV2AState *d = opaque;
|
|
|
|
NV2A_DPRINTF("nv2a PTIMER: [0x%llx] = 0x%02llx\n", addr, val);
|
|
|
|
switch (addr) {
|
|
case NV_PTIMER_INTR_0:
|
|
d->ptimer.pending_interrupts &= ~val;
|
|
nv2a_update_irq(d);
|
|
break;
|
|
case NV_PTIMER_INTR_EN_0:
|
|
d->ptimer.enabled_interrupts = val;
|
|
nv2a_update_irq(d);
|
|
break;
|
|
case NV_PTIMER_DENOMINATOR:
|
|
d->ptimer.denominator = val;
|
|
break;
|
|
case NV_PTIMER_NUMERATOR:
|
|
d->ptimer.numerator = val;
|
|
break;
|
|
case NV_PTIMER_ALARM_0:
|
|
d->ptimer.alarm_time = val;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static uint64_t nv2a_pcounter_read(void *opaque,
|
|
hwaddr addr, unsigned int size)
|
|
{
|
|
NV2A_DPRINTF("nv2a PCOUNTER: read [0x%llx]\n", addr);
|
|
return 0;
|
|
}
|
|
static void nv2a_pcounter_write(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned int size)
|
|
{
|
|
NV2A_DPRINTF("nv2a PCOUNTER: [0x%llx] = 0x%02llx\n", addr, val);
|
|
}
|
|
|
|
|
|
static uint64_t nv2a_pvpe_read(void *opaque,
|
|
hwaddr addr, unsigned int size)
|
|
{
|
|
NV2A_DPRINTF("nv2a PVPE: read [0x%llx]\n", addr);
|
|
return 0;
|
|
}
|
|
static void nv2a_pvpe_write(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned int size)
|
|
{
|
|
NV2A_DPRINTF("nv2a PVPE: [0x%llx] = 0x%02llx\n", addr, val);
|
|
}
|
|
|
|
|
|
static uint64_t nv2a_ptv_read(void *opaque,
|
|
hwaddr addr, unsigned int size)
|
|
{
|
|
NV2A_DPRINTF("nv2a PTV: read [0x%llx]\n", addr);
|
|
return 0;
|
|
}
|
|
static void nv2a_ptv_write(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned int size)
|
|
{
|
|
NV2A_DPRINTF("nv2a PTV: [0x%llx] = 0x%02llx\n", addr, val);
|
|
}
|
|
|
|
|
|
static uint64_t nv2a_prmfb_read(void *opaque,
|
|
hwaddr addr, unsigned int size)
|
|
{
|
|
NV2A_DPRINTF("nv2a PRMFB: read [0x%llx]\n", addr);
|
|
return 0;
|
|
}
|
|
static void nv2a_prmfb_write(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned int size)
|
|
{
|
|
NV2A_DPRINTF("nv2a PRMFB: [0x%llx] = 0x%02llx\n", addr, val);
|
|
}
|
|
|
|
|
|
/* PRMVIO - aliases VGA sequencer and graphics controller registers */
|
|
static uint64_t nv2a_prmvio_read(void *opaque,
|
|
hwaddr addr, unsigned int size)
|
|
{
|
|
NV2AState *d = opaque;
|
|
uint64_t r = vga_ioport_read(&d->vga, addr);
|
|
|
|
NV2A_DPRINTF("nv2a PRMVIO: read [0x%llx] -> 0x%llx\n", addr, r);
|
|
return r;
|
|
}
|
|
static void nv2a_prmvio_write(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned int size)
|
|
{
|
|
NV2AState *d = opaque;
|
|
|
|
NV2A_DPRINTF("nv2a PRMVIO: [0x%llx] = 0x%02llx\n", addr, val);
|
|
|
|
vga_ioport_write(&d->vga, addr, val);
|
|
}
|
|
|
|
|
|
static uint64_t nv2a_pfb_read(void *opaque,
|
|
hwaddr addr, unsigned int size)
|
|
{
|
|
NV2AState *d = opaque;
|
|
|
|
uint64_t r = 0;
|
|
switch (addr) {
|
|
case NV_PFB_CFG0:
|
|
/* 3-4 memory partitions. The debug bios checks this. */
|
|
r = 3;
|
|
break;
|
|
case NV_PFB_CSTATUS:
|
|
r = memory_region_size(&d->vram);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
NV2A_DPRINTF("nv2a PFB: read [0x%llx] -> 0x%llx\n", addr, r);
|
|
return r;
|
|
}
|
|
static void nv2a_pfb_write(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned int size)
|
|
{
|
|
NV2A_DPRINTF("nv2a PFB: [0x%llx] = 0x%02llx\n", addr, val);
|
|
}
|
|
|
|
|
|
static uint64_t nv2a_pstraps_read(void *opaque,
|
|
hwaddr addr, unsigned int size)
|
|
{
|
|
NV2A_DPRINTF("nv2a PSTRAPS: read [0x%llx]\n", addr);
|
|
return 0;
|
|
}
|
|
static void nv2a_pstraps_write(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned int size)
|
|
{
|
|
NV2A_DPRINTF("nv2a PSTRAPS: [0x%llx] = 0x%02llx\n", addr, val);
|
|
}
|
|
|
|
/* PGRAPH - accelerated 2d/3d drawing engine */
|
|
static uint64_t nv2a_pgraph_read(void *opaque,
|
|
hwaddr addr, unsigned int size)
|
|
{
|
|
NV2AState *d = opaque;
|
|
|
|
uint64_t r = 0;
|
|
switch (addr) {
|
|
case NV_PGRAPH_INTR:
|
|
r = d->pgraph.pending_interrupts;
|
|
break;
|
|
case NV_PGRAPH_INTR_EN:
|
|
r = d->pgraph.enabled_interrupts;
|
|
break;
|
|
case NV_PGRAPH_CTX_USER:
|
|
qemu_mutex_lock(&d->pgraph.lock);
|
|
r = d->pgraph.context[d->pgraph.channel_id].channel_3d
|
|
| NV_PGRAPH_CTX_USER_CHANNEL_3D_VALID
|
|
| (d->pgraph.context[d->pgraph.channel_id].subchannel << 13)
|
|
| (d->pgraph.channel_id << 24);
|
|
qemu_mutex_unlock(&d->pgraph.lock);
|
|
break;
|
|
case NV_PGRAPH_CHANNEL_CTX_TABLE:
|
|
r = d->pgraph.context_table >> 4;
|
|
break;
|
|
case NV_PGRAPH_CHANNEL_CTX_POINTER:
|
|
r = d->pgraph.context_address >> 4;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
NV2A_DPRINTF("nv2a PGRAPH: read [0x%llx]\n", addr);
|
|
return r;
|
|
}
|
|
static void nv2a_pgraph_set_context_user(NV2AState *d, uint32_t val)
|
|
{
|
|
d->pgraph.channel_id = (val & NV_PGRAPH_CTX_USER_CHID) >> 24;
|
|
|
|
d->pgraph.context[d->pgraph.channel_id].channel_3d =
|
|
val & NV_PGRAPH_CTX_USER_CHANNEL_3D;
|
|
d->pgraph.context[d->pgraph.channel_id].subchannel =
|
|
(val & NV_PGRAPH_CTX_USER_SUBCH) >> 13;
|
|
}
|
|
static void nv2a_pgraph_write(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned int size)
|
|
{
|
|
NV2AState *d = opaque;
|
|
|
|
NV2A_DPRINTF("nv2a PGRAPH: [0x%llx] = 0x%02llx\n", addr, val);
|
|
|
|
switch (addr) {
|
|
case NV_PGRAPH_INTR:
|
|
d->pgraph.pending_interrupts &= ~val;
|
|
break;
|
|
case NV_PGRAPH_INTR_EN:
|
|
d->pgraph.enabled_interrupts = val;
|
|
break;
|
|
case NV_PGRAPH_CTX_CONTROL:
|
|
if (!(val & NV_PGRAPH_CTX_CONTROL_TIME)) {
|
|
/* time expired */
|
|
d->pgraph.pending_interrupts |= NV_PGRAPH_INTR_CONTEXT_SWITCH;
|
|
nv2a_update_irq(d);
|
|
}
|
|
|
|
qemu_mutex_lock(&d->pgraph.lock);
|
|
d->pgraph.channel_valid = (val & NV_PGRAPH_CTX_CONTROL_CHID);
|
|
if (d->pgraph.channel_valid) {
|
|
qemu_cond_broadcast(&d->pgraph.context_cond);
|
|
}
|
|
qemu_mutex_unlock(&d->pgraph.lock);
|
|
break;
|
|
case NV_PGRAPH_CTX_USER:
|
|
qemu_mutex_lock(&d->pgraph.lock);
|
|
nv2a_pgraph_set_context_user(d, val);
|
|
qemu_mutex_unlock(&d->pgraph.lock);
|
|
break;
|
|
case NV_PGRAPH_CHANNEL_CTX_TABLE:
|
|
d->pgraph.context_table =
|
|
(val & NV_PGRAPH_CHANNEL_CTX_TABLE_INST) << 4;
|
|
break;
|
|
case NV_PGRAPH_CHANNEL_CTX_POINTER:
|
|
d->pgraph.context_address =
|
|
(val & NV_PGRAPH_CHANNEL_CTX_POINTER_INST) << 4;
|
|
break;
|
|
case NV_PGRAPH_CHANNEL_CTX_TRIGGER:
|
|
qemu_mutex_lock(&d->pgraph.lock);
|
|
|
|
if (val & NV_PGRAPH_CHANNEL_CTX_TRIGGER_READ_IN) {
|
|
NV2A_DPRINTF("nv2a PGRAPH: read channel context from %llx",
|
|
d->pgraph.context_address);
|
|
|
|
uint8_t *context_ptr = d->ramin_ptr + d->pgraph.context_address;
|
|
uint32_t context_user = le32_to_cpupu((uint32_t*)context_ptr);
|
|
|
|
nv2a_pgraph_set_context_user(d, context_user);
|
|
}
|
|
if (val & NV_PGRAPH_CHANNEL_CTX_TRIGGER_WRITE_OUT) {
|
|
/* do stuff ... */
|
|
}
|
|
|
|
qemu_mutex_unlock(&d->pgraph.lock);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static uint64_t nv2a_pcrtc_read(void *opaque,
|
|
hwaddr addr, unsigned int size)
|
|
{
|
|
NV2AState *d = opaque;
|
|
|
|
uint64_t r = 0;
|
|
switch (addr) {
|
|
case NV_PCRTC_INTR_0:
|
|
r = d->pcrtc.pending_interrupts;
|
|
break;
|
|
case NV_PCRTC_INTR_EN_0:
|
|
r = d->pcrtc.enabled_interrupts;
|
|
break;
|
|
case NV_PCRTC_START:
|
|
r = d->pcrtc.start;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
NV2A_DPRINTF("nv2a PCRTC: read [0x%llx] -> 0x%llx\n", addr, r);
|
|
return r;
|
|
}
|
|
static void nv2a_pcrtc_write(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned int size)
|
|
{
|
|
NV2AState *d = opaque;
|
|
|
|
NV2A_DPRINTF("nv2a PCRTC: [0x%llx] = 0x%02llx\n", addr, val);
|
|
|
|
switch (addr) {
|
|
case NV_PCRTC_INTR_0:
|
|
d->pcrtc.pending_interrupts &= ~val;
|
|
nv2a_update_irq(d);
|
|
break;
|
|
case NV_PCRTC_INTR_EN_0:
|
|
d->pcrtc.enabled_interrupts = val;
|
|
nv2a_update_irq(d);
|
|
break;
|
|
case NV_PCRTC_START:
|
|
val &= 0x03FFFFFF;
|
|
if (val != d->pcrtc.start) {
|
|
if (d->pcrtc.start) {
|
|
memory_region_del_subregion(&d->vram, &d->vga.vram);
|
|
}
|
|
d->pcrtc.start = val;
|
|
memory_region_add_subregion(&d->vram, val, &d->vga.vram);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* PRMCIO - aliases VGA CRTC and attribute controller registers */
|
|
static uint64_t nv2a_prmcio_read(void *opaque,
|
|
hwaddr addr, unsigned int size)
|
|
{
|
|
NV2AState *d = opaque;
|
|
uint64_t r = vga_ioport_read(&d->vga, addr);
|
|
|
|
NV2A_DPRINTF("nv2a PRMCIO: read [0x%llx] -> 0x%llx\n", addr, r);
|
|
return r;
|
|
}
|
|
static void nv2a_prmcio_write(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned int size)
|
|
{
|
|
NV2AState *d = opaque;
|
|
|
|
NV2A_DPRINTF("nv2a PRMCIO: [0x%llx] = 0x%02llx\n", addr, val);
|
|
|
|
switch (addr) {
|
|
case VGA_ATT_W:
|
|
/* Cromwell sets attrs without enabling VGA_AR_ENABLE_DISPLAY
|
|
* (which should result in a blank screen).
|
|
* Either nvidia's hardware is lenient or it is set through
|
|
* something else. The former seems more likely.
|
|
*/
|
|
if (d->vga.ar_flip_flop == 0) {
|
|
val |= VGA_AR_ENABLE_DISPLAY;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
vga_ioport_write(&d->vga, addr, val);
|
|
}
|
|
|
|
|
|
static uint64_t nv2a_pramdac_read(void *opaque,
|
|
hwaddr addr, unsigned int size)
|
|
{
|
|
NV2AState *d = opaque;
|
|
|
|
uint64_t r = 0;
|
|
switch (addr & ~3) {
|
|
case NV_PRAMDAC_NVPLL_COEFF:
|
|
r = d->pramdac.core_clock_coeff;
|
|
break;
|
|
case NV_PRAMDAC_MPLL_COEFF:
|
|
r = d->pramdac.memory_clock_coeff;
|
|
break;
|
|
case NV_PRAMDAC_VPLL_COEFF:
|
|
r = d->pramdac.video_clock_coeff;
|
|
break;
|
|
case NV_PRAMDAC_PLL_TEST_COUNTER:
|
|
/* emulated PLLs locked instantly? */
|
|
return NV_PRAMDAC_PLL_TEST_COUNTER_VPLL2_LOCK
|
|
| NV_PRAMDAC_PLL_TEST_COUNTER_NVPLL_LOCK
|
|
| NV_PRAMDAC_PLL_TEST_COUNTER_MPLL_LOCK
|
|
| NV_PRAMDAC_PLL_TEST_COUNTER_VPLL_LOCK;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Surprisingly, QEMU doesn't handle unaligned access for you properly */
|
|
r >>= 32 - 8 * size - 8 * (addr & 3);
|
|
|
|
NV2A_DPRINTF("nv2a PRAMDAC: read %d [0x%llx] -> %llx\n", size, addr, r);
|
|
return r;
|
|
}
|
|
static void nv2a_pramdac_write(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned int size)
|
|
{
|
|
NV2AState *d = opaque;
|
|
uint32_t m, n, p;
|
|
|
|
NV2A_DPRINTF("nv2a PRAMDAC: [0x%llx] = 0x%02llx\n", addr, val);
|
|
|
|
switch (addr) {
|
|
case NV_PRAMDAC_NVPLL_COEFF:
|
|
d->pramdac.core_clock_coeff = val;
|
|
|
|
m = val & NV_PRAMDAC_NVPLL_COEFF_MDIV;
|
|
n = (val & NV_PRAMDAC_NVPLL_COEFF_NDIV) >> 8;
|
|
p = (val & NV_PRAMDAC_NVPLL_COEFF_PDIV) >> 16;
|
|
|
|
if (m == 0) {
|
|
d->pramdac.core_clock_freq = 0;
|
|
} else {
|
|
d->pramdac.core_clock_freq = (NV2A_CRYSTAL_FREQ * n)
|
|
/ (1 << p) / m;
|
|
}
|
|
|
|
break;
|
|
case NV_PRAMDAC_MPLL_COEFF:
|
|
d->pramdac.memory_clock_coeff = val;
|
|
break;
|
|
case NV_PRAMDAC_VPLL_COEFF:
|
|
d->pramdac.video_clock_coeff = val;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static uint64_t nv2a_prmdio_read(void *opaque,
|
|
hwaddr addr, unsigned int size)
|
|
{
|
|
NV2A_DPRINTF("nv2a PRMDIO: read [0x%llx]\n", addr);
|
|
return 0;
|
|
}
|
|
static void nv2a_prmdio_write(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned int size)
|
|
{
|
|
NV2A_DPRINTF("nv2a PRMDIO: [0x%llx] = 0x%02llx\n", addr, val);
|
|
}
|
|
|
|
|
|
/* PRAMIN - RAMIN access */
|
|
/*
|
|
static uint64_t nv2a_pramin_read(void *opaque,
|
|
hwaddr addr, unsigned int size)
|
|
{
|
|
NV2A_DPRINTF("nv2a PRAMIN: read [0x%llx] -> 0x%llx\n", addr, r);
|
|
return 0;
|
|
}
|
|
static void nv2a_pramin_write(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned int size)
|
|
{
|
|
NV2A_DPRINTF("nv2a PRAMIN: [0x%llx] = 0x%02llx\n", addr, val);
|
|
}*/
|
|
|
|
|
|
/* USER - PFIFO MMIO and DMA submission area */
|
|
static uint64_t nv2a_user_read(void *opaque,
|
|
hwaddr addr, unsigned int size)
|
|
{
|
|
NV2AState *d = opaque;
|
|
|
|
unsigned int channel_id = addr >> 16;
|
|
assert(channel_id < NV2A_NUM_CHANNELS);
|
|
|
|
ChannelControl *control = &d->user.channel_control[channel_id];
|
|
|
|
uint64_t r = 0;
|
|
if (d->pfifo.channel_modes & (1 << channel_id)) {
|
|
/* DMA Mode */
|
|
switch (addr & 0xFFFF) {
|
|
case NV_USER_DMA_PUT:
|
|
r = control->dma_put;
|
|
break;
|
|
case NV_USER_DMA_GET:
|
|
r = control->dma_get;
|
|
break;
|
|
case NV_USER_REF:
|
|
r = control->ref;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else {
|
|
/* PIO Mode */
|
|
/* dunno */
|
|
}
|
|
|
|
NV2A_DPRINTF("nv2a USER: read [0x%llx] -> %llx\n", addr, r);
|
|
return r;
|
|
}
|
|
static void nv2a_user_write(void *opaque, hwaddr addr,
|
|
uint64_t val, unsigned int size)
|
|
{
|
|
NV2AState *d = opaque;
|
|
|
|
NV2A_DPRINTF("nv2a USER: [0x%llx] = 0x%02llx\n", addr, val);
|
|
|
|
unsigned int channel_id = addr >> 16;
|
|
assert(channel_id < NV2A_NUM_CHANNELS);
|
|
|
|
ChannelControl *control = &d->user.channel_control[channel_id];
|
|
|
|
if (d->pfifo.channel_modes & (1 << channel_id)) {
|
|
/* DMA Mode */
|
|
switch (addr & 0xFFFF) {
|
|
case NV_USER_DMA_PUT:
|
|
control->dma_put = val;
|
|
|
|
if (d->pfifo.cache1.push_enabled) {
|
|
nv2a_pfifo_run_pusher(d);
|
|
}
|
|
break;
|
|
case NV_USER_DMA_GET:
|
|
control->dma_get = val;
|
|
break;
|
|
case NV_USER_REF:
|
|
control->ref = val;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else {
|
|
/* PIO Mode */
|
|
assert(false);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef struct NV2ABlockInfo {
|
|
const char* name;
|
|
hwaddr offset;
|
|
uint64_t size;
|
|
MemoryRegionOps ops;
|
|
} NV2ABlockInfo;
|
|
|
|
static const struct NV2ABlockInfo blocktable[] = {
|
|
[ NV_PMC ] = {
|
|
.name = "PMC",
|
|
.offset = 0x000000,
|
|
.size = 0x001000,
|
|
.ops = {
|
|
.read = nv2a_pmc_read,
|
|
.write = nv2a_pmc_write,
|
|
},
|
|
},
|
|
[ NV_PBUS ] = {
|
|
.name = "PBUS",
|
|
.offset = 0x001000,
|
|
.size = 0x001000,
|
|
.ops = {
|
|
.read = nv2a_pbus_read,
|
|
.write = nv2a_pbus_write,
|
|
},
|
|
},
|
|
[ NV_PFIFO ] = {
|
|
.name = "PFIFO",
|
|
.offset = 0x002000,
|
|
.size = 0x002000,
|
|
.ops = {
|
|
.read = nv2a_pfifo_read,
|
|
.write = nv2a_pfifo_write,
|
|
},
|
|
},
|
|
[ NV_PRMA ] = {
|
|
.name = "PRMA",
|
|
.offset = 0x007000,
|
|
.size = 0x001000,
|
|
.ops = {
|
|
.read = nv2a_prma_read,
|
|
.write = nv2a_prma_write,
|
|
},
|
|
},
|
|
[ NV_PVIDEO ] = {
|
|
.name = "PVIDEO",
|
|
.offset = 0x008000,
|
|
.size = 0x001000,
|
|
.ops = {
|
|
.read = nv2a_pvideo_read,
|
|
.write = nv2a_pvideo_write,
|
|
},
|
|
},
|
|
[ NV_PTIMER ] = {
|
|
.name = "PTIMER",
|
|
.offset = 0x009000,
|
|
.size = 0x001000,
|
|
.ops = {
|
|
.read = nv2a_ptimer_read,
|
|
.write = nv2a_ptimer_write,
|
|
},
|
|
},
|
|
[ NV_PCOUNTER ] = {
|
|
.name = "PCOUNTER",
|
|
.offset = 0x00a000,
|
|
.size = 0x001000,
|
|
.ops = {
|
|
.read = nv2a_pcounter_read,
|
|
.write = nv2a_pcounter_write,
|
|
},
|
|
},
|
|
[ NV_PVPE ] = {
|
|
.name = "PVPE",
|
|
.offset = 0x00b000,
|
|
.size = 0x001000,
|
|
.ops = {
|
|
.read = nv2a_pvpe_read,
|
|
.write = nv2a_pvpe_write,
|
|
},
|
|
},
|
|
[ NV_PTV ] = {
|
|
.name = "PTV",
|
|
.offset = 0x00d000,
|
|
.size = 0x001000,
|
|
.ops = {
|
|
.read = nv2a_ptv_read,
|
|
.write = nv2a_ptv_write,
|
|
},
|
|
},
|
|
[ NV_PRMFB ] = {
|
|
.name = "PRMFB",
|
|
.offset = 0x0a0000,
|
|
.size = 0x020000,
|
|
.ops = {
|
|
.read = nv2a_prmfb_read,
|
|
.write = nv2a_prmfb_write,
|
|
},
|
|
},
|
|
[ NV_PRMVIO ] = {
|
|
.name = "PRMVIO",
|
|
.offset = 0x0c0000,
|
|
.size = 0x001000,
|
|
.ops = {
|
|
.read = nv2a_prmvio_read,
|
|
.write = nv2a_prmvio_write,
|
|
},
|
|
},
|
|
[ NV_PFB ] = {
|
|
.name = "PFB",
|
|
.offset = 0x100000,
|
|
.size = 0x001000,
|
|
.ops = {
|
|
.read = nv2a_pfb_read,
|
|
.write = nv2a_pfb_write,
|
|
},
|
|
},
|
|
[ NV_PSTRAPS ] = {
|
|
.name = "PSTRAPS",
|
|
.offset = 0x101000,
|
|
.size = 0x001000,
|
|
.ops = {
|
|
.read = nv2a_pstraps_read,
|
|
.write = nv2a_pstraps_write,
|
|
},
|
|
},
|
|
[ NV_PGRAPH ] = {
|
|
.name = "PGRAPH",
|
|
.offset = 0x400000,
|
|
.size = 0x002000,
|
|
.ops = {
|
|
.read = nv2a_pgraph_read,
|
|
.write = nv2a_pgraph_write,
|
|
},
|
|
},
|
|
[ NV_PCRTC ] = {
|
|
.name = "PCRTC",
|
|
.offset = 0x600000,
|
|
.size = 0x001000,
|
|
.ops = {
|
|
.read = nv2a_pcrtc_read,
|
|
.write = nv2a_pcrtc_write,
|
|
},
|
|
},
|
|
[ NV_PRMCIO ] = {
|
|
.name = "PRMCIO",
|
|
.offset = 0x601000,
|
|
.size = 0x001000,
|
|
.ops = {
|
|
.read = nv2a_prmcio_read,
|
|
.write = nv2a_prmcio_write,
|
|
},
|
|
},
|
|
[ NV_PRAMDAC ] = {
|
|
.name = "PRAMDAC",
|
|
.offset = 0x680000,
|
|
.size = 0x001000,
|
|
.ops = {
|
|
.read = nv2a_pramdac_read,
|
|
.write = nv2a_pramdac_write,
|
|
},
|
|
},
|
|
[ NV_PRMDIO ] = {
|
|
.name = "PRMDIO",
|
|
.offset = 0x681000,
|
|
.size = 0x001000,
|
|
.ops = {
|
|
.read = nv2a_prmdio_read,
|
|
.write = nv2a_prmdio_write,
|
|
},
|
|
},
|
|
/*[ NV_PRAMIN ] = {
|
|
.name = "PRAMIN",
|
|
.offset = 0x700000,
|
|
.size = 0x100000,
|
|
.ops = {
|
|
.read = nv2a_pramin_read,
|
|
.write = nv2a_pramin_write,
|
|
},
|
|
},*/
|
|
[ NV_USER ] = {
|
|
.name = "USER",
|
|
.offset = 0x800000,
|
|
.size = 0x800000,
|
|
.ops = {
|
|
.read = nv2a_user_read,
|
|
.write = nv2a_user_write,
|
|
},
|
|
},
|
|
};
|
|
|
|
|
|
|
|
static int nv2a_get_bpp(VGACommonState *s)
|
|
{
|
|
if ((s->cr[0x28] & 3) == 3) {
|
|
return 32;
|
|
}
|
|
return (s->cr[0x28] & 3) * 8;
|
|
}
|
|
|
|
|
|
/* Graphic console methods. Need to wrap all of these since
|
|
* graphic_console_init takes a single opaque, and we
|
|
* need access to the nv2a state to set the vblank interrupt */
|
|
static void nv2a_vga_update(void *opaque)
|
|
{
|
|
NV2AState *d = NV2A_DEVICE(opaque);
|
|
|
|
qemu_mutex_lock(&d->pgraph.lock);
|
|
GraphicsContext *context = &d->pgraph.context[d->pgraph.channel_id];
|
|
if (context->channel_3d) {
|
|
printf("3d ping! %d\n", nv2a_get_bpp(&d->vga));
|
|
|
|
nv2a_pgraph_context_set_current(context);
|
|
|
|
//glClearColor(1, 0, 0, 1);
|
|
//glClear(GL_COLOR_BUFFER_BIT);
|
|
glReadPixels(0, 0, 640, 480, GL_RGBA, GL_UNSIGNED_BYTE,
|
|
d->vga.vram_ptr);
|
|
assert(glGetError() == GL_NO_ERROR);
|
|
memory_region_set_dirty(&d->vga.vram, 0, 640*480*4);
|
|
|
|
nv2a_pgraph_context_set_current(NULL);
|
|
}
|
|
qemu_mutex_unlock(&d->pgraph.lock);
|
|
|
|
d->vga.update(&d->vga);
|
|
|
|
d->pcrtc.pending_interrupts |= NV_PCRTC_INTR_0_VBLANK;
|
|
nv2a_update_irq(d);
|
|
}
|
|
static void nv2a_vga_invalidate(void *opaque)
|
|
{
|
|
NV2AState *d = NV2A_DEVICE(opaque);
|
|
d->vga.invalidate(&d->vga);
|
|
}
|
|
static void nv2a_vga_screen_dump(void *opaque,
|
|
const char *filename,
|
|
bool cswitch,
|
|
Error **errp)
|
|
{
|
|
NV2AState *d = NV2A_DEVICE(opaque);
|
|
d->vga.screen_dump(&d->vga, filename, cswitch, errp);
|
|
}
|
|
static void nv2a_vga_text_update(void *opaque, console_ch_t *chardata)
|
|
{
|
|
NV2AState *d = NV2A_DEVICE(opaque);
|
|
d->vga.text_update(&d->vga, chardata);
|
|
}
|
|
|
|
static int nv2a_initfn(PCIDevice *dev)
|
|
{
|
|
int i;
|
|
NV2AState *d;
|
|
|
|
d = NV2A_DEVICE(dev);
|
|
|
|
d->pcrtc.start = 0;
|
|
|
|
d->pramdac.core_clock_coeff = 0x00011c01; /* 189MHz...? */
|
|
d->pramdac.core_clock_freq = 189000000;
|
|
d->pramdac.memory_clock_coeff = 0;
|
|
d->pramdac.video_clock_coeff = 0x0003C20D; /* 25182Khz...? */
|
|
|
|
|
|
|
|
/* legacy VGA shit */
|
|
VGACommonState *vga = &d->vga;
|
|
vga->vram_size_mb = 16;
|
|
/* seems to start in color mode */
|
|
vga->msr = VGA_MIS_COLOR;
|
|
|
|
vga_common_init(vga);
|
|
vga->get_bpp = nv2a_get_bpp;
|
|
|
|
vga->ds = graphic_console_init(nv2a_vga_update,
|
|
nv2a_vga_invalidate,
|
|
nv2a_vga_screen_dump,
|
|
nv2a_vga_text_update,
|
|
d);
|
|
|
|
|
|
/* mmio */
|
|
memory_region_init(&d->mmio, "nv2a-mmio", 0x1000000);
|
|
pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
|
|
|
|
for (i=0; i<sizeof(blocktable)/sizeof(blocktable[0]); i++) {
|
|
if (!blocktable[i].name) continue;
|
|
memory_region_init_io(&d->block_mmio[i], &blocktable[i].ops, d,
|
|
blocktable[i].name, blocktable[i].size);
|
|
memory_region_add_subregion(&d->mmio, blocktable[i].offset,
|
|
&d->block_mmio[i]);
|
|
}
|
|
|
|
|
|
/* vram */
|
|
memory_region_init_ram(&d->vram, "nv2a-vram", 128 * 0x100000);
|
|
pci_register_bar(&d->dev, 1, PCI_BASE_ADDRESS_MEM_PREFETCH, &d->vram);
|
|
|
|
memory_region_init_alias(&d->ramin, "nv2a-ramin", &d->vram,
|
|
memory_region_size(&d->vram) - 0x100000,
|
|
0x100000);
|
|
memory_region_add_subregion(&d->mmio, 0x700000, &d->ramin);
|
|
|
|
d->vram_ptr = memory_region_get_ram_ptr(&d->vram);
|
|
d->ramin_ptr = memory_region_get_ram_ptr(&d->ramin);
|
|
|
|
|
|
/* init fifo cache1 */
|
|
qemu_mutex_init(&d->pfifo.cache1.pull_lock);
|
|
qemu_mutex_init(&d->pfifo.cache1.cache_lock);
|
|
qemu_cond_init(&d->pfifo.cache1.cache_cond);
|
|
QSIMPLEQ_INIT(&d->pfifo.cache1.cache);
|
|
|
|
qemu_mutex_init(&d->pgraph.lock);
|
|
qemu_cond_init(&d->pgraph.context_cond);
|
|
|
|
/* fire up graphics contexts */
|
|
for (i=0; i<NV2A_NUM_CHANNELS; i++) {
|
|
nv2a_pgraph_context_init(&d->pgraph.context[i]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void nv2a_exitfn(PCIDevice *dev)
|
|
{
|
|
int i;
|
|
NV2AState *d;
|
|
d = NV2A_DEVICE(dev);
|
|
|
|
qemu_mutex_destroy(&d->pfifo.cache1.pull_lock);
|
|
qemu_mutex_destroy(&d->pfifo.cache1.cache_lock);
|
|
qemu_cond_destroy(&d->pfifo.cache1.cache_cond);
|
|
|
|
qemu_mutex_destroy(&d->pgraph.lock);
|
|
qemu_cond_destroy(&d->pgraph.context_cond);
|
|
|
|
for (i=0; i<NV2A_NUM_CHANNELS; i++) {
|
|
nv2a_pgraph_context_destroy(&d->pgraph.context[i]);
|
|
}
|
|
}
|
|
|
|
static void nv2a_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
|
|
|
k->vendor_id = PCI_VENDOR_ID_NVIDIA;
|
|
k->device_id = PCI_DEVICE_ID_NVIDIA_GEFORCE_NV2A;
|
|
k->revision = 161;
|
|
k->class_id = PCI_CLASS_DISPLAY_3D;
|
|
k->init = nv2a_initfn;
|
|
k->exit = nv2a_exitfn;
|
|
|
|
dc->desc = "GeForce NV2A Integrated Graphics";
|
|
}
|
|
|
|
static const TypeInfo nv2a_info = {
|
|
.name = "nv2a",
|
|
.parent = TYPE_PCI_DEVICE,
|
|
.instance_size = sizeof(NV2AState),
|
|
.class_init = nv2a_class_init,
|
|
};
|
|
|
|
static void nv2a_register(void)
|
|
{
|
|
type_register_static(&nv2a_info);
|
|
}
|
|
type_init(nv2a_register);
|
|
|
|
|
|
|
|
|
|
|
|
void nv2a_init(PCIBus *bus, int devfn, qemu_irq irq)
|
|
{
|
|
PCIDevice *dev;
|
|
NV2AState *d;
|
|
dev = pci_create_simple(bus, devfn, "nv2a");
|
|
d = NV2A_DEVICE(dev);
|
|
d->irq = irq;
|
|
} |