在uboot中获取设备树的gpio属性并控制+屏幕初始化分析

1.

在以往调驱动的经历中,改动最多的就是内核,在内核中我们可以去修改设备树,修改驱动源码,在驱动中去获取设备树中相关的硬件信息,但是我们都很少在uboot中修改或者添加代码,然而我们在工作中有时候也会碰到需要在uboot中去修改相关的驱动源码,去获取设备树的相关信息,以此满足项目需求。好了话不多少直接进入正题。
板子是基于rk3368的板子,板子上挂了一块lvds屏,硬件工程师提出要修改一下屏幕的时序,原本想来应该很简单的才对,结果按照以往的思维去内核中panel-simple.c的probe函数添加代码获取设备树中的IO口,然后延时处理,同时在panel_simple_prepare做延时处理,以此满足时序要求,如下:

static int panel_simple_prepare(struct drm_panel *panel)
{
	struct panel_simple *p = to_panel_simple(panel);
	int err;

	if (p->prepared)
		return 0;

	err = regulator_enable(p->supply);
	if (err < 0) {
		dev_err(panel->dev, "failed to enable supply: %d\n", err);
		return err;
	}

	printk("%s----------->\r\n",__func__);
	if (p->power_gpio)
	{
		gpiod_direction_output(p->power_gpio, 1);
		msleep(10);
		//printk("%s:panel->power_gpio, delay\r\n",__func__);
	}
	if (p->enable_gpio)
	{
		gpiod_direction_output(p->enable_gpio, 1);
		msleep(10);
		//printk("%s:panel->enable_gpio, delay\r\n",__func__);
	}

	if (p->reset_gpio)
	{
		gpiod_direction_output(p->reset_gpio, 1);
		//printk("%s:panel->reset_gpio, delay\r\n",__func__);
	}

	msleep(10);

	if (p->backlight_gpio) {
		printk(KERN_ERR"[PANEL]: set backlight power on.\n");
		gpiod_direction_output(p->backlight_gpio, 1);
	}

	if (p->desc && p->desc->delay.prepare)
		msleep(p->desc->delay.prepare);

	p->prepared = true;

	return 0;
}


static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
{
	struct device_node *backlight, *ddc;
	struct panel_simple *panel;
	struct panel_desc *of_desc;
	u32 val;
	int err;

	panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
	if (!panel)
		return -ENOMEM;

	if (!desc)
		of_desc = devm_kzalloc(dev, sizeof(*of_desc), GFP_KERNEL);
	else
		of_desc = devm_kmemdup(dev, desc, sizeof(*of_desc), GFP_KERNEL);

	if (!of_property_read_u32(dev->of_node, "bus-format", &val))
		of_desc->bus_format = val;
	if (!of_property_read_u32(dev->of_node, "delay,prepare", &val))
		of_desc->delay.prepare = val;
	if (!of_property_read_u32(dev->of_node, "delay,enable", &val))
		of_desc->delay.enable = val;
	if (!of_property_read_u32(dev->of_node, "delay,disable", &val))
		of_desc->delay.disable = val;
	if (!of_property_read_u32(dev->of_node, "delay,unprepare", &val))
		of_desc->delay.unprepare = val;

	panel->enabled = false;
	panel->prepared = false;
	panel->desc = of_desc;
	panel->dev = dev;

	panel->supply = devm_regulator_get(dev, "power");
	if (IS_ERR(panel->supply)) {
		err = PTR_ERR(panel->supply);
		dev_err(dev, "failed to request power supply: %d\n", err);
		return PTR_ERR(panel->supply);
	}

	panel->enable_gpio = devm_gpiod_get_optional(dev, "enable", 0);
	if (IS_ERR(panel->enable_gpio)) {
		err = PTR_ERR(panel->enable_gpio);
		dev_err(dev, "failed to request enable GPIO: %d\n", err);
		return err;
	} else {
		gpiod_direction_output(panel->enable_gpio, 1);
		printk("%s:panel->enable_gpio, 0\r\n",__func__);
	}

	panel->power_gpio = devm_gpiod_get_optional(dev, "power", 0);
	if (IS_ERR(panel->power_gpio)) {
		err = PTR_ERR(panel->power_gpio);
		dev_err(dev, "failed to request reset GPIO: %d\n", err);
	} else {
		gpiod_direction_output(panel->power_gpio, 1);
		printk("%s:panel->power_gpio, 0\r\n",__func__);
	}

	panel->backlight_gpio = devm_gpiod_get_optional(dev, "backlight", 0);
	if (IS_ERR(panel->backlight_gpio)) {
		err = PTR_ERR(panel->backlight_gpio);
		dev_err(dev, "failed to request reset GPIO: %d\n", err);
	} else {
		gpiod_direction_output(panel->backlight_gpio, 1);
	}

	panel->reset_gpio = devm_gpiod_get_optional(dev, "reset", 0);
	if (IS_ERR(panel->reset_gpio)) {
		err = PTR_ERR(panel->reset_gpio);
		dev_err(dev, "failed to request reset GPIO: %d\n", err);
	}
	else 
	{
		gpiod_direction_output(panel->reset_gpio, 1);
		printk("%s:panel->reset_gpio, 0\r\n",__func__);
	}
	.....
	}

2.

但是无论怎么修改发现都无法拉低power_gpio口,而硬件要求在上电过程中要把power,stb,reset完全拉低在进行相应的延时处理来达到时序要求,所以修改内核这条路就行不通,于是我们想到直接去uboot中获取设备树的中IO口信息,然后将三个IO口拉低,再做延时处理以此满足要求。下面贴出代码:
设备树:

/{
	lvds_panel: lvds-panel {
		status = "okay";
		compatible = "simple-panel";
		backlight = <&backlight>;
		bus-format = <MEDIA_BUS_FMT_RBG888_1X24>;
		enable-gpios = <&gpio0 RK_PD4 GPIO_ACTIVE_HIGH>;
		reset-gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_HIGH>;
		power-gpios = <&gpio0 RK_PC3 GPIO_ACTIVE_HIGH>;
		backlight-gpios = <&gpio0 RK_PC7 GPIO_ACTIVE_HIGH>;
		rockchip,data-mapping = "vesa";
		rockchip,data-width = <24>;
		rockchip,output = "lvds";
		display-timings {
			native-mode = <&timing0>;

			timing0: timing0 {
			clock-frequency = <60000000>;
			hactive = <1024>;
			vactive = <600>;
			hback-porch = <90>;
			hfront-porch = <160>;
			vback-porch = <13>;
			vfront-porch = <12>;
			hsync-len = <70>;
			vsync-len = <10>;
			hsync-active = <0>;
			vsync-active = <0>;
			de-active = <0>;
			pixelclk-active = <0>;
			};
		};

		ports {
			panel_in_lvds: endpoint {
				remote-endpoint = <&lvds_out_panel>;
			};
		};
	};
};
...

&lvds {
	status = "okay";
	rockchip,panel = <&lvds_panel>;
	pinctrl-names = "default";
	pinctrl-0 = <&gpio0_c3 &gpio0_c7 &gpio0_d4 &gpio0_b7>;

	ports {
		lvds_out: port@1 {
			reg = <1>;
			#address-cells = <1>;
			#size-cells = <0>;

			lvds_out_panel: endpoint@0 {
				reg = <0>;
				remote-endpoint = <&panel_in_lvds>;
			};
		};
	};
};

&route_lvds {
	status = "okay";
};

首先cpu会先执行到:


int rockchip_display_init(void)
{
	const void *blob = gd->fdt_blob;
	int route, child, phandle, connect, crtc_node, conn_node;
	const struct rockchip_connector *conn;
	const struct rockchip_crtc *crtc;
	struct display_state *s;
	const char *name;

	printf("%s--------------------------------%d\n",__func__,__LINE__);
	route = fdt_path_offset(blob, "/display-subsystem/route");
	if (route < 0) {
		printf("Can't find display display route node\n");
		return -ENODEV;
	}

	printf("%s--------------------->%d\n",__func__,__LINE__);
	if (!fdt_device_is_available(blob, route))
		return -ENODEV;

	init_display_buffer();

	fdt_for_each_subnode(blob, child, route) {
		if (!fdt_device_is_available(blob, child))
			continue;

		phandle = fdt_getprop_u32_default_node(blob, child, 0,
						       "connect", -1);
		if (phandle < 0) {
			printf("Warn: %s: can't find connect node's handle\n",
			       fdt_get_name(blob, child, NULL));
			continue;
		}

		connect = fdt_node_offset_by_phandle(blob, phandle);
		if (connect < 0) {
			printf("Warn: %s: can't find connect node\n",
			       fdt_get_name(blob, child, NULL));
			continue;
		}

		crtc_node = find_crtc_node(blob, connect);
		if (!fdt_device_is_available(blob, crtc_node)) {
			printf("Warn: %s: crtc node is not available\n",
			       fdt_get_name(blob, child, NULL));
			continue;
		}
		crtc = rockchip_get_crtc(blob, crtc_node);
		if (!crtc) {
			printf("Warn: %s: can't find crtc driver\n",
			       fdt_get_name(blob, child, NULL));
			continue;
		}

		conn_node = find_connector_node(blob, connect);
		if (!fdt_device_is_available(blob, conn_node)) {
			printf("Warn: %s: connector node is not available\n",
			       fdt_get_name(blob, child, NULL));
			continue;
		}

		conn = rockchip_get_connector(blob, conn_node);
		if (!conn) {
			printf("Warn: %s: can't find connector driver\n",
			       fdt_get_name(blob, child, NULL));
			continue;
		}

		s = malloc(sizeof(*s));
		if (!s)
			goto err_free;

		memset(s, 0, sizeof(*s));

		INIT_LIST_HEAD(&s->head);
		fdt_get_string(blob, child, "logo,uboot", &s->ulogo_name);
		fdt_get_string(blob, child, "logo,kernel", &s->klogo_name);
		fdt_get_string(blob, child, "logo,mode", &name);
		if (!strcmp(name, "fullscreen"))
			s->logo_mode = ROCKCHIP_DISPLAY_FULLSCREEN;
		else
			s->logo_mode = ROCKCHIP_DISPLAY_CENTER;
		fdt_get_string(blob, child, "charge_logo,mode", &name);
		if (!strcmp(name, "fullscreen"))
			s->charge_logo_mode = ROCKCHIP_DISPLAY_FULLSCREEN;
		else
			s->charge_logo_mode = ROCKCHIP_DISPLAY_CENTER;

		s->blob = blob;
		s->conn_state.node = conn_node;
		s->conn_state.connector = conn;
		s->crtc_state.node = crtc_node;
		s->crtc_state.crtc = crtc;
		s->crtc_state.crtc_id = get_crtc_id(blob, connect);
		s->node = child;

		connector_phy_init(s);
		connector_panel_init(s);
		list_add_tail(&s->head, &rockchip_display_list);
	}

	return 0;

err_free:
	list_for_each_entry(s, &rockchip_display_list, head) {
		list_del(&s->head);
		free(s);
	}
	return -ENODEV;
}

而在这个函数中会调用fdt_path_offset它会去获得lvds主节点关于这个函数有如下说明:

fdt_path_offset
int fdt_path_offset(const void *fdt, const char *path) 
eg:node = fdt_path_offset(gd->fdt_blob,/aliases”); 
功能:获得dtb下某个节点的路径path的偏移。这个偏移就代表了这个节点

fdt_device_is_available判断节点是否可用

fdt_for_each_subnode:这是一个循环去获取这个节点下的各个子节点属性原型:

/**
 * fdt_for_each_subnode - iterate over all subnodes of a parent
 *
 * This is actually a wrapper around a for loop and would be used like so:
 *
 *	fdt_for_each_subnode(fdt, node, parent) {
 *		...
 *		use node
 *		...
 *	}
 *
 * Note that this is implemented as a macro and node is used as iterator in
 * the loop. It should therefore be a locally allocated variable. The parent
 * variable on the other hand is never modified, so it can be constant or
 * even a literal.
 *
 * @fdt:	FDT blob (const void *)
 * @node:	child node (int)
 * @parent:	parent node (int)
 */
#define fdt_for_each_subnode(fdt, node, parent)		\
	for (node = fdt_first_subnode(fdt, parent);	\
	     node >= 0;					\
	     node = fdt_next_subnode(fdt, node))
  • connector_phy_init(s);
    这个函数是去初始化lvds外设

  • list_add_tail(&s->head, &rockchip_display_list);
    添加设备链表节点

  • connector_panel_init(s);
    这个函数当中就有初始化屏幕的调用,而我们的时序就是要在这个函数中去作处理。

3.

connector_panel_init(s)的源码:

static int connector_panel_init(struct display_state *state)
{
	struct connector_state *conn_state = &state->conn_state;
	struct panel_state *panel_state = &state->panel_state;
	const void *blob = state->blob;
	int conn_node = conn_state->node;
	const struct rockchip_panel *panel;
	int panel_node;
	int ret;

	printf("%s---------------------%d\r\n",__func__,__LINE__);
	panel_node = get_panel_node(state, conn_node);
	if (panel_node < 0) {
		printf("failed to find panel node\n");
		return -ENODEV;
	}

	if (!fdt_device_is_available(blob, panel_node)) {
		printf("panel is disabled\n");
		return -ENODEV;
	}

	panel_state->node = panel_node;

	panel = rockchip_get_panel(blob, panel_node);
	if (!panel) {
		printf("failed to find panel driver\n");
		return 0;
	}

	panel_state->panel = panel;

	ret = rockchip_panel_init(state);
	if (ret) {
		printf("failed to init panel driver\n");
		return ret;
	}

	return 0;
}
  • get_panel_node
    这个函数就是去获取lvds_panel这个节点,原型:
static int get_panel_node(struct display_state *state, int conn_node)
{
	const void *blob = state->blob;
	int panel, ports, port, ep, remote, ph, nodedepth;

	panel = fdt_subnode_offset(blob, conn_node, "panel");
	if (panel > 0)
		return panel;

	ports = fdt_subnode_offset(blob, conn_node, "ports");
	if (ports < 0)
		return -ENODEV;

	fdt_for_each_subnode(blob, port, ports) {
		fdt_for_each_subnode(blob, ep, port) {
			ph = fdt_getprop_u32_default_node(blob, ep, 0,
							  "remote-endpoint", 0);
			if (!ph)
				continue;

			remote = fdt_node_offset_by_phandle(blob, ph);

			nodedepth = fdt_node_depth(blob, remote);
			if (nodedepth < 2)
				continue;

			panel = fdt_supernode_atdepth_offset(blob, remote,
							     nodedepth - 2,
							     NULL);
			break;
		}
	}

	return panel;
}
  • rockchip_get_panel
    这个函数是获取匹配属性,原型:
const struct rockchip_panel *rockchip_get_panel(const void *blob, int node)
{
	const char *name;
	int i;

	fdt_get_string(blob, node, "compatible", &name);

	for (i = 0; i < ARRAY_SIZE(g_panel); i++)
		if (!strcmp(name, g_panel[i].compatible))
			break;

	if (i >= ARRAY_SIZE(g_panel))
		return NULL;

	return &g_panel[i];
}
  • rockchip_panel_init
    这个函数就会去初始化屏幕
3.

rockchip_panel_init原型如下:

int rockchip_panel_init(struct display_state *state)
{
struct panel_state *panel_state = &state->panel_state;
const struct rockchip_panel *panel = panel_state->panel;

printf("%s---------------------%d\r\n",__func__,__LINE__);
if (!panel || !panel->funcs || !panel->funcs->init) {
	printf("%s: failed to find panel init funcs\n", __func__);
	return -ENODEV;
}

return panel->funcs->init(state);

}
而在返回值这里使用了函数指针,panel->funcs->init(state);
而在这个const struct rockchip_panel *panel中有const struct rockchip_panel_funcs *funcs;这个成员变量,struct rockchip_panel 这个结构体内容如下:

struct rockchip_panel {
	char compatible[30];
	const struct rockchip_panel_funcs *funcs;
	const void *data;
};

struct rockchip_panel_funcs 这个结构体的内容如下:


struct rockchip_panel_funcs {
	int (*init)(struct display_state *state);
	void (*deinit)(struct display_state *state);
	int (*prepare)(struct display_state *state);
	int (*unprepare)(struct display_state *state);
	int (*enable)(struct display_state *state);
	int (*disable)(struct display_state *state);
};

而这些函数的实现是在panel_simple.c中,其中init函数赋值如下:

const struct rockchip_panel_funcs panel_simple_funcs = {
	.init		= panel_simple_init,
	.deinit		= panel_simple_deinit,
	.prepare	= panel_simple_prepare,
	.unprepare	= panel_simple_unprepare,
	.enable		= panel_simple_enable,
	.disable	= panel_simple_disable,
};

实现如下:


struct fdt_gpio_state gpio_ctl[3];
static int panel_simple_init(struct display_state *state)
{
	const void *blob = state->blob;
	struct connector_state *conn_state = &state->conn_state;
	struct panel_state *panel_state = &state->panel_state;
	int node = panel_state->node;
	const struct drm_display_mode *mode = panel_state->panel->data;
	struct panel_simple *panel;
	int ret;
	printf("%s---------------------%d\r\n",__func__,__LINE__);
	node = fdt_node_offset_by_compatible(blob,0, "simple-panel");
    if (node < 0) {
        printf("%s-->No detect Pine,mini_control.\n", __func__);
        return -ENODEV;
    }
    if (!fdt_device_is_available(blob,node)) {
        printf("%s-->device gpio_ctl is disabled\n", __func__);
        return -1;
    }
    fdtdec_decode_gpios(blob, node, "power-gpios", &gpio_ctl[0], 1);
	printf("%s:---------power_gpio:%d\r\n",__func__,gpio_ctl[0].gpio);

    fdtdec_decode_gpios(blob, node, "enable-gpios", &gpio_ctl[1], 1);
	printf("%s:---------enable_gpio:%d\r\n",__func__,gpio_ctl[1].gpio);

    fdtdec_decode_gpios(blob, node, "reset-gpios", &gpio_ctl[2], 1);
	printf("%s:---------reset_gpio:%d\r\n",__func__,gpio_ctl[2].gpio);

	gpio_direction_output(gpio_ctl[0].gpio,(gpio_ctl[0].flags& OF_GPIO_ACTIVE_LOW));
	gpio_direction_output(gpio_ctl[1].gpio,(gpio_ctl[1].flags& OF_GPIO_ACTIVE_LOW));
	gpio_direction_output(gpio_ctl[2].gpio,(gpio_ctl[2].flags& OF_GPIO_ACTIVE_LOW));
	msleep(1000);

	gpio_direction_output(gpio_ctl[0].gpio,!(gpio_ctl[0].flags& OF_GPIO_ACTIVE_LOW));
	msleep(10);
	gpio_direction_output(gpio_ctl[1].gpio,!(gpio_ctl[1].flags& OF_GPIO_ACTIVE_LOW));
	msleep(10);
	gpio_direction_output(gpio_ctl[2].gpio,!(gpio_ctl[2].flags& OF_GPIO_ACTIVE_LOW));


	panel = malloc(sizeof(*panel));
	if (!panel)
		return -ENOMEM;

	panel->blob = blob;
	panel->node = node;
	panel->mode = mode;
	panel_state->private = panel;

	ret = panel_simple_parse_dt(blob, node, panel);
	if (ret) {
		printf("%s: failed to parse DT\n", __func__);
		free(panel);
		return ret;
	}

	conn_state->bus_format = panel->bus_format;

	return 0;
}

在该函数中我是参考panel_simple_parse_dt这个函数去获取设备树中的IO属性的实现如下:

static int panel_simple_parse_dt(const void *blob, int node,
				 struct panel_simple *panel)
{
	struct fdt_gpio_state *enable_gpio = &panel->enable_gpio;

	fdtdec_decode_gpio(blob, node, "enable-gpios", enable_gpio);

	panel->delay_prepare = fdtdec_get_int(blob, node, "delay,prepare", 0);
	panel->delay_unprepare = fdtdec_get_int(blob, node, "delay,unprepare", 0);
	panel->delay_enable = fdtdec_get_int(blob, node, "delay,enable", 0);
	panel->delay_disable = fdtdec_get_int(blob, node, "delay,disable", 0);
	panel->bus_format = fdtdec_get_int(blob, node, "bus-format", MEDIA_BUS_FMT_RBG888_1X24);

	printf("delay prepare[%d] unprepare[%d] enable[%d] disable[%d]\n",
	       panel->delay_prepare, panel->delay_unprepare,
	       panel->delay_enable, panel->delay_disable);

	/* keep panel blank on init. */
	gpio_direction_output(enable_gpio->gpio,!(enable_gpio->flags & OF_GPIO_ACTIVE_LOW));

#ifdef CONFIG_RK_PWM_BL
	rk_pwm_bl_config(0);
#endif
	return 0;
}

fdtdec_decode_gpio该函数就是去获取IO口的,而这个struct fdt_gpio_state中的内容如下:

/* This is the state of a GPIO pin as defined by the fdt */
struct fdt_gpio_state {
	const char *name;	/* name of the fdt property defining this */
	uint gpio;		/* GPIO number, or FDT_GPIO_NONE if none */
	u8 flags;		/* FDT_GPIO_... flags */
};

用法就不说了上面有,控制输出高低:

	gpio_direction_output(gpio_ctl[2].gpio,(gpio_ctl[2].flags& OF_GPIO_ACTIVE_LOW));

驱动很绕,贴代码不容易呀,想想写出这驱动的更不容易~_~

  • 7
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值