diff options
92 files changed, 7102 insertions, 301 deletions
diff --git a/arch/arm/cpu/armv7/Kconfig b/arch/arm/cpu/armv7/Kconfig index 61e7c82459..6c5d5dd8e0 100644 --- a/arch/arm/cpu/armv7/Kconfig +++ b/arch/arm/cpu/armv7/Kconfig @@ -16,7 +16,7 @@ config ARMV7_NONSEC config ARMV7_BOOT_SEC_DEFAULT boolean "Boot in secure mode by default" if EXPERT depends on ARMV7_NONSEC - default n + default y if TEGRA ---help--- Say Y here to boot in secure mode by default even if non-secure mode is supported. This option is useful to boot kernels which do not diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile index e66c61e8ec..5a76100406 100644 --- a/arch/arm/cpu/armv7/Makefile +++ b/arch/arm/cpu/armv7/Makefile @@ -18,7 +18,7 @@ obj-y += lowlevel_init.o endif endif -ifneq ($(CONFIG_ARMV7_NONSEC)$(CONFIG_ARMV7_VIRT),) +ifneq ($(CONFIG_ARMV7_NONSEC),) obj-y += nonsec_virt.o obj-y += virt-v7.o obj-y += virt-dt.o diff --git a/arch/arm/cpu/armv7/ls102xa/cpu.c b/arch/arm/cpu/armv7/ls102xa/cpu.c index 1a640bbb9c..75f0d8c7f7 100644 --- a/arch/arm/cpu/armv7/ls102xa/cpu.c +++ b/arch/arm/cpu/armv7/ls102xa/cpu.c @@ -329,7 +329,7 @@ int arch_cpu_init(void) return 0; } -#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT) +#ifdef CONFIG_ARMV7_NONSEC /* Set the address at which the secondary core starts from.*/ void smp_set_core_boot_addr(unsigned long addr, int corenr) { diff --git a/arch/arm/cpu/armv7/psci.S b/arch/arm/cpu/armv7/psci.S index bf11a34e54..87c0c0b6f5 100644 --- a/arch/arm/cpu/armv7/psci.S +++ b/arch/arm/cpu/armv7/psci.S @@ -17,6 +17,7 @@ #include <config.h> #include <linux/linkage.h> +#include <asm/macro.h> #include <asm/psci.h> .pushsection ._secure.text, "ax" @@ -99,4 +100,124 @@ _smc_psci: pop {r4-r7, lr} movs pc, lr @ Return to the kernel +@ Requires dense and single-cluster CPU ID space +ENTRY(psci_get_cpu_id) + mrc p15, 0, r0, c0, c0, 5 /* read MPIDR */ + and r0, r0, #0xff /* return CPU ID in cluster */ + bx lr +ENDPROC(psci_get_cpu_id) +.weak psci_get_cpu_id + +/* Imported from Linux kernel */ +LENTRY(v7_flush_dcache_all) + dmb @ ensure ordering with previous memory accesses + mrc p15, 1, r0, c0, c0, 1 @ read clidr + ands r3, r0, #0x7000000 @ extract loc from clidr + mov r3, r3, lsr #23 @ left align loc bit field + beq finished @ if loc is 0, then no need to clean + mov r10, #0 @ start clean at cache level 0 +flush_levels: + add r2, r10, r10, lsr #1 @ work out 3x current cache level + mov r1, r0, lsr r2 @ extract cache type bits from clidr + and r1, r1, #7 @ mask of the bits for current cache only + cmp r1, #2 @ see what cache we have at this level + blt skip @ skip if no cache, or just i-cache + mrs r9, cpsr @ make cssr&csidr read atomic + mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr + isb @ isb to sych the new cssr&csidr + mrc p15, 1, r1, c0, c0, 0 @ read the new csidr + msr cpsr_c, r9 + and r2, r1, #7 @ extract the length of the cache lines + add r2, r2, #4 @ add 4 (line length offset) + ldr r4, =0x3ff + ands r4, r4, r1, lsr #3 @ find maximum number on the way size + clz r5, r4 @ find bit position of way size increment + ldr r7, =0x7fff + ands r7, r7, r1, lsr #13 @ extract max number of the index size +loop1: + mov r9, r7 @ create working copy of max index +loop2: + orr r11, r10, r4, lsl r5 @ factor way and cache number into r11 + orr r11, r11, r9, lsl r2 @ factor index number into r11 + mcr p15, 0, r11, c7, c14, 2 @ clean & invalidate by set/way + subs r9, r9, #1 @ decrement the index + bge loop2 + subs r4, r4, #1 @ decrement the way + bge loop1 +skip: + add r10, r10, #2 @ increment cache number + cmp r3, r10 + bgt flush_levels +finished: + mov r10, #0 @ swith back to cache level 0 + mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr + dsb st + isb + bx lr +ENDPROC(v7_flush_dcache_all) + +ENTRY(psci_disable_smp) + mrc p15, 0, r0, c1, c0, 1 @ ACTLR + bic r0, r0, #(1 << 6) @ Clear SMP bit + mcr p15, 0, r0, c1, c0, 1 @ ACTLR + isb + dsb + bx lr +ENDPROC(psci_disable_smp) +.weak psci_disable_smp + +ENTRY(psci_enable_smp) + mrc p15, 0, r0, c1, c0, 1 @ ACTLR + orr r0, r0, #(1 << 6) @ Set SMP bit + mcr p15, 0, r0, c1, c0, 1 @ ACTLR + isb + bx lr +ENDPROC(psci_enable_smp) +.weak psci_enable_smp + +ENTRY(psci_cpu_off_common) + push {lr} + + mrc p15, 0, r0, c1, c0, 0 @ SCTLR + bic r0, r0, #(1 << 2) @ Clear C bit + mcr p15, 0, r0, c1, c0, 0 @ SCTLR + isb + dsb + + bl v7_flush_dcache_all + + clrex @ Why??? + + bl psci_disable_smp + + pop {lr} + bx lr +ENDPROC(psci_cpu_off_common) + +@ expects CPU ID in r0 and returns stack top in r0 +ENTRY(psci_get_cpu_stack_top) + mov r5, #0x400 @ 1kB of stack per CPU + mul r0, r0, r5 + + ldr r5, =psci_text_end @ end of monitor text + add r5, r5, #0x2000 @ Skip two pages + lsr r5, r5, #12 @ Align to start of page + lsl r5, r5, #12 + sub r5, r5, #4 @ reserve 1 word for target PC + sub r0, r5, r0 @ here's our stack! + + bx lr +ENDPROC(psci_get_cpu_stack_top) + +ENTRY(psci_cpu_entry) + bl psci_enable_smp + + bl _nonsec_init + + bl psci_get_cpu_id @ CPU ID => r0 + bl psci_get_cpu_stack_top @ stack top => r0 + ldr r0, [r0] @ target PC at stack top + b _do_nonsec_entry +ENDPROC(psci_cpu_entry) + .popsection diff --git a/arch/arm/cpu/armv7/sunxi/psci.S b/arch/arm/cpu/armv7/sunxi/psci.S index 07b2d76194..7ec0500fac 100644 --- a/arch/arm/cpu/armv7/sunxi/psci.S +++ b/arch/arm/cpu/armv7/sunxi/psci.S @@ -19,6 +19,7 @@ #include <config.h> #include <asm/gic.h> +#include <asm/macro.h> #include <asm/psci.h> #include <asm/arch/cpu.h> @@ -138,8 +139,11 @@ out: mcr p15, 0, r7, c1, c1, 0 @ r2 = target PC .globl psci_cpu_on psci_cpu_on: - adr r0, _target_pc - str r2, [r0] + push {lr} + + mov r0, r1 + bl psci_get_cpu_stack_top @ get stack top of target CPU + str r2, [r0] @ store target PC at stack top dsb movw r0, #(SUN7I_CPUCFG_BASE & 0xffff) @@ -150,7 +154,7 @@ psci_cpu_on: mov r4, #1 lsl r4, r4, r1 - adr r6, _sunxi_cpu_entry + ldr r6, =psci_cpu_entry str r6, [r0, #0x1a4] @ PRIVATE_REG (boot vector) @ Assert reset on target CPU @@ -194,88 +198,11 @@ psci_cpu_on: str r6, [r0, #0x1e4] mov r0, #ARM_PSCI_RET_SUCCESS @ Return PSCI_RET_SUCCESS - mov pc, lr - -_target_pc: - .word 0 - -/* Imported from Linux kernel */ -v7_flush_dcache_all: - dmb @ ensure ordering with previous memory accesses - mrc p15, 1, r0, c0, c0, 1 @ read clidr - ands r3, r0, #0x7000000 @ extract loc from clidr - mov r3, r3, lsr #23 @ left align loc bit field - beq finished @ if loc is 0, then no need to clean - mov r10, #0 @ start clean at cache level 0 -flush_levels: - add r2, r10, r10, lsr #1 @ work out 3x current cache level - mov r1, r0, lsr r2 @ extract cache type bits from clidr - and r1, r1, #7 @ mask of the bits for current cache only - cmp r1, #2 @ see what cache we have at this level - blt skip @ skip if no cache, or just i-cache - mrs r9, cpsr @ make cssr&csidr read atomic - mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr - isb @ isb to sych the new cssr&csidr - mrc p15, 1, r1, c0, c0, 0 @ read the new csidr - msr cpsr_c, r9 - and r2, r1, #7 @ extract the length of the cache lines - add r2, r2, #4 @ add 4 (line length offset) - ldr r4, =0x3ff - ands r4, r4, r1, lsr #3 @ find maximum number on the way size - clz r5, r4 @ find bit position of way size increment - ldr r7, =0x7fff - ands r7, r7, r1, lsr #13 @ extract max number of the index size -loop1: - mov r9, r7 @ create working copy of max index -loop2: - orr r11, r10, r4, lsl r5 @ factor way and cache number into r11 - orr r11, r11, r9, lsl r2 @ factor index number into r11 - mcr p15, 0, r11, c7, c14, 2 @ clean & invalidate by set/way - subs r9, r9, #1 @ decrement the index - bge loop2 - subs r4, r4, #1 @ decrement the way - bge loop1 -skip: - add r10, r10, #2 @ increment cache number - cmp r3, r10 - bgt flush_levels -finished: - mov r10, #0 @ swith back to cache level 0 - mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr - dsb st - isb - bx lr - -_sunxi_cpu_entry: - @ Set SMP bit - mrc p15, 0, r0, c1, c0, 1 - orr r0, r0, #0x40 - mcr p15, 0, r0, c1, c0, 1 - isb - - bl _nonsec_init - - adr r0, _target_pc - ldr r0, [r0] - b _do_nonsec_entry + pop {pc} .globl psci_cpu_off psci_cpu_off: - mrc p15, 0, r0, c1, c0, 0 @ SCTLR - bic r0, r0, #(1 << 2) @ Clear C bit - mcr p15, 0, r0, c1, c0, 0 @ SCTLR - isb - dsb - - bl v7_flush_dcache_all - - clrex @ Why??? - - mrc p15, 0, r0, c1, c0, 1 @ ACTLR - bic r0, r0, #(1 << 6) @ Clear SMP bit - mcr p15, 0, r0, c1, c0, 1 @ ACTLR - isb - dsb + bl psci_cpu_off_common @ Ask CPU0 to pull the rug... movw r0, #(GICD_BASE & 0xffff) @@ -290,6 +217,8 @@ psci_cpu_off: .globl psci_arch_init psci_arch_init: + mov r6, lr + movw r4, #(GICD_BASE & 0xffff) movt r4, #(GICD_BASE >> 16) @@ -315,18 +244,12 @@ psci_arch_init: mcr p15, 0, r5, c1, c1, 0 @ Write SCR isb - mrc p15, 0, r4, c0, c0, 5 @ MPIDR - and r4, r4, #3 @ cpu number in cluster - mov r5, #0x400 @ 1kB of stack per CPU - mul r4, r4, r5 - - adr r5, text_end @ end of text - add r5, r5, #0x2000 @ Skip two pages - lsr r5, r5, #12 @ Align to start of page - lsl r5, r5, #12 - sub sp, r5, r4 @ here's our stack! + bl psci_get_cpu_id @ CPU ID => r0 + bl psci_get_cpu_stack_top @ stack top => r0 + mov sp, r0 - bx lr + bx r6 -text_end: + .globl psci_text_end +psci_text_end: .popsection diff --git a/arch/arm/cpu/armv7/virt-dt.c b/arch/arm/cpu/armv7/virt-dt.c index 9408e33203..32c368f145 100644 --- a/arch/arm/cpu/armv7/virt-dt.c +++ b/arch/arm/cpu/armv7/virt-dt.c @@ -16,6 +16,7 @@ */ #include <common.h> +#include <errno.h> #include <stdio_dev.h> #include <linux/ctype.h> #include <linux/types.h> @@ -88,9 +89,37 @@ static int fdt_psci(void *fdt) return 0; } +int armv7_apply_memory_carveout(u64 *start, u64 *size) +{ +#ifdef CONFIG_ARMV7_SECURE_RESERVE_SIZE + if (*start + *size < CONFIG_ARMV7_SECURE_BASE || + *start >= (u64)CONFIG_ARMV7_SECURE_BASE + + CONFIG_ARMV7_SECURE_RESERVE_SIZE) + return 0; + + /* carveout must be at the beginning or the end of the bank */ + if (*start == CONFIG_ARMV7_SECURE_BASE || + *start + *size == (u64)CONFIG_ARMV7_SECURE_BASE + + CONFIG_ARMV7_SECURE_RESERVE_SIZE) { + if (*size < CONFIG_ARMV7_SECURE_RESERVE_SIZE) { + debug("Secure monitor larger than RAM bank!?\n"); + return -EINVAL; + } + *size -= CONFIG_ARMV7_SECURE_RESERVE_SIZE; + if (*start == CONFIG_ARMV7_SECURE_BASE) + *start += CONFIG_ARMV7_SECURE_RESERVE_SIZE; + return 0; + } + debug("Secure monitor not located at beginning or end of RAM bank\n"); + return -EINVAL; +#else /* !CONFIG_ARMV7_SECURE_RESERVE_SIZE */ + return 0; +#endif +} + int psci_update_dt(void *fdt) { -#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT) +#ifdef CONFIG_ARMV7_NONSEC if (!armv7_boot_nonsec()) return 0; #endif diff --git a/arch/arm/cpu/armv7/virt-v7.c b/arch/arm/cpu/armv7/virt-v7.c index 4cb8806238..9c533060b8 100644 --- a/arch/arm/cpu/armv7/virt-v7.c +++ b/arch/arm/cpu/armv7/virt-v7.c @@ -46,6 +46,10 @@ static unsigned long get_gicd_base_address(void) #endif } +/* Define a specific version of this function to enable any available + * hardware protections for the reserved region */ +void __weak protect_secure_section(void) {} + static void relocate_secure_section(void) { #ifdef CONFIG_ARMV7_SECURE_BASE @@ -54,6 +58,7 @@ static void relocate_secure_section(void) memcpy((void *)CONFIG_ARMV7_SECURE_BASE, __secure_start, sz); flush_dcache_range(CONFIG_ARMV7_SECURE_BASE, CONFIG_ARMV7_SECURE_BASE + sz + 1); + protect_secure_section(); invalidate_icache_all(); #endif } @@ -75,6 +80,10 @@ void __weak smp_kick_all_cpus(void) kick_secondary_cpus_gic(gic_dist_addr); } +__weak void psci_board_init(void) +{ +} + int armv7_init_nonsec(void) { unsigned int reg; @@ -112,6 +121,8 @@ int armv7_init_nonsec(void) for (i = 1; i <= itlinesnr; i++) writel((unsigned)-1, gic_dist_addr + GICD_IGROUPRn + 4 * i); + psci_board_init(); + /* * Relocate secure section before any cpu runs in secure ram. * smp_kick_all_cpus may enable other cores and runs into secure diff --git a/arch/arm/cpu/u-boot.lds b/arch/arm/cpu/u-boot.lds index 7336162d80..03cd9f60f9 100644 --- a/arch/arm/cpu/u-boot.lds +++ b/arch/arm/cpu/u-boot.lds @@ -25,7 +25,7 @@ SECTIONS *(.text*) } -#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT) || defined(CONFIG_ARMV7_PSCI) +#ifdef CONFIG_ARMV7_NONSEC #ifndef CONFIG_ARMV7_SECURE_BASE #define CONFIG_ARMV7_SECURE_BASE diff --git a/arch/arm/dts/tegra124-nyan-big.dts b/arch/arm/dts/tegra124-nyan-big.dts index 9367193a24..5a39e93c68 100644 --- a/arch/arm/dts/tegra124-nyan-big.dts +++ b/arch/arm/dts/tegra124-nyan-big.dts @@ -29,6 +29,35 @@ reg = <0x80000000 0x80000000>; }; + host1x@50000000 { + dc@54200000 { + display-timings { + timing@0 { + clock-frequency = <69500000>; + hactive = <1366>; + vactive = <768>; + hsync-len = <32>; + hfront-porch = <48>; + hback-porch = <20>; + vfront-porch = <3>; + vback-porch = <13>; + vsync-len = <6>; + }; + }; + }; + + sor@54540000 { + status = "okay"; + + nvidia,dpaux = <&dpaux>; + nvidia,panel = <&panel>; + }; + + dpaux@545c0000 { + status = "okay"; + }; + }; + serial@70006000 { /* Debug connector on the bottom of the board near SD card. */ status = "okay"; @@ -258,6 +287,7 @@ compatible = "pwm-backlight"; enable-gpios = <&gpio TEGRA_GPIO(H, 2) GPIO_ACTIVE_HIGH>; + power-supply = <&vdd_led>; pwms = <&pwm 1 1000000>; default-brightness-level = <224>; @@ -310,6 +340,10 @@ }; }; + gpio@6000d000 { + u-boot,dm-pre-reloc; + }; + gpio-keys { compatible = "gpio-keys"; @@ -337,6 +371,19 @@ backlight = <&backlight>; }; + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + vdd_led: regulator@5 { + compatible = "regulator-fixed"; + reg = <5>; + regulator-name = "+VDD_LED"; + gpio = <&gpio TEGRA_GPIO(P, 2) GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + }; + sound { compatible = "nvidia,tegra-audio-max98090-nyan-big", "nvidia,tegra-audio-max98090"; diff --git a/arch/arm/dts/tegra124.dtsi b/arch/arm/dts/tegra124.dtsi index 9fa141d8fe..43b7f22814 100644 --- a/arch/arm/dts/tegra124.dtsi +++ b/arch/arm/dts/tegra124.dtsi @@ -76,6 +76,85 @@ }; }; + host1x@50000000 { + compatible = "nvidia,tegra124-host1x", "simple-bus"; + reg = <0x50000000 0x00034000>; + interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>, /* syncpt */ + <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>; /* general */ + clocks = <&tegra_car TEGRA124_CLK_HOST1X>; + resets = <&tegra_car 28>; + reset-names = "host1x"; + + #address-cells = <1>; + #size-cells = <1>; + + ranges = <0x54000000 0x54000000 0x01000000>; + + dc@54200000 { + compatible = "nvidia,tegra124-dc"; + reg = <0x54200000 0x00040000>; + interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&tegra_car TEGRA124_CLK_DISP1>, + <&tegra_car TEGRA124_CLK_PLL_P>; + clock-names = "dc", "parent"; + resets = <&tegra_car 27>; + reset-names = "dc"; + + nvidia,head = <0>; + }; + + dc@54240000 { + compatible = "nvidia,tegra124-dc"; + reg = <0x54240000 0x00040000>; + interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&tegra_car TEGRA124_CLK_DISP2>, + <&tegra_car TEGRA124_CLK_PLL_P>; + clock-names = "dc", "parent"; + resets = <&tegra_car 26>; + reset-names = "dc"; + + nvidia,head = <1>; + }; + + hdmi@54280000 { + compatible = "nvidia,tegra124-hdmi"; + reg = <0x54280000 0x00040000>; + interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&tegra_car TEGRA124_CLK_HDMI>, + <&tegra_car TEGRA124_CLK_PLL_D2_OUT0>; + clock-names = "hdmi", "parent"; + resets = <&tegra_car 51>; + reset-names = "hdmi"; + status = "disabled"; + }; + + sor@54540000 { + compatible = "nvidia,tegra124-sor"; + reg = <0x54540000 0x00040000>; + interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&tegra_car TEGRA124_CLK_SOR0>, + <&tegra_car TEGRA124_CLK_PLL_D_OUT0>, + <&tegra_car TEGRA124_CLK_PLL_DP>, + <&tegra_car TEGRA124_CLK_CLK_M>; + clock-names = "sor", "parent", "dp", "safe"; + resets = <&tegra_car 182>; + reset-names = "sor"; + status = "disabled"; + }; + + dpaux: dpaux@545c0000 { + compatible = "nvidia,tegra124-dpaux"; + reg = <0x545c0000 0x00040000>; + interrupts = <GIC_SPI 159 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&tegra_car TEGRA124_CLK_DPAUX>, + <&tegra_car TEGRA124_CLK_PLL_DP>; + clock-names = "dpaux", "parent"; + resets = <&tegra_car 181>; + reset-names = "dpaux"; + status = "disabled"; + }; + }; + gic: interrupt-controller@50041000 { compatible = "arm,cortex-a15-gic"; #interrupt-cells = <3>; @@ -349,6 +428,11 @@ clocks = <&tegra_car 105>; }; + pmc@7000e400 { + compatible = "nvidia,tegra124-pmc"; + reg = <0x7000e400 0x400>; + }; + padctl: padctl@7009f000 { compatible = "nvidia,tegra124-xusb-padctl"; reg = <0x7009f000 0x1000>; diff --git a/arch/arm/include/asm/arch-tegra/clk_rst.h b/arch/arm/include/asm/arch-tegra/clk_rst.h index 7d28e16f1c..de50e08201 100644 --- a/arch/arm/include/asm/arch-tegra/clk_rst.h +++ b/arch/arm/include/asm/arch-tegra/clk_rst.h @@ -202,9 +202,13 @@ struct clk_rst_ctlr { uint crc_reserved52[1]; /* _reserved_52, 0x554 */ uint crc_super_gr3d_clk_div; /* _SUPER_GR3D_CLK_DIVIDER_0, 0x558 */ uint crc_spare_reg0; /* _SPARE_REG0_0, 0x55C */ - - /* Tegra124 - skip to 0x600 here for new CLK_SOURCE_ regs */ - uint crc_reserved60[40]; /* _reserved_60, 0x560 - 0x5FC */ + u32 _rsv32[4]; /* 0x560-0x56c */ + u32 crc_plld2_ss_cfg; /* _PLLD2_SS_CFG 0x570 */ + u32 _rsv32_1[7]; /* 0x574-58c */ + struct clk_pll_simple plldp; /* _PLLDP_BASE, 0x590 _PLLDP_MISC */ + u32 crc_plldp_ss_cfg; /* _PLLDP_SS_CFG, 0x598 */ + u32 _rsrv32_2[25]; + /* Tegra124 */ uint crc_clk_src_x[TEGRA_CLK_SOURCES_X]; /* XUSB, etc, 0x600-0x678 */ }; @@ -440,4 +444,9 @@ enum { #define PLLX_IDDQ_SHIFT 3 #define PLLX_IDDQ_MASK (1U << PLLX_IDDQ_SHIFT) +/* CLK_RST_PLLDP_SS_CFG */ +#define PLLDP_SS_CFG_CLAMP (1 << 22) +#define PLLDP_SS_CFG_UNDOCUMENTED (1 << 24) +#define PLLDP_SS_CFG_DITHER (1 << 28) + #endif /* _TEGRA_CLK_RST_H_ */ diff --git a/arch/arm/include/asm/arch-tegra/clock.h b/arch/arm/include/asm/arch-tegra/clock.h index 9d8114c4ec..04011ae255 100644 --- a/arch/arm/include/asm/arch-tegra/clock.h +++ b/arch/arm/include/asm/arch-tegra/clock.h @@ -156,6 +156,17 @@ void reset_cmplx_set_enable(int cpu, int which, int reset); void clock_ll_set_source(enum periph_id periph_id, unsigned source); /** + * This function is similar to clock_ll_set_source() except that it can be + * used for clocks with more than 2 mux bits. + * + * @param periph_id peripheral to adjust + * @param mux_bits number of mux bits for the clock + * @param source source clock (0-15 depending on mux_bits) + */ +int clock_ll_set_source_bits(enum periph_id periph_id, int mux_bits, + unsigned source); + +/** * Set the source and divisor for a peripheral clock. This sets the * clock rate. You need to look up the datasheet to see the meaning of the * source parameter as it changes for each peripheral. @@ -265,6 +276,9 @@ void clock_early_init(void); /* Returns a pointer to the clock source register for a peripheral */ u32 *get_periph_source_reg(enum periph_id periph_id); +/* Returns a pointer to the given 'simple' PLL */ +struct clk_pll_simple *clock_get_simple_pll(enum clock_id clkid); + /** * Given a peripheral ID and the required source clock, this returns which * value should be programmed into the source mux for that peripheral. diff --git a/arch/arm/include/asm/arch-tegra20/dc.h b/arch/arm/include/asm/arch-tegra/dc.h index 20790b6c0e..6ffb468395 100644 --- a/arch/arm/include/asm/arch-tegra20/dc.h +++ b/arch/arm/include/asm/arch-tegra/dc.h @@ -234,7 +234,7 @@ struct dc_disp_reg { uint cursor_pos_ns; /* _DISP_CURSOR_POSITION_NS_0 */ uint seq_ctrl; /* _DISP_INIT_SEQ_CONTROL_0 */ - /* Address 0x442 ~ 0x446 */ + /* Address 0x443 ~ 0x446 */ uint spi_init_seq_data_a; /* _DISP_SPI_INIT_SEQ_DATA_A_0 */ uint spi_init_seq_data_b; /* _DISP_SPI_INIT_SEQ_DATA_B_0 */ uint spi_init_seq_data_c; /* _DISP_SPI_INIT_SEQ_DATA_C_0 */ @@ -254,6 +254,11 @@ struct dc_disp_reg { /* Address 0x4c0 ~ 0x4c1 */ uint dac_crt_ctrl; /* _DISP_DAC_CRT_CTRL_0 */ uint disp_misc_ctrl; /* _DISP_DISP_MISC_CONTROL_0 */ + + u32 rsvd_4c2[34]; /* 4c2 - 4e3 */ + + /* Address 0x4e4 */ + u32 blend_background_color; /* _DISP_BLEND_BACKGROUND_COLOR_0 */ }; enum dc_winc_filter_p { @@ -289,9 +294,9 @@ struct dc_winc_reg { uint v_filter_p[WINC_FILTER_COUNT]; }; -/* WIN A/B/C Register 0x700 ~ 0x714*/ +/* WIN A/B/C Register 0x700 ~ 0x719*/ struct dc_win_reg { - /* Address 0x700 ~ 0x714 */ + /* Address 0x700 ~ 0x719 */ uint win_opt; /* _WIN_WIN_OPTIONS_0 */ uint byte_swap; /* _WIN_BYTE_SWAP_0 */ uint buffer_ctrl; /* _WIN_BUFFER_CONTROL_0 */ @@ -313,11 +318,16 @@ struct dc_win_reg { uint blend_2win_y; /* _WIN_BLEND_2WIN_Y_0 */ uint blend_3win_xy; /* _WIN_BLEND_3WIN_XY_0 */ uint hp_fetch_ctrl; /* _WIN_HP_FETCH_CONTROL_0 */ + uint global_alpha; /* _WIN_GLOBAL_ALPHA */ + uint blend_layer_ctrl; /* _WINBUF_BLEND_LAYER_CONTROL_0 */ + uint blend_match_select; /* _WINBUF_BLEND_MATCH_SELECT_0 */ + uint blend_nomatch_select; /* _WINBUF_BLEND_NOMATCH_SELECT_0 */ + uint blend_alpha_1bit; /* _WINBUF_BLEND_ALPHA_1BIT_0 */ }; -/* WINBUF A/B/C Register 0x800 ~ 0x80a */ +/* WINBUF A/B/C Register 0x800 ~ 0x80d */ struct dc_winbuf_reg { - /* Address 0x800 ~ 0x80a */ + /* Address 0x800 ~ 0x80d */ uint start_addr; /* _WINBUF_START_ADDR_0 */ uint start_addr_ns; /* _WINBUF_START_ADDR_NS_0 */ uint start_addr_u; /* _WINBUF_START_ADDR_U_0 */ @@ -329,6 +339,9 @@ struct dc_winbuf_reg { uint addr_v_offset; /* _WINBUF_ADDR_V_OFFSET_0 */ uint addr_v_offset_ns; /* _WINBUF_ADDR_V_OFFSET_NS_0 */ uint uflow_status; /* _WINBUF_UFLOW_STATUS_0 */ + uint buffer_surface_kind; /* DC_WIN_BUFFER_SURFACE_KIND */ + uint rsvd_80c; + uint start_addr_hi; /* DC_WINBUF_START_ADDR_HI_0 */ }; /* Display Controller (DC_) regs */ @@ -339,16 +352,16 @@ struct dc_ctlr { struct dc_com_reg com; /* COM register 0x300 ~ 0x329 */ uint reserved1[0xd6]; - struct dc_disp_reg disp; /* DISP register 0x400 ~ 0x4c1 */ - uint reserved2[0x3e]; + struct dc_disp_reg disp; /* DISP register 0x400 ~ 0x4e4 */ + uint reserved2[0x1b]; struct dc_winc_reg winc; /* Window A/B/C 0x500 ~ 0x628 */ uint reserved3[0xd7]; - struct dc_win_reg win; /* WIN A/B/C 0x700 ~ 0x714*/ - uint reserved4[0xeb]; + struct dc_win_reg win; /* WIN A/B/C 0x700 ~ 0x719*/ + uint reserved4[0xe6]; - struct dc_winbuf_reg winbuf; /* WINBUF A/B/C 0x800 ~ 0x80a */ + struct dc_winbuf_reg winbuf; /* WINBUF A/B/C 0x800 ~ 0x80d */ }; #define BIT(pos) (1U << pos) @@ -399,20 +412,45 @@ enum win_color_depth_id { #define SPI_ENABLE BIT(24) #define HSPI_ENABLE BIT(25) +/* DC_CMD_STATE_ACCESS 0x040 */ +#define READ_MUX_ASSEMBLY (0 << 0) +#define READ_MUX_ACTIVE (1 << 0) +#define WRITE_MUX_ASSEMBLY (0 << 2) +#define WRITE_MUX_ACTIVE (1 << 2) + /* DC_CMD_STATE_CONTROL 0x041 */ #define GENERAL_ACT_REQ BIT(0) #define WIN_A_ACT_REQ BIT(1) #define WIN_B_ACT_REQ BIT(2) #define WIN_C_ACT_REQ BIT(3) +#define WIN_D_ACT_REQ BIT(4) +#define WIN_H_ACT_REQ BIT(5) +#define CURSOR_ACT_REQ BIT(7) #define GENERAL_UPDATE BIT(8) #define WIN_A_UPDATE BIT(9) #define WIN_B_UPDATE BIT(10) #define WIN_C_UPDATE BIT(11) +#define WIN_D_UPDATE BIT(12) +#define WIN_H_UPDATE BIT(13) +#define CURSOR_UPDATE BIT(15) +#define NC_HOST_TRIG BIT(24) /* DC_CMD_DISPLAY_WINDOW_HEADER 0x042 */ #define WINDOW_A_SELECT BIT(4) #define WINDOW_B_SELECT BIT(5) #define WINDOW_C_SELECT BIT(6) +#define WINDOW_D_SELECT BIT(7) +#define WINDOW_H_SELECT BIT(8) + +/* DC_DISP_DISP_WIN_OPTIONS 0x402 */ +#define CURSOR_ENABLE BIT(16) +#define SOR_ENABLE BIT(25) +#define TVO_ENABLE BIT(28) +#define DSI_ENABLE BIT(29) +#define HDMI_ENABLE BIT(30) + +/* DC_DISP_DISP_TIMING_OPTIONS 0x405 */ +#define VSYNC_H_POSITION(x) ((x) & 0xfff) /* DC_DISP_DISP_CLOCK_CONTROL 0x42e */ #define SHIFT_CLK_DIVIDER_SHIFT 0 @@ -526,4 +564,13 @@ enum { #define V_DDA_INC_SHIFT 16 #define V_DDA_INC_MASK (0xFFFF << V_DDA_INC_SHIFT) +#define DC_POLL_TIMEOUT_MS 50 +#define DC_N_WINDOWS 5 +#define DC_REG_SAVE_SPACE (DC_N_WINDOWS + 5) + +struct display_timing; + +int display_init(void *lcdbase, int fb_bits_per_pixel, + struct display_timing *timing); + #endif /* __ASM_ARCH_TEGRA_DC_H */ diff --git a/arch/arm/include/asm/arch-tegra/powergate.h b/arch/arm/include/asm/arch-tegra/powergate.h index 130b58bef1..2e491f1900 100644 --- a/arch/arm/include/asm/arch-tegra/powergate.h +++ b/arch/arm/include/asm/arch-tegra/powergate.h @@ -33,6 +33,7 @@ enum tegra_powergate { int tegra_powergate_sequence_power_up(enum tegra_powergate id, enum periph_id periph); +int tegra_powergate_power_on(enum tegra_powergate id); int tegra_powergate_power_off(enum tegra_powergate id); #endif diff --git a/arch/arm/include/asm/arch-tegra/pwm.h b/arch/arm/include/asm/arch-tegra/pwm.h new file mode 100644 index 0000000000..92dced448a --- /dev/null +++ b/arch/arm/include/asm/arch-tegra/pwm.h @@ -0,0 +1,60 @@ +/* + * Tegra pulse width frequency modulator definitions + * + * Copyright (c) 2011 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ASM_ARCH_TEGRA_PWM_H +#define __ASM_ARCH_TEGRA_PWM_H + +/* This is a single PWM channel */ +struct pwm_ctlr { + uint control; /* Control register */ + uint reserved[3]; /* Space space */ +}; + +#define PWM_NUM_CHANNELS 4 + +/* PWM_CONTROLLER_PWM_CSR_0/1/2/3_0 */ +#define PWM_ENABLE_SHIFT 31 +#define PWM_ENABLE_MASK (0x1 << PWM_ENABLE_SHIFT) + +#define PWM_WIDTH_SHIFT 16 +#define PWM_WIDTH_MASK (0x7FFF << PWM_WIDTH_SHIFT) + +#define PWM_DIVIDER_SHIFT 0 +#define PWM_DIVIDER_MASK (0x1FFF << PWM_DIVIDER_SHIFT) + +/** + * Program the PWM with the given parameters. + * + * @param channel PWM channel to update + * @param rate Clock rate to use for PWM, or 0 to leave alone + * @param pulse_width high pulse width: 0=always low, 1=1/256 pulse high, + * n = n/256 pulse high + * @param freq_divider frequency divider value (1 to use rate as is) + */ +void pwm_enable(unsigned channel, int rate, int pulse_width, int freq_divider); + +/** + * Request a pwm channel as referenced by a device tree node. + * + * This channel can then be passed to pwm_enable(). + * + * @param blob Device tree blob + * @param node Node containing reference to pwm + * @param prop_name Property name of pwm reference + * @return channel number, if ok, else -1 + */ +int pwm_request(const void *blob, int node, const char *prop_name); + +/** + * Set up the pwm controller, by looking it up in the fdt. + * + * @return 0 if ok, -1 if the device tree node was not found or invalid. + */ +int pwm_init(const void *blob); + +#endif /* __ASM_ARCH_TEGRA_PWM_H */ diff --git a/arch/arm/include/asm/arch-tegra/sys_proto.h b/arch/arm/include/asm/arch-tegra/sys_proto.h index 8b3fbe12fa..83f9f472c9 100644 --- a/arch/arm/include/asm/arch-tegra/sys_proto.h +++ b/arch/arm/include/asm/arch-tegra/sys_proto.h @@ -8,12 +8,21 @@ #ifndef _SYS_PROTO_H_ #define _SYS_PROTO_H_ -struct tegra_sysinfo { - char *board_string; -}; - void invalidate_dcache(void); -extern const struct tegra_sysinfo sysinfo; +/** + * tegra_board_id() - Get the board iD + * + * @return a board ID, or -ve on error + */ +int tegra_board_id(void); + +/** + * tegra_lcd_pmic_init() - Set up the PMIC for a board + * + * @board_id: Board ID which may be used to select LCD type + * @return 0 if OK, -ve on error + */ +int tegra_lcd_pmic_init(int board_id); #endif diff --git a/arch/arm/include/asm/arch-tegra124/clock-tables.h b/arch/arm/include/asm/arch-tegra124/clock-tables.h index daf9a2b351..7005855999 100644 --- a/arch/arm/include/asm/arch-tegra124/clock-tables.h +++ b/arch/arm/include/asm/arch-tegra124/clock-tables.h @@ -25,6 +25,7 @@ enum clock_id { CLOCK_ID_XCPU = CLOCK_ID_FIRST_SIMPLE, CLOCK_ID_EPCI, CLOCK_ID_SFROM32KHZ, + CLOCK_ID_DP, /* Special for Tegra124 */ /* These are the base clocks (inputs to the Tegra SoC) */ CLOCK_ID_32KHZ, @@ -424,7 +425,7 @@ enum periphc_internal_id { /* 0x58 */ PERIPHC_58h, - PERIPHC_59h, + PERIPHC_SOR, PERIPHC_5ah, PERIPHC_5bh, PERIPHC_SATAOOB, diff --git a/arch/arm/include/asm/arch-tegra124/clock.h b/arch/arm/include/asm/arch-tegra124/clock.h index 8e65086252..e202cc5a7f 100644 --- a/arch/arm/include/asm/arch-tegra124/clock.h +++ b/arch/arm/include/asm/arch-tegra124/clock.h @@ -16,6 +16,27 @@ #define OSC_FREQ_SHIFT 28 #define OSC_FREQ_MASK (0xF << OSC_FREQ_SHIFT) +/* CLK_RST_CONTROLLER_CLK_SOURCE_SOR0_0 */ +#define SOR0_CLK_SEL0 (1 << 14) +#define SOR0_CLK_SEL1 (1 << 15) + int tegra_plle_enable(void); +void clock_sor_enable_edp_clock(void); + +/** + * clock_set_display_rate() - Set the display clock rate + * + * @frequency: the requested PLLD frequency + * + * Return the PLLD frequenc (which may not quite what was requested), or 0 + * on failure + */ +u32 clock_set_display_rate(u32 frequency); + +/** + * clock_set_up_plldp() - Set up the EDP clock ready for use + */ +void clock_set_up_plldp(void); + #endif /* _TEGRA124_CLOCK_H_ */ diff --git a/arch/arm/include/asm/arch-tegra124/display.h b/arch/arm/include/asm/arch-tegra124/display.h new file mode 100644 index 0000000000..ca6644af34 --- /dev/null +++ b/arch/arm/include/asm/arch-tegra124/display.h @@ -0,0 +1,58 @@ +/* + * (C) Copyright 2010 + * NVIDIA Corporation <www.nvidia.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ASM_ARCH_TEGRA_DISPLAY_H +#define __ASM_ARCH_TEGRA_DISPLAY_H + +/** + * Register a new display based on device tree configuration. + * + * The frame buffer can be positioned by U-Boot or overriden by the fdt. + * You should pass in the U-Boot address here, and check the contents of + * struct fdt_disp_config to see what was actually chosen. + * + * @param blob Device tree blob + * @param default_lcd_base Default address of LCD frame buffer + * @return 0 if ok, -1 on error (unsupported bits per pixel) + */ +int tegra_display_probe(const void *blob, void *default_lcd_base); + +/** + * Return the current display configuration + * + * @return pointer to display configuration, or NULL if there is no valid + * config + */ +struct fdt_disp_config *tegra_display_get_config(void); + +/** + * Perform the next stage of the LCD init if it is time to do so. + * + * LCD init can be time-consuming because of the number of delays we need + * while waiting for the backlight power supply, etc. This function can + * be called at various times during U-Boot operation to advance the + * initialization of the LCD to the next stage if sufficient time has + * passed since the last stage. It keeps track of what stage it is up to + * and the time that it is permitted to move to the next stage. + * + * The final call should have wait=1 to complete the init. + * + * @param blob fdt blob containing LCD information + * @param wait 1 to wait until all init is complete, and then return + * 0 to return immediately, potentially doing nothing if it is + * not yet time for the next init. + */ +int tegra_lcd_check_next_stage(const void *blob, int wait); + +/** + * Set up the maximum LCD size so we can size the frame buffer. + * + * @param blob fdt blob containing LCD information + */ +void tegra_lcd_early_init(const void *blob); + +#endif /*__ASM_ARCH_TEGRA_DISPLAY_H*/ diff --git a/arch/arm/include/asm/arch-tegra124/flow.h b/arch/arm/include/asm/arch-tegra124/flow.h index 0db1881bc6..d6f515f1e9 100644 --- a/arch/arm/include/asm/arch-tegra124/flow.h +++ b/arch/arm/include/asm/arch-tegra124/flow.h @@ -37,4 +37,10 @@ struct flow_ctlr { /* FLOW_CTLR_CLUSTER_CONTROL_0 0x2c */ #define ACTIVE_LP (1 << 0) +/* CPUn_CSR_0 */ +#define CSR_ENABLE (1 << 0) +#define CSR_IMMEDIATE_WAKE (1 << 3) +#define CSR_WAIT_WFI_SHIFT 8 +#define CSR_PWR_OFF_STS (1 << 16) + #endif /* _TEGRA124_FLOW_H_ */ diff --git a/arch/arm/include/asm/arch-tegra124/mc.h b/arch/arm/include/asm/arch-tegra124/mc.h index d526dfe15c..37998a4d60 100644 --- a/arch/arm/include/asm/arch-tegra124/mc.h +++ b/arch/arm/include/asm/arch-tegra124/mc.h @@ -35,14 +35,47 @@ struct mc_ctlr { u32 mc_emem_adr_cfg; /* offset 0x54 */ u32 mc_emem_adr_cfg_dev0; /* offset 0x58 */ u32 mc_emem_adr_cfg_dev1; /* offset 0x5C */ - u32 reserved3[12]; /* offset 0x60 - 0x8C */ + u32 reserved3[4]; /* offset 0x60 - 0x6C */ + u32 mc_security_cfg0; /* offset 0x70 */ + u32 mc_security_cfg1; /* offset 0x74 */ + u32 reserved4[6]; /* offset 0x7C - 0x8C */ u32 mc_emem_arb_reserved[28]; /* offset 0x90 - 0xFC */ - u32 reserved4[338]; /* offset 0x100 - 0x644 */ + u32 reserved5[74]; /* offset 0x100 - 0x224 */ + u32 mc_smmu_translation_enable_0; /* offset 0x228 */ + u32 mc_smmu_translation_enable_1; /* offset 0x22C */ + u32 mc_smmu_translation_enable_2; /* offset 0x230 */ + u32 mc_smmu_translation_enable_3; /* offset 0x234 */ + u32 mc_smmu_afi_asid; /* offset 0x238 */ + u32 mc_smmu_avpc_asid; /* offset 0x23C */ + u32 mc_smmu_dc_asid; /* offset 0x240 */ + u32 mc_smmu_dcb_asid; /* offset 0x244 */ + u32 reserved6[2]; /* offset 0x248 - 0x24C */ + u32 mc_smmu_hc_asid; /* offset 0x250 */ + u32 mc_smmu_hda_asid; /* offset 0x254 */ + u32 mc_smmu_isp2_asid; /* offset 0x258 */ + u32 reserved7[2]; /* offset 0x25C - 0x260 */ + u32 mc_smmu_msenc_asid; /* offset 0x264 */ + u32 mc_smmu_nv_asid; /* offset 0x268 */ + u32 mc_smmu_nv2_asid; /* offset 0x26C */ + u32 mc_smmu_ppcs_asid; /* offset 0x270 */ + u32 mc_smmu_sata_asid; /* offset 0x274 */ + u32 reserved8[1]; /* offset 0x278 */ + u32 mc_smmu_vde_asid; /* offset 0x27C */ + u32 mc_smmu_vi_asid; /* offset 0x280 */ + u32 mc_smmu_vic_asid; /* offset 0x284 */ + u32 mc_smmu_xusb_host_asid; /* offset 0x288 */ + u32 mc_smmu_xusb_dev_asid; /* offset 0x28C */ + u32 reserved9[1]; /* offset 0x290 */ + u32 mc_smmu_tsec_asid; /* offset 0x294 */ + u32 mc_smmu_ppcs1_asid; /* offset 0x298 */ + u32 reserved10[235]; /* offset 0x29C - 0x644 */ u32 mc_video_protect_bom; /* offset 0x648 */ u32 mc_video_protect_size_mb; /* offset 0x64c */ u32 mc_video_protect_reg_ctrl; /* offset 0x650 */ }; +#define TEGRA_MC_SMMU_CONFIG_ENABLE (1 << 0) + #define TEGRA_MC_VIDEO_PROTECT_REG_WRITE_ACCESS_ENABLED (0 << 0) #define TEGRA_MC_VIDEO_PROTECT_REG_WRITE_ACCESS_DISABLED (1 << 0) diff --git a/arch/arm/include/asm/arch-tegra124/pwm.h b/arch/arm/include/asm/arch-tegra124/pwm.h new file mode 100644 index 0000000000..3d2c4324dc --- /dev/null +++ b/arch/arm/include/asm/arch-tegra124/pwm.h @@ -0,0 +1,14 @@ +/* + * Tegra pulse width frequency modulator definitions + * + * Copyright (c) 2011 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ASM_ARCH_TEGRA124_PWM_H +#define __ASM_ARCH_TEGRA124_PWM_H + +#include <asm/arch-tegra/pwm.h> + +#endif /* __ASM_ARCH_TEGRA124_PWM_H */ diff --git a/arch/arm/include/asm/arch-tegra20/display.h b/arch/arm/include/asm/arch-tegra20/display.h index 6feeda3ba8..018c9f9f76 100644 --- a/arch/arm/include/asm/arch-tegra20/display.h +++ b/arch/arm/include/asm/arch-tegra20/display.h @@ -8,7 +8,7 @@ #ifndef __ASM_ARCH_TEGRA_DISPLAY_H #define __ASM_ARCH_TEGRA_DISPLAY_H -#include <asm/arch/dc.h> +#include <asm/arch-tegra/dc.h> #include <fdtdec.h> #include <asm/gpio.h> diff --git a/arch/arm/include/asm/arch-tegra20/pwm.h b/arch/arm/include/asm/arch-tegra20/pwm.h index 8e7397d0e5..2207d9cd4d 100644 --- a/arch/arm/include/asm/arch-tegra20/pwm.h +++ b/arch/arm/include/asm/arch-tegra20/pwm.h @@ -6,55 +6,9 @@ * SPDX-License-Identifier: GPL-2.0+ */ -#ifndef __ASM_ARCH_TEGRA_PWM_H -#define __ASM_ARCH_TEGRA_PWM_H +#ifndef __ASM_ARCH_TEGRA20_PWM_H +#define __ASM_ARCH_TEGRA20_PWM_H -/* This is a single PWM channel */ -struct pwm_ctlr { - uint control; /* Control register */ - uint reserved[3]; /* Space space */ -}; +#include <asm/arch-tegra/pwm.h> -#define PWM_NUM_CHANNELS 4 - -/* PWM_CONTROLLER_PWM_CSR_0/1/2/3_0 */ -#define PWM_ENABLE_SHIFT 31 -#define PWM_ENABLE_MASK (0x1 << PWM_ENABLE_SHIFT) - -#define PWM_WIDTH_SHIFT 16 -#define PWM_WIDTH_MASK (0x7FFF << PWM_WIDTH_SHIFT) - -#define PWM_DIVIDER_SHIFT 0 -#define PWM_DIVIDER_MASK (0x1FFF << PWM_DIVIDER_SHIFT) - -/** - * Program the PWM with the given parameters. - * - * @param channel PWM channel to update - * @param rate Clock rate to use for PWM - * @param pulse_width high pulse width: 0=always low, 1=1/256 pulse high, - * n = n/256 pulse high - * @param freq_divider frequency divider value (1 to use rate as is) - */ -void pwm_enable(unsigned channel, int rate, int pulse_width, int freq_divider); - -/** - * Request a pwm channel as referenced by a device tree node. - * - * This channel can then be passed to pwm_enable(). - * - * @param blob Device tree blob - * @param node Node containing reference to pwm - * @param prop_name Property name of pwm reference - * @return channel number, if ok, else -1 - */ -int pwm_request(const void *blob, int node, const char *prop_name); - -/** - * Set up the pwm controller, by looking it up in the fdt. - * - * @return 0 if ok, -1 if the device tree node was not found or invalid. - */ -int pwm_init(const void *blob); - -#endif /* __ASM_ARCH_TEGRA_PWM_H */ +#endif /* __ASM_ARCH_TEGRA20_PWM_H */ diff --git a/arch/arm/include/asm/armv7.h b/arch/arm/include/asm/armv7.h index cbe7dc1a5c..30e7939d8e 100644 --- a/arch/arm/include/asm/armv7.h +++ b/arch/arm/include/asm/armv7.h @@ -131,9 +131,10 @@ void v7_outer_cache_inval_all(void); void v7_outer_cache_flush_range(u32 start, u32 end); void v7_outer_cache_inval_range(u32 start, u32 end); -#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT) +#ifdef CONFIG_ARMV7_NONSEC int armv7_init_nonsec(void); +int armv7_apply_memory_carveout(u64 *start, u64 *size); bool armv7_boot_nonsec(void); /* defined in assembly file */ @@ -145,7 +146,7 @@ void _smp_pen(void); extern char __secure_start[]; extern char __secure_end[]; -#endif /* CONFIG_ARMV7_NONSEC || CONFIG_ARMV7_VIRT */ +#endif /* CONFIG_ARMV7_NONSEC */ void v7_arch_cp15_set_l2aux_ctrl(u32 l2auxctrl, u32 cpu_midr, u32 cpu_rev_comb, u32 cpu_variant, diff --git a/arch/arm/include/asm/psci.h b/arch/arm/include/asm/psci.h index 50a3ca45e1..128a606444 100644 --- a/arch/arm/include/asm/psci.h +++ b/arch/arm/include/asm/psci.h @@ -34,6 +34,7 @@ #ifndef __ASSEMBLY__ int psci_update_dt(void *fdt); +void psci_board_init(void); #endif /* ! __ASSEMBLY__ */ #endif /* __ARM_PSCI_H__ */ diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index 9cd2f1e592..fe103352fc 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -77,6 +77,7 @@ void armv8_switch_to_el1(void); void gic_init(void); void gic_send_sgi(unsigned long sgino); void wait_for_wakeup(void); +void protect_secure_region(void); void smp_kick_all_cpus(void); void flush_l3_cache(void); diff --git a/arch/arm/lib/bootm-fdt.c b/arch/arm/lib/bootm-fdt.c index 665a3bc37f..0eb10a8687 100644 --- a/arch/arm/lib/bootm-fdt.c +++ b/arch/arm/lib/bootm-fdt.c @@ -17,6 +17,7 @@ #include <common.h> #include <fdt_support.h> +#include <asm/armv7.h> #include <asm/psci.h> DECLARE_GLOBAL_DATA_PTR; @@ -31,10 +32,15 @@ int arch_fixup_fdt(void *blob) for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { start[bank] = bd->bi_dram[bank].start; size[bank] = bd->bi_dram[bank].size; +#ifdef CONFIG_ARMV7_NONSEC + ret = armv7_apply_memory_carveout(&start[bank], &size[bank]); + if (ret) + return ret; +#endif } ret = fdt_fixup_memory_banks(blob, start, size, CONFIG_NR_DRAM_BANKS); -#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT) +#ifdef CONFIG_ARMV7_NONSEC if (ret) return ret; diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c index b1bff8ce26..ee56d7403e 100644 --- a/arch/arm/lib/bootm.c +++ b/arch/arm/lib/bootm.c @@ -26,7 +26,7 @@ #include <bootm.h> #include <vxworks.h> -#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT) +#ifdef CONFIG_ARMV7_NONSEC #include <asm/armv7.h> #endif @@ -238,7 +238,7 @@ static void boot_prep_linux(bootm_headers_t *images) } } -#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT) +#ifdef CONFIG_ARMV7_NONSEC bool armv7_boot_nonsec(void) { char *s = getenv("bootm_boot_mode"); @@ -305,7 +305,7 @@ static void boot_jump_linux(bootm_headers_t *images, int flag) r2 = gd->bd->bi_boot_params; if (!fake) { -#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT) +#ifdef CONFIG_ARMV7_NONSEC if (armv7_boot_nonsec()) { armv7_init_nonsec(); secure_ram_addr(_do_nonsec_entry)(kernel_entry, diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 04cef0a252..fefc180b13 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -12,10 +12,11 @@ obj-y += spl.o obj-y += cpu.o else obj-$(CONFIG_CMD_ENTERRCM) += cmd_enterrcm.o +obj-$(CONFIG_PWM_TEGRA) += pwm.o endif obj-y += ap.o -obj-y += board.o +obj-y += board.o board2.o obj-y += cache.o obj-y += clock.o obj-y += lowlevel_init.o @@ -24,6 +25,11 @@ obj-y += powergate.o obj-y += xusb-padctl.o obj-$(CONFIG_DISPLAY_CPUINFO) += sys_info.o obj-$(CONFIG_TEGRA124) += vpr.o +obj-$(CONFIG_TEGRA_CLOCK_SCALING) += emc.o + +ifndef CONFIG_SPL_BUILD +obj-$(CONFIG_ARMV7_PSCI) += psci.o +endif obj-$(CONFIG_TEGRA20) += tegra20/ obj-$(CONFIG_TEGRA30) += tegra30/ diff --git a/arch/arm/mach-tegra/ap.c b/arch/arm/mach-tegra/ap.c index a17dfd1e22..0b94e8aaf9 100644 --- a/arch/arm/mach-tegra/ap.c +++ b/arch/arm/mach-tegra/ap.c @@ -10,6 +10,7 @@ #include <common.h> #include <asm/io.h> #include <asm/arch/gp_padctrl.h> +#include <asm/arch/mc.h> #include <asm/arch-tegra/ap.h> #include <asm/arch-tegra/clock.h> #include <asm/arch-tegra/fuse.h> @@ -154,6 +155,57 @@ static void init_pmc_scratch(void) writel(odmdata, &pmc->pmc_scratch20); } +#ifdef CONFIG_ARMV7_SECURE_RESERVE_SIZE +void protect_secure_section(void) +{ + struct mc_ctlr *mc = (struct mc_ctlr *)NV_PA_MC_BASE; + + /* Must be MB aligned */ + BUILD_BUG_ON(CONFIG_ARMV7_SECURE_BASE & 0xFFFFF); + BUILD_BUG_ON(CONFIG_ARMV7_SECURE_RESERVE_SIZE & 0xFFFFF); + + writel(CONFIG_ARMV7_SECURE_BASE, &mc->mc_security_cfg0); + writel(CONFIG_ARMV7_SECURE_RESERVE_SIZE >> 20, &mc->mc_security_cfg1); +} +#endif + +#if defined(CONFIG_ARMV7_NONSEC) +static void smmu_flush(struct mc_ctlr *mc) +{ + (void)readl(&mc->mc_smmu_config); +} + +static void smmu_enable(void) +{ + struct mc_ctlr *mc = (struct mc_ctlr *)NV_PA_MC_BASE; + u32 value; + + /* + * Enable translation for all clients since access to this register + * is restricted to TrustZone-secured requestors. The kernel will use + * the per-SWGROUP enable bits to enable or disable translations. + */ + writel(0xffffffff, &mc->mc_smmu_translation_enable_0); + writel(0xffffffff, &mc->mc_smmu_translation_enable_1); + writel(0xffffffff, &mc->mc_smmu_translation_enable_2); + writel(0xffffffff, &mc->mc_smmu_translation_enable_3); + + /* + * Enable SMMU globally since access to this register is restricted + * to TrustZone-secured requestors. + */ + value = readl(&mc->mc_smmu_config); + value |= TEGRA_MC_SMMU_CONFIG_ENABLE; + writel(value, &mc->mc_smmu_config); + + smmu_flush(mc); +} +#else +static void smmu_enable(void) +{ +} +#endif + void s_init(void) { /* Init PMC scratch memory */ @@ -164,6 +216,9 @@ void s_init(void) /* init the cache */ config_cache(); + /* enable SMMU */ + smmu_enable(); + /* init vpr */ config_vpr(); } diff --git a/arch/arm/mach-tegra/board.c b/arch/arm/mach-tegra/board.c index 0ebaf19325..222de6a735 100644 --- a/arch/arm/mach-tegra/board.c +++ b/arch/arm/mach-tegra/board.c @@ -98,14 +98,6 @@ int dram_init(void) return 0; } -#ifdef CONFIG_DISPLAY_BOARDINFO -int checkboard(void) -{ - printf("Board: %s\n", sysinfo.board_string); - return 0; -} -#endif /* CONFIG_DISPLAY_BOARDINFO */ - static int uart_configs[] = { #if defined(CONFIG_TEGRA20) #if defined(CONFIG_TEGRA_UARTA_UAA_UAB) diff --git a/board/nvidia/common/board.c b/arch/arm/mach-tegra/board2.c index 018dddba15..131802ae62 100644 --- a/board/nvidia/common/board.c +++ b/arch/arm/mach-tegra/board2.c @@ -7,6 +7,7 @@ #include <common.h> #include <dm.h> +#include <errno.h> #include <ns16550.h> #include <linux/compiler.h> #include <asm/io.h> @@ -40,6 +41,7 @@ #include <asm/arch-tegra/mmc.h> #endif #include <asm/arch-tegra/xusb-padctl.h> +#include <power/as3722.h> #include <i2c.h> #include <spi.h> #include "emc.h" @@ -53,10 +55,6 @@ U_BOOT_DEVICE(tegra_gpios) = { }; #endif -const struct tegra_sysinfo sysinfo = { - CONFIG_TEGRA_BOARD_STRING -}; - __weak void pinmux_init(void) {} __weak void pin_mux_usb(void) {} __weak void pin_mux_spi(void) {} @@ -85,6 +83,30 @@ static void power_det_init(void) #endif } +__weak int tegra_board_id(void) +{ + return -1; +} + +#ifdef CONFIG_DISPLAY_BOARDINFO +int checkboard(void) +{ + int board_id = tegra_board_id(); + + printf("Board: %s", CONFIG_TEGRA_BOARD_STRING); + if (board_id != -1) + printf(", ID: %d\n", board_id); + printf("\n"); + + return 0; +} +#endif /* CONFIG_DISPLAY_BOARDINFO */ + +__weak int tegra_lcd_pmic_init(int board_it) +{ + return 0; +} + /* * Routine: board_init * Description: Early hardware init. @@ -92,6 +114,7 @@ static void power_det_init(void) int board_init(void) { __maybe_unused int err; + __maybe_unused int board_id; /* Do clocks and UART first so that printf() works */ clock_init(); @@ -124,6 +147,11 @@ int board_init(void) debug("Memory controller init failed: %d\n", err); # endif # endif /* CONFIG_TEGRA_PMU */ +#ifdef CONFIG_AS3722_POWER + err = as3722_init(NULL); + if (err && err != -ENODEV) + return err; +#endif #endif /* CONFIG_SYS_I2C_TEGRA */ #ifdef CONFIG_USB_EHCI_TEGRA @@ -132,6 +160,10 @@ int board_init(void) #endif #ifdef CONFIG_LCD + board_id = tegra_board_id(); + err = tegra_lcd_pmic_init(board_id); + if (err) + return err; tegra_lcd_check_next_stage(gd->fdt_blob, 0); #endif diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c index 7c274b5f99..cdd54388c5 100644 --- a/arch/arm/mach-tegra/clock.c +++ b/arch/arm/mach-tegra/clock.c @@ -81,9 +81,18 @@ static struct clk_pll *get_pll(enum clock_id clkid) (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; assert(clock_id_is_pll(clkid)); + if (clkid >= (enum clock_id)TEGRA_CLK_PLLS) { + debug("%s: Invalid PLL\n", __func__); + return NULL; + } return &clkrst->crc_pll[clkid]; } +__weak struct clk_pll_simple *clock_get_simple_pll(enum clock_id clkid) +{ + return NULL; +} + int clock_ll_read_pll(enum clock_id clkid, u32 *divm, u32 *divn, u32 *divp, u32 *cpcon, u32 *lfcon) { @@ -110,7 +119,7 @@ unsigned long clock_start_pll(enum clock_id clkid, u32 divm, u32 divn, u32 divp, u32 cpcon, u32 lfcon) { struct clk_pll *pll = get_pll(clkid); - u32 data; + u32 misc_data, data; /* * We cheat by treating all PLL (except PLLU) in the same fashion. @@ -119,8 +128,7 @@ unsigned long clock_start_pll(enum clock_id clkid, u32 divm, u32 divn, * - DCCON is always 0, doesn't conflict * - M,N, P of PLLP values are ignored for PLLP */ - data = (cpcon << PLL_CPCON_SHIFT) | (lfcon << PLL_LFCON_SHIFT); - writel(data, &pll->pll_misc); + misc_data = (cpcon << PLL_CPCON_SHIFT) | (lfcon << PLL_LFCON_SHIFT); data = (divm << PLL_DIVM_SHIFT) | (divn << PLL_DIVN_SHIFT) | (0 << PLL_BYPASS_SHIFT) | (1 << PLL_ENABLE_SHIFT); @@ -129,7 +137,19 @@ unsigned long clock_start_pll(enum clock_id clkid, u32 divm, u32 divn, data |= divp << PLLU_VCO_FREQ_SHIFT; else data |= divp << PLL_DIVP_SHIFT; - writel(data, &pll->pll_base); + if (pll) { + writel(misc_data, &pll->pll_misc); + writel(data, &pll->pll_base); + } else { + struct clk_pll_simple *pll = clock_get_simple_pll(clkid); + + if (!pll) { + debug("%s: Uknown simple PLL %d\n", __func__, clkid); + return 0; + } + writel(misc_data, &pll->pll_misc); + writel(data, &pll->pll_base); + } /* calculate the stable time */ return timer_get_us() + CLOCK_PLL_STABLE_DELAY_US; @@ -152,12 +172,37 @@ void clock_ll_set_source_divisor(enum periph_id periph_id, unsigned source, writel(value, reg); } -void clock_ll_set_source(enum periph_id periph_id, unsigned source) +int clock_ll_set_source_bits(enum periph_id periph_id, int mux_bits, + unsigned source) { u32 *reg = get_periph_source_reg(periph_id); - clrsetbits_le32(reg, OUT_CLK_SOURCE_31_30_MASK, - source << OUT_CLK_SOURCE_31_30_SHIFT); + switch (mux_bits) { + case MASK_BITS_31_30: + clrsetbits_le32(reg, OUT_CLK_SOURCE_31_30_MASK, + source << OUT_CLK_SOURCE_31_30_SHIFT); + break; + + case MASK_BITS_31_29: + clrsetbits_le32(reg, OUT_CLK_SOURCE_31_29_MASK, + source << OUT_CLK_SOURCE_31_29_SHIFT); + break; + + case MASK_BITS_31_28: + clrsetbits_le32(reg, OUT_CLK_SOURCE_31_28_MASK, + source << OUT_CLK_SOURCE_31_28_SHIFT); + break; + + default: + return -1; + } + + return 0; +} + +void clock_ll_set_source(enum periph_id periph_id, unsigned source) +{ + clock_ll_set_source_bits(periph_id, MASK_BITS_31_30, source); } /** @@ -306,25 +351,7 @@ static int adjust_periph_pll(enum periph_id periph_id, int source, if (source < 0) return -1; - switch (mux_bits) { - case MASK_BITS_31_30: - clrsetbits_le32(reg, OUT_CLK_SOURCE_31_30_MASK, - source << OUT_CLK_SOURCE_31_30_SHIFT); - break; - - case MASK_BITS_31_29: - clrsetbits_le32(reg, OUT_CLK_SOURCE_31_29_MASK, - source << OUT_CLK_SOURCE_31_29_SHIFT); - break; - - case MASK_BITS_31_28: - clrsetbits_le32(reg, OUT_CLK_SOURCE_31_28_MASK, - source << OUT_CLK_SOURCE_31_28_SHIFT); - break; - - default: - return -1; - } + clock_ll_set_source_bits(periph_id, mux_bits, source); udelay(2); return 0; @@ -431,6 +458,8 @@ unsigned clock_get_rate(enum clock_id clkid) return parent_rate; pll = get_pll(clkid); + if (!pll) + return 0; base = readl(&pll->pll_base); /* Oh for bf_unpack()... */ @@ -564,6 +593,7 @@ void clock_init(void) pll_rate[CLOCK_ID_MEMORY] = clock_get_rate(CLOCK_ID_MEMORY); pll_rate[CLOCK_ID_PERIPH] = clock_get_rate(CLOCK_ID_PERIPH); pll_rate[CLOCK_ID_CGENERAL] = clock_get_rate(CLOCK_ID_CGENERAL); + pll_rate[CLOCK_ID_DISPLAY] = clock_get_rate(CLOCK_ID_DISPLAY); pll_rate[CLOCK_ID_OSC] = clock_get_rate(CLOCK_ID_OSC); pll_rate[CLOCK_ID_SFROM32KHZ] = 32768; pll_rate[CLOCK_ID_XCPU] = clock_get_rate(CLOCK_ID_XCPU); @@ -571,6 +601,7 @@ void clock_init(void) debug("PLLM = %d\n", pll_rate[CLOCK_ID_MEMORY]); debug("PLLP = %d\n", pll_rate[CLOCK_ID_PERIPH]); debug("PLLC = %d\n", pll_rate[CLOCK_ID_CGENERAL]); + debug("PLLD = %d\n", pll_rate[CLOCK_ID_DISPLAY]); debug("PLLX = %d\n", pll_rate[CLOCK_ID_XCPU]); /* Do any special system timer/TSC setup */ diff --git a/board/nvidia/common/emc.c b/arch/arm/mach-tegra/emc.c index 8c62f36a7b..8c62f36a7b 100644 --- a/board/nvidia/common/emc.c +++ b/arch/arm/mach-tegra/emc.c diff --git a/board/nvidia/common/emc.h b/arch/arm/mach-tegra/emc.h index 4095235179..4095235179 100644 --- a/board/nvidia/common/emc.h +++ b/arch/arm/mach-tegra/emc.h diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index 439cff36b9..6331cd40fd 100644 --- a/arch/arm/mach-tegra/powergate.c +++ b/arch/arm/mach-tegra/powergate.c @@ -44,7 +44,7 @@ static int tegra_powergate_set(enum tegra_powergate id, bool state) return -ETIMEDOUT; } -static int tegra_powergate_power_on(enum tegra_powergate id) +int tegra_powergate_power_on(enum tegra_powergate id) { return tegra_powergate_set(id, true); } diff --git a/arch/arm/mach-tegra/psci.S b/arch/arm/mach-tegra/psci.S new file mode 100644 index 0000000000..b836da1c0e --- /dev/null +++ b/arch/arm/mach-tegra/psci.S @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2014, NVIDIA + * Copyright (C) 2015, Siemens AG + * + * Authors: + * Thierry Reding <treding@nvidia.com> + * Jan Kiszka <jan.kiszka@siemens.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <linux/linkage.h> +#include <asm/macro.h> +#include <asm/psci.h> + + .pushsection ._secure.text, "ax" + .arch_extension sec + +#define TEGRA_SB_CSR_0 0x6000c200 +#define NS_RST_VEC_WR_DIS (1 << 1) + +#define TEGRA_RESET_EXCEPTION_VECTOR 0x6000f100 + +#define TEGRA_FLOW_CTRL_BASE 0x60007000 +#define FLOW_CTRL_CPU_CSR 0x08 +#define CSR_ENABLE (1 << 0) +#define CSR_IMMEDIATE_WAKE (1 << 3) +#define CSR_WAIT_WFI_SHIFT 8 +#define FLOW_CTRL_CPU1_CSR 0x18 + +@ converts CPU ID into FLOW_CTRL_CPUn_CSR offset +.macro get_csr_reg cpu, ofs, tmp + cmp \cpu, #0 @ CPU0? + lsl \tmp, \cpu, #3 @ multiple by 8 (register offset CPU1-3) + moveq \ofs, #FLOW_CTRL_CPU_CSR + addne \ofs, \tmp, #FLOW_CTRL_CPU1_CSR - 8 +.endm + +ENTRY(psci_arch_init) + mov r6, lr + + mrc p15, 0, r5, c1, c1, 0 @ Read SCR + bic r5, r5, #1 @ Secure mode + mcr p15, 0, r5, c1, c1, 0 @ Write SCR + isb + + @ lock reset vector for non-secure + ldr r4, =TEGRA_SB_CSR_0 + ldr r5, [r4] + orr r5, r5, #NS_RST_VEC_WR_DIS + str r5, [r4] + + bl psci_get_cpu_id @ CPU ID => r0 + + adr r5, _sys_clock_freq + cmp r0, #0 + + mrceq p15, 0, r7, c14, c0, 0 @ read CNTFRQ from CPU0 + streq r7, [r5] + + ldrne r7, [r5] + mcrne p15, 0, r7, c14, c0, 0 @ write CNTFRQ to CPU1..3 + + bl psci_get_cpu_stack_top @ stack top => r0 + mov sp, r0 + + bx r6 +ENDPROC(psci_arch_init) + +_sys_clock_freq: + .word 0 + +ENTRY(psci_cpu_off) + bl psci_cpu_off_common + + bl psci_get_cpu_id @ CPU ID => r0 + + get_csr_reg r0, r2, r3 + + ldr r6, =TEGRA_FLOW_CTRL_BASE + mov r5, #(CSR_ENABLE) + mov r4, #(1 << CSR_WAIT_WFI_SHIFT) + add r5, r4, lsl r0 + str r5, [r6, r2] + +_loop: wfi + b _loop +ENDPROC(psci_cpu_off) + +ENTRY(psci_cpu_on) + push {lr} + + mov r0, r1 + bl psci_get_cpu_stack_top @ get stack top of target CPU + str r2, [r0] @ store target PC at stack top + dsb + + ldr r6, =TEGRA_RESET_EXCEPTION_VECTOR + ldr r5, =psci_cpu_entry + str r5, [r6] + + get_csr_reg r1, r2, r3 + + ldr r6, =TEGRA_FLOW_CTRL_BASE + mov r5, #(CSR_IMMEDIATE_WAKE | CSR_ENABLE) + str r5, [r6, r2] + + mov r0, #ARM_PSCI_RET_SUCCESS @ Return PSCI_RET_SUCCESS + pop {pc} +ENDPROC(psci_cpu_on) + + .globl psci_text_end +psci_text_end: + .popsection diff --git a/arch/arm/mach-tegra/tegra20/pwm.c b/arch/arm/mach-tegra/pwm.c index 5b886363f8..1c38fc1784 100644 --- a/arch/arm/mach-tegra/tegra20/pwm.c +++ b/arch/arm/mach-tegra/pwm.c @@ -1,5 +1,5 @@ /* - * Tegra2 pulse width frequency modulator definitions + * Tegra pulse width frequency modulator definitions * * Copyright (c) 2011 The Chromium OS Authors. * @@ -24,7 +24,10 @@ void pwm_enable(unsigned channel, int rate, int pulse_width, int freq_divider) assert(channel < PWM_NUM_CHANNELS); /* TODO: Can we use clock_adjust_periph_pll_div() here? */ - clock_start_periph_pll(PERIPH_ID_PWM, CLOCK_ID_SFROM32KHZ, rate); + if (rate) { + clock_start_periph_pll(PERIPH_ID_PWM, CLOCK_ID_SFROM32KHZ, + rate); + } reg = PWM_ENABLE_MASK; reg |= pulse_width << PWM_WIDTH_SHIFT; diff --git a/arch/arm/mach-tegra/tegra124/Kconfig b/arch/arm/mach-tegra/tegra124/Kconfig index 36bb636db4..6579e3f30c 100644 --- a/arch/arm/mach-tegra/tegra124/Kconfig +++ b/arch/arm/mach-tegra/tegra124/Kconfig @@ -6,6 +6,8 @@ choice config TARGET_JETSON_TK1 bool "NVIDIA Tegra124 Jetson TK1 board" + select CPU_V7_HAS_NONSEC if !SPL_BUILD + select CPU_V7_HAS_VIRT if !SPL_BUILD config TARGET_NYAN_BIG bool "Google/NVIDIA Nyan-big Chrombook" diff --git a/arch/arm/mach-tegra/tegra124/Makefile b/arch/arm/mach-tegra/tegra124/Makefile index ef2da29f30..f577f459be 100644 --- a/arch/arm/mach-tegra/tegra124/Makefile +++ b/arch/arm/mach-tegra/tegra124/Makefile @@ -11,3 +11,7 @@ obj-y += clock.o obj-y += funcmux.o obj-y += pinmux.o obj-y += xusb-padctl.o + +ifndef CONFIG_SPL_BUILD +obj-$(CONFIG_ARMV7_NONSEC) += psci.o +endif diff --git a/arch/arm/mach-tegra/tegra124/clock.c b/arch/arm/mach-tegra/tegra124/clock.c index fc8bd194dd..2d17550f73 100644 --- a/arch/arm/mach-tegra/tegra124/clock.c +++ b/arch/arm/mach-tegra/tegra124/clock.c @@ -42,6 +42,7 @@ enum clock_type_id { CLOCK_TYPE_ASPTE, CLOCK_TYPE_PMDACD2T, CLOCK_TYPE_PCST, + CLOCK_TYPE_DP, CLOCK_TYPE_PC2CC3M, CLOCK_TYPE_PC2CC3S_T, @@ -101,6 +102,10 @@ static enum clock_id clock_source[CLOCK_TYPE_COUNT][CLOCK_MAX_MUX+1] = { { CLK(PERIPH), CLK(CGENERAL), CLK(SFROM32KHZ), CLK(OSC), CLK(NONE), CLK(NONE), CLK(NONE), CLK(NONE), MASK_BITS_31_28}, + /* CLOCK_TYPE_DP */ + { CLK(NONE), CLK(NONE), CLK(NONE), CLK(NONE), + CLK(NONE), CLK(NONE), CLK(NONE), CLK(NONE), + MASK_BITS_31_28}, /* Additional clock types on Tegra114+ */ /* CLOCK_TYPE_PC2CC3M */ @@ -259,7 +264,7 @@ static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = { /* 0x58 */ TYPE(PERIPHC_58h, CLOCK_TYPE_NONE), - TYPE(PERIPHC_59h, CLOCK_TYPE_NONE), + TYPE(PERIPHC_SOR, CLOCK_TYPE_NONE), TYPE(PERIPHC_5ah, CLOCK_TYPE_NONE), TYPE(PERIPHC_5bh, CLOCK_TYPE_NONE), TYPE(PERIPHC_SATAOOB, CLOCK_TYPE_PCMT), @@ -546,7 +551,7 @@ static s8 periph_id_to_internal_id[PERIPH_ID_COUNT] = { NONE(X_RESERVED19), NONE(ADX1), NONE(DPAUX), - NONE(SOR0), + PERIPHC_SOR, NONE(X_RESERVED23), /* 184 */ @@ -594,7 +599,10 @@ u32 *get_periph_source_reg(enum periph_id periph_id) assert(periph_id >= PERIPH_ID_FIRST && periph_id < PERIPH_ID_COUNT); internal_id = periph_id_to_internal_id[periph_id]; assert(internal_id != -1); - if (internal_id >= PERIPHC_VW_FIRST) { + if (internal_id >= PERIPHC_X_FIRST) { + internal_id -= PERIPHC_X_FIRST; + return &clkrst->crc_clk_src_x[internal_id]; + } else if (internal_id >= PERIPHC_VW_FIRST) { internal_id -= PERIPHC_VW_FIRST; return &clkrst->crc_clk_src_vw[internal_id]; } else { @@ -657,8 +665,10 @@ void clock_set_enable(enum periph_id periph_id, int enable) assert(clock_periph_id_isvalid(periph_id)); if ((int)periph_id < (int)PERIPH_ID_VW_FIRST) clk = &clkrst->crc_clk_out_enb[PERIPH_REG(periph_id)]; - else + else if ((int)periph_id < PERIPH_ID_X_FIRST) clk = &clkrst->crc_clk_out_enb_vw[PERIPH_REG(periph_id)]; + else + clk = &clkrst->crc_clk_out_enb_x; reg = readl(clk); if (enable) reg |= PERIPH_MASK(periph_id); @@ -678,8 +688,10 @@ void reset_set_enable(enum periph_id periph_id, int enable) assert(clock_periph_id_isvalid(periph_id)); if (periph_id < PERIPH_ID_VW_FIRST) reset = &clkrst->crc_rst_dev[PERIPH_REG(periph_id)]; - else + else if ((int)periph_id < PERIPH_ID_X_FIRST) reset = &clkrst->crc_rst_dev_vw[PERIPH_REG(periph_id)]; + else + reset = &clkrst->crc_rst_devices_x; reg = readl(reset); if (enable) reg |= PERIPH_MASK(periph_id); @@ -933,3 +945,122 @@ int tegra_plle_enable(void) return 0; } + +void clock_sor_enable_edp_clock(void) +{ + u32 *reg; + + /* uses PLLP, has a non-standard bit layout. */ + reg = get_periph_source_reg(PERIPH_ID_SOR0); + setbits_le32(reg, SOR0_CLK_SEL0); +} + +u32 clock_set_display_rate(u32 frequency) +{ + /** + * plld (fo) = vco >> p, where 500MHz < vco < 1000MHz + * = (cf * n) >> p, where 1MHz < cf < 6MHz + * = ((ref / m) * n) >> p + * + * Iterate the possible values of p (3 bits, 2^7) to find out a minimum + * safe vco, then find best (m, n). since m has only 5 bits, we can + * iterate all possible values. Note Tegra 124 supports 11 bits for n, + * but our pll_fields has only 10 bits for n. + * + * Note values undershoot or overshoot target output frequency may not + * work if the values are not in "safe" range by panel specification. + */ + u32 ref = clock_get_rate(CLOCK_ID_OSC); + u32 divm, divn, divp, cpcon; + u32 cf, vco, rounded_rate = frequency; + u32 diff, best_diff, best_m = 0, best_n = 0, best_p; + const u32 max_m = 1 << 5, max_n = 1 << 10, max_p = 1 << 3, + mhz = 1000 * 1000, min_vco = 500 * mhz, max_vco = 1000 * mhz, + min_cf = 1 * mhz, max_cf = 6 * mhz; + int mux_bits, divider_bits, source; + + for (divp = 0, vco = frequency; vco < min_vco && divp < max_p; divp++) + vco <<= 1; + + if (vco < min_vco || vco > max_vco) { + printf("%s: Cannot find out a supported VCO for Frequency (%u)\n", + __func__, frequency); + return 0; + } + + best_p = divp; + best_diff = vco; + + for (divm = 1; divm < max_m && best_diff; divm++) { + cf = ref / divm; + if (cf < min_cf) + break; + if (cf > max_cf) + continue; + + divn = vco / cf; + if (divn >= max_n) + continue; + + diff = vco - divn * cf; + if (divn + 1 < max_n && diff > cf / 2) { + divn++; + diff = cf - diff; + } + + if (diff >= best_diff) + continue; + + best_diff = diff; + best_m = divm; + best_n = divn; + } + + if (best_n < 50) + cpcon = 2; + else if (best_n < 300) + cpcon = 3; + else if (best_n < 600) + cpcon = 8; + else + cpcon = 12; + + if (best_diff) { + printf("%s: Failed to match output frequency %u, best difference is %u\n", + __func__, frequency, best_diff); + rounded_rate = (ref / best_m * best_n) >> best_p; + } + + debug("%s: PLLD=%u ref=%u, m/n/p/cpcon=%u/%u/%u/%u\n", + __func__, rounded_rate, ref, best_m, best_n, best_p, cpcon); + + source = get_periph_clock_source(PERIPH_ID_DISP1, CLOCK_ID_DISPLAY, + &mux_bits, ÷r_bits); + clock_ll_set_source_bits(PERIPH_ID_DISP1, mux_bits, source); + clock_set_rate(CLOCK_ID_DISPLAY, best_n, best_m, best_p, cpcon); + + return rounded_rate; +} + +void clock_set_up_plldp(void) +{ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + u32 value; + + value = PLLDP_SS_CFG_UNDOCUMENTED | PLLDP_SS_CFG_DITHER; + writel(value | PLLDP_SS_CFG_CLAMP, &clkrst->crc_plldp_ss_cfg); + clock_start_pll(CLOCK_ID_DP, 1, 90, 3, 0, 0); + writel(value, &clkrst->crc_plldp_ss_cfg); +} + +struct clk_pll_simple *clock_get_simple_pll(enum clock_id clkid) +{ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + + if (clkid == CLOCK_ID_DP) + return &clkrst->plldp; + + return NULL; +} diff --git a/arch/arm/mach-tegra/tegra124/psci.c b/arch/arm/mach-tegra/tegra124/psci.c new file mode 100644 index 0000000000..16d196508c --- /dev/null +++ b/arch/arm/mach-tegra/tegra124/psci.c @@ -0,0 +1,59 @@ +/* + * (C) Copyright 2015, Siemens AG + * Author: Jan Kiszka <jan.kiszka@siemens.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/psci.h> +#include <asm/arch/flow.h> +#include <asm/arch/powergate.h> +#include <asm/arch-tegra/ap.h> +#include <asm/arch-tegra/pmc.h> + +static void park_cpu(void) +{ + while (1) + asm volatile("wfi"); +} + +/** + * Initialize power management for application processors + */ +void psci_board_init(void) +{ + struct flow_ctlr *flow = (struct flow_ctlr *)NV_PA_FLOW_BASE; + + writel((u32)park_cpu, EXCEP_VECTOR_CPU_RESET_VECTOR); + + /* + * The naturally expected order of putting these CPUs under Flow + * Controller regime would be + * - configure the Flow Controller + * - power up the CPUs + * - wait for the CPUs to hit wfi and be powered down again + * + * However, this doesn't work in practice. We rather need to power them + * up first and park them in wfi. While they are waiting there, we can + * indeed program the Flow Controller to powergate them on wfi, which + * will then happen immediately as they are already in that state. + */ + tegra_powergate_power_on(TEGRA_POWERGATE_CPU1); + tegra_powergate_power_on(TEGRA_POWERGATE_CPU2); + tegra_powergate_power_on(TEGRA_POWERGATE_CPU3); + + writel((2 << CSR_WAIT_WFI_SHIFT) | CSR_ENABLE, &flow->cpu1_csr); + writel((4 << CSR_WAIT_WFI_SHIFT) | CSR_ENABLE, &flow->cpu2_csr); + writel((8 << CSR_WAIT_WFI_SHIFT) | CSR_ENABLE, &flow->cpu3_csr); + + writel(EVENT_MODE_STOP, &flow->halt_cpu1_events); + writel(EVENT_MODE_STOP, &flow->halt_cpu2_events); + writel(EVENT_MODE_STOP, &flow->halt_cpu3_events); + + while (!(readl(&flow->cpu1_csr) & CSR_PWR_OFF_STS) || + !(readl(&flow->cpu2_csr) & CSR_PWR_OFF_STS) || + !(readl(&flow->cpu3_csr) & CSR_PWR_OFF_STS)) + /* wait */; +} diff --git a/arch/arm/mach-tegra/tegra20/Makefile b/arch/arm/mach-tegra/tegra20/Makefile index d48f9bb325..fc3fb4ae4c 100644 --- a/arch/arm/mach-tegra/tegra20/Makefile +++ b/arch/arm/mach-tegra/tegra20/Makefile @@ -7,7 +7,6 @@ ifdef CONFIG_SPL_BUILD obj-y += cpu.o else -obj-$(CONFIG_PWM_TEGRA) += pwm.o obj-$(CONFIG_VIDEO_TEGRA) += display.o endif diff --git a/arch/arm/mach-tegra/tegra20/display.c b/arch/arm/mach-tegra/tegra20/display.c index 61efed6464..b7605ff60e 100644 --- a/arch/arm/mach-tegra/tegra20/display.c +++ b/arch/arm/mach-tegra/tegra20/display.c @@ -10,7 +10,7 @@ #include <asm/arch/clock.h> #include <asm/arch/tegra.h> #include <asm/arch/display.h> -#include <asm/arch/dc.h> +#include <asm/arch-tegra/dc.h> #include <asm/arch-tegra/clk_rst.h> #include <asm/arch-tegra/timer.h> diff --git a/board/armltd/vexpress/vexpress_common.c b/board/armltd/vexpress/vexpress_common.c index cb2de2f4dd..d3b3b31924 100644 --- a/board/armltd/vexpress/vexpress_common.c +++ b/board/armltd/vexpress/vexpress_common.c @@ -181,7 +181,7 @@ ulong get_board_rev(void){ return readl((u32 *)SYS_ID); } -#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT) +#ifdef CONFIG_ARMV7_NONSEC /* Setting the address at which secondary cores start from. * Versatile Express uses one address for all cores, so ignore corenr */ diff --git a/board/avionic-design/medcom-wide/Makefile b/board/avionic-design/medcom-wide/Makefile index bcf7ccfe2a..1351d1fcfc 100644 --- a/board/avionic-design/medcom-wide/Makefile +++ b/board/avionic-design/medcom-wide/Makefile @@ -8,5 +8,3 @@ # obj-y := ../common/tamonten.o - -include $(srctree)/board/nvidia/common/common.mk diff --git a/board/avionic-design/plutux/Makefile b/board/avionic-design/plutux/Makefile index bcf7ccfe2a..1351d1fcfc 100644 --- a/board/avionic-design/plutux/Makefile +++ b/board/avionic-design/plutux/Makefile @@ -8,5 +8,3 @@ # obj-y := ../common/tamonten.o - -include $(srctree)/board/nvidia/common/common.mk diff --git a/board/avionic-design/tec-ng/Makefile b/board/avionic-design/tec-ng/Makefile index a556b92e8e..8ec9b88130 100644 --- a/board/avionic-design/tec-ng/Makefile +++ b/board/avionic-design/tec-ng/Makefile @@ -6,5 +6,3 @@ # obj-y := ../common/tamonten-ng.o - -include $(srctree)/board/nvidia/common/common.mk diff --git a/board/avionic-design/tec/Makefile b/board/avionic-design/tec/Makefile index bcf7ccfe2a..1351d1fcfc 100644 --- a/board/avionic-design/tec/Makefile +++ b/board/avionic-design/tec/Makefile @@ -8,5 +8,3 @@ # obj-y := ../common/tamonten.o - -include $(srctree)/board/nvidia/common/common.mk diff --git a/board/broadcom/bcm_ep/board.c b/board/broadcom/bcm_ep/board.c index 6a70a2e305..eaad0b3d98 100644 --- a/board/broadcom/bcm_ep/board.c +++ b/board/broadcom/bcm_ep/board.c @@ -54,7 +54,7 @@ int board_early_init_f(void) return status; } -#if defined(CONFIG_ARMV7_NONSEC) || defined(CONFIG_ARMV7_VIRT) +#ifdef CONFIG_ARMV7_NONSEC void smp_set_core_boot_addr(unsigned long addr, int corenr) { } diff --git a/board/compal/paz00/Makefile b/board/compal/paz00/Makefile index e6a0b29997..b5fde8d098 100644 --- a/board/compal/paz00/Makefile +++ b/board/compal/paz00/Makefile @@ -15,5 +15,3 @@ # obj-y := paz00.o - -include $(srctree)/board/nvidia/common/common.mk diff --git a/board/compulab/trimslice/Makefile b/board/compulab/trimslice/Makefile index 311eb92d7b..5396b21f6f 100644 --- a/board/compulab/trimslice/Makefile +++ b/board/compulab/trimslice/Makefile @@ -6,5 +6,3 @@ # obj-y := trimslice.o - -include $(srctree)/board/nvidia/common/common.mk diff --git a/board/freescale/common/arm_sleep.c b/board/freescale/common/arm_sleep.c index c06b86291a..8e8b7fa204 100644 --- a/board/freescale/common/arm_sleep.c +++ b/board/freescale/common/arm_sleep.c @@ -6,7 +6,7 @@ #include <common.h> #include <asm/io.h> -#if !defined(CONFIG_ARMV7_NONSEC) || !defined(CONFIG_ARMV7_VIRT) +#ifndef CONFIG_ARMV7_NONSEC #error " Deep sleep needs non-secure mode support. " #else #include <asm/secure.h> diff --git a/board/nvidia/common/Makefile b/board/nvidia/common/Makefile deleted file mode 100644 index e3b2651570..0000000000 --- a/board/nvidia/common/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) 2011 The Chromium OS Authors. -# SPDX-License-Identifier: GPL-2.0+ - -include $(src)/common.mk diff --git a/board/nvidia/common/common.mk b/board/nvidia/common/common.mk deleted file mode 100644 index 9a9b5298c7..0000000000 --- a/board/nvidia/common/common.mk +++ /dev/null @@ -1,3 +0,0 @@ -# common options for all tegra boards -obj-y += ../../nvidia/common/board.o -obj-$(CONFIG_TEGRA_CLOCK_SCALING) += ../../nvidia/common/emc.o diff --git a/board/nvidia/nyan-big/nyan-big.c b/board/nvidia/nyan-big/nyan-big.c index d4d2496639..ae8874bbd2 100644 --- a/board/nvidia/nyan-big/nyan-big.c +++ b/board/nvidia/nyan-big/nyan-big.c @@ -6,8 +6,11 @@ */ #include <common.h> -#include <asm/arch/gpio.h> +#include <errno.h> +#include <asm/gpio.h> #include <asm/arch/pinmux.h> +#include <power/as3722.h> +#include <power/pmic.h> #include "pinmux-config-nyan-big.h" /* @@ -25,3 +28,32 @@ void pinmux_init(void) pinmux_config_drvgrp_table(nyan_big_drvgrps, ARRAY_SIZE(nyan_big_drvgrps)); } + +int tegra_board_id(void) +{ + static const int vector[] = {GPIO_PQ3, GPIO_PT1, GPIO_PX1, + GPIO_PX4, -1}; + + gpio_claim_vector(vector, "board_id%d"); + return gpio_get_values_as_int(vector); +} + +int tegra_lcd_pmic_init(int board_id) +{ + struct udevice *pmic; + int ret; + + ret = as3722_get(&pmic); + if (ret) + return -ENOENT; + + if (board_id == 0) + as3722_write(pmic, 0x00, 0x3c); + else + as3722_write(pmic, 0x00, 0x50); + as3722_write(pmic, 0x12, 0x10); + as3722_write(pmic, 0x0c, 0x07); + as3722_write(pmic, 0x20, 0x10); + + return 0; +} diff --git a/board/toradex/apalis_t30/Makefile b/board/toradex/apalis_t30/Makefile index a968e6b79e..0ea3d8f217 100644 --- a/board/toradex/apalis_t30/Makefile +++ b/board/toradex/apalis_t30/Makefile @@ -1,6 +1,4 @@ # Copyright (c) 2014 Marcel Ziswiler # SPDX-License-Identifier: GPL-2.0+ -include $(srctree)/board/nvidia/common/common.mk - obj-y += apalis_t30.o diff --git a/board/toradex/colibri_t20/Makefile b/board/toradex/colibri_t20/Makefile index 86f78d9d9d..e5e71ac466 100644 --- a/board/toradex/colibri_t20/Makefile +++ b/board/toradex/colibri_t20/Makefile @@ -4,6 +4,4 @@ # SPDX-License-Identifier: GPL-2.0+ # -include $(srctree)/board/nvidia/common/common.mk - obj-y += colibri_t20.o diff --git a/board/toradex/colibri_t30/Makefile b/board/toradex/colibri_t30/Makefile index 3d58a4b2c1..4242902dae 100644 --- a/board/toradex/colibri_t30/Makefile +++ b/board/toradex/colibri_t30/Makefile @@ -1,6 +1,4 @@ # Copyright (c) 2013-2014 Stefan Agner # SPDX-License-Identifier: GPL-2.0+ -include $(srctree)/board/nvidia/common/common.mk - obj-y += colibri_t30.o diff --git a/common/edid.c b/common/edid.c index df797fcdd5..e08e420920 100644 --- a/common/edid.c +++ b/common/edid.c @@ -13,6 +13,7 @@ #include <common.h> #include <edid.h> #include <errno.h> +#include <fdtdec.h> #include <linux/ctype.h> #include <linux/string.h> @@ -65,6 +66,110 @@ int edid_get_ranges(struct edid1_info *edid, unsigned int *hmin, return -1; } +/* Set all parts of a timing entry to the same value */ +static void set_entry(struct timing_entry *entry, u32 value) +{ + entry->min = value; + entry->typ = value; + entry->max = value; +} + +/** + * decode_timing() - Decoding an 18-byte detailed timing record + * + * @buf: Pointer to EDID detailed timing record + * @timing: Place to put timing + */ +static void decode_timing(u8 *buf, struct display_timing *timing) +{ + uint x_mm, y_mm; + unsigned int ha, hbl, hso, hspw, hborder; + unsigned int va, vbl, vso, vspw, vborder; + + /* Edid contains pixel clock in terms of 10KHz */ + set_entry(&timing->pixelclock, (buf[0] + (buf[1] << 8)) * 10000); + x_mm = (buf[12] + ((buf[14] & 0xf0) << 4)); + y_mm = (buf[13] + ((buf[14] & 0x0f) << 8)); + ha = (buf[2] + ((buf[4] & 0xf0) << 4)); + hbl = (buf[3] + ((buf[4] & 0x0f) << 8)); + hso = (buf[8] + ((buf[11] & 0xc0) << 2)); + hspw = (buf[9] + ((buf[11] & 0x30) << 4)); + hborder = buf[15]; + va = (buf[5] + ((buf[7] & 0xf0) << 4)); + vbl = (buf[6] + ((buf[7] & 0x0f) << 8)); + vso = ((buf[10] >> 4) + ((buf[11] & 0x0c) << 2)); + vspw = ((buf[10] & 0x0f) + ((buf[11] & 0x03) << 4)); + vborder = buf[16]; + + set_entry(&timing->hactive, ha); + set_entry(&timing->hfront_porch, hso); + set_entry(&timing->hback_porch, hbl - hso - hspw); + set_entry(&timing->hsync_len, hspw); + + set_entry(&timing->vactive, va); + set_entry(&timing->vfront_porch, vso); + set_entry(&timing->vback_porch, vbl - vso - vspw); + set_entry(&timing->vsync_len, vspw); + + debug("Detailed mode clock %u Hz, %d mm x %d mm\n" + " %04x %04x %04x %04x hborder %x\n" + " %04x %04x %04x %04x vborder %x\n", + timing->pixelclock.typ, + x_mm, y_mm, + ha, ha + hso, ha + hso + hspw, + ha + hbl, hborder, + va, va + vso, va + vso + vspw, + va + vbl, vborder); +} + +int edid_get_timing(u8 *buf, int buf_size, struct display_timing *timing, + int *panel_bits_per_colourp) +{ + struct edid1_info *edid = (struct edid1_info *)buf; + bool timing_done; + int i; + + if (buf_size < sizeof(*edid) || edid_check_info(edid)) { + debug("%s: Invalid buffer\n", __func__); + return -EINVAL; + } + + if (!EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(*edid)) { + debug("%s: No preferred timing\n", __func__); + return -ENOENT; + } + + /* Look for detailed timing */ + timing_done = false; + for (i = 0; i < 4; i++) { + struct edid_monitor_descriptor *desc; + + desc = &edid->monitor_details.descriptor[i]; + if (desc->zero_flag_1 != 0) { + decode_timing((u8 *)desc, timing); + timing_done = true; + break; + } + } + if (!timing_done) + return -EINVAL; + + if (!EDID1_INFO_VIDEO_INPUT_DIGITAL(*edid)) { + debug("%s: Not a digital display\n", __func__); + return -ENOSYS; + } + if (edid->version != 1 || edid->revision < 4) { + debug("%s: EDID version %d.%d does not have required info\n", + __func__, edid->version, edid->revision); + *panel_bits_per_colourp = -1; + } else { + *panel_bits_per_colourp = + ((edid->video_input_definition & 0x70) >> 3) + 4; + } + + return 0; +} + /** * Snip the tailing whitespace/return of a string. * diff --git a/configs/nyan-big_defconfig b/configs/nyan-big_defconfig index 0d2ed51895..d447ddc56c 100644 --- a/configs/nyan-big_defconfig +++ b/configs/nyan-big_defconfig @@ -3,3 +3,5 @@ CONFIG_TEGRA=y CONFIG_TEGRA124=y CONFIG_TARGET_NYAN_BIG=y CONFIG_DEFAULT_DEVICE_TREE="tegra124-nyan-big" +CONFIG_DISPLAY_PORT=y +CONFIG_VIDEO_TEGRA124=y diff --git a/doc/device-tree-bindings/gpu/nvidia,tegra20-host1x.txt b/doc/device-tree-bindings/gpu/nvidia,tegra20-host1x.txt new file mode 100644 index 0000000000..b48f4ef31d --- /dev/null +++ b/doc/device-tree-bindings/gpu/nvidia,tegra20-host1x.txt @@ -0,0 +1,372 @@ +NVIDIA Tegra host1x + +Required properties: +- compatible: "nvidia,tegra<chip>-host1x" +- reg: Physical base address and length of the controller's registers. +- interrupts: The interrupt outputs from the controller. +- #address-cells: The number of cells used to represent physical base addresses + in the host1x address space. Should be 1. +- #size-cells: The number of cells used to represent the size of an address + range in the host1x address space. Should be 1. +- ranges: The mapping of the host1x address space to the CPU address space. +- clocks: Must contain one entry, for the module clock. + See ../clocks/clock-bindings.txt for details. +- resets: Must contain an entry for each entry in reset-names. + See ../reset/reset.txt for details. +- reset-names: Must include the following entries: + - host1x + +The host1x top-level node defines a number of children, each representing one +of the following host1x client modules: + +- mpe: video encoder + + Required properties: + - compatible: "nvidia,tegra<chip>-mpe" + - reg: Physical base address and length of the controller's registers. + - interrupts: The interrupt outputs from the controller. + - clocks: Must contain one entry, for the module clock. + See ../clocks/clock-bindings.txt for details. + - resets: Must contain an entry for each entry in reset-names. + See ../reset/reset.txt for details. + - reset-names: Must include the following entries: + - mpe + +- vi: video input + + Required properties: + - compatible: "nvidia,tegra<chip>-vi" + - reg: Physical base address and length of the controller's registers. + - interrupts: The interrupt outputs from the controller. + - clocks: Must contain one entry, for the module clock. + See ../clocks/clock-bindings.txt for details. + - resets: Must contain an entry for each entry in reset-names. + See ../reset/reset.txt for details. + - reset-names: Must include the following entries: + - vi + +- epp: encoder pre-processor + + Required properties: + - compatible: "nvidia,tegra<chip>-epp" + - reg: Physical base address and length of the controller's registers. + - interrupts: The interrupt outputs from the controller. + - clocks: Must contain one entry, for the module clock. + See ../clocks/clock-bindings.txt for details. + - resets: Must contain an entry for each entry in reset-names. + See ../reset/reset.txt for details. + - reset-names: Must include the following entries: + - epp + +- isp: image signal processor + + Required properties: + - compatible: "nvidia,tegra<chip>-isp" + - reg: Physical base address and length of the controller's registers. + - interrupts: The interrupt outputs from the controller. + - clocks: Must contain one entry, for the module clock. + See ../clocks/clock-bindings.txt for details. + - resets: Must contain an entry for each entry in reset-names. + See ../reset/reset.txt for details. + - reset-names: Must include the following entries: + - isp + +- gr2d: 2D graphics engine + + Required properties: + - compatible: "nvidia,tegra<chip>-gr2d" + - reg: Physical base address and length of the controller's registers. + - interrupts: The interrupt outputs from the controller. + - clocks: Must contain one entry, for the module clock. + See ../clocks/clock-bindings.txt for details. + - resets: Must contain an entry for each entry in reset-names. + See ../reset/reset.txt for details. + - reset-names: Must include the following entries: + - 2d + +- gr3d: 3D graphics engine + + Required properties: + - compatible: "nvidia,tegra<chip>-gr3d" + - reg: Physical base address and length of the controller's registers. + - clocks: Must contain an entry for each entry in clock-names. + See ../clocks/clock-bindings.txt for details. + - clock-names: Must include the following entries: + (This property may be omitted if the only clock in the list is "3d") + - 3d + This MUST be the first entry. + - 3d2 (Only required on SoCs with two 3D clocks) + - resets: Must contain an entry for each entry in reset-names. + See ../reset/reset.txt for details. + - reset-names: Must include the following entries: + - 3d + - 3d2 (Only required on SoCs with two 3D clocks) + +- dc: display controller + + Required properties: + - compatible: "nvidia,tegra<chip>-dc" + - reg: Physical base address and length of the controller's registers. + - interrupts: The interrupt outputs from the controller. + - clocks: Must contain an entry for each entry in clock-names. + See ../clocks/clock-bindings.txt for details. + - clock-names: Must include the following entries: + - dc + This MUST be the first entry. + - parent + - resets: Must contain an entry for each entry in reset-names. + See ../reset/reset.txt for details. + - reset-names: Must include the following entries: + - dc + - nvidia,head: The number of the display controller head. This is used to + setup the various types of output to receive video data from the given + head. + + Each display controller node has a child node, named "rgb", that represents + the RGB output associated with the controller. It can take the following + optional properties: + - nvidia,ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing + - nvidia,hpd-gpio: specifies a GPIO used for hotplug detection + - nvidia,edid: supplies a binary EDID blob + - nvidia,panel: phandle of a display panel + +- hdmi: High Definition Multimedia Interface + + Required properties: + - compatible: "nvidia,tegra<chip>-hdmi" + - reg: Physical base address and length of the controller's registers. + - interrupts: The interrupt outputs from the controller. + - hdmi-supply: supply for the +5V HDMI connector pin + - vdd-supply: regulator for supply voltage + - pll-supply: regulator for PLL + - clocks: Must contain an entry for each entry in clock-names. + See ../clocks/clock-bindings.txt for details. + - clock-names: Must include the following entries: + - hdmi + This MUST be the first entry. + - parent + - resets: Must contain an entry for each entry in reset-names. + See ../reset/reset.txt for details. + - reset-names: Must include the following entries: + - hdmi + + Optional properties: + - nvidia,ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing + - nvidia,hpd-gpio: specifies a GPIO used for hotplug detection + - nvidia,edid: supplies a binary EDID blob + - nvidia,panel: phandle of a display panel + +- tvo: TV encoder output + + Required properties: + - compatible: "nvidia,tegra<chip>-tvo" + - reg: Physical base address and length of the controller's registers. + - interrupts: The interrupt outputs from the controller. + - clocks: Must contain one entry, for the module clock. + See ../clocks/clock-bindings.txt for details. + +- dsi: display serial interface + + Required properties: + - compatible: "nvidia,tegra<chip>-dsi" + - reg: Physical base address and length of the controller's registers. + - clocks: Must contain an entry for each entry in clock-names. + See ../clocks/clock-bindings.txt for details. + - clock-names: Must include the following entries: + - dsi + This MUST be the first entry. + - lp + - parent + - resets: Must contain an entry for each entry in reset-names. + See ../reset/reset.txt for details. + - reset-names: Must include the following entries: + - dsi + - avdd-dsi-supply: phandle of a supply that powers the DSI controller + - nvidia,mipi-calibrate: Should contain a phandle and a specifier specifying + which pads are used by this DSI output and need to be calibrated. See also + ../mipi/nvidia,tegra114-mipi.txt. + + Optional properties: + - nvidia,ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing + - nvidia,hpd-gpio: specifies a GPIO used for hotplug detection + - nvidia,edid: supplies a binary EDID blob + - nvidia,panel: phandle of a display panel + +- sor: serial output resource + + Required properties: + - compatible: "nvidia,tegra124-sor" + - reg: Physical base address and length of the controller's registers. + - interrupts: The interrupt outputs from the controller. + - clocks: Must contain an entry for each entry in clock-names. + See ../clocks/clock-bindings.txt for details. + - clock-names: Must include the following entries: + - sor: clock input for the SOR hardware + - parent: input for the pixel clock + - dp: reference clock for the SOR clock + - safe: safe reference for the SOR clock during power up + - resets: Must contain an entry for each entry in reset-names. + See ../reset/reset.txt for details. + - reset-names: Must include the following entries: + - sor + + Optional properties: + - nvidia,ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing + - nvidia,hpd-gpio: specifies a GPIO used for hotplug detection + - nvidia,edid: supplies a binary EDID blob + - nvidia,panel: phandle of a display panel + + Optional properties when driving an eDP output: + - nvidia,dpaux: phandle to a DispayPort AUX interface + +- dpaux: DisplayPort AUX interface + - compatible: "nvidia,tegra124-dpaux" + - reg: Physical base address and length of the controller's registers. + - interrupts: The interrupt outputs from the controller. + - clocks: Must contain an entry for each entry in clock-names. + See ../clocks/clock-bindings.txt for details. + - clock-names: Must include the following entries: + - dpaux: clock input for the DPAUX hardware + - parent: reference clock + - resets: Must contain an entry for each entry in reset-names. + See ../reset/reset.txt for details. + - reset-names: Must include the following entries: + - dpaux + - vdd-supply: phandle of a supply that powers the DisplayPort link + +Example: + +/ { + ... + + host1x { + compatible = "nvidia,tegra20-host1x", "simple-bus"; + reg = <0x50000000 0x00024000>; + interrupts = <0 65 0x04 /* mpcore syncpt */ + 0 67 0x04>; /* mpcore general */ + clocks = <&tegra_car TEGRA20_CLK_HOST1X>; + resets = <&tegra_car 28>; + reset-names = "host1x"; + + #address-cells = <1>; + #size-cells = <1>; + + ranges = <0x54000000 0x54000000 0x04000000>; + + mpe { + compatible = "nvidia,tegra20-mpe"; + reg = <0x54040000 0x00040000>; + interrupts = <0 68 0x04>; + clocks = <&tegra_car TEGRA20_CLK_MPE>; + resets = <&tegra_car 60>; + reset-names = "mpe"; + }; + + vi { + compatible = "nvidia,tegra20-vi"; + reg = <0x54080000 0x00040000>; + interrupts = <0 69 0x04>; + clocks = <&tegra_car TEGRA20_CLK_VI>; + resets = <&tegra_car 100>; + reset-names = "vi"; + }; + + epp { + compatible = "nvidia,tegra20-epp"; + reg = <0x540c0000 0x00040000>; + interrupts = <0 70 0x04>; + clocks = <&tegra_car TEGRA20_CLK_EPP>; + resets = <&tegra_car 19>; + reset-names = "epp"; + }; + + isp { + compatible = "nvidia,tegra20-isp"; + reg = <0x54100000 0x00040000>; + interrupts = <0 71 0x04>; + clocks = <&tegra_car TEGRA20_CLK_ISP>; + resets = <&tegra_car 23>; + reset-names = "isp"; + }; + + gr2d { + compatible = "nvidia,tegra20-gr2d"; + reg = <0x54140000 0x00040000>; + interrupts = <0 72 0x04>; + clocks = <&tegra_car TEGRA20_CLK_GR2D>; + resets = <&tegra_car 21>; + reset-names = "2d"; + }; + + gr3d { + compatible = "nvidia,tegra20-gr3d"; + reg = <0x54180000 0x00040000>; + clocks = <&tegra_car TEGRA20_CLK_GR3D>; + resets = <&tegra_car 24>; + reset-names = "3d"; + }; + + dc@54200000 { + compatible = "nvidia,tegra20-dc"; + reg = <0x54200000 0x00040000>; + interrupts = <0 73 0x04>; + clocks = <&tegra_car TEGRA20_CLK_DISP1>, + <&tegra_car TEGRA20_CLK_PLL_P>; + clock-names = "dc", "parent"; + resets = <&tegra_car 27>; + reset-names = "dc"; + + rgb { + status = "disabled"; + }; + }; + + dc@54240000 { + compatible = "nvidia,tegra20-dc"; + reg = <0x54240000 0x00040000>; + interrupts = <0 74 0x04>; + clocks = <&tegra_car TEGRA20_CLK_DISP2>, + <&tegra_car TEGRA20_CLK_PLL_P>; + clock-names = "dc", "parent"; + resets = <&tegra_car 26>; + reset-names = "dc"; + + rgb { + status = "disabled"; + }; + }; + + hdmi { + compatible = "nvidia,tegra20-hdmi"; + reg = <0x54280000 0x00040000>; + interrupts = <0 75 0x04>; + clocks = <&tegra_car TEGRA20_CLK_HDMI>, + <&tegra_car TEGRA20_CLK_PLL_D_OUT0>; + clock-names = "hdmi", "parent"; + resets = <&tegra_car 51>; + reset-names = "hdmi"; + status = "disabled"; + }; + + tvo { + compatible = "nvidia,tegra20-tvo"; + reg = <0x542c0000 0x00040000>; + interrupts = <0 76 0x04>; + clocks = <&tegra_car TEGRA20_CLK_TVO>; + status = "disabled"; + }; + + dsi { + compatible = "nvidia,tegra20-dsi"; + reg = <0x54300000 0x00040000>; + clocks = <&tegra_car TEGRA20_CLK_DSI>, + <&tegra_car TEGRA20_CLK_PLL_D_OUT0>; + clock-names = "dsi", "parent"; + resets = <&tegra_car 48>; + reset-names = "dsi"; + status = "disabled"; + }; + }; + + ... +}; diff --git a/doc/device-tree-bindings/video/display-timing.txt b/doc/device-tree-bindings/video/display-timing.txt new file mode 100644 index 0000000000..e1d4a0b596 --- /dev/null +++ b/doc/device-tree-bindings/video/display-timing.txt @@ -0,0 +1,110 @@ +display-timing bindings +======================= + +display-timings node +-------------------- + +required properties: + - none + +optional properties: + - native-mode: The native mode for the display, in case multiple modes are + provided. When omitted, assume the first node is the native. + +timing subnode +-------------- + +required properties: + - hactive, vactive: display resolution + - hfront-porch, hback-porch, hsync-len: horizontal display timing parameters + in pixels + vfront-porch, vback-porch, vsync-len: vertical display timing parameters in + lines + - clock-frequency: display clock in Hz + +optional properties: + - hsync-active: hsync pulse is active low/high/ignored + - vsync-active: vsync pulse is active low/high/ignored + - de-active: data-enable pulse is active low/high/ignored + - pixelclk-active: with + - active high = drive pixel data on rising edge/ + sample data on falling edge + - active low = drive pixel data on falling edge/ + sample data on rising edge + - ignored = ignored + - interlaced (bool): boolean to enable interlaced mode + - doublescan (bool): boolean to enable doublescan mode + - doubleclk (bool): boolean to enable doubleclock mode + +All the optional properties that are not bool follow the following logic: + <1>: high active + <0>: low active + omitted: not used on hardware + +There are different ways of describing the capabilities of a display. The +devicetree representation corresponds to the one commonly found in datasheets +for displays. If a display supports multiple signal timings, the native-mode +can be specified. + +The parameters are defined as: + + +----------+-------------------------------------+----------+-------+ + | | ↑ | | | + | | |vback_porch | | | + | | ↓ | | | + +----------#######################################----------+-------+ + | # ↑ # | | + | # | # | | + | hback # | # hfront | hsync | + | porch # | hactive # porch | len | + |<-------->#<-------+--------------------------->#<-------->|<----->| + | # | # | | + | # |vactive # | | + | # | # | | + | # ↓ # | | + +----------#######################################----------+-------+ + | | ↑ | | | + | | |vfront_porch | | | + | | ↓ | | | + +----------+-------------------------------------+----------+-------+ + | | ↑ | | | + | | |vsync_len | | | + | | ↓ | | | + +----------+-------------------------------------+----------+-------+ + +Example: + + display-timings { + native-mode = <&timing0>; + timing0: 1080p24 { + /* 1920x1080p24 */ + clock-frequency = <52000000>; + hactive = <1920>; + vactive = <1080>; + hfront-porch = <25>; + hback-porch = <25>; + hsync-len = <25>; + vback-porch = <2>; + vfront-porch = <2>; + vsync-len = <2>; + hsync-active = <1>; + }; + }; + +Every required property also supports the use of ranges, so the commonly used +datasheet description with minimum, typical and maximum values can be used. + +Example: + + timing1: timing { + /* 1920x1080p24 */ + clock-frequency = <148500000>; + hactive = <1920>; + vactive = <1080>; + hsync-len = <0 44 60>; + hfront-porch = <80 88 95>; + hback-porch = <100 148 160>; + vfront-porch = <0 4 6>; + vback-porch = <0 36 50>; + vsync-len = <0 5 6>; + }; diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 381868bfb1..530bb3e128 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -495,22 +495,54 @@ int gpio_get_status(struct udevice *dev, int offset, char *buf, int buffsize) return 0; } +int gpio_claim_vector(const int *gpio_num_array, const char *fmt) +{ + int i, ret; + int gpio; + + for (i = 0; i < 32; i++) { + gpio = gpio_num_array[i]; + if (gpio == -1) + break; + ret = gpio_requestf(gpio, fmt, i); + if (ret) + goto err; + ret = gpio_direction_input(gpio); + if (ret) { + gpio_free(gpio); + goto err; + } + } + + return 0; +err: + for (i--; i >= 0; i--) + gpio_free(gpio_num_array[i]); + + return ret; +} + /* * get a number comprised of multiple GPIO values. gpio_num_array points to * the array of gpio pin numbers to scan, terminated by -1. */ -unsigned gpio_get_values_as_int(const int *gpio_num_array) +int gpio_get_values_as_int(const int *gpio_list) { int gpio; unsigned bitmask = 1; unsigned vector = 0; + int ret; while (bitmask && - ((gpio = *gpio_num_array++) != -1)) { - if (gpio_get_value(gpio)) + ((gpio = *gpio_list++) != -1)) { + ret = gpio_get_value(gpio); + if (ret < 0) + return ret; + else if (ret) vector |= bitmask; bitmask <<= 1; } + return vector; } diff --git a/drivers/power/as3722.c b/drivers/power/as3722.c index a60bb5f83f..c09e1de06f 100644 --- a/drivers/power/as3722.c +++ b/drivers/power/as3722.c @@ -27,7 +27,7 @@ #define AS3722_DEVICE_ID 0x0c #define AS3722_ASIC_ID2 0x91 -static int as3722_read(struct udevice *pmic, u8 reg, u8 *value) +int as3722_read(struct udevice *pmic, u8 reg, u8 *value) { int err; @@ -38,7 +38,7 @@ static int as3722_read(struct udevice *pmic, u8 reg, u8 *value) return 0; } -static int as3722_write(struct udevice *pmic, u8 reg, u8 value) +int as3722_write(struct udevice *pmic, u8 reg, u8 value) { int err; @@ -234,6 +234,15 @@ int as3722_gpio_direction_output(struct udevice *pmic, unsigned int gpio, return 0; } +/* Temporary function until we get the pmic framework */ +int as3722_get(struct udevice **devp) +{ + int bus = 0; + int address = 0x40; + + return i2c_get_chip_for_busnum(bus, address, 1, devp); +} + int as3722_init(struct udevice **devp) { struct udevice *pmic; @@ -258,7 +267,8 @@ int as3722_init(struct udevice **devp) debug("AS3722 revision %#x found on I2C bus %u, address %#x\n", revision, bus, address); - *devp = pmic; + if (devp) + *devp = pmic; return 0; } diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 51728b366f..2544301614 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -88,3 +88,18 @@ config VIDEO_LCD_SPI_MISO hardware and LCD panel id retrieval (if the panel can report it). The option takes a string in the format understood by 'name_to_gpio' function, e.g. PH1 for pin 1 of port H. + +config DISPLAY_PORT + bool "Enable DisplayPort support" + help + eDP (Embedded DisplayPort) is a standard widely used in laptops + to drive LCD panels. This framework provides support for enabling + these displays where supported by the video hardware. + +config VIDEO_TEGRA124 + bool "Enable video support on Tegra124" + help + Tegra124 supports many video output options including eDP and + HDMI. At present only eDP is supported by U-Boot. This option + enables this support which can be used on devices which + have an eDP display connected. diff --git a/drivers/video/Makefile b/drivers/video/Makefile index f64918e6ba..2ead7f192c 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -5,6 +5,10 @@ # SPDX-License-Identifier: GPL-2.0+ # +ifdef CONFIG_DM +obj-$(CONFIG_DISPLAY_PORT) += dp-uclass.o +endif + obj-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o obj-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o obj-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o @@ -48,3 +52,5 @@ obj-$(CONFIG_FORMIKE) += formike.o obj-$(CONFIG_LG4573) += lg4573.o obj-$(CONFIG_AM335X_LCD) += am335x-fb.o obj-$(CONFIG_VIDEO_PARADE) += parade.o + +obj-${CONFIG_VIDEO_TEGRA124} += tegra124/ diff --git a/drivers/video/dp-uclass.c b/drivers/video/dp-uclass.c new file mode 100644 index 0000000000..17f5de96ba --- /dev/null +++ b/drivers/video/dp-uclass.c @@ -0,0 +1,34 @@ +/* + * Copyright 2014 Google Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <displayport.h> +#include <errno.h> + +int display_port_read_edid(struct udevice *dev, u8 *buf, int buf_size) +{ + struct dm_display_port_ops *ops = display_port_get_ops(dev); + + if (!ops || !ops->read_edid) + return -ENOSYS; + return ops->read_edid(dev, buf, buf_size); +} + +int display_port_enable(struct udevice *dev, int panel_bpp, + const struct display_timing *timing) +{ + struct dm_display_port_ops *ops = display_port_get_ops(dev); + + if (!ops || !ops->enable) + return -ENOSYS; + return ops->enable(dev, panel_bpp, timing); +} + +UCLASS_DRIVER(display_port) = { + .id = UCLASS_DISPLAY_PORT, + .name = "display_port", +}; diff --git a/drivers/video/tegra124/Makefile b/drivers/video/tegra124/Makefile new file mode 100644 index 0000000000..52eedb0f08 --- /dev/null +++ b/drivers/video/tegra124/Makefile @@ -0,0 +1,10 @@ +# +# Copyright (c) 2014 Google, Inc +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += display.o +obj-y += dp.o +obj-y += sor.o +obj-y += tegra124-lcd.o diff --git a/drivers/video/tegra124/display.c b/drivers/video/tegra124/display.c new file mode 100644 index 0000000000..7179dbfe3c --- /dev/null +++ b/drivers/video/tegra124/display.c @@ -0,0 +1,472 @@ +/* + * Copyright 2014 Google Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Extracted from Chromium coreboot commit 3f59b13d + */ + +#include <common.h> +#include <dm.h> +#include <edid.h> +#include <errno.h> +#include <displayport.h> +#include <edid.h> +#include <fdtdec.h> +#include <lcd.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/pwm.h> +#include <asm/arch-tegra/dc.h> +#include "displayport.h" + +DECLARE_GLOBAL_DATA_PTR; + +/* return in 1000ths of a Hertz */ +static int tegra_dc_calc_refresh(const struct display_timing *timing) +{ + int h_total, v_total, refresh; + int pclk = timing->pixelclock.typ; + + h_total = timing->hactive.typ + timing->hfront_porch.typ + + timing->hback_porch.typ + timing->hsync_len.typ; + v_total = timing->vactive.typ + timing->vfront_porch.typ + + timing->vback_porch.typ + timing->vsync_len.typ; + if (!pclk || !h_total || !v_total) + return 0; + refresh = pclk / h_total; + refresh *= 1000; + refresh /= v_total; + + return refresh; +} + +static void print_mode(const struct display_timing *timing) +{ + int refresh = tegra_dc_calc_refresh(timing); + + debug("MODE:%dx%d@%d.%03uHz pclk=%d\n", + timing->hactive.typ, timing->vactive.typ, refresh / 1000, + refresh % 1000, timing->pixelclock.typ); +} + +static int update_display_mode(struct dc_ctlr *disp_ctrl, + const struct display_timing *timing, + int href_to_sync, int vref_to_sync) +{ + print_mode(timing); + + writel(0x1, &disp_ctrl->disp.disp_timing_opt); + + writel(vref_to_sync << 16 | href_to_sync, + &disp_ctrl->disp.ref_to_sync); + + writel(timing->vsync_len.typ << 16 | timing->hsync_len.typ, + &disp_ctrl->disp.sync_width); + + writel(((timing->vback_porch.typ - vref_to_sync) << 16) | + timing->hback_porch.typ, &disp_ctrl->disp.back_porch); + + writel(((timing->vfront_porch.typ + vref_to_sync) << 16) | + timing->hfront_porch.typ, &disp_ctrl->disp.front_porch); + + writel(timing->hactive.typ | (timing->vactive.typ << 16), + &disp_ctrl->disp.disp_active); + + /** + * We want to use PLLD_out0, which is PLLD / 2: + * PixelClock = (PLLD / 2) / ShiftClockDiv / PixelClockDiv. + * + * Currently most panels work inside clock range 50MHz~100MHz, and PLLD + * has some requirements to have VCO in range 500MHz~1000MHz (see + * clock.c for more detail). To simplify calculation, we set + * PixelClockDiv to 1 and ShiftClockDiv to 1. In future these values + * may be calculated by clock_display, to allow wider frequency range. + * + * Note ShiftClockDiv is a 7.1 format value. + */ + const u32 shift_clock_div = 1; + writel((PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT) | + ((shift_clock_div - 1) * 2) << SHIFT_CLK_DIVIDER_SHIFT, + &disp_ctrl->disp.disp_clk_ctrl); + debug("%s: PixelClock=%u, ShiftClockDiv=%u\n", __func__, + timing->pixelclock.typ, shift_clock_div); + return 0; +} + +static u32 tegra_dc_poll_register(void *reg, + u32 mask, u32 exp_val, u32 poll_interval_us, u32 timeout_us) +{ + u32 temp = timeout_us; + u32 reg_val = 0; + + do { + udelay(poll_interval_us); + reg_val = readl(reg); + if (timeout_us > poll_interval_us) + timeout_us -= poll_interval_us; + else + break; + } while ((reg_val & mask) != exp_val); + + if ((reg_val & mask) == exp_val) + return 0; /* success */ + + return temp; +} + +int tegra_dc_sor_general_act(struct dc_ctlr *disp_ctrl) +{ + writel(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl); + + if (tegra_dc_poll_register(&disp_ctrl->cmd.state_ctrl, + GENERAL_ACT_REQ, 0, 100, + DC_POLL_TIMEOUT_MS * 1000)) { + debug("dc timeout waiting for DC to stop\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static struct display_timing min_mode = { + .hsync_len = { .typ = 1 }, + .vsync_len = { .typ = 1 }, + .hback_porch = { .typ = 20 }, + .vback_porch = { .typ = 0 }, + .hactive = { .typ = 16 }, + .vactive = { .typ = 16 }, + .hfront_porch = { .typ = 1 }, + .vfront_porch = { .typ = 2 }, +}; + +/* Disable windows and set minimum raster timings */ +void tegra_dc_sor_disable_win_short_raster(struct dc_ctlr *disp_ctrl, + int *dc_reg_ctx) +{ + const int href_to_sync = 0, vref_to_sync = 1; + int selected_windows, i; + + selected_windows = readl(&disp_ctrl->cmd.disp_win_header); + + /* Store and clear window options */ + for (i = 0; i < DC_N_WINDOWS; ++i) { + writel(WINDOW_A_SELECT << i, &disp_ctrl->cmd.disp_win_header); + dc_reg_ctx[i] = readl(&disp_ctrl->win.win_opt); + writel(0, &disp_ctrl->win.win_opt); + writel(WIN_A_ACT_REQ << i, &disp_ctrl->cmd.state_ctrl); + } + + writel(selected_windows, &disp_ctrl->cmd.disp_win_header); + + /* Store current raster timings and set minimum timings */ + dc_reg_ctx[i++] = readl(&disp_ctrl->disp.ref_to_sync); + writel(href_to_sync | (vref_to_sync << 16), + &disp_ctrl->disp.ref_to_sync); + + dc_reg_ctx[i++] = readl(&disp_ctrl->disp.sync_width); + writel(min_mode.hsync_len.typ | (min_mode.vsync_len.typ << 16), + &disp_ctrl->disp.sync_width); + + dc_reg_ctx[i++] = readl(&disp_ctrl->disp.back_porch); + writel(min_mode.hback_porch.typ | (min_mode.vback_porch.typ << 16), + &disp_ctrl->disp.back_porch); + + dc_reg_ctx[i++] = readl(&disp_ctrl->disp.front_porch); + writel(min_mode.hfront_porch.typ | (min_mode.vfront_porch.typ << 16), + &disp_ctrl->disp.front_porch); + + dc_reg_ctx[i++] = readl(&disp_ctrl->disp.disp_active); + writel(min_mode.hactive.typ | (min_mode.vactive.typ << 16), + &disp_ctrl->disp.disp_active); + + writel(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl); +} + +/* Restore previous windows status and raster timings */ +void tegra_dc_sor_restore_win_and_raster(struct dc_ctlr *disp_ctrl, + int *dc_reg_ctx) +{ + int selected_windows, i; + + selected_windows = readl(&disp_ctrl->cmd.disp_win_header); + + for (i = 0; i < DC_N_WINDOWS; ++i) { + writel(WINDOW_A_SELECT << i, &disp_ctrl->cmd.disp_win_header); + writel(dc_reg_ctx[i], &disp_ctrl->win.win_opt); + writel(WIN_A_ACT_REQ << i, &disp_ctrl->cmd.state_ctrl); + } + + writel(selected_windows, &disp_ctrl->cmd.disp_win_header); + + writel(dc_reg_ctx[i++], &disp_ctrl->disp.ref_to_sync); + writel(dc_reg_ctx[i++], &disp_ctrl->disp.sync_width); + writel(dc_reg_ctx[i++], &disp_ctrl->disp.back_porch); + writel(dc_reg_ctx[i++], &disp_ctrl->disp.front_porch); + writel(dc_reg_ctx[i++], &disp_ctrl->disp.disp_active); + + writel(GENERAL_UPDATE, &disp_ctrl->cmd.state_ctrl); +} + +static int tegra_depth_for_bpp(int bpp) +{ + switch (bpp) { + case 32: + return COLOR_DEPTH_R8G8B8A8; + case 16: + return COLOR_DEPTH_B5G6R5; + default: + debug("Unsupported LCD bit depth"); + return -1; + } +} + +static int update_window(struct dc_ctlr *disp_ctrl, + u32 frame_buffer, int fb_bits_per_pixel, + const struct display_timing *timing) +{ + const u32 colour_white = 0xffffff; + int colour_depth; + u32 val; + + writel(WINDOW_A_SELECT, &disp_ctrl->cmd.disp_win_header); + + writel(((timing->vactive.typ << 16) | timing->hactive.typ), + &disp_ctrl->win.size); + writel(((timing->vactive.typ << 16) | + (timing->hactive.typ * fb_bits_per_pixel / 8)), + &disp_ctrl->win.prescaled_size); + writel(((timing->hactive.typ * fb_bits_per_pixel / 8 + 31) / + 32 * 32), &disp_ctrl->win.line_stride); + + colour_depth = tegra_depth_for_bpp(fb_bits_per_pixel); + if (colour_depth == -1) + return -EINVAL; + + writel(colour_depth, &disp_ctrl->win.color_depth); + + writel(frame_buffer, &disp_ctrl->winbuf.start_addr); + writel(0x1000 << V_DDA_INC_SHIFT | 0x1000 << H_DDA_INC_SHIFT, + &disp_ctrl->win.dda_increment); + + writel(colour_white, &disp_ctrl->disp.blend_background_color); + writel(CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT, + &disp_ctrl->cmd.disp_cmd); + + writel(WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access); + + val = GENERAL_ACT_REQ | WIN_A_ACT_REQ; + val |= GENERAL_UPDATE | WIN_A_UPDATE; + writel(val, &disp_ctrl->cmd.state_ctrl); + + /* Enable win_a */ + val = readl(&disp_ctrl->win.win_opt); + writel(val | WIN_ENABLE, &disp_ctrl->win.win_opt); + + return 0; +} + +static int tegra_dc_init(struct dc_ctlr *disp_ctrl) +{ + /* do not accept interrupts during initialization */ + writel(0x00000000, &disp_ctrl->cmd.int_mask); + writel(WRITE_MUX_ASSEMBLY | READ_MUX_ASSEMBLY, + &disp_ctrl->cmd.state_access); + writel(WINDOW_A_SELECT, &disp_ctrl->cmd.disp_win_header); + writel(0x00000000, &disp_ctrl->win.win_opt); + writel(0x00000000, &disp_ctrl->win.byte_swap); + writel(0x00000000, &disp_ctrl->win.buffer_ctrl); + + writel(0x00000000, &disp_ctrl->win.pos); + writel(0x00000000, &disp_ctrl->win.h_initial_dda); + writel(0x00000000, &disp_ctrl->win.v_initial_dda); + writel(0x00000000, &disp_ctrl->win.dda_increment); + writel(0x00000000, &disp_ctrl->win.dv_ctrl); + + writel(0x01000000, &disp_ctrl->win.blend_layer_ctrl); + writel(0x00000000, &disp_ctrl->win.blend_match_select); + writel(0x00000000, &disp_ctrl->win.blend_nomatch_select); + writel(0x00000000, &disp_ctrl->win.blend_alpha_1bit); + + writel(0x00000000, &disp_ctrl->winbuf.start_addr_hi); + writel(0x00000000, &disp_ctrl->winbuf.addr_h_offset); + writel(0x00000000, &disp_ctrl->winbuf.addr_v_offset); + + writel(0x00000000, &disp_ctrl->com.crc_checksum); + writel(0x00000000, &disp_ctrl->com.pin_output_enb[0]); + writel(0x00000000, &disp_ctrl->com.pin_output_enb[1]); + writel(0x00000000, &disp_ctrl->com.pin_output_enb[2]); + writel(0x00000000, &disp_ctrl->com.pin_output_enb[3]); + writel(0x00000000, &disp_ctrl->disp.disp_signal_opt0); + + return 0; +} + +static void dump_config(int panel_bpp, struct display_timing *timing) +{ + printf("timing->hactive.typ = %d\n", timing->hactive.typ); + printf("timing->vactive.typ = %d\n", timing->vactive.typ); + printf("timing->pixelclock.typ = %d\n", timing->pixelclock.typ); + + printf("timing->hfront_porch.typ = %d\n", timing->hfront_porch.typ); + printf("timing->hsync_len.typ = %d\n", timing->hsync_len.typ); + printf("timing->hback_porch.typ = %d\n", timing->hback_porch.typ); + + printf("timing->vfront_porch.typ %d\n", timing->vfront_porch.typ); + printf("timing->vsync_len.typ = %d\n", timing->vsync_len.typ); + printf("timing->vback_porch.typ = %d\n", timing->vback_porch.typ); + + printf("panel_bits_per_pixel = %d\n", panel_bpp); +} + +static int display_update_config_from_edid(struct udevice *dp_dev, + int *panel_bppp, + struct display_timing *timing) +{ + u8 buf[EDID_SIZE]; + int bpc, ret; + + ret = display_port_read_edid(dp_dev, buf, sizeof(buf)); + if (ret < 0) + return ret; + ret = edid_get_timing(buf, ret, timing, &bpc); + if (ret) + return ret; + + /* Use this information if valid */ + if (bpc != -1) + *panel_bppp = bpc * 3; + + return 0; +} + +/* Somewhat torturous method */ +static int get_backlight_info(const void *blob, struct gpio_desc *vdd, + struct gpio_desc *enable, int *pwmp) +{ + int sor, panel, backlight, power; + const u32 *prop; + int len; + int ret; + + *pwmp = 0; + sor = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_SOR); + if (sor < 0) + return -ENOENT; + panel = fdtdec_lookup_phandle(blob, sor, "nvidia,panel"); + if (panel < 0) + return -ENOENT; + backlight = fdtdec_lookup_phandle(blob, panel, "backlight"); + if (backlight < 0) + return -ENOENT; + ret = gpio_request_by_name_nodev(blob, backlight, "enable-gpios", 0, + enable, GPIOD_IS_OUT); + if (ret) + return ret; + prop = fdt_getprop(blob, backlight, "pwms", &len); + if (!prop || len != 3 * sizeof(u32)) + return -EINVAL; + *pwmp = fdt32_to_cpu(prop[1]); + + power = fdtdec_lookup_phandle(blob, backlight, "power-supply"); + if (power < 0) + return -ENOENT; + ret = gpio_request_by_name_nodev(blob, power, "gpio", 0, vdd, + GPIOD_IS_OUT); + if (ret) + goto err; + + return 0; + +err: + dm_gpio_free(NULL, enable); + return ret; +} + +int display_init(void *lcdbase, int fb_bits_per_pixel, + struct display_timing *timing) +{ + struct dc_ctlr *dc_ctlr; + const void *blob = gd->fdt_blob; + struct udevice *dp_dev; + const int href_to_sync = 1, vref_to_sync = 1; + int panel_bpp = 18; /* default 18 bits per pixel */ + u32 plld_rate; + struct gpio_desc vdd_gpio, enable_gpio; + int pwm; + int node; + int ret; + + ret = uclass_get_device(UCLASS_DISPLAY_PORT, 0, &dp_dev); + if (ret) + return ret; + + node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_DC); + if (node < 0) + return -ENOENT; + dc_ctlr = (struct dc_ctlr *)fdtdec_get_addr(blob, node, "reg"); + if (fdtdec_decode_display_timing(blob, node, 0, timing)) + return -EINVAL; + + ret = display_update_config_from_edid(dp_dev, &panel_bpp, timing); + if (ret) { + debug("%s: Failed to decode EDID, using defaults\n", __func__); + dump_config(panel_bpp, timing); + } + + if (!get_backlight_info(blob, &vdd_gpio, &enable_gpio, &pwm)) { + dm_gpio_set_value(&vdd_gpio, 1); + debug("%s: backlight vdd setting gpio %08x to %d\n", + __func__, gpio_get_number(&vdd_gpio), 1); + } + + /* + * The plld is programmed with the assumption of the SHIFT_CLK_DIVIDER + * and PIXEL_CLK_DIVIDER are zero (divide by 1). See the + * update_display_mode() for detail. + */ + plld_rate = clock_set_display_rate(timing->pixelclock.typ * 2); + if (plld_rate == 0) { + printf("dc: clock init failed\n"); + return -EIO; + } else if (plld_rate != timing->pixelclock.typ * 2) { + debug("dc: plld rounded to %u\n", plld_rate); + timing->pixelclock.typ = plld_rate / 2; + } + + /* Init dc */ + ret = tegra_dc_init(dc_ctlr); + if (ret) { + debug("dc: init failed\n"); + return ret; + } + + /* Configure dc mode */ + ret = update_display_mode(dc_ctlr, timing, href_to_sync, vref_to_sync); + if (ret) { + debug("dc: failed to configure display mode\n"); + return ret; + } + + /* Enable dp */ + ret = display_port_enable(dp_dev, panel_bpp, timing); + if (ret) + return ret; + + ret = update_window(dc_ctlr, (ulong)lcdbase, fb_bits_per_pixel, timing); + if (ret) + return ret; + + /* Set up Tegra PWM to drive the panel backlight */ + pwm_enable(pwm, 0, 220, 0x2e); + udelay(10 * 1000); + + if (dm_gpio_is_valid(&enable_gpio)) { + dm_gpio_set_value(&enable_gpio, 1); + debug("%s: backlight enable setting gpio %08x to %d\n", + __func__, gpio_get_number(&enable_gpio), 1); + } + + return 0; +} diff --git a/drivers/video/tegra124/displayport.h b/drivers/video/tegra124/displayport.h new file mode 100644 index 0000000000..ace6ab02a9 --- /dev/null +++ b/drivers/video/tegra124/displayport.h @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2014, NVIDIA Corporation. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _TEGRA_DISPLAYPORT_H +#define _TEGRA_DISPLAYPORT_H + +#include <linux/drm_dp_helper.h> + +struct dpaux_ctlr { + u32 reserved0; + u32 intr_en_aux; + u32 reserved2_4; + u32 intr_aux; +}; + +#define DPAUX_INTR_EN_AUX 0x1 +#define DPAUX_INTR_AUX 0x5 +#define DPAUX_DP_AUXDATA_WRITE_W(i) (0x9 + 4 * (i)) +#define DPAUX_DP_AUXDATA_READ_W(i) (0x19 + 4 * (i)) +#define DPAUX_DP_AUXADDR 0x29 +#define DPAUX_DP_AUXCTL 0x2d +#define DPAUX_DP_AUXCTL_CMDLEN_SHIFT 0 +#define DPAUX_DP_AUXCTL_CMDLEN_FIELD 0xff +#define DPAUX_DP_AUXCTL_CMD_SHIFT 12 +#define DPAUX_DP_AUXCTL_CMD_MASK (0xf << 12) +#define DPAUX_DP_AUXCTL_CMD_I2CWR (0 << 12) +#define DPAUX_DP_AUXCTL_CMD_I2CRD (1 << 12) +#define DPAUX_DP_AUXCTL_CMD_I2CREQWSTAT (2 << 12) +#define DPAUX_DP_AUXCTL_CMD_MOTWR (4 << 12) +#define DPAUX_DP_AUXCTL_CMD_MOTRD (5 << 12) +#define DPAUX_DP_AUXCTL_CMD_MOTREQWSTAT (6 << 12) +#define DPAUX_DP_AUXCTL_CMD_AUXWR (8 << 12) +#define DPAUX_DP_AUXCTL_CMD_AUXRD (9 << 12) +#define DPAUX_DP_AUXCTL_TRANSACTREQ_SHIFT 16 +#define DPAUX_DP_AUXCTL_TRANSACTREQ_MASK (0x1 << 16) +#define DPAUX_DP_AUXCTL_TRANSACTREQ_DONE (0 << 16) +#define DPAUX_DP_AUXCTL_TRANSACTREQ_PENDING (1 << 16) +#define DPAUX_DP_AUXCTL_RST_SHIFT 31 +#define DPAUX_DP_AUXCTL_RST_DEASSERT (0 << 31) +#define DPAUX_DP_AUXCTL_RST_ASSERT (1 << 31) +#define DPAUX_DP_AUXSTAT 0x31 +#define DPAUX_DP_AUXSTAT_HPD_STATUS_SHIFT 28 +#define DPAUX_DP_AUXSTAT_HPD_STATUS_UNPLUG (0 << 28) +#define DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED (1 << 28) +#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_SHIFT 20 +#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_MASK (0xf << 20) +#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_IDLE (0 << 20) +#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_SYNC (1 << 20) +#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_START1 (2 << 20) +#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_COMMAND (3 << 20) +#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_ADDRESS (4 << 20) +#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_LENGTH (5 << 20) +#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_WRITE1 (6 << 20) +#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_READ1 (7 << 20) +#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_GET_M (8 << 20) +#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_STOP1 (9 << 20) +#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_STOP2 (10 << 20) +#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_REPLY (11 << 20) +#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_CLEANUP (12 << 20) +#define DPAUX_DP_AUXSTAT_REPLYTYPE_SHIFT 16 +#define DPAUX_DP_AUXSTAT_REPLYTYPE_MASK (0xf << 16) +#define DPAUX_DP_AUXSTAT_REPLYTYPE_ACK (0 << 16) +#define DPAUX_DP_AUXSTAT_REPLYTYPE_NACK (1 << 16) +#define DPAUX_DP_AUXSTAT_REPLYTYPE_DEFER (2 << 16) +#define DPAUX_DP_AUXSTAT_REPLYTYPE_I2CNACK (4 << 16) +#define DPAUX_DP_AUXSTAT_REPLYTYPE_I2CDEFER (8 << 16) +#define DPAUX_DP_AUXSTAT_NO_STOP_ERROR_SHIFT 11 +#define DPAUX_DP_AUXSTAT_NO_STOP_ERROR_NOT_PENDING (0 << 11) +#define DPAUX_DP_AUXSTAT_NO_STOP_ERROR_PENDING (1 << 11) +#define DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_SHIFT 10 +#define DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_NOT_PENDING (0 << 10) +#define DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_PENDING (1 << 10) +#define DPAUX_DP_AUXSTAT_RX_ERROR_SHIFT 9 +#define DPAUX_DP_AUXSTAT_RX_ERROR_NOT_PENDING (0 << 9) +#define DPAUX_DP_AUXSTAT_RX_ERROR_PENDING (1 << 9) +#define DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_SHIFT 8 +#define DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_NOT_PENDING (0 << 8) +#define DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_PENDING (1 << 8) +#define DPAUX_DP_AUXSTAT_REPLY_M_SHIFT 0 +#define DPAUX_DP_AUXSTAT_REPLY_M_MASK (0xff << 0) +#define DPAUX_HPD_CONFIG (0x3d) +#define DPAUX_HPD_IRQ_CONFIG 0x41 +#define DPAUX_DP_AUX_CONFIG 0x45 +#define DPAUX_HYBRID_PADCTL 0x49 +#define DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV_SHIFT 15 +#define DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV_DISABLE (0 << 15) +#define DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV_ENABLE (1 << 15) +#define DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV_SHIFT 14 +#define DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV_DISABLE (0 << 14) +#define DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV_ENABLE (1 << 14) +#define DPAUX_HYBRID_PADCTL_AUX_CMH_SHIFT 12 +#define DPAUX_HYBRID_PADCTL_AUX_CMH_DEFAULT_MASK (0x3 << 12) +#define DPAUX_HYBRID_PADCTL_AUX_CMH_V0_60 (0 << 12) +#define DPAUX_HYBRID_PADCTL_AUX_CMH_V0_64 (1 << 12) +#define DPAUX_HYBRID_PADCTL_AUX_CMH_V0_70 (2 << 12) +#define DPAUX_HYBRID_PADCTL_AUX_CMH_V0_56 (3 << 12) +#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_SHIFT 8 +#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_DEFAULT_MASK (0x7 << 8) +#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_78 (0 << 8) +#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_60 (1 << 8) +#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_54 (2 << 8) +#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_45 (3 << 8) +#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_50 (4 << 8) +#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_42 (5 << 8) +#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_39 (6 << 8) +#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_34 (7 << 8) +#define DPAUX_HYBRID_PADCTL_AUX_DRVI_SHIFT 2 +#define DPAUX_HYBRID_PADCTL_AUX_DRVI_DEFAULT_MASK (0x3f << 2) +#define DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV_SHIFT 1 +#define DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV_DISABLE (0 << 1) +#define DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV_ENABLE (1 << 1) +#define DPAUX_HYBRID_PADCTL_MODE_SHIFT 0 +#define DPAUX_HYBRID_PADCTL_MODE_AUX 0 +#define DPAUX_HYBRID_PADCTL_MODE_I2C 1 +#define DPAUX_HYBRID_SPARE 0x4d +#define DPAUX_HYBRID_SPARE_PAD_PWR_POWERUP 0 +#define DPAUX_HYBRID_SPARE_PAD_PWR_POWERDOWN 1 + +#define DP_AUX_DEFER_MAX_TRIES 7 +#define DP_AUX_TIMEOUT_MAX_TRIES 2 +#define DP_POWER_ON_MAX_TRIES 3 + +#define DP_AUX_MAX_BYTES 16 + +#define DP_AUX_TIMEOUT_MS 40 +#define DP_DPCP_RETRY_SLEEP_NS 400 + +static const u32 tegra_dp_vs_regs[][4][4] = { + /* postcursor2 L0 */ + { + /* pre-emphasis: L0, L1, L2, L3 */ + {0x13, 0x19, 0x1e, 0x28}, /* voltage swing: L0 */ + {0x1e, 0x25, 0x2d}, /* L1 */ + {0x28, 0x32}, /* L2 */ + {0x3c}, /* L3 */ + }, + + /* postcursor2 L1 */ + { + {0x12, 0x17, 0x1b, 0x25}, + {0x1c, 0x23, 0x2a}, + {0x25, 0x2f}, + {0x39}, + }, + + /* postcursor2 L2 */ + { + {0x12, 0x16, 0x1a, 0x22}, + {0x1b, 0x20, 0x27}, + {0x24, 0x2d}, + {0x36}, + }, + + /* postcursor2 L3 */ + { + {0x11, 0x14, 0x17, 0x1f}, + {0x19, 0x1e, 0x24}, + {0x22, 0x2a}, + {0x32}, + }, +}; + +static const u32 tegra_dp_pe_regs[][4][4] = { + /* postcursor2 L0 */ + { + /* pre-emphasis: L0, L1, L2, L3 */ + {0x00, 0x09, 0x13, 0x25}, /* voltage swing: L0 */ + {0x00, 0x0f, 0x1e}, /* L1 */ + {0x00, 0x14}, /* L2 */ + {0x00}, /* L3 */ + }, + + /* postcursor2 L1 */ + { + {0x00, 0x0a, 0x14, 0x28}, + {0x00, 0x0f, 0x1e}, + {0x00, 0x14}, + {0x00}, + }, + + /* postcursor2 L2 */ + { + {0x00, 0x0a, 0x14, 0x28}, + {0x00, 0x0f, 0x1e}, + {0x00, 0x14}, + {0x00}, + }, + + /* postcursor2 L3 */ + { + {0x00, 0x0a, 0x14, 0x28}, + {0x00, 0x0f, 0x1e}, + {0x00, 0x14}, + {0x00}, + }, +}; + +static const u32 tegra_dp_pc_regs[][4][4] = { + /* postcursor2 L0 */ + { + /* pre-emphasis: L0, L1, L2, L3 */ + {0x00, 0x00, 0x00, 0x00}, /* voltage swing: L0 */ + {0x00, 0x00, 0x00}, /* L1 */ + {0x00, 0x00}, /* L2 */ + {0x00}, /* L3 */ + }, + + /* postcursor2 L1 */ + { + {0x02, 0x02, 0x04, 0x05}, + {0x02, 0x04, 0x05}, + {0x04, 0x05}, + {0x05}, + }, + + /* postcursor2 L2 */ + { + {0x04, 0x05, 0x08, 0x0b}, + {0x05, 0x09, 0x0b}, + {0x08, 0x0a}, + {0x0b}, + }, + + /* postcursor2 L3 */ + { + {0x05, 0x09, 0x0b, 0x12}, + {0x09, 0x0d, 0x12}, + {0x0b, 0x0f}, + {0x12}, + }, +}; + +static const u32 tegra_dp_tx_pu[][4][4] = { + /* postcursor2 L0 */ + { + /* pre-emphasis: L0, L1, L2, L3 */ + {0x20, 0x30, 0x40, 0x60}, /* voltage swing: L0 */ + {0x30, 0x40, 0x60}, /* L1 */ + {0x40, 0x60}, /* L2 */ + {0x60}, /* L3 */ + }, + + /* postcursor2 L1 */ + { + {0x20, 0x20, 0x30, 0x50}, + {0x30, 0x40, 0x50}, + {0x40, 0x50}, + {0x60}, + }, + + /* postcursor2 L2 */ + { + {0x20, 0x20, 0x30, 0x40}, + {0x30, 0x30, 0x40}, + {0x40, 0x50}, + {0x60}, + }, + + /* postcursor2 L3 */ + { + {0x20, 0x20, 0x20, 0x40}, + {0x30, 0x30, 0x40}, + {0x40, 0x40}, + {0x60}, + }, +}; + +enum { + DRIVECURRENT_LEVEL0 = 0, + DRIVECURRENT_LEVEL1 = 1, + DRIVECURRENT_LEVEL2 = 2, + DRIVECURRENT_LEVEL3 = 3, +}; + +enum { + PREEMPHASIS_DISABLED = 0, + PREEMPHASIS_LEVEL1 = 1, + PREEMPHASIS_LEVEL2 = 2, + PREEMPHASIS_LEVEL3 = 3, +}; + +enum { + POSTCURSOR2_LEVEL0 = 0, + POSTCURSOR2_LEVEL1 = 1, + POSTCURSOR2_LEVEL2 = 2, + POSTCURSOR2_LEVEL3 = 3, + POSTCURSOR2_SUPPORTED +}; + +static inline int tegra_dp_is_max_vs(u32 pe, u32 vs) +{ + return (vs < (DRIVECURRENT_LEVEL3 - pe)) ? 0 : 1; +} + +static inline int tegra_dp_is_max_pe(u32 pe, u32 vs) +{ + return (pe < (PREEMPHASIS_LEVEL3 - vs)) ? 0 : 1; +} + +static inline int tegra_dp_is_max_pc(u32 pc) +{ + return (pc < POSTCURSOR2_LEVEL3) ? 0 : 1; +} + +/* DPCD definitions which are not defined in drm_dp_helper.h */ +#define DP_DPCD_REV_MAJOR_SHIFT 4 +#define DP_DPCD_REV_MAJOR_MASK (0xf << 4) +#define DP_DPCD_REV_MINOR_SHIFT 0 +#define DP_DPCD_REV_MINOR_MASK 0xf + +#define DP_MAX_LINK_RATE_VAL_1_62_GPBS 0x6 +#define DP_MAX_LINK_RATE_VAL_2_70_GPBS 0xa +#define DP_MAX_LINK_RATE_VAL_5_40_GPBS 0x4 + +#define DP_MAX_LANE_COUNT_LANE_1 0x1 +#define DP_MAX_LANE_COUNT_LANE_2 0x2 +#define DP_MAX_LANE_COUNT_LANE_4 0x4 +#define DP_MAX_LANE_COUNT_TPS3_SUPPORTED_YES (1 << 6) +#define DP_MAX_LANE_COUNT_ENHANCED_FRAMING_YES (1 << 7) + +#define NV_DPCD_TRAINING_LANEX_SET_DC_SHIFT 0 +#define NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_T (0x00000001 << 2) +#define NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_F (0x00000000 << 2) +#define NV_DPCD_TRAINING_LANEX_SET_PE_SHIFT 3 +#define NV_DPCD_TRAINING_LANEX_SET_PE_MAX_REACHED_T (0x00000001 << 5) +#define NV_DPCD_TRAINING_LANEX_SET_PE_MAX_REACHED_F (0x00000000 << 5) + +#define DP_MAX_DOWNSPREAD_VAL_NONE 0 +#define DP_MAX_DOWNSPREAD_VAL_0_5_PCT 1 +#define DP_MAX_DOWNSPREAD_NO_AUX_HANDSHAKE_LT_T (1 << 6) + +#define DP_EDP_CONFIGURATION_CAP_ASC_RESET_YES 1 +#define DP_EDP_CONFIGURATION_CAP_FRAMING_CHANGE_YES (1 << 1) + +#define DP_LANE_COUNT_SET_ENHANCEDFRAMING_T (1 << 7) + +#define DP_TRAINING_PATTERN_SET_SC_DISABLED_T (1 << 5) +#define NV_DPCD_TRAINING_PATTERN_SET_SC_DISABLED_F (0x00000000 << 5) +#define NV_DPCD_TRAINING_PATTERN_SET_SC_DISABLED_T (0x00000001 << 5) + +#define DP_MAIN_LINK_CHANNEL_CODING_SET_ASC_RESET_DISABLE 0 +#define DP_MAIN_LINK_CHANNEL_CODING_SET_ASC_RESET_ENABLE 1 + +#define NV_DPCD_TRAINING_LANE0_1_SET2 0x10f +#define NV_DPCD_TRAINING_LANE2_3_SET2 0x110 +#define NV_DPCD_LANEX_SET2_PC2_MAX_REACHED_T (1 << 2) +#define NV_DPCD_LANEX_SET2_PC2_MAX_REACHED_F (0 << 2) +#define NV_DPCD_LANEXPLUS1_SET2_PC2_MAX_REACHED_T (1 << 6) +#define NV_DPCD_LANEXPLUS1_SET2_PC2_MAX_REACHED_F (0 << 6) +#define NV_DPCD_LANEX_SET2_PC2_SHIFT 0 +#define NV_DPCD_LANEXPLUS1_SET2_PC2_SHIFT 4 + +#define NV_DPCD_STATUS_LANEX_CR_DONE_SHIFT 0 +#define NV_DPCD_STATUS_LANEX_CR_DONE_NO (0x00000000) +#define NV_DPCD_STATUS_LANEX_CR_DONE_YES (0x00000001) +#define NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_SHIFT 1 +#define NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_NO (0x00000000 << 1) +#define NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_YES (0x00000001 << 1) +#define NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_SHFIT 2 +#define NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_NO (0x00000000 << 2) +#define NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_YES (0x00000001 << 2) +#define NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_SHIFT 4 +#define NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_NO (0x00000000 << 4) +#define NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_YES (0x00000001 << 4) +#define NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_SHIFT 5 +#define NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_NO (0x00000000 << 5) +#define NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_YES (0x00000001 << 5) +#define NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_SHIFT 6 +#define NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_NO (0x00000000 << 6) +#define NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_YES (0x00000001 << 6) + +#define NV_DPCD_LANE_ALIGN_STATUS_UPDATED (0x00000204) +#define NV_DPCD_LANE_ALIGN_STATUS_UPDATED_DONE_NO (0x00000000) +#define NV_DPCD_LANE_ALIGN_STATUS_UPDATED_DONE_YES (0x00000001) + +#define NV_DPCD_STATUS_LANEX_CR_DONE_SHIFT 0 +#define NV_DPCD_STATUS_LANEX_CR_DONE_NO (0x00000000) +#define NV_DPCD_STATUS_LANEX_CR_DONE_YES (0x00000001) +#define NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_SHIFT 1 +#define NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_NO (0x00000000 << 1) +#define NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_YES (0x00000001 << 1) +#define NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_SHFIT 2 +#define NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_NO (0x00000000 << 2) +#define NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_YES (0x00000001 << 2) +#define NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_SHIFT 4 +#define NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_NO (0x00000000 << 4) +#define NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_YES (0x00000001 << 4) +#define NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_SHIFT 5 +#define NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_NO (0x00000000 << 5) +#define NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_YES (0x00000001 << 5) +#define NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_SHIFT 6 +#define NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_NO (0x00000000 << 6) +#define NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_YES (0x00000001 << 6) + +#define NV_DPCD_ADJUST_REQ_LANEX_DC_SHIFT 0 +#define NV_DPCD_ADJUST_REQ_LANEX_DC_MASK 0x3 +#define NV_DPCD_ADJUST_REQ_LANEX_PE_SHIFT 2 +#define NV_DPCD_ADJUST_REQ_LANEX_PE_MASK (0x3 << 2) +#define NV_DPCD_ADJUST_REQ_LANEXPLUS1_DC_SHIFT 4 +#define NV_DPCD_ADJUST_REQ_LANEXPLUS1_DC_MASK (0x3 << 4) +#define NV_DPCD_ADJUST_REQ_LANEXPLUS1_PE_SHIFT 6 +#define NV_DPCD_ADJUST_REQ_LANEXPLUS1_PE_MASK (0x3 << 6) +#define NV_DPCD_ADJUST_REQ_POST_CURSOR2 (0x0000020C) +#define NV_DPCD_ADJUST_REQ_POST_CURSOR2_LANE_MASK 0x3 +#define NV_DPCD_ADJUST_REQ_POST_CURSOR2_LANE_SHIFT(i) (i*2) + +#define NV_DPCD_TRAINING_AUX_RD_INTERVAL (0x0000000E) +#define NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_F (0x00000000 << 2) +#endif diff --git a/drivers/video/tegra124/dp.c b/drivers/video/tegra124/dp.c new file mode 100644 index 0000000000..3c0b721e3b --- /dev/null +++ b/drivers/video/tegra124/dp.c @@ -0,0 +1,1607 @@ +/* + * Copyright (c) 2011-2013, NVIDIA Corporation. + * Copyright 2014 Google Inc. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <displayport.h> +#include <dm.h> +#include <div64.h> +#include <errno.h> +#include <fdtdec.h> +#include <asm/io.h> +#include <asm/arch-tegra/dc.h> +#include "displayport.h" +#include "edid.h" +#include "sor.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define DO_FAST_LINK_TRAINING 1 + +struct tegra_dp_plat { + ulong base; +}; + +struct tegra_dp_priv { + struct dpaux_ctlr *regs; + struct tegra_dc_sor_data *sor; + u8 revision; + int enabled; +}; + +struct tegra_dp_priv dp_data; + +static inline u32 tegra_dpaux_readl(struct tegra_dp_priv *dp, u32 reg) +{ + return readl((u32 *)dp->regs + reg); +} + +static inline void tegra_dpaux_writel(struct tegra_dp_priv *dp, u32 reg, + u32 val) +{ + writel(val, (u32 *)dp->regs + reg); +} + +static inline u32 tegra_dc_dpaux_poll_register(struct tegra_dp_priv *dp, + u32 reg, u32 mask, u32 exp_val, + u32 poll_interval_us, + u32 timeout_us) +{ + u32 reg_val = 0; + u32 temp = timeout_us; + + do { + udelay(poll_interval_us); + reg_val = tegra_dpaux_readl(dp, reg); + if (timeout_us > poll_interval_us) + timeout_us -= poll_interval_us; + else + break; + } while ((reg_val & mask) != exp_val); + + if ((reg_val & mask) == exp_val) + return 0; /* success */ + debug("dpaux_poll_register 0x%x: timeout: (reg_val)0x%08x & (mask)0x%08x != (exp_val)0x%08x\n", + reg, reg_val, mask, exp_val); + return temp; +} + +static inline int tegra_dpaux_wait_transaction(struct tegra_dp_priv *dp) +{ + /* According to DP spec, each aux transaction needs to finish + within 40ms. */ + if (tegra_dc_dpaux_poll_register(dp, DPAUX_DP_AUXCTL, + DPAUX_DP_AUXCTL_TRANSACTREQ_MASK, + DPAUX_DP_AUXCTL_TRANSACTREQ_DONE, + 100, DP_AUX_TIMEOUT_MS * 1000) != 0) { + debug("dp: DPAUX transaction timeout\n"); + return -1; + } + return 0; +} + +static int tegra_dc_dpaux_write_chunk(struct tegra_dp_priv *dp, u32 cmd, + u32 addr, u8 *data, u32 *size, + u32 *aux_stat) +{ + int i; + u32 reg_val; + u32 timeout_retries = DP_AUX_TIMEOUT_MAX_TRIES; + u32 defer_retries = DP_AUX_DEFER_MAX_TRIES; + u32 temp_data; + + if (*size > DP_AUX_MAX_BYTES) + return -1; /* only write one chunk of data */ + + /* Make sure the command is write command */ + switch (cmd) { + case DPAUX_DP_AUXCTL_CMD_I2CWR: + case DPAUX_DP_AUXCTL_CMD_MOTWR: + case DPAUX_DP_AUXCTL_CMD_AUXWR: + break; + default: + debug("dp: aux write cmd 0x%x is invalid\n", cmd); + return -EINVAL; + } + + tegra_dpaux_writel(dp, DPAUX_DP_AUXADDR, addr); + for (i = 0; i < DP_AUX_MAX_BYTES / 4; ++i) { + memcpy(&temp_data, data, 4); + tegra_dpaux_writel(dp, DPAUX_DP_AUXDATA_WRITE_W(i), temp_data); + data += 4; + } + + reg_val = tegra_dpaux_readl(dp, DPAUX_DP_AUXCTL); + reg_val &= ~DPAUX_DP_AUXCTL_CMD_MASK; + reg_val |= cmd; + reg_val &= ~DPAUX_DP_AUXCTL_CMDLEN_FIELD; + reg_val |= ((*size - 1) << DPAUX_DP_AUXCTL_CMDLEN_SHIFT); + + while ((timeout_retries > 0) && (defer_retries > 0)) { + if ((timeout_retries != DP_AUX_TIMEOUT_MAX_TRIES) || + (defer_retries != DP_AUX_DEFER_MAX_TRIES)) + udelay(1); + + reg_val |= DPAUX_DP_AUXCTL_TRANSACTREQ_PENDING; + tegra_dpaux_writel(dp, DPAUX_DP_AUXCTL, reg_val); + + if (tegra_dpaux_wait_transaction(dp)) + debug("dp: aux write transaction timeout\n"); + + *aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT); + + if ((*aux_stat & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_PENDING) || + (*aux_stat & DPAUX_DP_AUXSTAT_RX_ERROR_PENDING) || + (*aux_stat & DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_PENDING) || + (*aux_stat & DPAUX_DP_AUXSTAT_NO_STOP_ERROR_PENDING)) { + if (timeout_retries-- > 0) { + debug("dp: aux write retry (0x%x) -- %d\n", + *aux_stat, timeout_retries); + /* clear the error bits */ + tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT, + *aux_stat); + continue; + } else { + debug("dp: aux write got error (0x%x)\n", + *aux_stat); + return -ETIMEDOUT; + } + } + + if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_I2CDEFER) || + (*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_DEFER)) { + if (defer_retries-- > 0) { + debug("dp: aux write defer (0x%x) -- %d\n", + *aux_stat, defer_retries); + /* clear the error bits */ + tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT, + *aux_stat); + continue; + } else { + debug("dp: aux write defer exceeds max retries (0x%x)\n", + *aux_stat); + return -ETIMEDOUT; + } + } + + if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_MASK) == + DPAUX_DP_AUXSTAT_REPLYTYPE_ACK) { + *size = ((*aux_stat) & DPAUX_DP_AUXSTAT_REPLY_M_MASK); + return 0; + } else { + debug("dp: aux write failed (0x%x)\n", *aux_stat); + return -EIO; + } + } + /* Should never come to here */ + return -EIO; +} + +static int tegra_dc_dpaux_read_chunk(struct tegra_dp_priv *dp, u32 cmd, + u32 addr, u8 *data, u32 *size, + u32 *aux_stat) +{ + u32 reg_val; + u32 timeout_retries = DP_AUX_TIMEOUT_MAX_TRIES; + u32 defer_retries = DP_AUX_DEFER_MAX_TRIES; + + if (*size > DP_AUX_MAX_BYTES) { + debug("only read one chunk\n"); + return -EIO; /* only read one chunk */ + } + + /* Check to make sure the command is read command */ + switch (cmd) { + case DPAUX_DP_AUXCTL_CMD_I2CRD: + case DPAUX_DP_AUXCTL_CMD_I2CREQWSTAT: + case DPAUX_DP_AUXCTL_CMD_MOTRD: + case DPAUX_DP_AUXCTL_CMD_AUXRD: + break; + default: + debug("dp: aux read cmd 0x%x is invalid\n", cmd); + return -EIO; + } + + *aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT); + if (!(*aux_stat & DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)) { + debug("dp: HPD is not detected\n"); + return -EIO; + } + + tegra_dpaux_writel(dp, DPAUX_DP_AUXADDR, addr); + + reg_val = tegra_dpaux_readl(dp, DPAUX_DP_AUXCTL); + reg_val &= ~DPAUX_DP_AUXCTL_CMD_MASK; + reg_val |= cmd; + reg_val &= ~DPAUX_DP_AUXCTL_CMDLEN_FIELD; + reg_val |= ((*size - 1) << DPAUX_DP_AUXCTL_CMDLEN_SHIFT); + while ((timeout_retries > 0) && (defer_retries > 0)) { + if ((timeout_retries != DP_AUX_TIMEOUT_MAX_TRIES) || + (defer_retries != DP_AUX_DEFER_MAX_TRIES)) + udelay(DP_DPCP_RETRY_SLEEP_NS * 2); + + reg_val |= DPAUX_DP_AUXCTL_TRANSACTREQ_PENDING; + tegra_dpaux_writel(dp, DPAUX_DP_AUXCTL, reg_val); + + if (tegra_dpaux_wait_transaction(dp)) + debug("dp: aux read transaction timeout\n"); + + *aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT); + + if ((*aux_stat & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_PENDING) || + (*aux_stat & DPAUX_DP_AUXSTAT_RX_ERROR_PENDING) || + (*aux_stat & DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_PENDING) || + (*aux_stat & DPAUX_DP_AUXSTAT_NO_STOP_ERROR_PENDING)) { + if (timeout_retries-- > 0) { + debug("dp: aux read retry (0x%x) -- %d\n", + *aux_stat, timeout_retries); + /* clear the error bits */ + tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT, + *aux_stat); + continue; /* retry */ + } else { + debug("dp: aux read got error (0x%x)\n", + *aux_stat); + return -ETIMEDOUT; + } + } + + if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_I2CDEFER) || + (*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_DEFER)) { + if (defer_retries-- > 0) { + debug("dp: aux read defer (0x%x) -- %d\n", + *aux_stat, defer_retries); + /* clear the error bits */ + tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT, + *aux_stat); + continue; + } else { + debug("dp: aux read defer exceeds max retries (0x%x)\n", + *aux_stat); + return -ETIMEDOUT; + } + } + + if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_MASK) == + DPAUX_DP_AUXSTAT_REPLYTYPE_ACK) { + int i; + u32 temp_data[4]; + + for (i = 0; i < DP_AUX_MAX_BYTES / 4; ++i) + temp_data[i] = tegra_dpaux_readl(dp, + DPAUX_DP_AUXDATA_READ_W(i)); + + *size = ((*aux_stat) & DPAUX_DP_AUXSTAT_REPLY_M_MASK); + memcpy(data, temp_data, *size); + + return 0; + } else { + debug("dp: aux read failed (0x%x\n", *aux_stat); + return -EIO; + } + } + /* Should never come to here */ + debug("%s: can't\n", __func__); + + return -EIO; +} + +static int tegra_dc_dpaux_read(struct tegra_dp_priv *dp, u32 cmd, u32 addr, + u8 *data, u32 *size, u32 *aux_stat) +{ + u32 finished = 0; + u32 cur_size; + int ret = 0; + + do { + cur_size = *size - finished; + if (cur_size > DP_AUX_MAX_BYTES) + cur_size = DP_AUX_MAX_BYTES; + + ret = tegra_dc_dpaux_read_chunk(dp, cmd, addr, + data, &cur_size, aux_stat); + if (ret) + break; + + /* cur_size should be the real size returned */ + addr += cur_size; + data += cur_size; + finished += cur_size; + + } while (*size > finished); + *size = finished; + + return ret; +} + +static int tegra_dc_dp_dpcd_read(struct tegra_dp_priv *dp, u32 cmd, + u8 *data_ptr) +{ + u32 size = 1; + u32 status = 0; + int ret; + + ret = tegra_dc_dpaux_read_chunk(dp, DPAUX_DP_AUXCTL_CMD_AUXRD, + cmd, data_ptr, &size, &status); + if (ret) { + debug("dp: Failed to read DPCD data. CMD 0x%x, Status 0x%x\n", + cmd, status); + } + + return ret; +} + +static int tegra_dc_dp_dpcd_write(struct tegra_dp_priv *dp, u32 cmd, + u8 data) +{ + u32 size = 1; + u32 status = 0; + int ret; + + ret = tegra_dc_dpaux_write_chunk(dp, DPAUX_DP_AUXCTL_CMD_AUXWR, + cmd, &data, &size, &status); + if (ret) { + debug("dp: Failed to write DPCD data. CMD 0x%x, Status 0x%x\n", + cmd, status); + } + + return ret; +} + +static int tegra_dc_i2c_aux_read(struct tegra_dp_priv *dp, u32 i2c_addr, + u8 addr, u8 *data, u32 size, u32 *aux_stat) +{ + u32 finished = 0; + int ret = 0; + + do { + u32 cur_size = min((u32)DP_AUX_MAX_BYTES, size - finished); + + u32 len = 1; + ret = tegra_dc_dpaux_write_chunk( + dp, DPAUX_DP_AUXCTL_CMD_MOTWR, i2c_addr, + &addr, &len, aux_stat); + if (ret) { + debug("%s: error sending address to read.\n", + __func__); + return ret; + } + + ret = tegra_dc_dpaux_read_chunk( + dp, DPAUX_DP_AUXCTL_CMD_I2CRD, i2c_addr, + data, &cur_size, aux_stat); + if (ret) { + debug("%s: error reading data.\n", __func__); + return ret; + } + + /* cur_size should be the real size returned */ + addr += cur_size; + data += cur_size; + finished += cur_size; + } while (size > finished); + + return finished; +} + +static void tegra_dc_dpaux_enable(struct tegra_dp_priv *dp) +{ + /* clear interrupt */ + tegra_dpaux_writel(dp, DPAUX_INTR_AUX, 0xffffffff); + /* do not enable interrupt for now. Enable them when Isr in place */ + tegra_dpaux_writel(dp, DPAUX_INTR_EN_AUX, 0x0); + + tegra_dpaux_writel(dp, DPAUX_HYBRID_PADCTL, + DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_50 | + DPAUX_HYBRID_PADCTL_AUX_CMH_V0_70 | + 0x18 << DPAUX_HYBRID_PADCTL_AUX_DRVI_SHIFT | + DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV_ENABLE); + + tegra_dpaux_writel(dp, DPAUX_HYBRID_SPARE, + DPAUX_HYBRID_SPARE_PAD_PWR_POWERUP); +} + +#ifdef DEBUG +static void tegra_dc_dp_dump_link_cfg(struct tegra_dp_priv *dp, + const struct tegra_dp_link_config *link_cfg) +{ + debug("DP config: cfg_name cfg_value\n"); + debug(" Lane Count %d\n", + link_cfg->max_lane_count); + debug(" SupportEnhancedFraming %s\n", + link_cfg->support_enhanced_framing ? "Y" : "N"); + debug(" Bandwidth %d\n", + link_cfg->max_link_bw); + debug(" bpp %d\n", + link_cfg->bits_per_pixel); + debug(" EnhancedFraming %s\n", + link_cfg->enhanced_framing ? "Y" : "N"); + debug(" Scramble_enabled %s\n", + link_cfg->scramble_ena ? "Y" : "N"); + debug(" LinkBW %d\n", + link_cfg->link_bw); + debug(" lane_count %d\n", + link_cfg->lane_count); + debug(" activespolarity %d\n", + link_cfg->activepolarity); + debug(" active_count %d\n", + link_cfg->active_count); + debug(" tu_size %d\n", + link_cfg->tu_size); + debug(" active_frac %d\n", + link_cfg->active_frac); + debug(" watermark %d\n", + link_cfg->watermark); + debug(" hblank_sym %d\n", + link_cfg->hblank_sym); + debug(" vblank_sym %d\n", + link_cfg->vblank_sym); +} +#endif + +static int _tegra_dp_lower_link_config(struct tegra_dp_priv *dp, + struct tegra_dp_link_config *cfg) +{ + switch (cfg->link_bw) { + case SOR_LINK_SPEED_G1_62: + if (cfg->max_link_bw > SOR_LINK_SPEED_G1_62) + cfg->link_bw = SOR_LINK_SPEED_G2_7; + cfg->lane_count /= 2; + break; + case SOR_LINK_SPEED_G2_7: + cfg->link_bw = SOR_LINK_SPEED_G1_62; + break; + case SOR_LINK_SPEED_G5_4: + if (cfg->lane_count == 1) { + cfg->link_bw = SOR_LINK_SPEED_G2_7; + cfg->lane_count = cfg->max_lane_count; + } else { + cfg->lane_count /= 2; + } + break; + default: + debug("dp: Error link rate %d\n", cfg->link_bw); + return -ENOLINK; + } + + return (cfg->lane_count > 0) ? 0 : -ENOLINK; +} + +/* + * Calcuate if given cfg can meet the mode request. + * Return 0 if mode is possible, -1 otherwise + */ +static int tegra_dc_dp_calc_config(struct tegra_dp_priv *dp, + const struct display_timing *timing, + struct tegra_dp_link_config *link_cfg) +{ + const u32 link_rate = 27 * link_cfg->link_bw * 1000 * 1000; + const u64 f = 100000; /* precision factor */ + u32 num_linkclk_line; /* Number of link clocks per line */ + u64 ratio_f; /* Ratio of incoming to outgoing data rate */ + u64 frac_f; + u64 activesym_f; /* Activesym per TU */ + u64 activecount_f; + u32 activecount; + u32 activepolarity; + u64 approx_value_f; + u32 activefrac = 0; + u64 accumulated_error_f = 0; + u32 lowest_neg_activecount = 0; + u32 lowest_neg_activepolarity = 0; + u32 lowest_neg_tusize = 64; + u32 num_symbols_per_line; + u64 lowest_neg_activefrac = 0; + u64 lowest_neg_error_f = 64 * f; + u64 watermark_f; + int i; + int neg; + + if (!link_rate || !link_cfg->lane_count || !timing->pixelclock.typ || + !link_cfg->bits_per_pixel) + return -1; + + if ((u64)timing->pixelclock.typ * link_cfg->bits_per_pixel >= + (u64)link_rate * 8 * link_cfg->lane_count) + return -1; + + num_linkclk_line = (u32)(lldiv(link_rate * timing->hactive.typ, + timing->pixelclock.typ)); + + ratio_f = (u64)timing->pixelclock.typ * link_cfg->bits_per_pixel * f; + ratio_f /= 8; + do_div(ratio_f, link_rate * link_cfg->lane_count); + + for (i = 64; i >= 32; --i) { + activesym_f = ratio_f * i; + activecount_f = lldiv(activesym_f, (u32)f) * f; + frac_f = activesym_f - activecount_f; + activecount = (u32)(lldiv(activecount_f, (u32)f)); + + if (frac_f < (lldiv(f, 2))) /* fraction < 0.5 */ + activepolarity = 0; + else { + activepolarity = 1; + frac_f = f - frac_f; + } + + if (frac_f != 0) { + /* warning: frac_f should be 64-bit */ + frac_f = lldiv(f * f, frac_f); /* 1 / fraction */ + if (frac_f > (15 * f)) + activefrac = activepolarity ? 1 : 15; + else + activefrac = activepolarity ? + (u32)lldiv(frac_f, (u32)f) + 1 : + (u32)lldiv(frac_f, (u32)f); + } + + if (activefrac == 1) + activepolarity = 0; + + if (activepolarity == 1) + approx_value_f = activefrac ? lldiv( + (activecount_f + (activefrac * f - f) * f), + (activefrac * f)) : + activecount_f + f; + else + approx_value_f = activefrac ? + activecount_f + lldiv(f, activefrac) : + activecount_f; + + if (activesym_f < approx_value_f) { + accumulated_error_f = num_linkclk_line * + lldiv(approx_value_f - activesym_f, i); + neg = 1; + } else { + accumulated_error_f = num_linkclk_line * + lldiv(activesym_f - approx_value_f, i); + neg = 0; + } + + if ((neg && (lowest_neg_error_f > accumulated_error_f)) || + (accumulated_error_f == 0)) { + lowest_neg_error_f = accumulated_error_f; + lowest_neg_tusize = i; + lowest_neg_activecount = activecount; + lowest_neg_activepolarity = activepolarity; + lowest_neg_activefrac = activefrac; + + if (accumulated_error_f == 0) + break; + } + } + + if (lowest_neg_activefrac == 0) { + link_cfg->activepolarity = 0; + link_cfg->active_count = lowest_neg_activepolarity ? + lowest_neg_activecount : lowest_neg_activecount - 1; + link_cfg->tu_size = lowest_neg_tusize; + link_cfg->active_frac = 1; + } else { + link_cfg->activepolarity = lowest_neg_activepolarity; + link_cfg->active_count = (u32)lowest_neg_activecount; + link_cfg->tu_size = lowest_neg_tusize; + link_cfg->active_frac = (u32)lowest_neg_activefrac; + } + + watermark_f = lldiv(ratio_f * link_cfg->tu_size * (f - ratio_f), f); + link_cfg->watermark = (u32)(lldiv(watermark_f + lowest_neg_error_f, + f)) + link_cfg->bits_per_pixel / 4 - 1; + num_symbols_per_line = (timing->hactive.typ * + link_cfg->bits_per_pixel) / + (8 * link_cfg->lane_count); + + if (link_cfg->watermark > 30) { + debug("dp: sor setting: unable to get a good tusize, force watermark to 30\n"); + link_cfg->watermark = 30; + return -1; + } else if (link_cfg->watermark > num_symbols_per_line) { + debug("dp: sor setting: force watermark to the number of symbols in the line\n"); + link_cfg->watermark = num_symbols_per_line; + return -1; + } + + /* + * Refer to dev_disp.ref for more information. + * # symbols/hblank = ((SetRasterBlankEnd.X + SetRasterSize.Width - + * SetRasterBlankStart.X - 7) * link_clk / pclk) + * - 3 * enhanced_framing - Y + * where Y = (# lanes == 4) 3 : (# lanes == 2) ? 6 : 12 + */ + link_cfg->hblank_sym = (int)lldiv(((uint64_t)timing->hback_porch.typ + + timing->hfront_porch.typ + timing->hsync_len.typ - 7) * + link_rate, timing->pixelclock.typ) - + 3 * link_cfg->enhanced_framing - + (12 / link_cfg->lane_count); + + if (link_cfg->hblank_sym < 0) + link_cfg->hblank_sym = 0; + + + /* + * Refer to dev_disp.ref for more information. + * # symbols/vblank = ((SetRasterBlankStart.X - + * SetRasterBlankEen.X - 25) * link_clk / pclk) + * - Y - 1; + * where Y = (# lanes == 4) 12 : (# lanes == 2) ? 21 : 39 + */ + link_cfg->vblank_sym = (int)lldiv(((uint64_t)timing->hactive.typ - 25) + * link_rate, timing->pixelclock.typ) - (36 / + link_cfg->lane_count) - 4; + + if (link_cfg->vblank_sym < 0) + link_cfg->vblank_sym = 0; + + link_cfg->is_valid = 1; +#ifdef DEBUG + tegra_dc_dp_dump_link_cfg(dp, link_cfg); +#endif + + return 0; +} + +static int tegra_dc_dp_init_max_link_cfg( + const struct display_timing *timing, + struct tegra_dp_priv *dp, + struct tegra_dp_link_config *link_cfg) +{ + const int drive_current = 0x40404040; + const int preemphasis = 0x0f0f0f0f; + const int postcursor = 0; + u8 dpcd_data; + int ret; + + ret = tegra_dc_dp_dpcd_read(dp, DP_MAX_LANE_COUNT, &dpcd_data); + if (ret) + return ret; + link_cfg->max_lane_count = dpcd_data & DP_MAX_LANE_COUNT_MASK; + link_cfg->tps3_supported = (dpcd_data & + DP_MAX_LANE_COUNT_TPS3_SUPPORTED_YES) ? 1 : 0; + + link_cfg->support_enhanced_framing = + (dpcd_data & DP_MAX_LANE_COUNT_ENHANCED_FRAMING_YES) ? + 1 : 0; + + ret = tegra_dc_dp_dpcd_read(dp, DP_MAX_DOWNSPREAD, &dpcd_data); + if (ret) + return ret; + link_cfg->downspread = (dpcd_data & DP_MAX_DOWNSPREAD_VAL_0_5_PCT) ? + 1 : 0; + + ret = tegra_dc_dp_dpcd_read(dp, NV_DPCD_TRAINING_AUX_RD_INTERVAL, + &link_cfg->aux_rd_interval); + if (ret) + return ret; + ret = tegra_dc_dp_dpcd_read(dp, DP_MAX_LINK_RATE, + &link_cfg->max_link_bw); + if (ret) + return ret; + + /* + * Set to a high value for link training and attach. + * Will be re-programmed when dp is enabled. + */ + link_cfg->drive_current = drive_current; + link_cfg->preemphasis = preemphasis; + link_cfg->postcursor = postcursor; + + ret = tegra_dc_dp_dpcd_read(dp, DP_EDP_CONFIGURATION_CAP, &dpcd_data); + if (ret) + return ret; + + link_cfg->alt_scramber_reset_cap = + (dpcd_data & DP_EDP_CONFIGURATION_CAP_ASC_RESET_YES) ? + 1 : 0; + link_cfg->only_enhanced_framing = + (dpcd_data & DP_EDP_CONFIGURATION_CAP_FRAMING_CHANGE_YES) ? + 1 : 0; + + link_cfg->lane_count = link_cfg->max_lane_count; + link_cfg->link_bw = link_cfg->max_link_bw; + link_cfg->enhanced_framing = link_cfg->support_enhanced_framing; + link_cfg->frame_in_ms = (1000 / 60) + 1; + + tegra_dc_dp_calc_config(dp, timing, link_cfg); + return 0; +} + +static int tegra_dc_dp_set_assr(struct tegra_dp_priv *dp, + struct tegra_dc_sor_data *sor, int ena) +{ + int ret; + + u8 dpcd_data = ena ? + DP_MAIN_LINK_CHANNEL_CODING_SET_ASC_RESET_ENABLE : + DP_MAIN_LINK_CHANNEL_CODING_SET_ASC_RESET_DISABLE; + + ret = tegra_dc_dp_dpcd_write(dp, DP_EDP_CONFIGURATION_SET, + dpcd_data); + if (ret) + return ret; + + /* Also reset the scrambler to 0xfffe */ + tegra_dc_sor_set_internal_panel(sor, ena); + return 0; +} + +static int tegra_dp_set_link_bandwidth(struct tegra_dp_priv *dp, + struct tegra_dc_sor_data *sor, + u8 link_bw) +{ + tegra_dc_sor_set_link_bandwidth(sor, link_bw); + + /* Sink side */ + return tegra_dc_dp_dpcd_write(dp, DP_LINK_BW_SET, link_bw); +} + +static int tegra_dp_set_lane_count(struct tegra_dp_priv *dp, + const struct tegra_dp_link_config *link_cfg, + struct tegra_dc_sor_data *sor) +{ + u8 dpcd_data; + int ret; + + /* check if panel support enhanched_framing */ + dpcd_data = link_cfg->lane_count; + if (link_cfg->enhanced_framing) + dpcd_data |= DP_LANE_COUNT_SET_ENHANCEDFRAMING_T; + ret = tegra_dc_dp_dpcd_write(dp, DP_LANE_COUNT_SET, dpcd_data); + if (ret) + return ret; + + tegra_dc_sor_set_lane_count(sor, link_cfg->lane_count); + + /* Also power down lanes that will not be used */ + return 0; +} + +static int tegra_dc_dp_link_trained(struct tegra_dp_priv *dp, + const struct tegra_dp_link_config *cfg) +{ + u32 lane; + u8 mask; + u8 data; + int ret; + + for (lane = 0; lane < cfg->lane_count; ++lane) { + ret = tegra_dc_dp_dpcd_read(dp, (lane / 2) ? + DP_LANE2_3_STATUS : DP_LANE0_1_STATUS, + &data); + if (ret) + return ret; + mask = (lane & 1) ? + NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_YES | + NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_YES | + NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_YES : + DP_LANE_CR_DONE | + DP_LANE_CHANNEL_EQ_DONE | + DP_LANE_SYMBOL_LOCKED; + if ((data & mask) != mask) + return -1; + } + return 0; +} + +static int tegra_dp_channel_eq_status(struct tegra_dp_priv *dp, + const struct tegra_dp_link_config *cfg) +{ + u32 cnt; + u32 n_lanes = cfg->lane_count; + u8 data; + u8 ce_done = 1; + int ret; + + for (cnt = 0; cnt < n_lanes / 2; cnt++) { + ret = tegra_dc_dp_dpcd_read(dp, DP_LANE0_1_STATUS + cnt, &data); + if (ret) + return ret; + + if (n_lanes == 1) { + ce_done = (data & (0x1 << + NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_SHIFT)) && + (data & (0x1 << + NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_SHFIT)); + break; + } else if (!(data & (0x1 << + NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_SHIFT)) || + !(data & (0x1 << + NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_SHFIT)) || + !(data & (0x1 << + NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_SHIFT)) || + !(data & (0x1 << + NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_SHIFT))) + return -EIO; + } + + if (ce_done) { + ret = tegra_dc_dp_dpcd_read(dp, + DP_LANE_ALIGN_STATUS_UPDATED, + &data); + if (ret) + return ret; + if (!(data & NV_DPCD_LANE_ALIGN_STATUS_UPDATED_DONE_YES)) + ce_done = 0; + } + + return ce_done ? 0 : -EIO; +} + +static int tegra_dp_clock_recovery_status(struct tegra_dp_priv *dp, + const struct tegra_dp_link_config *cfg) +{ + u32 cnt; + u32 n_lanes = cfg->lane_count; + u8 data_ptr; + int ret; + + for (cnt = 0; cnt < n_lanes / 2; cnt++) { + ret = tegra_dc_dp_dpcd_read(dp, (DP_LANE0_1_STATUS + cnt), + &data_ptr); + if (ret) + return ret; + + if (n_lanes == 1) + return (data_ptr & NV_DPCD_STATUS_LANEX_CR_DONE_YES) ? + 1 : 0; + else if (!(data_ptr & NV_DPCD_STATUS_LANEX_CR_DONE_YES) || + !(data_ptr & (NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_YES))) + return 0; + } + + return 1; +} + +static int tegra_dp_lt_adjust(struct tegra_dp_priv *dp, u32 pe[4], u32 vs[4], + u32 pc[4], u8 pc_supported, + const struct tegra_dp_link_config *cfg) +{ + size_t cnt; + u8 data_ptr; + u32 n_lanes = cfg->lane_count; + int ret; + + for (cnt = 0; cnt < n_lanes / 2; cnt++) { + ret = tegra_dc_dp_dpcd_read(dp, DP_ADJUST_REQUEST_LANE0_1 + cnt, + &data_ptr); + if (ret) + return ret; + pe[2 * cnt] = (data_ptr & NV_DPCD_ADJUST_REQ_LANEX_PE_MASK) >> + NV_DPCD_ADJUST_REQ_LANEX_PE_SHIFT; + vs[2 * cnt] = (data_ptr & NV_DPCD_ADJUST_REQ_LANEX_DC_MASK) >> + NV_DPCD_ADJUST_REQ_LANEX_DC_SHIFT; + pe[1 + 2 * cnt] = + (data_ptr & NV_DPCD_ADJUST_REQ_LANEXPLUS1_PE_MASK) >> + NV_DPCD_ADJUST_REQ_LANEXPLUS1_PE_SHIFT; + vs[1 + 2 * cnt] = + (data_ptr & NV_DPCD_ADJUST_REQ_LANEXPLUS1_DC_MASK) >> + NV_DPCD_ADJUST_REQ_LANEXPLUS1_DC_SHIFT; + } + if (pc_supported) { + ret = tegra_dc_dp_dpcd_read(dp, NV_DPCD_ADJUST_REQ_POST_CURSOR2, + &data_ptr); + if (ret) + return ret; + for (cnt = 0; cnt < n_lanes; cnt++) { + pc[cnt] = (data_ptr >> + NV_DPCD_ADJUST_REQ_POST_CURSOR2_LANE_SHIFT(cnt)) & + NV_DPCD_ADJUST_REQ_POST_CURSOR2_LANE_MASK; + } + } + + return 0; +} + +static void tegra_dp_wait_aux_training(struct tegra_dp_priv *dp, + bool is_clk_recovery, + const struct tegra_dp_link_config *cfg) +{ + if (!cfg->aux_rd_interval) + udelay(is_clk_recovery ? 200 : 500); + else + mdelay(cfg->aux_rd_interval * 4); +} + +static void tegra_dp_tpg(struct tegra_dp_priv *dp, u32 tp, u32 n_lanes, + const struct tegra_dp_link_config *cfg) +{ + u8 data = (tp == training_pattern_disabled) + ? (tp | NV_DPCD_TRAINING_PATTERN_SET_SC_DISABLED_F) + : (tp | NV_DPCD_TRAINING_PATTERN_SET_SC_DISABLED_T); + + tegra_dc_sor_set_dp_linkctl(dp->sor, 1, tp, cfg); + tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET, data); +} + +static int tegra_dp_link_config(struct tegra_dp_priv *dp, + const struct tegra_dp_link_config *link_cfg) +{ + u8 dpcd_data; + u32 retry; + int ret; + + if (link_cfg->lane_count == 0) { + debug("dp: error: lane count is 0. Can not set link config.\n"); + return -ENOLINK; + } + + /* Set power state if it is not in normal level */ + ret = tegra_dc_dp_dpcd_read(dp, DP_SET_POWER, &dpcd_data); + if (ret) + return ret; + + if (dpcd_data == DP_SET_POWER_D3) { + dpcd_data = DP_SET_POWER_D0; + + /* DP spec requires 3 retries */ + for (retry = 3; retry > 0; --retry) { + ret = tegra_dc_dp_dpcd_write(dp, DP_SET_POWER, + dpcd_data); + if (!ret) + break; + if (retry == 1) { + debug("dp: Failed to set DP panel power\n"); + return ret; + } + } + } + + /* Enable ASSR if possible */ + if (link_cfg->alt_scramber_reset_cap) { + ret = tegra_dc_dp_set_assr(dp, dp->sor, 1); + if (ret) + return ret; + } + + ret = tegra_dp_set_link_bandwidth(dp, dp->sor, link_cfg->link_bw); + if (ret) { + debug("dp: Failed to set link bandwidth\n"); + return ret; + } + ret = tegra_dp_set_lane_count(dp, link_cfg, dp->sor); + if (ret) { + debug("dp: Failed to set lane count\n"); + return ret; + } + tegra_dc_sor_set_dp_linkctl(dp->sor, 1, training_pattern_none, + link_cfg); + + return 0; +} + +static int tegra_dp_lower_link_config(struct tegra_dp_priv *dp, + const struct display_timing *timing, + struct tegra_dp_link_config *cfg) +{ + struct tegra_dp_link_config tmp_cfg; + int ret; + + tmp_cfg = *cfg; + cfg->is_valid = 0; + + ret = _tegra_dp_lower_link_config(dp, cfg); + if (!ret) + ret = tegra_dc_dp_calc_config(dp, timing, cfg); + if (!ret) + ret = tegra_dp_link_config(dp, cfg); + if (ret) + goto fail; + + return 0; + +fail: + *cfg = tmp_cfg; + tegra_dp_link_config(dp, &tmp_cfg); + return ret; +} + +static int tegra_dp_lt_config(struct tegra_dp_priv *dp, u32 pe[4], u32 vs[4], + u32 pc[4], const struct tegra_dp_link_config *cfg) +{ + struct tegra_dc_sor_data *sor = dp->sor; + u32 n_lanes = cfg->lane_count; + u8 pc_supported = cfg->tps3_supported; + u32 cnt; + u32 val; + + for (cnt = 0; cnt < n_lanes; cnt++) { + u32 mask = 0; + u32 pe_reg, vs_reg, pc_reg; + u32 shift = 0; + + switch (cnt) { + case 0: + mask = PR_LANE2_DP_LANE0_MASK; + shift = PR_LANE2_DP_LANE0_SHIFT; + break; + case 1: + mask = PR_LANE1_DP_LANE1_MASK; + shift = PR_LANE1_DP_LANE1_SHIFT; + break; + case 2: + mask = PR_LANE0_DP_LANE2_MASK; + shift = PR_LANE0_DP_LANE2_SHIFT; + break; + case 3: + mask = PR_LANE3_DP_LANE3_MASK; + shift = PR_LANE3_DP_LANE3_SHIFT; + break; + default: + debug("dp: incorrect lane cnt\n"); + return -EINVAL; + } + + pe_reg = tegra_dp_pe_regs[pc[cnt]][vs[cnt]][pe[cnt]]; + vs_reg = tegra_dp_vs_regs[pc[cnt]][vs[cnt]][pe[cnt]]; + pc_reg = tegra_dp_pc_regs[pc[cnt]][vs[cnt]][pe[cnt]]; + + tegra_dp_set_pe_vs_pc(sor, mask, pe_reg << shift, + vs_reg << shift, pc_reg << shift, + pc_supported); + } + + tegra_dp_disable_tx_pu(dp->sor); + udelay(20); + + for (cnt = 0; cnt < n_lanes; cnt++) { + u32 max_vs_flag = tegra_dp_is_max_vs(pe[cnt], vs[cnt]); + u32 max_pe_flag = tegra_dp_is_max_pe(pe[cnt], vs[cnt]); + + val = (vs[cnt] << NV_DPCD_TRAINING_LANEX_SET_DC_SHIFT) | + (max_vs_flag ? + NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_T : + NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_F) | + (pe[cnt] << NV_DPCD_TRAINING_LANEX_SET_PE_SHIFT) | + (max_pe_flag ? + NV_DPCD_TRAINING_LANEX_SET_PE_MAX_REACHED_T : + NV_DPCD_TRAINING_LANEX_SET_PE_MAX_REACHED_F); + tegra_dc_dp_dpcd_write(dp, (DP_TRAINING_LANE0_SET + cnt), val); + } + + if (pc_supported) { + for (cnt = 0; cnt < n_lanes / 2; cnt++) { + u32 max_pc_flag0 = tegra_dp_is_max_pc(pc[cnt]); + u32 max_pc_flag1 = tegra_dp_is_max_pc(pc[cnt + 1]); + val = (pc[cnt] << NV_DPCD_LANEX_SET2_PC2_SHIFT) | + (max_pc_flag0 ? + NV_DPCD_LANEX_SET2_PC2_MAX_REACHED_T : + NV_DPCD_LANEX_SET2_PC2_MAX_REACHED_F) | + (pc[cnt + 1] << + NV_DPCD_LANEXPLUS1_SET2_PC2_SHIFT) | + (max_pc_flag1 ? + NV_DPCD_LANEXPLUS1_SET2_PC2_MAX_REACHED_T : + NV_DPCD_LANEXPLUS1_SET2_PC2_MAX_REACHED_F); + tegra_dc_dp_dpcd_write(dp, + NV_DPCD_TRAINING_LANE0_1_SET2 + + cnt, val); + } + } + + return 0; +} + +static int _tegra_dp_channel_eq(struct tegra_dp_priv *dp, u32 pe[4], + u32 vs[4], u32 pc[4], u8 pc_supported, + u32 n_lanes, + const struct tegra_dp_link_config *cfg) +{ + u32 retry_cnt; + + for (retry_cnt = 0; retry_cnt < 4; retry_cnt++) { + int ret; + + if (retry_cnt) { + ret = tegra_dp_lt_adjust(dp, pe, vs, pc, pc_supported, + cfg); + if (ret) + return ret; + tegra_dp_lt_config(dp, pe, vs, pc, cfg); + } + + tegra_dp_wait_aux_training(dp, false, cfg); + + if (!tegra_dp_clock_recovery_status(dp, cfg)) { + debug("dp: CR failed in channel EQ sequence!\n"); + break; + } + + if (!tegra_dp_channel_eq_status(dp, cfg)) + return 0; + } + + return -EIO; +} + +static int tegra_dp_channel_eq(struct tegra_dp_priv *dp, u32 pe[4], u32 vs[4], + u32 pc[4], + const struct tegra_dp_link_config *cfg) +{ + u32 n_lanes = cfg->lane_count; + u8 pc_supported = cfg->tps3_supported; + int ret; + u32 tp_src = training_pattern_2; + + if (pc_supported) + tp_src = training_pattern_3; + + tegra_dp_tpg(dp, tp_src, n_lanes, cfg); + + ret = _tegra_dp_channel_eq(dp, pe, vs, pc, pc_supported, n_lanes, cfg); + + tegra_dp_tpg(dp, training_pattern_disabled, n_lanes, cfg); + + return ret; +} + +static int _tegra_dp_clk_recovery(struct tegra_dp_priv *dp, u32 pe[4], + u32 vs[4], u32 pc[4], u8 pc_supported, + u32 n_lanes, + const struct tegra_dp_link_config *cfg) +{ + u32 vs_temp[4]; + u32 retry_cnt = 0; + + do { + tegra_dp_lt_config(dp, pe, vs, pc, cfg); + tegra_dp_wait_aux_training(dp, true, cfg); + + if (tegra_dp_clock_recovery_status(dp, cfg)) + return 0; + + memcpy(vs_temp, vs, sizeof(vs_temp)); + tegra_dp_lt_adjust(dp, pe, vs, pc, pc_supported, cfg); + + if (memcmp(vs_temp, vs, sizeof(vs_temp))) + retry_cnt = 0; + else + ++retry_cnt; + } while (retry_cnt < 5); + + return -EIO; +} + +static int tegra_dp_clk_recovery(struct tegra_dp_priv *dp, u32 pe[4], + u32 vs[4], u32 pc[4], + const struct tegra_dp_link_config *cfg) +{ + u32 n_lanes = cfg->lane_count; + u8 pc_supported = cfg->tps3_supported; + int err; + + tegra_dp_tpg(dp, training_pattern_1, n_lanes, cfg); + + err = _tegra_dp_clk_recovery(dp, pe, vs, pc, pc_supported, n_lanes, + cfg); + if (err < 0) + tegra_dp_tpg(dp, training_pattern_disabled, n_lanes, cfg); + + return err; +} + +static int tegra_dc_dp_full_link_training(struct tegra_dp_priv *dp, + const struct display_timing *timing, + struct tegra_dp_link_config *cfg) +{ + struct tegra_dc_sor_data *sor = dp->sor; + int err; + u32 pe[4], vs[4], pc[4]; + + tegra_sor_precharge_lanes(sor, cfg); + +retry_cr: + memset(pe, PREEMPHASIS_DISABLED, sizeof(pe)); + memset(vs, DRIVECURRENT_LEVEL0, sizeof(vs)); + memset(pc, POSTCURSOR2_LEVEL0, sizeof(pc)); + + err = tegra_dp_clk_recovery(dp, pe, vs, pc, cfg); + if (err) { + if (!tegra_dp_lower_link_config(dp, timing, cfg)) + goto retry_cr; + + debug("dp: clk recovery failed\n"); + goto fail; + } + + err = tegra_dp_channel_eq(dp, pe, vs, pc, cfg); + if (err) { + if (!tegra_dp_lower_link_config(dp, timing, cfg)) + goto retry_cr; + + debug("dp: channel equalization failed\n"); + goto fail; + } +#ifdef DEBUG + tegra_dc_dp_dump_link_cfg(dp, cfg); +#endif + return 0; + +fail: + return err; +} + +/* + * All link training functions are ported from kernel dc driver. + * See more details at drivers/video/tegra/dc/dp.c + */ +static int tegra_dc_dp_fast_link_training(struct tegra_dp_priv *dp, + const struct tegra_dp_link_config *link_cfg, + struct tegra_dc_sor_data *sor) +{ + u8 link_bw; + u8 lane_count; + u16 data16; + u32 data32; + u32 size; + u32 status; + int j; + u32 mask = 0xffff >> ((4 - link_cfg->lane_count) * 4); + + tegra_dc_sor_set_lane_parm(sor, link_cfg); + tegra_dc_dp_dpcd_write(dp, DP_MAIN_LINK_CHANNEL_CODING_SET, + DP_SET_ANSI_8B10B); + + /* Send TP1 */ + tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_1, link_cfg); + tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET, + DP_TRAINING_PATTERN_1); + + for (j = 0; j < link_cfg->lane_count; ++j) + tegra_dc_dp_dpcd_write(dp, DP_TRAINING_LANE0_SET + j, 0x24); + udelay(520); + + size = sizeof(data16); + tegra_dc_dpaux_read(dp, DPAUX_DP_AUXCTL_CMD_AUXRD, + DP_LANE0_1_STATUS, (u8 *)&data16, &size, &status); + status = mask & 0x1111; + if ((data16 & status) != status) { + debug("dp: Link training error for TP1 (%#x, status %#x)\n", + data16, status); + return -EFAULT; + } + + /* enable ASSR */ + tegra_dc_dp_set_assr(dp, sor, link_cfg->scramble_ena); + tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_3, link_cfg); + + tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET, + link_cfg->link_bw == 20 ? 0x23 : 0x22); + for (j = 0; j < link_cfg->lane_count; ++j) + tegra_dc_dp_dpcd_write(dp, DP_TRAINING_LANE0_SET + j, 0x24); + udelay(520); + + size = sizeof(data32); + tegra_dc_dpaux_read(dp, DPAUX_DP_AUXCTL_CMD_AUXRD, DP_LANE0_1_STATUS, + (u8 *)&data32, &size, &status); + if ((data32 & mask) != (0x7777 & mask)) { + debug("dp: Link training error for TP2/3 (0x%x)\n", data32); + return -EFAULT; + } + + tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_disabled, + link_cfg); + tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET, 0); + + if (tegra_dc_dp_link_trained(dp, link_cfg)) { + tegra_dc_sor_read_link_config(sor, &link_bw, &lane_count); + debug("Fast link training failed, link bw %d, lane # %d\n", + link_bw, lane_count); + return -EFAULT; + } + + debug("Fast link training succeeded, link bw %d, lane %d\n", + link_cfg->link_bw, link_cfg->lane_count); + + return 0; +} + +static int tegra_dp_do_link_training(struct tegra_dp_priv *dp, + struct tegra_dp_link_config *link_cfg, + const struct display_timing *timing, + struct tegra_dc_sor_data *sor) +{ + u8 link_bw; + u8 lane_count; + int ret; + + if (DO_FAST_LINK_TRAINING) { + ret = tegra_dc_dp_fast_link_training(dp, link_cfg, sor); + if (ret) { + debug("dp: fast link training failed\n"); + } else { + /* + * set to a known-good drive setting if fast link + * succeeded. Ignore any error. + */ + ret = tegra_dc_sor_set_voltage_swing(dp->sor, link_cfg); + if (ret) + debug("Failed to set voltage swing\n"); + } + } else { + ret = -ENOSYS; + } + if (ret) { + /* Try full link training then */ + ret = tegra_dc_dp_full_link_training(dp, timing, link_cfg); + if (ret) { + debug("dp: full link training failed\n"); + return ret; + } + } + + /* Everything is good; double check the link config */ + tegra_dc_sor_read_link_config(sor, &link_bw, &lane_count); + + if ((link_cfg->link_bw == link_bw) && + (link_cfg->lane_count == lane_count)) + return 0; + else + return -EFAULT; +} + +static int tegra_dc_dp_explore_link_cfg(struct tegra_dp_priv *dp, + struct tegra_dp_link_config *link_cfg, + struct tegra_dc_sor_data *sor, + const struct display_timing *timing) +{ + struct tegra_dp_link_config temp_cfg; + + if (!timing->pixelclock.typ || !timing->hactive.typ || + !timing->vactive.typ) { + debug("dp: error mode configuration"); + return -EINVAL; + } + if (!link_cfg->max_link_bw || !link_cfg->max_lane_count) { + debug("dp: error link configuration"); + return -EINVAL; + } + + link_cfg->is_valid = 0; + + memcpy(&temp_cfg, link_cfg, sizeof(temp_cfg)); + + temp_cfg.link_bw = temp_cfg.max_link_bw; + temp_cfg.lane_count = temp_cfg.max_lane_count; + + /* + * set to max link config + */ + if ((!tegra_dc_dp_calc_config(dp, timing, &temp_cfg)) && + (!tegra_dp_link_config(dp, &temp_cfg)) && + (!tegra_dp_do_link_training(dp, &temp_cfg, timing, sor))) + /* the max link cfg is doable */ + memcpy(link_cfg, &temp_cfg, sizeof(temp_cfg)); + + return link_cfg->is_valid ? 0 : -EFAULT; +} + +static int tegra_dp_hpd_plug(struct tegra_dp_priv *dp) +{ + const int vdd_to_hpd_delay_ms = 200; + u32 val; + ulong start; + + start = get_timer(0); + do { + val = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT); + if (val & DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED) + return 0; + udelay(100); + } while (get_timer(start) < vdd_to_hpd_delay_ms); + + return -EIO; +} + +static int tegra_dc_dp_sink_out_of_sync(struct tegra_dp_priv *dp, u32 delay_ms) +{ + u8 dpcd_data; + int out_of_sync; + int ret; + + debug("%s: delay=%d\n", __func__, delay_ms); + mdelay(delay_ms); + ret = tegra_dc_dp_dpcd_read(dp, DP_SINK_STATUS, &dpcd_data); + if (ret) + return ret; + + out_of_sync = !(dpcd_data & DP_SINK_STATUS_PORT0_IN_SYNC); + if (out_of_sync) + debug("SINK receive port 0 out of sync, data=%x\n", dpcd_data); + else + debug("SINK is in synchronization\n"); + + return out_of_sync; +} + +static int tegra_dc_dp_check_sink(struct tegra_dp_priv *dp, + struct tegra_dp_link_config *link_cfg, + const struct display_timing *timing) +{ + const int max_retry = 5; + int delay_frame; + int retries; + + /* + * DP TCON may skip some main stream frames, thus we need to wait + * some delay before reading the DPCD SINK STATUS register, starting + * from 5 + */ + delay_frame = 5; + + retries = max_retry; + do { + int ret; + + if (!tegra_dc_dp_sink_out_of_sync(dp, link_cfg->frame_in_ms * + delay_frame)) + return 0; + + debug("%s: retries left %d\n", __func__, retries); + if (!retries--) { + printf("DP: Out of sync after %d retries\n", max_retry); + return -EIO; + } + ret = tegra_dc_sor_detach(dp->sor); + if (ret) + return ret; + if (tegra_dc_dp_explore_link_cfg(dp, link_cfg, dp->sor, + timing)) { + debug("dp: %s: error to configure link\n", __func__); + continue; + } + + tegra_dc_sor_set_power_state(dp->sor, 1); + tegra_dc_sor_attach(dp->sor, link_cfg, timing); + + /* Increase delay_frame for next try in case the sink is + skipping more frames */ + delay_frame += 10; + } while (1); +} + +int tegra_dp_enable(struct udevice *dev, int panel_bpp, + const struct display_timing *timing) +{ + struct tegra_dp_priv *priv = dev_get_priv(dev); + struct tegra_dp_link_config slink_cfg, *link_cfg = &slink_cfg; + struct tegra_dc_sor_data *sor; + int data; + int retry; + int ret; + + memset(link_cfg, '\0', sizeof(*link_cfg)); + link_cfg->is_valid = 0; + link_cfg->scramble_ena = 1; + + tegra_dc_dpaux_enable(priv); + + if (tegra_dp_hpd_plug(priv) < 0) { + debug("dp: hpd plug failed\n"); + return -EIO; + } + + link_cfg->bits_per_pixel = panel_bpp; + if (tegra_dc_dp_init_max_link_cfg(timing, priv, link_cfg)) { + debug("dp: failed to init link configuration\n"); + return -ENOLINK; + } + + ret = tegra_dc_sor_init(&sor); + if (ret) + return ret; + priv->sor = sor; + ret = tegra_dc_sor_enable_dp(sor, link_cfg); + if (ret) + return ret; + + tegra_dc_sor_set_panel_power(sor, 1); + + /* Write power on to DPCD */ + data = DP_SET_POWER_D0; + retry = 0; + do { + ret = tegra_dc_dp_dpcd_write(priv, DP_SET_POWER, data); + } while ((retry++ < DP_POWER_ON_MAX_TRIES) && ret); + + if (ret || retry >= DP_POWER_ON_MAX_TRIES) { + debug("dp: failed to power on panel (0x%x)\n", ret); + return -ENETUNREACH; + goto error_enable; + } + + /* Confirm DP plugging status */ + if (!(tegra_dpaux_readl(priv, DPAUX_DP_AUXSTAT) & + DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)) { + debug("dp: could not detect HPD\n"); + return -ENXIO; + } + + /* Check DP version */ + if (tegra_dc_dp_dpcd_read(priv, DP_DPCD_REV, &priv->revision)) { + debug("dp: failed to read the revision number from sink\n"); + return -EIO; + } + + if (tegra_dc_dp_explore_link_cfg(priv, link_cfg, sor, timing)) { + debug("dp: error configuring link\n"); + return -ENOMEDIUM; + } + + tegra_dc_sor_set_power_state(sor, 1); + ret = tegra_dc_sor_attach(sor, link_cfg, timing); + if (ret && ret != -EEXIST) + return ret; + + /* + * This takes a long time, but can apparently resolve a failure to + * bring up the display correctly. + */ + if (0) { + ret = tegra_dc_dp_check_sink(priv, link_cfg, timing); + if (ret) + return ret; + } + + /* Power down the unused lanes to save power - a few hundred mW */ + tegra_dc_sor_power_down_unused_lanes(sor, link_cfg); + + priv->enabled = true; +error_enable: + return 0; +} + +static int tegra_dp_ofdata_to_platdata(struct udevice *dev) +{ + struct tegra_dp_plat *plat = dev_get_platdata(dev); + const void *blob = gd->fdt_blob; + + plat->base = fdtdec_get_addr(blob, dev->of_offset, "reg"); + + return 0; +} + +static int tegra_dp_read_edid(struct udevice *dev, u8 *buf, int buf_size) +{ + struct tegra_dp_priv *priv = dev_get_priv(dev); + const int tegra_edid_i2c_address = 0x50; + u32 aux_stat = 0; + + tegra_dc_dpaux_enable(priv); + + return tegra_dc_i2c_aux_read(priv, tegra_edid_i2c_address, 0, buf, + buf_size, &aux_stat); +} + +static const struct dm_display_port_ops dp_tegra_ops = { + .read_edid = tegra_dp_read_edid, + .enable = tegra_dp_enable, +}; + +static int dp_tegra_probe(struct udevice *dev) +{ + struct tegra_dp_plat *plat = dev_get_platdata(dev); + struct tegra_dp_priv *priv = dev_get_priv(dev); + + priv->regs = (struct dpaux_ctlr *)plat->base; + priv->enabled = false; + + return 0; +} + +static const struct udevice_id tegra_dp_ids[] = { + { .compatible = "nvidia,tegra124-dpaux" }, + { } +}; + +U_BOOT_DRIVER(dp_tegra) = { + .name = "dpaux_tegra", + .id = UCLASS_DISPLAY_PORT, + .of_match = tegra_dp_ids, + .ofdata_to_platdata = tegra_dp_ofdata_to_platdata, + .probe = dp_tegra_probe, + .ops = &dp_tegra_ops, + .priv_auto_alloc_size = sizeof(struct tegra_dp_priv), + .platdata_auto_alloc_size = sizeof(struct tegra_dp_plat), +}; diff --git a/drivers/video/tegra124/sor.c b/drivers/video/tegra124/sor.c new file mode 100644 index 0000000000..aa3d80c4c0 --- /dev/null +++ b/drivers/video/tegra124/sor.c @@ -0,0 +1,1024 @@ +/* + * Copyright (c) 2011-2013, NVIDIA Corporation. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <errno.h> +#include <fdtdec.h> +#include <malloc.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch-tegra/dc.h> +#include "displayport.h" +#include "sor.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define DEBUG_SOR 0 + +#define APBDEV_PMC_DPD_SAMPLE 0x20 +#define APBDEV_PMC_DPD_SAMPLE_ON_DISABLE 0 +#define APBDEV_PMC_DPD_SAMPLE_ON_ENABLE 1 +#define APBDEV_PMC_SEL_DPD_TIM 0x1c8 +#define APBDEV_PMC_SEL_DPD_TIM_SEL_DPD_TIM_DEFAULT 0x7f +#define APBDEV_PMC_IO_DPD2_REQ 0x1c0 +#define APBDEV_PMC_IO_DPD2_REQ_LVDS_SHIFT 25 +#define APBDEV_PMC_IO_DPD2_REQ_LVDS_OFF (0 << 25) +#define APBDEV_PMC_IO_DPD2_REQ_LVDS_ON (1 << 25) +#define APBDEV_PMC_IO_DPD2_REQ_CODE_SHIFT 30 +#define APBDEV_PMC_IO_DPD2_REQ_CODE_DEFAULT_MASK (0x3 << 30) +#define APBDEV_PMC_IO_DPD2_REQ_CODE_IDLE (0 << 30) +#define APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_OFF (1 << 30) +#define APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_ON (2 << 30) +#define APBDEV_PMC_IO_DPD2_STATUS 0x1c4 +#define APBDEV_PMC_IO_DPD2_STATUS_LVDS_SHIFT 25 +#define APBDEV_PMC_IO_DPD2_STATUS_LVDS_OFF (0 << 25) +#define APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON (1 << 25) + +static inline u32 tegra_sor_readl(struct tegra_dc_sor_data *sor, u32 reg) +{ + return readl((u32 *)sor->base + reg); +} + +static inline void tegra_sor_writel(struct tegra_dc_sor_data *sor, u32 reg, + u32 val) +{ + writel(val, (u32 *)sor->base + reg); +} + +static inline void tegra_sor_write_field(struct tegra_dc_sor_data *sor, + u32 reg, u32 mask, u32 val) +{ + u32 reg_val = tegra_sor_readl(sor, reg); + reg_val &= ~mask; + reg_val |= val; + tegra_sor_writel(sor, reg, reg_val); +} + +void tegra_dp_disable_tx_pu(struct tegra_dc_sor_data *sor) +{ + tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), + DP_PADCTL_TX_PU_MASK, DP_PADCTL_TX_PU_DISABLE); +} + +void tegra_dp_set_pe_vs_pc(struct tegra_dc_sor_data *sor, u32 mask, u32 pe_reg, + u32 vs_reg, u32 pc_reg, u8 pc_supported) +{ + tegra_sor_write_field(sor, PR(sor->portnum), mask, pe_reg); + tegra_sor_write_field(sor, DC(sor->portnum), mask, vs_reg); + if (pc_supported) { + tegra_sor_write_field(sor, POSTCURSOR(sor->portnum), mask, + pc_reg); + } +} + +static int tegra_dc_sor_poll_register(struct tegra_dc_sor_data *sor, u32 reg, + u32 mask, u32 exp_val, + int poll_interval_us, int timeout_ms) +{ + u32 reg_val = 0; + ulong start; + + start = get_timer(0); + do { + reg_val = tegra_sor_readl(sor, reg); + if (((reg_val & mask) == exp_val)) + return 0; + udelay(poll_interval_us); + } while (get_timer(start) < timeout_ms); + + debug("sor_poll_register 0x%x: timeout, (reg_val)0x%08x & (mask)0x%08x != (exp_val)0x%08x\n", + reg, reg_val, mask, exp_val); + + return -ETIMEDOUT; +} + +int tegra_dc_sor_set_power_state(struct tegra_dc_sor_data *sor, int pu_pd) +{ + u32 reg_val; + u32 orig_val; + + orig_val = tegra_sor_readl(sor, PWR); + + reg_val = pu_pd ? PWR_NORMAL_STATE_PU : + PWR_NORMAL_STATE_PD; /* normal state only */ + + if (reg_val == orig_val) + return 0; /* No update needed */ + + reg_val |= PWR_SETTING_NEW_TRIGGER; + tegra_sor_writel(sor, PWR, reg_val); + + /* Poll to confirm it is done */ + if (tegra_dc_sor_poll_register(sor, PWR, + PWR_SETTING_NEW_DEFAULT_MASK, + PWR_SETTING_NEW_DONE, + 100, TEGRA_SOR_TIMEOUT_MS)) { + debug("dc timeout waiting for SOR_PWR = NEW_DONE\n"); + return -EFAULT; + } + + return 0; +} + +void tegra_dc_sor_set_dp_linkctl(struct tegra_dc_sor_data *sor, int ena, + u8 training_pattern, + const struct tegra_dp_link_config *link_cfg) +{ + u32 reg_val; + + reg_val = tegra_sor_readl(sor, DP_LINKCTL(sor->portnum)); + + if (ena) + reg_val |= DP_LINKCTL_ENABLE_YES; + else + reg_val &= DP_LINKCTL_ENABLE_NO; + + reg_val &= ~DP_LINKCTL_TUSIZE_MASK; + reg_val |= (link_cfg->tu_size << DP_LINKCTL_TUSIZE_SHIFT); + + if (link_cfg->enhanced_framing) + reg_val |= DP_LINKCTL_ENHANCEDFRAME_ENABLE; + + tegra_sor_writel(sor, DP_LINKCTL(sor->portnum), reg_val); + + switch (training_pattern) { + case training_pattern_1: + tegra_sor_writel(sor, DP_TPG, 0x41414141); + break; + case training_pattern_2: + case training_pattern_3: + reg_val = (link_cfg->link_bw == SOR_LINK_SPEED_G5_4) ? + 0x43434343 : 0x42424242; + tegra_sor_writel(sor, DP_TPG, reg_val); + break; + default: + tegra_sor_writel(sor, DP_TPG, 0x50505050); + break; + } +} + +static int tegra_dc_sor_enable_lane_sequencer(struct tegra_dc_sor_data *sor, + int pu, int is_lvds) +{ + u32 reg_val; + + /* SOR lane sequencer */ + if (pu) { + reg_val = LANE_SEQ_CTL_SETTING_NEW_TRIGGER | + LANE_SEQ_CTL_SEQUENCE_DOWN | + LANE_SEQ_CTL_NEW_POWER_STATE_PU; + } else { + reg_val = LANE_SEQ_CTL_SETTING_NEW_TRIGGER | + LANE_SEQ_CTL_SEQUENCE_UP | + LANE_SEQ_CTL_NEW_POWER_STATE_PD; + } + + if (is_lvds) + reg_val |= 15 << LANE_SEQ_CTL_DELAY_SHIFT; + else + reg_val |= 1 << LANE_SEQ_CTL_DELAY_SHIFT; + + tegra_sor_writel(sor, LANE_SEQ_CTL, reg_val); + + if (tegra_dc_sor_poll_register(sor, LANE_SEQ_CTL, + LANE_SEQ_CTL_SETTING_MASK, + LANE_SEQ_CTL_SETTING_NEW_DONE, + 100, TEGRA_SOR_TIMEOUT_MS)) { + debug("dp: timeout while waiting for SOR lane sequencer to power down lanes\n"); + return -1; + } + + return 0; +} + +static int tegra_dc_sor_power_dplanes(struct tegra_dc_sor_data *sor, + u32 lane_count, int pu) +{ + u32 reg_val; + + reg_val = tegra_sor_readl(sor, DP_PADCTL(sor->portnum)); + + if (pu) { + switch (lane_count) { + case 4: + reg_val |= (DP_PADCTL_PD_TXD_3_NO | + DP_PADCTL_PD_TXD_2_NO); + /* fall through */ + case 2: + reg_val |= DP_PADCTL_PD_TXD_1_NO; + case 1: + reg_val |= DP_PADCTL_PD_TXD_0_NO; + break; + default: + debug("dp: invalid lane number %d\n", lane_count); + return -1; + } + + tegra_sor_writel(sor, DP_PADCTL(sor->portnum), reg_val); + tegra_dc_sor_set_lane_count(sor, lane_count); + } + + return tegra_dc_sor_enable_lane_sequencer(sor, pu, 0); +} + +void tegra_dc_sor_set_panel_power(struct tegra_dc_sor_data *sor, + int power_up) +{ + u32 reg_val; + + reg_val = tegra_sor_readl(sor, DP_PADCTL(sor->portnum)); + + if (power_up) + reg_val |= DP_PADCTL_PAD_CAL_PD_POWERUP; + else + reg_val &= ~DP_PADCTL_PAD_CAL_PD_POWERUP; + + tegra_sor_writel(sor, DP_PADCTL(sor->portnum), reg_val); +} + +static void tegra_dc_sor_config_pwm(struct tegra_dc_sor_data *sor, u32 pwm_div, + u32 pwm_dutycycle) +{ + tegra_sor_writel(sor, PWM_DIV, pwm_div); + tegra_sor_writel(sor, PWM_CTL, + (pwm_dutycycle & PWM_CTL_DUTY_CYCLE_MASK) | + PWM_CTL_SETTING_NEW_TRIGGER); + + if (tegra_dc_sor_poll_register(sor, PWM_CTL, + PWM_CTL_SETTING_NEW_SHIFT, + PWM_CTL_SETTING_NEW_DONE, + 100, TEGRA_SOR_TIMEOUT_MS)) { + debug("dp: timeout while waiting for SOR PWM setting\n"); + } +} + +static void tegra_dc_sor_set_dp_mode(struct tegra_dc_sor_data *sor, + const struct tegra_dp_link_config *link_cfg) +{ + u32 reg_val; + + tegra_dc_sor_set_link_bandwidth(sor, link_cfg->link_bw); + + tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_none, link_cfg); + reg_val = tegra_sor_readl(sor, DP_CONFIG(sor->portnum)); + reg_val &= ~DP_CONFIG_WATERMARK_MASK; + reg_val |= link_cfg->watermark; + reg_val &= ~DP_CONFIG_ACTIVESYM_COUNT_MASK; + reg_val |= (link_cfg->active_count << + DP_CONFIG_ACTIVESYM_COUNT_SHIFT); + reg_val &= ~DP_CONFIG_ACTIVESYM_FRAC_MASK; + reg_val |= (link_cfg->active_frac << + DP_CONFIG_ACTIVESYM_FRAC_SHIFT); + if (link_cfg->activepolarity) + reg_val |= DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE; + else + reg_val &= ~DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE; + reg_val |= (DP_CONFIG_ACTIVESYM_CNTL_ENABLE | + DP_CONFIG_RD_RESET_VAL_NEGATIVE); + + tegra_sor_writel(sor, DP_CONFIG(sor->portnum), reg_val); + + /* program h/vblank sym */ + tegra_sor_write_field(sor, DP_AUDIO_HBLANK_SYMBOLS, + DP_AUDIO_HBLANK_SYMBOLS_MASK, + link_cfg->hblank_sym); + + tegra_sor_write_field(sor, DP_AUDIO_VBLANK_SYMBOLS, + DP_AUDIO_VBLANK_SYMBOLS_MASK, + link_cfg->vblank_sym); +} + +static inline void tegra_dc_sor_super_update(struct tegra_dc_sor_data *sor) +{ + tegra_sor_writel(sor, SUPER_STATE0, 0); + tegra_sor_writel(sor, SUPER_STATE0, 1); + tegra_sor_writel(sor, SUPER_STATE0, 0); +} + +static inline void tegra_dc_sor_update(struct tegra_dc_sor_data *sor) +{ + tegra_sor_writel(sor, STATE0, 0); + tegra_sor_writel(sor, STATE0, 1); + tegra_sor_writel(sor, STATE0, 0); +} + +static int tegra_dc_sor_io_set_dpd(struct tegra_dc_sor_data *sor, int up) +{ + u32 reg_val; + void *pmc_base = sor->pmc_base; + + if (up) { + writel(APBDEV_PMC_DPD_SAMPLE_ON_ENABLE, + pmc_base + APBDEV_PMC_DPD_SAMPLE); + writel(10, pmc_base + APBDEV_PMC_SEL_DPD_TIM); + } + + reg_val = readl(pmc_base + APBDEV_PMC_IO_DPD2_REQ); + reg_val &= ~(APBDEV_PMC_IO_DPD2_REQ_LVDS_ON || + APBDEV_PMC_IO_DPD2_REQ_CODE_DEFAULT_MASK); + + reg_val = up ? APBDEV_PMC_IO_DPD2_REQ_LVDS_ON | + APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_OFF : + APBDEV_PMC_IO_DPD2_REQ_LVDS_OFF | + APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_ON; + + writel(reg_val, pmc_base + APBDEV_PMC_IO_DPD2_REQ); + + /* Polling */ + u32 temp = 10 * 1000; + do { + udelay(20); + reg_val = readl(pmc_base + APBDEV_PMC_IO_DPD2_STATUS); + if (temp > 20) + temp -= 20; + else + break; + } while ((reg_val & APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON) != 0); + + if ((reg_val & APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON) != 0) { + debug("PMC_IO_DPD2 polling failed (0x%x)\n", reg_val); + return -EIO; + } + + if (up) { + writel(APBDEV_PMC_DPD_SAMPLE_ON_DISABLE, + pmc_base + APBDEV_PMC_DPD_SAMPLE); + } + + return 0; +} + +void tegra_dc_sor_set_internal_panel(struct tegra_dc_sor_data *sor, int is_int) +{ + u32 reg_val; + + reg_val = tegra_sor_readl(sor, DP_SPARE(sor->portnum)); + if (is_int) + reg_val |= DP_SPARE_PANEL_INTERNAL; + else + reg_val &= ~DP_SPARE_PANEL_INTERNAL; + + reg_val |= DP_SPARE_SOR_CLK_SEL_MACRO_SORCLK | + DP_SPARE_SEQ_ENABLE_YES; + tegra_sor_writel(sor, DP_SPARE(sor->portnum), reg_val); +} + +void tegra_dc_sor_read_link_config(struct tegra_dc_sor_data *sor, u8 *link_bw, + u8 *lane_count) +{ + u32 reg_val; + + reg_val = tegra_sor_readl(sor, CLK_CNTRL); + *link_bw = (reg_val & CLK_CNTRL_DP_LINK_SPEED_MASK) + >> CLK_CNTRL_DP_LINK_SPEED_SHIFT; + reg_val = tegra_sor_readl(sor, + DP_LINKCTL(sor->portnum)); + + switch (reg_val & DP_LINKCTL_LANECOUNT_MASK) { + case DP_LINKCTL_LANECOUNT_ZERO: + *lane_count = 0; + break; + case DP_LINKCTL_LANECOUNT_ONE: + *lane_count = 1; + break; + case DP_LINKCTL_LANECOUNT_TWO: + *lane_count = 2; + break; + case DP_LINKCTL_LANECOUNT_FOUR: + *lane_count = 4; + break; + default: + printf("Unknown lane count\n"); + } +} + +void tegra_dc_sor_set_link_bandwidth(struct tegra_dc_sor_data *sor, u8 link_bw) +{ + tegra_sor_write_field(sor, CLK_CNTRL, + CLK_CNTRL_DP_LINK_SPEED_MASK, + link_bw << CLK_CNTRL_DP_LINK_SPEED_SHIFT); +} + +void tegra_dc_sor_set_lane_count(struct tegra_dc_sor_data *sor, u8 lane_count) +{ + u32 reg_val; + + reg_val = tegra_sor_readl(sor, DP_LINKCTL(sor->portnum)); + reg_val &= ~DP_LINKCTL_LANECOUNT_MASK; + switch (lane_count) { + case 0: + break; + case 1: + reg_val |= DP_LINKCTL_LANECOUNT_ONE; + break; + case 2: + reg_val |= DP_LINKCTL_LANECOUNT_TWO; + break; + case 4: + reg_val |= DP_LINKCTL_LANECOUNT_FOUR; + break; + default: + /* 0 should be handled earlier. */ + printf("dp: Invalid lane count %d\n", lane_count); + return; + } + tegra_sor_writel(sor, DP_LINKCTL(sor->portnum), reg_val); +} + +/* + * The SOR power sequencer does not work for t124 so SW has to + * go through the power sequence manually + * Power up steps from spec: + * STEP PDPORT PDPLL PDBG PLLVCOD PLLCAPD E_DPD PDCAL + * 1 1 1 1 1 1 1 1 + * 2 1 1 1 1 1 0 1 + * 3 1 1 0 1 1 0 1 + * 4 1 0 0 0 0 0 1 + * 5 0 0 0 0 0 0 1 + */ +static int tegra_dc_sor_power_up(struct tegra_dc_sor_data *sor, int is_lvds) +{ + int ret; + + if (sor->power_is_up) + return 0; + + /* Set link bw */ + tegra_dc_sor_set_link_bandwidth(sor, is_lvds ? + CLK_CNTRL_DP_LINK_SPEED_LVDS : + CLK_CNTRL_DP_LINK_SPEED_G1_62); + + /* step 1 */ + tegra_sor_write_field(sor, PLL2, + PLL2_AUX7_PORT_POWERDOWN_MASK | /* PDPORT */ + PLL2_AUX6_BANDGAP_POWERDOWN_MASK | /* PDBG */ + PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK, /* PLLCAPD */ + PLL2_AUX7_PORT_POWERDOWN_ENABLE | + PLL2_AUX6_BANDGAP_POWERDOWN_ENABLE | + PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_ENABLE); + tegra_sor_write_field(sor, PLL0, PLL0_PWR_MASK | /* PDPLL */ + PLL0_VCOPD_MASK, /* PLLVCOPD */ + PLL0_PWR_OFF | PLL0_VCOPD_ASSERT); + tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), + DP_PADCTL_PAD_CAL_PD_POWERDOWN, /* PDCAL */ + DP_PADCTL_PAD_CAL_PD_POWERDOWN); + + /* step 2 */ + ret = tegra_dc_sor_io_set_dpd(sor, 1); + if (ret) + return ret; + udelay(15); + + /* step 3 */ + tegra_sor_write_field(sor, PLL2, + PLL2_AUX6_BANDGAP_POWERDOWN_MASK, + PLL2_AUX6_BANDGAP_POWERDOWN_DISABLE); + udelay(25); + + /* step 4 */ + tegra_sor_write_field(sor, PLL0, + PLL0_PWR_MASK | /* PDPLL */ + PLL0_VCOPD_MASK, /* PLLVCOPD */ + PLL0_PWR_ON | PLL0_VCOPD_RESCIND); + /* PLLCAPD */ + tegra_sor_write_field(sor, PLL2, + PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK, + PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE); + udelay(225); + + /* step 5 PDPORT */ + tegra_sor_write_field(sor, PLL2, + PLL2_AUX7_PORT_POWERDOWN_MASK, + PLL2_AUX7_PORT_POWERDOWN_DISABLE); + + sor->power_is_up = 1; + + return 0; +} + +#if DEBUG_SOR +static void dump_sor_reg(struct tegra_dc_sor_data *sor) +{ +#define DUMP_REG(a) printk(BIOS_INFO, "%-32s %03x %08x\n", \ + #a, a, tegra_sor_readl(sor, a)); + + DUMP_REG(SUPER_STATE0); + DUMP_REG(SUPER_STATE1); + DUMP_REG(STATE0); + DUMP_REG(STATE1); + DUMP_REG(NV_HEAD_STATE0(0)); + DUMP_REG(NV_HEAD_STATE0(1)); + DUMP_REG(NV_HEAD_STATE1(0)); + DUMP_REG(NV_HEAD_STATE1(1)); + DUMP_REG(NV_HEAD_STATE2(0)); + DUMP_REG(NV_HEAD_STATE2(1)); + DUMP_REG(NV_HEAD_STATE3(0)); + DUMP_REG(NV_HEAD_STATE3(1)); + DUMP_REG(NV_HEAD_STATE4(0)); + DUMP_REG(NV_HEAD_STATE4(1)); + DUMP_REG(NV_HEAD_STATE5(0)); + DUMP_REG(NV_HEAD_STATE5(1)); + DUMP_REG(CRC_CNTRL); + DUMP_REG(CLK_CNTRL); + DUMP_REG(CAP); + DUMP_REG(PWR); + DUMP_REG(TEST); + DUMP_REG(PLL0); + DUMP_REG(PLL1); + DUMP_REG(PLL2); + DUMP_REG(PLL3); + DUMP_REG(CSTM); + DUMP_REG(LVDS); + DUMP_REG(CRCA); + DUMP_REG(CRCB); + DUMP_REG(SEQ_CTL); + DUMP_REG(LANE_SEQ_CTL); + DUMP_REG(SEQ_INST(0)); + DUMP_REG(SEQ_INST(1)); + DUMP_REG(SEQ_INST(2)); + DUMP_REG(SEQ_INST(3)); + DUMP_REG(SEQ_INST(4)); + DUMP_REG(SEQ_INST(5)); + DUMP_REG(SEQ_INST(6)); + DUMP_REG(SEQ_INST(7)); + DUMP_REG(SEQ_INST(8)); + DUMP_REG(PWM_DIV); + DUMP_REG(PWM_CTL); + DUMP_REG(MSCHECK); + DUMP_REG(XBAR_CTRL); + DUMP_REG(DP_LINKCTL(0)); + DUMP_REG(DP_LINKCTL(1)); + DUMP_REG(DC(0)); + DUMP_REG(DC(1)); + DUMP_REG(LANE_DRIVE_CURRENT(0)); + DUMP_REG(PR(0)); + DUMP_REG(LANE4_PREEMPHASIS(0)); + DUMP_REG(POSTCURSOR(0)); + DUMP_REG(DP_CONFIG(0)); + DUMP_REG(DP_CONFIG(1)); + DUMP_REG(DP_MN(0)); + DUMP_REG(DP_MN(1)); + DUMP_REG(DP_PADCTL(0)); + DUMP_REG(DP_PADCTL(1)); + DUMP_REG(DP_DEBUG(0)); + DUMP_REG(DP_DEBUG(1)); + DUMP_REG(DP_SPARE(0)); + DUMP_REG(DP_SPARE(1)); + DUMP_REG(DP_TPG); + + return; +} +#endif + +static void tegra_dc_sor_config_panel(struct tegra_dc_sor_data *sor, + int is_lvds, + const struct tegra_dp_link_config *link_cfg, + const struct display_timing *timing) +{ + const int head_num = 0; + u32 reg_val = STATE1_ASY_OWNER_HEAD0 << head_num; + u32 vtotal, htotal; + u32 vsync_end, hsync_end; + u32 vblank_end, hblank_end; + u32 vblank_start, hblank_start; + + reg_val |= is_lvds ? STATE1_ASY_PROTOCOL_LVDS_CUSTOM : + STATE1_ASY_PROTOCOL_DP_A; + reg_val |= STATE1_ASY_SUBOWNER_NONE | + STATE1_ASY_CRCMODE_COMPLETE_RASTER; + + reg_val |= STATE1_ASY_HSYNCPOL_NEGATIVE_TRUE; + reg_val |= STATE1_ASY_VSYNCPOL_NEGATIVE_TRUE; + reg_val |= (link_cfg->bits_per_pixel > 18) ? + STATE1_ASY_PIXELDEPTH_BPP_24_444 : + STATE1_ASY_PIXELDEPTH_BPP_18_444; + + tegra_sor_writel(sor, STATE1, reg_val); + + /* + * Skipping programming NV_HEAD_STATE0, assuming: + * interlacing: PROGRESSIVE, dynamic range: VESA, colorspace: RGB + */ + vtotal = timing->vsync_len.typ + timing->vback_porch.typ + + timing->vactive.typ + timing->vfront_porch.typ; + htotal = timing->hsync_len.typ + timing->hback_porch.typ + + timing->hactive.typ + timing->hfront_porch.typ; + + tegra_sor_writel(sor, NV_HEAD_STATE1(head_num), + vtotal << NV_HEAD_STATE1_VTOTAL_SHIFT | + htotal << NV_HEAD_STATE1_HTOTAL_SHIFT); + + vsync_end = timing->vsync_len.typ - 1; + hsync_end = timing->hsync_len.typ - 1; + tegra_sor_writel(sor, NV_HEAD_STATE2(head_num), + vsync_end << NV_HEAD_STATE2_VSYNC_END_SHIFT | + hsync_end << NV_HEAD_STATE2_HSYNC_END_SHIFT); + + vblank_end = vsync_end + timing->vback_porch.typ; + hblank_end = hsync_end + timing->hback_porch.typ; + tegra_sor_writel(sor, NV_HEAD_STATE3(head_num), + vblank_end << NV_HEAD_STATE3_VBLANK_END_SHIFT | + hblank_end << NV_HEAD_STATE3_HBLANK_END_SHIFT); + + vblank_start = vblank_end + timing->vactive.typ; + hblank_start = hblank_end + timing->hactive.typ; + tegra_sor_writel(sor, NV_HEAD_STATE4(head_num), + vblank_start << NV_HEAD_STATE4_VBLANK_START_SHIFT | + hblank_start << NV_HEAD_STATE4_HBLANK_START_SHIFT); + + /* TODO: adding interlace mode support */ + tegra_sor_writel(sor, NV_HEAD_STATE5(head_num), 0x1); + + tegra_sor_write_field(sor, CSTM, + CSTM_ROTCLK_DEFAULT_MASK | + CSTM_LVDS_EN_ENABLE, + 2 << CSTM_ROTCLK_SHIFT | + is_lvds ? CSTM_LVDS_EN_ENABLE : + CSTM_LVDS_EN_DISABLE); + + tegra_dc_sor_config_pwm(sor, 1024, 1024); +} + +static void tegra_dc_sor_enable_dc(struct dc_ctlr *disp_ctrl) +{ + u32 reg_val = readl(&disp_ctrl->cmd.state_access); + + writel(reg_val | WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access); + writel(VSYNC_H_POSITION(1), &disp_ctrl->disp.disp_timing_opt); + + /* Enable DC now - otherwise pure text console may not show. */ + writel(CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT, + &disp_ctrl->cmd.disp_cmd); + writel(reg_val, &disp_ctrl->cmd.state_access); +} + +int tegra_dc_sor_enable_dp(struct tegra_dc_sor_data *sor, + const struct tegra_dp_link_config *link_cfg) +{ + int ret; + + tegra_sor_write_field(sor, CLK_CNTRL, + CLK_CNTRL_DP_CLK_SEL_MASK, + CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK); + + tegra_sor_write_field(sor, PLL2, + PLL2_AUX6_BANDGAP_POWERDOWN_MASK, + PLL2_AUX6_BANDGAP_POWERDOWN_DISABLE); + udelay(25); + + tegra_sor_write_field(sor, PLL3, + PLL3_PLLVDD_MODE_MASK, + PLL3_PLLVDD_MODE_V3_3); + tegra_sor_writel(sor, PLL0, + 0xf << PLL0_ICHPMP_SHFIT | + 0x3 << PLL0_VCOCAP_SHIFT | + PLL0_PLLREG_LEVEL_V45 | + PLL0_RESISTORSEL_EXT | + PLL0_PWR_ON | PLL0_VCOPD_RESCIND); + tegra_sor_write_field(sor, PLL2, + PLL2_AUX1_SEQ_MASK | + PLL2_AUX9_LVDSEN_OVERRIDE | + PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK, + PLL2_AUX1_SEQ_PLLCAPPD_OVERRIDE | + PLL2_AUX9_LVDSEN_OVERRIDE | + PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE); + tegra_sor_writel(sor, PLL1, PLL1_TERM_COMPOUT_HIGH | + PLL1_TMDS_TERM_ENABLE); + + if (tegra_dc_sor_poll_register(sor, PLL2, + PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK, + PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE, + 100, TEGRA_SOR_TIMEOUT_MS)) { + printf("DP failed to lock PLL\n"); + return -EIO; + } + + tegra_sor_write_field(sor, PLL2, PLL2_AUX2_MASK | + PLL2_AUX7_PORT_POWERDOWN_MASK, + PLL2_AUX2_OVERRIDE_POWERDOWN | + PLL2_AUX7_PORT_POWERDOWN_DISABLE); + + ret = tegra_dc_sor_power_up(sor, 0); + if (ret) { + debug("DP failed to power up\n"); + return ret; + } + + /* re-enable SOR clock */ + clock_sor_enable_edp_clock(); + + /* Power up lanes */ + tegra_dc_sor_power_dplanes(sor, link_cfg->lane_count, 1); + + tegra_dc_sor_set_dp_mode(sor, link_cfg); + debug("%s ret\n", __func__); + + return 0; +} + +int tegra_dc_sor_attach(struct tegra_dc_sor_data *sor, + const struct tegra_dp_link_config *link_cfg, + const struct display_timing *timing) +{ + const void *blob = gd->fdt_blob; + struct dc_ctlr *disp_ctrl; + u32 reg_val; + int node; + + /* Use the first display controller */ + debug("%s\n", __func__); + node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_DC); + if (node < 0) + return -ENOENT; + disp_ctrl = (struct dc_ctlr *)fdtdec_get_addr(blob, node, "reg"); + + tegra_dc_sor_enable_dc(disp_ctrl); + tegra_dc_sor_config_panel(sor, 0, link_cfg, timing); + + writel(0x9f00, &disp_ctrl->cmd.state_ctrl); + writel(0x9f, &disp_ctrl->cmd.state_ctrl); + + writel(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | + PW4_ENABLE | PM0_ENABLE | PM1_ENABLE, + &disp_ctrl->cmd.disp_pow_ctrl); + + reg_val = tegra_sor_readl(sor, TEST); + if (reg_val & TEST_ATTACHED_TRUE) + return -EEXIST; + + tegra_sor_writel(sor, SUPER_STATE1, + SUPER_STATE1_ATTACHED_NO); + + /* + * Enable display2sor clock at least 2 cycles before DC start, + * to clear sor internal valid signal. + */ + writel(SOR_ENABLE, &disp_ctrl->disp.disp_win_opt); + writel(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl); + writel(0, &disp_ctrl->disp.disp_win_opt); + writel(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl); + + /* Attach head */ + tegra_dc_sor_update(sor); + tegra_sor_writel(sor, SUPER_STATE1, + SUPER_STATE1_ATTACHED_YES); + tegra_sor_writel(sor, SUPER_STATE1, + SUPER_STATE1_ATTACHED_YES | + SUPER_STATE1_ASY_HEAD_OP_AWAKE | + SUPER_STATE1_ASY_ORMODE_NORMAL); + tegra_dc_sor_super_update(sor); + + /* Enable dc */ + reg_val = readl(&disp_ctrl->cmd.state_access); + writel(reg_val | WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access); + writel(CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT, + &disp_ctrl->cmd.disp_cmd); + writel(SOR_ENABLE, &disp_ctrl->disp.disp_win_opt); + writel(reg_val, &disp_ctrl->cmd.state_access); + + if (tegra_dc_sor_poll_register(sor, TEST, + TEST_ACT_HEAD_OPMODE_DEFAULT_MASK, + TEST_ACT_HEAD_OPMODE_AWAKE, + 100, + TEGRA_SOR_ATTACH_TIMEOUT_MS)) { + printf("dc timeout waiting for OPMOD = AWAKE\n"); + return -ETIMEDOUT; + } else { + debug("%s: sor is attached\n", __func__); + } + +#if DEBUG_SOR + dump_sor_reg(sor); +#endif + debug("%s: ret=%d\n", __func__, 0); + + return 0; +} + +void tegra_dc_sor_set_lane_parm(struct tegra_dc_sor_data *sor, + const struct tegra_dp_link_config *link_cfg) +{ + tegra_sor_writel(sor, LANE_DRIVE_CURRENT(sor->portnum), + link_cfg->drive_current); + tegra_sor_writel(sor, PR(sor->portnum), + link_cfg->preemphasis); + tegra_sor_writel(sor, POSTCURSOR(sor->portnum), + link_cfg->postcursor); + tegra_sor_writel(sor, LVDS, 0); + + tegra_dc_sor_set_link_bandwidth(sor, link_cfg->link_bw); + tegra_dc_sor_set_lane_count(sor, link_cfg->lane_count); + + tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), + DP_PADCTL_TX_PU_ENABLE | + DP_PADCTL_TX_PU_VALUE_DEFAULT_MASK, + DP_PADCTL_TX_PU_ENABLE | + 2 << DP_PADCTL_TX_PU_VALUE_SHIFT); + + /* Precharge */ + tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), 0xf0, 0xf0); + udelay(20); + + tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), 0xf0, 0x0); +} + +int tegra_dc_sor_set_voltage_swing(struct tegra_dc_sor_data *sor, + const struct tegra_dp_link_config *link_cfg) +{ + u32 drive_current = 0; + u32 pre_emphasis = 0; + + /* Set to a known-good pre-calibrated setting */ + switch (link_cfg->link_bw) { + case SOR_LINK_SPEED_G1_62: + case SOR_LINK_SPEED_G2_7: + drive_current = 0x13131313; + pre_emphasis = 0; + break; + case SOR_LINK_SPEED_G5_4: + debug("T124 does not support 5.4G link clock.\n"); + default: + debug("Invalid sor link bandwidth: %d\n", link_cfg->link_bw); + return -ENOLINK; + } + + tegra_sor_writel(sor, LANE_DRIVE_CURRENT(sor->portnum), drive_current); + tegra_sor_writel(sor, PR(sor->portnum), pre_emphasis); + + return 0; +} + +void tegra_dc_sor_power_down_unused_lanes(struct tegra_dc_sor_data *sor, + const struct tegra_dp_link_config *link_cfg) +{ + u32 pad_ctrl = 0; + int err = 0; + + switch (link_cfg->lane_count) { + case 4: + pad_ctrl = DP_PADCTL_PD_TXD_0_NO | + DP_PADCTL_PD_TXD_1_NO | + DP_PADCTL_PD_TXD_2_NO | + DP_PADCTL_PD_TXD_3_NO; + break; + case 2: + pad_ctrl = DP_PADCTL_PD_TXD_0_NO | + DP_PADCTL_PD_TXD_1_NO | + DP_PADCTL_PD_TXD_2_YES | + DP_PADCTL_PD_TXD_3_YES; + break; + case 1: + pad_ctrl = DP_PADCTL_PD_TXD_0_NO | + DP_PADCTL_PD_TXD_1_YES | + DP_PADCTL_PD_TXD_2_YES | + DP_PADCTL_PD_TXD_3_YES; + break; + default: + printf("Invalid sor lane count: %u\n", link_cfg->lane_count); + return; + } + + pad_ctrl |= DP_PADCTL_PAD_CAL_PD_POWERDOWN; + tegra_sor_writel(sor, DP_PADCTL(sor->portnum), pad_ctrl); + + err = tegra_dc_sor_enable_lane_sequencer(sor, 0, 0); + if (err) { + debug("Wait for lane power down failed: %d\n", err); + return; + } +} + +int tegra_sor_precharge_lanes(struct tegra_dc_sor_data *sor, + const struct tegra_dp_link_config *cfg) +{ + u32 val = 0; + + switch (cfg->lane_count) { + case 4: + val |= (DP_PADCTL_PD_TXD_3_NO | + DP_PADCTL_PD_TXD_2_NO); + /* fall through */ + case 2: + val |= DP_PADCTL_PD_TXD_1_NO; + /* fall through */ + case 1: + val |= DP_PADCTL_PD_TXD_0_NO; + break; + default: + debug("dp: invalid lane number %d\n", cfg->lane_count); + return -EINVAL; + } + + tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), + (0xf << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT), + (val << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT)); + udelay(100); + tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), + (0xf << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT), + 0); + + return 0; +} + +static void tegra_dc_sor_enable_sor(struct dc_ctlr *disp_ctrl, bool enable) +{ + u32 reg_val = readl(&disp_ctrl->disp.disp_win_opt); + + reg_val = enable ? reg_val | SOR_ENABLE : reg_val & ~SOR_ENABLE; + writel(reg_val, &disp_ctrl->disp.disp_win_opt); +} + +int tegra_dc_sor_detach(struct tegra_dc_sor_data *sor) +{ + int dc_reg_ctx[DC_REG_SAVE_SPACE]; + const void *blob = gd->fdt_blob; + struct dc_ctlr *disp_ctrl; + unsigned long dc_int_mask; + int node; + int ret; + + debug("%s\n", __func__); + /* Use the first display controller */ + node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_DC); + if (node < 0) { + ret = -ENOENT; + goto err; + } + disp_ctrl = (struct dc_ctlr *)fdtdec_get_addr(blob, node, "reg"); + + /* Sleep mode */ + tegra_sor_writel(sor, SUPER_STATE1, SUPER_STATE1_ASY_HEAD_OP_SLEEP | + SUPER_STATE1_ASY_ORMODE_SAFE | + SUPER_STATE1_ATTACHED_YES); + tegra_dc_sor_super_update(sor); + + tegra_dc_sor_disable_win_short_raster(disp_ctrl, dc_reg_ctx); + + if (tegra_dc_sor_poll_register(sor, TEST, + TEST_ACT_HEAD_OPMODE_DEFAULT_MASK, + TEST_ACT_HEAD_OPMODE_SLEEP, 100, + TEGRA_SOR_ATTACH_TIMEOUT_MS)) { + debug("dc timeout waiting for OPMOD = SLEEP\n"); + ret = -ETIMEDOUT; + goto err; + } + + tegra_sor_writel(sor, SUPER_STATE1, SUPER_STATE1_ASY_HEAD_OP_SLEEP | + SUPER_STATE1_ASY_ORMODE_SAFE | + SUPER_STATE1_ATTACHED_NO); + + /* Mask DC interrupts during the 2 dummy frames required for detach */ + dc_int_mask = readl(&disp_ctrl->cmd.int_mask); + writel(0, &disp_ctrl->cmd.int_mask); + + /* Stop DC->SOR path */ + tegra_dc_sor_enable_sor(disp_ctrl, false); + ret = tegra_dc_sor_general_act(disp_ctrl); + if (ret) + goto err; + + /* Stop DC */ + writel(CTRL_MODE_STOP << CTRL_MODE_SHIFT, &disp_ctrl->cmd.disp_cmd); + ret = tegra_dc_sor_general_act(disp_ctrl); + if (ret) + goto err; + + tegra_dc_sor_restore_win_and_raster(disp_ctrl, dc_reg_ctx); + + writel(dc_int_mask, &disp_ctrl->cmd.int_mask); + + return 0; +err: + debug("%s: ret=%d\n", __func__, ret); + + return ret; +} + +int tegra_dc_sor_init(struct tegra_dc_sor_data **sorp) +{ + const void *blob = gd->fdt_blob; + struct tegra_dc_sor_data *sor; + int node; + + node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_SOR); + if (node < 0) + return -ENOENT; + sor = calloc(1, sizeof(*sor)); + if (!sor) + return -ENOMEM; + sor->base = (void *)fdtdec_get_addr(blob, node, "reg"); + + node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_PMC); + if (node < 0) + return -ENOENT; + sor->pmc_base = (void *)fdtdec_get_addr(blob, node, "reg"); + + sor->power_is_up = 0; + sor->portnum = 0; + *sorp = sor; + + return 0; +} diff --git a/drivers/video/tegra124/sor.h b/drivers/video/tegra124/sor.h new file mode 100644 index 0000000000..dc8fd03d80 --- /dev/null +++ b/drivers/video/tegra124/sor.h @@ -0,0 +1,922 @@ +/* + * Copyright (c) 2011-2013, NVIDIA Corporation. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _VIDEO_TEGRA124_SOR_H +#define _VIDEO_TEGRA124_SOR_H + +#define SUPER_STATE0 0x1 +#define SUPER_STATE0_UPDATE_SHIFT 0 +#define SUPER_STATE0_UPDATE_DEFAULT_MASK 0x1 +#define SUPER_STATE1 0x2 +#define SUPER_STATE1_ATTACHED_SHIFT 3 +#define SUPER_STATE1_ATTACHED_NO (0 << 3) +#define SUPER_STATE1_ATTACHED_YES (1 << 3) +#define SUPER_STATE1_ASY_ORMODE_SHIFT 2 +#define SUPER_STATE1_ASY_ORMODE_SAFE (0 << 2) +#define SUPER_STATE1_ASY_ORMODE_NORMAL (1 << 2) +#define SUPER_STATE1_ASY_HEAD_OP_SHIFT 0 +#define SUPER_STATE1_ASY_HEAD_OP_DEFAULT_MASK 0x3 +#define SUPER_STATE1_ASY_HEAD_OP_SLEEP 0 +#define SUPER_STATE1_ASY_HEAD_OP_SNOOZE 1 +#define SUPER_STATE1_ASY_HEAD_OP_AWAKE 2 +#define STATE0 0x3 +#define STATE0_UPDATE_SHIFT 0 +#define STATE0_UPDATE_DEFAULT_MASK 0x1 +#define STATE1 0x4 +#define STATE1_ASY_PIXELDEPTH_SHIFT 17 +#define STATE1_ASY_PIXELDEPTH_DEFAULT_MASK (0xf << 17) +#define STATE1_ASY_PIXELDEPTH_BPP_16_422 (1 << 17) +#define STATE1_ASY_PIXELDEPTH_BPP_18_444 (2 << 17) +#define STATE1_ASY_PIXELDEPTH_BPP_20_422 (3 << 17) +#define STATE1_ASY_PIXELDEPTH_BPP_24_422 (4 << 17) +#define STATE1_ASY_PIXELDEPTH_BPP_24_444 (5 << 17) +#define STATE1_ASY_PIXELDEPTH_BPP_30_444 (6 << 17) +#define STATE1_ASY_PIXELDEPTH_BPP_32_422 (7 << 17) +#define STATE1_ASY_PIXELDEPTH_BPP_36_444 (8 << 17) +#define STATE1_ASY_PIXELDEPTH_BPP_48_444 (9 << 17) +#define STATE1_ASY_REPLICATE_SHIFT 15 +#define STATE1_ASY_REPLICATE_DEFAULT_MASK (3 << 15) +#define STATE1_ASY_REPLICATE_OFF (0 << 15) +#define STATE1_ASY_REPLICATE_X2 (1 << 15) +#define STATE1_ASY_REPLICATE_X4 (2 << 15) +#define STATE1_ASY_DEPOL_SHIFT 14 +#define STATE1_ASY_DEPOL_DEFAULT_MASK (1 << 14) +#define STATE1_ASY_DEPOL_POSITIVE_TRUE (0 << 14) +#define STATE1_ASY_DEPOL_NEGATIVE_TRUE (1 << 14) +#define STATE1_ASY_VSYNCPOL_SHIFT 13 +#define STATE1_ASY_VSYNCPOL_DEFAULT_MASK (1 << 13) +#define STATE1_ASY_VSYNCPOL_POSITIVE_TRUE (0 << 13) +#define STATE1_ASY_VSYNCPOL_NEGATIVE_TRUE (1 << 13) +#define STATE1_ASY_HSYNCPOL_SHIFT 12 +#define STATE1_ASY_HSYNCPOL_DEFAULT_MASK (1 << 12) +#define STATE1_ASY_HSYNCPOL_POSITIVE_TRUE (0 << 12) +#define STATE1_ASY_HSYNCPOL_NEGATIVE_TRUE (1 << 12) +#define STATE1_ASY_PROTOCOL_SHIFT 8 +#define STATE1_ASY_PROTOCOL_DEFAULT_MASK (0xf << 8) +#define STATE1_ASY_PROTOCOL_LVDS_CUSTOM (0 << 8) +#define STATE1_ASY_PROTOCOL_DP_A (8 << 8) +#define STATE1_ASY_PROTOCOL_DP_B (9 << 8) +#define STATE1_ASY_PROTOCOL_CUSTOM (15 << 8) +#define STATE1_ASY_CRCMODE_SHIFT 6 +#define STATE1_ASY_CRCMODE_DEFAULT_MASK (3 << 6) +#define STATE1_ASY_CRCMODE_ACTIVE_RASTER (0 << 6) +#define STATE1_ASY_CRCMODE_COMPLETE_RASTER (1 << 6) +#define STATE1_ASY_CRCMODE_NON_ACTIVE_RASTER (2 << 6) +#define STATE1_ASY_SUBOWNER_SHIFT 4 +#define STATE1_ASY_SUBOWNER_DEFAULT_MASK (3 << 4) +#define STATE1_ASY_SUBOWNER_NONE (0 << 4) +#define STATE1_ASY_SUBOWNER_SUBHEAD0 (1 << 4) +#define STATE1_ASY_SUBOWNER_SUBHEAD1 (2 << 4) +#define STATE1_ASY_SUBOWNER_BOTH (3 << 4) +#define STATE1_ASY_OWNER_SHIFT 0 +#define STATE1_ASY_OWNER_DEFAULT_MASK 0xf +#define STATE1_ASY_OWNER_NONE 0 +#define STATE1_ASY_OWNER_HEAD0 1 +#define STATE1_ASY_OWNER_HEAD1 2 +#define NV_HEAD_STATE0(i) 0x5 +#define NV_HEAD_STATE0_INTERLACED_SHIFT 4 +#define NV_HEAD_STATE0_INTERLACED_DEFAULT_MASK (3 << 4) +#define NV_HEAD_STATE0_INTERLACED_PROGRESSIVE (0 << 4) +#define NV_HEAD_STATE0_INTERLACED_INTERLACED (1 << 4) +#define NV_HEAD_STATE0_RANGECOMPRESS_SHIFT 3 +#define NV_HEAD_STATE0_RANGECOMPRESS_DEFAULT_MASK (1 << 3) +#define NV_HEAD_STATE0_RANGECOMPRESS_DISABLE (0 << 3) +#define NV_HEAD_STATE0_RANGECOMPRESS_ENABLE (1 << 3) +#define NV_HEAD_STATE0_DYNRANGE_SHIFT 2 +#define NV_HEAD_STATE0_DYNRANGE_DEFAULT_MASK (1 << 2) +#define NV_HEAD_STATE0_DYNRANGE_VESA (0 << 2) +#define NV_HEAD_STATE0_DYNRANGE_CEA (1 << 2) +#define NV_HEAD_STATE0_COLORSPACE_SHIFT 0 +#define NV_HEAD_STATE0_COLORSPACE_DEFAULT_MASK 0x3 +#define NV_HEAD_STATE0_COLORSPACE_RGB 0 +#define NV_HEAD_STATE0_COLORSPACE_YUV_601 1 +#define NV_HEAD_STATE0_COLORSPACE_YUV_709 2 +#define NV_HEAD_STATE1(i) (7 + i) +#define NV_HEAD_STATE1_VTOTAL_SHIFT 16 +#define NV_HEAD_STATE1_VTOTAL_DEFAULT_MASK (0x7fff << 16) +#define NV_HEAD_STATE1_HTOTAL_SHIFT 0 +#define NV_HEAD_STATE1_HTOTAL_DEFAULT_MASK 0x7fff +#define NV_HEAD_STATE2(i) (9 + i) +#define NV_HEAD_STATE2_VSYNC_END_SHIFT 16 +#define NV_HEAD_STATE2_VSYNC_END_DEFAULT_MASK (0x7fff << 16) +#define NV_HEAD_STATE2_HSYNC_END_SHIFT 0 +#define NV_HEAD_STATE2_HSYNC_END_DEFAULT_MASK 0x7fff +#define NV_HEAD_STATE3(i) (0xb + i) +#define NV_HEAD_STATE3_VBLANK_END_SHIFT 16 +#define NV_HEAD_STATE3_VBLANK_END_DEFAULT_MASK (0x7fff << 16) +#define NV_HEAD_STATE3_HBLANK_END_SHIFT 0 +#define NV_HEAD_STATE3_HBLANK_END_DEFAULT_MASK 0x7fff +#define NV_HEAD_STATE4(i) (0xd + i) +#define NV_HEAD_STATE4_VBLANK_START_SHIFT 16 +#define NV_HEAD_STATE4_VBLANK_START_DEFAULT_MASK (0x7fff << 16) +#define NV_HEAD_STATE4_HBLANK_START_SHIFT 0 +#define NV_HEAD_STATE4_HBLANK_START_DEFAULT_MASK 0x7fff +#define NV_HEAD_STATE5(i) (0xf + i) +#define CRC_CNTRL 0x11 +#define CRC_CNTRL_ARM_CRC_ENABLE_SHIFT 0 +#define CRC_CNTRL_ARM_CRC_ENABLE_NO 0 +#define CRC_CNTRL_ARM_CRC_ENABLE_YES 1 +#define CRC_CNTRL_ARM_CRC_ENABLE_DIS 0 +#define CRC_CNTRL_ARM_CRC_ENABLE_EN 1 +#define CLK_CNTRL 0x13 +#define CLK_CNTRL_DP_CLK_SEL_SHIFT 0 +#define CLK_CNTRL_DP_CLK_SEL_MASK 0x3 +#define CLK_CNTRL_DP_CLK_SEL_SINGLE_PCLK 0 +#define CLK_CNTRL_DP_CLK_SEL_DIFF_PCLK 1 +#define CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK 2 +#define CLK_CNTRL_DP_CLK_SEL_DIFF_DPCLK 3 +#define CLK_CNTRL_DP_LINK_SPEED_SHIFT 2 +#define CLK_CNTRL_DP_LINK_SPEED_MASK (0x1f << 2) +#define CLK_CNTRL_DP_LINK_SPEED_G1_62 (6 << 2) +#define CLK_CNTRL_DP_LINK_SPEED_G2_7 (10 << 2) +#define CLK_CNTRL_DP_LINK_SPEED_LVDS (7 << 2) +#define CAP 0x14 +#define CAP_DP_A_SHIFT 24 +#define CAP_DP_A_DEFAULT_MASK (1 << 24) +#define CAP_DP_A_FALSE (0 << 24) +#define CAP_DP_A_TRUE (1 << 24) +#define CAP_DP_B_SHIFT 25 +#define CAP_DP_B_DEFAULT_MASK (1 << 24) +#define CAP_DP_B_FALSE (0 << 24) +#define CAP_DP_B_TRUE (1 << 24) +#define PWR 0x15 +#define PWR_SETTING_NEW_SHIFT 31 +#define PWR_SETTING_NEW_DEFAULT_MASK (1 << 31) +#define PWR_SETTING_NEW_DONE (0 << 31) +#define PWR_SETTING_NEW_PENDING (1 << 31) +#define PWR_SETTING_NEW_TRIGGER (1 << 31) +#define PWR_MODE_SHIFT 28 +#define PWR_MODE_DEFAULT_MASK (1 << 28) +#define PWR_MODE_NORMAL (0 << 28) +#define PWR_MODE_SAFE (1 << 28) +#define PWR_HALT_DELAY_SHIFT 24 +#define PWR_HALT_DELAY_DEFAULT_MASK (1 << 24) +#define PWR_HALT_DELAY_DONE (0 << 24) +#define PWR_HALT_DELAY_ACTIVE (1 << 24) +#define PWR_SAFE_START_SHIFT 17 +#define PWR_SAFE_START_DEFAULT_MASK (1 << 17) +#define PWR_SAFE_START_NORMAL (0 << 17) +#define PWR_SAFE_START_ALT (1 << 17) +#define PWR_SAFE_STATE_SHIFT 16 +#define PWR_SAFE_STATE_DEFAULT_MASK (1 << 16) +#define PWR_SAFE_STATE_PD (0 << 16) +#define PWR_SAFE_STATE_PU (1 << 16) +#define PWR_NORMAL_START_SHIFT 1 +#define PWR_NORMAL_START_DEFAULT_MASK (1 << 1) +#define PWR_NORMAL_START_NORMAL (0 << 16) +#define PWR_NORMAL_START_ALT (1 << 16) +#define PWR_NORMAL_STATE_SHIFT 0 +#define PWR_NORMAL_STATE_DEFAULT_MASK 0x1 +#define PWR_NORMAL_STATE_PD 0 +#define PWR_NORMAL_STATE_PU 1 +#define TEST 0x16 +#define TEST_TESTMUX_SHIFT 24 +#define TEST_TESTMUX_DEFAULT_MASK (0xff << 24) +#define TEST_TESTMUX_AVSS (0 << 24) +#define TEST_TESTMUX_CLOCKIN (2 << 24) +#define TEST_TESTMUX_PLL_VOL (4 << 24) +#define TEST_TESTMUX_SLOWCLKINT (8 << 24) +#define TEST_TESTMUX_AVDD (16 << 24) +#define TEST_TESTMUX_VDDREG (32 << 24) +#define TEST_TESTMUX_REGREF_VDDREG (64 << 24) +#define TEST_TESTMUX_REGREF_AVDD (128 << 24) +#define TEST_CRC_SHIFT 23 +#define TEST_CRC_PRE_SERIALIZE (0 << 23) +#define TEST_CRC_POST_DESERIALIZE (1 << 23) +#define TEST_TPAT_SHIFT 20 +#define TEST_TPAT_DEFAULT_MASK (7 << 20) +#define TEST_TPAT_LO (0 << 20) +#define TEST_TPAT_TDAT (1 << 20) +#define TEST_TPAT_RAMP (2 << 20) +#define TEST_TPAT_WALK (3 << 20) +#define TEST_TPAT_MAXSTEP (4 << 20) +#define TEST_TPAT_MINSTEP (5 << 20) +#define TEST_DSRC_SHIFT 16 +#define TEST_DSRC_DEFAULT_MASK (3 << 16) +#define TEST_DSRC_NORMAL (0 << 16) +#define TEST_DSRC_DEBUG (1 << 16) +#define TEST_DSRC_TGEN (2 << 16) +#define TEST_HEAD_NUMBER_SHIFT 12 +#define TEST_HEAD_NUMBER_DEFAULT_MASK (3 << 12) +#define TEST_HEAD_NUMBER_NONE (0 << 12) +#define TEST_HEAD_NUMBER_HEAD0 (1 << 12) +#define TEST_HEAD_NUMBER_HEAD1 (2 << 12) +#define TEST_ATTACHED_SHIFT 10 +#define TEST_ATTACHED_DEFAULT_MASK (1 << 10) +#define TEST_ATTACHED_FALSE (0 << 10) +#define TEST_ATTACHED_TRUE (1 << 10) +#define TEST_ACT_HEAD_OPMODE_SHIFT 8 +#define TEST_ACT_HEAD_OPMODE_DEFAULT_MASK (3 << 8) +#define TEST_ACT_HEAD_OPMODE_SLEEP (0 << 8) +#define TEST_ACT_HEAD_OPMODE_SNOOZE (1 << 8) +#define TEST_ACT_HEAD_OPMODE_AWAKE (2 << 8) +#define TEST_INVD_SHIFT 6 +#define TEST_INVD_DISABLE (0 << 6) +#define TEST_INVD_ENABLE (1 << 6) +#define TEST_TEST_ENABLE_SHIFT 1 +#define TEST_TEST_ENABLE_DISABLE (0 << 1) +#define TEST_TEST_ENABLE_ENABLE (1 << 1) +#define PLL0 0x17 +#define PLL0_ICHPMP_SHFIT 24 +#define PLL0_ICHPMP_DEFAULT_MASK (0xf << 24) +#define PLL0_VCOCAP_SHIFT 8 +#define PLL0_VCOCAP_DEFAULT_MASK (0xf << 8) +#define PLL0_PLLREG_LEVEL_SHIFT 6 +#define PLL0_PLLREG_LEVEL_DEFAULT_MASK (3 << 6) +#define PLL0_PLLREG_LEVEL_V25 (0 << 6) +#define PLL0_PLLREG_LEVEL_V15 (1 << 6) +#define PLL0_PLLREG_LEVEL_V35 (2 << 6) +#define PLL0_PLLREG_LEVEL_V45 (3 << 6) +#define PLL0_PULLDOWN_SHIFT 5 +#define PLL0_PULLDOWN_DEFAULT_MASK (1 << 5) +#define PLL0_PULLDOWN_DISABLE (0 << 5) +#define PLL0_PULLDOWN_ENABLE (1 << 5) +#define PLL0_RESISTORSEL_SHIFT 4 +#define PLL0_RESISTORSEL_DEFAULT_MASK (1 << 4) +#define PLL0_RESISTORSEL_INT (0 << 4) +#define PLL0_RESISTORSEL_EXT (1 << 4) +#define PLL0_VCOPD_SHIFT 2 +#define PLL0_VCOPD_MASK (1 << 2) +#define PLL0_VCOPD_RESCIND (0 << 2) +#define PLL0_VCOPD_ASSERT (1 << 2) +#define PLL0_PWR_SHIFT 0 +#define PLL0_PWR_MASK 1 +#define PLL0_PWR_ON 0 +#define PLL0_PWR_OFF 1 +#define PLL1_TMDS_TERM_SHIFT 8 +#define PLL1_TMDS_TERM_DISABLE (0 << 8) +#define PLL1_TMDS_TERM_ENABLE (1 << 8) +#define PLL1 0x18 +#define PLL1_TERM_COMPOUT_SHIFT 15 +#define PLL1_TERM_COMPOUT_LOW (0 << 15) +#define PLL1_TERM_COMPOUT_HIGH (1 << 15) +#define PLL2 0x19 +#define PLL2_DCIR_PLL_RESET_SHIFT 0 +#define PLL2_DCIR_PLL_RESET_OVERRIDE (0 << 0) +#define PLL2_DCIR_PLL_RESET_ALLOW (1 << 0) +#define PLL2_AUX1_SHIFT 17 +#define PLL2_AUX1_SEQ_MASK (1 << 17) +#define PLL2_AUX1_SEQ_PLLCAPPD_ALLOW (0 << 17) +#define PLL2_AUX1_SEQ_PLLCAPPD_OVERRIDE (1 << 17) +#define PLL2_AUX2_SHIFT 18 +#define PLL2_AUX2_MASK (1 << 18) +#define PLL2_AUX2_OVERRIDE_POWERDOWN (0 << 18) +#define PLL2_AUX2_ALLOW_POWERDOWN (1 << 18) +#define PLL2_AUX6_SHIFT 22 +#define PLL2_AUX6_BANDGAP_POWERDOWN_MASK (1 << 22) +#define PLL2_AUX6_BANDGAP_POWERDOWN_DISABLE (0 << 22) +#define PLL2_AUX6_BANDGAP_POWERDOWN_ENABLE (1 << 22) +#define PLL2_AUX7_SHIFT 23 +#define PLL2_AUX7_PORT_POWERDOWN_MASK (1 << 23) +#define PLL2_AUX7_PORT_POWERDOWN_DISABLE (0 << 23) +#define PLL2_AUX7_PORT_POWERDOWN_ENABLE (1 << 23) +#define PLL2_AUX8_SHIFT 24 +#define PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK (1 << 24) +#define PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE (0 << 24) +#define PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_ENABLE (1 << 24) +#define PLL2_AUX9_SHIFT 25 +#define PLL2_AUX9_LVDSEN_ALLOW (0 << 25) +#define PLL2_AUX9_LVDSEN_OVERRIDE (1 << 25) +#define PLL3 0x1a +#define PLL3_PLLVDD_MODE_SHIFT 13 +#define PLL3_PLLVDD_MODE_MASK (1 << 13) +#define PLL3_PLLVDD_MODE_V1_8 (0 << 13) +#define PLL3_PLLVDD_MODE_V3_3 (1 << 13) +#define CSTM 0x1b +#define CSTM_ROTDAT_SHIFT 28 +#define CSTM_ROTDAT_DEFAULT_MASK (7 << 28) +#define CSTM_ROTCLK_SHIFT 24 +#define CSTM_ROTCLK_DEFAULT_MASK (0xf << 24) +#define CSTM_LVDS_EN_SHIFT 16 +#define CSTM_LVDS_EN_DISABLE (0 << 16) +#define CSTM_LVDS_EN_ENABLE (1 << 16) +#define CSTM_LINKACTB_SHIFT 15 +#define CSTM_LINKACTB_DISABLE (0 << 15) +#define CSTM_LINKACTB_ENABLE (1 << 15) +#define CSTM_LINKACTA_SHIFT 14 +#define CSTM_LINKACTA_DISABLE (0 << 14) +#define CSTM_LINKACTA_ENABLE (1 << 14) +#define LVDS 0x1c +#define LVDS_ROTDAT_SHIFT 28 +#define LVDS_ROTDAT_DEFAULT_MASK (7 << 28) +#define LVDS_ROTDAT_RST (0 << 28) +#define LVDS_ROTCLK_SHIFT 24 +#define LVDS_ROTCLK_DEFAULT_MASK (0xf << 24) +#define LVDS_ROTCLK_RST (0 << 24) +#define LVDS_PLLDIV_SHIFT 21 +#define LVDS_PLLDIV_DEFAULT_MASK (1 << 21) +#define LVDS_PLLDIV_BY_7 (0 << 21) +#define LVDS_BALANCED_SHIFT 19 +#define LVDS_BALANCED_DEFAULT_MASK (1 << 19) +#define LVDS_BALANCED_DISABLE (0 << 19) +#define LVDS_BALANCED_ENABLE (1 << 19) +#define LVDS_NEW_MODE_SHIFT 18 +#define LVDS_NEW_MODE_DEFAULT_MASK (1 << 18) +#define LVDS_NEW_MODE_DISABLE (0 << 18) +#define LVDS_NEW_MODE_ENABLE (1 << 18) +#define LVDS_DUP_SYNC_SHIFT 17 +#define LVDS_DUP_SYNC_DEFAULT_MASK (1 << 17) +#define LVDS_DUP_SYNC_DISABLE (0 << 17) +#define LVDS_DUP_SYNC_ENABLE (1 << 17) +#define LVDS_LVDS_EN_SHIFT 16 +#define LVDS_LVDS_EN_DEFAULT_MASK (1 << 16) +#define LVDS_LVDS_EN_ENABLE (1 << 16) +#define LVDS_LINKACTB_SHIFT 15 +#define LVDS_LINKACTB_DEFAULT_MASK (1 << 15) +#define LVDS_LINKACTB_DISABLE (0 << 15) +#define LVDS_LINKACTB_ENABLE (1 << 15) +#define LVDS_LINKACTA_SHIFT 14 +#define LVDS_LINKACTA_DEFAULT_MASK (1 << 14) +#define LVDS_LINKACTA_ENABLE (1 << 14) +#define LVDS_MODE_SHIFT 12 +#define LVDS_MODE_DEFAULT_MASK (3 << 12) +#define LVDS_MODE_LVDS (0 << 12) +#define LVDS_UPPER_SHIFT 11 +#define LVDS_UPPER_DEFAULT_MASK (1 << 11) +#define LVDS_UPPER_FALSE (0 << 11) +#define LVDS_UPPER_TRUE (1 << 11) +#define LVDS_PD_TXCB_SHIFT 9 +#define LVDS_PD_TXCB_DEFAULT_MASK (1 << 9) +#define LVDS_PD_TXCB_ENABLE (0 << 9) +#define LVDS_PD_TXCB_DISABLE (1 << 9) +#define LVDS_PD_TXCA_SHIFT 8 +#define LVDS_PD_TXCA_DEFAULT_MASK (1 << 8) +#define LVDS_PD_TXCA_ENABLE (0 << 8) +#define LVDS_PD_TXDB_3_SHIFT 7 +#define LVDS_PD_TXDB_3_DEFAULT_MASK (1 << 7) +#define LVDS_PD_TXDB_3_ENABLE (0 << 7) +#define LVDS_PD_TXDB_3_DISABLE (1 << 7) +#define LVDS_PD_TXDB_2_SHIFT 6 +#define LVDS_PD_TXDB_2_DEFAULT_MASK (1 << 6) +#define LVDS_PD_TXDB_2_ENABLE (0 << 6) +#define LVDS_PD_TXDB_2_DISABLE (1 << 6) +#define LVDS_PD_TXDB_1_SHIFT 5 +#define LVDS_PD_TXDB_1_DEFAULT_MASK (1 << 5) +#define LVDS_PD_TXDB_1_ENABLE (0 << 5) +#define LVDS_PD_TXDB_1_DISABLE (1 << 5) +#define LVDS_PD_TXDB_0_SHIFT 4 +#define LVDS_PD_TXDB_0_DEFAULT_MASK (1 << 4) +#define LVDS_PD_TXDB_0_ENABLE (0 << 4) +#define LVDS_PD_TXDB_0_DISABLE (1 << 4) +#define LVDS_PD_TXDA_3_SHIFT 3 +#define LVDS_PD_TXDA_3_DEFAULT_MASK (1 << 3) +#define LVDS_PD_TXDA_3_ENABLE (0 << 3) +#define LVDS_PD_TXDA_3_DISABLE (1 << 3) +#define LVDS_PD_TXDA_2_SHIFT 2 +#define LVDS_PD_TXDA_2_DEFAULT_MASK (1 << 2) +#define LVDS_PD_TXDA_2_ENABLE (0 << 2) +#define LVDS_PD_TXDA_1_SHIFT 1 +#define LVDS_PD_TXDA_1_DEFAULT_MASK (1 << 1) +#define LVDS_PD_TXDA_1_ENABLE (0 << 1) +#define LVDS_PD_TXDA_0_SHIFT 0 +#define LVDS_PD_TXDA_0_DEFAULT_MASK 0x1 +#define LVDS_PD_TXDA_0_ENABLE 0 +#define CRCA 0x1d +#define CRCA_VALID_FALSE 0 +#define CRCA_VALID_TRUE 1 +#define CRCA_VALID_RST 1 +#define CRCB 0x1e +#define CRCB_CRC_DEFAULT_MASK 0xffffffff +#define SEQ_CTL 0x20 +#define SEQ_CTL_SWITCH_SHIFT 30 +#define SEQ_CTL_SWITCH_MASK (1 << 30) +#define SEQ_CTL_SWITCH_WAIT (0 << 30) +#define SEQ_CTL_SWITCH_FORCE (1 << 30) +#define SEQ_CTL_STATUS_SHIFT 28 +#define SEQ_CTL_STATUS_MASK (1 << 28) +#define SEQ_CTL_STATUS_STOPPED (0 << 28) +#define SEQ_CTL_STATUS_RUNNING (1 << 28) +#define SEQ_CTL_PC_SHIFT 16 +#define SEQ_CTL_PC_MASK (0xf << 16) +#define SEQ_CTL_PD_PC_ALT_SHIFT 12 +#define SEQ_CTL_PD_PC_ALT_MASK (0xf << 12) +#define SEQ_CTL_PD_PC_SHIFT 8 +#define SEQ_CTL_PD_PC_MASK (0xf << 8) +#define SEQ_CTL_PU_PC_ALT_SHIFT 4 +#define SEQ_CTL_PU_PC_ALT_MASK (0xf << 4) +#define SEQ_CTL_PU_PC_SHIFT 0 +#define SEQ_CTL_PU_PC_MASK 0xf +#define LANE_SEQ_CTL 0x21 +#define LANE_SEQ_CTL_SETTING_NEW_SHIFT 31 +#define LANE_SEQ_CTL_SETTING_MASK (1 << 31) +#define LANE_SEQ_CTL_SETTING_NEW_DONE (0 << 31) +#define LANE_SEQ_CTL_SETTING_NEW_PENDING (1 << 31) +#define LANE_SEQ_CTL_SETTING_NEW_TRIGGER (1 << 31) +#define LANE_SEQ_CTL_SEQ_STATE_SHIFT 28 +#define LANE_SEQ_CTL_SEQ_STATE_IDLE (0 << 28) +#define LANE_SEQ_CTL_SEQ_STATE_BUSY (1 << 28) +#define LANE_SEQ_CTL_SEQUENCE_SHIFT 20 +#define LANE_SEQ_CTL_SEQUENCE_UP (0 << 20) +#define LANE_SEQ_CTL_SEQUENCE_DOWN (1 << 20) +#define LANE_SEQ_CTL_NEW_POWER_STATE_SHIFT 16 +#define LANE_SEQ_CTL_NEW_POWER_STATE_PU (0 << 16) +#define LANE_SEQ_CTL_NEW_POWER_STATE_PD (1 << 16) +#define LANE_SEQ_CTL_DELAY_SHIFT 12 +#define LANE_SEQ_CTL_DELAY_DEFAULT_MASK (0xf << 12) +#define LANE_SEQ_CTL_LANE9_STATE_SHIFT 9 +#define LANE_SEQ_CTL_LANE9_STATE_POWERUP (0 << 9) +#define LANE_SEQ_CTL_LANE9_STATE_POWERDOWN (1 << 9) +#define LANE_SEQ_CTL_LANE8_STATE_SHIFT 8 +#define LANE_SEQ_CTL_LANE8_STATE_POWERUP (0 << 8) +#define LANE_SEQ_CTL_LANE8_STATE_POWERDOWN (1 << 8) +#define LANE_SEQ_CTL_LANE7_STATE_SHIFT 7 +#define LANE_SEQ_CTL_LANE7_STATE_POWERUP (0 << 7) +#define LANE_SEQ_CTL_LANE7_STATE_POWERDOWN (1 << 7) +#define LANE_SEQ_CTL_LANE6_STATE_SHIFT 6 +#define LANE_SEQ_CTL_LANE6_STATE_POWERUP (0 << 6) +#define LANE_SEQ_CTL_LANE6_STATE_POWERDOWN (1 << 6) +#define LANE_SEQ_CTL_LANE5_STATE_SHIFT 5 +#define LANE_SEQ_CTL_LANE5_STATE_POWERUP (0 << 5) +#define LANE_SEQ_CTL_LANE5_STATE_POWERDOWN (1 << 5) +#define LANE_SEQ_CTL_LANE4_STATE_SHIFT 4 +#define LANE_SEQ_CTL_LANE4_STATE_POWERUP (0 << 4) +#define LANE_SEQ_CTL_LANE4_STATE_POWERDOWN (1 << 4) +#define LANE_SEQ_CTL_LANE3_STATE_SHIFT 3 +#define LANE_SEQ_CTL_LANE3_STATE_POWERUP (0 << 3) +#define LANE_SEQ_CTL_LANE3_STATE_POWERDOWN (1 << 3) +#define LANE_SEQ_CTL_LANE2_STATE_SHIFT 2 +#define LANE_SEQ_CTL_LANE2_STATE_POWERUP (0 << 2) +#define LANE_SEQ_CTL_LANE2_STATE_POWERDOWN (1 << 2) +#define LANE_SEQ_CTL_LANE1_STATE_SHIFT 1 +#define LANE_SEQ_CTL_LANE1_STATE_POWERUP (0 << 1) +#define LANE_SEQ_CTL_LANE1_STATE_POWERDOWN (1 << 1) +#define LANE_SEQ_CTL_LANE0_STATE_SHIFT 0 +#define LANE_SEQ_CTL_LANE0_STATE_POWERUP 0 +#define LANE_SEQ_CTL_LANE0_STATE_POWERDOWN 1 +#define SEQ_INST(i) (0x22 + i) +#define SEQ_INST_PLL_PULLDOWN_SHIFT 31 +#define SEQ_INST_PLL_PULLDOWN_DISABLE (0 << 31) +#define SEQ_INST_PLL_PULLDOWN_ENABLE (1 << 31) +#define SEQ_INST_POWERDOWN_MACRO_SHIFT 30 +#define SEQ_INST_POWERDOWN_MACRO_NORMAL (0 << 30) +#define SEQ_INST_POWERDOWN_MACRO_POWERDOWN (1 << 30) +#define SEQ_INST_ASSERT_PLL_RESET_SHIFT 29 +#define SEQ_INST_ASSERT_PLL_RESET_NORMAL (0 << 29) +#define SEQ_INST_ASSERT_PLL_RESET_RST (1 << 29) +#define SEQ_INST_BLANK_V_SHIFT 28 +#define SEQ_INST_BLANK_V_NORMAL (0 << 28) +#define SEQ_INST_BLANK_V_INACTIVE (1 << 28) +#define SEQ_INST_BLANK_H_SHIFT 27 +#define SEQ_INST_BLANK_H_NORMAL (0 << 27) +#define SEQ_INST_BLANK_H_INACTIVE (1 << 27) +#define SEQ_INST_BLANK_DE_SHIFT 26 +#define SEQ_INST_BLANK_DE_NORMAL (0 << 26) +#define SEQ_INST_BLANK_DE_INACTIVE (1 << 26) +#define SEQ_INST_BLACK_DATA_SHIFT 25 +#define SEQ_INST_BLACK_DATA_NORMAL (0 << 25) +#define SEQ_INST_BLACK_DATA_BLACK (1 << 25) +#define SEQ_INST_TRISTATE_IOS_SHIFT 24 +#define SEQ_INST_TRISTATE_IOS_ENABLE_PINS (0 << 24) +#define SEQ_INST_TRISTATE_IOS_TRISTATE (1 << 24) +#define SEQ_INST_DRIVE_PWM_OUT_LO_SHIFT 23 +#define SEQ_INST_DRIVE_PWM_OUT_LO_FALSE (0 << 23) +#define SEQ_INST_DRIVE_PWM_OUT_LO_TRUE (1 << 23) +#define SEQ_INST_PIN_B_SHIFT 22 +#define SEQ_INST_PIN_B_LOW (0 << 22) +#define SEQ_INST_PIN_B_HIGH (1 << 22) +#define SEQ_INST_PIN_A_SHIFT 21 +#define SEQ_INST_PIN_A_LOW (0 << 21) +#define SEQ_INST_PIN_A_HIGH (1 << 21) +#define SEQ_INST_SEQUENCE_SHIFT 19 +#define SEQ_INST_SEQUENCE_UP (0 << 19) +#define SEQ_INST_SEQUENCE_DOWN (1 << 19) +#define SEQ_INST_LANE_SEQ_SHIFT 18 +#define SEQ_INST_LANE_SEQ_STOP (0 << 18) +#define SEQ_INST_LANE_SEQ_RUN (1 << 18) +#define SEQ_INST_PDPORT_SHIFT 17 +#define SEQ_INST_PDPORT_NO (0 << 17) +#define SEQ_INST_PDPORT_YES (1 << 17) +#define SEQ_INST_PDPLL_SHIFT 16 +#define SEQ_INST_PDPLL_NO (0 << 16) +#define SEQ_INST_PDPLL_YES (1 << 16) +#define SEQ_INST_HALT_SHIFT 15 +#define SEQ_INST_HALT_FALSE (0 << 15) +#define SEQ_INST_HALT_TRUE (1 << 15) +#define SEQ_INST_WAIT_UNITS_SHIFT 12 +#define SEQ_INST_WAIT_UNITS_DEFAULT_MASK (3 << 12) +#define SEQ_INST_WAIT_UNITS_US (0 << 12) +#define SEQ_INST_WAIT_UNITS_MS (1 << 12) +#define SEQ_INST_WAIT_UNITS_VSYNC (2 << 12) +#define SEQ_INST_WAIT_TIME_SHIFT 0 +#define SEQ_INST_WAIT_TIME_DEFAULT_MASK 0x3ff +#define PWM_DIV 0x32 +#define PWM_DIV_DIVIDE_DEFAULT_MASK 0xffffff +#define PWM_CTL 0x33 +#define PWM_CTL_SETTING_NEW_SHIFT 31 +#define PWM_CTL_SETTING_NEW_DONE (0 << 31) +#define PWM_CTL_SETTING_NEW_PENDING (1 << 31) +#define PWM_CTL_SETTING_NEW_TRIGGER (1 << 31) +#define PWM_CTL_CLKSEL_SHIFT 30 +#define PWM_CTL_CLKSEL_PCLK (0 << 30) +#define PWM_CTL_CLKSEL_XTAL (1 << 30) +#define PWM_CTL_DUTY_CYCLE_SHIFT 0 +#define PWM_CTL_DUTY_CYCLE_MASK 0xffffff +#define MSCHECK 0x49 +#define MSCHECK_CTL_SHIFT 31 +#define MSCHECK_CTL_CLEAR (0 << 31) +#define MSCHECK_CTL_RUN (1 << 31) +#define XBAR_CTRL 0x4a +#define DP_LINKCTL(i) (0x4c + (i)) +#define DP_LINKCTL_FORCE_IDLEPTTRN_SHIFT 31 +#define DP_LINKCTL_FORCE_IDLEPTTRN_NO (0 << 31) +#define DP_LINKCTL_FORCE_IDLEPTTRN_YES (1 << 31) +#define DP_LINKCTL_COMPLIANCEPTTRN_SHIFT 28 +#define DP_LINKCTL_COMPLIANCEPTTRN_NOPATTERN (0 << 28) +#define DP_LINKCTL_COMPLIANCEPTTRN_COLORSQARE (1 << 28) +#define DP_LINKCTL_LANECOUNT_SHIFT 16 +#define DP_LINKCTL_LANECOUNT_MASK (0x1f << 16) +#define DP_LINKCTL_LANECOUNT_ZERO (0 << 16) +#define DP_LINKCTL_LANECOUNT_ONE (1 << 16) +#define DP_LINKCTL_LANECOUNT_TWO (3 << 16) +#define DP_LINKCTL_LANECOUNT_FOUR (15 << 16) +#define DP_LINKCTL_ENHANCEDFRAME_SHIFT 14 +#define DP_LINKCTL_ENHANCEDFRAME_DISABLE (0 << 14) +#define DP_LINKCTL_ENHANCEDFRAME_ENABLE (1 << 14) +#define DP_LINKCTL_SYNCMODE_SHIFT 10 +#define DP_LINKCTL_SYNCMODE_DISABLE (0 << 10) +#define DP_LINKCTL_SYNCMODE_ENABLE (1 << 10) +#define DP_LINKCTL_TUSIZE_SHIFT 2 +#define DP_LINKCTL_TUSIZE_MASK (0x7f << 2) +#define DP_LINKCTL_ENABLE_SHIFT 0 +#define DP_LINKCTL_ENABLE_NO 0 +#define DP_LINKCTL_ENABLE_YES 1 +#define DC(i) (0x4e + (i)) +#define DC_LANE3_DP_LANE3_SHIFT 24 +#define DC_LANE3_DP_LANE3_MASK (0xff << 24) +#define DC_LANE3_DP_LANE3_P0_LEVEL0 (17 << 24) +#define DC_LANE3_DP_LANE3_P1_LEVEL0 (21 << 24) +#define DC_LANE3_DP_LANE3_P2_LEVEL0 (26 << 24) +#define DC_LANE3_DP_LANE3_P3_LEVEL0 (34 << 24) +#define DC_LANE3_DP_LANE3_P0_LEVEL1 (26 << 24) +#define DC_LANE3_DP_LANE3_P1_LEVEL1 (32 << 24) +#define DC_LANE3_DP_LANE3_P2_LEVEL1 (39 << 24) +#define DC_LANE3_DP_LANE3_P0_LEVEL2 (34 << 24) +#define DC_LANE3_DP_LANE3_P1_LEVEL2 (43 << 24) +#define DC_LANE3_DP_LANE3_P0_LEVEL3 (51 << 24) +#define DC_LANE2_DP_LANE0_SHIFT 16 +#define DC_LANE2_DP_LANE0_MASK (0xff << 16) +#define DC_LANE2_DP_LANE0_P0_LEVEL0 (17 << 16) +#define DC_LANE2_DP_LANE0_P1_LEVEL0 (21 << 16) +#define DC_LANE2_DP_LANE0_P2_LEVEL0 (26 << 16) +#define DC_LANE2_DP_LANE0_P3_LEVEL0 (34 << 16) +#define DC_LANE2_DP_LANE0_P0_LEVEL1 (26 << 16) +#define DC_LANE2_DP_LANE0_P1_LEVEL1 (32 << 16) +#define DC_LANE2_DP_LANE0_P2_LEVEL1 (39 << 16) +#define DC_LANE2_DP_LANE0_P0_LEVEL2 (34 << 16) +#define DC_LANE2_DP_LANE0_P1_LEVEL2 (43 << 16) +#define DC_LANE2_DP_LANE0_P0_LEVEL3 (51 << 16) +#define DC_LANE1_DP_LANE1_SHIFT 8 +#define DC_LANE1_DP_LANE1_MASK (0xff << 8) +#define DC_LANE1_DP_LANE1_P0_LEVEL0 (17 << 8) +#define DC_LANE1_DP_LANE1_P1_LEVEL0 (21 << 8) +#define DC_LANE1_DP_LANE1_P2_LEVEL0 (26 << 8) +#define DC_LANE1_DP_LANE1_P3_LEVEL0 (34 << 8) +#define DC_LANE1_DP_LANE1_P0_LEVEL1 (26 << 8) +#define DC_LANE1_DP_LANE1_P1_LEVEL1 (32 << 8) +#define DC_LANE1_DP_LANE1_P2_LEVEL1 (39 << 8) +#define DC_LANE1_DP_LANE1_P0_LEVEL2 (34 << 8) +#define DC_LANE1_DP_LANE1_P1_LEVEL2 (43 << 8) +#define DC_LANE1_DP_LANE1_P0_LEVEL3 (51 << 8) +#define DC_LANE0_DP_LANE2_SHIFT 0 +#define DC_LANE0_DP_LANE2_MASK 0xff +#define DC_LANE0_DP_LANE2_P0_LEVEL0 17 +#define DC_LANE0_DP_LANE2_P1_LEVEL0 21 +#define DC_LANE0_DP_LANE2_P2_LEVEL0 26 +#define DC_LANE0_DP_LANE2_P3_LEVEL0 34 +#define DC_LANE0_DP_LANE2_P0_LEVEL1 26 +#define DC_LANE0_DP_LANE2_P1_LEVEL1 32 +#define DC_LANE0_DP_LANE2_P2_LEVEL1 39 +#define DC_LANE0_DP_LANE2_P0_LEVEL2 34 +#define DC_LANE0_DP_LANE2_P1_LEVEL2 43 +#define DC_LANE0_DP_LANE2_P0_LEVEL3 51 +#define LANE_DRIVE_CURRENT(i) (0x4e + (i)) +#define PR(i) (0x52 + (i)) +#define PR_LANE3_DP_LANE3_SHIFT 24 +#define PR_LANE3_DP_LANE3_MASK (0xff << 24) +#define PR_LANE3_DP_LANE3_D0_LEVEL0 (0 << 24) +#define PR_LANE3_DP_LANE3_D1_LEVEL0 (0 << 24) +#define PR_LANE3_DP_LANE3_D2_LEVEL0 (0 << 24) +#define PR_LANE3_DP_LANE3_D3_LEVEL0 (0 << 24) +#define PR_LANE3_DP_LANE3_D0_LEVEL1 (4 << 24) +#define PR_LANE3_DP_LANE3_D1_LEVEL1 (6 << 24) +#define PR_LANE3_DP_LANE3_D2_LEVEL1 (17 << 24) +#define PR_LANE3_DP_LANE3_D0_LEVEL2 (8 << 24) +#define PR_LANE3_DP_LANE3_D1_LEVEL2 (13 << 24) +#define PR_LANE3_DP_LANE3_D0_LEVEL3 (17 << 24) +#define PR_LANE2_DP_LANE0_SHIFT 16 +#define PR_LANE2_DP_LANE0_MASK (0xff << 16) +#define PR_LANE2_DP_LANE0_D0_LEVEL0 (0 << 16) +#define PR_LANE2_DP_LANE0_D1_LEVEL0 (0 << 16) +#define PR_LANE2_DP_LANE0_D2_LEVEL0 (0 << 16) +#define PR_LANE2_DP_LANE0_D3_LEVEL0 (0 << 16) +#define PR_LANE2_DP_LANE0_D0_LEVEL1 (4 << 16) +#define PR_LANE2_DP_LANE0_D1_LEVEL1 (6 << 16) +#define PR_LANE2_DP_LANE0_D2_LEVEL1 (17 << 16) +#define PR_LANE2_DP_LANE0_D0_LEVEL2 (8 << 16) +#define PR_LANE2_DP_LANE0_D1_LEVEL2 (13 << 16) +#define PR_LANE2_DP_LANE0_D0_LEVEL3 (17 << 16) +#define PR_LANE1_DP_LANE1_SHIFT 8 +#define PR_LANE1_DP_LANE1_MASK (0xff >> 8) +#define PR_LANE1_DP_LANE1_D0_LEVEL0 (0 >> 8) +#define PR_LANE1_DP_LANE1_D1_LEVEL0 (0 >> 8) +#define PR_LANE1_DP_LANE1_D2_LEVEL0 (0 >> 8) +#define PR_LANE1_DP_LANE1_D3_LEVEL0 (0 >> 8) +#define PR_LANE1_DP_LANE1_D0_LEVEL1 (4 >> 8) +#define PR_LANE1_DP_LANE1_D1_LEVEL1 (6 >> 8) +#define PR_LANE1_DP_LANE1_D2_LEVEL1 (17 >> 8) +#define PR_LANE1_DP_LANE1_D0_LEVEL2 (8 >> 8) +#define PR_LANE1_DP_LANE1_D1_LEVEL2 (13 >> 8) +#define PR_LANE1_DP_LANE1_D0_LEVEL3 (17 >> 8) +#define PR_LANE0_DP_LANE2_SHIFT 0 +#define PR_LANE0_DP_LANE2_MASK 0xff +#define PR_LANE0_DP_LANE2_D0_LEVEL0 0 +#define PR_LANE0_DP_LANE2_D1_LEVEL0 0 +#define PR_LANE0_DP_LANE2_D2_LEVEL0 0 +#define PR_LANE0_DP_LANE2_D3_LEVEL0 0 +#define PR_LANE0_DP_LANE2_D0_LEVEL1 4 +#define PR_LANE0_DP_LANE2_D1_LEVEL1 6 +#define PR_LANE0_DP_LANE2_D2_LEVEL1 17 +#define PR_LANE0_DP_LANE2_D0_LEVEL2 8 +#define PR_LANE0_DP_LANE2_D1_LEVEL2 13 +#define PR_LANE0_DP_LANE2_D0_LEVEL3 17 +#define LANE4_PREEMPHASIS(i) (0x54 + (i)) +#define POSTCURSOR(i) (0x56 + (i)) +#define DP_CONFIG(i) (0x58 + (i)) +#define DP_CONFIG_RD_RESET_VAL_SHIFT 31 +#define DP_CONFIG_RD_RESET_VAL_POSITIVE (0 << 31) +#define DP_CONFIG_RD_RESET_VAL_NEGATIVE (1 << 31) +#define DP_CONFIG_IDLE_BEFORE_ATTACH_SHIFT 28 +#define DP_CONFIG_IDLE_BEFORE_ATTACH_DISABLE (0 << 28) +#define DP_CONFIG_IDLE_BEFORE_ATTACH_ENABLE (1 << 28) +#define DP_CONFIG_ACTIVESYM_CNTL_SHIFT 26 +#define DP_CONFIG_ACTIVESYM_CNTL_DISABLE (0 << 26) +#define DP_CONFIG_ACTIVESYM_CNTL_ENABLE (1 << 26) +#define DP_CONFIG_ACTIVESYM_POLARITY_SHIFT 24 +#define DP_CONFIG_ACTIVESYM_POLARITY_NEGATIVE (0 << 24) +#define DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE (1 << 24) +#define DP_CONFIG_ACTIVESYM_FRAC_SHIFT 16 +#define DP_CONFIG_ACTIVESYM_FRAC_MASK (0xf << 16) +#define DP_CONFIG_ACTIVESYM_COUNT_SHIFT 8 +#define DP_CONFIG_ACTIVESYM_COUNT_MASK (0x7f << 8) +#define DP_CONFIG_WATERMARK_SHIFT 0 +#define DP_CONFIG_WATERMARK_MASK 0x3f +#define DP_MN(i) (0x5a + i) +#define DP_MN_M_MOD_SHIFT 30 +#define DP_MN_M_MOD_DEFAULT_MASK (3 << 30) +#define DP_MN_M_MOD_NONE (0 << 30) +#define DP_MN_M_MOD_INC (1 << 30) +#define DP_MN_M_MOD_DEC (2 << 30) +#define DP_MN_M_DELTA_SHIFT 24 +#define DP_MN_M_DELTA_DEFAULT_MASK (0xf << 24) +#define DP_MN_N_VAL_SHIFT 0 +#define DP_MN_N_VAL_DEFAULT_MASK 0xffffff +#define DP_PADCTL(i) (0x5c + (i)) +#define DP_PADCTL_SPARE_SHIFT 25 +#define DP_PADCTL_SPARE_DEFAULT_MASK (0x7f << 25) +#define DP_PADCTL_VCO_2X_SHIFT 24 +#define DP_PADCTL_VCO_2X_DISABLE (0 << 24) +#define DP_PADCTL_VCO_2X_ENABLE (1 << 24) +#define DP_PADCTL_PAD_CAL_PD_SHIFT 23 +#define DP_PADCTL_PAD_CAL_PD_POWERUP (0 << 23) +#define DP_PADCTL_PAD_CAL_PD_POWERDOWN (1 << 23) +#define DP_PADCTL_TX_PU_SHIFT 22 +#define DP_PADCTL_TX_PU_DISABLE (0 << 22) +#define DP_PADCTL_TX_PU_ENABLE (1 << 22) +#define DP_PADCTL_TX_PU_MASK (1 << 22) +#define DP_PADCTL_REG_CTRL_SHIFT 20 +#define DP_PADCTL_REG_CTRL_DEFAULT_MASK (3 << 20) +#define DP_PADCTL_VCMMODE_SHIFT 16 +#define DP_PADCTL_VCMMODE_DEFAULT_MASK (0xf << 16) +#define DP_PADCTL_VCMMODE_TRISTATE (0 << 16) +#define DP_PADCTL_VCMMODE_TEST_MUX (1 << 16) +#define DP_PADCTL_VCMMODE_WEAK_PULLDOWN (2 << 16) +#define DP_PADCTL_VCMMODE_STRONG_PULLDOWN (4 << 16) +#define DP_PADCTL_TX_PU_VALUE_SHIFT 8 +#define DP_PADCTL_TX_PU_VALUE_DEFAULT_MASK (0xff << 8) +#define DP_PADCTL_COMODE_TXD_3_DP_TXD_3_SHIFT 7 +#define DP_PADCTL_COMODE_TXD_3_DP_TXD_3_DISABLE (0 << 7) +#define DP_PADCTL_COMODE_TXD_3_DP_TXD_3_ENABLE (1 << 7) +#define DP_PADCTL_COMODE_TXD_2_DP_TXD_0_SHIFT 6 +#define DP_PADCTL_COMODE_TXD_2_DP_TXD_0_DISABLE (0 << 6) +#define DP_PADCTL_COMODE_TXD_2_DP_TXD_0_ENABLE (1 << 6) +#define DP_PADCTL_COMODE_TXD_1_DP_TXD_1_SHIFT 5 +#define DP_PADCTL_COMODE_TXD_1_DP_TXD_1_DISABLE (0 << 5) +#define DP_PADCTL_COMODE_TXD_1_DP_TXD_1_ENABLE (1 << 5) +#define DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT 4 +#define DP_PADCTL_COMODE_TXD_0_DP_TXD_2_DISABLE (0 << 4) +#define DP_PADCTL_COMODE_TXD_0_DP_TXD_2_ENABLE (1 << 4) +#define DP_PADCTL_PD_TXD_3_SHIFT 3 +#define DP_PADCTL_PD_TXD_3_YES (0 << 3) +#define DP_PADCTL_PD_TXD_3_NO (1 << 3) +#define DP_PADCTL_PD_TXD_0_SHIFT 2 +#define DP_PADCTL_PD_TXD_0_YES (0 << 2) +#define DP_PADCTL_PD_TXD_0_NO (1 << 2) +#define DP_PADCTL_PD_TXD_1_SHIFT 1 +#define DP_PADCTL_PD_TXD_1_YES (0 << 1) +#define DP_PADCTL_PD_TXD_1_NO (1 << 1) +#define DP_PADCTL_PD_TXD_2_SHIFT 0 +#define DP_PADCTL_PD_TXD_2_YES 0 +#define DP_PADCTL_PD_TXD_2_NO 1 +#define DP_DEBUG(i) (0x5e + i) +#define DP_SPARE(i) (0x60 + (i)) +#define DP_SPARE_REG_SHIFT 3 +#define DP_SPARE_REG_DEFAULT_MASK (0x1fffffff << 3) +#define DP_SPARE_SOR_CLK_SEL_SHIFT 2 +#define DP_SPARE_SOR_CLK_SEL_DEFAULT_MASK (1 << 2) +#define DP_SPARE_SOR_CLK_SEL_SAFE_SORCLK (0 << 2) +#define DP_SPARE_SOR_CLK_SEL_MACRO_SORCLK (1 << 2) +#define DP_SPARE_PANEL_SHIFT 1 +#define DP_SPARE_PANEL_EXTERNAL (0 << 1) +#define DP_SPARE_PANEL_INTERNAL (1 << 1) +#define DP_SPARE_SEQ_ENABLE_SHIFT 0 +#define DP_SPARE_SEQ_ENABLE_NO 0 +#define DP_SPARE_SEQ_ENABLE_YES 1 +#define DP_AUDIO_CTRL 0x62 +#define DP_AUDIO_HBLANK_SYMBOLS 0x63 +#define DP_AUDIO_HBLANK_SYMBOLS_MASK 0x1ffff +#define DP_AUDIO_HBLANK_SYMBOLS_VALUE_SHIFT 0 +#define DP_AUDIO_VBLANK_SYMBOLS 0x64 +#define DP_AUDIO_VBLANK_SYMBOLS_MASK 0x1ffff +#define DP_AUDIO_VBLANK_SYMBOLS_SHIFT 0 +#define DP_GENERIC_INFOFRAME_HEADER 0x65 +#define DP_GENERIC_INFOFRAME_SUBPACK(i) (0x66 + (i)) +#define DP_TPG 0x6d +#define DP_TPG_LANE3_CHANNELCODING_SHIFT 30 +#define DP_TPG_LANE3_CHANNELCODING_DISABLE (0 << 30) +#define DP_TPG_LANE3_CHANNELCODING_ENABLE (1 << 30) +#define DP_TPG_LANE3_SCRAMBLEREN_SHIFT 28 +#define DP_TPG_LANE3_SCRAMBLEREN_ENABLE_GALIOS (1 << 28) +#define DP_TPG_LANE3_SCRAMBLEREN_ENABLE_FIBONACCI (2 << 28) +#define DP_TPG_LANE3_PATTERN_SHIFT 24 +#define DP_TPG_LANE3_PATTERN_DEFAULT_MASK (0xf << 24) +#define DP_TPG_LANE3_PATTERN_NOPATTERN (0 << 24) +#define DP_TPG_LANE3_PATTERN_TRAINING1 (1 << 24) +#define DP_TPG_LANE3_PATTERN_TRAINING2 (2 << 24) +#define DP_TPG_LANE3_PATTERN_TRAINING3 (3 << 24) +#define DP_TPG_LANE3_PATTERN_D102 (4 << 24) +#define DP_TPG_LANE3_PATTERN_SBLERRRATE (5 << 24) +#define DP_TPG_LANE3_PATTERN_PRBS7 (6 << 24) +#define DP_TPG_LANE3_PATTERN_CSTM (7 << 24) +#define DP_TPG_LANE3_PATTERN_HBR2_COMPLIANCE (8 << 24) +#define DP_TPG_LANE2_CHANNELCODING_SHIFT 22 +#define DP_TPG_LANE2_CHANNELCODING_DISABLE (0 << 22) +#define DP_TPG_LANE2_CHANNELCODING_ENABLE (1 << 22) +#define DP_TPG_LANE2_SCRAMBLEREN_SHIFT 20 +#define DP_TPG_LANE2_SCRAMBLEREN_DEFAULT_MASK (3 << 20) +#define DP_TPG_LANE2_SCRAMBLEREN_DISABLE (0 << 20) +#define DP_TPG_LANE2_SCRAMBLEREN_ENABLE_GALIOS (1 << 20) +#define DP_TPG_LANE2_SCRAMBLEREN_ENABLE_FIBONACCI (2 << 20) +#define DP_TPG_LANE2_PATTERN_SHIFT 16 +#define DP_TPG_LANE2_PATTERN_DEFAULT_MASK (0xf << 16) +#define DP_TPG_LANE2_PATTERN_NOPATTERN (0 << 16) +#define DP_TPG_LANE2_PATTERN_TRAINING1 (1 << 16) +#define DP_TPG_LANE2_PATTERN_TRAINING2 (2 << 16) +#define DP_TPG_LANE2_PATTERN_TRAINING3 (3 << 16) +#define DP_TPG_LANE2_PATTERN_D102 (4 << 16) +#define DP_TPG_LANE2_PATTERN_SBLERRRATE (5 << 16) +#define DP_TPG_LANE2_PATTERN_PRBS7 (6 << 16) +#define DP_TPG_LANE2_PATTERN_CSTM (7 << 16) +#define DP_TPG_LANE2_PATTERN_HBR2_COMPLIANCE (8 << 16) +#define DP_TPG_LANE1_CHANNELCODING_SHIFT 14 +#define DP_TPG_LANE1_CHANNELCODING_DISABLE (0 << 14) +#define DP_TPG_LANE1_CHANNELCODING_ENABLE (1 << 14) +#define DP_TPG_LANE1_SCRAMBLEREN_SHIFT 12 +#define DP_TPG_LANE1_SCRAMBLEREN_DEFAULT_MASK (3 << 12) +#define DP_TPG_LANE1_SCRAMBLEREN_DISABLE (0 << 12) +#define DP_TPG_LANE1_SCRAMBLEREN_ENABLE_GALIOS (1 << 12) +#define DP_TPG_LANE1_SCRAMBLEREN_ENABLE_FIBONACCI (2 << 12) +#define DP_TPG_LANE1_PATTERN_SHIFT 8 +#define DP_TPG_LANE1_PATTERN_DEFAULT_MASK (0xf << 8) +#define DP_TPG_LANE1_PATTERN_NOPATTERN (0 << 8) +#define DP_TPG_LANE1_PATTERN_TRAINING1 (1 << 8) +#define DP_TPG_LANE1_PATTERN_TRAINING2 (2 << 8) +#define DP_TPG_LANE1_PATTERN_TRAINING3 (3 << 8) +#define DP_TPG_LANE1_PATTERN_D102 (4 << 8) +#define DP_TPG_LANE1_PATTERN_SBLERRRATE (5 << 8) +#define DP_TPG_LANE1_PATTERN_PRBS7 (6 << 8) +#define DP_TPG_LANE1_PATTERN_CSTM (7 << 8) +#define DP_TPG_LANE1_PATTERN_HBR2_COMPLIANCE (8 << 8) +#define DP_TPG_LANE0_CHANNELCODING_SHIFT 6 +#define DP_TPG_LANE0_CHANNELCODING_DISABLE (0 << 6) +#define DP_TPG_LANE0_CHANNELCODING_ENABLE (1 << 6) +#define DP_TPG_LANE0_SCRAMBLEREN_SHIFT 4 +#define DP_TPG_LANE0_SCRAMBLEREN_DEFAULT_MASK (3 << 4) +#define DP_TPG_LANE0_SCRAMBLEREN_DISABLE (0 << 4) +#define DP_TPG_LANE0_SCRAMBLEREN_ENABLE_GALIOS (1 << 4) +#define DP_TPG_LANE0_SCRAMBLEREN_ENABLE_FIBONACCI (2 << 4) +#define DP_TPG_LANE0_PATTERN_SHIFT 0 +#define DP_TPG_LANE0_PATTERN_DEFAULT_MASK 0xf +#define DP_TPG_LANE0_PATTERN_NOPATTERN 0 +#define DP_TPG_LANE0_PATTERN_TRAINING1 1 +#define DP_TPG_LANE0_PATTERN_TRAINING2 2 +#define DP_TPG_LANE0_PATTERN_TRAINING3 3 +#define DP_TPG_LANE0_PATTERN_D102 4 +#define DP_TPG_LANE0_PATTERN_SBLERRRATE 5 +#define DP_TPG_LANE0_PATTERN_PRBS7 6 +#define DP_TPG_LANE0_PATTERN_CSTM 7 +#define DP_TPG_LANE0_PATTERN_HBR2_COMPLIANCE 8 + +enum { + training_pattern_disabled = 0, + training_pattern_1 = 1, + training_pattern_2 = 2, + training_pattern_3 = 3, + training_pattern_none = 0xff +}; + +enum tegra_dc_sor_protocol { + SOR_DP, + SOR_LVDS, +}; + +#define SOR_LINK_SPEED_G1_62 6 +#define SOR_LINK_SPEED_G2_7 10 +#define SOR_LINK_SPEED_G5_4 20 +#define SOR_LINK_SPEED_LVDS 7 + +struct tegra_dp_link_config { + int is_valid; + + /* Supported configuration */ + u8 max_link_bw; + u8 max_lane_count; + int downspread; + int support_enhanced_framing; + u32 bits_per_pixel; + int alt_scramber_reset_cap; /* true for eDP */ + int only_enhanced_framing; /* enhanced_frame_en ignored */ + int frame_in_ms; + + /* Actual configuration */ + u8 link_bw; + u8 lane_count; + int enhanced_framing; + int scramble_ena; + + u32 activepolarity; + u32 active_count; + u32 tu_size; + u32 active_frac; + u32 watermark; + + s32 hblank_sym; + s32 vblank_sym; + + /* Training data */ + u32 drive_current; + u32 preemphasis; + u32 postcursor; + u8 aux_rd_interval; + u8 tps3_supported; +}; + +struct tegra_dc_sor_data { + void *base; + void *pmc_base; + u8 portnum; /* 0 or 1 */ + int power_is_up; +}; + +#define TEGRA_SOR_TIMEOUT_MS 1000 +#define TEGRA_SOR_ATTACH_TIMEOUT_MS 1000 + +int tegra_dc_sor_enable_dp(struct tegra_dc_sor_data *sor, + const struct tegra_dp_link_config *link_cfg); +int tegra_dc_sor_set_power_state(struct tegra_dc_sor_data *sor, int pu_pd); +void tegra_dc_sor_set_dp_linkctl(struct tegra_dc_sor_data *sor, int ena, + u8 training_pattern, const struct tegra_dp_link_config *link_cfg); +void tegra_dc_sor_set_link_bandwidth(struct tegra_dc_sor_data *sor, u8 link_bw); +void tegra_dc_sor_set_lane_count(struct tegra_dc_sor_data *sor, u8 lane_count); +void tegra_dc_sor_set_panel_power(struct tegra_dc_sor_data *sor, + int power_up); +void tegra_dc_sor_set_internal_panel(struct tegra_dc_sor_data *sor, int is_int); +void tegra_dc_sor_read_link_config(struct tegra_dc_sor_data *sor, u8 *link_bw, + u8 *lane_count); +void tegra_dc_sor_set_lane_parm(struct tegra_dc_sor_data *sor, + const struct tegra_dp_link_config *link_cfg); +void tegra_dc_sor_power_down_unused_lanes(struct tegra_dc_sor_data *sor, + const struct tegra_dp_link_config *link_cfg); +int tegra_dc_sor_set_voltage_swing(struct tegra_dc_sor_data *sor, + const struct tegra_dp_link_config *link_cfg); +int tegra_sor_precharge_lanes(struct tegra_dc_sor_data *sor, + const struct tegra_dp_link_config *cfg); +void tegra_dp_disable_tx_pu(struct tegra_dc_sor_data *sor); +void tegra_dp_set_pe_vs_pc(struct tegra_dc_sor_data *sor, u32 mask, + u32 pe_reg, u32 vs_reg, u32 pc_reg, u8 pc_supported); + +int tegra_dc_sor_attach(struct tegra_dc_sor_data *sor, + const struct tegra_dp_link_config *link_cfg, + const struct display_timing *timing); +int tegra_dc_sor_detach(struct tegra_dc_sor_data *sor); + +void tegra_dc_sor_disable_win_short_raster(struct dc_ctlr *disp_ctrl, + int *dc_reg_ctx); +int tegra_dc_sor_general_act(struct dc_ctlr *disp_ctrl); +void tegra_dc_sor_restore_win_and_raster(struct dc_ctlr *disp_ctrl, + int *dc_reg_ctx); + +int tegra_dc_sor_init(struct tegra_dc_sor_data **sorp); +#endif diff --git a/drivers/video/tegra124/tegra124-lcd.c b/drivers/video/tegra124/tegra124-lcd.c new file mode 100644 index 0000000000..2733590754 --- /dev/null +++ b/drivers/video/tegra124/tegra124-lcd.c @@ -0,0 +1,97 @@ +/* + * Copyright 2014 Google Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ + +#include <common.h> +#include <errno.h> +#include <fdtdec.h> +#include <lcd.h> +#include <asm/gpio.h> +#include <asm/arch-tegra/clk_rst.h> +#include <asm/arch/clock.h> +#include <asm/arch-tegra/dc.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +enum { + /* Maximum LCD size we support */ + LCD_MAX_WIDTH = 1920, + LCD_MAX_HEIGHT = 1200, + LCD_MAX_LOG2_BPP = 4, /* 2^4 = 16 bpp */ +}; + +vidinfo_t panel_info = { + /* Insert a value here so that we don't end up in the BSS */ + .vl_col = -1, +}; + +int tegra_lcd_check_next_stage(const void *blob, int wait) +{ + return 0; +} + +void tegra_lcd_early_init(const void *blob) +{ + /* + * Go with the maximum size for now. We will fix this up after + * relocation. These values are only used for memory alocation. + */ + panel_info.vl_col = LCD_MAX_WIDTH; + panel_info.vl_row = LCD_MAX_HEIGHT; + panel_info.vl_bpix = LCD_MAX_LOG2_BPP; +} + +static int tegra124_lcd_init(void *lcdbase) +{ + struct display_timing timing; + int ret; + + clock_set_up_plldp(); + clock_adjust_periph_pll_div(PERIPH_ID_HOST1X, CLOCK_ID_PERIPH, + 408000000, NULL); + + clock_enable(PERIPH_ID_HOST1X); + clock_enable(PERIPH_ID_DISP1); + clock_enable(PERIPH_ID_PWM); + clock_enable(PERIPH_ID_DPAUX); + clock_enable(PERIPH_ID_SOR0); + + udelay(2); + + reset_set_enable(PERIPH_ID_HOST1X, 0); + reset_set_enable(PERIPH_ID_DISP1, 0); + reset_set_enable(PERIPH_ID_PWM, 0); + reset_set_enable(PERIPH_ID_DPAUX, 0); + reset_set_enable(PERIPH_ID_SOR0, 0); + + ret = display_init(lcdbase, 1 << LCD_BPP, &timing); + if (ret) + return ret; + + panel_info.vl_col = roundup(timing.hactive.typ, 16); + panel_info.vl_row = timing.vactive.typ; + + lcd_set_flush_dcache(1); + + return 0; +} + +void lcd_ctrl_init(void *lcdbase) +{ + ulong start; + int ret; + + start = get_timer(0); + ret = tegra124_lcd_init(lcdbase); + debug("LCD init took %lu ms\n", get_timer(start)); + if (ret) + printf("%s: Error %d\n", __func__, ret); +} + +void lcd_enable(void) +{ +} diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 3b96b8209a..4752ea4d82 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -336,15 +336,24 @@ int gpio_lookup_name(const char *name, struct udevice **devp, unsigned int *offsetp, unsigned int *gpiop); /** - * get_gpios() - Turn the values of a list of GPIOs into an integer + * gpio_get_values_as_int() - Turn the values of a list of GPIOs into an int * * This puts the value of the first GPIO into bit 0, the second into bit 1, * etc. then returns the resulting integer. * * @gpio_list: List of GPIOs to collect - * @return resulting integer value + * @return resulting integer value, or -ve on error */ -unsigned gpio_get_values_as_int(const int *gpio_list); +int gpio_get_values_as_int(const int *gpio_list); + +/** + * gpio_claim_vector() - claim a number of GPIOs for input + * + * @gpio_num_array: array of gpios to claim, terminated by -1 + * @fmt: format string for GPIO names, e.g. "board_id%d" + * @return 0 if OK, -ve on error + */ +int gpio_claim_vector(const int *gpio_num_array, const char *fmt); /** * gpio_request_by_name() - Locate and request a GPIO by name diff --git a/include/configs/jetson-tk1.h b/include/configs/jetson-tk1.h index 8c016b7955..aeafbd5a6c 100644 --- a/include/configs/jetson-tk1.h +++ b/include/configs/jetson-tk1.h @@ -79,4 +79,9 @@ #include "tegra-common-usb-gadget.h" #include "tegra-common-post.h" +#define CONFIG_ARMV7_PSCI 1 +/* Reserve top 1M for secure RAM */ +#define CONFIG_ARMV7_SECURE_BASE 0xfff00000 +#define CONFIG_ARMV7_SECURE_RESERVE_SIZE 0x00100000 + #endif /* __CONFIG_H */ diff --git a/include/configs/nyan-big.h b/include/configs/nyan-big.h index 5397599911..caca98b5a9 100644 --- a/include/configs/nyan-big.h +++ b/include/configs/nyan-big.h @@ -21,6 +21,8 @@ #define CONFIG_TEGRA_ENABLE_UARTA #define CONFIG_SYS_NS16550_COM1 NV_PA_APB_UARTA_BASE +#define CONFIG_DISPLAY_BOARDINFO_LATE + /* I2C */ #define CONFIG_SYS_I2C_TEGRA #define CONFIG_CMD_I2C @@ -37,6 +39,18 @@ #define CONFIG_SYS_MMC_ENV_PART 2 #define CONFIG_ENV_OFFSET (-CONFIG_ENV_SIZE) +#define CONFIG_I2C_EDID + +/* LCD support */ +#define CONFIG_LCD +#define CONFIG_PWM_TEGRA +#define CONFIG_AS3722_POWER +#define LCD_BPP LCD_COLOR16 +#define CONFIG_SYS_WHITE_ON_BLACK + +/* Align LCD to 1MB boundary */ +#define CONFIG_LCD_ALIGNMENT MMU_SECTION_SIZE + /* SPI */ #define CONFIG_TEGRA114_SPI /* Compatible w/ Tegra114 SPI */ #define CONFIG_TEGRA114_SPI_CTRLS 6 diff --git a/include/configs/tegra-common-post.h b/include/configs/tegra-common-post.h index c3ad8beb90..0cea795de1 100644 --- a/include/configs/tegra-common-post.h +++ b/include/configs/tegra-common-post.h @@ -34,7 +34,7 @@ #define STDIN_KBD_USB "" #endif -#ifdef CONFIG_VIDEO_TEGRA +#ifdef CONFIG_LCD #define STDOUT_LCD ",lcd" #else #define STDOUT_LCD "" @@ -50,6 +50,8 @@ #define BOARD_EXTRA_ENV_SETTINGS #endif +#define CONFIG_SYS_LOAD_ADDR CONFIG_LOADADDR + #define CONFIG_EXTRA_ENV_SETTINGS \ TEGRA_DEVICE_SETTINGS \ MEM_LAYOUT_ENV_SETTINGS \ diff --git a/include/configs/tegra-common.h b/include/configs/tegra-common.h index 2cf1f68404..7ae1792354 100644 --- a/include/configs/tegra-common.h +++ b/include/configs/tegra-common.h @@ -89,6 +89,9 @@ #define CONFIG_CONSOLE_MUX #define CONFIG_SYS_CONSOLE_IS_IN_ENV +#ifndef CONFIG_SPL_BUILD +#define CONFIG_SYS_STDIO_DEREGISTER +#endif /* * Miscellaneous configurable options diff --git a/include/configs/tegra114-common.h b/include/configs/tegra114-common.h index 9eba5d517d..252e607d73 100644 --- a/include/configs/tegra114-common.h +++ b/include/configs/tegra114-common.h @@ -26,13 +26,9 @@ */ #define V_NS16550_CLK 408000000 /* 408MHz (pllp_out0) */ -/* Environment information, boards can override if required */ -#define CONFIG_LOADADDR 0x80408000 /* def. location for kernel */ - /* * Miscellaneous configurable options */ -#define CONFIG_SYS_LOAD_ADDR 0x80A00800 /* default */ #define CONFIG_STACKBASE 0x82800000 /* 40MB */ /*----------------------------------------------------------------------- @@ -64,10 +60,11 @@ * ramdisk_addr_r simply shouldn't overlap anything else. Choosing 33M allows * for the FDT/DTB to be up to 1M, which is hopefully plenty. */ +#define CONFIG_LOADADDR 0x81000000 #define MEM_LAYOUT_ENV_SETTINGS \ "scriptaddr=0x90000000\0" \ "pxefile_addr_r=0x90100000\0" \ - "kernel_addr_r=0x81000000\0" \ + "kernel_addr_r=" __stringify(CONFIG_LOADADDR) "\0" \ "fdt_addr_r=0x82000000\0" \ "ramdisk_addr_r=0x82100000\0" diff --git a/include/configs/tegra124-common.h b/include/configs/tegra124-common.h index f2b3774da8..1aee5c89f4 100644 --- a/include/configs/tegra124-common.h +++ b/include/configs/tegra124-common.h @@ -18,13 +18,9 @@ */ #define V_NS16550_CLK 408000000 /* 408MHz (pllp_out0) */ -/* Environment information, boards can override if required */ -#define CONFIG_LOADADDR 0x80408000 /* def. location for kernel */ - /* * Miscellaneous configurable options */ -#define CONFIG_SYS_LOAD_ADDR 0x80A00800 /* default */ #define CONFIG_STACKBASE 0x82800000 /* 40MB */ /*----------------------------------------------------------------------- @@ -56,10 +52,11 @@ * ramdisk_addr_r simply shouldn't overlap anything else. Choosing 33M allows * for the FDT/DTB to be up to 1M, which is hopefully plenty. */ +#define CONFIG_LOADADDR 0x81000000 #define MEM_LAYOUT_ENV_SETTINGS \ "scriptaddr=0x90000000\0" \ "pxefile_addr_r=0x90100000\0" \ - "kernel_addr_r=0x81000000\0" \ + "kernel_addr_r=" __stringify(CONFIG_LOADADDR) "\0" \ "fdt_addr_r=0x82000000\0" \ "ramdisk_addr_r=0x82100000\0" diff --git a/include/configs/tegra20-common.h b/include/configs/tegra20-common.h index 6330281df7..0841f33bfc 100644 --- a/include/configs/tegra20-common.h +++ b/include/configs/tegra20-common.h @@ -24,13 +24,9 @@ */ #define V_NS16550_CLK 216000000 /* 216MHz (pllp_out0) */ -/* Environment information, boards can override if required */ -#define CONFIG_LOADADDR 0x00408000 /* def. location for kernel */ - /* * Miscellaneous configurable options */ -#define CONFIG_SYS_LOAD_ADDR 0x00A00800 /* default */ #define CONFIG_STACKBASE 0x02800000 /* 40MB */ /*----------------------------------------------------------------------- @@ -62,10 +58,11 @@ * ramdisk_addr_r simply shouldn't overlap anything else. Choosing 33M allows * for the FDT/DTB to be up to 1M, which is hopefully plenty. */ +#define CONFIG_LOADADDR 0x01000000 #define MEM_LAYOUT_ENV_SETTINGS \ "scriptaddr=0x10000000\0" \ "pxefile_addr_r=0x10100000\0" \ - "kernel_addr_r=0x01000000\0" \ + "kernel_addr_r=" __stringify(CONFIG_LOADADDR) "\0" \ "fdt_addr_r=0x02000000\0" \ "ramdisk_addr_r=0x02100000\0" diff --git a/include/configs/tegra30-common.h b/include/configs/tegra30-common.h index bfdbeb70d2..3e8e3c1e5b 100644 --- a/include/configs/tegra30-common.h +++ b/include/configs/tegra30-common.h @@ -23,13 +23,9 @@ */ #define V_NS16550_CLK 408000000 /* 408MHz (pllp_out0) */ -/* Environment information, boards can override if required */ -#define CONFIG_LOADADDR 0x80408000 /* def. location for kernel */ - /* * Miscellaneous configurable options */ -#define CONFIG_SYS_LOAD_ADDR 0x80A00800 /* default */ #define CONFIG_STACKBASE 0x82800000 /* 40MB */ /*----------------------------------------------------------------------- @@ -61,10 +57,11 @@ * ramdisk_addr_r simply shouldn't overlap anything else. Choosing 33M allows * for the FDT/DTB to be up to 1M, which is hopefully plenty. */ +#define CONFIG_LOADADDR 0x81000000 #define MEM_LAYOUT_ENV_SETTINGS \ "scriptaddr=0x90000000\0" \ "pxefile_addr_r=0x90100000\0" \ - "kernel_addr_r=0x81000000\0" \ + "kernel_addr_r=" __stringify(CONFIG_LOADADDR) "\0" \ "fdt_addr_r=0x82000000\0" \ "ramdisk_addr_r=0x82100000\0" diff --git a/include/displayport.h b/include/displayport.h new file mode 100644 index 0000000000..f7c7e25285 --- /dev/null +++ b/include/displayport.h @@ -0,0 +1,60 @@ +/* + * Copyright 2014 Google Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _DISPLAYPORT_H +#define _DISPLAYPORT_H + +struct udevice; +struct display_timing; + +/** + * display_port_read_edid() - Read information from EDID + * + * @dev: Device to read from + * @buf: Buffer to read into (should be EDID_SIZE bytes) + * @buf_size: Buffer size (should be EDID_SIZE) + * @return number of bytes read, <=0 for error + */ +int display_port_read_edid(struct udevice *dev, u8 *buf, int buf_size); + +/** + * display_port_enable() - Enable a display port device + * + * @dev: Device to enable + * @panel_bpp: Number of bits per pixel for panel + * @timing: Display timings + * @return 0 if OK, -ve on error + */ +int display_port_enable(struct udevice *dev, int panel_bpp, + const struct display_timing *timing); + +struct dm_display_port_ops { + /** + * read_edid() - Read information from EDID + * + * @dev: Device to read from + * @buf: Buffer to read into (should be EDID_SIZE bytes) + * @buf_size: Buffer size (should be EDID_SIZE) + * @return number of bytes read, <=0 for error + */ + int (*read_edid)(struct udevice *dev, u8 *buf, int buf_size); + + /** + * enable() - Enable the display port device + * + * @dev: Device to enable + * @panel_bpp: Number of bits per pixel for panel + * @timing: Display timings + * @return 0 if OK, -ve on error + */ + int (*enable)(struct udevice *dev, int panel_bpp, + const struct display_timing *timing); +}; + +#define display_port_get_ops(dev) \ + ((struct dm_display_port_ops *)(dev)->driver->ops) + +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 08f1bad938..095bcb22b9 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -22,31 +22,32 @@ enum uclass_id { UCLASS_I2C_EMUL, /* sandbox I2C device emulator */ UCLASS_PCI_EMUL, /* sandbox PCI device emulator */ UCLASS_USB_EMUL, /* sandbox USB bus device emulator */ - UCLASS_SIMPLE_BUS, + UCLASS_SIMPLE_BUS, /* bus with child devices */ - /* U-Boot uclasses start here */ + /* U-Boot uclasses start here - in alphabetical order */ + UCLASS_CPU, /* CPU, typically part of an SoC */ + UCLASS_CROS_EC, /* Chrome OS EC */ + UCLASS_DISPLAY_PORT, /* Display port video */ + UCLASS_ETH, /* Ethernet device */ UCLASS_GPIO, /* Bank of general-purpose I/O pins */ - UCLASS_SERIAL, /* Serial UART */ - UCLASS_SPI, /* SPI bus */ - UCLASS_SPI_GENERIC, /* Generic SPI flash target */ - UCLASS_SPI_FLASH, /* SPI flash */ - UCLASS_CROS_EC, /* Chrome OS EC */ - UCLASS_THERMAL, /* Thermal sensor */ UCLASS_I2C, /* I2C bus */ - UCLASS_I2C_GENERIC, /* Generic I2C device */ UCLASS_I2C_EEPROM, /* I2C EEPROM device */ + UCLASS_I2C_GENERIC, /* Generic I2C device */ + UCLASS_LPC, /* x86 'low pin count' interface */ + UCLASS_MASS_STORAGE, /* Mass storage device */ UCLASS_MOD_EXP, /* RSA Mod Exp device */ + UCLASS_PCH, /* x86 platform controller hub */ UCLASS_PCI, /* PCI bus */ UCLASS_PCI_GENERIC, /* Generic PCI bus device */ - UCLASS_PCH, /* x86 platform controller hub */ - UCLASS_ETH, /* Ethernet device */ - UCLASS_LPC, /* x86 'low pin count' interface */ + UCLASS_RTC, /* Real time clock device */ + UCLASS_SERIAL, /* Serial UART */ + UCLASS_SPI, /* SPI bus */ + UCLASS_SPI_GENERIC, /* Generic SPI flash target */ + UCLASS_SPI_FLASH, /* SPI flash */ + UCLASS_THERMAL, /* Thermal sensor */ UCLASS_USB, /* USB bus */ - UCLASS_USB_HUB, /* USB hub */ UCLASS_USB_DEV_GENERIC, /* USB generic device */ - UCLASS_MASS_STORAGE, /* Mass storage device */ - UCLASS_CPU, /* CPU, typically part of an SoC */ - UCLASS_RTC, /* Real time clock device */ + UCLASS_USB_HUB, /* USB hub */ UCLASS_COUNT, UCLASS_INVALID = -1, diff --git a/include/edid.h b/include/edid.h index 18ec1d5ab0..88b4b7d854 100644 --- a/include/edid.h +++ b/include/edid.h @@ -15,6 +15,9 @@ #include <linux/types.h> +/* Size of the EDID data */ +#define EDID_SIZE 128 + #define GET_BIT(_x, _pos) \ (((_x) >> (_pos)) & 1) #define GET_BITS(_x, _pos_msb, _pos_lsb) \ @@ -287,4 +290,20 @@ int edid_get_ranges(struct edid1_info *edid, unsigned int *hmin, unsigned int *hmax, unsigned int *vmin, unsigned int *vmax); +struct display_timing; + +/** + * edid_get_timing() - Get basic digital display parameters + * + * @param buf Buffer containing EDID data + * @param buf_size Size of buffer in bytes + * @param timing Place to put preferring timing information + * @param panel_bits_per_colourp Place to put the number of bits per + * colour supported by the panel. This will be set to + * -1 if not available + * @return 0 if timings are OK, -ve on error + */ +int edid_get_timing(u8 *buf, int buf_size, struct display_timing *timing, + int *panel_bits_per_colourp); + #endif /* __EDID_H_ */ diff --git a/include/fdtdec.h b/include/fdtdec.h index f11475b5fd..6bf5f614e8 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -130,6 +130,9 @@ enum fdt_compat_id { COMPAT_NVIDIA_TEGRA20_KBC, /* Tegra20 Keyboard */ COMPAT_NVIDIA_TEGRA20_NAND, /* Tegra2 NAND controller */ COMPAT_NVIDIA_TEGRA20_PWM, /* Tegra 2 PWM controller */ + COMPAT_NVIDIA_TEGRA124_DC, /* Tegra 124 Display controller */ + COMPAT_NVIDIA_TEGRA124_SOR, /* Tegra 124 Serial Output Resource */ + COMPAT_NVIDIA_TEGRA124_PMC, /* Tegra 124 power mgmt controller */ COMPAT_NVIDIA_TEGRA20_DC, /* Tegra 2 Display controller */ COMPAT_NVIDIA_TEGRA124_SDMMC, /* Tegra124 SDMMC controller */ COMPAT_NVIDIA_TEGRA30_SDMMC, /* Tegra30 SDMMC controller */ @@ -802,6 +805,83 @@ int fdtdec_decode_memory_region(const void *blob, int node, const char *mem_type, const char *suffix, fdt_addr_t *basep, fdt_size_t *sizep); +/* Display timings from linux include/video/display_timing.h */ +enum display_flags { + DISPLAY_FLAGS_HSYNC_LOW = 1 << 0, + DISPLAY_FLAGS_HSYNC_HIGH = 1 << 1, + DISPLAY_FLAGS_VSYNC_LOW = 1 << 2, + DISPLAY_FLAGS_VSYNC_HIGH = 1 << 3, + + /* data enable flag */ + DISPLAY_FLAGS_DE_LOW = 1 << 4, + DISPLAY_FLAGS_DE_HIGH = 1 << 5, + /* drive data on pos. edge */ + DISPLAY_FLAGS_PIXDATA_POSEDGE = 1 << 6, + /* drive data on neg. edge */ + DISPLAY_FLAGS_PIXDATA_NEGEDGE = 1 << 7, + DISPLAY_FLAGS_INTERLACED = 1 << 8, + DISPLAY_FLAGS_DOUBLESCAN = 1 << 9, + DISPLAY_FLAGS_DOUBLECLK = 1 << 10, +}; + +/* + * A single signal can be specified via a range of minimal and maximal values + * with a typical value, that lies somewhere inbetween. + */ +struct timing_entry { + u32 min; + u32 typ; + u32 max; +}; + +/* + * Single "mode" entry. This describes one set of signal timings a display can + * have in one setting. This struct can later be converted to struct videomode + * (see include/video/videomode.h). As each timing_entry can be defined as a + * range, one struct display_timing may become multiple struct videomodes. + * + * Example: hsync active high, vsync active low + * + * Active Video + * Video ______________________XXXXXXXXXXXXXXXXXXXXXX_____________________ + * |<- sync ->|<- back ->|<----- active ----->|<- front ->|<- sync.. + * | | porch | | porch | + * + * HSync _|¯¯¯¯¯¯¯¯¯¯|___________________________________________|¯¯¯¯¯¯¯¯¯ + * + * VSync ¯|__________|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_________ + */ +struct display_timing { + struct timing_entry pixelclock; + + struct timing_entry hactive; /* hor. active video */ + struct timing_entry hfront_porch; /* hor. front porch */ + struct timing_entry hback_porch; /* hor. back porch */ + struct timing_entry hsync_len; /* hor. sync len */ + + struct timing_entry vactive; /* ver. active video */ + struct timing_entry vfront_porch; /* ver. front porch */ + struct timing_entry vback_porch; /* ver. back porch */ + struct timing_entry vsync_len; /* ver. sync len */ + + enum display_flags flags; /* display flags */ +}; + +/** + * fdtdec_decode_display_timing() - decode display timings + * + * Decode display timings from the supplied 'display-timings' node. + * See doc/device-tree-bindings/video/display-timing.txt for binding + * information. + * + * @param blob FDT blob + * @param node 'display-timing' node containing the timing subnodes + * @param index Index number to read (0=first timing subnode) + * @param config Place to put timings + * @return 0 if OK, -FDT_ERR_NOTFOUND if not found + */ +int fdtdec_decode_display_timing(const void *blob, int node, int index, + struct display_timing *config); /** * Set up the device tree ready for use */ diff --git a/include/linux/drm_dp_helper.h b/include/linux/drm_dp_helper.h new file mode 100644 index 0000000000..758e4a4015 --- /dev/null +++ b/include/linux/drm_dp_helper.h @@ -0,0 +1,406 @@ +/* + * Copyright © 2008 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _DRM_DP_HELPER_H_ +#define _DRM_DP_HELPER_H_ + +/* + * Unless otherwise noted, all values are from the DP 1.1a spec. Note that + * DP and DPCD versions are independent. Differences from 1.0 are not noted, + * 1.0 devices basically don't exist in the wild. + * + * Abbreviations, in chronological order: + * + * eDP: Embedded DisplayPort version 1 + * DPI: DisplayPort Interoperability Guideline v1.1a + * 1.2: DisplayPort 1.2 + * MST: Multistream Transport - part of DP 1.2a + * + * 1.2 formally includes both eDP and DPI definitions. + */ + +#define DP_AUX_I2C_WRITE 0x0 +#define DP_AUX_I2C_READ 0x1 +#define DP_AUX_I2C_STATUS 0x2 +#define DP_AUX_I2C_MOT 0x4 +#define DP_AUX_NATIVE_WRITE 0x8 +#define DP_AUX_NATIVE_READ 0x9 + +#define DP_AUX_NATIVE_REPLY_ACK (0x0 << 0) +#define DP_AUX_NATIVE_REPLY_NACK (0x1 << 0) +#define DP_AUX_NATIVE_REPLY_DEFER (0x2 << 0) +#define DP_AUX_NATIVE_REPLY_MASK (0x3 << 0) + +#define DP_AUX_I2C_REPLY_ACK (0x0 << 2) +#define DP_AUX_I2C_REPLY_NACK (0x1 << 2) +#define DP_AUX_I2C_REPLY_DEFER (0x2 << 2) +#define DP_AUX_I2C_REPLY_MASK (0x3 << 2) + +/* AUX CH addresses */ +/* DPCD */ +#define DP_DPCD_REV 0x000 + +#define DP_MAX_LINK_RATE 0x001 + +#define DP_MAX_LANE_COUNT 0x002 +# define DP_MAX_LANE_COUNT_MASK 0x1f +# define DP_TPS3_SUPPORTED (1 << 6) /* 1.2 */ +# define DP_ENHANCED_FRAME_CAP (1 << 7) + +#define DP_MAX_DOWNSPREAD 0x003 +# define DP_NO_AUX_HANDSHAKE_LINK_TRAINING (1 << 6) + +#define DP_NORP 0x004 + +#define DP_DOWNSTREAMPORT_PRESENT 0x005 +# define DP_DWN_STRM_PORT_PRESENT (1 << 0) +# define DP_DWN_STRM_PORT_TYPE_MASK 0x06 +# define DP_DWN_STRM_PORT_TYPE_DP (0 << 1) +# define DP_DWN_STRM_PORT_TYPE_ANALOG (1 << 1) +# define DP_DWN_STRM_PORT_TYPE_TMDS (2 << 1) +# define DP_DWN_STRM_PORT_TYPE_OTHER (3 << 1) +# define DP_FORMAT_CONVERSION (1 << 3) +# define DP_DETAILED_CAP_INFO_AVAILABLE (1 << 4) /* DPI */ + +#define DP_MAIN_LINK_CHANNEL_CODING 0x006 + +#define DP_DOWN_STREAM_PORT_COUNT 0x007 +# define DP_PORT_COUNT_MASK 0x0f +# define DP_MSA_TIMING_PAR_IGNORED (1 << 6) /* eDP */ +# define DP_OUI_SUPPORT (1 << 7) + +#define DP_I2C_SPEED_CAP 0x00c /* DPI */ +# define DP_I2C_SPEED_1K 0x01 +# define DP_I2C_SPEED_5K 0x02 +# define DP_I2C_SPEED_10K 0x04 +# define DP_I2C_SPEED_100K 0x08 +# define DP_I2C_SPEED_400K 0x10 +# define DP_I2C_SPEED_1M 0x20 + +#define DP_EDP_CONFIGURATION_CAP 0x00d /* XXX 1.2? */ +#define DP_TRAINING_AUX_RD_INTERVAL 0x00e /* XXX 1.2? */ + +/* Multiple stream transport */ +#define DP_FAUX_CAP 0x020 /* 1.2 */ +# define DP_FAUX_CAP_1 (1 << 0) + +#define DP_MSTM_CAP 0x021 /* 1.2 */ +# define DP_MST_CAP (1 << 0) + +#define DP_GUID 0x030 /* 1.2 */ + +#define DP_PSR_SUPPORT 0x070 /* XXX 1.2? */ +# define DP_PSR_IS_SUPPORTED 1 +#define DP_PSR_CAPS 0x071 /* XXX 1.2? */ +# define DP_PSR_NO_TRAIN_ON_EXIT 1 +# define DP_PSR_SETUP_TIME_330 (0 << 1) +# define DP_PSR_SETUP_TIME_275 (1 << 1) +# define DP_PSR_SETUP_TIME_220 (2 << 1) +# define DP_PSR_SETUP_TIME_165 (3 << 1) +# define DP_PSR_SETUP_TIME_110 (4 << 1) +# define DP_PSR_SETUP_TIME_55 (5 << 1) +# define DP_PSR_SETUP_TIME_0 (6 << 1) +# define DP_PSR_SETUP_TIME_MASK (7 << 1) +# define DP_PSR_SETUP_TIME_SHIFT 1 + +/* + * 0x80-0x8f describe downstream port capabilities, but there are two layouts + * based on whether DP_DETAILED_CAP_INFO_AVAILABLE was set. If it was not, + * each port's descriptor is one byte wide. If it was set, each port's is + * four bytes wide, starting with the one byte from the base info. As of + * DP interop v1.1a only VGA defines additional detail. + */ + +/* offset 0 */ +#define DP_DOWNSTREAM_PORT_0 0x80 +# define DP_DS_PORT_TYPE_MASK (7 << 0) +# define DP_DS_PORT_TYPE_DP 0 +# define DP_DS_PORT_TYPE_VGA 1 +# define DP_DS_PORT_TYPE_DVI 2 +# define DP_DS_PORT_TYPE_HDMI 3 +# define DP_DS_PORT_TYPE_NON_EDID 4 +# define DP_DS_PORT_HPD (1 << 3) +/* offset 1 for VGA is maximum megapixels per second / 8 */ +/* offset 2 */ +# define DP_DS_VGA_MAX_BPC_MASK (3 << 0) +# define DP_DS_VGA_8BPC 0 +# define DP_DS_VGA_10BPC 1 +# define DP_DS_VGA_12BPC 2 +# define DP_DS_VGA_16BPC 3 + +/* link configuration */ +#define DP_LINK_BW_SET 0x100 +# define DP_LINK_BW_1_62 0x06 +# define DP_LINK_BW_2_7 0x0a +# define DP_LINK_BW_5_4 0x14 /* 1.2 */ + +#define DP_LANE_COUNT_SET 0x101 +# define DP_LANE_COUNT_MASK 0x0f +# define DP_LANE_COUNT_ENHANCED_FRAME_EN (1 << 7) + +#define DP_TRAINING_PATTERN_SET 0x102 +# define DP_TRAINING_PATTERN_DISABLE 0 +# define DP_TRAINING_PATTERN_1 1 +# define DP_TRAINING_PATTERN_2 2 +# define DP_TRAINING_PATTERN_3 3 /* 1.2 */ +# define DP_TRAINING_PATTERN_MASK 0x3 + +# define DP_LINK_QUAL_PATTERN_DISABLE (0 << 2) +# define DP_LINK_QUAL_PATTERN_D10_2 (1 << 2) +# define DP_LINK_QUAL_PATTERN_ERROR_RATE (2 << 2) +# define DP_LINK_QUAL_PATTERN_PRBS7 (3 << 2) +# define DP_LINK_QUAL_PATTERN_MASK (3 << 2) + +# define DP_RECOVERED_CLOCK_OUT_EN (1 << 4) +# define DP_LINK_SCRAMBLING_DISABLE (1 << 5) + +# define DP_SYMBOL_ERROR_COUNT_BOTH (0 << 6) +# define DP_SYMBOL_ERROR_COUNT_DISPARITY (1 << 6) +# define DP_SYMBOL_ERROR_COUNT_SYMBOL (2 << 6) +# define DP_SYMBOL_ERROR_COUNT_MASK (3 << 6) + +#define DP_TRAINING_LANE0_SET 0x103 +#define DP_TRAINING_LANE1_SET 0x104 +#define DP_TRAINING_LANE2_SET 0x105 +#define DP_TRAINING_LANE3_SET 0x106 + +# define DP_TRAIN_VOLTAGE_SWING_MASK 0x3 +# define DP_TRAIN_VOLTAGE_SWING_SHIFT 0 +# define DP_TRAIN_MAX_SWING_REACHED (1 << 2) +# define DP_TRAIN_VOLTAGE_SWING_LEVEL_0 (0 << 0) +# define DP_TRAIN_VOLTAGE_SWING_LEVEL_1 (1 << 0) +# define DP_TRAIN_VOLTAGE_SWING_LEVEL_2 (2 << 0) +# define DP_TRAIN_VOLTAGE_SWING_LEVEL_3 (3 << 0) + +# define DP_TRAIN_PRE_EMPHASIS_MASK (3 << 3) +# define DP_TRAIN_PRE_EMPH_LEVEL_0 (0 << 3) +# define DP_TRAIN_PRE_EMPH_LEVEL_1 (1 << 3) +# define DP_TRAIN_PRE_EMPH_LEVEL_2 (2 << 3) +# define DP_TRAIN_PRE_EMPH_LEVEL_3 (3 << 3) + +# define DP_TRAIN_PRE_EMPHASIS_SHIFT 3 +# define DP_TRAIN_MAX_PRE_EMPHASIS_REACHED (1 << 5) + +#define DP_DOWNSPREAD_CTRL 0x107 +# define DP_SPREAD_AMP_0_5 (1 << 4) +# define DP_MSA_TIMING_PAR_IGNORE_EN (1 << 7) /* eDP */ + +#define DP_MAIN_LINK_CHANNEL_CODING_SET 0x108 +# define DP_SET_ANSI_8B10B (1 << 0) + +#define DP_I2C_SPEED_CONTROL_STATUS 0x109 /* DPI */ +/* bitmask as for DP_I2C_SPEED_CAP */ + +#define DP_EDP_CONFIGURATION_SET 0x10a /* XXX 1.2? */ + +#define DP_MSTM_CTRL 0x111 /* 1.2 */ +# define DP_MST_EN (1 << 0) +# define DP_UP_REQ_EN (1 << 1) +# define DP_UPSTREAM_IS_SRC (1 << 2) + +#define DP_PSR_EN_CFG 0x170 /* XXX 1.2? */ +# define DP_PSR_ENABLE (1 << 0) +# define DP_PSR_MAIN_LINK_ACTIVE (1 << 1) +# define DP_PSR_CRC_VERIFICATION (1 << 2) +# define DP_PSR_FRAME_CAPTURE (1 << 3) + +#define DP_ADAPTER_CTRL 0x1a0 +# define DP_ADAPTER_CTRL_FORCE_LOAD_SENSE (1 << 0) + +#define DP_BRANCH_DEVICE_CTRL 0x1a1 +# define DP_BRANCH_DEVICE_IRQ_HPD (1 << 0) + +#define DP_PAYLOAD_ALLOCATE_SET 0x1c0 +#define DP_PAYLOAD_ALLOCATE_START_TIME_SLOT 0x1c1 +#define DP_PAYLOAD_ALLOCATE_TIME_SLOT_COUNT 0x1c2 + +#define DP_SINK_COUNT 0x200 +/* prior to 1.2 bit 7 was reserved mbz */ +# define DP_GET_SINK_COUNT(x) ((((x) & 0x80) >> 1) | ((x) & 0x3f)) +# define DP_SINK_CP_READY (1 << 6) + +#define DP_DEVICE_SERVICE_IRQ_VECTOR 0x201 +# define DP_REMOTE_CONTROL_COMMAND_PENDING (1 << 0) +# define DP_AUTOMATED_TEST_REQUEST (1 << 1) +# define DP_CP_IRQ (1 << 2) +# define DP_MCCS_IRQ (1 << 3) +# define DP_DOWN_REP_MSG_RDY (1 << 4) /* 1.2 MST */ +# define DP_UP_REQ_MSG_RDY (1 << 5) /* 1.2 MST */ +# define DP_SINK_SPECIFIC_IRQ (1 << 6) + +#define DP_LANE0_1_STATUS 0x202 +#define DP_LANE2_3_STATUS 0x203 +# define DP_LANE_CR_DONE (1 << 0) +# define DP_LANE_CHANNEL_EQ_DONE (1 << 1) +# define DP_LANE_SYMBOL_LOCKED (1 << 2) + +#define DP_CHANNEL_EQ_BITS (DP_LANE_CR_DONE | \ + DP_LANE_CHANNEL_EQ_DONE | \ + DP_LANE_SYMBOL_LOCKED) + +#define DP_LANE_ALIGN_STATUS_UPDATED 0x204 + +#define DP_INTERLANE_ALIGN_DONE (1 << 0) +#define DP_DOWNSTREAM_PORT_STATUS_CHANGED (1 << 6) +#define DP_LINK_STATUS_UPDATED (1 << 7) + +#define DP_SINK_STATUS 0x205 +#define DP_SINK_STATUS_PORT0_IN_SYNC (1 << 0) + +#define DP_RECEIVE_PORT_0_STATUS (1 << 0) +#define DP_RECEIVE_PORT_1_STATUS (1 << 1) + +#define DP_ADJUST_REQUEST_LANE0_1 0x206 +#define DP_ADJUST_REQUEST_LANE2_3 0x207 +# define DP_ADJUST_VOLTAGE_SWING_LANE0_MASK 0x03 +# define DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT 0 +# define DP_ADJUST_PRE_EMPHASIS_LANE0_MASK 0x0c +# define DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT 2 +# define DP_ADJUST_VOLTAGE_SWING_LANE1_MASK 0x30 +# define DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT 4 +# define DP_ADJUST_PRE_EMPHASIS_LANE1_MASK 0xc0 +# define DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT 6 + +#define DP_TEST_REQUEST 0x218 +# define DP_TEST_LINK_TRAINING (1 << 0) +# define DP_TEST_LINK_VIDEO_PATTERN (1 << 1) +# define DP_TEST_LINK_EDID_READ (1 << 2) +# define DP_TEST_LINK_PHY_TEST_PATTERN (1 << 3) /* DPCD >= 1.1 */ +# define DP_TEST_LINK_FAUX_PATTERN (1 << 4) /* DPCD >= 1.2 */ + +#define DP_TEST_LINK_RATE 0x219 +# define DP_LINK_RATE_162 (0x6) +# define DP_LINK_RATE_27 (0xa) + +#define DP_TEST_LANE_COUNT 0x220 + +#define DP_TEST_PATTERN 0x221 + +#define DP_TEST_CRC_R_CR 0x240 +#define DP_TEST_CRC_G_Y 0x242 +#define DP_TEST_CRC_B_CB 0x244 + +#define DP_TEST_SINK_MISC 0x246 +#define DP_TEST_CRC_SUPPORTED (1 << 5) + +#define DP_TEST_RESPONSE 0x260 +# define DP_TEST_ACK (1 << 0) +# define DP_TEST_NAK (1 << 1) +# define DP_TEST_EDID_CHECKSUM_WRITE (1 << 2) + +#define DP_TEST_EDID_CHECKSUM 0x261 + +#define DP_TEST_SINK 0x270 +#define DP_TEST_SINK_START (1 << 0) + +#define DP_PAYLOAD_TABLE_UPDATE_STATUS 0x2c0 /* 1.2 MST */ +# define DP_PAYLOAD_TABLE_UPDATED (1 << 0) +# define DP_PAYLOAD_ACT_HANDLED (1 << 1) + +#define DP_VC_PAYLOAD_ID_SLOT_1 0x2c1 /* 1.2 MST */ +/* up to ID_SLOT_63 at 0x2ff */ + +#define DP_SOURCE_OUI 0x300 +#define DP_SINK_OUI 0x400 +#define DP_BRANCH_OUI 0x500 + +#define DP_SET_POWER 0x600 +# define DP_SET_POWER_D0 0x1 +# define DP_SET_POWER_D3 0x2 +# define DP_SET_POWER_MASK 0x3 + +#define DP_SIDEBAND_MSG_DOWN_REQ_BASE 0x1000 /* 1.2 MST */ +#define DP_SIDEBAND_MSG_UP_REP_BASE 0x1200 /* 1.2 MST */ +#define DP_SIDEBAND_MSG_DOWN_REP_BASE 0x1400 /* 1.2 MST */ +#define DP_SIDEBAND_MSG_UP_REQ_BASE 0x1600 /* 1.2 MST */ + +#define DP_SINK_COUNT_ESI 0x2002 /* 1.2 */ +/* 0-5 sink count */ +# define DP_SINK_COUNT_CP_READY (1 << 6) + +#define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 0x2003 /* 1.2 */ + +#define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI1 0x2004 /* 1.2 */ + +#define DP_LINK_SERVICE_IRQ_VECTOR_ESI0 0x2005 /* 1.2 */ + +#define DP_PSR_ERROR_STATUS 0x2006 /* XXX 1.2? */ +# define DP_PSR_LINK_CRC_ERROR (1 << 0) +# define DP_PSR_RFB_STORAGE_ERROR (1 << 1) + +#define DP_PSR_ESI 0x2007 /* XXX 1.2? */ +# define DP_PSR_CAPS_CHANGE (1 << 0) + +#define DP_PSR_STATUS 0x2008 /* XXX 1.2? */ +# define DP_PSR_SINK_INACTIVE 0 +# define DP_PSR_SINK_ACTIVE_SRC_SYNCED 1 +# define DP_PSR_SINK_ACTIVE_RFB 2 +# define DP_PSR_SINK_ACTIVE_SINK_SYNCED 3 +# define DP_PSR_SINK_ACTIVE_RESYNC 4 +# define DP_PSR_SINK_INTERNAL_ERROR 7 +# define DP_PSR_SINK_STATE_MASK 0x07 + +/* DP 1.2 Sideband message defines */ +/* peer device type - DP 1.2a Table 2-92 */ +#define DP_PEER_DEVICE_NONE 0x0 +#define DP_PEER_DEVICE_SOURCE_OR_SST 0x1 +#define DP_PEER_DEVICE_MST_BRANCHING 0x2 +#define DP_PEER_DEVICE_SST_SINK 0x3 +#define DP_PEER_DEVICE_DP_LEGACY_CONV 0x4 + +/* DP 1.2 MST sideband request names DP 1.2a Table 2-80 */ +#define DP_LINK_ADDRESS 0x01 +#define DP_CONNECTION_STATUS_NOTIFY 0x02 +#define DP_ENUM_PATH_RESOURCES 0x10 +#define DP_ALLOCATE_PAYLOAD 0x11 +#define DP_QUERY_PAYLOAD 0x12 +#define DP_RESOURCE_STATUS_NOTIFY 0x13 +#define DP_CLEAR_PAYLOAD_ID_TABLE 0x14 +#define DP_REMOTE_DPCD_READ 0x20 +#define DP_REMOTE_DPCD_WRITE 0x21 +#define DP_REMOTE_I2C_READ 0x22 +#define DP_REMOTE_I2C_WRITE 0x23 +#define DP_POWER_UP_PHY 0x24 +#define DP_POWER_DOWN_PHY 0x25 +#define DP_SINK_EVENT_NOTIFY 0x30 +#define DP_QUERY_STREAM_ENC_STATUS 0x38 + +/* DP 1.2 MST sideband nak reasons - table 2.84 */ +#define DP_NAK_WRITE_FAILURE 0x01 +#define DP_NAK_INVALID_READ 0x02 +#define DP_NAK_CRC_FAILURE 0x03 +#define DP_NAK_BAD_PARAM 0x04 +#define DP_NAK_DEFER 0x05 +#define DP_NAK_LINK_FAILURE 0x06 +#define DP_NAK_NO_RESOURCES 0x07 +#define DP_NAK_DPCD_FAIL 0x08 +#define DP_NAK_I2C_NAK 0x09 +#define DP_NAK_ALLOCATE_FAIL 0x0a + +#define MODE_I2C_START 1 +#define MODE_I2C_WRITE 2 +#define MODE_I2C_READ 4 +#define MODE_I2C_STOP 8 + +/* Rest of file omitted as it is not used in U-Boot */ + +#endif /* _DRM_DP_HELPER_H_ */ diff --git a/include/power/as3722.h b/include/power/as3722.h index aa966d2cca..0f22482ff7 100644 --- a/include/power/as3722.h +++ b/include/power/as3722.h @@ -23,5 +23,8 @@ int as3722_gpio_configure(struct udevice *pmic, unsigned int gpio, unsigned long flags); int as3722_gpio_direction_output(struct udevice *pmic, unsigned int gpio, unsigned int level); +int as3722_read(struct udevice *pmic, u8 reg, u8 *value); +int as3722_write(struct udevice *pmic, u8 reg, u8 value); +int as3722_get(struct udevice **devp); #endif /* __POWER_AS3722_H__ */ diff --git a/lib/fdtdec.c b/lib/fdtdec.c index b76d9cad83..a7b45d255d 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -30,6 +30,9 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(NVIDIA_TEGRA20_KBC, "nvidia,tegra20-kbc"), COMPAT(NVIDIA_TEGRA20_NAND, "nvidia,tegra20-nand"), COMPAT(NVIDIA_TEGRA20_PWM, "nvidia,tegra20-pwm"), + COMPAT(NVIDIA_TEGRA124_DC, "nvidia,tegra124-dc"), + COMPAT(NVIDIA_TEGRA124_SOR, "nvidia,tegra124-sor"), + COMPAT(NVIDIA_TEGRA124_PMC, "nvidia,tegra124-pmc"), COMPAT(NVIDIA_TEGRA20_DC, "nvidia,tegra20-dc"), COMPAT(NVIDIA_TEGRA124_SDMMC, "nvidia,tegra124-sdhci"), COMPAT(NVIDIA_TEGRA30_SDMMC, "nvidia,tegra30-sdhci"), @@ -1037,6 +1040,98 @@ int fdtdec_decode_memory_region(const void *blob, int config_node, return 0; } +static int decode_timing_property(const void *blob, int node, const char *name, + struct timing_entry *result) +{ + int length, ret = 0; + const u32 *prop; + + prop = fdt_getprop(blob, node, name, &length); + if (!prop) { + debug("%s: could not find property %s\n", + fdt_get_name(blob, node, NULL), name); + return length; + } + + if (length == sizeof(u32)) { + result->typ = fdtdec_get_int(blob, node, name, 0); + result->min = result->typ; + result->max = result->typ; + } else { + ret = fdtdec_get_int_array(blob, node, name, &result->min, 3); + } + + return ret; +} + +int fdtdec_decode_display_timing(const void *blob, int parent, int index, + struct display_timing *dt) +{ + int i, node, timings_node; + u32 val = 0; + int ret = 0; + + timings_node = fdt_subnode_offset(blob, parent, "display-timings"); + if (timings_node < 0) + return timings_node; + + for (i = 0, node = fdt_first_subnode(blob, timings_node); + node > 0 && i != index; + node = fdt_next_subnode(blob, node)) + i++; + + if (node < 0) + return node; + + memset(dt, 0, sizeof(*dt)); + + ret |= decode_timing_property(blob, node, "hback-porch", + &dt->hback_porch); + ret |= decode_timing_property(blob, node, "hfront-porch", + &dt->hfront_porch); + ret |= decode_timing_property(blob, node, "hactive", &dt->hactive); + ret |= decode_timing_property(blob, node, "hsync-len", &dt->hsync_len); + ret |= decode_timing_property(blob, node, "vback-porch", + &dt->vback_porch); + ret |= decode_timing_property(blob, node, "vfront-porch", + &dt->vfront_porch); + ret |= decode_timing_property(blob, node, "vactive", &dt->vactive); + ret |= decode_timing_property(blob, node, "vsync-len", &dt->vsync_len); + ret |= decode_timing_property(blob, node, "clock-frequency", + &dt->pixelclock); + + dt->flags = 0; + val = fdtdec_get_int(blob, node, "vsync-active", -1); + if (val != -1) { + dt->flags |= val ? DISPLAY_FLAGS_VSYNC_HIGH : + DISPLAY_FLAGS_VSYNC_LOW; + } + val = fdtdec_get_int(blob, node, "hsync-active", -1); + if (val != -1) { + dt->flags |= val ? DISPLAY_FLAGS_HSYNC_HIGH : + DISPLAY_FLAGS_HSYNC_LOW; + } + val = fdtdec_get_int(blob, node, "de-active", -1); + if (val != -1) { + dt->flags |= val ? DISPLAY_FLAGS_DE_HIGH : + DISPLAY_FLAGS_DE_LOW; + } + val = fdtdec_get_int(blob, node, "pixelclk-active", -1); + if (val != -1) { + dt->flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE : + DISPLAY_FLAGS_PIXDATA_NEGEDGE; + } + + if (fdtdec_get_bool(blob, node, "interlaced")) + dt->flags |= DISPLAY_FLAGS_INTERLACED; + if (fdtdec_get_bool(blob, node, "doublescan")) + dt->flags |= DISPLAY_FLAGS_DOUBLESCAN; + if (fdtdec_get_bool(blob, node, "doubleclk")) + dt->flags |= DISPLAY_FLAGS_DOUBLECLK; + + return 0; +} + int fdtdec_setup(void) { #ifdef CONFIG_OF_CONTROL |