From 224920afa9141e3c0b09162e4f5b7308aefe384d Mon Sep 17 00:00:00 2001 From: cepetr Date: Tue, 16 Apr 2024 15:52:17 +0200 Subject: [PATCH] feat(core): refactor display drivers [no changelog] --- core/Makefile | 20 +- core/SConscript.boardloader | 20 +- core/SConscript.bootloader | 26 +- core/SConscript.bootloader_ci | 3 +- core/SConscript.bootloader_emu | 69 +- core/SConscript.firmware | 28 +- core/SConscript.prodtest | 2 +- core/SConscript.reflash | 2 +- core/SConscript.unix | 60 +- core/embed/boardloader/main.c | 2 + core/embed/bootloader/bootui.c | 1 + core/embed/bootloader/emulator.c | 4 - core/embed/bootloader/memory_stm32u58.ld | 2 + core/embed/bootloader/memory_stm32u5a.ld | 2 + core/embed/bootloader_ci/bootui.c | 1 + core/embed/bootloader_ci/main.c | 1 + core/embed/bootloader_ci/memory_stm32f4.ld | 6 + .../extmod/modtrezorui/modtrezorui-display.h | 6 + core/embed/firmware/memory_DISC2.ld | 2 + core/embed/firmware/memory_T.ld | 5 + core/embed/lib/{display.c => display_draw.c} | 8 +- core/embed/lib/{display.h => display_draw.h} | 5 +- core/embed/lib/dma2d_emul.c | 2 +- core/embed/lib/gl_bitblt.h | 120 ++ core/embed/lib/gl_bitblt_mono8.c | 110 ++ core/embed/lib/gl_bitblt_rgb565.c | 136 ++ core/embed/lib/gl_bitblt_rgba8888.c | 156 ++ core/embed/lib/gl_color.c | 49 + core/embed/lib/gl_color.h | 337 ++++ core/embed/lib/gl_draw.c | 275 +++ core/embed/lib/gl_draw.h | 152 ++ core/embed/lib/terminal.c | 66 +- core/embed/lib/terminal.h | 2 - core/embed/prodtest/main.c | 1 + core/embed/reflash/main.c | 1 + core/embed/rust/Cargo.toml | 8 + core/embed/rust/trezorhal.h | 5 +- .../embed/trezorhal/boards/stm32f429i-disc1.h | 6 +- core/embed/trezorhal/boards/stm32u5a9j-dk.h | 6 +- core/embed/trezorhal/boards/trezor_1.h | 3 +- core/embed/trezorhal/boards/trezor_r_v10.h | 4 +- core/embed/trezorhal/boards/trezor_r_v3.h | 4 +- core/embed/trezorhal/boards/trezor_r_v4.h | 4 +- core/embed/trezorhal/boards/trezor_r_v6.h | 4 +- core/embed/trezorhal/boards/trezor_t.h | 11 +- .../embed/trezorhal/boards/trezor_t3t1_revE.h | 14 +- core/embed/trezorhal/boards/trezor_t3t1_v4.h | 14 +- .../display.h} | 16 +- core/embed/trezorhal/dma2d_bitblt.h | 45 + core/embed/trezorhal/stm32f4/common.c | 4 + .../stm32f4/display/st-7789/display_driver.c | 149 ++ .../stm32f4/display/st-7789/display_fb.c | 218 +++ .../stm32f4/display/st-7789/display_fb.h | 32 + .../stm32f4/display/st-7789/display_io.c | 145 ++ .../stm32f4/display/st-7789/display_io.h | 67 + .../stm32f4/display/st-7789/display_nofb.c | 103 ++ .../stm32f4/display/st-7789/display_panel.c | 244 +++ .../stm32f4/display/st-7789/display_panel.h | 57 + .../stm32f4/display/st-7789/panels/154a.c | 132 ++ .../stm32f4/display/st-7789/panels/154a.h | 27 + .../display/st-7789/panels/lx154a2411.c | 82 + .../display/st-7789/panels/lx154a2411.h | 27 + .../display/st-7789/panels/lx154a2422.c | 159 ++ .../display/st-7789/panels/lx154a2422.h | 29 + .../stm32f4/display/st-7789/panels/tf15411a.c | 174 ++ .../stm32f4/display/st-7789/panels/tf15411a.h | 30 + .../display/stm32f429i-disc1/display_driver.c | 154 ++ .../stm32f429i-disc1/display_internal.h | 36 + .../display/stm32f429i-disc1/display_ltdc.c | 291 +++ .../display/stm32f429i-disc1/ili9341_spi.c | 512 ++++++ .../display/stm32f429i-disc1/ili9341_spi.h | 13 + .../stm32f4/display/ug-2828/display_driver.c | 398 +++++ .../stm32f4/display/vg-2864/display_driver.c | 379 ++++ core/embed/trezorhal/stm32f4/displays/ltdc.c | 20 +- core/embed/trezorhal/stm32f4/displays/ltdc.h | 4 +- .../stm32f4/displays/panels/lx154a2422.c | 2 +- .../stm32f4/displays/panels/tf15411a.c | 2 +- .../trezorhal/stm32f4/displays/st7789v.c | 8 +- .../stm32f4/displays/ug-2828tswig01.c | 2 +- .../stm32f4/displays/vg-2864ksweg01.c | 2 +- core/embed/trezorhal/stm32f4/dma2d.c | 2 +- core/embed/trezorhal/stm32f4/dma2d_bitblt.c | 599 +++++++ core/embed/trezorhal/stm32f4/secret.c | 2 +- core/embed/trezorhal/stm32f4/touch/ft6x36.c | 4 +- core/embed/trezorhal/stm32u5/display/st-7789 | 1 + .../display/stm32u5a9j-dk/display_driver.c | 155 ++ .../display/stm32u5a9j-dk/display_fb.c | 77 + .../stm32u5a9j-dk/display_gfxmmu_lut.h | 1006 +++++++++++ .../display/stm32u5a9j-dk/display_internal.h | 58 + .../display/stm32u5a9j-dk/display_ltdc_dsi.c | 1562 +++++++++++++++++ core/embed/trezorhal/stm32u5/display/vg-2864 | 1 + core/embed/trezorhal/stm32u5/displays/dsi.h | 4 - core/embed/trezorhal/stm32u5/dma2d_bitblt.c | 1 + core/embed/trezorhal/unix/display-unix.c | 4 +- core/embed/trezorhal/unix/display_driver.c | 461 +++++ core/embed/trezorhal/unix/dma2d_bitblt.c | 22 + core/embed/trezorhal/xdisplay.h | 156 ++ core/embed/trezorhal/xdisplay_legacy.c | 43 + core/embed/trezorhal/xdisplay_legacy.h | 56 + core/embed/unix/main.c | 3 + core/site_scons/boards/discovery.py | 26 +- core/site_scons/boards/discovery2.py | 27 +- core/site_scons/boards/trezor_r_v10.py | 17 +- core/site_scons/boards/trezor_r_v3.py | 17 +- core/site_scons/boards/trezor_r_v4.py | 17 +- core/site_scons/boards/trezor_r_v6.py | 17 +- core/site_scons/boards/trezor_t.py | 51 +- core/site_scons/boards/trezor_t3t1_revE.py | 28 +- core/site_scons/boards/trezor_t3t1_v4.py | 28 +- 109 files changed, 9631 insertions(+), 142 deletions(-) rename core/embed/lib/{display.c => display_draw.c} (98%) rename core/embed/lib/{display.h => display_draw.h} (95%) create mode 100644 core/embed/lib/gl_bitblt.h create mode 100644 core/embed/lib/gl_bitblt_mono8.c create mode 100644 core/embed/lib/gl_bitblt_rgb565.c create mode 100644 core/embed/lib/gl_bitblt_rgba8888.c create mode 100644 core/embed/lib/gl_color.c create mode 100644 core/embed/lib/gl_color.h create mode 100644 core/embed/lib/gl_draw.c create mode 100644 core/embed/lib/gl_draw.h rename core/embed/{lib/display_interface.h => trezorhal/display.h} (89%) create mode 100644 core/embed/trezorhal/dma2d_bitblt.h create mode 100644 core/embed/trezorhal/stm32f4/display/st-7789/display_driver.c create mode 100644 core/embed/trezorhal/stm32f4/display/st-7789/display_fb.c create mode 100644 core/embed/trezorhal/stm32f4/display/st-7789/display_fb.h create mode 100644 core/embed/trezorhal/stm32f4/display/st-7789/display_io.c create mode 100644 core/embed/trezorhal/stm32f4/display/st-7789/display_io.h create mode 100644 core/embed/trezorhal/stm32f4/display/st-7789/display_nofb.c create mode 100644 core/embed/trezorhal/stm32f4/display/st-7789/display_panel.c create mode 100644 core/embed/trezorhal/stm32f4/display/st-7789/display_panel.h create mode 100644 core/embed/trezorhal/stm32f4/display/st-7789/panels/154a.c create mode 100644 core/embed/trezorhal/stm32f4/display/st-7789/panels/154a.h create mode 100644 core/embed/trezorhal/stm32f4/display/st-7789/panels/lx154a2411.c create mode 100644 core/embed/trezorhal/stm32f4/display/st-7789/panels/lx154a2411.h create mode 100644 core/embed/trezorhal/stm32f4/display/st-7789/panels/lx154a2422.c create mode 100644 core/embed/trezorhal/stm32f4/display/st-7789/panels/lx154a2422.h create mode 100644 core/embed/trezorhal/stm32f4/display/st-7789/panels/tf15411a.c create mode 100644 core/embed/trezorhal/stm32f4/display/st-7789/panels/tf15411a.h create mode 100644 core/embed/trezorhal/stm32f4/display/stm32f429i-disc1/display_driver.c create mode 100644 core/embed/trezorhal/stm32f4/display/stm32f429i-disc1/display_internal.h create mode 100644 core/embed/trezorhal/stm32f4/display/stm32f429i-disc1/display_ltdc.c create mode 100644 core/embed/trezorhal/stm32f4/display/stm32f429i-disc1/ili9341_spi.c create mode 100644 core/embed/trezorhal/stm32f4/display/stm32f429i-disc1/ili9341_spi.h create mode 100644 core/embed/trezorhal/stm32f4/display/ug-2828/display_driver.c create mode 100644 core/embed/trezorhal/stm32f4/display/vg-2864/display_driver.c create mode 100644 core/embed/trezorhal/stm32f4/dma2d_bitblt.c create mode 120000 core/embed/trezorhal/stm32u5/display/st-7789 create mode 100644 core/embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_driver.c create mode 100644 core/embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_fb.c create mode 100644 core/embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_gfxmmu_lut.h create mode 100644 core/embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_internal.h create mode 100644 core/embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_ltdc_dsi.c create mode 120000 core/embed/trezorhal/stm32u5/display/vg-2864 create mode 120000 core/embed/trezorhal/stm32u5/dma2d_bitblt.c create mode 100644 core/embed/trezorhal/unix/display_driver.c create mode 100644 core/embed/trezorhal/unix/dma2d_bitblt.c create mode 100644 core/embed/trezorhal/xdisplay.h create mode 100644 core/embed/trezorhal/xdisplay_legacy.c create mode 100644 core/embed/trezorhal/xdisplay_legacy.h diff --git a/core/Makefile b/core/Makefile index dba27525e..ae74b037f 100644 --- a/core/Makefile +++ b/core/Makefile @@ -233,18 +233,20 @@ build_embed: build_boardloader build_bootloader build_firmware # build boardload build_boardloader: ## build boardloader $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" \ - CMAKELISTS="$(CMAKELISTS)" $(BOARDLOADER_BUILD_DIR)/boardloader.bin + CMAKELISTS="$(CMAKELISTS)" NEW_RENDERING="$(NEW_RENDERING)" $(BOARDLOADER_BUILD_DIR)/boardloader.bin build_bootloader: ## build bootloader $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" \ - CMAKELISTS="$(CMAKELISTS)" BOOTLOADER_QA="$(BOOTLOADER_QA)" BOOTLOADER_DEVEL="$(BOOTLOADER_DEVEL)" $(BOOTLOADER_BUILD_DIR)/bootloader.bin + CMAKELISTS="$(CMAKELISTS)" BOOTLOADER_QA="$(BOOTLOADER_QA)" BOOTLOADER_DEVEL="$(BOOTLOADER_DEVEL)" \ + NEW_RENDERING="$(NEW_RENDERING)" $(BOOTLOADER_BUILD_DIR)/bootloader.bin build_bootloader_ci: ## build CI device testing bootloader $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" \ CMAKELISTS="$(CMAKELISTS)" $(BOOTLOADER_CI_BUILD_DIR)/bootloader.bin build_bootloader_emu: ## build the unix bootloader emulator - $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" $(BOOTLOADER_EMU_BUILD_DIR)/bootloader.elf + $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" \ + CMAKELISTS="$(CMAKELISTS)" NEW_RENDERING="$(NEW_RENDERING)" $(BOOTLOADER_EMU_BUILD_DIR)/bootloader.elf build_prodtest: ## build production test firmware $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" \ @@ -252,7 +254,7 @@ build_prodtest: ## build production test firmware build_reflash: ## build reflash firmware + reflash image $(SCONS) CFLAGS="$(CFLAGS)" PRODUCTION="$(PRODUCTION)" TREZOR_MODEL="$(TREZOR_MODEL)" \ - CMAKELISTS="$(CMAKELISTS)" $(REFLASH_BUILD_DIR)/reflash.bin + CMAKELISTS="$(CMAKELISTS)" NEW_RENDERING="$(NEW_RENDERING)" $(REFLASH_BUILD_DIR)/reflash.bin dd if=build/boardloader/boardloader.bin of=$(REFLASH_BUILD_DIR)/sdimage.bin bs=1 seek=0 dd if=build/bootloader/bootloader.bin of=$(REFLASH_BUILD_DIR)/sdimage.bin bs=1 seek=49152 @@ -261,24 +263,26 @@ build_firmware: templates build_cross ## build firmware with frozen modules TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" \ PYOPT="$(PYOPT)" BITCOIN_ONLY="$(BITCOIN_ONLY)" \ BOOTLOADER_QA="$(BOOTLOADER_QA)" BOOTLOADER_DEVEL="$(BOOTLOADER_DEVEL)" \ - DISABLE_OPTIGA="$(DISABLE_OPTIGA)" \ + DISABLE_OPTIGA="$(DISABLE_OPTIGA)" NEW_RENDERING="$(NEW_RENDERING)"\ $(FIRMWARE_BUILD_DIR)/firmware.bin build_unix: templates ## build unix port $(SCONS) CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) \ TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" \ - PYOPT="0" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN="$(ADDRESS_SANITIZER)" + PYOPT="0" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN="$(ADDRESS_SANITIZER)" \ + NEW_RENDERING="$(NEW_RENDERING)" build_unix_frozen: templates build_cross ## build unix port with frozen modules $(SCONS) CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) \ TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" \ PYOPT="$(PYOPT)" BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN="$(ADDRESS_SANITIZER)" \ - TREZOR_MEMPERF="$(TREZOR_MEMPERF)" TREZOR_EMULATOR_FROZEN=1 + TREZOR_MEMPERF="$(TREZOR_MEMPERF)" TREZOR_EMULATOR_FROZEN=1 NEW_RENDERING="$(NEW_RENDERING)" build_unix_debug: templates ## build unix port $(SCONS) --max-drift=1 CFLAGS="$(CFLAGS)" $(UNIX_BUILD_DIR)/trezor-emu-core $(UNIX_PORT_OPTS) \ TREZOR_MODEL="$(TREZOR_MODEL)" CMAKELISTS="$(CMAKELISTS)" \ - BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN=1 TREZOR_EMULATOR_DEBUGGABLE=1 + BITCOIN_ONLY="$(BITCOIN_ONLY)" TREZOR_EMULATOR_ASAN=1 TREZOR_EMULATOR_DEBUGGABLE=1 \ + NEW_RENDERING="$(NEW_RENDERING)" build_cross: ## build mpy-cross port $(MAKE) -C vendor/micropython/mpy-cross $(CROSS_PORT_OPTS) diff --git a/core/SConscript.boardloader b/core/SConscript.boardloader index 03a7b731b..4e604874d 100644 --- a/core/SConscript.boardloader +++ b/core/SConscript.boardloader @@ -5,6 +5,7 @@ import tools TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T') CMAKELISTS = int(ARGUMENTS.get('CMAKELISTS', 0)) +NEW_RENDERING = ARGUMENTS.get('NEW_RENDERING', '1') == '1' if TREZOR_MODEL in ('1', ): # skip boardloader build @@ -20,6 +21,9 @@ if TREZOR_MODEL in ('1', ): FEATURES_WANTED = ["sd_card"] +if NEW_RENDERING: + FEATURES_WANTED.append("new_rendering") + CCFLAGS_MOD = '' CPPPATH_MOD = [] CPPDEFINES_MOD = ["BOARDLOADER"] @@ -60,13 +64,27 @@ CPPPATH_MOD += [ SOURCE_MOD += [ 'embed/lib/colors.c', 'embed/lib/display_utils.c', - 'embed/lib/display.c', 'embed/lib/fonts/font_bitmap.c', 'embed/lib/fonts/fonts.c', + 'embed/lib/gl_color.c', + 'embed/lib/gl_bitblt_rgb565.c', + 'embed/lib/gl_bitblt_rgba8888.c', + 'embed/lib/gl_bitblt_mono8.c', 'embed/lib/image.c', 'embed/lib/mini_printf.c', 'embed/lib/terminal.c', ] +if NEW_RENDERING: + CPPDEFINES_MOD += ['NEW_RENDERING'] + SOURCE_MOD += [ + 'embed/lib/gl_draw.c', + ] +else: + SOURCE_MOD += [ + 'embed/lib/display_draw.c', + ] + + env = Environment(ENV=os.environ, CFLAGS='%s -DPRODUCTION=%s' % (ARGUMENTS.get('CFLAGS', ''), ARGUMENTS.get('PRODUCTION', '0')), diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader index 5ae55ca56..7b54648c0 100644 --- a/core/SConscript.bootloader +++ b/core/SConscript.bootloader @@ -7,6 +7,7 @@ TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T') CMAKELISTS = int(ARGUMENTS.get('CMAKELISTS', 0)) BOOTLOADER_QA = ARGUMENTS.get('BOOTLOADER_QA', '0') == '1' PRODUCTION = 0 if BOOTLOADER_QA else ARGUMENTS.get('PRODUCTION', '0') == '1' +NEW_RENDERING = ARGUMENTS.get('NEW_RENDERING', '1') == '1' if TREZOR_MODEL in ('1', ): # skip bootloader build @@ -22,6 +23,9 @@ if TREZOR_MODEL in ('1', ): FEATURES_WANTED = ["input", "rgb_led", "consumption_mask", "usb", "optiga", "dma2d"] +if NEW_RENDERING: + FEATURES_WANTED.append("new_rendering") + CCFLAGS_MOD = '' CPPPATH_MOD = [] CPPDEFINES_MOD = [] @@ -90,9 +94,12 @@ SOURCE_MOD += [ 'embed/lib/buffers.c', 'embed/lib/colors.c', 'embed/lib/display_utils.c', - 'embed/lib/display.c', 'embed/lib/fonts/font_bitmap.c', 'embed/lib/fonts/fonts.c', + 'embed/lib/gl_color.c', + 'embed/lib/gl_bitblt_mono8.c', + 'embed/lib/gl_bitblt_rgb565.c', + 'embed/lib/gl_bitblt_rgba8888.c', 'embed/lib/image.c', 'embed/lib/mini_printf.c', 'embed/lib/terminal.c', @@ -102,6 +109,17 @@ SOURCE_MOD += [ 'vendor/micropython/lib/uzlib/tinflate.c', ] +if NEW_RENDERING: + CPPDEFINES_MOD += ['NEW_RENDERING'] + SOURCE_MOD += [ + 'embed/lib/gl_draw.c', + ] +else: + SOURCE_MOD += [ + 'embed/lib/display_draw.c', + ] + + SOURCE_NANOPB = [ 'vendor/nanopb/pb_common.c', 'vendor/nanopb/pb_decode.c', @@ -228,6 +246,12 @@ def cargo_build(): features.append("bootloader") features.extend(FEATURES_AVAILABLE) + if NEW_RENDERING: + features.append('new_rendering') + + if TREZOR_MODEL in ('T',): + features.append('ui_antialiasing') + cargo_opts = [ f'--target={env.get("ENV")["RUST_TARGET"]}', f'--target-dir=../../build/bootloader/rust', diff --git a/core/SConscript.bootloader_ci b/core/SConscript.bootloader_ci index 1835b9305..c1f540868 100644 --- a/core/SConscript.bootloader_ci +++ b/core/SConscript.bootloader_ci @@ -5,6 +5,7 @@ import tools TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T') CMAKELISTS = int(ARGUMENTS.get('CMAKELISTS', 0)) +NEW_RENDERING = ARGUMENTS.get('NEW_RENDERING', '1') == '1' if TREZOR_MODEL in ('1', 'DISC1', 'DISC2'): # skip bootloader_ci build @@ -82,8 +83,8 @@ CPPPATH_MOD += [ SOURCE_MOD += [ 'embed/extmod/modtrezorcrypto/rand.c', 'embed/lib/colors.c', + 'embed/lib/display_draw.c', 'embed/lib/display_utils.c', - 'embed/lib/display.c', 'embed/lib/fonts/font_bitmap.c', 'embed/lib/fonts/fonts.c', 'embed/lib/image.c', diff --git a/core/SConscript.bootloader_emu b/core/SConscript.bootloader_emu index b09500f1a..c57cecd0b 100644 --- a/core/SConscript.bootloader_emu +++ b/core/SConscript.bootloader_emu @@ -6,6 +6,7 @@ import boards TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T') CMAKELISTS = int(ARGUMENTS.get('CMAKELISTS', 0)) +NEW_RENDERING = ARGUMENTS.get('NEW_RENDERING', '1') == '1' DMA2D = False if TREZOR_MODEL in ('1', 'DISC1'): @@ -22,6 +23,9 @@ if TREZOR_MODEL in ('1', 'DISC1'): FEATURES_WANTED = ["input", "rgb_led", "dma2d"] +if NEW_RENDERING: + FEATURES_WANTED.append("new_rendering") + CCFLAGS_MOD = '' CPPPATH_MOD = [] CPPDEFINES_MOD = [] @@ -87,10 +91,12 @@ SOURCE_MOD += [ 'embed/lib/buffers.c', 'embed/lib/colors.c', 'embed/lib/display_utils.c', - 'embed/lib/display.c', - 'embed/lib/dma2d_emul.c', 'embed/lib/fonts/font_bitmap.c', 'embed/lib/fonts/fonts.c', + 'embed/lib/gl_color.c', + 'embed/lib/gl_bitblt_mono8.c', + 'embed/lib/gl_bitblt_rgb565.c', + 'embed/lib/gl_bitblt_rgba8888.c', 'embed/lib/image.c', 'embed/lib/terminal.c', 'embed/lib/touch.c', @@ -101,6 +107,23 @@ SOURCE_MOD += [ 'vendor/trezor-storage/flash_area.c', ] +if NEW_RENDERING: + CPPDEFINES_MOD += ['NEW_RENDERING'] + if TREZOR_MODEL in ('T',): + CPPDEFINES_MOD += ['DISPLAY_RGB565'] + elif TREZOR_MODEL in ('R', '1',): + CPPDEFINES_MOD += ['XFRAMEBUFFER', 'DISPLAY_MONO'] + elif TREZOR_MODEL in ('T3T1',): + CPPDEFINES_MOD += ['XFRAMEBUFFER', 'DISPLAY_RGB565'] + SOURCE_MOD += [ + 'embed/lib/gl_draw.c', + ] +else: + SOURCE_MOD += [ + 'embed/lib/display_draw.c', + 'embed/lib/dma2d_emul.c', +] + if TREZOR_MODEL in ('1', ): SOURCE_MOD += [ 'embed/models/model_T1B1_layout.c', @@ -135,7 +158,6 @@ SOURCE_BOOTLOADER = [ SOURCE_TREZORHAL = [ 'embed/trezorhal/unix/boot_args.c', - 'embed/trezorhal/unix/display-unix.c', 'embed/trezorhal/unix/fault_handlers.c', 'embed/trezorhal/unix/flash.c', 'embed/trezorhal/unix/flash_otp.c', @@ -147,6 +169,17 @@ SOURCE_TREZORHAL = [ 'embed/trezorhal/unix/secret.c', ] +if NEW_RENDERING: + SOURCE_TREZORHAL += [ + 'embed/trezorhal/unix/display_driver.c', + 'embed/trezorhal/unix/dma2d_bitblt.c', + 'embed/trezorhal/xdisplay_legacy.c', + ] +else: + SOURCE_TREZORHAL += [ + 'embed/trezorhal/unix/display-unix.c', + ] + if TREZOR_MODEL in ('R', 'T3T1'): SOURCE_TREZORHAL += [ 'embed/trezorhal/unix/optiga_hal.c', @@ -260,17 +293,18 @@ cmake_gen = env.Command( # RUST_TARGET = 'x86_64-unknown-linux-gnu' -RUST_PROFILE = 'release' RUST_LIB = 'trezor_lib' -RUST_LIBDIR = f'build/bootloader_emu/rust/{RUST_TARGET}/{RUST_PROFILE}' + +if ARGUMENTS.get('TREZOR_EMULATOR_DEBUGGABLE', '0') == '1': + RUST_PROFILE = 'dev' + RUST_LIBDIR = f'build/bootloader_emu/rust/{RUST_TARGET}/debug' +else: + RUST_PROFILE = 'release' + RUST_LIBDIR = f'build/bootloader_emu/rust/{RUST_TARGET}/release' + RUST_LIBPATH = f'{RUST_LIBDIR}/lib{RUST_LIB}.a' def cargo_build(): - # Determine the profile build flags. - if RUST_PROFILE == 'release': - profile = '--release' - else: - profile = '' if TREZOR_MODEL in ("1",): features = ["model_t1"] elif TREZOR_MODEL in ("R",): @@ -280,6 +314,19 @@ def cargo_build(): else: features = ["model_tt"] + if NEW_RENDERING: + features.append('new_rendering') + if TREZOR_MODEL in ('T',): + features.append('display_rgb565') + features.append('ui_antialiasing') + elif TREZOR_MODEL in ('R', '1',): + features.append('display_mono') + features.append('xframebuffer') + elif TREZOR_MODEL in ('T3T1',): + features.append('display_rgb565') + features.append('xframebuffer') + features.append('ui_antialiasing') + if TREZOR_MODEL in ('T', 'T3T1'): features.append('touch') features.append('backlight') @@ -299,7 +346,7 @@ def cargo_build(): '-Z build-std-features=panic_immediate_abort', ] - return f'cd embed/rust; cargo build {profile} ' + ' '.join(cargo_opts) + return f'cd embed/rust; cargo build --profile {RUST_PROFILE} ' + ' '.join(cargo_opts) rust = env.Command( target=RUST_LIBPATH, diff --git a/core/SConscript.firmware b/core/SConscript.firmware index e7c6ea4cf..aacafa3ab 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -13,6 +13,7 @@ TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T') CMAKELISTS = int(ARGUMENTS.get('CMAKELISTS', 0)) PYOPT = ARGUMENTS.get('PYOPT', '1') DISABLE_OPTIGA = ARGUMENTS.get('DISABLE_OPTIGA', '0') == '1' +NEW_RENDERING = ARGUMENTS.get('NEW_RENDERING', '1') == '1' FEATURE_FLAGS = { @@ -25,6 +26,8 @@ FEATURE_FLAGS = { FEATURES_WANTED = ["input", "sbu", "sd_card", "rgb_led", "dma2d", "consumption_mask", "usb" ,"optiga", "haptic"] if DISABLE_OPTIGA and PYOPT == '0': FEATURES_WANTED.remove("optiga") +if NEW_RENDERING: + FEATURES_WANTED.append("new_rendering") CCFLAGS_MOD = '' CPPPATH_MOD = [] @@ -203,9 +206,12 @@ SOURCE_MOD += [ 'embed/lib/buffers.c', 'embed/lib/colors.c', 'embed/lib/display_utils.c', - 'embed/lib/display.c', 'embed/lib/fonts/font_bitmap.c', 'embed/lib/fonts/fonts.c', + 'embed/lib/gl_color.c', + 'embed/lib/gl_bitblt_rgb565.c', + 'embed/lib/gl_bitblt_rgba8888.c', + 'embed/lib/gl_bitblt_mono8.c', 'embed/lib/image.c', 'embed/lib/mini_printf.c', 'embed/lib/terminal.c', @@ -216,6 +222,17 @@ SOURCE_MOD += [ 'vendor/micropython/lib/uzlib/tinflate.c', ] +if NEW_RENDERING: + CPPDEFINES_MOD += ['NEW_RENDERING'] + SOURCE_MOD += [ + 'embed/lib/gl_draw.c', + ] +else: + SOURCE_MOD += [ + 'embed/lib/display_draw.c', + ] + + CPPDEFINES_MOD += [ 'TREZOR_UI2', 'TRANSLATIONS', @@ -744,8 +761,17 @@ def cargo_build(): features.append('universal_fw') features.append('ui') features.append('translations') + + if NEW_RENDERING: + features.append('new_rendering') + if PYOPT == '0': features.append('debug') + features.append('ui_debug') + if TREZOR_MODEL in ('T', 'T3T1', 'DISC1', 'DISC2'): + features.append('ui_antialiasing') + features.append('ui_blurring') + features.append('ui_jpeg_decoder') features.extend(FEATURES_AVAILABLE) diff --git a/core/SConscript.prodtest b/core/SConscript.prodtest index 22a6626d0..8f802a344 100644 --- a/core/SConscript.prodtest +++ b/core/SConscript.prodtest @@ -81,8 +81,8 @@ CPPPATH_MOD += [ SOURCE_MOD += [ 'embed/lib/colors.c', + 'embed/lib/display_draw.c', 'embed/lib/display_utils.c', - 'embed/lib/display.c', 'embed/lib/fonts/font_bitmap.c', 'embed/lib/fonts/fonts.c', 'embed/lib/image.c', diff --git a/core/SConscript.reflash b/core/SConscript.reflash index 4772d45a9..9b4e2d481 100644 --- a/core/SConscript.reflash +++ b/core/SConscript.reflash @@ -55,8 +55,8 @@ CPPPATH_MOD += [ ] SOURCE_MOD += [ 'embed/lib/colors.c', + 'embed/lib/display_draw.c', 'embed/lib/display_utils.c', - 'embed/lib/display.c', 'embed/lib/fonts/font_bitmap.c', 'embed/lib/fonts/fonts.c', 'embed/lib/image.c', diff --git a/core/SConscript.unix b/core/SConscript.unix index 64f639bfe..0a848b301 100644 --- a/core/SConscript.unix +++ b/core/SConscript.unix @@ -10,6 +10,7 @@ TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T') DMA2D = TREZOR_MODEL in ('T', 'T3T1') OPTIGA = TREZOR_MODEL in ('R', 'T3T1') CMAKELISTS = int(ARGUMENTS.get('CMAKELISTS', 0)) +NEW_RENDERING = ARGUMENTS.get('NEW_RENDERING', '1') == '1' if TREZOR_MODEL in ('DISC1', 'DISC2'): # skip unix build @@ -205,9 +206,12 @@ SOURCE_MOD += [ 'embed/lib/buffers.c', 'embed/lib/colors.c', 'embed/lib/display_utils.c', - 'embed/lib/display.c', 'embed/lib/fonts/font_bitmap.c', 'embed/lib/fonts/fonts.c', + 'embed/lib/gl_color.c', + 'embed/lib/gl_bitblt_rgb565.c', + 'embed/lib/gl_bitblt_rgba8888.c', + 'embed/lib/gl_bitblt_mono8.c', 'embed/lib/image.c', 'embed/lib/terminal.c', 'embed/lib/translations.c', @@ -217,6 +221,16 @@ SOURCE_MOD += [ 'vendor/micropython/lib/uzlib/tinflate.c', ] +if NEW_RENDERING: + SOURCE_MOD += [ + 'embed/lib/gl_draw.c', + ] +else: + SOURCE_MOD += [ + 'embed/lib/display_draw.c', + ] + + if TREZOR_MODEL in ('1', ): SOURCE_MOD += [ 'embed/models/model_T1B1_layout.c', @@ -250,6 +264,17 @@ if FROZEN: if RASPI: CPPDEFINES_MOD += ['TREZOR_EMULATOR_RASPI'] +if NEW_RENDERING: + CPPDEFINES_MOD += ['NEW_RENDERING'] + if TREZOR_MODEL in ('T',): + CPPDEFINES_MOD += ['DISPLAY_RGB565'] + elif TREZOR_MODEL in ('R', '1',): + CPPDEFINES_MOD += ['XFRAMEBUFFER', 'DISPLAY_MONO'] + elif TREZOR_MODEL in ('T3T1',): + CPPDEFINES_MOD += ['XFRAMEBUFFER', 'DISPLAY_RGB565'] + + + # modtrezorutils SOURCE_MOD += [ 'embed/extmod/modtrezorutils/modtrezorutils.c', @@ -394,7 +419,6 @@ SOURCE_MICROPYTHON = [ SOURCE_UNIX = [ 'embed/trezorhal/unix/boot_args.c', 'embed/trezorhal/unix/common.c', - 'embed/trezorhal/unix/display-unix.c', 'embed/trezorhal/unix/flash.c', 'embed/trezorhal/unix/flash_otp.c', 'embed/trezorhal/unix/random_delays.c', @@ -410,6 +434,20 @@ SOURCE_UNIX = [ 'vendor/micropython/ports/unix/input.c', 'vendor/micropython/ports/unix/unix_mphal.c', ] + +if NEW_RENDERING: + SOURCE_MOD += [ + 'embed/trezorhal/unix/display_driver.c', + 'embed/trezorhal/unix/dma2d_bitblt.c', + 'embed/trezorhal/xdisplay_legacy.c', + ] +else: + SOURCE_MOD += [ + 'embed/trezorhal/unix/display-unix.c', + 'embed/lib/dma2d_emul.c', + ] + + if TREZOR_MODEL in ('T', 'R', 'T3T1'): SOURCE_UNIX += [ 'embed/trezorhal/unix/sbu.c', @@ -424,9 +462,6 @@ if DMA2D: CPPDEFINES_MOD += [ 'USE_DMA2D', ] - SOURCE_UNIX += [ - 'embed/lib/dma2d_emul.c', - ] TRANSLATION_DATA = [ @@ -839,9 +874,24 @@ def cargo_build(): if TREZOR_MODEL in ('T', 'T3T1'): features.append('touch') features.append('sd_card') + features.append('ui_antialiasing') + features.append('ui_blurring') + features.append('ui_jpeg_decoder') if TREZOR_MODEL in ('R', '1'): features.append('button') + + if NEW_RENDERING: + features.append('new_rendering') + if TREZOR_MODEL in ('T',): + features.append('display_rgb565') + elif TREZOR_MODEL in ('R', '1',): + features.append('display_mono') + features.append('xframebuffer') + elif TREZOR_MODEL in ('T3T1',): + features.append('display_rgb565') + features.append('xframebuffer') + env.get('ENV')['TREZOR_MODEL'] = TREZOR_MODEL return f'cd embed/rust; cargo build --profile {RUST_PROFILE} --target-dir=../../build/unix/rust --no-default-features --features "{" ".join(features)}" --target {TARGET}' diff --git a/core/embed/boardloader/main.c b/core/embed/boardloader/main.c index d5dedfe02..6da1c2d8e 100644 --- a/core/embed/boardloader/main.c +++ b/core/embed/boardloader/main.c @@ -21,9 +21,11 @@ #include TREZOR_BOARD #include "board_capabilities.h" +#include "buffers.h" #include "common.h" #include "compiler_traits.h" #include "display.h" +#include "display_draw.h" #include "fault_handlers.h" #include "flash.h" #include "image.h" diff --git a/core/embed/bootloader/bootui.c b/core/embed/bootloader/bootui.c index ae5937073..0033a5e1e 100644 --- a/core/embed/bootloader/bootui.c +++ b/core/embed/bootloader/bootui.c @@ -23,6 +23,7 @@ #include "bootui.h" #include "display.h" +#include "display_draw.h" #include "display_utils.h" #ifdef TREZOR_EMULATOR #include "emulator.h" diff --git a/core/embed/bootloader/emulator.c b/core/embed/bootloader/emulator.c index 1ad001af1..221741212 100644 --- a/core/embed/bootloader/emulator.c +++ b/core/embed/bootloader/emulator.c @@ -187,10 +187,6 @@ __attribute__((noreturn)) int main(int argc, char **argv) { jump_to(NULL); } -void display_set_little_endian(void) {} - -void display_reinit(void) {} - void mpu_config_bootloader(void) {} void mpu_config_off(void) {} diff --git a/core/embed/bootloader/memory_stm32u58.ld b/core/embed/bootloader/memory_stm32u58.ld index 059b4ca2d..0812d1925 100644 --- a/core/embed/bootloader/memory_stm32u58.ld +++ b/core/embed/bootloader/memory_stm32u58.ld @@ -78,6 +78,8 @@ SECTIONS { .buf : ALIGN(4) { *(.buf*); . = ALIGN(4); + *(.no_dma_buffers*); + . = ALIGN(4); } >SRAM1 .stack : ALIGN(8) { diff --git a/core/embed/bootloader/memory_stm32u5a.ld b/core/embed/bootloader/memory_stm32u5a.ld index 9803e696d..c45ed1273 100644 --- a/core/embed/bootloader/memory_stm32u5a.ld +++ b/core/embed/bootloader/memory_stm32u5a.ld @@ -78,6 +78,8 @@ SECTIONS { .buf : ALIGN(4) { *(.buf*); . = ALIGN(4); + *(.no_dma_buffers*); + . = ALIGN(4); } >SRAM1 .stack : ALIGN(8) { diff --git a/core/embed/bootloader_ci/bootui.c b/core/embed/bootloader_ci/bootui.c index e98326536..7b6a4e288 100644 --- a/core/embed/bootloader_ci/bootui.c +++ b/core/embed/bootloader_ci/bootui.c @@ -21,6 +21,7 @@ #include "bootui.h" #include "display.h" +#include "display_draw.h" #include "display_utils.h" #include "icon_done.h" #include "icon_fail.h" diff --git a/core/embed/bootloader_ci/main.c b/core/embed/bootloader_ci/main.c index 40c56d58a..0e44e0b47 100644 --- a/core/embed/bootloader_ci/main.c +++ b/core/embed/bootloader_ci/main.c @@ -22,6 +22,7 @@ #include "common.h" #include "display.h" +#include "display_draw.h" #include "flash.h" #include "flash_otp.h" #include "image.h" diff --git a/core/embed/bootloader_ci/memory_stm32f4.ld b/core/embed/bootloader_ci/memory_stm32f4.ld index d66d8bfc3..42eb1cd20 100644 --- a/core/embed/bootloader_ci/memory_stm32f4.ld +++ b/core/embed/bootloader_ci/memory_stm32f4.ld @@ -73,4 +73,10 @@ SECTIONS { *(.boot_args*); . = ALIGN(8); } >BOOT_ARGS + + .data_ccm : ALIGN(4) { + *(.no_dma_buffers*); + . = ALIGN(4); + } >CCMRAM + } diff --git a/core/embed/extmod/modtrezorui/modtrezorui-display.h b/core/embed/extmod/modtrezorui/modtrezorui-display.h index 85662585a..2e5de1150 100644 --- a/core/embed/extmod/modtrezorui/modtrezorui-display.h +++ b/core/embed/extmod/modtrezorui/modtrezorui-display.h @@ -18,6 +18,8 @@ */ #include "display.h" +#include "display_draw.h" +#include "fonts/fonts.h" /// class Display: /// """ @@ -128,11 +130,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorui_Display_backlight_obj, /// Saves current display contents to PNG file with given prefix. /// """ STATIC mp_obj_t mod_trezorui_Display_save(mp_obj_t self, mp_obj_t prefix) { +#ifdef TREZOR_EMULATOR mp_buffer_info_t pfx = {0}; mp_get_buffer_raise(prefix, &pfx, MP_BUFFER_READ); if (pfx.len > 0) { display_save(pfx.buf); } +#endif return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorui_Display_save_obj, @@ -143,7 +147,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorui_Display_save_obj, /// Clears buffers in display saving. /// """ STATIC mp_obj_t mod_trezorui_Display_clear_save(mp_obj_t self) { +#ifdef TREZOR_EMULATOR display_clear_save(); +#endif return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorui_Display_clear_save_obj, diff --git a/core/embed/firmware/memory_DISC2.ld b/core/embed/firmware/memory_DISC2.ld index ec4a5ae26..a79d3e078 100644 --- a/core/embed/firmware/memory_DISC2.ld +++ b/core/embed/firmware/memory_DISC2.ld @@ -89,6 +89,8 @@ SECTIONS { .data_ccm : ALIGN(4) { *(.no_dma_buffers*); . = ALIGN(4); + *(.buf*); + . = ALIGN(4); } >SRAM1 .heap : ALIGN(4) { diff --git a/core/embed/firmware/memory_T.ld b/core/embed/firmware/memory_T.ld index 8f65dd6c1..9affc00a9 100644 --- a/core/embed/firmware/memory_T.ld +++ b/core/embed/firmware/memory_T.ld @@ -87,6 +87,11 @@ SECTIONS { . = ALIGN(4); } >SRAM + .buf : ALIGN(4) { + *(.buf*); + . = ALIGN(4); + } >SRAM + .heap : ALIGN(4) { . = 37K; /* this acts as a build time assertion that at least this much memory is available for heap use */ . = ABSOLUTE(sram_end); /* this explicitly sets the end of the heap */ diff --git a/core/embed/lib/display.c b/core/embed/lib/display_draw.c similarity index 98% rename from core/embed/lib/display.c rename to core/embed/lib/display_draw.c index 5c99bfaff..138155ebb 100644 --- a/core/embed/lib/display.c +++ b/core/embed/lib/display_draw.c @@ -19,7 +19,7 @@ #define _GNU_SOURCE -#include "display.h" +#include "display_draw.h" #include "buffers.h" #include "common.h" @@ -34,7 +34,7 @@ #include "memzero.h" -#include "display_interface.h" +#include "display.h" static struct { int x, y; } DISPLAY_OFFSET; @@ -61,8 +61,8 @@ void display_clear(void) { // set MADCTL first so that we can set the window correctly next display_orientation(0); // address the complete frame memory - display_set_window(0, 0, MAX_DISPLAY_RESX - 1, MAX_DISPLAY_RESY - 1); - for (uint32_t i = 0; i < MAX_DISPLAY_RESX * MAX_DISPLAY_RESY; i++) { + display_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1); + for (uint32_t i = 0; i < DISPLAY_RESX * DISPLAY_RESY; i++) { // 2 bytes per pixel because we're using RGB 5-6-5 format PIXELDATA(0x0000); } diff --git a/core/embed/lib/display.h b/core/embed/lib/display_draw.h similarity index 95% rename from core/embed/lib/display.h rename to core/embed/lib/display_draw.h index f76c8b4cc..dcd046681 100644 --- a/core/embed/lib/display.h +++ b/core/embed/lib/display_draw.h @@ -17,8 +17,8 @@ * along with this program. If not, see . */ -#ifndef __DISPLAY_H__ -#define __DISPLAY_H__ +#ifndef __DISPLAY_DRAW_H__ +#define __DISPLAY_DRAW_H__ #include #include @@ -27,7 +27,6 @@ #include "buffers.h" #include "colors.h" #include TREZOR_BOARD -#include "display_interface.h" #include "fonts/fonts.h" // provided by common diff --git a/core/embed/lib/dma2d_emul.c b/core/embed/lib/dma2d_emul.c index a9ccd9d10..99fb880be 100644 --- a/core/embed/lib/dma2d_emul.c +++ b/core/embed/lib/dma2d_emul.c @@ -18,7 +18,7 @@ */ #include "colors.h" -#include "display_interface.h" +#include "display.h" typedef enum { DMA2D_LAYER_FG = 1, diff --git a/core/embed/lib/gl_bitblt.h b/core/embed/lib/gl_bitblt.h new file mode 100644 index 000000000..9594f4bb5 --- /dev/null +++ b/core/embed/lib/gl_bitblt.h @@ -0,0 +1,120 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#ifndef GL_BITBLT_H +#define GL_BITBLT_H + +#include +#include + +#include "gl_color.h" + +// These module provides low-level bit block transfer (bitblt) +// operations on different bitmap/framebuffer types. +// +// `fill` - fills a rectangle with a solid color (with an optional +// alpha, allowing color blending). +// +// `copy` - copies a bitmap or part of it to the destination bitmap. +// +// `blend` - blends a bitmap with a 1- or 4-bit alpha channel to the +// destination using background and foreground colors. +// +// These operations might be accelerated using DMA2D (ChromART accelerator) +// on the STM32 platform. + +// Represents a set of parameters for a bit block transfer operation. +typedef struct { + // Pointer to the destination bitmap's first row + void* dst_row; + // Number of bytes per line in the destination bitmap + uint16_t dst_stride; + // X-coordinate of the top-left corner inside the destination + uint16_t dst_x; + // Y-coordinate of the top-left corner inside the destination + uint16_t dst_y; + // Height of the filled/copied/blended area + uint16_t height; + // Width of the filled/copied/blended area + uint16_t width; + + // Pointer to the source bitmap's first row + // (unused for fill operations) + void* src_row; + // Number of bytes per line in the source bitmap + // (unused for fill operations) + uint16_t src_stride; + // X-coordinate of the origin in the source bitmap + // (unused for fill operations) + uint16_t src_x; + // Y-coordinate of the origin in the source bitmap + // (unused for fill operations) + uint16_t src_y; + + // Foreground color used when copying/blending/filling + gl_color_t src_fg; + // Background color used when copying mono bitmaps + gl_color_t src_bg; + // Alpha value for fill operation (255 => normal fill, 0 => noop) + uint8_t src_alpha; + +} gl_bitblt_t; + +// Functions for RGB565 bitmap/framebuffer + +// Fills a rectangle with a solid color +void gl_rgb565_fill(const gl_bitblt_t* bb); +// Copies a mono bitmap (with 1-bit alpha channel) +void gl_rgb565_copy_mono1p(const gl_bitblt_t* bb); +// Copies a mono bitmap (with 4-bit alpha channel) +void gl_rgb565_copy_mono4(const gl_bitblt_t* bb); +// Copies an RGB565 bitmap +void gl_rgb565_copy_rgb565(const gl_bitblt_t* bb); +// Blends a mono bitmap (with 4-bit alpha channel) +// with the destination bitmap +void gl_rgb565_blend_mono4(const gl_bitblt_t* bb); + +// Functions for RGBA8888 bitmap/framebuffer +void gl_rgba8888_fill(const gl_bitblt_t* bb); +// Copies a mono bitmap (with 1-bit alpha channel) +void gl_rgba8888_copy_mono1p(const gl_bitblt_t* bb); +// Copies a mono bitmap (with 4-bit alpha channel) +void gl_rgba8888_copy_mono4(const gl_bitblt_t* bb); +// Copies an RGB565 bitmap +void gl_rgba8888_copy_rgb565(const gl_bitblt_t* bb); +// Copies an RGBA8888 bitmap +void gl_rgba8888_copy_rgba8888(const gl_bitblt_t* bb); +// Blends a mono bitmap (with 4-bit alpha channel) +// with the destination bitmap +void gl_rgba8888_blend_mono4(const gl_bitblt_t* bb); + +// Functions for Mono8 bitmap/framebuffer +void gl_mono8_fill(const gl_bitblt_t* bb); +// Copies a mono bitmap (with 1-bit alpha channel) +void gl_mono8_copy_mono1p(const gl_bitblt_t* bb); +// Copies a mono bitmap (with 4-bit alpha channel) +void gl_mono8_copy_mono4(const gl_bitblt_t* bb); +// Blends a mono bitmap (with 1-bit alpha channel) +// with the destination bitmap +void gl_mono8_blend_mono1p(const gl_bitblt_t* bb); +// Blends a mono bitmap (with 4-bit alpha channel) +// with the destination bitmap +void gl_mono8_blend_mono4(const gl_bitblt_t* bb); + +#endif // GL_BITBLT_H diff --git a/core/embed/lib/gl_bitblt_mono8.c b/core/embed/lib/gl_bitblt_mono8.c new file mode 100644 index 000000000..502944de3 --- /dev/null +++ b/core/embed/lib/gl_bitblt_mono8.c @@ -0,0 +1,110 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#include "gl_bitblt.h" + +void gl_mono8_fill(const gl_bitblt_t* bb) { + uint8_t* dst_ptr = (uint8_t*)bb->dst_row + bb->dst_x; + uint16_t height = bb->height; + + uint8_t fg = gl_color_lum(bb->src_fg); + + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + dst_ptr[x] = fg; + } + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + } +} + +void gl_mono8_copy_mono1p(const gl_bitblt_t* bb) { + uint8_t* dst_ptr = (uint8_t*)bb->dst_row + bb->dst_x; + uint8_t* src = (uint8_t*)bb->src_row; + uint16_t src_ofs = bb->src_stride * bb->src_y + bb->src_x; + uint16_t height = bb->height; + + uint8_t fg = gl_color_lum(bb->src_fg); + uint8_t bg = gl_color_lum(bb->src_bg); + + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + uint8_t mask = 1 << (7 - ((src_ofs + x) & 7)); + uint8_t data = src[(src_ofs + x) / 8]; + dst_ptr[x] = (data & mask) ? fg : bg; + } + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_ofs += bb->src_stride; + } +} + +void gl_mono8_copy_mono4(const gl_bitblt_t* bb) { + uint8_t* dst_ptr = (uint8_t*)bb->dst_row + bb->dst_x; + uint8_t* src_row = (uint8_t*)bb->src_row; + uint16_t height = bb->height; + + uint8_t fg = gl_color_lum(bb->src_fg); + uint8_t bg = gl_color_lum(bb->src_bg); + + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + uint8_t src_data = src_row[(x + bb->src_x) / 2]; + uint8_t src_lum = (x + bb->src_x) & 1 ? src_data >> 4 : src_data & 0xF; + dst_ptr[x] = (fg * src_lum + bg * (15 - src_lum)) / 15; + } + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_row += bb->src_stride / sizeof(*src_row); + } +} + +void gl_mono8_blend_mono1p(const gl_bitblt_t* bb) { + uint8_t* dst_ptr = (uint8_t*)bb->dst_row + bb->dst_x; + uint8_t* src = (uint8_t*)bb->src_row; + uint16_t src_ofs = bb->src_stride * bb->src_y + bb->src_x; + uint16_t height = bb->height; + + uint8_t fg = gl_color_lum(bb->src_fg); + + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + uint8_t mask = 1 << (7 - ((src_ofs + x) & 7)); + uint8_t data = src[(src_ofs + x) / 8]; + dst_ptr[x] = (data & mask) ? fg : dst_ptr[x]; + } + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_ofs += bb->src_stride; + } +} + +void gl_mono8_blend_mono4(const gl_bitblt_t* bb) { + uint8_t* dst_ptr = (uint8_t*)bb->dst_row + bb->dst_x; + uint8_t* src_row = (uint8_t*)bb->src_row; + uint16_t height = bb->height; + + uint8_t fg = gl_color_lum(bb->src_fg); + + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + uint8_t src_data = src_row[(x + bb->src_x) / 2]; + uint8_t src_alpha = (x + bb->src_x) & 1 ? src_data >> 4 : src_data & 0x0F; + dst_ptr[x] = (fg * src_alpha + dst_ptr[x] * (15 - src_alpha)) / 15; + } + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_row += bb->src_stride / sizeof(*src_row); + } +} diff --git a/core/embed/lib/gl_bitblt_rgb565.c b/core/embed/lib/gl_bitblt_rgb565.c new file mode 100644 index 000000000..15c929c28 --- /dev/null +++ b/core/embed/lib/gl_bitblt_rgb565.c @@ -0,0 +1,136 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#include "gl_bitblt.h" + +#if USE_DMA2D +#include "dma2d_bitblt.h" +#endif + +void gl_rgb565_fill(const gl_bitblt_t* bb) { +#if defined(USE_DMA2D) && !defined(TREZOR_EMULATOR) + if (!dma2d_rgb565_fill(bb)) +#endif + { + uint16_t* dst_ptr = (uint16_t*)bb->dst_row + bb->dst_x; + uint16_t height = bb->height; + + if (bb->src_alpha == 255) { + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + dst_ptr[x] = bb->src_fg; + } + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + } + } else { + uint8_t alpha = bb->src_alpha; + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + dst_ptr[x] = gl_color16_blend_a8(bb->src_fg, dst_ptr[x], alpha); + } + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + } + } + } +} + +void gl_rgb565_copy_mono1p(const gl_bitblt_t* bb) { + uint16_t* dst_ptr = (uint16_t*)bb->dst_row + bb->dst_x; + uint8_t* src = (uint8_t*)bb->src_row; + uint16_t src_ofs = bb->src_stride * bb->src_y + bb->src_x; + uint16_t height = bb->height; + + uint16_t fg = gl_color_to_color16(bb->src_fg); + uint16_t bg = gl_color_to_color16(bb->src_bg); + + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + uint8_t mask = 1 << (7 - ((src_ofs + x) & 7)); + uint8_t data = src[(src_ofs + x) / 8]; + dst_ptr[x] = (data & mask) ? fg : bg; + } + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_ofs += bb->src_stride; + } +} + +void gl_rgb565_copy_mono4(const gl_bitblt_t* bb) { +#if defined(USE_DMA2D) && !defined(TREZOR_EMULATOR) + if (!dma2d_rgb565_copy_mono4(bb)) +#endif + { + const gl_color16_t* gradient = + gl_color16_gradient_a4(bb->src_fg, bb->src_bg); + + uint16_t* dst_ptr = (uint16_t*)bb->dst_row + bb->dst_x; + uint8_t* src_row = (uint8_t*)bb->src_row; + uint16_t height = bb->height; + + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + uint8_t fg_data = src_row[(x + bb->src_x) / 2]; + uint8_t fg_lum = (x + bb->src_x) & 1 ? fg_data >> 4 : fg_data & 0xF; + dst_ptr[x] = gradient[fg_lum]; + } + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_row += bb->src_stride / sizeof(*src_row); + } + } +} + +void gl_rgb565_copy_rgb565(const gl_bitblt_t* bb) { +#if defined(USE_DMA2D) && !defined(TREZOR_EMULATOR) + if (!dma2d_rgb565_copy_rgb565(bb)) +#endif + { + uint16_t* dst_ptr = (uint16_t*)bb->dst_row + bb->dst_x; + uint16_t* src_ptr = (uint16_t*)bb->src_row + bb->src_x; + uint16_t height = bb->height; + + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + dst_ptr[x] = src_ptr[x]; + } + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_ptr += bb->src_stride / sizeof(*src_ptr); + } + } +} + +void gl_rgb565_blend_mono4(const gl_bitblt_t* bb) { +#if defined(USE_DMA2D) && !defined(TREZOR_EMULATOR) + if (!dma2d_rgb565_blend_mono4(bb)) +#endif + { + uint16_t* dst_ptr = (uint16_t*)bb->dst_row + bb->dst_x; + uint8_t* src_row = (uint8_t*)bb->src_row; + uint16_t height = bb->height; + + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + uint8_t fg_data = src_row[(x + bb->src_x) / 2]; + uint8_t fg_alpha = (x + bb->src_x) & 1 ? fg_data >> 4 : fg_data & 0x0F; + dst_ptr[x] = gl_color16_blend_a4( + bb->src_fg, gl_color16_to_color(dst_ptr[x]), fg_alpha); + } + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_row += bb->src_stride / sizeof(*src_row); + } + } +} diff --git a/core/embed/lib/gl_bitblt_rgba8888.c b/core/embed/lib/gl_bitblt_rgba8888.c new file mode 100644 index 000000000..6c709d120 --- /dev/null +++ b/core/embed/lib/gl_bitblt_rgba8888.c @@ -0,0 +1,156 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#include "gl_bitblt.h" + +#if USE_DMA2D +#include "dma2d_bitblt.h" +#endif + +void gl_rgba8888_fill(const gl_bitblt_t* bb) { +#if defined(USE_DMA2D) && !defined(TREZOR_EMULATOR) + if (!dma2d_rgba8888_fill(bb)) +#endif + { + uint32_t* dst_ptr = (uint32_t*)bb->dst_row + bb->dst_x; + uint16_t height = bb->height; + + if (bb->src_alpha == 255) { + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + dst_ptr[x] = gl_color_to_color32(bb->src_fg); + } + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + } + } else { + uint8_t alpha = bb->src_alpha; + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + dst_ptr[x] = gl_color32_blend_a8( + bb->src_fg, gl_color32_to_color(dst_ptr[x]), alpha); + } + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + } + } + } +} + +void gl_rgba8888_copy_mono1p(const gl_bitblt_t* bb) { + uint32_t* dst_ptr = (uint32_t*)bb->dst_row + bb->dst_x; + uint8_t* src = (uint8_t*)bb->src_row; + uint16_t src_ofs = bb->src_stride * bb->src_y + bb->src_x; + uint16_t height = bb->height; + + uint32_t fg = gl_color_to_color32(bb->src_fg); + uint32_t bg = gl_color_to_color32(bb->src_bg); + + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + uint8_t mask = 1 << (7 - ((src_ofs + x) & 7)); + uint8_t data = src[(src_ofs + x) / 8]; + dst_ptr[x] = (data & mask) ? fg : bg; + } + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_ofs += bb->src_stride; + } +} + +void gl_rgba8888_copy_mono4(const gl_bitblt_t* bb) { +#if defined(USE_DMA2D) && !defined(TREZOR_EMULATOR) + if (!dma2d_rgba8888_copy_mono4(bb)) +#endif + { + const gl_color32_t* gradient = + gl_color32_gradient_a4(bb->src_fg, bb->src_bg); + + uint32_t* dst_ptr = (uint32_t*)bb->dst_row + bb->dst_x; + uint8_t* src_row = (uint8_t*)bb->src_row; + uint16_t height = bb->height; + + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + uint8_t fg_data = src_row[(x + bb->src_x) / 2]; + uint8_t fg_lum = (x + bb->src_x) & 1 ? fg_data >> 4 : fg_data & 0xF; + dst_ptr[x] = gradient[fg_lum]; + } + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_row += bb->src_stride / sizeof(*src_row); + } + } +} + +void gl_rgba8888_copy_rgb565(const gl_bitblt_t* bb) { +#if defined(USE_DMA2D) && !defined(TREZOR_EMULATOR) + if (!dma2d_rgba8888_copy_rgb565(bb)) +#endif + { + uint32_t* dst_ptr = (uint32_t*)bb->dst_row + bb->dst_x; + uint16_t* src_ptr = (uint16_t*)bb->src_row + bb->src_x; + uint16_t height = bb->height; + + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + dst_ptr[x] = gl_color16_to_color32(src_ptr[x]); + } + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_ptr += bb->src_stride / sizeof(*src_ptr); + } + } +} + +void gl_rgba8888_copy_rgba8888(const gl_bitblt_t* bb) { +#if defined(USE_DMA2D) && !defined(TREZOR_EMULATOR) + if (!dma2d_rgba8888_copy_rgba8888(bb)) +#endif + { + uint32_t* dst_ptr = (uint32_t*)bb->dst_row + bb->dst_x; + uint32_t* src_ptr = (uint32_t*)bb->src_row + bb->src_x; + uint16_t height = bb->height; + + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + dst_ptr[x] = src_ptr[x]; + } + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_ptr += bb->src_stride / sizeof(*src_ptr); + } + } +} + +void gl_rgba8888_blend_mono4(const gl_bitblt_t* bb) { +#if defined(USE_DMA2D) && !defined(TREZOR_EMULATOR) + if (!dma2d_rgba8888_blend_mono4(bb)) +#endif + { + uint32_t* dst_ptr = (uint32_t*)bb->dst_row + bb->dst_x; + uint8_t* src_row = (uint8_t*)bb->src_row; + uint16_t height = bb->height; + + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + uint8_t fg_data = src_row[(x + bb->src_x) / 2]; + uint8_t fg_alpha = (x + bb->src_x) & 1 ? fg_data >> 4 : fg_data & 0x0F; + dst_ptr[x] = gl_color32_blend_a4( + bb->src_fg, gl_color32_to_color(dst_ptr[x]), fg_alpha); + } + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_row += bb->src_stride / sizeof(*src_row); + } + } +} diff --git a/core/embed/lib/gl_color.c b/core/embed/lib/gl_color.c new file mode 100644 index 000000000..31841418e --- /dev/null +++ b/core/embed/lib/gl_color.c @@ -0,0 +1,49 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#include "gl_color.h" +#include "colors.h" + +const gl_color16_t* gl_color16_gradient_a4(gl_color_t fg_color, + gl_color_t bg_color) { + static gl_color16_t cache[16] = {0}; + + if (gl_color_to_color16(bg_color) != cache[0] || + gl_color_to_color16(fg_color) != cache[15]) { + for (int alpha = 0; alpha < 16; alpha++) { + cache[alpha] = gl_color16_blend_a4(fg_color, bg_color, alpha); + } + } + + return cache; +} + +const gl_color32_t* gl_color32_gradient_a4(gl_color_t fg_color, + gl_color_t bg_color) { + static gl_color32_t cache[16] = {0}; + + if (bg_color != gl_color32_to_color(cache[0]) || + fg_color != gl_color32_to_color(cache[15])) { + for (int alpha = 0; alpha < 16; alpha++) { + cache[alpha] = gl_color32_blend_a4(fg_color, bg_color, alpha); + } + } + + return cache; +} diff --git a/core/embed/lib/gl_color.h b/core/embed/lib/gl_color.h new file mode 100644 index 000000000..bd298741e --- /dev/null +++ b/core/embed/lib/gl_color.h @@ -0,0 +1,337 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#ifndef GL_COLOR_H +#define GL_COLOR_H + +#include + +#define GL_COLOR_16BIT +// #define GL_COLOR_32BIT + +// Color in RGB565 format +// +// |15 8 | 7 0| +// |---------------------------------| +// |r r r r r g g g | g g g b b b b b| +// |---------------------------------| + +typedef uint16_t gl_color16_t; + +// Color in RGBA8888 format +// +// |31 24 |23 16 |15 8 | 7 0 | +// |----------------------------------------------------------------------| +// |a a a a a a a a | r r r r r r r r | g g g g g g g g | b b b b b b b b | +// |----------------------------------------------------------------------| +// + +typedef uint32_t gl_color32_t; + +#ifdef GL_COLOR_16BIT +#define gl_color_t gl_color16_t +#define gl_color_to_color16(c) (c) +#define gl_color16_to_color(c) (c) +#define gl_color_to_color32(c) (gl_color16_to_color32(c)) +#define gl_color32_to_color(c) (gl_color32_to_color16(c)) +#define gl_color_lum(c) (gl_color16_lum(c)) +#elif GL_COLOR_32BIT +#define gl_color_t gl_color32_t +#define gl_color_to_color16(c) (gl_color32_to_color16(c)) +#define gl_color16_to_color(c) (gl_color16_to_color32(c)) +#define gl_color_to_color32(c) (c) +#define gl_color32_to_color(c) (c) +#define gl_color_lum(c) (gl_color32_lum(c)) +#else +#error "GL_COLOR_16BIT/32BIT not specified" +#endif + +// Extracts red component from gl_color16_t and converts it to 8-bit value +#define gl_color16_to_r(c) ((((c)&0xF800) >> 8) | (((c)&0xF800) >> 13)) +// Extracts green component from gl_color16_t and converts it to 8-bit value +#define gl_color16_to_g(c) ((((c)&0x07E0) >> 3) | (((c)&0x07E0) >> 9)) +// Extracts blue component from gl_color16_t and converts it to 8-bit value +#define gl_color16_to_b(c) ((((c)&0x001F) << 3) | (((c)&0x001F) >> 2)) + +// Extracts red component from gl_color32_t +#define gl_color32_to_r(c) (((c)&0x00FF0000) >> 16) +// Extracts green component from gl_color32_t +#define gl_color32_to_g(c) (((c)&0x0000FF00) >> 8) +// Extracts blue component from gl_color32_t +#define gl_color32_to_b(c) (((c)&0x000000FF) >> 0) + +// 4-bit linear interpolation between `fg` and `bg` +#define a4_lerp(fg, bg, alpha) (((fg) * (alpha) + ((bg) * (15 - (alpha)))) / 15) +// 8-bit linear interpolation between `fg` and `bg` +#define a8_lerp(fg, bg, alpha) \ + (((fg) * (alpha) + ((bg) * (255 - (alpha)))) / 255) + +// Constructs a 16-bit color from the given red (r), +// green (g), and blue (b) values in the range 0..255 +static inline gl_color16_t gl_color16_rgb(uint8_t r, uint8_t g, uint8_t b) { + return ((r & 0xF8U) << 8) | ((g & 0xFCU) << 3) | ((b & 0xF8U) >> 3); +} + +// Constructs a 32-bit color from the given red (r), +// green (g), and blue (b) values in the range 0..255. +// Alpha is set to 255. +static inline gl_color32_t gl_color32_rgb(uint8_t r, uint8_t g, uint8_t b) { + return (0xFFU << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; +} + +// Converts a 16-bit color to a 32-bit color; alpha is set to 255 +static inline gl_color32_t gl_color16_to_color32(gl_color16_t color) { + uint32_t r = gl_color16_to_r(color); + uint32_t g = gl_color16_to_g(color); + uint32_t b = gl_color16_to_b(color); + + return gl_color32_rgb(r, g, b); +} + +// Converts 32-bit color to 16-bit color, alpha is ignored +static inline gl_color16_t gl_color32_to_color16(gl_color32_t color) { + uint16_t r = (color & 0x00F80000) >> 8; + uint16_t g = (color & 0x0000FC00) >> 5; + uint16_t b = (color & 0x000000F8) >> 3; + + return r | g | b; +} + +// Converts 16-bit color into luminance (ranging from 0 to 255) +static inline uint8_t gl_color16_lum(gl_color16_t color) { + uint32_t r = gl_color16_to_r(color); + uint32_t g = gl_color16_to_g(color); + uint32_t b = gl_color16_to_b(color); + + return (r + g + b) / 3; +} + +// Converts 32-bit color into luminance (ranging from 0 to 255) +static inline uint8_t gl_color32_lum(gl_color16_t color) { + uint32_t r = gl_color32_to_r(color); + uint32_t g = gl_color32_to_g(color); + uint32_t b = gl_color32_to_b(color); + + return (r + g + b) / 3; +} + +#ifdef GL_COLOR_16BIT +// Blends foreground and background colors with 4-bit alpha +// +// Returns a color in 16-bit format +// +// If `alpha` is 0, the function returns the background color +// If `alpha` is 15, the function returns the foreground color +static inline gl_color16_t gl_color16_blend_a4(gl_color16_t fg, gl_color16_t bg, + uint8_t alpha) { + uint16_t fg_r = (fg & 0xF800) >> 11; + uint16_t bg_r = (bg & 0xF800) >> 11; + uint16_t r = a4_lerp(fg_r, bg_r, alpha); + + uint16_t fg_g = (fg & 0x07E0) >> 5; + uint16_t bg_g = (bg & 0x07E0) >> 5; + uint16_t g = a4_lerp(fg_g, bg_g, alpha); + + uint16_t fg_b = (fg & 0x001F) >> 0; + uint16_t bg_b = (bg & 0x001F) >> 0; + uint16_t b = a4_lerp(fg_b, bg_b, alpha); + + return (r << 11) | (g << 5) | b; +} + +// Blends foreground and background colors with 8-bit alpha +// +// Returns a color in 16-bit format +// +// If `alpha` is 0, the function returns the background color +// If `alpha` is 15, the function returns the foreground color +static inline gl_color16_t gl_color16_blend_a8(gl_color16_t fg, gl_color16_t bg, + uint8_t alpha) { + uint16_t fg_r = (fg & 0xF800) >> 11; + uint16_t bg_r = (bg & 0xF800) >> 11; + uint16_t r = a8_lerp(fg_r, bg_r, alpha); + + uint16_t fg_g = (fg & 0x07E0) >> 5; + uint16_t bg_g = (bg & 0x07E0) >> 5; + uint16_t g = a8_lerp(fg_g, bg_g, alpha); + + uint16_t fg_b = (fg & 0x001F) >> 0; + uint16_t bg_b = (bg & 0x001F) >> 0; + uint16_t b = a8_lerp(fg_b, bg_b, alpha); + + return (r << 11) | (g << 5) | b; +} + +// Blends foreground and background colors with 4-bit alpha +// +// Returns a color in 32-bit format +// +// If alpha is 0, the function returns the background color +// If alpha is 15, the function returns the foreground color +static inline gl_color32_t gl_color32_blend_a4(gl_color16_t fg, gl_color16_t bg, + uint8_t alpha) { + uint16_t fg_r = gl_color16_to_r(fg); + uint16_t bg_r = gl_color16_to_r(bg); + uint16_t r = a4_lerp(fg_r, bg_r, alpha); + + uint16_t fg_g = gl_color16_to_g(fg); + uint16_t bg_g = gl_color16_to_g(bg); + uint16_t g = a4_lerp(fg_g, bg_g, alpha); + + uint16_t fg_b = gl_color16_to_b(fg); + uint16_t bg_b = gl_color16_to_b(bg); + uint16_t b = a4_lerp(fg_b, bg_b, alpha); + + return gl_color32_rgb(r, g, b); +} + +// Blends foreground and background colors with 8-bit alpha +// +// Returns a color in 32-bit format +// +// If `alpha` is 0, the function returns the background color +// If `alpha` is 255, the function returns the foreground color +static inline gl_color32_t gl_color32_blend_a8(gl_color16_t fg, gl_color16_t bg, + uint8_t alpha) { + uint16_t fg_r = gl_color16_to_r(fg); + uint16_t bg_r = gl_color16_to_r(bg); + uint16_t r = a8_lerp(fg_r, bg_r, alpha); + + uint16_t fg_g = gl_color16_to_g(fg); + uint16_t bg_g = gl_color16_to_g(bg); + uint16_t g = a8_lerp(fg_g, bg_g, alpha); + + uint16_t fg_b = gl_color16_to_b(fg); + uint16_t bg_b = gl_color16_to_b(bg); + uint16_t b = a8_lerp(fg_b, bg_b, alpha); + + return gl_color32_rgb(r, g, b); +} + +#elif GL_COLOR_32BIT + +// Blends foreground and background colors with 4-bit alpha +// +// Returns a color in 16-bit format +// +// If `alpha` is 0, the function returns the background color +// If `alpha` is 15, the function returns the foreground color +static inline gl_color16_t gl_color16_blend_a4(gl_color32_t fg, gl_color32_t bg, + uint8_t alpha) { + uint16_t fg_r = gl_color32_to_r(fg); + uint16_t bg_r = gl_color32_to_r(bg); + uint16_t r = a4_lerp(fg_r, bg_r, alpha); + + uint16_t fg_g = gl_color32_to_g(fg); + uint16_t bg_g = gl_color32_to_g(bg); + uint16_t g = a4_lerp(fg_g, bg_g, alpha); + + uint16_t fg_b = gl_color32_to_b(fg); + uint16_t bg_b = gl_color32_to_b(bg); + uint16_t b = a4_lerp(fg_b, bg_b, alpha); + + return gl_color16_rgb(r, g, b); +} + +// Blends foreground and background colors with 8-bit alpha +// +// Returns a color in 16-bit format +// +// If `alpha` is 0, the function returns the background color +// If `alpha` is 255, the function returns the foreground color +static inline gl_color16_t gl_color16_blend_a8(gl_color32_t fg, gl_color32_t bg, + uint8_t alpha) { + uint16_t fg_r = gl_color32_to_r(fg); + uint16_t bg_r = gl_color32_to_r(bg); + uint16_t r = a8_lerp(fg_r, bg_r, alpha); + + uint16_t fg_g = gl_color32_to_g(fg); + uint16_t bg_g = gl_color32_to_g(bg); + uint16_t g = a8_lerp(fg_g, bg_g, alpha); + + uint16_t fg_b = gl_color32_to_b(fg); + uint16_t bg_b = gl_color32_to_b(bg); + uint16_t b = g = a8_lerp(fg_b, bg_b, alpha); + + return gl_color16_rgb(r, g, b); +} + +// Blends foreground and background colors with 4-bit alpha +// +// Returns a color in 32-bit format +// +// If `alpha` is 0, the function returns the background color +// If `alpha` is 15, the function returns the foreground color +static inline gl_color32_t gl_color32_blend_a4(gl_color32_t fg, gl_color32_t bg, + uint8_t alpha) { + uint16_t fg_r = gl_color32_to_r(fg); + uint16_t bg_r = gl_color32_to_r(bg); + uint16_t r = a4_lerp(fg_r, bg_r, alpha); + + uint16_t fg_g = gl_color32_to_g(fg); + uint16_t bg_g = gl_color32_to_g(bg); + uint16_t g = a4_lerp(fg_g, bg_g, alpha); + + uint16_t fg_b = gl_color32_to_b(fg); + uint16_t bg_b = gl_color32_to_b(bg); + uint16_t b = a4_lerp(fg_b, bg_b, alpha); + + return gl_color32_rgb(r, g, b); +} + +// Blends foreground and background colors with 8-bit alpha +// +// Returns a color in 32-bit format +// +// If `alpha` is 0, the function returns the background color +// If `alpha` is 15, the function returns the foreground color +static inline gl_color32_t gl_color32_blend_a8(gl_color32_t fg, gl_color32_t bg, + uint8_t alpha) { + uint16_t fg_r = gl_color32_to_r(fg); + uint16_t bg_r = gl_color32_to_r(bg); + uint16_t r = a8_lerp(fg_r, bg_r, alpha); + + uint16_t fg_g = gl_color32_to_g(fg); + uint16_t bg_g = gl_color32_to_g(bg); + uint16_t g = a8_lerp(fg_g, bg_g, alpha); + + uint16_t fg_b = gl_color32_to_b(fg); + uint16_t bg_b = gl_color32_to_b(bg); + uint16_t b = a8_lerp(fg_b, bg_b, alpha); + + return gl_color32_rgb(r, g, b); +} + +#else +#error "GL_COLOR_16BIT/32BIT not specified" +#endif + +// Returns a gradient as an array of 16 consecutive 16-bit colors +// +// Each element in the array represents a color, with `retval[0]` being +// the background (`bg`) color and `retval[15]` the foreground (`fg`) color +const gl_color16_t* gl_color16_gradient_a4(gl_color_t fg, gl_color_t bg); + +// Returns a gradient as an array of 16 consecutive 32-bit colors +// +// Each element in the array represents a color, with `retval[0]` being +// the background (`bg`) color and `retval[15]` the foreground (`fg`) color +const gl_color32_t* gl_color32_gradient_a4(gl_color_t fg, gl_color_t bg); + +#endif // TREZORHAL_GL_COLOR_H diff --git a/core/embed/lib/gl_draw.c b/core/embed/lib/gl_draw.c new file mode 100644 index 000000000..64f0c46c5 --- /dev/null +++ b/core/embed/lib/gl_draw.c @@ -0,0 +1,275 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#include + +#include "display_draw.h" +#include "fonts/fonts.h" +#include "gl_draw.h" + +typedef struct { + int16_t dst_x; + int16_t dst_y; + int16_t src_x; + int16_t src_y; + int16_t width; + int16_t height; +} gl_clip_t; + +static inline gl_clip_t gl_clip(gl_rect_t dst, const gl_bitmap_t* bitmap) { + int16_t dst_x = dst.x0; + int16_t dst_y = dst.y0; + + int16_t src_x = 0; + int16_t src_y = 0; + + if (bitmap != NULL) { + src_x += bitmap->offset.x; + src_y += bitmap->offset.y; + + // Normalize negative x-offset of bitmap + if (src_x < 0) { + dst_x -= src_x; + src_x = 0; + } + + // Normalize negative y-offset of src bitmap + if (src_y < 0) { + dst_y -= src_y; + src_y = 0; + } + } + + // Normalize negative top-left of destination rectangle + if (dst_x < 0) { + src_x -= dst_x; + dst_x = 0; + } + + if (dst_y < 0) { + src_y -= dst_y; + dst_y = 0; + } + + // Calculate dimension of effective rectangle + int16_t width = MIN(DISPLAY_RESX, dst.x1) - dst_x; + int16_t height = MIN(DISPLAY_RESY, dst.y1) - dst_y; + + if (bitmap != NULL) { + width = MIN(width, bitmap->size.x - src_x); + height = MIN(height, bitmap->size.y - src_y); + } + + gl_clip_t clip = { + .dst_x = dst_x, + .dst_y = dst_y, + .src_x = src_x, + .src_y = src_y, + .width = width, + .height = height, + }; + + return clip; +} + +void gl_clear(void) { + gl_bitblt_t bb = { + // Destination bitmap + .height = DISPLAY_RESX, + .width = DISPLAY_RESY, + .dst_row = NULL, + .dst_x = 0, + .dst_y = 0, + .dst_stride = 0, + + // Source bitmap + .src_fg = 0, + .src_alpha = 255, + }; + + display_fill(&bb); +} + +void gl_draw_bar(gl_rect_t rect, gl_color_t color) { + gl_clip_t clip = gl_clip(rect, NULL); + + if (clip.width <= 0 || clip.height <= 0) { + return; + } + + gl_bitblt_t bb = { + // Destination bitmap + .height = clip.height, + .width = clip.width, + .dst_row = NULL, + .dst_x = clip.dst_x, + .dst_y = clip.dst_y, + .dst_stride = 0, + + // Source bitmap + .src_fg = color, + .src_alpha = 255, + }; + + display_fill(&bb); +} + +void gl_draw_bitmap(gl_rect_t rect, const gl_bitmap_t* bitmap) { + gl_clip_t clip = gl_clip(rect, bitmap); + + if (clip.width <= 0 || clip.height <= 0) { + return; + } + + gl_bitblt_t bb = { + // Destination bitmap + .height = clip.height, + .width = clip.width, + .dst_row = NULL, + .dst_x = clip.dst_x, + .dst_y = clip.dst_y, + .dst_stride = 0, + + // Source bitmap + .src_row = (uint8_t*)bitmap->ptr + bitmap->stride * clip.src_y, + .src_x = clip.src_x, + .src_y = clip.src_y, + .src_stride = bitmap->stride, + .src_fg = bitmap->fg_color, + .src_bg = bitmap->bg_color, + .src_alpha = 255, + }; + + // Currently, we use `gl_draw_bitmap` exclusively for text rendering. + // Therefore, we are including the variant of `display_copy_xxx()` that is + // specifically needed for drawing glyphs in the format we are using + // to save some space in the flash memory. + +#if TREZOR_FONT_BPP == 1 + if (bitmap->format == GL_FORMAT_MONO1P) { + display_copy_mono1p(&bb); + } +#endif +#if TREZOR_FONT_BPP == 4 + if (bitmap->format == GL_FORMAT_MONO4) { + display_copy_mono4(&bb); + } +#endif +} + +#if TREZOR_FONT_BPP == 1 +#define GLYPH_FORMAT GL_FORMAT_MONO1P +#define GLYPH_STRIDE(w) (((w) + 7) / 8) +#elif TREZOR_FONT_BPP == 2 +#error Unsupported TREZOR_FONT_BPP value +#define GLYPH_FORMAT GL_FORMAT_MONO2 +#define GLYPH_STRIDE(w) (((w) + 3) / 4) +#elif TREZOR_FONT_BPP == 4 +#define GLYPH_FORMAT GL_FORMAT_MONO4 +#define GLYPH_STRIDE(w) (((w) + 1) / 2) +#elif TREZOR_FONT_BPP == 8 +#error Unsupported TREZOR_FONT_BPP value +#define GLYPH_FORMAT GL_FORMAT_MONO8 +#define GLYPH_STRIDE(w) (w) +#else +#error Unsupported TREZOR_FONT_BPP value +#endif + +#define GLYPH_WIDTH(g) ((g)[0]) +#define GLYPH_HEIGHT(g) ((g)[1]) +#define GLYPH_ADVANCE(g) ((g)[2]) +#define GLYPH_BEARING_X(g) ((g)[3]) +#define GLYPH_BEARING_Y(g) ((g)[4]) +#define GLYPH_DATA(g) ((void*)&(g)[5]) + +void gl_draw_text(gl_offset_t pos, const char* text, size_t maxlen, + const gl_text_attr_t* attr) { + if (text == NULL) { + return; + } + + gl_bitmap_t bitmap = { + .format = GLYPH_FORMAT, + .fg_color = attr->fg_color, + .bg_color = attr->bg_color, + }; + + int max_height = font_max_height(attr->font); + int baseline = font_baseline(attr->font); + + for (int i = 0; i < maxlen; i++) { + uint8_t ch = (uint8_t)text[i]; + + if (ch == 0 || pos.x >= DISPLAY_RESX) { + break; + } + + const uint8_t* glyph = font_get_glyph(attr->font, ch); + + if (glyph == NULL) { + continue; + } + + bitmap.ptr = GLYPH_DATA(glyph); + bitmap.stride = GLYPH_STRIDE(GLYPH_WIDTH(glyph)); + bitmap.size.x = GLYPH_WIDTH(glyph); + bitmap.size.y = GLYPH_HEIGHT(glyph); + + bitmap.offset.x = -GLYPH_BEARING_X(glyph); + bitmap.offset.y = -(max_height - baseline - GLYPH_BEARING_Y(glyph)); + + gl_draw_bitmap(gl_rect(pos.x, pos.y, DISPLAY_RESX, DISPLAY_RESY), &bitmap); + + pos.x += GLYPH_ADVANCE(glyph); + } +} + +// =============================================================== +// emulation of legacy functions + +void display_clear(void) { gl_clear(); } + +void display_bar(int x, int y, int w, int h, uint16_t c) { + gl_draw_bar(gl_rect_wh(x, y, w, h), c); +} + +void display_text(int x, int y, const char* text, int textlen, int font, + uint16_t fg_color, uint16_t bg_color) { + gl_text_attr_t attr = { + .font = font, + .fg_color = fg_color, + .bg_color = bg_color, + }; + + size_t maxlen = textlen < 0 ? UINT32_MAX : textlen; + gl_draw_text(gl_offset(x, y), text, maxlen, &attr); +} + +void display_text_center(int x, int y, const char* text, int textlen, int font, + uint16_t fg_color, uint16_t bg_color) { + gl_text_attr_t attr = { + .font = font, + .fg_color = fg_color, + .bg_color = bg_color, + }; + + size_t maxlen = textlen < 0 ? UINT32_MAX : textlen; + int w = font_text_width(font, text, textlen); + gl_draw_text(gl_offset(x - w / 2, y), text, maxlen, &attr); +} diff --git a/core/embed/lib/gl_draw.h b/core/embed/lib/gl_draw.h new file mode 100644 index 000000000..38b45e8be --- /dev/null +++ b/core/embed/lib/gl_draw.h @@ -0,0 +1,152 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#ifndef GL_DRAW_H +#define GL_DRAW_H + +#include "gl_color.h" + +// 2D rectangle coordinates +// +// `x0`, `y0` - top-left coordinates +// `x1`, `y1` - bottom-right coordinates point (not included) +typedef struct { + int16_t x0; + int16_t y0; + int16_t x1; + int16_t y1; +} gl_rect_t; + +// Builds a rectangle (`gl_rect_t`) from top-left coordinates and dimensions +static inline gl_rect_t gl_rect_wh(int16_t x, int16_t y, int16_t w, int16_t h) { + gl_rect_t rect = { + .x0 = x, + .y0 = y, + .x1 = x + w, + .y1 = y + h, + }; + + return rect; +} + +// Builds a rectangle (`gl_rect_t`) from top-left and bottom-right coordinates +static inline gl_rect_t gl_rect(int16_t x0, int16_t y0, int16_t x1, + int16_t y1) { + gl_rect_t rect = { + .x0 = x0, + .y0 = y0, + .x1 = x1, + .y1 = y1, + }; + + return rect; +} + +// 2D offset/ coordinates +typedef struct { + int16_t x; + int16_t y; +} gl_offset_t; + +// Builds a `gl_offset_t` structure +static inline gl_offset_t gl_offset(int16_t x, int16_t y) { + gl_offset_t offset = { + .x = x, + .y = y, + }; + + return offset; +} + +// 2D size in pixels +typedef struct { + int16_t x; + int16_t y; +} gl_size_t; + +// Builds a `gl_size_t` structure +static inline gl_size_t gl_size(int16_t x, int16_t y) { + gl_size_t size = { + .x = x, + .y = y, + }; + + return size; +} + +// Format of pixels in a bitmap +typedef enum { + GL_FORMAT_UNKNOWN, // + GL_FORMAT_MONO1P, // 1-bpp per pixel (packed) + GL_FORMAT_MONO4, // 4-bpp per pixel + GL_FORMAT_RGB565, // 16-bpp per pixel + GL_FORMAT_RGBA8888, // 32-bpp +} gl_format_t; + +// 2D bitmap reference +typedef struct { + // pointer to top-left pixel + void* ptr; + // stride in bytes + size_t stride; + // size in pixels + gl_size_t size; + // format of pixels, GL_FORMAT_xxx + uint8_t format; + // offset used when bitmap is drawed using gl_draw_bitmap() + gl_offset_t offset; + // foreground color (used with MONOx formats) + gl_color_t fg_color; + // background color (used with MONOx formats) + gl_color_t bg_color; +} gl_bitmap_t; + +// Text attributes (font and color) +typedef struct { + // Font identifier + int font; + // Foreground color + gl_color_t fg_color; + // Background color + gl_color_t bg_color; +} gl_text_attr_t; + +// Fills a rectangle with a specified color. +void gl_draw_bar(gl_rect_t rect, gl_color_t color); + +// Draws a bitmap into the specified rectangle. +// +// The destination rectangle may not be fully filled if the source bitmap +// is smaller than destination rectangle or if the bitmap is translated by +// an offset partially or completely outside the destination rectangle. +// +// Currently, we use `gl_draw_bitmap` exclusively for text rendering. +// Not all bitmap formats are supported now. Please see the implementation. +void gl_draw_bitmap(gl_rect_t rect, const gl_bitmap_t* bitmap); + +// Draws a text to the specified position. +// +// `offset` - the most left point on the font baseline +// `text` - utf-8 text +// `maxlen` - maximum number of characters displayed (use SIZE_MAX when not +// specified) `attr` - font & text color +void gl_draw_text(gl_offset_t offset, const char* text, size_t maxlen, + const gl_text_attr_t* attr); + +#endif // GL_DRAW_H diff --git a/core/embed/lib/terminal.c b/core/embed/lib/terminal.c index 9411cbdff..9a471aebc 100644 --- a/core/embed/lib/terminal.c +++ b/core/embed/lib/terminal.c @@ -24,7 +24,8 @@ #include "display.h" #include TREZOR_BOARD -#ifndef TREZOR_PRINT_DISABLE +#include "fonts/fonts.h" +#include "gl_draw.h" #define TERMINAL_COLS (DISPLAY_RESX / 6) #define TERMINAL_ROWS (DISPLAY_RESY / 8) @@ -39,6 +40,62 @@ void term_set_color(uint16_t fgcolor, uint16_t bgcolor) { terminal_bgcolor = bgcolor; } +#ifdef NEW_RENDERING + +// Font_Bitmap contains 96 (0x20 - 0x7F) 5x7 glyphs +// Each glyph consists of 5 bytes (each byte represents one column) +// +// This function converts the glyph into the format compatible +// with `display_copy_mono1p()` functions. +static uint64_t term_glyph_bits(char ch) { + union { + uint64_t u64; + uint8_t bytes[8]; + } result = {0}; + + if (ch > ' ' && ch < 128) { + const uint8_t *b = &Font_Bitmap[(ch - ' ') * 5]; + + for (int y = 0; y < 7; y++) { + uint8_t mask = 1 << y; + result.bytes[y] |= ((b[0] & mask) ? 128 : 0) + ((b[1] & mask) ? 64 : 0) + + ((b[2] & mask) ? 32 : 0) + ((b[3] & mask) ? 16 : 0) + + ((b[4] & mask) ? 8 : 0); + } + } + return result.u64; +} + +// Redraws specified rows to the display +static void term_redraw_rows(int start_row, int row_count) { + uint64_t glyph_bits = 0; + gl_bitblt_t bb = { + .height = 8, + .width = 6, + .dst_row = NULL, + .dst_x = 0, + .dst_y = 0, + .dst_stride = 0, + + .src_row = &glyph_bits, + .src_x = 0, + .src_y = 0, + .src_stride = 8, + .src_fg = terminal_fgcolor, + .src_bg = terminal_bgcolor, + }; + + for (int y = start_row; y < start_row + row_count; y++) { + bb.dst_y = y * 8; + for (int x = 0; x < TERMINAL_COLS; x++) { + glyph_bits = term_glyph_bits(terminal_fb[y][x]); + bb.dst_x = x * 6; + display_copy_mono1p(&bb); + } + } +} +#endif // NEW_RENDERING + // display text using bitmap font void term_print(const char *text, int textlen) { static uint8_t row = 0, col = 0; @@ -77,6 +134,10 @@ void term_print(const char *text, int textlen) { } } +#ifdef NEW_RENDERING + term_redraw_rows(0, TERMINAL_ROWS); + display_refresh(); +#else // NEW RENDERING // render buffer to display display_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1); for (int i = 0; i < DISPLAY_RESX * DISPLAY_RESY; i++) { @@ -105,6 +166,7 @@ void term_print(const char *text, int textlen) { } display_pixeldata_dirty(); display_refresh(); +#endif } #ifdef TREZOR_EMULATOR @@ -127,5 +189,3 @@ void term_printf(const char *fmt, ...) { va_end(va); } } - -#endif // TREZOR_PRINT_DISABLE diff --git a/core/embed/lib/terminal.h b/core/embed/lib/terminal.h index 7cc036dff..d1dc9bfa0 100644 --- a/core/embed/lib/terminal.h +++ b/core/embed/lib/terminal.h @@ -22,11 +22,9 @@ #include "colors.h" -#ifndef TREZOR_PRINT_DISABLE void term_set_color(uint16_t fgcolor, uint16_t bgcolor); void term_print(const char *text, int textlen); void term_printf(const char *fmt, ...) __attribute__((__format__(__printf__, 1, 2))); -#endif #endif // LIB_TERMINAL_H diff --git a/core/embed/prodtest/main.c b/core/embed/prodtest/main.c index 71a822c99..69f40bb2a 100644 --- a/core/embed/prodtest/main.c +++ b/core/embed/prodtest/main.c @@ -26,6 +26,7 @@ #include "button.h" #include "common.h" #include "display.h" +#include "display_draw.h" #include "display_utils.h" #include "fault_handlers.h" #include "flash.h" diff --git a/core/embed/reflash/main.c b/core/embed/reflash/main.c index 06b3b2d3a..b98a3bceb 100644 --- a/core/embed/reflash/main.c +++ b/core/embed/reflash/main.c @@ -25,6 +25,7 @@ #include "common.h" #include "display.h" +#include "display_draw.h" #include "flash.h" #include "image.h" #include "model.h" diff --git a/core/embed/rust/Cargo.toml b/core/embed/rust/Cargo.toml index 2faab74a7..6454358e8 100644 --- a/core/embed/rust/Cargo.toml +++ b/core/embed/rust/Cargo.toml @@ -15,10 +15,18 @@ micropython = [] protobuf = ["micropython"] ui = [] dma2d = [] +xframebuffer = [] +display_mono = [] +display_rgb565 = [] +display_rgba8888 = [] framebuffer = [] framebuffer32bit = [] ui_debug = [] ui_bounds = [] +ui_antialiasing = [] +ui_blurring = [] +ui_jpeg_decoder = ["jpeg"] +new_rendering = [] bootloader = [] button = [] touch = [] diff --git a/core/embed/rust/trezorhal.h b/core/embed/rust/trezorhal.h index 1ee7ac69a..e393d3451 100644 --- a/core/embed/rust/trezorhal.h +++ b/core/embed/rust/trezorhal.h @@ -1,12 +1,15 @@ #include TREZOR_BOARD + #include "buffers.h" #include "button.h" #include "common.h" #include "display.h" -#include "display_interface.h" +#include "display_draw.h" #include "dma2d.h" +#include "dma2d_bitblt.h" #include "flash.h" #include "fonts/fonts.h" +#include "gl_bitblt.h" #include "haptic.h" #include "model.h" #include "rgb_led.h" diff --git a/core/embed/trezorhal/boards/stm32f429i-disc1.h b/core/embed/trezorhal/boards/stm32f429i-disc1.h index 9f8b0a65e..cd37a9712 100644 --- a/core/embed/trezorhal/boards/stm32f429i-disc1.h +++ b/core/embed/trezorhal/boards/stm32f429i-disc1.h @@ -3,18 +3,16 @@ #define HSE_8MHZ -#define MAX_DISPLAY_RESX 240 -#define MAX_DISPLAY_RESY 320 #define DISPLAY_RESX 240 #define DISPLAY_RESY 320 +#define DISPLAY_COLOR_MODE DMA2D_OUTPUT_RGB565 +#define DISPLAY_LEGACY_HEADER "displays/ltdc.h" #define USE_I2C 1 #define USE_TOUCH 1 #define USE_SDRAM 1 #define USE_RGB_COLORS 1 -#include "displays/ltdc.h" - #define I2C_COUNT 1 #define I2C_INSTANCE_0 I2C3 #define I2C_INSTANCE_0_CLK_EN __HAL_RCC_I2C3_CLK_ENABLE diff --git a/core/embed/trezorhal/boards/stm32u5a9j-dk.h b/core/embed/trezorhal/boards/stm32u5a9j-dk.h index 3ff3673ee..015cc1643 100644 --- a/core/embed/trezorhal/boards/stm32u5a9j-dk.h +++ b/core/embed/trezorhal/boards/stm32u5a9j-dk.h @@ -11,7 +11,11 @@ //#define USE_DISP_I8080_8BIT_DW 1 #define USE_HASH_PROCESSOR 1 -#include "displays/dsi.h" +#define DISPLAY_RESX 240 +#define DISPLAY_RESY 240 + +#define DISPLAY_COLOR_MODE DMA2D_OUTPUT_ARGB8888 +#define DISPLAY_LEGACY_HEADER "displays/dsi.h" #define I2C_COUNT 1 #define I2C_INSTANCE_0 I2C5 diff --git a/core/embed/trezorhal/boards/trezor_1.h b/core/embed/trezorhal/boards/trezor_1.h index 4f126a720..7fec05ac7 100644 --- a/core/embed/trezorhal/boards/trezor_1.h +++ b/core/embed/trezorhal/boards/trezor_1.h @@ -5,7 +5,8 @@ #define USE_BUTTON 1 -#include "displays/vg-2864ksweg01.h" +#define DISPLAY_RESX 128 +#define DISPLAY_RESY 64 #define BTN_LEFT_PIN GPIO_PIN_5 #define BTN_LEFT_PORT GPIOC diff --git a/core/embed/trezorhal/boards/trezor_r_v10.h b/core/embed/trezorhal/boards/trezor_r_v10.h index be44e5585..b48479d52 100644 --- a/core/embed/trezorhal/boards/trezor_r_v10.h +++ b/core/embed/trezorhal/boards/trezor_r_v10.h @@ -8,7 +8,9 @@ #define USE_I2C 1 #define USE_CONSUMPTION_MASK 1 -#include "displays/vg-2864ksweg01.h" +#define DISPLAY_RESX 128 +#define DISPLAY_RESY 64 +#define DISPLAY_LEGACY_HEADER "displays/vg-2864ksweg01.h" #define BTN_LEFT_PIN GPIO_PIN_10 #define BTN_LEFT_PORT GPIOC diff --git a/core/embed/trezorhal/boards/trezor_r_v3.h b/core/embed/trezorhal/boards/trezor_r_v3.h index 242a3f541..ffe8b6914 100644 --- a/core/embed/trezorhal/boards/trezor_r_v3.h +++ b/core/embed/trezorhal/boards/trezor_r_v3.h @@ -6,7 +6,9 @@ #define USE_BUTTON 1 #define USE_SBU 1 -#include "displays/ug-2828tswig01.h" +#define DISPLAY_RESX 128 +#define DISPLAY_RESY 128 +#define DISPLAY_LEGACY_HEADER "displays/ug-2828tswig01.h" #define BTN_LEFT_PIN GPIO_PIN_0 #define BTN_LEFT_PORT GPIOA diff --git a/core/embed/trezorhal/boards/trezor_r_v4.h b/core/embed/trezorhal/boards/trezor_r_v4.h index 29054d200..9dbbb6267 100644 --- a/core/embed/trezorhal/boards/trezor_r_v4.h +++ b/core/embed/trezorhal/boards/trezor_r_v4.h @@ -6,7 +6,9 @@ #define USE_BUTTON 1 #define USE_SBU 1 -#include "displays/vg-2864ksweg01.h" +#define DISPLAY_RESX 128 +#define DISPLAY_RESY 64 +#define DISPLAY_LEGACY_HEADER "displays/vg-2864ksweg01.h" #define BTN_LEFT_PIN GPIO_PIN_5 #define BTN_LEFT_PORT GPIOC diff --git a/core/embed/trezorhal/boards/trezor_r_v6.h b/core/embed/trezorhal/boards/trezor_r_v6.h index 72a7cedea..d177c3d72 100644 --- a/core/embed/trezorhal/boards/trezor_r_v6.h +++ b/core/embed/trezorhal/boards/trezor_r_v6.h @@ -6,7 +6,9 @@ #define USE_BUTTON 1 #define USE_SBU 1 -#include "displays/vg-2864ksweg01.h" +#define DISPLAY_RESX 128 +#define DISPLAY_RESY 64 +#define DISPLAY_LEGACY_HEADER "displays/vg-2864ksweg01.h" #define BTN_LEFT_PIN GPIO_PIN_10 #define BTN_LEFT_PORT GPIOC diff --git a/core/embed/trezorhal/boards/trezor_t.h b/core/embed/trezorhal/boards/trezor_t.h index 557f8bb55..14e2ffd3f 100644 --- a/core/embed/trezorhal/boards/trezor_t.h +++ b/core/embed/trezorhal/boards/trezor_t.h @@ -3,9 +3,6 @@ #define HSE_8MHZ -#define DISPLAY_RESX 240 -#define DISPLAY_RESY 240 - #define USE_SD_CARD 1 #define USE_I2C 1 #define USE_TOUCH 1 @@ -14,12 +11,14 @@ #define USE_BACKLIGHT 1 #define USE_DISP_I8080_8BIT_DW 1 -#include "displays/panels/lx154a2422.h" -#include "displays/st7789v.h" +#define DISPLAY_RESX 240 +#define DISPLAY_RESY 240 +#define DISPLAY_COLOR_MODE DMA2D_OUTPUT_RGB565 +#define DISPLAY_LEGACY_HEADER "displays/st7789v.h" + #define DISPLAY_IDENTIFY 1 #define DISPLAY_TE_PORT GPIOD #define DISPLAY_TE_PIN GPIO_PIN_12 -#define TRANSFORM_TOUCH_COORDS lx154a2422_transform_touch_coords #define BACKLIGHT_PWM_FREQ 10000 #define BACKLIGHT_PWM_TIM TIM1 diff --git a/core/embed/trezorhal/boards/trezor_t3t1_revE.h b/core/embed/trezorhal/boards/trezor_t3t1_revE.h index 95e11f796..4fd2f7b48 100644 --- a/core/embed/trezorhal/boards/trezor_t3t1_revE.h +++ b/core/embed/trezorhal/boards/trezor_t3t1_revE.h @@ -1,9 +1,6 @@ #ifndef _TREZOR_T3T1_H #define _TREZOR_T3T1_H -#define DISPLAY_RESX 240 -#define DISPLAY_RESY 240 - #define VDD_3V3 1 #define USE_SD_CARD 1 @@ -16,8 +13,11 @@ #define USE_BACKLIGHT 1 #define USE_HASH_PROCESSOR 1 -#include "displays/panels/lx154a2422.h" -#include "displays/st7789v.h" +#define DISPLAY_RESX 240 +#define DISPLAY_RESY 240 +#define DISPLAY_LEGACY_HEADER "displays/st7789v.h" +#define DISPLAY_COLOR_MODE DMA2D_OUTPUT_RGB565 + #define DISPLAY_IDENTIFY 1 #define DISPLAY_TE_PORT GPIOD #define DISPLAY_TE_PIN GPIO_PIN_12 @@ -26,10 +26,6 @@ #define DISPLAY_TE_INTERRUPT_GPIOSEL EXTI_GPIOD #define DISPLAY_TE_INTERRUPT_EXTI_LINE EXTI_LINE_12 -#define DISPLAY_PANEL_INIT_SEQ lx154a2422_init_seq -#define DISPLAY_PANEL_ROTATE lx154a2422_rotate -#define TRANSFORM_TOUCH_COORDS lx154a2422_transform_touch_coords - #define BACKLIGHT_PWM_FREQ 12500 #define BACKLIGHT_PWM_TIM TIM17 #define BACKLIGHT_PWM_TIM_CLK_EN __HAL_RCC_TIM17_CLK_ENABLE diff --git a/core/embed/trezorhal/boards/trezor_t3t1_v4.h b/core/embed/trezorhal/boards/trezor_t3t1_v4.h index a43dee378..bf2c0a00d 100644 --- a/core/embed/trezorhal/boards/trezor_t3t1_v4.h +++ b/core/embed/trezorhal/boards/trezor_t3t1_v4.h @@ -1,9 +1,6 @@ #ifndef _TREZOR_T3T1_H #define _TREZOR_T3T1_H -#define DISPLAY_RESX 240 -#define DISPLAY_RESY 240 - #define VDD_3V3 1 #define HSE_16MHZ 1 @@ -17,8 +14,11 @@ #define USE_BACKLIGHT 1 #define USE_HASH_PROCESSOR 1 -#include "displays/panels/lx154a2422.h" -#include "displays/st7789v.h" +#define DISPLAY_RESX 240 +#define DISPLAY_RESY 240 +#define DISPLAY_LEGACY_HEADER "displays/st7789v.h" +#define DISPLAY_COLOR_MODE DMA2D_OUTPUT_RGB565 + #define DISPLAY_IDENTIFY 1 #define DISPLAY_TE_PORT GPIOD #define DISPLAY_TE_PIN GPIO_PIN_12 @@ -27,10 +27,6 @@ #define DISPLAY_TE_INTERRUPT_GPIOSEL EXTI_GPIOD #define DISPLAY_TE_INTERRUPT_EXTI_LINE EXTI_LINE_12 -#define DISPLAY_PANEL_INIT_SEQ lx154a2422_init_seq -#define DISPLAY_PANEL_ROTATE lx154a2422_rotate -#define TRANSFORM_TOUCH_COORDS lx154a2422_transform_touch_coords - #define BACKLIGHT_PWM_FREQ 12500 #define BACKLIGHT_PWM_TIM TIM8 #define BACKLIGHT_PWM_TIM_CLK_EN __HAL_RCC_TIM8_CLK_ENABLE diff --git a/core/embed/lib/display_interface.h b/core/embed/trezorhal/display.h similarity index 89% rename from core/embed/lib/display_interface.h rename to core/embed/trezorhal/display.h index 19f68161a..5e296a8f7 100644 --- a/core/embed/lib/display_interface.h +++ b/core/embed/trezorhal/display.h @@ -17,13 +17,22 @@ * along with this program. If not, see . */ -#ifndef _DISPLAY_INTERFACE_H -#define _DISPLAY_INTERFACE_H +#ifndef TREZORHAL_DISPLAY_H +#define TREZORHAL_DISPLAY_H + +#if NEW_RENDERING +#include +#else #include #include "common.h" + #include TREZOR_BOARD +#ifdef DISPLAY_LEGACY_HEADER +#include DISPLAY_LEGACY_HEADER +#endif + #ifndef DISPLAY_FRAMEBUFFER_OFFSET_Y #define DISPLAY_FRAMEBUFFER_OFFSET_Y 0 #endif @@ -68,4 +77,5 @@ uint8_t *display_get_wr_addr(void); void display_shift_window(uint16_t pixels); uint16_t display_get_window_offset(void); -#endif //_DISPLAY_INTERFACE_H +#endif // NEW_RENDERING +#endif // TREZORHAL_DISPLAY_H diff --git a/core/embed/trezorhal/dma2d_bitblt.h b/core/embed/trezorhal/dma2d_bitblt.h new file mode 100644 index 000000000..fc327de6e --- /dev/null +++ b/core/embed/trezorhal/dma2d_bitblt.h @@ -0,0 +1,45 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#ifndef TREZORHAL_DMA2D_BITBLT_H +#define TREZORHAL_DMA2D_BITBLT_H + +#include "gl_bitblt.h" + +// Waits until any pending DMA2D operation is finished +void dma2d_wait(void); + +// Following functions are hardware (DMA2D) accelerated versions +// of `gl_rgb565_xxx()` and `gl_rgba8888_xxx()` function from `gl_bitblt.h` + +// These functions may return `false`, indicating that the accelerated +// operation cannot be performed and must be implemented in software + +bool dma2d_rgb565_fill(const gl_bitblt_t* bb); +bool dma2d_rgb565_copy_mono4(const gl_bitblt_t* bb); +bool dma2d_rgb565_copy_rgb565(const gl_bitblt_t* bb); +bool dma2d_rgb565_blend_mono4(const gl_bitblt_t* bb); + +bool dma2d_rgba8888_fill(const gl_bitblt_t* bb); +bool dma2d_rgba8888_copy_mono4(const gl_bitblt_t* bb); +bool dma2d_rgba8888_copy_rgb565(const gl_bitblt_t* bb); +bool dma2d_rgba8888_copy_rgba8888(const gl_bitblt_t* bb); +bool dma2d_rgba8888_blend_mono4(const gl_bitblt_t* bb); + +#endif // TREZORHAL_DMA2D_BITBLT_H diff --git a/core/embed/trezorhal/stm32f4/common.c b/core/embed/trezorhal/stm32f4/common.c index 1c6d48f76..058407388 100644 --- a/core/embed/trezorhal/stm32f4/common.c +++ b/core/embed/trezorhal/stm32f4/common.c @@ -219,7 +219,11 @@ void collect_hw_entropy(void) { // where this setting might be unknown void ensure_compatible_settings(void) { #ifdef TREZOR_MODEL_T +#ifdef NEW_RENDERING + display_set_compatible_settings(); +#else display_set_big_endian(); +#endif display_orientation(0); set_core_clock(CLOCK_168_MHZ); backlight_pwm_set_slow(); diff --git a/core/embed/trezorhal/stm32f4/display/st-7789/display_driver.c b/core/embed/trezorhal/stm32f4/display/st-7789/display_driver.c new file mode 100644 index 000000000..848843c2f --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/st-7789/display_driver.c @@ -0,0 +1,149 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#include + +#include + +#include "display_fb.h" +#include "display_io.h" +#include "display_panel.h" + +#include "backlight_pwm.h" +#include "supervise.h" + +#ifndef BOARDLOADER +#include "bg_copy.h" +#endif + +#if (DISPLAY_RESX != 240) || (DISPLAY_RESY != 240) +#error "Incompatible display resolution" +#endif + +// Display driver context. +typedef struct { + // Current display orientation (0, 90, 180, 270) + int orientation_angle; +} display_driver_t; + +// Display driver instance +static display_driver_t g_display_driver; + +void display_init(void) { + display_driver_t* drv = &g_display_driver; + memset(drv, 0, sizeof(display_driver_t)); + + display_io_init_gpio(); + display_io_init_fmc(); + display_panel_init(); + display_panel_set_little_endian(); + backlight_pwm_init(); + +#ifdef XFRAMEBUFFER + display_io_init_te_interrupt(); +#endif +} + +void display_reinit(void) { + display_driver_t* drv = &g_display_driver; + memset(drv, 0, sizeof(display_driver_t)); + + // Reinitialize FMC to set correct timing + // We have to do this in reinit because boardloader is fixed. + display_io_init_fmc(); + + // Important for model T as this is not set in boardloader + display_panel_set_little_endian(); + display_panel_init_gamma(); + backlight_pwm_reinit(); + +#ifdef XFRAMEBUFFER + display_io_init_te_interrupt(); +#endif +} + +void display_finish_actions(void) { +#ifdef XFRAMEBUFFER +#ifndef BOARDLOADER + bg_copy_wait(); +#endif +#endif +} + +int display_set_backlight(int level) { +#ifdef XFRAMEBUFFER +#ifndef BOARDLOADER + // wait for DMA transfer to finish before changing backlight + // so that we know that panel has current data + if (backlight_pwm_get() != level && !is_mode_handler()) { + bg_copy_wait(); + } +#endif +#endif + + return backlight_pwm_set(level); +} + +int display_get_backlight(void) { return backlight_pwm_get(); } + +int display_set_orientation(int angle) { + display_driver_t* drv = &g_display_driver; + + if (angle != drv->orientation_angle) { + if (angle == 0 || angle == 90 || angle == 180 || angle == 270) { + drv->orientation_angle = angle; + +#ifdef XFRAMEBUFFER + display_physical_fb_clear(); +#endif + + display_panel_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1); + for (uint32_t i = 0; i < DISPLAY_RESX * DISPLAY_RESY; i++) { + // 2 bytes per pixel because we're using RGB 5-6-5 format + ISSUE_PIXEL_DATA(0x0000); + } + + display_panel_rotate(angle); + } + } + + return drv->orientation_angle; +} + +int display_get_orientation(void) { + display_driver_t* drv = &g_display_driver; + + return drv->orientation_angle; +} + +void display_wait_for_sync(void) { +#ifdef DISPLAY_TE_PIN + uint32_t id = display_panel_identify(); + if (id && (id != DISPLAY_ID_GC9307)) { + // synchronize with the panel synchronization signal + // in order to avoid visual tearing effects + while (GPIO_PIN_SET == HAL_GPIO_ReadPin(DISPLAY_TE_PORT, DISPLAY_TE_PIN)) + ; + while (GPIO_PIN_RESET == HAL_GPIO_ReadPin(DISPLAY_TE_PORT, DISPLAY_TE_PIN)) + ; + } +#endif +} + +void display_set_compatible_settings(void) { display_panel_set_big_endian(); } diff --git a/core/embed/trezorhal/stm32f4/display/st-7789/display_fb.c b/core/embed/trezorhal/stm32f4/display/st-7789/display_fb.c new file mode 100644 index 000000000..551189db6 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/st-7789/display_fb.c @@ -0,0 +1,218 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#include +#include +#include + +#include TREZOR_BOARD +#include STM32_HAL_H + +#include "display_fb.h" +#include "display_io.h" +#include "display_panel.h" +#include "xdisplay.h" + +#include "gl_bitblt.h" +#include "irq.h" +#include "supervise.h" + +#ifndef BOARDLOADER +#include "bg_copy.h" +#endif + +#ifndef STM32U5 +#error Framebuffer only supported on STM32U5 for now +#endif + +// Size of the physical frame buffer in bytes +#define PHYSICAL_FRAME_BUFFER_SIZE (DISPLAY_RESX * DISPLAY_RESY * 2) + +// Physical frame buffers in internal SRAM memory. +// Both frame buffers layes in the fixed addresses that +// are shared between bootloaders and the firmware. +__attribute__((section(".fb1"))) +ALIGN_32BYTES(uint8_t physical_frame_buffer_0[PHYSICAL_FRAME_BUFFER_SIZE]); +__attribute__((section(".fb2"))) +ALIGN_32BYTES(uint8_t physical_frame_buffer_1[PHYSICAL_FRAME_BUFFER_SIZE]); + +// The current frame buffer selector at fixed memory address +// It's shared between bootloaders and the firmware +__attribute__((section(".framebuffer_select"))) uint32_t current_frame_buffer = + 0; + +void display_physical_fb_clear(void) { + memset(physical_frame_buffer_0, 0, sizeof(physical_frame_buffer_0)); + memset(physical_frame_buffer_1, 0, sizeof(physical_frame_buffer_1)); +} + +#ifndef BOARDLOADER +static bool pending_fb_switch = false; +#endif + +#ifndef BOARDLOADER +void DISPLAY_TE_INTERRUPT_HANDLER(void) { + HAL_NVIC_DisableIRQ(DISPLAY_TE_INTERRUPT_NUM); + + if (current_frame_buffer == 1) { + bg_copy_start_const_out_8((uint8_t *)physical_frame_buffer_1, + (uint8_t *)DISPLAY_DATA_ADDRESS, + DISPLAY_RESX * DISPLAY_RESY * 2); + + } else { + bg_copy_start_const_out_8((uint8_t *)physical_frame_buffer_0, + (uint8_t *)DISPLAY_DATA_ADDRESS, + DISPLAY_RESX * DISPLAY_RESY * 2); + } + + pending_fb_switch = false; + __HAL_GPIO_EXTI_CLEAR_FLAG(DISPLAY_TE_PIN); +} + +static void wait_for_fb_switch(void) { + while (pending_fb_switch) { + __WFI(); + } + bg_copy_wait(); +} +#endif + +static void copy_fb_to_display(uint16_t *fb) { + for (int i = 0; i < DISPLAY_RESX * DISPLAY_RESY; i++) { + // 2 bytes per pixel because we're using RGB 5-6-5 format + ISSUE_PIXEL_DATA(fb[i]); + } +} + +static void switch_fb_manually(void) { + // sync with the panel refresh + while (GPIO_PIN_SET == HAL_GPIO_ReadPin(DISPLAY_TE_PORT, DISPLAY_TE_PIN)) { + } + while (GPIO_PIN_RESET == HAL_GPIO_ReadPin(DISPLAY_TE_PORT, DISPLAY_TE_PIN)) { + } + + if (current_frame_buffer == 0) { + current_frame_buffer = 1; + copy_fb_to_display((uint16_t *)physical_frame_buffer_1); + memcpy(physical_frame_buffer_0, physical_frame_buffer_1, + sizeof(physical_frame_buffer_0)); + + } else { + current_frame_buffer = 0; + copy_fb_to_display((uint16_t *)physical_frame_buffer_0); + memcpy(physical_frame_buffer_1, physical_frame_buffer_0, + sizeof(physical_frame_buffer_1)); + } +} + +#ifndef BOARDLOADER +static void switch_fb_in_backround(void) { + if (current_frame_buffer == 0) { + current_frame_buffer = 1; + + memcpy(physical_frame_buffer_0, physical_frame_buffer_1, + sizeof(physical_frame_buffer_0)); + + pending_fb_switch = true; + __HAL_GPIO_EXTI_CLEAR_FLAG(DISPLAY_TE_PIN); + svc_enableIRQ(DISPLAY_TE_INTERRUPT_NUM); + } else { + current_frame_buffer = 0; + memcpy(physical_frame_buffer_1, physical_frame_buffer_0, + sizeof(physical_frame_buffer_1)); + + pending_fb_switch = true; + __HAL_GPIO_EXTI_CLEAR_FLAG(DISPLAY_TE_PIN); + svc_enableIRQ(DISPLAY_TE_INTERRUPT_NUM); + } +} +#endif + +display_fb_info_t display_get_frame_buffer(void) { + void *addr; + + if (current_frame_buffer == 0) { + addr = (void *)physical_frame_buffer_1; + } else { + addr = (void *)physical_frame_buffer_0; + } + + display_fb_info_t fb = { + .ptr = addr, + .stride = DISPLAY_RESX * sizeof(uint16_t), + }; + + return fb; +} + +void display_refresh(void) { +#ifndef BOARDLOADER + wait_for_fb_switch(); + display_panel_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1); + + if (is_mode_handler()) { + switch_fb_manually(); + } else { + switch_fb_in_backround(); + } +#else + display_panel_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1); + switch_fb_manually(); +#endif +} + +void display_fill(const gl_bitblt_t *bb) { + display_fb_info_t fb = display_get_frame_buffer(); + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = (uint16_t *)((uintptr_t)fb.ptr + fb.stride * bb_new.dst_y); + bb_new.dst_stride = fb.stride; + + gl_rgb565_fill(&bb_new); +} + +void display_copy_rgb565(const gl_bitblt_t *bb) { + display_fb_info_t fb = display_get_frame_buffer(); + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = (uint16_t *)((uintptr_t)fb.ptr + fb.stride * bb_new.dst_y); + bb_new.dst_stride = fb.stride; + + gl_rgb565_copy_rgb565(&bb_new); +} + +void display_copy_mono1p(const gl_bitblt_t *bb) { + display_fb_info_t fb = display_get_frame_buffer(); + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = (uint16_t *)((uintptr_t)fb.ptr + fb.stride * bb_new.dst_y); + bb_new.dst_stride = fb.stride; + + gl_rgb565_copy_mono1p(&bb_new); +} + +void display_copy_mono4(const gl_bitblt_t *bb) { + display_fb_info_t fb = display_get_frame_buffer(); + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = (uint16_t *)((uintptr_t)fb.ptr + fb.stride * bb_new.dst_y); + bb_new.dst_stride = fb.stride; + + gl_rgb565_copy_mono4(&bb_new); +} diff --git a/core/embed/trezorhal/stm32f4/display/st-7789/display_fb.h b/core/embed/trezorhal/stm32f4/display/st-7789/display_fb.h new file mode 100644 index 000000000..d216be9c4 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/st-7789/display_fb.h @@ -0,0 +1,32 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#ifndef TREZORHAL_DISPLAY_FB_H +#define TREZORHAL_DISPLAY_FB_H + +#include + +#ifdef XFRAMEBUFFER + +// Clears both physical frame buffers +void display_physical_fb_clear(void); + +#endif // XFRAMEBUFFER + +#endif // TREZORHAL_DISPLAY_FB_H diff --git a/core/embed/trezorhal/stm32f4/display/st-7789/display_io.c b/core/embed/trezorhal/stm32f4/display/st-7789/display_io.c new file mode 100644 index 000000000..fe02ca207 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/st-7789/display_io.c @@ -0,0 +1,145 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#include TREZOR_BOARD +#include STM32_HAL_H + +#include "display_io.h" +#include "irq.h" + +__IO DISP_MEM_TYPE *const DISPLAY_CMD_ADDRESS = + (__IO DISP_MEM_TYPE *const)((uint32_t)DISPLAY_MEMORY_BASE); +__IO DISP_MEM_TYPE *const DISPLAY_DATA_ADDRESS = + (__IO DISP_MEM_TYPE *const)((uint32_t)DISPLAY_MEMORY_BASE | + (DISPLAY_ADDR_SHIFT << DISPLAY_MEMORY_PIN)); + +void display_io_init_gpio(void) { + // init peripherals + __HAL_RCC_GPIOE_CLK_ENABLE(); + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOD_CLK_ENABLE(); + __HAL_RCC_FMC_CLK_ENABLE(); + + GPIO_InitTypeDef GPIO_InitStructure; + + // LCD_RST/PC14 + GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStructure.Pull = GPIO_NOPULL; + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStructure.Alternate = 0; + GPIO_InitStructure.Pin = GPIO_PIN_14; + // default to keeping display in reset + HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_RESET); + HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); + +#ifdef DISPLAY_TE_PIN + // LCD_FMARK (tearing effect) + GPIO_InitStructure.Mode = GPIO_MODE_INPUT; + GPIO_InitStructure.Pull = GPIO_NOPULL; + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStructure.Alternate = 0; + GPIO_InitStructure.Pin = DISPLAY_TE_PIN; + HAL_GPIO_Init(DISPLAY_TE_PORT, &GPIO_InitStructure); +#endif + + GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; + GPIO_InitStructure.Pull = GPIO_NOPULL; + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStructure.Alternate = GPIO_AF12_FMC; + // LCD_CS/PD7 LCD_RS/PD11 LCD_RD/PD4 LCD_WR/PD5 + GPIO_InitStructure.Pin = GPIO_PIN_7 | GPIO_PIN_11 | GPIO_PIN_4 | GPIO_PIN_5; + HAL_GPIO_Init(GPIOD, &GPIO_InitStructure); + // LCD_D0/PD14 LCD_D1/PD15 LCD_D2/PD0 LCD_D3/PD1 + GPIO_InitStructure.Pin = GPIO_PIN_14 | GPIO_PIN_15 | GPIO_PIN_0 | GPIO_PIN_1; + HAL_GPIO_Init(GPIOD, &GPIO_InitStructure); + // LCD_D4/PE7 LCD_D5/PE8 LCD_D6/PE9 LCD_D7/PE10 + GPIO_InitStructure.Pin = GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10; + HAL_GPIO_Init(GPIOE, &GPIO_InitStructure); +#ifdef USE_DISP_I8080_16BIT_DW + // LCD_D8/PE11 LCD_D9/PE12 LCD_D10/PE13 LCD_D11/PE14 + GPIO_InitStructure.Pin = + GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14; + HAL_GPIO_Init(GPIOE, &GPIO_InitStructure); + // LCD_D12/PE15 + GPIO_InitStructure.Pin = GPIO_PIN_15; + HAL_GPIO_Init(GPIOE, &GPIO_InitStructure); + // LCD_D13/PD8 LCD_D14/PD9 LCD_D15/PD10 + GPIO_InitStructure.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10; + HAL_GPIO_Init(GPIOD, &GPIO_InitStructure); +#endif +} + +void display_io_init_fmc(void) { + // Reference UM1725 "Description of STM32F4 HAL and LL drivers", + // section 64.2.1 "How to use this driver" + SRAM_HandleTypeDef external_display_data_sram = {0}; + external_display_data_sram.Instance = FMC_NORSRAM_DEVICE; + external_display_data_sram.Extended = FMC_NORSRAM_EXTENDED_DEVICE; + external_display_data_sram.Init.NSBank = FMC_NORSRAM_BANK1; + external_display_data_sram.Init.DataAddressMux = FMC_DATA_ADDRESS_MUX_DISABLE; + external_display_data_sram.Init.MemoryType = FMC_MEMORY_TYPE_SRAM; +#ifdef USE_DISP_I8080_16BIT_DW + external_display_data_sram.Init.MemoryDataWidth = + FMC_NORSRAM_MEM_BUS_WIDTH_16; +#elif USE_DISP_I8080_8BIT_DW + external_display_data_sram.Init.MemoryDataWidth = FMC_NORSRAM_MEM_BUS_WIDTH_8; +#endif + external_display_data_sram.Init.BurstAccessMode = + FMC_BURST_ACCESS_MODE_DISABLE; + external_display_data_sram.Init.WaitSignalPolarity = + FMC_WAIT_SIGNAL_POLARITY_LOW; + external_display_data_sram.Init.WaitSignalActive = FMC_WAIT_TIMING_BEFORE_WS; + external_display_data_sram.Init.WriteOperation = FMC_WRITE_OPERATION_ENABLE; + external_display_data_sram.Init.WaitSignal = FMC_WAIT_SIGNAL_DISABLE; + external_display_data_sram.Init.ExtendedMode = FMC_EXTENDED_MODE_DISABLE; + external_display_data_sram.Init.AsynchronousWait = + FMC_ASYNCHRONOUS_WAIT_DISABLE; + external_display_data_sram.Init.WriteBurst = FMC_WRITE_BURST_DISABLE; + external_display_data_sram.Init.ContinuousClock = + FMC_CONTINUOUS_CLOCK_SYNC_ONLY; + external_display_data_sram.Init.PageSize = FMC_PAGE_SIZE_NONE; + + // reference RM0090 section 37.5 Table 259, 37.5.4, Mode 1 SRAM, and 37.5.6 + FMC_NORSRAM_TimingTypeDef normal_mode_timing = {0}; + normal_mode_timing.AddressSetupTime = 5; + normal_mode_timing.AddressHoldTime = 1; // don't care + normal_mode_timing.DataSetupTime = 6; + normal_mode_timing.BusTurnAroundDuration = 0; // don't care + normal_mode_timing.CLKDivision = 2; // don't care + normal_mode_timing.DataLatency = 2; // don't care + normal_mode_timing.AccessMode = FMC_ACCESS_MODE_A; + + HAL_SRAM_Init(&external_display_data_sram, &normal_mode_timing, NULL); +} + +#ifdef DISPLAY_TE_INTERRUPT_HANDLER +void display_io_init_te_interrupt(void) { + EXTI_HandleTypeDef EXTI_Handle = {0}; + EXTI_ConfigTypeDef EXTI_Config = {0}; + EXTI_Config.GPIOSel = DISPLAY_TE_INTERRUPT_GPIOSEL; + EXTI_Config.Line = DISPLAY_TE_INTERRUPT_EXTI_LINE; + EXTI_Config.Mode = EXTI_MODE_INTERRUPT; + EXTI_Config.Trigger = EXTI_TRIGGER_RISING; + HAL_EXTI_SetConfigLine(&EXTI_Handle, &EXTI_Config); + + // setup interrupt for tearing effect pin + HAL_NVIC_SetPriority(DISPLAY_TE_INTERRUPT_NUM, IRQ_PRI_DMA, 0); +} +#endif diff --git a/core/embed/trezorhal/stm32f4/display/st-7789/display_io.h b/core/embed/trezorhal/stm32f4/display/st-7789/display_io.h new file mode 100644 index 000000000..603157c67 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/st-7789/display_io.h @@ -0,0 +1,67 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#ifndef TREZORHAL_DISPLAY_IO_H +#define TREZORHAL_DISPLAY_IO_H + +#include STM32_HAL_H +#include TREZOR_BOARD + +void display_io_init_gpio(void); +void display_io_init_fmc(void); +void display_io_init_te_interrupt(void); + +#ifndef FMC_BANK1 +#define FMC_BANK1 0x60000000U +#endif + +#define DISPLAY_MEMORY_BASE FMC_BANK1 +#define DISPLAY_MEMORY_PIN 16 + +#ifdef USE_DISP_I8080_16BIT_DW +#define DISPLAY_ADDR_SHIFT 2 +#define DISP_MEM_TYPE uint16_t +#elif USE_DISP_I8080_8BIT_DW +#define DISPLAY_ADDR_SHIFT 1 +#define DISP_MEM_TYPE uint8_t +#else +#error "Unsupported display interface" +#endif + +/*#define DISPLAY_CMD_ADDRESS ((__IO DISP_MEM_TYPE *)(DISPLAY_MEMORY_BASE)) +#define DISPLAY_DATA_ADDRESS \ + ((__IO DISP_MEM_TYPE *)(DISPLAY_MEMORY_BASE | \ + (DISPLAY_ADDR_SHIFT << DISPLAY_MEMORY_PIN))) +*/ + +extern __IO DISP_MEM_TYPE *const DISPLAY_CMD_ADDRESS; +extern __IO DISP_MEM_TYPE *const DISPLAY_DATA_ADDRESS; + +#define ISSUE_CMD_BYTE(X) (*(DISPLAY_CMD_ADDRESS) = (X)) +#define ISSUE_DATA_BYTE(X) (*(DISPLAY_DATA_ADDRESS) = (X)) + +#ifdef USE_DISP_I8080_16BIT_DW +#define ISSUE_PIXEL_DATA(X) DATA(X) +#elif USE_DISP_I8080_8BIT_DW +#define ISSUE_PIXEL_DATA(X) \ + ISSUE_DATA_BYTE((X)&0xFF); \ + ISSUE_DATA_BYTE((X) >> 8) +#endif + +#endif // TREZORHAL_DISPLAY_IO_H diff --git a/core/embed/trezorhal/stm32f4/display/st-7789/display_nofb.c b/core/embed/trezorhal/stm32f4/display/st-7789/display_nofb.c new file mode 100644 index 000000000..b4d76f3a3 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/st-7789/display_nofb.c @@ -0,0 +1,103 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#include TREZOR_BOARD + +#include + +#include "display_io.h" +#include "display_panel.h" + +void display_refresh(void) { + // if the framebuffer is not used the implementation is empty +} + +static inline void set_window(const gl_bitblt_t* bb) { + display_panel_set_window(bb->dst_x, bb->dst_y, bb->dst_x + bb->width - 1, + bb->dst_y + bb->height + 1); +} + +// For future notice, if we ever want to do a new model using progressive +// rendering. +// +// Following functions can be optimized by using DMA (regular is likely enough) +// to copy the data, along with the fill function. If even more performance is +// needed, we could use double-slice similarly to double-framebuffer and render +// to one with DMA2D while copying the other to the display with DMA. + +void display_fill(const gl_bitblt_t* bb) { + set_window(bb); + + uint16_t height = bb->height; + + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + ISSUE_PIXEL_DATA(bb->src_fg); + } + } +} + +void display_copy_rgb565(const gl_bitblt_t* bb) { + set_window(bb); + + uint16_t* src_ptr = (uint16_t*)bb->src_row + bb->src_x; + uint16_t height = bb->height; + + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + ISSUE_PIXEL_DATA(src_ptr[x]); + } + src_ptr += bb->src_stride / sizeof(*src_ptr); + } +} + +void display_copy_mono1p(const gl_bitblt_t* bb) { + set_window(bb); + + uint8_t* src = (uint8_t*)bb->src_row; + uint16_t src_ofs = bb->src_stride * bb->src_y + bb->src_x; + uint16_t height = bb->height; + + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + uint8_t mask = 1 << (7 - ((src_ofs + x) & 7)); + uint8_t data = src[(src_ofs + x) / 8]; + ISSUE_PIXEL_DATA((data & mask) ? bb->src_fg : bb->src_bg); + } + src_ofs += bb->src_stride; + } +} + +void display_copy_mono4(const gl_bitblt_t* bb) { + set_window(bb); + + const gl_color16_t* gradient = gl_color16_gradient_a4(bb->src_fg, bb->src_bg); + + uint8_t* src_row = (uint8_t*)bb->src_row; + uint16_t height = bb->height; + + while (height-- > 0) { + for (int x = 0; x < bb->width; x++) { + uint8_t fg_data = src_row[(x + bb->src_x) / 2]; + uint8_t fg_lum = (x + bb->src_x) & 1 ? fg_data >> 4 : fg_data & 0xF; + ISSUE_PIXEL_DATA(gradient[fg_lum]); + } + src_row += bb->src_stride / sizeof(*src_row); + } +} diff --git a/core/embed/trezorhal/stm32f4/display/st-7789/display_panel.c b/core/embed/trezorhal/stm32f4/display/st-7789/display_panel.c new file mode 100644 index 000000000..aaf86d8e8 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/st-7789/display_panel.c @@ -0,0 +1,244 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +// using const volatile instead of #define results in binaries that change +// only in 1-byte when the flag changes. +// using #define leads compiler to over-optimize the code leading to bigger +// differencies in the resulting binaries. + +#include "display_panel.h" +#include "display_io.h" + +#ifdef TREZOR_MODEL_T +#include "panels/154a.h" +#include "panels/lx154a2411.h" +#include "panels/lx154a2422.h" +#include "panels/tf15411a.h" +#else +#include "panels/lx154a2422.h" +#endif + +// using const volatile instead of #define results in binaries that change +// only in 1-byte when the flag changes. +// using #define leads compiler to over-optimize the code leading to bigger +// differencies in the resulting binaries. +const volatile uint8_t DISPLAY_ST7789V_INVERT_COLORS2 = 1; + +// Window padding (correction) when using 90dg or 270dg orientation +// (internally the display is 240x320 but we use only 240x240) +static display_padding_t g_window_padding; + +#ifdef DISPLAY_IDENTIFY +static uint32_t read_display_id(uint8_t command) { + volatile uint8_t c = 0; + uint32_t id = 0; + ISSUE_CMD_BYTE(command); + c = *DISPLAY_DATA_ADDRESS; // first returned value is a dummy value and + // should be discarded + c = *DISPLAY_DATA_ADDRESS; + id |= (c << 16); + c = *DISPLAY_DATA_ADDRESS; + id |= (c << 8); + c = *DISPLAY_DATA_ADDRESS; + id |= c; + return id; +} + +uint32_t display_panel_identify(void) { + static uint32_t id = 0x000000U; + static bool id_initialized = false; + + // Return immediately if id has been already initialized + if (id_initialized) return id; + + // RDDID: Read Display ID + id = read_display_id(0x04); + // the default RDDID for ILI9341 should be 0x8000. + // some display modules return 0x0. + // the ILI9341 has an extra id, let's check it here. + if ((id != DISPLAY_ID_ST7789V) && (id != DISPLAY_ID_GC9307)) { + // Read ID4 + uint32_t id4 = read_display_id(0xD3); + if (id4 == DISPLAY_ID_ILI9341V) { // definitely found a ILI9341 + id = id4; + } + } + id_initialized = true; + return id; +} +#else +uint32_t display_panel_identify(void) { return DISPLAY_ID_ST7789V; } +#endif + +bool display_panel_is_inverted() { + bool inv_on = false; + uint32_t id = display_panel_identify(); + if (id == DISPLAY_ID_ST7789V) { + volatile uint8_t c = 0; + ISSUE_CMD_BYTE(0x09); // read display status + c = *DISPLAY_DATA_ADDRESS; // don't care + c = *DISPLAY_DATA_ADDRESS; // don't care + c = *DISPLAY_DATA_ADDRESS; // don't care + c = *DISPLAY_DATA_ADDRESS; + if (c & 0x20) { + inv_on = true; + } + c = *DISPLAY_DATA_ADDRESS; // don't care + } + + return inv_on; +} + +void display_panel_sleep(void) { + uint32_t id = display_panel_identify(); + if ((id == DISPLAY_ID_ILI9341V) || (id == DISPLAY_ID_GC9307) || + (id == DISPLAY_ID_ST7789V)) { + ISSUE_CMD_BYTE(0x28); // DISPOFF: Display Off + ISSUE_CMD_BYTE(0x10); // SLPIN: Sleep in + HAL_Delay(5); // need to wait 5 milliseconds after "sleep in" before + // sending any new commands + } +} + +void display_panel_unsleep(void) { + uint32_t id = display_panel_identify(); + if ((id == DISPLAY_ID_ILI9341V) || (id == DISPLAY_ID_GC9307) || + (id == DISPLAY_ID_ST7789V)) { + ISSUE_CMD_BYTE(0x11); // SLPOUT: Sleep Out + HAL_Delay(5); // need to wait 5 milliseconds after "sleep out" before + // sending any new commands + ISSUE_CMD_BYTE(0x29); // DISPON: Display On + } +} + +void display_panel_set_window(uint16_t x0, uint16_t y0, uint16_t x1, + uint16_t y1) { + x0 += g_window_padding.x; + x1 += g_window_padding.x; + y0 += g_window_padding.y; + y1 += g_window_padding.y; + + uint32_t id = display_panel_identify(); + if ((id == DISPLAY_ID_ILI9341V) || (id == DISPLAY_ID_GC9307) || + (id == DISPLAY_ID_ST7789V)) { + ISSUE_CMD_BYTE(0x2A); + ISSUE_DATA_BYTE(x0 >> 8); + ISSUE_DATA_BYTE(x0 & 0xFF); + ISSUE_DATA_BYTE(x1 >> 8); + ISSUE_DATA_BYTE(x1 & 0xFF); // column addr set + ISSUE_CMD_BYTE(0x2B); + ISSUE_DATA_BYTE(y0 >> 8); + ISSUE_DATA_BYTE(y0 & 0xFF); + ISSUE_DATA_BYTE(y1 >> 8); + ISSUE_DATA_BYTE(y1 & 0xFF); // row addr set + ISSUE_CMD_BYTE(0x2C); + } +} + +void display_panel_set_little_endian(void) { + uint32_t id = display_panel_identify(); + if (id == DISPLAY_ID_GC9307) { + // CANNOT SET ENDIAN FOR GC9307 + } else if (id == DISPLAY_ID_ST7789V) { + ISSUE_CMD_BYTE(0xB0); + ISSUE_DATA_BYTE(0x00); + ISSUE_DATA_BYTE(0xF8); + } else if (id == DISPLAY_ID_ILI9341V) { + // Interface Control: XOR BGR as ST7789V does + ISSUE_CMD_BYTE(0xF6); + ISSUE_DATA_BYTE(0x09); + ISSUE_DATA_BYTE(0x30); + ISSUE_DATA_BYTE(0x20); + } +} + +void display_panel_set_big_endian(void) { + uint32_t id = display_panel_identify(); + if (id == DISPLAY_ID_GC9307) { + // CANNOT SET ENDIAN FOR GC9307 + } else if (id == DISPLAY_ID_ST7789V) { + ISSUE_CMD_BYTE(0xB0); + ISSUE_DATA_BYTE(0x00); + ISSUE_DATA_BYTE(0xF0); + } else if (id == DISPLAY_ID_ILI9341V) { + // Interface Control: XOR BGR as ST7789V does + ISSUE_CMD_BYTE(0xF6); + ISSUE_DATA_BYTE(0x09); + ISSUE_DATA_BYTE(0x30); + ISSUE_DATA_BYTE(0x00); + } +} + +void display_panel_init(void) { + HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_RESET); // LCD_RST/PC14 + // wait 10 milliseconds. only needs to be low for 10 microseconds. + // my dev display module ties display reset and touch panel reset together. + // keeping this low for max(display_reset_time, ctpm_reset_time) aids + // development and does not hurt. + HAL_Delay(10); + HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_SET); // LCD_RST/PC14 + // max wait time for hardware reset is 120 milliseconds + // (experienced display flakiness using only 5ms wait before sending commands) + HAL_Delay(120); + + // identify the controller we will communicate with +#ifdef TREZOR_MODEL_T + uint32_t id = display_panel_identify(); + if (id == DISPLAY_ID_GC9307) { + tf15411a_init_seq(); + } else if (id == DISPLAY_ID_ST7789V) { + if (DISPLAY_ST7789V_INVERT_COLORS2) { + lx154a2422_init_seq(); + } else { + lx154a2411_init_seq(); + } + } else if (id == DISPLAY_ID_ILI9341V) { + _154a_init_seq(); + } +#else + lx154a2422_init_seq(); +#endif + + display_panel_unsleep(); +} + +void display_panel_init_gamma(void) { +#ifdef TREZOR_MODEL_T + uint32_t id = display_panel_identify(); + if (id == DISPLAY_ID_ST7789V && display_panel_is_inverted()) { + // newest TT display - set proper gamma + lx154a2422_gamma(); + } else if (id == DISPLAY_ID_ST7789V) { + lx154a2411_gamma(); + } +#endif +} + +void display_panel_rotate(int angle) { +#ifdef TREZOR_MODEL_T + uint32_t id = display_panel_identify(); + if (id == DISPLAY_ID_GC9307) { + tf15411a_rotate(angle, &g_window_padding); + } else { + lx154a2422_rotate(angle, &g_window_padding); + } +#else + lx154a2422_rotate(angle, &g_window_padding); +#endif +} diff --git a/core/embed/trezorhal/stm32f4/display/st-7789/display_panel.h b/core/embed/trezorhal/stm32f4/display/st-7789/display_panel.h new file mode 100644 index 000000000..99935a7ba --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/st-7789/display_panel.h @@ -0,0 +1,57 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#ifndef TREZORHAL_ST7789_PANEL_H +#define TREZORHAL_ST7789_PANEL_H + +#include +#include + +// section "9.1.3 RDDID (04h): Read Display ID" +// of ST7789V datasheet +#define DISPLAY_ID_ST7789V 0x858552U +// section "6.2.1. Read display identification information (04h)" +// of GC9307 datasheet +#define DISPLAY_ID_GC9307 0x009307U +// section "8.3.23 Read ID4 (D3h)" +// of ILI9341V datasheet +#define DISPLAY_ID_ILI9341V 0x009341U + +typedef struct { + uint16_t x; + uint16_t y; +} display_padding_t; + +// Identifies the connected display panel and +// returns one of DISPLAY_ID_xxx constant +uint32_t display_panel_identify(void); +bool display_panel_is_inverted(); + +void display_panel_init(void); +void display_panel_init_gamma(void); +void display_panel_set_little_endian(void); +void display_panel_set_big_endian(void); + +void display_panel_sleep(void); +void display_panel_unsleep(void); +void display_panel_set_window(uint16_t x0, uint16_t y0, uint16_t x1, + uint16_t y1); +void display_panel_rotate(int angle); + +#endif // TREZORHAL_ST7789_PANEL_H diff --git a/core/embed/trezorhal/stm32f4/display/st-7789/panels/154a.c b/core/embed/trezorhal/stm32f4/display/st-7789/panels/154a.c new file mode 100644 index 000000000..8d7dc0fe9 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/st-7789/panels/154a.c @@ -0,0 +1,132 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#include "../display_io.h" + +void _154a_init_seq(void) { + // most recent manual: https://www.newhavendisplay.com/app_notes/ILI9341.pdf + // TEON: Tearing Effect Line On; V-blanking only + ISSUE_CMD_BYTE(0x35); + ISSUE_DATA_BYTE(0x00); + + // COLMOD: Interface Pixel format; 65K color: 16-bit/pixel (RGB 5-6-5 bits + // input) + ISSUE_CMD_BYTE(0x3A); + ISSUE_DATA_BYTE(0x55); + + // Display Function Control: gate scan direction 319 -> 0 + ISSUE_CMD_BYTE(0xB6); + ISSUE_DATA_BYTE(0x0A); + ISSUE_DATA_BYTE(0xC2); + ISSUE_DATA_BYTE(0x27); + ISSUE_DATA_BYTE(0x00); + + // Interface Control: XOR BGR as ST7789V does + ISSUE_CMD_BYTE(0xF6); + ISSUE_DATA_BYTE(0x09); + ISSUE_DATA_BYTE(0x30); + ISSUE_DATA_BYTE(0x00); + + // the above config is the most important and definitely necessary + + ISSUE_CMD_BYTE(0xCF); + ISSUE_DATA_BYTE(0x00); + ISSUE_DATA_BYTE(0xC1); + ISSUE_DATA_BYTE(0x30); + + ISSUE_CMD_BYTE(0xED); + ISSUE_DATA_BYTE(0x64); + ISSUE_DATA_BYTE(0x03); + ISSUE_DATA_BYTE(0x12); + ISSUE_DATA_BYTE(0x81); + + ISSUE_CMD_BYTE(0xE8); + ISSUE_DATA_BYTE(0x85); + ISSUE_DATA_BYTE(0x10); + ISSUE_DATA_BYTE(0x7A); + + ISSUE_CMD_BYTE(0xF7); + ISSUE_DATA_BYTE(0x20); + + ISSUE_CMD_BYTE(0xEA); + ISSUE_DATA_BYTE(0x00); + ISSUE_DATA_BYTE(0x00); + + // power control VRH[5:0] + ISSUE_CMD_BYTE(0xC0); + ISSUE_DATA_BYTE(0x23); + + // power control SAP[2:0] BT[3:0] + ISSUE_CMD_BYTE(0xC1); + ISSUE_DATA_BYTE(0x12); + + // vcm control 1 + ISSUE_CMD_BYTE(0xC5); + ISSUE_DATA_BYTE(0x60); + ISSUE_DATA_BYTE(0x44); + + // vcm control 2 + ISSUE_CMD_BYTE(0xC7); + ISSUE_DATA_BYTE(0x8A); + + // framerate + ISSUE_CMD_BYTE(0xB1); + ISSUE_DATA_BYTE(0x00); + ISSUE_DATA_BYTE(0x18); + + // 3 gamma func disable + ISSUE_CMD_BYTE(0xF2); + ISSUE_DATA_BYTE(0x00); + + // gamma curve 1 + ISSUE_CMD_BYTE(0xE0); + ISSUE_DATA_BYTE(0x0F); + ISSUE_DATA_BYTE(0x2F); + ISSUE_DATA_BYTE(0x2C); + ISSUE_DATA_BYTE(0x0B); + ISSUE_DATA_BYTE(0x0F); + ISSUE_DATA_BYTE(0x09); + ISSUE_DATA_BYTE(0x56); + ISSUE_DATA_BYTE(0xD9); + ISSUE_DATA_BYTE(0x4A); + ISSUE_DATA_BYTE(0x0B); + ISSUE_DATA_BYTE(0x14); + ISSUE_DATA_BYTE(0x05); + ISSUE_DATA_BYTE(0x0C); + ISSUE_DATA_BYTE(0x06); + ISSUE_DATA_BYTE(0x00); + + // gamma curve 2 + ISSUE_CMD_BYTE(0xE1); + ISSUE_DATA_BYTE(0x00); + ISSUE_DATA_BYTE(0x10); + ISSUE_DATA_BYTE(0x13); + ISSUE_DATA_BYTE(0x04); + ISSUE_DATA_BYTE(0x10); + ISSUE_DATA_BYTE(0x06); + ISSUE_DATA_BYTE(0x25); + ISSUE_DATA_BYTE(0x26); + ISSUE_DATA_BYTE(0x3B); + ISSUE_DATA_BYTE(0x04); + ISSUE_DATA_BYTE(0x0B); + ISSUE_DATA_BYTE(0x0A); + ISSUE_DATA_BYTE(0x33); + ISSUE_DATA_BYTE(0x39); + ISSUE_DATA_BYTE(0x0F); +} diff --git a/core/embed/trezorhal/stm32f4/display/st-7789/panels/154a.h b/core/embed/trezorhal/stm32f4/display/st-7789/panels/154a.h new file mode 100644 index 000000000..410455abb --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/st-7789/panels/154a.h @@ -0,0 +1,27 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#ifndef _154A_H_ +#define _154A_H_ + +// ILI9341 IC controller + +void _154a_init_seq(void); + +#endif diff --git a/core/embed/trezorhal/stm32f4/display/st-7789/panels/lx154a2411.c b/core/embed/trezorhal/stm32f4/display/st-7789/panels/lx154a2411.c new file mode 100644 index 000000000..b81ee75bb --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/st-7789/panels/lx154a2411.c @@ -0,0 +1,82 @@ + +#include "../display_io.h" + +void lx154a2411_gamma(void) { + // positive voltage correction + ISSUE_CMD_BYTE(0xE0); + ISSUE_DATA_BYTE(0xD0); + ISSUE_DATA_BYTE(0x03); + ISSUE_DATA_BYTE(0x08); + ISSUE_DATA_BYTE(0x0E); + ISSUE_DATA_BYTE(0x11); + ISSUE_DATA_BYTE(0x2B); + ISSUE_DATA_BYTE(0x3B); + ISSUE_DATA_BYTE(0x44); + ISSUE_DATA_BYTE(0x4C); + ISSUE_DATA_BYTE(0x2B); + ISSUE_DATA_BYTE(0x16); + ISSUE_DATA_BYTE(0x15); + ISSUE_DATA_BYTE(0x1E); + ISSUE_DATA_BYTE(0x21); + + // negative voltage correction + ISSUE_CMD_BYTE(0xE1); + ISSUE_DATA_BYTE(0xD0); + ISSUE_DATA_BYTE(0x03); + ISSUE_DATA_BYTE(0x08); + ISSUE_DATA_BYTE(0x0E); + ISSUE_DATA_BYTE(0x11); + ISSUE_DATA_BYTE(0x2B); + ISSUE_DATA_BYTE(0x3B); + ISSUE_DATA_BYTE(0x54); + ISSUE_DATA_BYTE(0x4C); + ISSUE_DATA_BYTE(0x2B); + ISSUE_DATA_BYTE(0x16); + ISSUE_DATA_BYTE(0x15); + ISSUE_DATA_BYTE(0x1E); + ISSUE_DATA_BYTE(0x21); +} + +void lx154a2411_init_seq(void) { + // most recent manual: + // https://www.newhavendisplay.com/appnotes/datasheets/LCDs/ST7789V.pdf + // TEON: Tearing Effect Line On; V-blanking only + ISSUE_CMD_BYTE(0x35); + ISSUE_DATA_BYTE(0x00); + + // COLMOD: Interface Pixel format; 65K color: 16-bit/pixel (RGB 5-6-5 bits + // input) + ISSUE_CMD_BYTE(0x3A); + ISSUE_DATA_BYTE(0x55); + + // CMD2EN: Commands in command table 2 can be executed when EXTC level is Low + ISSUE_CMD_BYTE(0xDF); + ISSUE_DATA_BYTE(0x5A); + ISSUE_DATA_BYTE(0x69); + ISSUE_DATA_BYTE(0x02); + ISSUE_DATA_BYTE(0x01); + + // LCMCTRL: LCM Control: XOR RGB setting + ISSUE_CMD_BYTE(0xC0); + ISSUE_DATA_BYTE(0x20); + + // GATECTRL: Gate Control; NL = 240 gate lines, first scan line is gate 80.; + // gate scan direction 319 -> 0 + ISSUE_CMD_BYTE(0xE4); + ISSUE_DATA_BYTE(0x1D); + ISSUE_DATA_BYTE(0x0A); + ISSUE_DATA_BYTE(0x11); + + // INVOFF (20h): Display Inversion Off + // INVON (21h): Display Inversion On + ISSUE_CMD_BYTE(0x20); + + // the above config is the most important and definitely necessary + + // PWCTRL1: Power Control 1 + ISSUE_CMD_BYTE(0xD0); + ISSUE_DATA_BYTE(0xA4); + ISSUE_DATA_BYTE(0xA1); + + lx154a2411_gamma(); +} diff --git a/core/embed/trezorhal/stm32f4/display/st-7789/panels/lx154a2411.h b/core/embed/trezorhal/stm32f4/display/st-7789/panels/lx154a2411.h new file mode 100644 index 000000000..347aa3b58 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/st-7789/panels/lx154a2411.h @@ -0,0 +1,27 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#ifndef LX154A2411_H_ +#define LX154A2411_H_ + +// ST7789_V IC controller +void lx154a2411_gamma(void); +void lx154a2411_init_seq(void); + +#endif diff --git a/core/embed/trezorhal/stm32f4/display/st-7789/panels/lx154a2422.c b/core/embed/trezorhal/stm32f4/display/st-7789/panels/lx154a2422.c new file mode 100644 index 000000000..d793e0952 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/st-7789/panels/lx154a2422.c @@ -0,0 +1,159 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#include "lx154a2422.h" + +#include "../display_io.h" + +void lx154a2422_gamma(void) { + // positive voltage correction + ISSUE_CMD_BYTE(0xE0); + ISSUE_DATA_BYTE(0xD0); + ISSUE_DATA_BYTE(0x0A); + ISSUE_DATA_BYTE(0x10); + ISSUE_DATA_BYTE(0x0A); + ISSUE_DATA_BYTE(0x0A); + ISSUE_DATA_BYTE(0x26); + ISSUE_DATA_BYTE(0x36); + ISSUE_DATA_BYTE(0x34); + ISSUE_DATA_BYTE(0x4D); + ISSUE_DATA_BYTE(0x18); + ISSUE_DATA_BYTE(0x13); + ISSUE_DATA_BYTE(0x14); + ISSUE_DATA_BYTE(0x2F); + ISSUE_DATA_BYTE(0x34); + + // negative voltage correction + ISSUE_CMD_BYTE(0xE1); + ISSUE_DATA_BYTE(0xD0); + ISSUE_DATA_BYTE(0x0A); + ISSUE_DATA_BYTE(0x10); + ISSUE_DATA_BYTE(0x0A); + ISSUE_DATA_BYTE(0x09); + ISSUE_DATA_BYTE(0x26); + ISSUE_DATA_BYTE(0x36); + ISSUE_DATA_BYTE(0x53); + ISSUE_DATA_BYTE(0x4C); + ISSUE_DATA_BYTE(0x18); + ISSUE_DATA_BYTE(0x14); + ISSUE_DATA_BYTE(0x14); + ISSUE_DATA_BYTE(0x2F); + ISSUE_DATA_BYTE(0x34); +} + +void lx154a2422_init_seq(void) { + // most recent manual: + // https://www.newhavendisplay.com/appnotes/datasheets/LCDs/ST7789V.pdf + // TEON: Tearing Effect Line On; V-blanking only + ISSUE_CMD_BYTE(0x35); + ISSUE_DATA_BYTE(0x00); + + // COLMOD: Interface Pixel format; 65K color: 16-bit/pixel (RGB 5-6-5 bits + // input) + ISSUE_CMD_BYTE(0x3A); + ISSUE_DATA_BYTE(0x55); + + // CMD2EN: Commands in command table 2 can be executed when EXTC level is Low + ISSUE_CMD_BYTE(0xDF); + ISSUE_DATA_BYTE(0x5A); + ISSUE_DATA_BYTE(0x69); + ISSUE_DATA_BYTE(0x02); + ISSUE_DATA_BYTE(0x01); + + // LCMCTRL: LCM Control: XOR RGB setting + ISSUE_CMD_BYTE(0xC0); + ISSUE_DATA_BYTE(0x20); + + // GATECTRL: Gate Control; NL = 240 gate lines, first scan line is gate 80.; + // gate scan direction 319 -> 0 + ISSUE_CMD_BYTE(0xE4); + ISSUE_DATA_BYTE(0x1D); + ISSUE_DATA_BYTE(0x0A); + ISSUE_DATA_BYTE(0x11); + + // INVOFF (20h): Display Inversion Off + // INVON (21h): Display Inversion On + ISSUE_CMD_BYTE(0x21); + + // the above config is the most important and definitely necessary + + // PWCTRL1: Power Control 1 + ISSUE_CMD_BYTE(0xD0); + ISSUE_DATA_BYTE(0xA4); + ISSUE_DATA_BYTE(0xA1); + + lx154a2422_gamma(); +} + +void lx154a2422_rotate(int degrees, display_padding_t* padding) { + uint16_t shift = 0; + char BX = 0, BY = 0; + +#define RGB (1 << 3) +#define ML (1 << 4) // vertical refresh order +#define MH (1 << 2) // horizontal refresh order +#define MV (1 << 5) +#define MX (1 << 6) +#define MY (1 << 7) + // MADCTL: Memory Data Access Control - reference: + // section 8.12 in the ST7789V manual + uint8_t display_command_parameter = 0; + switch (degrees) { + case 0: + display_command_parameter = 0; + BY = 0; + break; + case 90: + display_command_parameter = MV | MX | MH | ML; + BX = 1; + shift = 1; + break; + case 180: + display_command_parameter = MX | MY | MH | ML; + BY = 0; + shift = 1; + break; + case 270: + display_command_parameter = MV | MY; + BX = 1; + break; + } + + ISSUE_CMD_BYTE(0x36); + ISSUE_DATA_BYTE(display_command_parameter); + + if (shift) { + // GATECTRL: Gate Control; NL = 240 gate lines, first scan line is + // gate 80.; gate scan direction 319 -> 0 + ISSUE_CMD_BYTE(0xE4); + ISSUE_DATA_BYTE(0x1D); + ISSUE_DATA_BYTE(0x00); + ISSUE_DATA_BYTE(0x11); + } else { + // GATECTRL: Gate Control; NL = 240 gate lines, first scan line is + // gate 80.; gate scan direction 319 -> 0 + ISSUE_CMD_BYTE(0xE4); + ISSUE_DATA_BYTE(0x1D); + ISSUE_DATA_BYTE(0x0A); + ISSUE_DATA_BYTE(0x11); + } + + padding->x = BX ? (320 - DISPLAY_RESY) : 0; + padding->y = BY ? (320 - DISPLAY_RESY) : 0; +} diff --git a/core/embed/trezorhal/stm32f4/display/st-7789/panels/lx154a2422.h b/core/embed/trezorhal/stm32f4/display/st-7789/panels/lx154a2422.h new file mode 100644 index 000000000..ba726ba1f --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/st-7789/panels/lx154a2422.h @@ -0,0 +1,29 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#ifndef LX154A2422_H_ +#define LX154A2422_H_ + +#include "../display_panel.h" + +void lx154a2422_init_seq(void); +void lx154a2422_gamma(void); +void lx154a2422_rotate(int degrees, display_padding_t* padding); + +#endif diff --git a/core/embed/trezorhal/stm32f4/display/st-7789/panels/tf15411a.c b/core/embed/trezorhal/stm32f4/display/st-7789/panels/tf15411a.c new file mode 100644 index 000000000..155bf017a --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/st-7789/panels/tf15411a.c @@ -0,0 +1,174 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#include "tf15411a.h" +#include "../display_io.h" + +void tf15411a_init_seq(void) { + // Inter Register Enable1 + ISSUE_CMD_BYTE(0xFE); + + // Inter Register Enable2 + ISSUE_CMD_BYTE(0xEF); + + // TEON: Tearing Effect Line On; V-blanking only + ISSUE_CMD_BYTE(0x35); + ISSUE_DATA_BYTE(0x00); + + // COLMOD: Interface Pixel format; 65K color: 16-bit/pixel (RGB 5-6-5 bits + // input) + ISSUE_CMD_BYTE(0x3A); + ISSUE_DATA_BYTE(0x55); + + // Frame Rate + // ISSUE_CMD_BYTE(0xE8); ISSUE_DATA_BYTE(0x12); ISSUE_DATA_BYTE(0x00); + + // Power Control 2 + ISSUE_CMD_BYTE(0xC3); + ISSUE_DATA_BYTE(0x27); + + // Power Control 3 + ISSUE_CMD_BYTE(0xC4); + ISSUE_DATA_BYTE(0x18); + + // Power Control 4 + ISSUE_CMD_BYTE(0xC9); + ISSUE_DATA_BYTE(0x1F); + + ISSUE_CMD_BYTE(0xC5); + ISSUE_DATA_BYTE(0x0F); + + ISSUE_CMD_BYTE(0xC6); + ISSUE_DATA_BYTE(0x00); + + ISSUE_CMD_BYTE(0xC7); + ISSUE_DATA_BYTE(0x10); + + ISSUE_CMD_BYTE(0xC8); + ISSUE_DATA_BYTE(0x01); + + ISSUE_CMD_BYTE(0xFF); + ISSUE_DATA_BYTE(0x62); + + ISSUE_CMD_BYTE(0x99); + ISSUE_DATA_BYTE(0x3E); + + ISSUE_CMD_BYTE(0x9D); + ISSUE_DATA_BYTE(0x4B); + + ISSUE_CMD_BYTE(0x8E); + ISSUE_DATA_BYTE(0x0F); + + // SET_GAMMA1 + ISSUE_CMD_BYTE(0xF0); + ISSUE_DATA_BYTE(0x8F); + ISSUE_DATA_BYTE(0x1B); + ISSUE_DATA_BYTE(0x05); + ISSUE_DATA_BYTE(0x06); + ISSUE_DATA_BYTE(0x07); + ISSUE_DATA_BYTE(0x42); + + // SET_GAMMA3 + ISSUE_CMD_BYTE(0xF2); + ISSUE_DATA_BYTE(0x5C); + ISSUE_DATA_BYTE(0x1F); + ISSUE_DATA_BYTE(0x12); + ISSUE_DATA_BYTE(0x10); + ISSUE_DATA_BYTE(0x07); + ISSUE_DATA_BYTE(0x43); + + // SET_GAMMA2 + ISSUE_CMD_BYTE(0xF1); + ISSUE_DATA_BYTE(0x59); + ISSUE_DATA_BYTE(0xCF); + ISSUE_DATA_BYTE(0xCF); + ISSUE_DATA_BYTE(0x35); + ISSUE_DATA_BYTE(0x37); + ISSUE_DATA_BYTE(0x8F); + + // SET_GAMMA4 + ISSUE_CMD_BYTE(0xF3); + ISSUE_DATA_BYTE(0x58); + ISSUE_DATA_BYTE(0xCF); + ISSUE_DATA_BYTE(0xCF); + ISSUE_DATA_BYTE(0x35); + ISSUE_DATA_BYTE(0x37); + ISSUE_DATA_BYTE(0x8F); +} + +void tf15411a_rotate(int degrees, display_padding_t* padding) { + uint16_t shift = 0; + char BX = 0, BY = 0; + +#define RGB (1 << 3) +#define ML (1 << 4) // vertical refresh order +#define MH (1 << 2) // horizontal refresh order +#define MV (1 << 5) +#define MX (1 << 6) +#define MY (1 << 7) + // MADCTL: Memory Data Access Control - reference: + // section 9.3 in the ILI9341 manual + // section 6.2.18 in the GC9307 manual + // section 8.12 in the ST7789V manual + uint8_t display_command_parameter = 0; + switch (degrees) { + case 0: + display_command_parameter = 0; + BY = 1; + break; + case 90: + display_command_parameter = MV | MX | MH | ML; + BX = 0; + shift = 1; + break; + case 180: + display_command_parameter = MX | MY | MH | ML; + BY = 1; + shift = 1; + break; + case 270: + display_command_parameter = MV | MY; + BX = 0; + break; + } + + display_command_parameter ^= RGB | MY; // XOR RGB and MY settings + + ISSUE_CMD_BYTE(0x36); + ISSUE_DATA_BYTE(display_command_parameter); + + if (shift) { + // GATECTRL: Gate Control; NL = 240 gate lines, first scan line is + // gate 80.; gate scan direction 319 -> 0 + ISSUE_CMD_BYTE(0xE4); + ISSUE_DATA_BYTE(0x1D); + ISSUE_DATA_BYTE(0x00); + ISSUE_DATA_BYTE(0x11); + } else { + // GATECTRL: Gate Control; NL = 240 gate lines, first scan line is + // gate 80.; gate scan direction 319 -> 0 + ISSUE_CMD_BYTE(0xE4); + ISSUE_DATA_BYTE(0x1D); + ISSUE_DATA_BYTE(0x0A); + ISSUE_DATA_BYTE(0x11); + } + + padding->x = BX ? (320 - DISPLAY_RESY) : 0; + padding->y = BY ? (320 - DISPLAY_RESY) : 0; +} diff --git a/core/embed/trezorhal/stm32f4/display/st-7789/panels/tf15411a.h b/core/embed/trezorhal/stm32f4/display/st-7789/panels/tf15411a.h new file mode 100644 index 000000000..33988e0a7 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/st-7789/panels/tf15411a.h @@ -0,0 +1,30 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#ifndef TF15411A_H_ +#define TF15411A_H_ + +#include "../display_panel.h" + +// GC9307 IC controller + +void tf15411a_init_seq(void); +void tf15411a_rotate(int degrees, display_padding_t* padding); + +#endif diff --git a/core/embed/trezorhal/stm32f4/display/stm32f429i-disc1/display_driver.c b/core/embed/trezorhal/stm32f4/display/stm32f429i-disc1/display_driver.c new file mode 100644 index 000000000..a62ea9239 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/stm32f429i-disc1/display_driver.c @@ -0,0 +1,154 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#include +#include + +#include TREZOR_BOARD +#include STM32_HAL_H + +#include "display_internal.h" +#include "ili9341_spi.h" +#include "xdisplay.h" + +#if (DISPLAY_RESX != 240) || (DISPLAY_RESY != 320) +#error "Incompatible display resolution" +#endif + +// Display driver context. +typedef struct { + // Pointer to the frame buffer + uint16_t *framebuf; + // Current display orientation (0, 90, 180, 270) + int orientation_angle; + // Current backlight level ranging from 0 to 255 + int backlight_level; +} display_driver_t; + +// Display driver instance +static display_driver_t g_display_driver; + +void display_init(void) { + display_driver_t *drv = &g_display_driver; + memset(drv, 0, sizeof(display_driver_t)); + drv->framebuf = (uint16_t *)FRAME_BUFFER_ADDR; + + // Initialize LTDC controller + BSP_LCD_Init(); + // Initialize external display controller + ili9341_init(); +} + +void display_reinit(void) { + display_driver_t *drv = &g_display_driver; + memset(drv, 0, sizeof(display_driver_t)); + drv->framebuf = (uint16_t *)FRAME_BUFFER_ADDR; +} + +void display_finish_actions(void) { + // Not used and intentionally left empty +} + +int display_set_backlight(int level) { + display_driver_t *drv = &g_display_driver; + + // Just emulation, not doing anything + drv->backlight_level = level; + return level; +} + +int display_get_backlight(void) { + display_driver_t *drv = &g_display_driver; + + return drv->backlight_level; +} + +int display_set_orientation(int angle) { + display_driver_t *drv = &g_display_driver; + + if (angle == 0 || angle == 90 || angle == 180 || angle == 270) { + // Just emulation, not doing anything + drv->orientation_angle = angle; + } + + return drv->orientation_angle; +} + +int display_get_orientation(void) { + display_driver_t *drv = &g_display_driver; + + return drv->orientation_angle; +} + +display_fb_info_t display_get_frame_buffer(void) { + display_driver_t *drv = &g_display_driver; + + display_fb_info_t fb = { + .ptr = (void *)drv->framebuf, + .stride = DISPLAY_RESX * sizeof(uint16_t), + }; + + return fb; +} + +void display_refresh(void) { + // Do nothing as using just a single frame buffer +} + +void display_set_compatible_settings() {} + +void display_fill(const gl_bitblt_t *bb) { + display_driver_t *drv = &g_display_driver; + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = drv->framebuf + (DISPLAY_RESX * bb_new.dst_y); + bb_new.dst_stride = DISPLAY_RESX * sizeof(uint16_t); + + gl_rgb565_fill(&bb_new); +} + +void display_copy_rgb565(const gl_bitblt_t *bb) { + display_driver_t *drv = &g_display_driver; + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = drv->framebuf + (DISPLAY_RESX * bb_new.dst_y); + bb_new.dst_stride = DISPLAY_RESX * sizeof(uint16_t); + + gl_rgb565_copy_rgb565(&bb_new); +} + +void display_copy_mono1p(const gl_bitblt_t *bb) { + display_driver_t *drv = &g_display_driver; + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = drv->framebuf + (DISPLAY_RESX * bb_new.dst_y); + bb_new.dst_stride = DISPLAY_RESX * sizeof(uint16_t); + + gl_rgb565_copy_mono1p(&bb_new); +} + +void display_copy_mono4(const gl_bitblt_t *bb) { + display_driver_t *drv = &g_display_driver; + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = drv->framebuf + (DISPLAY_RESX * bb_new.dst_y); + bb_new.dst_stride = DISPLAY_RESX * sizeof(uint16_t); + + gl_rgb565_copy_mono4(&bb_new); +} diff --git a/core/embed/trezorhal/stm32f4/display/stm32f429i-disc1/display_internal.h b/core/embed/trezorhal/stm32f4/display/stm32f429i-disc1/display_internal.h new file mode 100644 index 000000000..c19c63a65 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/stm32f429i-disc1/display_internal.h @@ -0,0 +1,36 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#ifndef TREZORHAL_DISPLAY_INTERNAL_H +#define TREZORHAL_DISPLAY_INTERNAL_H + +#include TREZOR_BOARD +#include STM32_HAL_H + +#include "sdram.h" + +// Frame buffer address in external SDRAM +#define FRAME_BUFFER_ADDR ((uint32_t)SDRAM_DEVICE_ADDR) +// Frame buffer size (16-bit per pixel RGB565) +#define FRAME_BUFFER_SIZE (DISPLAY_RESX * DISPLAY_RESY * 2) + +// Initializes LTDC controller and I/O pins +void BSP_LCD_Init(void); + +#endif // TREZORHAL_DISPLAY_INTERNAL_H diff --git a/core/embed/trezorhal/stm32f4/display/stm32f429i-disc1/display_ltdc.c b/core/embed/trezorhal/stm32f4/display/stm32f429i-disc1/display_ltdc.c new file mode 100644 index 000000000..df3a752c3 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/stm32f429i-disc1/display_ltdc.c @@ -0,0 +1,291 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#include +#include + +#include TREZOR_BOARD +#include STM32_HAL_H + +#include "display_internal.h" +#include "ili9341_spi.h" +#include "xdisplay.h" + +#define MAX_LAYER_NUMBER 2 + +LTDC_HandleTypeDef LtdcHandler; +static RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; + +/* Default LCD configuration with LCD Layer 1 */ +uint32_t ActiveLayer = 0; + +/** + * @brief Initializes the LCD layers. + * @param LayerIndex: the layer foreground or background. + * @param FB_Address: the layer frame buffer. + */ +void BSP_LCD_LayerDefaultInit(uint16_t LayerIndex, uint32_t FB_Address) { + LTDC_LayerCfgTypeDef Layercfg; + + /* Layer Init */ + Layercfg.WindowX0 = 0; + Layercfg.WindowX1 = DISPLAY_RESX; + Layercfg.WindowY0 = 0; + Layercfg.WindowY1 = DISPLAY_RESY; + Layercfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565; + Layercfg.FBStartAdress = FB_Address; + Layercfg.Alpha = 255; + Layercfg.Alpha0 = 0; + Layercfg.Backcolor.Blue = 0; + Layercfg.Backcolor.Green = 0; + Layercfg.Backcolor.Red = 0; + Layercfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_PAxCA; + Layercfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_PAxCA; + Layercfg.ImageWidth = DISPLAY_RESX; + Layercfg.ImageHeight = DISPLAY_RESY; + + HAL_LTDC_ConfigLayer(&LtdcHandler, &Layercfg, LayerIndex); + + // DrawProp[LayerIndex].BackColor = LCD_COLOR_WHITE; + // DrawProp[LayerIndex].pFont = &Font24; + // DrawProp[LayerIndex].TextColor = LCD_COLOR_BLACK; + + /* Dithering activation */ + HAL_LTDC_EnableDither(&LtdcHandler); +} + +/** + * @brief Selects the LCD Layer. + * @param LayerIndex: the Layer foreground or background. + */ +void BSP_LCD_SelectLayer(uint32_t LayerIndex) { ActiveLayer = LayerIndex; } + +/** + * @brief Sets a LCD Layer visible. + * @param LayerIndex: the visible Layer. + * @param state: new state of the specified layer. + * This parameter can be: ENABLE or DISABLE. + */ +void BSP_LCD_SetLayerVisible(uint32_t LayerIndex, FunctionalState state) { + if (state == ENABLE) { + __HAL_LTDC_LAYER_ENABLE(&LtdcHandler, LayerIndex); + } else { + __HAL_LTDC_LAYER_DISABLE(&LtdcHandler, LayerIndex); + } + __HAL_LTDC_RELOAD_CONFIG(&LtdcHandler); +} + +/** + * @brief Sets an LCD Layer visible without reloading. + * @param LayerIndex: Visible Layer + * @param State: New state of the specified layer + * This parameter can be one of the following values: + * @arg ENABLE + * @arg DISABLE + * @retval None + */ +void BSP_LCD_SetLayerVisible_NoReload(uint32_t LayerIndex, + FunctionalState State) { + if (State == ENABLE) { + __HAL_LTDC_LAYER_ENABLE(&LtdcHandler, LayerIndex); + } else { + __HAL_LTDC_LAYER_DISABLE(&LtdcHandler, LayerIndex); + } + /* Do not Sets the Reload */ +} + +/** + * @brief Configures the Transparency. + * @param LayerIndex: the Layer foreground or background. + * @param Transparency: the Transparency, + * This parameter must range from 0x00 to 0xFF. + */ +void BSP_LCD_SetTransparency(uint32_t LayerIndex, uint8_t Transparency) { + HAL_LTDC_SetAlpha(&LtdcHandler, Transparency, LayerIndex); +} + +/** + * @brief Configures the transparency without reloading. + * @param LayerIndex: Layer foreground or background. + * @param Transparency: Transparency + * This parameter must be a number between Min_Data = 0x00 and + * Max_Data = 0xFF + * @retval None + */ +void BSP_LCD_SetTransparency_NoReload(uint32_t LayerIndex, + uint8_t Transparency) { + HAL_LTDC_SetAlpha_NoReload(&LtdcHandler, Transparency, LayerIndex); +} + +/** + * @brief Sets a LCD layer frame buffer address. + * @param LayerIndex: specifies the Layer foreground or background + * @param Address: new LCD frame buffer value + */ +void BSP_LCD_SetLayerAddress(uint32_t LayerIndex, uint32_t Address) { + HAL_LTDC_SetAddress(&LtdcHandler, Address, LayerIndex); +} + +/** + * @brief Sets an LCD layer frame buffer address without reloading. + * @param LayerIndex: Layer foreground or background + * @param Address: New LCD frame buffer value + * @retval None + */ +void BSP_LCD_SetLayerAddress_NoReload(uint32_t LayerIndex, uint32_t Address) { + HAL_LTDC_SetAddress_NoReload(&LtdcHandler, Address, LayerIndex); +} + +void BSP_LCD_Init(void) { + GPIO_InitTypeDef GPIO_InitStructure = {0}; + + /* Enable the LTDC and DMA2D Clock */ + __HAL_RCC_LTDC_CLK_ENABLE(); + __HAL_RCC_DMA2D_CLK_ENABLE(); + + /* Enable GPIOs clock */ + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOD_CLK_ENABLE(); + __HAL_RCC_GPIOF_CLK_ENABLE(); + __HAL_RCC_GPIOG_CLK_ENABLE(); + + /* GPIOs Configuration */ + /* + +------------------------+-----------------------+----------------------------+ + + LCD pins assignment + + +------------------------+-----------------------+----------------------------+ + | LCD_TFT R2 <-> PC.10 | LCD_TFT G2 <-> PA.06 | LCD_TFT B2 <-> PD.06 | | + LCD_TFT R3 <-> PB.00 | LCD_TFT G3 <-> PG.10 | LCD_TFT B3 <-> PG.11 | + | LCD_TFT R4 <-> PA.11 | LCD_TFT G4 <-> PB.10 | LCD_TFT B4 <-> PG.12 | | + LCD_TFT R5 <-> PA.12 | LCD_TFT G5 <-> PB.11 | LCD_TFT B5 <-> PA.03 | + | LCD_TFT R6 <-> PB.01 | LCD_TFT G6 <-> PC.07 | LCD_TFT B6 <-> PB.08 | | + LCD_TFT R7 <-> PG.06 | LCD_TFT G7 <-> PD.03 | LCD_TFT B7 <-> PB.09 | + ------------------------------------------------------------------------------- + | LCD_TFT HSYNC <-> PC.06 | LCDTFT VSYNC <-> PA.04 | + | LCD_TFT CLK <-> PG.07 | LCD_TFT DE <-> PF.10 | + ----------------------------------------------------- + */ + + /* GPIOA configuration */ + GPIO_InitStructure.Pin = + GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_6 | GPIO_PIN_11 | GPIO_PIN_12; + GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; + GPIO_InitStructure.Pull = GPIO_NOPULL; + GPIO_InitStructure.Speed = GPIO_SPEED_FAST; + GPIO_InitStructure.Alternate = GPIO_AF14_LTDC; + HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); + + /* GPIOB configuration */ + GPIO_InitStructure.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11; + HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); + + /* GPIOC configuration */ + GPIO_InitStructure.Pin = GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_10; + HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); + + /* GPIOD configuration */ + GPIO_InitStructure.Pin = GPIO_PIN_3 | GPIO_PIN_6; + HAL_GPIO_Init(GPIOD, &GPIO_InitStructure); + + /* GPIOF configuration */ + GPIO_InitStructure.Pin = GPIO_PIN_10; + HAL_GPIO_Init(GPIOF, &GPIO_InitStructure); + + /* GPIOG configuration */ + GPIO_InitStructure.Pin = GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_11; + HAL_GPIO_Init(GPIOG, &GPIO_InitStructure); + + /* GPIOB configuration */ + GPIO_InitStructure.Pin = GPIO_PIN_0 | GPIO_PIN_1; + GPIO_InitStructure.Alternate = GPIO_AF9_LTDC; + HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); + + /* GPIOG configuration */ + GPIO_InitStructure.Pin = GPIO_PIN_10 | GPIO_PIN_12; + HAL_GPIO_Init(GPIOG, &GPIO_InitStructure); + + /* On STM32F429I-DISCO, it is not possible to read ILI9341 ID because */ + /* PIN EXTC is not connected to VDD and then LCD_READ_ID4 is not accessible. + */ + /* In this case, ReadID function is bypassed.*/ + /*if(ili9341_drv.ReadID() == ILI9341_ID)*/ + + /* LTDC Configuration ----------------------------------------------------*/ + LtdcHandler.Instance = LTDC; + + /* Timing configuration (Typical configuration from ILI9341 datasheet) + HSYNC=10 (9+1) + HBP=20 (29-10+1) + ActiveW=240 (269-20-10+1) + HFP=10 (279-240-20-10+1) + + VSYNC=2 (1+1) + VBP=2 (3-2+1) + ActiveH=320 (323-2-2+1) + VFP=4 (327-320-2-2+1) + */ + + /* Configure horizontal synchronization width */ + LtdcHandler.Init.HorizontalSync = ILI9341_HSYNC; + /* Configure vertical synchronization height */ + LtdcHandler.Init.VerticalSync = ILI9341_VSYNC; + /* Configure accumulated horizontal back porch */ + LtdcHandler.Init.AccumulatedHBP = ILI9341_HBP; + /* Configure accumulated vertical back porch */ + LtdcHandler.Init.AccumulatedVBP = ILI9341_VBP; + /* Configure accumulated active width */ + LtdcHandler.Init.AccumulatedActiveW = 269; + /* Configure accumulated active height */ + LtdcHandler.Init.AccumulatedActiveH = 323; + /* Configure total width */ + LtdcHandler.Init.TotalWidth = 279; + /* Configure total height */ + LtdcHandler.Init.TotalHeigh = 327; + + /* Configure R,G,B component values for LCD background color */ + LtdcHandler.Init.Backcolor.Red = 0; + LtdcHandler.Init.Backcolor.Blue = 0; + LtdcHandler.Init.Backcolor.Green = 0; + + /* LCD clock configuration */ + /* PLLSAI_VCO Input = HSE_VALUE/PLL_M = 1 Mhz */ + /* PLLSAI_VCO Output = PLLSAI_VCO Input * PLLSAIN = 192 Mhz */ + /* PLLLCDCLK = PLLSAI_VCO Output/PLLSAIR = 192/4 = 48 Mhz */ + /* LTDC clock frequency = PLLLCDCLK / LTDC_PLLSAI_DIVR_8 = 48/4 = 6Mhz */ + PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC; + PeriphClkInitStruct.PLLSAI.PLLSAIN = 192; + PeriphClkInitStruct.PLLSAI.PLLSAIR = 4; + PeriphClkInitStruct.PLLSAIDivR = RCC_PLLSAIDIVR_8; + HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); + + /* Polarity */ + LtdcHandler.Init.HSPolarity = LTDC_HSPOLARITY_AL; + LtdcHandler.Init.VSPolarity = LTDC_VSPOLARITY_AL; + LtdcHandler.Init.DEPolarity = LTDC_DEPOLARITY_AL; + LtdcHandler.Init.PCPolarity = LTDC_PCPOLARITY_IPC; + + HAL_LTDC_Init(&LtdcHandler); + + /* Initialize the LCD Layers */ + BSP_LCD_LayerDefaultInit(1, FRAME_BUFFER_ADDR); + + memset((void *)FRAME_BUFFER_ADDR, 0, FRAME_BUFFER_SIZE); +} diff --git a/core/embed/trezorhal/stm32f4/display/stm32f429i-disc1/ili9341_spi.c b/core/embed/trezorhal/stm32f4/display/stm32f429i-disc1/ili9341_spi.c new file mode 100644 index 000000000..309fb3f0d --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/stm32f429i-disc1/ili9341_spi.c @@ -0,0 +1,512 @@ + + +#include +#include TREZOR_BOARD +#include "ili9341_spi.h" +#include STM32_HAL_H + +/** + * @brief ILI9341 chip IDs + */ +#define ILI9341_ID 0x9341 + +/** + * @brief ILI9341 Size + */ +#define ILI9341_LCD_PIXEL_WIDTH ((uint16_t)240) +#define ILI9341_LCD_PIXEL_HEIGHT ((uint16_t)320) + +/** + * @brief ILI9341 Timing + */ +/* Timing configuration (Typical configuration from ILI9341 datasheet) + HSYNC=10 (9+1) + HBP=20 (29-10+1) + ActiveW=240 (269-20-10+1) + HFP=10 (279-240-20-10+1) + + VSYNC=2 (1+1) + VBP=2 (3-2+1) + ActiveH=320 (323-2-2+1) + VFP=4 (327-320-2-2+1) +*/ + +/** + * @brief ILI9341 Registers + */ + +/* Level 1 Commands */ +#define LCD_SWRESET 0x01 /* Software Reset */ +#define LCD_READ_DISPLAY_ID 0x04 /* Read display identification information */ +#define LCD_RDDST 0x09 /* Read Display Status */ +#define LCD_RDDPM 0x0A /* Read Display Power Mode */ +#define LCD_RDDMADCTL 0x0B /* Read Display MADCTL */ +#define LCD_RDDCOLMOD 0x0C /* Read Display Pixel Format */ +#define LCD_RDDIM 0x0D /* Read Display Image Format */ +#define LCD_RDDSM 0x0E /* Read Display Signal Mode */ +#define LCD_RDDSDR 0x0F /* Read Display Self-Diagnostic Result */ +#define LCD_SPLIN 0x10 /* Enter Sleep Mode */ +#define LCD_SLEEP_OUT 0x11 /* Sleep out register */ +#define LCD_PTLON 0x12 /* Partial Mode ON */ +#define LCD_NORMAL_MODE_ON 0x13 /* Normal Display Mode ON */ +#define LCD_DINVOFF 0x20 /* Display Inversion OFF */ +#define LCD_DINVON 0x21 /* Display Inversion ON */ +#define LCD_GAMMA 0x26 /* Gamma register */ +#define LCD_DISPLAY_OFF 0x28 /* Display off register */ +#define LCD_DISPLAY_ON 0x29 /* Display on register */ +#define LCD_COLUMN_ADDR 0x2A /* Colomn address register */ +#define LCD_PAGE_ADDR 0x2B /* Page address register */ +#define LCD_GRAM 0x2C /* GRAM register */ +#define LCD_RGBSET 0x2D /* Color SET */ +#define LCD_RAMRD 0x2E /* Memory Read */ +#define LCD_PLTAR 0x30 /* Partial Area */ +#define LCD_VSCRDEF 0x33 /* Vertical Scrolling Definition */ +#define LCD_TEOFF 0x34 /* Tearing Effect Line OFF */ +#define LCD_TEON 0x35 /* Tearing Effect Line ON */ +#define LCD_MAC 0x36 /* Memory Access Control register*/ +#define LCD_VSCRSADD 0x37 /* Vertical Scrolling Start Address */ +#define LCD_IDMOFF 0x38 /* Idle Mode OFF */ +#define LCD_IDMON 0x39 /* Idle Mode ON */ +#define LCD_PIXEL_FORMAT 0x3A /* Pixel Format register */ +#define LCD_WRITE_MEM_CONTINUE 0x3C /* Write Memory Continue */ +#define LCD_READ_MEM_CONTINUE 0x3E /* Read Memory Continue */ +#define LCD_SET_TEAR_SCANLINE 0x44 /* Set Tear Scanline */ +#define LCD_GET_SCANLINE 0x45 /* Get Scanline */ +#define LCD_WDB 0x51 /* Write Brightness Display register */ +#define LCD_RDDISBV 0x52 /* Read Display Brightness */ +#define LCD_WCD 0x53 /* Write Control Display register*/ +#define LCD_RDCTRLD 0x54 /* Read CTRL Display */ +#define LCD_WRCABC 0x55 /* Write Content Adaptive Brightness Control */ +#define LCD_RDCABC 0x56 /* Read Content Adaptive Brightness Control */ +#define LCD_WRITE_CABC 0x5E /* Write CABC Minimum Brightness */ +#define LCD_READ_CABC 0x5F /* Read CABC Minimum Brightness */ +#define LCD_READ_ID1 0xDA /* Read ID1 */ +#define LCD_READ_ID2 0xDB /* Read ID2 */ +#define LCD_READ_ID3 0xDC /* Read ID3 */ + +/* Level 2 Commands */ +#define LCD_RGB_INTERFACE 0xB0 /* RGB Interface Signal Control */ +#define LCD_FRMCTR1 0xB1 /* Frame Rate Control (In Normal Mode) */ +#define LCD_FRMCTR2 0xB2 /* Frame Rate Control (In Idle Mode) */ +#define LCD_FRMCTR3 0xB3 /* Frame Rate Control (In Partial Mode) */ +#define LCD_INVTR 0xB4 /* Display Inversion Control */ +#define LCD_BPC 0xB5 /* Blanking Porch Control register */ +#define LCD_DFC 0xB6 /* Display Function Control register */ +#define LCD_ETMOD 0xB7 /* Entry Mode Set */ +#define LCD_BACKLIGHT1 0xB8 /* Backlight Control 1 */ +#define LCD_BACKLIGHT2 0xB9 /* Backlight Control 2 */ +#define LCD_BACKLIGHT3 0xBA /* Backlight Control 3 */ +#define LCD_BACKLIGHT4 0xBB /* Backlight Control 4 */ +#define LCD_BACKLIGHT5 0xBC /* Backlight Control 5 */ +#define LCD_BACKLIGHT7 0xBE /* Backlight Control 7 */ +#define LCD_BACKLIGHT8 0xBF /* Backlight Control 8 */ +#define LCD_POWER1 0xC0 /* Power Control 1 register */ +#define LCD_POWER2 0xC1 /* Power Control 2 register */ +#define LCD_VCOM1 0xC5 /* VCOM Control 1 register */ +#define LCD_VCOM2 0xC7 /* VCOM Control 2 register */ +#define LCD_NVMWR 0xD0 /* NV Memory Write */ +#define LCD_NVMPKEY 0xD1 /* NV Memory Protection Key */ +#define LCD_RDNVM 0xD2 /* NV Memory Status Read */ +#define LCD_READ_ID4 0xD3 /* Read ID4 */ +#define LCD_PGAMMA 0xE0 /* Positive Gamma Correction register */ +#define LCD_NGAMMA 0xE1 /* Negative Gamma Correction register */ +#define LCD_DGAMCTRL1 0xE2 /* Digital Gamma Control 1 */ +#define LCD_DGAMCTRL2 0xE3 /* Digital Gamma Control 2 */ +#define LCD_INTERFACE 0xF6 /* Interface control register */ + +/* Extend register commands */ +#define LCD_POWERA 0xCB /* Power control A register */ +#define LCD_POWERB 0xCF /* Power control B register */ +#define LCD_DTCA 0xE8 /* Driver timing control A */ +#define LCD_DTCB 0xEA /* Driver timing control B */ +#define LCD_POWER_SEQ 0xED /* Power on sequence register */ +#define LCD_3GAMMA_EN 0xF2 /* 3 Gamma enable register */ +#define LCD_PRC 0xF7 /* Pump ratio control register */ + +/* Size of read registers */ +#define LCD_READ_ID4_SIZE 3 /* Size of Read ID4 */ + +/*############################### SPIx #######################################*/ +#define DISCOVERY_SPIx SPI5 +#define DISCOVERY_SPIx_CLK_ENABLE() __HAL_RCC_SPI5_CLK_ENABLE() +#define DISCOVERY_SPIx_GPIO_PORT GPIOF /* GPIOF */ +#define DISCOVERY_SPIx_AF GPIO_AF5_SPI5 +#define DISCOVERY_SPIx_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE() +#define DISCOVERY_SPIx_GPIO_CLK_DISABLE() __HAL_RCC_GPIOF_CLK_DISABLE() +#define DISCOVERY_SPIx_SCK_PIN GPIO_PIN_7 /* PF.07 */ +#define DISCOVERY_SPIx_MISO_PIN GPIO_PIN_8 /* PF.08 */ +#define DISCOVERY_SPIx_MOSI_PIN GPIO_PIN_9 /* PF.09 */ +/* Maximum Timeout values for flags waiting loops. These timeouts are not based + on accurate values, they just guarantee that the application will not remain + stuck if the SPI communication is corrupted. + You may modify these timeout values depending on CPU frequency and + application conditions (interrupts routines ...). */ +#define SPIx_TIMEOUT_MAX ((uint32_t)0x1000) + +/*################################ LCD #######################################*/ +/* Chip Select macro definition */ +#define LCD_CS_LOW() \ + HAL_GPIO_WritePin(LCD_NCS_GPIO_PORT, LCD_NCS_PIN, GPIO_PIN_RESET) +#define LCD_CS_HIGH() \ + HAL_GPIO_WritePin(LCD_NCS_GPIO_PORT, LCD_NCS_PIN, GPIO_PIN_SET) + +/* Set WRX High to send data */ +#define LCD_WRX_LOW() \ + HAL_GPIO_WritePin(LCD_WRX_GPIO_PORT, LCD_WRX_PIN, GPIO_PIN_RESET) +#define LCD_WRX_HIGH() \ + HAL_GPIO_WritePin(LCD_WRX_GPIO_PORT, LCD_WRX_PIN, GPIO_PIN_SET) + +/* Set WRX High to send data */ +#define LCD_RDX_LOW() \ + HAL_GPIO_WritePin(LCD_RDX_GPIO_PORT, LCD_RDX_PIN, GPIO_PIN_RESET) +#define LCD_RDX_HIGH() \ + HAL_GPIO_WritePin(LCD_RDX_GPIO_PORT, LCD_RDX_PIN, GPIO_PIN_SET) + +/** + * @brief LCD Control pin + */ +#define LCD_NCS_PIN GPIO_PIN_2 +#define LCD_NCS_GPIO_PORT GPIOC +#define LCD_NCS_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE() +#define LCD_NCS_GPIO_CLK_DISABLE() __HAL_RCC_GPIOC_CLK_DISABLE() +/** + * @} + */ +/** + * @brief LCD Command/data pin + */ +#define LCD_WRX_PIN GPIO_PIN_13 +#define LCD_WRX_GPIO_PORT GPIOD +#define LCD_WRX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE() +#define LCD_WRX_GPIO_CLK_DISABLE() __HAL_RCC_GPIOD_CLK_DISABLE() + +#define LCD_RDX_PIN GPIO_PIN_12 +#define LCD_RDX_GPIO_PORT GPIOD +#define LCD_RDX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE() +#define LCD_RDX_GPIO_CLK_DISABLE() __HAL_RCC_GPIOD_CLK_DISABLE() + +static SPI_HandleTypeDef SpiHandle; +uint32_t SpixTimeout = + SPIx_TIMEOUT_MAX; /*. + */ + +#include +#include + +#include TREZOR_BOARD +#include STM32_HAL_H + +#include "xdisplay.h" + +#if (DISPLAY_RESX != 128) || (DISPLAY_RESY != 128) +#error "Incompatible display resolution" +#endif + +// This file implements display driver for monochromatic display V-2864KSWEG01 +// with 128x128 resolution connected to CPU via SPI interface. +// +// This type of displayed was used on some preliminary dev kits for T3T1 (Trezor +// TS3) + +// Display driver context. +typedef struct { + // Frame buffer (8-bit Mono) + uint8_t framebuf[DISPLAY_RESX * DISPLAY_RESY]; + // Current display orientation (0 or 180) + int orientation_angle; + // Current backlight level ranging from 0 to 255 + int backlight_level; +} display_driver_t; + +// Display driver instance +static display_driver_t g_display_driver; + +// Macros to access display parallel interface + +// FSMC/FMC Bank 1 - NOR/PSRAM 1 +#define DISPLAY_MEMORY_BASE 0x60000000 +#define DISPLAY_MEMORY_PIN 16 + +#define CMD_ADDR *((__IO uint8_t *)((uint32_t)(DISPLAY_MEMORY_BASE))) +#define DATA_ADDR \ + (*((__IO uint8_t *)((uint32_t)(DISPLAY_MEMORY_BASE | \ + (1 << DISPLAY_MEMORY_PIN))))) + +#define ISSUE_CMD_BYTE(X) \ + do { \ + (CMD_ADDR) = (X); \ + } while (0) +#define ISSUE_DATA_BYTE(X) \ + do { \ + (DATA_ADDR) = (X); \ + } while (0) + +// --------------------------------------------------------------------------- +// Display controller registers +// --------------------------------------------------------------------------- + +#define OLED_SETCONTRAST 0x81 +#define OLED_DISPLAYALLON_RESUME 0xA4 +#define OLED_DISPLAYALLON 0xA5 +#define OLED_NORMALDISPLAY 0xA6 +#define OLED_INVERTDISPLAY 0xA7 +#define OLED_DISPLAYOFF 0xAE +#define OLED_DISPLAYON 0xAF +#define OLED_SETDISPLAYOFFSET 0xD3 +#define OLED_SETCOMPINS 0xDA +#define OLED_SETVCOMDETECT 0xDB +#define OLED_SETDISPLAYCLOCKDIV 0xD5 +#define OLED_SETPRECHARGE 0xD9 +#define OLED_SETMULTIPLEX 0xA8 +#define OLED_SETLOWCOLUMN 0x00 +#define OLED_SETHIGHCOLUMN 0x10 +#define OLED_SETSTARTLINE 0x40 +#define OLED_MEMORYMODE 0x20 +#define OLED_COMSCANINC 0xC0 +#define OLED_COMSCANDEC 0xC8 +#define OLED_SEGREMAP 0xA0 +#define OLED_CHARGEPUMP 0x8D + +// Dipslay specific initialization sequence +static const uint8_t ug_2828tswig01_init_seq[] = { + OLED_DISPLAYOFF, + // Divide ratio 0, Oscillator Frequency +0% + OLED_SETDISPLAYCLOCKDIV, 0x50, + // Set Memory Addressing Mode - page addressing mode + 0x20, + // Set Contrast Control Register + OLED_SETCONTRAST, 0x8F, + // Set DC-DC Setting: (Double Bytes Command) + 0xAD, 0x8A, + // Set Segment Re-map + OLED_SEGREMAP | 0x01, + // Set COM Output Scan Direction + OLED_COMSCANDEC, + // Set Display Start Line:(Double Bytes Command) + 0xDC, 0x00, + // Set Display Offset:(Double Bytes Command) + OLED_SETDISPLAYOFFSET, 0x00, + // Set Discharge / Pre-Charge Period (Double Bytes Command) + OLED_SETPRECHARGE, 0x22, + // Set VCOM Deselect Level + OLED_SETVCOMDETECT, 0x35, + // Set Multiplex Ratio + OLED_SETMULTIPLEX, 0x7F, + // Set Page + 0xB0, + // Reset column + OLED_SETLOWCOLUMN | 0, OLED_SETHIGHCOLUMN | 0, + + // Set Entire Display Off + // to be clear, this command turns off the function + // which turns entire display on, but it does not clear + // the data in display RAM + OLED_DISPLAYALLON_RESUME, + // Set Normal Display + OLED_NORMALDISPLAY}; + +static void __attribute__((unused)) display_sleep(void) { + // Display OFF + ISSUE_CMD_BYTE(OLED_DISPLAYOFF); + HAL_Delay(5); + // Vpp disable + HAL_GPIO_WritePin(GPIOD, GPIO_PIN_8, GPIO_PIN_RESET); +} + +static void display_resume(void) { + // Vpp enable + HAL_GPIO_WritePin(GPIOD, GPIO_PIN_8, GPIO_PIN_SET); + // 100 ms mandatory wait + HAL_Delay(100); + // Display ON + ISSUE_CMD_BYTE(OLED_DISPLAYON); +} + +// Sets the display cursor to the specific row and column +static void display_set_page_and_col(uint8_t page, uint8_t col) { + if (page < (DISPLAY_RESY / 8)) { + ISSUE_CMD_BYTE(0xB0 | (page & 0xF)); + + if (col < DISPLAY_RESX) { + ISSUE_CMD_BYTE(OLED_SETHIGHCOLUMN | ((col & 0x70) >> 4)); + ISSUE_CMD_BYTE(OLED_SETLOWCOLUMN | (col & 0x0F)); + } else { + // Reset column to start + ISSUE_CMD_BYTE(OLED_SETHIGHCOLUMN); + ISSUE_CMD_BYTE(OLED_SETLOWCOLUMN); + } + } +} + +#define COLLECT_ROW_BYTE(src) \ + (0 | (*(src + (0 * DISPLAY_RESX)) >= 128 ? 128 : 0) | \ + (*(src + (1 * DISPLAY_RESX)) >= 128 ? 64 : 0) | \ + (*(src + (2 * DISPLAY_RESX)) >= 128 ? 32 : 0) | \ + (*(src + (3 * DISPLAY_RESX)) >= 128 ? 16 : 0) | \ + (*(src + (4 * DISPLAY_RESX)) >= 128 ? 8 : 0) | \ + (*(src + (5 * DISPLAY_RESX)) >= 128 ? 4 : 0) | \ + (*(src + (6 * DISPLAY_RESX)) >= 128 ? 2 : 0) | \ + (*(src + (7 * DISPLAY_RESX)) >= 128 ? 1 : 0)) + +// Copies the framebuffer to the display via SPI interface +static void display_sync_with_fb(void) { + display_driver_t *drv = &g_display_driver; + + for (int y = 0; y < DISPLAY_RESY / 8; y++) { + display_set_page_and_col(y, 0); + uint8_t *src = &drv->framebuf[y * DISPLAY_RESX * 8]; + for (int x = 0; x < DISPLAY_RESX; x++) { + ISSUE_DATA_BYTE(COLLECT_ROW_BYTE(src)); + src++; + } + } +} + +static void display_init_controller(void) { + // LCD_RST/PC14 + HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_RESET); + // wait 10 milliseconds. only needs to be low for 10 microseconds. + // my dev display module ties display reset and touch panel reset together. + // keeping this low for max(display_reset_time, ctpm_reset_time) aids + // development and does not hurt. + HAL_Delay(10); + + // LCD_RST/PC14 + HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_SET); + // max wait time for hardware reset is 120 milliseconds + // (experienced display flakiness using only 5ms wait before sending commands) + HAL_Delay(120); + + // Apply initialization sequence specific to this display controller/panel + for (int i = 0; i < sizeof(ug_2828tswig01_init_seq); i++) { + ISSUE_CMD_BYTE(ug_2828tswig01_init_seq[i]); + } + + // Resume the suspended display + display_resume(); + // Clear display internal framebuffer + display_sync_with_fb(); +} + +static void display_init_interface(void) { + // init peripherals + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOD_CLK_ENABLE(); + __HAL_RCC_GPIOE_CLK_ENABLE(); + __HAL_RCC_FMC_CLK_ENABLE(); + + GPIO_InitTypeDef GPIO_InitStructure = {0}; + + // LCD_RST/PC14 + GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStructure.Pull = GPIO_NOPULL; + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStructure.Alternate = 0; + GPIO_InitStructure.Pin = GPIO_PIN_14; + // default to keeping display in reset + HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_RESET); + HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); + + // VPP Enable + GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStructure.Pull = GPIO_PULLDOWN; + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStructure.Alternate = 0; + GPIO_InitStructure.Pin = GPIO_PIN_8; + HAL_GPIO_WritePin(GPIOD, GPIO_PIN_8, GPIO_PIN_RESET); + HAL_GPIO_Init(GPIOD, &GPIO_InitStructure); + + GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; + GPIO_InitStructure.Pull = GPIO_NOPULL; + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStructure.Alternate = GPIO_AF12_FMC; + // LCD_CS/PD7 LCD_RS/PD11 LCD_RD/PD4 LCD_WR/PD5 + GPIO_InitStructure.Pin = GPIO_PIN_7 | GPIO_PIN_11 | GPIO_PIN_4 | GPIO_PIN_5; + HAL_GPIO_Init(GPIOD, &GPIO_InitStructure); + // LCD_D0/PD14 LCD_D1/PD15 LCD_D2/PD0 LCD_D3/PD1 + GPIO_InitStructure.Pin = GPIO_PIN_14 | GPIO_PIN_15 | GPIO_PIN_0 | GPIO_PIN_1; + HAL_GPIO_Init(GPIOD, &GPIO_InitStructure); + // LCD_D4/PE7 LCD_D5/PE8 LCD_D6/PE9 LCD_D7/PE10 + GPIO_InitStructure.Pin = GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10; + HAL_GPIO_Init(GPIOE, &GPIO_InitStructure); + + // Reference UM1725 "Description of STM32F4 HAL and LL drivers", + // section 64.2.1 "How to use this driver" + SRAM_HandleTypeDef display_sram = {0}; + display_sram.Instance = FMC_NORSRAM_DEVICE; + display_sram.Extended = FMC_NORSRAM_EXTENDED_DEVICE; + display_sram.Init.NSBank = FMC_NORSRAM_BANK1; + display_sram.Init.DataAddressMux = FMC_DATA_ADDRESS_MUX_DISABLE; + display_sram.Init.MemoryType = FMC_MEMORY_TYPE_SRAM; + display_sram.Init.MemoryDataWidth = FMC_NORSRAM_MEM_BUS_WIDTH_8; + display_sram.Init.BurstAccessMode = FMC_BURST_ACCESS_MODE_DISABLE; + display_sram.Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_LOW; + display_sram.Init.WrapMode = FMC_WRAP_MODE_DISABLE; + display_sram.Init.WaitSignalActive = FMC_WAIT_TIMING_BEFORE_WS; + display_sram.Init.WriteOperation = FMC_WRITE_OPERATION_ENABLE; + display_sram.Init.WaitSignal = FMC_WAIT_SIGNAL_DISABLE; + display_sram.Init.ExtendedMode = FMC_EXTENDED_MODE_DISABLE; + display_sram.Init.AsynchronousWait = FMC_ASYNCHRONOUS_WAIT_DISABLE; + display_sram.Init.WriteBurst = FMC_WRITE_BURST_DISABLE; + display_sram.Init.ContinuousClock = FMC_CONTINUOUS_CLOCK_SYNC_ONLY; + display_sram.Init.PageSize = FMC_PAGE_SIZE_NONE; + + // reference RM0090 section 37.5 Table 259, 37.5.4, Mode 1 SRAM, and 37.5.6 + FMC_NORSRAM_TimingTypeDef normal_mode_timing = {0}; + normal_mode_timing.AddressSetupTime = 10; + normal_mode_timing.AddressHoldTime = 10; + normal_mode_timing.DataSetupTime = 10; + normal_mode_timing.BusTurnAroundDuration = 0; + normal_mode_timing.CLKDivision = 2; + normal_mode_timing.DataLatency = 2; + normal_mode_timing.AccessMode = FMC_ACCESS_MODE_A; + + HAL_SRAM_Init(&display_sram, &normal_mode_timing, NULL); +} + +void display_init(void) { + display_driver_t *drv = &g_display_driver; + memset(drv, 0, sizeof(display_driver_t)); + + // Initialize GPIO & FSMC controller + display_init_interface(); + // Initialize display controller + display_init_controller(); +} + +void display_reinit(void) { + display_driver_t *drv = &g_display_driver; + memset(drv, 0, sizeof(display_driver_t)); + + // !@# TODO backlight level?? +} + +void display_finish_actions(void) { + /// Not used and intentionally left empty +} + +int display_set_backlight(int level) { + display_driver_t *drv = &g_display_driver; + + if (level != drv->backlight_level) { + if (level >= 0 && level <= 255) { + drv->backlight_level = level; + // Set Contrast Control Register: (Double Bytes Command) + ISSUE_CMD_BYTE(OLED_SETCONTRAST); + ISSUE_CMD_BYTE(level & 0xFF); + } + } + + return drv->backlight_level; +} + +int display_get_backlight(void) { + display_driver_t *drv = &g_display_driver; + + return drv->backlight_level; +} + +int display_set_orientation(int angle) { + display_driver_t *drv = &g_display_driver; + + if (angle != drv->orientation_angle) { + if (angle == 0 || angle == 180) { + drv->orientation_angle = angle; + if (angle == 0) { + // Set Segment Re-map: (A0H - A1H) + ISSUE_CMD_BYTE(OLED_SEGREMAP | 0x01); + // Set COM Output Scan Direction + ISSUE_CMD_BYTE(OLED_COMSCANDEC); + } else { + // Set Segment Re-map: (A0H - A1H) + ISSUE_CMD_BYTE(OLED_SEGREMAP | 0x00); + // Set COM Output Scan Direction + ISSUE_CMD_BYTE(OLED_COMSCANINC); + } + } + } + + return drv->orientation_angle; +} + +int display_get_orientation(void) { + display_driver_t *drv = &g_display_driver; + + return drv->orientation_angle; +} + +display_fb_info_t display_get_frame_buffer(void) { + display_driver_t *drv = &g_display_driver; + + display_fb_info_t fb = { + .ptr = &drv->framebuf[0], + .stride = DISPLAY_RESX, + }; + + return fb; +} + +void display_refresh(void) { display_sync_with_fb(); } + +void display_set_compatible_settings() {} + +void display_fill(const gl_bitblt_t *bb) { + display_driver_t *drv = &g_display_driver; + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = &drv->framebuf[DISPLAY_RESX * bb_new.dst_y]; + bb_new.dst_stride = DISPLAY_RESX; + + mono8_fill(&bb_new); +} + +void display_copy_mono1p(const gl_bitblt_t *bb) { + display_driver_t *drv = &g_display_driver; + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = &drv->framebuf[DISPLAY_RESX * bb_new.dst_y]; + bb_new.dst_stride = DISPLAY_RESX; + + mono8_copy_mono1p(&bb_new); +} diff --git a/core/embed/trezorhal/stm32f4/display/vg-2864/display_driver.c b/core/embed/trezorhal/stm32f4/display/vg-2864/display_driver.c new file mode 100644 index 000000000..e1495a67b --- /dev/null +++ b/core/embed/trezorhal/stm32f4/display/vg-2864/display_driver.c @@ -0,0 +1,379 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#include +#include +#include +#include + +#include TREZOR_BOARD +#include STM32_HAL_H + +#include "xdisplay.h" + +#ifdef USE_CONSUMPTION_MASK +#include "consumption_mask.h" +#endif + +#if (DISPLAY_RESX != 128) || (DISPLAY_RESY != 64) +#error "Incompatible display resolution" +#endif + +// This file implements display driver for monochromatic display V-2864KSWEG01 +// with 128x64 resolution connected to CPU via SPI interface. +// +// This type of display is used with T3T1 model (Trezor TS3) + +// Display driver context. +typedef struct { + // SPI driver instance + SPI_HandleTypeDef spi; + // Frame buffer (8-bit Mono) + uint8_t framebuf[DISPLAY_RESX * DISPLAY_RESY]; + // Current display orientation (0 or 180) + int orientation_angle; + // Current backlight level ranging from 0 to 255 + int backlight_level; +} display_driver_t; + +// Display driver instance +static display_driver_t g_display_driver; + +// Display controller registers +#define OLED_SETCONTRAST 0x81 +#define OLED_DISPLAYALLON_RESUME 0xA4 +#define OLED_DISPLAYALLON 0xA5 +#define OLED_NORMALDISPLAY 0xA6 +#define OLED_INVERTDISPLAY 0xA7 +#define OLED_DISPLAYOFF 0xAE +#define OLED_DISPLAYON 0xAF +#define OLED_SETDISPLAYOFFSET 0xD3 +#define OLED_SETCOMPINS 0xDA +#define OLED_SETVCOMDETECT 0xDB +#define OLED_SETDISPLAYCLOCKDIV 0xD5 +#define OLED_SETPRECHARGE 0xD9 +#define OLED_SETMULTIPLEX 0xA8 +#define OLED_SETLOWCOLUMN 0x00 +#define OLED_SETHIGHCOLUMN 0x10 +#define OLED_SETSTARTLINE 0x40 +#define OLED_MEMORYMODE 0x20 +#define OLED_COMSCANINC 0xC0 +#define OLED_COMSCANDEC 0xC8 +#define OLED_SEGREMAP 0xA0 +#define OLED_CHARGEPUMP 0x8D + +// Display controller initialization sequence +static const uint8_t vg_2864ksweg01_init_seq[] = {OLED_DISPLAYOFF, + OLED_SETDISPLAYCLOCKDIV, + 0x80, + OLED_SETMULTIPLEX, + 0x3F, // 128x64 + OLED_SETDISPLAYOFFSET, + 0x00, + OLED_SETSTARTLINE | 0x00, + OLED_CHARGEPUMP, + 0x14, + OLED_MEMORYMODE, + 0x00, + OLED_SEGREMAP | 0x01, + OLED_COMSCANDEC, + OLED_SETCOMPINS, + 0x12, // 128x64 + OLED_SETCONTRAST, + 0xCF, + OLED_SETPRECHARGE, + 0xF1, + OLED_SETVCOMDETECT, + 0x40, + OLED_DISPLAYALLON_RESUME, + OLED_NORMALDISPLAY, + OLED_DISPLAYON}; + +// Configures SPI driver/controller +static bool display_init_spi(display_driver_t *drv) { + drv->spi.Instance = OLED_SPI; + drv->spi.State = HAL_SPI_STATE_RESET; + drv->spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; + drv->spi.Init.Direction = SPI_DIRECTION_2LINES; + drv->spi.Init.CLKPhase = SPI_PHASE_1EDGE; + drv->spi.Init.CLKPolarity = SPI_POLARITY_LOW; + drv->spi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; + drv->spi.Init.CRCPolynomial = 7; + drv->spi.Init.DataSize = SPI_DATASIZE_8BIT; + drv->spi.Init.FirstBit = SPI_FIRSTBIT_MSB; + drv->spi.Init.NSS = SPI_NSS_HARD_OUTPUT; + drv->spi.Init.TIMode = SPI_TIMODE_DISABLE; + drv->spi.Init.Mode = SPI_MODE_MASTER; + + return (HAL_OK == HAL_SPI_Init(&drv->spi)) ? true : false; +} + +// Sends specified number of bytes to the display via SPI interface +static void display_send_bytes(display_driver_t *drv, const uint8_t *data, + size_t len) { + volatile int32_t timeout = 1000; + for (int i = 0; i < timeout; i++) + ; + + if (HAL_OK != HAL_SPI_Transmit(&drv->spi, (uint8_t *)data, len, 1000)) { + // TODO: error + return; + } + while (HAL_SPI_STATE_READY != HAL_SPI_GetState(&drv->spi)) { + } +} + +#define COLLECT_ROW_BYTE(src) \ + (0 | (*(src + (0 * DISPLAY_RESX)) >= 128 ? 128 : 0) | \ + (*(src + (1 * DISPLAY_RESX)) >= 128 ? 64 : 0) | \ + (*(src + (2 * DISPLAY_RESX)) >= 128 ? 32 : 0) | \ + (*(src + (3 * DISPLAY_RESX)) >= 128 ? 16 : 0) | \ + (*(src + (4 * DISPLAY_RESX)) >= 128 ? 8 : 0) | \ + (*(src + (5 * DISPLAY_RESX)) >= 128 ? 4 : 0) | \ + (*(src + (6 * DISPLAY_RESX)) >= 128 ? 2 : 0) | \ + (*(src + (7 * DISPLAY_RESX)) >= 128 ? 1 : 0)) + +#define COLLECT_ROW_BYTE_REV(src) \ + (0 | (*(src + (0 * DISPLAY_RESX)) >= 128 ? 1 : 0) | \ + (*(src + (1 * DISPLAY_RESX)) >= 128 ? 2 : 0) | \ + (*(src + (2 * DISPLAY_RESX)) >= 128 ? 4 : 0) | \ + (*(src + (3 * DISPLAY_RESX)) >= 128 ? 8 : 0) | \ + (*(src + (4 * DISPLAY_RESX)) >= 128 ? 16 : 0) | \ + (*(src + (5 * DISPLAY_RESX)) >= 128 ? 32 : 0) | \ + (*(src + (6 * DISPLAY_RESX)) >= 128 ? 64 : 0) | \ + (*(src + (7 * DISPLAY_RESX)) >= 128 ? 128 : 0)) + +// Copies the framebuffer to the display via SPI interface +static void display_sync_with_fb(display_driver_t *drv) { + static const uint8_t cursor_set_seq[3] = {OLED_SETLOWCOLUMN | 0x00, + OLED_SETHIGHCOLUMN | 0x00, + OLED_SETSTARTLINE | 0x00}; + + // SPI select + HAL_GPIO_WritePin(OLED_CS_PORT, OLED_CS_PIN, GPIO_PIN_RESET); + // Set the cursor to the screen top-left corner + display_send_bytes(drv, &cursor_set_seq[0], sizeof(cursor_set_seq)); + + // SPI deselect + HAL_GPIO_WritePin(OLED_CS_PORT, OLED_CS_PIN, GPIO_PIN_SET); + // Set to DATA + HAL_GPIO_WritePin(OLED_DC_PORT, OLED_DC_PIN, GPIO_PIN_SET); + // SPI select + HAL_GPIO_WritePin(OLED_CS_PORT, OLED_CS_PIN, GPIO_PIN_RESET); + + // Send whole framebuffer to the display + + if (drv->orientation_angle == 0) { + for (int y = DISPLAY_RESY / 8 - 1; y >= 0; y--) { + uint8_t buff[DISPLAY_RESX]; + uint8_t *src = &drv->framebuf[y * DISPLAY_RESX * 8]; + + for (int x = DISPLAY_RESX - 1; x >= 0; x--) { + buff[x] = COLLECT_ROW_BYTE(src); + src++; + } + + if (HAL_OK != HAL_SPI_Transmit(&drv->spi, &buff[0], sizeof(buff), 1000)) { + // TODO: error + return; + } + } + } else { + for (int y = 0; y < DISPLAY_RESY / 8; y++) { + uint8_t buff[DISPLAY_RESX]; + uint8_t *src = &drv->framebuf[y * DISPLAY_RESX * 8]; + + for (int x = 0; x < DISPLAY_RESX; x++) { + buff[x] = COLLECT_ROW_BYTE_REV(src); + src++; + } + + if (HAL_OK != HAL_SPI_Transmit(&drv->spi, &buff[0], sizeof(buff), 1000)) { + // TODO: error + return; + } + } + } + + while (HAL_SPI_STATE_READY != HAL_SPI_GetState(&drv->spi)) { + } + + // SPI deselect + HAL_GPIO_WritePin(OLED_CS_PORT, OLED_CS_PIN, GPIO_PIN_SET); + // Set to CMD + HAL_GPIO_WritePin(OLED_DC_PORT, OLED_DC_PIN, GPIO_PIN_RESET); +} + +void display_init(void) { + display_driver_t *drv = &g_display_driver; + + memset(drv, 0, sizeof(display_driver_t)); + drv->backlight_level = 255; + + OLED_DC_CLK_ENA(); + OLED_CS_CLK_ENA(); + OLED_RST_CLK_ENA(); + OLED_SPI_SCK_CLK_ENA(); + OLED_SPI_MOSI_CLK_ENA(); + OLED_SPI_CLK_ENA(); + + GPIO_InitTypeDef GPIO_InitStructure; + + // Set GPIO for OLED display + GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStructure.Pull = GPIO_NOPULL; + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStructure.Alternate = 0; + GPIO_InitStructure.Pin = OLED_CS_PIN; + HAL_GPIO_WritePin(OLED_CS_PORT, OLED_CS_PIN, GPIO_PIN_RESET); + HAL_GPIO_Init(OLED_CS_PORT, &GPIO_InitStructure); + GPIO_InitStructure.Pin = OLED_DC_PIN; + HAL_GPIO_WritePin(OLED_DC_PORT, OLED_DC_PIN, GPIO_PIN_RESET); + HAL_GPIO_Init(OLED_DC_PORT, &GPIO_InitStructure); + GPIO_InitStructure.Pin = OLED_RST_PIN; + HAL_GPIO_WritePin(OLED_RST_PORT, OLED_RST_PIN, GPIO_PIN_RESET); + HAL_GPIO_Init(OLED_RST_PORT, &GPIO_InitStructure); + + // Enable SPI 1 for OLED display + GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; + GPIO_InitStructure.Pull = GPIO_NOPULL; + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStructure.Alternate = OLED_SPI_AF; + GPIO_InitStructure.Pin = OLED_SPI_SCK_PIN; + HAL_GPIO_Init(OLED_SPI_SCK_PORT, &GPIO_InitStructure); + GPIO_InitStructure.Pin = OLED_SPI_MOSI_PIN; + HAL_GPIO_Init(OLED_SPI_MOSI_PORT, &GPIO_InitStructure); + + // Initialize SPI controller + display_init_spi(drv); + + // Set to CMD + HAL_GPIO_WritePin(OLED_DC_PORT, OLED_DC_PIN, GPIO_PIN_RESET); + // SPI deselect + HAL_GPIO_WritePin(OLED_CS_PORT, OLED_CS_PIN, GPIO_PIN_SET); + + // Reset the LCD + HAL_GPIO_WritePin(OLED_RST_PORT, OLED_RST_PIN, GPIO_PIN_SET); + HAL_Delay(1); + HAL_GPIO_WritePin(OLED_RST_PORT, OLED_RST_PIN, GPIO_PIN_RESET); + HAL_Delay(1); + HAL_GPIO_WritePin(OLED_RST_PORT, OLED_RST_PIN, GPIO_PIN_SET); + + // SPI select + HAL_GPIO_WritePin(OLED_CS_PORT, OLED_CS_PIN, GPIO_PIN_RESET); + // Send initialization command sequence + display_send_bytes(drv, &vg_2864ksweg01_init_seq[0], + sizeof(vg_2864ksweg01_init_seq)); + // SPI deselect + HAL_GPIO_WritePin(OLED_CS_PORT, OLED_CS_PIN, GPIO_PIN_SET); + + // Clear display internal framebuffer + display_sync_with_fb(drv); +} + +void display_reinit(void) { + display_driver_t *drv = &g_display_driver; + + memset(drv, 0, sizeof(display_driver_t)); + drv->backlight_level = 255; + + display_init_spi(drv); +} + +void display_finish_actions(void) { + /// Not used and intentionally left empty +} + +int display_set_backlight(int level) { + display_driver_t *drv = &g_display_driver; + + drv->backlight_level = 255; + return drv->backlight_level; +} + +int display_get_backlight(void) { + display_driver_t *drv = &g_display_driver; + + return drv->backlight_level; +} + +int display_set_orientation(int angle) { + display_driver_t *drv = &g_display_driver; + + if (angle != drv->orientation_angle) { + if (angle == 0 || angle == 180) { + drv->orientation_angle = angle; + display_sync_with_fb(drv); + } + } + + return drv->orientation_angle; +} + +int display_get_orientation(void) { + display_driver_t *drv = &g_display_driver; + + return drv->orientation_angle; +} + +display_fb_info_t display_get_frame_buffer(void) { + display_driver_t *drv = &g_display_driver; + + display_fb_info_t fb = { + .ptr = &drv->framebuf[0], + .stride = DISPLAY_RESX, + }; + + return fb; +} + +void display_refresh(void) { + display_driver_t *drv = &g_display_driver; + +#if defined USE_CONSUMPTION_MASK && !defined BOARDLOADER + // This is an intentional randomization of the consumption masking algorithm + // after every change on the display + consumption_mask_randomize(); +#endif + + // Sends the current frame buffer to the display + display_sync_with_fb(drv); +} + +void display_set_compatible_settings() {} + +void display_fill(const gl_bitblt_t *bb) { + display_driver_t *drv = &g_display_driver; + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = &drv->framebuf[DISPLAY_RESX * bb_new.dst_y]; + bb_new.dst_stride = DISPLAY_RESX; + + gl_mono8_fill(&bb_new); +} + +void display_copy_mono1p(const gl_bitblt_t *bb) { + display_driver_t *drv = &g_display_driver; + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = &drv->framebuf[DISPLAY_RESX * bb_new.dst_y]; + bb_new.dst_stride = DISPLAY_RESX; + + gl_mono8_copy_mono1p(&bb_new); +} diff --git a/core/embed/trezorhal/stm32f4/displays/ltdc.c b/core/embed/trezorhal/stm32f4/displays/ltdc.c index 8d5bd3eca..93ee9659e 100644 --- a/core/embed/trezorhal/stm32f4/displays/ltdc.c +++ b/core/embed/trezorhal/stm32f4/displays/ltdc.c @@ -19,7 +19,7 @@ #include #include TREZOR_BOARD -#include "display_interface.h" +#include "display.h" #include "memzero.h" #include STM32_HAL_H @@ -46,12 +46,12 @@ uint8_t *const DISPLAY_DATA_ADDRESS = 0; uint16_t cursor_x = 0; uint16_t cursor_y = 0; uint16_t window_x0 = 0; -uint16_t window_y0 = MAX_DISPLAY_RESX - 1; +uint16_t window_y0 = DISPLAY_RESX - 1; uint16_t window_x1 = 0; -uint16_t window_y1 = MAX_DISPLAY_RESY - 1; +uint16_t window_y1 = DISPLAY_RESY - 1; void display_pixeldata(uint16_t c) { - ((uint16_t *)LCD_FRAME_BUFFER)[(cursor_y * MAX_DISPLAY_RESX) + cursor_x] = c; + ((uint16_t *)LCD_FRAME_BUFFER)[(cursor_y * DISPLAY_RESX) + cursor_x] = c; cursor_x++; @@ -83,9 +83,9 @@ void BSP_LCD_LayerDefaultInit(uint16_t LayerIndex, uint32_t FB_Address) { /* Layer Init */ Layercfg.WindowX0 = 0; - Layercfg.WindowX1 = MAX_DISPLAY_RESX; + Layercfg.WindowX1 = DISPLAY_RESX; Layercfg.WindowY0 = 0; - Layercfg.WindowY1 = MAX_DISPLAY_RESY; + Layercfg.WindowY1 = DISPLAY_RESY; Layercfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565; Layercfg.FBStartAdress = FB_Address; Layercfg.Alpha = 255; @@ -95,8 +95,8 @@ void BSP_LCD_LayerDefaultInit(uint16_t LayerIndex, uint32_t FB_Address) { Layercfg.Backcolor.Red = 0; Layercfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_PAxCA; Layercfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_PAxCA; - Layercfg.ImageWidth = MAX_DISPLAY_RESX; - Layercfg.ImageHeight = MAX_DISPLAY_RESY; + Layercfg.ImageWidth = DISPLAY_RESX; + Layercfg.ImageHeight = DISPLAY_RESY; HAL_LTDC_ConfigLayer(&LtdcHandler, &Layercfg, LayerIndex); @@ -380,7 +380,7 @@ void display_efficient_clear(void) { uint8_t *display_get_wr_addr(void) { uint32_t address = LCD_FRAME_BUFFER; /* Get the rectangle start address */ - address = (address + (2 * ((cursor_y)*MAX_DISPLAY_RESX + (cursor_x)))); + address = (address + (2 * ((cursor_y)*DISPLAY_RESX + (cursor_x)))); return (uint8_t *)address; } @@ -413,7 +413,7 @@ void display_shift_window(uint16_t pixels) { } uint16_t display_get_window_offset(void) { - return MAX_DISPLAY_RESX - display_get_window_width(); + return DISPLAY_RESX - display_get_window_width(); } void display_finish_actions(void) {} diff --git a/core/embed/trezorhal/stm32f4/displays/ltdc.h b/core/embed/trezorhal/stm32f4/displays/ltdc.h index 2efb2be96..94d83a026 100644 --- a/core/embed/trezorhal/stm32f4/displays/ltdc.h +++ b/core/embed/trezorhal/stm32f4/displays/ltdc.h @@ -5,8 +5,8 @@ #include STM32_HAL_H #define TREZOR_FONT_BPP 4 -#define DISPLAY_FRAMEBUFFER_WIDTH MAX_DISPLAY_RESX -#define DISPLAY_FRAMEBUFFER_HEIGHT MAX_DISPLAY_RESY +#define DISPLAY_FRAMEBUFFER_WIDTH DISPLAY_RESX +#define DISPLAY_FRAMEBUFFER_HEIGHT DISPLAY_RESY #define DISPLAY_FRAMEBUFFER_OFFSET_X 0 #define DISPLAY_FRAMEBUFFER_OFFSET_Y 0 #define DISPLAY_COLOR_MODE DMA2D_OUTPUT_RGB565 diff --git a/core/embed/trezorhal/stm32f4/displays/panels/lx154a2422.c b/core/embed/trezorhal/stm32f4/displays/panels/lx154a2422.c index acc0efe3f..9a632fd68 100644 --- a/core/embed/trezorhal/stm32f4/displays/panels/lx154a2422.c +++ b/core/embed/trezorhal/stm32f4/displays/panels/lx154a2422.c @@ -1,5 +1,5 @@ -#include "display_interface.h" +#include "display.h" #include "displays/st7789v.h" #include "touch.h" diff --git a/core/embed/trezorhal/stm32f4/displays/panels/tf15411a.c b/core/embed/trezorhal/stm32f4/displays/panels/tf15411a.c index 4c186bc74..179e3018f 100644 --- a/core/embed/trezorhal/stm32f4/displays/panels/tf15411a.c +++ b/core/embed/trezorhal/stm32f4/displays/panels/tf15411a.c @@ -1,4 +1,4 @@ -#include "display_interface.h" +#include "display.h" #include "displays/st7789v.h" void tf15411a_init_seq(void) { diff --git a/core/embed/trezorhal/stm32f4/displays/st7789v.c b/core/embed/trezorhal/stm32f4/displays/st7789v.c index 7e9f1392c..5b9197c62 100644 --- a/core/embed/trezorhal/stm32f4/displays/st7789v.c +++ b/core/embed/trezorhal/stm32f4/displays/st7789v.c @@ -22,7 +22,7 @@ #include #include TREZOR_BOARD #include "backlight_pwm.h" -#include "display_interface.h" +#include "display.h" #include "irq.h" #include "memzero.h" #include "st7789v.h" @@ -34,6 +34,8 @@ #include "displays/panels/lx154a2411.h" #include "displays/panels/lx154a2422.h" #include "displays/panels/tf15411a.h" +#else +#include "displays/panels/lx154a2422.h" #endif // using const volatile instead of #define results in binaries that change @@ -241,7 +243,7 @@ int display_orientation(int degrees) { lx154a2422_rotate(degrees, &DISPLAY_PADDING); } #else - DISPLAY_PANEL_ROTATE(degrees, &DISPLAY_PADDING); + lx154a2422_rotate(degrees, &DISPLAY_PADDING); #endif panel_set_window(0, 0, DISPLAY_RESX - 1, DISPLAY_RESY - 1); } @@ -292,7 +294,7 @@ void display_init_seq(void) { _154a_init_seq(); } #else - DISPLAY_PANEL_INIT_SEQ(); + lx154a2422_init_seq(); #endif display_unsleep(); diff --git a/core/embed/trezorhal/stm32f4/displays/ug-2828tswig01.c b/core/embed/trezorhal/stm32f4/displays/ug-2828tswig01.c index 2d165bbaa..4e09309a6 100644 --- a/core/embed/trezorhal/stm32f4/displays/ug-2828tswig01.c +++ b/core/embed/trezorhal/stm32f4/displays/ug-2828tswig01.c @@ -19,7 +19,7 @@ #include #include TREZOR_BOARD -#include "display_interface.h" +#include "display.h" #include "memzero.h" #include STM32_HAL_H diff --git a/core/embed/trezorhal/stm32f4/displays/vg-2864ksweg01.c b/core/embed/trezorhal/stm32f4/displays/vg-2864ksweg01.c index d8568923d..ad3ea481b 100644 --- a/core/embed/trezorhal/stm32f4/displays/vg-2864ksweg01.c +++ b/core/embed/trezorhal/stm32f4/displays/vg-2864ksweg01.c @@ -20,7 +20,7 @@ #include #include #include TREZOR_BOARD -#include "display_interface.h" +#include "display.h" #include STM32_HAL_H #ifdef USE_CONSUMPTION_MASK diff --git a/core/embed/trezorhal/stm32f4/dma2d.c b/core/embed/trezorhal/stm32f4/dma2d.c index c27880cd5..c479d4b4c 100644 --- a/core/embed/trezorhal/stm32f4/dma2d.c +++ b/core/embed/trezorhal/stm32f4/dma2d.c @@ -20,7 +20,7 @@ #include "dma2d.h" #include "colors.h" #include STM32_HAL_H -#include "display_interface.h" +#include "display.h" typedef enum { DMA2D_LAYER_FG = 1, diff --git a/core/embed/trezorhal/stm32f4/dma2d_bitblt.c b/core/embed/trezorhal/stm32f4/dma2d_bitblt.c new file mode 100644 index 000000000..f478d7d07 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/dma2d_bitblt.c @@ -0,0 +1,599 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#include STM32_HAL_H + +#include + +#include "dma2d_bitblt.h" +#include "gl_color.h" + +static DMA2D_HandleTypeDef dma2d_handle = { + .Instance = (DMA2D_TypeDef*)DMA2D_BASE, +}; + +// Returns `true` if the specified address is accessible by DMA2D +// and can be used by any of the following functions +static inline bool dma2d_accessible(const void* ptr) { +#ifdef STM32F4 + const void* ccm_start = (const void*)0x10000000; + const void* ccm_end = (const void*)0x1000FFFF; + return !(ptr >= ccm_start && ptr <= ccm_end); +#else + return true; +#endif +} + +void dma2d_wait(void) { + while (HAL_DMA2D_PollForTransfer(&dma2d_handle, 10) != HAL_OK) + ; +} + +bool dma2d_rgb565_fill(const gl_bitblt_t* bb) { + dma2d_wait(); + + if (!dma2d_accessible(bb->dst_row)) { + return false; + } + + if (bb->src_alpha == 255) { + dma2d_handle.Init.ColorMode = DMA2D_OUTPUT_RGB565; + dma2d_handle.Init.Mode = DMA2D_R2M; + dma2d_handle.Init.OutputOffset = + bb->dst_stride / sizeof(uint16_t) - bb->width; + HAL_DMA2D_Init(&dma2d_handle); + + HAL_DMA2D_Start(&dma2d_handle, gl_color_to_color32(bb->src_fg), + (uint32_t)bb->dst_row + bb->dst_x * sizeof(uint16_t), + bb->width, bb->height); + } else { +#ifdef STM32U5 + dma2d_handle.Init.ColorMode = DMA2D_OUTPUT_RGB565; + dma2d_handle.Init.Mode = DMA2D_M2M_BLEND_FG; + dma2d_handle.Init.OutputOffset = + bb->dst_stride / sizeof(uint16_t) - bb->width; + HAL_DMA2D_Init(&dma2d_handle); + + dma2d_handle.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB565; + dma2d_handle.LayerCfg[1].InputOffset = 0; + dma2d_handle.LayerCfg[1].AlphaMode = DMA2D_REPLACE_ALPHA; + dma2d_handle.LayerCfg[1].InputAlpha = bb->src_alpha; + HAL_DMA2D_ConfigLayer(&dma2d_handle, 1); + + dma2d_handle.LayerCfg[0].InputColorMode = DMA2D_INPUT_RGB565; + dma2d_handle.LayerCfg[0].InputOffset = + bb->dst_stride / sizeof(uint16_t) - bb->width; + dma2d_handle.LayerCfg[0].AlphaMode = 0; + dma2d_handle.LayerCfg[0].InputAlpha = 0; + HAL_DMA2D_ConfigLayer(&dma2d_handle, 0); + + HAL_DMA2D_BlendingStart( + &dma2d_handle, gl_color_to_color32(bb->src_fg), + (uint32_t)bb->dst_row + bb->dst_x * sizeof(uint16_t), + (uint32_t)bb->dst_row + bb->dst_x * sizeof(uint16_t), bb->width, + bb->height); +#else + // STM32F4 can not accelerate blending with the fixed color + return false; +#endif + } + + return true; +} + +static void dma2d_config_clut(uint32_t layer, gl_color_t fg, gl_color_t bg) { +#define LAYER_COUNT 2 +#define GRADIENT_STEPS 16 + + static struct { + gl_color_t c_fg; + gl_color_t c_bg; + } cache[LAYER_COUNT] = {0}; + + if (layer >= LAYER_COUNT) { + return; + } + + volatile uint32_t* clut = + layer ? dma2d_handle.Instance->FGCLUT : dma2d_handle.Instance->BGCLUT; + + if (fg != cache[layer].c_fg || bg != cache[layer].c_bg) { + cache[layer].c_fg = fg; + cache[layer].c_bg = bg; + + for (int step = 0; step < GRADIENT_STEPS; step++) { + clut[step] = gl_color32_blend_a4(fg, bg, step); + } + + DMA2D_CLUTCfgTypeDef clut; + clut.CLUTColorMode = DMA2D_CCM_ARGB8888; + clut.Size = GRADIENT_STEPS - 1; + clut.pCLUT = 0; // ??? + + HAL_DMA2D_ConfigCLUT(&dma2d_handle, clut, layer); + } +} + +static void dma2d_rgb565_copy_mono4_first_col(gl_bitblt_t* bb, + const gl_color16_t* gradient) { + uint16_t* dst_ptr = (uint16_t*)bb->dst_row + bb->dst_x; + uint8_t* src_ptr = (uint8_t*)bb->src_row + bb->src_x / 2; + + int height = bb->height; + + while (height-- > 0) { + uint8_t fg_lum = src_ptr[0] >> 4; + dst_ptr[0] = gradient[fg_lum]; + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_ptr += bb->src_stride / sizeof(*src_ptr); + } +} + +static void dma2d_rgb565_copy_mono4_last_col(gl_bitblt_t* bb, + const gl_color16_t* gradient) { + uint16_t* dst_ptr = (uint16_t*)bb->dst_row + (bb->dst_x + bb->width - 1); + uint8_t* src_ptr = (uint8_t*)bb->src_row + (bb->src_x + bb->width - 1) / 2; + + int height = bb->height; + + while (height-- > 0) { + uint8_t fg_lum = src_ptr[0] & 0x0F; + dst_ptr[0] = gradient[fg_lum]; + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_ptr += bb->src_stride / sizeof(*src_ptr); + } +} + +bool dma2d_rgb565_copy_mono4(const gl_bitblt_t* params) { + const gl_color16_t* src_gradient = NULL; + + gl_bitblt_t bb_copy = *params; + gl_bitblt_t* bb = &bb_copy; + + dma2d_wait(); + + if (!dma2d_accessible(bb->dst_row) || !dma2d_accessible(bb->src_row)) { + return false; + } + + if (bb->src_x & 1) { + // First column of mono4 bitmap is odd + // Use the CPU to draw the first column + src_gradient = gl_color16_gradient_a4(bb->src_fg, bb->src_bg); + dma2d_rgb565_copy_mono4_first_col(bb, src_gradient); + bb->dst_x += 1; + bb->src_x += 1; + bb->width -= 1; + } + + if (bb->width > 0 && bb->width & 1) { + // The width is odd + // Use the CPU to draw the last column + if (src_gradient == NULL) { + src_gradient = gl_color16_gradient_a4(bb->src_fg, bb->src_bg); + } + dma2d_rgb565_copy_mono4_last_col(bb, src_gradient); + bb->width -= 1; + } + + dma2d_handle.Init.ColorMode = DMA2D_OUTPUT_RGB565; + dma2d_handle.Init.Mode = DMA2D_M2M_PFC; + dma2d_handle.Init.OutputOffset = + bb->dst_stride / sizeof(uint16_t) - bb->width; + HAL_DMA2D_Init(&dma2d_handle); + + dma2d_handle.LayerCfg[1].InputColorMode = DMA2D_INPUT_L4; + dma2d_handle.LayerCfg[1].InputOffset = bb->src_stride * 2 - bb->width; + dma2d_handle.LayerCfg[1].AlphaMode = 0; + dma2d_handle.LayerCfg[1].InputAlpha = 0; + HAL_DMA2D_ConfigLayer(&dma2d_handle, 1); + + dma2d_config_clut(1, bb->src_fg, bb->src_bg); + + HAL_DMA2D_Start(&dma2d_handle, (uint32_t)bb->src_row + bb->src_x / 2, + (uint32_t)bb->dst_row + bb->dst_x * sizeof(uint16_t), + bb->width, bb->height); + return true; +} + +bool dma2d_rgb565_copy_rgb565(const gl_bitblt_t* bb) { + dma2d_wait(); + + if (!dma2d_accessible(bb->dst_row) || !dma2d_accessible(bb->src_row)) { + return false; + } + + dma2d_handle.Init.ColorMode = DMA2D_OUTPUT_RGB565; + dma2d_handle.Init.Mode = DMA2D_M2M_PFC; + dma2d_handle.Init.OutputOffset = + bb->dst_stride / sizeof(uint16_t) - bb->width; + HAL_DMA2D_Init(&dma2d_handle); + + dma2d_handle.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB565; + dma2d_handle.LayerCfg[1].InputOffset = + bb->src_stride / sizeof(uint16_t) - bb->width; + dma2d_handle.LayerCfg[1].AlphaMode = 0; + dma2d_handle.LayerCfg[1].InputAlpha = 0; + HAL_DMA2D_ConfigLayer(&dma2d_handle, 1); + + HAL_DMA2D_Start(&dma2d_handle, + (uint32_t)bb->src_row + bb->src_x * sizeof(uint16_t), + (uint32_t)bb->dst_row + bb->dst_x * sizeof(uint16_t), + bb->width, bb->height); + return true; +} + +static void dma2d_rgb565_blend_mono4_first_col(const gl_bitblt_t* bb) { + uint16_t* dst_ptr = (uint16_t*)bb->dst_row + bb->dst_x; + uint8_t* src_ptr = (uint8_t*)bb->src_row + bb->src_x / 2; + + int height = bb->height; + + while (height-- > 0) { + uint8_t fg_alpha = src_ptr[0] >> 4; + dst_ptr[0] = gl_color16_blend_a4(bb->src_fg, + gl_color16_to_color(dst_ptr[0]), fg_alpha); + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_ptr += bb->src_stride / sizeof(*src_ptr); + } +} + +static void dma2d_rgb565_blend_mono4_last_col(const gl_bitblt_t* bb) { + uint16_t* dst_ptr = (uint16_t*)bb->dst_row + (bb->dst_x + bb->width - 1); + uint8_t* src_ptr = (uint8_t*)bb->src_row + (bb->src_x + bb->width - 1) / 2; + + int height = bb->height; + + while (height-- > 0) { + uint8_t fg_alpha = src_ptr[0] & 0x0F; + dst_ptr[0] = gl_color16_blend_a4(bb->src_fg, + gl_color16_to_color(dst_ptr[0]), fg_alpha); + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_ptr += bb->src_stride / sizeof(*src_ptr); + } +} + +bool dma2d_rgb565_blend_mono4(const gl_bitblt_t* params) { + dma2d_wait(); + + gl_bitblt_t bb_copy = *params; + gl_bitblt_t* bb = &bb_copy; + + if (!dma2d_accessible(bb->dst_row) || !dma2d_accessible(bb->src_row)) { + return false; + } + + if (bb->src_x & 1) { + // First column of mono4 bitmap is odd + // Use the CPU to draw the first column + dma2d_rgb565_blend_mono4_first_col(bb); + bb->dst_x += 1; + bb->src_x += 1; + bb->width -= 1; + } + + if (bb->width > 0 && bb->width & 1) { + // The width is odd + // Use the CPU to draw the last column + dma2d_rgb565_blend_mono4_last_col(bb); + bb->width -= 1; + } + + if (bb->width > 0) { + dma2d_handle.Init.ColorMode = DMA2D_OUTPUT_RGB565; + dma2d_handle.Init.Mode = DMA2D_M2M_BLEND; + dma2d_handle.Init.OutputOffset = + bb->dst_stride / sizeof(uint16_t) - bb->width; + HAL_DMA2D_Init(&dma2d_handle); + + dma2d_handle.LayerCfg[1].InputColorMode = DMA2D_INPUT_A4; + dma2d_handle.LayerCfg[1].InputOffset = bb->src_stride * 2 - bb->width; + dma2d_handle.LayerCfg[1].AlphaMode = 0; + dma2d_handle.LayerCfg[1].InputAlpha = gl_color_to_color32(bb->src_fg); + HAL_DMA2D_ConfigLayer(&dma2d_handle, 1); + + dma2d_handle.LayerCfg[0].InputColorMode = DMA2D_INPUT_RGB565; + dma2d_handle.LayerCfg[0].InputOffset = + bb->dst_stride / sizeof(uint16_t) - bb->width; + dma2d_handle.LayerCfg[0].AlphaMode = 0; + dma2d_handle.LayerCfg[0].InputAlpha = 0; + HAL_DMA2D_ConfigLayer(&dma2d_handle, 0); + + HAL_DMA2D_BlendingStart( + &dma2d_handle, (uint32_t)bb->src_row + bb->src_x / 2, + (uint32_t)bb->dst_row + bb->dst_x * sizeof(uint16_t), + (uint32_t)bb->dst_row + bb->dst_x * sizeof(uint16_t), bb->width, + bb->height); + } + + return true; +} + +bool dma2d_rgba8888_fill(const gl_bitblt_t* bb) { + dma2d_wait(); + + if (!dma2d_accessible(bb->dst_row)) { + return false; + } + + if (bb->src_alpha == 255) { + dma2d_handle.Init.ColorMode = DMA2D_OUTPUT_ARGB8888; + dma2d_handle.Init.Mode = DMA2D_R2M; + dma2d_handle.Init.OutputOffset = + bb->dst_stride / sizeof(uint32_t) - bb->width; + HAL_DMA2D_Init(&dma2d_handle); + + HAL_DMA2D_Start(&dma2d_handle, gl_color_to_color32(bb->src_fg), + (uint32_t)bb->dst_row + bb->dst_x * sizeof(uint32_t), + bb->width, bb->height); + } else { +#ifdef STM32U5 + dma2d_handle.Init.ColorMode = DMA2D_OUTPUT_ARGB8888; + dma2d_handle.Init.Mode = DMA2D_M2M_BLEND_FG; + dma2d_handle.Init.OutputOffset = + bb->dst_stride / sizeof(uint32_t) - bb->width; + HAL_DMA2D_Init(&dma2d_handle); + + dma2d_handle.LayerCfg[1].InputColorMode = DMA2D_INPUT_ARGB8888; + dma2d_handle.LayerCfg[1].InputOffset = 0; + dma2d_handle.LayerCfg[1].AlphaMode = DMA2D_REPLACE_ALPHA; + dma2d_handle.LayerCfg[1].InputAlpha = bb->src_alpha; + HAL_DMA2D_ConfigLayer(&dma2d_handle, 1); + + dma2d_handle.LayerCfg[0].InputColorMode = DMA2D_INPUT_ARGB8888; + dma2d_handle.LayerCfg[0].InputOffset = + bb->dst_stride / sizeof(uint32_t) - bb->width; + dma2d_handle.LayerCfg[0].AlphaMode = 0; + dma2d_handle.LayerCfg[0].InputAlpha = 0; + HAL_DMA2D_ConfigLayer(&dma2d_handle, 0); + + HAL_DMA2D_BlendingStart( + &dma2d_handle, gl_color_to_color32(bb->src_fg), + (uint32_t)bb->dst_row + bb->dst_x * sizeof(uint32_t), + (uint32_t)bb->dst_row + bb->dst_x * sizeof(uint32_t), bb->width, + bb->height); +#else + // STM32F4 can not accelerate blending with the fixed color + return false; +#endif + } + return true; +} + +static void dma2d_rgba8888_copy_mono4_first_col(gl_bitblt_t* bb, + const gl_color32_t* gradient) { + uint32_t* dst_ptr = (uint32_t*)bb->dst_row + bb->dst_x; + uint8_t* src_ptr = (uint8_t*)bb->src_row + bb->src_x / 2; + + int height = bb->height; + + while (height-- > 0) { + uint8_t fg_lum = src_ptr[0] >> 4; + dst_ptr[0] = gradient[fg_lum]; + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_ptr += bb->src_stride / sizeof(*src_ptr); + } +} + +static void dma2d_rgba8888_copy_mono4_last_col(gl_bitblt_t* bb, + const gl_color32_t* gradient) { + uint32_t* dst_ptr = (uint32_t*)bb->dst_row + (bb->dst_x + bb->width - 1); + uint8_t* src_ptr = (uint8_t*)bb->src_row + (bb->src_x + bb->width - 1) / 2; + + int height = bb->height; + + while (height-- > 0) { + uint8_t fg_lum = src_ptr[0] & 0x0F; + dst_ptr[0] = gradient[fg_lum]; + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_ptr += bb->src_stride / sizeof(*src_ptr); + } +} + +bool dma2d_rgba8888_copy_mono4(const gl_bitblt_t* params) { + const gl_color32_t* src_gradient = NULL; + + gl_bitblt_t bb_copy = *params; + gl_bitblt_t* bb = &bb_copy; + + dma2d_wait(); + + if (!dma2d_accessible(bb->dst_row) || !dma2d_accessible(bb->src_row)) { + return false; + } + + if (bb->src_x & 1) { + // First column of mono4 bitmap is odd + // Use the CPU to draw the first column + src_gradient = gl_color32_gradient_a4(bb->src_fg, bb->src_bg); + dma2d_rgba8888_copy_mono4_first_col(bb, src_gradient); + bb->dst_x += 1; + bb->src_x += 1; + bb->width -= 1; + } + + if (bb->width > 0 && bb->width & 1) { + // The width is odd + // Use the CPU to draw the last column + if (src_gradient == NULL) { + src_gradient = gl_color32_gradient_a4(bb->src_fg, bb->src_bg); + } + dma2d_rgba8888_copy_mono4_last_col(bb, src_gradient); + bb->width -= 1; + } + + dma2d_handle.Init.ColorMode = DMA2D_OUTPUT_ARGB8888; + dma2d_handle.Init.Mode = DMA2D_M2M_PFC; + dma2d_handle.Init.OutputOffset = + bb->dst_stride / sizeof(uint32_t) - bb->width; + HAL_DMA2D_Init(&dma2d_handle); + + dma2d_handle.LayerCfg[1].InputColorMode = DMA2D_INPUT_L4; + dma2d_handle.LayerCfg[1].InputOffset = bb->src_stride * 2 - bb->width; + dma2d_handle.LayerCfg[1].AlphaMode = 0; + dma2d_handle.LayerCfg[1].InputAlpha = 0; + HAL_DMA2D_ConfigLayer(&dma2d_handle, 1); + + dma2d_config_clut(1, bb->src_fg, bb->src_bg); + + HAL_DMA2D_Start(&dma2d_handle, (uint32_t)bb->src_row + bb->src_x / 2, + (uint32_t)bb->dst_row + bb->dst_x * sizeof(uint32_t), + bb->width, bb->height); + return true; +} + +bool dma2d_rgba8888_copy_rgb565(const gl_bitblt_t* bb) { + dma2d_wait(); + + if (!dma2d_accessible(bb->dst_row) || !dma2d_accessible(bb->src_row)) { + return false; + } + + dma2d_handle.Init.ColorMode = DMA2D_OUTPUT_ARGB8888; + dma2d_handle.Init.Mode = DMA2D_M2M_PFC; + dma2d_handle.Init.OutputOffset = + bb->dst_stride / sizeof(uint32_t) - bb->width; + HAL_DMA2D_Init(&dma2d_handle); + + dma2d_handle.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB565; + dma2d_handle.LayerCfg[1].InputOffset = + bb->src_stride / sizeof(uint16_t) - bb->width; + dma2d_handle.LayerCfg[1].AlphaMode = 0; + dma2d_handle.LayerCfg[1].InputAlpha = 0; + HAL_DMA2D_ConfigLayer(&dma2d_handle, 1); + + HAL_DMA2D_Start(&dma2d_handle, + (uint32_t)bb->src_row + bb->src_x * sizeof(uint16_t), + (uint32_t)bb->dst_row + bb->dst_x * sizeof(uint32_t), + bb->width, bb->height); + return true; +} + +static void dma2d_rgba8888_blend_mono4_first_col(const gl_bitblt_t* bb) { + uint32_t* dst_ptr = (uint32_t*)bb->dst_row + bb->dst_x; + uint8_t* src_ptr = (uint8_t*)bb->src_row + bb->src_x / 2; + + int height = bb->height; + + while (height-- > 0) { + uint8_t fg_alpha = src_ptr[0] >> 4; + dst_ptr[0] = gl_color32_blend_a4(bb->src_fg, + gl_color32_to_color(dst_ptr[0]), fg_alpha); + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_ptr += bb->src_stride / sizeof(*src_ptr); + } +} + +static void dma2d_rgba8888_blend_mono4_last_col(const gl_bitblt_t* bb) { + uint32_t* dst_ptr = (uint32_t*)bb->dst_row + (bb->dst_x + bb->width - 1); + uint8_t* src_ptr = (uint8_t*)bb->src_row + (bb->src_x + bb->width - 1) / 2; + + int height = bb->height; + + while (height-- > 0) { + uint8_t fg_alpha = src_ptr[0] & 0x0F; + dst_ptr[0] = gl_color32_blend_a4(bb->src_fg, + gl_color32_to_color(dst_ptr[0]), fg_alpha); + dst_ptr += bb->dst_stride / sizeof(*dst_ptr); + src_ptr += bb->src_stride / sizeof(*src_ptr); + } +} + +bool dma2d_rgba8888_blend_mono4(const gl_bitblt_t* params) { + dma2d_wait(); + + gl_bitblt_t bb_copy = *params; + gl_bitblt_t* bb = &bb_copy; + + if (!dma2d_accessible(bb->dst_row) || !dma2d_accessible(bb->src_row)) { + return false; + } + + if (bb->src_x & 1) { + // First column of mono4 bitmap is odd + // Use the CPU to draw the first column + dma2d_rgba8888_blend_mono4_first_col(bb); + bb->dst_x += 1; + bb->src_x += 1; + bb->width -= 1; + } + + if (bb->width > 0 && bb->width & 1) { + // The width is odd + // Use the CPU to draw the last column + dma2d_rgba8888_blend_mono4_last_col(bb); + bb->width -= 1; + } + + if (bb->width > 0) { + dma2d_handle.Init.ColorMode = DMA2D_OUTPUT_ARGB8888; + dma2d_handle.Init.Mode = DMA2D_M2M_BLEND; + dma2d_handle.Init.OutputOffset = + bb->dst_stride / sizeof(uint32_t) - bb->width; + HAL_DMA2D_Init(&dma2d_handle); + + dma2d_handle.LayerCfg[1].InputColorMode = DMA2D_INPUT_A4; + dma2d_handle.LayerCfg[1].InputOffset = bb->src_stride * 2 - bb->width; + dma2d_handle.LayerCfg[1].AlphaMode = 0; + dma2d_handle.LayerCfg[1].InputAlpha = gl_color_to_color32(bb->src_fg); + HAL_DMA2D_ConfigLayer(&dma2d_handle, 1); + + dma2d_handle.LayerCfg[0].InputColorMode = DMA2D_INPUT_ARGB8888; + dma2d_handle.LayerCfg[0].InputOffset = + bb->dst_stride / sizeof(uint32_t) - bb->width; + dma2d_handle.LayerCfg[0].AlphaMode = 0; + dma2d_handle.LayerCfg[0].InputAlpha = 0; + HAL_DMA2D_ConfigLayer(&dma2d_handle, 0); + + HAL_DMA2D_BlendingStart( + &dma2d_handle, (uint32_t)bb->src_row + bb->src_x / 2, + (uint32_t)bb->dst_row + bb->dst_x * sizeof(uint32_t), + (uint32_t)bb->dst_row + bb->dst_x * sizeof(uint32_t), bb->width, + bb->height); + } + + return true; +} + +bool dma2d_rgba8888_copy_rgba8888(const gl_bitblt_t* bb) { + dma2d_wait(); + + if (!dma2d_accessible(bb->dst_row) || !dma2d_accessible(bb->src_row)) { + return false; + } + + dma2d_handle.Init.ColorMode = DMA2D_OUTPUT_ARGB8888; + dma2d_handle.Init.Mode = DMA2D_M2M_PFC; + dma2d_handle.Init.OutputOffset = + bb->dst_stride / sizeof(uint32_t) - bb->width; + HAL_DMA2D_Init(&dma2d_handle); + + dma2d_handle.LayerCfg[1].InputColorMode = DMA2D_INPUT_ARGB8888; + dma2d_handle.LayerCfg[1].InputOffset = + bb->src_stride / sizeof(uint32_t) - bb->width; + dma2d_handle.LayerCfg[1].AlphaMode = 0; + dma2d_handle.LayerCfg[1].InputAlpha = 0; + HAL_DMA2D_ConfigLayer(&dma2d_handle, 1); + + HAL_DMA2D_Start(&dma2d_handle, + (uint32_t)bb->src_row + bb->src_x * sizeof(uint32_t), + (uint32_t)bb->dst_row + bb->dst_x * sizeof(uint32_t), + bb->width, bb->height); + return true; +} diff --git a/core/embed/trezorhal/stm32f4/secret.c b/core/embed/trezorhal/stm32f4/secret.c index d299f2068..9a90511fd 100644 --- a/core/embed/trezorhal/stm32f4/secret.c +++ b/core/embed/trezorhal/stm32f4/secret.c @@ -1,7 +1,7 @@ #include "secret.h" #include #include "common.h" -#include "display.h" +#include "display_draw.h" #include "flash.h" #include "model.h" diff --git a/core/embed/trezorhal/stm32f4/touch/ft6x36.c b/core/embed/trezorhal/stm32f4/touch/ft6x36.c index 439dd16aa..f2a7d96bc 100644 --- a/core/embed/trezorhal/stm32f4/touch/ft6x36.c +++ b/core/embed/trezorhal/stm32f4/touch/ft6x36.c @@ -260,8 +260,8 @@ uint32_t touch_read(void) { // first touch) (tested with FT6206) const uint32_t event_flag = touch_data[3] & 0xC0; if (touch_data[1] == GESTURE_NO_GESTURE) { - xy = TRANSFORM_TOUCH_COORDS((X_POS_MSB << 8) | X_POS_LSB, - (Y_POS_MSB << 8) | Y_POS_LSB); + xy = touch_pack_xy((X_POS_MSB << 8) | X_POS_LSB, + (Y_POS_MSB << 8) | Y_POS_LSB); if ((number_of_touch_points == 1) && (event_flag == EVENT_PRESS_DOWN)) { touching = 1; return TOUCH_START | xy; diff --git a/core/embed/trezorhal/stm32u5/display/st-7789 b/core/embed/trezorhal/stm32u5/display/st-7789 new file mode 120000 index 000000000..3ad01c673 --- /dev/null +++ b/core/embed/trezorhal/stm32u5/display/st-7789 @@ -0,0 +1 @@ +../../stm32f4/display/st-7789 \ No newline at end of file diff --git a/core/embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_driver.c b/core/embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_driver.c new file mode 100644 index 000000000..39b4be02b --- /dev/null +++ b/core/embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_driver.c @@ -0,0 +1,155 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#include +#include + +#include TREZOR_BOARD +#include STM32_HAL_H + +#include "display_internal.h" +#include "xdisplay.h" + +#if (DISPLAY_RESX != 240) || (DISPLAY_RESY != 240) +#error "Incompatible display resolution" +#endif + +// Display driver context. +typedef struct { + // Current display orientation (0, 90, 180, 270) + int orientation_angle; + // Current backlight level ranging from 0 to 255 + int backlight_level; +} display_driver_t; + +// Display driver instance +static display_driver_t g_display_driver; + +void display_init(void) { + RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; + + // Initializes the common periph clock + PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_LTDC | RCC_PERIPHCLK_DSI; + PeriphClkInit.DsiClockSelection = RCC_DSICLKSOURCE_PLL3; + PeriphClkInit.LtdcClockSelection = RCC_LTDCCLKSOURCE_PLL3; + PeriphClkInit.PLL3.PLL3Source = RCC_PLLSOURCE_HSE; + PeriphClkInit.PLL3.PLL3M = 4; + PeriphClkInit.PLL3.PLL3N = 125; + PeriphClkInit.PLL3.PLL3P = 8; + PeriphClkInit.PLL3.PLL3Q = 2; + PeriphClkInit.PLL3.PLL3R = 24; + PeriphClkInit.PLL3.PLL3RGE = RCC_PLLVCIRANGE_0; + PeriphClkInit.PLL3.PLL3FRACN = 0; + PeriphClkInit.PLL3.PLL3ClockOut = RCC_PLL3_DIVP | RCC_PLL3_DIVR; + HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit); + + // Clear framebuffers + memset(physical_frame_buffer_0, 0x00, PHYSICAL_FRAME_BUFFER_SIZE); + memset(physical_frame_buffer_1, 0x00, PHYSICAL_FRAME_BUFFER_SIZE); + + BSP_LCD_Init(0, LCD_ORIENTATION_PORTRAIT); + BSP_LCD_SetBrightness(0, 100); + BSP_LCD_DisplayOn(0); +} + +void display_reinit(void) { + BSP_LCD_Reinit(0); + if (current_frame_buffer == 0) { + BSP_LCD_SetFrameBuffer(0, GFXMMU_VIRTUAL_BUFFER1_BASE_S); + } else { + BSP_LCD_SetFrameBuffer(0, GFXMMU_VIRTUAL_BUFFER0_BASE_S); + } +} + +void display_finish_actions(void) { + // Not used and intentionally left empty +} + +int display_set_backlight(int level) { + display_driver_t *drv = &g_display_driver; + + // Just emulation, not doing anything + drv->backlight_level = level; + return level; +} + +int display_get_backlight(void) { + display_driver_t *drv = &g_display_driver; + + return drv->orientation_angle; +} + +int display_set_orientation(int angle) { + display_driver_t *drv = &g_display_driver; + + if (angle == 0 || angle == 90 || angle == 180 || angle == 270) { + // Just emulation, not doing anything + drv->orientation_angle = angle; + } + + return drv->orientation_angle; +} + +int display_get_orientation(void) { + display_driver_t *drv = &g_display_driver; + + return drv->orientation_angle; +} + +void display_set_compatible_settings() {} + +void display_fill(const gl_bitblt_t *bb) { + display_fb_info_t fb = display_get_frame_buffer(); + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = (uint8_t *)fb.ptr + (fb.stride * bb_new.dst_y); + bb_new.dst_stride = fb.stride; + + gl_rgba8888_fill(&bb_new); +} + +void display_copy_rgb565(const gl_bitblt_t *bb) { + display_fb_info_t fb = display_get_frame_buffer(); + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = (uint8_t *)fb.ptr + (fb.stride * bb_new.dst_y); + bb_new.dst_stride = fb.stride; + + gl_rgba8888_copy_rgb565(&bb_new); +} + +void display_copy_mono1p(const gl_bitblt_t *bb) { + display_fb_info_t fb = display_get_frame_buffer(); + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = (uint8_t *)fb.ptr + (fb.stride * bb_new.dst_y); + bb_new.dst_stride = fb.stride; + + gl_rgba8888_copy_mono1p(&bb_new); +} + +void display_copy_mono4(const gl_bitblt_t *bb) { + display_fb_info_t fb = display_get_frame_buffer(); + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = (uint8_t *)fb.ptr + (fb.stride * bb_new.dst_y); + bb_new.dst_stride = fb.stride; + + gl_rgba8888_copy_mono4(&bb_new); +} diff --git a/core/embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_fb.c b/core/embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_fb.c new file mode 100644 index 000000000..5b425176c --- /dev/null +++ b/core/embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_fb.c @@ -0,0 +1,77 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#include +#include + +#include TREZOR_BOARD +#include STM32_HAL_H + +#include +#include "display_internal.h" + +// Physical frame buffers in internal SRAM memory +__attribute__((section(".fb1"))) +ALIGN_32BYTES(uint32_t physical_frame_buffer_0[PHYSICAL_FRAME_BUFFER_SIZE]); + +__attribute__((section(".fb2"))) +ALIGN_32BYTES(uint32_t physical_frame_buffer_1[PHYSICAL_FRAME_BUFFER_SIZE]); + +// The current frame buffer selector at fixed memory address +// It's shared between bootloaders and the firmware +__attribute__((section(".framebuffer_select"))) uint32_t current_frame_buffer = + 0; + +display_fb_info_t display_get_frame_buffer(void) { + uintptr_t addr; + + if (current_frame_buffer == 0) { + addr = GFXMMU_VIRTUAL_BUFFER1_BASE_S; + } else { + addr = GFXMMU_VIRTUAL_BUFFER0_BASE_S; + } + + uint32_t fb_stride = FRAME_BUFFER_PIXELS_PER_LINE * sizeof(uint32_t); + + // We do not utilize whole area of the display + // (discovery kit display is 480x480 and we need just 240x240) + addr += (480 - DISPLAY_RESY) / 2 * sizeof(uint32_t); + addr += (480 - DISPLAY_RESX) / 2 * fb_stride; + + display_fb_info_t fb = { + .ptr = (void *)addr, + .stride = fb_stride, + }; + + return fb; +} + +void display_refresh(void) { + if (current_frame_buffer == 0) { + current_frame_buffer = 1; + BSP_LCD_SetFrameBuffer(0, GFXMMU_VIRTUAL_BUFFER1_BASE_S); + memcpy(physical_frame_buffer_0, physical_frame_buffer_1, + sizeof(physical_frame_buffer_0)); + } else { + current_frame_buffer = 0; + BSP_LCD_SetFrameBuffer(0, GFXMMU_VIRTUAL_BUFFER0_BASE_S); + memcpy(physical_frame_buffer_1, physical_frame_buffer_0, + sizeof(physical_frame_buffer_1)); + } +} diff --git a/core/embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_gfxmmu_lut.h b/core/embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_gfxmmu_lut.h new file mode 100644 index 000000000..86528b9d9 --- /dev/null +++ b/core/embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_gfxmmu_lut.h @@ -0,0 +1,1006 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * File Name : gfxmmu_lut.h + * Description : header file for GFX MMU Configuration Table + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __gfxmmu_lut_H +#define __gfxmmu_lut_H +#ifdef __cplusplus +extern "C" { +#endif +// GFX MMU Configuration Table + +#define GFXMMU_FB_SIZE 733936 +#define GFXMMU_LUT_FIRST 0 +#define GFXMMU_LUT_LAST 479 +#define GFXMMU_LUT_SIZE 480 + +uint32_t gfxmmu_lut[2 * GFXMMU_LUT_SIZE] = { + 0x00413601, // GFXMMU_LUT0L + 0x003FFCA0, // GFXMMU_LUT0H + 0x00433401, // GFXMMU_LUT1L + 0x003FFD80, // GFXMMU_LUT1H + 0x00453201, // GFXMMU_LUT2L + 0x003FFEA0, // GFXMMU_LUT2H + 0x00463101, // GFXMMU_LUT3L + 0x003FFFF0, // GFXMMU_LUT3H + 0x00482F01, // GFXMMU_LUT4L + 0x00000170, // GFXMMU_LUT4H + 0x00492E01, // GFXMMU_LUT5L + 0x00000320, // GFXMMU_LUT5H + 0x004A2D01, // GFXMMU_LUT6L + 0x000004F0, // GFXMMU_LUT6H + 0x004B2C01, // GFXMMU_LUT7L + 0x000006E0, // GFXMMU_LUT7H + 0x004C2B01, // GFXMMU_LUT8L + 0x000008F0, // GFXMMU_LUT8H + 0x004D2A01, // GFXMMU_LUT9L + 0x00000B20, // GFXMMU_LUT9H + 0x004D2A01, // GFXMMU_LUT10L + 0x00000D60, // GFXMMU_LUT10H + 0x004E2901, // GFXMMU_LUT11L + 0x00000FB0, // GFXMMU_LUT11H + 0x004F2801, // GFXMMU_LUT12L + 0x00001220, // GFXMMU_LUT12H + 0x00502701, // GFXMMU_LUT13L + 0x000014B0, // GFXMMU_LUT13H + 0x00502701, // GFXMMU_LUT14L + 0x00001750, // GFXMMU_LUT14H + 0x00512601, // GFXMMU_LUT15L + 0x00001A00, // GFXMMU_LUT15H + 0x00522501, // GFXMMU_LUT16L + 0x00001CD0, // GFXMMU_LUT16H + 0x00522501, // GFXMMU_LUT17L + 0x00001FB0, // GFXMMU_LUT17H + 0x00532401, // GFXMMU_LUT18L + 0x000022A0, // GFXMMU_LUT18H + 0x00532401, // GFXMMU_LUT19L + 0x000025A0, // GFXMMU_LUT19H + 0x00542301, // GFXMMU_LUT20L + 0x000028B0, // GFXMMU_LUT20H + 0x00552201, // GFXMMU_LUT21L + 0x00002BE0, // GFXMMU_LUT21H + 0x00552201, // GFXMMU_LUT22L + 0x00002F20, // GFXMMU_LUT22H + 0x00562101, // GFXMMU_LUT23L + 0x00003270, // GFXMMU_LUT23H + 0x00562101, // GFXMMU_LUT24L + 0x000035D0, // GFXMMU_LUT24H + 0x00572001, // GFXMMU_LUT25L + 0x00003940, // GFXMMU_LUT25H + 0x00572001, // GFXMMU_LUT26L + 0x00003CC0, // GFXMMU_LUT26H + 0x00581F01, // GFXMMU_LUT27L + 0x00004050, // GFXMMU_LUT27H + 0x00581F01, // GFXMMU_LUT28L + 0x000043F0, // GFXMMU_LUT28H + 0x00591E01, // GFXMMU_LUT29L + 0x000047A0, // GFXMMU_LUT29H + 0x00591E01, // GFXMMU_LUT30L + 0x00004B60, // GFXMMU_LUT30H + 0x00591E01, // GFXMMU_LUT31L + 0x00004F20, // GFXMMU_LUT31H + 0x005A1D01, // GFXMMU_LUT32L + 0x000052F0, // GFXMMU_LUT32H + 0x005A1D01, // GFXMMU_LUT33L + 0x000056D0, // GFXMMU_LUT33H + 0x005B1C01, // GFXMMU_LUT34L + 0x00005AC0, // GFXMMU_LUT34H + 0x005B1C01, // GFXMMU_LUT35L + 0x00005EC0, // GFXMMU_LUT35H + 0x005C1B01, // GFXMMU_LUT36L + 0x000062D0, // GFXMMU_LUT36H + 0x005C1B01, // GFXMMU_LUT37L + 0x000066F0, // GFXMMU_LUT37H + 0x005C1B01, // GFXMMU_LUT38L + 0x00006B10, // GFXMMU_LUT38H + 0x005D1A01, // GFXMMU_LUT39L + 0x00006F40, // GFXMMU_LUT39H + 0x005D1A01, // GFXMMU_LUT40L + 0x00007380, // GFXMMU_LUT40H + 0x005D1A01, // GFXMMU_LUT41L + 0x000077C0, // GFXMMU_LUT41H + 0x005E1901, // GFXMMU_LUT42L + 0x00007C10, // GFXMMU_LUT42H + 0x005E1901, // GFXMMU_LUT43L + 0x00008070, // GFXMMU_LUT43H + 0x005E1901, // GFXMMU_LUT44L + 0x000084D0, // GFXMMU_LUT44H + 0x005F1801, // GFXMMU_LUT45L + 0x00008940, // GFXMMU_LUT45H + 0x005F1801, // GFXMMU_LUT46L + 0x00008DC0, // GFXMMU_LUT46H + 0x00601801, // GFXMMU_LUT47L + 0x00009240, // GFXMMU_LUT47H + 0x00601701, // GFXMMU_LUT48L + 0x000096E0, // GFXMMU_LUT48H + 0x00601701, // GFXMMU_LUT49L + 0x00009B80, // GFXMMU_LUT49H + 0x00601701, // GFXMMU_LUT50L + 0x0000A020, // GFXMMU_LUT50H + 0x00611601, // GFXMMU_LUT51L + 0x0000A4D0, // GFXMMU_LUT51H + 0x00611601, // GFXMMU_LUT52L + 0x0000A990, // GFXMMU_LUT52H + 0x00611601, // GFXMMU_LUT53L + 0x0000AE50, // GFXMMU_LUT53H + 0x00621501, // GFXMMU_LUT54L + 0x0000B320, // GFXMMU_LUT54H + 0x00621501, // GFXMMU_LUT55L + 0x0000B800, // GFXMMU_LUT55H + 0x00621501, // GFXMMU_LUT56L + 0x0000BCE0, // GFXMMU_LUT56H + 0x00631401, // GFXMMU_LUT57L + 0x0000C1D0, // GFXMMU_LUT57H + 0x00631401, // GFXMMU_LUT58L + 0x0000C6D0, // GFXMMU_LUT58H + 0x00631401, // GFXMMU_LUT59L + 0x0000CBD0, // GFXMMU_LUT59H + 0x00631401, // GFXMMU_LUT60L + 0x0000D0D0, // GFXMMU_LUT60H + 0x00641301, // GFXMMU_LUT61L + 0x0000D5E0, // GFXMMU_LUT61H + 0x00641301, // GFXMMU_LUT62L + 0x0000DB00, // GFXMMU_LUT62H + 0x00641301, // GFXMMU_LUT63L + 0x0000E020, // GFXMMU_LUT63H + 0x00651201, // GFXMMU_LUT64L + 0x0000E550, // GFXMMU_LUT64H + 0x00651201, // GFXMMU_LUT65L + 0x0000EA90, // GFXMMU_LUT65H + 0x00651201, // GFXMMU_LUT66L + 0x0000EFD0, // GFXMMU_LUT66H + 0x00651201, // GFXMMU_LUT67L + 0x0000F510, // GFXMMU_LUT67H + 0x00661101, // GFXMMU_LUT68L + 0x0000FA60, // GFXMMU_LUT68H + 0x00661101, // GFXMMU_LUT69L + 0x0000FFC0, // GFXMMU_LUT69H + 0x00661101, // GFXMMU_LUT70L + 0x00010520, // GFXMMU_LUT70H + 0x00661101, // GFXMMU_LUT71L + 0x00010A80, // GFXMMU_LUT71H + 0x00671001, // GFXMMU_LUT72L + 0x00010FF0, // GFXMMU_LUT72H + 0x00671001, // GFXMMU_LUT73L + 0x00011570, // GFXMMU_LUT73H + 0x00671001, // GFXMMU_LUT74L + 0x00011AF0, // GFXMMU_LUT74H + 0x00671001, // GFXMMU_LUT75L + 0x00012070, // GFXMMU_LUT75H + 0x00680F01, // GFXMMU_LUT76L + 0x00012600, // GFXMMU_LUT76H + 0x00680F01, // GFXMMU_LUT77L + 0x00012BA0, // GFXMMU_LUT77H + 0x00680F01, // GFXMMU_LUT78L + 0x00013140, // GFXMMU_LUT78H + 0x00680F01, // GFXMMU_LUT79L + 0x000136E0, // GFXMMU_LUT79H + 0x00680F01, // GFXMMU_LUT80L + 0x00013C80, // GFXMMU_LUT80H + 0x00690E01, // GFXMMU_LUT81L + 0x00014230, // GFXMMU_LUT81H + 0x00690E01, // GFXMMU_LUT82L + 0x000147F0, // GFXMMU_LUT82H + 0x00690E01, // GFXMMU_LUT83L + 0x00014DB0, // GFXMMU_LUT83H + 0x00690E01, // GFXMMU_LUT84L + 0x00015370, // GFXMMU_LUT84H + 0x006A0D01, // GFXMMU_LUT85L + 0x00015940, // GFXMMU_LUT85H + 0x006A0D01, // GFXMMU_LUT86L + 0x00015F20, // GFXMMU_LUT86H + 0x006A0D01, // GFXMMU_LUT87L + 0x00016500, // GFXMMU_LUT87H + 0x006A0D01, // GFXMMU_LUT88L + 0x00016AE0, // GFXMMU_LUT88H + 0x006A0D01, // GFXMMU_LUT89L + 0x000170C0, // GFXMMU_LUT89H + 0x006B0C01, // GFXMMU_LUT90L + 0x000176B0, // GFXMMU_LUT90H + 0x006B0C01, // GFXMMU_LUT91L + 0x00017CB0, // GFXMMU_LUT91H + 0x006B0C01, // GFXMMU_LUT92L + 0x000182B0, // GFXMMU_LUT92H + 0x006B0C01, // GFXMMU_LUT93L + 0x000188B0, // GFXMMU_LUT93H + 0x006B0C01, // GFXMMU_LUT94L + 0x00018EB0, // GFXMMU_LUT94H + 0x006C0C01, // GFXMMU_LUT95L + 0x000194B0, // GFXMMU_LUT95H + 0x006C0B01, // GFXMMU_LUT96L + 0x00019AD0, // GFXMMU_LUT96H + 0x006C0B01, // GFXMMU_LUT97L + 0x0001A0F0, // GFXMMU_LUT97H + 0x006C0B01, // GFXMMU_LUT98L + 0x0001A710, // GFXMMU_LUT98H + 0x006C0B01, // GFXMMU_LUT99L + 0x0001AD30, // GFXMMU_LUT99H + 0x006C0B01, // GFXMMU_LUT100L + 0x0001B350, // GFXMMU_LUT100H + 0x006D0A01, // GFXMMU_LUT101L + 0x0001B980, // GFXMMU_LUT101H + 0x006D0A01, // GFXMMU_LUT102L + 0x0001BFC0, // GFXMMU_LUT102H + 0x006D0A01, // GFXMMU_LUT103L + 0x0001C600, // GFXMMU_LUT103H + 0x006D0A01, // GFXMMU_LUT104L + 0x0001CC40, // GFXMMU_LUT104H + 0x006D0A01, // GFXMMU_LUT105L + 0x0001D280, // GFXMMU_LUT105H + 0x006D0A01, // GFXMMU_LUT106L + 0x0001D8C0, // GFXMMU_LUT106H + 0x006E0901, // GFXMMU_LUT107L + 0x0001DF10, // GFXMMU_LUT107H + 0x006E0901, // GFXMMU_LUT108L + 0x0001E570, // GFXMMU_LUT108H + 0x006E0901, // GFXMMU_LUT109L + 0x0001EBD0, // GFXMMU_LUT109H + 0x006E0901, // GFXMMU_LUT110L + 0x0001F230, // GFXMMU_LUT110H + 0x006E0901, // GFXMMU_LUT111L + 0x0001F890, // GFXMMU_LUT111H + 0x006E0901, // GFXMMU_LUT112L + 0x0001FEF0, // GFXMMU_LUT112H + 0x006F0801, // GFXMMU_LUT113L + 0x00020560, // GFXMMU_LUT113H + 0x006F0801, // GFXMMU_LUT114L + 0x00020BE0, // GFXMMU_LUT114H + 0x006F0801, // GFXMMU_LUT115L + 0x00021260, // GFXMMU_LUT115H + 0x006F0801, // GFXMMU_LUT116L + 0x000218E0, // GFXMMU_LUT116H + 0x006F0801, // GFXMMU_LUT117L + 0x00021F60, // GFXMMU_LUT117H + 0x006F0801, // GFXMMU_LUT118L + 0x000225E0, // GFXMMU_LUT118H + 0x006F0801, // GFXMMU_LUT119L + 0x00022C60, // GFXMMU_LUT119H + 0x00700701, // GFXMMU_LUT120L + 0x000232F0, // GFXMMU_LUT120H + 0x00700701, // GFXMMU_LUT121L + 0x00023990, // GFXMMU_LUT121H + 0x00700701, // GFXMMU_LUT122L + 0x00024030, // GFXMMU_LUT122H + 0x00700701, // GFXMMU_LUT123L + 0x000246D0, // GFXMMU_LUT123H + 0x00700701, // GFXMMU_LUT124L + 0x00024D70, // GFXMMU_LUT124H + 0x00700701, // GFXMMU_LUT125L + 0x00025410, // GFXMMU_LUT125H + 0x00700701, // GFXMMU_LUT126L + 0x00025AB0, // GFXMMU_LUT126H + 0x00710601, // GFXMMU_LUT127L + 0x00026160, // GFXMMU_LUT127H + 0x00710601, // GFXMMU_LUT128L + 0x00026820, // GFXMMU_LUT128H + 0x00710601, // GFXMMU_LUT129L + 0x00026EE0, // GFXMMU_LUT129H + 0x00710601, // GFXMMU_LUT130L + 0x000275A0, // GFXMMU_LUT130H + 0x00710601, // GFXMMU_LUT131L + 0x00027C60, // GFXMMU_LUT131H + 0x00710601, // GFXMMU_LUT132L + 0x00028320, // GFXMMU_LUT132H + 0x00710601, // GFXMMU_LUT133L + 0x000289E0, // GFXMMU_LUT133H + 0x00710601, // GFXMMU_LUT134L + 0x000290A0, // GFXMMU_LUT134H + 0x00720501, // GFXMMU_LUT135L + 0x00029770, // GFXMMU_LUT135H + 0x00720501, // GFXMMU_LUT136L + 0x00029E50, // GFXMMU_LUT136H + 0x00720501, // GFXMMU_LUT137L + 0x0002A530, // GFXMMU_LUT137H + 0x00720501, // GFXMMU_LUT138L + 0x0002AC10, // GFXMMU_LUT138H + 0x00720501, // GFXMMU_LUT139L + 0x0002B2F0, // GFXMMU_LUT139H + 0x00720501, // GFXMMU_LUT140L + 0x0002B9D0, // GFXMMU_LUT140H + 0x00720501, // GFXMMU_LUT141L + 0x0002C0B0, // GFXMMU_LUT141H + 0x00720501, // GFXMMU_LUT142L + 0x0002C790, // GFXMMU_LUT142H + 0x00720501, // GFXMMU_LUT143L + 0x0002CE70, // GFXMMU_LUT143H + 0x00730401, // GFXMMU_LUT144L + 0x0002D560, // GFXMMU_LUT144H + 0x00730401, // GFXMMU_LUT145L + 0x0002DC60, // GFXMMU_LUT145H + 0x00730401, // GFXMMU_LUT146L + 0x0002E360, // GFXMMU_LUT146H + 0x00730401, // GFXMMU_LUT147L + 0x0002EA60, // GFXMMU_LUT147H + 0x00730401, // GFXMMU_LUT148L + 0x0002F160, // GFXMMU_LUT148H + 0x00730401, // GFXMMU_LUT149L + 0x0002F860, // GFXMMU_LUT149H + 0x00730401, // GFXMMU_LUT150L + 0x0002FF60, // GFXMMU_LUT150H + 0x00730401, // GFXMMU_LUT151L + 0x00030660, // GFXMMU_LUT151H + 0x00730401, // GFXMMU_LUT152L + 0x00030D60, // GFXMMU_LUT152H + 0x00740301, // GFXMMU_LUT153L + 0x00031470, // GFXMMU_LUT153H + 0x00740301, // GFXMMU_LUT154L + 0x00031B90, // GFXMMU_LUT154H + 0x00740301, // GFXMMU_LUT155L + 0x000322B0, // GFXMMU_LUT155H + 0x00740301, // GFXMMU_LUT156L + 0x000329D0, // GFXMMU_LUT156H + 0x00740301, // GFXMMU_LUT157L + 0x000330F0, // GFXMMU_LUT157H + 0x00740301, // GFXMMU_LUT158L + 0x00033810, // GFXMMU_LUT158H + 0x00740301, // GFXMMU_LUT159L + 0x00033F30, // GFXMMU_LUT159H + 0x00740301, // GFXMMU_LUT160L + 0x00034650, // GFXMMU_LUT160H + 0x00740301, // GFXMMU_LUT161L + 0x00034D70, // GFXMMU_LUT161H + 0x00740301, // GFXMMU_LUT162L + 0x00035490, // GFXMMU_LUT162H + 0x00740301, // GFXMMU_LUT163L + 0x00035BB0, // GFXMMU_LUT163H + 0x00740301, // GFXMMU_LUT164L + 0x000362D0, // GFXMMU_LUT164H + 0x00750201, // GFXMMU_LUT165L + 0x00036A00, // GFXMMU_LUT165H + 0x00750201, // GFXMMU_LUT166L + 0x00037140, // GFXMMU_LUT166H + 0x00750201, // GFXMMU_LUT167L + 0x00037880, // GFXMMU_LUT167H + 0x00750201, // GFXMMU_LUT168L + 0x00037FC0, // GFXMMU_LUT168H + 0x00750201, // GFXMMU_LUT169L + 0x00038700, // GFXMMU_LUT169H + 0x00750201, // GFXMMU_LUT170L + 0x00038E40, // GFXMMU_LUT170H + 0x00750201, // GFXMMU_LUT171L + 0x00039580, // GFXMMU_LUT171H + 0x00750201, // GFXMMU_LUT172L + 0x00039CC0, // GFXMMU_LUT172H + 0x00750201, // GFXMMU_LUT173L + 0x0003A400, // GFXMMU_LUT173H + 0x00750201, // GFXMMU_LUT174L + 0x0003AB40, // GFXMMU_LUT174H + 0x00750201, // GFXMMU_LUT175L + 0x0003B280, // GFXMMU_LUT175H + 0x00750201, // GFXMMU_LUT176L + 0x0003B9C0, // GFXMMU_LUT176H + 0x00750201, // GFXMMU_LUT177L + 0x0003C100, // GFXMMU_LUT177H + 0x00760101, // GFXMMU_LUT178L + 0x0003C850, // GFXMMU_LUT178H + 0x00760101, // GFXMMU_LUT179L + 0x0003CFB0, // GFXMMU_LUT179H + 0x00760101, // GFXMMU_LUT180L + 0x0003D710, // GFXMMU_LUT180H + 0x00760101, // GFXMMU_LUT181L + 0x0003DE70, // GFXMMU_LUT181H + 0x00760101, // GFXMMU_LUT182L + 0x0003E5D0, // GFXMMU_LUT182H + 0x00760101, // GFXMMU_LUT183L + 0x0003ED30, // GFXMMU_LUT183H + 0x00760101, // GFXMMU_LUT184L + 0x0003F490, // GFXMMU_LUT184H + 0x00760101, // GFXMMU_LUT185L + 0x0003FBF0, // GFXMMU_LUT185H + 0x00760101, // GFXMMU_LUT186L + 0x00040350, // GFXMMU_LUT186H + 0x00760101, // GFXMMU_LUT187L + 0x00040AB0, // GFXMMU_LUT187H + 0x00760101, // GFXMMU_LUT188L + 0x00041210, // GFXMMU_LUT188H + 0x00760101, // GFXMMU_LUT189L + 0x00041970, // GFXMMU_LUT189H + 0x00760101, // GFXMMU_LUT190L + 0x000420D0, // GFXMMU_LUT190H + 0x00760101, // GFXMMU_LUT191L + 0x00042830, // GFXMMU_LUT191H + 0x00760101, // GFXMMU_LUT192L + 0x00042F90, // GFXMMU_LUT192H + 0x00760101, // GFXMMU_LUT193L + 0x000436F0, // GFXMMU_LUT193H + 0x00760101, // GFXMMU_LUT194L + 0x00043E50, // GFXMMU_LUT194H + 0x00760101, // GFXMMU_LUT195L + 0x000445B0, // GFXMMU_LUT195H + 0x00770001, // GFXMMU_LUT196L + 0x00044D20, // GFXMMU_LUT196H + 0x00770001, // GFXMMU_LUT197L + 0x000454A0, // GFXMMU_LUT197H + 0x00770001, // GFXMMU_LUT198L + 0x00045C20, // GFXMMU_LUT198H + 0x00770001, // GFXMMU_LUT199L + 0x000463A0, // GFXMMU_LUT199H + 0x00770001, // GFXMMU_LUT200L + 0x00046B20, // GFXMMU_LUT200H + 0x00770001, // GFXMMU_LUT201L + 0x000472A0, // GFXMMU_LUT201H + 0x00770001, // GFXMMU_LUT202L + 0x00047A20, // GFXMMU_LUT202H + 0x00770001, // GFXMMU_LUT203L + 0x000481A0, // GFXMMU_LUT203H + 0x00770001, // GFXMMU_LUT204L + 0x00048920, // GFXMMU_LUT204H + 0x00770001, // GFXMMU_LUT205L + 0x000490A0, // GFXMMU_LUT205H + 0x00770001, // GFXMMU_LUT206L + 0x00049820, // GFXMMU_LUT206H + 0x00770001, // GFXMMU_LUT207L + 0x00049FA0, // GFXMMU_LUT207H + 0x00770001, // GFXMMU_LUT208L + 0x0004A720, // GFXMMU_LUT208H + 0x00770001, // GFXMMU_LUT209L + 0x0004AEA0, // GFXMMU_LUT209H + 0x00770001, // GFXMMU_LUT210L + 0x0004B620, // GFXMMU_LUT210H + 0x00770001, // GFXMMU_LUT211L + 0x0004BDA0, // GFXMMU_LUT211H + 0x00770001, // GFXMMU_LUT212L + 0x0004C520, // GFXMMU_LUT212H + 0x00770001, // GFXMMU_LUT213L + 0x0004CCA0, // GFXMMU_LUT213H + 0x00770001, // GFXMMU_LUT214L + 0x0004D420, // GFXMMU_LUT214H + 0x00770001, // GFXMMU_LUT215L + 0x0004DBA0, // GFXMMU_LUT215H + 0x00770001, // GFXMMU_LUT216L + 0x0004E320, // GFXMMU_LUT216H + 0x00770001, // GFXMMU_LUT217L + 0x0004EAA0, // GFXMMU_LUT217H + 0x00770001, // GFXMMU_LUT218L + 0x0004F220, // GFXMMU_LUT218H + 0x00770001, // GFXMMU_LUT219L + 0x0004F9A0, // GFXMMU_LUT219H + 0x00770001, // GFXMMU_LUT220L + 0x00050120, // GFXMMU_LUT220H + 0x00770001, // GFXMMU_LUT221L + 0x000508A0, // GFXMMU_LUT221H + 0x00770001, // GFXMMU_LUT222L + 0x00051020, // GFXMMU_LUT222H + 0x00770001, // GFXMMU_LUT223L + 0x000517A0, // GFXMMU_LUT223H + 0x00770001, // GFXMMU_LUT224L + 0x00051F20, // GFXMMU_LUT224H + 0x00770001, // GFXMMU_LUT225L + 0x000526A0, // GFXMMU_LUT225H + 0x00770001, // GFXMMU_LUT226L + 0x00052E20, // GFXMMU_LUT226H + 0x00770001, // GFXMMU_LUT227L + 0x000535A0, // GFXMMU_LUT227H + 0x00770001, // GFXMMU_LUT228L + 0x00053D20, // GFXMMU_LUT228H + 0x00770001, // GFXMMU_LUT229L + 0x000544A0, // GFXMMU_LUT229H + 0x00770001, // GFXMMU_LUT230L + 0x00054C20, // GFXMMU_LUT230H + 0x00770001, // GFXMMU_LUT231L + 0x000553A0, // GFXMMU_LUT231H + 0x00770001, // GFXMMU_LUT232L + 0x00055B20, // GFXMMU_LUT232H + 0x00770001, // GFXMMU_LUT233L + 0x000562A0, // GFXMMU_LUT233H + 0x00770001, // GFXMMU_LUT234L + 0x00056A20, // GFXMMU_LUT234H + 0x00770001, // GFXMMU_LUT235L + 0x000571A0, // GFXMMU_LUT235H + 0x00770001, // GFXMMU_LUT236L + 0x00057920, // GFXMMU_LUT236H + 0x00770001, // GFXMMU_LUT237L + 0x000580A0, // GFXMMU_LUT237H + 0x00770001, // GFXMMU_LUT238L + 0x00058820, // GFXMMU_LUT238H + 0x00770001, // GFXMMU_LUT239L + 0x00058FA0, // GFXMMU_LUT239H + 0x00780001, // GFXMMU_LUT240L + 0x00059720, // GFXMMU_LUT240H + 0x00770001, // GFXMMU_LUT241L + 0x00059EB0, // GFXMMU_LUT241H + 0x00770001, // GFXMMU_LUT242L + 0x0005A630, // GFXMMU_LUT242H + 0x00770001, // GFXMMU_LUT243L + 0x0005ADB0, // GFXMMU_LUT243H + 0x00770001, // GFXMMU_LUT244L + 0x0005B530, // GFXMMU_LUT244H + 0x00770001, // GFXMMU_LUT245L + 0x0005BCB0, // GFXMMU_LUT245H + 0x00770001, // GFXMMU_LUT246L + 0x0005C430, // GFXMMU_LUT246H + 0x00770001, // GFXMMU_LUT247L + 0x0005CBB0, // GFXMMU_LUT247H + 0x00770001, // GFXMMU_LUT248L + 0x0005D330, // GFXMMU_LUT248H + 0x00770001, // GFXMMU_LUT249L + 0x0005DAB0, // GFXMMU_LUT249H + 0x00770001, // GFXMMU_LUT250L + 0x0005E230, // GFXMMU_LUT250H + 0x00770001, // GFXMMU_LUT251L + 0x0005E9B0, // GFXMMU_LUT251H + 0x00770001, // GFXMMU_LUT252L + 0x0005F130, // GFXMMU_LUT252H + 0x00770001, // GFXMMU_LUT253L + 0x0005F8B0, // GFXMMU_LUT253H + 0x00770001, // GFXMMU_LUT254L + 0x00060030, // GFXMMU_LUT254H + 0x00770001, // GFXMMU_LUT255L + 0x000607B0, // GFXMMU_LUT255H + 0x00770001, // GFXMMU_LUT256L + 0x00060F30, // GFXMMU_LUT256H + 0x00770001, // GFXMMU_LUT257L + 0x000616B0, // GFXMMU_LUT257H + 0x00770001, // GFXMMU_LUT258L + 0x00061E30, // GFXMMU_LUT258H + 0x00770001, // GFXMMU_LUT259L + 0x000625B0, // GFXMMU_LUT259H + 0x00770001, // GFXMMU_LUT260L + 0x00062D30, // GFXMMU_LUT260H + 0x00770001, // GFXMMU_LUT261L + 0x000634B0, // GFXMMU_LUT261H + 0x00770001, // GFXMMU_LUT262L + 0x00063C30, // GFXMMU_LUT262H + 0x00770001, // GFXMMU_LUT263L + 0x000643B0, // GFXMMU_LUT263H + 0x00770001, // GFXMMU_LUT264L + 0x00064B30, // GFXMMU_LUT264H + 0x00770001, // GFXMMU_LUT265L + 0x000652B0, // GFXMMU_LUT265H + 0x00770001, // GFXMMU_LUT266L + 0x00065A30, // GFXMMU_LUT266H + 0x00770001, // GFXMMU_LUT267L + 0x000661B0, // GFXMMU_LUT267H + 0x00770001, // GFXMMU_LUT268L + 0x00066930, // GFXMMU_LUT268H + 0x00770001, // GFXMMU_LUT269L + 0x000670B0, // GFXMMU_LUT269H + 0x00770001, // GFXMMU_LUT270L + 0x00067830, // GFXMMU_LUT270H + 0x00770001, // GFXMMU_LUT271L + 0x00067FB0, // GFXMMU_LUT271H + 0x00770001, // GFXMMU_LUT272L + 0x00068730, // GFXMMU_LUT272H + 0x00770001, // GFXMMU_LUT273L + 0x00068EB0, // GFXMMU_LUT273H + 0x00770001, // GFXMMU_LUT274L + 0x00069630, // GFXMMU_LUT274H + 0x00770001, // GFXMMU_LUT275L + 0x00069DB0, // GFXMMU_LUT275H + 0x00770001, // GFXMMU_LUT276L + 0x0006A530, // GFXMMU_LUT276H + 0x00770001, // GFXMMU_LUT277L + 0x0006ACB0, // GFXMMU_LUT277H + 0x00770001, // GFXMMU_LUT278L + 0x0006B430, // GFXMMU_LUT278H + 0x00770001, // GFXMMU_LUT279L + 0x0006BBB0, // GFXMMU_LUT279H + 0x00770001, // GFXMMU_LUT280L + 0x0006C330, // GFXMMU_LUT280H + 0x00770001, // GFXMMU_LUT281L + 0x0006CAB0, // GFXMMU_LUT281H + 0x00770001, // GFXMMU_LUT282L + 0x0006D230, // GFXMMU_LUT282H + 0x00770001, // GFXMMU_LUT283L + 0x0006D9B0, // GFXMMU_LUT283H + 0x00770001, // GFXMMU_LUT284L + 0x0006E130, // GFXMMU_LUT284H + 0x00760101, // GFXMMU_LUT285L + 0x0006E8A0, // GFXMMU_LUT285H + 0x00760101, // GFXMMU_LUT286L + 0x0006F000, // GFXMMU_LUT286H + 0x00760101, // GFXMMU_LUT287L + 0x0006F760, // GFXMMU_LUT287H + 0x00760101, // GFXMMU_LUT288L + 0x0006FEC0, // GFXMMU_LUT288H + 0x00760101, // GFXMMU_LUT289L + 0x00070620, // GFXMMU_LUT289H + 0x00760101, // GFXMMU_LUT290L + 0x00070D80, // GFXMMU_LUT290H + 0x00760101, // GFXMMU_LUT291L + 0x000714E0, // GFXMMU_LUT291H + 0x00760101, // GFXMMU_LUT292L + 0x00071C40, // GFXMMU_LUT292H + 0x00760101, // GFXMMU_LUT293L + 0x000723A0, // GFXMMU_LUT293H + 0x00760101, // GFXMMU_LUT294L + 0x00072B00, // GFXMMU_LUT294H + 0x00760101, // GFXMMU_LUT295L + 0x00073260, // GFXMMU_LUT295H + 0x00760101, // GFXMMU_LUT296L + 0x000739C0, // GFXMMU_LUT296H + 0x00760101, // GFXMMU_LUT297L + 0x00074120, // GFXMMU_LUT297H + 0x00760101, // GFXMMU_LUT298L + 0x00074880, // GFXMMU_LUT298H + 0x00760101, // GFXMMU_LUT299L + 0x00074FE0, // GFXMMU_LUT299H + 0x00760101, // GFXMMU_LUT300L + 0x00075740, // GFXMMU_LUT300H + 0x00760101, // GFXMMU_LUT301L + 0x00075EA0, // GFXMMU_LUT301H + 0x00760101, // GFXMMU_LUT302L + 0x00076600, // GFXMMU_LUT302H + 0x00750201, // GFXMMU_LUT303L + 0x00076D50, // GFXMMU_LUT303H + 0x00750201, // GFXMMU_LUT304L + 0x00077490, // GFXMMU_LUT304H + 0x00750201, // GFXMMU_LUT305L + 0x00077BD0, // GFXMMU_LUT305H + 0x00750201, // GFXMMU_LUT306L + 0x00078310, // GFXMMU_LUT306H + 0x00750201, // GFXMMU_LUT307L + 0x00078A50, // GFXMMU_LUT307H + 0x00750201, // GFXMMU_LUT308L + 0x00079190, // GFXMMU_LUT308H + 0x00750201, // GFXMMU_LUT309L + 0x000798D0, // GFXMMU_LUT309H + 0x00750201, // GFXMMU_LUT310L + 0x0007A010, // GFXMMU_LUT310H + 0x00750201, // GFXMMU_LUT311L + 0x0007A750, // GFXMMU_LUT311H + 0x00750201, // GFXMMU_LUT312L + 0x0007AE90, // GFXMMU_LUT312H + 0x00750201, // GFXMMU_LUT313L + 0x0007B5D0, // GFXMMU_LUT313H + 0x00750201, // GFXMMU_LUT314L + 0x0007BD10, // GFXMMU_LUT314H + 0x00750201, // GFXMMU_LUT315L + 0x0007C450, // GFXMMU_LUT315H + 0x00740301, // GFXMMU_LUT316L + 0x0007CB80, // GFXMMU_LUT316H + 0x00740301, // GFXMMU_LUT317L + 0x0007D2A0, // GFXMMU_LUT317H + 0x00740301, // GFXMMU_LUT318L + 0x0007D9C0, // GFXMMU_LUT318H + 0x00740301, // GFXMMU_LUT319L + 0x0007E0E0, // GFXMMU_LUT319H + 0x00740301, // GFXMMU_LUT320L + 0x0007E800, // GFXMMU_LUT320H + 0x00740301, // GFXMMU_LUT321L + 0x0007EF20, // GFXMMU_LUT321H + 0x00740301, // GFXMMU_LUT322L + 0x0007F640, // GFXMMU_LUT322H + 0x00740301, // GFXMMU_LUT323L + 0x0007FD60, // GFXMMU_LUT323H + 0x00740301, // GFXMMU_LUT324L + 0x00080480, // GFXMMU_LUT324H + 0x00740301, // GFXMMU_LUT325L + 0x00080BA0, // GFXMMU_LUT325H + 0x00740301, // GFXMMU_LUT326L + 0x000812C0, // GFXMMU_LUT326H + 0x00740301, // GFXMMU_LUT327L + 0x000819E0, // GFXMMU_LUT327H + 0x00730401, // GFXMMU_LUT328L + 0x000820F0, // GFXMMU_LUT328H + 0x00730401, // GFXMMU_LUT329L + 0x000827F0, // GFXMMU_LUT329H + 0x00730401, // GFXMMU_LUT330L + 0x00082EF0, // GFXMMU_LUT330H + 0x00730401, // GFXMMU_LUT331L + 0x000835F0, // GFXMMU_LUT331H + 0x00730401, // GFXMMU_LUT332L + 0x00083CF0, // GFXMMU_LUT332H + 0x00730401, // GFXMMU_LUT333L + 0x000843F0, // GFXMMU_LUT333H + 0x00730401, // GFXMMU_LUT334L + 0x00084AF0, // GFXMMU_LUT334H + 0x00730401, // GFXMMU_LUT335L + 0x000851F0, // GFXMMU_LUT335H + 0x00730401, // GFXMMU_LUT336L + 0x000858F0, // GFXMMU_LUT336H + 0x00730401, // GFXMMU_LUT337L + 0x00085FF0, // GFXMMU_LUT337H + 0x00720501, // GFXMMU_LUT338L + 0x000866E0, // GFXMMU_LUT338H + 0x00720501, // GFXMMU_LUT339L + 0x00086DC0, // GFXMMU_LUT339H + 0x00720501, // GFXMMU_LUT340L + 0x000874A0, // GFXMMU_LUT340H + 0x00720501, // GFXMMU_LUT341L + 0x00087B80, // GFXMMU_LUT341H + 0x00720501, // GFXMMU_LUT342L + 0x00088260, // GFXMMU_LUT342H + 0x00720501, // GFXMMU_LUT343L + 0x00088940, // GFXMMU_LUT343H + 0x00720501, // GFXMMU_LUT344L + 0x00089020, // GFXMMU_LUT344H + 0x00720501, // GFXMMU_LUT345L + 0x00089700, // GFXMMU_LUT345H + 0x00710601, // GFXMMU_LUT346L + 0x00089DD0, // GFXMMU_LUT346H + 0x00710601, // GFXMMU_LUT347L + 0x0008A490, // GFXMMU_LUT347H + 0x00710601, // GFXMMU_LUT348L + 0x0008AB50, // GFXMMU_LUT348H + 0x00710601, // GFXMMU_LUT349L + 0x0008B210, // GFXMMU_LUT349H + 0x00710601, // GFXMMU_LUT350L + 0x0008B8D0, // GFXMMU_LUT350H + 0x00710601, // GFXMMU_LUT351L + 0x0008BF90, // GFXMMU_LUT351H + 0x00710601, // GFXMMU_LUT352L + 0x0008C650, // GFXMMU_LUT352H + 0x00710601, // GFXMMU_LUT353L + 0x0008CD10, // GFXMMU_LUT353H + 0x00700701, // GFXMMU_LUT354L + 0x0008D3C0, // GFXMMU_LUT354H + 0x00700701, // GFXMMU_LUT355L + 0x0008DA60, // GFXMMU_LUT355H + 0x00700701, // GFXMMU_LUT356L + 0x0008E100, // GFXMMU_LUT356H + 0x00700701, // GFXMMU_LUT357L + 0x0008E7A0, // GFXMMU_LUT357H + 0x00700701, // GFXMMU_LUT358L + 0x0008EE40, // GFXMMU_LUT358H + 0x00700701, // GFXMMU_LUT359L + 0x0008F4E0, // GFXMMU_LUT359H + 0x00700701, // GFXMMU_LUT360L + 0x0008FB80, // GFXMMU_LUT360H + 0x006F0801, // GFXMMU_LUT361L + 0x00090210, // GFXMMU_LUT361H + 0x006F0801, // GFXMMU_LUT362L + 0x00090890, // GFXMMU_LUT362H + 0x006F0801, // GFXMMU_LUT363L + 0x00090F10, // GFXMMU_LUT363H + 0x006F0801, // GFXMMU_LUT364L + 0x00091590, // GFXMMU_LUT364H + 0x006F0801, // GFXMMU_LUT365L + 0x00091C10, // GFXMMU_LUT365H + 0x006F0801, // GFXMMU_LUT366L + 0x00092290, // GFXMMU_LUT366H + 0x006F0801, // GFXMMU_LUT367L + 0x00092910, // GFXMMU_LUT367H + 0x006E0901, // GFXMMU_LUT368L + 0x00092F80, // GFXMMU_LUT368H + 0x006E0901, // GFXMMU_LUT369L + 0x000935E0, // GFXMMU_LUT369H + 0x006E0901, // GFXMMU_LUT370L + 0x00093C40, // GFXMMU_LUT370H + 0x006E0901, // GFXMMU_LUT371L + 0x000942A0, // GFXMMU_LUT371H + 0x006E0901, // GFXMMU_LUT372L + 0x00094900, // GFXMMU_LUT372H + 0x006E0901, // GFXMMU_LUT373L + 0x00094F60, // GFXMMU_LUT373H + 0x006D0A01, // GFXMMU_LUT374L + 0x000955B0, // GFXMMU_LUT374H + 0x006D0A01, // GFXMMU_LUT375L + 0x00095BF0, // GFXMMU_LUT375H + 0x006D0A01, // GFXMMU_LUT376L + 0x00096230, // GFXMMU_LUT376H + 0x006D0A01, // GFXMMU_LUT377L + 0x00096870, // GFXMMU_LUT377H + 0x006D0A01, // GFXMMU_LUT378L + 0x00096EB0, // GFXMMU_LUT378H + 0x006C0B01, // GFXMMU_LUT379L + 0x000974E0, // GFXMMU_LUT379H + 0x006C0B01, // GFXMMU_LUT380L + 0x00097B00, // GFXMMU_LUT380H + 0x006C0B01, // GFXMMU_LUT381L + 0x00098120, // GFXMMU_LUT381H + 0x006C0B01, // GFXMMU_LUT382L + 0x00098740, // GFXMMU_LUT382H + 0x006C0B01, // GFXMMU_LUT383L + 0x00098D60, // GFXMMU_LUT383H + 0x006C0B01, // GFXMMU_LUT384L + 0x00099380, // GFXMMU_LUT384H + 0x006B0C01, // GFXMMU_LUT385L + 0x00099990, // GFXMMU_LUT385H + 0x006B0C01, // GFXMMU_LUT386L + 0x00099F90, // GFXMMU_LUT386H + 0x006B0C01, // GFXMMU_LUT387L + 0x0009A590, // GFXMMU_LUT387H + 0x006B0C01, // GFXMMU_LUT388L + 0x0009AB90, // GFXMMU_LUT388H + 0x006B0C01, // GFXMMU_LUT389L + 0x0009B190, // GFXMMU_LUT389H + 0x006A0D01, // GFXMMU_LUT390L + 0x0009B780, // GFXMMU_LUT390H + 0x006A0D01, // GFXMMU_LUT391L + 0x0009BD60, // GFXMMU_LUT391H + 0x006A0D01, // GFXMMU_LUT392L + 0x0009C340, // GFXMMU_LUT392H + 0x006A0D01, // GFXMMU_LUT393L + 0x0009C920, // GFXMMU_LUT393H + 0x006A0D01, // GFXMMU_LUT394L + 0x0009CF00, // GFXMMU_LUT394H + 0x00690E01, // GFXMMU_LUT395L + 0x0009D4D0, // GFXMMU_LUT395H + 0x00690E01, // GFXMMU_LUT396L + 0x0009DA90, // GFXMMU_LUT396H + 0x00690E01, // GFXMMU_LUT397L + 0x0009E050, // GFXMMU_LUT397H + 0x00690E01, // GFXMMU_LUT398L + 0x0009E610, // GFXMMU_LUT398H + 0x00690E01, // GFXMMU_LUT399L + 0x0009EBD0, // GFXMMU_LUT399H + 0x00690E01, // GFXMMU_LUT400L + 0x0009F190, // GFXMMU_LUT400H + 0x00690E01, // GFXMMU_LUT401L + 0x0009F750, // GFXMMU_LUT401H + 0x00680F01, // GFXMMU_LUT402L + 0x0009FD00, // GFXMMU_LUT402H + 0x00680F01, // GFXMMU_LUT403L + 0x000A02A0, // GFXMMU_LUT403H + 0x00680F01, // GFXMMU_LUT404L + 0x000A0840, // GFXMMU_LUT404H + 0x00680F01, // GFXMMU_LUT405L + 0x000A0DE0, // GFXMMU_LUT405H + 0x00671001, // GFXMMU_LUT406L + 0x000A1370, // GFXMMU_LUT406H + 0x00671001, // GFXMMU_LUT407L + 0x000A18F0, // GFXMMU_LUT407H + 0x00671001, // GFXMMU_LUT408L + 0x000A1E70, // GFXMMU_LUT408H + 0x00671001, // GFXMMU_LUT409L + 0x000A23F0, // GFXMMU_LUT409H + 0x00661101, // GFXMMU_LUT410L + 0x000A2960, // GFXMMU_LUT410H + 0x00661101, // GFXMMU_LUT411L + 0x000A2EC0, // GFXMMU_LUT411H + 0x00661101, // GFXMMU_LUT412L + 0x000A3420, // GFXMMU_LUT412H + 0x00661101, // GFXMMU_LUT413L + 0x000A3980, // GFXMMU_LUT413H + 0x00651201, // GFXMMU_LUT414L + 0x000A3ED0, // GFXMMU_LUT414H + 0x00651201, // GFXMMU_LUT415L + 0x000A4410, // GFXMMU_LUT415H + 0x00651201, // GFXMMU_LUT416L + 0x000A4950, // GFXMMU_LUT416H + 0x00641301, // GFXMMU_LUT417L + 0x000A4E80, // GFXMMU_LUT417H + 0x00641301, // GFXMMU_LUT418L + 0x000A53A0, // GFXMMU_LUT418H + 0x00641301, // GFXMMU_LUT419L + 0x000A58C0, // GFXMMU_LUT419H + 0x00641301, // GFXMMU_LUT420L + 0x000A5DE0, // GFXMMU_LUT420H + 0x00631401, // GFXMMU_LUT421L + 0x000A62F0, // GFXMMU_LUT421H + 0x00631401, // GFXMMU_LUT422L + 0x000A67F0, // GFXMMU_LUT422H + 0x00631401, // GFXMMU_LUT423L + 0x000A6CF0, // GFXMMU_LUT423H + 0x00621501, // GFXMMU_LUT424L + 0x000A71E0, // GFXMMU_LUT424H + 0x00621501, // GFXMMU_LUT425L + 0x000A76C0, // GFXMMU_LUT425H + 0x00621501, // GFXMMU_LUT426L + 0x000A7BA0, // GFXMMU_LUT426H + 0x00621501, // GFXMMU_LUT427L + 0x000A8080, // GFXMMU_LUT427H + 0x00611601, // GFXMMU_LUT428L + 0x000A8550, // GFXMMU_LUT428H + 0x00611601, // GFXMMU_LUT429L + 0x000A8A10, // GFXMMU_LUT429H + 0x00611601, // GFXMMU_LUT430L + 0x000A8ED0, // GFXMMU_LUT430H + 0x00601701, // GFXMMU_LUT431L + 0x000A9380, // GFXMMU_LUT431H + 0x00601701, // GFXMMU_LUT432L + 0x000A9820, // GFXMMU_LUT432H + 0x00601701, // GFXMMU_LUT433L + 0x000A9CC0, // GFXMMU_LUT433H + 0x005F1801, // GFXMMU_LUT434L + 0x000AA150, // GFXMMU_LUT434H + 0x005F1801, // GFXMMU_LUT435L + 0x000AA5D0, // GFXMMU_LUT435H + 0x005F1801, // GFXMMU_LUT436L + 0x000AAA50, // GFXMMU_LUT436H + 0x005E1901, // GFXMMU_LUT437L + 0x000AAEC0, // GFXMMU_LUT437H + 0x005E1901, // GFXMMU_LUT438L + 0x000AB320, // GFXMMU_LUT438H + 0x005D1A01, // GFXMMU_LUT439L + 0x000AB770, // GFXMMU_LUT439H + 0x005D1A01, // GFXMMU_LUT440L + 0x000ABBB0, // GFXMMU_LUT440H + 0x005D1A01, // GFXMMU_LUT441L + 0x000ABFF0, // GFXMMU_LUT441H + 0x005C1B01, // GFXMMU_LUT442L + 0x000AC420, // GFXMMU_LUT442H + 0x005C1B01, // GFXMMU_LUT443L + 0x000AC840, // GFXMMU_LUT443H + 0x005B1C01, // GFXMMU_LUT444L + 0x000ACC50, // GFXMMU_LUT444H + 0x005B1C01, // GFXMMU_LUT445L + 0x000AD050, // GFXMMU_LUT445H + 0x005B1C01, // GFXMMU_LUT446L + 0x000AD450, // GFXMMU_LUT446H + 0x005A1D01, // GFXMMU_LUT447L + 0x000AD840, // GFXMMU_LUT447H + 0x005A1D01, // GFXMMU_LUT448L + 0x000ADC20, // GFXMMU_LUT448H + 0x00591E01, // GFXMMU_LUT449L + 0x000ADFF0, // GFXMMU_LUT449H + 0x00591E01, // GFXMMU_LUT450L + 0x000AE3B0, // GFXMMU_LUT450H + 0x00581F01, // GFXMMU_LUT451L + 0x000AE760, // GFXMMU_LUT451H + 0x00581F01, // GFXMMU_LUT452L + 0x000AEB00, // GFXMMU_LUT452H + 0x00572001, // GFXMMU_LUT453L + 0x000AEE90, // GFXMMU_LUT453H + 0x00572001, // GFXMMU_LUT454L + 0x000AF210, // GFXMMU_LUT454H + 0x00562101, // GFXMMU_LUT455L + 0x000AF580, // GFXMMU_LUT455H + 0x00562101, // GFXMMU_LUT456L + 0x000AF8E0, // GFXMMU_LUT456H + 0x00552201, // GFXMMU_LUT457L + 0x000AFC30, // GFXMMU_LUT457H + 0x00552201, // GFXMMU_LUT458L + 0x000AFF70, // GFXMMU_LUT458H + 0x00542301, // GFXMMU_LUT459L + 0x000B02A0, // GFXMMU_LUT459H + 0x00542301, // GFXMMU_LUT460L + 0x000B05C0, // GFXMMU_LUT460H + 0x00532401, // GFXMMU_LUT461L + 0x000B08D0, // GFXMMU_LUT461H + 0x00522501, // GFXMMU_LUT462L + 0x000B0BC0, // GFXMMU_LUT462H + 0x00522501, // GFXMMU_LUT463L + 0x000B0EA0, // GFXMMU_LUT463H + 0x00512601, // GFXMMU_LUT464L + 0x000B1170, // GFXMMU_LUT464H + 0x00502701, // GFXMMU_LUT465L + 0x000B1420, // GFXMMU_LUT465H + 0x00502701, // GFXMMU_LUT466L + 0x000B16C0, // GFXMMU_LUT466H + 0x004F2801, // GFXMMU_LUT467L + 0x000B1950, // GFXMMU_LUT467H + 0x004E2901, // GFXMMU_LUT468L + 0x000B1BC0, // GFXMMU_LUT468H + 0x004D2A01, // GFXMMU_LUT469L + 0x000B1E10, // GFXMMU_LUT469H + 0x004D2A01, // GFXMMU_LUT470L + 0x000B2050, // GFXMMU_LUT470H + 0x004C2B01, // GFXMMU_LUT471L + 0x000B2280, // GFXMMU_LUT471H + 0x004B2C01, // GFXMMU_LUT472L + 0x000B2490, // GFXMMU_LUT472H + 0x004A2D01, // GFXMMU_LUT473L + 0x000B2680, // GFXMMU_LUT473H + 0x00492E01, // GFXMMU_LUT474L + 0x000B2850, // GFXMMU_LUT474H + 0x00482F01, // GFXMMU_LUT475L + 0x000B2A00, // GFXMMU_LUT475H + 0x00463101, // GFXMMU_LUT476L + 0x000B2B80, // GFXMMU_LUT476H + 0x00453201, // GFXMMU_LUT477L + 0x000B2CD0, // GFXMMU_LUT477H + 0x00433401, // GFXMMU_LUT478L + 0x000B2DF0, // GFXMMU_LUT478H + 0x00413601, // GFXMMU_LUT479L + 0x000B2ED0 // GFXMMU_LUT479H +}; + +#ifdef __cplusplus +} +#endif +#endif /*__ gfxmmu_lut_H */ + +/** + * @} + */ + +/** + * @} + */ diff --git a/core/embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_internal.h b/core/embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_internal.h new file mode 100644 index 000000000..cf3a1e8b6 --- /dev/null +++ b/core/embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_internal.h @@ -0,0 +1,58 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#ifndef TREZOR_HAL_DISPLAY_INTERNAL_H +#define TREZOR_HAL_DISPLAY_INTERNAL_H + +#include + +// Size of the physical frame buffer in bytes +// +// It's smaller than size of the virtual frame buffer +// due to used GFXMMU settings +#define PHYSICAL_FRAME_BUFFER_SIZE 184320 + +// Pitch (in pixels) of the virtual frame buffer +#define FRAME_BUFFER_PIXELS_PER_LINE 768 + +// Physical frame buffers in internal SRAM memory +// +// Both frame buffers layes in the fixed addresses that +// are shared between bootloaders and the firmware. +extern uint32_t physical_frame_buffer_0[PHYSICAL_FRAME_BUFFER_SIZE]; +extern uint32_t physical_frame_buffer_1[PHYSICAL_FRAME_BUFFER_SIZE]; + +// The current frame buffer selector at fixed memory address +// +// The variable address is shared between bootloaders and the firmware +extern uint32_t current_frame_buffer; + +// LCD orientations +#define LCD_ORIENTATION_PORTRAIT 0U +#define LCD_ORIENTATION_LANDSCAPE 1U +#define LCD_ORIENTATION_PORTRAIT_ROT180 2U +#define LCD_ORIENTATION_LANDSCAPE_ROT180 3U + +int32_t BSP_LCD_Init(uint32_t Instance, uint32_t Orientation); +int32_t BSP_LCD_Reinit(uint32_t Instance); +int32_t BSP_LCD_SetBrightness(uint32_t Instance, uint32_t Brightness); +int32_t BSP_LCD_DisplayOn(uint32_t Instance); +int32_t BSP_LCD_SetFrameBuffer(uint32_t Instance, uint32_t fb_addr); + +#endif // TREZOR_HAL_DISPLAY_INTERNAL_H diff --git a/core/embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_ltdc_dsi.c b/core/embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_ltdc_dsi.c new file mode 100644 index 000000000..fb3e11632 --- /dev/null +++ b/core/embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_ltdc_dsi.c @@ -0,0 +1,1562 @@ +/** + ****************************************************************************** + * @file stm32u5x9j_discovery_lcd.c + * @author MCD Application Team + * @brief This file includes the driver for Liquid Crystal Display (LCD) + module + * mounted on MB1829A board (ARGB8888 color format). + @verbatim + 1. How To use this driver: + -------------------------- + - This driver is used to drive directly in command mode a LCD TFT using the + DSI interface. + The following IPs are implied : DSI Host IP block working + in conjunction to the LTDC controller. + - This driver is linked by construction to LCD. + + 2. Driver description: + ---------------------- + + Initialization steps: + o Initialize the LCD using the BSP_LCD_Init() function. You can select + display orientation with "Orientation" parameter (portrait, landscape, + portrait with 180 degrees rotation or landscape with 180 degrees + rotation). + o Call BSP_LCD_GetXSize() and BSP_LCD_GetYsize() to get respectively + width and height in pixels of LCD in the current orientation. + o Call BSP_LCD_SetBrightness() and BSP_LCD_GetBrightness() to + respectively set and get LCD brightness. + o Call BSP_LCD_SetActiveLayer() to select the current active layer. + o Call BSP_LCD_GetFormat() to get LCD pixel format supported. + + + Display on LCD: + o Call BSP_LCD_DisplayOn() and BSP_LCD_DisplayOff() to respectively + switch on and switch off the LCD display. + o First, check that frame buffer is available using + BSP_LCD_IsFrameBufferAvailable(). o When frame buffer is available, modify it + using following functions: o Call BSP_LCD_WritePixel() and BSP_LCD_ReadPixel() + to respectively write and read a pixel. o Call BSP_LCD_DrawHLine() to draw a + horizontal line. o Call BSP_LCD_DrawVLine() to draw a vertical line. o Call + BSP_LCD_DrawBitmap() to draw a bitmap. o Call BSP_LCD_FillRect() to draw a + rectangle. o Call BSP_LCD_FillRGBRect() to draw a rectangle with RGB buffer. + o Call BSP_LCD_Refresh() to refresh LCD display. + + + De-initialization steps: + o De-initialize the LCD using the BSP_LCD_DeInit() function. + + @endverbatim + ****************************************************************************** + * @attention + * + * Copyright (c) 2023 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "colors.h" +#include "stdint.h" +#include "string.h" + +#include TREZOR_BOARD +#include STM32_HAL_H + +#include "display_internal.h" + +/* Common Error codes */ +#define BSP_ERROR_NONE 0 +#define BSP_ERROR_NO_INIT -1 +#define BSP_ERROR_WRONG_PARAM -2 +#define BSP_ERROR_BUSY -3 +#define BSP_ERROR_PERIPH_FAILURE -4 +#define BSP_ERROR_COMPONENT_FAILURE -5 +#define BSP_ERROR_UNKNOWN_FAILURE -6 +#define BSP_ERROR_UNKNOWN_COMPONENT -7 +#define BSP_ERROR_BUS_FAILURE -8 +#define BSP_ERROR_CLOCK_FAILURE -9 +#define BSP_ERROR_MSP_FAILURE -10 +#define BSP_ERROR_FEATURE_NOT_SUPPORTED -11 + +#define BSP_ERROR_BUS_ACKNOWLEDGE_FAILURE (-102) +/* Button user interrupt priority */ +#define BSP_BUTTON_USER_IT_PRIORITY \ + 0x0FUL /* Default is lowest priority level */ + +/* LCD interrupt priorities */ +#define BSP_LCD_GFXMMU_IT_PRIORITY \ + 0x0FUL /* Default is lowest priority level \ + */ +#define BSP_LCD_LTDC_IT_PRIORITY 0x0FUL /* Default is lowest priority level */ +#define BSP_LCD_DSI_IT_PRIORITY 0x0FUL /* Default is lowest priority level */ + +/* HSPI RAM interrupt priority */ +#define BSP_HSPI_RAM_IT_PRIORITY 0x0FUL /* Default is lowest priority level */ +#define LCD_PIXEL_FORMAT_ARGB8888 \ + 0x00000000U /*!< ARGB8888 LTDC pixel format \ + */ +#define LCD_PIXEL_FORMAT_RGB888 0x00000001U /*!< RGB888 LTDC pixel format */ +#define LCD_PIXEL_FORMAT_RGB565 0x00000002U /*!< RGB565 LTDC pixel format */ +#define LCD_PIXEL_FORMAT_ARGB1555 \ + 0x00000003U /*!< ARGB1555 LTDC pixel format \ + */ +#define LCD_PIXEL_FORMAT_ARGB4444 \ + 0x00000004U /*!< ARGB4444 LTDC pixel format \ + */ +#define LCD_PIXEL_FORMAT_L8 0x00000005U /*!< L8 LTDC pixel format */ +#define LCD_PIXEL_FORMAT_AL44 0x00000006U /*!< AL44 LTDC pixel format */ +#define LCD_PIXEL_FORMAT_AL88 0x00000007U /*!< AL88 LTDC pixel format */ +/* LCD instances */ +#define LCD_INSTANCES_NBR 1U + +#define DSI_POWERON_GPIO_PORT GPIOI +#define DSI_POWERON_GPIO_PIN GPIO_PIN_5 +#define DSI_POWERON_GPIO_CLOCK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE() + +#define DSI_RESET_GPIO_PORT GPIOD +#define DSI_RESET_GPIO_PIN GPIO_PIN_5 +#define DSI_RESET_GPIO_CLOCK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE() + +#define VSYNC 1 +#define VBP 12 +#define VFP 50 +#define VACT 481 +#define HSYNC 2 +#define HBP 1 +#define HFP 1 +#define HACT 480 +#define LCD_WIDTH 480 +#define LCD_HEIGHT 480 + +#include "display_gfxmmu_lut.h" + +/** @addtogroup BSP + * @{ + */ + +/** @addtogroup STM32U5x9J_DISCOVERY + * @{ + */ + +/** @defgroup STM32U5x9J_DISCOVERY_LCD LCD + * @{ + */ + +/** @defgroup STM32U5x9J_DISCOVERY_LCD_Private_Defines LCD Private Constants + * @{ + */ + +/** + * @} + */ + +/** @defgroup STM32U5x9J_DISCOVERY_LCD_Private_Variables LCD Private Variables + * @{ + */ + +#if (USE_HAL_GFXMMU_REGISTER_CALLBACKS == 1) +static uint32_t LcdGfxmmu_IsMspCbValid[LCD_INSTANCES_NBR] = {0}; +#endif /* (USE_HAL_GFXMMU_REGISTER_CALLBACKS == 1) */ + +#if (USE_HAL_LTDC_REGISTER_CALLBACKS == 1) +static uint32_t LcdLtdc_IsMspCbValid[LCD_INSTANCES_NBR] = {0}; +#endif /* (USE_HAL_LTDC_REGISTER_CALLBACKS == 1) */ + +#if (USE_HAL_DSI_REGISTER_CALLBACKS == 1) +static uint32_t LcdDsi_IsMspCbValid[LCD_INSTANCES_NBR] = {0}; +#endif /* (USE_HAL_DSI_REGISTER_CALLBACKS == 1) */ + +GFXMMU_HandleTypeDef hlcd_gfxmmu = {0}; +LTDC_HandleTypeDef hlcd_ltdc = {0}; +DSI_HandleTypeDef hlcd_dsi = {0}; +static DSI_VidCfgTypeDef DSIVidCfg = {0}; + +/** + * @} + */ + +/** @defgroup STM32U5x9J_DISCOVERY_LCD_Private_FunctionPrototypes LCD Private + * Function Prototypes + * @{ + */ +static int32_t LCD_Init(void); +static int32_t LCD_DeInit(void); + +static void GFXMMU_MspInit(GFXMMU_HandleTypeDef *hgfxmmu); +static void GFXMMU_MspDeInit(GFXMMU_HandleTypeDef *hgfxmmu); +static void LTDC_MspInit(LTDC_HandleTypeDef *hltdc); +static void LTDC_MspDeInit(LTDC_HandleTypeDef *hltdc); +static void DSI_MspInit(DSI_HandleTypeDef *hdsi); +static void DSI_MspDeInit(DSI_HandleTypeDef *hdsi); +#if (USE_HAL_DSI_REGISTER_CALLBACKS == 1) +static void DSI_EndOfRefreshCallback(DSI_HandleTypeDef *hdsi); +#endif /* (USE_HAL_DSI_REGISTER_CALLBACKS == 1) */ +/** + * @} + */ + +/** @addtogroup STM32U5x9J_DISCOVERY_LCD_Exported_Functions + * @{ + */ +/** + * @brief Initialize the LCD. + * @param Instance LCD Instance. + * @param Orientation LCD_ORIENTATION_PORTRAIT, LCD_ORIENTATION_LANDSCAPE, + * LCD_ORIENTATION_PORTRAIT_ROT180 or + * LCD_ORIENTATION_LANDSCAPE_ROT180. + * @retval BSP status. + */ +int32_t BSP_LCD_Init(uint32_t Instance, uint32_t Orientation) { + int32_t status = BSP_ERROR_NONE; + + if ((Instance >= LCD_INSTANCES_NBR) || + (Orientation > LCD_ORIENTATION_LANDSCAPE_ROT180)) { + status = BSP_ERROR_WRONG_PARAM; + } else if ((Orientation == LCD_ORIENTATION_LANDSCAPE) || + (Orientation == LCD_ORIENTATION_LANDSCAPE_ROT180)) { + status = BSP_ERROR_FEATURE_NOT_SUPPORTED; + } else { + if (LCD_Init() != 0) { + status = BSP_ERROR_PERIPH_FAILURE; + } + } + + return status; +} + +/** + * @brief De-Initialize the LCD. + * @param Instance LCD Instance. + * @retval BSP status. + */ +int32_t BSP_LCD_DeInit(uint32_t Instance) { + int32_t status = BSP_ERROR_NONE; + + if (Instance >= LCD_INSTANCES_NBR) { + status = BSP_ERROR_WRONG_PARAM; + } else { + if (LCD_DeInit() != 0) { + status = BSP_ERROR_PERIPH_FAILURE; + } + } + + return status; +} + +/** + * @brief Set the display on. + * @param Instance LCD Instance. + * @retval BSP status. + */ +int32_t BSP_LCD_DisplayOn(uint32_t Instance) { + int32_t status = BSP_ERROR_NONE; + + if (Instance >= LCD_INSTANCES_NBR) { + status = BSP_ERROR_WRONG_PARAM; + } else { + /* Set the display on */ + if (HAL_DSI_ShortWrite(&hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, + DSI_SET_DISPLAY_ON, 0x00) != HAL_OK) { + status = BSP_ERROR_WRONG_PARAM; + } + } + + return status; +} + +/** + * @brief Set the display off. + * @param Instance LCD Instance. + * @retval BSP status. + */ +int32_t BSP_LCD_DisplayOff(uint32_t Instance) { + int32_t status = BSP_ERROR_NONE; + + if (Instance >= LCD_INSTANCES_NBR) { + status = BSP_ERROR_WRONG_PARAM; + } else { + /* Set the display off */ + if (HAL_DSI_ShortWrite(&hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, + DSI_SET_DISPLAY_OFF, 0x00) != HAL_OK) { + status = BSP_ERROR_WRONG_PARAM; + } + } + + return status; +} + +/** + * @brief Set the display brightness. + * @param Instance LCD Instance. + * @param Brightness [00: Min (black), 100 Max]. + * @retval BSP status. + */ +int32_t BSP_LCD_SetBrightness(uint32_t Instance, uint32_t Brightness) { + int32_t status; + + if ((Instance >= LCD_INSTANCES_NBR) || (Brightness > 100U)) { + status = BSP_ERROR_WRONG_PARAM; + } else { + status = BSP_ERROR_FEATURE_NOT_SUPPORTED; + } + + return status; +} + +/** + * @brief Get the display brightness. + * @param Instance LCD Instance. + * @param Brightness [00: Min (black), 100 Max]. + * @retval BSP status. + */ +int32_t BSP_LCD_GetBrightness(uint32_t Instance, uint32_t *Brightness) { + int32_t status; + + if ((Instance >= LCD_INSTANCES_NBR) || (Brightness == NULL)) { + status = BSP_ERROR_WRONG_PARAM; + } else { + status = BSP_ERROR_FEATURE_NOT_SUPPORTED; + } + + return status; +} + +/** + * @brief Get the LCD X size. + * @param Instance LCD Instance. + * @param Xsize LCD X size. + * @retval BSP status. + */ +int32_t BSP_LCD_GetXSize(uint32_t Instance, uint32_t *Xsize) { + int32_t status = BSP_ERROR_NONE; + + if ((Instance >= LCD_INSTANCES_NBR) || (Xsize == NULL)) { + status = BSP_ERROR_WRONG_PARAM; + } else { + /* Get the display Xsize */ + *Xsize = LCD_WIDTH; + } + + return status; +} + +/** + * @brief Get the LCD Y size. + * @param Instance LCD Instance. + * @param Ysize LCD Y size. + * @retval BSP status. + */ +int32_t BSP_LCD_GetYSize(uint32_t Instance, uint32_t *Ysize) { + int32_t status = BSP_ERROR_NONE; + + if ((Instance >= LCD_INSTANCES_NBR) || (Ysize == NULL)) { + status = BSP_ERROR_WRONG_PARAM; + } else { + /* Get the display Ysize */ + *Ysize = LCD_HEIGHT; + } + + return status; +} + +/** + * @brief Set the LCD active layer. + * @param Instance LCD Instance. + * @param LayerIndex Active layer index. + * @retval BSP status. + */ +int32_t BSP_LCD_SetActiveLayer(uint32_t Instance, uint32_t LayerIndex) { + int32_t status = BSP_ERROR_NONE; + + if (Instance >= LCD_INSTANCES_NBR) { + status = BSP_ERROR_WRONG_PARAM; + } else { + /* Nothing to do */ + UNUSED(LayerIndex); + } + + return status; +} + +/** + * @brief Get pixel format supported by LCD. + * @param Instance LCD Instance. + * @param Format Pointer on pixel format. + * @retval BSP status. + */ +int32_t BSP_LCD_GetFormat(uint32_t Instance, uint32_t *Format) { + int32_t status = BSP_ERROR_NONE; + + if (Instance >= LCD_INSTANCES_NBR) { + status = BSP_ERROR_WRONG_PARAM; + } else { + /* Get pixel format supported by LCD */ + *Format = LCD_PIXEL_FORMAT_ARGB8888; + } + + return status; +} + +void MX_GFXMMU_Reinit(GFXMMU_HandleTypeDef *hgfxmmu) { + /* Initialize GFXMMU */ + hgfxmmu->Instance = GFXMMU; + hgfxmmu->Init.BlocksPerLine = GFXMMU_192BLOCKS; + hgfxmmu->Init.DefaultValue = 0xFFFFFFFFU; + hgfxmmu->Init.Buffers.Buf0Address = (uint32_t)physical_frame_buffer_0; + hgfxmmu->Init.Buffers.Buf1Address = (uint32_t)physical_frame_buffer_1; + hgfxmmu->Init.Buffers.Buf2Address = 0; + hgfxmmu->Init.Buffers.Buf3Address = 0; +#if defined(GFXMMU_CR_CE) + hgfxmmu->Init.CachePrefetch.Activation = DISABLE; + hgfxmmu->Init.CachePrefetch.CacheLock = GFXMMU_CACHE_LOCK_DISABLE; + hgfxmmu->Init.CachePrefetch.CacheLockBuffer = + GFXMMU_CACHE_LOCK_BUFFER0; /* NU */ + hgfxmmu->Init.CachePrefetch.CacheForce = GFXMMU_CACHE_FORCE_ENABLE; /* NU */ + hgfxmmu->Init.CachePrefetch.OutterBufferability = + GFXMMU_OUTTER_BUFFERABILITY_DISABLE; + hgfxmmu->Init.CachePrefetch.OutterCachability = + GFXMMU_OUTTER_CACHABILITY_DISABLE; + hgfxmmu->Init.CachePrefetch.Prefetch = GFXMMU_PREFETCH_DISABLE; +#endif /* GFXMMU_CR_CE */ +#if defined(GFXMMU_CR_ACE) + hgfxmmu->Init.AddressCache.Activation = DISABLE; + hgfxmmu->Init.AddressCache.AddressCacheLockBuffer = + GFXMMU_ADDRESSCACHE_LOCK_BUFFER0; +#endif /* GFXMMU_CR_ACE */ + hgfxmmu->Init.Interrupts.Activation = DISABLE; + hgfxmmu->Init.Interrupts.UsedInterrupts = GFXMMU_AHB_MASTER_ERROR_IT; /* NU */ +} + +/** + * @brief MX GFXMMU initialization. + * @param hgfxmmu GFXMMU handle. + * @retval HAL status. + */ +__weak HAL_StatusTypeDef MX_GFXMMU_Init(GFXMMU_HandleTypeDef *hgfxmmu) { + MX_GFXMMU_Reinit(hgfxmmu); + return HAL_GFXMMU_Init(hgfxmmu); +} + +/** + * @brief MX LTDC clock configuration. + * @param hltdc LTDC handle. + * @retval HAL status. + */ +__weak HAL_StatusTypeDef MX_LTDC_ClockConfig(LTDC_HandleTypeDef *hltdc) { + RCC_PeriphCLKInitTypeDef PLL3InitPeriph = {0}; + + /* Prevent unused argument(s) compilation warning */ + UNUSED(hltdc); + + /* Start and configurre PLL3 */ + /* HSE = 16MHZ */ + /* 16/(M=4) = 4MHz input (min) */ + /* 4*(N=125) = 500MHz VCO (almost max) */ + /* 500/(P=8) = 62.5 for DSI ie exactly the lane byte clock*/ + /* 500/(R=24) = 20.83 for LTDC exact match with DSI bandwidth */ + PLL3InitPeriph.PeriphClockSelection = RCC_PERIPHCLK_LTDC; + PLL3InitPeriph.LtdcClockSelection = RCC_LTDCCLKSOURCE_PLL3; + PLL3InitPeriph.PLL3.PLL3M = 4; + PLL3InitPeriph.PLL3.PLL3N = 125; + PLL3InitPeriph.PLL3.PLL3P = 8; + PLL3InitPeriph.PLL3.PLL3Q = 8; + PLL3InitPeriph.PLL3.PLL3R = 24; + PLL3InitPeriph.PLL3.PLL3FRACN = 0; + PLL3InitPeriph.PLL3.PLL3RGE = RCC_PLLVCIRANGE_1; + PLL3InitPeriph.PLL3.PLL3ClockOut = RCC_PLL3_DIVR | RCC_PLL3_DIVP; + PLL3InitPeriph.PLL3.PLL3Source = RCC_PLLSOURCE_HSE; + return HAL_RCCEx_PeriphCLKConfig(&PLL3InitPeriph); +} + +void MX_LTDC_Reinit(LTDC_HandleTypeDef *hltdc) { + /* LTDC initialization */ + hltdc->Instance = LTDC; + hltdc->Init.HSPolarity = LTDC_HSPOLARITY_AL; + hltdc->Init.VSPolarity = LTDC_VSPOLARITY_AL; + hltdc->Init.DEPolarity = LTDC_DEPOLARITY_AL; + hltdc->Init.PCPolarity = LTDC_PCPOLARITY_IPC; + hltdc->Init.HorizontalSync = HSYNC - 1; + hltdc->Init.AccumulatedHBP = HSYNC + HBP - 1; + hltdc->Init.AccumulatedActiveW = HACT + HBP + HSYNC - 1; + hltdc->Init.TotalWidth = HACT + HBP + HFP + HSYNC - 1; + hltdc->Init.Backcolor.Red = 0; /* Not used default value */ + hltdc->Init.Backcolor.Green = 0; /* Not used default value */ + hltdc->Init.Backcolor.Blue = 0; /* Not used default value */ + hltdc->Init.Backcolor.Reserved = 0xFF; + + HAL_LTDCEx_StructInitFromVideoConfig(&hlcd_ltdc, &DSIVidCfg); +} + +/** + * @brief MX LTDC initialization. + * @param hltdc LTDC handle. + * @retval HAL status. + */ +__weak HAL_StatusTypeDef MX_LTDC_Init(LTDC_HandleTypeDef *hltdc) { + MX_LTDC_Reinit(hltdc); + + return HAL_LTDC_Init(hltdc); +} + +// HAL_StatusTypeDef MX_LTDC_ReConfigLayer(LTDC_HandleTypeDef *hltdc, uint32_t +// LayerIndex) +//{ +// LTDC_LayerCfgTypeDef LayerCfg = {0}; +// +///* LTDC layer configuration */ +// LayerCfg.WindowX0 = 0; +// LayerCfg.WindowX1 = LCD_WIDTH; +// LayerCfg.WindowY0 = 1; +// LayerCfg.WindowY1 = (uint32_t)LCD_HEIGHT + 1UL; +// LayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_ARGB8888; +// LayerCfg.Alpha = 0xFF; /* NU default value */ +// LayerCfg.Alpha0 = 0; /* NU default value */ +// LayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_PAxCA; /* Not Used: default +// value */ LayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_PAxCA; /* Not +// Used: default value */ LayerCfg.FBStartAdress = +// GFXMMU_VIRTUAL_BUFFER0_BASE; LayerCfg.ImageWidth = +// FRAME_BUFFER_PIXELS_PER_LINE; /* Number of pixels per line in virtual frame +// buffer */ LayerCfg.ImageHeight = LCD_HEIGHT; LayerCfg.Backcolor.Red = 0; +// /* Not Used: default value */ LayerCfg.Backcolor.Green = 0; /* Not Used: +// default value */ LayerCfg.Backcolor.Blue = 0; /* Not Used: default value */ +// LayerCfg.Bac +// return HAL_LTDC_ConfigLayer(hltdc, &LayerCfg, LayerIndex);kcolor.Reserved = +// 0xFF; +//} + +/** + * @brief MX LTDC layer configuration. + * @param hltdc LTDC handle. + * @param LayerIndex LTDC layer index. + * @retval HAL status. + */ +__weak HAL_StatusTypeDef MX_LTDC_ConfigLayer(LTDC_HandleTypeDef *hltdc, + uint32_t LayerIndex, + uint32_t fb_addr) { + LTDC_LayerCfgTypeDef LayerCfg = {0}; + + /* LTDC layer configuration */ + LayerCfg.WindowX0 = 0; + LayerCfg.WindowX1 = LCD_WIDTH; + LayerCfg.WindowY0 = 1; + LayerCfg.WindowY1 = (uint32_t)LCD_HEIGHT + 1UL; + LayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_ARGB8888; + LayerCfg.Alpha = 0xFF; /* NU default value */ + LayerCfg.Alpha0 = 0; /* NU default value */ + LayerCfg.BlendingFactor1 = + LTDC_BLENDING_FACTOR1_PAxCA; /* Not Used: default value */ + LayerCfg.BlendingFactor2 = + LTDC_BLENDING_FACTOR2_PAxCA; /* Not Used: default value */ + LayerCfg.FBStartAdress = fb_addr; + LayerCfg.ImageWidth = + FRAME_BUFFER_PIXELS_PER_LINE; /* Number of pixels per line in virtual + frame buffer */ + LayerCfg.ImageHeight = LCD_HEIGHT; + LayerCfg.Backcolor.Red = 0; /* Not Used: default value */ + LayerCfg.Backcolor.Green = 0; /* Not Used: default value */ + LayerCfg.Backcolor.Blue = 0; /* Not Used: default value */ + LayerCfg.Backcolor.Reserved = 0xFF; + return HAL_LTDC_ConfigLayer(hltdc, &LayerCfg, LayerIndex); +} + +/** + * @brief MX DSI initialization. + * @param hdsi DSI handle. + * @retval HAL status. + */ +HAL_StatusTypeDef MX_DSI_Reinit(DSI_HandleTypeDef *hdsi) { + /* DSI initialization */ + hdsi->Instance = DSI; + hdsi->Init.AutomaticClockLaneControl = DSI_AUTO_CLK_LANE_CTRL_DISABLE; + /* We have 1 data lane at 500Mbps => lane byte clock at 500/8 = 62,5 MHZ */ + /* We want TX escape clock at around 20MHz and under 20MHz so clock division + * is set to 4 */ + hdsi->Init.TXEscapeCkdiv = 4; + hdsi->Init.NumberOfLanes = DSI_TWO_DATA_LANES; + hdsi->Init.PHYFrequencyRange = DSI_DPHY_FRANGE_450MHZ_510MHZ; + hdsi->Init.PHYLowPowerOffset = 0; + + /* Configure the DSI for Video mode */ + DSIVidCfg.VirtualChannelID = 0; + DSIVidCfg.HSPolarity = DSI_HSYNC_ACTIVE_HIGH; + DSIVidCfg.VSPolarity = DSI_VSYNC_ACTIVE_HIGH; + DSIVidCfg.DEPolarity = DSI_DATA_ENABLE_ACTIVE_HIGH; + DSIVidCfg.ColorCoding = DSI_RGB888; + DSIVidCfg.Mode = DSI_VID_MODE_BURST; + DSIVidCfg.PacketSize = LCD_WIDTH; + DSIVidCfg.NullPacketSize = 0xFFFU; + DSIVidCfg.HorizontalSyncActive = HSYNC * 3; + DSIVidCfg.HorizontalBackPorch = HBP * 3; + DSIVidCfg.HorizontalLine = (HACT + HSYNC + HBP + HFP) * 3; + DSIVidCfg.VerticalSyncActive = VSYNC; + DSIVidCfg.VerticalBackPorch = VBP; + DSIVidCfg.VerticalFrontPorch = VFP; + DSIVidCfg.VerticalActive = VACT; + DSIVidCfg.LPCommandEnable = DSI_LP_COMMAND_ENABLE; + DSIVidCfg.LPLargestPacketSize = 64; + /* Specify for each region of the video frame, if the transmission of command + * in LP mode is allowed in this region */ + /* while streaming is active in video mode */ + DSIVidCfg.LPHorizontalFrontPorchEnable = DSI_LP_HFP_ENABLE; + DSIVidCfg.LPHorizontalBackPorchEnable = DSI_LP_HBP_ENABLE; + DSIVidCfg.LPVerticalActiveEnable = DSI_LP_VACT_ENABLE; + DSIVidCfg.LPVerticalFrontPorchEnable = DSI_LP_VFP_ENABLE; + DSIVidCfg.LPVerticalBackPorchEnable = DSI_LP_VBP_ENABLE; + DSIVidCfg.LPVerticalSyncActiveEnable = DSI_LP_VSYNC_ENABLE; + DSIVidCfg.FrameBTAAcknowledgeEnable = DSI_FBTAA_ENABLE; + DSIVidCfg.LooselyPacked = DSI_LOOSELY_PACKED_DISABLE; + + return HAL_OK; +} + +/** + * @brief MX DSI initialization. + * @param hdsi DSI handle. + * @retval HAL status. + */ +__weak HAL_StatusTypeDef MX_DSI_Init(DSI_HandleTypeDef *hdsi) { + DSI_PLLInitTypeDef PLLInit = {0}; + + /* DSI initialization */ + hdsi->Instance = DSI; + hdsi->Init.AutomaticClockLaneControl = DSI_AUTO_CLK_LANE_CTRL_DISABLE; + /* We have 1 data lane at 500Mbps => lane byte clock at 500/8 = 62,5 MHZ */ + /* We want TX escape clock at around 20MHz and under 20MHz so clock division + * is set to 4 */ + hdsi->Init.TXEscapeCkdiv = 4; + hdsi->Init.NumberOfLanes = DSI_TWO_DATA_LANES; + hdsi->Init.PHYFrequencyRange = DSI_DPHY_FRANGE_450MHZ_510MHZ; + hdsi->Init.PHYLowPowerOffset = 0; + + PLLInit.PLLNDIV = 125; + PLLInit.PLLIDF = 4; + PLLInit.PLLODF = 2; + PLLInit.PLLVCORange = DSI_DPHY_VCO_FRANGE_800MHZ_1GHZ; + PLLInit.PLLChargePump = DSI_PLL_CHARGE_PUMP_2000HZ_4400HZ; + PLLInit.PLLTuning = DSI_PLL_LOOP_FILTER_2000HZ_4400HZ; + + if (HAL_DSI_Init(hdsi, &PLLInit) != HAL_OK) { + return HAL_ERROR; + } + + if (HAL_DSI_SetGenericVCID(&hlcd_dsi, 0) != HAL_OK) { + return HAL_ERROR; + } + + /* Configure the DSI for Video mode */ + DSIVidCfg.VirtualChannelID = 0; + DSIVidCfg.HSPolarity = DSI_HSYNC_ACTIVE_HIGH; + DSIVidCfg.VSPolarity = DSI_VSYNC_ACTIVE_HIGH; + DSIVidCfg.DEPolarity = DSI_DATA_ENABLE_ACTIVE_HIGH; + DSIVidCfg.ColorCoding = DSI_RGB888; + DSIVidCfg.Mode = DSI_VID_MODE_BURST; + DSIVidCfg.PacketSize = LCD_WIDTH; + DSIVidCfg.NullPacketSize = 0xFFFU; + DSIVidCfg.HorizontalSyncActive = HSYNC * 3; + DSIVidCfg.HorizontalBackPorch = HBP * 3; + DSIVidCfg.HorizontalLine = (HACT + HSYNC + HBP + HFP) * 3; + DSIVidCfg.VerticalSyncActive = VSYNC; + DSIVidCfg.VerticalBackPorch = VBP; + DSIVidCfg.VerticalFrontPorch = VFP; + DSIVidCfg.VerticalActive = VACT; + DSIVidCfg.LPCommandEnable = DSI_LP_COMMAND_ENABLE; + DSIVidCfg.LPLargestPacketSize = 64; + /* Specify for each region of the video frame, if the transmission of command + * in LP mode is allowed in this region */ + /* while streaming is active in video mode */ + DSIVidCfg.LPHorizontalFrontPorchEnable = DSI_LP_HFP_ENABLE; + DSIVidCfg.LPHorizontalBackPorchEnable = DSI_LP_HBP_ENABLE; + DSIVidCfg.LPVerticalActiveEnable = DSI_LP_VACT_ENABLE; + DSIVidCfg.LPVerticalFrontPorchEnable = DSI_LP_VFP_ENABLE; + DSIVidCfg.LPVerticalBackPorchEnable = DSI_LP_VBP_ENABLE; + DSIVidCfg.LPVerticalSyncActiveEnable = DSI_LP_VSYNC_ENABLE; + DSIVidCfg.FrameBTAAcknowledgeEnable = DSI_FBTAA_ENABLE; + DSIVidCfg.LooselyPacked = DSI_LOOSELY_PACKED_DISABLE; + + /* Drive the display */ + if (HAL_DSI_ConfigVideoMode(&hlcd_dsi, &DSIVidCfg) != HAL_OK) { + return HAL_ERROR; + } + + return HAL_OK; +} + +/** + * @brief MX DMA2D initialization. + * @param hdma2d DMA2D handle. + * @param Mode DMA2D transfer mode. + * @param OffLine DMA2D output offset. + * @retval HAL status. + */ +__weak HAL_StatusTypeDef MX_DMA2D_Init(DMA2D_HandleTypeDef *hdma2d, + uint32_t Mode, uint32_t OffLine) { + /* Register to memory mode with ARGB8888 as color Mode */ + hdma2d->Instance = DMA2D; + hdma2d->Init.Mode = Mode; + hdma2d->Init.ColorMode = DMA2D_OUTPUT_ARGB8888; + hdma2d->Init.OutputOffset = OffLine; + hdma2d->Init.AlphaInverted = DMA2D_REGULAR_ALPHA; + hdma2d->Init.RedBlueSwap = DMA2D_RB_REGULAR; + hdma2d->Init.BytesSwap = DMA2D_BYTES_REGULAR; + hdma2d->Init.LineOffsetMode = DMA2D_LOM_PIXELS; + + /* DMA2D Initialization */ + return HAL_DMA2D_Init(hdma2d); +} + +#if (USE_HAL_GFXMMU_REGISTER_CALLBACKS == 1) +/** + * @brief Register Default LCD GFXMMU Msp Callbacks + * @retval BSP status + */ +int32_t BSP_LCD_GFXMMU_RegisterDefaultMspCallbacks(uint32_t Instance) { + int32_t status = BSP_ERROR_NONE; + + if (Instance >= LCD_INSTANCES_NBR) { + status = BSP_ERROR_WRONG_PARAM; + } else { + __HAL_GFXMMU_RESET_HANDLE_STATE(&hlcd_gfxmmu); + + /* Register default MspInit/MspDeInit Callback */ + if (HAL_GFXMMU_RegisterCallback(&hlcd_gfxmmu, HAL_GFXMMU_MSPINIT_CB_ID, + GFXMMU_MspInit) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } else if (HAL_GFXMMU_RegisterCallback(&hlcd_gfxmmu, + HAL_GFXMMU_MSPDEINIT_CB_ID, + GFXMMU_MspDeInit) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } else { + LcdGfxmmu_IsMspCbValid[Instance] = 1U; + } + } + + /* BSP status */ + return status; +} + +/** + * @brief Register LCD GFXMMU Msp Callback + * @param Callbacks pointer to LCD MspInit/MspDeInit callback functions + * @retval BSP status + */ +int32_t BSP_LCD_GFXMMU_RegisterMspCallbacks(uint32_t Instance, + BSP_LCD_GFXMMU_Cb_t *Callback) { + int32_t status = BSP_ERROR_NONE; + + if (Instance >= LCD_INSTANCES_NBR) { + status = BSP_ERROR_WRONG_PARAM; + } else { + __HAL_GFXMMU_RESET_HANDLE_STATE(&hlcd_gfxmmu); + + /* Register MspInit/MspDeInit Callbacks */ + if (HAL_GFXMMU_RegisterCallback(&hlcd_gfxmmu, HAL_GFXMMU_MSPINIT_CB_ID, + Callback->pMspGfxmmuInitCb) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } else if (HAL_GFXMMU_RegisterCallback( + &hlcd_gfxmmu, HAL_GFXMMU_MSPDEINIT_CB_ID, + Callback->pMspGfxmmuDeInitCb) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } else { + LcdGfxmmu_IsMspCbValid[Instance] = 1U; + } + } + + /* BSP status */ + return status; +} +#endif /* USE_HAL_GFXMMU_REGISTER_CALLBACKS */ + +#if (USE_HAL_LTDC_REGISTER_CALLBACKS == 1) +/** + * @brief Register Default LCD LTDC Msp Callbacks + * @retval BSP status + */ +int32_t BSP_LCD_LTDC_RegisterDefaultMspCallbacks(uint32_t Instance) { + int32_t status = BSP_ERROR_NONE; + + if (Instance >= LCD_INSTANCES_NBR) { + status = BSP_ERROR_WRONG_PARAM; + } else { + __HAL_LTDC_RESET_HANDLE_STATE(&hlcd_ltdc); + + /* Register default MspInit/MspDeInit Callback */ + if (HAL_LTDC_RegisterCallback(&hlcd_ltdc, HAL_LTDC_MSPINIT_CB_ID, + LTDC_MspInit) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } else if (HAL_LTDC_RegisterCallback(&hlcd_ltdc, HAL_LTDC_MSPDEINIT_CB_ID, + LTDC_MspDeInit) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } else { + LcdLtdc_IsMspCbValid[Instance] = 1U; + } + } + + /* BSP status */ + return status; +} + +/** + * @brief Register LCD LTDC Msp Callback + * @param Callbacks pointer to LCD MspInit/MspDeInit callback functions + * @retval BSP status + */ +int32_t BSP_LCD_LTDC_RegisterMspCallbacks(uint32_t Instance, + BSP_LCD_LTDC_Cb_t *Callback) { + int32_t status = BSP_ERROR_NONE; + + if (Instance >= LCD_INSTANCES_NBR) { + status = BSP_ERROR_WRONG_PARAM; + } else { + __HAL_LTDC_RESET_HANDLE_STATE(&hlcd_ltdc); + + /* Register MspInit/MspDeInit Callbacks */ + if (HAL_LTDC_RegisterCallback(&hlcd_ltdc, HAL_LTDC_MSPINIT_CB_ID, + Callback->pMspLtdcInitCb) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } else if (HAL_LTDC_RegisterCallback(&hlcd_ltdc, HAL_LTDC_MSPDEINIT_CB_ID, + Callback->pMspLtdcDeInitCb) != + HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } else { + LcdLtdc_IsMspCbValid[Instance] = 1U; + } + } + + /* BSP status */ + return status; +} +#endif /* USE_HAL_LTDC_REGISTER_CALLBACKS */ + +#if (USE_HAL_DSI_REGISTER_CALLBACKS == 1) +/** + * @brief Register Default LCD DSI Msp Callbacks + * @retval BSP status + */ +int32_t BSP_LCD_DSI_RegisterDefaultMspCallbacks(uint32_t Instance) { + int32_t status = BSP_ERROR_NONE; + + if (Instance >= LCD_INSTANCES_NBR) { + status = BSP_ERROR_WRONG_PARAM; + } else { + __HAL_DSI_RESET_HANDLE_STATE(&hlcd_dsi); + + /* Register default MspInit/MspDeInit Callback */ + if (HAL_DSI_RegisterCallback(&hlcd_dsi, HAL_DSI_MSPINIT_CB_ID, + DSI_MspInit) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } else if (HAL_DSI_RegisterCallback(&hlcd_dsi, HAL_DSI_MSPDEINIT_CB_ID, + DSI_MspDeInit) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } else { + LcdDsi_IsMspCbValid[Instance] = 1U; + } + } + + /* BSP status */ + return status; +} + +/** + * @brief Register LCD DSI Msp Callback + * @param Callbacks pointer to LCD MspInit/MspDeInit callback functions + * @retval BSP status + */ +int32_t BSP_LCD_DSI_RegisterMspCallbacks(uint32_t Instance, + BSP_LCD_DSI_Cb_t *Callback) { + int32_t status = BSP_ERROR_NONE; + + if (Instance >= LCD_INSTANCES_NBR) { + status = BSP_ERROR_WRONG_PARAM; + } else { + __HAL_DSI_RESET_HANDLE_STATE(&hlcd_dsi); + + /* Register MspInit/MspDeInit Callbacks */ + if (HAL_DSI_RegisterCallback(&hlcd_dsi, HAL_DSI_MSPINIT_CB_ID, + Callback->pMspDsiInitCb) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } else if (HAL_DSI_RegisterCallback(&hlcd_dsi, HAL_DSI_MSPDEINIT_CB_ID, + Callback->pMspDsiDeInitCb) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } else { + LcdDsi_IsMspCbValid[Instance] = 1U; + } + } + + /* BSP status */ + return status; +} +#endif /* USE_HAL_DSI_REGISTER_CALLBACKS */ + +#if (USE_HAL_DMA2D_REGISTER_CALLBACKS == 1) +/** + * @brief Register Default LCD DMA2D Msp Callbacks + * @retval BSP status + */ +int32_t BSP_LCD_DMA2D_RegisterDefaultMspCallbacks(uint32_t Instance) { + int32_t status = BSP_ERROR_NONE; + + if (Instance >= LCD_INSTANCES_NBR) { + status = BSP_ERROR_WRONG_PARAM; + } else { + __HAL_DMA2D_RESET_HANDLE_STATE(&hlcd_dma2d); + + /* Register default MspInit/MspDeInit Callback */ + if (HAL_DMA2D_RegisterCallback(&hlcd_dma2d, HAL_DMA2D_MSPINIT_CB_ID, + DMA2D_MspInit) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } else if (HAL_DMA2D_RegisterCallback(&hlcd_dma2d, + HAL_DMA2D_MSPDEINIT_CB_ID, + DMA2D_MspDeInit) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } else { + LcdDma2d_IsMspCbValid[Instance] = 1U; + } + } + + /* BSP status */ + return status; +} + +/** + * @brief Register LCD DMA2D Msp Callback + * @param Callbacks pointer to LCD MspInit/MspDeInit callback functions + * @retval BSP status + */ +int32_t BSP_LCD_DMA2D_RegisterMspCallbacks(uint32_t Instance, + BSP_LCD_DMA2D_Cb_t *Callback) { + int32_t status = BSP_ERROR_NONE; + + if (Instance >= LCD_INSTANCES_NBR) { + status = BSP_ERROR_WRONG_PARAM; + } else { + __HAL_DMA2D_RESET_HANDLE_STATE(&hlcd_dma2d); + + /* Register MspInit/MspDeInit Callbacks */ + if (HAL_DMA2D_RegisterCallback(&hlcd_dma2d, HAL_DMA2D_MSPINIT_CB_ID, + Callback->pMspDma2dInitCb) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } else if (HAL_DMA2D_RegisterCallback( + &hlcd_dma2d, HAL_DMA2D_MSPDEINIT_CB_ID, + Callback->pMspDma2dDeInitCb) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } else { + LcdDma2d_IsMspCbValid[Instance] = 1U; + } + } + + /* BSP status */ + return status; +} +#endif /* USE_HAL_DMA2D_REGISTER_CALLBACKS */ +/** + * @} + */ + +/** @defgroup STM32U5x9J_DISCOVERY_LCD_Private_Functions LCD Private Functions + * @{ + */ + +/** + * @brief Initialize LCD. + * @retval BSP status. + */ +static int32_t LCD_Init(void) { + int32_t status = BSP_ERROR_NONE; + uint32_t ErrorNumber = 0; + DSI_PHY_TimerTypeDef PhyTimers = {0}; + DSI_HOST_TimeoutTypeDef HostTimeouts = {0}; + + /***************/ + /* GFXMMU init */ + /***************/ +#if (USE_HAL_GFXMMU_REGISTER_CALLBACKS == 0) + GFXMMU_MspInit(&hlcd_gfxmmu); +#else + /* Register the GFXMMU MSP Callbacks */ + if (LcdGfxmmu_IsMspCbValid[0] == 0U) { + if (BSP_LCD_GFXMMU_RegisterDefaultMspCallbacks(0) != BSP_ERROR_NONE) { + status = BSP_ERROR_PERIPH_FAILURE; + } + } +#endif /* (USE_HAL_GFXMMU_REGISTER_CALLBACKS == 0) */ + + if (status == BSP_ERROR_NONE) { + /* GFXMMU peripheral initialization */ + if (MX_GFXMMU_Init(&hlcd_gfxmmu) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } + /* Initialize LUT */ + else if (HAL_GFXMMU_ConfigLut(&hlcd_gfxmmu, 0, LCD_WIDTH, + (uint32_t)&gfxmmu_lut) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } else { + /* Disable non visible lines : from line 480 to 1023 */ + if (HAL_OK != HAL_GFXMMU_DisableLutLines(&hlcd_gfxmmu, LCD_WIDTH, 544)) { + status = BSP_ERROR_PERIPH_FAILURE; + } + } + } + + /************/ + /* DSI init */ + /************/ + if (status == BSP_ERROR_NONE) { +#if (USE_HAL_DSI_REGISTER_CALLBACKS == 0) + DSI_MspInit(&hlcd_dsi); +#else + /* Register the DSI MSP Callbacks */ + if (LcdDsi_IsMspCbValid[0] == 0U) { + if (BSP_LCD_DSI_RegisterDefaultMspCallbacks(0) != BSP_ERROR_NONE) { + status = BSP_ERROR_PERIPH_FAILURE; + } + } +#endif /* (USE_HAL_DSI_REGISTER_CALLBACKS == 0) */ + + if (status == BSP_ERROR_NONE) { + /* DSI peripheral initialization */ + if (MX_DSI_Init(&hlcd_dsi) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } + } + } + + /*********************/ + /* LCD configuration */ + /*********************/ + if (status == BSP_ERROR_NONE) { + PhyTimers.ClockLaneHS2LPTime = 11; + PhyTimers.ClockLaneLP2HSTime = 40; + PhyTimers.DataLaneHS2LPTime = 12; + PhyTimers.DataLaneLP2HSTime = 23; + PhyTimers.DataLaneMaxReadTime = 0; + PhyTimers.StopWaitTime = 7; + + if (HAL_DSI_ConfigPhyTimer(&hlcd_dsi, &PhyTimers) != HAL_OK) { + return 6; + } + + HostTimeouts.TimeoutCkdiv = 1; + HostTimeouts.HighSpeedTransmissionTimeout = 0; + HostTimeouts.LowPowerReceptionTimeout = 0; + HostTimeouts.HighSpeedReadTimeout = 0; + HostTimeouts.LowPowerReadTimeout = 0; + HostTimeouts.HighSpeedWriteTimeout = 0; + HostTimeouts.HighSpeedWritePrespMode = 0; + HostTimeouts.LowPowerWriteTimeout = 0; + HostTimeouts.BTATimeout = 0; + + if (HAL_DSI_ConfigHostTimeouts(&hlcd_dsi, &HostTimeouts) != HAL_OK) { + return 7; + } + + if (HAL_DSI_ConfigFlowControl(&hlcd_dsi, DSI_FLOW_CONTROL_BTA) != HAL_OK) { + return 7; + } + + /* Enable the DSI host */ + __HAL_DSI_ENABLE(&hlcd_dsi); + + /*************/ + /* LTDC init */ + /*************/ + if (status == BSP_ERROR_NONE) { + if (MX_LTDC_ClockConfig(&hlcd_ltdc) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } else { +#if (USE_HAL_LTDC_REGISTER_CALLBACKS == 0) + LTDC_MspInit(&hlcd_ltdc); +#else + /* Register the LTDC MSP Callbacks */ + if (LcdLtdc_IsMspCbValid[0] == 0U) { + if (BSP_LCD_LTDC_RegisterDefaultMspCallbacks(0) != BSP_ERROR_NONE) { + status = BSP_ERROR_PERIPH_FAILURE; + } + } +#endif /* (USE_HAL_GFXMMU_REGISTER_CALLBACKS == 0) */ + + if (status == BSP_ERROR_NONE) { + /* LTDC peripheral initialization */ + if (MX_LTDC_Init(&hlcd_ltdc) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } else { + if (MX_LTDC_ConfigLayer(&hlcd_ltdc, LTDC_LAYER_1, + GFXMMU_VIRTUAL_BUFFER0_BASE_S) != HAL_OK) { + status = BSP_ERROR_PERIPH_FAILURE; + } + } + } + } + } + + /* Start DSI */ + if (HAL_DSI_Start(&(hlcd_dsi)) != HAL_OK) { + return 8; + } + + /* CMD Mode */ + uint8_t InitParam1[3] = {0xFF, 0x83, 0x79}; + if (HAL_DSI_LongWrite(&hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 3, 0xB9, + InitParam1) != HAL_OK) { + ErrorNumber++; + } + + /* SETPOWER */ + uint8_t InitParam2[16] = {0x44, 0x1C, 0x1C, 0x37, 0x57, 0x90, 0xD0, 0xE2, + 0x58, 0x80, 0x38, 0x38, 0xF8, 0x33, 0x34, 0x42}; + if (HAL_DSI_LongWrite(&hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 16, 0xB1, + InitParam2) != HAL_OK) { + ErrorNumber++; + } + + /* SETDISP */ + uint8_t InitParam3[9] = {0x80, 0x14, 0x0C, 0x30, 0x20, + 0x50, 0x11, 0x42, 0x1D}; + if (HAL_DSI_LongWrite(&hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 9, 0xB2, + InitParam3) != HAL_OK) { + ErrorNumber++; + } + + /* Set display cycle timing */ + uint8_t InitParam4[10] = {0x01, 0xAA, 0x01, 0xAF, 0x01, + 0xAF, 0x10, 0xEA, 0x1C, 0xEA}; + if (HAL_DSI_LongWrite(&hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 10, 0xB4, + InitParam4) != HAL_OK) { + ErrorNumber++; + } + + /* SETVCOM */ + uint8_t InitParam5[4] = {00, 00, 00, 0xC0}; + if (HAL_DSI_LongWrite(&hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 4, 0xC7, + InitParam5) != HAL_OK) { + ErrorNumber++; + } + + /* Set Panel Related Registers */ + if (HAL_DSI_ShortWrite(&hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, 0xCC, + 0x02) != HAL_OK) { + ErrorNumber++; + } + + if (HAL_DSI_ShortWrite(&hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, 0xD2, + 0x77) != HAL_OK) { + ErrorNumber++; + } + + uint8_t InitParam6[37] = {0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x08, 0x32, + 0x10, 0x01, 0x00, 0x01, 0x03, 0x72, 0x03, 0x72, + 0x00, 0x08, 0x00, 0x08, 0x33, 0x33, 0x05, 0x05, + 0x37, 0x05, 0x05, 0x37, 0x0A, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x01, 0x00, 0x0E}; + if (HAL_DSI_LongWrite(&hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 37, 0xD3, + InitParam6) != HAL_OK) { + ErrorNumber++; + } + + uint8_t InitParam7[34] = { + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x18, 0x18, + 0x18, 0x18, 0x19, 0x19, 0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, + 0x23, 0x22, 0x21, 0x20, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00}; + if (HAL_DSI_LongWrite(&hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 34, 0xD5, + InitParam7) != HAL_OK) { + ErrorNumber++; + } + + uint8_t InitParam8[32] = {0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x19, 0x19, 0x18, 0x18, 0x19, 0x19, 0x18, 0x18, + 0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 0x00, 0x01, + 0x20, 0x21, 0x22, 0x23, 0x18, 0x18, 0x18, 0x18}; + if (HAL_DSI_LongWrite(&hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 35, 0xD6, + InitParam8) != HAL_OK) { + ErrorNumber++; + } + + /* SET GAMMA */ + uint8_t InitParam9[42] = { + 0x00, 0x16, 0x1B, 0x30, 0x36, 0x3F, 0x24, 0x40, 0x09, 0x0D, 0x0F, + 0x18, 0x0E, 0x11, 0x12, 0x11, 0x14, 0x07, 0x12, 0x13, 0x18, 0x00, + 0x17, 0x1C, 0x30, 0x36, 0x3F, 0x24, 0x40, 0x09, 0x0C, 0x0F, 0x18, + 0x0E, 0x11, 0x14, 0x11, 0x12, 0x07, 0x12, 0x14, 0x18}; + if (HAL_DSI_LongWrite(&hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 42, 0xE0, + InitParam9) != HAL_OK) { + ErrorNumber++; + } + + uint8_t InitParam10[3] = {0x2C, 0x2C, 00}; + if (HAL_DSI_LongWrite(&hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 3, 0xB6, + InitParam10) != HAL_OK) { + ErrorNumber++; + } + + if (HAL_DSI_ShortWrite(&hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, 0xBD, + 0x00) != HAL_OK) { + ErrorNumber++; + } + + uint8_t InitParam11[] = { + 0x01, 0x00, 0x07, 0x0F, 0x16, 0x1F, 0x27, 0x30, 0x38, 0x40, 0x47, + 0x4E, 0x56, 0x5D, 0x65, 0x6D, 0x74, 0x7D, 0x84, 0x8A, 0x90, 0x99, + 0xA1, 0xA9, 0xB0, 0xB6, 0xBD, 0xC4, 0xCD, 0xD4, 0xDD, 0xE5, 0xEC, + 0xF3, 0x36, 0x07, 0x1C, 0xC0, 0x1B, 0x01, 0xF1, 0x34, 0x00}; + if (HAL_DSI_LongWrite(&hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 42, 0xC1, + InitParam11) != HAL_OK) { + ErrorNumber++; + } + + if (HAL_DSI_ShortWrite(&hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, 0xBD, + 0x01) != HAL_OK) { + ErrorNumber++; + } + + uint8_t InitParam12[] = { + 0x00, 0x08, 0x0F, 0x16, 0x1F, 0x28, 0x31, 0x39, 0x41, 0x48, 0x51, + 0x59, 0x60, 0x68, 0x70, 0x78, 0x7F, 0x87, 0x8D, 0x94, 0x9C, 0xA3, + 0xAB, 0xB3, 0xB9, 0xC1, 0xC8, 0xD0, 0xD8, 0xE0, 0xE8, 0xEE, 0xF5, + 0x3B, 0x1A, 0xB6, 0xA0, 0x07, 0x45, 0xC5, 0x37, 0x00}; + if (HAL_DSI_LongWrite(&hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 42, 0xC1, + InitParam12) != HAL_OK) { + ErrorNumber++; + } + + if (HAL_DSI_ShortWrite(&hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, 0xBD, + 0x02) != HAL_OK) { + ErrorNumber++; + } + + uint8_t InitParam13[42] = { + 0x00, 0x09, 0x0F, 0x18, 0x21, 0x2A, 0x34, 0x3C, 0x45, 0x4C, 0x56, + 0x5E, 0x66, 0x6E, 0x76, 0x7E, 0x87, 0x8E, 0x95, 0x9D, 0xA6, 0xAF, + 0xB7, 0xBD, 0xC5, 0xCE, 0xD5, 0xDF, 0xE7, 0xEE, 0xF4, 0xFA, 0xFF, + 0x0C, 0x31, 0x83, 0x3C, 0x5B, 0x56, 0x1E, 0x5A, 0xFF}; + if (HAL_DSI_LongWrite(&hlcd_dsi, 0, DSI_DCS_LONG_PKT_WRITE, 42, 0xC1, + InitParam13) != HAL_OK) { + ErrorNumber++; + } + + if (HAL_DSI_ShortWrite(&hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, 0xBD, + 0x00) != HAL_OK) { + ErrorNumber++; + } + + /* Exit Sleep Mode*/ + if (HAL_DSI_ShortWrite(&hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P0, 0x11, + 0x00) != HAL_OK) { + ErrorNumber++; + } + + HAL_Delay(120); + + /* Display On */ + if (HAL_DSI_ShortWrite(&hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P0, 0x29, + 0x00) != HAL_OK) { + ErrorNumber++; + } + + HAL_Delay(120); + + if (ErrorNumber != 0U) { + status = BSP_ERROR_PERIPH_FAILURE; + } + } + + return status; +} + +/** + * @brief De-Initialize LCD. + * @retval BSP status. + */ +static int32_t LCD_DeInit(void) { + int32_t status = BSP_ERROR_NONE; + uint32_t ErrorNumber = 0; + + /* Disable DSI wrapper */ + __HAL_DSI_WRAPPER_DISABLE(&hlcd_dsi); + + /* Set display off */ + if (HAL_DSI_ShortWrite(&hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, + DSI_SET_DISPLAY_OFF, 0x00) != HAL_OK) { + ErrorNumber++; + } + + /* Wait before entering in sleep mode */ + HAL_Delay(2000); + + /* Put LCD in sleep mode */ + if (HAL_DSI_ShortWrite(&hlcd_dsi, 0, DSI_DCS_SHORT_PKT_WRITE_P0, + DSI_ENTER_SLEEP_MODE, 0x0) != HAL_OK) { + ErrorNumber++; + } + + HAL_Delay(120); + + /* De-initialize DSI */ + if (HAL_DSI_DeInit(&hlcd_dsi) != HAL_OK) { + ErrorNumber++; + } +#if (USE_HAL_DSI_REGISTER_CALLBACKS == 0) + DSI_MspDeInit(&hlcd_dsi); +#endif /* (USE_HAL_DSI_REGISTER_CALLBACKS == 0) */ + + /* De-initialize LTDC */ + if (HAL_LTDC_DeInit(&hlcd_ltdc) != HAL_OK) { + ErrorNumber++; + } +#if (USE_HAL_LTDC_REGISTER_CALLBACKS == 0) + LTDC_MspDeInit(&hlcd_ltdc); +#endif /* (USE_HAL_LTDC_REGISTER_CALLBACKS == 0) */ + + /* De-initialize GFXMMU */ + if (HAL_GFXMMU_DeInit(&hlcd_gfxmmu) != HAL_OK) { + ErrorNumber++; + } +#if (USE_HAL_GFXMMU_REGISTER_CALLBACKS == 0) + GFXMMU_MspDeInit(&hlcd_gfxmmu); +#endif /* (USE_HAL_GFXMMU_REGISTER_CALLBACKS == 0) */ + + if (ErrorNumber != 0U) { + status = BSP_ERROR_PERIPH_FAILURE; + } + + return status; +} + +/** + * @brief Initialize GFXMMU MSP. + * @param hgfxmmu GFXMMU handle + * @retval None + */ +static void GFXMMU_MspInit(GFXMMU_HandleTypeDef *hgfxmmu) { + /* Prevent unused argument(s) compilation warning */ + UNUSED(hgfxmmu); + + /* GFXMMU clock enable */ + __HAL_RCC_GFXMMU_CLK_ENABLE(); + + /* Enable GFXMMU interrupt */ + HAL_NVIC_SetPriority(GFXMMU_IRQn, BSP_LCD_GFXMMU_IT_PRIORITY, 0); + HAL_NVIC_EnableIRQ(GFXMMU_IRQn); +} + +/** + * @brief De-Initialize GFXMMU MSP. + * @param hgfxmmu GFXMMU handle + * @retval None + */ +static void GFXMMU_MspDeInit(GFXMMU_HandleTypeDef *hgfxmmu) { + /* Prevent unused argument(s) compilation warning */ + UNUSED(hgfxmmu); + + /* Disable GFXMMU interrupt */ + HAL_NVIC_DisableIRQ(GFXMMU_IRQn); + + /* GFXMMU clock disable */ + __HAL_RCC_GFXMMU_CLK_DISABLE(); +} + +/** + * @brief Initialize LTDC MSP. + * @param hltdc LTDC handle + * @retval None + */ +static void LTDC_MspInit(LTDC_HandleTypeDef *hltdc) { + /* Prevent unused argument(s) compilation warning */ + UNUSED(hltdc); + + /* Enable LCD clock */ + __HAL_RCC_LTDC_CLK_ENABLE(); + + /* Enable LTDC interrupt */ + HAL_NVIC_SetPriority(LTDC_IRQn, BSP_LCD_LTDC_IT_PRIORITY, 0); + HAL_NVIC_EnableIRQ(LTDC_IRQn); + + HAL_NVIC_SetPriority(LTDC_ER_IRQn, BSP_LCD_LTDC_IT_PRIORITY, 0); + HAL_NVIC_EnableIRQ(LTDC_ER_IRQn); +} + +/** + * @brief De-Initialize LTDC MSP. + * @param hltdc LTDC handle + * @retval None + */ +static void LTDC_MspDeInit(LTDC_HandleTypeDef *hltdc) { + /* Prevent unused argument(s) compilation warning */ + UNUSED(hltdc); + + /* Disable LTDC interrupts */ + HAL_NVIC_DisableIRQ(LTDC_ER_IRQn); + HAL_NVIC_DisableIRQ(LTDC_IRQn); + + /* LTDC clock disable */ + __HAL_RCC_LTDC_CLK_DISABLE(); +} + +/** + * @brief Initialize DSI MSP. + * @param hdsi DSI handle + * @retval None + */ +static void DSI_MspInit(DSI_HandleTypeDef *hdsi) { + RCC_PeriphCLKInitTypeDef PLL3InitPeriph = {0}; + RCC_PeriphCLKInitTypeDef DSIPHYInitPeriph = {0}; + GPIO_InitTypeDef GPIO_InitStruct = {0}; + + UNUSED(hdsi); + + /* Enable GPIOI & GPIOD clocks */ + __HAL_RCC_GPIOD_CLK_ENABLE(); + __HAL_RCC_GPIOI_CLK_ENABLE(); + + /* Configure DSI Reset pin */ + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_PULLDOWN; + GPIO_InitStruct.Pin = GPIO_PIN_5; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); + + /* Configure LCD Backlight Pin */ + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; + GPIO_InitStruct.Pull = GPIO_PULLUP; + GPIO_InitStruct.Pin = GPIO_PIN_6; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + HAL_GPIO_Init(GPIOI, &GPIO_InitStruct); + + /* Enable DSI clock */ + __HAL_RCC_DSI_CLK_ENABLE(); + + /** ################ Set DSI clock to D-PHY source clock ################## + * **/ + + /* Start and configurre PLL3 */ + /* HSE = 16MHZ */ + /* 16/(M=4) = 4MHz input (min) */ + /* 4*(N=125) = 500MHz VCO (almost max) */ + /* 500/(P=8) = 62.5 for DSI ie exactly the lane byte clock*/ + + PLL3InitPeriph.PeriphClockSelection = RCC_PERIPHCLK_DSI; + PLL3InitPeriph.DsiClockSelection = RCC_DSICLKSOURCE_PLL3; + PLL3InitPeriph.PLL3.PLL3M = 4; + PLL3InitPeriph.PLL3.PLL3N = 125; + PLL3InitPeriph.PLL3.PLL3P = 8; + PLL3InitPeriph.PLL3.PLL3Q = 8; + PLL3InitPeriph.PLL3.PLL3R = 24; + PLL3InitPeriph.PLL3.PLL3FRACN = 0; + PLL3InitPeriph.PLL3.PLL3RGE = RCC_PLLVCIRANGE_1; + PLL3InitPeriph.PLL3.PLL3ClockOut = RCC_PLL3_DIVR | RCC_PLL3_DIVP; + PLL3InitPeriph.PLL3.PLL3Source = RCC_PLLSOURCE_HSE; + (void)HAL_RCCEx_PeriphCLKConfig(&PLL3InitPeriph); + + __HAL_RCC_DSI_CLK_ENABLE(); + + /* Switch to D-PHY source clock */ + /* Enable the DSI host */ + hlcd_dsi.Instance = DSI; + + __HAL_DSI_ENABLE(&hlcd_dsi); + + /* Enable the DSI PLL */ + __HAL_DSI_PLL_ENABLE(&hlcd_dsi); + + HAL_Delay(1); + + /* Enable the clock lane and the digital section of the D-PHY */ + hlcd_dsi.Instance->PCTLR |= (DSI_PCTLR_CKE | DSI_PCTLR_DEN); + + /* Set the TX escape clock division factor */ + hlcd_dsi.Instance->CCR = 4; + + HAL_Delay(1); + + /* Config DSI Clock to DSI PHY */ + DSIPHYInitPeriph.PeriphClockSelection = RCC_PERIPHCLK_DSI; + DSIPHYInitPeriph.DsiClockSelection = RCC_DSICLKSOURCE_DSIPHY; + + (void)HAL_RCCEx_PeriphCLKConfig(&DSIPHYInitPeriph); + + /* Reset */ + HAL_Delay(11); + HAL_GPIO_WritePin(GPIOD, GPIO_PIN_5, GPIO_PIN_SET); + HAL_Delay(150); + + /* Reset the TX escape clock division factor */ + hlcd_dsi.Instance->CCR &= ~DSI_CCR_TXECKDIV; + + /* Disable the DSI PLL */ + __HAL_DSI_PLL_DISABLE(&hlcd_dsi); + + /* Disable the DSI host */ + __HAL_DSI_DISABLE(&hlcd_dsi); + + /** ######################################################################### + * **/ + + /* Enable DSI NVIC interrupt */ + /* Default is lowest priority level */ + HAL_NVIC_SetPriority(DSI_IRQn, 0x0FUL, 0); + HAL_NVIC_EnableIRQ(DSI_IRQn); +} + +/** + * @brief De-Initialize DSI MSP. + * @param hdsi DSI handle + * @retval None + */ +static void DSI_MspDeInit(DSI_HandleTypeDef *hdsi) { + RCC_PeriphCLKInitTypeDef PLL3InitPeriph = {0}; + + UNUSED(hdsi); + + /* Switch to PLL3 before Disable */ + PLL3InitPeriph.PeriphClockSelection = RCC_PERIPHCLK_DSI; + PLL3InitPeriph.DsiClockSelection = RCC_DSICLKSOURCE_PLL3; + PLL3InitPeriph.PLL3.PLL3M = 4; + PLL3InitPeriph.PLL3.PLL3N = 125; + PLL3InitPeriph.PLL3.PLL3P = 8; + PLL3InitPeriph.PLL3.PLL3Q = 8; + PLL3InitPeriph.PLL3.PLL3R = 24; + PLL3InitPeriph.PLL3.PLL3FRACN = 0; + PLL3InitPeriph.PLL3.PLL3RGE = RCC_PLLVCIRANGE_1; + PLL3InitPeriph.PLL3.PLL3ClockOut = RCC_PLL3_DIVR | RCC_PLL3_DIVP; + PLL3InitPeriph.PLL3.PLL3Source = RCC_PLLSOURCE_HSE; + (void)HAL_RCCEx_PeriphCLKConfig(&PLL3InitPeriph); + + /* DSI clock disable */ + __HAL_RCC_DSI_CLK_DISABLE(); + + /** @brief Toggle Sw reset of DSI IP */ + __HAL_RCC_DSI_FORCE_RESET(); + __HAL_RCC_DSI_RELEASE_RESET(); + + /* Disable DSI interrupts */ + HAL_NVIC_DisableIRQ(DSI_IRQn); +} + +int32_t BSP_LCD_SetFrameBuffer(uint32_t Instance, uint32_t fb_addr) { + int32_t status = BSP_ERROR_NONE; + if (Instance >= LCD_INSTANCES_NBR) { + status = BSP_ERROR_WRONG_PARAM; + } else { + MX_LTDC_ConfigLayer(&hlcd_ltdc, 0, fb_addr); + } + + return status; +} + +int32_t BSP_LCD_Reinit(uint32_t Instance) { + int32_t status = BSP_ERROR_NONE; + if (Instance >= LCD_INSTANCES_NBR) { + status = BSP_ERROR_WRONG_PARAM; + } else { + // Switch to D-PHY source clock + // Enable the DSI host + hlcd_dsi.Instance = DSI; + + MX_GFXMMU_Reinit(&hlcd_gfxmmu); + MX_DSI_Reinit(&hlcd_dsi); + MX_LTDC_Reinit(&hlcd_ltdc); + } + + return status; +} diff --git a/core/embed/trezorhal/stm32u5/display/vg-2864 b/core/embed/trezorhal/stm32u5/display/vg-2864 new file mode 120000 index 000000000..88c9a8fad --- /dev/null +++ b/core/embed/trezorhal/stm32u5/display/vg-2864 @@ -0,0 +1 @@ +../../stm32f4/display/vg-2864 \ No newline at end of file diff --git a/core/embed/trezorhal/stm32u5/displays/dsi.h b/core/embed/trezorhal/stm32u5/displays/dsi.h index a717e4bb7..913adb369 100644 --- a/core/embed/trezorhal/stm32u5/displays/dsi.h +++ b/core/embed/trezorhal/stm32u5/displays/dsi.h @@ -3,10 +3,6 @@ #include STM32_HAL_H -#define MAX_DISPLAY_RESX 240 -#define MAX_DISPLAY_RESY 240 -#define DISPLAY_RESX 240 -#define DISPLAY_RESY 240 #define DISPLAY_COLOR_MODE DMA2D_OUTPUT_ARGB8888 #define DISPLAY_FRAMEBUFFER_WIDTH 768 #define DISPLAY_FRAMEBUFFER_HEIGHT 480 diff --git a/core/embed/trezorhal/stm32u5/dma2d_bitblt.c b/core/embed/trezorhal/stm32u5/dma2d_bitblt.c new file mode 120000 index 000000000..faeb9cc40 --- /dev/null +++ b/core/embed/trezorhal/stm32u5/dma2d_bitblt.c @@ -0,0 +1 @@ +../stm32f4/dma2d_bitblt.c \ No newline at end of file diff --git a/core/embed/trezorhal/unix/display-unix.c b/core/embed/trezorhal/unix/display-unix.c index f0442cf4a..51fe9cc8c 100644 --- a/core/embed/trezorhal/unix/display-unix.c +++ b/core/embed/trezorhal/unix/display-unix.c @@ -31,7 +31,7 @@ #include "common.h" #include "display-unix.h" -#include "display_interface.h" +#include "display.h" #include "profile.h" #define EMULATOR_BORDER 16 @@ -354,3 +354,5 @@ void display_clear_save(void) { uint8_t *display_get_wr_addr(void) { return (uint8_t *)DISPLAY_DATA_ADDRESS; } void display_finish_actions(void) {} + +void display_reinit(void) {} diff --git a/core/embed/trezorhal/unix/display_driver.c b/core/embed/trezorhal/unix/display_driver.c new file mode 100644 index 000000000..9844f6304 --- /dev/null +++ b/core/embed/trezorhal/unix/display_driver.c @@ -0,0 +1,461 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#define _GNU_SOURCE + +#include + +#include +#include + +#include "common.h" +#include "profile.h" + +#define EMULATOR_BORDER 16 + +#if defined TREZOR_MODEL_T + +#ifdef TREZOR_EMULATOR_RASPI +#define WINDOW_WIDTH 480 +#define WINDOW_HEIGHT 320 +#define TOUCH_OFFSET_X 110 +#define TOUCH_OFFSET_Y 40 +#else +#define WINDOW_WIDTH 400 +#define WINDOW_HEIGHT 600 +#define TOUCH_OFFSET_X 80 +#define TOUCH_OFFSET_Y 110 +#endif + +#elif defined TREZOR_MODEL_1 + +#define WINDOW_WIDTH 200 +#define WINDOW_HEIGHT 340 +#define TOUCH_OFFSET_X 36 +#define TOUCH_OFFSET_Y 92 + +#elif defined TREZOR_MODEL_R + +#define WINDOW_WIDTH 193 +#define WINDOW_HEIGHT 339 +#define TOUCH_OFFSET_X 32 +#define TOUCH_OFFSET_Y 84 + +#elif defined TREZOR_MODEL_T3T1 + +#define WINDOW_WIDTH 400 +#define WINDOW_HEIGHT 600 +#define TOUCH_OFFSET_X 80 +#define TOUCH_OFFSET_Y 110 + +#else +#error Unknown Trezor model +#endif + +typedef struct { + // Current display orientation (0 or 180) + int orientation_angle; + // Current backlight level ranging from 0 to 255 + int backlight_level; + + SDL_Window *window; + SDL_Renderer *renderer; + SDL_Surface *buffer; + SDL_Texture *texture; + SDL_Texture *background; + SDL_Surface *prev_saved; + +#if DISPLAY_MONO + // SDL2 does not support 8bit surface/texture + // and we have to simulate it + uint8_t mono_framebuf[DISPLAY_RESX * DISPLAY_RESY]; +#endif + +} display_driver_t; + +static display_driver_t g_display_driver; + +//!@# TODO get rid of this... +int sdl_display_res_x = DISPLAY_RESX, sdl_display_res_y = DISPLAY_RESY; +int sdl_touch_offset_x, sdl_touch_offset_y; + +void display_deinit(void) { + display_driver_t *drv = &g_display_driver; + + SDL_FreeSurface(drv->prev_saved); + SDL_FreeSurface(drv->buffer); + if (drv->background != NULL) { + SDL_DestroyTexture(drv->background); + } + if (drv->texture != NULL) { + SDL_DestroyTexture(drv->texture); + } + if (drv->renderer != NULL) { + SDL_DestroyRenderer(drv->renderer); + } + if (drv->window != NULL) { + SDL_DestroyWindow(drv->window); + } + SDL_Quit(); +} + +void display_init(void) { + display_driver_t *drv = &g_display_driver; + + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + printf("%s\n", SDL_GetError()); + ensure(secfalse, "SDL_Init error"); + } + atexit(display_deinit); + + char *window_title = NULL; + char *window_title_alloc = NULL; + if (asprintf(&window_title_alloc, "Trezor^emu: %s", profile_name()) > 0) { + window_title = window_title_alloc; + } else { + window_title = "Trezor^emu"; + window_title_alloc = NULL; + } + + drv->window = + SDL_CreateWindow(window_title, SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_HEIGHT, +#ifdef TREZOR_EMULATOR_RASPI + SDL_WINDOW_SHOWN | SDL_WINDOW_FULLSCREEN +#else + SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI +#endif + ); + free(window_title_alloc); + if (!drv->window) { + printf("%s\n", SDL_GetError()); + ensure(secfalse, "SDL_CreateWindow error"); + } + drv->renderer = SDL_CreateRenderer(drv->window, -1, SDL_RENDERER_SOFTWARE); + if (!drv->renderer) { + printf("%s\n", SDL_GetError()); + SDL_DestroyWindow(drv->window); + ensure(secfalse, "SDL_CreateRenderer error"); + } + SDL_SetRenderDrawColor(drv->renderer, 0, 0, 0, 255); + SDL_RenderClear(drv->renderer); + + drv->buffer = SDL_CreateRGBSurface(0, DISPLAY_RESX, DISPLAY_RESY, 16, 0xF800, + 0x07E0, 0x001F, 0x0000); + drv->texture = SDL_CreateTexture(drv->renderer, SDL_PIXELFORMAT_RGB565, + SDL_TEXTUREACCESS_STREAMING, DISPLAY_RESX, + DISPLAY_RESY); + SDL_SetTextureBlendMode(drv->texture, SDL_BLENDMODE_BLEND); +#ifdef __APPLE__ + // macOS Mojave SDL black screen workaround + SDL_PumpEvents(); + SDL_SetWindowSize(drv->window, WINDOW_WIDTH, WINDOW_HEIGHT); +#endif +#ifdef TREZOR_EMULATOR_RASPI +#include "background_raspi.h" + drv->background = IMG_LoadTexture_RW( + drv->renderer, + SDL_RWFromMem(background_raspi_jpg, background_raspi_jpg_len), 0); +#else +#if defined TREZOR_MODEL_T +#include "background_T.h" + drv->background = IMG_LoadTexture_RW( + drv->renderer, SDL_RWFromMem(background_T_jpg, background_T_jpg_len), 0); +#elif defined TREZOR_MODEL_1 +#include "background_1.h" + drv->background = IMG_LoadTexture_RW( + drv->renderer, SDL_RWFromMem(background_1_jpg, background_1_jpg_len), 0); +#elif defined TREZOR_MODEL_R +#include "background_T2B1.h" + drv->background = IMG_LoadTexture_RW( + drv->renderer, + SDL_RWFromMem(background_T2B1_png, background_T2B1_png_len), 0); +#endif +#endif + if (drv->background) { + SDL_SetTextureBlendMode(drv->background, SDL_BLENDMODE_NONE); + sdl_touch_offset_x = TOUCH_OFFSET_X; + sdl_touch_offset_y = TOUCH_OFFSET_Y; + } else { + SDL_SetWindowSize(drv->window, DISPLAY_RESX + 2 * EMULATOR_BORDER, + DISPLAY_RESY + 2 * EMULATOR_BORDER); + sdl_touch_offset_x = EMULATOR_BORDER; + sdl_touch_offset_y = EMULATOR_BORDER; + } +#if defined TREZOR_MODEL_1 || defined TREZOR_MODEL_R + // T1 and TR do not have backlight capabilities in hardware, so + // setting its value here for emulator to avoid + // calling any `set_backlight` functions + drv->backlight_level = 255; +#else + drv->backlight_level = 0; +#endif +#ifdef TREZOR_EMULATOR_RASPI + drv->orientation_angle = 270; + SDL_ShowCursor(SDL_DISABLE); +#else + drv->orientation_angle = 0; +#endif +} + +void display_reinit(void) { + // not used +} + +void display_finish_actions(void) { + // not used +} + +int display_set_backlight(int level) { + display_driver_t *drv = &g_display_driver; + +#if defined TREZOR_MODEL_1 || defined TREZOR_MODEL_R + level = 255; +#endif + + if (drv->backlight_level != level && level >= 0 && level <= 255) { + drv->backlight_level = level; + display_refresh(); + } + + return drv->backlight_level; +} + +int display_get_backlight(void) { + display_driver_t *drv = &g_display_driver; + return drv->backlight_level; +} + +int display_set_orientation(int angle) { + display_driver_t *drv = &g_display_driver; + if (angle != drv->orientation_angle) { +#if defined TREZOR_MODEL_T || defined TREZOR_MODEL_T3T1 + if (angle == 0 || angle == 90 || angle == 180 || angle == 270) { +#elif defined TREZOR_MODEL_1 || defined TREZOR_MODEL_R + if (angle == 0 || angle == 180) { +#else +#error Unknown Trezor model +#endif + drv->orientation_angle = angle; + display_refresh(); + } + } + return drv->orientation_angle; +} + +int display_get_orientation(void) { + display_driver_t *drv = &g_display_driver; + return drv->orientation_angle; +} + +#ifdef XFRAMEBUFFER +display_fb_info_t display_get_frame_buffer(void) { + display_driver_t *drv = &g_display_driver; + +#ifdef DISPLAY_MONO + display_fb_info_t fb = { + .ptr = drv->mono_framebuf, + .stride = DISPLAY_RESX, + }; +#else + display_fb_info_t fb = { + .ptr = drv->buffer->pixels, + .stride = DISPLAY_RESX * sizeof(uint16_t), + }; +#endif + + return fb; +} + +#else // XFRAMEBUFFER + +void display_wait_for_sync(void) { + // not used +} +#endif + +#ifdef DISPLAY_MONO +// Copies driver's monochromatic framebuffer into the RGB framebuffer used by +// SDL +static void copy_mono_framebuf(display_driver_t *drv) { + for (int y = 0; y < DISPLAY_RESY; y++) { + uint16_t *dst = + (uint16_t *)((uint8_t *)drv->buffer->pixels + drv->buffer->pitch * y); + uint8_t *src = &drv->mono_framebuf[y * DISPLAY_RESX]; + for (int x = 0; x < DISPLAY_RESX; x++) { + uint8_t lum = src[x] > 40 ? 255 : 0; + dst[x] = gl_color16_rgb(lum, lum, lum); + } + } +} +#endif + +void display_refresh(void) { + display_driver_t *drv = &g_display_driver; + + if (!drv->renderer) { + display_init(); + } + +#ifdef DISPLAY_MONO + copy_mono_framebuf(drv); +#endif + + if (drv->background) { + const SDL_Rect r = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}; + SDL_RenderCopy(drv->renderer, drv->background, NULL, &r); + } else { + SDL_RenderClear(drv->renderer); + } + // Show the display buffer + SDL_UpdateTexture(drv->texture, NULL, drv->buffer->pixels, + drv->buffer->pitch); +#define BACKLIGHT_NORMAL 150 + SDL_SetTextureAlphaMod( + drv->texture, MIN(255, 255 * drv->backlight_level / BACKLIGHT_NORMAL)); + if (drv->background) { + const SDL_Rect r = {TOUCH_OFFSET_X, TOUCH_OFFSET_Y, DISPLAY_RESX, + DISPLAY_RESY}; + SDL_RenderCopyEx(drv->renderer, drv->texture, NULL, &r, + drv->orientation_angle, NULL, 0); + } else { + const SDL_Rect r = {EMULATOR_BORDER, EMULATOR_BORDER, DISPLAY_RESX, + DISPLAY_RESY}; + SDL_RenderCopyEx(drv->renderer, drv->texture, NULL, &r, + drv->orientation_angle, NULL, 0); + } + SDL_RenderPresent(drv->renderer); +} + +void display_set_compatible_settings(void) { + // not used +} + +#ifndef DISPLAY_MONO + +void display_fill(const gl_bitblt_t *bb) { + display_driver_t *drv = &g_display_driver; + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = + (uint8_t *)drv->buffer->pixels + (drv->buffer->pitch * bb_new.dst_y); + bb_new.dst_stride = drv->buffer->pitch; + + gl_rgb565_fill(&bb_new); +} + +void display_copy_rgb565(const gl_bitblt_t *bb) { + display_driver_t *drv = &g_display_driver; + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = + (uint8_t *)drv->buffer->pixels + (drv->buffer->pitch * bb_new.dst_y); + bb_new.dst_stride = drv->buffer->pitch; + + gl_rgb565_copy_rgb565(&bb_new); +} + +void display_copy_mono1p(const gl_bitblt_t *bb) { + display_driver_t *drv = &g_display_driver; + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = + (uint8_t *)drv->buffer->pixels + (drv->buffer->pitch * bb_new.dst_y); + bb_new.dst_stride = DISPLAY_RESX; + + gl_rgb565_copy_mono1p(&bb_new); +} + +void display_copy_mono4(const gl_bitblt_t *bb) { + display_driver_t *drv = &g_display_driver; + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = + (uint8_t *)drv->buffer->pixels + (drv->buffer->pitch * bb_new.dst_y); + bb_new.dst_stride = drv->buffer->pitch; + + gl_rgb565_copy_mono4(&bb_new); +} + +#else // DISPLAY_MONO + +void display_fill(const gl_bitblt_t *bb) { + display_driver_t *drv = &g_display_driver; + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = drv->mono_framebuf + (DISPLAY_RESX * bb_new.dst_y); + bb_new.dst_stride = DISPLAY_RESX; + + gl_mono8_fill(&bb_new); +} + +void display_copy_mono1p(const gl_bitblt_t *bb) { + display_driver_t *drv = &g_display_driver; + + gl_bitblt_t bb_new = *bb; + bb_new.dst_row = drv->mono_framebuf + (DISPLAY_RESX * bb_new.dst_y); + bb_new.dst_stride = DISPLAY_RESX; + + gl_mono8_copy_mono1p(&bb_new); +} + +#endif + +const char *display_save(const char *prefix) { + display_driver_t *drv = &g_display_driver; + + if (!drv->renderer) { + display_init(); + } + +#ifdef DISPLAY_MONO + copy_mono_framebuf(drv); +#endif + + static int count; + static char filename[256]; + // take a cropped view of the screen contents + const SDL_Rect rect = {0, 0, DISPLAY_RESX, DISPLAY_RESY}; + SDL_Surface *crop = SDL_CreateRGBSurface( + drv->buffer->flags, rect.w, rect.h, drv->buffer->format->BitsPerPixel, + drv->buffer->format->Rmask, drv->buffer->format->Gmask, + drv->buffer->format->Bmask, drv->buffer->format->Amask); + SDL_BlitSurface(drv->buffer, &rect, crop, NULL); + // compare with previous screen, skip if equal + if (drv->prev_saved != NULL) { + if (memcmp(drv->prev_saved->pixels, crop->pixels, crop->pitch * crop->h) == + 0) { + SDL_FreeSurface(crop); + return filename; + } + SDL_FreeSurface(drv->prev_saved); + } + // save to png + snprintf(filename, sizeof(filename), "%s%08d.png", prefix, count++); + IMG_SavePNG(crop, filename); + drv->prev_saved = crop; + return filename; +} + +void display_clear_save(void) { + display_driver_t *drv = &g_display_driver; + + SDL_FreeSurface(drv->prev_saved); + drv->prev_saved = NULL; +} diff --git a/core/embed/trezorhal/unix/dma2d_bitblt.c b/core/embed/trezorhal/unix/dma2d_bitblt.c new file mode 100644 index 000000000..c2fe9c71a --- /dev/null +++ b/core/embed/trezorhal/unix/dma2d_bitblt.c @@ -0,0 +1,22 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#include "dma2d_bitblt.h" + +void dma2d_wait(void) {} diff --git a/core/embed/trezorhal/xdisplay.h b/core/embed/trezorhal/xdisplay.h new file mode 100644 index 000000000..53cfb54e4 --- /dev/null +++ b/core/embed/trezorhal/xdisplay.h @@ -0,0 +1,156 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#ifndef TREZORHAL_XDISPLAY_H +#define TREZORHAL_XDISPLAY_H + +#include +#include +#include "gl_bitblt.h" + +#include TREZOR_BOARD + +// This is a universal API for controlling different types of display +// controllers. +// +// Currently, following displays displays are supported +// +// VG-2864KSWEG01 - OLED Mono / 128x64 pixels / SPI +// - Model T1B1 / Model T2B1 +// +// UG-2828SWIG01 - OLED Mono / 128x128 pixels / Parallel +// - Early revisions of T2B1 +// +// ST7789V - TFT RGB / 240x240 pixels / Parallel +// - Model T2T1 / Model T3T1 +// +// ILI9341 - TFT RGB / 320x240 pixels / Parallel / LTDC + SPI +// - STM32F429I-DISC1 Discovery Board +// +// MIPI - +// - STM32U5A9J-DK Discovery Board + +// Fully initializes the display controller. +void display_init(void); + +// Called in application or bootloader to reinitialize an already initialized +// display controller without any distrubing visible effect (blinking, etc.). +void display_reinit(void); + +// Waits for any backround operations (such as DMA copying) and returns. +// +// The function provides a barrier when jumping between +// boardloader/bootloader and firmware. +void display_finish_actions(void); + +// Sets display backlight level ranging from 0 (off)..255 (maximum). +// +// The default backligt level is 0. Without settings it +// to some higher value the displayed pixels are not visible. +// Beware that his also applies to the emulator. +// +// Returns the set level (usually the same value or the +// closest value to the `level` argument) +int display_set_backlight(int level); + +// Gets current display level ranging from 0 (off)..255 (maximum). +int display_get_backlight(void); + +// Sets the display orientation. +// +// May accept one of following values: 0, 90, 180, 270 +// but accepted values are model-dependent. +// Default display orientation is always 0. +// +// Returns the set orientation +int display_set_orientation(int angle); + +// Gets the display's current orientation +// +// Returned value is one of 0, 90, 180, 270. +int display_get_orientation(void); + +#ifdef XFRAMEBUFFER + +typedef struct { + // Pointer to the top-left pixel + void *ptr; + // Stride in bytes + size_t stride; + +} display_fb_info_t; + +// Provides pointer to the inactive (writeable) framebuffer. +// +// If framebuffer is not available yet due to display refreshing etc., +// the function may block until the buffer is ready to write. +display_fb_info_t display_get_frame_buffer(void); + +#else // XFRAMEBUFFER + +// Waits for the vertical synchronization pulse. +// +// Used for synchronization with the display refresh cycle +// to achieve tearless UX if possible when not using a frame buffer. +void display_wait_for_sync(void); +#endif + +// Swaps the frame buffers +// +// The function waits for vertical synchronization and +// swaps the active (currently displayed) and the inactive frame buffers. +void display_refresh(void); + +// Sets display to the mode compatible with the legacy bootloader code. +// +// This is used when switching between the firmware and the bootloader. +void display_set_compatible_settings(void); + +// Following function define display's bitblt interface. +// +// These functions draw directly to to display or to the +// currently inactive framebuffer. +// +// bb->dst_row and bb->dst_stride must be 0 + +// Fills a rectangle with a solid color. +// This function is supported by all types of displays. +void display_fill(const gl_bitblt_t *bb); +// Copies an RGB565 bitmap. +// This function is supported by RGB displays only. +void display_copy_rgb565(const gl_bitblt_t *bb); +// Copies a MONO4 bitmap (supported only with RGB displays). +// This function is supported by RGB displays only. +void display_copy_mono4(const gl_bitblt_t *bb); +// Copies a MONO1P bitmap. +// This function is supported by all types of displays. +void display_copy_mono1p(const gl_bitblt_t *bb); + +#ifdef TREZOR_EMULATOR +// Save the screen content to a file. +// The function is available only on the emulator. +const char *display_save(const char *prefix); +void display_clear_save(void); +#endif + +// Adds some declarations needed to compile with the legacy code +// (will be removed with the display legacy code) +#include "xdisplay_legacy.h" + +#endif // TREZORHAL_XDISPLAY_H diff --git a/core/embed/trezorhal/xdisplay_legacy.c b/core/embed/trezorhal/xdisplay_legacy.c new file mode 100644 index 000000000..516f74640 --- /dev/null +++ b/core/embed/trezorhal/xdisplay_legacy.c @@ -0,0 +1,43 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#include "xdisplay_legacy.h" +#include "xdisplay.h" + +// This code emulates the legacy display interface and will be +// removed after final cleanup of display drivers when the legacy code +// is removed. + +int display_orientation(int angle) { + if (angle >= 0) { + return display_set_orientation(angle); + } else { + return display_get_orientation(); + } +} + +int display_backlight(int level) { + if (level >= 0) { + return display_set_backlight(level); + } else { + return display_get_backlight(); + } +} + +void display_sync(void) {} diff --git a/core/embed/trezorhal/xdisplay_legacy.h b/core/embed/trezorhal/xdisplay_legacy.h new file mode 100644 index 000000000..0fc4d75cc --- /dev/null +++ b/core/embed/trezorhal/xdisplay_legacy.h @@ -0,0 +1,56 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * 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 . + */ + +#ifndef TREZORHAL_DISPLAY_LEGACY_H +#define TREZORHAL_DISPLAY_LEGACY_H + +#include +#include + +// These declarationscode emulates will be removed after the +// final cleanup of display drivers. They are here just to simplify +// integration with the legacy code. +// +// Most of these function are not called when NEW_RENDERING=1 +// and they are only needed to for succesfully code compilation + +#define DISPLAY_FRAMEBUFFER_WIDTH 768 +#define DISPLAY_FRAMEBUFFER_HEIGHT 480 +#define DISPLAY_FRAMEBUFFER_OFFSET_X 0 +#define DISPLAY_FRAMEBUFFER_OFFSET_Y 0 + +int display_orientation(int angle); +int display_backlight(int level); +void display_refresh(void); +void display_shift_window(uint16_t pixels); +uint16_t display_get_window_offset(void); +void display_pixeldata_dirty(void); +uint8_t* display_get_wr_addr(void); +void display_sync(void); +void display_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1); +void display_pixeldata(uint16_t c); +uint32_t* display_get_fb_addr(void); + +void display_clear(void); +void display_text_render_buffer(const char* text, int textlen, int font, + buffer_text_t* buffer, int text_offset); + +#define PIXELDATA(c) display_pixeldata(c) + +#endif // TREZORHAL_DISPLAY_LEGACY_H diff --git a/core/embed/unix/main.c b/core/embed/unix/main.c index f7c6461ea..2acd5b528 100644 --- a/core/embed/unix/main.c +++ b/core/embed/unix/main.c @@ -37,6 +37,7 @@ #include #include +#include "display.h" #include "extmod/misc.h" #include "extmod/vfs_posix.h" #include "flash.h" @@ -481,6 +482,8 @@ MP_NOINLINE int main_(int argc, char **argv) { pre_process_options(argc, argv); + display_init(); + // Map trezor.flash to memory. flash_init(); flash_otp_init(); diff --git a/core/site_scons/boards/discovery.py b/core/site_scons/boards/discovery.py index 8f0738429..aa9ed0bbf 100644 --- a/core/site_scons/boards/discovery.py +++ b/core/site_scons/boards/discovery.py @@ -36,9 +36,20 @@ def configure( sources += [ "embed/models/model_D001_layout.c", ] - sources += [f"embed/trezorhal/stm32f4/displays/{display}"] - sources += ["embed/trezorhal/stm32f4/displays/ili9341_spi.c"] + + if "new_rendering" in features_wanted: + sources += [ + "embed/trezorhal/xdisplay_legacy.c", + "embed/trezorhal/stm32f4/display/stm32f429i-disc1/display_driver.c", + "embed/trezorhal/stm32f4/display/stm32f429i-disc1/display_ltdc.c", + "embed/trezorhal/stm32f4/display/stm32f429i-disc1/ili9341_spi.c", + ] + else: + sources += [f"embed/trezorhal/stm32f4/displays/{display}"] + sources += ["embed/trezorhal/stm32f4/displays/ili9341_spi.c"] + sources += ["embed/trezorhal/stm32f4/dma2d.c"] + sources += ["embed/trezorhal/stm32f4/dma2d_bitblt.c"] sources += [ "vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma2d.c" ] @@ -51,6 +62,11 @@ def configure( features_available.append("dma2d") features_available.append("framebuffer") + if "new_rendering" in features_wanted: + defines += ["XFRAMEBUFFER"] + features_available.append("xframebuffer") + features_available.append("display_rgb565") + if "input" in features_wanted: sources += ["embed/trezorhal/stm32f4/i2c.c"] sources += ["embed/trezorhal/stm32f4/touch/stmpe811.c"] @@ -71,4 +87,10 @@ def configure( env.get("ENV")["TREZOR_BOARD"] = board env.get("ENV")["MCU_TYPE"] = mcu + rust_defs = env.get("ENV")["RUST_INCLUDES"] + rust_defs += "-DFRAMEBUFFER;" + if "new_rendering" in features_wanted: + rust_defs += "-DXFRAMEBUFFER;" + env.get("ENV")["RUST_INCLUDES"] = rust_defs + return features_available diff --git a/core/site_scons/boards/discovery2.py b/core/site_scons/boards/discovery2.py index 5a7c2d32a..b15ebcabc 100644 --- a/core/site_scons/boards/discovery2.py +++ b/core/site_scons/boards/discovery2.py @@ -43,9 +43,18 @@ def configure( sources += [ "embed/models/model_D002_layout.c", ] - sources += [ - f"embed/trezorhal/stm32u5/displays/{display}", - ] + + if "new_rendering" in features_wanted: + sources += [ + "embed/trezorhal/xdisplay_legacy.c", + "embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_driver.c", + "embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_fb.c", + "embed/trezorhal/stm32u5/display/stm32u5a9j-dk/display_ltdc_dsi.c", + ] + else: + sources += [ + f"embed/trezorhal/stm32u5/displays/{display}", + ] if "input" in features_wanted: sources += [ @@ -85,11 +94,17 @@ def configure( defines += ["USE_DMA2D", "FRAMEBUFFER", "FRAMEBUFFER32BIT"] sources += [ "embed/trezorhal/stm32u5/dma2d.c", + "embed/trezorhal/stm32u5/dma2d_bitblt.c", ] features_available.append("dma2d") features_available.append("framebuffer") features_available.append("framebuffer32bit") + if "new_rendering" in features_wanted: + defines += ["XFRAMEBUFFER"] + features_available.append("xframebuffer") + features_available.append("display_rgba8888") + env.get("ENV")["TREZOR_BOARD"] = board env.get("ENV")["MCU_TYPE"] = mcu env.get("ENV")["LINKER_SCRIPT"] = linker_script @@ -97,4 +112,10 @@ def configure( defs = env.get("CPPDEFINES_IMPLICIT") defs += ["__ARM_FEATURE_CMSE=3"] + rust_defs = env.get("ENV")["RUST_INCLUDES"] + rust_defs += "-DFRAMEBUFFER;" + if "new_rendering" in features_wanted: + rust_defs += "-DXFRAMEBUFFER;" + env.get("ENV")["RUST_INCLUDES"] = rust_defs + return features_available diff --git a/core/site_scons/boards/trezor_r_v10.py b/core/site_scons/boards/trezor_r_v10.py index f1b9dd029..10570dc2c 100644 --- a/core/site_scons/boards/trezor_r_v10.py +++ b/core/site_scons/boards/trezor_r_v10.py @@ -17,6 +17,11 @@ def configure( board = "trezor_r_v10.h" display = "vg-2864ksweg01.c" + if "new_rendering" in features_wanted: + defines += ["XFRAMEBUFFER"] + features_available.append("xframebuffer") + features_available.append("display_mono") + mcu = "STM32F427xx" stm32f4_common_files(env, defines, sources, paths) @@ -36,7 +41,12 @@ def configure( sources += [ "embed/models/model_T2B1_layout.c", ] - sources += [f"embed/trezorhal/stm32f4/displays/{display}"] + + if "new_rendering" in features_wanted: + sources += ["embed/trezorhal/xdisplay_legacy.c"] + sources += ["embed/trezorhal/stm32f4/display/vg-2864/display_driver.c"] + else: + sources += [f"embed/trezorhal/stm32f4/displays/{display}"] sources += ["embed/trezorhal/stm32f4/i2c.c"] @@ -76,4 +86,9 @@ def configure( env.get("ENV")["TREZOR_BOARD"] = board env.get("ENV")["MCU_TYPE"] = mcu + if "new_rendering" in features_wanted: + rust_defs = env.get("ENV")["RUST_INCLUDES"] + rust_defs += "-DXFRAMEBUFFER;" + env.get("ENV")["RUST_INCLUDES"] = rust_defs + return features_available diff --git a/core/site_scons/boards/trezor_r_v3.py b/core/site_scons/boards/trezor_r_v3.py index 25e2c9dec..f2565ede1 100644 --- a/core/site_scons/boards/trezor_r_v3.py +++ b/core/site_scons/boards/trezor_r_v3.py @@ -17,6 +17,11 @@ def configure( board = "trezor_r_v3.h" display = "ug-2828tswig01.c" + if "new_rendering" in features_wanted: + defines += ["XFRAMEBUFFER"] + features_available.append("xframebuffer") + features_available.append("display_mono") + mcu = "STM32F427xx" stm32f4_common_files(env, defines, sources, paths) @@ -36,7 +41,12 @@ def configure( sources += [ "embed/models/model_T2B1_layout.c", ] - sources += [f"embed/trezorhal/stm32f4/displays/{display}"] + + if "new_rendering" in features_wanted: + sources += ["embed/trezorhal/xdisplay_legacy.c"] + sources += ["embed/trezorhal/stm32f4/display/ug-2828/display_driver.c"] + else: + sources += [f"embed/trezorhal/stm32f4/displays/{display}"] if "input" in features_wanted: sources += ["embed/trezorhal/stm32f4/button.c"] @@ -64,4 +74,9 @@ def configure( env.get("ENV")["TREZOR_BOARD"] = board env.get("ENV")["MCU_TYPE"] = mcu + if "new_rendering" in features_wanted: + rust_defs = env.get("ENV")["RUST_INCLUDES"] + rust_defs += "-DXFRAMEBUFFER;" + env.get("ENV")["RUST_INCLUDES"] = rust_defs + return features_available diff --git a/core/site_scons/boards/trezor_r_v4.py b/core/site_scons/boards/trezor_r_v4.py index 63dab36fc..e3461e5ee 100644 --- a/core/site_scons/boards/trezor_r_v4.py +++ b/core/site_scons/boards/trezor_r_v4.py @@ -17,6 +17,11 @@ def configure( board = "trezor_r_v4.h" display = "vg-2864ksweg01.c" + if "new_rendering" in features_wanted: + defines += ["XFRAMEBUFFER"] + features_available.append("xframebuffer") + features_available.append("display_mono") + mcu = "STM32F427xx" stm32f4_common_files(env, defines, sources, paths) @@ -36,7 +41,12 @@ def configure( sources += [ "embed/models/model_T2B1_layout.c", ] - sources += [f"embed/trezorhal/stm32f4/displays/{display}"] + + if "new_rendering" in features_wanted: + sources += ["embed/trezorhal/xdisplay_legacy.c"] + sources += ["embed/trezorhal/stm32f4/display/vg-2864/display_driver.c"] + else: + sources += [f"embed/trezorhal/stm32f4/displays/{display}"] if "input" in features_wanted: sources += ["embed/trezorhal/stm32f4/button.c"] @@ -60,4 +70,9 @@ def configure( env.get("ENV")["TREZOR_BOARD"] = board env.get("ENV")["MCU_TYPE"] = mcu + if "new_rendering" in features_wanted: + rust_defs = env.get("ENV")["RUST_INCLUDES"] + rust_defs += "-DXFRAMEBUFFER;" + env.get("ENV")["RUST_INCLUDES"] = rust_defs + return features_available diff --git a/core/site_scons/boards/trezor_r_v6.py b/core/site_scons/boards/trezor_r_v6.py index 6fe8f3044..3444c4bb1 100644 --- a/core/site_scons/boards/trezor_r_v6.py +++ b/core/site_scons/boards/trezor_r_v6.py @@ -17,6 +17,11 @@ def configure( board = "trezor_r_v6.h" display = "vg-2864ksweg01.c" + if "new_rendering" in features_wanted: + defines += ["XFRAMEBUFFER"] + features_available.append("xframebuffer") + features_available.append("display_mono") + mcu = "STM32F427xx" stm32f4_common_files(env, defines, sources, paths) @@ -36,7 +41,12 @@ def configure( sources += [ "embed/models/model_T2B1_layout.c", ] - sources += [f"embed/trezorhal/stm32f4/displays/{display}"] + + if "new_rendering" in features_wanted: + sources += ["embed/trezorhal/xdisplay_legacy.c"] + sources += ["embed/trezorhal/stm32f4/display/vg-2864/display_driver.c"] + else: + sources += [f"embed/trezorhal/stm32f4/displays/{display}"] if "input" in features_wanted: sources += ["embed/trezorhal/stm32f4/button.c"] @@ -60,4 +70,9 @@ def configure( env.get("ENV")["TREZOR_BOARD"] = board env.get("ENV")["MCU_TYPE"] = mcu + if "new_rendering" in features_wanted: + rust_defs = env.get("ENV")["RUST_INCLUDES"] + rust_defs += "-DXFRAMEBUFFER;" + env.get("ENV")["RUST_INCLUDES"] = rust_defs + return features_available diff --git a/core/site_scons/boards/trezor_t.py b/core/site_scons/boards/trezor_t.py index 1bf39befb..999018edb 100644 --- a/core/site_scons/boards/trezor_t.py +++ b/core/site_scons/boards/trezor_t.py @@ -18,6 +18,9 @@ def configure( hw_revision = 0 features_available.append("disp_i8080_8bit_dw") + if "new_rendering" in features_wanted: + features_available.append("display_rgb565") + mcu = "STM32F427xx" stm32f4_common_files(env, defines, sources, paths) @@ -37,20 +40,41 @@ def configure( sources += [ "embed/models/model_T2T1_layout.c", ] - sources += [f"embed/trezorhal/stm32f4/displays/{display}"] + if "new_rendering" in features_wanted: + sources += ["embed/trezorhal/xdisplay_legacy.c"] + sources += ["embed/trezorhal/stm32f4/display/st-7789/display_nofb.c"] + sources += ["embed/trezorhal/stm32f4/display/st-7789/display_driver.c"] + sources += ["embed/trezorhal/stm32f4/display/st-7789/display_io.c"] + sources += ["embed/trezorhal/stm32f4/display/st-7789/display_panel.c"] + sources += [ + "embed/trezorhal/stm32f4/display/st-7789/panels/tf15411a.c", + ] + sources += [ + "embed/trezorhal/stm32f4/display/st-7789/panels/154a.c", + ] + sources += [ + "embed/trezorhal/stm32f4/display/st-7789/panels/lx154a2411.c", + ] + sources += [ + "embed/trezorhal/stm32f4/display/st-7789/panels/lx154a2422.c", + ] + + else: + sources += [f"embed/trezorhal/stm32f4/displays/{display}"] + sources += [ + "embed/trezorhal/stm32f4/displays/panels/tf15411a.c", + ] + sources += [ + "embed/trezorhal/stm32f4/displays/panels/154a.c", + ] + sources += [ + "embed/trezorhal/stm32f4/displays/panels/lx154a2411.c", + ] + sources += [ + "embed/trezorhal/stm32f4/displays/panels/lx154a2422.c", + ] + sources += ["embed/trezorhal/stm32f4/backlight_pwm.c"] - sources += [ - "embed/trezorhal/stm32f4/displays/panels/tf15411a.c", - ] - sources += [ - "embed/trezorhal/stm32f4/displays/panels/154a.c", - ] - sources += [ - "embed/trezorhal/stm32f4/displays/panels/lx154a2411.c", - ] - sources += [ - "embed/trezorhal/stm32f4/displays/panels/lx154a2422.c", - ] features_available.append("backlight") @@ -87,6 +111,7 @@ def configure( if "dma2d" in features_wanted: defines += ["USE_DMA2D"] sources += ["embed/trezorhal/stm32f4/dma2d.c"] + sources += ["embed/trezorhal/stm32f4/dma2d_bitblt.c"] sources += [ "vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma2d.c" ] diff --git a/core/site_scons/boards/trezor_t3t1_revE.py b/core/site_scons/boards/trezor_t3t1_revE.py index e005a17eb..0af6572ae 100644 --- a/core/site_scons/boards/trezor_t3t1_revE.py +++ b/core/site_scons/boards/trezor_t3t1_revE.py @@ -20,6 +20,11 @@ def configure( features_available.append("framebuffer") defines += ["FRAMEBUFFER"] + if "new_rendering" in features_wanted: + features_available.append("xframebuffer") + features_available.append("display_rgb565") + defines += ["XFRAMEBUFFER"] + mcu = "STM32U585xx" linker_script = "stm32u58" @@ -40,11 +45,23 @@ def configure( sources += [ "embed/models/model_T3T1_layout.c", ] - sources += [f"embed/trezorhal/stm32u5/displays/{display}"] + + if "new_rendering" in features_wanted: + sources += ["embed/trezorhal/xdisplay_legacy.c"] + sources += ["embed/trezorhal/stm32u5/display/st-7789/display_fb.c"] + sources += ["embed/trezorhal/stm32u5/display/st-7789/display_driver.c"] + sources += ["embed/trezorhal/stm32u5/display/st-7789/display_io.c"] + sources += ["embed/trezorhal/stm32u5/display/st-7789/display_panel.c"] + sources += [ + "embed/trezorhal/stm32u5/display/st-7789/panels/lx154a2422.c", + ] + else: + sources += [f"embed/trezorhal/stm32u5/displays/{display}"] + sources += [ + "embed/trezorhal/stm32u5/displays/panels/lx154a2422.c", + ] + sources += ["embed/trezorhal/stm32u5/backlight_pwm.c"] - sources += [ - "embed/trezorhal/stm32u5/displays/panels/lx154a2422.c", - ] env_constraints = env.get("CONSTRAINTS") if not (env_constraints and "limited_util_s" in env_constraints): @@ -94,6 +111,7 @@ def configure( if "dma2d" in features_wanted: defines += ["USE_DMA2D"] sources += ["embed/trezorhal/stm32u5/dma2d.c"] + sources += ["embed/trezorhal/stm32u5/dma2d_bitblt.c"] features_available.append("dma2d") if "optiga" in features_wanted: @@ -114,6 +132,8 @@ def configure( rust_defs = env.get("ENV")["RUST_INCLUDES"] rust_defs += "-DFRAMEBUFFER;" + if "new_rendering" in features_wanted: + rust_defs += "-DXFRAMEBUFFER;" env.get("ENV")["RUST_INCLUDES"] = rust_defs return features_available diff --git a/core/site_scons/boards/trezor_t3t1_v4.py b/core/site_scons/boards/trezor_t3t1_v4.py index f11949c2f..ba1dd672a 100644 --- a/core/site_scons/boards/trezor_t3t1_v4.py +++ b/core/site_scons/boards/trezor_t3t1_v4.py @@ -20,6 +20,11 @@ def configure( features_available.append("framebuffer") defines += ["FRAMEBUFFER"] + if "new_rendering" in features_wanted: + features_available.append("xframebuffer") + features_available.append("display_rgb565") + defines += ["XFRAMEBUFFER"] + mcu = "STM32U585xx" linker_script = "stm32u58" @@ -41,10 +46,24 @@ def configure( "embed/models/model_T3T1_layout.c", ] sources += [f"embed/trezorhal/stm32u5/displays/{display}"] + + if "new_rendering" in features_wanted: + sources += ["embed/trezorhal/xdisplay_legacy.c"] + sources += ["embed/trezorhal/stm32u5/display/st-7789/display_fb.c"] + sources += ["embed/trezorhal/stm32u5/display/st-7789/display_driver.c"] + sources += ["embed/trezorhal/stm32u5/display/st-7789/display_io.c"] + sources += ["embed/trezorhal/stm32u5/display/st-7789/display_panel.c"] + sources += [ + "embed/trezorhal/stm32u5/display/st-7789/panels/lx154a2422.c", + ] + + else: + sources += [f"embed/trezorhal/stm32u5/displays/{display}"] + sources += [ + "embed/trezorhal/stm32u5/displays/panels/lx154a2422.c", + ] + sources += ["embed/trezorhal/stm32u5/backlight_pwm.c"] - sources += [ - "embed/trezorhal/stm32u5/displays/panels/lx154a2422.c", - ] env_constraints = env.get("CONSTRAINTS") if not (env_constraints and "limited_util_s" in env_constraints): @@ -94,6 +113,7 @@ def configure( if "dma2d" in features_wanted: defines += ["USE_DMA2D"] sources += ["embed/trezorhal/stm32u5/dma2d.c"] + sources += ["embed/trezorhal/stm32u5/dma2d_bitblt.c"] features_available.append("dma2d") if "optiga" in features_wanted: @@ -114,6 +134,8 @@ def configure( rust_defs = env.get("ENV")["RUST_INCLUDES"] rust_defs += "-DFRAMEBUFFER;" + if "new_rendering" in features_wanted: + rust_defs += "-DXFRAMEBUFFER;" env.get("ENV")["RUST_INCLUDES"] = rust_defs return features_available