平台: RK3368
系统版本: Android8.1
内核版本: Linux4.4
按瑞芯微默认的休眠方式来看,二级休眠可以关闭背光功能,一级休眠才能够完全关闭LCD屏幕的运行,如何在不关闭其他外设的情况下,只对LCD屏幕进行完全关闭,那么就需要在二级休眠中增加对LCD的初始化。
走pm runtime通路,这个就不详述了,驱动中实现pm这个方法就行了。
drm原生只支持pm runtime的二级休眠方式,不支持一级二级休眠,为了支持android的一级休眠功能,瑞芯微提供了可操作的sys文件节点
1、使能CONFIG_DRM_FBDEV_EMULATION
2、echo 3 > /sys/class/graphics/fb0/blank //进入一级待机
echo 0 > /sys/class/graphics/fb0/blank //退出一级待机
使用fbdev的blank时,kernel会发notify到需要一级待机的外设,如touchscreen,keyboard等
drivers/video/fbdev/core/fbsysfs.c这个文件中声明了blank这个文件节点
/* When cmap is added back in it should be a binary attribute
* not a text one. Consideration should also be given to converting
* fbdev to use configfs instead of sysfs */
static struct device_attribute device_attrs[] = {
__ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
__ATTR_URW(blank, S_IRUGO|S_IWUGO, show_blank, store_blank),
__ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
__ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
__ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
__ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
__ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),
__ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),
__ATTR(name, S_IRUGO, show_name, NULL),
__ATTR(stride, S_IRUGO, show_stride, NULL),
__ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
__ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
#ifdef CONFIG_FB_BACKLIGHT
__ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
#endif
};
然后实现echo的方法
static ssize_t store_blank(struct device *device,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fb_info *fb_info = dev_get_drvdata(device);
char *last = NULL;
int err;
console_lock();
fb_info->flags |= FBINFO_MISC_USEREVENT;
err = fb_blank(fb_info, simple_strtoul(buf, &last, 0));
fb_info->flags &= ~FBINFO_MISC_USEREVENT;
console_unlock();
if (err < 0)
return err;
return count;
}
在fb_blank中回调 Notifier机制的handle
int fb_blank(struct fb_info *info, int blank)
{
struct fb_event event;
int ret = -EINVAL, early_ret;
if (blank > FB_BLANK_POWERDOWN)
blank = FB_BLANK_POWERDOWN;
event.info = info;
event.data = ␣
early_ret = fb_notifier_call_chain(FB_EARLY_EVENT_BLANK, &event);
if (info->fbops->fb_blank)
ret = info->fbops->fb_blank(blank, info);
if (!ret)
fb_notifier_call_chain(FB_EVENT_BLANK, &event);
else {
/*
* if fb_blank is failed then revert effects of
* the early blank event.
*/
if (!early_ret)
fb_notifier_call_chain(FB_R_EARLY_EVENT_BLANK, &event);
}
return ret;
}
EXPORT_SYMBOL(fb_blank);
在这个函数中可以看到是通过fb_notifier_list这个链表唤醒相对应的handle
int fb_notifier_call_chain(unsigned long val, void *v)
{
return blocking_notifier_call_chain(&fb_notifier_list, val, v);
}
EXPORT_SYMBOL_GPL(fb_notifier_call_chain);
在显示框架的probe中注册回调
dsi->fb_notif.notifier_call = fb_mipi_notifier_callback;
ret = fb_register_client(&dsi->fb_notif);
int fb_register_client(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&fb_notifier_list, nb);
}
EXPORT_SYMBOL(fb_register_client);
最后是回调函数的实现
static int fb_mipi_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
int fb_blank = 0;
struct dw_mipi_dsi *pdsi;
struct drm_encoder *encoder;
struct fb_event *evdata = data;
pdsi = container_of(self, struct dw_mipi_dsi, fb_notif);
encoder = &pdsi->encoder;
if(event != FB_EVENT_BLANK)
return 0;
fb_blank = *(int *)evdata->data;
if(fb_blank == FB_BLANK_UNBLANK)
dw_mipi_dsi_encoder_enable(encoder);
else
dw_mipi_dsi_encoder_disable(encoder);
return 0;
}