題圖:河北太行山脈,山頭密密麻麻都被太陽能板覆蓋了。
歡迎關注,每周更新!?
本合集分享的是,我當初學習Linux驅動的來時路——《《驅動之路》開篇:自序&前言》。
正文
在實際工作中,雖然并不需要我們從零編寫 LCD 驅動程序,但必須掌握 LCD 驅動框架 —— 這是分析現有驅動、定位問題,以及在必要時修改驅動以適配硬件或需求的必修課。
分析之前,首先需要掌握字符設備驅動的基本框架,看前面文章《驅動之路#01:Hello World!》。LCD 驅動本質上也是字符設備驅動,理解了字符設備驅動的框架,有助于分析 LCD 驅動程序。
下面我們就運用前面掌握的字符設備驅動基本框架,以 RK3576 平臺的 panel-simple.c 為例,淺淺分析 LCD 驅動。
驅動位置:kernel-6.1/drivers/gpu/drm/panel/panel-simple.c
如何閱讀驅動程序,準確說是如何閱讀字符設備驅動程序?閱讀一個字符設備驅動程序,應該從它的入口函數開始看。
看到 panel-simple.c 的入口函數*register() 注冊了 3 個驅動,說明該驅動程序同時兼容DPI、DSI和SPI接口連接的 panel。

拿 panel_simple_dsi_driver 舉例分析,繼續往下看。當 driver 與 device(即 dts) 的 compatible 屬性匹配就會調用 panel_simple_dsi_probe 函數。

只有我們弄清楚panel_simple_dsi_probe函數的具體操作就基本了解了panel_simple_dsi_driver驅動,這就是分析字符設備驅動程序的大致流程。
接下來,分析 panel_simple_dsi_probe 函數,直接看代碼注釋。
/** MIPI-DSI 面板探測函數* 負責初始化DSI接口的面板設備*/staticintpanel_simple_dsi_probe(structmipi_dsi_device *dsi){ /* 局部變量定義 */ structpanel_simple *panel; // 通用面板數據結構 structdevice *dev = &dsi->dev; // 獲取設備指針,簡化后續訪問 conststructpanel_desc_dsi *desc;// DSI面板描述符(預定義或動態) structpanel_desc_dsi *d; // 動態分配的描述符指針 conststructof_device_id *id; // 設備樹匹配結果 interr; // 錯誤碼 /* 1. 設備樹匹配 - 檢查設備是否在驅動支持列表中 */ id = of_match_node(dsi_of_match, dsi->dev.of_node); if(!id) return-ENODEV; // 設備不在支持列表中,返回設備不存在錯誤 /* 2. 面板描述符處理 */ if(!id->data) { /* 情況A: 設備樹中定義了自定義面板,沒有預定義描述符 */ // 動態分配DSI面板描述符內存 d = devm_kzalloc(dev,sizeof(*d), GFP_KERNEL); if(!d) return-ENOMEM; // 內存分配失敗 /* 從設備樹節點解析面板參數 */ err = panel_simple_dsi_of_get_desc_data(dev, d); if(err) { dev_err(dev,"failed to get desc data: %dn", err); returnerr; // 參數解析失敗 } } /* 注意: 如果 id->data 不為空,表示使用預定義的描述符,跳過動態分配 */ /* 3. 選擇最終使用的描述符 */ desc = id->data ? id->data : d; // 優先使用預定義,否則使用動態解析的描述符 /* 4. 調用通用面板探測函數進行基礎初始化 */ err = panel_simple_probe(&dsi->dev, &desc->desc); if(err 0)? ? ? ??return?err; ?// 通用面板初始化失敗? ??/* 5. 獲取面板數據結構并關聯DSI設備 */? ? panel = dev_get_drvdata(dev); ?// 從設備私有數據中獲取panel_simple指針? ? panel->dsi = dsi; // 將DSI設備指針保存到面板結構中,建立雙向關聯 /* 6. 背光設備處理 */ if(!panel->base.backlight) { /* 如果面板沒有背光設備,注冊一個DCS背光設備 */ structbacklight_properties props; // 背光屬性結構 /* 初始化背光屬性 */ memset(&props,0,sizeof(props)); props.type = BACKLIGHT_RAW; // 背光類型:原始模式 props.brightness =255; // 初始亮度:最大值 props.max_brightness =255; // 最大亮度:255級 /* 注冊背光設備 */ panel->base.backlight = devm_backlight_device_register(dev, dev_name(dev), dev, panel, &dcs_bl_ops, &props); /* 錯誤處理 */ if(IS_ERR(panel->base.backlight)) { err = PTR_ERR(panel->base.backlight); dev_err(dev,"failed to register dcs backlight: %dn", err); returnerr; // 背光設備注冊失敗 } } /* 注意: 如果面板已有背光設備(如通過設備樹引用),則跳過此步驟 */ /* 7. 配置DSI主機控制器參數 */ dsi->mode_flags = desc->flags; // 設置DSI工作模式標志 dsi->format = desc->format; // 設置像素數據格式 dsi->lanes = desc->lanes; // 設置數據通道數量 /* 8. 將面板附加到DSI主機 */ err = mipi_dsi_attach(dsi); if(err) { /* 附件失敗時的清理工作 */ structpanel_simple *panel = mipi_dsi_get_drvdata(dsi); drm_panel_remove(&panel->base); // 從DRM子系統移除面板 } /* 9. 返回執行結果 */ returnerr; // 成功返回0,失敗返回錯誤碼}
我們在設備樹中定義的屏參以及mipi 屏幕相關初始化代碼。

最終是通過函數 panel_simple_dsi_of_get_desc_data()和 panel_simple_of_get_desc_data()獲取到的。

至此,mipi-dsi 屏幕驅動分析完畢!能分析閱讀整體驅動框架和函數調用流程就行,不需要花大量時間和精力去解讀每行代碼解讀,AI時代沒多大意義。
最后附上兩個比較重要的函數,供閱讀參考。
附錄:static int panel_simple_dsi_of_get_desc_data(struct device *dev, struct panel_desc_dsi *desc){ struct device_node *np = dev->of_node; u32val; int err; // 1. 首先解析通用面板參數 err = panel_simple_of_get_desc_data(dev, &desc->desc); if(err) returnerr; // 2. 設置連接器類型為DSI desc->desc.connector_type = DRM_MODE_CONNECTOR_DSI; // 3. 從dts解析DSI特有參數 if(!of_property_read_u32(np,"dsi,flags", &val)) desc->flags =val; if(!of_property_read_u32(np,"dsi,format", &val)) desc->format =val; if(!of_property_read_u32(np,"dsi,lanes", &val)) desc->lanes =val; return0;}函數 panel_simple_dsi_of_get_desc_data( )中調用 panel_simple_dsi_of_get_desc_data( )獲取面板 desc_data。static int panel_simple_of_get_desc_data(struct device *dev, struct panel_desc *desc){ struct device_node *np = dev->of_node; u32 bus_flags; constvoid *data; int len; int err; // 1. 解析顯示時序 if(of_child_node_is_present(np,"display-timings")) { struct drm_display_mode *mode; mode = devm_kzalloc(dev, sizeof(*mode), GFP_KERNEL); if(!mode) return-ENOMEM; // 從設備樹解析DRM顯示模式 if(!of_get_drm_display_mode(np, mode, &bus_flags, OF_USE_NATIVE_MODE)) { desc->modes = mode; desc->num_modes =1; desc->bus_flags = bus_flags; } }elseif(of_child_node_is_present(np,"panel-timing")) { struct display_timing *timing; struct videomode vm; timing = devm_kzalloc(dev, sizeof(*timing), GFP_KERNEL); if(!timing) return-ENOMEM; // 從設備樹解析顯示時序 if(!of_get_display_timing(np,"panel-timing", timing)) { desc->timings = timing; desc->num_timings =1; // 轉換時序標志為總線標志 bus_flags =0; vm.flags = timing->flags; drm_bus_flags_from_videomode(&vm, &bus_flags); desc->bus_flags = bus_flags; } } // 2. 解析基本面板屬性 if(desc->num_modes || desc->num_timings) { of_property_read_u32(np,"bpc", &desc->bpc); // 每顏色位數 of_property_read_u32(np,"connector-type", &desc->connector_type); of_property_read_u32(np,"bus-format", &desc->bus_format);// 總線格式 of_property_read_u32(np,"width-mm", &desc->size.width); // 物理寬度 of_property_read_u32(np,"height-mm", &desc->size.height);// 物理高度 } // 3. 解析時序延遲參數 of_property_read_u32(np,"prepare-delay-ms", &desc->delay.prepare); of_property_read_u32(np,"enable-delay-ms", &desc->delay.enable); of_property_read_u32(np,"disable-delay-ms", &desc->delay.disable); of_property_read_u32(np,"unprepare-delay-ms", &desc->delay.unprepare); of_property_read_u32(np,"reset-delay-ms", &desc->delay.reset); of_property_read_u32(np,"init-delay-ms", &desc->delay.init); // 4. 解析初始化序列 data= of_get_property(np,"panel-init-sequence", &len); if(data) { desc->init_seq = devm_kzalloc(dev, sizeof(*desc->init_seq), GFP_KERNEL); if(!desc->init_seq) return-ENOMEM; // 解析初始化命令序列 err = panel_simple_parse_cmd_seq(dev,data, len, desc->init_seq); if(err) { dev_err(dev,"failed to parse init sequencen"); returnerr; } } // 5. 解析退出序列 data= of_get_property(np,"panel-exit-sequence", &len); if(data) { desc->exit_seq = devm_kzalloc(dev, sizeof(*desc->exit_seq), GFP_KERNEL); if(!desc->exit_seq) return-ENOMEM; // 解析退出命令序列 err = panel_simple_parse_cmd_seq(dev,data, len, desc->exit_seq); if(err) { dev_err(dev,"failed to parse exit sequencen"); returnerr; } } return0;}
(完)
審核編輯 黃宇
-
lcd
+關注
關注
36文章
4615瀏覽量
177203 -
驅動程序
+關注
關注
19文章
869瀏覽量
50456 -
rk3576
+關注
關注
1文章
265瀏覽量
1546
發布評論請先 登錄
硬核進階:RK3576 Android15?驅動與系統開發實戰指南
RK3576驅動高端顯控系統升級:多屏拼控與AI視覺融合解決方案
【作品合集】米爾RK3576開發板測評
【作品合集】靈眸科技EASY EAI Orin Nano(RK3576)開發板測評
瑞芯微RK3576與RK3576S有什么區別,性能參數配置與型號差異解析
驅動之路#04:LCD 驅動程序分析(基于RK3576)
評論