- added new SPI and CODEC drivers ported from linux, thanks to xerpi

- circle pad simulates dpad keys (up, right, down, left)
- raw touchscreen data is provided but currently unused
- added a simple shared memory region thing
- fixed the 10ms delay to be _after_ the backlights are turned on, thanks to profi again

as always, other stuff I probably forgot about
This commit is contained in:
Wolfvak 2019-04-18 16:39:45 -03:00 committed by d0k3
parent bcff09a389
commit bf45ee3900
14 changed files with 472 additions and 27 deletions

188
arm11/source/hw/codec.c Executable file
View File

@ -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 <common.h>
#include <types.h>
#include <hid_map.h>
#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;
}
}

14
arm11/source/hw/codec.h Executable file
View File

@ -0,0 +1,14 @@
#pragma once
#include <types.h>
#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);

55
arm11/source/hw/hid.c Executable file
View File

@ -0,0 +1,55 @@
#include <common.h>
#include <types.h>
#include <hid_map.h>
#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;
}

6
arm11/source/hw/hid.h Executable file
View File

@ -0,0 +1,6 @@
#pragma once
#include <types.h>
#include <hid_map.h>
u64 HID_GetState(void);

129
arm11/source/hw/spi.c Executable file
View File

@ -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 <common.h>
#include <types.h>
#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);
}

13
arm11/source/hw/spi.h Executable file
View File

@ -0,0 +1,13 @@
#pragma once
#include <types.h>
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);

View File

@ -1,12 +1,12 @@
#include <hid_map.h>
#include <common.h> #include <common.h>
#include <types.h> #include <types.h>
#include <vram.h> #include <shmem.h>
#include <arm.h> #include <arm.h>
#include <pxi.h> #include <pxi.h>
#include "arm/gic.h" #include "arm/gic.h"
#include "hw/hid.h"
#include "hw/gpulcd.h" #include "hw/gpulcd.h"
#include "hw/i2c.h" #include "hw/i2c.h"
#include "hw/mcu.h" #include "hw/mcu.h"
@ -15,7 +15,7 @@
static bool legacy = false; static bool legacy = false;
#define REG_HID (~(*(vu16*)(0x10146000)) & BUTTON_ANY) static GlobalSharedMemory SharedMemory_State;
static const u8 brightness_lvls[] = { static const u8 brightness_lvls[] = {
0x10, 0x17, 0x1E, 0x25, 0x10, 0x17, 0x1E, 0x25,
0x2C, 0x34, 0x3C, 0x44, 0x2C, 0x34, 0x3C, 0x44,
@ -23,7 +23,6 @@ static const u8 brightness_lvls[] = {
0x79, 0x8C, 0xA7, 0xD2 0x79, 0x8C, 0xA7, 0xD2
}; };
static int prev_bright_lvl = -1; static int prev_bright_lvl = -1;
static vu32 global_hid_state = 0;
void VBlank_Handler(u32 __attribute__((unused)) irqn) 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 // the state should probably be stored on its own
// setion without caching enabled, since it must // setion without caching enabled, since it must
// be readable by the ARM9 at all times anyway // be readable by the ARM9 at all times anyway
global_hid_state = REG_HID | MCU_GetSpecialHID(); SharedMemory_State.hid_state = HID_GetState();
ARM_WbDC_Range((void*)&global_hid_state, 4); ARM_WbDC_Range(&SharedMemory_State, sizeof(SharedMemory_State));
ARM_DMB(); ARM_DMB();
} }
@ -69,8 +68,7 @@ void PXI_RX_Handler(u32 __attribute__((unused)) irqn)
case PXI_GET_SHMEM: case PXI_GET_SHMEM:
{ {
ret = (u32)&global_hid_state; ret = (u32)&SharedMemory_State;
//ret = 0xFFFFFFFF;
break; break;
} }
@ -110,13 +108,13 @@ void PXI_RX_Handler(u32 __attribute__((unused)) irqn)
void __attribute__((noreturn)) MainLoop(void) void __attribute__((noreturn)) MainLoop(void)
{ {
// enable MCU interrupts
GIC_Enable(MCU_INTERRUPT, BIT(0), GIC_HIGHEST_PRIO, MCU_HandleInterrupts);
// enable PXI RX interrupt // enable PXI RX interrupt
GIC_Enable(PXI_RX_INTERRUPT, BIT(0), GIC_HIGHEST_PRIO, PXI_RX_Handler); 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 // ARM9 won't try anything funny until this point
PXI_Barrier(ARM11_READY_BARRIER); PXI_Barrier(ARM11_READY_BARRIER);

View File

@ -8,9 +8,11 @@
#include "arm/scu.h" #include "arm/scu.h"
#include "arm/timer.h" #include "arm/timer.h"
#include "hw/codec.h"
#include "hw/gpulcd.h" #include "hw/gpulcd.h"
#include "hw/i2c.h" #include "hw/i2c.h"
#include "hw/mcu.h" #include "hw/mcu.h"
#include "hw/spi.h"
#include "system/sections.h" #include "system/sections.h"
@ -85,6 +87,8 @@ void SYS_CoreZeroInit(void)
PXI_Reset(); PXI_Reset();
I2C_init(); I2C_init();
MCU_Init(); MCU_Init();
SPI_Init();
CODEC_Init();
GPU_Init(); GPU_Init();
GPU_PSCFill(VRAM_START, VRAM_END, 0); GPU_PSCFill(VRAM_START, VRAM_END, 0);
@ -95,8 +99,8 @@ void SYS_CoreZeroInit(void)
GPU_SetFramebufferMode(0, PDC_RGB24); GPU_SetFramebufferMode(0, PDC_RGB24);
GPU_SetFramebufferMode(1, PDC_RGB24); GPU_SetFramebufferMode(1, PDC_RGB24);
TIMER_WaitTicks(CLK_MS_TO_TICKS(10));
MCU_WriteReg(0x22, 0x2A); MCU_WriteReg(0x22, 0x2A);
TIMER_WaitTicks(CLK_MS_TO_TICKS(10));
} }
void SYS_CoreInit(void) void SYS_CoreInit(void)

View File

@ -4,10 +4,19 @@
#include "screenshot.h" // for screenshots #include "screenshot.h" // for screenshots
#include "arm.h" #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) 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) { u32 InputWait(u32 timeout_sec) {

View File

@ -6,17 +6,13 @@
// see: http://3dbrew.org/wiki/CONFIG9_Registers // see: http://3dbrew.org/wiki/CONFIG9_Registers
// see: http://3dbrew.org/wiki/EMMC_Registers // see: http://3dbrew.org/wiki/EMMC_Registers
u32 HID_ReadState(void); u32 HID_ReadState(void);
u32 HID_ReadRawTouchState(void);
#define CART_STATE (~(*(volatile u8*)0x10000010) & 0x1) #define CART_STATE (~(*(volatile u8*)0x10000010) & 0x1)
#define SD_STATE ((*(volatile u16*)0x1000601C) & (0x1<<5)) #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); u32 InputWait(u32 timeout_sec);
bool CheckButton(u32 button); bool CheckButton(u32 button);

View File

@ -5,6 +5,7 @@
#include "i2c.h" #include "i2c.h"
#include "arm.h" #include "arm.h"
#include "shmem.h"
void main(int argc, char** argv, int entrypoint) 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 // A pointer to the shared memory region is
// stored in the thread ID register in the ARM9 // stored in the thread ID register in the ARM9
ARM_SetTID(PXI_DoCMD(PXI_GET_SHMEM, NULL, 0)); ARM_InitSHMEM();
#ifdef SCRIPT_RUNNER #ifdef SCRIPT_RUNNER
// Run the script runner // Run the script runner

View File

@ -25,6 +25,12 @@
#define min(a,b) \ #define min(a,b) \
(((a) < (b)) ? (a) : (b)) (((a) < (b)) ? (a) : (b))
#define abs(x) \
(((x) >= 0) ? (x) : -(x))
#define int_sign(x) \
(((x) > 0) - ((x) < 0))
#define getbe16(d) \ #define getbe16(d) \
(((d)[0]<<8) | (d)[1]) (((d)[0]<<8) | (d)[1])
#define getbe32(d) \ #define getbe32(d) \

View File

@ -15,13 +15,18 @@
#define BUTTON_ANY 0x00000FFF #define BUTTON_ANY 0x00000FFF
#define BUTTON_ARROW (BUTTON_RIGHT|BUTTON_LEFT|BUTTON_UP|BUTTON_DOWN) #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_POWER ((u32)1 << 12)
#define BUTTON_HOME ((u32)1 << 13) #define BUTTON_HOME ((u32)1 << 13)
#define BUTTON_WIFI ((u32)1 << 14) #define BUTTON_WIFI ((u32)1 << 14)
#define CART_INSERT ((u32)1 << 15) #define BUTTON_TOUCH ((u32)1 << 15)
#define CART_EJECT ((u32)1 << 16)
#define SD_INSERT ((u32)1 << 17) #define CART_INSERT ((u32)1 << 16)
#define SD_EJECT ((u32)1 << 18) #define CART_EJECT ((u32)1 << 17)
#define SD_INSERT ((u32)1 << 18)
#define SD_EJECT ((u32)1 << 19)
#define TIMEOUT_HID ((u32)1 << 31) #define TIMEOUT_HID ((u32)1 << 31)

21
common/shmem.h Executable file
View File

@ -0,0 +1,21 @@
#pragma once
#include <arm.h>
typedef struct {
u64 hid_state;
} GlobalSharedMemory;
#ifdef ARM9
#include <pxi.h>
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