首先我们来看一个例子:
lcd0: display {
compatible = "osddisplays,osd057T0559-34ts", "panel-dpi";
label = "lcd";
backlight = <&lcd_bl>;
enable-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
panel-timing {
clock-frequency = <33000000>;
hactive = <800>;
vactive = <600>;
hfront-porch = <210>;
hback-porch = <46>;
hsync-len = <1>;
vback-porch = <23>;
vfront-porch = <12>;
vsync-len = <1>;
hsync-active = <0>;
vsync-active = <0>;
de-active = <1>;
pixelclk-active = <1>;
};
关于一些基础的使用在这里就不累述了。
我们会想,enable-gpios这个属性,看名字,我们就知道应该是GPIO驱动要做的事情,那么我们在我们这个驱动中使用了这个。
那么内核是怎么进行处理的呢?是GPIO驱动在加载过程中,遍历所有的节点,找到所有的enable-gpios属性,然后进行配置的吗?还是说,这个是由我们LCD驱动自己配置的,调用了GPIO驱动的接口,也就是说,在这种情况下,如果我们的LCD没有实现的话,那么这个属性将不会起任何作用。
下面,我们就带着这些疑问,从代码中来,从代码中去吧。
一、找到相应的驱动
我们可以通过compatible属性找到我们这个设备树会适配的驱动。找到:\drivers\video\fbdev\omap2\omapfb\displays\panel-dpi.c
匹配:
static const struct of_device_id panel_dpi_of_match[] = {
{ .compatible = "omapdss,panel-dpi", },
{},
};
MODULE_DEVICE_TABLE(of, panel_dpi_of_match);
static struct platform_driver panel_dpi_driver = {
.probe = panel_dpi_probe,
.remove = __exit_p(panel_dpi_remove),
.driver = {
.name = "panel-dpi",
.of_match_table = panel_dpi_of_match,
.suppress_bind_attrs = true,
},
};
module_platform_driver(panel_dpi_driver);
我们知道,设备树是通过.driver.name来找到对应的驱动的。如果匹配到后会调用probe函数,在我们这个驱动里就是panel_dpi_probe函数。
static int panel_dpi_probe(struct platform_device *pdev)
{
struct panel_drv_data *ddata;
struct omap_dss_device *dssdev;
int r;
---***************省略代码**************************-
if (dev_get_platdata(&pdev->dev)) {
r = panel_dpi_probe_pdata(pdev);
if (r)
return r;
} else if (pdev->dev.of_node) {
r = panel_dpi_probe_of(pdev); //判断是否是由设备树匹配到的,如果是的话,那么就传递设备树的节点进去
if (r)
return r;
} else {
return -ENODEV;
}
if (gpio_is_valid(ddata->backlight_gpio)) {
r = devm_gpio_request_one(&pdev->dev, ddata->backlight_gpio,
GPIOF_OUT_INIT_LOW, "panel backlight");
if (r)
goto err_gpio;
}
---***************省略代码**************************-
}
在上面的代码中,有个if语句来去判断,到底我们这个驱动是在什么情况下被匹配到的。
有两种情况,一种是通过在代码中注册设备来实现匹配的。而一种是使用设备树被匹配到的。
也就是说,如果我们的驱动是通过,设备树进行匹配的,那么其pdev->dev.of_node就会指向,当前该设备在设备树中node节点。
从这里我们也可以知道,驱动程序是只知道它当前的node节点,而不是整个设备树的根节点。
如果判断是通过设备树匹配驱动成功的,那么就会调用panel_dpi_probe_of函数。
那么我们来看看这个函数都做了什么。
static int panel_dpi_probe_of(struct platform_device *pdev)
{
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
struct device_node *node = pdev->dev.of_node;
struct omap_dss_device *in;
int r;
struct display_timing timing;
struct videomode vm;
struct gpio_desc *gpio;
gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW); // -------a
if (IS_ERR(gpio))
return PTR_ERR(gpio);
ddata->enable_gpio = gpio;
ddata->backlight_gpio = -ENOENT;
r = of_get_display_timing(node, "panel-timing", &timing);
if (r) {
dev_err(&pdev->dev, "failed to get video timing\n");
return r;
}
videomode_from_timing(&timing, &vm);
videomode_to_omap_video_timings(&vm, &ddata->videomode);
in = omapdss_of_find_source_for_first_ep(node);
if (IS_ERR(in)) {
dev_err(&pdev->dev, "failed to find video source\n");
return PTR_ERR(in);
}
ddata->in = in;
return 0;
}
代码a:
这个函数一上来,就给我调用了这么一个devm_gpiod_get_optional()函数。通过这个函数名字,我们也可以大概知道它是和GPIO相关的,好家伙,我们就是要研究我们这个LCD驱动是怎么驱动GPIO的,那么我们就研究这个函数好了。
这个函数的第二个参数是”enable”,是不是看着很眼熟?
是的。这个和我们在LCD的设备树中的”enable-gpios”貌似有着千丝万缕的关系。而且,此刻!这个函数名也指向了GPIO。那么我们完全有理由相信,这个函数就是处理设备树中”enable-gpios”属性的。
在这里,如果我们跟着进行查看的话,我们会发现,这个函数就是在处理这个节点关于GPIO操作的代码。所以,我们之前的一个问题就可以得到了解答:
问:设备树驱动之间是怎么互相调用接口的,是需要在驱动中自己实现呢,还是Linux设备驱动模型已经帮我们处理好了?
答:如果我们在我们的程序中调用其他驱动的功能,那么我们需要使用其他驱动提供的接口,不需要我们自己去将每个属性的功能实现一遍。
未完待续,07-08-11