Archive for the Display Category

Note: SN65DSI86 的使用與注意事項

Posted in Display with tags on 2014 年 06 月 08 日 by Kun-Yi

最近案子用上 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 
廣告