diff --git a/Documentation/platforms/arm/a64/boards/pinephone/index.rst b/Documentation/platforms/arm/a64/boards/pinephone/index.rst index fa9b8417aad..c7e79a61e2c 100644 --- a/Documentation/platforms/arm/a64/boards/pinephone/index.rst +++ b/Documentation/platforms/arm/a64/boards/pinephone/index.rst @@ -12,7 +12,7 @@ Features - **GPU:** ARM Mali400 MP2 - **Interrupt Controller:** ARM GIC PL400 (Generic Interrupt Controller v2) - **Display Engine:** Allwinner Display Engine 2.0 (MIPI DSI with DMA) -- **Display:** Xingbangda XBD599 HD IPS Capacitive Touchscreen (5.95 inches, 1440x720 resolution, 16M colors) +- **Display:** Xingbangda XBD599 HD IPS Capacitive Touchscreen (5.95 inches, 1440x720 resolution, 16M colors, PWM Backlight) - **LCD Controller:** Sitronix ST7703 (MIPI DSI) - **RAM:** 2GB or 3GB LPDDR3 SDRAM - **Internal Storage:** 16GB or 32GB eMMC, extendable up to 2TB via microSD @@ -29,6 +29,7 @@ Features - **Privacy Switches:** Modem, WiFi & Bluetooth, Microphone, Cameras, Headphone - **Battery:** Lithium-ion, rated capacity 2800mAh (10.64Wh), typical capacity 3000mAh (11.40Wh) - **I/O:** USB Type-C, USB Host, DisplayPort Alternate Mode output, 15W 5V 3A Quick Charge, follows USB PD specification +- **Power Management Integrated Circuit:** X-Powers AXP803 (Reduced Serial Bus) Serial Console ============== @@ -73,7 +74,7 @@ Configure the NuttX project and build the project: .. code:: console $ cd nuttx - $ tools/configure.sh pinephone:nsh + $ tools/configure.sh pinephone:lcd $ make $ cp nuttx.bin Image $ rm -f Image.gz @@ -129,6 +130,15 @@ LED3 Blue LED PD20 Configurations ============== +lcd +___ + +Supports LCD Display (XBD599) with LCD Controller (ST7703), +Display Engine 2.0, MIPI Display Serial Interface (DSI), +Power Management Integrated Circuit (AXP803) and +Reduced Serial Bus (RSB). +Serial Console is enabled on UART0 at 115.2 kbps. + nsh --- @@ -142,14 +152,18 @@ Peripheral Support NuttX for PinePhone supports these peripherals: -============== ======= ===== -Peripheral Support NOTES -============== ======= ===== -Display Engine Yes -MIPI D-PHY Yes -MIPI DSI Yes -PIO Yes -RSB Yes -TCON0 Yes -UART Yes Only UART0 is supported -============== ======= ===== +======================= ======= ===== +Peripheral Support NOTES +======================= ======= ===== +Backlight Yes +Display Engine Yes +LCD Controller (ST7703) Yes +LCD Panel (XBD599) Yes +MIPI D-PHY Yes +MIPI DSI Yes +PIO Yes +PMIC (AXP803) Yes +RSB Yes +TCON0 Yes +UART Yes Only UART0 is supported +======================= ======= ===== diff --git a/arch/arm64/src/a64/a64_de.c b/arch/arm64/src/a64/a64_de.c index 4021778dac7..ef3fa71e219 100644 --- a/arch/arm64/src/a64/a64_de.c +++ b/arch/arm64/src/a64/a64_de.c @@ -735,7 +735,7 @@ int a64_de_blender_init(void) ****************************************************************************/ int a64_de_ui_channel_init(uint8_t channel, - void *fbmem, + const void *fbmem, size_t fblen, uint16_t xres, uint16_t yres, diff --git a/arch/arm64/src/a64/a64_de.h b/arch/arm64/src/a64/a64_de.h index eae6a213194..7daaa2d9d18 100644 --- a/arch/arm64/src/a64/a64_de.h +++ b/arch/arm64/src/a64/a64_de.h @@ -92,7 +92,7 @@ int a64_de_blender_init(void); ****************************************************************************/ int a64_de_ui_channel_init(uint8_t channel, - void *fbmem, + const void *fbmem, size_t fblen, uint16_t xres, uint16_t yres, diff --git a/arch/arm64/src/a64/hardware/a64_memorymap.h b/arch/arm64/src/a64/hardware/a64_memorymap.h index cad88735fde..8d530256ef1 100644 --- a/arch/arm64/src/a64/hardware/a64_memorymap.h +++ b/arch/arm64/src/a64/hardware/a64_memorymap.h @@ -38,10 +38,12 @@ #define A64_TCON0_ADDR 0x01c0c000 /* TCON 0 0x01c0:c000-0x01c0:cfff 4K */ #define A64_CCU_ADDR 0x01c20000 /* CCU 0x01c2:0000-0x01c2:03ff 1K */ #define A64_PIO_ADDR 0x01c20800 /* PIO 0x01c2:0800-0x01c2:0bff 1K */ +#define A64_PWM_ADDR 0x01c21400 /* PWM 0x01c2:1400-0x01c2:17ff 1K */ #define A64_DSI_ADDR 0x01ca0000 /* MIPI DSI 0x01ca:0000-0x01ca:0fff 4K */ #define A64_DPHY_ADDR 0x01ca1000 /* MIPI DSI-PHY 0x01ca:1000-0x01ca:1fff 4K */ #define A64_RPIO_ADDR 0x01f02c00 /* R_PIO 0x01f0:2c00-0x01f0:2fff 1K */ #define A64_RSB_ADDR 0x01f03400 /* R_RSB 0x01f0:3400-0x01f0:37ff 1K */ +#define A64_RPWM_ADDR 0x01f03800 /* R_PWM 0x01f0:3800-0x01f0-3bff 1K */ /**************************************************************************** * Public Types diff --git a/boards/arm64/a64/pinephone/Kconfig b/boards/arm64/a64/pinephone/Kconfig index 16dbc39161f..777d4d5a605 100644 --- a/boards/arm64/a64/pinephone/Kconfig +++ b/boards/arm64/a64/pinephone/Kconfig @@ -4,4 +4,16 @@ # if ARCH_BOARD_PINEPHONE -endif + +config PINEPHONE_LCD + bool "LCD Display" + default n + select A64_DE + select A64_RSB + select DRIVERS_VIDEO + select VIDEO_FB + select FB_OVERLAY + ---help--- + Select to enable support for LCD Display. + +endif # ARCH_BOARD_PINEPHONE diff --git a/boards/arm64/a64/pinephone/configs/lcd/defconfig b/boards/arm64/a64/pinephone/configs/lcd/defconfig new file mode 100644 index 00000000000..f9e0313b6c7 --- /dev/null +++ b/boards/arm64/a64/pinephone/configs/lcd/defconfig @@ -0,0 +1,69 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +CONFIG_A64_UART=y +CONFIG_ARCH="arm64" +CONFIG_ARCH_ARM64=y +CONFIG_ARCH_BOARD="pinephone" +CONFIG_ARCH_BOARD_PINEPHONE=y +CONFIG_ARCH_CHIP="a64" +CONFIG_ARCH_CHIP_A64=y +CONFIG_ARCH_INTERRUPTSTACK=4096 +CONFIG_BOARDCTL_RESET=y +CONFIG_BOARD_LOOPSPERMSEC=116524 +CONFIG_BUILTIN=y +CONFIG_DEBUG_ASSERTIONS=y +CONFIG_DEBUG_ERROR=y +CONFIG_DEBUG_FEATURES=y +CONFIG_DEBUG_FULLOPT=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_SCHED=y +CONFIG_DEBUG_SCHED_ERROR=y +CONFIG_DEBUG_SCHED_WARN=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DEBUG_WARN=y +CONFIG_DEFAULT_TASK_STACKSIZE=8192 +CONFIG_DEV_ZERO=y +CONFIG_EXAMPLES_HELLO=y +CONFIG_EXAMPLES_LEDS=y +CONFIG_EXPERIMENTAL=y +CONFIG_FS_PROCFS=y +CONFIG_FS_ROMFS=y +CONFIG_HAVE_CXX=y +CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_IDLETHREAD_STACKSIZE=8192 +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_INTELHEX_BINARY=y +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_READLINE=y +CONFIG_NSH_ROMFSETC=y +CONFIG_PINEPHONE_LCD=y +CONFIG_PREALLOC_TIMERS=4 +CONFIG_PTHREAD_STACK_MIN=8192 +CONFIG_RAMLOG=y +CONFIG_RAM_SIZE=134217728 +CONFIG_RAM_START=0x40000000 +CONFIG_RAW_BINARY=y +CONFIG_READLINE_CMD_HISTORY=y +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_HPWORK=y +CONFIG_SCHED_HPWORKPRIORITY=192 +CONFIG_SPINLOCK=y +CONFIG_STACK_COLORATION=y +CONFIG_START_MONTH=11 +CONFIG_START_YEAR=2022 +CONFIG_SYMTAB_ORDEREDBYNAME=y +CONFIG_SYSTEM_NSH=y +CONFIG_SYSTEM_SYSTEM=y +CONFIG_TESTING_GETPRIME=y +CONFIG_TESTING_OSTEST=y +CONFIG_UART1_SERIAL_CONSOLE=y +CONFIG_USEC_PER_TICK=1000 +CONFIG_USERLED=y +CONFIG_USERLED_LOWER=y diff --git a/boards/arm64/a64/pinephone/src/Makefile b/boards/arm64/a64/pinephone/src/Makefile index 36847e891ec..3c9fc78abee 100644 --- a/boards/arm64/a64/pinephone/src/Makefile +++ b/boards/arm64/a64/pinephone/src/Makefile @@ -37,4 +37,8 @@ ifeq ($(CONFIG_USERLED),y) CSRCS += pinephone_userleds.c endif +ifeq ($(CONFIG_VIDEO_FB),y) +CSRCS += pinephone_display.c pinephone_lcd.c pinephone_pmic.c +endif + include $(TOPDIR)/boards/Board.mk diff --git a/boards/arm64/a64/pinephone/src/pinephone_bringup.c b/boards/arm64/a64/pinephone/src/pinephone_bringup.c index 1b9f2e125a8..1ba75f1fbf5 100644 --- a/boards/arm64/a64/pinephone/src/pinephone_bringup.c +++ b/boards/arm64/a64/pinephone/src/pinephone_bringup.c @@ -34,6 +34,10 @@ # include #endif +#ifdef CONFIG_VIDEO_FB +# include +#endif + #include "pinephone.h" /**************************************************************************** @@ -72,6 +76,16 @@ int pinephone_bringup(void) } #endif +#ifdef CONFIG_VIDEO_FB + /* Initialize and register the framebuffer driver */ + + ret = fb_register(0, 0); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: fb_register() failed: %d\n", ret); + } +#endif + UNUSED(ret); return OK; } diff --git a/boards/arm64/a64/pinephone/src/pinephone_display.c b/boards/arm64/a64/pinephone/src/pinephone_display.c new file mode 100644 index 00000000000..3d8541bc119 --- /dev/null +++ b/boards/arm64/a64/pinephone/src/pinephone_display.c @@ -0,0 +1,633 @@ +/**************************************************************************** + * boards/arm64/a64/pinephone/src/pinephone_display.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Reference: + * + * "Understanding PinePhone's Display (MIPI DSI)" + * https://lupyuen.github.io/articles/dsi + * + * "NuttX RTOS for PinePhone: Display Driver in Zig" + * https://lupyuen.github.io/articles/dsi2 + * + * "Rendering PinePhone's Display (DE and TCON0)" + * https://lupyuen.github.io/articles/de + * + * "NuttX RTOS for PinePhone: Render Graphics in Zig" + * https://lupyuen.github.io/articles/de2 + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include "chip.h" +#include "arm64_internal.h" +#include "a64_de.h" +#include "a64_mipi_dphy.h" +#include "a64_mipi_dsi.h" +#include "a64_tcon0.h" +#include "pinephone_lcd.h" +#include "pinephone_pmic.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* LCD Backlight Brightness (percentage) */ + +#define BACKLIGHT_BRIGHTNESS_PERCENT 90 + +/* Number of UI Channels to render: 1 or 3 */ + +#define UI_CHANNELS 3 + +/* LCD Panel Width and Height (pixels) */ + +#define PANEL_WIDTH PINEPHONE_LCD_PANEL_WIDTH /* 720 pixels */ +#define PANEL_HEIGHT PINEPHONE_LCD_PANEL_HEIGHT /* 1440 pixels */ + +/* Framebuffer 1 Width and Height (pixels) */ + +#define FB1_WIDTH 600 +#define FB1_HEIGHT 600 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Frame Buffers for Display Engine *****************************************/ + +/* Frame Buffer 0: (Base UI Channel) + * Fullscreen 720 x 1440 (4 bytes per XRGB 8888 pixel) + */ + +static uint32_t g_pinephone_fb0[PANEL_WIDTH * PANEL_HEIGHT]; + +/* Frame Buffer 1: (First Overlay UI Channel) + * Square 600 x 600 (4 bytes per ARGB 8888 pixel) + */ + +static uint32_t g_pinephone_fb1[FB1_WIDTH * FB1_HEIGHT]; + +/* Frame Buffer 2: (Second Overlay UI Channel) + * Fullscreen 720 x 1440 (4 bytes per ARGB 8888 pixel) + */ + +static uint32_t g_pinephone_fb2[PANEL_WIDTH * PANEL_HEIGHT]; + +/* Video Controller for 3 UI Channels: + * Fullscreen 720 x 1440 (4 bytes per ARGB 8888 pixel) + */ + +static struct fb_videoinfo_s g_pinephone_video = +{ + .fmt = FB_FMT_RGBA32, /* Pixel format (XRGB 8888) */ + .xres = PANEL_WIDTH, /* Horizontal resolution in pixel columns */ + .yres = PANEL_HEIGHT, /* Vertical resolution in pixel rows */ + .nplanes = 1, /* Color planes: Base UI Channel */ + .noverlays = 2 /* Overlays: 2 Overlay UI Channels) */ +}; + +/* Color Plane for Base UI Channel: + * Fullscreen 720 x 1440 (4 bytes per XRGB 8888 pixel) + */ + +static struct fb_planeinfo_s g_pinephone_plane = +{ + .fbmem = &g_pinephone_fb0, + .fblen = sizeof(g_pinephone_fb0), + .stride = PANEL_WIDTH * 4, /* Length of a line (4-byte pixel) */ + .display = 0, /* Display number (Unused) */ + .bpp = 32, /* Bits per pixel (XRGB 8888) */ + .xres_virtual = PANEL_WIDTH, /* Virtual Horizontal resolution */ + .yres_virtual = PANEL_HEIGHT, /* Virtual Vertical resolution */ + .xoffset = 0, /* Offset from virtual to visible */ + .yoffset = 0 /* Offset from virtual to visible */ +}; + +/* Overlays for 2 Overlay UI Channels */ + +static struct fb_overlayinfo_s g_pinephone_overlays[2] = +{ + /* First Overlay UI Channel: + * Square 600 x 600 (4 bytes per ARGB 8888 pixel) + */ + + { + .fbmem = &g_pinephone_fb1, + .fblen = sizeof(g_pinephone_fb1), + .stride = FB1_WIDTH * 4, /* Length of a line (4-byte pixel) */ + .overlay = 0, /* Overlay number (First Overlay) */ + .bpp = 32, /* Bits per pixel (ARGB 8888) */ + .blank = 0, /* TODO: Blank or unblank */ + .chromakey = 0, /* TODO: Chroma key argb8888 formatted */ + .color = 0, /* TODO: Color argb8888 formatted */ + .transp = /* TODO: Transparency */ + { + .transp = 0, + .transp_mode = 0 + }, + .sarea = /* Selected area within the overlay */ + { + .x = 52, + .y = 52, + .w = FB1_WIDTH, + .h = FB1_HEIGHT + }, + .accl = 0 /* TODO: Supported hardware acceleration */ + }, + + /* Second Overlay UI Channel: + * Fullscreen 720 x 1440 (4 bytes per ARGB 8888 pixel) + */ + + { + .fbmem = &g_pinephone_fb2, + .fblen = sizeof(g_pinephone_fb2), + .stride = PANEL_WIDTH * 4, /* Length of a line (4-byte pixel) */ + .overlay = 1, /* Overlay number (First Overlay) */ + .bpp = 32, /* Bits per pixel (ARGB 8888) */ + .blank = 0, /* TODO: Blank or unblank */ + .chromakey = 0, /* TODO: Chroma key argb8888 formatted */ + .color = 0, /* TODO: Color argb8888 formatted */ + .transp = /* TODO: Transparency */ + { + .transp = 0, + .transp_mode = 0 + }, + .sarea = /* Selected area within the overlay */ + { + .x = 0, + .y = 0, + .w = PANEL_WIDTH, + .h = PANEL_HEIGHT + }, + .accl = 0 /* TODO: Supported hardware acceleration */ + } +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: test_pattern + * + * Description: + * Fill the 3 Frame Buffers with a Test Pattern. Should be called after + * Display Engine is Enabled, or the rendered image will have missing + * rows. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void test_pattern(void) +{ + int i; + int x; + int y; + const int fb0_len = sizeof(g_pinephone_fb0) / sizeof(g_pinephone_fb0[0]); + const int fb1_len = sizeof(g_pinephone_fb1) / sizeof(g_pinephone_fb1[0]); + + /* Zero the Framebuffers */ + + memset(g_pinephone_fb0, 0, sizeof(g_pinephone_fb0)); + memset(g_pinephone_fb1, 0, sizeof(g_pinephone_fb1)); + memset(g_pinephone_fb2, 0, sizeof(g_pinephone_fb2)); + + /* Init Framebuffer 0: + * Fill with Blue, Green and Red + */ + + for (i = 0; i < fb0_len; i++) + { + /* Colours are in XRGB 8888 format */ + + if (i < fb0_len / 4) + { + /* Blue for top quarter */ + + g_pinephone_fb0[i] = 0x80000080; + } + else if (i < fb0_len / 2) + { + /* Green for next quarter */ + + g_pinephone_fb0[i] = 0x80008000; + } + else + { + /* Red for lower half */ + + g_pinephone_fb0[i] = 0x80800000; + } + + /* Fixes missing rows in the rendered image, not sure why */ + + ARM64_DMB(); + ARM64_DSB(); + ARM64_ISB(); + } + + /* Init Framebuffer 1: + * Fill with Semi-Transparent White + */ + + for (i = 0; i < fb1_len; i++) + { + /* Semi-Transparent White in ARGB 8888 format */ + + g_pinephone_fb1[i] = 0x40ffffff; + + /* Fixes missing rows in the rendered image, not sure why */ + + ARM64_DMB(); + ARM64_DSB(); + ARM64_ISB(); + } + + /* Init Framebuffer 2: + * Fill with Semi-Transparent Green Circle + */ + + for (y = 0; y < PANEL_HEIGHT; y++) + { + for (x = 0; x < PANEL_WIDTH; x++) + { + /* Get pixel index */ + + const int p = (y * PANEL_WIDTH) + x; + + /* Shift coordinates so that centre of screen is (0,0) */ + + const int half_width = PANEL_WIDTH / 2; + const int half_height = PANEL_HEIGHT / 2; + const int x_shift = x - half_width; + const int y_shift = y - half_height; + + /* If x^2 + y^2 < radius^2, set to Semi-Transparent Green */ + + if (x_shift*x_shift + y_shift*y_shift < half_width*half_width) + { + /* Semi-Transparent Green in ARGB 8888 Format */ + + g_pinephone_fb2[p] = 0x80008000; + } + else /* Otherwise set to Transparent Black */ + { + /* Transparent Black in ARGB 8888 Format */ + + g_pinephone_fb2[p] = 0x00000000; + } + + /* Fixes missing rows in the rendered image, not sure why */ + + ARM64_DMB(); + ARM64_DSB(); + ARM64_ISB(); + } + } +} + +/**************************************************************************** + * Name: render_framebuffers + * + * Description: + * Render the 3 Frame Buffers with the Display Engine. + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +static int render_framebuffers(void) +{ + int i; + int ret; + const int overlay_len = sizeof(g_pinephone_overlays) / + sizeof(g_pinephone_overlays[0]); + + /* Validate the Frame Buffer Sizes */ + + DEBUGASSERT(UI_CHANNELS == 1 || UI_CHANNELS == 3); + DEBUGASSERT(g_pinephone_plane.xres_virtual == g_pinephone_video.xres); + DEBUGASSERT(g_pinephone_plane.yres_virtual == g_pinephone_video.yres); + DEBUGASSERT(g_pinephone_plane.fblen == + g_pinephone_plane.xres_virtual * + g_pinephone_plane.yres_virtual * 4); + DEBUGASSERT(g_pinephone_plane.stride == + g_pinephone_plane.xres_virtual * 4); + DEBUGASSERT(g_pinephone_overlays[0].fblen == + (g_pinephone_overlays[0].sarea.w) * + g_pinephone_overlays[0].sarea.h * 4); + DEBUGASSERT(g_pinephone_overlays[0].stride == + g_pinephone_overlays[0].sarea.w * 4); + DEBUGASSERT(g_pinephone_overlays[1].fblen == + (g_pinephone_overlays[1].sarea.w) * + g_pinephone_overlays[1].sarea.h * 4); + DEBUGASSERT(g_pinephone_overlays[1].stride == + g_pinephone_overlays[1].sarea.w * 4); + + /* Init the UI Blender for Display Engine */ + + ret = a64_de_blender_init(); + if (ret < 0) + { + gerr("Init UI Blender failed: %d\n", ret); + return ret; + } + + /* Init the Base UI Channel. Frame Buffer Address should be 32-bit. */ + + ret = a64_de_ui_channel_init(1, /* UI Channel 1 (Base UI Channel) */ + g_pinephone_plane.fbmem, + g_pinephone_plane.fblen, + g_pinephone_plane.xres_virtual, + g_pinephone_plane.yres_virtual, + g_pinephone_plane.xoffset, + g_pinephone_plane.yoffset); + if (ret < 0) + { + gerr("Init UI Channel 1 failed: %d\n", ret); + return ret; + } + + /* Init the 2 Overlay UI Channels */ + + for (i = 0; i < overlay_len; i++) + { + const struct fb_overlayinfo_s *ov = &g_pinephone_overlays[i]; + + /* Pass Frame Buffer as null if UI Channel should be disabled */ + + const void *fb = (UI_CHANNELS == 3) ? ov->fbmem : NULL; + + ret = a64_de_ui_channel_init(i + 2, /* UI Channel 2 or 3 */ + fb, /* 32-bit address */ + ov->fblen, /* Frame Buffer length */ + ov->sarea.w, /* Horiz res */ + ov->sarea.h, /* Vert res */ + ov->sarea.x, /* Horiz offset */ + ov->sarea.y); /* Vert offset */ + if (ret < 0) + { + gerr("Init UI Channel %d failed: %d\n", i + 2, ret); + return ret; + } + } + + /* Set UI Blender Route, enable Blender Pipes and apply the settings */ + + ret = a64_de_enable(UI_CHANNELS); + if (ret < 0) + { + gerr("Enable Display Engine failed: %d\n", ret); + return ret; + } + + /* Fill Frame Buffers with Test Pattern. Should be called after + * Display Engine is Enabled, or the rendered image will have + * missing rows. + */ + + test_pattern(); + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_fbinitialize + * + * Description: + * Initialize the framebuffer video hardware associated with the display. + * + * There are multiple logic paths that may call up_fbinitialize() so any + * implementation of up_fbinitialize() should be tolerant of being called + * multiple times. + * + * Input Parameters: + * display - In the case of hardware with multiple displays, this + * specifies the display. Normally this is zero. + * + * Returned Value: + * Zero is returned on success; a negated errno value is returned on any + * failure. + * + ****************************************************************************/ + +int up_fbinitialize(int display) +{ + int ret; + static bool initialized = false; + + /* Allow multiple calls */ + + DEBUGASSERT(display == 0); + if (initialized) + { + return OK; + } + + initialized = true; + + /* Turn on Display Backlight */ + + ret = pinephone_lcd_backlight_enable(BACKLIGHT_BRIGHTNESS_PERCENT); + if (ret < 0) + { + gerr("Enable Backlight failed: %d\n", ret); + return ret; + } + + /* Init Timing Controller TCON0 */ + + ret = a64_tcon0_init(PANEL_WIDTH, PANEL_HEIGHT); + if (ret < 0) + { + gerr("Init Timing Controller TCON0 failed: %d\n", ret); + return ret; + } + + /* Reset LCD Panel to Low */ + + ret = pinephone_lcd_panel_reset(false); + if (ret < 0) + { + gerr("Reset LCD Panel failed: %d\n", ret); + return ret; + } + + /* Init PMIC */ + + ret = pinephone_pmic_init(); + if (ret < 0) + { + gerr("Init PMIC failed: %d\n", ret); + return ret; + } + + /* Wait 15 milliseconds for power supply and power-on init */ + + up_mdelay(15); + + /* Enable MIPI DSI */ + + ret = a64_mipi_dsi_enable(); + if (ret < 0) + { + gerr("Enable MIPI DSI failed: %d\n", ret); + return ret; + } + + /* Enable MIPI D-PHY */ + + ret = a64_mipi_dphy_enable(); + if (ret < 0) + { + gerr("Enable MIPI D-PHY failed: %d\n", ret); + return ret; + } + + /* Reset LCD Panel to High */ + + ret = pinephone_lcd_panel_reset(true); + if (ret < 0) + { + gerr("Reset LCD Panel failed: %d\n", ret); + return ret; + } + + /* Wait 15 milliseconds for LCD Panel */ + + up_mdelay(15); + + /* Initialise ST7703 LCD Controller */ + + ret = pinephone_lcd_panel_init(); + if (ret < 0) + { + gerr("Init ST7703 LCD Controller failed: %d\n", ret); + return ret; + } + + /* Start MIPI DSI Bus in HSC and HSD modes */ + + ret = a64_mipi_dsi_start(); + if (ret < 0) + { + gerr("Start MIPI DSI failed: %d\n", ret); + return ret; + } + + /* Init Display Engine */ + + ret = a64_de_init(); + if (ret < 0) + { + gerr("Init Display Engine failed: %d\n", ret); + return ret; + } + + /* Wait 160 milliseconds for Display Engine */ + + up_mdelay(160); + + /* Render Frame Buffers with Display Engine */ + + ret = render_framebuffers(); + if (ret < 0) + { + gerr("Display Engine Frame Buffers failed: %d\n", ret); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Name: up_fbgetvplane + * + * Description: + * Return a a reference to the framebuffer object for the specified video + * plane of the specified plane. Many OSDs support multiple planes of + * video. + * + * Input Parameters: + * display - In the case of hardware with multiple displays, this + * specifies the display. Normally this is zero. + * vplane - Identifies the plane being queried. + * + * Returned Value: + * A non-NULL pointer to the frame buffer access structure is returned on + * success; NULL is returned on any failure. + * + ****************************************************************************/ + +struct fb_vtable_s *up_fbgetvplane(int display, int vplane) +{ + /* TODO: Implement up_fbgetvplane */ + + DEBUGASSERT(display == 0); + _err("up_fbgetvplane not implemented\n"); + + return NULL; +} + +/**************************************************************************** + * Name: up_fbuninitialize + * + * Description: + * Uninitialize the framebuffer support for the specified display. + * + * Input Parameters: + * display - In the case of hardware with multiple displays, this + * specifies the display. Normally this is zero. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void up_fbuninitialize(int display) +{ + /* Uninitialize is not supported */ + + UNUSED(display); +} diff --git a/boards/arm64/a64/pinephone/src/pinephone_lcd.c b/boards/arm64/a64/pinephone/src/pinephone_lcd.c new file mode 100644 index 00000000000..ec8b6c6788c --- /dev/null +++ b/boards/arm64/a64/pinephone/src/pinephone_lcd.c @@ -0,0 +1,1017 @@ +/**************************************************************************** + * boards/arm64/a64/pinephone/src/pinephone_lcd.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Reference: + * + * "Understanding PinePhone's Display (MIPI DSI)" + * https://lupyuen.github.io/articles/dsi + * + * "NuttX RTOS for PinePhone: Display Driver in Zig" + * https://lupyuen.github.io/articles/dsi2 + * + * "Rendering PinePhone's Display (DE and TCON0)" + * https://lupyuen.github.io/articles/de + * + * "NuttX RTOS for PinePhone: Render Graphics in Zig" + * https://lupyuen.github.io/articles/de2 + * + * "A64 Page" refers to Allwinner A64 User Manual + * https://lupyuen.github.io/images/Allwinner_A64_User_Manual_V1.1.pdf + * + * "ST7703 Page" refers to Sitronix ST7703 Data Sheet + * https://lupyuen.github.io/images/ST7703_DS_v01_20160128.pdf + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include "chip.h" +#include "arm64_internal.h" +#include "a64_mipi_dsi.h" +#include "a64_pio.h" +#include "pinephone_lcd.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Period of LCD Backlight PWM (Number of cycles of PWM Clock) */ + +#define BACKLIGHT_PWM_PERIOD 1199 + +/* LCD Panel Reset connected on PD23 */ + +#define LCD_RESET (PIO_OUTPUT | PIO_PULL_NONE | PIO_DRIVE_MEDLOW | \ + PIO_INT_NONE | PIO_OUTPUT_SET | PIO_PORT_PIOD | \ + PIO_PIN23) + +/* LCD Backlight PWM connected on PL10 */ + +#define LCD_PWM (PIO_PWM | PIO_PULL_NONE | PIO_DRIVE_MEDLOW | \ + PIO_INT_NONE | PIO_OUTPUT_SET | PIO_PORT_PIOL | \ + PIO_PIN10) + +/* LCD Backlight Enable connected on PH10 */ + +#define LCD_BL_EN (PIO_OUTPUT | PIO_PULL_NONE | PIO_DRIVE_MEDLOW | \ + PIO_INT_NONE | PIO_OUTPUT_SET | PIO_PORT_PIOH | \ + PIO_PIN10) + +/* A64 R_PWM Registers and Bit Definitions **********************************/ + +/* R_PWM Control Register (Undocumented) + * Assume same as PWM Control Register (A64 Page 194) + */ + +#define R_PWM_CTRL_REG (A64_RPWM_ADDR + 0) +#define PWM_CH0_PRESCAL(n) ((n) << 0) +#define PWM_CH0_EN (1 << 4) +#define SCLK_CH0_GATING (1 << 6) + +/* R_PWM Channel 0 Period Register (Undocumented) + * Assume same as PWM Channel 0 Period Register (A64 Page 195) + */ + +#define R_PWM_CH0_PERIOD (A64_RPWM_ADDR + 4) +#define PWM_CH0_ENTIRE_ACT_CYS(n) ((n) << 0) +#define PWM_CH0_ENTIRE_CYS(n) ((n) << 16) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Initialization Command for Sitronix ST7703 LCD Controller ****************/ + +struct pinephone_cmd_s +{ + const uint8_t *cmd; /* ST7703 Command and Parameters */ + uint8_t len; /* Length of Command and Parameters */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Initialization Commands for Sitronix ST7703 LCD Controller ***************/ + +/* Command #1: SETEXTC (ST7703 Page 131) + * Enable USER Command + */ + +static const uint8_t g_pinephone_setextc[] = +{ + 0xb9, /* SETEXTC (ST7703 Page 131): Enable USER Command */ + 0xf1, /* Enable User command */ + 0x12, /* (Continued) */ + 0x83 /* (Continued) */ +}; + +/* Command #2: SETMIPI (ST7703 Page 144) + * Set MIPI related register + */ + +static const uint8_t g_pinephone_setmipi[] = +{ + 0xba, /* SETMIPI (ST7703 Page 144): Set MIPI related register */ + 0x33, /* Virtual Channel = 0 (VC_Main = 0); + * Number of Lanes = 4 (Lane_Number = 3) */ + 0x81, /* LDO = 1.7 V (DSI_LDO_SEL = 4); + * Terminal Resistance = 90 Ohm (RTERM = 1) */ + 0x05, /* MIPI Low High Speed driving ability = x6 (IHSRX = 5) */ + 0xf9, /* TXCLK speed in DSI LP mode = fDSICLK / 16 (Tx_clk_sel = 2) */ + 0x0e, /* Min HFP number in DSI mode = 14 (HFP_OSC = 14) */ + 0x0e, /* Min HBP number in DSI mode = 14 (HBP_OSC = 14) */ + 0x20, /* Undocumented */ + 0x00, /* Undocumented */ + 0x00, /* Undocumented */ + 0x00, /* Undocumented */ + 0x00, /* Undocumented */ + 0x00, /* Undocumented */ + 0x00, /* Undocumented */ + 0x00, /* Undocumented */ + 0x44, /* Undocumented */ + 0x25, /* Undocumented */ + 0x00, /* Undocumented */ + 0x91, /* Undocumented */ + 0x0a, /* Undocumented */ + 0x00, /* Undocumented */ + 0x00, /* Undocumented */ + 0x02, /* Undocumented */ + 0x4f, /* Undocumented */ + 0x11, /* Undocumented */ + 0x00, /* Undocumented */ + 0x00, /* Undocumented */ + 0x37 /* Undocumented */ +}; + +/* Command #3: SETPOWER_EXT (ST7703 Page 142) + * Set display related register + */ + +static const uint8_t g_pinephone_setpower_ext[] = +{ + 0xb8, /* SETPOWER_EXT: Set display related register */ + 0x25, /* External power IC or PFM: VSP = FL1002, VSN = FL1002 (PCCS = 2); + * VCSW1 / VCSW2 Frequency for Pumping VSP / VSN = 1/4 Hsync + * (ECP_DC_DIV = 5) */ + 0x22, /* VCSW1/VCSW2 soft start time = 15 ms (DT = 2) ; Pumping ratio of + * VSP / VSN with VCI = x2 (XDK_ECP = 1) */ + 0x20, /* PFM operation frequency FoscD = Fosc/1 (PFM_DC_DIV = 0) */ + 0x03 /* Enable power IC pumping frequency synchronization = + * Synchronize with external Hsync (ECP_SYNC_EN = 1); + * Enable VGH/VGL pumping frequency synchronization = + * Synchronize with external Hsync (VGX_SYNC_EN = 1) */ +}; + +/* Command #4: SETRGBIF (ST7703 Page 134) + * Control RGB I/F porch timing for internal use + */ + +static const uint8_t g_pinephone_setrgbif[] = +{ + 0xb3, /* SETRGBIF: Control RGB I/F porch timing for internal use */ + 0x10, /* Vertical back porch HS number in Blank Frame Period = + * Hsync number 16 (VBP_RGB_GEN = 16) */ + 0x10, /* Vertical front porch HS number in Blank Frame Period = + * Hsync number 16 (VFP_RGB_GEN = 16) */ + 0x05, /* HBP OSC number in Blank Frame Period = OSC number 5 + * (DE_BP_RGB_GEN = 5) */ + 0x05, /* HFP OSC number in Blank Frame Period = OSC number 5 + * (DE_FP_RGB_GEN = 5) */ + 0x03, /* Undocumented */ + 0xff, /* Undocumented */ + 0x00, /* Undocumented */ + 0x00, /* Undocumented */ + 0x00, /* Undocumented */ + 0x00 /* Undocumented */ +}; + +/* Command #5: SETSCR (ST7703 Page 147) + * Set related setting of Source driving + */ + +static const uint8_t g_pinephone_setscr[] = +{ + 0xc0, /* SETSCR: Set related setting of Source driving */ + 0x73, /* Source OP Amp driving period for positive polarity in + * Normal Mode: Source OP Period = 115*4/Fosc (N_POPON = 115) */ + 0x73, /* Source OP Amp driving period for negative polarity in + * Normal Mode: Source OP Period = 115*4/Fosc (N_NOPON = 115) */ + 0x50, /* Source OP Amp driving period for positive polarity in + * Idle mode: Source OP Period = 80*4/Fosc (I_POPON = 80) */ + 0x50, /* Source OP Amp dirivng period for negative polarity in + * Idle Mode: Source OP Period = 80*4/Fosc (I_NOPON = 80) */ + 0x00, /* (SCR Bits 24-31 = 0x00) */ + 0xc0, /* (SCR Bits 16-23 = 0xC0) */ + 0x08, /* Gamma bias current fine tune: Current xIbias = 4 + * (SCR Bits 9-13 = 4) ; (SCR Bits 8-15 = 0x08) */ + 0x70, /* Source and Gamma bias current core tune: Ibias = 1 + * (SCR Bits 0-3 = 0) ; Source bias current fine tune: + * Current xIbias = 7 (SCR Bits 4-8 = 7) ; + * (SCR Bits 0-7 = 0x70) */ + 0x00 /* Undocumented */ +}; + +/* Command #6: SETVDC (ST7703 Page 146) + * Control NVDDD/VDDD Voltage + */ + +static const uint8_t g_pinephone_setvdc[] = +{ + 0xbc, /* SETVDC: Control NVDDD/VDDD Voltage */ + 0x4e /* NVDDD voltage = -1.8 V (NVDDD_SEL = 4); + * VDDD voltage = 1.9 V (VDDD_SEL = 6) */ +}; + +/* Command #7: SETPANEL (ST7703 Page 154) + * Set display related register + */ + +static const uint8_t g_pinephone_setpanel[] = +{ + 0xcc, /* SETPANEL: Set display related register */ + 0x0b /* Enable reverse the source scan direction (SS_PANEL = 1); + * Normal vertical scan direction (GS_PANEL = 0); + * Normally black panel (REV_PANEL = 1); + * S1:S2:S3 = B:G:R (BGR_PANEL = 1) */ +}; + +/* Command #8: SETCYC (ST7703 Page 135) + * Control display inversion type + */ + +static const uint8_t g_pinephone_setcyc[] = +{ + 0xb4, /* SETCYC: Control display inversion type */ + 0x80 /* Extra source for Zig-Zag Inversion = S2401 (ZINV_S2401_EN = 1); + * Row source data dislocates = Even row (ZINV_G_EVEN_EN = 0); + * Disable Zig-Zag Inversion (ZINV_EN = 0); + * Enable Zig-Zag1 Inversion (ZINV2_EN = 0); + * Normal mode inversion type = Column inversion (N_NW = 0) */ +}; + +/* Command #9: SETDISP (ST7703 Page 132) + * Control the display resolution + */ + +static const uint8_t g_pinephone_setdisp[] = +{ + 0xb2, /* SETDISP: Control the display resolution */ + 0xf0, /* Gate number of vertical direction = 480 + (240*4) (NL = 240) */ + 0x12, /* (RES_V_LSB = 0); + * Non-display area source output control: + * Source output = VSSD (BLK_CON = 1); + * Channel number of source direction = 720RGB (RESO_SEL = 2) */ + 0xf0 /* Source voltage during Blanking Time when accessing Sleep-Out / + * Sleep-In command = GND (WHITE_GND_EN = 1); + * Blank timing control when access sleep out command: + * Blank Frame Period = 7 Frames (WHITE_FRAME_SEL = 7); + * Source output refresh control: + * Refresh Period = 0 Frames (ISC = 0) */ +}; + +/* Command #10: SETEQ (ST7703 Page 159) + * Set EQ related register + */ + +static const uint8_t g_pinephone_seteq[] = +{ + 0xe3, /* SETEQ: Set EQ related register */ + 0x00, /* Temporal spacing between HSYNC and PEQGND = 0*4/Fosc + * (PNOEQ = 0) */ + 0x00, /* Temporal spacing between HSYNC and NEQGND = 0*4/Fosc + * (NNOEQ = 0) */ + 0x0b, /* Source EQ GND period when Source up to positive voltage = + * 11*4/Fosc (PEQGND = 11) */ + 0x0b, /* Source EQ GND period when Source down to negative voltage = + * 11*4/Fosc (NEQGND = 11) */ + 0x10, /* Source EQ VCI period when Source up to positive voltage = + * 16*4/Fosc (PEQVCI = 16) */ + 0x10, /* Source EQ VCI period when Source down to negative voltage = + * 16*4/Fosc (NEQVCI = 16) */ + 0x00, /* Temporal period of PEQVCI1 = 0*4/Fosc (PEQVCI1 = 0) */ + 0x00, /* Temporal period of NEQVCI1 = 0*4/Fosc (NEQVCI1 = 0) */ + 0x00, /* (Reserved) */ + 0x00, /* (Reserved) */ + 0xff, /* (Undocumented) */ + 0x00, /* (Reserved) */ + 0xc0, /* White pattern to protect GOA glass (ESD_DET_DATA_WHITE = 1); + * Enable ESD detection function to protect GOA glass + * (ESD_WHITE_EN = 1) */ + 0x10 /* No Need VSYNC (additional frame) after Sleep-In command + * to display sleep-in blanking frame then into Sleep-In State + * (SLPIN_OPTION = 1); + * Enable video function detection (VEDIO_NO_CHECK_EN = 0); + * Disable ESD white pattern scanning voltage pull ground + * (ESD_WHITE_GND_EN = 0); + * ESD detection function period = 0 Frames + * (ESD_DET_TIME_SEL = 0) */ +}; + +/* Command #11: Undocumented */ + +static const uint8_t g_pinephone_c6[] = +{ + 0xc6, /* Undocumented */ + 0x01, /* Undocumented */ + 0x00, /* Undocumented */ + 0xff, /* Undocumented */ + 0xff, /* Undocumented */ + 0x00 /* Undocumented */ +}; + +/* Command #12: SETPOWER (ST7703 Page 149) + * Set related setting of power + */ + +static const uint8_t g_pinephone_setpower[] = +{ + 0xc1, /* SETPOWER: Set related setting of power */ + 0x74, /* VGH Voltage Adjustment = 17 V (VBTHS = 7); + * VGL Voltage Adjustment = -11 V (VBTLS = 4) */ + 0x00, /* Enable VGH feedback voltage detection. + * Output voltage = VBTHS (FBOFF_VGH = 0); + * Enable VGL feedback voltage detection. + * Output voltage = VBTLS (FBOFF_VGL = 0) */ + 0x32, /* VSPROUT Voltage = (VRH[5:0] x 0.05 + 3.3) x (VREF/4.8) + * if VREF [4]=0 (VRP = 50) */ + 0x32, /* VSNROUT Voltage = (VRH[5:0] x 0.05 + 3.3) x (VREF/5.6) + * if VREF [4]=1 (VRN = 50) */ + 0x77, /* Undocumented */ + 0xf1, /* Enable VGL voltage Detect Function = + * VGL voltage Abnormal (VGL_DET_EN = 1); + * Enable VGH voltage Detect Function = + * VGH voltage Abnormal (VGH_DET_EN = 1); + * Enlarge VGL Voltage at "FBOFF_VGL=1" = + * "VGL=-15V" (VGL_TURBO = 1); + * Enlarge VGH Voltage at "FBOFF_VGH=1" = + * "VGH=20V" (VGH_TURBO = 1); (APS = 1) */ + 0xff, /* Left side VGH stage 1 pumping frequency = + * 1.5 MHz (VGH1_L_DIV = 15); + * Left side VGL stage 1 pumping frequency = + * 1.5 MHz (VGL1_L_DIV = 15) */ + 0xff, /* Right side VGH stage 1 pumping frequency = + * 1.5 MHz (VGH1_R_DIV = 15); + * Right side VGL stage 1 pumping frequency = + * 1.5 MHz (VGL1_R_DIV = 15) */ + 0xcc, /* Left side VGH stage 2 pumping frequency = + * 2.6 MHz (VGH2_L_DIV = 12); + * Left side VGL stage 2 pumping frequency = + * 2.6 MHz (VGL2_L_DIV = 12) */ + 0xcc, /* Right side VGH stage 2 pumping frequency = + * 2.6 MHz (VGH2_R_DIV = 12); + * Right side VGL stage 2 pumping frequency = + * 2.6 MHz (VGL2_R_DIV = 12) */ + 0x77, /* Left side VGH stage 3 pumping frequency = + * 4.5 MHz (VGH3_L_DIV = 7); + * Left side VGL stage 3 pumping frequency = + * 4.5 MHz (VGL3_L_DIV = 7) */ + 0x77 /* Right side VGH stage 3 pumping frequency = + * 4.5 MHz (VGH3_R_DIV = 7); + * Right side VGL stage 3 pumping frequency = + * 4.5 MHz (VGL3_R_DIV = 7) */ +}; + +/* Command #13: SETBGP (ST7703 Page 136) + * Internal reference voltage setting + */ + +static const uint8_t g_pinephone_setbgp[] = +{ + 0xb5, /* SETBGP: Internal reference voltage setting */ + 0x07, /* VREF Voltage: 4.2 V (VREF_SEL = 7) */ + 0x07 /* NVREF Voltage: 4.2 V (NVREF_SEL = 7) */ +}; + +/* Command #14: SETVCOM (ST7703 Page 137) + * Set VCOM Voltage + */ + +static const uint8_t g_pinephone_setvcom[] = +{ + 0xb6, /* SETVCOM: Set VCOM Voltage */ + 0x2c, /* VCOMDC voltage at "GS_PANEL=0" = -0.67 V (VCOMDC_F = 0x2C) */ + 0x2c /* VCOMDC voltage at "GS_PANEL=1" = -0.67 V (VCOMDC_B = 0x2C) */ +}; + +/* Command #15: Undocumented */ + +static const uint8_t g_pinephone_bf[] = +{ + 0xbf, /* Undocumented */ + 0x02, /* Undocumented */ + 0x11, /* Undocumented */ + 0x00 /* Undocumented */ +}; + +/* Command #16: SETGIP1 (ST7703 Page 163) + * Set forward GIP timing + */ + +static const uint8_t g_pinephone_setgip1[] = +{ + 0xe9, /* SETGIP1: Set forward GIP timing */ + 0x82, /* SHR0, SHR1, CHR, CHR2 refer to Internal DE (REF_EN = 1); + * (PANEL_SEL = 2) */ + 0x10, /* Starting position of GIP STV group 0 = 4102 HSYNC + * (SHR0 Bits 8-12 = 0x10) */ + 0x06, /* (SHR0 Bits 0-7 = 0x06) */ + 0x05, /* Starting position of GIP STV group 1 = 1442 HSYNC + * (SHR1 Bits 8-12 = 0x05) */ + 0xa2, /* (SHR1 Bits 0-7 = 0xA2) */ + 0x0a, /* Distance of STV rising edge and HYSNC = 10*2 Fosc + * (SPON Bits 0-7 = 0x0A) */ + 0xa5, /* Distance of STV falling edge and HYSNC = 165*2 Fosc + * (SPOFF Bits 0-7 = 0xA5) */ + 0x12, /* STV0_1 distance with STV0_0 = 1 HSYNC (SHR0_1 = 1); + * STV0_2 distance with STV0_0 = 2 HSYNC (SHR0_2 = 2) */ + 0x31, /* STV0_3 distance with STV0_0 = 3 HSYNC (SHR0_3 = 3); + * STV1_1 distance with STV1_0 = 1 HSYNC (SHR1_1 = 1) */ + 0x23, /* STV1_2 distance with STV1_0 = 2 HSYNC (SHR1_2 = 2); + * STV1_3 distance with STV1_0 = 3 HSYNC (SHR1_3 = 3) */ + 0x37, /* STV signal high pulse width = 3 HSYNC (SHP = 3); + * Total number of STV signal = 7 (SCP = 7) */ + 0x83, /* Starting position of GIP CKV group 0 (CKV0_0) = 131 HSYNC + * (CHR = 0x83) */ + 0x04, /* Distance of CKV rising edge and HYSNC = 4*2 Fosc + * (CON Bits 0-7 = 0x04) */ + 0xbc, /* Distance of CKV falling edge and HYSNC = 188*2 Fosc + * (COFF Bits 0-7 = 0xBC) */ + 0x27, /* CKV signal high pulse width = 2 HSYNC (CHP = 2); + * Total period cycle of CKV signal = 7 HSYNC (CCP = 7) */ + 0x38, /* Extra gate counter at blanking area: Gate number = 56 + * (USER_GIP_GATE = 0x38) */ + 0x0c, /* Left side GIP output pad signal = ? (CGTS_L Bits 16-21 = 0x0C) */ + 0x00, /* (CGTS_L Bits 8-15 = 0x00) */ + 0x03, /* (CGTS_L Bits 0-7 = 0x03) */ + 0x00, /* Normal polarity of Left side GIP output pad signal + * (CGTS_INV_L Bits 16-21 = 0x00) */ + 0x00, /* (CGTS_INV_L Bits 8-15 = 0x00) */ + 0x00, /* (CGTS_INV_L Bits 0-7 = 0x00) */ + 0x0c, /* Right side GIP output pad signal = ? + * (CGTS_R Bits 16-21 = 0x0C) */ + 0x00, /* (CGTS_R Bits 8-15 = 0x00) */ + 0x03, /* (CGTS_R Bits 0-7 = 0x03) */ + 0x00, /* Normal polarity of Right side GIP output pad signal + * (CGTS_INV_R Bits 16-21 = 0x00) */ + 0x00, /* (CGTS_INV_R Bits 8-15 = 0x00) */ + 0x00, /* (CGTS_INV_R Bits 0-7 = 0x00) */ + 0x75, /* Left side GIP output pad signal = ? (COS1_L = 7); + * Left side GIP output pad signal = ? (COS2_L = 5) */ + 0x75, /* Left side GIP output pad signal = ? (COS3_L = 7); (COS4_L = 5) */ + 0x31, /* Left side GIP output pad signal = ? (COS5_L = 3); (COS6_L = 1) */ + 0x88, /* Reserved (Parameter 32) */ + 0x88, /* Reserved (Parameter 33) */ + 0x88, /* Reserved (Parameter 34) */ + 0x88, /* Reserved (Parameter 35) */ + 0x88, /* Reserved (Parameter 36) */ + 0x88, /* Left side GIP output pad signal = ? (COS17_L = 8); + * Left side GIP output pad signal = ? (COS18_L = 8) */ + 0x13, /* Left side GIP output pad signal = ? (COS19_L = 1); + * Left side GIP output pad signal = ? (COS20_L = 3) */ + 0x88, /* Left side GIP output pad signal = ? (COS21_L = 8); + * Left side GIP output pad signal = ? (COS22_L = 8) */ + 0x64, /* Right side GIP output pad signal = ? (COS1_R = 6); + * Right side GIP output pad signal = ? (COS2_R = 4) */ + 0x64, /* Right side GIP output pad signal = ? (COS3_R = 6); + * Right side GIP output pad signal = ? (COS4_R = 4) */ + 0x20, /* Right side GIP output pad signal = ? (COS5_R = 2); + * Right side GIP output pad signal = ? (COS6_R = 0) */ + 0x88, /* Reserved (Parameter 43) */ + 0x88, /* Reserved (Parameter 44) */ + 0x88, /* Reserved (Parameter 45) */ + 0x88, /* Reserved (Parameter 46) */ + 0x88, /* Reserved (Parameter 47) */ + 0x88, /* Right side GIP output pad signal = ? (COS17_R = 8); + * Right side GIP output pad signal = ? (COS18_R = 8) */ + 0x02, /* Right side GIP output pad signal = ? (COS19_R = 0); + * Right side GIP output pad signal = ? (COS20_R = 2) */ + 0x88, /* Right side GIP output pad signal = ? (COS21_R = 8); + * Right side GIP output pad signal = ? (COS22_R = 8) */ + 0x00, /* (TCON_OPT = 0x00) */ + 0x00, /* (GIP_OPT Bits 16-22 = 0x00) */ + 0x00, /* (GIP_OPT Bits 8-15 = 0x00) */ + 0x00, /* (GIP_OPT Bits 0-7 = 0x00) */ + 0x00, /* Starting position of GIP CKV group 1 (CKV1_0) = 0 HSYNC + * (CHR2 = 0x00) */ + 0x00, /* Distance of CKV1 rising edge and HYSNC = 0*2 Fosc + * (CON2 Bits 0-7 = 0x00) */ + 0x00, /* Distance of CKV1 falling edge and HYSNC = 0*2 Fosc + * (COFF2 Bits 0-7 = 0x00) */ + 0x00, /* CKV1 signal high pulse width = 0 HSYNC (CHP2 = 0); + * Total period cycle of CKV1 signal = 0 HSYNC (CCP2 = 0) */ + 0x00, /* (CKS Bits 16-21 = 0x00) */ + 0x00, /* (CKS Bits 8-15 = 0x00) */ + 0x00, /* (CKS Bits 0-7 = 0x00) */ + 0x00, /* (COFF Bits 8-9 = 0); (CON Bits 8-9 = 0); + * (SPOFF Bits 8-9 = 0); (SPON Bits 8-9 = 0) */ + 0x00 /* (COFF2 Bits 8-9 = 0); (CON2 Bits 8-9 = 0) */ +}; + +/* Command #17: SETGIP2 (ST7703 Page 170) + * Set backward GIP timing + */ + +static const uint8_t g_pinephone_setgip2[] = +{ + 0xea, /* SETGIP2: Set backward GIP timing */ + 0x02, /* YS2 Signal Mode = INYS1/INYS2 (YS2_SEL = 0); + * YS2 Signal Mode = INYS1/INYS2 (YS1_SEL = 0); + * Don't reverse YS2 signal (YS2_XOR = 0); + * Don't reverse YS1 signal (YS1_XOR = 0); + * Enable YS signal function (YS_FLAG_EN = 1); + * Disable ALL ON function (ALL_ON_EN = 0) */ + 0x21, /* (GATE = 0x21) */ + 0x00, /* (CK_ALL_ON_EN = 0); (STV_ALL_ON_EN = 0); + * Timing of YS1 and YS2 signal = ? (CK_ALL_ON_WIDTH1 = 0) */ + 0x00, /* Timing of YS1 and YS2 signal = ? (CK_ALL_ON_WIDTH2 = 0) */ + 0x00, /* Timing of YS1 and YS2 signal = ? (CK_ALL_ON_WIDTH3 = 0) */ + 0x00, /* (YS_FLAG_PERIOD = 0) */ + 0x00, /* (YS2_SEL_2 = 0); (YS1_SEL_2 = 0); (YS2_XOR_2 = 0); + * (YS_FLAG_EN_2 = 0); (ALL_ON_EN_2 = 0) */ + 0x00, /* Distance of GIP ALL On rising edge and DE = ? + * (USER_GIP_GATE1_2 = 0) */ + 0x00, /* (CK_ALL_ON_EN_2 = 0); (STV_ALL_ON_EN_2 = 0); + * (CK_ALL_ON_WIDTH1_2 = 0) */ + 0x00, /* (CK_ALL_ON_WIDTH2_2 = 0) */ + 0x00, /* (CK_ALL_ON_WIDTH3_2 = 0) */ + 0x00, /* (YS_FLAG_PERIOD_2 = 0) */ + 0x02, /* (COS1_L_GS = 0); (COS2_L_GS = 2) */ + 0x46, /* (COS3_L_GS = 4); (COS4_L_GS = 6) */ + 0x02, /* (COS5_L_GS = 0); (COS6_L_GS = 2) */ + 0x88, /* Reserved (Parameter 16) */ + 0x88, /* Reserved (Parameter 17) */ + 0x88, /* Reserved (Parameter 18) */ + 0x88, /* Reserved (Parameter 19) */ + 0x88, /* Reserved (Parameter 20) */ + 0x88, /* (COS17_L_GS = 8); (COS18_L_GS = 8) */ + 0x64, /* (COS19_L_GS = 6); (COS20_L_GS = 4) */ + 0x88, /* (COS21_L_GS = 8); (COS22_L_GS = 8) */ + 0x13, /* (COS1_R_GS = 1); (COS2_R_GS = 3) */ + 0x57, /* (COS3_R_GS = 5); (COS4_R_GS = 7) */ + 0x13, /* (COS5_R_GS = 1); (COS6_R_GS = 3) */ + 0x88, /* Reserved (Parameter 27) */ + 0x88, /* Reserved (Parameter 28) */ + 0x88, /* Reserved (Parameter 29) */ + 0x88, /* Reserved (Parameter 30) */ + 0x88, /* Reserved (Parameter 31) */ + 0x88, /* (COS17_R_GS = 8); (COS18_R_GS = 8) */ + 0x75, /* (COS19_R_GS = 7); (COS20_R_GS = 5) */ + 0x88, /* (COS21_R_GS = 8); (COS22_R_GS = 8) */ + 0x23, /* GIP output EQ signal: P_EQ = Yes, N_EQ = No (EQOPT = 2); + * GIP output EQ signal level: P_EQ = GND, N_EQ = GND + * (EQ_SEL = 3) */ + 0x14, /* Distance of EQ rising edge and HYSNC = 20 Fosc + * (EQ_DELAY = 0x14) */ + 0x00, /* Distance of EQ rising edge and HYSNC = 0 HSYNC + * (EQ_DELAY_HSYNC = 0) */ + 0x00, /* (HSYNC_TO_CL1_CNT10 Bits 8-9 = 0) */ + 0x02, /* GIP reference HSYNC between external HSYNC = 2 Fosc + * (HSYNC_TO_CL1_CNT10 Bits 0-7 = 2) */ + 0x00, /* Undocumented (Parameter 40) */ + 0x00, /* Undocumented (Parameter 41) */ + 0x00, /* Undocumented (Parameter 42) */ + 0x00, /* Undocumented (Parameter 43) */ + 0x00, /* Undocumented (Parameter 44) */ + 0x00, /* Undocumented (Parameter 45) */ + 0x00, /* Undocumented (Parameter 46) */ + 0x00, /* Undocumented (Parameter 47) */ + 0x00, /* Undocumented (Parameter 48) */ + 0x00, /* Undocumented (Parameter 49) */ + 0x00, /* Undocumented (Parameter 50) */ + 0x00, /* Undocumented (Parameter 51) */ + 0x00, /* Undocumented (Parameter 52) */ + 0x00, /* Undocumented (Parameter 53) */ + 0x00, /* Undocumented (Parameter 54) */ + 0x03, /* Undocumented (Parameter 55) */ + 0x0a, /* Undocumented (Parameter 56) */ + 0xa5, /* Undocumented (Parameter 57) */ + 0x00, /* Undocumented (Parameter 58) */ + 0x00, /* Undocumented (Parameter 59) */ + 0x00, /* Undocumented (Parameter 60) */ + 0x00 /* Undocumented (Parameter 61) */ +}; + +/* Command #18: SETGAMMA (ST7703 Page 158) + * Set the gray scale voltage to adjust the gamma characteristics of the + * TFT panel + */ + +static const uint8_t g_pinephone_setgamma[] = +{ + 0xe0, /* SETGAMMA: Set the gray scale voltage to adjust the gamma */ + 0x00, /* (PVR0 = 0x00) */ + 0x09, /* (PVR1 = 0x09) */ + 0x0d, /* (PVR2 = 0x0D) */ + 0x23, /* (PVR3 = 0x23) */ + 0x27, /* (PVR4 = 0x27) */ + 0x3c, /* (PVR5 = 0x3C) */ + 0x41, /* (PPR0 = 0x41) */ + 0x35, /* (PPR1 = 0x35) */ + 0x07, /* (PPK0 = 0x07) */ + 0x0d, /* (PPK1 = 0x0D) */ + 0x0e, /* (PPK2 = 0x0E) */ + 0x12, /* (PPK3 = 0x12) */ + 0x13, /* (PPK4 = 0x13) */ + 0x10, /* (PPK5 = 0x10) */ + 0x12, /* (PPK6 = 0x12) */ + 0x12, /* (PPK7 = 0x12) */ + 0x18, /* (PPK8 = 0x18) */ + 0x00, /* (NVR0 = 0x00) */ + 0x09, /* (NVR1 = 0x09) */ + 0x0d, /* (NVR2 = 0x0D) */ + 0x23, /* (NVR3 = 0x23) */ + 0x27, /* (NVR4 = 0x27) */ + 0x3c, /* (NVR5 = 0x3C) */ + 0x41, /* (NPR0 = 0x41) */ + 0x35, /* (NPR1 = 0x35) */ + 0x07, /* (NPK0 = 0x07) */ + 0x0d, /* (NPK1 = 0x0D) */ + 0x0e, /* (NPK2 = 0x0E) */ + 0x12, /* (NPK3 = 0x12) */ + 0x13, /* (NPK4 = 0x13) */ + 0x10, /* (NPK5 = 0x10) */ + 0x12, /* (NPK6 = 0x12) */ + 0x12, /* (NPK7 = 0x12) */ + 0x18 /* (NPK8 = 0x18) */ +}; + +/* Command #19: SLPOUT (ST7703 Page 89) + * Turn off sleep mode (MIPI_DCS_EXIT_SLEEP_MODE) + */ + +static const uint8_t g_pinephone_slpout[] = +{ + 0x11 /* SLPOUT: Turn off sleep mode */ +}; + +/* Wait 120 milliseconds */ + +/* Command #20: Display On (ST7703 Page 97) + * Recover from DISPLAY OFF mode (MIPI_DCS_SET_DISPLAY_ON) + */ + +static const uint8_t g_pinephone_displayon[] = +{ + 0x29 /* Display On: Recover from DISPLAY OFF mode */ +}; + +/* Consolidate Initialization Commands **************************************/ + +/* 20 Initialization Commands to be sent to ST7703 LCD Controller */ + +static const struct pinephone_cmd_s g_pinephone_commands[] = +{ + { + g_pinephone_setextc, /* Command #1: SETEXTC (ST7703 Page 131) */ + sizeof(g_pinephone_setextc) + }, + { + g_pinephone_setmipi, /* Command #2: SETMIPI (ST7703 Page 144) */ + sizeof(g_pinephone_setmipi) + }, + { + g_pinephone_setpower_ext, /* Command #3: SETPOWER_EXT (ST7703 Pg 142) */ + sizeof(g_pinephone_setpower_ext) + }, + { + g_pinephone_setrgbif, /* Command #4: SETRGBIF (ST7703 Page 134) */ + sizeof(g_pinephone_setrgbif) + }, + { + g_pinephone_setscr, /* Command #5: SETSCR (ST7703 Page 147) */ + sizeof(g_pinephone_setscr) + }, + { + g_pinephone_setvdc, /* Command #6: SETVDC (ST7703 Page 146) */ + sizeof(g_pinephone_setvdc) + }, + { + g_pinephone_setpanel, /* Command #7: SETPANEL (ST7703 Page 154) */ + sizeof(g_pinephone_setpanel) + }, + { + g_pinephone_setcyc, /* Command #8: SETCYC (ST7703 Page 135) */ + sizeof(g_pinephone_setcyc) + }, + { + g_pinephone_setdisp, /* Command #9: SETDISP (ST7703 Page 132) */ + sizeof(g_pinephone_setdisp) + }, + { + g_pinephone_seteq, /* Command #10: SETEQ (ST7703 Page 159) */ + sizeof(g_pinephone_seteq) + }, + { + g_pinephone_c6, /* Command #11: Undocumented */ + sizeof(g_pinephone_c6) + }, + { + g_pinephone_setpower, /* Command #12: SETPOWER (ST7703 Page 149) */ + sizeof(g_pinephone_setpower) + }, + { + g_pinephone_setbgp, /* Command #13: SETBGP (ST7703 Page 136) */ + sizeof(g_pinephone_setbgp) + }, + { + g_pinephone_setvcom, /* Command #14: SETVCOM (ST7703 Page 137) */ + sizeof(g_pinephone_setvcom) + }, + { + g_pinephone_bf, /* Command #15: Undocumented */ + sizeof(g_pinephone_bf) + }, + { + g_pinephone_setgip1, /* Command #16: SETGIP1 (ST7703 Page 163) */ + sizeof(g_pinephone_setgip1) + }, + { + g_pinephone_setgip2, /* Command #17: SETGIP2 (ST7703 Page 170) */ + sizeof(g_pinephone_setgip2) + }, + { + g_pinephone_setgamma, /* Command #18: SETGAMMA (ST7703 Page 158) */ + sizeof(g_pinephone_setgamma) + }, + { + g_pinephone_slpout, /* Command #19: SLPOUT (ST7703 Page 89) */ + sizeof(g_pinephone_slpout) + }, + { + NULL, /* Wait 120 milliseconds */ + 0 + }, + { + g_pinephone_displayon, /* Command #20: Display On (ST7703 Page 97) */ + sizeof(g_pinephone_displayon) + } +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: write_dcs + * + * Description: + * Write the DCS Command to MIPI DSI Bus. + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +static int write_dcs(const uint8_t *buf, size_t len) +{ + int ret = -1; + + ginfo("len=%ld\n", len); + ginfodumpbuffer("buf", buf, len); + DEBUGASSERT(len > 0); + + /* Do DCS Short Write or Long Write depending on command length */ + + switch (len) + { + /* DCS Short Write (without parameter) */ + + case 1: + ret = a64_mipi_dsi_write(A64_MIPI_DSI_VIRTUAL_CHANNEL, + MIPI_DSI_DCS_SHORT_WRITE, + buf, len); + break; + + /* DCS Short Write (with parameter) */ + + case 2: + ret = a64_mipi_dsi_write(A64_MIPI_DSI_VIRTUAL_CHANNEL, + MIPI_DSI_DCS_SHORT_WRITE_PARAM, + buf, len); + break; + + /* DCS Long Write */ + + default: + ret = a64_mipi_dsi_write(A64_MIPI_DSI_VIRTUAL_CHANNEL, + MIPI_DSI_DCS_LONG_WRITE, + buf, len); + break; + } + + if (ret < 0) + { + gerr("MIPI DSI Write failed: %d\n", ret); + return ret; + } + + DEBUGASSERT(ret == len); + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pinephone_lcd_backlight_enable + * + * Description: + * Turn on the Backlight in Xingbangda XBD599 LCD Panel. + * + * Input Parameters: + * percent - Percent Brightness + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +int pinephone_lcd_backlight_enable(uint32_t percent) +{ + int ret; + uint32_t period; + uint32_t ctrl; + + /* Configure PL10 for PWM */ + + ginfo("Configure PL10 for PWM\n"); + ret = a64_pio_config(LCD_PWM); + if (ret < 0) + { + gerr("Configure PL10 failed: %d\n", ret); + return ret; + } + + /* R_PWM Control Register (Undocumented) + * Assume same as PWM Control Register (A64 Page 194) + * Set SCLK_CH0_GATING (Bit 6) to 0 (Mask) + */ + + ginfo("Disable R_PWM\n"); + modreg32(0, SCLK_CH0_GATING, R_PWM_CTRL_REG); + + /* R_PWM Channel 0 Period Register (Undocumented) + * Assume same as PWM Channel 0 Period Register (A64 Page 195) + * Set PWM_CH0_ENTIRE_CYS (Bits 16 to 31) to PWM Period + * Set PWM_CH0_ENTIRE_ACT_CYS (Bits 0 to 15) to PWM Period * Percent / 100 + */ + + ginfo("Configure R_PWM Period\n"); + period = PWM_CH0_ENTIRE_CYS(BACKLIGHT_PWM_PERIOD) | + PWM_CH0_ENTIRE_ACT_CYS(BACKLIGHT_PWM_PERIOD * percent / 100); + putreg32(period, R_PWM_CH0_PERIOD); + + /* R_PWM Control Register (Undocumented) + * Assume same as PWM Control Register (A64 Page 194) + * Set SCLK_CH0_GATING (Bit 6) to 1 (Pass) + * Set PWM_CH0_EN (Bit 4) to 1 (Enable) + * Set PWM_CH0_PRESCAL (Bits 0 to 3) to 0b1111 (Prescaler 1) + */ + + ginfo("Enable R_PWM\n"); + ctrl = SCLK_CH0_GATING | PWM_CH0_EN | PWM_CH0_PRESCAL(0b1111); + putreg32(ctrl, R_PWM_CTRL_REG); + + /* Configure PH10 for Output */ + + ginfo("Configure PH10 for Output\n"); + ret = a64_pio_config(LCD_BL_EN); + if (ret < 0) + { + gerr("Configure PH10 failed: %d\n", ret); + return ret; + } + + /* Set PH10 to High */ + + ginfo("Set PH10 to High\n"); + a64_pio_write(LCD_BL_EN, true); + + return OK; +} + +/**************************************************************************** + * Name: pinephone_lcd_panel_reset + * + * Description: + * Reset the Xingbangda XBD599 LCD Panel. + * + * Input Parameters: + * val - True if Reset should be set to High; False if Reset should be + * set to Low + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +int pinephone_lcd_panel_reset(bool val) +{ + int ret; + + /* Reset LCD Panel at PD23 (Active Low), configure PD23 for Output */ + + ginfo("Configure PD23 for Output\n"); + ret = a64_pio_config(LCD_RESET); + if (ret < 0) + { + gerr("Configure PD23 failed: %d\n", ret); + return ret; + } + + /* Set PD23 to High or Low */ + + ginfo("Set PD23 to %d\n", val); + a64_pio_write(LCD_RESET, val); + + return OK; +} + +/**************************************************************************** + * Name: pinephone_lcd_panel_init + * + * Description: + * Initialize the Sitronix ST7703 LCD Controller in Xingbangda XBD599 + * LCD Panel. Send 20 Initialization Commands to ST7703 over MIPI DSI. + * Assumes that the LCD Panel has been powered on (via AXP803 PMIC), + * MIPI DSI and D-PHY have been enabled on the SoC, and LCD Panel has + * been Reset (to Low then High). After the LCD Panel has been + * initialized, we may start MIPI DSI (in HSC and HSD modes) and + * Display Engine. + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +int pinephone_lcd_panel_init(void) +{ + int i; + int ret; + const int cmd_len = sizeof(g_pinephone_commands) / + sizeof(g_pinephone_commands[0]); + + /* For every ST7703 Initialization Command */ + + ginfo("Init ST7703 LCD Controller\n"); + for (i = 0; i < cmd_len; i++) + { + /* Get the ST7703 command and length */ + + const uint8_t *cmd = g_pinephone_commands[i].cmd; + const uint8_t len = g_pinephone_commands[i].len; + + /* If command is null, wait 120 milliseconds */ + + if (cmd == NULL) + { + up_mdelay(120); + continue; + } + + /* Send the command to ST7703 over MIPI DSI */ + + ret = write_dcs(cmd, len); + if (ret < 0) + { + gerr("Write DCS failed: %d\n", ret); + return ret; + } + } + + return OK; +} diff --git a/boards/arm64/a64/pinephone/src/pinephone_lcd.h b/boards/arm64/a64/pinephone/src/pinephone_lcd.h new file mode 100644 index 00000000000..77ad379498e --- /dev/null +++ b/boards/arm64/a64/pinephone/src/pinephone_lcd.h @@ -0,0 +1,98 @@ +/**************************************************************************** + * boards/arm64/a64/pinephone/src/pinephone_lcd.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __BOARDS_ARM64_A64_PINEPHONE_SRC_PINEPHONE_LCD_H +#define __BOARDS_ARM64_A64_PINEPHONE_SRC_PINEPHONE_LCD_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Xingbangda XBD599 LCD Panel Width and Height (pixels) */ + +#define PINEPHONE_LCD_PANEL_WIDTH 720 +#define PINEPHONE_LCD_PANEL_HEIGHT 1440 + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: pinephone_lcd_backlight_enable + * + * Description: + * Turn on the Backlight in Xingbangda XBD599 LCD Panel. + * + * Input Parameters: + * percent - Percent Brightness + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +int pinephone_lcd_backlight_enable(uint32_t percent); + +/**************************************************************************** + * Name: pinephone_lcd_panel_reset + * + * Description: + * Reset the Xingbangda XBD599 LCD Panel. + * + * Input Parameters: + * val - True if Reset should be set to High; False if Reset should be + * set to Low + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +int pinephone_lcd_panel_reset(bool val); + +/**************************************************************************** + * Name: pinephone_lcd_panel_init + * + * Description: + * Initialize the Sitronix ST7703 LCD Controller in Xingbangda XBD599 + * LCD Panel. Send 20 Initialization Commands to ST7703 over MIPI DSI. + * Assumes that the LCD Panel has been powered on (via AXP803 PMIC), + * MIPI DSI and D-PHY have been enabled on the SoC, and LCD Panel has + * been Reset (to Low then High). After the LCD Panel has been + * initialized, we may start MIPI DSI (in HSC and HSD modes) and + * Display Engine. + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +int pinephone_lcd_panel_init(void); + +#endif /* __BOARDS_ARM64_A64_PINEPHONE_SRC_PINEPHONE_LCD_H */ diff --git a/boards/arm64/a64/pinephone/src/pinephone_pmic.c b/boards/arm64/a64/pinephone/src/pinephone_pmic.c new file mode 100644 index 00000000000..76229560494 --- /dev/null +++ b/boards/arm64/a64/pinephone/src/pinephone_pmic.c @@ -0,0 +1,282 @@ +/**************************************************************************** + * boards/arm64/a64/pinephone/src/pinephone_pmic.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Reference: + * + * "Rendering PinePhone's Display (DE and TCON0)" + * https://lupyuen.github.io/articles/de + * + * "NuttX RTOS for PinePhone: Render Graphics in Zig" + * https://lupyuen.github.io/articles/de2 + * + * "AXP803 Page" refers to X-Powers AXP803 Data Sheet + * https://lupyuen.github.io/images/AXP803_Datasheet_V1.0.pdf + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include "chip.h" +#include "arm64_internal.h" +#include "a64_rsb.h" +#include "pinephone_pmic.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Address of AXP803 PMIC on Reduced Serial Bus */ + +#define AXP803_RT_ADDR 0x2d + +/* AXP803 PMIC Registers and Bit Definitions ********************************/ + +/* DLDO1 Voltage Control (AXP803 Page 52) */ + +#define DLDO1_VOLTAGE_CONTROL 0x15 +#define DLDO1_VOLTAGE(n) ((n) << 0) + +/* Output Power On-Off Control 2 (AXP803 Page 51) */ + +#define OUTPUT_POWER_ON_OFF_CONTROL2 0x12 +#define DLDO1_ON_OFF_CONTROL (1 << 3) +#define DLDO2_ON_OFF_CONTROL (1 << 4) + +/* GPIO0LDO and GPIO0 High Level Voltage Setting (AXP803 Page 77) */ + +#define GPIO0LDO_HIGH_LEVEL_VOLTAGE_SETTING 0x91 +#define GPIO0LDO_HIGH_LEVEL_VOLTAGE(n) ((n) << 0) + +/* GPIO0 (GPADC) Control (AXP803 Page 76) */ + +#define GPIO0_CONTROL 0x90 +#define GPIO0_PIN_FUNCTION(n) ((n) << 0) + +/* DLDO2 Voltage Control (AXP803 Page 52) */ + +#define DLDO2_VOLTAGE_CONTROL 0x16 +#define DLDO2_VOLTAGE(n) ((n) << 0) + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pmic_write + * + * Description: + * Write a byte to an AXP803 PMIC Register. + * + * Input Parameters: + * reg - AXP803 Register ID + * val - Byte to be written + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +static int pmic_write(uint8_t reg, uint8_t val) +{ + int ret; + + /* Write to AXP803 PMIC on Reduced Serial Bus */ + + batinfo("reg=0x%x, val=0x%x\n", reg, val); + ret = a64_rsb_write(AXP803_RT_ADDR, reg, val); + if (ret < 0) + { + baterr("PMIC Write failed: %d\n", ret); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Name: pmic_clrsetbits + * + * Description: + * Clear and set the bits in an AXP803 PMIC Register. + * + * Input Parameters: + * reg - AXP803 Register ID + * clr_mask - Bits to be masked + * set_mask - Bits to be set + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +static int pmic_clrsetbits(uint8_t reg, uint8_t clr_mask, uint8_t set_mask) +{ + int ret; + uint8_t regval; + + /* Read from AXP803 PMIC on Reduced Serial Bus */ + + batinfo("reg=0x%x, clr_mask=0x%x, set_mask=0x%x\n", + reg, clr_mask, set_mask); + ret = a64_rsb_read(AXP803_RT_ADDR, reg); + if (ret < 0) + { + baterr("PMIC Read failed: %d\n", ret); + return ret; + } + + /* Write to AXP803 PMIC on Reduced Serial Bus */ + + regval = (ret & ~clr_mask) | set_mask; + ret = a64_rsb_write(AXP803_RT_ADDR, reg, regval); + if (ret < 0) + { + baterr("PMIC Write failed: %d\n", ret); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pinephone_pmic_init + * + * Description: + * Initialize the X-Powers AXP803 Power Management Integrated Circuit, + * connected on Reduced Serial Bus. Power on the MIPI DSI Interface of + * Xingbangda XBD599 LCD Panel. Doesn't switch on the LCD Panel + * Backlight, which is controlled by PIO and PWM. + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +int pinephone_pmic_init(void) +{ + int ret; + + /* Set DLDO1 Voltage to 3.3V. DLDO1 powers the Front Camera / USB HSIC / + * I2C Sensors. + */ + + /* DLDO1 Voltage Control (AXP803 Page 52) + * Set Voltage (Bits 0 to 4) to 26 (2.6V + 0.7V = 3.3V) + */ + + batinfo("Set DLDO1 Voltage to 3.3V\n"); + ret = pmic_write(DLDO1_VOLTAGE_CONTROL, DLDO1_VOLTAGE(26)); + if (ret < 0) + { + baterr("Set DLDO1 failed: %d\n", ret); + return ret; + } + + /* Power on DLDO1 */ + + /* Output Power On-Off Control 2 (AXP803 Page 51) + * Set DLDO1 On-Off Control (Bit 3) to 1 (Power On) + */ + + ret = pmic_clrsetbits(OUTPUT_POWER_ON_OFF_CONTROL2, 0, + DLDO1_ON_OFF_CONTROL); + if (ret < 0) + { + baterr("Power on DLDO1 failed: %d\n", ret); + return ret; + } + + /* Set LDO Voltage to 3.3V. GPIO0LDO powers the Capacitive Touch Panel. */ + + /* GPIO0LDO and GPIO0 High Level Voltage Setting (AXP803 Page 77) + * Set GPIO0LDO and GPIO0 High Level Voltage (Bits 0 to 4) to 26 + * (2.6V + 0.7V = 3.3V) + */ + + batinfo("Set LDO Voltage to 3.3V\n"); + ret = pmic_write(GPIO0LDO_HIGH_LEVEL_VOLTAGE_SETTING, + GPIO0LDO_HIGH_LEVEL_VOLTAGE(26)); + if (ret < 0) + { + baterr("Set LDO failed: %d\n", ret); + return ret; + } + + /* Enable LDO Mode on GPIO0 */ + + /* GPIO0 (GPADC) Control (AXP803 Page 76) + * Set GPIO0 Pin Function Control (Bits 0 to 2) to 0b11 (Low Noise LDO on) + */ + + batinfo("Enable LDO mode on GPIO0\n"); + ret = pmic_write(GPIO0_CONTROL, GPIO0_PIN_FUNCTION(0b11)); + if (ret < 0) + { + baterr("Enable LDO failed: %d\n", ret); + return ret; + } + + /* Set DLDO2 Voltage to 1.8V. DLDO2 powers the MIPI DSI Interface of + * Xingbangda XBD599 LCD Panel. + */ + + /* DLDO2 Voltage Control (AXP803 Page 52) + * Set Voltage (Bits 0 to 4) to 11 (1.1V + 0.7V = 1.8V) + */ + + batinfo("Set DLDO2 Voltage to 1.8V\n"); + ret = pmic_write(DLDO2_VOLTAGE_CONTROL, DLDO2_VOLTAGE(11)); + if (ret < 0) + { + baterr("Set DLDO2 failed: %d\n", ret); + return ret; + } + + /* Power on DLDO2 */ + + /* Output Power On-Off Control 2 (AXP803 Page 51) + * Set DLDO2 On-Off Control (Bit 4) to 1 (Power On) + */ + + ret = pmic_clrsetbits(OUTPUT_POWER_ON_OFF_CONTROL2, 0, + DLDO2_ON_OFF_CONTROL); + if (ret < 0) + { + baterr("Power on DLDO2 failed: %d\n", ret); + return ret; + } + + return OK; +} diff --git a/boards/arm64/a64/pinephone/src/pinephone_pmic.h b/boards/arm64/a64/pinephone/src/pinephone_pmic.h new file mode 100644 index 00000000000..c284845437f --- /dev/null +++ b/boards/arm64/a64/pinephone/src/pinephone_pmic.h @@ -0,0 +1,53 @@ +/**************************************************************************** + * boards/arm64/a64/pinephone/src/pinephone_pmic.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __BOARDS_ARM64_A64_PINEPHONE_SRC_PINEPHONE_PMIC_H +#define __BOARDS_ARM64_A64_PINEPHONE_SRC_PINEPHONE_PMIC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: pinephone_pmic_init + * + * Description: + * Initialize the X-Powers AXP803 Power Management Integrated Circuit, + * connected on Reduced Serial Bus. Power on the MIPI DSI Interface of + * Xingbangda XBD599 LCD Panel. Doesn't switch on the LCD Panel + * Backlight, which is controlled by PIO and PWM. + * + * Input Parameters: + * None + * + * Returned Value: + * Zero (OK) on success; a negated errno value is returned on any failure. + * + ****************************************************************************/ + +int pinephone_pmic_init(void); + +#endif /* __BOARDS_ARM64_A64_PINEPHONE_SRC_PINEPHONE_PMIC_H */