2018-03-24 08:04:15 +00:00
/*
* QEMU MCPX Audio Processing Unit implementation
*
* Copyright ( c ) 2012 espes
2019-02-11 19:05:37 +00:00
* Copyright ( c ) 2018 - 2019 Jannik Vogel
2018-03-24 08:04:15 +00:00
*
2018-10-10 03:38:16 +00:00
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2 of the License , or ( at your option ) any later version .
2018-03-24 08:04:15 +00:00
*
2018-10-10 03:38:16 +00:00
* This library is distributed in the hope that it will be useful ,
2018-03-24 08:04:15 +00:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
2018-10-10 03:38:16 +00:00
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
2018-03-24 08:04:15 +00:00
*
2018-10-10 03:38:16 +00:00
* You should have received a copy of the GNU Lesser General Public
* License along with this library ; if not , see < http : //www.gnu.org/licenses/>.
2018-03-24 08:04:15 +00:00
*/
2018-06-26 21:47:29 +00:00
# include "qemu/osdep.h"
2018-03-24 08:04:15 +00:00
# include "hw/hw.h"
# include "hw/i386/pc.h"
# include "hw/pci/pci.h"
2018-06-26 21:47:29 +00:00
# include "cpu.h"
2018-03-24 08:04:15 +00:00
# include "hw/xbox/dsp/dsp.h"
2019-02-11 19:05:40 +00:00
# include <math.h>
2018-03-24 08:04:15 +00:00
2019-02-11 19:05:37 +00:00
# define NUM_SAMPLES_PER_FRAME 32
# define NUM_MIXBINS 32
2018-12-26 21:23:25 +00:00
# include "hw/xbox/mcpx_apu.h"
2018-03-24 08:04:15 +00:00
# define NV_PAPU_ISTS 0x00001000
# define NV_PAPU_ISTS_GINTSTS (1 << 0)
# define NV_PAPU_ISTS_FETINTSTS (1 << 4)
# define NV_PAPU_IEN 0x00001004
# define NV_PAPU_FECTL 0x00001100
# define NV_PAPU_FECTL_FEMETHMODE 0x000000E0
# define NV_PAPU_FECTL_FEMETHMODE_FREE_RUNNING 0x00000000
# define NV_PAPU_FECTL_FEMETHMODE_HALTED 0x00000080
# define NV_PAPU_FECTL_FEMETHMODE_TRAPPED 0x000000E0
# define NV_PAPU_FECTL_FETRAPREASON 0x00000F00
# define NV_PAPU_FECTL_FETRAPREASON_REQUESTED 0x00000F00
# define NV_PAPU_FECV 0x00001110
# define NV_PAPU_FEAV 0x00001118
# define NV_PAPU_FEAV_VALUE 0x0000FFFF
# define NV_PAPU_FEAV_LST 0x00030000
# define NV_PAPU_FEDECMETH 0x00001300
# define NV_PAPU_FEDECPARAM 0x00001304
# define NV_PAPU_FEMEMADDR 0x00001324
# define NV_PAPU_FEMEMDATA 0x00001334
# define NV_PAPU_FETFORCE0 0x00001500
# define NV_PAPU_FETFORCE1 0x00001504
# define NV_PAPU_FETFORCE1_SE2FE_IDLE_VOICE (1 << 15)
# define NV_PAPU_SECTL 0x00002000
# define NV_PAPU_SECTL_XCNTMODE 0x00000018
# define NV_PAPU_SECTL_XCNTMODE_OFF 0
# define NV_PAPU_XGSCNT 0x0000200C
# define NV_PAPU_VPVADDR 0x0000202C
2019-07-08 20:44:30 +00:00
# define NV_PAPU_VPSGEADDR 0x00002030
2018-03-24 08:04:15 +00:00
# define NV_PAPU_GPSADDR 0x00002040
2018-12-28 07:10:36 +00:00
# define NV_PAPU_GPFADDR 0x00002044
2018-03-24 08:04:15 +00:00
# define NV_PAPU_EPSADDR 0x00002048
2018-12-28 07:10:36 +00:00
# define NV_PAPU_EPFADDR 0x0000204C
2018-03-24 08:04:15 +00:00
# define NV_PAPU_TVL2D 0x00002054
# define NV_PAPU_CVL2D 0x00002058
# define NV_PAPU_NVL2D 0x0000205C
# define NV_PAPU_TVL3D 0x00002060
# define NV_PAPU_CVL3D 0x00002064
# define NV_PAPU_NVL3D 0x00002068
# define NV_PAPU_TVLMP 0x0000206C
# define NV_PAPU_CVLMP 0x00002070
# define NV_PAPU_NVLMP 0x00002074
# define NV_PAPU_GPSMAXSGE 0x000020D4
2018-12-28 07:10:36 +00:00
# define NV_PAPU_GPFMAXSGE 0x000020D8
2018-03-24 08:04:15 +00:00
# define NV_PAPU_EPSMAXSGE 0x000020DC
2018-12-28 07:10:36 +00:00
# define NV_PAPU_EPFMAXSGE 0x000020E0
/* Each FIFO has the same fields */
# define NV_PAPU_GPOFBASE0 0x00003024
# define NV_PAPU_GPOFBASE0_VALUE 0x00FFFF00
# define NV_PAPU_GPOFEND0 0x00003028
# define NV_PAPU_GPOFEND0_VALUE 0x00FFFF00
# define NV_PAPU_GPOFCUR0 0x0000302C
# define NV_PAPU_GPOFCUR0_VALUE 0x00FFFFFC
# define NV_PAPU_GPOFBASE1 0x00003034
# define NV_PAPU_GPOFEND1 0x00003038
# define NV_PAPU_GPOFCUR1 0x0000303C
# define NV_PAPU_GPOFBASE2 0x00003044
# define NV_PAPU_GPOFEND2 0x00003048
# define NV_PAPU_GPOFCUR2 0x0000304C
# define NV_PAPU_GPOFBASE3 0x00003054
# define NV_PAPU_GPOFEND3 0x00003058
# define NV_PAPU_GPOFCUR3 0x0000305C
/* Fields are same as for the 4 output FIFOs, but only 2 input FIFOs */
# define NV_PAPU_GPIFBASE0 0x00003064
# define NV_PAPU_GPIFEND0 0x00003068
# define NV_PAPU_GPIFCUR0 0x0000306C
# define NV_PAPU_GPIFBASE1 0x00003074
# define NV_PAPU_GPIFEND1 0x00003078
# define NV_PAPU_GPIFCUR1 0x0000307C
/* Fields, strides and count is same as for GP FIFOs */
# define NV_PAPU_EPOFBASE0 0x00004024
# define NV_PAPU_EPOFEND0 0x00004028
# define NV_PAPU_EPOFCUR0 0x0000402C
# define NV_PAPU_EPIFBASE0 0x00004064
# define NV_PAPU_EPIFEND0 0x00004068
# define NV_PAPU_EPIFCUR0 0x0000406C
2018-03-24 08:04:15 +00:00
2018-07-21 21:19:15 +00:00
# define NV_PAPU_GPXMEM 0x00000000
# define NV_PAPU_GPMIXBUF 0x00005000
# define NV_PAPU_GPYMEM 0x00006000
# define NV_PAPU_GPPMEM 0x0000A000
2018-03-24 08:04:15 +00:00
# define NV_PAPU_GPRST 0x0000FFFC
# define NV_PAPU_GPRST_GPRST (1 << 0)
# define NV_PAPU_GPRST_GPDSPRST (1 << 1)
# define NV_PAPU_GPRST_GPNMI (1 << 2)
# define NV_PAPU_GPRST_GPABORT (1 << 3)
# define NV_PAPU_EPXMEM 0x00000000
# define NV_PAPU_EPYMEM 0x00006000
# define NV_PAPU_EPPMEM 0x0000A000
# define NV_PAPU_EPRST 0x0000FFFC
static const struct {
hwaddr top , current , next ;
} voice_list_regs [ ] = {
{ NV_PAPU_TVL2D , NV_PAPU_CVL2D , NV_PAPU_NVL2D } , //2D
{ NV_PAPU_TVL3D , NV_PAPU_CVL3D , NV_PAPU_NVL3D } , //3D
{ NV_PAPU_TVLMP , NV_PAPU_CVLMP , NV_PAPU_NVLMP } , //MP
} ;
/* audio processor object / front-end messages */
# define NV1BA0_PIO_FREE 0x00000010
# define NV1BA0_PIO_SET_ANTECEDENT_VOICE 0x00000120
# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_HANDLE 0x0000FFFF
# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST 0x00030000
# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST_INHERIT 0
# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST_2D_TOP 1
# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST_3D_TOP 2
# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST_MP_TOP 3
# define NV1BA0_PIO_VOICE_ON 0x00000124
# define NV1BA0_PIO_VOICE_ON_HANDLE 0x0000FFFF
2019-07-08 20:44:30 +00:00
# define NV1BA0_PIO_VOICE_ON_ENVF 0x0F000000
# define NV1BA0_PIO_VOICE_ON_ENVA 0xF0000000
2018-03-24 08:04:15 +00:00
# define NV1BA0_PIO_VOICE_OFF 0x00000128
2018-12-26 03:54:16 +00:00
# define NV1BA0_PIO_VOICE_OFF_HANDLE 0x0000FFFF
2019-07-08 20:44:30 +00:00
# define NV1BA0_PIO_VOICE_RELEASE 0x0000012C
# define NV1BA0_PIO_VOICE_RELEASE_HANDLE 0x0000FFFF
2018-03-24 08:04:15 +00:00
# define NV1BA0_PIO_VOICE_PAUSE 0x00000140
# define NV1BA0_PIO_VOICE_PAUSE_HANDLE 0x0000FFFF
# define NV1BA0_PIO_VOICE_PAUSE_ACTION (1 << 18)
# define NV1BA0_PIO_SET_CURRENT_VOICE 0x000002F8
2019-07-08 20:44:30 +00:00
# define NV1BA0_PIO_SET_VOICE_CFG_VBIN 0x00000300
# define NV1BA0_PIO_SET_VOICE_CFG_FMT 0x00000304
# define NV1BA0_PIO_SET_VOICE_CFG_ENV0 0x00000308
# define NV1BA0_PIO_SET_VOICE_CFG_ENVA 0x0000030C
# define NV1BA0_PIO_SET_VOICE_CFG_ENV1 0x00000310
# define NV1BA0_PIO_SET_VOICE_CFG_ENVF 0x00000314
# define NV1BA0_PIO_SET_VOICE_CFG_MISC 0x00000318
# define NV1BA0_PIO_SET_VOICE_TAR_VOLA 0x00000360
# define NV1BA0_PIO_SET_VOICE_TAR_VOLB 0x00000364
# define NV1BA0_PIO_SET_VOICE_TAR_VOLC 0x00000368
# define NV1BA0_PIO_SET_VOICE_LFO_ENV 0x0000036C
# define NV1BA0_PIO_SET_VOICE_TAR_PITCH 0x0000037C
# define NV1BA0_PIO_SET_VOICE_TAR_PITCH_STEP 0xFFFF0000
# define NV1BA0_PIO_SET_VOICE_CFG_BUF_BASE 0x000003A0
# define NV1BA0_PIO_SET_VOICE_CFG_BUF_BASE_OFFSET 0x00FFFFFF
# define NV1BA0_PIO_SET_VOICE_CFG_BUF_LBO 0x000003A4
# define NV1BA0_PIO_SET_VOICE_CFG_BUF_LBO_OFFSET 0x00FFFFFF
# define NV1BA0_PIO_SET_VOICE_BUF_CBO 0x000003D8
# define NV1BA0_PIO_SET_VOICE_BUF_CBO_OFFSET 0x00FFFFFF
# define NV1BA0_PIO_SET_VOICE_CFG_BUF_EBO 0x000003DC
# define NV1BA0_PIO_SET_VOICE_CFG_BUF_EBO_OFFSET 0x00FFFFFF
# define NV1BA0_PIO_SET_CURRENT_INBUF_SGE 0x00000804
# define NV1BA0_PIO_SET_CURRENT_INBUF_SGE_HANDLE 0xFFFFFFFF
# define NV1BA0_PIO_SET_CURRENT_INBUF_SGE_OFFSET 0x00000808
# define NV1BA0_PIO_SET_CURRENT_INBUF_SGE_OFFSET_PARAMETER 0xFFFFF000
# define NV1BA0_PIO_SET_OUTBUF_BA 0x00001000 // 8 byte pitch, 4 entries
# define NV1BA0_PIO_SET_OUTBUF_BA_ADDRESS 0x007FFF00
# define NV1BA0_PIO_SET_OUTBUF_LEN 0x00001004 // 8 byte pitch, 4 entries
# define NV1BA0_PIO_SET_OUTBUF_LEN_VALUE 0x007FFF00
# define NV1BA0_PIO_SET_CURRENT_OUTBUF_SGE 0x00001800
# define NV1BA0_PIO_SET_CURRENT_OUTBUF_SGE_HANDLE 0xFFFFFFFF
# define NV1BA0_PIO_SET_CURRENT_OUTBUF_SGE_OFFSET 0x00001808
# define NV1BA0_PIO_SET_CURRENT_OUTBUF_SGE_OFFSET_PARAMETER 0xFFFFF000
2018-03-24 08:04:15 +00:00
# define SE2FE_IDLE_VOICE 0x00008000
/* voice structure */
# define NV_PAVS_SIZE 0x00000080
2019-07-08 20:44:30 +00:00
# define NV_PAVS_VOICE_CFG_VBIN 0x00000000
# define NV_PAVS_VOICE_CFG_VBIN_V0BIN (0x1F << 0)
# define NV_PAVS_VOICE_CFG_VBIN_V1BIN (0x1F << 5)
# define NV_PAVS_VOICE_CFG_VBIN_V2BIN (0x1F << 10)
# define NV_PAVS_VOICE_CFG_VBIN_V3BIN (0x1F << 16)
# define NV_PAVS_VOICE_CFG_VBIN_V4BIN (0x1F << 21)
# define NV_PAVS_VOICE_CFG_VBIN_V5BIN (0x1F << 26)
# define NV_PAVS_VOICE_CFG_FMT 0x00000004
# define NV_PAVS_VOICE_CFG_FMT_V6BIN (0x1F << 0)
# define NV_PAVS_VOICE_CFG_FMT_V7BIN (0x1F << 5)
# define NV_PAVS_VOICE_CFG_FMT_SAMPLES_PER_BLOCK (0x1F << 16)
# define NV_PAVS_VOICE_CFG_FMT_DATA_TYPE (1 << 24)
# define NV_PAVS_VOICE_CFG_FMT_LOOP (1 << 25)
# define NV_PAVS_VOICE_CFG_FMT_STEREO (1 << 27)
# define NV_PAVS_VOICE_CFG_FMT_SAMPLE_SIZE (0x3 << 28)
# define NV_PAVS_VOICE_CFG_FMT_SAMPLE_SIZE_U8 0
# define NV_PAVS_VOICE_CFG_FMT_SAMPLE_SIZE_S16 1
# define NV_PAVS_VOICE_CFG_FMT_SAMPLE_SIZE_S24 2
# define NV_PAVS_VOICE_CFG_FMT_SAMPLE_SIZE_S32 3
# define NV_PAVS_VOICE_CFG_FMT_CONTAINER_SIZE (0x3 << 30)
# define NV_PAVS_VOICE_CFG_FMT_CONTAINER_SIZE_B8 0
# define NV_PAVS_VOICE_CFG_FMT_CONTAINER_SIZE_B16 1
# define NV_PAVS_VOICE_CFG_FMT_CONTAINER_SIZE_ADPCM 2
# define NV_PAVS_VOICE_CFG_FMT_CONTAINER_SIZE_B32 3
# define NV_PAVS_VOICE_CFG_ENV0 0x00000008
# define NV_PAVS_VOICE_CFG_ENV0_EA_ATTACKRATE (0xFFF << 0)
# define NV_PAVS_VOICE_CFG_ENV0_EA_DELAYTIME (0xFFF << 12)
# define NV_PAVS_VOICE_CFG_ENV0_EF_PITCHSCALE (0xFF << 24)
# define NV_PAVS_VOICE_CFG_ENVA 0x0000000C
# define NV_PAVS_VOICE_CFG_ENVA_EA_DECAYRATE (0xFFF << 0)
# define NV_PAVS_VOICE_CFG_ENVA_EA_HOLDTIME (0xFFF << 12)
# define NV_PAVS_VOICE_CFG_ENVA_EA_SUSTAINLEVEL (0xFF << 24)
# define NV_PAVS_VOICE_CFG_ENV1 0x00000010
# define NV_PAVS_VOICE_CFG_ENV1_EF_FCSCALE (0xFF << 24)
# define NV_PAVS_VOICE_CFG_ENVF 0x00000014
# define NV_PAVS_VOICE_CFG_MISC 0x00000018
# define NV_PAVS_VOICE_CFG_MISC_EF_RELEASERATE (0xFFF << 0)
# define NV_PAVS_VOICE_CUR_PSL_START 0x00000020
# define NV_PAVS_VOICE_CUR_PSL_START_BA 0x00FFFFFF
# define NV_PAVS_VOICE_CUR_PSH_SAMPLE 0x00000024
# define NV_PAVS_VOICE_CUR_PSH_SAMPLE_LBO 0x00FFFFFF
# define NV_PAVS_VOICE_CUR_ECNT 0x00000034
# define NV_PAVS_VOICE_CUR_ECNT_EACOUNT 0x0000FFFF
# define NV_PAVS_VOICE_CUR_ECNT_EFCOUNT 0xFFFF0000
2018-03-24 08:04:15 +00:00
# define NV_PAVS_VOICE_PAR_STATE 0x00000054
# define NV_PAVS_VOICE_PAR_STATE_PAUSED (1 << 18)
2019-07-08 20:44:30 +00:00
# define NV_PAVS_VOICE_PAR_STATE_NEW_VOICE (1 << 20)
2018-03-24 08:04:15 +00:00
# define NV_PAVS_VOICE_PAR_STATE_ACTIVE_VOICE (1 << 21)
2019-07-08 20:44:30 +00:00
# define NV_PAVS_VOICE_PAR_STATE_EFCUR (0xF << 24)
# define NV_PAVS_VOICE_PAR_STATE_EACUR (0xF << 28)
# define NV_PAVS_VOICE_PAR_OFFSET 0x00000058
# define NV_PAVS_VOICE_PAR_OFFSET_CBO 0x00FFFFFF
# define NV_PAVS_VOICE_PAR_OFFSET_EALVL 0xFF000000
# define NV_PAVS_VOICE_PAR_NEXT 0x0000005C
# define NV_PAVS_VOICE_PAR_NEXT_EBO 0x00FFFFFF
# define NV_PAVS_VOICE_PAR_NEXT_EFLVL 0xFF000000
# define NV_PAVS_VOICE_TAR_VOLA 0x00000060
# define NV_PAVS_VOICE_TAR_VOLA_VOLUME6_B3_0 0x0000000F
# define NV_PAVS_VOICE_TAR_VOLA_VOLUME0 0x0000FFF0
# define NV_PAVS_VOICE_TAR_VOLA_VOLUME7_B3_0 0x000F0000
# define NV_PAVS_VOICE_TAR_VOLA_VOLUME1 0xFFF00000
# define NV_PAVS_VOICE_TAR_VOLB 0x00000064
# define NV_PAVS_VOICE_TAR_VOLB_VOLUME6_B7_4 0x0000000F
# define NV_PAVS_VOICE_TAR_VOLB_VOLUME2 0x0000FFF0
# define NV_PAVS_VOICE_TAR_VOLB_VOLUME7_B7_4 0x000F0000
# define NV_PAVS_VOICE_TAR_VOLB_VOLUME3 0xFFF00000
# define NV_PAVS_VOICE_TAR_VOLC 0x00000068
# define NV_PAVS_VOICE_TAR_VOLC_VOLUME6_B11_8 0x0000000F
# define NV_PAVS_VOICE_TAR_VOLC_VOLUME4 0x0000FFF0
# define NV_PAVS_VOICE_TAR_VOLC_VOLUME7_B11_8 0x000F0000
# define NV_PAVS_VOICE_TAR_VOLC_VOLUME5 0xFFF00000
# define NV_PAVS_VOICE_TAR_LFO_ENV 0x0000006C
# define NV_PAVS_VOICE_TAR_LFO_ENV_EA_RELEASERATE (0xFFF << 0)
# define NV_PAVS_VOICE_TAR_PITCH_LINK 0x0000007C
2018-03-24 08:04:15 +00:00
# define NV_PAVS_VOICE_TAR_PITCH_LINK_NEXT_VOICE_HANDLE 0x0000FFFF
2019-07-08 20:44:30 +00:00
# define NV_PAVS_VOICE_TAR_PITCH_LINK_PITCH 0xFFFF0000
2018-03-24 08:04:15 +00:00
2019-02-11 19:05:32 +00:00
# define GP_DSP_MIXBUF_BASE 0x001400
2018-12-28 07:10:36 +00:00
# define GP_OUTPUT_FIFO_COUNT 4
# define GP_INPUT_FIFO_COUNT 2
# define EP_OUTPUT_FIFO_COUNT 4
# define EP_INPUT_FIFO_COUNT 2
2018-03-24 08:04:15 +00:00
# define MCPX_HW_MAX_VOICES 256
2018-10-08 07:57:09 +00:00
# define GET_MASK(v, mask) (((v) & (mask)) >> ctz32(mask))
2018-03-24 08:04:15 +00:00
# define SET_MASK(v, mask, val) \
do { \
( v ) & = ~ ( mask ) ; \
2018-10-08 07:57:09 +00:00
( v ) | = ( ( val ) < < ctz32 ( mask ) ) & ( mask ) ; \
2018-03-24 08:04:15 +00:00
} while ( 0 )
2019-07-08 20:44:30 +00:00
# define CASE_4(v, step) \
case ( v ) : \
case ( v ) + ( step ) : \
case ( v ) + ( step ) * 2 : \
case ( v ) + ( step ) * 3
2018-03-24 08:04:15 +00:00
// #define MCPX_DEBUG
# ifdef MCPX_DEBUG
# define MCPX_DPRINTF(format, ...) printf(format, ## __VA_ARGS__)
# else
# define MCPX_DPRINTF(format, ...) do { } while (0)
# endif
2019-02-11 19:05:40 +00:00
/* More debug functionality */
# define GENERATE_MIXBIN_BEEP 0
2018-03-24 08:04:15 +00:00
typedef struct MCPXAPUState {
PCIDevice dev ;
2018-12-26 21:23:25 +00:00
MemoryRegion * ram ;
uint8_t * ram_ptr ;
2018-03-24 08:04:15 +00:00
MemoryRegion mmio ;
/* Setup Engine */
struct {
QEMUTimer * frame_timer ;
} se ;
/* Voice Processor */
struct {
MemoryRegion mmio ;
} vp ;
/* Global Processor */
struct {
MemoryRegion mmio ;
DSPState * dsp ;
uint32_t regs [ 0x10000 ] ;
} gp ;
/* Encode Processor */
struct {
MemoryRegion mmio ;
DSPState * dsp ;
uint32_t regs [ 0x10000 ] ;
} ep ;
2019-07-08 20:44:30 +00:00
uint32_t inbuf_sge_handle ; //FIXME: Where is this stored?
uint32_t outbuf_sge_handle ; //FIXME: Where is this stored?
2018-03-24 08:04:15 +00:00
uint32_t regs [ 0x20000 ] ;
} MCPXAPUState ;
# define MCPX_APU_DEVICE(obj) \
OBJECT_CHECK ( MCPXAPUState , ( obj ) , " mcpx-apu " )
static uint32_t voice_get_mask ( MCPXAPUState * d ,
unsigned int voice_handle ,
hwaddr offset ,
uint32_t mask )
{
assert ( voice_handle < 0xFFFF ) ;
hwaddr voice = d - > regs [ NV_PAPU_VPVADDR ]
+ voice_handle * NV_PAVS_SIZE ;
2018-06-26 21:47:29 +00:00
return ( ldl_le_phys ( & address_space_memory , voice + offset ) & mask )
2018-10-08 07:57:09 +00:00
> > ctz32 ( mask ) ;
2018-03-24 08:04:15 +00:00
}
static void voice_set_mask ( MCPXAPUState * d ,
unsigned int voice_handle ,
hwaddr offset ,
uint32_t mask ,
uint32_t val )
{
assert ( voice_handle < 0xFFFF ) ;
hwaddr voice = d - > regs [ NV_PAPU_VPVADDR ]
+ voice_handle * NV_PAVS_SIZE ;
2018-06-26 21:47:29 +00:00
uint32_t v = ldl_le_phys ( & address_space_memory , voice + offset ) & ~ mask ;
stl_le_phys ( & address_space_memory , voice + offset ,
2018-10-08 07:57:09 +00:00
v | ( ( val < < ctz32 ( mask ) ) & mask ) ) ;
2018-03-24 08:04:15 +00:00
}
static void update_irq ( MCPXAPUState * d )
{
if ( ( d - > regs [ NV_PAPU_IEN ] & NV_PAPU_ISTS_GINTSTS )
& & ( ( d - > regs [ NV_PAPU_ISTS ] & ~ NV_PAPU_ISTS_GINTSTS )
& d - > regs [ NV_PAPU_IEN ] ) ) {
d - > regs [ NV_PAPU_ISTS ] | = NV_PAPU_ISTS_GINTSTS ;
MCPX_DPRINTF ( " mcpx irq raise \n " ) ;
pci_irq_assert ( & d - > dev ) ;
} else {
d - > regs [ NV_PAPU_ISTS ] & = ~ NV_PAPU_ISTS_GINTSTS ;
MCPX_DPRINTF ( " mcpx irq lower \n " ) ;
pci_irq_deassert ( & d - > dev ) ;
}
}
static uint64_t mcpx_apu_read ( void * opaque ,
hwaddr addr , unsigned int size )
{
MCPXAPUState * d = opaque ;
uint64_t r = 0 ;
switch ( addr ) {
case NV_PAPU_XGSCNT :
r = qemu_clock_get_ns ( QEMU_CLOCK_VIRTUAL ) / 100 ; //???
break ;
default :
if ( addr < 0x20000 ) {
r = d - > regs [ addr ] ;
}
break ;
}
MCPX_DPRINTF ( " mcpx apu: read [0x%llx] -> 0x%llx \n " , addr , r ) ;
return r ;
}
2018-06-26 21:47:29 +00:00
2018-03-24 08:04:15 +00:00
static void mcpx_apu_write ( void * opaque , hwaddr addr ,
uint64_t val , unsigned int size )
{
MCPXAPUState * d = opaque ;
MCPX_DPRINTF ( " mcpx apu: [0x%llx] = 0x%llx \n " , addr , val ) ;
switch ( addr ) {
case NV_PAPU_ISTS :
/* the bits of the interrupts to clear are wrtten */
d - > regs [ NV_PAPU_ISTS ] & = ~ val ;
update_irq ( d ) ;
break ;
case NV_PAPU_SECTL :
2018-06-26 21:47:29 +00:00
if ( ( ( val & NV_PAPU_SECTL_XCNTMODE ) > > 3 )
= = NV_PAPU_SECTL_XCNTMODE_OFF ) {
2018-03-24 08:04:15 +00:00
timer_del ( d - > se . frame_timer ) ;
} else {
timer_mod ( d - > se . frame_timer ,
qemu_clock_get_ms ( QEMU_CLOCK_VIRTUAL ) + 10 ) ;
}
d - > regs [ addr ] = val ;
break ;
case NV_PAPU_FEMEMDATA :
/* 'magic write'
* This value is expected to be written to FEMEMADDR on completion of
* something to do with notifies . Just do it now : / */
2018-06-26 21:47:29 +00:00
stl_le_phys ( & address_space_memory , d - > regs [ NV_PAPU_FEMEMADDR ] , val ) ;
2018-03-24 08:04:15 +00:00
d - > regs [ addr ] = val ;
break ;
default :
if ( addr < 0x20000 ) {
d - > regs [ addr ] = val ;
}
break ;
}
}
2018-06-26 21:47:29 +00:00
2018-03-24 08:04:15 +00:00
static const MemoryRegionOps mcpx_apu_mmio_ops = {
. read = mcpx_apu_read ,
. write = mcpx_apu_write ,
} ;
static void fe_method ( MCPXAPUState * d ,
uint32_t method , uint32_t argument )
{
2019-07-12 17:23:00 +00:00
# ifdef MCPX_DEBUG
2019-07-08 20:44:30 +00:00
unsigned int slot ;
2019-07-12 17:23:00 +00:00
# endif
2019-07-08 20:44:30 +00:00
2018-03-24 08:04:15 +00:00
MCPX_DPRINTF ( " mcpx fe_method 0x%x 0x%x \n " , method , argument ) ;
//assert((d->regs[NV_PAPU_FECTL] & NV_PAPU_FECTL_FEMETHMODE) == 0);
d - > regs [ NV_PAPU_FEDECMETH ] = method ;
d - > regs [ NV_PAPU_FEDECPARAM ] = argument ;
unsigned int selected_handle , list ;
switch ( method ) {
case NV1BA0_PIO_SET_ANTECEDENT_VOICE :
d - > regs [ NV_PAPU_FEAV ] = argument ;
break ;
case NV1BA0_PIO_VOICE_ON :
selected_handle = argument & NV1BA0_PIO_VOICE_ON_HANDLE ;
list = GET_MASK ( d - > regs [ NV_PAPU_FEAV ] , NV_PAPU_FEAV_LST ) ;
if ( list ! = NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST_INHERIT ) {
/* voice is added to the top of the selected list */
2018-06-26 21:47:29 +00:00
unsigned int top_reg = voice_list_regs [ list - 1 ] . top ;
2018-03-24 08:04:15 +00:00
voice_set_mask ( d , selected_handle ,
NV_PAVS_VOICE_TAR_PITCH_LINK ,
NV_PAVS_VOICE_TAR_PITCH_LINK_NEXT_VOICE_HANDLE ,
d - > regs [ top_reg ] ) ;
d - > regs [ top_reg ] = selected_handle ;
} else {
unsigned int antecedent_voice =
GET_MASK ( d - > regs [ NV_PAPU_FEAV ] , NV_PAPU_FEAV_VALUE ) ;
/* voice is added after the antecedent voice */
assert ( antecedent_voice ! = 0xFFFF ) ;
uint32_t next_handle = voice_get_mask ( d , antecedent_voice ,
NV_PAVS_VOICE_TAR_PITCH_LINK ,
NV_PAVS_VOICE_TAR_PITCH_LINK_NEXT_VOICE_HANDLE ) ;
voice_set_mask ( d , selected_handle ,
NV_PAVS_VOICE_TAR_PITCH_LINK ,
NV_PAVS_VOICE_TAR_PITCH_LINK_NEXT_VOICE_HANDLE ,
next_handle ) ;
voice_set_mask ( d , antecedent_voice ,
NV_PAVS_VOICE_TAR_PITCH_LINK ,
NV_PAVS_VOICE_TAR_PITCH_LINK_NEXT_VOICE_HANDLE ,
selected_handle ) ;
}
2019-07-08 20:44:30 +00:00
unsigned int ea_start = GET_MASK ( argument , NV1BA0_PIO_VOICE_ON_ENVA ) ;
voice_set_mask ( d , selected_handle ,
NV_PAVS_VOICE_PAR_STATE ,
NV_PAVS_VOICE_PAR_STATE_EACUR ,
ea_start ) ;
if ( ea_start = = 1 ) { // Delay
uint16_t delay_time = voice_get_mask ( d , selected_handle , NV_PAVS_VOICE_CFG_ENV0 , NV_PAVS_VOICE_CFG_ENV0_EA_DELAYTIME ) ;
voice_set_mask ( d , selected_handle , NV_PAVS_VOICE_CUR_ECNT , NV_PAVS_VOICE_CUR_ECNT_EACOUNT , delay_time * 16 ) ;
} else if ( ea_start = = 2 ) { // Attack
voice_set_mask ( d , selected_handle , NV_PAVS_VOICE_CUR_ECNT , NV_PAVS_VOICE_CUR_ECNT_EACOUNT , 0x0000 ) ;
} else if ( ea_start = = 3 ) { // Hold
uint16_t hold_time = voice_get_mask ( d , selected_handle , NV_PAVS_VOICE_CFG_ENVA , NV_PAVS_VOICE_CFG_ENVA_EA_HOLDTIME ) ;
voice_set_mask ( d , selected_handle , NV_PAVS_VOICE_CUR_ECNT , NV_PAVS_VOICE_CUR_ECNT_EACOUNT , hold_time * 16 ) ;
}
//FIXME: Will count be overwritten in other cases too?
unsigned int ef_start = GET_MASK ( argument , NV1BA0_PIO_VOICE_ON_ENVF ) ;
voice_set_mask ( d , selected_handle ,
NV_PAVS_VOICE_PAR_STATE ,
NV_PAVS_VOICE_PAR_STATE_EFCUR ,
ef_start ) ;
if ( ef_start = = 1 ) { // Delay
uint16_t delay_time = voice_get_mask ( d , selected_handle , NV_PAVS_VOICE_CFG_ENV1 , NV_PAVS_VOICE_CFG_ENV0_EA_DELAYTIME ) ;
voice_set_mask ( d , selected_handle , NV_PAVS_VOICE_CUR_ECNT , NV_PAVS_VOICE_CUR_ECNT_EFCOUNT , delay_time * 16 ) ;
} else if ( ef_start = = 2 ) { // Attack
voice_set_mask ( d , selected_handle , NV_PAVS_VOICE_CUR_ECNT , NV_PAVS_VOICE_CUR_ECNT_EFCOUNT , 0x0000 ) ;
} else if ( ef_start = = 3 ) { // Hold
uint16_t hold_time = voice_get_mask ( d , selected_handle , NV_PAVS_VOICE_CFG_ENVF , NV_PAVS_VOICE_CFG_ENVA_EA_HOLDTIME ) ;
voice_set_mask ( d , selected_handle , NV_PAVS_VOICE_CUR_ECNT , NV_PAVS_VOICE_CUR_ECNT_EFCOUNT , hold_time * 16 ) ;
}
//FIXME: Will count be overwritten in other cases too?
2018-12-26 03:56:12 +00:00
voice_set_mask ( d , selected_handle ,
NV_PAVS_VOICE_PAR_STATE ,
NV_PAVS_VOICE_PAR_STATE_ACTIVE_VOICE ,
1 ) ;
2018-03-24 08:04:15 +00:00
break ;
case NV1BA0_PIO_VOICE_OFF :
2018-12-26 03:54:16 +00:00
voice_set_mask ( d , argument & NV1BA0_PIO_VOICE_OFF_HANDLE ,
2018-03-24 08:04:15 +00:00
NV_PAVS_VOICE_PAR_STATE ,
NV_PAVS_VOICE_PAR_STATE_ACTIVE_VOICE ,
0 ) ;
break ;
case NV1BA0_PIO_VOICE_PAUSE :
voice_set_mask ( d , argument & NV1BA0_PIO_VOICE_PAUSE_HANDLE ,
NV_PAVS_VOICE_PAR_STATE ,
NV_PAVS_VOICE_PAR_STATE_PAUSED ,
( argument & NV1BA0_PIO_VOICE_PAUSE_ACTION ) ! = 0 ) ;
break ;
case NV1BA0_PIO_SET_CURRENT_VOICE :
d - > regs [ NV_PAPU_FECV ] = argument ;
break ;
2019-07-08 20:44:30 +00:00
case NV1BA0_PIO_SET_VOICE_CFG_VBIN :
voice_set_mask ( d , d - > regs [ NV_PAPU_FECV ] ,
NV_PAVS_VOICE_CFG_VBIN ,
0xFFFFFFFF ,
argument ) ;
break ;
case NV1BA0_PIO_SET_VOICE_CFG_FMT :
voice_set_mask ( d , d - > regs [ NV_PAPU_FECV ] ,
NV_PAVS_VOICE_CFG_FMT ,
0xFFFFFFFF ,
argument ) ;
break ;
case NV1BA0_PIO_SET_VOICE_CFG_ENV0 :
voice_set_mask ( d , d - > regs [ NV_PAPU_FECV ] ,
NV_PAVS_VOICE_CFG_ENV0 ,
0xFFFFFFFF ,
argument ) ;
break ;
case NV1BA0_PIO_SET_VOICE_CFG_ENVA :
voice_set_mask ( d , d - > regs [ NV_PAPU_FECV ] ,
NV_PAVS_VOICE_CFG_ENVA ,
0xFFFFFFFF ,
argument ) ;
break ;
case NV1BA0_PIO_SET_VOICE_CFG_ENV1 :
voice_set_mask ( d , d - > regs [ NV_PAPU_FECV ] ,
NV_PAVS_VOICE_CFG_ENV1 ,
0xFFFFFFFF ,
argument ) ;
break ;
case NV1BA0_PIO_SET_VOICE_CFG_ENVF :
voice_set_mask ( d , d - > regs [ NV_PAPU_FECV ] ,
NV_PAVS_VOICE_CFG_ENVF ,
0xFFFFFFFF ,
argument ) ;
break ;
case NV1BA0_PIO_SET_VOICE_CFG_MISC :
voice_set_mask ( d , d - > regs [ NV_PAPU_FECV ] ,
NV_PAVS_VOICE_CFG_MISC ,
0xFFFFFFFF ,
argument ) ;
break ;
case NV1BA0_PIO_SET_VOICE_TAR_VOLA :
voice_set_mask ( d , d - > regs [ NV_PAPU_FECV ] ,
NV_PAVS_VOICE_TAR_VOLA ,
0xFFFFFFFF ,
argument ) ;
break ;
case NV1BA0_PIO_SET_VOICE_TAR_VOLB :
voice_set_mask ( d , d - > regs [ NV_PAPU_FECV ] ,
NV_PAVS_VOICE_TAR_VOLB ,
0xFFFFFFFF ,
argument ) ;
break ;
case NV1BA0_PIO_SET_VOICE_TAR_VOLC :
voice_set_mask ( d , d - > regs [ NV_PAPU_FECV ] ,
NV_PAVS_VOICE_TAR_VOLC ,
0xFFFFFFFF ,
argument ) ;
break ;
case NV1BA0_PIO_SET_VOICE_LFO_ENV :
voice_set_mask ( d , d - > regs [ NV_PAPU_FECV ] ,
NV_PAVS_VOICE_TAR_LFO_ENV ,
0xFFFFFFFF ,
argument ) ;
break ;
case NV1BA0_PIO_SET_VOICE_TAR_PITCH :
voice_set_mask ( d , d - > regs [ NV_PAPU_FECV ] ,
NV_PAVS_VOICE_TAR_PITCH_LINK ,
NV_PAVS_VOICE_TAR_PITCH_LINK_PITCH ,
( argument & NV1BA0_PIO_SET_VOICE_TAR_PITCH_STEP ) > > 16 ) ;
break ;
case NV1BA0_PIO_SET_VOICE_CFG_BUF_BASE :
voice_set_mask ( d , d - > regs [ NV_PAPU_FECV ] ,
NV_PAVS_VOICE_CUR_PSL_START ,
NV_PAVS_VOICE_CUR_PSL_START_BA ,
argument ) ;
break ;
case NV1BA0_PIO_SET_VOICE_CFG_BUF_LBO :
voice_set_mask ( d , d - > regs [ NV_PAPU_FECV ] ,
NV_PAVS_VOICE_CUR_PSH_SAMPLE ,
NV_PAVS_VOICE_CUR_PSH_SAMPLE_LBO ,
argument ) ;
break ;
case NV1BA0_PIO_SET_VOICE_BUF_CBO :
voice_set_mask ( d , d - > regs [ NV_PAPU_FECV ] ,
NV_PAVS_VOICE_PAR_OFFSET ,
NV_PAVS_VOICE_PAR_OFFSET_CBO ,
argument ) ;
break ;
case NV1BA0_PIO_SET_VOICE_CFG_BUF_EBO :
voice_set_mask ( d , d - > regs [ NV_PAPU_FECV ] ,
NV_PAVS_VOICE_PAR_NEXT ,
NV_PAVS_VOICE_PAR_NEXT_EBO ,
argument ) ;
break ;
case NV1BA0_PIO_SET_CURRENT_INBUF_SGE :
d - > inbuf_sge_handle = argument & NV1BA0_PIO_SET_CURRENT_INBUF_SGE_HANDLE ;
break ;
case NV1BA0_PIO_SET_CURRENT_INBUF_SGE_OFFSET : {
//FIXME: Is there an upper limit for the SGE table size?
//FIXME: NV_PAPU_VPSGEADDR is probably bad, as outbuf SGE use the same handle range (or that is also wrong)
hwaddr sge_address = d - > regs [ NV_PAPU_VPSGEADDR ] + d - > inbuf_sge_handle * 8 ;
2019-07-08 21:14:15 +00:00
stl_le_phys ( & address_space_memory , sge_address , argument & NV1BA0_PIO_SET_CURRENT_INBUF_SGE_OFFSET_PARAMETER ) ;
2019-07-12 16:58:29 +00:00
MCPX_DPRINTF ( " Wrote inbuf SGE[0x%X] = 0x%08X \n " , d - > inbuf_sge_handle , argument & NV1BA0_PIO_SET_CURRENT_INBUF_SGE_OFFSET_PARAMETER ) ;
2019-07-08 20:44:30 +00:00
break ;
}
CASE_4 ( NV1BA0_PIO_SET_OUTBUF_BA , 8 ) : // 8 byte pitch, 4 entries
2019-07-12 17:23:00 +00:00
# ifdef MCPX_DEBUG
2019-07-08 20:44:30 +00:00
slot = ( method - NV1BA0_PIO_SET_OUTBUF_BA ) / 8 ;
//FIXME: Use NV1BA0_PIO_SET_OUTBUF_BA_ADDRESS = 0x007FFF00 ?
2019-07-12 16:58:29 +00:00
MCPX_DPRINTF ( " outbuf_ba[%d]: 0x%08X \n " , slot , argument ) ;
2019-07-12 17:23:00 +00:00
# endif
2019-07-08 20:44:30 +00:00
//assert(false); //FIXME: Enable assert! no idea what this reg does
break ;
CASE_4 ( NV1BA0_PIO_SET_OUTBUF_LEN , 8 ) : // 8 byte pitch, 4 entries
2019-07-12 17:23:00 +00:00
# ifdef MCPX_DEBUG
2019-07-08 20:44:30 +00:00
slot = ( method - NV1BA0_PIO_SET_OUTBUF_LEN ) / 8 ;
//FIXME: Use NV1BA0_PIO_SET_OUTBUF_LEN_VALUE = 0x007FFF00 ?
2019-07-12 16:58:29 +00:00
MCPX_DPRINTF ( " outbuf_len[%d]: 0x%08X \n " , slot , argument ) ;
2019-07-12 17:23:00 +00:00
# endif
2019-07-08 20:44:30 +00:00
//assert(false); //FIXME: Enable assert! no idea what this reg does
break ;
case NV1BA0_PIO_SET_CURRENT_OUTBUF_SGE :
d - > outbuf_sge_handle = argument & NV1BA0_PIO_SET_CURRENT_OUTBUF_SGE_HANDLE ;
break ;
case NV1BA0_PIO_SET_CURRENT_OUTBUF_SGE_OFFSET : {
//FIXME: Is there an upper limit for the SGE table size?
//FIXME: NV_PAPU_VPSGEADDR is probably bad, as inbuf SGE use the same handle range (or that is also wrong)
// NV_PAPU_EPFADDR EP outbufs
// NV_PAPU_GPFADDR GP outbufs
// But how does it know which outbuf is being written?!
hwaddr sge_address = d - > regs [ NV_PAPU_VPSGEADDR ] + d - > outbuf_sge_handle * 8 ;
2019-07-08 21:14:15 +00:00
stl_le_phys ( & address_space_memory , sge_address , argument & NV1BA0_PIO_SET_CURRENT_OUTBUF_SGE_OFFSET_PARAMETER ) ;
2019-07-12 16:58:29 +00:00
MCPX_DPRINTF ( " Wrote outbuf SGE[0x%X] = 0x%08X \n " , d - > outbuf_sge_handle , argument & NV1BA0_PIO_SET_CURRENT_OUTBUF_SGE_OFFSET_PARAMETER ) ;
2019-07-08 20:44:30 +00:00
break ;
}
2018-03-24 08:04:15 +00:00
case SE2FE_IDLE_VOICE :
if ( d - > regs [ NV_PAPU_FETFORCE1 ] & NV_PAPU_FETFORCE1_SE2FE_IDLE_VOICE ) {
2018-06-26 21:47:29 +00:00
2018-03-24 08:04:15 +00:00
d - > regs [ NV_PAPU_FECTL ] & = ~ NV_PAPU_FECTL_FEMETHMODE ;
d - > regs [ NV_PAPU_FECTL ] | = NV_PAPU_FECTL_FEMETHMODE_TRAPPED ;
d - > regs [ NV_PAPU_FECTL ] & = ~ NV_PAPU_FECTL_FETRAPREASON ;
d - > regs [ NV_PAPU_FECTL ] | = NV_PAPU_FECTL_FETRAPREASON_REQUESTED ;
d - > regs [ NV_PAPU_ISTS ] | = NV_PAPU_ISTS_FETINTSTS ;
update_irq ( d ) ;
} else {
assert ( false ) ;
}
break ;
default :
assert ( false ) ;
break ;
}
}
static uint64_t vp_read ( void * opaque ,
hwaddr addr , unsigned int size )
{
MCPX_DPRINTF ( " mcpx apu VP: read [0x%llx] \n " , addr ) ;
switch ( addr ) {
case NV1BA0_PIO_FREE :
/* we don't simulate the queue for now,
* pretend to always be empty */
return 0x80 ;
default :
break ;
}
return 0 ;
}
2018-06-26 21:47:29 +00:00
2018-03-24 08:04:15 +00:00
static void vp_write ( void * opaque , hwaddr addr ,
uint64_t val , unsigned int size )
{
MCPXAPUState * d = opaque ;
MCPX_DPRINTF ( " mcpx apu VP: [0x%llx] = 0x%llx \n " , addr , val ) ;
switch ( addr ) {
case NV1BA0_PIO_SET_ANTECEDENT_VOICE :
case NV1BA0_PIO_VOICE_ON :
case NV1BA0_PIO_VOICE_OFF :
case NV1BA0_PIO_VOICE_PAUSE :
case NV1BA0_PIO_SET_CURRENT_VOICE :
2019-07-08 20:44:30 +00:00
case NV1BA0_PIO_SET_VOICE_CFG_VBIN :
case NV1BA0_PIO_SET_VOICE_CFG_FMT :
case NV1BA0_PIO_SET_VOICE_CFG_ENV0 :
case NV1BA0_PIO_SET_VOICE_CFG_ENVA :
case NV1BA0_PIO_SET_VOICE_CFG_ENV1 :
case NV1BA0_PIO_SET_VOICE_CFG_ENVF :
case NV1BA0_PIO_SET_VOICE_CFG_MISC :
case NV1BA0_PIO_SET_VOICE_TAR_VOLA :
case NV1BA0_PIO_SET_VOICE_TAR_VOLB :
case NV1BA0_PIO_SET_VOICE_TAR_VOLC :
case NV1BA0_PIO_SET_VOICE_LFO_ENV :
case NV1BA0_PIO_SET_VOICE_TAR_PITCH :
case NV1BA0_PIO_SET_VOICE_CFG_BUF_BASE :
case NV1BA0_PIO_SET_VOICE_CFG_BUF_LBO :
case NV1BA0_PIO_SET_VOICE_BUF_CBO :
case NV1BA0_PIO_SET_VOICE_CFG_BUF_EBO :
case NV1BA0_PIO_SET_CURRENT_INBUF_SGE :
case NV1BA0_PIO_SET_CURRENT_INBUF_SGE_OFFSET :
CASE_4 ( NV1BA0_PIO_SET_OUTBUF_BA , 8 ) : // 8 byte pitch, 4 entries
CASE_4 ( NV1BA0_PIO_SET_OUTBUF_LEN , 8 ) : // 8 byte pitch, 4 entries
case NV1BA0_PIO_SET_CURRENT_OUTBUF_SGE :
case NV1BA0_PIO_SET_CURRENT_OUTBUF_SGE_OFFSET :
2018-03-24 08:04:15 +00:00
/* TODO: these should instead be queueing up fe commands */
fe_method ( d , addr , val ) ;
break ;
default :
break ;
}
}
2018-06-26 21:47:29 +00:00
2018-03-24 08:04:15 +00:00
static const MemoryRegionOps vp_ops = {
. read = vp_read ,
. write = vp_write ,
} ;
2018-12-26 21:23:25 +00:00
static void scatter_gather_rw ( MCPXAPUState * d ,
hwaddr sge_base , unsigned int max_sge ,
uint8_t * ptr , uint32_t addr , size_t len ,
bool dir )
2018-03-24 08:04:15 +00:00
{
2018-12-26 21:23:25 +00:00
unsigned int page_entry = addr / TARGET_PAGE_SIZE ;
unsigned int offset_in_page = addr % TARGET_PAGE_SIZE ;
unsigned int bytes_to_copy = TARGET_PAGE_SIZE - offset_in_page ;
while ( len > 0 ) {
2018-12-28 06:42:12 +00:00
assert ( page_entry < = max_sge ) ;
2018-12-26 21:23:25 +00:00
2018-06-26 21:47:29 +00:00
uint32_t prd_address = ldl_le_phys ( & address_space_memory ,
2018-12-26 21:23:25 +00:00
sge_base + page_entry * 8 + 0 ) ;
2018-06-26 21:47:29 +00:00
/* uint32_t prd_control = ldl_le_phys(&address_space_memory,
2018-12-26 21:23:25 +00:00
sge_base + page_entry * 8 + 4 ) ; */
2018-03-24 08:04:15 +00:00
2018-12-26 21:23:25 +00:00
hwaddr paddr = prd_address + offset_in_page ;
if ( bytes_to_copy > len ) {
bytes_to_copy = len ;
}
assert ( paddr + bytes_to_copy < memory_region_size ( d - > ram ) ) ;
2018-03-24 08:04:15 +00:00
if ( dir ) {
2018-12-26 21:23:25 +00:00
memcpy ( & d - > ram_ptr [ paddr ] , ptr , bytes_to_copy ) ;
memory_region_set_dirty ( d - > ram , paddr , bytes_to_copy ) ;
2018-03-24 08:04:15 +00:00
} else {
2018-12-26 21:23:25 +00:00
memcpy ( ptr , & d - > ram_ptr [ paddr ] , bytes_to_copy ) ;
2018-03-24 08:04:15 +00:00
}
2018-12-26 21:23:25 +00:00
ptr + = bytes_to_copy ;
len - = bytes_to_copy ;
/* After the first iteration, we are page aligned */
page_entry + = 1 ;
bytes_to_copy = TARGET_PAGE_SIZE ;
offset_in_page = 0 ;
2018-03-24 08:04:15 +00:00
}
}
2018-06-26 21:47:29 +00:00
static void gp_scratch_rw ( void * opaque ,
uint8_t * ptr ,
uint32_t addr ,
size_t len ,
bool dir )
2018-03-24 08:04:15 +00:00
{
MCPXAPUState * d = opaque ;
2018-12-26 21:23:25 +00:00
scatter_gather_rw ( d , d - > regs [ NV_PAPU_GPSADDR ] , d - > regs [ NV_PAPU_GPSMAXSGE ] ,
ptr , addr , len , dir ) ;
2018-03-24 08:04:15 +00:00
}
2018-06-26 21:47:29 +00:00
static void ep_scratch_rw ( void * opaque ,
uint8_t * ptr ,
uint32_t addr ,
size_t len ,
bool dir )
2018-03-24 08:04:15 +00:00
{
MCPXAPUState * d = opaque ;
2018-12-26 21:23:25 +00:00
scatter_gather_rw ( d , d - > regs [ NV_PAPU_EPSADDR ] , d - > regs [ NV_PAPU_EPSMAXSGE ] ,
ptr , addr , len , dir ) ;
2018-03-24 08:04:15 +00:00
}
2018-12-28 07:10:36 +00:00
static uint32_t circular_scatter_gather_rw ( MCPXAPUState * d ,
hwaddr sge_base ,
unsigned int max_sge ,
uint8_t * ptr ,
uint32_t base , uint32_t end ,
uint32_t cur ,
size_t len ,
bool dir )
{
while ( len > 0 ) {
unsigned int bytes_to_copy = end - cur ;
if ( bytes_to_copy > len ) {
bytes_to_copy = len ;
}
MCPX_DPRINTF ( " circular scatter gather %s in range 0x%x - 0x%x at 0x%x of length 0x%x / 0x%lx bytes \n " ,
dir ? " write " : " read " , base , end , cur , bytes_to_copy , len ) ;
assert ( ( cur > = base ) & & ( ( cur + bytes_to_copy ) < = end ) ) ;
scatter_gather_rw ( d , sge_base , max_sge , ptr , cur , bytes_to_copy , dir ) ;
ptr + = bytes_to_copy ;
len - = bytes_to_copy ;
/* After the first iteration we might have to wrap */
cur + = bytes_to_copy ;
if ( cur > = end ) {
assert ( cur = = end ) ;
cur = base ;
}
}
return cur ;
}
static void gp_fifo_rw ( void * opaque , uint8_t * ptr ,
unsigned int index , size_t len ,
bool dir )
{
MCPXAPUState * d = opaque ;
uint32_t base ;
uint32_t end ;
hwaddr cur_reg ;
if ( dir ) {
assert ( index < GP_OUTPUT_FIFO_COUNT ) ;
base = GET_MASK ( d - > regs [ NV_PAPU_GPOFBASE0 + 0x10 * index ] ,
NV_PAPU_GPOFBASE0_VALUE ) ;
end = GET_MASK ( d - > regs [ NV_PAPU_GPOFEND0 + 0x10 * index ] ,
NV_PAPU_GPOFEND0_VALUE ) ;
cur_reg = NV_PAPU_GPOFCUR0 + 0x10 * index ;
} else {
assert ( index < GP_INPUT_FIFO_COUNT ) ;
base = GET_MASK ( d - > regs [ NV_PAPU_GPIFBASE0 + 0x10 * index ] ,
NV_PAPU_GPOFBASE0_VALUE ) ;
end = GET_MASK ( d - > regs [ NV_PAPU_GPIFEND0 + 0x10 * index ] ,
NV_PAPU_GPOFEND0_VALUE ) ;
cur_reg = NV_PAPU_GPIFCUR0 + 0x10 * index ;
}
uint32_t cur = GET_MASK ( d - > regs [ cur_reg ] , NV_PAPU_GPOFCUR0_VALUE ) ;
/* DSP hangs if current >= end; but forces current >= base */
assert ( cur < end ) ;
if ( cur < base ) {
cur = base ;
}
cur = circular_scatter_gather_rw ( d ,
d - > regs [ NV_PAPU_GPFADDR ] , d - > regs [ NV_PAPU_GPFMAXSGE ] ,
ptr , base , end , cur , len , dir ) ;
SET_MASK ( d - > regs [ cur_reg ] , NV_PAPU_GPOFCUR0_VALUE , cur ) ;
}
static void ep_fifo_rw ( void * opaque , uint8_t * ptr ,
unsigned int index , size_t len ,
bool dir )
{
MCPXAPUState * d = opaque ;
uint32_t base ;
uint32_t end ;
hwaddr cur_reg ;
if ( dir ) {
assert ( index < EP_OUTPUT_FIFO_COUNT ) ;
base = GET_MASK ( d - > regs [ NV_PAPU_EPOFBASE0 + 0x10 * index ] ,
NV_PAPU_GPOFBASE0_VALUE ) ;
end = GET_MASK ( d - > regs [ NV_PAPU_EPOFEND0 + 0x10 * index ] ,
NV_PAPU_GPOFEND0_VALUE ) ;
cur_reg = NV_PAPU_EPOFCUR0 + 0x10 * index ;
} else {
assert ( index < EP_INPUT_FIFO_COUNT ) ;
base = GET_MASK ( d - > regs [ NV_PAPU_EPIFBASE0 + 0x10 * index ] ,
NV_PAPU_GPOFBASE0_VALUE ) ;
end = GET_MASK ( d - > regs [ NV_PAPU_EPIFEND0 + 0x10 * index ] ,
NV_PAPU_GPOFEND0_VALUE ) ;
cur_reg = NV_PAPU_EPIFCUR0 + 0x10 * index ;
}
uint32_t cur = GET_MASK ( d - > regs [ cur_reg ] , NV_PAPU_GPOFCUR0_VALUE ) ;
/* DSP hangs if current >= end; but forces current >= base */
assert ( cur < end ) ;
if ( cur < base ) {
cur = base ;
}
cur = circular_scatter_gather_rw ( d ,
d - > regs [ NV_PAPU_EPFADDR ] , d - > regs [ NV_PAPU_EPFMAXSGE ] ,
ptr , base , end , cur , len , dir ) ;
SET_MASK ( d - > regs [ cur_reg ] , NV_PAPU_GPOFCUR0_VALUE , cur ) ;
}
2018-03-24 08:04:15 +00:00
static void proc_rst_write ( DSPState * dsp , uint32_t oldval , uint32_t val )
{
if ( ! ( val & NV_PAPU_GPRST_GPRST ) | | ! ( val & NV_PAPU_GPRST_GPDSPRST ) ) {
dsp_reset ( dsp ) ;
2018-06-26 21:47:29 +00:00
} else if (
( ! ( oldval & NV_PAPU_GPRST_GPRST ) | | ! ( oldval & NV_PAPU_GPRST_GPDSPRST ) )
& & ( ( val & NV_PAPU_GPRST_GPRST ) & & ( val & NV_PAPU_GPRST_GPDSPRST ) ) ) {
2018-03-24 08:04:15 +00:00
dsp_bootstrap ( dsp ) ;
}
}
/* Global Processor - programmable DSP */
static uint64_t gp_read ( void * opaque ,
2018-06-26 21:47:29 +00:00
hwaddr addr ,
unsigned int size )
2018-03-24 08:04:15 +00:00
{
MCPXAPUState * d = opaque ;
2018-07-21 21:19:15 +00:00
assert ( size = = 4 ) ;
assert ( addr % 4 = = 0 ) ;
uint64_t r = 0 ;
switch ( addr ) {
case NV_PAPU_GPXMEM . . . NV_PAPU_GPXMEM + 0x1000 * 4 - 1 : {
uint32_t xaddr = ( addr - NV_PAPU_GPXMEM ) / 4 ;
r = dsp_read_memory ( d - > gp . dsp , ' X ' , xaddr ) ;
break ;
}
case NV_PAPU_GPMIXBUF . . . NV_PAPU_GPMIXBUF + 0x400 * 4 - 1 : {
uint32_t xaddr = ( addr - NV_PAPU_GPMIXBUF ) / 4 ;
2019-02-11 19:05:32 +00:00
r = dsp_read_memory ( d - > gp . dsp , ' X ' , GP_DSP_MIXBUF_BASE + xaddr ) ;
2018-07-21 21:19:15 +00:00
break ;
}
case NV_PAPU_GPYMEM . . . NV_PAPU_GPYMEM + 0x800 * 4 - 1 : {
uint32_t yaddr = ( addr - NV_PAPU_GPYMEM ) / 4 ;
r = dsp_read_memory ( d - > gp . dsp , ' Y ' , yaddr ) ;
break ;
}
case NV_PAPU_GPPMEM . . . NV_PAPU_GPPMEM + 0x1000 * 4 - 1 : {
uint32_t paddr = ( addr - NV_PAPU_GPPMEM ) / 4 ;
r = dsp_read_memory ( d - > gp . dsp , ' P ' , paddr ) ;
break ;
}
default :
r = d - > gp . regs [ addr ] ;
break ;
}
2018-03-24 08:04:15 +00:00
MCPX_DPRINTF ( " mcpx apu GP: read [0x%llx] -> 0x%llx \n " , addr , r ) ;
return r ;
}
2018-06-26 21:47:29 +00:00
2018-03-24 08:04:15 +00:00
static void gp_write ( void * opaque , hwaddr addr ,
uint64_t val , unsigned int size )
{
MCPXAPUState * d = opaque ;
2018-07-21 21:19:15 +00:00
assert ( size = = 4 ) ;
assert ( addr % 4 = = 0 ) ;
2018-03-24 08:04:15 +00:00
MCPX_DPRINTF ( " mcpx apu GP: [0x%llx] = 0x%llx \n " , addr , val ) ;
switch ( addr ) {
2018-07-21 21:19:15 +00:00
case NV_PAPU_GPXMEM . . . NV_PAPU_GPXMEM + 0x1000 * 4 - 1 : {
uint32_t xaddr = ( addr - NV_PAPU_GPXMEM ) / 4 ;
dsp_write_memory ( d - > gp . dsp , ' X ' , xaddr , val ) ;
break ;
}
case NV_PAPU_GPMIXBUF . . . NV_PAPU_GPMIXBUF + 0x400 * 4 - 1 : {
uint32_t xaddr = ( addr - NV_PAPU_GPMIXBUF ) / 4 ;
2019-02-11 19:05:32 +00:00
dsp_write_memory ( d - > gp . dsp , ' X ' , GP_DSP_MIXBUF_BASE + xaddr , val ) ;
2018-07-21 21:19:15 +00:00
break ;
}
case NV_PAPU_GPYMEM . . . NV_PAPU_GPYMEM + 0x800 * 4 - 1 : {
uint32_t yaddr = ( addr - NV_PAPU_GPYMEM ) / 4 ;
dsp_write_memory ( d - > gp . dsp , ' Y ' , yaddr , val ) ;
break ;
}
case NV_PAPU_GPPMEM . . . NV_PAPU_GPPMEM + 0x1000 * 4 - 1 : {
uint32_t paddr = ( addr - NV_PAPU_GPPMEM ) / 4 ;
dsp_write_memory ( d - > gp . dsp , ' P ' , paddr , val ) ;
break ;
}
2018-03-24 08:04:15 +00:00
case NV_PAPU_GPRST :
proc_rst_write ( d - > gp . dsp , d - > gp . regs [ NV_PAPU_GPRST ] , val ) ;
d - > gp . regs [ NV_PAPU_GPRST ] = val ;
break ;
default :
d - > gp . regs [ addr ] = val ;
break ;
}
}
2018-06-26 21:47:29 +00:00
2018-03-24 08:04:15 +00:00
static const MemoryRegionOps gp_ops = {
. read = gp_read ,
. write = gp_write ,
} ;
/* Encode Processor - encoding DSP */
static uint64_t ep_read ( void * opaque ,
2018-06-26 21:47:29 +00:00
hwaddr addr ,
unsigned int size )
2018-03-24 08:04:15 +00:00
{
MCPXAPUState * d = opaque ;
2018-07-21 21:19:03 +00:00
assert ( size = = 4 ) ;
assert ( addr % 4 = = 0 ) ;
2018-06-26 21:47:29 +00:00
2018-07-21 21:19:03 +00:00
uint64_t r = 0 ;
2018-03-24 08:04:15 +00:00
switch ( addr ) {
2018-07-21 21:19:03 +00:00
case NV_PAPU_EPXMEM . . . NV_PAPU_EPXMEM + 0xC00 * 4 - 1 : {
2018-03-24 08:04:15 +00:00
uint32_t xaddr = ( addr - NV_PAPU_EPXMEM ) / 4 ;
r = dsp_read_memory ( d - > ep . dsp , ' X ' , xaddr ) ;
break ;
}
2018-07-21 21:19:03 +00:00
case NV_PAPU_EPYMEM . . . NV_PAPU_EPYMEM + 0x100 * 4 - 1 : {
2018-03-24 08:04:15 +00:00
uint32_t yaddr = ( addr - NV_PAPU_EPYMEM ) / 4 ;
r = dsp_read_memory ( d - > ep . dsp , ' Y ' , yaddr ) ;
break ;
}
2018-07-21 21:19:03 +00:00
case NV_PAPU_EPPMEM . . . NV_PAPU_EPPMEM + 0x1000 * 4 - 1 : {
2018-03-24 08:04:15 +00:00
uint32_t paddr = ( addr - NV_PAPU_EPPMEM ) / 4 ;
r = dsp_read_memory ( d - > ep . dsp , ' P ' , paddr ) ;
break ;
}
default :
r = d - > ep . regs [ addr ] ;
break ;
}
MCPX_DPRINTF ( " mcpx apu EP: read [0x%llx] -> 0x%llx \n " , addr , r ) ;
return r ;
}
2018-06-26 21:47:29 +00:00
2018-03-24 08:04:15 +00:00
static void ep_write ( void * opaque , hwaddr addr ,
uint64_t val , unsigned int size )
{
MCPXAPUState * d = opaque ;
2018-07-21 21:19:03 +00:00
assert ( size = = 4 ) ;
assert ( addr % 4 = = 0 ) ;
2018-03-24 08:04:15 +00:00
MCPX_DPRINTF ( " mcpx apu EP: [0x%llx] = 0x%llx \n " , addr , val ) ;
switch ( addr ) {
2018-07-21 21:19:03 +00:00
case NV_PAPU_EPXMEM . . . NV_PAPU_EPXMEM + 0xC00 * 4 - 1 : {
uint32_t xaddr = ( addr - NV_PAPU_EPXMEM ) / 4 ;
dsp_write_memory ( d - > ep . dsp , ' X ' , xaddr , val ) ;
2018-03-24 08:04:15 +00:00
break ;
}
2018-07-21 21:19:03 +00:00
case NV_PAPU_EPYMEM . . . NV_PAPU_EPYMEM + 0x100 * 4 - 1 : {
uint32_t yaddr = ( addr - NV_PAPU_EPYMEM ) / 4 ;
dsp_write_memory ( d - > ep . dsp , ' Y ' , yaddr , val ) ;
2018-03-24 08:04:15 +00:00
break ;
}
2018-07-21 21:19:03 +00:00
case NV_PAPU_EPPMEM . . . NV_PAPU_EPPMEM + 0x1000 * 4 - 1 : {
uint32_t paddr = ( addr - NV_PAPU_EPPMEM ) / 4 ;
dsp_write_memory ( d - > ep . dsp , ' P ' , paddr , val ) ;
2018-03-24 08:04:15 +00:00
break ;
}
case NV_PAPU_EPRST :
proc_rst_write ( d - > ep . dsp , d - > ep . regs [ NV_PAPU_EPRST ] , val ) ;
d - > ep . regs [ NV_PAPU_EPRST ] = val ;
break ;
default :
d - > ep . regs [ addr ] = val ;
break ;
}
}
2018-06-26 21:47:29 +00:00
2018-03-24 08:04:15 +00:00
static const MemoryRegionOps ep_ops = {
. read = ep_read ,
. write = ep_write ,
} ;
2019-07-12 18:20:39 +00:00
#if 0
# include "adpcm_block.h"
2019-07-08 20:44:30 +00:00
static hwaddr get_data_ptr ( hwaddr sge_base , unsigned int max_sge , uint32_t addr ) {
unsigned int entry = addr / TARGET_PAGE_SIZE ;
assert ( entry < = max_sge ) ;
2019-07-08 21:14:15 +00:00
uint32_t prd_address = ldl_le_phys ( & address_space_memory , sge_base + entry * 4 * 2 ) ;
// uint32_t prd_control = ldl_le_phys(&address_space_memory, sge_base + entry*4*2 + 4);
2019-07-12 16:58:29 +00:00
MCPX_DPRINTF ( " Addr: 0x%08X, control: 0x%08X \n " , prd_address , prd_control ) ;
2019-07-08 20:44:30 +00:00
return prd_address + addr % TARGET_PAGE_SIZE ;
}
static float step_envelope ( MCPXAPUState * d , unsigned int v , uint32_t reg_0 , uint32_t reg_a , uint32_t rr_reg , uint32_t rr_mask , uint32_t lvl_reg , uint32_t lvl_mask , uint32_t count_mask , uint32_t cur_mask ) {
2019-07-12 17:17:25 +00:00
uint8_t cur = voice_get_mask ( d , v , NV_PAVS_VOICE_PAR_STATE , cur_mask ) ;
switch ( cur ) {
case 0 : // Off
voice_set_mask ( d , v , NV_PAVS_VOICE_CUR_ECNT , count_mask , 0 ) ;
voice_set_mask ( d , v , lvl_reg , lvl_mask , 0xFF ) ;
return 1.0f ;
case 1 : { // Delay
uint16_t count = voice_get_mask ( d , v , NV_PAVS_VOICE_CUR_ECNT , count_mask ) ;
voice_set_mask ( d , v , lvl_reg , lvl_mask , 0x00 ) ; // FIXME: Confirm this?
if ( count = = 0 ) {
cur + + ;
voice_set_mask ( d , v , NV_PAVS_VOICE_PAR_STATE , cur_mask , cur ) ;
count = 0 ;
} else {
count - - ;
}
voice_set_mask ( d , v , NV_PAVS_VOICE_CUR_ECNT , count_mask , count ) ;
break ;
2019-07-08 20:44:30 +00:00
}
2019-07-12 17:17:25 +00:00
case 2 : { // Attack
uint16_t count = voice_get_mask ( d , v , NV_PAVS_VOICE_CUR_ECNT , count_mask ) ;
uint16_t attack_rate = voice_get_mask ( d , v , reg_0 , NV_PAVS_VOICE_CFG_ENV0_EA_ATTACKRATE ) ;
float value ;
if ( attack_rate = = 0 ) {
//FIXME: [division by zero]
// Got crackling sound in hardware for amplitude env.
value = 255.0f ;
} else {
if ( count < = attack_rate ) {
value = ( count * 0xFF ) / attack_rate ;
} else {
//FIXME: Overflow in hardware
// The actual value seems to overflow, but not sure how
value = 255.0f ;
}
}
voice_set_mask ( d , v , lvl_reg , lvl_mask , value ) ;
//FIXME: Comparison could also be the other way around?! Test please.
if ( count = = ( attack_rate * 16 ) ) {
cur + + ;
voice_set_mask ( d , v , NV_PAVS_VOICE_PAR_STATE , cur_mask , cur ) ;
uint16_t hold_time = voice_get_mask ( d , v , reg_a , NV_PAVS_VOICE_CFG_ENVA_EA_HOLDTIME ) ;
count = hold_time * 16 ; //FIXME: Skip next phase if count is 0? [other instances too]
2019-07-08 20:44:30 +00:00
} else {
2019-07-12 17:17:25 +00:00
count + + ;
2019-07-08 20:44:30 +00:00
}
2019-07-12 17:17:25 +00:00
voice_set_mask ( d , v , NV_PAVS_VOICE_CUR_ECNT , count_mask , count ) ;
return value / 255.0f ;
2019-07-08 20:44:30 +00:00
}
2019-07-12 17:17:25 +00:00
case 3 : { // Hold
uint16_t count = voice_get_mask ( d , v , NV_PAVS_VOICE_CUR_ECNT , count_mask ) ;
voice_set_mask ( d , v , lvl_reg , lvl_mask , 0xFF ) ;
if ( count = = 0 ) {
cur + + ;
voice_set_mask ( d , v , NV_PAVS_VOICE_PAR_STATE , cur_mask , cur ) ;
uint16_t decay_rate = voice_get_mask ( d , v , reg_a , NV_PAVS_VOICE_CFG_ENVA_EA_DECAYRATE ) ;
count = decay_rate * 16 ;
} else {
count - - ;
}
voice_set_mask ( d , v , NV_PAVS_VOICE_CUR_ECNT , count_mask , count ) ;
return 1.0f ;
2019-07-08 20:44:30 +00:00
}
2019-07-12 17:17:25 +00:00
case 4 : { // Decay
uint16_t count = voice_get_mask ( d , v , NV_PAVS_VOICE_CUR_ECNT , count_mask ) ;
2019-07-08 20:44:30 +00:00
uint16_t decay_rate = voice_get_mask ( d , v , reg_a , NV_PAVS_VOICE_CFG_ENVA_EA_DECAYRATE ) ;
2019-07-12 17:17:25 +00:00
uint8_t sustain_level = voice_get_mask ( d , v , reg_a , NV_PAVS_VOICE_CFG_ENVA_EA_SUSTAINLEVEL ) ;
float value ;
if ( decay_rate = = 0 ) {
//FIXME: [division by zero]
// Not tested on hardware
value = 0.0f ;
} else {
//FIXME: This formula and threshold is not accurate, but I can't get it any better for now
value = 255.0f * powf ( 0.99988799f , ( decay_rate * 16 - count ) * 4096 / decay_rate ) ;
}
if ( value < = ( sustain_level + 0.2f ) | | ( value > 255.0f ) ) {
//FIXME: Should we still update lvl?
cur + + ;
voice_set_mask ( d , v , NV_PAVS_VOICE_PAR_STATE , cur_mask , cur ) ;
} else {
count - - ;
voice_set_mask ( d , v , NV_PAVS_VOICE_CUR_ECNT , count_mask , count ) ;
voice_set_mask ( d , v , lvl_reg , lvl_mask , value ) ;
}
return value / 255.0f ;
2019-07-08 20:44:30 +00:00
}
2019-07-12 17:17:25 +00:00
case 5 : { // Sustain
uint8_t sustain_level = voice_get_mask ( d , v , reg_a , NV_PAVS_VOICE_CFG_ENVA_EA_SUSTAINLEVEL ) ;
voice_set_mask ( d , v , NV_PAVS_VOICE_CUR_ECNT , count_mask , 0x00 ) ; // FIXME: is this only set to 0 once or forced to zero?
voice_set_mask ( d , v , lvl_reg , lvl_mask , sustain_level ) ;
return sustain_level / 255.0f ;
2019-07-08 20:44:30 +00:00
}
2019-07-12 17:17:25 +00:00
case 6 : { // Release
uint16_t release_rate = voice_get_mask ( d , v , rr_reg , rr_mask ) ;
uint16_t count = voice_get_mask ( d , v , NV_PAVS_VOICE_CUR_ECNT , count_mask ) ;
2019-07-08 20:44:30 +00:00
count - - ;
voice_set_mask ( d , v , NV_PAVS_VOICE_CUR_ECNT , count_mask , count ) ;
2019-07-12 17:17:25 +00:00
uint8_t lvl = voice_get_mask ( d , v , lvl_reg , lvl_mask ) ;
float value = count * lvl / ( release_rate * 16 ) ;
if ( count = = 0 ) {
//FIXME: What to do now?!
2019-07-08 20:44:30 +00:00
#if 0 // Hack so we don't assert
2019-07-12 17:17:25 +00:00
cur + + ; // FIXME: Does this happen?!
voice_set_mask ( d , v , NV_PAVS_VOICE_PAR_STATE , cur_mask , cur ) ;
2019-07-08 20:44:30 +00:00
# else
2019-07-12 17:17:25 +00:00
voice_set_mask ( d , v , NV_PAVS_VOICE_PAR_STATE , cur_mask , 0x0 ) ; // Is this correct? FIXME: Turn off voice?
2019-07-08 20:44:30 +00:00
# endif
2019-07-12 17:17:25 +00:00
}
return value / 255.0f ;
2019-07-08 20:44:30 +00:00
}
2019-07-12 17:17:25 +00:00
case 7 : // Force release
assert ( false ) ; //FIXME: This mode is not understood yet
return 1.0f ;
default :
fprintf ( stderr , " Unknown envelope state 0x%x \n " , cur ) ;
assert ( false ) ;
}
return 0 ;
2019-07-08 20:44:30 +00:00
}
2019-07-12 18:20:39 +00:00
# endif
2019-07-08 20:44:30 +00:00
2019-02-11 19:05:37 +00:00
static void process_voice ( MCPXAPUState * d ,
int32_t mixbins [ NUM_MIXBINS ] [ NUM_SAMPLES_PER_FRAME ] ,
uint32_t voice )
{
2019-07-12 18:20:39 +00:00
#if 0
2019-07-08 21:14:15 +00:00
uint32_t v = voice ;
2019-07-08 20:44:30 +00:00
int32_t samples [ 2 ] [ 0x20 ] = { 0 } ;
float ea_value = step_envelope ( d , v , NV_PAVS_VOICE_CFG_ENV0 , NV_PAVS_VOICE_CFG_ENVA , NV_PAVS_VOICE_TAR_LFO_ENV , NV_PAVS_VOICE_TAR_LFO_ENV_EA_RELEASERATE , NV_PAVS_VOICE_PAR_OFFSET , NV_PAVS_VOICE_PAR_OFFSET_EALVL , NV_PAVS_VOICE_CUR_ECNT_EACOUNT , NV_PAVS_VOICE_PAR_STATE_EACUR ) ;
float ef_value = step_envelope ( d , v , NV_PAVS_VOICE_CFG_ENV1 , NV_PAVS_VOICE_CFG_ENVF , NV_PAVS_VOICE_CFG_MISC , NV_PAVS_VOICE_CFG_MISC_EF_RELEASERATE , NV_PAVS_VOICE_PAR_NEXT , NV_PAVS_VOICE_PAR_NEXT_EFLVL , NV_PAVS_VOICE_CUR_ECNT_EFCOUNT , NV_PAVS_VOICE_PAR_STATE_EFCUR ) ;
assert ( ea_value > = 0.0f ) ;
assert ( ea_value < = 1.0f ) ;
assert ( ef_value > = 0.0f ) ;
assert ( ef_value < = 1.0f ) ;
int16_t p = voice_get_mask ( d , v , NV_PAVS_VOICE_TAR_PITCH_LINK , NV_PAVS_VOICE_TAR_PITCH_LINK_PITCH ) ;
int8_t pm = voice_get_mask ( d , v , NV_PAVS_VOICE_CFG_ENV0 , NV_PAVS_VOICE_CFG_ENV0_EF_PITCHSCALE ) ;
float rate = powf ( 2.0f , ( p + pm * 32 * ef_value ) / 4096.0f ) ;
2019-07-12 16:58:29 +00:00
MCPX_DPRINTF ( " Got %f \n " , rate * 48000.0f ) ;
2019-07-08 20:44:30 +00:00
float overdrive = 1.0f ; //FIXME: This is just a hack because our APU runs too rarely
//NV_PAVS_VOICE_PAR_OFFSET_CBO
bool stereo = voice_get_mask ( d , v , NV_PAVS_VOICE_CFG_FMT , NV_PAVS_VOICE_CFG_FMT_STEREO ) ;
unsigned int channels = stereo ? 2 : 1 ;
unsigned int sample_size = voice_get_mask ( d , v , NV_PAVS_VOICE_CFG_FMT , NV_PAVS_VOICE_CFG_FMT_SAMPLE_SIZE ) ;
// B8, B16, ADPCM, B32
unsigned int container_sizes [ 4 ] = { 1 , 2 , 0 , 4 } ;
unsigned int container_size_index = voice_get_mask ( d , v , NV_PAVS_VOICE_CFG_FMT , NV_PAVS_VOICE_CFG_FMT_CONTAINER_SIZE ) ;
//FIXME: Move down, but currently debug code depends on this
unsigned int container_size = container_sizes [ container_size_index ] ;
bool stream = voice_get_mask ( d , v , NV_PAVS_VOICE_CFG_FMT , NV_PAVS_VOICE_CFG_FMT_DATA_TYPE ) ;
assert ( ! stream ) ;
bool paused = voice_get_mask ( d , v , NV_PAVS_VOICE_PAR_STATE , NV_PAVS_VOICE_PAR_STATE_PAUSED ) ;
bool loop = voice_get_mask ( d , v , NV_PAVS_VOICE_CFG_FMT , NV_PAVS_VOICE_CFG_FMT_LOOP ) ;
uint32_t ebo = voice_get_mask ( d , v , NV_PAVS_VOICE_PAR_NEXT , NV_PAVS_VOICE_PAR_NEXT_EBO ) ;
uint32_t cbo = voice_get_mask ( d , v , NV_PAVS_VOICE_PAR_OFFSET , NV_PAVS_VOICE_PAR_OFFSET_CBO ) ;
uint32_t lbo = voice_get_mask ( d , v , NV_PAVS_VOICE_CUR_PSH_SAMPLE , NV_PAVS_VOICE_CUR_PSH_SAMPLE_LBO ) ;
uint32_t ba = voice_get_mask ( d , v , NV_PAVS_VOICE_CUR_PSL_START , NV_PAVS_VOICE_CUR_PSL_START_BA ) ;
unsigned int samples_per_block = 1 + voice_get_mask ( d , v , NV_PAVS_VOICE_CFG_FMT , NV_PAVS_VOICE_CFG_FMT_SAMPLES_PER_BLOCK ) ;
// This is probably cleared when the first sample is played
//FIXME: How will this behave if CBO > EBO on first play?
//FIXME: How will this behave if paused?
voice_set_mask ( d , v , NV_PAVS_VOICE_PAR_STATE , NV_PAVS_VOICE_PAR_STATE_NEW_VOICE , 0 ) ;
for ( unsigned int i = 0 ; i < 0x20 ; i + + ) {
uint32_t sample_pos = ( uint32_t ) ( i * rate * overdrive ) ;
if ( ( cbo + sample_pos ) > ebo ) {
if ( ! loop ) {
// Set to safe state
cbo = ebo ; //FIXME: Will the hw do this?
//FIXME: Not sure if this happens.. needs a hwtest.
// Some RE also suggests that the voices will automaticly be removed from the list (!!!)
voice_set_mask ( d , v , NV_PAVS_VOICE_PAR_STATE , NV_PAVS_VOICE_PAR_STATE_ACTIVE_VOICE , 0 ) ;
break ;
}
// Now go to loop start
//FIXME: Make sure this logic still works for very high sample_pos greater than ebo
cbo + = sample_pos ;
cbo % = ebo + 1 ;
cbo + = lbo ;
cbo - = sample_pos ;
}
sample_pos + = cbo ;
assert ( sample_pos < = ebo ) ;
if ( container_size_index = = NV_PAVS_VOICE_CFG_FMT_CONTAINER_SIZE_ADPCM ) {
//FIXME: Not sure how this behaves otherwise
assert ( sample_size = = NV_PAVS_VOICE_CFG_FMT_SAMPLE_SIZE_S24 ) ;
unsigned int block_index = sample_pos / 65 ;
unsigned int block_position = sample_pos % 65 ;
2019-07-12 16:58:29 +00:00
MCPX_DPRINTF ( " ADPCM: %d + %d \n " , block_index , block_position ) ;
2019-07-08 20:44:30 +00:00
//FIXME: Remove this from the loop which collects required samples
// We can always just grab one or two blocks to get all required samples
if ( stereo ) {
assert ( samples_per_block = = 2 ) ;
// There are 65 samples per 72 byte block
uint32_t block [ 72 / 4 ] ;
uint32_t linear_addr = ba + block_index * 72 ;
// FIXME: Only load block data which will be used
for ( unsigned int word_index = 0 ; word_index < 18 ; word_index + + ) {
hwaddr addr = get_data_ptr ( d - > regs [ NV_PAPU_VPSGEADDR ] , 0xFFFFFFFF , linear_addr ) ;
2019-07-08 21:14:15 +00:00
block [ word_index ] = ldl_le_phys ( & address_space_memory , addr ) ;
2019-07-08 20:44:30 +00:00
linear_addr + = 4 ;
}
//FIXME: Used wrong sample format in my own decoder.. stupid me lol
int16_t tmp [ 2 ] ;
2019-07-08 21:14:15 +00:00
adpcm_decode_stereo_block ( & tmp [ 0 ] , & tmp [ 1 ] , ( uint8_t * ) block , block_position , block_position ) ;
2019-07-08 20:44:30 +00:00
samples [ 0 ] [ i ] = tmp [ 0 ] ;
samples [ 1 ] [ i ] = tmp [ 1 ] ;
} else {
assert ( samples_per_block = = 1 ) ;
// There are 65 samples per 36 byte block
uint32_t block [ 36 / 4 ] ;
uint32_t linear_addr = ba + block_index * 36 ;
for ( unsigned int word_index = 0 ; word_index < 9 ; word_index + + ) {
hwaddr addr = get_data_ptr ( d - > regs [ NV_PAPU_VPSGEADDR ] , 0xFFFFFFFF , linear_addr ) ;
2019-07-08 21:14:15 +00:00
block [ word_index ] = ldl_le_phys ( & address_space_memory , addr ) ;
2019-07-08 20:44:30 +00:00
linear_addr + = 4 ;
}
//FIXME: Used wrong sample format in my own decoder.. stupid me lol
int16_t tmp ;
2019-07-08 21:14:15 +00:00
adpcm_decode_mono_block ( & tmp , ( uint8_t * ) block , block_position , block_position ) ;
2019-07-08 20:44:30 +00:00
samples [ 0 ] [ i ] = tmp ;
}
} else {
unsigned int block_size = container_size ;
block_size * = samples_per_block ;
uint32_t linear_addr = ba + sample_pos * block_size ;
hwaddr addr = get_data_ptr ( d - > regs [ NV_PAPU_VPSGEADDR ] , 0xFFFFFFFF , linear_addr ) ;
//FIXME: Handle reading accross pages?!
2019-07-12 16:58:29 +00:00
MCPX_DPRINTF ( " Sampling from 0x%08X \n " , addr ) ;
2019-07-08 20:44:30 +00:00
// Get samples for this voice
for ( unsigned int channel = 0 ; channel < channels ; channel + + ) {
switch ( sample_size ) {
case NV_PAVS_VOICE_CFG_FMT_SAMPLE_SIZE_U8 :
2019-07-08 21:14:15 +00:00
samples [ channel ] [ i ] = ldub_phys ( & address_space_memory , addr ) ;
2019-07-08 20:44:30 +00:00
break ;
case NV_PAVS_VOICE_CFG_FMT_SAMPLE_SIZE_S16 :
2019-07-08 21:14:15 +00:00
samples [ channel ] [ i ] = ( int16_t ) lduw_le_phys ( & address_space_memory , addr ) ;
2019-07-08 20:44:30 +00:00
break ;
case NV_PAVS_VOICE_CFG_FMT_SAMPLE_SIZE_S24 :
2019-07-08 21:14:15 +00:00
samples [ channel ] [ i ] = ( int32_t ) ( ldl_le_phys ( & address_space_memory , addr ) < < 8 ) > > 8 ;
2019-07-08 20:44:30 +00:00
break ;
case NV_PAVS_VOICE_CFG_FMT_SAMPLE_SIZE_S32 :
2019-07-08 21:14:15 +00:00
samples [ channel ] [ i ] = ( int32_t ) ldl_le_phys ( & address_space_memory , addr ) ;
2019-07-08 20:44:30 +00:00
break ;
}
// Advance cursor to second channel for stereo
addr + = container_size ;
}
}
}
//FIXME: Decode voice volume and bins
int bin [ 8 ] = {
voice_get_mask ( d , v , NV_PAVS_VOICE_CFG_VBIN , NV_PAVS_VOICE_CFG_VBIN_V0BIN ) ,
voice_get_mask ( d , v , NV_PAVS_VOICE_CFG_VBIN , NV_PAVS_VOICE_CFG_VBIN_V1BIN ) ,
voice_get_mask ( d , v , NV_PAVS_VOICE_CFG_VBIN , NV_PAVS_VOICE_CFG_VBIN_V2BIN ) ,
voice_get_mask ( d , v , NV_PAVS_VOICE_CFG_VBIN , NV_PAVS_VOICE_CFG_VBIN_V3BIN ) ,
voice_get_mask ( d , v , NV_PAVS_VOICE_CFG_VBIN , NV_PAVS_VOICE_CFG_VBIN_V4BIN ) ,
voice_get_mask ( d , v , NV_PAVS_VOICE_CFG_VBIN , NV_PAVS_VOICE_CFG_VBIN_V5BIN ) ,
voice_get_mask ( d , v , NV_PAVS_VOICE_CFG_FMT , NV_PAVS_VOICE_CFG_FMT_V6BIN ) ,
voice_get_mask ( d , v , NV_PAVS_VOICE_CFG_FMT , NV_PAVS_VOICE_CFG_FMT_V7BIN )
} ;
uint16_t vol [ 8 ] = {
voice_get_mask ( d , v , NV_PAVS_VOICE_TAR_VOLA , NV_PAVS_VOICE_TAR_VOLA_VOLUME0 ) ,
voice_get_mask ( d , v , NV_PAVS_VOICE_TAR_VOLA , NV_PAVS_VOICE_TAR_VOLA_VOLUME1 ) ,
voice_get_mask ( d , v , NV_PAVS_VOICE_TAR_VOLB , NV_PAVS_VOICE_TAR_VOLB_VOLUME2 ) ,
voice_get_mask ( d , v , NV_PAVS_VOICE_TAR_VOLB , NV_PAVS_VOICE_TAR_VOLB_VOLUME3 ) ,
voice_get_mask ( d , v , NV_PAVS_VOICE_TAR_VOLC , NV_PAVS_VOICE_TAR_VOLC_VOLUME4 ) ,
voice_get_mask ( d , v , NV_PAVS_VOICE_TAR_VOLC , NV_PAVS_VOICE_TAR_VOLC_VOLUME5 ) ,
( voice_get_mask ( d , v , NV_PAVS_VOICE_TAR_VOLC , NV_PAVS_VOICE_TAR_VOLC_VOLUME6_B11_8 ) < < 8 ) |
( voice_get_mask ( d , v , NV_PAVS_VOICE_TAR_VOLB , NV_PAVS_VOICE_TAR_VOLB_VOLUME6_B7_4 ) < < 4 ) |
voice_get_mask ( d , v , NV_PAVS_VOICE_TAR_VOLA , NV_PAVS_VOICE_TAR_VOLA_VOLUME6_B3_0 ) ,
( voice_get_mask ( d , v , NV_PAVS_VOICE_TAR_VOLC , NV_PAVS_VOICE_TAR_VOLC_VOLUME7_B11_8 ) < < 8 ) |
( voice_get_mask ( d , v , NV_PAVS_VOICE_TAR_VOLB , NV_PAVS_VOICE_TAR_VOLB_VOLUME7_B7_4 ) < < 4 ) |
voice_get_mask ( d , v , NV_PAVS_VOICE_TAR_VOLA , NV_PAVS_VOICE_TAR_VOLA_VOLUME7_B3_0 ) ,
} ;
if ( paused ) {
assert ( sizeof ( samples ) = = 0x20 * 4 * 2 ) ;
memset ( samples , 0x00 , sizeof ( samples ) ) ;
} else {
cbo + = 0x20 * rate * overdrive ;
voice_set_mask ( d , v , NV_PAVS_VOICE_PAR_OFFSET , NV_PAVS_VOICE_PAR_OFFSET_CBO , cbo ) ;
}
// Apply the amplitude envelope
//FIXME: Figure out when exactly and how exactly this is actually done
for ( unsigned int j = 0 ; j < channels ; j + + ) {
for ( unsigned int i = 0 ; i < 0x20 ; i + + ) {
samples [ j ] [ i ] * = ea_value ;
}
}
//FIXME: If phase negations means to flip the signal upside down
// we should modify volume of bin6 and bin7 here.
// Mix samples into voice bins
for ( unsigned int j = 0 ; j < 8 ; j + + ) {
2019-07-12 16:58:29 +00:00
MCPX_DPRINTF ( " Adding voice 0x%04X to bin %d [Rate %.2f, Volume 0x%03X] sample %d at %d [%.2fs] \n " , v , bin [ j ] , rate , vol [ j ] , samples [ 0 ] , cbo , cbo / ( rate * 48000.0f ) ) ;
2019-07-08 20:44:30 +00:00
for ( unsigned int i = 0 ; i < 0x20 ; i + + ) {
//FIXME: how is the volume added?
//FIXME: What happens to the other channel? Is this behaviour correct?
2019-07-08 21:14:15 +00:00
mixbins [ bin [ j ] ] [ i ] + = ( 0xFFF - vol [ j ] ) * samples [ j % channels ] [ i ] / 0xFFF ;
2019-07-08 20:44:30 +00:00
}
}
2019-07-12 18:20:39 +00:00
# endif
2019-02-11 19:05:37 +00:00
}
/* This routine must run at 1500 Hz */
2018-03-24 08:04:15 +00:00
/* TODO: this should be on a thread so it waits on the voice lock */
static void se_frame ( void * opaque )
{
MCPXAPUState * d = opaque ;
2019-02-11 19:05:37 +00:00
int mixbin ;
int sample ;
2018-03-24 08:04:15 +00:00
timer_mod ( d - > se . frame_timer , qemu_clock_get_ms ( QEMU_CLOCK_VIRTUAL ) + 10 ) ;
MCPX_DPRINTF ( " mcpx frame ping \n " ) ;
2019-02-11 19:05:37 +00:00
/* Buffer for all mixbins for this frame */
int32_t mixbins [ NUM_MIXBINS ] [ NUM_SAMPLES_PER_FRAME ] = { 0 } ;
/* Process all voices, mixing each into the affected MIXBINs */
2018-03-24 08:04:15 +00:00
int list ;
2018-06-26 21:47:29 +00:00
for ( list = 0 ; list < 3 ; list + + ) {
2018-03-24 08:04:15 +00:00
hwaddr top , current , next ;
top = voice_list_regs [ list ] . top ;
current = voice_list_regs [ list ] . current ;
next = voice_list_regs [ list ] . next ;
d - > regs [ current ] = d - > regs [ top ] ;
MCPX_DPRINTF ( " list %d current voice %d \n " , list , d - > regs [ current ] ) ;
while ( d - > regs [ current ] ! = 0xFFFF ) {
d - > regs [ next ] = voice_get_mask ( d , d - > regs [ current ] ,
NV_PAVS_VOICE_TAR_PITCH_LINK ,
NV_PAVS_VOICE_TAR_PITCH_LINK_NEXT_VOICE_HANDLE ) ;
if ( ! voice_get_mask ( d , d - > regs [ current ] ,
NV_PAVS_VOICE_PAR_STATE ,
NV_PAVS_VOICE_PAR_STATE_ACTIVE_VOICE ) ) {
MCPX_DPRINTF ( " voice %d not active...! \n " , d - > regs [ current ] ) ;
fe_method ( d , SE2FE_IDLE_VOICE , d - > regs [ current ] ) ;
2019-02-11 19:05:37 +00:00
} else {
process_voice ( d , mixbins , d - > regs [ current ] ) ;
2018-03-24 08:04:15 +00:00
}
MCPX_DPRINTF ( " next voice %d \n " , d - > regs [ next ] ) ;
d - > regs [ current ] = d - > regs [ next ] ;
}
}
2019-02-11 19:05:40 +00:00
# if GENERATE_MIXBIN_BEEP
/* Inject some audio to the mixbin for debugging.
* Signal is 1500 Hz sine wave , phase shifted by mixbin number . */
for ( mixbin = 0 ; mixbin < NUM_MIXBINS ; mixbin + + ) {
for ( sample = 0 ; sample < NUM_SAMPLES_PER_FRAME ; sample + + ) {
/* Avoid multiple of 1.0 / NUM_SAMPLES_PER_FRAME for phase shift,
* or waves cancel out */
float offset = sample / ( float ) NUM_SAMPLES_PER_FRAME -
mixbin / ( float ) ( NUM_SAMPLES_PER_FRAME + 1 ) ;
float wave = sinf ( offset * M_PI * 2.0f ) ;
mixbins [ mixbin ] [ sample ] + = wave * 0x3FFFFF ;
}
}
# endif
2019-02-11 19:05:37 +00:00
/* Write VP results to the GP DSP MIXBUF */
for ( mixbin = 0 ; mixbin < NUM_MIXBINS ; mixbin + + ) {
for ( sample = 0 ; sample < NUM_SAMPLES_PER_FRAME ; sample + + ) {
dsp_write_memory ( d - > gp . dsp ,
' X ' , GP_DSP_MIXBUF_BASE + mixbin * 0x20 + sample ,
mixbins [ mixbin ] [ sample ] & 0xFFFFFF ) ;
}
}
/* Kickoff DSP processing */
2018-03-24 08:04:15 +00:00
if ( ( d - > gp . regs [ NV_PAPU_GPRST ] & NV_PAPU_GPRST_GPRST )
& & ( d - > gp . regs [ NV_PAPU_GPRST ] & NV_PAPU_GPRST_GPDSPRST ) ) {
dsp_start_frame ( d - > gp . dsp ) ;
// hax
dsp_run ( d - > gp . dsp , 1000 ) ;
}
if ( ( d - > ep . regs [ NV_PAPU_EPRST ] & NV_PAPU_GPRST_GPRST )
& & ( d - > ep . regs [ NV_PAPU_EPRST ] & NV_PAPU_GPRST_GPDSPRST ) ) {
dsp_start_frame ( d - > ep . dsp ) ;
// hax
// dsp_run(d->ep.dsp, 1000);
}
}
2018-06-26 21:47:29 +00:00
static void mcpx_apu_realize ( PCIDevice * dev , Error * * errp )
2018-03-24 08:04:15 +00:00
{
MCPXAPUState * d = MCPX_APU_DEVICE ( dev ) ;
dev - > config [ PCI_INTERRUPT_PIN ] = 0x01 ;
memory_region_init_io ( & d - > mmio , OBJECT ( dev ) , & mcpx_apu_mmio_ops , d ,
" mcpx-apu-mmio " , 0x80000 ) ;
memory_region_init_io ( & d - > vp . mmio , OBJECT ( dev ) , & vp_ops , d ,
" mcpx-apu-vp " , 0x10000 ) ;
memory_region_add_subregion ( & d - > mmio , 0x20000 , & d - > vp . mmio ) ;
memory_region_init_io ( & d - > gp . mmio , OBJECT ( dev ) , & gp_ops , d ,
" mcpx-apu-gp " , 0x10000 ) ;
memory_region_add_subregion ( & d - > mmio , 0x30000 , & d - > gp . mmio ) ;
memory_region_init_io ( & d - > ep . mmio , OBJECT ( dev ) , & ep_ops , d ,
" mcpx-apu-ep " , 0x10000 ) ;
memory_region_add_subregion ( & d - > mmio , 0x50000 , & d - > ep . mmio ) ;
pci_register_bar ( & d - > dev , 0 , PCI_BASE_ADDRESS_SPACE_MEMORY , & d - > mmio ) ;
d - > se . frame_timer = timer_new_ms ( QEMU_CLOCK_VIRTUAL , se_frame , d ) ;
2018-12-28 07:22:25 +00:00
d - > gp . dsp = dsp_init ( d , gp_scratch_rw , gp_fifo_rw ) ;
d - > ep . dsp = dsp_init ( d , ep_scratch_rw , ep_fifo_rw ) ;
2018-03-24 08:04:15 +00:00
}
static void mcpx_apu_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_MCPX_APU ;
k - > revision = 210 ;
k - > class_id = PCI_CLASS_MULTIMEDIA_AUDIO ;
2018-06-26 21:47:29 +00:00
k - > realize = mcpx_apu_realize ;
2018-03-24 08:04:15 +00:00
dc - > desc = " MCPX Audio Processing Unit " ;
}
static const TypeInfo mcpx_apu_info = {
. name = " mcpx-apu " ,
. parent = TYPE_PCI_DEVICE ,
. instance_size = sizeof ( MCPXAPUState ) ,
. class_init = mcpx_apu_class_init ,
2018-06-26 21:47:29 +00:00
. interfaces = ( InterfaceInfo [ ] ) {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE } ,
{ } ,
} ,
2018-03-24 08:04:15 +00:00
} ;
static void mcpx_apu_register ( void )
{
type_register_static ( & mcpx_apu_info ) ;
}
2018-06-26 21:47:29 +00:00
type_init ( mcpx_apu_register ) ;
2018-12-26 21:23:25 +00:00
void mcpx_apu_init ( PCIBus * bus , int devfn , MemoryRegion * ram )
{
PCIDevice * dev = pci_create_simple ( bus , devfn , " mcpx-apu " ) ;
MCPXAPUState * d = MCPX_APU_DEVICE ( dev ) ;
/* Keep pointers to system memory */
d - > ram = ram ;
d - > ram_ptr = memory_region_get_ram_ptr ( d - > ram ) ;
}