https://kunyichen.wordpress.com/2014/06/08/note-sn65dsi86-%E7%9A%84%E4%BD%BF%E7%94%A8%E8%88%87%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A0%85/
Note: SN65DSI86 的使用與注意事項
最近案子用上 TI 出的這顆 DSI to eDP 的Bridge, 搞了一陣子, eDP 方面基本上只有一點要注意, 就是Panel 一定要能支援 ASSR (Alternative scrambler seed reset), 如果想要用再沒有support ASSR 的面板上基本上也是有機會的, 只要TI 願意跟你講保留的測試pin 跟暫存器設定, 另外Colorbar 設定完後, VSTREAM bit 也設定後就可以正常看到 Color bar的畫面, 無關DSI 的信號與否
判讀 eDP Panel 有沒有支援ASSR 可以從下列資訊判讀
DPCD(DisplayPort Configuration Data)
eDP_CONFIGURATION_CAP(000DH) defined at page 19 (VESA Embedded DisplayPort Standard Version 1.2, 2 May 2010)
For Embedded DisplayPort receivers:
Bit 0 = ALTERNATE_SCRAMBLER_RESET_CAPABLE
A setting of 1 indicates that this is a eDP device that can use the eDP
alternat scrambler reset value of FFFEh
Bit 1 = FRAMING_CHANGE_CAPABLE
A setting of 1 indicates that this is an eDP device uses only
Enhanced Framing, independently of the setting by the source of
ENHANCED_FRAME_EN
Bit2 = RESERVED for eDP Read 0s.
Bit3 = DPCD_DISPLAY_CONTROL_CAPABLE
A setting of 1 indicates that display control registers starting at
address 00700h are enabled.
下面是測試程式的片斷, 展示如何讀寫DPCD & 讀取EDID 的資訊解析, 硬體平台用的是之前TI 特價買的 Stellaris Launchpad LM4F120 (現在已經改成Tiva C series Launchpad TM4C123G) , 實際用 ASSR 的panel 時記得要寫入 DPCD eDP_CONFIGURATION_SET (0010Ah)
bit0 為1 , 且要在 eDP traning 之前, 基本上就是eDP 的 Source & Sink 端同步 reset 亂數產生器的初始值為 0FFFEH
完整的程式可以在 http://pastebin.com/fg79hLvz 找到,
輸出則是 http://pastebin.com/rvN9a6iP
(ps. 這個基本上是hello 改過來的, 不過default 的stack size 太小, 要改大)
static void READ_DPCD(uint8_t* buff, uint32_t v_addr, uint32_t cnt) { uint8_t tmp[4]; // AUX 20bit address tmp[0] = (v_addr>>16) & 0x0F; tmp[1] = (v_addr>>8) & 0xFF; tmp[2] = (v_addr & 0xFF); // length tmp[3] = (cnt & 0x1F); I2C_Write8(tmp, 0x74, 4); // write DPCD register address // AUX_NATIVE_CMD (0x9) tmp[0] = (9<<4) | // AUX_NATIVE_READ (1<<0); // EN_SEND_AUX I2C_Write8(tmp, 0x78, 1); ROM_SysCtlDelay(ROM_SysCtlClockGet()/10/3); // waiting for response I2C_Read8(buff, 0x79, (cnt&0x1F)); } static void WRITE_DPCD(uint8_t* buff, uint32_t v_addr, uint32_t cnt) { uint8_t tmp[4]; // AUX 20bit address tmp[0] = (v_addr>>16) & 0x0F; tmp[1] = (v_addr>>8) & 0xFF; tmp[2] = (v_addr & 0xFF); // length tmp[3] = (cnt & 0x1F); I2C_Write8(tmp, 0x74, 4); // write DPCD register address I2C_Write8(buff, 0x64, cnt); // AUX_NATIVE_CMD (0x8) tmp[0] = (8<<4) | // AUX_NATIVE_WRITE (1<<0); // EN_SEND_AUX I2C_Write8(tmp, 0x78, 1); ROM_SysCtlDelay(ROM_SysCtlClockGet()/10/3); } static void opAUX(uint32_t v_addr, uint8_t* buff, uint8_t cnt, uint8_t cmd) { uint8_t tmp[4]; // AUX 20bit address tmp[0] = (v_addr>>16) & 0x0F; tmp[1] = (v_addr>>8) & 0xFF; tmp[2] = (v_addr & 0xFF); // length tmp[3] = (cnt & 0x1F); I2C_Write8(tmp, 0x74, 4); // write address if ((cnt != 0) && cmd != 5) I2C_Write8(buff, 0x64, cnt); tmp[0] = (cmd << 4) | ( 1 << 0); // SEND flag I2C_Write8(tmp, 0x78, 1); ROM_SysCtlDelay(ROM_SysCtlClockGet()/10/3); } static void readEDID(void) { uint8_t buf[2]; uint8_t count; memset(buf, 0, sizeof(buf)); opAUX(0x50, buf, 0, 4); // Write with MOT, I2C over AUX, SYNC opAUX(0x50, buf, 1, 4); // Write with MOT, Offset 0 opAUX(0x50, buf, 0, 5); // Read with MOT, READ START for (count=0; count < 128; count+= 16) { opAUX(0x50, buf, 0x10, 5); // Read with MOT, read 16 byte I2C_Read8((g_ui8EDID+count), 0x79, 0x10); } opAUX(0x50, buf, 0, 1); // read without MOT, read stop } static void dumpEDID(void) { uint8_t count; uint8_t vo; count = vo = 0; UARTprintf("--------------------------------------------------------\n"); UARTprintf("Dump Panel EDID \n"); UARTprintf("--------------------------------------------------------\n"); UARTprintf(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n"); UARTprintf("--------------------------------------------------------"); for(count = 0; count < sizeof(g_ui8EDID); count++) { if ((count % 16) == 0) { UARTprintf("\n%02X|", vo); vo += 0x10; } UARTprintf(" %02x", g_ui8EDID[count]); } UARTprintf("\n"); } static void dumpPanelTiming(void) { uint32_t t; uint16_t ha; uint16_t hb; uint16_t ho; uint16_t va; uint16_t vb; uint16_t vo; uint16_t hpw; uint16_t vpw; uint16_t hsz; uint16_t vsz; uint16_t hbp; uint16_t vbp; UARTprintf("--------------------------------------------------------\n"); UARTprintf("Extract Panel timing from EDID DTD1\n"); UARTprintf("--------------------------------------------------------\n"); ROM_SysCtlDelay(ROM_SysCtlClockGet()/10/3); // wating for UARTprintf #define DTD1_OFFSET (0x36) t = (g_ui8EDID[DTD1_OFFSET+1]<<8) + g_ui8EDID[DTD1_OFFSET+0]; ha = ((g_ui8EDID[DTD1_OFFSET+4] & 0xF0)<<4) + g_ui8EDID[DTD1_OFFSET+2]; hb = ((g_ui8EDID[DTD1_OFFSET+4] & 0x0F)<<8) + g_ui8EDID[DTD1_OFFSET+3]; va = ((g_ui8EDID[DTD1_OFFSET+7] & 0xF0)<<4) + g_ui8EDID[DTD1_OFFSET+5]; vb = ((g_ui8EDID[DTD1_OFFSET+7] & 0x0F)<<8) + g_ui8EDID[DTD1_OFFSET+6]; ho = ((g_ui8EDID[DTD1_OFFSET+11] & 0xC0)<<2) + g_ui8EDID[DTD1_OFFSET+8]; vo = ((g_ui8EDID[DTD1_OFFSET+11] & 0x0C)<<2) + (g_ui8EDID[DTD1_OFFSET+10] >> 4); hpw = ((g_ui8EDID[DTD1_OFFSET+11] & 0x30)<<4) + g_ui8EDID[DTD1_OFFSET+9]; vpw = ((g_ui8EDID[DTD1_OFFSET+11] & 0x03)<<4) + (g_ui8EDID[DTD1_OFFSET+10]&0x0F); hsz = ((g_ui8EDID[DTD1_OFFSET+14] & 0xF0)<<4) + g_ui8EDID[DTD1_OFFSET+12]; vsz = ((g_ui8EDID[DTD1_OFFSET+14] & 0x0F)<<8) + g_ui8EDID[DTD1_OFFSET+13]; hbp = g_ui8EDID[DTD1_OFFSET+15]; vbp = g_ui8EDID[DTD1_OFFSET+16]; UARTprintf("Pixel Clock %dMHz(%dKHz)\n", t/100, t*10); UARTprintf("Active zone(Hori.xVert.): %dx%d\n", ha, va); ROM_SysCtlDelay(ROM_SysCtlClockGet()/10/3); // wating for UARTprintf UARTprintf("Blinking zone(Hori. & Vert.): %d & %d\n", hb, vb); ROM_SysCtlDelay(ROM_SysCtlClockGet()/10/3); // wating for UARTprintf UARTprintf("Horizontal sync Offset & Pulse width: %d & %d\n", ho, hpw); ROM_SysCtlDelay(ROM_SysCtlClockGet()/10/3); // wating for UARTprintf UARTprintf("Vertical sync Offset & Pulse width: %d & %d\n", vo, vpw); ROM_SysCtlDelay(ROM_SysCtlClockGet()/10/3); // wating for UARTprintf UARTprintf("Border (Hori. & Vert.): %d & %d\n", hbp, vbp); UARTprintf("Display Size(Hori.xVert.): %dx%d mm2\n", hsz, vsz); ROM_SysCtlDelay(ROM_SysCtlClockGet()/10/3); // wating for UARTprintf if ((g_ui8EDID[DTD1_OFFSET+17] & 0x18) != 0x18) { UARTprintf("can't support the panel, because we need a digital separate type\n"); return; } UARTprintf("Horizontal sync. Polarity: "); if (g_ui8EDID[DTD1_OFFSET+17] & (1<<1)) { UARTprintf("Positive\n"); } else { UARTprintf("Negative\n"); } UARTprintf("Vertical sync. Polarity: "); if (g_ui8EDID[DTD1_OFFSET+17] & (1<<2)) { UARTprintf("Positive\n"); } else { UARTprintf("Negative\n"); } } static void dumpDPCD(void) { uint8_t count; uint8_t i; uint8_t buf[16]; uint16_t lists[] = { 0x0000, 0x0100, 0x0200, 0x0210 }; UARTprintf("--------------------------------------------------------\n"); UARTprintf("dump panel DPCD value\n"); UARTprintf("--------------------------------------------------------\n"); for (i = 0; i < (sizeof(lists)/sizeof(uint16_t)); i++) { READ_DPCD(buf, lists[i], 16); UARTprintf("DPCD %04xh:", lists[i]); for(count = 0; count < 16; count++) { UARTprintf(" %02X", buf[count]); } UARTprintf("\n"); ROM_SysCtlDelay(ROM_SysCtlClockGet()/10/3); // wating for UARTprintf } } static void showDPCDInfo(void) { uint8_t buf[16]; READ_DPCD(buf, 0, 16); UARTprintf("DPCD: REV:%d.%d, MAX_LINK_RATE:", (buf[0] >> 4), (buf[0]&0xF)); if (buf[1] == 0x06) { UARTprintf("1.62Gbps"); } else if (buf[1] == 0x0A) { UARTprintf("2.7Gbps"); } UARTprintf(" MAX_LINK_LANE:%d\n", buf[2]); if (buf[0x0D] & ASSR_SUPPORT) { UARTprintf(" support ASSR"); } else { UARTprintf(" not support ASSR"); } if (buf[0x0D] & ENHANCE_FRAMING) { UARTprintf(" support Enhance framing"); } else { UARTprintf(" not support Enhance framing"); } UARTprintf("\n"); } //***************************************************************************** // // //***************************************************************************** int main(void) { //volatile uint32_t ui32Loop; uint32_t count; uint8_t BUF[32]; uint32_t tmp; uint32_t vo = 0; tmp = 0; BUF[0] = BUF[1] = BUF[2] = BUF[3] = tmp; tmp = BUF[0]; // // Enable lazy stacking for interrupt handlers. This allows floating-point // instructions to be used within interrupt handlers, but at the expense of // extra stack usage. // ROM_FPULazyStackingEnable(); // // Set the clocking to run directly from the crystal. // ROM_SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN); // // Enable the GPIO port that is used for the on-board LED. // ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); // // Enable the GPIO pins for the LED (PF2 & PF3). // ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2); // // Initialize SoftI2C // ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0); ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); GPIOPinTypeI2C(GPIO_PORTA_BASE, GPIO_PIN_6 | GPIO_PIN_7); memset(&g_sI2C, 0, sizeof(g_sI2C)); SoftI2CCallbackSet(&g_sI2C, SoftI2CCallback); SoftI2CSCLGPIOSet(&g_sI2C, GPIO_PORTA_BASE, GPIO_PIN_6); SoftI2CSDAGPIOSet(&g_sI2C, GPIO_PORTA_BASE, GPIO_PIN_7); SoftI2CInit(&g_sI2C); SoftI2CIntEnable(&g_sI2C); TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC); TimerLoadSet(TIMER0_BASE, TIMER_A, SysCtlClockGet() / 40000); TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT); TimerEnable(TIMER0_BASE, TIMER_A); IntEnable(INT_TIMER0A); // // Initialize the UART. // ConfigureUART(); UARTprintf("for ColorBar Test \r\n"); BUF[0] = 1; I2C_Write8(BUF, 0x09, 1); // software reset ROM_SysCtlDelay(ROM_SysCtlClockGet() / 10 / 3); BUF[0] = 0 ; I2C_Write8(BUF, 0x5A, 1); // disable VSTREAM ROM_SysCtlDelay(ROM_SysCtlClockGet() / 10 / 3); BUF[0] = 0x0; I2C_Write8(BUF, 0x0D, 1); // disable DP_PLL_EN ROM_SysCtlDelay(ROM_SysCtlClockGet() / 10 / 3); BUF[0] = 1; I2C_Write8(BUF, 0x09, 1); // software reset ROM_SysCtlDelay(ROM_SysCtlClockGet() / 10 / 3); BUF[0] = 0x2; I2C_Write8(BUF, 0x0A, 1); // Clock Ref = 19.2MHz readEDID(); dumpEDID(); dumpPanelTiming(); dumpDPCD(); showDPCDInfo(); BUF[0] = (1 << 5) | // SINGL CHANNEL DSI (A) (0 << 3) | // Four Lane (0); // SOT_ERR_TOL_DIS I2C_Write8(BUF, 0x10, 1); BUF[0] = 0; // disable ASSR I2C_Write8(BUF, 0x5A, 1); BUF[0] = 1<<1; // 24BPP I2C_Write8(BUF, 0x5B, 1); BUF[0] = 0; // HPD Enable I2C_Write8(BUF, 0x5C, 1); BUF[0] = (0 << 6) | // DP pre emphasis lv 0 (2 << 4) | // DP 2 lane (2 << 1) | // Downspread 3750ppm (0 << 0); // disable SSC I2C_Write8(BUF, 0x93, 1); BUF[0] = (4 << 5) | // 2.70Gbps (0 << 2) | // 61ps (0 << 0); // Voltage I2C_Write8(BUF, 0x94, 1); // Panel timing #define HA (1920) #define HSPW (39) #define HFP (59) #define HBP (62) #define HSP (0) // Hsyn Polarity:0 POS #define VA (1200) #define VSPW (3) #define VFP (6) #define VBP (26) #define VSP (1) // Vsyn Polarity: NEG BUF[0] = (HA & 0xFF); BUF[1] = ((HA>>8) & 0xFF); I2C_Write8(BUF, 0x20, 2); // ActiveLine ROM_SysCtlDelay(ROM_SysCtlClockGet() / 10 / 3); BUF[0] = (VA & 0xFF); BUF[1] = ((VA>>8) & 0xFF); I2C_Write8(BUF, 0x24, 2); // Vertical line ROM_SysCtlDelay(ROM_SysCtlClockGet() / 10 / 3); BUF[0] = (HSPW & 0xFF); BUF[1] = ((HSPW>>8) & 0x7F) | (HSP << 7); I2C_Write8(BUF, 0x2C, 2); // HSYNC Pulse width ROM_SysCtlDelay(ROM_SysCtlClockGet() / 10 / 3); BUF[0] = (VSPW & 0xFF); BUF[1] = ((VSPW>>8) & 0xFF) | (VSP << 7); // neg I2C_Write8(BUF, 0x30, 2); // VSYNC Pulse width ROM_SysCtlDelay(ROM_SysCtlClockGet() / 10 / 3); BUF[0] = HBP; I2C_Write8(BUF, 0x34, 1); // H_BACK_PORCH ROM_SysCtlDelay(ROM_SysCtlClockGet() / 10 / 3); BUF[0] = VBP; I2C_Write8(BUF, 0x36, 1); // V_BACK_PORCH ROM_SysCtlDelay(ROM_SysCtlClockGet() / 10 / 3); BUF[0] = HFP; I2C_Write8(BUF, 0x38, 1); // H_FRONT_PORCH ROM_SysCtlDelay(ROM_SysCtlClockGet() / 10 / 3); BUF[0] = VFP; I2C_Write8(BUF, 0x3A, 1); // V_FRONT_PORCH ROM_SysCtlDelay(ROM_SysCtlClockGet() / 10 / 3); BUF[0] = 0; I2C_Write8(BUF, 0x5B, 1); //24bpp ROM_SysCtlDelay(ROM_SysCtlClockGet() / 10 / 3); BUF[0] = 0x1; I2C_Write8(BUF, 0x0D, 1); // Enable DP_PLL_EN ROM_SysCtlDelay(ROM_SysCtlClockGet() / 10 / 3); I2C_Read8(BUF, 0x0A, 1); if (BUF[0] & 0x80) UARTprintf("DSI86 PLL has Locked now\n"); BUF[0] = 0xA; // ML_TX_MODE,Semi Auto Link Training I2C_Write8(BUF, 0x96, 1); ROM_SysCtlDelay(ROM_SysCtlClockGet() / 10 / 3); I2C_Read8(BUF, 0x96, 1); UARTprintf("SemiTraning result:%02X\n", BUF[0]); #if 0 BUF[0] = EN_SELF_TEST; WRITE_DPCD(BUF, EDP_CONFIGURATION_SET, 1); #endif BUF[0] = 0x18; // Color Bar enable I2C_Write8(BUF, 0x3C, 1); SysCtlDelay(SysCtlClockGet() / 10 / 3); I2C_Read8(BUF, 0x20, 4); BUF[0] = (1<<3) ; // VStream //(0<<2) | // Enhanced Framing I2C_Write8(BUF, 0x5A, 1); SysCtlDelay(SysCtlClockGet() / 10 / 3); UARTprintf("--------------------------------------------------------\n"); UARTprintf(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n"); UARTprintf("--------------------------------------------------------"); #if 1 for (tmp = 0; tmp < 8; tmp++) { I2C_Read8(BUF, tmp*sizeof(BUF), sizeof(BUF)); for(count = 0; count < sizeof(BUF); count++) { if ((count % 16) == 0) { UARTprintf("\n%02X|", vo); vo += 0x10; } UARTprintf(" %02x", BUF[count]); } } #endif UARTprintf("\n"); dumpDPCD(); while(1) { // // Turn on the BLUE LED. // GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_PIN_2); // // Delay for a bit. // SysCtlDelay(SysCtlClockGet() / 10 / 3); // // Turn off the BLUE LED. // GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0); // // Delay for a bit. // SysCtlDelay(SysCtlClockGet() / 10 / 3); } } ref. eDP Standard v1.2