/* this is too ugly to be allowed to live. But it's what works for now. */ /* * This file is part of the coreboot project. * * Copyright 2013 Google 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "clk_rst.h" #include #include "chip.h" #include "sor.h" #include //#include extern int dump; unsigned long READL(void *p); void WRITEL(unsigned long value, void *p); void debug_dpaux_print(u32 addr, u32 size); int dpaux_write(u32 addr, u32 size, u32 data); int dpaux_read(u32 addr, u32 size, u8 * data); #define DCA_WRITE(reg, val) \ { \ WRITEL(val, (void *)(TEGRA_ARM_DISPLAYA + (reg<<2))); \ } #define DCA_READ_M_WRITE(reg, mask, val) \ { \ u32 _reg_val; \ _reg_val = READL( (void *)(TEGRA_ARM_DISPLAYA + (reg<<2))); \ _reg_val &= ~mask; \ _reg_val |= val; \ WRITEL(_reg_val, (void *)(TEGRA_ARM_DISPLAYA + (reg<<2))); \ } #define SOR_WRITE(reg, val) \ { \ WRITEL(val, (void *)(TEGRA_ARM_SOR + (reg<<2))); \ } #define SOR_READ(reg) READL((void *)(TEGRA_ARM_SOR + (reg<<2))) #define SOR_READ_M_WRITE(reg, mask, val) \ { \ u32 _reg_val; \ _reg_val = READL((void *)(TEGRA_ARM_SOR + (reg<<2))); \ _reg_val &= ~mask; \ _reg_val |= val; \ WRITEL(_reg_val, (void *)(TEGRA_ARM_SOR + (reg<<2))); \ } #define DPAUX_WRITE(reg, val) \ { \ WRITEL(val, (void *)(TEGRA_ARM_DPAUX + (reg<<2))); \ } #define DPAUX_READ(reg) READL((void *)(TEGRA_ARM_DPAUX + (reg<<2))) void init_dca_regs(void) { DCA_WRITE (DC_CMD_DISPLAY_WINDOW_HEADER_0, 0x000000F0); DCA_WRITE (DC_WIN_A_WIN_OPTIONS_0, 0x00000000); DCA_WRITE (DC_WIN_A_BYTE_SWAP_0, 0x00000000); DCA_WRITE (DC_WIN_A_BUFFER_CONTROL_0, 0x00000000); DCA_WRITE (DC_WIN_A_COLOR_DEPTH_0, 0x0000000C); DCA_WRITE (DC_WIN_A_POSITION_0, 0x00000000); DCA_WRITE (DC_WIN_A_SIZE_0, 0x00000000); DCA_WRITE (DC_WIN_A_PRESCALED_SIZE_0, 0x00000000); DCA_WRITE (DC_WIN_A_H_INITIAL_DDA_0, 0x00000000); DCA_WRITE (DC_WIN_A_V_INITIAL_DDA_0, 0x00000000); DCA_WRITE (DC_WIN_A_DDA_INCREMENT_0, 0x00000000); DCA_WRITE (DC_WIN_A_LINE_STRIDE_0, 0x00000000); DCA_WRITE (DC_WIN_A_DV_CONTROL_0, 0x00000000); DCA_WRITE (DC_WIN_A_BLEND_LAYER_CONTROL_0, 0x01000000); DCA_WRITE (DC_WIN_A_BLEND_MATCH_SELECT_0, 0x00000000); DCA_WRITE (DC_WIN_A_BLEND_NOMATCH_SELECT_0, 0x00000000); DCA_WRITE (DC_WIN_A_BLEND_ALPHA_1BIT_0, 0x00000000); DCA_WRITE (DC_WINBUF_A_START_ADDR_HI_0, 0x00000000); DCA_WRITE (DC_WINBUF_A_ADDR_H_OFFSET_0, 0x00000000); DCA_WRITE (DC_WINBUF_A_ADDR_V_OFFSET_0, 0x00000000); DCA_WRITE (DC_CMD_DISPLAY_WINDOW_HEADER_0, 0x00000000); DCA_WRITE (DC_COM_CRC_CONTROL_0, 0x00000000); DCA_WRITE (DC_COM_CRC_CHECKSUM_0, 0x00000000); DCA_WRITE (DC_COM_PIN_OUTPUT_ENABLE0_0, 0x00000000); DCA_WRITE (DC_COM_PIN_OUTPUT_ENABLE1_0, 0x00000000); DCA_WRITE (DC_COM_PIN_OUTPUT_ENABLE2_0, 0x00510104); DCA_WRITE (DC_COM_PIN_OUTPUT_ENABLE3_0, 0x00000555); } static int dp_poll_register(void *addr, u32 exp_val, u32 mask, u32 timeout_ms) { u32 reg_val = 0; do { udelay(1000); reg_val = READL(addr); } while (((reg_val & mask) != exp_val) && (--timeout_ms > 0)); if ((reg_val & mask) == exp_val) return 0; /* success */ printk(BIOS_WARNING, "poll_register %p: timeout\n", addr); return timeout_ms; } static void dp_io_set_dpd(u32 power_down) { /* * power_down: * 0: out of Deep power down * 1: into deep power down */ u32 val_reg; val_reg = READL((void *)(0x7000e400 + 0x1c4)); /* APBDEV_PMC_IO_DPD2_STATUS_0 */ if ((((val_reg & DP_LVDS) >> DP_LVDS_SHIFT) & 1) == power_down) { printk(BIOS_DEBUG, "PAD already POWER=%d\n", 1 - power_down); return; } /* APBDEV_PMC_IO_DPD2_REQ_0: E_DPD = power on */ WRITEL((DP_LVDS | ((1 + power_down) << 30)), (void *)(0x7000e400 + 0x1c0)); dp_poll_register((void *)(0x7000e400 + 0x1C4), 0, DP_LVDS, 1000); /* APBDEV_PMC_IO_DPD2_STATUS_0 */ } void dp_io_powerup(void) { SOR_WRITE(SOR_NV_PDISP_SOR_CLK_CNTRL_0, (6 << 2) | 2);//select PLLDP, lowest speed(6x) SOR_WRITE(SOR_NV_PDISP_SOR_DP_PADCTL0_0, 0x00800000); //set PDCAL SOR_WRITE(SOR_NV_PDISP_SOR_PLL0_0, 0x050003D5); //set PWR,VCOPD SOR_WRITE(SOR_NV_PDISP_SOR_PLL1_0, 0x00001100); //default SOR_WRITE(SOR_NV_PDISP_SOR_PLL2_0, 0x01C20000); //set AUX1,6,7,8; clr AUX2 SOR_WRITE(SOR_NV_PDISP_SOR_PLL3_0, 0x38002220); /* Deassert E_DPD to enable core logic circuits, and wait for > 5us */ dp_io_set_dpd(0); udelay(10); /* Deassert PDBG to enable bandgap, and wait for > 20us. */ SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_PLL2_0, SOR_NV_PDISP_SOR_PLL2_0_AUX6_FIELD, (0 << SOR_NV_PDISP_SOR_PLL2_0_AUX6_SHIFT)); udelay(25); /* * Enable the PLL/charge-pump/VCO, and wait for >200us for the PLL to * lock. Input Clock must be running and stable before PDPLL * de-assertion. */ SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_PLL0_0, SOR_NV_PDISP_SOR_PLL0_0_PWR_FIELD, (0 << SOR_NV_PDISP_SOR_PLL0_0_PWR_SHIFT)); SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_PLL0_0, SOR_NV_PDISP_SOR_PLL0_0_VCOPD_FIELD, (0 << SOR_NV_PDISP_SOR_PLL0_0_VCOPD_SHIFT)); SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_PLL2_0, SOR_NV_PDISP_SOR_PLL2_0_AUX8_FIELD, (0 << SOR_NV_PDISP_SOR_PLL2_0_AUX8_SHIFT)); udelay(210); SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_PLL2_0, SOR_NV_PDISP_SOR_PLL2_0_AUX7_FIELD, (0 << SOR_NV_PDISP_SOR_PLL2_0_AUX7_SHIFT)); SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_PLL2_0, SOR_NV_PDISP_SOR_PLL2_0_AUX9_FIELD, (1 << SOR_NV_PDISP_SOR_PLL2_0_AUX9_SHIFT)); udelay(100); } static int dpaux_check(u32 bytes, u32 data, u32 mask) { u32 status = 0; u8 buf[16]; u32 temp; DPAUX_WRITE(DPAUX_DP_AUXDATA_READ_W0, 0); status = dpaux_read(0x202, bytes, buf); if (status != 0) printk(BIOS_ERR, "******AuxRead Error:%04x: status %08x\n", 0x202, status); else { temp = DPAUX_READ(DPAUX_DP_AUXDATA_READ_W0); if ((temp & mask) != (data & mask)) { printk(BIOS_ERR, "AuxCheck ERROR:(r_data) %08x &" " (mask) %08x != (data) %08x & (mask) %08x\n", temp, mask, data, mask); return -1; } else { printk(BIOS_DEBUG, "AuxCheck PASS:(bytes=%d, " "data=%08x, mask=%08x):0x%08x\n", bytes, data, mask, temp); return 0; } } return -1; } /* Modify the drive parameters for DP. There are up to four DP * lanes. In principle, each lane can have different current, * pre-emphasis, and postcur values. Nobody individualizes them; every * single driver I've seen drives all the lanes to the same value * (across x86 and ARM code). Actualy adjusting them individually and * getting it all to work is probably a PhD thesis anyway. So, rather * than the very complex code we see many places, the people who wrote * this code realize: we can represent the 'volume' as a number in the * range 0..3, with '0' as the base and '3' as being 'not to exceed'. * * So they abstract the values away, take care of the proper values, * and set it all in one blow. Very nice. By far the easiest one of * these functions we've seen. Sure, they could have constants, but * nobody knows what PRE_EMPHASIS_3_5 and the other values actually * *mean* anyway. Well, the hardware guys might. */ static void pattern_level(u32 current, u32 preemph, u32 postcur) { //calibrating required if (current == 0) SOR_WRITE(SOR_NV_PDISP_SOR_LANE_DRIVE_CURRENT0_0, 0x20202020); if (current == 1) SOR_WRITE(SOR_NV_PDISP_SOR_LANE_DRIVE_CURRENT0_0, 0x24242424); if (current == 2) SOR_WRITE(SOR_NV_PDISP_SOR_LANE_DRIVE_CURRENT0_0, 0x30303030); if (current == 3) SOR_WRITE(SOR_NV_PDISP_SOR_LANE_DRIVE_CURRENT0_0, 0x40404040); if (preemph == 0) SOR_WRITE(SOR_NV_PDISP_SOR_LANE_PREEMPHASIS0_0, 0x00000000); if (preemph == 1) SOR_WRITE(SOR_NV_PDISP_SOR_LANE_PREEMPHASIS0_0, 0x08080808); if (preemph == 2) SOR_WRITE(SOR_NV_PDISP_SOR_LANE_PREEMPHASIS0_0, 0x10101010); if (preemph == 3) SOR_WRITE(SOR_NV_PDISP_SOR_LANE_PREEMPHASIS0_0, 0x18181818); if (postcur == 0) SOR_WRITE(SOR_NV_PDISP_SOR_POSTCURSOR0_0, 0x00000000); if (postcur == 1) SOR_WRITE(SOR_NV_PDISP_SOR_POSTCURSOR0_0, 0x04040404); if (postcur == 2) SOR_WRITE(SOR_NV_PDISP_SOR_POSTCURSOR0_0, 0x08080808); if (postcur == 3) SOR_WRITE(SOR_NV_PDISP_SOR_POSTCURSOR0_0, 0x10101010); } static int dp_training(u32 level, u32 check, u32 speed) { /* The levels are one of four choices. This code * packs them into the three lowest nibl's. We may change this. */ u32 dc_lv = level & 0x0f; u32 pe_lv = (level >> 4) & 0x0f; u32 pc_lv = (level >> 8) & 0x0f; u32 cnt = 0; u32 cfg, cfg_d = 0; u32 wcfg; u8 buf[16]; while (cnt <= 5) { pattern_level(dc_lv, pe_lv, pc_lv); wcfg = (pe_lv << 3) | dc_lv; if (dc_lv == 3) wcfg = wcfg | 0x04; if (pe_lv == 3) wcfg = wcfg | 0x20; wcfg = wcfg | (wcfg << 8) | (wcfg << 16) | (wcfg << 24); dpaux_write(0x103, 4, wcfg); udelay(100); DPAUX_WRITE(DPAUX_DP_AUXDATA_READ_W0, 0); if (!dpaux_check(2, check, check)) cnt = 100; else { dpaux_read(0x206, 1, buf); cfg = DPAUX_READ(DPAUX_DP_AUXDATA_READ_W0); cfg &= 0x00ff; if (cfg == cfg_d) { ++cnt; if (cnt > 5) printk(BIOS_ERR, "Error: link training FAILED\n"); } else { cnt = 0; cfg_d = cfg; dc_lv = cfg & 0x3; pe_lv = (cfg >> 2) & 0x3; if (speed == 20) { dpaux_read(0x20C, 1, buf); cfg = DPAUX_READ(DPAUX_DP_AUXDATA_READ_W0); pc_lv = cfg & 0x3; } else { pc_lv = 0; } } } debug_dpaux_print(0x200, 16); } return ((pc_lv << 8) | (pe_lv << 4) | (dc_lv)); } void dp_link_training(u32 lanes, u32 speed); void dp_link_training(u32 lanes, u32 speed) { u32 lane_on; u32 mask, level; u32 reg_val; printk(BIOS_DEBUG, "\nLink training start\n"); switch (lanes) { case 1: lane_on = 0x04; break; case 2: lane_on = 0x06; break; case 4: lane_on = 0x0f; break; default: printk(BIOS_DEBUG, "dp: invalid lane count: %d\n", lanes); return; } SOR_WRITE(SOR_NV_PDISP_SOR_DP_PADCTL0_0, (0x008000000 | lane_on)); SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_DP_PADCTL0_0, SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_VALUE_FIELD, (6 << SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_VALUE_SHIFT)); SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_DP_PADCTL0_0, SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_FIELD, (1 << SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_SHIFT)); SOR_WRITE(SOR_NV_PDISP_SOR_LVDS_0, 0); SOR_WRITE(SOR_NV_PDISP_SOR_CLK_CNTRL_0, ((speed << 2) | 2)); udelay(100 * 1000); sor_clock_start(); SOR_WRITE(SOR_NV_PDISP_SOR_DP_LINKCTL0_0, (((0xF >> (4 - lanes)) << 16) | 1)); SOR_WRITE(SOR_NV_PDISP_SOR_LANE_SEQ_CTL_0, 0x80100000); printk(BIOS_DEBUG, "Polling SOR_NV_PDISP_SOR_LANE_SEQ_CTL_0.DONE\n"); dp_poll_register((void *)0x54540084, 0x00000000, 0x80000000, 1000); debug_dpaux_print(0x202, 4); printk(BIOS_DEBUG, "set link rate and lane number: %dMHz, %d lanes\n", (speed * 27), lanes); dpaux_write(0x100, 2, ((lanes << 8) | speed)); printk(BIOS_DEBUG, "precharge lane 10us\n"); reg_val = SOR_READ(SOR_NV_PDISP_SOR_DP_PADCTL0_0); SOR_WRITE(SOR_NV_PDISP_SOR_DP_PADCTL0_0, (0x000000f0 | reg_val)); udelay(100 * 1000); SOR_WRITE(SOR_NV_PDISP_SOR_DP_PADCTL0_0, reg_val); printk(BIOS_DEBUG, "link training cr start\n"); SOR_WRITE(SOR_NV_PDISP_SOR_DP_TPG_0, 0x41414141); dpaux_write(0x102, 1, 0x21); mask = 0x0000ffff >> ((4 - lanes) * 4); level = 0; level = dp_training(level, 0x1111 & mask, speed); printk(BIOS_DEBUG, "level:%x\n", level); debug_dpaux_print(0x210, 16); printk(BIOS_DEBUG, "link training eq start\n"); if (speed == 20) { SOR_WRITE(SOR_NV_PDISP_SOR_DP_TPG_0, 0x43434343); dpaux_write(0x102, 1, 0x23); } else { SOR_WRITE(SOR_NV_PDISP_SOR_DP_TPG_0, 0x42424242); dpaux_write(0x102, 1, 0x22); } level = dp_training(level, (0x7777 & mask) | 0x10000, speed); printk(BIOS_DEBUG, "level:%x\n", level); debug_dpaux_print(0x210, 16); SOR_WRITE(SOR_NV_PDISP_SOR_DP_TPG_0, 0x50505050); dpaux_write(0x102, 1, 0); dpaux_write(0x600, 1, 1); debug_dpaux_print(0x200, 16); debug_dpaux_print(0x210, 16); printk(BIOS_DEBUG, "Link training done\n\n"); } static u32 div_f(u32 a, u32 b, u32 one) { u32 d = (((a - (a / b * b)) * one) + (b / 2)) / b; return (d); } u32 dp_setup_timing(u32 width, u32 height) { u32 pclk_freq = 0; u32 PLL_FREQ = (12 / 12 * 283) / 1 / 2; /* 141.5 */ u32 PLL_DIV = 2; u32 SYNC_WIDTH = (8 << 16) | 46; u32 BACK_PORCH = (6 << 16) | 44; u32 FRONT_PORCH = (6 << 16) | 44; u32 HSYNC_NEG = 1; u32 VSYNC_NEG = 1; u32 SHIFT_CLK_DIVIDER = PLL_DIV * 2 - 2; u32 DISP_ACTIVE = (height << 16) | width; u32 DISP_TOTAL = DISP_ACTIVE + SYNC_WIDTH + BACK_PORCH + FRONT_PORCH; u32 SYNC_END = SYNC_WIDTH - 0x10001; u32 BLANK_END = SYNC_END + BACK_PORCH; u32 BLANK_START = BLANK_END + DISP_ACTIVE; u32 TOTAL_PIXELS = (DISP_TOTAL & 0xffff) * (DISP_TOTAL >> 16); u32 PLL_FREQ_I, PLL_FREQ_F; u32 PCLK_FREQ_I, PCLK_FREQ_F; u32 FRATE_I, FRATE_F; PLL_FREQ = PLL_FREQ * 1000000; pclk_freq = PLL_FREQ / PLL_DIV; PLL_FREQ_I = PLL_FREQ / 1000000; PLL_FREQ_F = div_f(PLL_FREQ, 1000000, 100); PCLK_FREQ_I = PLL_FREQ / (PLL_DIV * 1000000); PCLK_FREQ_F = div_f(PLL_FREQ, PLL_DIV * 1000000, 100); FRATE_I = PLL_FREQ / (PLL_DIV * TOTAL_PIXELS); FRATE_F = div_f(PLL_FREQ, (PLL_DIV * TOTAL_PIXELS), 100); /* nv_bug 1021453 */ BACK_PORCH = BACK_PORCH - 0x10000; FRONT_PORCH = FRONT_PORCH + 0x10000; printk(BIOS_DEBUG, "ACTIVE: %dx%d\n", (DISP_ACTIVE & 0xFFFF), (DISP_ACTIVE >> 16)); printk(BIOS_DEBUG, "TOTAL: %dx%d\n", (DISP_TOTAL & 0xffff), (DISP_TOTAL >> 16)); printk(BIOS_DEBUG, "PLL Freq: %d.%d MHz\n", PLL_FREQ_I, PLL_FREQ_F); printk(BIOS_DEBUG, "Pclk Freq: %d.%d MHz\n", PCLK_FREQ_I, PCLK_FREQ_F); printk(BIOS_DEBUG, "Frame Rate: %d.%d Hz\n", FRATE_I, FRATE_F); printk(BIOS_DEBUG, "\n"); DCA_WRITE(DC_CMD_STATE_ACCESS_0, 0x00000004); DCA_WRITE(DC_DISP_DISP_CLOCK_CONTROL_0, SHIFT_CLK_DIVIDER); //Raster Timing DCA_WRITE(DC_DISP_DISP_TIMING_OPTIONS_0, 0x00000001); DCA_WRITE(DC_DISP_REF_TO_SYNC_0, 0x00010001); DCA_WRITE(DC_DISP_SYNC_WIDTH_0, SYNC_WIDTH); DCA_WRITE(DC_DISP_BACK_PORCH_0, BACK_PORCH); DCA_WRITE(DC_DISP_DISP_ACTIVE_0, DISP_ACTIVE); DCA_WRITE(DC_DISP_FRONT_PORCH_0, FRONT_PORCH); //REG(DC_DISP_DISP_WIN_OPTIONS_0, SOR_ENABLE , 1) DCA_READ_M_WRITE(DC_DISP_DISP_WIN_OPTIONS_0, DC_DISP_DISP_WIN_OPTIONS_0_SOR_ENABLE_FIELD, (1 << DC_DISP_DISP_WIN_OPTIONS_0_SOR_ENABLE_SHIFT)); SOR_WRITE(SOR_NV_PDISP_HEAD_STATE1_0, DISP_TOTAL); SOR_WRITE(SOR_NV_PDISP_HEAD_STATE2_0, SYNC_END); SOR_WRITE(SOR_NV_PDISP_HEAD_STATE3_0, BLANK_END); SOR_WRITE(SOR_NV_PDISP_HEAD_STATE4_0, BLANK_START); SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_STATE1_0, SOR_NV_PDISP_SOR_STATE1_0_ASY_HSYNCPOL_FIELD, (HSYNC_NEG << SOR_NV_PDISP_SOR_STATE1_0_ASY_HSYNCPOL_SHIFT)); SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_STATE1_0, SOR_NV_PDISP_SOR_STATE1_0_ASY_VSYNCPOL_FIELD, (VSYNC_NEG << SOR_NV_PDISP_SOR_STATE1_0_ASY_VSYNCPOL_SHIFT)); SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_STATE1_0, SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_FIELD, (SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_DP_A << SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_SHIFT)); SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_STATE1_0, SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_FIELD, (SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_COMPLETE_RASTER << SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_SHIFT)); SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_STATE1_0, SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_FIELD, (SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_NONE << SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_SHIFT)); SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_STATE1_0, SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_FIELD, (SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_HEAD0 << SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_SHIFT)); return pclk_freq; } static u32 calc_config(u32 ts, u32 a, u32 b, u32 bpp) { u32 act_cnt = (ts * a) / b; u32 diff = (ts * a) - (act_cnt * b); u32 act_pol; u32 act_frac; u32 err; u32 water_mark; printk(BIOS_DEBUG, "calc_config ts %d a %d b %d bpp %d\n", ts, a, b, bpp); if (diff != 0) { if (diff > (b / 2)) { diff = b - diff; act_pol = 1; act_frac = (b + diff - 1) / diff; err = diff * act_frac - b; } else { act_pol = 0; act_frac = b / diff; err = b - (diff * act_frac); } if (act_frac > 15) { act_pol = 1 - act_pol; act_frac = 1; err = diff; } } else { act_pol = 1; act_frac = 1; err = 0; } if (bpp) { water_mark = (a * (b - a) * ts / (b * b)) + (2 * bpp / 8); if (water_mark > 30) water_mark = 30; SOR_WRITE(SOR_NV_PDISP_SOR_DP_CONFIG0_0, 0x84000000); SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_DP_CONFIG0_0, SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_POLARITY_FIELD, (act_pol << SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_POLARITY_SHIFT)); SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_DP_CONFIG0_0, SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_FRAC_FIELD, (act_frac << SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_FRAC_SHIFT)); SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_DP_CONFIG0_0, SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_COUNT_FIELD, (act_cnt << SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_COUNT_SHIFT)); SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_DP_CONFIG0_0, SOR_NV_PDISP_SOR_DP_CONFIG0_0_WATERMARK_FIELD, (water_mark << SOR_NV_PDISP_SOR_DP_CONFIG0_0_WATERMARK_SHIFT)); printk(BIOS_DEBUG, "SOR_DP_CONFIG0:TU,CNT,POL,FRAC,WMK,ERR=%d,%d,%d,%d,%d,%d/%d\n", ts, act_cnt, act_pol, act_frac, water_mark, err, b); } return (err); } static u32 dp_buf_config(u32 pclkfreq, u32 linkfreq, u32 lanes, u32 bpp) { //to avoid 32bit overflow u32 tusize = 0; u32 pf = pclkfreq; u32 lf = linkfreq; u32 i; u32 a, b; u32 min_err = 1000000000; u32 ts = 64; u32 c_err; printk(BIOS_DEBUG, "dp buf config pclkfreq %d linkfreq %d lanes %d bpp %d\n", pclkfreq, linkfreq, lanes, bpp); for (i = 2; i <= 7; ++i) { while (((pf / i * i) == pf) && ((lf / i * i) == lf)) { pf = pf / i; lf = lf / i; } } a = pf * bpp / 8; b = lf * lanes; printk(BIOS_DEBUG, "ratio:%d/%d\n", a, b); if (a > (b * 98 / 100)) printk(BIOS_ERR, "Error:link speed not enough\n"); //search best tusize //min_err = 1000000000; //ts = 64; while (ts >= 32) { c_err = calc_config(ts, a, b, 0); if (c_err < min_err) { if (c_err == 0) { tusize = ts; ts = 1; } else { min_err = c_err; tusize = ts; } } --ts; } SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_DP_LINKCTL0_0, SOR_NV_PDISP_SOR_DP_LINKCTL0_0_TUSIZE_FIELD, (tusize << SOR_NV_PDISP_SOR_DP_LINKCTL0_0_TUSIZE_SHIFT)); calc_config(tusize, a, b, bpp); return (tusize); } /* void dp_misc_setting(u32 panel_bpp, u32 width, u32 height, u32 winb_addr, u32 lane_count, u32 enhanced_framing, u32 panel_edp, u32 pclkfreq, u32 linkfreq); */ void dp_misc_setting(u32 panel_bpp, u32 width, u32 height, u32 winb_addr, u32 lane_count, u32 enhanced_framing, u32 panel_edp, u32 pclkfreq, u32 linkfreq) { u32 tusize; u32 linkctl; printk(BIOS_DEBUG, "%s: winb: 0x%08x, panel_bpp %d ", __func__, winb_addr, panel_bpp); if (panel_bpp == 18) { //0x54540010 SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_STATE1_0, SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_FIELD, (SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_18_444 << SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_SHIFT)); } if (panel_bpp == 24) { SOR_READ_M_WRITE(SOR_NV_PDISP_SOR_STATE1_0, SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_FIELD, (SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_24_444 << SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_SHIFT)); } DCA_WRITE(DC_CMD_DISPLAY_WINDOW_HEADER_0, 0x00000010); DCA_WRITE(DC_WIN_A_SIZE_0, ((height << 16) | width)); DCA_WRITE(DC_WIN_A_PRESCALED_SIZE_0, ((height << 16) | (width * SRC_BPP / 8))); DCA_WRITE(DC_WIN_A_LINE_STRIDE_0, ((width * SRC_BPP / 8 + 31) / 32 * 32)); DCA_WRITE(DC_WIN_A_COLOR_DEPTH_0, COLORDEPTH); DCA_WRITE(DC_WINBUF_A_START_ADDR_LO_0, winb_addr); DCA_WRITE(DC_WIN_A_DDA_INCREMENT_0, 0x10001000); SOR_WRITE(SOR_NV_PDISP_SOR_CRC_CNTRL_0, 0x00000001); DCA_WRITE(DC_COM_CRC_CONTROL_0, 0x00000009); //CRC_ALWAYS+CRC_ENABLE DCA_WRITE(DC_COM_PIN_OUTPUT_ENABLE2_0, 0x00000000); DCA_WRITE(DC_COM_PIN_OUTPUT_ENABLE3_0, 0x00000000); DCA_WRITE(DC_DISP_DISP_SIGNAL_OPTIONS0_0, 0x00000000); DCA_WRITE(DC_DISP_BLEND_BACKGROUND_COLOR_0, COLOR_WHITE); DCA_WRITE(DC_CMD_DISPLAY_COMMAND_0, 0x00000020); SOR_WRITE(SOR_NV_PDISP_SOR_DP_AUDIO_VBLANK_SYMBOLS_0, 0x00000e48); dpaux_write(0x101, 1, (enhanced_framing << 7) | lane_count); if (panel_edp) dpaux_write(0x10A, 1, 1); tusize = dp_buf_config(pclkfreq, (linkfreq * 1000000), lane_count, panel_bpp); linkctl = ((0xF >> (4 - lane_count)) << 16) | (enhanced_framing << 14) | (tusize << 2) | 1; SOR_WRITE(SOR_NV_PDISP_SOR_DP_LINKCTL0_0, linkctl); SOR_WRITE(SOR_NV_PDISP_SOR_DP_SPARE0_0, ((panel_edp << 1) | 0x05)); SOR_WRITE(SOR_NV_PDISP_SOR_PWR_0, 0x80000001); printk(BIOS_DEBUG, "Polling SOR_NV_PDISP_SOR_PWR_0.DONE\n"); dp_poll_register((void *)0x54540054, 0x00000000, 0x80000000, 1000); //SOR_NV_PDISP_SOR_PWR_0 //sor_update SOR_WRITE(SOR_NV_PDISP_SOR_STATE0_0, 0x00000000); SOR_WRITE(SOR_NV_PDISP_SOR_SUPER_STATE1_0, 0x00000006); //sor_super_update SOR_WRITE(SOR_NV_PDISP_SOR_SUPER_STATE0_0, 0x00000000); SOR_WRITE(SOR_NV_PDISP_SOR_SUPER_STATE1_0, 0x0000000e); //sor_super_update SOR_WRITE(SOR_NV_PDISP_SOR_SUPER_STATE0_0, 0x00000000); printk(BIOS_DEBUG, "Polling SOR_NV_PDISP_SOR_TEST_0.ATTACHED\n"); dp_poll_register((void *)0x54540058, 0x00000400, 0x00000400, 1000); //SOR_NV_PDISP_SOR_TEST_0 DCA_WRITE(DC_CMD_STATE_CONTROL_0, 0x00009f00); DCA_WRITE(DC_CMD_STATE_CONTROL_0, 0x0000009f); DCA_WRITE(DC_CMD_DISPLAY_POWER_CONTROL_0, 0x00050155); printk(BIOS_DEBUG, "Polling SOR_NV_PDISP_SOR_TEST_0.AWAKE\n"); dp_poll_register((void *)0x54540058, 0x00000200, 0x00000300, 1000); //SOR_NV_PDISP_SOR_TEST_0 // DCA_WRITE (DC_CMD_STATE_ACCESS_0 ,0); DCA_WRITE(DC_CMD_STATE_ACCESS_0, 4); DCA_WRITE(DC_CMD_STATE_CONTROL_0, 0x0000ffff); DCA_READ_M_WRITE(DC_WIN_A_WIN_OPTIONS_0, DC_WIN_A_WIN_OPTIONS_0_A_WIN_ENABLE_FIELD, (DC_WIN_A_WIN_OPTIONS_0_A_WIN_ENABLE_ENABLE << DC_WIN_A_WIN_OPTIONS_0_A_WIN_ENABLE_SHIFT)); }