* arch/arm64/src/a64/a64_tcon0.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.
*
****************************************************************************/
*
* "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
*/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/arch.h>
#include "hardware/a64_memorymap.h"
#include "arm64_arch.h"
#include "a64_tcon0.h"
* Pre-processor Definitions
****************************************************************************/
#define PLL_VIDEO0_CTRL_REG (A64_CCU_ADDR + 0x10)
#define PLL_VIDEO0_PREDIV_M(n) ((n) << 0)
#define PLL_VIDEO0_FACTOR_N(n) ((n) << 8)
#define PLL_VIDEO0_MODE_SEL (1 << 24)
#define PLL_VIDEO0_ENABLE (1 << 31)
#define PLL_MIPI_CTRL_REG (A64_CCU_ADDR + 0x40)
#define PLL_MIPI_PRE_DIV_M(n) ((n) << 0)
#define PLL_MIPI_FACTOR_K(n) ((n) << 4)
#define PLL_MIPI_FACTOR_N(n) ((n) << 8)
#define PLL_MIPI_SRC(n) ((n) << 21)
#define PLL_MIPI_LDO2_EN (1 << 22)
#define PLL_MIPI_LDO1_EN (1 << 23)
#define PLL_MIPI_ENABLE (1 << 31)
#define BUS_CLK_GATING_REG1 (A64_CCU_ADDR + 0x64)
#define TCON0_GATING (1 << 3)
#define TCON0_CLK_REG (A64_CCU_ADDR + 0x118)
#define CLK_SRC_SEL(n) ((n) << 24)
#define SCLK_GATING (1 << 31)
#define BUS_SOFT_RST_REG1 (A64_CCU_ADDR + 0x2c4)
#define TCON0_RST (1 << 3)
#define TCON_GCTL_REG (A64_TCON0_ADDR + 0x00)
#define TCON_EN (1 << 31)
#define TCON_GINT0_REG (A64_TCON0_ADDR + 0x04)
#define TCON_GINT1_REG (A64_TCON0_ADDR + 0x08)
#define TCON0_CTL_REG (A64_TCON0_ADDR + 0x40)
#define TCON0_SRC_SEL(n) ((n) << 0)
#define TCON0_IF(n) ((n) << 24)
#define TCON0_EN (1 << 31)
#define TCON0_DCLK_REG (A64_TCON0_ADDR + 0x44)
#define TCON0_DCLK_DIV(n) ((n) << 0)
#define TCON0_DCLK_EN(n) ((n) << 28)
#define TCON0_BASIC0_REG (A64_TCON0_ADDR + 0x48)
#define TCON0_Y(n) ((n) << 0)
#define TCON0_X(n) ((n) << 16)
#define TCON0_CPU_IF_REG (A64_TCON0_ADDR + 0x60)
#define TRIGGER_EN (1 << 0)
#define TRIGGER_FIFO_EN (1 << 2)
#define FLUSH_MODE (1 << 16)
#define CPU_MODE (1 << 28)
#define TCON0_IO_TRI_REG (A64_TCON0_ADDR + 0x8c)
#define DATA_OUTPUT_TRI_EN(n) ((n) << 0)
#define IO0_OUTPUT_TRI_EN(n) ((n) << 24)
#define IO1_OUTPUT_TRI_EN(n) ((n) << 25)
#define IO2_OUTPUT_TRI_EN(n) ((n) << 26)
#define IO3_OUTPUT_TRI_EN(n) ((n) << 27)
#define RGB_ENDIAN(n) ((n) << 28)
#define IO_RESERVED (0b111 << 29)
#define TCON1_IO_TRI_REG (A64_TCON0_ADDR + 0xf4)
#define TCON0_ECC_FIFO (A64_TCON0_ADDR + 0xf8)
#define TCON0_CPU_TRI0_REG (A64_TCON0_ADDR + 0x160)
#define BLOCK_SIZE(n) ((n) << 0)
#define BLOCK_SPACE(n) ((n) << 16)
#define TCON0_CPU_TRI1_REG (A64_TCON0_ADDR + 0x164)
#define BLOCK_NUM(n) ((n) << 0)
#define BLOCK_CURRENT_NUM(n) ((n) << 16)
#define TCON0_CPU_TRI2_REG (A64_TCON0_ADDR + 0x168)
#define TRANS_START_SET(n) ((n) << 0)
#define SYNC_MODE(n) ((n) << 13)
#define TRANS_START_MODE(n) ((n) << 15)
#define START_DELAY(n) ((n) << 16)
#define TCON_SAFE_PERIOD_REG (A64_TCON0_ADDR + 0x1f0)
#define SAFE_PERIOD_MODE(n) ((n) << 0)
#define SAFE_PERIOD_LINE(n) ((n) << 4)
#define SAFE_PERIOD_FIFO_NUM(n) ((n) << 16)
* Public Functions
****************************************************************************/
* Name: a64_tcon0_init
*
* Description:
* Initialize Timing Controller TCON0 to stream pixel data from Display
* Engine to MIPI Display Serial Interface. Should be called before
* enabling the MIPI DSI Block on the SoC.
*
* Input Parameters:
* panel_width - LCD Panel Width (pixels)
* panel_height - LCD Panel Height (pixels)
*
* Returned Value:
* OK is always returned at present.
*
****************************************************************************/
int a64_tcon0_init(uint16_t panel_width, uint16_t panel_height)
{
uint32_t pll_video0_ctrl;
uint32_t pll_mipi_ctrl1;
uint32_t pll_mipi_ctrl2;
uint32_t tcon0_clk;
uint32_t tcon0_dclk;
uint32_t tcon0_ctl;
uint32_t tcon0_basic0;
uint32_t tcon0_cpu_if;
uint32_t tcon0_cpu_tri0;
uint32_t tcon0_cpu_tri1;
uint32_t tcon0_cpu_tri2;
uint32_t tcon_safe_period;
uint32_t tcon0_io_tri;
ginfo("Configure PLL_VIDEO0\n");
* Set PLL_ENABLE (Bit 31) to 1 (Enable PLL)
* Set PLL_MODE_SEL (Bit 24) to 1 (Integer Mode)
* Set PLL_FACTOR_N (Bits 8 to 14) to 0x62 (PLL Factor N)
* Set PLL_PREDIV_M (Bits 0 to 3) to 7 (PLL Pre Divider)
*/
pll_video0_ctrl = PLL_VIDEO0_ENABLE |
PLL_VIDEO0_MODE_SEL |
PLL_VIDEO0_FACTOR_N(0x62) |
PLL_VIDEO0_PREDIV_M(7);
putreg32(pll_video0_ctrl, PLL_VIDEO0_CTRL_REG);
ginfo("Enable LDO1 and LDO2\n");
* Set LDO1_EN (Bit 23) to 1 (Enable On-chip LDO1)
* Set LDO2_EN (Bit 22) to 1 (Enable On-chip LDO2)
*/
pll_mipi_ctrl1 = PLL_MIPI_LDO1_EN | PLL_MIPI_LDO2_EN;
putreg32(pll_mipi_ctrl1, PLL_MIPI_CTRL_REG);
up_mdelay(1);
ginfo("Configure MIPI PLL\n");
* Set PLL_ENABLE (Bit 31) to 1 (Enable MIPI PLL)
* Set LDO1_EN (Bit 23) to 1 (Enable On-chip LDO1)
* Set LDO2_EN (Bit 22) to 1 (Enable On-chip LDO2)
* Set PLL_SRC (Bit 21) to 0 (PLL Source is VIDEO0 PLL)
* Set PLL_FACTOR_N (Bits 8 to 11) to 7 (PLL Factor N)
* Set PLL_FACTOR_K (Bits 4 to 5) to 1 (PLL Factor K)
* Set PLL_PRE_DIV_M (Bits 0 to 3) to 10 (PLL Pre Divider)
*/
pll_mipi_ctrl2 = PLL_MIPI_ENABLE |
PLL_MIPI_LDO1_EN |
PLL_MIPI_LDO2_EN |
PLL_MIPI_SRC(0) |
PLL_MIPI_FACTOR_N(7) |
PLL_MIPI_FACTOR_K(1) |
PLL_MIPI_PRE_DIV_M(10);
putreg32(pll_mipi_ctrl2, PLL_MIPI_CTRL_REG);
ginfo("Set TCON0 Clock Source to MIPI PLL\n");
* Set SCLK_GATING (Bit 31) to 1 (Special Clock is On)
* Set CLK_SRC_SEL (Bits 24 to 26) to 0 (Clock Source is MIPI PLL)
*/
tcon0_clk = SCLK_GATING | CLK_SRC_SEL(0);
putreg32(tcon0_clk, TCON0_CLK_REG);
ginfo("Enable TCON0 Clock\n");
* Set TCON0_GATING (Bit 3) to 1 (Pass Clock for TCON0)
*/
putreg32(TCON0_GATING, BUS_CLK_GATING_REG1);
ginfo("Deassert TCON0 Reset\n");
* Set TCON0_RST (Bit 3) to 1 (Deassert TCON0 Reset)
*/
putreg32(TCON0_RST, BUS_SOFT_RST_REG1);
ginfo("Disable TCON0 and Interrupts\n");
* Set to 0 (Disable TCON0)
*/
putreg32(0, TCON_GCTL_REG);
* Set to 0 (Disable TCON0 Interrupts)
*/
putreg32(0x0, TCON_GINT0_REG);
* Set to 0 (Disable TCON0 Interrupts)
*/
putreg32(0x0, TCON_GINT1_REG);
ginfo("Enable Tristate Output\n");
* Set to 0xffff ffff to Enable TCON0 Tristate Output
*/
putreg32(0xffffffff, TCON0_IO_TRI_REG);
* Set to 0xffff ffff to Enable TCON1 Tristate Output
* Note: TCON1_IO_TRI_REG is actually in TCON0 Address Range,
* not in TCON1 Address Range as stated in A64 User Manual
*/
putreg32(0xffffffff, TCON1_IO_TRI_REG);
ginfo("Set DCLK to MIPI PLL / 6\n");
* Set TCON0_Dclk_En (Bits 28 to 31) to 8
* (Enable TCON0 Clocks: DCLK, DCLK1, DCLK2, DCLKM2)
* Set TCON0_Dclk_Div (Bits 0 to 6) to 6 (DCLK Divisor)
*/
tcon0_dclk = TCON0_DCLK_EN(8) | TCON0_DCLK_DIV(6);
putreg32(tcon0_dclk, TCON0_DCLK_REG);
* Set TCON0_En (Bit 31) to 1 (Enable TCON0)
* Set TCON0_IF (Bits 24 to 25) to 1 (8080 Interface)
* Set TCON0_SRC_SEL (Bits 0 to 2) to 0 (TCON0 Source is DE0)
*/
tcon0_ctl = TCON0_EN | TCON0_IF(1) | TCON0_SRC_SEL(0);
putreg32(tcon0_ctl, TCON0_CTL_REG);
* Set TCON0_X (Bits 16 to 27) to 719 (Panel Width - 1)
* Set TCON0_Y (Bits 0 to 11) to 1439 (Panel Height - 1)
*/
tcon0_basic0 = TCON0_X(panel_width - 1) | TCON0_Y(panel_height - 1);
putreg32(tcon0_basic0, TCON0_BASIC0_REG);
* Set to 8
*/
putreg32(0x8, TCON0_ECC_FIFO);
* Set CPU_Mode (Bits 28 to 31) to 1 (24-bit DSI)
* Set FLUSH_Mode (Bit 16) to 1 (Enable Direct Transfer Mode)
* Set Trigger_FIFO_En (Bit 2) to 1 (Enable FIFO Trigger)
* Set Trigger_En (Bit 0) to 1 (Enable Trigger Mode)
*/
tcon0_cpu_if = CPU_MODE | FLUSH_MODE | TRIGGER_FIFO_EN | TRIGGER_EN;
putreg32(tcon0_cpu_if, TCON0_CPU_IF_REG);
ginfo("Set CPU Panel Trigger\n");
* Set Block_Space (Bits 16 to 27) to 47 (Block Space)
* Set Block_Size (Bits 0 to 11) to 719 (Panel Width - 1)
* Note: Block Space is probably derived from Panel Width
*/
tcon0_cpu_tri0 = BLOCK_SPACE(47) | BLOCK_SIZE(panel_width - 1);
putreg32(tcon0_cpu_tri0, TCON0_CPU_TRI0_REG);
* Set Block_Current_Num (Bits 16 to 31) to 0 (Block Current Number)
* Set Block_Num (Bits 0 to 15) to 1439 (Panel Height - 1)
*/
tcon0_cpu_tri1 = BLOCK_CURRENT_NUM(0) | BLOCK_NUM(panel_height - 1);
putreg32(tcon0_cpu_tri1, TCON0_CPU_TRI1_REG);
* Set Start_Delay (Bits 16 to 31) to 7106 (Start Delay)
* Set Trans_Start_Mode (Bit 15) to 0
* (Trans Start Mode is ECC FIFO + TRI FIFO)
* Set Sync_Mode (Bits 13 to 14) to 0 (Sync Mode is Auto)
* Set Trans_Start_Set (Bits 0 to 12) to 10 (Trans Start Set)
*/
tcon0_cpu_tri2 = START_DELAY(7106) |
TRANS_START_MODE(0) |
SYNC_MODE(0) |
TRANS_START_SET(10);
putreg32(tcon0_cpu_tri2, TCON0_CPU_TRI2_REG);
ginfo("Set Safe Period\n");
* Set Safe_Period_FIFO_Num (Bits 16 to 28) to 3000
* Set Safe_Period_Line (Bits 4 to 15) to 0
* Set Safe_Period_Mode (Bits 0 to 2) to 3
* (Safe Period Mode: Safe at 2 and safe at sync active)
*/
tcon_safe_period = SAFE_PERIOD_FIFO_NUM(3000) |
SAFE_PERIOD_LINE(0) |
SAFE_PERIOD_MODE(3);
putreg32(tcon_safe_period, TCON_SAFE_PERIOD_REG);
ginfo("Enable Output Triggers\n");
* Set Reserved (Bits 29 to 31) to 0b111
* Set RGB_Endian (Bit 28) to 0 (Normal RGB Endian)
* Set IO3_Output_Tri_En (Bit 27) to 0 (Enable IO3 Output Tri)
* Set IO2_Output_Tri_En (Bit 26) to 0 (Enable IO2 Output Tri)
* Set IO1_Output_Tri_En (Bit 25) to 0 (Enable IO1 Output Tri)
* Set IO0_Output_Tri_En (Bit 24) to 0 (Enable IO0 Output Tri)
* Set Data_Output_Tri_En (Bits 0 to 23) to 0 (Enable TCON0 Output Port)
*/
tcon0_io_tri = IO_RESERVED |
RGB_ENDIAN(0) |
IO3_OUTPUT_TRI_EN(0) |
IO2_OUTPUT_TRI_EN(0) |
IO1_OUTPUT_TRI_EN(0) |
IO0_OUTPUT_TRI_EN(0) |
DATA_OUTPUT_TRI_EN(0);
putreg32(tcon0_io_tri, TCON0_IO_TRI_REG);
ginfo("Enable TCON0\n");
* Set TCON_En (Bit 31) to 1 (Enable TCON0)
*/
modreg32(TCON_EN, TCON_EN, TCON_GCTL_REG);
return OK;
}