Rockchip DRM主驱动流程梳理

DRM主驱动流程梳理

kernel 配置与编译

留在草稿箱很久了,就直接发了。
过去的事,很可惜。

make rockchip_defconfig
make rk3399-sapphire-excavator-edp.img

DTS

arch/arm64/boot/dts/rockchip/rk3399-sapphire-excavator-edp.dts

#include "rk3399-android.dtsi"

&edp {
    status = "okay";
    force-hpd;

    ports {
        port@1 {
            reg = <1>;

            edp_out: endpoint {
                remote-endpoint = <&panel_in>;
            };
        };
    };
};

arch/arm64/boot/dts/rockchip/rk3399-android.dtsi

&display_subsystem {
    status = "okay";

    ports = <&vopb_out>, <&vopl_out>;
    logo-memory-region = <&drm_logo>;
    secure-memory-region = <&secure_memory>;
    route {
        route_hdmi: route-hdmi {
            status = "disabled";
            logo,uboot = "logo.bmp";
            logo,kernel = "logo_kernel.bmp";
            logo,mode = "center";
            charge_logo,mode = "center";
            connect = <&vopb_out_hdmi>;
        };  

        route_dsi: route-dsi {
            status = "disabled";
            logo,uboot = "logo.bmp";
            logo,kernel = "logo_kernel.bmp";
            logo,mode = "center";
            charge_logo,mode = "center";
            connect = <&vopb_out_dsi>;
        };  

        route_dsi1: route-dsi1 {
            status = "disabled";
            logo,uboot = "logo.bmp";
            logo,kernel = "logo_kernel.bmp";
            logo,mode = "center";
            charge_logo,mode = "center";
            connect = <&vopl_out_dsi1>;
        };  

        route_edp: route-edp {
            status = "disabled";
            logo,uboot = "logo.bmp";
            logo,kernel = "logo_kernel.bmp";
            logo,mode = "center";
            charge_logo,mode = "center";
            connect = <&vopb_out_edp>;
        };  
    };  
};


arch/arm64/boot/dts/rockchip/rk3399.dtsi

    display_subsystem: display-subsystem {
        compatible = "rockchip,display-subsystem";
        /* 主驱动查找的ports 
         * 指向两个VOP的port
         */
        ports = <&vopl_out>, <&vopb_out>;
        clocks = <&cru PLL_VPLL>, <&cru PLL_CPLL>;
        clock-names = "hdmi-tmds-pll", "default-vop-pll";
        devfreq = <&dmc>;
        status = "disabled";
    };

    vopl: vop@ff8f0000 {
        compatible = "rockchip,rk3399-vop-lit";
        reg = <0x0 0xff8f0000 0x0 0x600>,
            <0x0 0xff8f1c00 0x0 0x200>,
            <0x0 0xff8f2000 0x0 0x400>;
        reg-names = "regs", "cabc_lut", "gamma_lut";
        interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH 0>;
        clocks = <&cru ACLK_VOP1>, <&cru DCLK_VOP1>, <&cru HCLK_VOP1>, <&cru DCLK_VOP1_DIV>;
        clock-names = "aclk_vop", "dclk_vop", "hclk_vop", "dclk_source";
        iommus = <&vopl_mmu>;
        power-domains = <&power RK3399_PD_VOPL>;
        resets = <&cru SRST_A_VOP1>, <&cru SRST_H_VOP1>, <&cru SRST_D_VOP1>;
        reset-names = "axi", "ahb", "dclk";
        status = "disabled";

        /* 在displsy_subsystemprots 中被引用*/
        vopl_out: port {
            #address-cells = <1>;
            #size-cells = <0>;

            vopl_out_dsi: endpoint@0 {
                reg = <0>;
                remote-endpoint = <&dsi_in_vopl>;
            };

            vopl_out_edp: endpoint@1 {
                reg = <1>;
                remote-endpoint = <&edp_in_vopl>;
            };

            vopl_out_hdmi: endpoint@2 {
                reg = <2>;
                remote-endpoint = <&hdmi_in_vopl>;
            };

            vopl_out_dp: endpoint@3 {
                reg = <3>;
                remote-endpoint = <&dp_in_vopl>;
            };

            vopl_out_dsi1: endpoint@4 {
                reg = <4>;
                remote-endpoint = <&dsi1_in_vopl>;
            };
        };
    };

主驱动入口

rockchip_drm_drv.c kernel\drivers\gpu\drm\rockchip

// 注册平台驱动 rockchip_drm_platform_driver
module_platform_driver(rockchip_drm_platform_driver);

static struct platform_driver rockchip_drm_platform_driver = {
    .probe = rockchip_drm_platform_probe,
    ...
    .driver = {
        .name = "rockchip-drm",
        .of_match_table = rockchip_drm_dt_ids,
        .pm = &rockchip_drm_pm_ops,
    },
};
// DTS 相关
static const struct of_device_id rockchip_drm_dt_ids[] = {
    { .compatible = "rockchip,display-subsystem", },
    { /* sentinel */ },
};

-> probe

probe做了一下工作:
1. 遍历所有VOP设备并添加到match列表;
2. 遍历所有display设备并添加到match列表;
3. 将backlight设备添加到match列表;
4. 通过match列表尝试bind所有设备,启动主驱动。

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.
     */
     /* 得到ports这个属性所包含的所有phandle的父类device node 
      * 也就是VOP对应抽象对象就是CRTC 
      */
    for (i = 0;; i++) {
        struct device_node *iommu;
        /* 在DTS中
         * ports = <&vopl_out>, <&vopb_out>; vopl-> visual Output Processor little
         */
        port = of_parse_phandle(np, "ports", i);
        if (!port)
            break;
        /* prot->parent 
         * 既是 vop@ff8f0000,代号:vopl
         * vopl: vop@ff8f0000 {
         *         ...
         *         vopl_out: port {
         */
        if (!of_device_is_available(port->parent)) {
            of_node_put(port);
            continue;
        }
        /* 检查vop是否支持iommu */
        iommu = of_parse_phandle(port->parent, "iommus", 0);
        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;
        }
        /* 将该crtc(VOP)添加到match列表 */
        component_match_add(dev, &match, compare_of, port->parent);
        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;
        }
        /* 记录每个port的endpoint
         * 即每个VOP下连接的显示器connect(edp、mipi、hdmi等)
         */
        rockchip_add_endpoints(dev, &match, port);
        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);
    }
    /* 将match列表添加到master中 */
    return component_master_add_with_match(dev, &rockchip_drm_ops, match);
}

其中的rockchip_drm_ops在所有components被found后会调用bind来绑定所有devices.

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

调用过程:

component_master_add_with_match
    |-- try_to_bring_up_master
        |-- find_components
            | -- master->ops->bind

每个component在被add的时候都会去重新调用try_to_bring_up_master()去判断是否所有component全部被match上,当且仅当所有的component都ready之后,master才会被bring up。因此在开机log中刚开始看到“failed to bind xxx”的信息是正常的。

find_components()就是用来寻找并判断所有match列表中的components是不是都被add即初始化了,如果没有ready,那么就不会再去走后面bring up master的流程。

-> component_match_add

添加一个component到matched列表

/*
 * Add a component to be matched.
 *
 * The match array is first created or extended if necessary.
 */
void component_match_add(struct device *dev, struct component_match **matchptr,
    int (*compare)(struct device *, void *), void *compare_data)
{
    struct component_match *match = *matchptr;

    if (IS_ERR(match))
        return;
    /* match数组还未创建 
     * 或者还未申请内存
     */
    if (!match || match->num == match->alloc) {
        size_t new_size = match ? match->alloc + 16 : 15;
        match = component_match_realloc(dev, match, new_size);
        *matchptr = match;
        if (IS_ERR(match))
            return;
    }
    /* 记录该component的compare函数和参数 */
    match->compare[match->num].fn = compare;
    match->compare[match->num].data = compare_data;
    match->num++;
}

-> component_master_add_with_match 未完

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;
    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);

    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;
}

-> rockchip_drm_ops

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



static int rockchip_drm_bind(struct device *dev)
{
    struct drm_device *drm_dev;
    struct rockchip_drm_private *private;
    int ret;
    struct device_node *np = dev->of_node;
    struct device_node *parent_np;
    struct drm_crtc *crtc;
    /* 申请一个DRM设备数据结构,对应驱动为rockchip_drm_driver */
    drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev);
    if (!drm_dev)
        return -ENOMEM;
    /* 设置drm设备的名字 */
    ret = drm_dev_set_unique(drm_dev, "%s", dev_name(dev));
    if (ret)
        goto err_free;

    dev_set_drvdata(dev, drm_dev);

    private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
    if (!private) {
        ret = -ENOMEM;
        goto err_free;
    }
    /* 初始化工作队列 */
    mutex_init(&private->commit_lock);
    INIT_WORK(&private->commit_work, rockchip_drm_atomic_work);

    drm_dev->dev_private = private;
    /* 动态内存控制支持检查 */
    private->dmc_support = false;
    private->devfreq = devfreq_get_devfreq_by_phandle(dev, 0);
    if (IS_ERR(private->devfreq)) {
        if (PTR_ERR(private->devfreq) == -EPROBE_DEFER) {
            parent_np = of_parse_phandle(np, "devfreq", 0);
            if (parent_np &&
                of_device_is_available(parent_np)) {
                private->dmc_support = true;
                dev_warn(dev, "defer getting devfreq\n");
            } else {
                dev_info(dev, "dmc is disabled\n");
            }
        } else {
            dev_info(dev, "devfreq is not set\n");
        }
        private->devfreq = NULL;
    } else {
        private->dmc_support = true;
        dev_info(dev, "devfreq is ready\n");
    }
    /* hdmi-tmds-pll 始终获取 */
    private->hdmi_pll.pll = devm_clk_get(dev, "hdmi-tmds-pll");
    if (PTR_ERR(private->hdmi_pll.pll) == -ENOENT) {
        private->hdmi_pll.pll = NULL;
    } else if (PTR_ERR(private->hdmi_pll.pll) == -EPROBE_DEFER) {
        ret = -EPROBE_DEFER;
        goto err_free;
    } else if (IS_ERR(private->hdmi_pll.pll)) {
        dev_err(dev, "failed to get hdmi-tmds-pll\n");
        ret = PTR_ERR(private->hdmi_pll.pll);
        goto err_free;
    }
    /* default-vop-pll始终获取 */
    private->default_pll.pll = devm_clk_get(dev, "default-vop-pll");
    if (PTR_ERR(private->default_pll.pll) == -ENOENT) {
        private->default_pll.pll = NULL;
    } else if (PTR_ERR(private->default_pll.pll) == -EPROBE_DEFER) {
        ret = -EPROBE_DEFER;
        goto err_free;
    } else if (IS_ERR(private->default_pll.pll)) {
        dev_err(dev, "failed to get default vop pll\n");
        ret = PTR_ERR(private->default_pll.pll);
        goto err_free;
    }

#ifdef CONFIG_DRM_DMA_SYNC
    private->cpu_fence_context = fence_context_alloc(1);
    atomic_set(&private->cpu_fence_seqno, 0);
#endif
    /* iommu 初始化 */
    ret = rockchip_drm_init_iommu(drm_dev);
    if (ret)
        goto err_free;
    /* mode_config结构体初始化 */
    drm_mode_config_init(drm_dev);
    /* 宽、高限制*/
    rockchip_drm_mode_config_init(drm_dev);
    /* 创建相关属性 */
    rockchip_drm_create_properties(drm_dev);
    /* 调用所有子设备的bind函数 */
    /* Try to bind all sub drivers. */
    ret = component_bind_all(dev, drm_dev);
    if (ret)
        goto err_mode_config_cleanup;
    /* 为每个connect某些属性复制(brightness、contrast、saturation、hue)基本值(100) */
    rockchip_attach_connector_property(drm_dev);
    /* vblank 初始化 */
    ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
    if (ret)
        goto err_unbind_all;

    drm_mode_config_reset(drm_dev);

    rockchip_drm_set_property_default(drm_dev);

    /*
     * enable drm irq mode.
     * - with irq_enabled = true, we can use the vblank feature.
     */
    drm_dev->irq_enabled = true;

    /* init kms poll for handling hpd */
    drm_kms_helper_poll_init(drm_dev);

    /*
     * with vblank_disable_allowed = true, vblank interrupt will be disabled
     * by drm timer once a current process gives up ownership of
     * vblank event.(after drm_vblank_put function is called)
     */
    drm_dev->vblank_disable_allowed = true;
    
    rockchip_gem_pool_init(drm_dev);
#ifndef MODULE
    /* 加载保留的logo内存空间内容
     * 显示logo?
     */
    show_loader_logo(drm_dev);
#endif
    /* 读取reserved region 资源新给device */
    ret = of_reserved_mem_device_init(drm_dev->dev);
    if (ret)
        DRM_DEBUG_KMS("No reserved memory region assign to drm\n");
    /* fbdev 相关操作的初始化 */
    ret = rockchip_drm_fbdev_init(drm_dev);
    if (ret)
        goto err_kms_helper_poll_fini;
    /* 检查每个crtc
     * 如果支持热插拔,那么增加引用计数
     */
    drm_for_each_crtc(crtc, drm_dev) {
        struct drm_fb_helper *helper = private->fbdev_helper;
        struct rockchip_crtc_state *s = NULL;

        if (!helper)
            break;

        s = to_rockchip_crtc_state(crtc->state);
        if (is_support_hotplug(s->output_type)) {
            s->crtc_primary_fb = crtc->primary->fb;
            crtc->primary->fb = helper->fb;
            drm_framebuffer_reference(helper->fb);
        }
    }
    drm_dev->mode_config.allow_fb_modifiers = true;
    /* 注册DRM devices 设备 
     * 调用个组件的注册函数
     * plane、ctrc、encoder*/
    ret = drm_dev_register(drm_dev, 0);
    if (ret)
        goto err_fbdev_fini;

    return 0;
err_fbdev_fini:
    rockchip_drm_fbdev_fini(drm_dev);
err_kms_helper_poll_fini:
    rockchip_gem_pool_destroy(drm_dev);
    drm_kms_helper_poll_fini(drm_dev);
    drm_vblank_cleanup(drm_dev);
err_unbind_all:
    component_unbind_all(dev, drm_dev);
err_mode_config_cleanup:
    drm_mode_config_cleanup(drm_dev);
    rockchip_iommu_cleanup(drm_dev);
err_free:
    drm_dev->dev_private = NULL;
    dev_set_drvdata(dev, NULL);
    drm_dev_unref(drm_dev);
    return ret;
}

static struct drm_driver rockchip_drm_driver = {
    .driver_features    = DRIVER_MODESET | DRIVER_GEM |
                  DRIVER_PRIME | DRIVER_ATOMIC |
                  DRIVER_RENDER,
  • 8
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值