对国产板子有阴影?这些软硬件开源的ARM开发板可以学习Linux驱动开发

为了点亮一块MIPI屏幕,我们除了要了解MIPI DSI的工作原理之外,大前提要了解整个MIPI DSI图显系统的组成,更需要清楚点亮一块MIPI屏幕需要做哪些事情。

///插播一条:我自己在今年年初录制了一套还比较系统的入门单片机教程,想要的同学找我拿就行了免費的,私信我就可以哦~点我头像黑色字体加我地球呺也能领取哦。最近比较闲,带做毕设,带学生参加省级或以上比赛///

正文开始:

1. MIPI DSI图显系统组成

MIPI图显系统的硬件组成如下图表示:

MIPI DSI图显系统组成

图显处理器通过DPI接口将像素数据传输到MIPI DSI Host,MIPI D-PHY作为显示屏和DSI Host之间由物理媒介,将编码后的像素数据发送到MIPI显示屏。

对于MIPI DSI显示屏而言,无需MIPI信号转换的称之为panel,内部有数据信号转换桥片的称之为bridge。

整个MIPI图显系统除了基本的像素数据信号外,为了使整个显示系统能够正常工作,还包含其他与显示屏相关的控制信号,包括显示屏内部IC配置、显示屏背光配置、显示屏的复位和上电配置。

对于点亮一块MIPI屏幕而言,重中之重是要正确的配置显示屏参数,配置方式主要有如下3种:

·I2C或SPI等总线配置

·显示屏内部集成的MCU完成配置

·MIPI DSI DCS初始化序列

通过PWM来实现MIPI屏幕的背光控制,使用GPIO完成显示屏的复位、上电的控制。

在设备树中定义MIPI DSI图显系统的联结关系。

以RK3399为例,其提供了两路MIPI DSI通道,分别是dsi@ff960000和dsi1: dsi@ff968000,代表MIPI DSI host。

2050 dsi: dsi@ff960000 {

2051 compatible = "rockchip,rk3399-mipi-dsi";

2052 reg = ;

...

2083 dsi1: dsi@ff968000 {

2084 compatible = "rockchip,rk3399-mipi-dsi";

2085 reg = ;

...

RK3399芯片使用了Synopsy的DPHY。控制器与DPHY之间的关系如下图所示:

MIPI DSI设备树结点中有一个信息同MIPI显示密切相关,那就是时钟信息。可以看出MIPI DSI需要三路时钟,分别是ref、pclk、phy_cfg。

2050 dsi: dsi@ff960000 {

2051 compatible = "rockchip,rk3399-mipi-dsi";

...

2054 clocks = , ,

2055 ;

2056 clock-names = "ref", "pclk", "phy_cfg";

pclk是MIPI DSI host的APB时钟,用于配置MIPI DSI host寄存器以及中断等。ref和phy_cfg是MIPI DPHY所需时钟。这两路时钟由MIPI DSI host提供。其中ref时钟用于MIPI DPHY内部PLL产生主机侧的串行发送时钟。phy_cfg是在配置MIPI DPHY时使用。

4知识体系搭建

MIPI DSI同图显控制器vop之间在逻辑层面上的联结关系如下:

# MIPI DSI Host

dsi_in_vopb: endpoint@0 {

        reg = ;

        remote-endpoint = ;

};

dsi_in_vopl: endpoint@1 {

        reg = ;

        remote-endpoint = ;

};

# VOP

vopb_out_dsi: endpoint@1 {

        reg = ;

        remote-endpoint = ;

};

vopb_out_dsi1: endpoint@4 {

        reg = ;

        remote-endpoint = ;

};

❝「后文所涉及到的代码部分,均基于DRM架构实现」

2.关键数据结构

请注意以下几个关键的数据结构,后文的MIPI DSI初始化以及MIPI显示系统初始化均以它们为核心进行展开,包括各数据结构的例化工作和创建各数据结构之间的联结关系。

「struct mipi_dsi_host」

struct mipi_dsi_host {

 struct device *dev;

 const struct mipi_dsi_host_ops *ops;

 struct list_head list;

};

该数据结构由DRM MIPI DSI提供,用以描述MIPI DSI Host,包括Host的驱动设备、Host提供的功能函数(后文会介绍具体功能),Host链表的设备注册、管理。

「struct mipi_dsi_device」

struct mipi_dsi_device {

 struct mipi_dsi_host *host;

 struct device dev;

 char name[DSI_DEV_NAME_SIZE];

 unsigned int channel;

 unsigned int lanes;

 enum mipi_dsi_pixel_format format;

 unsigned long mode_flags;

};

该数据结构由DRM MIPI DSI提供,用以描述DSI设备信息,主要包括DSI设备的lane数量(最多4lane)、像素格式(由Host决定,如RGB888、RGB565等)。

「struct mipi_dsi_host_ops」

struct mipi_dsi_host_ops {

 int (*attach)(struct mipi_dsi_host *host,

        struct mipi_dsi_device *dsi);

 int (*detach)(struct mipi_dsi_host *host,

        struct mipi_dsi_device *dsi);

 ssize_t (*transfer)(struct mipi_dsi_host *host,

       const struct mipi_dsi_msg *msg);

};

该数据结构由DRM MIPI DSI提供,用以描述DSI Host所能提供的功能函数,包括DSI Host和显示屏之间创建联结关系需要使用的attach、通过DSI Host配置显示屏的transfer。

3. MIPI DSI软件架构

基于DRM的图显系统中,MIPI DSI子系统主要由DSI CORE与PANEL CORE组成,二者内建连接关系后注册到DRM CORE系统。

MIPI DSI软件架构

在DRM架构下,提供了drm_mipi_dsi.c、drm_panel.c以及drm_bridge.c三个核心文件。用户的MIPI DSI驱动以drm_mipi_dsi.c为核心进行代码编写,例如RK3399的MIPI驱动dw-mipi-dsi.c。各显示屏厂商的驱动代码基于后两者进行编写,例如较为广泛使用的panel-simple.c。

4. MIPI图显系统初始化

从第一章内容来看,点亮MIPI DSI屏幕并能正常的显示图像,在DRM架构下需完成如下初始化工作:

.MIPI DSI Host初始化

.MIPI DSI D-PHY初始化

.MIPI DSI屏幕初始化

.构建MIPI DSI各模块间的联结关系

为了实现以上初始化工作,我们需要在设备树DTS文件中约定各种初始化参数,包括各个模块的功能参数以及各个模块之间的联结关系,在驱动代码中配置各IP及外设硬件,并基于DRM架构注册出各组件,从软件层面勾勒出MIPI DSI的数据流路径。

4.1 MIPI DSI初始化

RK3399使用数据结构struct dw_mipi_dsi描述MIPI DSI设备,该数据结构也是整个MIPI DSI图显系统的核心,囊括了系统中的各个组件。

struct dw_mipi_dsi {

 struct drm_encoder encoder;

 struct drm_connector connector;

 struct drm_bridge *bridge;

 struct device_node *client;

 struct mipi_dsi_host dsi_host;

 struct mipi_dphy dphy;

 struct drm_panel *panel;

 struct device *dev;

 struct regmap *grf;

 struct reset_control *rst;

 struct regmap *regmap;

 struct clk *pclk;

 struct clk *h2p_clk;

 int irq;

 struct dw_mipi_dsi *master;

 struct dw_mipi_dsi *slave;

 struct mutex mutex;

 bool prepared;

 unsigned int id;

 unsigned long mode_flags;

 unsigned int lane_mbps; /* per lane */

 u32 channel;

 u32 lanes;

 u32 format;

 struct drm_display_mode mode;

 const struct dw_mipi_dsi_plat_data *pdata;

};

「DRM架构相关」
基于DRM KMS架构的MIPI DSI图显系统也同样遵循其设定的组件规则,MIPI Host属于DRM encoder,D-PHY和PANEL部分属于DRM connector。在实际使用过程种你可能发现这样一个现象,就是电路板并没有连接MIPI屏幕,但是DRM connector的连接状态依然是connected,这是因为MIPI DSI无法真实的检测到物理连接关系,通过软件定义DRM connector和encoder之间的连接关系,当一切都初始化成功的时候,connector就处于connected状态了。

 struct drm_encoder encoder;

 struct drm_connector connector;

 struct drm_bridge *bridge;

  struct drm_panel *panel;

「MIPI DSI主体」
这里所说的主体主要包括MIPI DSI Host、D-PHY、bridge或pannel。不仅需要表示MIPI DSI图显系统的关键组成模块,也需要定义彼此之间硬件与软件层面的连接关系。

 struct mipi_dsi_host dsi_host;

 struct mipi_dphy dphy;

 struct drm_bridge *bridge;

  struct drm_panel *panel;

「MIPI DSI参数」
我们关心的MIPI DSI参数主要包括物理硬件定义、时钟频率、复位控制等。而物理硬件定义又包括lane数目、通道数、lane速率等。

 unsigned long mode_flags;

 unsigned int lane_mbps; /* per lane */

 u32 channel;

 u32 lanes;

 u32 format;

「显示参数」
MIPI DSI图显系统不像HDMI、VGA那样可以通过EDID获取到显示参数,也同样不支持热插拔操作。其显示参数如分辨率均是在显示屏初始化时注册到drm_display_mode中,当我们将MIPI DSI注册进DRM系统中的时候,直接解析drm_display_mode数据结构获取显示参数。

 struct drm_display_mode mode;

 const struct dw_mipi_dsi_plat_data *pdata;

介绍了MIPI DSI关键数据结构之后,接下来我们看看MIPI DSI的初始化流程是什么样的。

概况来讲,MIPI DSI Host初始化包括两部分内容:初始化MIPI DSI Host/D-PHY功能、构建MIPI DSI与D-PHY、显示屏之间的联结关系。关于Host/D-PHY的配置细节,因为每一家IP的实现不尽相同,故本篇文章不做具体分析。

初始化流程如下图所示:

MIPI DSI初始化流程

「初始化流程解析」

同所有设备驱动一样,在驱动代码的开始处解析设备树,这里只需要解析MIPI DSI结点即可。SoC厂商会实现这部分设备树的定义,我们只需要关心时钟相关的选项即可。

MIPI DSI host和D-PHY之间的初始化存在天生的联系,可以基于通用phy代码架构进行D-PHY初始化,也可以在Host代码进行D-PHY初始化。

在整个的Host初始化流程中,非常关键的一步是注册Host ops,其存在的意义是为panel或bridge提供Host控制的一种机制。例如panel通过.attach联结Host,通过.transfer控制DSI Host发送屏幕初始化序列的DCS包。

基于DRM架构的MIPI DSI图显系统,必须注册encoder和connector两个组件,这属于常规操作,同HDMI、VGA、LVDS等图显系统一样。此处我们需要重点关注encoder的.enable,该函数会完成MIPI DSI Host、D-PHY的功能配置。如下图所示:

Host与D-PHY功能初始化

以上所有Host、D-PHY相关初始化均成功之后,构建Host与panel(bridge)之间的联结关系:

int drm_panel_attach(struct drm_panel *panel, struct drm_connector *connector)

{

 if (panel->connector)

  return -EBUSY;

 panel->connector = connector;

 panel->drm = connector->dev;

 return 0;

}

4.2 PANEL初始化

本文只分析panel的初始化流程,对于bridge的初始化不做过多阐述。从原理上来讲,二者区别不大。在嵌入式领域,使用最多的是panel类型的屏幕。基于DRM架构,有专门的panel驱动,如下所示:

rk@ubuntu:~/OK3399-linux-release/kernel$ ls drivers/gpu/drm/panel/

Makefile panel-samsung-ld9040.c panel-sharp-lq101r1sx01.c panel-simple.o

Kconfig panel-lg-lg4573.c panel-samsung-s6e8aa0.c panel-simple.c

对于大多数的MIPI DSI显示屏,我们都可以基于panel-simple.c编写代码,在其中例化进我们的屏幕配置。当然,LCD、LVDS的panel驱动也在这个文件中,要按需编写。

当我们拿到一块MIPI DSI显示屏之后,首先需要确定其硬件连接关系,查看是否需要通过GPIO控制上下电、确定屏幕参数配置方式、PWM背光调节等。

通过GPIO上下电的控制方式并不多见,若存在这种需求,则需要在设备树中配置pinctrl子系统。

屏幕参数的配置方式比较灵活,在前面已经介绍过。

PWM背光调节功能是MIPI DSI屏幕的必备项,但这部分并不难,我们需要做的是挂载相应的PWM背光驱动即可。

对于panel的初始化,其流程如下图所示:

panel初始化流程

「初始化流程解析」初始化的第一步是解析MIPI DSI屏幕的参数,包括上电参数、背光控制、lane数量、图显时序参数等等。这个是需要我们自己在设备树文件中补充定义的,根据自己手中的屏幕量身定制。

&dsi {

 panel@0{

  status = "okay";

  compatible ="simple-panel-dsi";

  reg = ;

  backlight = ;

  re-delay-ms = ;

  ...

  dsi,flags =

    MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET)>;

  dsi,format = ;

  dsi,lanes = ;

  display-timings {

   native-mode = ;

   timing1: timing1 {

     clock-frequency = ;

...

     pixelclk-active = ;

   };

  };

 };

};

若需要通过MIPI DSI DCS配置屏幕,则还需要定义init-sequence初始化序列,通过DCS配置屏幕时,需要注册出panel prepare函数,当MIPI DSI Host使能时回调到它,然通过MIPI DSI host的.transfer完成DCS数据的发送。例如panel_simple_prepare()函数:

static int panel_simple_prepare(struct drm_panel *panel)

{

 struct panel_simple *p = to_panel_simple(panel);

 int err;

...

 if (p->on_cmds) {

  if (p->dsi)

   err = panel_simple_dsi_send_cmds(p, p->on_cmds);

  else if (p->cmd_type == CMD_TYPE_SPI)

   err = panel_simple_spi_send_cmds(p, p->on_cmds);

  if (err)

   dev_err(p->dev, "failed to send on cmds\n");

 }

...

}

通过panel_simple_dsi_send_cmds()调用DCS发送函数:

static int panel_simple_dsi_send_cmds(struct panel_simple *panel,

          struct panel_cmds *cmds)

{

...

  switch (cmd->dchdr.dtype) {

  case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:

  case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:

  case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:

  case MIPI_DSI_GENERIC_LONG_WRITE:

   err = mipi_dsi_generic_write(dsi, cmd->payload,

           cmd->dchdr.dlen);

   break;

  case MIPI_DSI_DCS_SHORT_WRITE:

  case MIPI_DSI_DCS_SHORT_WRITE_PARAM:

  case MIPI_DSI_DCS_LONG_WRITE:

   err = mipi_dsi_dcs_write_buffer(dsi, cmd->payload,

       cmd->dchdr.dlen);

   break;

  default:

   return -EINVAL;

  }

...

 return 0;

}

最后调用到MIPI DSI Host的ops函数:

static ssize_t mipi_dsi_device_transfer(struct mipi_dsi_device *dsi,

     struct mipi_dsi_msg *msg)

{

 const struct mipi_dsi_host_ops *ops = dsi->host->ops;

 if (!ops || !ops->transfer)

  return -ENOSYS;

 if (dsi->mode_flags & MIPI_DSI_MODE_LPM)

  msg->flags |= MIPI_DSI_MSG_USE_LPM;

 return ops->transfer(dsi->host, msg);

}

当以上具体的配置工作正常结束之后,更新panel-list链表以使得DRM架构下可以找到对应的panel组件。

对国产板子有阴影这些软硬件开源的ARM开发板可以学习Linux驱动相关推荐

  1. 【开源】STC8G2K64S4开发板

    [开源]STC8G2K64S4开发板 2022-7-6 补充说明: STC8G2K64S4所有的 I/O 口均有 4 种工作模式:准双向口/弱上拉(标准 8051 输出口模式).推挽输出/强上拉.高 ...

  2. 开源海思开发板(HIVIEW开发板)

    大家好,今天给大家分享一个有料的海思开发板,可用于快速构建产品样机. HIVIEW简介: HIVIEW是个小而专业的设计公司(海思软/硬件设计能力13年以上)     我们的目标 **以开放的软/硬件 ...

  3. Linux下ffmpeg开源库移植到ARM开发板。

    Linux下移植ffmpeg开源库. Make install 成功后,用户安装目录下出现bin,include,lib,share目录. 进入bin目录执行file ffmpeg 查看编译出的执行程 ...

  4. 低功耗智能硬件开源平台-FireBLE开发板

    FireBLE是一个面向于打造智能生活的开源硬件平台,以BLE(Bluetooth Low Energy)技术为核心.拥有超低的功耗,不俗的处理能力和广泛的应用场合,专注于更智慧.高效率的工作模式,让 ...

  5. 【小月电子】安路国产FPGA开发板系统学习教程-LESSON7串口通信

    串口通信例程讲解 若要观看该博客配套的视频教程,可点击此链接 根据多年工作经验,总结出的FPGA的设计流程,概括起来总共有以上12步,其中根据项目难易度可省去其中一些步骤.比如非常简单的项目,我们可以 ...

  6. 【小月电子】安路国产FPGA开发板系统学习教程-LESSON3 LED流水灯

    LED流水灯例程讲解 若要观看该博客配套的视频教程,可点击此链接 根据多年工作经验,总结出的FPGA的设计流程,概括起来总共有以上12步,其中根据项目难易度可省去其中一些步骤.比如非常简单的项目,我们 ...

  7. 【小月电子】安路国产FPGA开发板系统学习教程-LESSON10无源蜂鸣器驱动

    无源蜂鸣器驱动实验例程讲解 根据多年工作经验,总结出的FPGA的设计流程,概括起来总共有以上12步,其中根据项目难易度可省去其中一些步骤.比如非常简单的项目,我们可以省去虚线框里面的步骤,但是我们的入 ...

  8. 【小月电子】安路国产FPGA开发板系统学习教程-LESSON1点亮LED灯

    点亮LED灯例程讲解 若要观看该博客配套的视频教程,可点击此链接 根据多年工作经验,总结出的FPGA的设计流程,概括起来总共有以上12步,其中根据项目难易度可省去其中一些步骤.比如非常简单的项目,我们 ...

  9. 【小月电子】安路国产FPGA开发板系统学习教程-LESSON2 LED灯闪烁

    LED灯闪烁例程讲解 若要观看该博客配套的视频教程,可点击此链接 根据多年工作经验,总结出的FPGA的设计流程,概括起来总共有以上12步,其中根据项目难易度可省去其中一些步骤.比如非常简单的项目,我们 ...

最新文章

  1. 基于pytorch的模型稀疏训练与模型剪枝示例
  2. python书籍_python书籍
  3. vscode 新建php模板,使用vscode快速建立vue模板
  4. Autofac 之 基于 Castle DynamicProxy2 的 Interceptor 功能
  5. gitlab的用户使用手册
  6. 2020年中国消费市场发展报告
  7. c++ 动态数组初始值不为0?
  8. python基础课程第12章_流畅的python学习笔记-第12章
  9. DBSCAN聚类算法初探(五)
  10. 0基础小白都说简单!10秒做好酷炫图表,可视化工具帮了大忙
  11. 我国研发5款自主产权3D显卡;哈啰单车回应异常;Jboot 2.2.4发布 | 极客头条
  12. MAGENTO DEBUG
  13. java 创建类带泛型_java-创建泛型类列表
  14. 保护自己之手机定位信息收集
  15. caffe的caffe.proto
  16. 迅歌点歌系统服务器过期或不信任怎么办,点歌机常见问题解析
  17. word文档密码破解
  18. 210 Ruby 方法、类和模块【Rails后端开发训练营】
  19. Java设计模式——模版方法模式
  20. C语言学习笔记——typedef

热门文章

  1. lua版本base64加密和解密
  2. 题目0074-时间格式化
  3. 精简jre(JDK6瘦身)
  4. 初中时特别喜欢的电视剧叫《力克千年虫》不知道大家看过没,分享一下
  5. python 字符的编码
  6. hsqldb mysql 语法_[spring batch]建表语句(hsqldb改mysql)
  7. java毕业设计二次元信息分享平台mybatis+源码+调试部署+系统+数据库+lw
  8. 100天以前是几月几号
  9. 考研高等数学知识框架
  10. 读文献“大数据可视分析综述”