龙芯闪屏问题

  • 分析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+ 1208   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和屏保设定

猜你喜欢

转载自blog.csdn.net/weixin_41028621/article/details/111029564