/* * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 . */ #include #include "fatal_device_page_table.hpp" #include "fatal_registers_di.hpp" namespace ams::secmon::fatal { namespace { #include "fatal_display_config.inc" } namespace { /* Helpful defines. */ constexpr size_t FrameBufferHeight = 768; constexpr size_t FrameBufferWidth = 1280; constexpr size_t FrameBufferSize = FrameBufferHeight * FrameBufferWidth * sizeof(u32); constexpr int DsiWaitForCommandMilliSecondsMax = 250; constexpr int DsiWaitForCommandCompletionMilliSeconds = 5; constexpr int DsiWaitForHostControlMilliSecondsMax = 150; constexpr inline int I2cAddressMax77620Pmic = 0x3C; constexpr size_t GPIO_PORT3_CNF_0 = 0x200; constexpr size_t GPIO_PORT3_OE_0 = 0x210; constexpr size_t GPIO_PORT3_OUT_0 = 0x220; constexpr size_t GPIO_PORT6_CNF_1 = 0x504; constexpr size_t GPIO_PORT6_OE_1 = 0x514; constexpr size_t GPIO_PORT6_OUT_1 = 0x524; /* Globals. */ constexpr inline const uintptr_t PMC = secmon::MemoryRegionVirtualDevicePmc .GetAddress(); constexpr inline const uintptr_t g_disp1_regs = secmon::MemoryRegionVirtualDeviceDisp1 .GetAddress(); constexpr inline const uintptr_t g_dsi_regs = secmon::MemoryRegionVirtualDeviceDsi .GetAddress(); constexpr inline const uintptr_t g_clk_rst_regs = secmon::MemoryRegionVirtualDeviceClkRst .GetAddress(); constexpr inline const uintptr_t g_gpio_regs = secmon::MemoryRegionVirtualDeviceGpio .GetAddress(); constexpr inline const uintptr_t g_apb_misc_regs = secmon::MemoryRegionVirtualDeviceApbMisc.GetAddress(); constexpr inline const uintptr_t g_mipi_cal_regs = secmon::MemoryRegionVirtualDeviceMipiCal.GetAddress(); constinit u32 *g_frame_buffer = nullptr; inline void DoRegisterWrites(uintptr_t base_address, const RegisterWrite *reg_writes, size_t num_writes) { for (size_t i = 0; i < num_writes; i++) { reg::Write(base_address + reg_writes[i].offset, reg_writes[i].value); } } inline void DoSocDependentRegisterWrites(uintptr_t base_address, const RegisterWrite *reg_writes_erista, size_t num_writes_erista, const RegisterWrite *reg_writes_mariko, size_t num_writes_mariko) { switch (GetSocType()) { case fuse::SocType_Erista: DoRegisterWrites(base_address, reg_writes_erista, num_writes_erista); break; case fuse::SocType_Mariko: DoRegisterWrites(base_address, reg_writes_mariko, num_writes_mariko); break; AMS_UNREACHABLE_DEFAULT_CASE(); } } inline void DoSleepOrRegisterWrites(uintptr_t base_address, const SleepOrRegisterWrite *reg_writes, size_t num_writes) { for (size_t i = 0; i < num_writes; i++) { switch (reg_writes[i].kind) { case SleepOrRegisterWriteKind_Write: reg::Write(base_address + sizeof(u32) * reg_writes[i].offset, reg_writes[i].value); break; case SleepOrRegisterWriteKind_Sleep: util::WaitMicroSeconds(reg_writes[i].offset * UINT64_C(1000)); break; AMS_UNREACHABLE_DEFAULT_CASE(); } } } void WaitDsiTrigger() { const u32 timeout = util::GetMicroSeconds() + (DsiWaitForCommandMilliSecondsMax * 1000u); while (true) { if (util::GetMicroSeconds() >= timeout) { break; } if (reg::Read(g_dsi_regs + sizeof(u32) * DSI_TRIGGER) == 0) { break; } } util::WaitMicroSeconds(DsiWaitForCommandCompletionMilliSeconds * 1000u); } void WaitDsiHostControl() { const u32 timeout = util::GetMicroSeconds() + (DsiWaitForHostControlMilliSecondsMax * 1000u); while (true) { if (util::GetMicroSeconds() >= timeout) { break; } if ((reg::Read(g_dsi_regs + sizeof(u32) * DSI_HOST_CONTROL) & DSI_HOST_CONTROL_IMM_BTA) == 0) { break; } } } void EnableBacklightForVendor2050ForHardwareTypeFive(int brightness) { /* Enable FRAME_END_INT */ reg::Write(g_disp1_regs + sizeof(u32) * DC_CMD_INT_ENABLE, 2); /* Configure DSI_LINE_TYPE as FOUR */ reg::Write(g_dsi_regs + sizeof(u32) * DSI_VIDEO_MODE_CONTROL, 1); reg::Write(g_dsi_regs + sizeof(u32) * DSI_VIDEO_MODE_CONTROL, 9); /* Set and wait for FRAME_END_INT */ reg::Write(g_disp1_regs + sizeof(u32) * DC_CMD_INT_STATUS, 2); while ((reg::Read(g_disp1_regs + sizeof(u32) * DC_CMD_INT_STATUS) & 2) != 0) { /* ... */ } /* Configure display brightness. */ const u32 brightness_val = ((0x7FF * brightness) / 100); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x339); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, (brightness_val & 0x700) | ((brightness_val & 0xFF) << 16) | 0x51); /* Set and wait for FRAME_END_INT */ reg::Write(g_disp1_regs + sizeof(u32) * DC_CMD_INT_STATUS, 2); while ((reg::Read(g_disp1_regs + sizeof(u32) * DC_CMD_INT_STATUS) & 2) != 0) { /* ... */ } /* Set client sync point block reset. */ reg::Write(g_dsi_regs + sizeof(u32) * DSI_INCR_SYNCPT_CNTRL, 1); util::WaitMicroSeconds(300'000ul); /* Clear client sync point block resest. */ reg::Write(g_dsi_regs + sizeof(u32) * DSI_INCR_SYNCPT_CNTRL, 0); util::WaitMicroSeconds(300'000ul); /* Clear DSI_LINE_TYPE config. */ reg::Write(g_dsi_regs + sizeof(u32) * DSI_VIDEO_MODE_CONTROL, 0); /* Disable FRAME_END_INT */ reg::Write(g_disp1_regs + sizeof(u32) * DC_CMD_INT_ENABLE, 0); reg::Write(g_disp1_regs + sizeof(u32) * DC_CMD_INT_STATUS, 2); } void EnableBacklightForGeneric(int brightness) { AMS_UNUSED(brightness); reg::SetBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x1); } #define DO_REGISTER_WRITES(base_address, writes) DoRegisterWrites(base_address, writes, util::size(writes)) #define DO_SOC_DEPENDENT_REGISTER_WRITES(base_address, writes) DoSocDependentRegisterWrites(base_address, writes##Erista, util::size(writes##Erista), writes##Mariko, util::size(writes##Mariko)) #define DO_SLEEP_OR_REGISTER_WRITES(base_address, writes) DoSleepOrRegisterWrites(base_address, writes, util::size(writes)) void InitializeFrameBuffer() { if (g_frame_buffer != nullptr) { std::memset(g_frame_buffer, 0, FrameBufferSize); hw::FlushDataCache(g_frame_buffer, FrameBufferSize); } else { /* Clear the frame buffer. */ g_frame_buffer = secmon::MemoryRegionDramDcFramebuffer.GetPointer(); std::memset(g_frame_buffer, 0, FrameBufferSize); hw::FlushDataCache(g_frame_buffer, FrameBufferSize); /* Attach the frame buffer to DC. */ InitializeDevicePageTableForDc(); } } void FinalizeFrameBuffer() { /* We don't actually support finalizing the framebuffer, so do nothing here. */ } } void FinalizeDisplay() { FinalizeFrameBuffer(); /* TODO */ AMS_SECMON_LOG("FinalizeDisplay not yet implemented\n"); } void InitializeDisplay() { /* Ensure that the display is finalized. */ /* TODO */ /* Setup the framebuffer. */ InitializeFrameBuffer(); /* Get the hardware type. */ const auto hw_type = fuse::GetHardwareType(); /* Turn on DSI/voltage rail. */ { if (GetSocType() == fuse::SocType_Mariko) { i2c::SendByte(i2c::Port_5, I2cAddressMax77620Pmic, 0x18, 0x3A); i2c::SendByte(i2c::Port_5, I2cAddressMax77620Pmic, 0x18, 0x3A); } i2c::SendByte(i2c::Port_5, I2cAddressMax77620Pmic, 0x23, 0xD0); } /* Enable MIPI CAL, DSI, DISP1, HOST1X, UART_FST_MIPI_CAL, DSIA LP clocks. */ reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_H_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_H_CLR_CLR_MIPI_CAL_RST, ENABLE), CLK_RST_REG_BITS_ENUM(RST_DEV_H_CLR_CLR_DSI_RST, ENABLE)); reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_H_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_H_SET_SET_CLK_ENB_MIPI_CAL, ENABLE), CLK_RST_REG_BITS_ENUM(CLK_ENB_H_SET_SET_CLK_ENB_DSI, ENABLE)); reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_CLR_CLR_HOST1X_RST, ENABLE), CLK_RST_REG_BITS_ENUM(RST_DEV_L_CLR_CLR_DISP1_RST, ENABLE)); reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_SET_SET_CLK_ENB_HOST1X, ENABLE), CLK_RST_REG_BITS_ENUM(CLK_ENB_L_SET_SET_CLK_ENB_DISP1, ENABLE)); reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_X_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_X_SET_SET_CLK_ENB_UART_FST_MIPI_CAL, ENABLE)); reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIPI_CAL, CLK_RST_REG_BITS_VALUE(CLK_SOURCE_UART_FST_MIPI_CAL_UART_FST_MIPI_CAL_CLK_DIVISOR, 10), CLK_RST_REG_BITS_ENUM (CLK_SOURCE_UART_FST_MIPI_CAL_UART_FST_MIPI_CAL_CLK_SRC, PLLP_OUT3)); reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_W_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_W_SET_SET_CLK_ENB_DSIA_LP, ENABLE)); reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_SOURCE_DSIA_LP, CLK_RST_REG_BITS_VALUE(CLK_SOURCE_DSIA_LP_DSIA_LP_CLK_DIVISOR, 10), CLK_RST_REG_BITS_ENUM (CLK_SOURCE_DSIA_LP_DSIA_LP_CLK_SRC, PLLP_OUT0)); /* Set IO_DPD_REQ to DPD_OFF. */ reg::ReadWrite(PMC + APBDEV_PMC_IO_DPD_REQ, PMC_REG_BITS_ENUM(IO_DPD_REQ_CODE, DPD_OFF)); reg::ReadWrite(PMC + APBDEV_PMC_IO_DPD2_REQ, PMC_REG_BITS_ENUM(IO_DPD2_REQ_CODE, DPD_OFF)); /* Configure LCD pinmux tristate + passthrough. */ reg::ClearBits(g_apb_misc_regs + PINMUX_AUX_NFC_EN, reg::EncodeMask(PINMUX_REG_BITS_MASK(AUX_TRISTATE))); reg::ClearBits(g_apb_misc_regs + PINMUX_AUX_NFC_INT, reg::EncodeMask(PINMUX_REG_BITS_MASK(AUX_TRISTATE))); reg::ClearBits(g_apb_misc_regs + PINMUX_AUX_LCD_BL_PWM, reg::EncodeMask(PINMUX_REG_BITS_MASK(AUX_TRISTATE))); reg::ClearBits(g_apb_misc_regs + PINMUX_AUX_LCD_BL_EN, reg::EncodeMask(PINMUX_REG_BITS_MASK(AUX_TRISTATE))); reg::ClearBits(g_apb_misc_regs + PINMUX_AUX_LCD_RST, reg::EncodeMask(PINMUX_REG_BITS_MASK(AUX_TRISTATE))); if (hw_type == fuse::HardwareType_Five) { /* Configure LCD backlight. */ reg::SetBits(g_gpio_regs + GPIO_PORT6_CNF_1, 0x4); reg::SetBits(g_gpio_regs + GPIO_PORT6_OE_1, 0x4); } else { /* Configure LCD power, VDD. */ reg::SetBits(g_gpio_regs + GPIO_PORT3_CNF_0, 0x3); reg::SetBits(g_gpio_regs + GPIO_PORT3_OE_0, 0x3); reg::SetBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x1); util::WaitMicroSeconds(10'000ul); reg::SetBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x2); util::WaitMicroSeconds(10'000ul); /* Configure LCD backlight. */ reg::SetBits(g_gpio_regs + GPIO_PORT6_CNF_1, 0x7); reg::SetBits(g_gpio_regs + GPIO_PORT6_OE_1, 0x7); reg::SetBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x2); } /* Configure display interface and display. */ reg::Write(g_mipi_cal_regs + MIPI_CAL_MIPI_BIAS_PAD_CFG2, 0); if (GetSocType() == fuse::SocType_Mariko) { reg::Write(g_mipi_cal_regs + MIPI_CAL_MIPI_BIAS_PAD_CFG0, 0); reg::Write(g_apb_misc_regs + APB_MISC_GP_DSI_PAD_CONTROL, 0); } /* Execute configs. */ DO_SOC_DEPENDENT_REGISTER_WRITES(g_clk_rst_regs, DisplayConfigPlld01); DO_SLEEP_OR_REGISTER_WRITES(g_disp1_regs, DisplayConfigDc01); DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init01); DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init02); DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init03); DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init04); DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init05); DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming); DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init06); DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming); DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init07); util::WaitMicroSeconds(10'000ul); /* Enable backlight reset. */ reg::SetBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x4); util::WaitMicroSeconds(60'000ul); if (hw_type == fuse::HardwareType_Five) { reg::Write(g_dsi_regs + sizeof(u32) * DSI_BTA_TIMING, 0x40103); } else { reg::Write(g_dsi_regs + sizeof(u32) * DSI_BTA_TIMING, 0x50204); } reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x337); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); WaitDsiTrigger(); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x406); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); WaitDsiTrigger(); reg::Write(g_dsi_regs + sizeof(u32) * DSI_HOST_CONTROL, DSI_HOST_CONTROL_TX_TRIG_HOST | DSI_HOST_CONTROL_IMM_BTA | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC); WaitDsiHostControl(); util::WaitMicroSeconds(5'000ul); /* Parse LCD vendor. */ { u32 host_response[3]; for (size_t i = 0; i < util::size(host_response); i++) { host_response[i] = reg::Read(g_dsi_regs + sizeof(u32) * DSI_RD_DATA); } /* The last word from host response is: Bits 0-7: FAB Bits 8-15: REV Bits 16-23: Minor REV */ u32 lcd_vendor; if ((host_response[2] & 0xFF) == 0x10) { lcd_vendor = 0; } else { lcd_vendor = (host_response[2] >> 8) & 0xFF00; } lcd_vendor = (lcd_vendor & 0xFFFFFF00) | (host_response[2] & 0xFF); AMS_ASSERT(lcd_vendor == GetLcdVendor()); } /* LCD vendor specific configuration. */ switch (GetLcdVendor()) { case 0x10: /* Japan Display Inc screens. */ DO_SLEEP_OR_REGISTER_WRITES(g_dsi_regs, DisplayConfigJdiSpecificInit01); break; case 0xF20: /* Innolux first revision screens. */ reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); util::WaitMicroSeconds(180'000ul); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); util::WaitMicroSeconds(5'000ul); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x739); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x751548B1); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x143209); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); util::WaitMicroSeconds(5'000ul); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); break; case 0xF30: /* AUO first revision screens. */ reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); util::WaitMicroSeconds(180'000ul); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); util::WaitMicroSeconds(5'000ul); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x739); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x711148B1); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x143209); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); util::WaitMicroSeconds(5'000ul); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); break; case 0x2050: /* Unknown (hardware type 5) screen. */ reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); util::WaitMicroSeconds(180'000ul); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0xA015); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x205315); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x339); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x51); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); util::WaitMicroSeconds(5'000ul); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); break; case 0x1020: /* Innolux second revision screen. */ case 0x1030: /* AUO second revision screen. */ case 0x1040: /* Unknown second revision screen. */ default: reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); util::WaitMicroSeconds(120'000ul); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); break; } util::WaitMicroSeconds(20'000ul); DO_SOC_DEPENDENT_REGISTER_WRITES(g_clk_rst_regs, DisplayConfigPlld02); DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init08); DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming); DO_SLEEP_OR_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init09); reg::Write(g_disp1_regs + sizeof(u32) * DC_DISP_DISP_CLOCK_CONTROL, SHIFT_CLK_DIVIDER(4)); DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init10); util::WaitMicroSeconds(10'000ul); /* Configure MIPI CAL. */ DO_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal01); DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal02); DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init11); DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal03); DO_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal04); if (GetSocType() == fuse::SocType_Mariko) { /* On Mariko the above configurations are executed twice, for some reason. */ DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal02); DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init11); DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal03); DO_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal04); } util::WaitMicroSeconds(10'000ul); /* Write DISP1, FrameBuffer config. */ DO_SLEEP_OR_REGISTER_WRITES(g_disp1_regs, DisplayConfigDc02); DO_SLEEP_OR_REGISTER_WRITES(g_disp1_regs, DisplayConfigFrameBuffer); if (GetLcdVendor() != 0x2050) { util::WaitMicroSeconds(35'000ul); } } void ShowDisplay(const ams::impl::FatalErrorContext *f_ctx, const Result save_result) { /* Draw the image to the screen. */ std::memset(g_frame_buffer, 0, FrameBufferSize); { /* TODO: Actually print the contents of the report. */ AMS_UNUSED(f_ctx, save_result); for (size_t n = 0; n < 8; n++) { const size_t x = n * (FrameBufferWidth / 8); for (size_t cur_y = 0; cur_y < FrameBufferHeight; cur_y++) { for (size_t cur_x = 0; cur_x < (FrameBufferWidth / 8); cur_x++) { g_frame_buffer[(FrameBufferWidth - (x + cur_x)) * FrameBufferHeight + 0 + cur_y] = ((0x20 * n) | 0x1F); } } } } hw::FlushDataCache(g_frame_buffer, FrameBufferSize); /* Enable backlight. */ constexpr auto DisplayBrightness = 100; if (GetLcdVendor() == 0x2050) { EnableBacklightForVendor2050ForHardwareTypeFive(DisplayBrightness); } else { EnableBacklightForGeneric(DisplayBrightness); } } }