mipi_dsi 接口转 lvds显示(GM8775C)

目录


前言

本文章主要是简解一下mipi DSI 协议,以及mipi 的调试记录,以及mipi 转 LVDS的转接芯片GM8775C的调试记录。

场景:由于我司需要将mipi dsi 接口用于LVDS来使用,就需要使用GM8775C的转接芯片。该芯片是双通道的,就是说可以外接两块LVDS屏。但我司只外接了一块LVDS屏,当然这取决与你电路的连接方式。

分析:这样的话我们就需要调试两个模块,一是mipi 屏端,一端是GM8775C转换芯片端,而GM8775C是没有驱动代码的,需要我们自己去编写添加,不过这也是非常简单的,因为它其实就是一个I2C的设备。


一、MIPI_DSI 协议简介

MIPI-DSI是一种应用于显示技术的串行接口,兼容DPI(显示像素接口,Display Pixel Interface)、DBI(显示总线接口,Display Bus Interface)和DCS(显示命令集,Display Command Set),以串行的方式发送像素信息或指令给外设,而且从外设中读取状态信息或像素信息,而且在传输的过程中享有自己独立的通信协议,包括数据包格式和纠错检错机制。

MIPI-DSI具备高速模式和低速模式两种工作模式,全部数据通道(1~4组数据通道都可以用于单向的高速传输,但只有第一个数据通道才可用于低速双向传输(如果是4条数据线的话,data0就可以用做低速模式的双向传输,而data1-data3就只能够单向传输)。外设的状态信息、像素格式等信息通过第一个数据通道返回。时钟通道用于在传输数据的过程中传输同步时钟信号(严格意义上讲,低速模式下,一般都是进行参数下发或者外设状态读取的,低速模式下是不需要时钟进行同步的,因为数据通道一低速模式时,本身用差分信号就可以实现同步)。

1、名词解释

1、DCS (DisplayCommandSet):DCS是一个标准化的命令集,用于命令模式的显示模组。

• DSI, CSI (DisplaySerialInterface, CameraSerialInterface

  • DSI 定义了一个位于处理器和显示模组之间的高速串行接口。

  • CSI 定义了一个位于处理器和摄像模组之间的高速串行接口。

• D-PHY:提供DSI和CSI的物理层定义

 2、DSI分层结构

DSI分四层,对应D-PHY、DSI、DCS规范、分层结构图如下:
• PHY 定义了传输媒介,输入/输出电路和和时钟和信号机制。
• Lane Management层:发送和收集数据流到每条lane。
• Low Level Protocol层:定义了如何组帧和解析以及错误检测等。
• Application层:描述高层编码和解析数据流。

3、Command和Video模式
• DSI兼容的外设支持Command或Video操作模式,用哪个模式由外设的构架决定
• Command模式是指采用发送命令和数据到具有显示缓存的控制器。主机通过命令间接的控制外设。Command模式采用双向接口。
• Video模式是指从主机传输到外设采用时实象素流。这种模式只能以高速传输。为减少复杂性和节约成本,只采用Video模式的系统可能只有一个单向数据路径。

4、三个主要的lane的类型

  • 单向时钟Lane

  • 单向数据Lane

  • 双向数据Lane

5、D-PHY的传输模式

  • 低功耗(Low-Power)信号模式(用于控制):10MHz (max)

  • 高速(High-Speed)信号模式(用于高速数据传输):80Mbps ~ 1Gbps/Lane

• D-PHY低层协议规定最小数据单位是一个字节

  • 发送数据时必须低位在前,高位在后.

• D-PHY适用于移动应用

  • DSI:显示串行接口

     • 一个时钟lane,一个或多个数据lane 

  • CSI:摄像串行接口

6、Lane模块

• PHY由D-PHY(Lane模块)组成

• D-PHY可能包含:

  • 低功耗发送器(LP-TX) 

  • 低功耗接收器(LP-RX)

  • 高速发送器(HS-TX)  

  • 高速接收器(HS-RX)

  • 低功耗竞争检测器(LP-CD)

7、 三个主要lane类型

  • 单向时钟Lane

    • Master:HS-TX, LP-TX

    • Slave:HS-RX, LP-RX

  • 单向数据Lane

    • Master:HS-TX, LP-TX

    • Slave:HS-RX, LP-RX

  • 双向数据Lane

    • Master, Slave:HS-TX, LP-TX, HS-RX, LP-RX, LP-CD

8、Lane状态和电压

  • Lane状态 

    • LP-00, LP-01, LP-10, LP-11 (单端)

    • HS-0, HS-1 (差分)

  • Lane电压(典型) 

    • LP:0-1.2V

    • HS:100-300mV (200mV)

2、DSI应用电路分层

MIPI-DSI分四层:应用层,协议层,通道管理层,物理层。MIPI-DSI是一个应用于接口方面的通信协议,其整体电路工作依据协议所定义的各个层协同工作。如下图:

应用层:根据应用模块的需要,在发送端对发送的命令和数据进行初步编码转化为MIPI-DSI所规定的格式,而在接收端则将接收的数据还原为应用模块所支持的数据格式及时序要求。 

协议层:信息传送采用数据包格式,包括长数据包和短数据包。发送数据的时候,将数据按照信息类型及内容进行压包,完成ECC码的生成和 CRC码的添加。接收数据的时候,依据ECC码和CRC码就整个数据包进行检错纠错,完成对包头和数据内容的译码并合理输送到应用层中。

通道管理层:依据设计所设定N(N最多为4)个数据通道,该层在发送端将需要发送的数据按照通道次序分成N组输送到相应的数据通道,使其经数据通道同时发送至从属端。在接收端,该层需要做的是将接收到的N组数据组合在一起恢复成原始的数据序列。 

物理层:在接收到低速发送请求时,逻辑电路将待发送的指令或数据串行化,并控制输出高摆幅低功耗电平进行低功耗传送请求,随后将指令或数据串行地加载到通道,接收端则识别传送模式完成串行数据的并行转换。当接收到高速请求后,电路发送高摆幅低功耗电平进行高速传送请求,然后将待发送数据经过串行转换之后驱动差分驱动模块以低摆幅信号在通道上传送,接收端则识别高速模式后正确地检测其同步信号将高速串行数据完成串并转换成功地接收。除此之外,总线控制权可在主机端与从属端之间交换,发送端发送放弃总线请求,接收端接收请求之后控制总线发起应答,总的过程为TA(turn around)操作。当主机端需要获取从属端接收数据的状况或者读取显示模块的参数或显示数据的时候,主机端发起TA操作,在从属端发送数据完毕之后自动发起TA操作,归还总线控制权。(总的来说就是一个交互过程,有点像TCP三次握手和四次挥手一样。而在交互的过程中会发送一些指令,和应答数据具体表现为多少需要看具体的模块分析。也可在数据波形中分析出来。)

MIPI-DSI 物理层(D-PHY协议)

        在整个协议的物理层中,在主机端和从属端之间采用的是同步连接,时钟通道用于传送高速时钟,一个或多个数据通道用于传送低功耗数据信号或高速数据信号。每一个通道都是利用两根互连线实现主机端和从属端的连接,并且支持高速模式和低速ESCAPE模式。在高速模式下,发送端同时驱动该通道仅有的两根互连线,输出低摆幅差分信号,例如200mV;低速模式下,发送端分别驱动互连线,各自输出单端信号,但摆幅相对较大,例如1.2V。
                            

关键总结:

       

信号两种工作状态,高速模式状态(HS)和低速模式状态(LP)。
物理层,就是要控制硬件信号的(时序),MIPI-DSI两种信号类型:时钟lane,两根线一个Dp,Dn。
数据lane,两根线一个Dp,Dn;数据lane根据需求有多个,最少一个lane(lane0),最多4个lane(lane0,lane1,lane2,lan3),其中只有lane0是双向的(低速模式双向,高速模式也只能是单向),其他的都只能是单向(高速模式单向),这里咱们之前提到过。

互连线两端是驱动单元和接收单元。驱动单元有差分发送模块(HS_TX)和低功耗单端发送模块(LP_TX),接收单元有差分接收模块(HS_RX)、低功耗单端接收模块(LP_RX)。

数据lane电平:

低速模式LP:0-1.2V,空闲电平(LP11)Dp,Dn是1.2V。
高速模式HS:100-300mV (200mV)
数据通道状态表格图:

         数据通道有6个状态:HS-0,HS-1,LP00,LP01,LP10,LP11.
其中HS-0表示高速模式下数据lane的Dp为低,Dn位高,LP01表示低速模式数据lane的Dp为低,Dn为高。在系统上电开机后,LP11保持100us左右(硬件初始化),然后进入stop状态,模式是间的转换都要回到这个状态,一般控制状态也是LP11状态。模式之间的转换,将在后面描述。既然提到了这6个状态,那么就不得不附上一个图了。6个状态转换图如下:

差分发送模块(HS_TX)以差分信号驱动互连线,高速通道上呈现两种状态:differential-0和differential-1。低功耗单端发送模块(LP_TX)独立地驱动两根互连线,通道上则有四种不同的状态:LP00、LP01、LP10和LP11。协议针对线路电平作了具体的定义并设置了三种工作模式:高速模式、控制模式和ESCAPE模式。通道默认为控制模式,线路处于STOP状态。当通道需要进入高速模式或ESCAPE模式,发送模块则需驱动线路为LP01或LP10向接收端发送请求。发送完请求序列之后便进行相应工作模式,发送LP11便可退出结束传送回到STOP控制状态。接收端需要时刻进行LP-RX接收,侦测线路电平明确通道的工作模式。

具体的操作过程:

 • 数据Lane的三种操作模式 

    • Escape mode, High-Speed(Burst) mode, Control mode

  •从控制模式的停止状态开始的可能事件有: 

    • Escape mode request (LP-11→LP-10→LP-00→LP-01→LP-00)

    • High-Speed mode request (LP-11→LP-01→LP-00)

    • Turnaround request (LP-11→LP-10→LP-00→LP-10→LP-00)

  • Escape mode是数据Lane在LP状态下的一种特殊操作 

    •在这种模式下,可以进入一些额外的功能:LPDT, ULPS, Trigger

    •数据Lane进入Escape mode模式通过LP-11→LP-10→LP-00→LP-01→LP-00

    •一旦进入Escape mode模式,发送端必须发送1个8-bit的命令来响应请求的动作

    • Escape mode 使用Spaced-One-Hot Encoding

  •超低功耗状态(Ultra-Low Power State)
    •这个状态下,lines处于空状态 (LP-00)
  • 时钟Lane的超低功耗状态
    •时钟Lane通过LP-11→LP-10→LP-00进入ULPS状态
    •通过LP-10 → TWAKEUP →LP-11退出这种状态,最小TWAKEUP时间为1ms

  • 高速数据传输
    •发送高速串行数据的行为称为高速数据传输或触发(burst)

    •全部Lanes门同步开始,结束的时间可能不同。
    •时钟应该处于高速模式

  • 各模操作式下的传输过程

    •进入Escape模式的过程 :LP-11→LP-10→LP-00→LP-01→LP-00→Entry Code → LPD (10MHz)
    •退出Escape模式的过程:LP-10→LP-11
    •进入高速模式的过程:LP-11→LP-01→LP-00→SoT(00011101) → HSD (80Mbps ~ 1Gbps)
    •退出高速模式的过程:EoT→LP-11
    •控制模式 - BTA 传输过程(该过程为主从交换时,作为主机端发的命令参数):LP-11→LP-10→LP-00→LP-10→LP-00
    •控制模式 - BTA 接收过程(ACK其就是从机应答主机的命令参数):LP-00→LP-10→LP-11

注:是不是有点像我上面所说的TCP的三次握手四次挥手的感觉。

下面继续!!!!!!!!!

主机与从机之间是以同步方式进行传输数据的,并且是DDR模式,即双边沿触发;这样可以在时钟频率不变的情况下将数据传输速率提高一倍,更加有效的利用带宽资源。主机的物理层将通道管理层分发下来的数据串行地转换成差分信号送到数据线上,同时产生差分时钟,每个数据通道是并行发送的,最多为4个通道,最小为1个通道,根据带宽需要而定。从机的物理层负责从差分串行比特流中捕获0或1,即Dp比Dn高时为1,Dn比Dp高时为0,然后组成8位数据送到通道管理层。同时还可以从数据包中检出水平同步信号和垂直同步信号。HS模式时需要依靠高速时钟,LP模式是不需要高速时钟,只需差分信号线即可,此时的时钟由差分线Dp和Dn异或得到

                                                             HS模式(高速模式时序图)

                                                             LP模式(低功耗模式时序图)        

这里需要仔细的分析图,然后与我们上面所提到的流程做一个对比,你会发现豁然开朗!!,好家伙我居然用了一个成语,哈哈哈哈!!!!

感觉所要了解的还有好多,以后再慢慢更新吧!!先上调试正菜。!!!

二、MIPI 端调试

         MIPI端代码上不需要我们去动它的架构,最最主要的还是dts文件的配置,不多说了,上菜!!!

#include <dt-bindings/display/media-bus-format.h>


/*---------mipi-dsi------------------*/

&dsi0_in_vp0 {
	status = "disabled";
};

&dsi0_in_vp1 {
        status = "okay";
};

&mipi_dphy0 {
        status = "okay";
};

&mipi_dphy1 {
        status = "disabled";
};

&route_dsi0 {
        status = "okay";
	connect = <&vp1_out_dsi0>;
};

&dsi0 {
	//rockchip,lane-rate = <1000>;
        status = "okay";
	dsi0_panel: panel@0 {
		compatible = "simple-panel-dsi";
		reg = <0>;
		//power-supply = <&vcc3v3_lcd0_n>;
		//reset-gpios = <&gpio3 RK_PD4 GPIO_ACTIVE_HIGH>;
		enable-gpios = <&gpio0 RK_PC7 GPIO_ACTIVE_HIGH>;
		pinctrl-0 = <&lcd_pwr_en_mipi &lcd_blk_en_mipi>;
		pinctrl-names = "default";
		backlight = <&backlight>;
		reset-delay-ms = <10>;
		enable-delay-ms = <10>;
		prepare-delay-ms = <5>;
		init-delay-ms = <20> ;
		unprepare-delay-ms = <10>;
		disable-delay-ms = <10>;
		dsi,flags = <(MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
			MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET)>;
		dsi,format = <MIPI_DSI_FMT_RGB888>;
		dsi,lanes  = <4>;
		panel-init-sequence = [
					39 02 04 FF 98 81 03
					15 02 02 01 00
					15 02 02 02 00
					15 02 02 03 53    
					15 02 02 04 53    
					15 02 02 05 13
					15 02 02 06 04
					15 02 02 07 02
					15 02 02 08 02
					15 02 02 09 00
					15 02 02 0a 00
					15 02 02 0b 00
					15 02 02 0c 00
					15 02 02 0d 00
					15 02 02 0e 00
					15 02 02 0f 00
					15 02 02 10 00
					15 02 02 11 00
					15 02 02 12 00
					15 02 02 13 00
					15 02 02 14 00
					15 02 02 15 05  //gpm
					15 02 02 16 05  //gpm
					15 02 02 17 03  //gpm
					15 02 02 18 03  //gpm
					15 02 02 19 00
					15 02 02 1a 00
					15 02 02 1b 00
					15 02 02 1c 00
					15 02 02 1d 00
					15 02 02 1e c0
					15 02 02 1f 00
					15 02 02 20 02
					15 02 02 21 09
					15 02 02 22 00
					15 02 02 23 00
					15 02 02 24 00
					15 02 02 25 00
					15 02 02 26 00
					15 02 02 27 00
					15 02 02 28 55
					15 02 02 29 03
					15 02 02 2a 00
					15 02 02 2b 00
					15 02 02 2c 00
					15 02 02 2d 00
					15 02 02 2e 00
					15 02 02 2f 00
					15 02 02 30 00
					15 02 02 31 00
					15 02 02 32 00
					15 02 02 33 00
					15 02 02 34 00
					15 02 02 35 00
					15 02 02 36 00
					15 02 02 37 00
					15 02 02 38 3C
					15 02 02 39 00
					15 02 02 3a 00
					15 02 02 3b 00
					15 02 02 3c 00
					15 02 02 3d 00
					15 02 02 3e 00
					15 02 02 3f 00
					15 02 02 40 00
					15 02 02 41 00
					15 02 02 42 00
					15 02 02 43 00
					15 02 02 44 00
					15 02 02 50 01
					15 02 02 51 23
					15 02 02 52 45
					15 02 02 53 67
					15 02 02 54 89
					15 02 02 55 ab
					15 02 02 56 01
					15 02 02 57 23
					15 02 02 58 45
					15 02 02 59 67
					15 02 02 5a 89
					15 02 02 5b ab
					15 02 02 5c cd
					15 02 02 5d ef
					15 02 02 5e 01
					15 02 02 5f 0A     //FW_CGOUT_L[1] RESE_ODD
					15 02 02 60 02     //FW_CGOUT_L[2] VSSG_ODD
					15 02 02 61 02     //FW_CGOUT_L[3] VSSG_ODD
					15 02 02 62 08     //FW_CGOUT_L[4] STV2_ODD
					15 02 02 63 15     //FW_CGOUT_L[5] VDD2_ODD
					15 02 02 64 14     //FW_CGOUT_L[6] VDD1_ODD
					15 02 02 65 02     //FW_CGOUT_L[7]
					15 02 02 66 11     //FW_CGOUT_L[8] CK11
					15 02 02 67 10     //FW_CGOUT_L[9] CK9
					15 02 02 68 02     //FW_CGOUT_L[10]
					15 02 02 69 0F     //FW_CGOUT_L[11] CK7
					15 02 02 6a 0E     //FW_CGOUT_L[12] CK5
					15 02 02 6b 02     //FW_CGOUT_L[13]   
					15 02 02 6c 0D     //FW_CGOUT_L[14] CK3  
					15 02 02 6d 0C     //FW_CGOUT_L[15] CK1  
					15 02 02 6e 06     //FW_CGOUT_L[16] STV1_ODD  
					15 02 02 6f 02     //FW_CGOUT_L[17]   
					15 02 02 70 02     //FW_CGOUT_L[18]   
					15 02 02 71 02     //FW_CGOUT_L[19]   
					15 02 02 72 02     //FW_CGOUT_L[20]   
					15 02 02 73 02     //FW_CGOUT_L[21]   
					15 02 02 74 02     //FW_CGOUT_L[22]   
					15 02 02 75 0A     //BW_CGOUT_L[1]   RESE_ODD 
					15 02 02 76 02     //BW_CGOUT_L[2]   VSSG_ODD 
					15 02 02 77 02     //BW_CGOUT_L[3]   VSSG_ODD  
					15 02 02 78 06     //BW_CGOUT_L[4]   STV2_ODD 
					15 02 02 79 15     //BW_CGOUT_L[5]   VDD2_ODD 
					15 02 02 7a 14     //BW_CGOUT_L[6]   VDD1_ODD 
					15 02 02 7b 02     //BW_CGOUT_L[7]    
					15 02 02 7c 10     //BW_CGOUT_L[8]   CK11 
					15 02 02 7d 11     //BW_CGOUT_L[9]   CK9 
					15 02 02 7e 02     //BW_CGOUT_L[10]   
					15 02 02 7f 0C     //BW_CGOUT_L[11]  CK7
					15 02 02 80 0D     //BW_CGOUT_L[12]  CK5 
					15 02 02 81 02     //BW_CGOUT_L[13]   
					15 02 02 82 0E     //BW_CGOUT_L[14]  CK3 
					15 02 02 83 0F     //BW_CGOUT_L[15]  CK1 
					15 02 02 84 08     //BW_CGOUT_L[16]  STV1_ODD 
					15 02 02 85 02     //BW_CGOUT_L[17]   
					15 02 02 86 02     //BW_CGOUT_L[18]   
					15 02 02 87 02     //BW_CGOUT_L[19]   
					15 02 02 88 02     //BW_CGOUT_L[20]   
					15 02 02 89 02     //BW_CGOUT_L[21]   
					15 02 02 8A 02     //BW_CGOUT_L[22] 
					39 02 04 FF 98 81 04 	//self test  15 00 02 2F 01
					15 02 02 3B C0     // ILI4003D sel 
					15 02 02 6C 15
					15 02 02 6E 30     //VGH 16V   vgh Clamp level
					15 02 02 6F 33     //Pump ratio VGH=VSPX3 VGL=VSNX2
					15 02 02 8D 1F   // VGL clamp -12.03    LCD SPEC -12
					15 02 02 3A A4     //power saving
					15 02 02 35 17    // Source OP drive time
					15 02 02 87 BA
					15 02 02 26 76
					15 02 02 B2 D1
					15 02 02 B5 07
					15 02 02 88 0B
					15 02 02 21 30
					39 02 04 FF 98 81 01
					15 02 02 22 0A
					15 02 02 31 09   //zig-zag type1 09
					15 02 02 40 33   //for ext_cpck_sel
					15 02 02 42 44   //VGHL-CLK-SELA   VGHL-CLK-SELB
					15 02 02 53 2E  // FW VCOM
					15 02 02 55 88  // BW VCOM
					15 02 02 50 95    //Source 4.5, 95    AE 4.8
					15 02 02 51 95    //Source -4.5 95    AE -4.8
					15 02 02 60 30
					15 02 02 2E C8
					15 02 02 A0 0F        //VP255Gamma P
					15 02 02 A1 19               //VP251
					15 02 02 A2 28              //VP247
					15 02 02 A3 12              //VP243
					15 02 02 A4 14               //VP239
					15 02 02 A5 26               //VP231
					15 02 02 A6 1A               //VP219
					15 02 02 A7 1C               //VP203
					15 02 02 A8 7C               //VP175
					15 02 02 A9 1D               //VP144
					15 02 02 AA 2A               //VP111
					15 02 02 AB 6A               //VP80
					15 02 02 AC 18               //VP52
					15 02 02 AD 17               //VP36
					15 02 02 AE 4B               //VP24
					15 02 02 AF 21               //VP16
					15 02 02 B0 26               //VP12
					15 02 02 B1 4B               //VP8
					15 02 02 B2 6C               //VP4
					15 02 02 B3 3F               //VP0
					15 02 02 C0 01         //VN255 GAMMA N
					15 02 02 C1 18               //VN251
					15 02 02 C2 26               //VN247
					15 02 02 C3 0F              //VN243
					15 02 02 C4 12               //VN239
					15 02 02 C5 23               //VN231
					15 02 02 C6 18               //VN219
					15 02 02 C7 1C               //VN203
					15 02 02 C8 76               //VN175
					15 02 02 C9 1A               //VN144
					15 02 02 CA 26               //VN111
					15 02 02 CB 63               //VN80
					15 02 02 CC 1B              //VN52
					15 02 02 CD 1A               //VN36
					15 02 02 CE 4E               //VN24
					15 02 02 CF 21              //VN16
					15 02 02 D0 29               //VN12
					15 02 02 D1 47               //VN8
					15 02 02 D2 6A               //VN4
					15 02 02 D3 3F               //VN0
					39 02 04 FF 98 81 00
					05 02 01 35               //TE OUT
					05 78 01 11        //sleep out
					05 05 01 29        //display on
				];

		panel-exit-sequence = [
					05 16 01 28
					05 78 01 10
				];



               disp_timings0: display-timings {
                        native-mode = <&dsi0_timing0>;
                        dsi0_timing0: timing0 {
						clock-frequency = <145000000>;//mipi时钟的算法,根据如下的配置算出来的,如果不准确,或者时钟太低会导致屏闪的问题出现。
dsi clock = H-total * V-total * fps*位深 / lane numberl / 2  
          =(HSYNC+HBP+HACTIVE+HFP)*(VSYNC+VBP+VACTIVE+VFP) * 60 * 24 / 4 / 2 
						hactive = <1920>;//行有效像素
						vactive = <1200>;//场有效像素
						hback-porch = <12>;//行后肩
						hfront-porch = <128>;//行前肩
						hsync-len = <20>;//行同步
						vback-porch = <20>;//场后肩
						vfront-porch = <19>;//场前肩
						vsync-len = <4>;//场同步
						hsync-active = <0>;
						vsync-active = <0>;
						de-active = <0>;
						pixelclk-active = <0>;
						swap-rb = <0>;
						swap-rg = <0>;
						swap-gb = <0>;
                    };
                };

		ports {
			#address-cells = <1>;
			#size-cells = <0>;

			port@0 {
				reg = <0>;
				panel_in_dsi: endpoint {
					remote-endpoint = <&dsi_out_panel>;
				};
			};
		};
	};

	ports {
		#address-cells = <1>;
		#size-cells = <0>;

		port@1 {
			reg = <1>;
			dsi_out_panel: endpoint {
				remote-endpoint = <&panel_in_dsi>;
			};
		};
	};
};
&pinctrl {
	mipi_gpio {
		lcd_pwr_en_mipi:lcd_pwr_en_mipi {
			rockchip,pins = <0 RK_PC7 RK_FUNC_GPIO &pcfg_output_high>;
		};
		lcd_blk_en_mipi:lcd_blk_en_mipi {
			rockchip,pins = <3 RK_PA1 RK_FUNC_GPIO &pcfg_output_high>;
		};
	};

};






&i2c1 { //这是I2C的dts配置,我GM8775C是挂载到I2C1上的。
	status = "okay";
	
       gm8775c@2c {
               compatible = "gm8775c,mipi_to_lvds";
               reg = <0x2c>;
               status = "okay";
       };
}

三、GM8775C 简介及驱动文件编写

       

        GM8775C 会有一个GM8775C_A1.1__IIC20190819.exe,用于生成配置文件,寄存器以及寄存器里对应的值,最开始我是通过shell脚本的方式,通过挂载到i2c的地址写入配置文件,但是一掉电,寄存器里的值就又恢复了默认值(但是在我们调试的时候就需要用到shell脚本的方式,比较便捷,我的博客中有shell脚本相关的文章,以及如何使用的),就会导致外接的LVDS屏不亮,所以我们需要将GM8775C_A1.1__IIC20190819 生成的文件,加载到驱动中,自己写一个I2C的驱动就可以了,比较简单。

         上图的配置是根据你自己的LVDS屏的参数进行配置的。而我的屏就是上图的一些配置参数。

接下来就是GM8775C的驱动文件了。

#include <linux/clk.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/of_gpio.h>
#include <linux/proc_fs.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/regmap.h>

#define REGISTER_NUM 34

struct gm8775c_priv {
	struct regmap *regmap;
	struct i2c_client *client;
	
};

//Modify screen parameters and modify default values
static const struct reg_default gm8775c_reg_defaults[] = {   
		{0x00,0xAA},
		{0x48,0x02},
		{0xB6,0x20},
		{0x01,0x80},
		{0x02,0xB0},
		{0x03,0x47},
		{0x04,0x80},
		{0x05,0x14},
		{0x06,0x0C},
		{0x07,0x00},
	    {0x08,0x13},
		{0x09,0x04},
		{0x0A,0x14},
		{0x0B,0x82},
		{0x0C,0x14},
		{0x0D,0x01},
		{0x0E,0x80},
		{0x0F,0x20},
		{0x10,0x20},
		{0x11,0x03},
		{0x12,0x1B},
		{0x13,0x63},
		{0x14,0x32},
		{0x15,0x10},
		{0x16,0x40},
		{0x17,0x00},
		{0x18,0x32},
		{0x19,0x10},
     	{0x1A,0x40},
		{0x1B,0x00},
		{0x1E,0x46},
		{0x51,0x30},
		{0x1F,0x10},
		{0x2A,0x01},
};

static int gm8775c_read(u8 reg, u8 * rt_value, struct i2c_client *client)
{
	int ret;
	u8 read_cmd[3] = { 0 };
	u8 cmd_len = 0;

	read_cmd[0] = reg;
	cmd_len = 1;

	if (client->adapter == NULL)
		printk("gm8775c_read client->adapter==NULL\n");

	ret = i2c_master_send(client, read_cmd, cmd_len);
	if (ret != cmd_len) {
		printk("gm8775c_read error1\n");
		return -1;
	}

	ret = i2c_master_recv(client, rt_value, 1);
	if (ret != 1) {
		printk("gm8775c_read error2, ret = %d.\n", ret);
		return -1;
	}

	return 0;
}

static int gm8775c_write(u8 reg, unsigned char value, struct i2c_client *client)
{
	int ret = 0;
	u8 write_cmd[2] = { 0 };

	write_cmd[0] = reg;
	write_cmd[1] = value;

	ret = i2c_master_send(client, write_cmd, 2);
	if (ret != 2) {
		printk("gm8775c_write error->[REG-0x%02x,val-0x%02x]\n",
		       reg, value);
		return -1;
	}

	return 0;
}

static const struct of_device_id gm8775_of_match[] = { 
	{
		.compatible = "gm8775c,mipi_to_lvds",
	}, 
	{ }
};
MODULE_DEVICE_TABLE(of, gm8775_of_match);

static const struct i2c_device_id gm8775_i2c_id[] = {
	{"GM8775C_MTL", 0},
	{}
};

static ssize_t gm8775c_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
	int val=0, flag=0;
	u8 i=0, reg, num, value_w, value_r;

	
	struct gm8775c_priv *gm8775c = dev_get_drvdata(dev);
	
	val = simple_strtol(buf, NULL, 16);
	flag = (val >> 16) & 0xFF;
	
	if (flag) {
		reg = (val >> 8) & 0xFF;
		value_w = val & 0xFF;
		printk("\nWrite: start REG:0x%02x,val:0x%02x,count:0x%02x\n", reg, value_w, flag);
		while(flag--) {
			gm8775c_write(reg, value_w,  gm8775c->client);
			printk("Write 0x%02x to REG:0x%02x\n", value_w, reg);
			reg++;
		}
	} else {
		reg = (val >> 8) & 0xFF;
		num = val & 0xff;
		printk("\nRead: start REG:0x%02x,count:0x%02x\n", reg, num);
		do {
			value_r = 0;
			gm8775c_read(reg, &value_r, gm8775c->client);
			printk("REG[0x%02x]: 0x%02x;  ", reg, value_r);
			reg++;
			i++;
			if ((i==num) || (i%4==0))	printk("\n");
		} while (i<num);
	}
	
	return count;
}

static ssize_t gm8775c_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	printk("echo flag|reg|val > gm8775c\n");
	printk("eg read star addres=0x06,count 0x10:echo 0610 >gm8775c\n");
	printk("eg write star addres=0x90,value=0x3c,count=4:echo 4903c >gm8775c\n");
	return 0;
}

static DEVICE_ATTR(gm8775c, 0644, gm8775c_show, gm8775c_store);

static struct attribute *egm8775c_debug_attrs[] = {
	&dev_attr_gm8775c.attr,
	NULL,
};

static struct attribute_group gm8775c_debug_attr_group = {
	.name   = "gm8775c_debug",
	.attrs  = egm8775c_debug_attrs,
};

/*
 *****************************************************************************
 *
 *	Driver Interface
 *
 *****************************************************************************
 */

static int gm8775c_probe(struct i2c_client *client,
			const struct i2c_device_id *id)
{
	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
	struct gm8775c_priv *gm8775c;
	int i;	
	int ret;
	
	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK |
						 I2C_FUNC_SMBUS_BYTE_DATA)) {
			dev_err(&adapter->dev, "doesn't support I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK\n");
			return -ENODEV;
		}
	
	gm8775c = devm_kzalloc(&client->dev, sizeof(*gm8775c), GFP_KERNEL);
	if (!gm8775c)
		return -ENOMEM;
	
  	gm8775c->client = client;
	i2c_set_clientdata(client, gm8775c);
	for(i = 0;i < REGISTER_NUM; i++)
			gm8775c_write(gm8775c_reg_defaults[i].reg, gm8775c_reg_defaults[i].def, gm8775c->client);	

	ret = sysfs_create_group(&client->dev.kobj, &gm8775c_debug_attr_group);
	if (ret) {
		pr_err("failed to create attr group\n");
	}
	
	return ret;
}

static int gm8775c_remove(struct i2c_client *client)
{ 
	return 0;
}

static struct i2c_driver gm8775c_driver = {
	.driver = {
		.name = "gm8775c-mipi-to-lvds",
		.of_match_table = of_match_ptr(gm8775_of_match),
	},
	.probe = gm8775c_probe,
	.remove = gm8775c_remove,
	.id_table = gm8775_i2c_id,
};

static int __init gm8775c_modinit(void)
{
	int ret;
	ret = i2c_add_driver(&gm8775c_driver);
	if (ret != 0)
		printk("Failed to register gm8775c i2c driver : %d \n", ret);
	return ret;
}
late_initcall(gm8775c_modinit);

static void __exit gm8775c_exit(void)
{
	i2c_del_driver(&gm8775c_driver);
}

module_exit(gm8775c_exit);


//module_i2c_driver(gm8775c_driver);

MODULE_AUTHOR("Alexander Bigga <[email protected]>");
MODULE_DESCRIPTION("ST Microelectronics M41T80 series RTC I2C Client Driver");
MODULE_LICENSE("GPL");

总结

        GM8775C硬件连接尽量上它在系统不掉电的情况下,让它也不能够掉电,不然的会在息屏掉电后,我们还需要去处理使它初始化。

        上面提到的那个.exe文件,有一个测试模式,如你的测试模式能够亮屏的话,就证明你8775C端的电路肯定是没有问题的,就需要分析mipi端与8775C端的配置文件参数是否正在,能够匹配上。

        注意:首先要保证mipi端输出的信号能够点亮mipi屏,再考虑GM8775C端的问题。

猜你喜欢

转载自blog.csdn.net/qq_48709036/article/details/124296103