diff --git a/arm11/source/hw/codec.c b/arm11/source/hw/codec.c new file mode 100755 index 0000000..04aa1e8 --- /dev/null +++ b/arm11/source/hw/codec.c @@ -0,0 +1,188 @@ +// Based on xerpi's CODEC driver for Linux +// Original comment follows: +/* + * nintendo3ds_codec.c + * + * Copyright (C) 2016 Sergi Granell (xerpi) + * Copyright (C) 2017 Paul LaMendola (paulguy) + * based on ad7879-spi.c + * + * Licensed under the GPL-2 or later. + */ + +#include +#include + +#include + +#include "hw/codec.h" +#include "hw/spi.h" + +#define CODEC_SPI_DEV (3) + +#define MAX_12BIT (BIT(12) - 1) +#define CPAD_THRESH (150) +#define CPAD_FACTOR (150) + +/* SPI stuff */ +static void CODEC_DualTX(u8 *tx0, u8 len0, u8 *tx1, u8 len1) +{ + SPI_XferInfo xfers[2]; + + xfers[0].buf = (u32*)tx0; + xfers[0].len = len0; + xfers[0].read = false; + + xfers[1].buf = (u32*)tx1; + xfers[1].len = len1; + xfers[1].read = false; + + SPI_DoXfer(CODEC_SPI_DEV, xfers, 2); +} + +static void CODEC_WriteRead(u8 *tx_buf, u8 tx_len, + u8 *rx_buf, u8 rx_len) +{ + SPI_XferInfo xfers[2]; + + xfers[0].buf = (u32*)tx_buf; + xfers[0].len = tx_len; + xfers[0].read = false; + + xfers[1].buf = (u32*)rx_buf; + xfers[1].len = rx_len; + xfers[1].read = true; + + SPI_DoXfer(CODEC_SPI_DEV, xfers, 2); +} + +static void CODEC_RegSelect(u8 reg) +{ + u8 buffer1[4]; + u8 buffer2[0x40]; + + buffer1[0] = 0; + buffer2[0] = reg; + + CODEC_DualTX(buffer1, 1, buffer2, 1); +} + +static u8 CODEC_RegRead(u8 offset) +{ + u8 buffer_wr[8]; + u8 buffer_rd[0x40]; + + buffer_wr[0] = 1 | (offset << 1); + + CODEC_WriteRead(buffer_wr, 1, buffer_rd, 1); + + return buffer_rd[0]; +} + +static void CODEC_RegWrite(u8 reg, u8 val) +{ + u8 buffer1[8]; + u8 buffer2[0x40]; + + buffer1[0] = (reg << 1); // Write + buffer2[0] = val; + + CODEC_DualTX(buffer1, 1, buffer2, 1); +} + +static void CODEC_RegReadBuf(u8 offset, void *buffer, u8 size) +{ + u8 buffer_wr[0x10]; + + buffer_wr[0] = 1 | (offset << 1); + + CODEC_WriteRead(buffer_wr, 1, buffer, size); +} + +static void CODEC_RegMask(u8 offset, u8 mask0, u8 mask1) +{ + u8 buffer1[4]; + u8 buffer2[0x40]; + + buffer1[0] = 1 | (offset << 1); + + CODEC_WriteRead(buffer1, 1, buffer2, 1); + + buffer1[0] = offset << 1; + buffer2[0] = (buffer2[0] & ~mask1) | (mask0 & mask1); + + CODEC_DualTX(buffer1, 1, buffer2, 1); +} + +void CODEC_Init(void) +{ + CODEC_RegSelect(0x67); + CODEC_RegWrite(0x24, 0x98); + CODEC_RegSelect(0x67); + CODEC_RegWrite(0x26, 0x00); + CODEC_RegSelect(0x67); + CODEC_RegWrite(0x25, 0x43); + CODEC_RegSelect(0x67); + CODEC_RegWrite(0x24, 0x18); + CODEC_RegSelect(0x67); + CODEC_RegWrite(0x17, 0x43); + CODEC_RegSelect(0x67); + CODEC_RegWrite(0x19, 0x69); + CODEC_RegSelect(0x67); + CODEC_RegWrite(0x1B, 0x80); + CODEC_RegSelect(0x67); + CODEC_RegWrite(0x27, 0x11); + CODEC_RegSelect(0x67); + CODEC_RegWrite(0x26, 0xEC); + CODEC_RegSelect(0x67); + CODEC_RegWrite(0x24, 0x18); + CODEC_RegSelect(0x67); + CODEC_RegWrite(0x25, 0x53); + + CODEC_RegSelect(0x67); + CODEC_RegMask(0x26, 0x80, 0x80); + CODEC_RegSelect(0x67); + CODEC_RegMask(0x24, 0x00, 0x80); + CODEC_RegSelect(0x67); + CODEC_RegMask(0x25, 0x10, 0x3C); +} + +static void CODEC_GetRawData(u8 *buffer) +{ + CODEC_RegSelect(0x67); + CODEC_RegRead(0x26); + CODEC_RegSelect(0xFB); + CODEC_RegReadBuf(1, buffer, 0x34); +} + +void CODEC_Get(CODEC_Input *input) +{ + u8 raw_data[0x34] = {0}; + s16 cpad_x, cpad_y; + bool ts_pressed; + + CODEC_GetRawData(raw_data); + + cpad_x = ((raw_data[0x24] << 8 | raw_data[0x25]) & 0xFFF) - 2048; + cpad_y = ((raw_data[0x14] << 8 | raw_data[0x15]) & 0xFFF) - 2048; + + // X axis is inverted + if (abs(cpad_x) > CPAD_THRESH) + input->cpad_x = -cpad_x / CPAD_FACTOR; + else + input->cpad_x = 0; + + if (abs(cpad_y) > CPAD_THRESH) + input->cpad_y = cpad_y / CPAD_FACTOR; + else + input->cpad_y = 0; + + ts_pressed = !(raw_data[0] & BIT(4)); + if (ts_pressed) { + input->ts_x = (raw_data[0] << 8) | raw_data[1]; + input->ts_y = (raw_data[10] << 8) | raw_data[11]; + } else { + input->ts_x = 0xFFFF; + input->ts_y = 0xFFFF; + } +} diff --git a/arm11/source/hw/codec.h b/arm11/source/hw/codec.h new file mode 100755 index 0000000..25e2a85 --- /dev/null +++ b/arm11/source/hw/codec.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#define CODEC_TOUCH_VALID(x) ((x) != 0xFFFFFFFF) + +typedef struct { + s16 cpad_x, cpad_y; + s16 ts_x, ts_y; +} CODEC_Input; + +void CODEC_Init(void); + +void CODEC_Get(CODEC_Input *input); diff --git a/arm11/source/hw/hid.c b/arm11/source/hw/hid.c new file mode 100755 index 0000000..d3d588a --- /dev/null +++ b/arm11/source/hw/hid.c @@ -0,0 +1,55 @@ +#include +#include +#include + +#include "hw/codec.h" +#include "hw/hid.h" +#include "hw/mcu.h" + +#define REG_HID (~(*(vu16*)(0x10146000)) & BUTTON_ANY) + +static u32 HID_ConvertCPAD(s16 cpad_x, s16 cpad_y) +{ + u32 ret = 0; + + switch(int_sign(cpad_x)) { + default: + break; + case 1: + ret |= BUTTON_RIGHT; + break; + case -1: + ret |= BUTTON_LEFT; + } + + switch(int_sign(cpad_y)) { + default: + break; + case 1: + ret |= BUTTON_UP; + break; + case -1: + ret |= BUTTON_DOWN; + } + + return ret; +} + +u64 HID_GetState(void) +{ + CODEC_Input codec; + u64 ret = 0; + + CODEC_Get(&codec); + + ret = REG_HID | MCU_GetSpecialHID(); + if (!(ret & BUTTON_ARROW)) + ret |= HID_ConvertCPAD(codec.cpad_x, codec.cpad_y); + + if (codec.ts_x <= 0xFFF) + ret |= BUTTON_TOUCH; + + ret |= (((u64)codec.ts_x << 16) | (u64)codec.ts_y) << 32; + + return ret; +} diff --git a/arm11/source/hw/hid.h b/arm11/source/hw/hid.h new file mode 100755 index 0000000..78cf360 --- /dev/null +++ b/arm11/source/hw/hid.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +u64 HID_GetState(void); diff --git a/arm11/source/hw/spi.c b/arm11/source/hw/spi.c new file mode 100755 index 0000000..58cc2f8 --- /dev/null +++ b/arm11/source/hw/spi.c @@ -0,0 +1,129 @@ +// Somewhat based on xerpi's SPI driver for Linux +// Original comment follows: +/* + * nintendo3ds_spi.c + * + * Copyright (C) 2016 Sergi Granell (xerpi) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +#include "hw/spi.h" + +#define CFG_SPI_CNT ((vu32*)0x101401C0) + +// TODO: CURRENTLY HARDCODED FOR DEVICE 3 (TOUCHSCREEN) +// IF ANY OTHER DEVICES ARE TO BE USED, ANOTHER BUS MUST +// BE ACCESSED, CHECK 3dbrew.org/wiki/SPI_{Registers, Services} +static const u32 SPI_Buses[] = { 0x10142800 }; +#define REG_SPI(b, n) (*(vu32*)(SPI_Buses[b] + (n))) + +#define REG_SPI_CNT REG_SPI(0, 0x00) +#define REG_SPI_DONE REG_SPI(0, 0x04) +#define REG_SPI_BLKLEN REG_SPI(0, 0x08) +#define REG_SPI_FIFO REG_SPI(0, 0x0C) +#define REG_SPI_STATUS REG_SPI(0, 0x10) + +#define SPI_CNT_BUSY BIT(15) +#define SPI_CNT_START BIT(15) + +#define SPI_CNT_READ (0) +#define SPI_CNT_WRITE BIT(13) + +#define SPI_CNT_RATE(n) (n) +#define SPI_CNT_CS(n) ((n) << 6) + +#define SPI_STAT_BUSY BIT(0) + +#define SPI_FIFO_WIDTH (32) + +static u8 SPI_GetDevSelect(u32 dev) +{ + static const u8 SPI_DevSelect[] = { 0, 1, 2, 0, 1, 2 }; + if (dev < countof(SPI_DevSelect)) { + return SPI_DevSelect[dev]; + } else { + return 0; + } +} + +static u8 SPI_GetDevBaudrate(u32 dev) +{ + static const u8 SPI_BaudRates[] = { 2, 0, 0, 5 }; + if (dev < countof(SPI_BaudRates)) { + return SPI_BaudRates[dev]; + } else { + return 0; + } +} + +static void SPI_WaitBusy(void) +{ + while(REG_SPI_CNT & SPI_CNT_BUSY); +} + +static void SPI_WaitFIFO(void) +{ + while(REG_SPI_STATUS & SPI_STAT_BUSY); +} + +static void SPI_Done(void) +{ + REG_SPI_DONE = 0; +} + +static void SPI_SingleXfer(u32 reg, bool read, u32 *buffer, u32 len) +{ + u32 pos = 0; + + REG_SPI_BLKLEN = len; + REG_SPI_CNT = reg | (read ? SPI_CNT_READ : SPI_CNT_WRITE) | SPI_CNT_START; + + do { + if ((pos % SPI_FIFO_WIDTH) == 0) + SPI_WaitFIFO(); + + if (read) { + buffer[pos / 4] = REG_SPI_FIFO; + } else { + REG_SPI_FIFO = buffer[pos / 4]; + } + + pos += 4; + } while(pos < len); +} + +int SPI_DoXfer(u32 dev, SPI_XferInfo *xfers, u32 xfer_cnt) +{ + u32 dev_cfg; + int baud, cs; + + baud = SPI_GetDevBaudrate(dev); + cs = SPI_GetDevSelect(dev); + dev_cfg = SPI_CNT_RATE(baud) | SPI_CNT_CS(cs); + + for (u32 i = 0; i < xfer_cnt; i++) { + SPI_XferInfo *xfer = &xfers[i]; + + SPI_WaitBusy(); + SPI_SingleXfer(dev_cfg, xfer->read, xfer->buf, xfer->len); + } + + SPI_WaitBusy(); + SPI_Done(); + return 0; +} + +void SPI_Init(void) +{ + // Hack: here all registers should be set to the "new" mode + // but GM9 uses the old interface to access NVRAM + // as such, only the bus used by CODEC will be set to new + // *CFG_SPI_CNT = 7; + *CFG_SPI_CNT = BIT(1); +} diff --git a/arm11/source/hw/spi.h b/arm11/source/hw/spi.h new file mode 100755 index 0000000..b2dc6fb --- /dev/null +++ b/arm11/source/hw/spi.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +typedef struct { + u32 *buf; + u8 len; + bool read; +} SPI_XferInfo; + +int SPI_DoXfer(u32 dev, SPI_XferInfo *xfer, u32 xfer_cnt); + +void SPI_Init(void); diff --git a/arm11/source/main.c b/arm11/source/main.c index 587d133..47dbf7c 100644 --- a/arm11/source/main.c +++ b/arm11/source/main.c @@ -1,12 +1,12 @@ -#include #include #include -#include +#include #include #include #include "arm/gic.h" +#include "hw/hid.h" #include "hw/gpulcd.h" #include "hw/i2c.h" #include "hw/mcu.h" @@ -15,7 +15,7 @@ static bool legacy = false; -#define REG_HID (~(*(vu16*)(0x10146000)) & BUTTON_ANY) +static GlobalSharedMemory SharedMemory_State; static const u8 brightness_lvls[] = { 0x10, 0x17, 0x1E, 0x25, 0x2C, 0x34, 0x3C, 0x44, @@ -23,7 +23,6 @@ static const u8 brightness_lvls[] = { 0x79, 0x8C, 0xA7, 0xD2 }; static int prev_bright_lvl = -1; -static vu32 global_hid_state = 0; void VBlank_Handler(u32 __attribute__((unused)) irqn) { @@ -38,8 +37,8 @@ void VBlank_Handler(u32 __attribute__((unused)) irqn) // the state should probably be stored on its own // setion without caching enabled, since it must // be readable by the ARM9 at all times anyway - global_hid_state = REG_HID | MCU_GetSpecialHID(); - ARM_WbDC_Range((void*)&global_hid_state, 4); + SharedMemory_State.hid_state = HID_GetState(); + ARM_WbDC_Range(&SharedMemory_State, sizeof(SharedMemory_State)); ARM_DMB(); } @@ -69,8 +68,7 @@ void PXI_RX_Handler(u32 __attribute__((unused)) irqn) case PXI_GET_SHMEM: { - ret = (u32)&global_hid_state; - //ret = 0xFFFFFFFF; + ret = (u32)&SharedMemory_State; break; } @@ -110,13 +108,13 @@ void PXI_RX_Handler(u32 __attribute__((unused)) irqn) void __attribute__((noreturn)) MainLoop(void) { - // enable MCU interrupts - GIC_Enable(MCU_INTERRUPT, BIT(0), GIC_HIGHEST_PRIO, MCU_HandleInterrupts); - // enable PXI RX interrupt GIC_Enable(PXI_RX_INTERRUPT, BIT(0), GIC_HIGHEST_PRIO, PXI_RX_Handler); - GIC_Enable(VBLANK_INTERRUPT, BIT(0), GIC_HIGHEST_PRIO + 1, VBlank_Handler); + // enable MCU interrupts + GIC_Enable(MCU_INTERRUPT, BIT(0), GIC_HIGHEST_PRIO + 1, MCU_HandleInterrupts); + + GIC_Enable(VBLANK_INTERRUPT, BIT(0), GIC_HIGHEST_PRIO + 2, VBlank_Handler); // ARM9 won't try anything funny until this point PXI_Barrier(ARM11_READY_BARRIER); diff --git a/arm11/source/system/sys.c b/arm11/source/system/sys.c index 9869cbb..e75e2c4 100755 --- a/arm11/source/system/sys.c +++ b/arm11/source/system/sys.c @@ -8,9 +8,11 @@ #include "arm/scu.h" #include "arm/timer.h" +#include "hw/codec.h" #include "hw/gpulcd.h" #include "hw/i2c.h" #include "hw/mcu.h" +#include "hw/spi.h" #include "system/sections.h" @@ -85,6 +87,8 @@ void SYS_CoreZeroInit(void) PXI_Reset(); I2C_init(); MCU_Init(); + SPI_Init(); + CODEC_Init(); GPU_Init(); GPU_PSCFill(VRAM_START, VRAM_END, 0); @@ -95,8 +99,8 @@ void SYS_CoreZeroInit(void) GPU_SetFramebufferMode(0, PDC_RGB24); GPU_SetFramebufferMode(1, PDC_RGB24); - TIMER_WaitTicks(CLK_MS_TO_TICKS(10)); MCU_WriteReg(0x22, 0x2A); + TIMER_WaitTicks(CLK_MS_TO_TICKS(10)); } void SYS_CoreInit(void) diff --git a/arm9/source/common/hid.c b/arm9/source/common/hid.c index 4c7d3b7..dee4dba 100644 --- a/arm9/source/common/hid.c +++ b/arm9/source/common/hid.c @@ -4,10 +4,19 @@ #include "screenshot.h" // for screenshots #include "arm.h" +#include "shmem.h" +// there's some weird thing going on when reading this +// with an LDRD instruction so for now they'll be two +// separate things - hopefully LTO won't get in the way u32 HID_ReadState(void) -{ // this should probably be abstracted away in something like "SHMEM_GetHID" - return *(u32*)ARM_GetTID(); +{ + return ARM_GetSHMEM()->hid_state; +} + +u32 HID_ReadRawTouchState(void) +{ + return ARM_GetSHMEM()->hid_state >> 32; } u32 InputWait(u32 timeout_sec) { diff --git a/arm9/source/common/hid.h b/arm9/source/common/hid.h index eead3f8..814aff6 100644 --- a/arm9/source/common/hid.h +++ b/arm9/source/common/hid.h @@ -6,17 +6,13 @@ // see: http://3dbrew.org/wiki/CONFIG9_Registers // see: http://3dbrew.org/wiki/EMMC_Registers + u32 HID_ReadState(void); +u32 HID_ReadRawTouchState(void); #define CART_STATE (~(*(volatile u8*)0x10000010) & 0x1) #define SD_STATE ((*(volatile u16*)0x1000601C) & (0x1<<5)) - - -// strings for button conversion -#define BUTTON_STRINGS "A", "B", "SELECT", "START", "RIGHT", "LEFT", "UP", "DOWN", "R", "L", "X", "Y" - - u32 InputWait(u32 timeout_sec); bool CheckButton(u32 button); diff --git a/arm9/source/main.c b/arm9/source/main.c index 80327cd..61f4101 100644 --- a/arm9/source/main.c +++ b/arm9/source/main.c @@ -5,6 +5,7 @@ #include "i2c.h" #include "arm.h" +#include "shmem.h" void main(int argc, char** argv, int entrypoint) { @@ -19,7 +20,7 @@ void main(int argc, char** argv, int entrypoint) // A pointer to the shared memory region is // stored in the thread ID register in the ARM9 - ARM_SetTID(PXI_DoCMD(PXI_GET_SHMEM, NULL, 0)); + ARM_InitSHMEM(); #ifdef SCRIPT_RUNNER // Run the script runner diff --git a/common/common.h b/common/common.h index a6e5c1f..211e45d 100755 --- a/common/common.h +++ b/common/common.h @@ -25,6 +25,12 @@ #define min(a,b) \ (((a) < (b)) ? (a) : (b)) +#define abs(x) \ + (((x) >= 0) ? (x) : -(x)) + +#define int_sign(x) \ + (((x) > 0) - ((x) < 0)) + #define getbe16(d) \ (((d)[0]<<8) | (d)[1]) #define getbe32(d) \ diff --git a/common/hid_map.h b/common/hid_map.h index 890f155..4544b1f 100755 --- a/common/hid_map.h +++ b/common/hid_map.h @@ -15,13 +15,18 @@ #define BUTTON_ANY 0x00000FFF #define BUTTON_ARROW (BUTTON_RIGHT|BUTTON_LEFT|BUTTON_UP|BUTTON_DOWN) -// special buttons / cart / sd +// strings for button conversion +#define BUTTON_STRINGS "A", "B", "SELECT", "START", "RIGHT", "LEFT", "UP", "DOWN", "R", "L", "X", "Y" + +// special buttons / touchscreen / cart / sd #define BUTTON_POWER ((u32)1 << 12) #define BUTTON_HOME ((u32)1 << 13) #define BUTTON_WIFI ((u32)1 << 14) -#define CART_INSERT ((u32)1 << 15) -#define CART_EJECT ((u32)1 << 16) -#define SD_INSERT ((u32)1 << 17) -#define SD_EJECT ((u32)1 << 18) +#define BUTTON_TOUCH ((u32)1 << 15) + +#define CART_INSERT ((u32)1 << 16) +#define CART_EJECT ((u32)1 << 17) +#define SD_INSERT ((u32)1 << 18) +#define SD_EJECT ((u32)1 << 19) #define TIMEOUT_HID ((u32)1 << 31) diff --git a/common/shmem.h b/common/shmem.h new file mode 100755 index 0000000..a81a9d6 --- /dev/null +++ b/common/shmem.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +typedef struct { + u64 hid_state; +} GlobalSharedMemory; + +#ifdef ARM9 +#include + +static inline const GlobalSharedMemory *ARM_GetSHMEM(void) +{ + return (const GlobalSharedMemory*)ARM_GetTID(); +} + +static void ARM_InitSHMEM(void) +{ + ARM_SetTID(PXI_DoCMD(PXI_GET_SHMEM, NULL, 0)); +} +#endif