写在前面
主要是介绍一下rk平台px30 移植LCD屏的工作心得
一、DRM基础
二、DRM相关目录结构
driver | file | doc |
---|---|---|
Core | rockchip_drm_drv.c | |
Framebufferrock | chip_drm_fb.c | |
GEM | rockchip_drm_gem.c | |
VOP | rockchip_drm_vop.c ,rockchip_vop_reg.c | |
LVDS | rockchip_lvds.c | |
RGB | rockchip_rgb.c | |
MIPI | dw_mipi_dsi.c,phy_rockchip-inno-mipi-dphy.c | |
HDMI | dw_hdmi-rockchip.c,dw-hdmi.c | |
INNO HDMI | inno_hdmi.c | |
eDp | analogix_dp-rockchip.c,analogix_dp_core.c,analogix_dp_reg.c,phy-rockchip-dp.c | |
DP | cdn-dp-core.c,cdn-dp-reg.c | |
Panel | panel-simple.c |
三、设备树
绑定路径如下
//数据传递流程如下:
vop(相当于显示控制器) --> dis/vlds/edp/rgb(相当于encoder) --> panel(相当于显示器)
//分析2代的配置,数据流向
vopb_out_rgb -> rgb_in_vopb -> rgb_out_panel -> panel_in_rgb
//phy
&video_phy {
status = "okay";
};
//vop 配置
&display_subsystem {
status = "okay";
};
&rgb_in_vopb {
status = "okay";
};
&rgb_in_vopl {
status = "disabled";
};
&route_rgb {
connect = <&vopb_out_rgb>;
//这块使能了,相当于使能了vopb_out_rgb输出
status = "okay";
};
//总结:以上配置相当于配置了vop -> vopb -> vopb_out_rgb
//配置encoder
&rgb {
//1.这块使能相当于使能了encoder
status = "okay";
ports {
port@1 {
reg = <1>;
rgb_out_panel: endpoint {
//2.这块定义了连接的panel,即显示器
remote-endpoint = <&panel_in_rgb>;
};
};
};
};
//配置panel,在panel中设置与液晶屏相关内容,例如分辨率、时钟,时序等各参数
panel {
compatible = "simple-panel";
backlight = <&backlight>;
power-supply = <&vcc_lcd>;
enable-gpios = <&gpio0 RK_PA2 GPIO_ACTIVE_HIGH>;
// prepare-delay-ms = <120>;
// enable-delay-ms = <120>;
// disable-delay-ms = <120>;
// unprepare-delay-ms = <120>;
bus-format = <MEDIA_BUS_FMT_RBG888_1X24>;
width-mm = <108>;
height-mm = <64>;
display-timings {
native-mode = <&timing0>;
timing0: timing0 {
clock-frequency = <33300000>;
hactive = <800>;
vactive = <480>;
hback-porch = <36>;
hfront-porch = <210>;
vback-porch = <13>;
vfront-porch = <22>;
hsync-len = <10>;
vsync-len = <10>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <1>;
pixelclk-active = <1>;
};
};
port {
panel_in_rgb: endpoint {
remote-endpoint = <&rgb_out_panel>;
};
};
};
};
四、component
在DRM、ALSA等子系统中,通过超级设备(superdevice)管理多个组件(component)加载顺序,保证所有组件都可正常使用,在drm驱动中,component设备用来表示vop和各显示接口(如:HDMI、MIPI等)
在设备树中定义了如下的显示子系统
&display_subsystem {
status = "disabled";
ports = <&vopb_out>, <&vopl_out>;
logo-memory-region = <&drm_logo>;
route {
route_lvds: route-lvds {
status = "disabled";
logo,uboot = "logo.bmp";
logo,kernel = "logo_kernel.bmp";
logo,mode = "center";
charge_logo,mode = "center";
connect = <&vopb_out_lvds>;
};
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_rgb: route-rgb {
status = "disabled";
logo,uboot = "logo.bmp";
logo,kernel = "logo_kernel.bmp";
logo,mode = "center";
charge_logo,mode = "center";
connect = <&vopb_out_rgb>;
};
};
};
其中添加vop组件
struct platform_driver vop_platform_driver = {
.probe = vop_probe,
.remove = vop_remove,
.driver = {
.name = "rockchip-vop",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(vop_driver_dt_match),
},
};
//探测函数 添加组件
static int vop_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
if (!dev->of_node) {
dev_err(dev, "can't find vop devices\n");
return -ENODEV;
}
//添加组件
return component_add(dev, &vop_component_ops);
}
添加rgb组件
static const struct of_device_id rockchip_rgb_dt_ids[] = {
{ .compatible = "rockchip,px30-rgb", .data = &px30_rgb_funcs },
{ .compatible = "rockchip,rk1808-rgb", .data = &rk1808_rgb_funcs },
{ .compatible = "rockchip,rk3066-rgb", },
{ .compatible = "rockchip,rk3128-rgb", },
{ .compatible = "rockchip,rk3288-rgb", .data = &rk3288_rgb_funcs },
{ .compatible = "rockchip,rk3308-rgb", },
{ .compatible = "rockchip,rk3368-rgb", },
{ .compatible = "rockchip,rv1108-rgb", },
{}
};
MODULE_DEVICE_TABLE(of, rockchip_rgb_dt_ids);
static struct platform_driver rockchip_rgb_driver = {
.probe = rockchip_rgb_probe,
.remove = rockchip_rgb_remove,
.driver = {
.name = "rockchip-rgb",
.of_match_table = of_match_ptr(rockchip_rgb_dt_ids),
},
};
//探测函数 添加组件
static int rockchip_rgb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rockchip_rgb *rgb;
int ret;
rgb = devm_kzalloc(&pdev->dev, sizeof(*rgb), GFP_KERNEL);
if (!rgb)
return -ENOMEM;
rgb->dev = dev;
rgb->funcs = of_device_get_match_data(dev);
platform_set_drvdata(pdev, rgb);
rgb->data_sync = of_property_read_bool(dev->of_node,
"rockchip,data-sync");
if (dev->parent && dev->parent->of_node) {
rgb->grf = syscon_node_to_regmap(dev->parent->of_node);
if (IS_ERR(rgb->grf)) {
ret = PTR_ERR(rgb->grf);
dev_err(dev, "Unable to get grf: %d\n", ret);
return ret;
}
}
rgb->phy = devm_phy_optional_get(dev, "phy");
if (IS_ERR(rgb->phy)) {
ret = PTR_ERR(rgb->phy);
dev_err(dev, "failed to get phy: %d\n", ret);
return ret;
}
//添加组件
return component_add(dev, &rockchip_rgb_component_ops);
}
其实核心函数为
static const struct of_device_id rockchip_drm_dt_ids[] = {
{ .compatible = "rockchip,display-subsystem", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids);
static struct platform_driver rockchip_drm_platform_driver = {
.probe = rockchip_drm_platform_probe,
.remove = rockchip_drm_platform_remove,
.shutdown = rockchip_drm_platform_shutdown,
.driver = {
.name = "rockchip-drm",
.of_match_table = rockchip_drm_dt_ids,
.pm = &rockchip_drm_pm_ops,
},
};
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);
if (!port)
break;
if (!of_device_is_available(port->parent)) {
of_node_put(port);
continue;
}
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;
}
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;
}
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);
}
return component_master_add_with_match(dev, &rockchip_drm_ops, match);
}
component_master_add_with_match的函数调用栈为
rockchip_drm_platform_probe
component_master_add_with_match
try_to_bring_up_master
find_components
/* Found all components */
rockchip_drm_bind //ret = master->ops->bind(master->dev);
component_bind_all
component->ops->bind //循环bind了所有组件
其实 也就是这张图