250 lines
8.3 KiB
C

#pragma once
/*
* This file is part of libn3ds
* Copyright (C) 2024 derrek, profi200
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "types.h"
#include "arm11/drivers/mcu.h"
#include "mem_map.h"
#ifdef __cplusplus
extern "C"
{
#endif
#define LCD_REGS_BASE (IO_AXI_BASE + 0x2000)
#define MCU_LCD_IRQ_MASK (MCU_IRQ_TOP_BL_ON | MCU_IRQ_TOP_BL_OFF | \
MCU_IRQ_BOT_BL_ON | MCU_IRQ_BOT_BL_OFF | \
MCU_IRQ_LCD_POWER_ON | MCU_IRQ_LCD_POWER_OFF)
// Adaptive/Active backlight registers.
typedef struct
{
vu32 cnt; // 0x000 Control.
vu32 fill; // 0x004 Fill color. Works if ABL is disabled.
u8 _0x08[8];
vu32 win_sx; // 0x010 Histogram start X.
vu32 win_ex; // 0x014 Histogram end X (inclusive).
vu32 win_sy; // 0x018 Histogram start Y.
vu32 win_ey; // 0x01C Histogram end Y (inclusive).
vu32 gth_ratio; // 0x020
vu32 gth_min; // 0x024
vu32 gth_max; // 0x028
vu32 gth_max_ex; // 0x02C
vu32 inertia; // 0x030
u8 _0x34[4];
vu32 rs_min; // 0x038
vu32 rs_max; // 0x03C
vu32 bl_pwm_duty; // 0x040 Backlight PWM duty (on time). Works if ABL is disabled.
vu32 bl_pwm_cnt; // 0x044 Backlight PWM control. Works if ABL is disabled.
u8 _0x48[8];
vu32 unk50; // 0x050 Histogram RGB?
vu32 unk54; // 0x054 Histogram RGB too?
u8 _0x58[8];
vu32 dither_patt[8]; // 0x060 ? Mask 0xCCCC for each entry. Every second reg is a filler and read-only.
vu32 rs_lut[9]; // 0x080
u8 _0xa4[0x4c];
const vu32 unkF0; // 0x0F0 ?
const vu32 unkF4; // 0x0F4 ?
const vu32 unkF8; // 0x0F8 ?
u8 _0xfc[0x204];
vu32 unk_coef[64]; // 0x300 ? New 3DS only. RGB coefficients?
vu32 unk_coef2[100]; // 0x400 ? Each entry has 10 writable bits. Is this supposed to be read-only?
} Abl;
static_assert(offsetof(Abl, unk_coef2[99]) == 0x58C, "Error: Member unk_coef2[99] of Abl is not at offset 0x58C!");
typedef struct
{
vu32 parallax_cnt; // 0x000 Parallax PWM control.
vu32 parallax_pwm; // 0x004 Parallax PWM timings.
const vu32 status; // 0x008 ?
vu32 signal_cnt; // 0x00C LCD signal control.
vu32 unk_lvds; // 0x010 LVDS related? Bits 0-3 (LCD0?) and 8-12 (LCD1?) writable. Usually 0x900.
vu32 rst; // 0x014 Active low reset for external LCD controllers.
vu32 unk18; // 0x018 ?
u8 _0x1c[0x1e4];
Abl abl0; // 0x200 LCD0 adaptive/active backlight control.
u8 _0x790[0x270];
Abl abl1; // 0xA00 LCD1 adaptive/active backlight control.
} LcdRegs;
static_assert(offsetof(LcdRegs, abl1) == 0xA00, "Error: Member abl1 of LcdRegs is not at offset 0xA00!");
ALWAYS_INLINE LcdRegs* getLcdRegs(void)
{
return (LcdRegs*)LCD_REGS_BASE;
}
// REG_LCD_PARALLAX_CNT
#define PARALLAX_CNT_PWM0_EN BIT(0) // PWM0 enable.
#define PARALLAX_CNT_PWM0_UNK BIT(1) // Turns PWM0 automatically on and off?
#define PARALLAX_CNT_PWM0_INV BIT(2) // Swaps PWM0 on/off timings.
#define PARALLAX_CNT_PWM1_EN BIT(16) // PWM1 enable. TODO: Or is this rather a selection bit (1 PWM out at a time)?
#define PARALLAX_CNT_PWM1_UNK BIT(17) // Turns PWM1 automatically on and off?
#define PARALLAX_CNT_PWM1_INV BIT(18) // Swaps PWM1 on/off timings.
// REG_LCD_PARALLAX_PWM
#define PARALLAX_PWM_TIMING(on, off) ((on)<<16 | (off)) // PWM on/off time. For each "(N+1)*0.9µs".
// REG_LCD_STATUS
// REG_LCD_SIGNAL_CNT
#define SIGNAL_CNT_LCD0_DIS BIT(0) // Disables top LCD control signals.
#define SIGNAL_CNT_LCD1_DIS BIT(16) // Disables bottom LCD control signals.
#define SIGNAL_CNT_BOTH_DIS (SIGNAL_CNT_LCD1_DIS | SIGNAL_CNT_LCD0_DIS)
// REG_LCD_RST
#define LCD_RST_RST (0u) // LCD controllers under eset.
#define LCD_RST_NORST BIT(0) // LCD controllers out of reset.
// REG_LCD_ABL_CNT
#define ABL_EN BIT(0)
#define ABL_SPATIAL_DITHER_EN BIT(8)
#define ABL_TEMPORAL_DITHER_EN BIT(9)
// REG_LCD_ABL_FILL
#define ABL_FILL_RGB(r, g, b) ((b)<<16 | (g)<<8 | (r))
#define ABL_FILL_EN BIT(24)
// REG_LCD_ABL_BL_PWM_CNT
#define BL_PWM_DENOMINATOR(d) ((d) & 0x3FFu)
#define BL_PWM_DENOMINATOR_MASK (0x3FFu)
#define BL_PWM_PRESCALER(p) (((p) & 15u)<<12)
#define BL_PWM_PRESCALER_MASK (15u<<12)
#define BL_PWM_EN BIT(16)
// TODO: Bits 16-18 belong together. Find out their purpose. PWM counter maybe?
#define BL_PWM_UNK19 BIT(19) // N3DS only.
#define BL_PWM_UNK20(x) (((x) & 15u)<<20) // N3DS only.
#define BL_PWM_UNK24(x) (((x) & 0x7Fu)<<24) // N3DS only.
#define BL_PWM_UNK31 BIT(31) // N3DS only. Doubles PWM frequency?
/**
* @brief Forces black output instead of the frame buffer.
*
* @param[in] top Top screen output disable. true = disabled.
* @param[in] bot Bottom screen output disable. true = disabled.
*/
static inline void LCD_setForceBlack(const bool top, const bool bot)
{
LcdRegs *const lcd = getLcdRegs();
lcd->abl0.fill = (top ? ABL_FILL_EN | ABL_FILL_RGB(0, 0, 0) : 0);
lcd->abl1.fill = (bot ? ABL_FILL_EN | ABL_FILL_RGB(0, 0, 0) : 0);
}
/**
* @brief Initializes LCDs and backlights.
*
* @param[in] mcuLcdOnMask LCD/backlight on mask. See mcu driver.
* @param[in] lcdLum The initial luminance for both LCDs.
*/
void LCD_init(u8 mcuLcdOnMask, const u32 lcdLum);
/**
* @brief Deinitializes LCDs and backlights.
*
* @param[in] mcuLcdOffMask LCD/backlight off mask. See mcu driver.
*/
void LCD_deinit(const u8 mcuLcdOffMask);
/**
* @brief Powers LCD backlights on or off.
*
* @param[in] mcuBlMask Backlight on/off mask. See mcu driver.
*/
void LCD_setBacklightPower(u8 mcuBlMask);
/**
* @brief Sets the luminance (cd/m²) for both LCDs.
*
* @param[in] lum The luminance.
*/
void LCD_setLuminance(u32 lum);
/**
* @brief Sets the luminance level for both LCDs. Same as backlight levels in HOME menu.
*
* @param[in] level The luminance level. 1-5.
*/
void LCD_setLuminanceLevel(u8 level);
/**
* @brief Turns on or off 3D mode for the top LCD.
*
* @param[in] on Set to true for on and false for off.
*/
//void LCD_setParallaxBarrier(const bool on);
// ------------------------------------------------------------------------------------------ //
// LCD I2C regs.
typedef enum
{
LCD_I2C_REG_POWER = 0x01u,
LCD_I2C_REG_UNK11 = 0x11u,
LCD_I2C_REG_READ_ADDR = 0x40u,
LCD_I2C_REG_HS_SERIAL = 0x50u, // Highspeed serial for upper LCD only.
LCD_I2C_REG_UNK54 = 0x54u, // Checksum on/off?
LCD_I2C_REG_UNK55 = 0x55u, // Checksum status?
LCD_I2C_REG_STATUS = 0x60u, // Initially 0x01.
LCD_I2C_REG_BL_STATUS = 0x62u, // Backlight status.
LCD_I2C_REG_RST_STATUS = 0xFEu, // Reset status. Initially 0x00.
LCD_I2C_REG_REVISION = 0xFFu, // Revision/vendor infos.
} LcdI2cReg;
// LCD_I2C_REG_POWER
#define LCD_REG_POWER_BLACK (0x11u) // Force blackscreen.
#define LCD_REG_POWER_ON (0x10u) // Normal operation.
#define LCD_REG_POWER_OFF (0x00u) // LCD powered off.
// LCD_I2C_REG_UNK11
#define LCD_REG_UNK11_UNK10 (0x10u) // Written on init.
// LCD_I2C_REG_HS_SERIAL
#define LCD_REG_HS_SERIAL_ON (0x01u) // Enable highspeed serial.
// LCD_I2C_REG_UNK54
// LCD_I2C_REG_UNK55
// LCD_I2C_REG_STATUS
#define LCD_REG_STATUS_OK (0x00u)
#define LCD_REG_STATUS_ERR (0x01u)
// LCD_I2C_REG_BL_STATUS
#define LCD_REG_BL_STATUS_OFF (0x00u)
#define LCD_REG_BL_STATUS_ON (0x01u)
// LCD_I2C_REG_RST_STATUS
#define LCD_REG_RST_STATUS_NONE (0xAAu)
#define LCD_REG_RST_STATUS_RST (0x00u)
u8 LCDI2C_readReg(const u8 lcd, const LcdI2cReg reg);
bool LCDI2C_writeReg(const u8 lcd, const LcdI2cReg reg, const u8 data);
void LCDI2C_init(void);
void LCDI2C_waitBacklightsOn(void);
u16 LCDI2C_getRevisions(void);
#ifdef __cplusplus
} // extern "C"
#endif