diff options
author | huang lin <hl@rock-chips.com> | 2014-09-19 14:51:52 +0800 |
---|---|---|
committer | Patrick Georgi <pgeorgi@google.com> | 2015-04-10 20:50:53 +0200 |
commit | 40f558e8f4f77ab70a8a2eb9bdfa850e362cb553 (patch) | |
tree | 6ef4fd3fca8bbf8f0e07070b224ba29dccec8021 | |
parent | 1c8f2a6f968bec72a7060ba264f44fbea96d68e9 (diff) | |
download | coreboot-40f558e8f4f77ab70a8a2eb9bdfa850e362cb553.tar.xz |
rockchip: support display
Implement VOP and eDP drivers, vop and edp clock configuration,
framebuffer allocation and display configuration logic.
The eDP driver reads panel EDID to determine panel dimensions
and the pixel clock used by the VOP.
The pixel clock is generating using the NPLL.
BUG=chrome-os-partner:31897
TEST=Booted Veyron Pinky and display normal
BRANCH=None
Change-Id: I01b5c347a3433a108806aec61aa3a875cab8c129
Signed-off-by: Patrick Georgi <pgeorgi@chromium.org>
Original-Commit-Id: e4f863b0b57f2f5293ea8015db86cf7f8acc5853
Original-Change-Id: I61214f55e96bc1dcda9b0f700e5db11e49e5e533
Original-Signed-off-by: huang lin <hl@rock-chips.com>
Original-Reviewed-on: https://chromium-review.googlesource.com/219050
Original-Reviewed-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: http://review.coreboot.org/9553
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
-rw-r--r-- | src/mainboard/google/veyron_jerry/devicetree.cb | 33 | ||||
-rw-r--r-- | src/mainboard/google/veyron_jerry/mainboard.c | 12 | ||||
-rw-r--r-- | src/mainboard/google/veyron_pinky/devicetree.cb | 33 | ||||
-rw-r--r-- | src/mainboard/google/veyron_pinky/mainboard.c | 20 | ||||
-rw-r--r-- | src/soc/rockchip/rk3288/Makefile.inc | 3 | ||||
-rw-r--r-- | src/soc/rockchip/rk3288/chip.h | 30 | ||||
-rw-r--r-- | src/soc/rockchip/rk3288/clock.c | 156 | ||||
-rw-r--r-- | src/soc/rockchip/rk3288/display.c | 89 | ||||
-rw-r--r-- | src/soc/rockchip/rk3288/edp.c | 1014 | ||||
-rw-r--r-- | src/soc/rockchip/rk3288/include/soc/addressmap.h | 4 | ||||
-rw-r--r-- | src/soc/rockchip/rk3288/include/soc/clock.h | 5 | ||||
-rw-r--r-- | src/soc/rockchip/rk3288/include/soc/display.h | 30 | ||||
-rw-r--r-- | src/soc/rockchip/rk3288/include/soc/edp.h | 666 | ||||
-rw-r--r-- | src/soc/rockchip/rk3288/include/soc/soc.h | 2 | ||||
-rw-r--r-- | src/soc/rockchip/rk3288/include/soc/vop.h | 356 | ||||
-rw-r--r-- | src/soc/rockchip/rk3288/soc.c | 17 | ||||
-rw-r--r-- | src/soc/rockchip/rk3288/vop.c | 144 |
17 files changed, 2516 insertions, 98 deletions
diff --git a/src/mainboard/google/veyron_jerry/devicetree.cb b/src/mainboard/google/veyron_jerry/devicetree.cb index 0acbae9e18..be0e58c86d 100644 --- a/src/mainboard/google/veyron_jerry/devicetree.cb +++ b/src/mainboard/google/veyron_jerry/devicetree.cb @@ -20,30 +20,11 @@ # TODO fill with Versatile Express board data in QEMU. chip soc/rockchip/rk3288 device cpu_cluster 0 on end - #SCREEN_RGB - register "screen_type" = "2" - #LVDS_8BIT_2 - register "lvds_format" = "1" - #OUT_D888_P666 - register "out_face" = "33" - register "clock_frequency" = "71000000" - register "hactive" = "1280" - register "vactive" = "800" - register "hback_porch" = "100" - register "hfront_porch" = "18" - register "vback_porch" = "8" - register "vfront_porch" = "6" - register "hsync_len" = "10" - register "vsync_len" = "2" - register "hsync_active" = "0" - register "vsync_active" = "0" - register "de_active" = "0" - register "pixelclk_active" = "0" - register "swap_rb" = "0" - register "swap_rg" = "0" - register "swap_gb" = "0" - #LCD_EN_GPIO:GPIO7_A3 - register "lcd_en_gpio" = "0xff7e0004" - #LCD_CS_GPIO:GPIO7_A4 - register "lcd_cs_gpio" = "0xff7e0005" + register "vop_id" = "1" + register "framebuffer_bits_per_pixel" = "16" + register "lcd_bl_pwm_gpio" = "GPIO(7, A, 0)" + register "lcd_bl_en_gpio" = "GPIO(7, A, 2)" + register "lcd_power_on_udelay" = "200000" + register "bl_power_on_udelay" = "1000" + register "bl_pwm_to_enable_udelay" = "1000" end diff --git a/src/mainboard/google/veyron_jerry/mainboard.c b/src/mainboard/google/veyron_jerry/mainboard.c index c15c765cf4..1c7dc7f5ad 100644 --- a/src/mainboard/google/veyron_jerry/mainboard.c +++ b/src/mainboard/google/veyron_jerry/mainboard.c @@ -82,16 +82,16 @@ static void configure_codec(void) rkclk_configure_i2s(12288000); } -static void configure_lcd(void) +static void configure_vop(void) { writel(IOMUX_LCDC, &rk3288_grf->iomux_lcdc); + /* lcdc(vop) iodomain select 1.8V */ + writel(RK_SETBITS(1 << 0), &rk3288_grf->io_vsel); + rk808_configure_switch(PMIC_BUS, 2, 1); /* VCC18_LCD */ - rk808_configure_ldo(PMIC_BUS, 7, 2500); /* VCC10_LCD_PWREN_H */ + rk808_configure_ldo(PMIC_BUS, 7, 2500); /* VCC10_LCD_PWREN_H */ rk808_configure_switch(PMIC_BUS, 1, 1); /* VCC33_LCD */ - - gpio_output(GPIO(7, A, 0), 0); /* LCDC_BL */ - gpio_output(GPIO(7, A, 2), 1); /* BL_EN */ } static void mainboard_init(device_t dev) @@ -106,7 +106,7 @@ static void mainboard_init(device_t dev) configure_sdmmc(); configure_emmc(); configure_codec(); - configure_lcd(); + configure_vop(); } static void mainboard_enable(device_t dev) diff --git a/src/mainboard/google/veyron_pinky/devicetree.cb b/src/mainboard/google/veyron_pinky/devicetree.cb index 0acbae9e18..be0e58c86d 100644 --- a/src/mainboard/google/veyron_pinky/devicetree.cb +++ b/src/mainboard/google/veyron_pinky/devicetree.cb @@ -20,30 +20,11 @@ # TODO fill with Versatile Express board data in QEMU. chip soc/rockchip/rk3288 device cpu_cluster 0 on end - #SCREEN_RGB - register "screen_type" = "2" - #LVDS_8BIT_2 - register "lvds_format" = "1" - #OUT_D888_P666 - register "out_face" = "33" - register "clock_frequency" = "71000000" - register "hactive" = "1280" - register "vactive" = "800" - register "hback_porch" = "100" - register "hfront_porch" = "18" - register "vback_porch" = "8" - register "vfront_porch" = "6" - register "hsync_len" = "10" - register "vsync_len" = "2" - register "hsync_active" = "0" - register "vsync_active" = "0" - register "de_active" = "0" - register "pixelclk_active" = "0" - register "swap_rb" = "0" - register "swap_rg" = "0" - register "swap_gb" = "0" - #LCD_EN_GPIO:GPIO7_A3 - register "lcd_en_gpio" = "0xff7e0004" - #LCD_CS_GPIO:GPIO7_A4 - register "lcd_cs_gpio" = "0xff7e0005" + register "vop_id" = "1" + register "framebuffer_bits_per_pixel" = "16" + register "lcd_bl_pwm_gpio" = "GPIO(7, A, 0)" + register "lcd_bl_en_gpio" = "GPIO(7, A, 2)" + register "lcd_power_on_udelay" = "200000" + register "bl_power_on_udelay" = "1000" + register "bl_pwm_to_enable_udelay" = "1000" end diff --git a/src/mainboard/google/veyron_pinky/mainboard.c b/src/mainboard/google/veyron_pinky/mainboard.c index 2a887497b0..4bf1e088b6 100644 --- a/src/mainboard/google/veyron_pinky/mainboard.c +++ b/src/mainboard/google/veyron_pinky/mainboard.c @@ -120,25 +120,25 @@ static void configure_codec(void) rkclk_configure_i2s(12288000); } -static void configure_lcd(void) +static void configure_vop(void) { writel(IOMUX_LCDC, &rk3288_grf->iomux_lcdc); + /* lcdc(vop) iodomain select 1.8V */ + writel(RK_SETBITS(1 << 0), &rk3288_grf->io_vsel); + switch (board_id()) { case 0: - rk808_configure_ldo(PMIC_BUS, 4, 1800); /* VCC18_LCD */ - rk808_configure_ldo(PMIC_BUS, 6, 1000); /* VCC10_LCD */ - gpio_output(GPIO(7, B, 7), 1); /* LCD_EN */ + rk808_configure_ldo(PMIC_BUS, 4, 1800); /* VCC18_LCD */ + rk808_configure_ldo(PMIC_BUS, 6, 1000); /* VCC10_LCD */ + gpio_output(GPIO(7, B, 7), 1); /* LCD_EN */ break; default: - rk808_configure_switch(PMIC_BUS, 2, 1); /* VCC18_LCD */ + rk808_configure_switch(PMIC_BUS, 2, 1); /* VCC18_LCD */ rk808_configure_ldo(PMIC_BUS, 7, 2500); /* VCC10_LCD_PWREN_H */ - rk808_configure_switch(PMIC_BUS, 1, 1); /* VCC33_LCD */ + rk808_configure_switch(PMIC_BUS, 1, 1); /* VCC33_LCD */ break; } - - gpio_output(GPIO(7, A, 0), 0); /* LCDC_BL */ - gpio_output(GPIO(7, A, 2), 1); /* BL_EN */ } static void mainboard_init(device_t dev) @@ -153,7 +153,7 @@ static void mainboard_init(device_t dev) configure_sdmmc(); configure_emmc(); configure_codec(); - configure_lcd(); + configure_vop(); } static void mainboard_enable(device_t dev) diff --git a/src/soc/rockchip/rk3288/Makefile.inc b/src/soc/rockchip/rk3288/Makefile.inc index d19378377d..836f2a7bea 100644 --- a/src/soc/rockchip/rk3288/Makefile.inc +++ b/src/soc/rockchip/rk3288/Makefile.inc @@ -66,6 +66,9 @@ ramstage-y += gpio.c ramstage-y += media.c ramstage-y += rk808.c ramstage-y += pwm.c +ramstage-y += vop.c +ramstage-y += edp.c +ramstage-y += display.c ramstage-$(CONFIG_DRIVERS_UART) += uart.c CPPFLAGS_common += -Isrc/soc/rockchip/rk3288/include/ diff --git a/src/soc/rockchip/rk3288/chip.h b/src/soc/rockchip/rk3288/chip.h index a7fded09cb..b401ab4b42 100644 --- a/src/soc/rockchip/rk3288/chip.h +++ b/src/soc/rockchip/rk3288/chip.h @@ -20,28 +20,16 @@ #ifndef __SOC_ROCKCHIP_RK3288_CHIP_H__ #define __SOC_ROCKCHIP_RK3288_CHIP_H__ +#include <soc/gpio.h> + struct soc_rockchip_rk3288_config { - int screen_type; - int lvds_format; - int out_face; - int clock_frequency; - int hactive; - int vactive; - int hback_porch; - int hfront_porch; - int vback_porch; - int vfront_porch; - int hsync_len; - int vsync_len; - int hsync_active; - int vsync_active; - int de_active; - int pixelclk_active; - int swap_rb; - int swap_rg; - int swap_gb; - int lcd_en_gpio; - int lcd_cs_gpio; + u32 vop_id; + gpio_t lcd_bl_pwm_gpio; + gpio_t lcd_bl_en_gpio; + u32 lcd_power_on_udelay; + u32 bl_power_on_udelay; + u32 bl_pwm_to_enable_udelay; + u32 framebuffer_bits_per_pixel; }; #endif /* __SOC_ROCKCHIP_RK3288_CHIP_H__ */ diff --git a/src/soc/rockchip/rk3288/clock.c b/src/soc/rockchip/rk3288/clock.c index d95257f610..c69c90baec 100644 --- a/src/soc/rockchip/rk3288/clock.c +++ b/src/soc/rockchip/rk3288/clock.c @@ -186,10 +186,22 @@ static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 2); #define GPLL_MODE_SLOW (0 << 12) #define GPLL_MODE_NORM (1 << 12) +#define NPLL_MODE_MSK (0x3 << 14) +#define NPLL_MODE_SLOW (0 << 14) +#define NPLL_MODE_NORM (1 << 14) + #define SOCSTS_DPLL_LOCK (1 << 5) #define SOCSTS_APLL_LOCK (1 << 6) #define SOCSTS_CPLL_LOCK (1 << 7) #define SOCSTS_GPLL_LOCK (1 << 8) +#define SOCSTS_NPLL_LOCK (1 << 9) + +#define VCO_MAX_KHZ (2200 * (MHz/KHz)) +#define VCO_MIN_KHZ (440 * (MHz/KHz)) +#define OUTPUT_MAX_KHZ (2200 * (MHz/KHz)) +#define OUTPUT_MIN_KHZ 27500 +#define FREF_MAX_KHZ (2200 * (MHz/KHz)) +#define FREF_MIN_KHZ 269 static int rkclk_set_pll(u32 *pll_con, const struct pll_div *div) { @@ -200,8 +212,8 @@ static int rkclk_set_pll(u32 *pll_con, const struct pll_div *div) printk(BIOS_DEBUG, "Configuring PLL at %p with NF = %d, NR = %d and " "NO = %d (VCO = %uKHz, output = %uKHz)\n", pll_con, div->nf, div->nr, div->no, vco_khz, output_khz); - assert(vco_khz >= 440*(MHz/KHz) && vco_khz <= 2200*(MHz/KHz) && - output_khz >= 27500 && output_khz <= 2200*(MHz/KHz) && + assert(vco_khz >= VCO_MIN_KHZ && vco_khz <= VCO_MAX_KHZ && + output_khz >= OUTPUT_MIN_KHZ && output_khz <= OUTPUT_MAX_KHZ && (div->no == 1 || !(div->no % 2))); /* enter rest */ @@ -317,7 +329,7 @@ void rkclk_init(void) } -void rkclk_configure_cpu() +void rkclk_configure_cpu(void) { /* pll enter slow-mode */ writel(RK_CLRSETBITS(APLL_MODE_MSK, APLL_MODE_SLOW), @@ -491,3 +503,141 @@ void rkclk_configure_tsadc(unsigned int hz) writel(RK_CLRSETBITS(0x3f << 0, (div - 1) << 0), &cru_ptr->cru_clksel_con[2]); } + +static int pll_para_config(u32 freq_hz, struct pll_div *div) +{ + u32 ref_khz = OSC_HZ / KHz, nr, nf = 0; + u32 fref_khz; + u32 diff_khz, best_diff_khz; + const u32 max_nr = 1 << 6, max_nf = 1 << 12, max_no = 1 << 4; + u32 vco_khz; + u32 no = 1; + u32 freq_khz = freq_hz / KHz; + + if (!freq_hz) { + printk(BIOS_ERR, "%s: the frequency can not be 0 Hz\n", __func__); + return -1; + } + no = div_round_up(VCO_MIN_KHZ, freq_khz); + + /* only even divisors (and 1) are supported */ + if (no > 1) + no = div_round_up(no, 2) * 2; + vco_khz = freq_khz * no; + if (vco_khz < VCO_MIN_KHZ || vco_khz > VCO_MAX_KHZ || no > max_no) { + printk(BIOS_ERR, "%s: Cannot find out a supported VCO" + " for Frequency (%uHz).\n", __func__, freq_hz); + return -1; + } + div->no = no; + + best_diff_khz = vco_khz; + for (nr = 1; nr < max_nr && best_diff_khz; nr++) { + fref_khz = ref_khz / nr; + if (fref_khz < FREF_MIN_KHZ) + break; + if (fref_khz > FREF_MAX_KHZ) + continue; + + nf = vco_khz / fref_khz; + if (nf >= max_nf) + continue; + diff_khz = vco_khz - nf * fref_khz; + if (nf + 1 < max_nf && diff_khz > fref_khz / 2) { + nf++; + diff_khz = fref_khz - diff_khz; + } + + if (diff_khz >= best_diff_khz) + continue; + + best_diff_khz = diff_khz; + div->nr = nr; + div->nf = nf; + } + + if (best_diff_khz > 4 * (MHz/KHz)) { + printk(BIOS_ERR, "%s: Failed to match output frequency %u, " + "difference is %u Hz,exceed 4MHZ\n", __func__, freq_hz, + best_diff_khz * KHz); + return -1; + } + + return 0; +} + +void rkclk_configure_edp(void) +{ + /* rst edp */ + writel(RK_SETBITS(1 << 15), &cru_ptr->cru_softrst_con[6]); + udelay(1); + writel(RK_CLRBITS(1 << 15), &cru_ptr->cru_softrst_con[6]); + + /* clk_edp_24M source: 24M */ + writel(RK_SETBITS(1 << 15), &cru_ptr->cru_clksel_con[28]); +} + +void rkclk_configure_vop_aclk(u32 vop_id, u32 aclk_hz) +{ + u32 div; + + /* vop aclk source clk: cpll */ + div = CPLL_HZ / aclk_hz; + assert((div - 1 < 64) && (div * aclk_hz == CPLL_HZ)); + + switch (vop_id) { + case 0: + writel(RK_CLRSETBITS(3 << 6 | 0x1f << 0, + 0 << 6 | (div - 1) << 0), + &cru_ptr->cru_clksel_con[31]); + break; + + case 1: + writel(RK_CLRSETBITS(3 << 14 | 0x1f << 8, + 0 << 14 | (div - 1) << 8), + &cru_ptr->cru_clksel_con[31]); + break; + } +} + + +int rkclk_configure_vop_dclk(u32 vop_id, u32 dclk_hz) +{ + struct pll_div npll_config = {0}; + + if (pll_para_config(dclk_hz, &npll_config)) + return -1; + + /* npll enter slow-mode */ + writel(RK_CLRSETBITS(NPLL_MODE_MSK, NPLL_MODE_SLOW), + &cru_ptr->cru_mode_con); + + rkclk_set_pll(&cru_ptr->cru_npll_con[0], &npll_config); + + /* waiting for pll lock */ + while (1) { + if (readl(&rk3288_grf->soc_status[1]) & SOCSTS_NPLL_LOCK) + break; + udelay(1); + } + + /* npll enter normal-mode */ + writel(RK_CLRSETBITS(NPLL_MODE_MSK, NPLL_MODE_NORM), + &cru_ptr->cru_mode_con); + + /* vop dclk source clk: npll,dclk_div: 1 */ + switch (vop_id) { + case 0: + writel(RK_CLRSETBITS(0xff << 8 | 3 << 0, + 0 << 8 | 2 << 0), + &cru_ptr->cru_clksel_con[27]); + break; + + case 1: + writel(RK_CLRSETBITS(0xff << 8 | 3 << 6, + 0 << 8 | 2 << 6), + &cru_ptr->cru_clksel_con[29]); + break; + } + return 0; +} diff --git a/src/soc/rockchip/rk3288/display.c b/src/soc/rockchip/rk3288/display.c new file mode 100644 index 0000000000..a8ba31a129 --- /dev/null +++ b/src/soc/rockchip/rk3288/display.c @@ -0,0 +1,89 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 Rockchip Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <arch/cache.h> +#include <arch/io.h> +#include <console/console.h> +#include <device/device.h> +#include <delay.h> +#include <edid.h> +#include <gpio.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <soc/addressmap.h> +#include <soc/clock.h> +#include <soc/display.h> +#include <soc/edp.h> +#include <soc/gpio.h> +#include <soc/grf.h> +#include <soc/soc.h> +#include <soc/vop.h> + +#include "chip.h" + +void rk_display_init(device_t dev, u32 lcdbase, + unsigned long fb_size) +{ + struct edid edid; + struct soc_rockchip_rk3288_config *conf = dev->chip_info; + uint32_t lower = ALIGN_DOWN(lcdbase, MiB); + uint32_t upper = ALIGN_UP(lcdbase + fb_size, MiB); + + printk(BIOS_SPEW, "LCD framebuffer @%p\n", (void *)(lcdbase)); + memset((void *)lcdbase, 0, fb_size); /* clear the framebuffer */ + dcache_clean_invalidate_by_mva((void *)lower, upper - lower); + mmu_config_range(lower / MiB, (upper - lower) / MiB, DCACHE_OFF); + + rkclk_configure_edp(); + + rkclk_configure_vop_aclk(conf->vop_id, 192 * MHz); + + rk_edp_init(conf->vop_id); + udelay(conf->lcd_power_on_udelay); + + if (rk_edp_get_edid(&edid)) { + printk(BIOS_WARNING, "can not get edid\n"); + return; + } + + if (rkclk_configure_vop_dclk(conf->vop_id, edid.pixel_clock * KHz)) { + printk(BIOS_WARNING, "config vop err\n"); + return; + } + + edid.framebuffer_bits_per_pixel = conf->framebuffer_bits_per_pixel; + edid.bytes_per_line = edid.ha * conf->framebuffer_bits_per_pixel / 8; + edid.x_resolution = edid.ha; + edid.y_resolution = edid.va; + rkvop_mode_set(conf->vop_id, &edid); + + rkvop_enable(conf->vop_id, lcdbase, &edid); + + if (rk_edp_enable()) { + printk(BIOS_WARNING, "edp enable err\n"); + return; + } + + set_vbe_mode_info_valid(&edid, (uintptr_t)lcdbase); + gpio_output(conf->lcd_bl_pwm_gpio, 0); + gpio_output(conf->lcd_bl_en_gpio, 1); /* LCD_BL */ + udelay(conf->bl_power_on_udelay); + gpio_output(conf->lcd_bl_pwm_gpio, 1); /* BL_EN */ +} diff --git a/src/soc/rockchip/rk3288/edp.c b/src/soc/rockchip/rk3288/edp.c new file mode 100644 index 0000000000..556dc16a39 --- /dev/null +++ b/src/soc/rockchip/rk3288/edp.c @@ -0,0 +1,1014 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 Rockchip Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <arch/io.h> +#include <assert.h> +#include <console/console.h> +#include <delay.h> +#include <edid.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <soc/addressmap.h> +#include <soc/edp.h> +#include <soc/grf.h> +#include <soc/vop.h> +#include <timer.h> + +#include "chip.h" + +#define edp_debug(x...) do {if (0) printk(BIOS_DEBUG, x); } while (0) + +static struct rk_edp rk_edp; + +#define MAX_CR_LOOP 5 +#define MAX_EQ_LOOP 5 +#define DP_LINK_STATUS_SIZE 6 + +static const char *voltage_names[] = { + "0.4V", "0.6V", "0.8V", "1.2V" +}; +static const char *pre_emph_names[] = { + "0dB", "3.5dB", "6dB", "9.5dB" +}; + +#define DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_1200 +#define DP_PRE_EMPHASIS_MAX DP_TRAIN_PRE_EMPHASIS_9_5 + +static void rk_edp_reset(struct rk_edp *edp) +{ + u32 reg; + + /* Stop Video */ + clrbits_le32(&edp->regs->video_ctl_1, VIDEO_EN); + setbits_le32(&edp->regs->video_ctl_1, VIDEO_MUTE); + + reg = VID_CAP_FUNC_EN_N | AUD_FIFO_FUNC_EN_N | + AUD_FUNC_EN_N | HDCP_FUNC_EN_N | SW_FUNC_EN_N; + writel(reg, &edp->regs->func_en_1); + + reg = SSC_FUNC_EN_N | AUX_FUNC_EN_N | + SERDES_FIFO_FUNC_EN_N | + LS_CLK_DOMAIN_FUNC_EN_N; + writel(reg, &edp->regs->func_en_2); + + udelay(20); + + reg = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 | + LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0; + writel(reg, &edp->regs->lane_map); +} + +static void rk_edp_init_refclk(struct rk_edp *edp) +{ + writel(SEL_24M, &edp->regs->analog_ctl_2); + writel(REF_CLK_24M, &edp->regs->pll_reg_1); + + /*initial value*/ + writel(LDO_OUTPUT_V_SEL_145 | + KVCO_DEFALUT | + CHG_PUMP_CUR_SEL_5US | + V2L_CUR_SEL_1MA, &edp->regs->pll_reg_2); + + writel(LOCK_DET_CNT_SEL_256 | + LOOP_FILTER_RESET | + PALL_SSC_RESET | + LOCK_DET_BYPASS | + PLL_LOCK_DET_MODE | + PLL_LOCK_DET_FORCE, &edp->regs->pll_reg_3); + + writel(REGULATOR_V_SEL_950MV | + STANDBY_CUR_SEL | + CHG_PUMP_INOUT_CTRL_1200MV | + CHG_PUMP_INPUT_CTRL_OP, &edp->regs->pll_reg_5); + + writel(SSC_OFFSET | SSC_MODE | SSC_DEPTH, &edp->regs->ssc_reg); + + writel(TX_SWING_PRE_EMP_MODE | + PRE_DRIVER_PW_CTRL1 | + LP_MODE_CLK_REGULATOR | + RESISTOR_MSB_CTRL | + RESISTOR_CTRL, &edp->regs->tx_common); + + writel(DP_AUX_COMMON_MODE | + DP_AUX_EN | + AUX_TERM_50OHM, &edp->regs->dp_aux); + + writel(DP_BG_OUT_SEL | + DP_DB_CUR_CTRL | + DP_BG_SEL | + DP_RESISTOR_TUNE_BG, &edp->regs->dp_bias); + + writel(CH1_CH3_SWING_EMP_CTRL | + CH0_CH2_SWING_EMP_CTRL, &edp->regs->dp_reserv2); +} + +static void rk_edp_init_interrupt(struct rk_edp *edp) +{ + /* Set interrupt pin assertion polarity as high */ + writel(INT_POL, &edp->regs->int_ctl); + + /* Clear pending registers */ + writel(0xff, &edp->regs->common_int_sta_1); + writel(0x4f, &edp->regs->common_int_sta_2); + writel(0xff, &edp->regs->common_int_sta_3); + writel(0x27, &edp->regs->common_int_sta_4); + writel(0x7f, &edp->regs->dp_int_sta); + + /* 0:mask,1: unmask */ + writel(0x00, &edp->regs->common_int_mask_1); + writel(0x00, &edp->regs->common_int_mask_2); + writel(0x00, &edp->regs->common_int_mask_3); + writel(0x00, &edp->regs->common_int_mask_4); + writel(0x00, &edp->regs->int_sta_mask); +} + +static void rk_edp_enable_sw_function(struct rk_edp *edp) +{ + clrbits_le32(&edp->regs->func_en_1, SW_FUNC_EN_N); +} + +static int rk_edp_get_pll_lock_status(struct rk_edp *edp) +{ + u32 val; + + val = readl(&edp->regs->dp_debug_ctl); + return (val & PLL_LOCK) ? DP_PLL_LOCKED : DP_PLL_UNLOCKED; +} + +static void rk_edp_init_analog_func(struct rk_edp *edp) +{ + struct stopwatch sw; + + writel(0x00, &edp->regs->dp_pd); + + writel(PLL_LOCK_CHG, &edp->regs->common_int_sta_1); + + clrbits_le32(&edp->regs->dp_debug_ctl, F_PLL_LOCK | PLL_LOCK_CTRL); + + stopwatch_init_msecs_expire(&sw, PLL_LOCK_TIMEOUT); + + while (rk_edp_get_pll_lock_status(edp) == DP_PLL_UNLOCKED) { + if (stopwatch_expired(&sw)) { + printk(BIOS_ERR, "%s: PLL is not locked\n", __func__); + return; + } + } + + /* Enable Serdes FIFO function and Link symbol clock domain module */ + clrbits_le32(&edp->regs->func_en_2, SERDES_FIFO_FUNC_EN_N | + LS_CLK_DOMAIN_FUNC_EN_N | AUX_FUNC_EN_N | + SSC_FUNC_EN_N); +} + +static void rk_edp_init_aux(struct rk_edp *edp) +{ + /* Clear inerrupts related to AUX channel */ + writel(AUX_FUNC_EN_N, &edp->regs->dp_int_sta); + + /* Disable AUX channel module */ + setbits_le32(&edp->regs->func_en_2, AUX_FUNC_EN_N); + + /* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */ + writel(DEFER_CTRL_EN | DEFER_COUNT(1), &edp->regs->aux_ch_defer_dtl); + + /* Enable AUX channel module */ + clrbits_le32(&edp->regs->func_en_2, AUX_FUNC_EN_N); +} + +static int rk_edp_aux_enable(struct rk_edp *edp) +{ + struct stopwatch sw; + + setbits_le32(&edp->regs->aux_ch_ctl_2, AUX_EN); + stopwatch_init_msecs_expire(&sw, 20); + do { + if (!(readl(&edp->regs->aux_ch_ctl_2) & AUX_EN)) + return 0; + } while (!stopwatch_expired(&sw)); + + return -1; + +} + +static int rk_edp_is_aux_reply(struct rk_edp *edp) +{ + struct stopwatch sw; + + stopwatch_init_msecs_expire(&sw, 10); + + while (!(readl(&edp->regs->dp_int_sta) & RPLY_RECEIV)) { + if (stopwatch_expired(&sw)) + return -1; + } + + writel(RPLY_RECEIV, &edp->regs->dp_int_sta); + + return 0; +} + +static int rk_edp_start_aux_transaction(struct rk_edp *edp) +{ + int val; + + /* Enable AUX CH operation */ + if (rk_edp_aux_enable(edp)) { + edp_debug("AUX CH enable timeout!\n"); + return -1; + } + + /* Is AUX CH command reply received? */ + if (rk_edp_is_aux_reply(edp)) { + edp_debug("AUX CH command reply failed!\n"); + return -1; + } + + /* Clear interrupt source for AUX CH access error */ + val = readl(&edp->regs->dp_int_sta); + if (val & AUX_ERR) { + writel(AUX_ERR, &edp->regs->dp_int_sta); + return -1; + } + + /* Check AUX CH error access status */ + val = readl(&edp->regs->dp_int_sta); + if ((val & AUX_STATUS_MASK) != 0) { + edp_debug("AUX CH error happens: %d\n\n", + val & AUX_STATUS_MASK); + return -1; + } + + return 0; +} + +static int rk_edp_dpcd_transfer(struct rk_edp *edp, + unsigned int val_addr, u8 *data, + unsigned int length, + enum dpcd_request request) +{ + int val; + int i, try_times; + int retval = 0; + u32 len = 0; + + while (length) { + len = MIN(length, 16); + for (try_times = 0; try_times < 10; try_times++) { + + /* Clear AUX CH data buffer */ + val = BUF_CLR; + writel(val, &edp->regs->buf_data_ctl); + + /* Select DPCD device address */ + val = AUX_ADDR_7_0(val_addr); + writel(val, &edp->regs->aux_addr_7_0); + val = AUX_ADDR_15_8(val_addr); + writel(val, &edp->regs->aux_addr_15_8); + val = AUX_ADDR_19_16(val_addr); + writel(val, &edp->regs->aux_addr_19_16); + + /* + * Set DisplayPort transaction and read 1 byte + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + if (request == DPCD_WRITE) { + val = AUX_LENGTH(len) | + AUX_TX_COMM_DP_TRANSACTION | + AUX_TX_COMM_WRITE; + for (i = 0; i < len; i++) + writel(*data++, &edp->regs->buf_data[i]); + } else + val = AUX_LENGTH(len) | + AUX_TX_COMM_DP_TRANSACTION | + AUX_TX_COMM_READ; + + writel(val, &edp->regs->aux_ch_ctl_1); + + /* Start AUX transaction */ + retval = rk_edp_start_aux_transaction(edp); + if (retval == 0) + break; + else + printk(BIOS_WARNING, "read dpcd Aux Transaction fail!\n"); + + } + + if (retval) + return -1; + + if (request == DPCD_READ) { + for (i = 0; i < len; i++) + *data++ = (u8)readl(&edp->regs->buf_data[i]); + } + + length -= len; + val_addr += 16; + } + return 0; +} + +static int rk_edp_dpcd_read(struct rk_edp *edp, u32 addr, u8 *values, size_t size) +{ + return rk_edp_dpcd_transfer(edp, addr, values, size, DPCD_READ); +} + +static int rk_edp_dpcd_write(struct rk_edp *edp, u32 addr, u8 *values, size_t size) +{ + return rk_edp_dpcd_transfer(edp, addr, values, size, DPCD_WRITE); +} + + +static int rk_edp_link_power_up(struct rk_edp *edp) +{ + u8 value; + int err; + + /* DP_SET_POWER register is only available on DPCD v1.1 and later */ + if (edp->link_train.revision < 0x11) + return 0; + + err = rk_edp_dpcd_read(edp, DPCD_LINK_POWER_STATE, &value, 1); + if (err < 0) + return err; + + value &= ~DP_SET_POWER_MASK; + value |= DP_SET_POWER_D0; + + err = rk_edp_dpcd_write(edp, DPCD_LINK_POWER_STATE, &value, 1); + if (err < 0) + return err; + + /* + * According to the DP 1.1 specification, a "Sink Device must exit the + * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink + * Control Field" (register 0x600). + */ + mdelay(1); + + return 0; +} + +static int rk_edp_link_configure(struct rk_edp *edp) +{ + u8 values[2]; + + values[0] = edp->link_train.link_rate; + values[1] = edp->link_train.lane_count; + + return rk_edp_dpcd_write(edp, DPCD_LINK_BW_SET, values, sizeof(values)); +} + +static void rk_edp_set_link_training(struct rk_edp *edp, + const u8 *training_values) +{ + int i; + + for (i = 0; i < edp->link_train.lane_count; i++) + writel(training_values[i], &edp->regs->ln_link_trn_ctl[i]); +} + +static u8 edp_link_status(const u8 *link_status, int r) +{ + return link_status[r - DPCD_LANE0_1_STATUS]; +} + +static int rk_edp_dpcd_read_link_status(struct rk_edp *edp, u8 *link_status) +{ + return rk_edp_dpcd_read(edp, DPCD_LANE0_1_STATUS, link_status, + DP_LINK_STATUS_SIZE); +} + +static u8 edp_get_lane_status(const u8 *link_status, int lane) +{ + int i = DPCD_LANE0_1_STATUS + (lane >> 1); + int s = (lane & 1) * 4; + u8 l = edp_link_status(link_status, i); + return (l >> s) & 0xf; +} + +static int rk_edp_clock_recovery_ok(const u8 *link_status, int lane_count) +{ + int lane; + u8 lane_status; + + for (lane = 0; lane < lane_count; lane++) { + lane_status = edp_get_lane_status(link_status, lane); + if ((lane_status & DP_LANE_CR_DONE) == 0) + return 0; + } + return 1; +} + +static int rk_edp_channel_eq_ok(const u8 *link_status, int lane_count) +{ + u8 lane_align; + u8 lane_status; + int lane; + + lane_align = edp_link_status(link_status, + DPCD_LANE_ALIGN_STATUS_UPDATED); + if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) + return 0; + for (lane = 0; lane < lane_count; lane++) { + lane_status = edp_get_lane_status(link_status, lane); + if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS) + return 0; + } + return 1; +} + +static u8 +rk_edp_get_adjust_request_voltage(const u8 *link_status, int lane) +{ + int i = DPCD_ADJUST_REQUEST_LANE0_1 + (lane >> 1); + int s = ((lane & 1) ? + DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : + DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); + u8 l = edp_link_status(link_status, i); + + return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; +} + +static u8 rk_edp_get_adjust_request_pre_emphasis(const u8 *link_status, + int lane) +{ + int i = DPCD_ADJUST_REQUEST_LANE0_1 + (lane >> 1); + int s = ((lane & 1) ? + DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : + DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); + u8 l = edp_link_status(link_status, i); + + return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; +} + +static void edp_get_adjust_train(const u8 *link_status, int lane_count, + u8 train_set[]) +{ + u8 v = 0; + u8 p = 0; + int lane; + + for (lane = 0; lane < lane_count; lane++) { + u8 this_v = + rk_edp_get_adjust_request_voltage(link_status, lane); + u8 this_p = + rk_edp_get_adjust_request_pre_emphasis(link_status, + lane); + + printk(BIOS_DEBUG, "requested signal parameters: lane %d " + "voltage %s pre_emph %s\n", lane, + voltage_names[this_v >> DP_TRAIN_VOLTAGE_SWING_SHIFT], + pre_emph_names[this_p >> DP_TRAIN_PRE_EMPHASIS_SHIFT]); + + if (this_v > v) + v = this_v; + if (this_p > p) + p = this_p; + } + + if (v >= DP_VOLTAGE_MAX) + v |= DP_TRAIN_MAX_SWING_REACHED; + + if (p >= DP_PRE_EMPHASIS_MAX) + p |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; + + printk(BIOS_DEBUG, "using signal parameters: voltage %s pre_emph %s\n", + voltage_names[(v & DP_TRAIN_VOLTAGE_SWING_MASK) + >> DP_TRAIN_VOLTAGE_SWING_SHIFT], + pre_emph_names[(p & DP_TRAIN_PRE_EMPHASIS_MASK) + >> DP_TRAIN_PRE_EMPHASIS_SHIFT]); + + for (lane = 0; lane < 4; lane++) + train_set[lane] = v | p; +} + +static int rk_edp_link_train_cr(struct rk_edp *edp) +{ + int clock_recovery; + u8 voltage, tries = 0; + u8 status[DP_LINK_STATUS_SIZE]; + int i; + u8 value; + + value = DP_TRAINING_PATTERN_1; + writel(value, &edp->regs->dp_training_ptn_set); + rk_edp_dpcd_write(edp, DPCD_TRAINING_PATTERN_SET, &value, 1); + memset(edp->train_set, 0, 4); + + /* clock recovery loop */ + clock_recovery = 0; + tries = 0; + voltage = 0xff; + + while (1) { + rk_edp_set_link_training(edp, edp->train_set); + rk_edp_dpcd_write(edp, DPCD_TRAINING_LANE0_SET, + edp->train_set, + edp->link_train.lane_count); + + mdelay(1); + + if (rk_edp_dpcd_read_link_status(edp, status) < 0) { + printk(BIOS_ERR, "displayport link status failed\n"); + break; + } + + if (rk_edp_clock_recovery_ok(status, + edp->link_train.lane_count)) { + clock_recovery = 1; + break; + } + + for (i = 0; i < edp->link_train.lane_count; i++) { + if ((edp->train_set[i] & + DP_TRAIN_MAX_SWING_REACHED) == 0) + break; + } + if (i == edp->link_train.lane_count) { + printk(BIOS_ERR, "clock recovery reached max voltage\n"); + break; + } + + if ((edp->train_set[0] & + DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) { + ++tries; + if (tries == MAX_CR_LOOP) { + printk(BIOS_ERR, "clock recovery tried 5 times\n"); + break; + } + } else + tries = 0; + + voltage = edp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; + + /* Compute new train_set as requested by sink */ + edp_get_adjust_train(status, edp->link_train.lane_count, + edp->train_set); + } + if (!clock_recovery) { + printk(BIOS_ERR, "clock recovery failed\n"); + return -1; + } else { + printk(BIOS_DEBUG, "clock recovery at voltage %d pre-emphasis %d\n", + edp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK, + (edp->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >> + DP_TRAIN_PRE_EMPHASIS_SHIFT); + return 0; + } +} + +static int rk_edp_link_train_ce(struct rk_edp *edp) +{ + int channel_eq; + u8 value, tries = 0; + u8 status[DP_LINK_STATUS_SIZE]; + + value = DP_TRAINING_PATTERN_2; + writel(value, &edp->regs->dp_training_ptn_set); + rk_edp_dpcd_write(edp, DPCD_TRAINING_PATTERN_SET, &value, 1); + + /* channel equalization loop */ + channel_eq = 0; + for (tries = 0; tries < 5; tries++) { + rk_edp_set_link_training(edp, edp->train_set); + udelay(400); + + if (rk_edp_dpcd_read_link_status(edp, status) < 0) { + printk(BIOS_ERR, "displayport link status failed\n"); + return -1; + } + + if (rk_edp_channel_eq_ok(status, + edp->link_train.lane_count)) { + channel_eq = 1; + break; + } + edp_get_adjust_train(status, + edp->link_train.lane_count, + edp->train_set); + } + + if (!channel_eq) { + printk(BIOS_ERR, "channel eq failed\n"); + return -1; + } else { + printk(BIOS_DEBUG, "channel eq at voltage %d pre-emphasis %d\n", + edp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK, + (edp->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) + >> DP_TRAIN_PRE_EMPHASIS_SHIFT); + return 0; + } +} + +static int rk_edp_init_training(struct rk_edp *edp) +{ + u8 values[3]; + int err; + + err = rk_edp_dpcd_read(edp, DPCD_DPCD_REV, values, sizeof(values)); + if (err < 0) + return err; + + edp->link_train.revision = values[0]; + edp->link_train.link_rate = values[1]; + edp->link_train.lane_count = values[2] & DP_MAX_LANE_COUNT_MASK; + + edp_debug("max link rate:%d.%dGps max number of lanes:%d\n", + edp->link_train.link_rate * 27 / 100, + edp->link_train.link_rate * 27 % 100, + edp->link_train.lane_count); + + if ((edp->link_train.link_rate != LINK_RATE_1_62GBPS) && + (edp->link_train.link_rate != LINK_RATE_2_70GBPS)) { + edp_debug("Rx Max Link Rate is abnormal :%x\n", + edp->link_train.link_rate); + return -1; + } + + if (edp->link_train.lane_count == 0) { + edp_debug("Rx Max Lane count is abnormal :%x\n", + edp->link_train.lane_count); + return -1; + } + + rk_edp_link_power_up(edp); + rk_edp_link_configure(edp); + return 0; +} + +static int rk_edp_hw_link_training(struct rk_edp *edp) +{ + u32 val; + struct stopwatch sw; + + /* Set link rate and count as you want to establish*/ + writel(edp->link_train.link_rate, &edp->regs->link_bw_set); + writel(edp->link_train.lane_count, &edp->regs->lane_count_set); + + if (rk_edp_link_train_cr(edp)) + return -1; + if (rk_edp_link_train_ce(edp)) + return -1; + + writel(HW_LT_EN, &edp->regs->dp_hw_link_training); + stopwatch_init_msecs_expire(&sw, 10); + do { + val = readl(&edp->regs->dp_hw_link_training); + if (!(val & HW_LT_EN)) + break; + } while (!stopwatch_expired(&sw)); + if (val & HW_LT_ERR_CODE_MASK) { + printk(BIOS_ERR, "edp hw link training error: %d\n", + val >> HW_LT_ERR_CODE_SHIFT); + return -1; + } + return 0; + +} + +static int rk_edp_select_i2c_device(struct rk_edp *edp, + unsigned int device_addr, + unsigned int val_addr) +{ + u32 val; + int retval; + + /* Set EDID device address */ + val = device_addr; + writel(val, &edp->regs->aux_addr_7_0); + writel(0x0, &edp->regs->aux_addr_15_8); + writel(0x0, &edp->regs->aux_addr_19_16); + + /* Set offset from base address of EDID device */ + writel(val_addr, &edp->regs->buf_data[0]); + + /* + * Set I2C transaction and write address + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + val = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT | + AUX_TX_COMM_WRITE; + writel(val, &edp->regs->aux_ch_ctl_1); + + /* Start AUX transaction */ + retval = rk_edp_start_aux_transaction(edp); + if (retval != 0) + edp_debug("select_i2c_device Aux Transaction fail!\n"); + + return retval; +} + +static int rk_edp_read_bytes_from_i2c(struct rk_edp *edp, + unsigned int device_addr, + unsigned int val_addr, + unsigned int count, + u8 edid[]) +{ + u32 val; + unsigned int i, j; + unsigned int cur_data_idx; + unsigned int defer = 0; + int retval = 0; + + for (i = 0; i < count; i += 16) { + for (j = 0; j < 10; j++) { /* try 10 times */ + /* Clear AUX CH data buffer */ + val = BUF_CLR; + writel(val, &edp->regs->buf_data_ctl); + + /* Set normal AUX CH command */ + clrbits_le32(&edp->regs->aux_ch_ctl_2, ADDR_ONLY); + + /* + * If Rx sends defer, Tx sends only reads + * request without sending addres + */ + if (!defer) + retval = rk_edp_select_i2c_device(edp, + device_addr, val_addr + i); + else + defer = 0; + + /* + * Set I2C transaction and write data + * If bit 3 is 1, DisplayPort transaction. + * If Bit 3 is 0, I2C transaction. + */ + val = AUX_LENGTH(16) | AUX_TX_COMM_I2C_TRANSACTION | + AUX_TX_COMM_READ; + writel(val, &edp->regs->aux_ch_ctl_1); + + /* Start AUX transaction */ + retval = rk_edp_start_aux_transaction(edp); + if (retval == 0) + break; + else { + edp_debug("Aux Transaction fail!\n"); + continue; + } + + /* Check if Rx sends defer */ + val = readl(&edp->regs->aux_rx_comm); + if (val == AUX_RX_COMM_AUX_DEFER || + val == AUX_RX_COMM_I2C_DEFER) { + edp_debug("Defer: %d\n\n", val); + defer = 1; + } + } + + if (retval) + return -1; + + for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) { + val = readl(&edp->regs->buf_data[cur_data_idx]); + edid[i + cur_data_idx] = (u8)val; + } + } + + return retval; +} + +static int rk_edp_read_edid(struct rk_edp *edp, struct edid *edid) +{ + u8 buf[EDID_LENGTH * 2]; + int retval; + + /* Read EDID data */ + retval = rk_edp_read_bytes_from_i2c(edp, EDID_ADDR, + EDID_HEADER, EDID_LENGTH, + &buf[EDID_HEADER]); + if (retval != 0) { + printk(BIOS_ERR, "EDID Read failed!\n"); + return -1; + } + + /* check if edid have extension flag, and read additional EDID data */ + if (buf[EDID_EXTENSION_FLAG]) { + retval = rk_edp_read_bytes_from_i2c(edp, EDID_ADDR, + EDID_LENGTH, EDID_LENGTH, + &buf[EDID_LENGTH]); + if (retval != 0) { + printk(BIOS_ERR, "EDID Read failed!\n"); + return -1; + } + } + + if (decode_edid(buf, sizeof(buf), edid)) { + printk(BIOS_ERR, "%s: Failed to decode EDID.\n", + __func__); + return -1; + } + + edp_debug("EDID Read success!\n"); + return 0; +} + +static int rk_edp_set_link_train(struct rk_edp *edp) +{ + int retval; + + if (rk_edp_init_training(edp)) { + printk(BIOS_ERR, "DP LT init failed!\n"); + return -1; + } + + retval = rk_edp_hw_link_training(edp); + + return retval; +} + +static void rk_edp_init_video(struct rk_edp *edp) +{ + u32 val; + + val = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG; + writel(val, &edp->regs->common_int_sta_1); + + val = CHA_CRI(4) | CHA_CTRL; + writel(val, &edp->regs->sys_ctl_2); + + val = VID_HRES_TH(2) | VID_VRES_TH(0); + writel(val, &edp->regs->video_ctl_8); +} + +static void rk_edp_config_video_slave_mode(struct rk_edp *edp) +{ + clrbits_le32(&edp->regs->func_en_1, + VID_FIFO_FUNC_EN_N | VID_CAP_FUNC_EN_N); +} + +static void rk_edp_set_video_cr_mn(struct rk_edp *edp, + enum clock_recovery_m_value_type type, + u32 m_value, + u32 n_value) +{ + u32 val; + + if (type == REGISTER_M) { + setbits_le32(&edp->regs->sys_ctl_4, FIX_M_VID); + val = m_value & 0xff; + writel(val, &edp->regs->m_vid_0); + val = (m_value >> 8) & 0xff; + writel(val, &edp->regs->m_vid_1); + val = (m_value >> 16) & 0xff; + writel(val, &edp->regs->m_vid_2); + + val = n_value & 0xff; + writel(val, &edp->regs->n_vid_0); + val = (n_value >> 8) & 0xff; + writel(val, &edp->regs->n_vid_1); + val = (n_value >> 16) & 0xff; + writel(val, &edp->regs->n_vid_2); + } else { + clrbits_le32(&edp->regs->sys_ctl_4, FIX_M_VID); + + writel(0x00, &edp->regs->n_vid_0); + writel(0x80, &edp->regs->n_vid_1); + writel(0x00, &edp->regs->n_vid_2); + } +} + +static int rk_edp_is_video_stream_clock_on(struct rk_edp *edp) +{ + u32 val; + struct stopwatch sw; + + stopwatch_init_msecs_expire(&sw, 100); + do { + val = readl(&edp->regs->sys_ctl_1); + + /*must write value to update DET_STA bit status*/ + writel(val, &edp->regs->sys_ctl_1); + val = readl(&edp->regs->sys_ctl_1); + if (!(val & DET_STA)) + continue; + + val = readl(&edp->regs->sys_ctl_2); + + /*must write value to update CHA_STA bit status*/ + writel(val, &edp->regs->sys_ctl_2); + val = readl(&edp->regs->sys_ctl_2); + if (!(val & CHA_STA)) + return 0; + } while (!stopwatch_expired(&sw)); + + return -1; +} + +static int rk_edp_is_video_stream_on(struct rk_edp *edp) +{ + u32 val; + struct stopwatch sw; + + stopwatch_init_msecs_expire(&sw, 100); + do { + val = readl(&edp->regs->sys_ctl_3); + + /*must write value to update STRM_VALID bit status*/ + writel(val, &edp->regs->sys_ctl_3); + + val = readl(&edp->regs->sys_ctl_3); + if (!(val & STRM_VALID)) + return 0; + } while (!stopwatch_expired(&sw)); + + return -1; +} + +static int rk_edp_config_video(struct rk_edp *edp) +{ + rk_edp_config_video_slave_mode(edp); + + if (rk_edp_get_pll_lock_status(edp) == DP_PLL_UNLOCKED) { + edp_debug("PLL is not locked yet.\n"); + return -1; + } + + if (rk_edp_is_video_stream_clock_on(edp)) + return -1; + + /* Set to use the register calculated M/N video */ + rk_edp_set_video_cr_mn(edp, CALCULATED_M, 0, 0); + + /* For video bist, Video timing must be generated by register */ + clrbits_le32(&edp->regs->video_ctl_10, F_SEL); + + /* Disable video mute */ + clrbits_le32(&edp->regs->video_ctl_1, VIDEO_MUTE); + + /* Enable video at next frame */ + setbits_le32(&edp->regs->video_ctl_1, VIDEO_EN); + + return rk_edp_is_video_stream_on(edp); +} + +int rk_edp_get_edid(struct edid *edid) +{ + int i; + int retval; + + /* Read EDID */ + for (i = 0; i < 3; i++) { + retval = rk_edp_read_edid(&rk_edp, edid); + if (retval == 0) + break; + } + + return retval; +} + +int rk_edp_enable(void) +{ + int ret = 0; + + if (rk_edp_set_link_train(&rk_edp)) { + printk(BIOS_ERR, "link train failed!\n"); + return -1; + } + + rk_edp_init_video(&rk_edp); + ret = rk_edp_config_video(&rk_edp); + if (ret) + printk(BIOS_ERR, "config video failed\n"); + + return ret; +} + +void rk_edp_init(u32 vop_id) +{ + u32 val; + + rk_edp.regs = (struct rk3288_edp_regs *)EDP_BASE; + + /* grf_edp_ref_clk_sel: from internal 24MHz or 27MHz clock */ + writel(RK_SETBITS(1 << 4), &rk3288_grf->soc_con12); + + /* select epd signal from vop0 or vop1 */ + val = (vop_id == 1) ? RK_SETBITS(1 << 5) : RK_CLRBITS(1 << 5); + writel(val, &rk3288_grf->soc_con6); + + rk_edp_reset(&rk_edp); + rk_edp_init_refclk(&rk_edp); + rk_edp_init_interrupt(&rk_edp); + rk_edp_enable_sw_function(&rk_edp); + rk_edp_init_analog_func(&rk_edp); + rk_edp_init_aux(&rk_edp); +} diff --git a/src/soc/rockchip/rk3288/include/soc/addressmap.h b/src/soc/rockchip/rk3288/include/soc/addressmap.h index aea3bc0081..9be7f7bd0e 100644 --- a/src/soc/rockchip/rk3288/include/soc/addressmap.h +++ b/src/soc/rockchip/rk3288/include/soc/addressmap.h @@ -79,8 +79,10 @@ #define TIMER7_BASE 0xFF810020 #define VOP_BIG_BASE 0xFF930000 +#define VOP_LIT_BASE 0xFF940000 +#define EDP_BASE 0xFF970000 + #define HDMI_TX_BASE 0xFF980000 -#define DMACS_BUS_BASE 0xFFB20000 #define SERVICE_CORE_BASE 0xFFA80000 #define SERVICE_DMA_BASE 0xFFA90000 diff --git a/src/soc/rockchip/rk3288/include/soc/clock.h b/src/soc/rockchip/rk3288/include/soc/clock.h index d11c6ec0f3..f17428da58 100644 --- a/src/soc/rockchip/rk3288/include/soc/clock.h +++ b/src/soc/rockchip/rk3288/include/soc/clock.h @@ -27,6 +27,7 @@ #define APLL_HZ (1800*MHz) #define GPLL_HZ (594*MHz) #define CPLL_HZ (384*MHz) +#define NPLL_HZ (384*MHz) #define PD_BUS_ACLK_HZ (148500*KHz) #define PD_BUS_HCLK_HZ (148500*KHz) @@ -44,5 +45,7 @@ void rkclk_configure_ddr(unsigned int hz); void rkclk_configure_i2s(unsigned int hz); void rkclk_configure_cpu(void); void rkclk_configure_tsadc(unsigned int hz); - +void rkclk_configure_vop_aclk(u32 vop_id, u32 aclk_hz); +int rkclk_configure_vop_dclk(u32 vop_id, u32 dclk_hz); +void rkclk_configure_edp(void); #endif /* __SOC_ROCKCHIP_RK3288_CLOCK_H__ */ diff --git a/src/soc/rockchip/rk3288/include/soc/display.h b/src/soc/rockchip/rk3288/include/soc/display.h new file mode 100644 index 0000000000..8353c8a1b3 --- /dev/null +++ b/src/soc/rockchip/rk3288/include/soc/display.h @@ -0,0 +1,30 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 Rockchip Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __SOC_ROCKCHIP_RK3288_DISPLAY_H__ +#define __SOC_ROCKCHIP_RK3288_DISPLAY_H__ + +void rk_display_init(device_t dev, u32 lcdbase, + unsigned long fb_size); + +#endif + + + + diff --git a/src/soc/rockchip/rk3288/include/soc/edp.h b/src/soc/rockchip/rk3288/include/soc/edp.h new file mode 100644 index 0000000000..fac1b81f31 --- /dev/null +++ b/src/soc/rockchip/rk3288/include/soc/edp.h @@ -0,0 +1,666 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 Rockchip Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __RK32_DP_H +#define __RK32_DP_H + +#include <edid.h> +#include <stdlib.h> + +struct rk3288_edp_regs { + u8 res0[0x10]; + u32 dp_tx_version; + u8 res1[0x4]; + u32 func_en_1; + u32 func_en_2; + u32 video_ctl_1; + u32 video_ctl_2; + u32 video_ctl_3; + u32 video_ctl_4; + u8 res2[0xc]; + u32 video_ctl_8; + u8 res3[0x4]; + u32 video_ctl_10; + u32 total_line_l; + u32 total_line_h; + u32 active_line_l; + u32 active_line_h; + u32 v_f_porch; + u32 vsync; + u32 v_b_porch; + u32 total_pixel_l; + u32 total_pixel_h; + u32 active_pixel_l; + u32 active_pixel_h; + u32 h_f_porch_l; + u32 h_f_porch_h; + u32 hsync_l; + u32 hysnc_h; + u32 h_b_porch_l; + u32 h_b_porch_h; + u32 vid_status; + u32 total_line_sta_l; + u32 total_line_sta_h; + u32 active_line_sta_l; + u32 active_line_sta_h; + u32 v_f_porch_sta; + u32 vsync_sta; + u32 v_b_porch_sta; + u32 total_pixel_sta_l; + u32 total_pixel_sta_h; + u32 active_pixel_sta_l; + u32 active_pixel_sta_h; + u32 h_f_porch_sta_l; + u32 h_f_porch_sta_h; + u32 hsync_sta_l; + u32 hsync_sta_h; + u32 h_b_porch_sta_l; + u32 h_b_porch__sta_h; + u8 res4[0x28]; + u32 pll_reg_1; + u8 res5[4]; + u32 ssc_reg; + u8 res6[0xc]; + u32 tx_common; + u32 tx_common2; + u8 res7[0x4]; + u32 dp_aux; + u32 dp_bias; + u32 dp_test; + u32 dp_pd; + u32 dp_reserv1; + u32 dp_reserv2; + u8 res8[0x224]; + u32 lane_map; + u8 res9[0x14]; + u32 analog_ctl_2; + u8 res10[0x48]; + u32 int_state; + u32 common_int_sta_1; + u32 common_int_sta_2; + u32 common_int_sta_3; + u32 common_int_sta_4; + u32 spdif_biphase_int_sta; + u8 res11[0x4]; + u32 dp_int_sta; + u32 common_int_mask_1; + u32 common_int_mask_2; + u32 common_int_mask_3; + u32 common_int_mask_4; + u8 res12[0x08]; + u32 int_sta_mask; + u32 int_ctl; + u8 res13[0x200]; + u32 sys_ctl_1; + u32 sys_ctl_2; + u32 sys_ctl_3; + u32 sys_ctl_4; + u32 dp_vid_ctl; + u8 res14[0x4]; + u32 dp_aud_ctl; + u8 res15[0x24]; + u32 pkt_send_ctl; + u8 res16[0x4]; + u32 dp_hdcp_ctl; + u8 res17[0x34]; + u32 link_bw_set; + u32 lane_count_set; + u32 dp_training_ptn_set; + u32 ln_link_trn_ctl[4]; + u8 res18[0x4]; + u32 dp_hw_link_training; + u8 res19[0x1c]; + u32 dp_debug_ctl; + u32 hpd_deglitch_l; + u32 hpd_deglitch_h; + u8 res20[0x14]; + u32 dp_link_debug_ctl; + u8 res21[0x1c]; + u32 m_vid_0; + u32 m_vid_1; + u32 m_vid_2; + u32 n_vid_0; + u32 n_vid_1; + u32 n_vid_2; + u32 m_vid_mon; + u8 res22[0x14]; + u32 dp_video_fifo_thrd; + u8 res23[0x8]; + u32 dp_audio_margin; + u8 res24[0x20]; + u32 dp_m_cal_ctl; + u32 m_vid_gen_filter_th; + u8 res25[0x10]; + u32 m_aud_gen_filter_th; + u8 res26[0x4]; + u32 aux_ch_sta; + u32 aux_err_num; + u32 aux_ch_defer_dtl; + u32 aux_rx_comm; + u32 buf_data_ctl; + u32 aux_ch_ctl_1; + u32 aux_addr_7_0; + u32 aux_addr_15_8; + u32 aux_addr_19_16; + u32 aux_ch_ctl_2; + u8 res27[0x18]; + u32 buf_data[16]; + u32 soc_general_ctl; + u8 res29[0x1e0]; + u32 pll_reg_2; + u32 pll_reg_3; + u32 pll_reg_4; + u8 res30[0x10]; + u32 pll_reg_5; +}; +check_member(rk3288_edp_regs, pll_reg_5, 0xa00); + +/* func_en_1 */ +#define VID_CAP_FUNC_EN_N (0x1 << 6) +#define VID_FIFO_FUNC_EN_N (0x1 << 5) +#define AUD_FIFO_FUNC_EN_N (0x1 << 4) +#define AUD_FUNC_EN_N (0x1 << 3) +#define HDCP_FUNC_EN_N (0x1 << 2) +#define SW_FUNC_EN_N (0x1 << 0) + +/* func_en_2 */ +#define SSC_FUNC_EN_N (0x1 << 7) +#define AUX_FUNC_EN_N (0x1 << 2) +#define SERDES_FIFO_FUNC_EN_N (0x1 << 1) +#define LS_CLK_DOMAIN_FUNC_EN_N (0x1 << 0) + +/* video_ctl_1 */ +#define VIDEO_EN (0x1 << 7) +#define VIDEO_MUTE (0x1 << 6) + +/* video_ctl_2 */ +#define IN_D_RANGE_MASK (0x1 << 7) +#define IN_D_RANGE_SHIFT (7) +#define IN_D_RANGE_CEA (0x1 << 7) +#define IN_D_RANGE_VESA (0x0 << 7) +#define IN_BPC_MASK (0x7 << 4) +#define IN_BPC_SHIFT (4) +#define IN_BPC_12_BITS (0x3 << 4) +#define IN_BPC_10_BITS (0x2 << 4) +#define IN_BPC_8_BITS (0x1 << 4) +#define IN_BPC_6_BITS (0x0 << 4) +#define IN_COLOR_F_MASK (0x3 << 0) +#define IN_COLOR_F_SHIFT (0) +#define IN_COLOR_F_YCBCR444 (0x2 << 0) +#define IN_COLOR_F_YCBCR422 (0x1 << 0) +#define IN_COLOR_F_RGB (0x0 << 0) + +/* video_ctl_3 */ +#define IN_YC_COEFFI_MASK (0x1 << 7) +#define IN_YC_COEFFI_SHIFT (7) +#define IN_YC_COEFFI_ITU709 (0x1 << 7) +#define IN_YC_COEFFI_ITU601 (0x0 << 7) +#define VID_CHK_UPDATE_TYPE_MASK (0x1 << 4) +#define VID_CHK_UPDATE_TYPE_SHIFT (4) +#define VID_CHK_UPDATE_TYPE_1 (0x1 << 4) +#define VID_CHK_UPDATE_TYPE_0 (0x0 << 4) + +/* video_ctl_4 */ +#define BIST_EN (0x1 << 3) +#define BIST_WH_64 (0x1 << 2) +#define BIST_WH_32 (0x0 << 2) +#define BIST_TYPE_COLR_BAR (0x0 << 0) +#define BIST_TYPE_GRAY_BAR (0x1 << 0) +#define BIST_TYPE_MOBILE_BAR (0x2 << 0) + +/* video_ctl_8 */ +#define VID_HRES_TH(x) (((x) & 0xf) << 4) +#define VID_VRES_TH(x) (((x) & 0xf) << 0) + +/* video_ctl_10 */ +#define F_SEL (0x1 << 4) +#define INTERACE_SCAN_CFG (0x1 << 2) +#define INTERACD_SCAN_CFG_OFFSET 2 +#define VSYNC_POLARITY_CFG (0x1 << 1) +#define VSYNC_POLARITY_CFG_OFFSET 1 +#define HSYNC_POLARITY_CFG (0x1 << 0) +#define HSYNC_POLARITY_CFG_OFFSET 0 + +/* dp_pd */ +#define PD_INC_BG (0x1 << 7) +#define PD_EXP_BG (0x1 << 6) +#define PD_AUX (0x1 << 5) +#define PD_PLL (0x1 << 4) +#define PD_CH3 (0x1 << 3) +#define PD_CH2 (0x1 << 2) +#define PD_CH1 (0x1 << 1) +#define PD_CH0 (0x1 << 0) + +/* pll_reg_1 */ +#define REF_CLK_24M (0x1 << 1) +#define REF_CLK_27M (0x0 << 1) + +/* line_map */ +#define LANE3_MAP_LOGIC_LANE_0 (0x0 << 6) +#define LANE3_MAP_LOGIC_LANE_1 (0x1 << 6) +#define LANE3_MAP_LOGIC_LANE_2 (0x2 << 6) +#define LANE3_MAP_LOGIC_LANE_3 (0x3 << 6) +#define LANE2_MAP_LOGIC_LANE_0 (0x0 << 4) +#define LANE2_MAP_LOGIC_LANE_1 (0x1 << 4) +#define LANE2_MAP_LOGIC_LANE_2 (0x2 << 4) +#define LANE2_MAP_LOGIC_LANE_3 (0x3 << 4) +#define LANE1_MAP_LOGIC_LANE_0 (0x0 << 2) +#define LANE1_MAP_LOGIC_LANE_1 (0x1 << 2) +#define LANE1_MAP_LOGIC_LANE_2 (0x2 << 2) +#define LANE1_MAP_LOGIC_LANE_3 (0x3 << 2) +#define LANE0_MAP_LOGIC_LANE_0 (0x0 << 0) +#define LANE0_MAP_LOGIC_LANE_1 (0x1 << 0) +#define LANE0_MAP_LOGIC_LANE_2 (0x2 << 0) +#define LANE0_MAP_LOGIC_LANE_3 (0x3 << 0) + +/* analog_ctl_2 */ +#define SEL_24M (0x1 << 3) + +/* common_int_sta_1 */ +#define VSYNC_DET (0x1 << 7) +#define PLL_LOCK_CHG (0x1 << 6) +#define SPDIF_ERR (0x1 << 5) +#define SPDIF_UNSTBL (0x1 << 4) +#define VID_FORMAT_CHG (0x1 << 3) +#define AUD_CLK_CHG (0x1 << 2) +#define VID_CLK_CHG (0x1 << 1) +#define SW_INT (0x1 << 0) + +/* common_int_sta_2 */ +#define ENC_EN_CHG (0x1 << 6) +#define HW_BKSV_RDY (0x1 << 3) +#define HW_SHA_DONE (0x1 << 2) +#define HW_AUTH_STATE_CHG (0x1 << 1) +#define HW_AUTH_DONE (0x1 << 0) + +/* common_int_sta_3 */ +#define AFIFO_UNDER (0x1 << 7) +#define AFIFO_OVER (0x1 << 6) +#define R0_CHK_FLAG (0x1 << 5) + +/* common_int_sta_4 */ +#define PSR_ACTIVE (0x1 << 7) +#define PSR_INACTIVE (0x1 << 6) +#define SPDIF_BI_PHASE_ERR (0x1 << 5) +#define HOTPLUG_CHG (0x1 << 2) +#define HPD_LOST (0x1 << 1) +#define PLUG (0x1 << 0) + +/* dp_int_sta */ +#define INT_HPD (0x1 << 6) +#define HW_LT_DONE (0x1 << 5) +#define SINK_LOST (0x1 << 3) +#define LINK_LOST (0x1 << 2) +#define RPLY_RECEIV (0x1 << 1) +#define AUX_ERR (0x1 << 0) + +/* int_ctl */ +#define SOFT_INT_CTRL (0x1 << 2) +#define INT_POL (0x1 << 0) + +/* sys_ctl_1 */ +#define DET_STA (0x1 << 2) +#define FORCE_DET (0x1 << 1) +#define DET_CTRL (0x1 << 0) + +/* sys_ctl_2 */ +#define CHA_CRI(x) (((x) & 0xf) << 4) +#define CHA_STA (0x1 << 2) +#define FORCE_CHA (0x1 << 1) +#define CHA_CTRL (0x1 << 0) + +/* sys_ctl_3 */ +#define HPD_STATUS (0x1 << 6) +#define F_HPD (0x1 << 5) +#define HPD_CTRL (0x1 << 4) +#define HDCP_RDY (0x1 << 3) +#define STRM_VALID (0x1 << 2) +#define F_VALID (0x1 << 1) +#define VALID_CTRL (0x1 << 0) + +/* sys_ctl_4 */ +#define FIX_M_AUD (0x1 << 4) +#define ENHANCED (0x1 << 3) +#define FIX_M_VID (0x1 << 2) +#define M_VID_UPDATE_CTRL (0x3 << 0) + +/* pll_reg_2 */ +#define LDO_OUTPUT_V_SEL_145 (2 << 6) +#define KVCO_DEFALUT (1 << 4) +#define CHG_PUMP_CUR_SEL_5US (1 << 2) +#define V2L_CUR_SEL_1MA (1 << 0) + +/* pll_reg_3 */ +#define LOCK_DET_CNT_SEL_256 (2 << 5) +#define LOOP_FILTER_RESET (0 << 4) +#define PALL_SSC_RESET (0 << 3) +#define LOCK_DET_BYPASS (0 << 2) +#define PLL_LOCK_DET_MODE (0 << 1) +#define PLL_LOCK_DET_FORCE (0 << 0) + +/* pll_reg_5 */ +#define REGULATOR_V_SEL_950MV (2 << 4) +#define STANDBY_CUR_SEL (0 << 3) +#define CHG_PUMP_INOUT_CTRL_1200MV (1 << 1) +#define CHG_PUMP_INPUT_CTRL_OP (0 << 0) + +/* ssc_reg */ +#define SSC_OFFSET (0 << 6) +#define SSC_MODE (1 << 4) +#define SSC_DEPTH (9 << 0) + +/* tx_common */ +#define TX_SWING_PRE_EMP_MODE (1 << 7) +#define PRE_DRIVER_PW_CTRL1 (0 << 5) +#define LP_MODE_CLK_REGULATOR (0 << 4) +#define RESISTOR_MSB_CTRL (0 << 3) +#define RESISTOR_CTRL (7 << 0) + +/* dp_aux */ +#define DP_AUX_COMMON_MODE (0 << 4) +#define DP_AUX_EN (0 << 3) +#define AUX_TERM_50OHM (3 << 0) + +/* dp_bias */ +#define DP_BG_OUT_SEL (4 << 4) +#define DP_DB_CUR_CTRL (0 << 3) +#define DP_BG_SEL (1 << 2) +#define DP_RESISTOR_TUNE_BG (2 << 0) + +/* dp_reserv2 */ +#define CH1_CH3_SWING_EMP_CTRL (5 << 4) +#define CH0_CH2_SWING_EMP_CTRL (5 << 0) + +/* dp_training_ptn_set */ +#define SCRAMBLING_DISABLE (0x1 << 5) +#define SCRAMBLING_ENABLE (0x0 << 5) +#define LINK_QUAL_PATTERN_SET_MASK (0x7 << 2) +#define LINK_QUAL_PATTERN_SET_HBR2 (0x5 << 2) +#define LINK_QUAL_PATTERN_SET_80BIT (0x4 << 2) +#define LINK_QUAL_PATTERN_SET_PRBS7 (0x3 << 2) +#define LINK_QUAL_PATTERN_SET_D10_2 (0x1 << 2) +#define LINK_QUAL_PATTERN_SET_DISABLE (0x0 << 2) +#define SW_TRAINING_PATTERN_SET_MASK (0x3 << 0) +#define SW_TRAINING_PATTERN_SET_PTN2 (0x2 << 0) +#define SW_TRAINING_PATTERN_SET_PTN1 (0x1 << 0) +#define SW_TRAINING_PATTERN_SET_DISABLE (0x0 << 0) + +/* dp_hw_link_training_ctl */ +#define HW_LT_ERR_CODE_MASK 0x70 +#define HW_LT_ERR_CODE_SHIFT 4 +#define HW_LT_EN (0x1 << 0) + +/* dp_debug_ctl */ +#define PLL_LOCK (0x1 << 4) +#define F_PLL_LOCK (0x1 << 3) +#define PLL_LOCK_CTRL (0x1 << 2) +#define POLL_EN (0x1 << 1) +#define PN_INV (0x1 << 0) + +/* aux_ch_sta */ +#define AUX_BUSY (0x1 << 4) +#define AUX_STATUS_MASK (0xf << 0) + +/* aux_ch_defer_ctl */ +#define DEFER_CTRL_EN (0x1 << 7) +#define DEFER_COUNT(x) (((x) & 0x7f) << 0) + +/* aux_rx_comm */ +#define AUX_RX_COMM_I2C_DEFER (0x2 << 2) +#define AUX_RX_COMM_AUX_DEFER (0x2 << 0) + +/* buffer_data_ctl */ +#define BUF_CLR (0x1 << 7) +#define BUF_HAVE_DATA (0x1 << 4) +#define BUF_DATA_COUNT(x) (((x) & 0xf) << 0) + +/* aux_ch_ctl_1 */ +#define AUX_LENGTH(x) (((x - 1) & 0xf) << 4) +#define AUX_TX_COMM_MASK (0xf << 0) +#define AUX_TX_COMM_DP_TRANSACTION (0x1 << 3) +#define AUX_TX_COMM_I2C_TRANSACTION (0x0 << 3) +#define AUX_TX_COMM_MOT (0x1 << 2) +#define AUX_TX_COMM_WRITE (0x0 << 0) +#define AUX_TX_COMM_READ (0x1 << 0) + +/* aux_ch_ctl_2 */ +#define PD_AUX_IDLE (0x1 << 3) +#define ADDR_ONLY (0x1 << 1) +#define AUX_EN (0x1 << 0) + +/* tx_sw_reset */ +#define RST_DP_TX (0x1 << 0) + +/* analog_ctl_1 */ +#define TX_TERMINAL_CTRL_50_OHM (0x1 << 4) + +/* analog_ctl_3 */ +#define DRIVE_DVDD_BIT_1_0625V (0x4 << 5) +#define VCO_BIT_600_MICRO (0x5 << 0) + +/* pll_filter_ctl_1 */ +#define PD_RING_OSC (0x1 << 6) +#define AUX_TERMINAL_CTRL_37_5_OHM (0x0 << 4) +#define AUX_TERMINAL_CTRL_45_OHM (0x1 << 4) +#define AUX_TERMINAL_CTRL_50_OHM (0x2 << 4) +#define AUX_TERMINAL_CTRL_65_OHM (0x3 << 4) +#define TX_CUR1_2X (0x1 << 2) +#define TX_CUR_16_MA (0x3 << 0) + +/* Definition for DPCD Register */ +#define DPCD_DPCD_REV (0x0000) +#define DPCD_MAX_LINK_RATE (0x0001) +#define DPCD_MAX_LANE_COUNT (0x0002) +#define DP_MAX_LANE_COUNT_MASK 0x1f +#define DP_TPS3_SUPPORTED (1 << 6) +#define DP_ENHANCED_FRAME_CAP (1 << 7) + +#define DPCD_LINK_BW_SET (0x0100) +#define DPCD_LANE_COUNT_SET (0x0101) + +#define DPCD_TRAINING_PATTERN_SET (0x0102) +#define DP_TRAINING_PATTERN_DISABLE 0 +#define DP_TRAINING_PATTERN_1 1 +#define DP_TRAINING_PATTERN_2 2 +#define DP_TRAINING_PATTERN_3 3 +#define DP_TRAINING_PATTERN_MASK 0x3 + +#define DPCD_TRAINING_LANE0_SET (0x0103) +#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_400 (0 << 0) +#define DP_TRAIN_VOLTAGE_SWING_600 (1 << 0) +#define DP_TRAIN_VOLTAGE_SWING_800 (2 << 0) +#define DP_TRAIN_VOLTAGE_SWING_1200 (3 << 0) + +#define DP_TRAIN_PRE_EMPHASIS_MASK (3 << 3) +#define DP_TRAIN_PRE_EMPHASIS_0 (0 << 3) +#define DP_TRAIN_PRE_EMPHASIS_3_5 (1 << 3) +#define DP_TRAIN_PRE_EMPHASIS_6 (2 << 3) +#define DP_TRAIN_PRE_EMPHASIS_9_5 (3 << 3) + +#define DP_TRAIN_PRE_EMPHASIS_SHIFT 3 +#define DP_TRAIN_MAX_PRE_EMPHASIS_REACHED (1 << 5) + +#define DPCD_LANE0_1_STATUS (0x0202) +#define DPCD_LANE2_3_STATUS (0x0203) +#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 DPCD_LANE_ALIGN_STATUS_UPDATED (0x0204) +#define DP_INTERLANE_ALIGN_DONE (1 << 0) +#define DP_DOWNSTREAM_PORT_STATUS_CHANGED (1 << 6) +#define DP_LINK_STATUS_UPDATED (1 << 7) + +#define DPCD_ADJUST_REQUEST_LANE0_1 (0x0206) +#define DPCD_ADJUST_REQUEST_LANE2_3 (0x0207) +#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 DPCD_TEST_REQUEST (0x0218) +#define DPCD_TEST_RESPONSE (0x0260) +#define DPCD_TEST_EDID_CHECKSUM (0x0261) +#define DPCD_LINK_POWER_STATE (0x0600) +#define DP_SET_POWER_D0 0x1 +#define DP_SET_POWER_D3 0x2 +#define DP_SET_POWER_MASK 0x3 + +#define AUX_ADDR_7_0(x) (((x) >> 0) & 0xff) +#define AUX_ADDR_15_8(x) (((x) >> 8) & 0xff) +#define AUX_ADDR_19_16(x) (((x) >> 16) & 0x0f) + +#define STREAM_ON_TIMEOUT 100 +#define PLL_LOCK_TIMEOUT 10 +#define DP_INIT_TRIES 10 + +#define EDID_ADDR 0x50 +#define EDID_LENGTH 0x80 +#define EDID_HEADER 0x00 +#define EDID_EXTENSION_FLAG 0x7e + + +enum dpcd_request { + DPCD_READ, + DPCD_WRITE, +}; + +enum dp_irq_type { + DP_IRQ_TYPE_HP_CABLE_IN, + DP_IRQ_TYPE_HP_CABLE_OUT, + DP_IRQ_TYPE_HP_CHANGE, + DP_IRQ_TYPE_UNKNOWN, +}; + +enum color_coefficient { + COLOR_YCBCR601, + COLOR_YCBCR709 +}; + +enum dynamic_range { + VESA, + CEA +}; + +enum pll_status { + DP_PLL_UNLOCKED, + DP_PLL_LOCKED +}; + +enum clock_recovery_m_value_type { + CALCULATED_M, + REGISTER_M +}; + +enum video_timing_recognition_type { + VIDEO_TIMING_FROM_CAPTURE, + VIDEO_TIMING_FROM_REGISTER +}; + +enum pattern_set { + PRBS7, + D10_2, + TRAINING_PTN1, + TRAINING_PTN2, + DP_NONE +}; + +enum color_space { + CS_RGB, + CS_YCBCR422, + CS_YCBCR444 +}; + +enum color_depth { + COLOR_6, + COLOR_8, + COLOR_10, + COLOR_12 +}; + +enum link_rate_type { + LINK_RATE_1_62GBPS = 0x06, + LINK_RATE_2_70GBPS = 0x0a +}; + +enum link_lane_count_type { + LANE_CNT1 = 1, + LANE_CNT2 = 2, + LANE_CNT4 = 4 +}; + +enum link_training_state { + LT_START, + LT_CLK_RECOVERY, + LT_EQ_TRAINING, + FINISHED, + FAILED +}; + +enum voltage_swing_level { + VOLTAGE_LEVEL_0, + VOLTAGE_LEVEL_1, + VOLTAGE_LEVEL_2, + VOLTAGE_LEVEL_3, +}; + +enum pre_emphasis_level { + PRE_EMPHASIS_LEVEL_0, + PRE_EMPHASIS_LEVEL_1, + PRE_EMPHASIS_LEVEL_2, + PRE_EMPHASIS_LEVEL_3, +}; + +enum analog_power_block { + AUX_BLOCK, + CH0_BLOCK, + CH1_BLOCK, + CH2_BLOCK, + CH3_BLOCK, + ANALOG_TOTAL, + POWER_ALL +}; + +struct link_train { + unsigned char revision; + u8 link_rate; + u8 lane_count; +}; + +struct rk_edp { + struct rk3288_edp_regs *regs; + struct link_train link_train; + u8 train_set[4]; +}; + +int rk_edp_enable(void); +void rk_edp_init(u32 vop_id); +int rk_edp_get_edid(struct edid *edid); + +#endif diff --git a/src/soc/rockchip/rk3288/include/soc/soc.h b/src/soc/rockchip/rk3288/include/soc/soc.h index 00f35b2f3c..c6ab7f46f6 100644 --- a/src/soc/rockchip/rk3288/include/soc/soc.h +++ b/src/soc/rockchip/rk3288/include/soc/soc.h @@ -27,7 +27,7 @@ #define RK_SETBITS(set) RK_CLRSETBITS(0, set) #define RK_CLRBITS(clr) RK_CLRSETBITS(clr, 0) -#define FB_SIZE_KB 4096 +#define FB_SIZE_KB 8192 #define RAM_BASE_KB ((uintptr_t)_dram >> 10) #define RAM_SIZE_KB (CONFIG_DRAM_SIZE_MB << 10UL) diff --git a/src/soc/rockchip/rk3288/include/soc/vop.h b/src/soc/rockchip/rk3288/include/soc/vop.h new file mode 100644 index 0000000000..3766a7b879 --- /dev/null +++ b/src/soc/rockchip/rk3288/include/soc/vop.h @@ -0,0 +1,356 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 Rockchip Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _RK3288_LCD_H_ +#define _RK3288_LCD_H_ +#include <stdint.h> +#include <edid.h> + +struct rk3288_vop_regs { + u32 reg_cfg_done; + u32 version_info; + u32 sys_ctrl; + u32 sys_ctrl1; + u32 dsp_ctrl0; + u32 dsp_ctrl1; + u32 dsp_bg; + u32 mcu_ctrl; + u32 intr_ctrl0; + u32 intr_ctrl1; + u32 intr_reserved0; + u32 intr_reserved1; + + u32 win0_ctrl0; + u32 win0_ctrl1; + u32 win0_color_key; + u32 win0_vir; + u32 win0_yrgb_mst; + u32 win0_cbr_mst; + u32 win0_act_info; + u32 win0_dsp_info; + u32 win0_dsp_st; + u32 win0_scl_factor_yrgb; + u32 win0_scl_factor_cbr; + u32 win0_scl_offset; + u32 win0_src_alpha_ctrl; + u32 win0_dst_alpha_ctrl; + u32 win0_fading_ctrl; + u32 win0_reserved0; + + u32 win1_ctrl0; + u32 win1_ctrl1; + u32 win1_color_key; + u32 win1_vir; + u32 win1_yrgb_mst; + u32 win1_cbr_mst; + u32 win1_act_info; + u32 win1_dsp_info; + u32 win1_dsp_st; + u32 win1_scl_factor_yrgb; + u32 win1_scl_factor_cbr; + u32 win1_scl_offset; + u32 win1_src_alpha_ctrl; + u32 win1_dst_alpha_ctrl; + u32 win1_fading_ctrl; + u32 win1_reservd0; + u32 reserved2[48]; + u32 post_dsp_hact_info; + u32 post_dsp_vact_info; + u32 post_scl_factor_yrgb; + u32 post_reserved; + u32 post_scl_ctrl; + u32 post_dsp_vact_info_f1; + u32 dsp_htotal_hs_end; + u32 dsp_hact_st_end; + u32 dsp_vtotal_vs_end; + u32 dsp_vact_st_end; + u32 dsp_vs_st_end_f1; + u32 dsp_vact_st_end_f1; +}; +check_member(rk3288_vop_regs, dsp_vact_st_end_f1, 0x19c); + +enum rockchip_fb_data_format_t { + ARGB8888 = 0, + RGB888 = 1, + RGB565 = 2, +}; + +enum { + LB_YUV_3840X5 = 0x0, + LB_YUV_2560X8 = 0x1, + LB_RGB_3840X2 = 0x2, + LB_RGB_2560X4 = 0x3, + LB_RGB_1920X5 = 0x4, + LB_RGB_1280X8 = 0x5 +}; + +/* VOP_VERSION_INFO */ +#define M_FPGA_VERSION (0xffff << 16) +#define M_RTL_VERSION (0xffff) + +/* VOP_SYS_CTRL */ +#define M_AUTO_GATING_EN (1 << 23) +#define M_STANDBY_EN (1 << 22) +#define M_DMA_STOP (1 << 21) +#define M_MMU_EN (1 << 20) +#define M_DAM_BURST_LENGTH (0x3 << 18) +#define M_MIPI_OUT_EN (1 << 15) +#define M_EDP_OUT_EN (1 << 14) +#define M_HDMI_OUT_EN (1 << 13) +#define M_RGB_OUT_EN (1 << 12) +#define M_ALL_OUT_EN (M_MIPI_OUT_EN | M_EDP_OUT_EN | M_HDMI_OUT_EN | M_RGB_OUT_EN) +#define M_EDPI_WMS_FS (1 << 10) +#define M_EDPI_WMS_MODE (1 << 9) +#define M_EDPI_HALT_EN (1 << 8) +#define M_DOUB_CH_OVERLAP_NUM (0xf << 4) +#define M_DOUB_CHANNEL_EN (1 << 3) +#define M_DIRECT_PATH_LAYER_SEL (0x3 << 1) +#define M_DIRECT_PATH_EN (1) + +#define V_AUTO_GATING_EN(x) (((x) & 1) << 23) +#define V_STANDBY_EN(x) (((x) & 1) << 22) +#define V_DMA_STOP(x) (((x) & 1) << 21) +#define V_MMU_EN(x) (((x) & 1) << 20) +#define V_DMA_BURST_LENGTH(x) (((x) & 3) << 18) +#define V_MIPI_OUT_EN(x) (((x) & 1) << 15) +#define V_EDP_OUT_EN(x) (((x) & 1) << 14) +#define V_HDMI_OUT_EN(x) (((x) & 1) << 13) +#define V_RGB_OUT_EN(x) (((x) & 1) << 12) +#define V_EDPI_WMS_FS(x) (((x) & 1) << 10) +#define V_EDPI_WMS_MODE(x) (((x) & 1) << 9) +#define V_EDPI_HALT_EN(x) (((x)&1)<<8) +#define V_DOUB_CH_OVERLAP_NUM(x) (((x) & 0xf) << 4) +#define V_DOUB_CHANNEL_EN(x) (((x) & 1) << 3) +#define V_DIRECT_PATH_LAYER_SEL(x) (((x) & 3) << 1) +#define V_DIRECT_PATH_EN(x) ((x) & 1) + +/* VOP_SYS_CTRL1 */ +#define M_AXI_OUTSTANDING_MAX_NUM (0x1f << 13) +#define M_AXI_MAX_OUTSTANDING_EN (1 << 12) +#define M_NOC_WIN_QOS (3 << 10) +#define M_NOC_QOS_EN (1 << 9) +#define M_NOC_HURRY_THRESHOLD (0x3f << 3) +#define M_NOC_HURRY_VALUE (0x3 << 1) +#define M_NOC_HURRY_EN (1) + +#define V_AXI_OUTSTANDING_MAX_NUM(x) (((x) & 0x1f) << 13) +#define V_AXI_MAX_OUTSTANDING_EN(x) (((x) & 1) << 12) +#define V_NOC_WIN_QOS(x) (((x) & 3) << 10) +#define V_NOC_QOS_EN(x) (((x) & 1) << 9) +#define V_NOC_HURRY_THRESHOLD(x) (((x) & 0x3f) << 3) +#define V_NOC_HURRY_VALUE(x) (((x) & 3) << 1) +#define V_NOC_HURRY_EN(x) ((x) & 1) + +/* VOP_DSP_CTRL0 */ +#define M_DSP_Y_MIR_EN (1 << 23) +#define M_DSP_X_MIR_EN (1 << 22) +#define M_DSP_YUV_CLIP (1 << 21) +#define M_DSP_CCIR656_AVG (1 << 20) +#define M_DSP_BLACK_EN (1 << 19) +#define M_DSP_BLANK_EN (1 << 18) +#define M_DSP_OUT_ZERO (1 << 17) +#define M_DSP_DUMMY_SWAP (1 << 16) +#define M_DSP_DELTA_SWAP (1 << 15) +#define M_DSP_RG_SWAP (1 << 14) +#define M_DSP_RB_SWAP (1 << 13) +#define M_DSP_BG_SWAP (1 << 12) +#define M_DSP_FIELD_POL (1 << 11) +#define M_DSP_INTERLACE (1 << 10) +#define M_DSP_DDR_PHASE (1 << 9) +#define M_DSP_DCLK_DDR (1 << 8) +#define M_DSP_DCLK_POL (1 << 7) +#define M_DSP_DEN_POL (1 << 6) +#define M_DSP_VSYNC_POL (1 << 5) +#define M_DSP_HSYNC_POL (1 << 4) +#define M_DSP_OUT_MODE (0xf) + +#define V_DSP_Y_MIR_EN(x) (((x) & 1) << 23) +#define V_DSP_X_MIR_EN(x) (((x) & 1) << 22) +#define V_DSP_YUV_CLIP(x) (((x) & 1) << 21) +#define V_DSP_CCIR656_AVG(x) (((x) & 1) << 20) +#define V_DSP_BLACK_EN(x) (((x) & 1) << 19) +#define V_DSP_BLANK_EN(x) (((x) & 1) << 18) +#define V_DSP_OUT_ZERO(x) (((x) & 1) << 17) +#define V_DSP_DUMMY_SWAP(x) (((x) & 1) << 16) +#define V_DSP_DELTA_SWAP(x) (((x) & 1) << 15) +#define V_DSP_RG_SWAP(x) (((x) & 1) << 14) +#define V_DSP_RB_SWAP(x) (((x) & 1) << 13) +#define V_DSP_BG_SWAP(x) (((x) & 1) << 12) +#define V_DSP_FIELD_POL(x) (((x) & 1) << 11) +#define V_DSP_INTERLACE(x) (((x) & 1) << 10) +#define V_DSP_DDR_PHASE(x) (((x) & 1) << 9) +#define V_DSP_DCLK_DDR(x) (((x) & 1) << 8) +#define V_DSP_DCLK_POL(x) (((x) & 1) << 7) +#define V_DSP_DEN_POL(x) (((x) & 1) << 6) +#define V_DSP_VSYNC_POL(x) (((x) & 1) << 5) +#define V_DSP_HSYNC_POL(x) (((x) & 1) << 4) +#define V_DSP_OUT_MODE(x) ((x) & 0xf) + +/* VOP_DSP_CTRL1 */ +#define M_DSP_LAYER3_SEL (3 << 14) +#define M_DSP_LAYER2_SEL (3 << 12) +#define M_DSP_LAYER1_SEL (3 << 10) +#define M_DSP_LAYER0_SEL (3 << 8) +#define M_DITHER_UP_EN (1 << 6) +#define M_DITHER_DOWN_SEL (1 << 4) +#define M_DITHER_DOWN_MODE (1 << 3) +#define M_DITHER_DOWN_EN (1 << 2) +#define M_PRE_DITHER_DOWN_EN (1 << 1) +#define M_DSP_LUT_EN (1) + +#define V_DSP_LAYER3_SEL(x) (((x) & 3) << 14) +#define V_DSP_LAYER2_SEL(x) (((x) & 3) << 12) +#define V_DSP_LAYER1_SEL(x) (((x) & 3) << 10) +#define V_DSP_LAYER0_SEL(x) (((x) & 3) << 8) +#define V_DITHER_UP_EN(x) (((x) & 1) << 6) +#define V_DITHER_DOWN_SEL(x) (((x) & 1) << 4) +#define V_DITHER_DOWN_MODE(x) (((x) & 1) << 3) +#define V_DITHER_DOWN_EN(x) (((x) & 1) << 2) +#define V_PRE_DITHER_DOWN_EN(x) (((x) & 1) << 1) +#define V_DSP_LUT_EN(x) ((x)&1) + +/* VOP_DSP_BG */ +#define M_DSP_BG_RED (0x3f << 20) +#define M_DSP_BG_GREEN (0x3f << 10) +#define M_DSP_BG_BLUE (0x3f << 0) + +#define V_DSP_BG_RED(x) (((x) & 0x3f) << 20) +#define V_DSP_BG_GREEN(x) (((x) & 0x3f) << 10) +#define V_DSP_BG_BLUE(x) (((x) & 0x3f) << 0) + +/* VOP_WIN0_CTRL0 */ +#define M_WIN0_YUV_CLIP (1 << 20) +#define M_WIN0_CBR_DEFLICK (1 << 19) +#define M_WIN0_YRGB_DEFLICK (1 << 18) +#define M_WIN0_PPAS_ZERO_EN (1 << 16) +#define M_WIN0_UV_SWAP (1 << 15) +#define M_WIN0_MID_SWAP (1 << 14) +#define M_WIN0_ALPHA_SWAP (1 << 13) +#define M_WIN0_RB_SWAP (1 << 12) +#define M_WIN0_CSC_MODE (3 << 10) +#define M_WIN0_NO_OUTSTANDING (1 << 9) +#define M_WIN0_INTERLACE_READ (1 << 8) +#define M_WIN0_LB_MODE (7 << 5) +#define M_WIN0_FMT_10 (1 << 4) +#define M_WIN0_DATA_FMT (7 << 1) +#define M_WIN0_EN (1 << 0) + +#define V_WIN0_YUV_CLIP(x) (((x) & 1) << 20) +#define V_WIN0_CBR_DEFLICK(x) (((x) & 1) << 19) +#define V_WIN0_YRGB_DEFLICK(x) (((x) & 1) << 18) +#define V_WIN0_PPAS_ZERO_EN(x) (((x) & 1) << 16) +#define V_WIN0_UV_SWAP(x) (((x) & 1) << 15) +#define V_WIN0_MID_SWAP(x) (((x) & 1) << 14) +#define V_WIN0_ALPHA_SWAP(x) (((x) & 1) << 13) +#define V_WIN0_RB_SWAP(x) (((x) & 1) << 12) +#define V_WIN0_CSC_MODE(x) (((x) & 3) << 10) +#define V_WIN0_NO_OUTSTANDING(x) (((x) & 1) << 9) +#define V_WIN0_INTERLACE_READ(x) (((x) & 1) << 8) +#define V_WIN0_LB_MODE(x) (((x) & 7) << 5) +#define V_WIN0_FMT_10(x) (((x) & 1) << 4) +#define V_WIN0_DATA_FMT(x) (((x) & 7) << 1) +#define V_WIN0_EN(x) ((x) & 1) + +/* VOP_WIN0_CTRL1 */ +#define M_WIN0_CBR_VSD_MODE (1 << 31) +#define M_WIN0_CBR_VSU_MODE (1 << 30) +#define M_WIN0_CBR_HSD_MODE (3 << 28) +#define M_WIN0_CBR_VER_SCL_MODE (3 << 26) +#define M_WIN0_CBR_HOR_SCL_MODE (3 << 24) +#define M_WIN0_YRGB_VSD_MODE (1 << 23) +#define M_WIN0_YRGB_VSU_MODE (1 << 22) +#define M_WIN0_YRGB_HSD_MODE (3 << 20) +#define M_WIN0_YRGB_VER_SCL_MODE (3 << 18) +#define M_WIN0_YRGB_HOR_SCL_MODE (3 << 16) +#define M_WIN0_LINE_LOAD_MODE (1 << 15) +#define M_WIN0_CBR_AXI_GATHER_NUM (7 << 12) +#define M_WIN0_YRGB_AXI_GATHER_NUM (0xf << 8) +#define M_WIN0_VSD_CBR_GT2 (1 << 7) +#define M_WIN0_VSD_CBR_GT4 (1 << 6) +#define M_WIN0_VSD_YRGB_GT2 (1 << 5) +#define M_WIN0_VSD_YRGB_GT4 (1 << 4) +#define M_WIN0_BIC_COE_SEL (3 << 2) +#define M_WIN0_CBR_AXI_GATHER_EN (1 << 1) +#define M_WIN0_YRGB_AXI_GATHER_EN (1) + +#define V_WIN0_CBR_VSD_MODE(x) (((x) & 1) << 31) +#define V_WIN0_CBR_VSU_MODE(x) (((x) & 1) << 30) +#define V_WIN0_CBR_HSD_MODE(x) (((x) & 3) << 28) +#define V_WIN0_CBR_VER_SCL_MODE(x) (((x) & 3) << 26) +#define V_WIN0_CBR_HOR_SCL_MODE(x) (((x) & 3) << 24) +#define V_WIN0_YRGB_VSD_MODE(x) (((x) & 1) << 23) +#define V_WIN0_YRGB_VSU_MODE(x) (((x) & 1) << 22) +#define V_WIN0_YRGB_HSD_MODE(x) (((x) & 3) << 20) +#define V_WIN0_YRGB_VER_SCL_MODE(x) (((x) & 3) << 18) +#define V_WIN0_YRGB_HOR_SCL_MODE(x) (((x) & 3) << 16) +#define V_WIN0_LINE_LOAD_MODE(x) (((x) & 1) << 15) +#define V_WIN0_CBR_AXI_GATHER_NUM(x) (((x) & 7) << 12) +#define V_WIN0_YRGB_AXI_GATHER_NUM(x) (((x) & 0xf) << 8) +#define V_WIN0_VSD_CBR_GT2(x) (((x) & 1) << 7) +#define V_WIN0_VSD_CBR_GT4(x) (((x) & 1) << 6) +#define V_WIN0_VSD_YRGB_GT2(x) (((x) & 1) << 5) +#define V_WIN0_VSD_YRGB_GT4(x) (((x) & 1) << 4) +#define V_WIN0_BIC_COE_SEL(x) (((x) & 3) << 2) +#define V_WIN0_CBR_AXI_GATHER_EN(x) (((x) & 1) << 1) +#define V_WIN0_YRGB_AXI_GATHER_EN(x) ((x) & 1) + +/*VOP_WIN0_COLOR_KEY*/ +#define M_WIN0_KEY_EN (1 << 31) +#define M_WIN0_KEY_COLOR (0x3fffffff) + +#define V_WIN0_KEY_EN(x) (((x) & 1) << 31) +#define V_WIN0_KEY_COLOR(x) ((x) & 0x3fffffff) + +/* VOP_WIN0_VIR */ +#define V_ARGB888_VIRWIDTH(x) (((x) & 0x3fff) << 0) +#define V_RGB888_VIRWIDTH(x) (((((x * 3) >> 2)+((x) % 3)) & 0x3fff) << 0) +#define V_RGB565_VIRWIDTH(x) (((x / 2) & 0x3fff) << 0) +#define YUV_VIRWIDTH(x) (((x / 4) & 0x3fff) << 0) + +/* VOP_WIN0_ACT_INFO */ +#define V_ACT_HEIGHT(x) (((x) & 0x1fff) << 16) +#define V_ACT_WIDTH(x) ((x) & 0x1fff) + +/* VOP_WIN0_DSP_INFO */ +#define V_DSP_HEIGHT(x) (((x) & 0xfff) << 16) +#define V_DSP_WIDTH(x) ((x) & 0xfff) + +/* VOP_WIN0_DSP_ST */ +#define V_DSP_YST(x) (((x) & 0x1fff) << 16) +#define V_DSP_XST(x) ((x) & 0x1fff) + +/* VOP_WIN0_SCL_OFFSET */ +#define V_WIN0_VS_OFFSET_CBR(x) (((x) & 0xff) << 24) +#define V_WIN0_VS_OFFSET_YRGB(x) (((x) & 0xff) << 16) +#define V_WIN0_HS_OFFSET_CBR(x) (((x) & 0xff) << 8) +#define V_WIN0_HS_OFFSET_YRGB(x) ((x) & 0xff) + +#define V_HSYNC(x) (((x)&0x1fff)<<0) /* hsync pulse width */ +#define V_HORPRD(x) (((x)&0x1fff)<<16) /* horizontal period */ +#define V_VSYNC(x) (((x)&0x1fff)<<0) +#define V_VERPRD(x) (((x)&0x1fff)<<16) + +#define V_HEAP(x) (((x)&0x1fff)<<0)/* horizontal active end */ +#define V_HASP(x) (((x)&0x1fff)<<16)/* horizontal active start */ +#define V_VAEP(x) (((x)&0x1fff)<<0) +#define V_VASP(x) (((x)&0x1fff)<<16) + +void rkvop_mode_set(u32 vop_id, const struct edid *edid); +void rkvop_enable(u32 vop_id, u32 fbbase, const struct edid *edid); +#endif diff --git a/src/soc/rockchip/rk3288/soc.c b/src/soc/rockchip/rk3288/soc.c index 3048547ff9..c227dab2f3 100644 --- a/src/soc/rockchip/rk3288/soc.c +++ b/src/soc/rockchip/rk3288/soc.c @@ -23,23 +23,34 @@ #include <delay.h> #include <device/device.h> #include <gpio.h> +#include <soc/display.h> #include <soc/soc.h> #include <stddef.h> #include <stdlib.h> #include <string.h> +#include <vendorcode/google/chromeos/chromeos.h> #include "chip.h" -static void soc_enable(device_t dev) +static void soc_init(device_t dev) { + unsigned long fb_size = FB_SIZE_KB * KiB; + u32 lcdbase = get_fb_base_kb() * KiB; + ram_resource(dev, 0, RAM_BASE_KB, RAM_SIZE_KB); + mmio_resource(dev, 1, lcdbase / KiB, fb_size / KiB); + + if (vboot_skip_display_init()) + printk(BIOS_INFO, "Skipping display init.\n"); + else + rk_display_init(dev, lcdbase, fb_size); } static struct device_operations soc_ops = { .read_resources = DEVICE_NOOP, .set_resources = DEVICE_NOOP, - .enable_resources = soc_enable, - .init = DEVICE_NOOP, + .enable_resources = DEVICE_NOOP, + .init = soc_init, .scan_bus = 0, }; diff --git a/src/soc/rockchip/rk3288/vop.c b/src/soc/rockchip/rk3288/vop.c new file mode 100644 index 0000000000..378e6b8e90 --- /dev/null +++ b/src/soc/rockchip/rk3288/vop.c @@ -0,0 +1,144 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 Rockchip Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <arch/io.h> +#include <console/console.h> +#include <delay.h> +#include <stdlib.h> +#include <string.h> +#include <stddef.h> +#include <soc/addressmap.h> +#include <soc/clock.h> +#include <soc/edp.h> +#include <soc/vop.h> + +#include "chip.h" + +static struct rk3288_vop_regs * const vop_regs[] = { + (struct rk3288_vop_regs *)VOP_BIG_BASE, + (struct rk3288_vop_regs *)VOP_LIT_BASE +}; + +void rkvop_enable(u32 vop_id, u32 fbbase, const struct edid *edid) +{ + u32 lb_mode; + u32 rgb_mode; + u32 hactive = edid->ha; + u32 vactive = edid->va; + u32 hsync_len = edid->hspw; + u32 hback_porch = edid->hbl - edid->hso - edid->hspw; + u32 vsync_len = edid->vspw; + u32 vback_porch = edid->vbl - edid->vso - edid->vspw; + u32 xpos = 0, ypos = 0; + struct rk3288_vop_regs *preg = vop_regs[vop_id]; + + writel(V_ACT_WIDTH(hactive - 1) | V_ACT_HEIGHT(vactive - 1), + &preg->win0_act_info); + + writel(V_DSP_XST(xpos + hsync_len + hback_porch) | + V_DSP_YST(ypos + vsync_len + vback_porch), + &preg->win0_dsp_st); + + writel(V_DSP_WIDTH(hactive - 1) | + V_DSP_HEIGHT(vactive - 1), + &preg->win0_dsp_info); + + clrsetbits_le32(&preg->win0_color_key, M_WIN0_KEY_EN | M_WIN0_KEY_COLOR, + V_WIN0_KEY_EN(0) | + V_WIN0_KEY_COLOR(0)); + + switch (edid->framebuffer_bits_per_pixel) { + case 16: + rgb_mode = RGB565; + writel(V_RGB565_VIRWIDTH(hactive), + &preg->win0_vir); + break; + case 24: + rgb_mode = RGB888; + writel(V_RGB888_VIRWIDTH(hactive), + &preg->win0_vir); + break; + case 32: + default: + rgb_mode = ARGB8888; + writel(V_ARGB888_VIRWIDTH(hactive), + &preg->win0_vir); + break; + } + + if (hactive > 2560) + lb_mode = LB_RGB_3840X2; + else if (hactive > 1920) + lb_mode = LB_RGB_2560X4; + else if (hactive > 1280) + lb_mode = LB_RGB_1920X5; + else + lb_mode = LB_RGB_1280X8; + + clrsetbits_le32(&preg->win0_ctrl0, + M_WIN0_LB_MODE | M_WIN0_DATA_FMT | M_WIN0_EN, + V_WIN0_LB_MODE(lb_mode) | + V_WIN0_DATA_FMT(rgb_mode) | V_WIN0_EN(1)); + + writel(fbbase, &preg->win0_yrgb_mst); + + writel(0x01, &preg->reg_cfg_done); /* enable reg config */ +} + +void rkvop_mode_set(u32 vop_id, const struct edid *edid) +{ + u32 hactive = edid->ha; + u32 vactive = edid->va; + u32 hfront_porch = edid->hso; + u32 hsync_len = edid->hspw; + u32 hback_porch = edid->hbl - edid->hso - edid->hspw; + u32 vfront_porch = edid->vso; + u32 vsync_len = edid->vspw; + u32 vback_porch = edid->vbl - edid->vso - edid->vspw; + struct rk3288_vop_regs *preg = vop_regs[vop_id]; + + clrsetbits_le32(&preg->sys_ctrl, M_ALL_OUT_EN, V_EDP_OUT_EN(1)); + clrsetbits_le32(&preg->dsp_ctrl0, M_DSP_OUT_MODE, + V_DSP_OUT_MODE(15)); + writel(V_HSYNC(hsync_len) | + V_HORPRD(hsync_len + hback_porch + hactive + hfront_porch), + &preg->dsp_htotal_hs_end); + + writel(V_HEAP(hsync_len + hback_porch + hactive) | + V_HASP(hsync_len + hback_porch), + &preg->dsp_hact_st_end); + + writel(V_VSYNC(vsync_len) | + V_VERPRD(vsync_len + vback_porch + vactive + vfront_porch), + &preg->dsp_vtotal_vs_end); + + writel(V_VAEP(vsync_len + vback_porch + vactive)| + V_VASP(vsync_len + vback_porch), + &preg->dsp_vact_st_end); + + writel(V_HEAP(hsync_len + hback_porch + hactive) | + V_HASP(hsync_len + hback_porch), + &preg->post_dsp_hact_info); + + writel(V_VAEP(vsync_len + vback_porch + vactive)| + V_VASP(vsync_len + vback_porch), + &preg->post_dsp_vact_info); + + writel(0x01, &preg->reg_cfg_done); /* enable reg config */ +} |