RK3399 MIPI屏幕調試記錄

發佈於2019年1月14日 -

RK3399是瑞芯微Rockchip公司目前最高端的SoC,它的上一代RK3288廣泛用在各個地方,例如,ASUS C100PA Chromebook Flip使用的就是RK3288。作爲RK3288的升級型號,RK3399的核心數提高到了由兩個A72和四個A53核心組成的六核,GPU升級爲了Mali-T860。

這次的目標是給基於RK3399的某個開發板移植一款MIPI屏幕。首先簡單介紹一下MIPI。MIPI是Mobile Industry Processor Interface的縮寫,隨着移動設備的不斷髮展,MIPI已經發展出多個部分,最常用的是CSI和DSI,分別對應攝像頭(camera)和顯示屏(display)。本文所述的內容就是這裏的DSI部分。

網上有不少介紹給RK3399移植MIPI屏幕的教程,例如最出名的firefly開發板就在其官網給出了移植指南,如果移植很順利,這篇文章就不存在了😂 下面主要記錄一下調試流程,供有需要的人蔘考吧。

第一次嘗試

在我真正動手之前,z4yx大佬教育我說,MIPI非常難調;gaoyichuan大佬則舉了他調試MIPI CSI的例子,說調了一個暑假最終放棄了。究其原因,MIPI DSI的時鐘是差分信號,幅度很低(100mV級別),頻率卻高達數十甚至數百MHz,此外,DSI有兩種工作模式,分別是命令(Command)和視頻(Video)模式,這兩者的頻率和幅度也不盡相同。因此,使用普通的示波器是無法看到DSI信號的。實踐證明,z4yx大佬總是對的,使用普通的Tektronix示波器,只能看到工頻干擾信號,完全沒法分析。

可是我怎麼會信邪呢?搞之!

由於每個廠家的引腳定義都不同,所以在開始調試之前,需要根據引腳定義製作轉接板。轉接板負責兩件事:一是給屏幕本身提供合適的電壓,二是利用LED驅動芯片驅動背光。這次調試的轉接板不是我畫的,也不是我焊的。我按照1腳對1腳的方法,用FPC排線接好之後,一上電,鉭電容就炸了(就像這樣)。顯然,電源接反了,我仔細檢查了一下,我拿到的這塊轉接板的FPC畫反了。出師未捷身先死,屏幕和開發板都燒了😂

重新買了開發板、屏幕並重新焊了轉接板後,屏幕的背光亮了。

作爲一個有良心的廠家,Rockchip已經寫好了常見設備的驅動,我們增加一個新外設的標準流程就是改設備樹(Device Tree)。而開發板自帶的屏幕也是DSI的,所以事實上驅動是有的。由於各種資料都講了DSI的時序,所以這裏就不再重複,只補充一下,DSI部分的clock-frequency指的是PCLK,不要弄錯了。開機,啥都沒有😂

移植代碼

我忽然想到廠家還給了一個初始化腳本,所謂的初始化腳本,就是一個需要在上電後,給屏幕發的一組命令,至於它是什麼,我沒有查到資料介紹。可是我手裏的這份設備樹沒有地方可以寫,令人絕望。

我對比了一下手裏的代碼和Rockchip在GitHub上的代碼,發現我的代碼版本非常老,於是我立刻想到了兩個辦法:一是把內核換成Rockchip最新的,二是把DSI部分的代碼替換成最新的。使用第一個方法,設備樹變化太大,基本上需要重新配置一遍所有的設備,這個基本做不到。於是在z4yx大佬的建議下,我又試了第二個方法。

本以爲換一下Rockchip的顯示屏驅動panel-simple.c就好,然而它依賴若干變動了的結構體,這些結構體又依賴若干新的頭文件,一路換下來,甚至已經換到HDMI去了,這讓我想到了這幅圖:

在折騰了幾個小時之後,我放棄了這個潛入無底洞的做法,轉而專注於修改屏幕驅動panel-simple.c。這一次我沒有選擇最新的代碼,而是去Rockchip的倉庫裏找了最早加上有初始化命令的代碼,然後把初始化命令相關的代碼移了過來,修完了缺失的結構體後,順利通過編譯。

我滿懷期待地上電,然而什麼都沒有發生,dmesg也沒有任何異常,而使用Vysor也能看到屏幕尺寸改變了,我很絕望🤣🤣🤣

MIPI測試儀

那麼,屏幕是好的嗎?

我從來沒見這個屏幕顯示過畫面,所以開始懷疑這件事,是不是我把屏幕弄壞了。由於沒有合適的示波器,我也沒法看數據到底哪裏不對(事實上即使有,也非常不好調)。一籌莫展之際,抱着試試看的想法,我打開了萬能的淘寶,還真找到了MIPI測試儀。

買回來,把時序的參數填上,再把初始化代碼填進去:

原來屏幕是好的!(這是視頻截圖,所以畫質渣)

再戰DSI

仔細看一下MIPI測試儀的配置,無非就是設置好時序,然後發指令。既然設備樹不好搞,我直接寫在代碼裏發總行了吧。我把發指令的代碼直接寫在了panel_simple_enable函數裏,這個函數會在屏幕點亮之前調用,所以應該是個發指令的好地方。於是屏幕真的有東西了!

打開可以讓屏幕顯示純色的apk,可以看到它的顏色基本都是對的,但是,畫面就這樣詭異,充滿了條紋。諮詢廠家,建議修改PCLK,但怎麼改都無效。廠家還建議替換初始化代碼,也無效。

我又去檢查了一遍MIPI測試儀的代碼,發現發完指令之後,需要讓屏幕進入Video模式,那我的屏幕現在是Video模式嗎?我翻了RK3399的寄存器手冊,但是讀到的值似乎都不太對,於是還是回到了代碼。

經過反覆比對,我最終在DSI的驅動dw-mipi-dsi.c裏找到了端倪。它的dw_mipi_dsi_encoder_enable函數是這樣寫的:

if (drm_panel_prepare(dsi->panel))
  dev_err(dsi->dev, "failed to prepare panel\n");
if (pdata->grf_dsi0_mode_reg)
  regmap_write(dsi->grf_regmap, pdata->grf_dsi0_mode_reg,
			   pdata->grf_dsi0_mode);
dw_mipi_dsi_phy_init(dsi);
dw_mipi_dsi_wait_for_two_frames(dsi);
dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_VID_MODE);
drm_panel_enable(dsi->panel);

它的流程是,先調用prepare函數,然後初始化DSI的phy部分,接下來設置模式爲Video,最後調用enable函數。所以我在enable函數執行完命令之後,並沒有進入Video模式。

這個時候回頭再去看當時移植的代碼,Rockchip修正後的驅動是把初始化代碼寫在了prepare函數裏,而在dw_mipi_dsi_phy_init函數裏,寫了這樣一句:

dsi_write(dsi, DSI_PWR_UP, POWERUP);

也就是說,如果混合兩個版本的代碼(修正過的panel-simple.c和修正前的dw-mipi-dsi.c),實際的執行流程是,先配置屏幕,再初始化,能用就見鬼了🤣

所以,只需要修改dw_mipi_dsi_encoder_enable的實現,調換一下初始化順序即可:

if (pdata->grf_dsi0_mode_reg)
  regmap_write(dsi->grf_regmap, pdata->grf_dsi0_mode_reg,
			   pdata->grf_dsi0_mode);
dw_mipi_dsi_phy_init(dsi);
dw_mipi_dsi_wait_for_two_frames(dsi);
if (drm_panel_prepare(dsi->panel))
  dev_err(dsi->dev, "failed to prepare panel\n");
dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_VID_MODE);
drm_panel_enable(dsi->panel);

經過如此一番修改,屏幕終於能工作了:

總結與後續

雖然MIPI測試儀並不能幫我解決實際問題,但一個正常工作的設備確實給了我調試的信心,儘管它的代碼不開源,它的工作流程還是給了我不少啓發。

值得一說的後續是,在我燒了第一塊板子之後,每次摸MIPI接口之前,我都會去摸一下電源的地線放放靜電,然而在調完屏幕之後,這塊板子的MIPI接口還是被我莫名其妙地燒壞了。在後續的設計裏,我使用了EMI4183作爲放ESD的器件,不過目前還沒有實際測試。


題圖:Untitled

作者:Mathew Schwartz