当前位置:网站首页>基于STM32MP157调试MIPI-DSI屏幕
基于STM32MP157调试MIPI-DSI屏幕
2022-06-25 06:41:00 【Talent、me】
平台:STM32MP157
屏幕:mipi-dsi接口,1024x600
内核版本:linux5-4
本人是第一次调试mipi屏,在157这个平台上遇到的问题有一点多,接下来简单的描述下我的调试经验
一、先配置一下设备树DTB
<dc {
port {
#address-cells = <1>;
#size-cells = <0>;
ltdc_ep1_out: [email protected]1 {
reg = <1>;
remote-endpoint = <&dsi_in>;
};
};
};
&dsi {
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
ports {
#address-cells = <1>;
#size-cells = <0>;
[email protected]0 {
reg = <0>;
dsi_in: endpoint {
remote-endpoint = <<dc_ep1_out>;
};
};
[email protected]1 {
reg = <1>;
dsi_out: endpoint {
remote-endpoint = <&dsi_panel_in>;
};
};
};
[email protected]0 {
compatible = "Hyb-Mipi";
reg = <0>;
enable-gpios = <&gpioc 6 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpioe 4 GPIO_ACTIVE_HIGH>;
status = "okay";
port {
dsi_panel_in: endpoint {
remote-endpoint = <&dsi_out>;
};
};
};
};
二、驱动选择
在STM32MP157的源码中,自带了一个文件名为panel-simple.c通用驱动。之前已经在这个文件调通了LCD屏幕,打开文件可以看到,其实这个文件也是可以兼容mipi-dsi的屏幕。可能有些屏幕可以使用这个文件去调试,注意是看看CONFIG_DRM_MIPI_DSI这个选项有无有选择。
static int __init panel_simple_init(void)
{
int err;
err = platform_driver_register(&panel_simple_platform_driver);
if (err < 0)
return err;
if (IS_ENABLED(CONFIG_DRM_MIPI_DSI)) {
err = mipi_dsi_driver_register(&panel_simple_dsi_driver);
if (err < 0)
return err;
}
return 0;
}
module_init(panel_simple_init);
而我接下来调试的屏幕无法使用这个文件。
找一个类似的驱动去进行修改填充
大家可以在driver/gpu/drm/panel/源码目录下找到一个其他屏幕厂商的驱动代码进行修改。
接下来驱动代码修改:
首先是驱动和设备树适配
static const struct of_device_id hyb_of_match[] = {
{
.compatible = "Hyb-Mipi", },
{
/* sentinel */ }
};
MODULE_DEVICE_TABLE(of, hyb_of_match);
static struct mipi_dsi_driver hyb_driver = {
.probe = hyb_dsi_probe,
.remove = hyb_dsi_remove,
.driver = {
.name = "Hyb-Mipi",
.of_match_table = hyb_of_match,
},
};
适配成功后进入hyb_dsi_probe函数,这个函数接口主要是获取一下设备树设置的一些参数,配置一下DSI格式。
static int hyb_dsi_probe(struct mipi_dsi_device *dsi)
{
struct hyb *ctx;
int ret;
ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
mipi_dsi_set_drvdata(dsi, ctx);
ctx->dsi = dsi;
drm_panel_init(&ctx->panel);
ctx->panel.dev = &dsi->dev;
ctx->panel.funcs = &hyb_funcs;
ctx->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(ctx->reset)) {
DRM_DEV_ERROR(&dsi->dev, "Couldn't get our reset GPIO\n");
return PTR_ERR(ctx->reset);
} else {
DRM_DEV_ERROR(&dsi->dev, "Success get our reset GPIO\n");
gpiod_set_value(ctx->reset, 1);
}
ctx->enable = devm_gpiod_get(&dsi->dev, "enable", GPIOD_OUT_HIGH);
if (IS_ERR(ctx->enable)) {
DRM_DEV_ERROR(&dsi->dev, "Couldn't get our enable GPIO\n");
return PTR_ERR(ctx->enable);
} else {
DRM_DEV_ERROR(&dsi->dev, "Success get our enable GPIO\n");
gpiod_set_value(ctx->enable, 1);
}
ret = drm_panel_add(&ctx->panel);
if (ret < 0)
return ret;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
MIPI_DSI_CLOCK_NON_CONTINUOUS|MIPI_DSI_MODE_VIDEO_BURST;
dsi->format = MIPI_DSI_FMT_RGB888;//显示格式
dsi->lanes = 2;//通道
return mipi_dsi_attach(dsi);
}
接下来进入hyb_get_modes()函数,这函数主要是配置屏幕参数
static const struct drm_display_mode hyb_default_mode = {
.clock = 51200,
.hdisplay = 1024,
.hsync_start = 1024 + 60,
.hsync_end = 1024 + 60 + 60,
.htotal = 1024 + 60 + 60 + 90,
.vdisplay = 600,
.vsync_start = 600 + 12,
.vsync_end = 600 + 10 + 5,
.vtotal = 600 + 10 + 5 + 2,
.vrefresh = 60,
.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
};
static int hyb_get_modes(struct drm_panel *panel)
{
struct drm_connector *connector = panel->connector;
struct hyb *ctx = panel_to_hyb(panel);
struct drm_display_mode *mode;
mode = drm_mode_duplicate(panel->drm, &hyb_default_mode);
if (!mode) {
DRM_DEV_ERROR(&ctx->dsi->dev, "failed to add mode %ux%[email protected]%u\n",
hyb_default_mode.hdisplay,
hyb_default_mode.vdisplay,
hyb_default_mode.vrefresh);
return -ENOMEM;
}
drm_mode_set_name(mode);
drm_mode_probed_add(connector, mode);
return 1;
}
接下来就是进入hyb_prepare()函数,MIPI_DSI_MODE_LPM表示默认在 LP 模式下发送初始化序列。这个很重要,因为需要我们发送一个初始化序列到mipi屏上。
static const struct hyb_init_cmd hyb_init_cmds[] = {
{
.data = {
0x80, 0x8B } },
{
.data = {
0x81, 0x78 } },
{
.data = {
0x82, 0x78 } },
{
.data = {
0x83, 0x78 } },
{
.data = {
0x84, 0x78 } },
{
.data = {
0x85, 0x78 } },
{
.data = {
0x86, 0x78 } },
};
static int hyb_prepare(struct drm_panel *panel)
{
struct hyb *ctx = panel_to_hyb(panel);
struct mipi_dsi_device *dsi = ctx->dsi;
unsigned int i;
int ret;
gpiod_set_value(ctx->reset, 1);
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
msleep(200);
/* Select User Command Set table (CMD1) */
ret = mipi_dsi_generic_write(dsi, (u8[]){
0xfe, 0x00 }, 2);
if (ret < 0) {
DRM_DEV_ERROR(dev, "Failed to Set table (%d)\n", ret);
}
/* Software reset */
ret = mipi_dsi_dcs_soft_reset(dsi); /* 0x01 */
if (ret < 0) {
DRM_DEV_ERROR(dev, "Failed to do Software Reset (%d)\n", ret);
//return -1;
}
usleep_range(5000, 10000); /* > 5ms */
//发送初始化序列
for (i = 0; i < ARRAY_SIZE(hyb_init_cmds); i++) {
const struct hyb_init_cmd *cmd =
&hyb_init_cmds[i];
ret = mipi_dsi_dcs_write_buffer(dsi, cmd->data,
HYB_INIT_CMD_LEN);
if (ret < 0) {
return ret;
}
}
ret = mipi_dsi_generic_write(dsi, (u8[]){
0xC2, 0x0B }, 2);
if (ret < 0) {
printk("Failed to set DSI mode\n");
//dev_err(dev, "Failed to set DSI mode (%d)\n", ret);
//goto fail;
}
ret = mipi_dsi_generic_write(dsi, (u8[]){
0xB2, 0x10 }, 2);
if (ret < 0) {
//dev_err(dev, "Failed to set DSI mode (%d)\n", ret);
//goto fail;
}
u8 buffer[2] = {
0xB2, 0x10};
int value = 0;
ret = mipi_dsi_generic_read(dsi, &buffer[0], 1, (void *)&value, sizeof(value));
printk("MIPI_DSI_DEBUG: Reg.%02x Set.%02x Get.%02x Ret.%d Flag.%c\n",
buffer[0], buffer[1], value, ret, ((buffer[1] == value) ? 'T' : 'F'));
/* Set pixel format */
ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77);
//dev_dbg(dev, "Interface color format set to 0x%x\n", color_format);
if (ret < 0) {
printk("Failed to set pixel format\n");
//dev_err(dev, "Failed to set pixel format (%d)\n", ret);
//goto fail;
}
/* Exit sleep mode */
ret = mipi_dsi_dcs_exit_sleep_mode(dsi); /* 0x11 */
if (ret < 0) {
printk("Failed to exit sleep mode\n");
//DRM_DEV_ERROR(&dsi->dev, "Failed to exit sleep mode (%d)\n", ret);
//goto fail;
}
usleep_range(120000, 125000); /* > 120ms */
ret = mipi_dsi_dcs_set_display_on(dsi); /* 0x29 */
if (ret < 0) {
printk("Failed to set display ON\n");
//DRM_DEV_ERROR(&dsi->dev, "Failed to set display ON (%d)\n", ret);
//goto fail;
}
usleep_range(5000, 10000); /* > 5ms */
return 0;
}
由于这个屏幕默认是四个通道,但是STM32MP157最多只有两个通道,通过厂商提供的手册,可以通过寄存器进行通道选择的配置。这时候我们在发完初始化序列后,再发配置通道的指令语句,然后,为了验证发送的指令是否改变了MIPI芯片的寄存器,可以进行读取。这样可以保证万无一失,也可以确定是否发送成功,走少很多弯路。

//写入参数
ret = mipi_dsi_generic_write(dsi, (u8[]){
0xB2, 0x10 }, 2);
if (ret < 0) {
}
u8 buffer[2] = {
0xB2, 0x10};
int value = 0;
//读取对应的寄存器
ret = mipi_dsi_generic_read(dsi, &buffer[0], 1, (void *)&value, sizeof(value));
printk("MIPI_DSI_DEBUG: Reg.%02x Set.%02x Get.%02x\n",
buffer[0], buffer[1], value);
如果顺利的话,基本上开机就能正常启动屏幕了。
调试过程中遇到的坑
第一个问题:在驱动加载中IO口申请失败
我在这个问题停留了有一段时间,用了各种办法都不知道,然后进入系统,手动申请又可以成功。然后把驱动变成模块进去就可以申请成功了,那这个问题就很明显是驱动加载顺序的问题导致的。
问题解决方案:
在内核源码中include/linux/init.h文件定义了加载优先级

我看了一下引脚子系统加载的优先级是arch_initcall
而我们经常使用的module_init()是 device_initcall(fn),驱动对应的加载的优先级为6。所以可以在之后在加载。
static int __init panel_simple_init(void)
{
int err;
if (IS_ENABLED(CONFIG_DRM_MIPI_DSI)) {
err = mipi_dsi_driver_register(&hyb_driver);
if (err < 0)
return err;
}
return 0;
}
module_init(panel_simple_init);
static void __exit panel_simple_exit(void)
{
if (IS_ENABLED(CONFIG_DRM_MIPI_DSI))
mipi_dsi_driver_unregister(&hyb_driver);
}
module_exit(panel_simple_exit);
//module_mipi_dsi_driver(hyb_driver);把这个给屏蔽了,把module_init()添加进去
第二个问题:一直报stm32-display-dsi 5a000000.dsi: Read payload FIFO is empty
[ 3.000353] Hyb-Mipi 5a000000.dsi.0: [drm:hyb_dsi_probe] Success get our reset GPIO
[ 3.007902] Hyb-Mipi 5a000000.dsi.0: [drm:hyb_dsi_probe] Success get our enable GPIO
[ 3.020736] [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
[ 3.025898] [drm] Driver supports precise vblank timestamp query.
[ 3.033213] [drm] Initialized stm 1.0.0 20170330 for 5a001000.display-controller on minor 0
[ 3.388206] stm32-display-dsi 5a000000.dsi: Read payload FIFO is empty
[ 3.409780] stm32-display-dsi 5a000000.dsi: Read payload FIFO is empty
[ 3.431353] stm32-display-dsi 5a000000.dsi: Read payload FIFO is empty
[ 3.431368] MIPI_DSI_DEBUG: Reg.b2 Set.10 Get.00
[ 3.819225] Console: switching to colour frame buffer device 128x37
[ 3.880960] stm32-display 5a001000.display-controller: fb0: stmdrmfb frame buffer device

这个问题是没有在代码中添加dsi->mode_flags |= MIPI_DSI_MODE_LPM;
这个直接关于能否正常发送指令和获取寄存器值。
总结:
在现有的条件下,多去对比类似的驱动编写的模式,从中找到规律,并且结合自身的屏幕问题去修改和补充。
边栏推荐
- GUI pull-down menu of unity3d evil door implementation dropdown design has no duplicate items
- 57. 插入区间
- Collection of common terms and meanings in forestry investigation based on lidar
- Sichuan earth microelectronics ca-is1300 isolated operational amplifier for current detection is on the market
- Misunderstanding of switching triode
- NSIS 静默安装vs2013运行时
- 2265. 统计值等于子树平均值的节点数
- Accès à la boîte aux lettres du nom de domaine Lead à l'étranger
- “空间转换”显著提升陡崖点云的地面点提取质量
- Do you know why the PCB produces tin beads? 2021-09-30
猜你喜欢

Requirements for Power PCB circuit board design 2021-11-09

Estimation of dense forest volume based on LIDAR point cloud with few ground points

Without "rice", you can cook "rice". Strategy for retrieving missing ground points under airborne lidar forest using "point cloud intelligent mapping"

Leetcode daily question - 515 Find the maximum value in each tree row

一“石”二“鸟”,PCA有效改善机载LiDAR林下地面点部分缺失的困局

How to select lead-free and lead-free tin spraying for PCB? 2021-11-16

Sichuan Tuwei ca-is3105w fully integrated DC-DC converter

CPDA|数据分析师成长之路如何起步?

搞清信息化是什么,让企业转型升级走上正确的道路

Sichuan Tuwei ca-if1051 can transceiver has passed aec-q100 grade 1 certification
随机推荐
Atlassian confluence漏洞分析合集
Static bit rate (CBR) and dynamic bit rate (VBR)
Application scheme | application of Sichuan earth microelectronics ca-is398x in PLC field
点云智绘在智慧工地中的应用
realsense d455 semantic_ Slam implements semantic octree mapping
Cglib dynamic proxy
Usememo simulation usecallback
C#中如何调整图像大小
Shell tips (134) simple keyboard input recorder
smartBugs安装小问题总结
Introduction to Sichuan Tuwei ca-is3082w isolated rs-485/rs-422 transceiver
Storage of Galileo broadcast ephemeris in rtklib-b33
Lebel only wants an asterisk in front of it, but doesn't want to verify it
传统的IO存在什么问题?为什么引入零拷贝的?
Home environment monitoring system design (PC version) (mobile app version to be determined)
test
The method of judging whether triode can amplify AC signal
Knowledge sharing 𞓜 conventional laminated structure of six layer PCB
Tips 𞓜 how to clean PCB boards 2021-10-22
IAR compiler flashback