- 分析loongson 闪屏问题。
1.问题概述
三诺龙芯笔记本,测试进入待机或者休眠,发现在进入过程中出现闪烁问题。
1.1.测试环境
-
硬件:三诺 NB15L4A 龙芯3A4000 笔记本
-
系统:uniontechos-desktop-20-professional-1030_mips64
1.2.复现步骤
选择进入待机或者休眠模式。
2.代码分析
在桌面设置待机或者休眠模式后,Xorg 进程最终调用drm_ioctl改变系统属性,进入省电模式。
drivers/gpu/drm/drm_ioctl.c:
775 long drm_ioctl(struct file *filp,
776 unsigned int cmd, unsigned long arg)
777 {
778 struct drm_file *file_priv = filp->private_data;
779 struct drm_device *dev;
780 const struct drm_ioctl_desc *ioctl = NULL;
781 drm_ioctl_t *func;
782 unsigned int nr = DRM_IOCTL_NR(cmd);
783 int retcode = -EINVAL;
784 char stack_kdata[128];
785 char *kdata = NULL;
786 unsigned int in_size, out_size, drv_size, ksize;
787 bool is_driver_ioctl;
788
789 dev = file_priv->minor->dev;
793
794 is_driver_ioctl = nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END;
795
798 if (is_driver_ioctl) {
799 /* driver ioctl */
800 unsigned int index = nr - DRM_COMMAND_BASE;
801
802 if (index >= dev->driver->num_ioctls)
803 goto err_i1;
804 index = array_index_nospec(index, dev->driver->num_ioctls);
805 ioctl = &dev->driver->ioctls[index];
806 } else {
807 /* core ioctl */
808 if (nr >= DRM_CORE_IOCTL_COUNT)
809 goto err_i1;
810 nr = array_index_nospec(nr, DRM_CORE_IOCTL_COUNT);
812 ioctl = &drm_ioctls[nr];
813 }
827
828 /* Do not trust userspace, use our own definition */
829 func = ioctl->func;
830
847 if (copy_from_user(kdata, (void __user *)arg, in_size) != 0) {
848 retcode = -EFAULT;
849 goto err_i1;
850 }
857 retcode = drm_ioctl_kernel(filp, func, kdata, ioctl->flags);
858 if (copy_to_user((void __user *)arg, kdata, out_size) != 0)
859 retcode = -EFAULT;
873 }
判断 is_driver_ioctl,如果为0, 则设置 ioctl = &drm_ioctls[nr]; 根据DRM_IOCTL_MODE_SETPROPERTY,调用drm_connector_property_set_ioctl进行设置。
542 #define DRM_IOCTL_DEF(ioctl, _func, _flags) \
543 [DRM_IOCTL_NR(ioctl)] = {
\
544 .cmd = ioctl, \
545 .func = _func, \
546 .flags = _flags, \
547 .name = #ioctl \
548 }
550 /* Ioctl table */
551 static const struct drm_ioctl_desc drm_ioctls[] = {
552 DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version,
553 DRM_UNLOCKED|DRM_RENDER_ALLOW),
554 DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, DRM_UNLOCKED),
555 DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, DRM_UNLOCKED),
556 DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY),
557 DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_legacy_getmap_ioctl, DRM_UNLOCKED),
558 DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, DRM_UNLOCKED),
559 DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, DRM_UNLOCKED),
560 DRM_IOCTL_DEF(DRM_IOCTL_GET_CAP, drm_getcap, DRM_UNLOCKED|DRM_RENDER_ALLOW),
561 DRM_IOCTL_DEF(DRM_IOCTL_SET_CLIENT_CAP, drm_setclientcap, DRM_UNLOCKED),
562 DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_UNLOCKED | DRM_MASTER),
563
564 DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_invalid_op, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
565 DRM_IOCTL_DEF(DRM_IOCTL_BLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
566 DRM_IOCTL_DEF(DRM_IOCTL_UNBLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
567 DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_UNLOCKED|DRM_MASTER),
568
569 DRM_IOCTL_DEF(DRM_IOCTL_ADD_MAP, drm_legacy_addmap_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
570 DRM_IOCTL_DEF(DRM_IOCTL_RM_MAP, drm_legacy_rmmap_ioctl, DRM_AUTH),
571
572 DRM_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_legacy_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
573 DRM_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_legacy_getsareactx, DRM_AUTH),
574
575 DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_UNLOCKED|DRM_ROOT_ONLY),
576 DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_UNLOCKED|DRM_ROOT_ONLY),
577
578 DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_legacy_addctx, DRM_AUTH|DRM_ROOT_ONLY),
579 DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_legacy_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
580 DRM_IOCTL_DEF(DRM_IOCTL_MOD_CTX, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
581 DRM_IOCTL_DEF(DRM_IOCTL_GET_CTX, drm_legacy_getctx, DRM_AUTH),
582 DRM_IOCTL_DEF(DRM_IOCTL_SWITCH_CTX, drm_legacy_switchctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
583 DRM_IOCTL_DEF(DRM_IOCTL_NEW_CTX, drm_legacy_newctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
584 DRM_IOCTL_DEF(DRM_IOCTL_RES_CTX, drm_legacy_resctx, DRM_AUTH),
585
586 DRM_IOCTL_DEF(DRM_IOCTL_ADD_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
587 DRM_IOCTL_DEF(DRM_IOCTL_RM_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
588
589 DRM_IOCTL_DEF(DRM_IOCTL_LOCK, drm_legacy_lock, DRM_AUTH),
590 DRM_IOCTL_DEF(DRM_IOCTL_UNLOCK, drm_legacy_unlock, DRM_AUTH),
591
645 DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_connector_property_set_ioctl, DRM_MASTER|DRM_UNLOCKED),
646 ...
647 }
drm_mode_obj_set_property_ioctl调用流程:
->drm_mode_obj_set_property_ioctl
->set_property_atomic
->set_property_legacy
->drm_connector_set_obj_prop
->drm_helper_connector_dpms
499 int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
500 struct drm_file *file_priv)
501 {
502 struct drm_mode_obj_set_property *arg = data;
503 struct drm_mode_object *arg_obj;
504 struct drm_property *property;
505 int ret = -EINVAL;
510
511 arg_obj = drm_mode_object_find(dev, file_priv, arg->obj_id, arg->obj_type);
517
518 property = drm_mode_obj_find_prop_id(arg_obj, arg->prop_id);
522 if (drm_drv_uses_atomic_modeset(property->dev)) {
523 ret = set_property_atomic(arg_obj, property, arg->value);
524 } else {
526 ret = set_property_legacy(arg_obj, property, arg->value);
527 }
532 }
422 static int set_property_legacy(struct drm_mode_object *obj,
423 struct drm_property *prop,
424 uint64_t prop_value)
425 {
426 struct drm_device *dev = prop->dev;
427 struct drm_mode_object *ref;
428 int ret = -EINVAL;
429
433 drm_modeset_lock_all(dev);
434 switch (obj->type) {
435 case DRM_MODE_OBJECT_CONNECTOR:
436 ret = drm_connector_set_obj_prop(obj, prop, prop_value);
437 break;
438 case DRM_MODE_OBJECT_CRTC:
439 ret = drm_mode_crtc_set_obj_prop(obj, prop, prop_value);
440 break;
441 case DRM_MODE_OBJECT_PLANE:
442 ret = drm_mode_plane_set_obj_prop(obj_to_plane(obj),
443 prop, prop_value);
444 break;
445 }
446 drm_property_change_valid_put(prop, ref);
447 drm_modeset_unlock_all(dev);
448
449 return ret;
450 }
1632 int drm_connector_set_obj_prop(struct drm_mode_object *obj,
1633 struct drm_property *property,
1634 uint64_t value)
1635 {
1636 int ret = -EINVAL;
1637 struct drm_connector *connector = obj_to_connector(obj);
1638
1640 /* Do DPMS ourselves */
1641 if (property == connector->dev->mode_config.dpms_property) {
1643 ret = (*connector->funcs->dpms)(connector, (int)value);
1644 } else if (connector->funcs->set_property) {
1645 ret = connector->funcs->set_property(connector, property, value);
1646 }
1647
1648 if (!ret)
1649 drm_object_property_set_value(&connector->base, property, value);
1650 return ret;
1651 }
drivers/gpu/drm/loongson/loongson_connector.c:
633 static const struct drm_connector_funcs loongson_connector_funcs = {
634 .dpms = drm_helper_connector_dpms,
635 .detect = loongson_connector_detect,
636 .late_register = loongson_connector_late_register,
637 .early_unregister = loongson_connector_early_unregister,
638 .fill_modes = drm_helper_probe_single_connector_modes,
639 .destroy = loongson_connector_destroy,
640 };
3.异常log:
drivers/gpu/drm/drm_crtc_helper.c:
875 int drm_helper_connector_dpms(struct drm_connector *connector, int mode)
876 {
877 struct drm_encoder *encoder = connector->encoder;
878 struct drm_crtc *crtc = encoder ? encoder->crtc : NULL;
879 int old_dpms, encoder_dpms = DRM_MODE_DPMS_OFF;
880
883
884 if (mode == connector->dpms)
885 return 0;
886
887 old_dpms = connector->dpms;
888 connector->dpms = mode;
889
890 if (encoder)
891 encoder_dpms = drm_helper_choose_encoder_dpms(encoder);
892
893 printk("albert---------11111111-----------:%s, mode=%d, old_dpms=%d\n",__func__, mode, old_dpms);
894 /* from off to on, do crtc then encoder */
895 if (mode < old_dpms) {
896 printk("albert---------2222222-----------:%s, mode=%d, old_dpms=%d\n",__func__, mode, old_dpms);
897 #ifdef CONFIG_CPU_LOONGSON3
898 if(connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
899 turn_on_lvds();
901 }
902 #endif
903 if (crtc) {
904 const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
905 if (crtc_funcs->dpms)
906 (*crtc_funcs->dpms) (crtc,
907 drm_helper_choose_crtc_dpms(crtc));
908 }
909 if (encoder)
910 drm_helper_encoder_dpms(encoder, encoder_dpms);
911 }
912
913 /* from on to off, do encoder then crtc */
914 if (mode > old_dpms) {
915 printk("albert---------33333333-----------:%s, mode=%d, old_dpms=%d\n",__func__, mode, old_dpms);
916 #ifdef CONFIG_CPU_LOONGSON3
917 if(connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
918 turn_off_lvds();
920 }
921 #endif
922 if (encoder)
923 drm_helper_encoder_dpms(encoder, encoder_dpms);
924 if (crtc) {
925 const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
926 if (crtc_funcs->dpms)
927 (*crtc_funcs->dpms) (crtc,
928 drm_helper_choose_crtc_dpms(crtc));
929 }
930 }
931
932 return 0;
933 }
鼠标点击待机,系统进入待机状态,log信息如下:
Dec 9 09:13:56 uos-PC kernel: [ 5992.213030] [drm:drm_ioctl] pid=2844, dev=0xe200, auth=1, DRM_IOCTL_MODE_SETPROPERTY
Dec 9 09:13:56 uos-PC kernel: [ 5992.213042] albert---------11111111-----------:drm_helper_connector_dpms, mode=3, old_dpms=0
Dec 9 09:13:56 uos-PC kernel: [ 5992.213045] albert---------33333333-----------:drm_helper_connector_dpms, mode=3, old_dpms=0
Dec 9 09:13:56 uos-PC kernel: [ 5992.213054] albert---------------------------:loongson_connector_lvds_power, enable=0
Dec 9 09:14:01 uos-PC kernel: [ 5997.457192] PM: suspend entry (deep)
Dec 9 09:14:01 uos-PC kernel: [ 5997.457203] PM: Syncing filesystems ... done.
Dec 9 09:14:01 uos-PC kernel: [ 5997.509230] [drm:drm_ioctl] pid=2844, dev=0xe200, auth=1, DRM_IOCTL_MODE_SETPROPERTY
Dec 9 09:14:01 uos-PC kernel: [ 5997.509303] albert---------------------------:drm_helper_connector_dpms, mode=0, connector->dpms=3
Dec 9 09:14:01 uos-PC kernel: [ 5997.509338] albert---------11111111-----------:drm_helper_connector_dpms, mode=0, old_dpms=3
Dec 9 09:14:01 uos-PC kernel: [ 5997.509360] albert---------2222222-----------:drm_helper_connector_dpms, mode=0, old_dpms=3
Dec 9 09:14:01 uos-PC kernel: [ 5997.509420] albert---------------------------:loongson_connector_lvds_power, enable=1
Dec 9 09:14:25 uos-PC kernel: [ 5998.428890] Freezing user space processes ... (elapsed 0.005 seconds) done.
uos@uos-PC:~$ ps aux | grep 2844
root 2844 0.1 2.0 629936 160176 tty1 Ssl+ 12月08 1:57 /usr/lib/xorg/Xorg -background none :0 -seat seat0 -auth /var/run/lightdm/root/:0 -nolisten tcp vt1 -novtswitch
分析:
设置系统进入待机状态,xorg(PID 2844 )最终会调用drm_helper_connector_dpms,在进入待机之前,调用该函数设置关闭屏幕,但是发现进入待机后,即PM: suspend entry (deep),xorg 又调用了该函数,引起闪屏。
4.DPMS (显示电源管理信号,简称DPMS)
可以在计算机一定时间无操作时,将显示器置于节电模式。
4.1.用xset修改DPMS和屏保设定
可以用 xorg-xset 提供的xset工具关闭屏幕。
命令描述:
xset s off 禁用屏保清空
xset s 3600 3600 将清空时间设置到 1 小时
xset -dpms 关闭 DPMS
xset s off -dpms 禁用 DPMS 并阻止屏幕清空
xset dpms force off 立即关闭屏幕
xset dpms force standby 待机界面
xset dpms force suspend 休眠界面
Note: 通过 dpms 0 0 0 可以将 DPMS 超时都设置成零,这样也可以禁用 DPMS. 这种方式更方便,可以使用 xset dpms force off 关闭屏幕。
查看当前设置:
$ xset q
...
Screen Saver:
prefer blanking: yes allow exposures: yes
timeout: 600 cycle: 600
DPMS (Energy Star):
Standby: 600 Suspend: 600 Off: 600
DPMS is Enabled
Monitor is On
refer to
- https://wiki.archlinux.org/index.php/Display_Power_Management_Signaling_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)#用xset修改DPMS和屏保设定