板子:rk3568
UBOOT版本:2017.09
1.前言
网上有很多关于uboot驱动DM的讲解,所以框架的部分就不过多赘述,写这篇博文的目的也是为了更好地了解uboot中的驱动模型,好了直接不如正文吧。
在include/dm/uclass.h头文件里面定义了很多有DM驱动模型相关的函数,要明白这些函数是干嘛用的首先要明白代码里面驱动和设备之间的关系;我个人的理解是驱动就是提供操作设备的方法,而代码里设备是为驱动提供设备相关的参数,所以在开发过程中光有设备参数没有驱动提供的方法就无法操控设备,相反光有设备提供的方法没有设备提供的参数也是无法将设备驱动起来的,所谓巧妇难为无米之炊嘛。所以在include/dm/uclass.h中我们可以看到很多的获取设备的函数:
int uclass_get_device(enum uclass_id id, int index, struct udevice **devp);
int uclass_get_device_by_name(enum uclass_id id, const char *name, struct udevice **devp);
int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp);
int uclass_get_device_by_of_offset(enum uclass_id id, int node, struct udevice **devp);
int uclass_get_device_by_ofnode(enum uclass_id id, ofnode node, struct udevice **devp);
int uclass_get_device_by_phandle_id(enum uclass_id id, uint phandle_id, struct udevice **devp);
int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent, const char *name, struct udevice **devp);
int uclass_get_device_by_driver(enum uclass_id id, const struct driver *drv,struct udevice **devp);
上面这些函数有一个共同点,那就他们都是为了去获取struct udevice *dev;这样一个结构体指针,这里其实是运用了链表的思想的,DM将驱动的操作和信息做成了链表这上面的所有的函数可以通过不同的方式去遍历链表上的节点从而取得所需要的设备驱动,上面的函数都会直接或者间接地去调用uclass_find这个函数:
struct uclass *uclass_find(enum uclass_id key)
{
struct uclass *uc;
if (!gd->dm_root)
return NULL;
/*
* TODO(sjg@chromium.org): Optimise this, perhaps moving the found
* node to the start of the list, or creating a linear array mapping
* id to node.
*/
list_for_each_entry(uc, &gd->uclass_root, sibling_node) {
if (uc->uc_drv->id == key)
return uc;
if (uc->uc_drv->id == UCLASS_ROOT)
break;
}
return NULL;
}
而这个list_for_each_entry就是去寻找链表上所需要的节点,当找到这个节点后相应的probe函数就会被执行,包含在结构体里面的方法也就可以使用了,详细实现过程就不过多地描述了,后面有机会再出这方面的博文。
2.屏幕驱动分析
static int rockchip_display_probe(struct udevice *dev)
{
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
const void *blob = gd->fdt_blob;
int phandle;
struct udevice *crtc_dev, *conn_dev;
struct rockchip_crtc *crtc;
const struct rockchip_connector *conn;
struct rockchip_panel *panel = NULL;
struct rockchip_bridge *bridge = NULL;
struct rockchip_phy *phy = NULL;
struct display_state *s;
const char *name;
int ret;
ofnode node, route_node, timing_node;
struct device_node *port_node, *vop_node, *ep_node, *port_parent_node;
struct public_phy_data *data;
bool is_ports_node = false;
printf("%s--------------------------kli\r\n",__func__);
#if defined(CONFIG_ROCKCHIP_RK3568)
rockchip_display_fixup_dts((void *)blob);
#endif
/* Before relocation we don't need to do anything */
if (!(gd->flags & GD_FLG_RELOC))
return 0;
data = malloc(sizeof(struct public_phy_data));
if (!data) {
printf("failed to alloc phy data\n");
return -ENOMEM;
}
data->phy_init = false;
init_display_buffer(plat->base);
route_node = dev_read_subnode(dev, "route");
if (!ofnode_valid(route_node))
return -ENODEV;
ofnode_for_each_subnode(node, route_node) {
if (!ofnode_is_available(node))
continue;
phandle = ofnode_read_u32_default(node, "connect", -1);
if (phandle < 0) {
printf("Warn: can't find connect node's handle\n");
continue;
}
ep_node = of_find_node_by_phandle(phandle);
if (!ofnode_valid(np_to_ofnode(ep_node))) {
printf("Warn: can't find endpoint node from phandle\n");
continue;
}
port_node = of_get_parent(ep_node);
if (!ofnode_valid(np_to_ofnode(port_node))) {
printf("Warn: can't find port node from phandle\n");
continue;
}
port_parent_node = of_get_parent(port_node);
if (!ofnode_valid(np_to_ofnode(port_parent_node))) {
printf("Warn: can't find port parent node from phandle\n");
continue;
}
is_ports_node = strstr(port_parent_node->full_name, "ports") ? 1 : 0;
if (is_ports_node) {
vop_node = of_get_parent(port_parent_node);
if (!ofnode_valid(np_to_ofnode(vop_node))) {
printf("Warn: can't find crtc node from phandle\n");
continue;
}
} else {
vop_node = port_parent_node;
}
ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_CRTC,
np_to_ofnode(vop_node),
&crtc_dev);
if (ret) {
printf("Warn: can't find crtc driver %d\n", ret);
continue;
}
crtc = (struct rockchip_crtc *)dev_get_driver_data(crtc_dev);
conn_dev = rockchip_of_find_connector(np_to_ofnode(ep_node));
if (!conn_dev) {
printf("Warn: can't find connect driver\n");
continue;
}
conn = (const struct rockchip_connector *)dev_get_driver_data(conn_dev);
phy = rockchip_of_find_phy(conn_dev);
bridge = rockchip_of_find_bridge(conn_dev);
if (bridge)
panel = rockchip_of_find_panel(bridge->dev);
else
panel = rockchip_of_find_panel(conn_dev);
s = malloc(sizeof(*s));
if (!s)
continue;
memset(s, 0, sizeof(*s));
INIT_LIST_HEAD(&s->head);
ret = ofnode_read_string_index(node, "logo,uboot", 0, &name);
if (!ret)
memcpy(s->ulogo_name, name, strlen(name));
ret = ofnode_read_string_index(node, "logo,kernel", 0, &name);
if (!ret)
memcpy(s->klogo_name, name, strlen(name));
ret = ofnode_read_string_index(node, "logo,mode", 0, &name);
if (!strcmp(name, "fullscreen"))
s->logo_mode = ROCKCHIP_DISPLAY_FULLSCREEN;
else
s->logo_mode = ROCKCHIP_DISPLAY_CENTER;
ret = ofnode_read_string_index(node, "charge_logo,mode", 0, &name);
if (!strcmp(name, "fullscreen"))
s->charge_logo_mode = ROCKCHIP_DISPLAY_FULLSCREEN;
else
s->charge_logo_mode = ROCKCHIP_DISPLAY_CENTER;
s->force_output = ofnode_read_bool(node, "force-output");
if (s->force_output) {
timing_node = ofnode_find_subnode(node, "force_timing");
ret = display_get_force_timing_from_dts(timing_node, &s->force_mode);
if (ofnode_read_u32(node, "force-bus-format", &s->force_bus_format))
s->force_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
}
s->blob = blob;
s->panel_state.panel = panel;
s->conn_state.node = conn_dev->node;
s->conn_state.dev = conn_dev;
s->conn_state.connector = conn;
s->conn_state.phy = phy;
s->conn_state.bridge = bridge;
s->conn_state.overscan.left_margin = 100;
s->conn_state.overscan.right_margin = 100;
s->conn_state.overscan.top_margin = 100;
s->conn_state.overscan.bottom_margin = 100;
s->crtc_state.node = np_to_ofnode(vop_node);
s->crtc_state.dev = crtc_dev;
s->crtc_state.crtc = crtc;
s->crtc_state.crtc_id = get_crtc_id(np_to_ofnode(ep_node), is_ports_node);
s->node = node;
if (is_ports_node) { /* only vop2 will get into here */
ofnode vp_node = np_to_ofnode(port_node);
static bool get_plane_mask_from_dts;
s->crtc_state.ports_node = port_parent_node;
if (!get_plane_mask_from_dts) {
ofnode vp_sub_node;
int vp_id = 0;
bool vp_enable = false;
ofnode_for_each_subnode(vp_node, np_to_ofnode(port_parent_node)) {
int cursor_plane = -1;
vp_id = ofnode_read_u32_default(vp_node, "reg", 0);
ret = ofnode_read_u32_default(vp_node, "rockchip,plane-mask", 0);
cursor_plane = ofnode_read_u32_default(vp_node, "cursor-win-id", -1);
s->crtc_state.crtc->vps[vp_id].cursor_plane = cursor_plane;
if (ret) {
int primary_plane = 0;
s->crtc_state.crtc->vps[vp_id].plane_mask = ret;
s->crtc_state.crtc->assign_plane |= true;
primary_plane = ofnode_read_u32_default(vp_node, "rockchip,primary-plane", 0);
printf("get vp%d plane mask:0x%x, primary id:%d, cursor_plane:%d, from dts\n",
vp_id,
s->crtc_state.crtc->vps[vp_id].plane_mask,
primary_plane,
cursor_plane);
}
/* To check current vp status */
vp_enable = false;
ofnode_for_each_subnode(vp_sub_node, vp_node)
vp_enable |= rockchip_get_display_path_status(vp_sub_node);
s->crtc_state.crtc->vps[vp_id].enable = vp_enable;
}
get_plane_mask_from_dts = true;
}
}
if (bridge)
bridge->state = s;
if (panel)
panel->state = s;
get_crtc_mcu_mode(&s->crtc_state);
ret = ofnode_read_u32_default(s->crtc_state.node,
"rockchip,dual-channel-swap", 0);
s->crtc_state.dual_channel_swap = ret;
if (connector_panel_init(s)) {
printf("Warn: Failed to init panel drivers\n");
free(s);
continue;
}
if (connector_phy_init(s, data)) {
printf("Warn: Failed to init phy drivers\n");
free(s);
continue;
}
list_add_tail(&s->head, &rockchip_display_list);
}
if (list_empty(&rockchip_display_list)) {
debug("Failed to found available display route\n");
return -ENODEV;
}
rockchip_get_baseparameter();
display_pre_init();
uc_priv->xsize = DRM_ROCKCHIP_FB_WIDTH;
uc_priv->ysize = DRM_ROCKCHIP_FB_HEIGHT;
uc_priv->bpix = VIDEO_BPP32;
#ifdef CONFIG_DRM_ROCKCHIP_VIDEO_FRAMEBUFFER
rockchip_show_fbbase(plat->base);
video_set_flush_dcache(dev, true);
#endif
return 0;
}
我这里是以rk的为例,不同厂家的可能流程不一样,但我想应该也是大同小异不会差太多。在上面这个函数中我们只重点分析几个调用。
bridge = rockchip_of_find_bridge(conn_dev);
if (bridge)
panel = rockchip_of_find_panel(bridge->dev);
else
panel = rockchip_of_find_panel(conn_dev);
rockchip_of_find_connector:这个函数的作用就是去获取接口的,如lvds,rgb,mipi,hdmi等等
rockchip_of_find_bridge:这个函数中会去寻找UCLASS_VIDEO_BRIDGE,好像是跟桥接相关的,这个懂得不多,有知道的可以私信告诉我哈不管这个返回什么都会去寻找panel。
rockchip_of_find_panel:这个函数会去寻找panel,也就是我们的屏幕描述,调用如下:
ret = uclass_get_device_by_ofnode(UCLASS_PANEL,
panel_node,
&panel_dev);
当找到panel后相应的probe函数就会被执行,所以我添加了打印,结果下面这个函数被调用了:
static int rockchip_panel_probe(struct udevice *dev)
{
struct rockchip_panel_priv *priv = dev_get_priv(dev);
struct rockchip_panel_plat *plat = dev_get_platdata(dev);
struct rockchip_panel *panel;
int ret;
const char *cmd_type;
printf("%s--------------------------kli\r\n",__func__);
ret = gpio_request_by_name(dev, "enable-gpios", 0,
&priv->enable_gpio, GPIOD_IS_OUT);
if (ret && ret != -ENOENT) {
printf("%s: Cannot get enable GPIO: %d\n", __func__, ret);
return ret;
}
ret = gpio_request_by_name(dev, "reset-gpios", 0,
&priv->reset_gpio, GPIOD_IS_OUT);
if (ret && ret != -ENOENT) {
printf("%s: Cannot get reset GPIO: %d\n", __func__, ret);
return ret;
}
ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
"backlight", &priv->backlight);
if (ret && ret != -ENOENT) {
printf("%s: Cannot get backlight: %d\n", __func__, ret);
return ret;
}
ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
"power-supply", &priv->power_supply);
if (ret && ret != -ENOENT) {
printf("%s: Cannot get power supply: %d\n", __func__, ret);
return ret;
}
ret = dev_read_string_index(dev, "rockchip,cmd-type", 0, &cmd_type);
if (ret)
priv->cmd_type = CMD_TYPE_DEFAULT;
else
priv->cmd_type = get_panel_cmd_type(cmd_type);
if (priv->cmd_type == CMD_TYPE_SPI) {
ret = gpio_request_by_name(dev, "spi-sdi-gpios", 0,
&priv->spi_sdi_gpio, GPIOD_IS_OUT);
if (ret && ret != -ENOENT) {
printf("%s: Cannot get spi sdi GPIO: %d\n",
__func__, ret);
return ret;
}
ret = gpio_request_by_name(dev, "spi-scl-gpios", 0,
&priv->spi_scl_gpio, GPIOD_IS_OUT);
if (ret && ret != -ENOENT) {
printf("%s: Cannot get spi scl GPIO: %d\n",
__func__, ret);
return ret;
}
ret = gpio_request_by_name(dev, "spi-cs-gpios", 0,
&priv->spi_cs_gpio, GPIOD_IS_OUT);
if (ret && ret != -ENOENT) {
printf("%s: Cannot get spi cs GPIO: %d\n",
__func__, ret);
return ret;
}
dm_gpio_set_value(&priv->spi_sdi_gpio, 1);
dm_gpio_set_value(&priv->spi_scl_gpio, 1);
dm_gpio_set_value(&priv->spi_cs_gpio, 1);
dm_gpio_set_value(&priv->reset_gpio, 0);
}
panel = calloc(1, sizeof(*panel));
if (!panel)
return -ENOMEM;
dev->driver_data = (ulong)panel;
panel->dev = dev;
panel->bus_format = plat->bus_format;
panel->bpc = plat->bpc;
panel->funcs = &rockchip_panel_funcs;
return 0;
}
而在上面分析的个函数中就有很多我们熟悉的东西了,我们仔细观察就会发现好多东西都是我们在设备树里面配置过的东西,包括uboot背光驱动的获取和相关供电控制驱动的获取如下:
ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
"backlight", &priv->backlight);
if (ret && ret != -ENOENT) {
printf("%s: Cannot get backlight: %d\n", __func__, ret);
return ret;
}
ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
"power-supply", &priv->power_supply);
if (ret && ret != -ENOENT) {
printf("%s: Cannot get power supply: %d\n", __func__, ret);
return ret;
}
函数的最后一条语句panel->funcs = &rockchip_panel_funcs;就与我上一次发的博文衔接上了,这个rockchip_panel_funcs是一个函数操作集内容如下:
static const struct rockchip_panel_funcs rockchip_panel_funcs = {
.init = panel_simple_init,
.prepare = panel_simple_prepare,
.unprepare = panel_simple_unprepare,
.enable = panel_simple_enable,
.disable = panel_simple_disable,
};
其实这其中都是通过调用uclass_get_device_by_phandle获取到相应的驱动的,所以看懂后也就觉得没什么了,但是所谓好记性不如烂笔头嘛,记录下来也是为了哪天忘了来看一看这其中的原理,也让哪些在项目中不知道怎样添加相关参数的博友们能从中受到一点启发,最后附上我添加的打印:
vdd_cpu init 900000 uV
PMIC: RK8090 (on=0x40, off=0x00)
vdd_logic init 900000 uV
vdd_gpu init 900000 uV
vdd_npu init 900000 uV
io-domain: OK
rockchip_display_probe--------------------------kli
rockchip_of_find_panel--------------------------kli
rockchip_panel_probe--------------------------kli