硬件上,高通平台有一个mipi-dsi接口连接LCM,由MDP(mobile display processor)进行管理,就是一般说的LCD控制器
软件上,高通平台提供MDSS(Multimedia Display Sub-system)进行管理
软件驱动目录:kernel/msm-4.9/drivers/video/fbdev/msm
主要分为三部分:
MDP驱动:对使用的硬件资源进行初始化,同时在fb设备中注册mdp的使用接口,文件mdss_mdp3.c
DSI驱动:解析模组厂商提供的panel的dtsi文件,从哪个文件中能够获取到panel的mode,分辨率,还有Driver IC的初始化command;文件mdss_dsi_panel.c
FB驱动:实现Linux Framebuffer的注册和为上层用户提供访问控制接口;文件mdss_fb.c
一般的执行顺序是: MDP probe → DSI probe → FB probe
下面分析每部分的代码
LCD控制器、I2C控制器等一般都是平台设备,设备用platform_device表示,驱动用platform_driver表示。
MDP设备在dts文件中定义:
msm8953-mdss.dtsi
&soc {
mdss_mdp: qcom,mdss_mdp@1a00000 {
compatible = "qcom,mdss_mdp";
reg = <0x01a00000 0x90000>,
<0x01ab0000 0x1040>;
reg-names = "mdp_phys", "vbif_phys";
interrupts = <0 72 0>;
vdd-supply = <&gdsc_mdss>;
/* Bus Scale Settings */
qcom,msm-bus,name = "mdss_mdp";
qcom,msm-bus,num-cases = <3>;
qcom,msm-bus,num-paths = <1>;
qcom,msm-bus,vectors-KBps =
<22 512 0 0>,
<22 512 0 6400000>,
<22 512 0 6400000>;
/* Fudge factors */
qcom,mdss-ab-factor = <1 1>; /* 1 time */
qcom,mdss-ib-factor = <1 1>; /* 1 time */
qcom,mdss-clk-factor = <105 100>; /* 1.05 times */
qcom,max-mixer-width = <2048>;
qcom,max-pipe-width = <2048>;
MDP驱动的注册:
static const struct of_device_id mdss_mdp_dt_match[] = {
{
.compatible = "qcom,mdss_mdp",},
{
}
};
MODULE_DEVICE_TABLE(of, mdss_mdp_dt_match);
static struct platform_driver mdss_mdp_driver = {
.probe = mdss_mdp_probe,
.remove = mdss_mdp_remove,
.suspend = mdss_mdp_suspend,
.resume = mdss_mdp_resume,
.shutdown = NULL,
.driver = {
/*
* Driver name must match the device name added in
* platform.c.
*/
.name = "mdp",
.of_match_table = mdss_mdp_dt_match,
.pm = &mdss_mdp_pm_ops,
},
};
static int mdss_mdp_register_driver(void)
{
return platform_driver_register(&mdss_mdp_driver);
}
static int __init mdss_mdp_driver_init(void)
{
int ret;
ret = mdss_mdp_register_driver();
if (ret) {
pr_err("mdp_register_driver() failed!\n");
return ret;
}
return 0;
}
老套路,设备和驱动匹配之后,调用mdss_mdp_probe函数,这个函数首先分配mdss_data_type结构体内存,然后对成员进行初始化:
static int mdss_mdp_probe(struct platform_device *pdev)
{
struct resource *res;
int rc;
struct mdss_data_type *mdata;
uint32_t intf_sel = 0;
uint32_t split_display = 0;
int num_of_display_on = 0;
int i = 0;
if (!pdev->dev.of_node) {
pr_err("MDP driver only supports device tree probe\n");
return -ENOTSUPP;
}
if (mdss_res) {
pr_err("MDP already initialized\n");
return -EINVAL;
}
mdata = devm_kzalloc(&pdev->dev, sizeof(*mdata), GFP_KERNEL);
if (mdata == NULL)
return -ENOMEM;
pdev->id = 0;
mdata->pdev = pdev;
platform_set_drvdata(pdev, mdata);
mdss_res = mdata;
mutex_init(&mdata->reg_lock);
mutex_init(&mdata->reg_bus_lock);
mutex_init(&mdata->bus_lock);
INIT_LIST_HEAD(&mdata->reg_bus_clist);
atomic_set(&mdata->sd_client_count, 0);
atomic_set(&mdata->active_intf_cnt, 0);
mdss_res->mdss_util = mdss_get_util_intf();
if (mdss_res->mdss_util == NULL) {
pr_err("Failed to get mdss utility functions\n");
return -ENODEV;
}
mdss_res->mdss_util->get_iommu_domain = mdss_smmu_get_domain_id;
mdss_res->mdss_util->iommu_attached = is_mdss_iommu_attached;
mdss_res->mdss_util->iommu_ctrl = mdss_iommu_ctrl;
mdss_res->mdss_util->bus_scale_set_quota = mdss_bus_scale_set_quota;
mdss_res->mdss_util->bus_bandwidth_ctrl = mdss_bus_bandwidth_ctrl;
mdss_res->mdss_util->panel_intf_type = mdss_panel_intf_type;
mdss_res->mdss_util->panel_intf_status = mdss_panel_get_intf_status;
rc = msm_mdss_ioremap_byname(pdev, &mdata->mdss_io, "mdp_phys");
if (rc) {
pr_err("unable to map MDP base\n");
goto probe_done;
}
pr_debug("MDSS HW Base addr=0x%x len=0x%x\n",
(int) (unsigned long) mdata->mdss_io.base,
mdata->mdss_io.len);
rc = msm_mdss_ioremap_byname(pdev, &mdata->vbif_io, "vbif_phys");
if (rc) {
pr_err("unable to map MDSS VBIF base\n");
goto probe_done;
}
pr_debug("MDSS VBIF HW Base addr=0x%x len=0x%x\n",
(int) (unsigned long) mdata->vbif_io.base,
mdata->vbif_io.len);
rc = msm_mdss_ioremap_byname(pdev, &mdata->vbif_nrt_io,
"vbif_nrt_phys");
if (rc)
pr_debug("unable to map MDSS VBIF non-realtime base\n");
else
pr_debug("MDSS VBIF NRT HW Base addr=%pK len=0x%x\n",
mdata->vbif_nrt_io.base, mdata->vbif_nrt_io.len);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
pr_err("unable to get MDSS irq\n");
rc = -ENOMEM;
goto probe_done;
}
mdss_mdp_hw.irq_info = kcalloc(1, sizeof(struct irq_info), GFP_KERNEL);
if (!mdss_mdp_hw.irq_info)
return -ENOMEM;
mdss_mdp_hw.irq_info->irq = res->start;
mdss_mdp_hw.ptr = mdata;
/* export misc. interrupts to external driver */
mdata->irq_domain = irq_domain_add_linear(pdev->dev.of_node, 32,
&mdss_irq_domain_ops, mdata);
if (!mdata->irq_domain) {