留在草稿箱很久了,就直接发了。
过去的事,很可惜。
make rockchip_defconfig
make rk3399-sapphire-excavator-edp.img
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 = ;
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做了一下工作:
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到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++;
}
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;
}
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,