linux基于DRM显示框架分析

图片来自https://blog.csdn.net/u014674293/article/details/105732627?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2aggregatepagefirst_rank_v2~rank_aggregation-14-105732627.pc_agg_rank_aggregation&utm_term=drm%E6%98%BE%E7%A4%BA%E6%9E%B6%E6%9E%84&spm=1000.2123.3001.4430
jit图片来自https://www.eefocus.com/embedded/479281

display_subsystem: display-subsystem {
    compatible = "rockchip,display-subsystem";
    ports = <&vopl_out>, <&vopb_out>;
    clocks = <&cru PLL_VPLL>, <&cru PLL_CPLL>;
    clock-names = "hdmi-tmds-pll", "default-vop-pll";
    devfreq = <&dmc>;
    status = "disabled";
};

H:\RK3399\kernel\drivers\gpu\drm\rockchip\rockchip_drm_drv.c

vopl,vopb相当于是crtc,dp_in_vopl,dsi_in_vopl,edp_in_vopl,dp_in_vopl相当于是connector
static int rockchip_drm_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct component_match *match = NULL;
struct device_node *np = dev->of_node;
struct device_node *port;
int i;

DRM_INFO("Rockchip DRM driver version: %s\n", DRIVER_VERSION);
if (!np)
    return -ENODEV;
/*
 * Bind the crtc ports first, so that
 * drm_of_find_possible_crtcs called from encoder .bind callbacks
 * works as expected.
 */
for (i = 0;; i++) {
    struct device_node *iommu;

    port = of_parse_phandle(np, "ports", i); //查找prots属性,找到它的 device_node节点,比如vopl_out节点
    if (!port)
        break;

    if (!of_device_is_available(port->parent)) {  //看vopl_out节点的父节点vopl是否可以使用 status 是否 "okay"或“ok”;
        of_node_put(port);
        continue;
    }

    iommu = of_parse_phandle(port->parent, "iommus", 0);  //找到其中的iommus的device_node节点
    if (!iommu || !of_device_is_available(iommu->parent)) {
        dev_dbg(dev, "no iommu attached for %s, using non-iommu buffers\n",
            port->parent->full_name);
        /*
         * if there is a crtc not support iommu, force set all
         * crtc use non-iommu buffer.
         */
        is_support_iommu = false;    //不存在或者父节点不可取就置为false
    }

    component_match_add(dev, &match, compare_of, port->parent); 在match中添加一个compare数组成员比如vopl,vopb,还会再将每个属性值关联的设备添加到match中
    of_node_put(port);
}

if (i == 0) {
    dev_err(dev, "missing 'ports' property\n");
    return -ENODEV;
}

if (!match) {
    dev_err(dev, "No available vop found for display-subsystem.\n");
    return -ENODEV;
}
/*
 * For each bound crtc, bind the encoders attached to its
 * remote endpoint.
 */
for (i = 0;; i++) {
    port = of_parse_phandle(np, "ports", i);
    if (!port)
        break;

    if (!of_device_is_available(port->parent)) {
        of_node_put(port);
        continue;
    }

    rockchip_add_endpoints(dev, &match, port);  //在match中添加一个remote-endpoint数组成员比如dp_in_vopl,dsi_in_vopl,edp_in_vopl,dp_in_vopl等
    of_node_put(port);
}

port = of_parse_phandle(np, "backlight", 0);
if (port && of_device_is_available(port)) {
    component_match_add(dev, &match, compare_of, port);
    of_node_put(port);
}

return component_master_add_with_match(dev, &rockchip_drm_ops, match); // 首先定义了一个component_master_ops 对象,为设备相关操作函数回调,当该master下的所有设备都初始化完成后,调用该回调的bind指针

}
static const struct component_master_ops rockchip_drm_ops = {
.bind = rockchip_drm_bind,
.unbind = rockchip_drm_unbind,
};

int component_master_add_with_match(struct device *dev,
const struct component_master_ops *ops,
struct component_match *match)
{
struct master *master;
int ret;

if (ops->add_components && match)
    return -EINVAL;

if (match) {
    /* Reallocate the match array for its true size */
    match = component_match_realloc(dev, match, match->num);
    if (IS_ERR(match))
        return PTR_ERR(match);
}

master = kzalloc(sizeof(*master), GFP_KERNEL);
if (!master)
    return -ENOMEM;

master->dev = dev;
master->ops = ops;   //把rockchip_drm_ops给了master->ops 为后面调用ops的bind做准备
master->match = match;
INIT_LIST_HEAD(&master->components);

/* Add to the list of available masters. */
mutex_lock(&component_mutex);
list_add(&master->node, &masters);

ret = try_to_bring_up_master(master, NULL); 这个函 数就是检查是否所有的componet都加载完成,完成了就调用rockchip_drm_bind函数,后面分析rockchip_drm_bind 

if (ret < 0) {
    /* Delete off the list if we weren't successful */
    list_del(&master->node);
    kfree(master);
}
mutex_unlock(&component_mutex);

return ret < 0 ? ret : 0;

}

static int try_to_bring_up_master(struct master *master,
struct component *component)
{
int ret;

if (master->bound)
    return 0;

/*
 * Search the list of components, looking for components that
 * belong to this master, and attach them to the master.
 */
if (find_components(master)) {    //从component_list链表中找到所有属于本master的components,如果有component没有添加完成,就返回退出,不会调用下面的bind,如果全部找到就往下走调用bind,下面进入函数分析
        /* Failed to find all components */
        ret = 0;
        goto out;
    }

    if (component && component->master != master) {
        ret = 0;
        goto out;
    }

    if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) {
        ret = -ENOMEM;
        goto out;
    }

    /* Found all components */
    ret = master->ops->bind(master->dev);//调用之前传来的rockchip_drm_ops->bind,这里  .bind = rockchip_drm_bind,
    if (ret < 0) {
        devres_release_group(master->dev, NULL);
        dev_info(master->dev, "master bind failed: %d\n", ret);
        goto out;
    }

    master->bound = true;
    return 1;

out:
    master_remove_components(master);

    return ret;
}
static int find_components(struct master *master)
{
    struct component_match *match = master->match;
    size_t i;
    int ret = 0;

    if (!match) {
        /*
         * Search the list of components, looking for components that
         * belong to this master, and attach them to the master.
         */
        return master->ops->add_components(master->dev, master);
    }

    /*
     * Scan the array of match functions and attach
     * any components which are found to this master.
     */
    for (i = 0; i < match->num; i++) { //本match已经添加的crtc跟连接器connector
        ret = component_master_add_child(master,   下面分析
                         match->compare[i].fn,
                         match->compare[i].data);
        if (ret)
            break;
    }
    return ret;
}
int component_master_add_child(struct master *master,
    int (*compare)(struct device *, void *), void *compare_data)
{
    struct component *c;
    int ret = -ENXIO;

    list_for_each_entry(c, &component_list, node) {  查找component_list链表,找到属于本master的component
        if (c->master && c->master != master)
            continue;

        if (compare(c->dev, compare_data)) {  //比较这个设备的节点是不是这个器件,通过component_match_add绑定好的,下面分析
            if (!c->master)
                component_attach_master(master, c); 把这个component添加到master的component链表
            ret = 0;     找到就返回0
            break;
        }
    }

    return ret;  没找到返回错误
}

static int compare_of(struct device *dev, void *data)
{
struct device_node *np = data;

return dev->of_node == np;   判断这个节点跟传入的是不是一个

}
主drm驱动的加载过程结束

rockchip_drm_bind H:\RK3399\kernel\drivers\gpu\drm\rockchip\rockchip_drm_drv.c
component_bind_all

int component_bind_all(struct device *master_dev, void *data)
{
    struct master *master;
    struct component *c;
    int ret = 0;

    WARN_ON(!mutex_is_locked(&component_mutex));

    master = __master_find(master_dev, NULL);
    if (!master)
        return -EINVAL;

    list_for_each_entry(c, &master->components, master_node) {
        ret = component_bind(c, master, data);   //调用各components的bind函数
        if (ret)
            break;
    }

    if (ret != 0) {
        list_for_each_entry_continue_reverse(c, &master->components,
                             master_node)
            component_unbind(c, master, data);
    }

    return ret;
}

2.component从器件mipi的添加过程分析

dw_mipi_dsi_probe H:\RK3399\kernel\drivers\gpu\drm\rockchip\dw-mipi-dsi.c
component_add(dev, &dw_mipi_dsi_ops); 下面分析
static const struct component_ops dw_mipi_dsi_ops = {
.bind = dw_mipi_dsi_bind, 这个方法在rockchip_drm_bind中会被调用到
.unbind = dw_mipi_dsi_unbind,
};

int component_add(struct device *dev, const struct component_ops *ops) H:\RK3399\kernel\drivers\base\component.c
{
    struct component *component;
    int ret;

    component = kzalloc(sizeof(*component), GFP_KERNEL);
    if (!component)
        return -ENOMEM;

    component->ops = ops;
    component->dev = dev;

    dev_dbg(dev, "adding component (ops %ps)\n", ops);

    mutex_lock(&component_mutex);
    list_add_tail(&component->node, &component_list);

    ret = try_to_bring_up_masters(component);  查找masters链表中所有的master,下面分析
    if (ret < 0) {
        list_del(&component->node);

        kfree(component);
    }
    mutex_unlock(&component_mutex);

    return ret < 0 ? ret : 0;
}
static int try_to_bring_up_masters(struct component *component)
{
    struct master *m;
    int ret = 0;

    list_for_each_entry(m, &masters, node) {   查找masters链表中所有的master
        ret = try_to_bring_up_master(m, component); 这 个函数上面master的时候分析过,从component引发到master检查它所有的component是否加载完成,完成之后调用master上面的bind函数,没完成则退出,master上的bind函数又会调用componet上的bind函数
        if (ret != 0)
            break;
    }

    return ret;
}

If a required resource is not available yet, a driver can

  • request probing to be deferred by returning -EPROBE_DEFER from its probe hook
    如果一个驱动在加载的过程中没有得到想要的资源,比如dsi0驱动没得到panel的话,就会返回一个-EPROBE_DEFER错误,而一旦返回这个错误的话,这个驱动就会被添加到延时probe线程进而再次进行probe,执行这个probe的函数就是deferred_probe_work_func,
    顺序如下:
    worker_thread->
    process_one_work->
    deferred_probe_work_func->
    bus_probe_device->
    __device_attach->
    bus_for_each_drv->
    bus_for_each_drv->
    driver_probe_device->
    platform_drv_probe->
    rockchip_drm_platform_probe->
    component_master_add_with_match->
    try_to_bring_up_master->
    rockchip_drm_bind-> display_subsystem驱动的bind函数
    component_bind_all -> 调用各个compnet的bind函数
    dw_mipi_dsi_bind mipi的bind函数

static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
void *data)
{
struct drm_device *drm = data;
struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
int ret;

ret = dw_mipi_dsi_dual_channel_probe(dsi);
if (ret)
    return ret;

if (dsi->master)
    return 0;

dsi->panel = of_drm_find_panel(dsi->client);  //获取绑定到此dsi的panel设备,没有绑定好则检查是否绑定有bridge
if (!dsi->panel) {
    dsi->bridge = of_drm_find_bridge(dsi->client);  没有绑定bridge则退出返回-EPROBE_DEFER,一旦返回就把

本驱动又加入了延时probe线程,等会会再次进行probe,直到发现了panel,而panel的加载在H:\RK3399\kernel\drivers\gpu\drm\panel\panel-simple.c完成,这里同时注册了mipi跟lvds的驱动在一起,通过comptable判断区分

        if (!dsi->bridge)
            return -EPROBE_DEFER;
    }

    ret = dw_mipi_dsi_register(drm, dsi);  绑定编码器,连接器,panel
    if (ret) {
        dev_err(dev, "Failed to register mipi_dsi: %d\n", ret);
        return ret;
    }

    dev_set_drvdata(dev, dsi);

    pm_runtime_enable(dev);
    if (dsi->slave)
        pm_runtime_enable(dsi->slave->dev);

    return ret;
}
static int dw_mipi_dsi_register(struct drm_device *drm,
                      struct dw_mipi_dsi *dsi)
{
    struct drm_encoder *encoder = &dsi->encoder;
    struct drm_connector *connector = &dsi->connector;
    struct device *dev = dsi->dev;
    int ret;

    encoder->possible_crtcs = drm_of_find_possible_crtcs(drm,
                                 dev->of_node);
    /*
     * If we failed to find the CRTC(s) which this encoder is
     * supposed to be connected to, it's because the CRTC has
     * not been registered yet.  Defer probing, and hope that
     * the required CRTC is added later.
     */
    if (encoder->possible_crtcs == 0)
        return -EPROBE_DEFER;

    drm_encoder_helper_add(&dsi->encoder,
                   &dw_mipi_dsi_encoder_helper_funcs);
    ret = drm_encoder_init(drm, &dsi->encoder, &dw_mipi_dsi_encoder_funcs,   //初始化encoder,格式为DRM_MODE_ENCODER_DSI,功能函数为dw_mipi_dsi_encoder_funcs
             DRM_MODE_ENCODER_DSI, NULL);
    if (ret) {
        dev_err(dev, "Failed to initialize encoder with drm\n");
        return ret;
    }

    /* If there's a bridge, attach to it and let it create the connector. */
    if (dsi->bridge) {
        dsi->bridge->driver_private = &dsi->dsi_host;
        dsi->bridge->encoder = encoder;

        ret = drm_bridge_attach(drm, dsi->bridge);
        if (ret) {
            dev_err(dev, "Failed to attach bridge: %d\n", ret);
            goto encoder_cleanup;
        }

        encoder->bridge = dsi->bridge;
    /* Otherwise create our own connector and attach to a panel */
    } else {
        dsi->connector.port = dev->of_node;
        ret = drm_connector_init(drm, &dsi->connector,
                     &dw_mipi_dsi_atomic_connector_funcs,
                     DRM_MODE_CONNECTOR_DSI);
        if (ret) {
            dev_err(dev, "Failed to initialize connector\n");
            goto encoder_cleanup;
        }
        drm_connector_helper_add(connector,
                     &dw_mipi_dsi_connector_helper_funcs);
        drm_mode_connector_attach_encoder(connector, encoder); 把编码器绑定到连接器

        ret = drm_panel_attach(dsi->panel, &dsi->connector);  把panel绑定到连接器
        if (ret) {
            dev_err(dev, "Failed to attach panel: %d\n", ret);
            goto connector_cleanup;
        }
    }

    return 0;

connector_cleanup:
    drm_connector_cleanup(connector);
encoder_cleanup:
    drm_encoder_cleanup(encoder);
    return ret;
}
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
LinuxDRM(Direct Rendering Manager)框架是一个用于图形硬件驱动程序的子系统。它提供了一个抽象层,使得用户空间程序(比如窗口系统和渲染引擎)可以方便地访问图形硬件。下面对LinuxDRM框架进行详解。 首先,DRM框架通过提供一组标准的API,将用户空间程序与图形硬件之间进行了解耦。这些API包括了设备初始化、命令交互、内存管理、DMA、中断处理等功能。用户空间程序可以通过这些API与图形驱动程序进行交互,从而实现图形渲染等功能。 其次,DRM框架引入了一种叫做KMS(Kernel Mode Setting)的机制,使得图形模式设置可以在内核空间中进行。通过KMS,用户空间程序可以请求内核来设置显示模式,包括分辨率、刷新率等。这样做的好处是可以避免用户空间程序对驱动程序的直接控制,提高了系统的稳定性和安全性。 另外,DRM框架也支持对硬件加速的操作。它定义了一组称为DRM核心的代码,用于管理硬件资源和提供硬件加速功能。通过这些代码,用户空间程序可以利用图形硬件进行2D、3D渲染、视频解码等加速操作。 此外,DRM框架还具备了多种设备驱动的支持。它提供了开发者需要开发各种不同硬件的驱动程序的接口。因此,开发者可以很容易地为自己的硬件编写驱动程序,并将其集成到DRM框架中。这种模块化的设计方式使得DRM框架在适应不同硬件平台上具有很高的灵活性。 综上所述,LinuxDRM框架是一个用于图形硬件驱动程序的子系统,它提供了一组标准的API、KMS机制、硬件加速支持和多种设备驱动的接口。通过这些功能,用户空间程序可以方便地访问图形硬件,并实现图形渲染等功能。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术求索者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值