- 了解loongson 显卡驱动
1.代码分析
drivers/gpu/drm/loongson/loongson_drv.c
1112 static int __init loongson_init(void)
1113 {
1114 struct pci_dev *pdev = NULL;
1115 ...
1129 return pci_register_driver(&loongson_pci_driver);
1130 }
调用pci_register_driver(&loongson_pci_driver); 注册loongson_pci_driver。
1101 static struct pci_driver loongson_pci_driver = {
1102 .name = DRIVER_NAME,
1103 .id_table = pciidlist,
1104 .probe = loongson_pci_probe,
1105 .remove = loongson_pci_remove,
1106 .driver.pm = &loongson_pmops,
1107 };
当pci device 和pci driver匹配之后,调用loongson_pci_probe,该函数register a PCI device with the DRM subsystem。
888 static int loongson_pci_probe(struct pci_dev *pdev,
889 const struct pci_device_id *ent)
890
891 {
892 return drm_get_pci_dev(pdev, ent, &loongson_kms_driver);
893 }
833 static struct drm_driver loongson_kms_driver = {
834 .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_HAVE_IRQ,
835 .load = loongson_load_kms,
836 .unload = loongson_unload_kms,
837 .open = loongson_open_kms,
838 .fops = &loongson_drm_driver_fops,
839 .gem_free_object = loongson_gem_free_object,
840 .dumb_create = loongson_dumb_create,
841 .dumb_map_offset = loongson_dumb_mmap_offset,
842
843 .ioctls = loongson_ioctls_kms,
844 .num_ioctls = DRM_LOONGSON_KMS_MAX_IOCTLS,
845
846 .name = DRIVER_NAME,
847 .desc = DRIVER_DESC,
848 .date = DRIVER_DATE,
849 .major = DRIVER_MAJOR,
850 .minor = DRIVER_MINOR,
851 .patchlevel = DRIVER_PATCHLEVEL,
852 /*vblank*/
853 .enable_vblank = loongson_irq_enable_vblank,
854 .disable_vblank = loongson_irq_disable_vblank,
855 .get_vblank_counter = loongson_crtc_vblank_count,
856 /*IRQ*/
857 .irq_preinstall = loongson_irq_preinstall,
858 .irq_postinstall = loongson_irq_postinstall,
859 .irq_uninstall = loongson_irq_uninstall,
860 .irq_handler = loongson_irq_handler,
861
862 #ifdef CONFIG_DEBUG_FS
863 .debugfs_init = loongson_debugfs_init,
864 #endif
865 };
drm_get_pci_dev:
- dev = drm_dev_alloc(driver, &pdev->dev);
- ret = pci_enable_device(pdev);
- ret = drm_dev_register(dev, ent->driver_data); //Register DRM device
- ret = dev->driver->load(dev, flags); //loongson_load_kms
- drm_modeset_register_all(dev);
- ret = drm_plane_register_all(dev);
- ret = drm_crtc_register_all(dev);
- ret = drm_encoder_register_all(dev);
- ret = drm_connector_register_all(dev);
重点loongson_load_kms:
575 static int loongson_load_kms(struct drm_device *dev, unsigned long flags)
576 {
577 struct loongson_device *ldev;
578 int ret, r;
579
580 ldev = devm_kzalloc(dev->dev, sizeof(struct loongson_device),
581 GFP_KERNEL);
584 dev->dev_private = (void *)ldev;
585 ldev->dev = dev;
586 spin_lock_init(&ldev->mmio_lock);
587
588 ret = loongson_device_init(dev, flags);
590 loongson_ttm_init(ldev);
591
592 drm_mode_config_init(dev);
593 dev->mode_config.funcs = (void *)&loongson_mode_funcs;
594 dev->mode_config.preferred_depth = 24;
595 dev->mode_config.prefer_shadow = 1;
596
597 r = loongson_modeset_init(ldev);
602
603 ldev->inited = true;
604
605 /*Enable IRQ*/
606 loongson_irq_init(ldev);
607
608 drm_kms_helper_poll_init(dev);
609
610 return 0;
611 }
-
588 ret = loongson_device_init(dev, flags);
- loongson_vbios_init:调用 get_vbios_from_bios获取vbios,如果失败,依次调用get_vbios_from_flash,loongson_vbios_default;复制loongson_sysconf.vgabios_addr中的内容到struct loongson_vbios,内容大小为#define VBIOS_SIZE 0x40000。
drivers/gpu/drm/loongson/loongson_legacy_vbios.h:
12 struct loongson_vbios {
13 char title[16];
>> 14 uint32_t version_major;
>> 15 uint32_t version_minor;
16 char information[20];
>> 17 uint32_t crtc_num;
>> 18 uint32_t crtc_offset;
>> 19 uint32_t connector_num;
>> 20 uint32_t connector_offset;
>> 21 uint32_t encoder_num;
>> 22 uint32_t encoder_offset;
23 } __attribute__((packed));
如果vbios版本<= 2,则调用loongson_vbios_init_legacy->show_legacy_vbios。
drivers/gpu/drm/loongson/loongson_vbios.c:
1136 bool loongson_vbios_init(struct loongson_device *ldev)
1141 vbios = get_vbios_from_bios();
1142 if (!vbios)
1143 vbios = get_vbios_from_flash();
1144 if (!vbios)
1145 vbios = loongson_vbios_default();
222 static int show_legacy_vbios(struct loongson_device *ldev)
223 {
224 int index;
225 struct loongson_vbios *vbios = (struct loongson_vbios *)ldev->vbios;
226 struct loongson_vbios_crtc *crtc;
227 struct loongson_vbios_connector *connector;
228 struct loongson_vbios_encoder *encoder;
229 char *config_method;
230 char *encoder_methods[] = {
"NONE", "OS", "BIOS", "ERR" };
231
232 char *edid_methods[] = {
"No EDID", "Reading EDID via built-in I2C",
233 "Use the VBIOS built-in EDID information",
234 "Get EDID via encoder chip" };
235
236 char *detect_methods[] = {
"SHOW", "POLL", "HPD", "NONE" };
237
241
242 for (index = 0; index < vbios->crtc_num; index++) {
243 crtc = get_crtc_legacy(ldev, index);
244 if (!crtc)
245 continue;
246 encoder = get_encoder_legacy(ldev, crtc->encoder_id);
247 if (!encoder)
248 continue;
249 connector = get_connector_legacy(ldev, encoder->connector_id);
250 if (!connector)
251 continue;
252 config_method = encoder_methods[encoder->config_type & 0x3];
259 }
260
261 return 0;
262 }
- 590 loongson_ttm_init(ldev);
- 597 r = loongson_modeset_init(ldev);
根据ldev->num_crtc的数目,进行for循环loongson_crtc_init/loongson_encoder_init/loongson_connector_init.-
loongson_crtc_init
- drm_crtc_init(ldev->dev, &ls_crtc->base, &loongson_crtc_funcs);
- drm_crtc_helper_add(&ls_crtc->base, &loongson_helper_funcs);
-
loongson_encoder_init
- drm_encoder_init(ldev->dev, encoder, &loongson_encoder_encoder_funcs, ls_encoder->type, NULL);
- drm_encoder_helper_add(encoder, &loongson_encoder_helper_funcs);
-
loongson_connector_init
- drm_connector_init(ldev->dev, connector, &loongson_connector_funcs, ls_connector->type);
- drm_connector_helper_add(connector, &loongson_connector_helper);
- drm_connector_register(connector);
-
loongson_fbdev_init
-
如上所示:
crtc/encoder/connector初始化struct drm_crtc_funcs:
691 static const struct drm_crtc_funcs loongson_crtc_funcs = {
692 .cursor_set2 = loongson_crtc_cursor_set2,
693 .cursor_move = loongson_crtc_cursor_move,
694 .set_config = drm_crtc_helper_set_config,
695 .destroy = loongson_crtc_destroy,
696 .page_flip = loongson_crtc_page_flip,
697 };
220 static const struct drm_encoder_funcs loongson_encoder_encoder_funcs = {
221 .reset = loongson_encoder_reset,
222 .destroy = loongson_encoder_destroy,
223 };
628 static const struct drm_connector_funcs loongson_connector_funcs = {
629 .dpms = drm_helper_connector_dpms,
630 .detect = loongson_connector_detect,
631 .late_register = loongson_connector_late_register,
632 .early_unregister = loongson_connector_early_unregister,
633 .fill_modes = drm_helper_probe_single_connector_modes,
634 .destroy = loongson_connector_destroy,
635 };
crtc/encoder/connector初始化drm_crtc_helper_funcs:
713 static const struct drm_crtc_helper_funcs loongson_helper_funcs = {
714 .disable = loongson_crtc_disable,
715 .dpms = loongson_crtc_dpms,
716 .mode_set = loongson_crtc_mode_set,
717 .mode_set_base = loongson_crtc_mode_set_base,
718 .prepare = loongson_crtc_prepare,
719 .commit = loongson_crtc_commit,
720 .mode_valid = loongson_crtc_mode_valid,
721 };
208 static const struct drm_encoder_helper_funcs loongson_encoder_helper_funcs = {
209 .dpms = loongson_encoder_dpms,
210 .mode_set = loongson_encoder_mode_set,
211 .prepare = loongson_encoder_prepare,
212 .commit = loongson_encoder_commit,
213 };
256 static const struct drm_connector_helper_funcs loongson_connector_helper = {
257 .get_modes = loongson_get_modes,
258 .mode_valid = loongson_mode_valid,
259 .best_encoder = loongson_connector_best_encoder,
260 };
2.龙芯显卡驱动背光
2.1.重要结构体
drivers/gpu/drm/loongson/loongson_drv.h:
291 struct loongson_connector {
292 struct drm_connector base;
293 struct loongson_device *ldev;
294 u16 id;
295 u32 type;
296 u16 i2c_id;
297 u16 hotplug;
298 u16 edid_method;
299 u8 *vbios_edid;
300 struct loongson_i2c *i2c;
301 struct loongson_backlight bl;
302 };
148 struct loongson_backlight {
149 struct backlight_device *device;
150 struct pwm_device *pwm;
151 u32 pwm_id;
152 u32 pwm_polarity;
153 u32 pwm_period;
154 bool present;
155 bool hw_enabled;
156 unsigned int level, max, min;
157
158 int (*get_resource)(struct loongson_connector *ls_connector);
159 void (*free_resource)(struct loongson_connector *ls_connector);
160 int (*setup)(struct loongson_connector *ls_connector);
161 unsigned int (*get_brightness)(struct loongson_connector *ls_connector);
162 void (*set_brightness)(struct loongson_connector *ls_connector,
163 unsigned int level);
164 void (*enable)(struct loongson_connector *ls_connector);
165 void (*disable)(struct loongson_connector *ls_connector);
166 void (*power)(struct loongson_connector *ls_connector, bool enable);
167 };
2.2.loongson_connector_init
646 struct loongson_connector *loongson_connector_init(struct loongson_device *ldev,
647 int index)
648 {
649 ...
660 ls_connector->type = get_connector_type(ldev, index);
661 ls_connector->i2c_id = get_connector_i2cid(ldev, index);
662 ls_connector->hotplug = get_hotplug_mode(ldev, index);
663 ls_connector->edid_method = get_edid_method(ldev, index);
664 if (ls_connector->edid_method == via_vbios)
665 ls_connector->vbios_edid = get_vbios_edid(ldev, index);
666 ls_connector->bl.pwm_id = get_vbios_pwm(ldev, index, VBIOS_PWM_ID);
667 ls_connector->bl.pwm_polarity =
668 get_vbios_pwm(ldev, index, VBIOS_PWM_POLARITY);
669 ls_connector->bl.pwm_period =
670 get_vbios_pwm(ldev, index, VBIOS_PWM_PERIOD);
671 ...
679 drm_connector_init(ldev->dev, connector, &loongson_connector_funcs,
680 ls_connector->type);
681 }
获取vbios中数据填充到struct loongson_backlight中,然后注册loongson_connector_funcs,当调用late_register时,即loongson_connector_late_register。
577 int loongson_connector_late_register(struct drm_connector *connector)
578 {
579 struct loongson_connector *ls_connector =
580 to_loongson_connector(connector);
581 u32 type = ls_connector->type;
582 int ret;
583
584 if (type == DRM_MODE_CONNECTOR_LVDS || type == DRM_MODE_CONNECTOR_eDP) {
585 backlight_pwm_register(ls_connector);
586 ret = loongson_connector_pwm_init(ls_connector);
587 if (ret == 0) {
588 ret = loongson_connector_backlight_register(
589 ls_connector);
590
591 if (ret == 0)
592 ls_connector->bl.present = true;
593 }
594 }
595
596 return 0;
597 }
获取ls_connector->type = get_connector_type(ldev, index); 然后判断connector type(读vbios获取), 如果是DRM_MODE_CONNECTOR_LVDS || DRM_MODE_CONNECTOR_eDP) ,则注册pwm。如下所示:
- backlight_pwm_register : 填充struct loongson_backlight
455 void backlight_pwm_register(struct loongson_connector *ls_connector)
456 {
457 ls_connector->bl.min = LOONGSON_BL_MIN_LEVEL;
458 ls_connector->bl.max = LOONGSON_BL_MAX_LEVEL;
459
460 ls_connector->bl.get_resource = loongson_connector_pwm_get_resource;
461 ls_connector->bl.free_resource = loongson_connector_pwm_free_resource;
462 ls_connector->bl.setup = loongson_connector_pwm_setup;
463 ls_connector->bl.get_brightness = loongson_connector_pwm_get;
464 ls_connector->bl.set_brightness = loongson_connector_pwm_set;
465 ls_connector->bl.enable = loongson_connector_bl_enable;
466 ls_connector->bl.disable = loongson_connector_bl_disable;
467 ls_connector->bl.power = loongson_connector_lvds_power;
468 }
- loongson_connector_pwm_init
- ret = bl->get_resource(ls_connector); //loongson_connector_pwm_get_resource
- ret = bl->setup(ls_connector); //loongson_connector_pwm_setup
- loongson_connector_backlight_register //创建sys/class/backlight/loongson-gpu
516 static const struct backlight_ops ls_backlight_device_ops = {
517 .update_status = loongson_connector_backlight_update,
518 .get_brightness = loongson_connector_get_brightness,
519 };
526 int loongson_connector_backlight_register(
527 struct loongson_connector *ls_connector)
528 {
529 struct backlight_properties props;
530
531 memset(&props, 0, sizeof(props));
532 props.type = BACKLIGHT_RAW;
533 props.max_brightness = ls_connector->bl.max;
534 props.brightness = ls_connector->bl.level;
535
536 ls_connector->bl.device =
537 backlight_device_register("loongson-gpu",
538 ls_connector->base.kdev, ls_connector,
539 &ls_backlight_device_ops, &props);
552 return 0;
553 }
2.3.背光驱动
创建backlight class,并定义属性文件bl_device_attrs。
drivers/video/backlight/backlight.c:
661 static int __init backlight_class_init(void)
662 {
663 backlight_class = class_create(THIS_MODULE, "backlight");
669
670 backlight_class->dev_groups = bl_device_groups;
671 backlight_class->pm = &backlight_class_dev_pm_ops;
676 return 0;
677 }
289 static struct attribute *bl_device_attrs[] = {
290 &dev_attr_bl_power.attr,
291 &dev_attr_brightness.attr,
292 &dev_attr_actual_brightness.attr,
293 &dev_attr_max_brightness.attr,
294 &dev_attr_type.attr,
295 NULL,
296 };
297 ATTRIBUTE_GROUPS(bl_device);
以&dev_attr_brightness.attr 为例:
196 static ssize_t brightness_store(struct device *dev,
197 struct device_attribute *attr, const char *buf, size_t count)
198 {
199 int rc;
200 struct backlight_device *bd = to_backlight_device(dev);
201 unsigned long brightness;
202
203 rc = kstrtoul(buf, 0, &brightness);
204 if (rc)
205 return rc;
206
207 rc = backlight_device_set_brightness(bd, brightness);
208
209 return rc ? rc : count;
210 }
211 static DEVICE_ATTR_RW(brightness);
函数调用流程 :
brightness_store
->backlight_device_set_brightness
->backlight_update_status
->ret = bd->ops->update_status(bd); //.update_status = loongson_connector_backlight_update,
2.3.1.手动echo设置亮度时,调用brightness_store函数。
root@uos-PC:/sys/class/backlight/loongson-gpu# echo 10 > brightness
2.3.2.DDE 控制亮度
调用流程:
2月 01 17:12:56 uos-PC daemon/session/power[3978]: utils.go:219: Change output "eDP-1" brightness to 1.00
2月 01 17:12:56 uos-PC kernel: CPU: 0 PID: 2364 Comm: backlight_helpe Not tainted 4.19.0-loongson-3-desktop-zhaoxiao #1471
2月 01 17:12:56 uos-PC kernel: Hardware name: HT706 TR4191/706-LX3A4000-4-V1.0-B20-3A40, BIOS V4.0 12/14/2020
2月 01 17:12:56 uos-PC kernel: Stack : 000000000000004e 000000007400cce0 0000000000000000 0000000000000007
2月 01 17:12:56 uos-PC kernel: 0000000000000000 0000000000000000 000000000000031a ffffffff8222c650
2月 01 17:12:56 uos-PC kernel: 000000000000031a 0000000000000000 0000000000000000 ffffffff81ff0000
2月 01 17:12:56 uos-PC kernel: ffffffff80212270 ffffffff80212268 ffffffff82240000 303441332d303242
2月 01 17:12:56 uos-PC kernel: ffff000000000000 0000000000000000 000000007400cce1 98000002526dd018
2月 01 17:12:56 uos-PC kernel: 9800000259a8fe60 00000001202fedf8 000000c0000388c8 00000001204e0000
2月 01 17:12:56 uos-PC kernel: 0000000000000006 fffffffffffffffb 0000000000006000 9800000252654000
2月 01 17:12:56 uos-PC kernel: 9800000259a8c000 9800000259a8fb70 000000c000001500 ffffffff81b810d8
2月 01 17:12:56 uos-PC kernel: 0000000000000000 0000000000000000 0000000000000000 98000002526dd018
2月 01 17:12:56 uos-PC kernel: 9800000259a8fe60 ffffffff80230184 ffffffff82050000 ffffffff81b810d8
2月 01 17:12:56 uos-PC kernel: ...
2月 01 17:12:56 uos-PC kernel: Call Trace:
2月 01 17:12:56 uos-PC kernel: [<ffffffff80230184>] show_stack+0x104/0x1c0
2月 01 17:12:56 uos-PC kernel: [<ffffffff81b810d8>] dump_stack+0x1d8/0x240
2月 01 17:12:56 uos-PC kernel: [<ffffffffc0029ec4>] loongson_connector_pwm_set+0x44/0x140 [loongson]
2月 01 17:12:56 uos-PC kernel: [<ffffffffc0029858>] loongson_connector_backlight_update+0x98/0x140 [loongson]
2月 01 17:12:56 uos-PC kernel: [<ffffffff80fef5ec>] backlight_device_set_brightness+0xac/0x200
2月 01 17:12:56 uos-PC kernel: [<ffffffff80ff06c0>] brightness_store+0x40/0xc0
2月 01 17:12:56 uos-PC kernel: [<ffffffff8071fdcc>] kernfs_fop_write+0x14c/0x440
2月 01 17:12:56 uos-PC kernel: [<ffffffff805f07e8>] __vfs_write+0x28/0x340
2月 01 17:12:56 uos-PC kernel: [<ffffffff805f0e10>] vfs_write+0xd0/0x3c0
2月 01 17:12:56 uos-PC kernel: [<ffffffff805f1498>] ksys_write+0x98/0x280
2月 01 17:12:56 uos-PC kernel: [<ffffffff8023ea94>] syscall_common+0x34/0xa4
系统设置查看: dde-daemon/session/power/power_save_plan.go
内核调试节点如下:
root@uos-PC:/sys/class/backlight/loongson-gpu# ls -l
总用量 0
-r--r--r-- 1 root root 16384 2月 1 17:08 actual_brightness
-rw-r--r-- 1 root root 16384 2月 1 17:08 bl_power
-rw-r--r-- 1 root root 16384 2月 1 17:08 brightness
lrwxrwxrwx 1 root root 0 2月 1 17:08 device -> ../../card0-eDP-1
-r--r--r-- 1 root root 16384 2月 1 17:08 max_brightness
drwxr-xr-x 2 root root 0 2月 1 17:08 power
lrwxrwxrwx 1 root root 0 2月 1 17:08 subsystem -> ../../../../../../../class/backlight
-r--r--r-- 1 root root 16384 2月 1 17:08 type
-rw-r--r-- 1 root root 16384 2月 1 17:08 uevent
[email protected] 服务 在系统启动早期加载显示器的背光亮度, 并在系统关机时保存显示器的背光亮度。背光亮度的设置保存在磁盘上的 /var/lib/systemd/backlight/ 目录中。 在加载背光亮度的过程中,如果 udev 属性 ID_BACKLIGHT_CLAMP 不等于"no",那么实际亮度将会被设为 绝对值 1 与相对值 5% 两者中的较亮者。 当内核允许用户空间设置不会导致显示器关机的亮度值时, 这个限制将会被解除。
内核引导选项:
systemd-backlight 能够识别 下列内核引导选项:
systemd.restore_state=
接受一个布尔值。默认值是 “1”(yes) 。若设为 “0”(no) 则表示 不在系统启动时恢复显示器的背光亮度。不过,依然会在系统关机时 保存显示器的背光亮度。
systemd-backlight 它会去读取 ACPI 提供的背光设备,为每个设备创建对应的 systemd 服务。 比如/sys/class/backlight 下包含 loongson-gpu目录, 创建systemd-backlight@backlight:loongson-gpu.service。
root@uos-PC:/home/uos# systemctl status systemd-backlight@backlight:loongson-gpu.service
● systemd-backlight@backlight:loongson-gpu.service - Load/Save Screen Backlight Brightness of backlight:loongson-gpu
Loaded: loaded (/lib/systemd/system/systemd-backlight@.service; static; vendor preset: enabled)
Active: active (exited) since Mon 2021-02-01 15:38:55 CST; 10min ago
Docs: man:systemd-backlight@.service(8)
Process: 1719 ExecStart=/lib/systemd/systemd-backlight load backlight:loongson-gpu (code=exited, status=0/SUCCESS)
Main PID: 1719 (code=exited, status=0/SUCCESS)
2月 01 15:38:54 uos-PC systemd[1]: Starting Load/Save Screen Backlight Brightness of backlight:loongson-gpu...
2月 01 15:38:55 uos-PC systemd[1]: Started Load/Save Screen Backlight Brightness of backlight:loongson-gpu.
文件定义/lib/systemd/system/[email protected]:
[Unit]
Description=Load/Save Screen Backlight Brightness of %i
Documentation=man:systemd-backlight@.service(8)
DefaultDependencies=no
Conflicts=shutdown.target
After=systemd-remount-fs.service
Before=sysinit.target shutdown.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/lib/systemd/systemd-backlight load %i
ExecStop=/lib/systemd/systemd-backlight save %i
TimeoutSec=90s
StateDirectory=systemd/backlight
4.log
Feb 1 14:25:47 uos-PC kernel: [ 2.351617] [drm] loongson kernel modesetting enabled.
Feb 1 14:25:47 uos-PC kernel: [ 2.351668] loongson-drm 0000:00:06.1: Device 14:7a06, irq 92
Feb 1 14:25:47 uos-PC kernel: [ 2.352132] [drm] Get vbios from bios Success.
Feb 1 14:25:47 uos-PC kernel: [ 2.352135] [drm] Loongson vbios version 0.2
Feb 1 14:25:47 uos-PC kernel: [ 2.352137] [drm] encoder0(OS) i2c:6
Feb 1 14:25:47 uos-PC kernel: [ 2.352139] [drm] connector0:
Feb 1 14:25:47 uos-PC kernel: [ 2.352142] [drm] Use the VBIOS built-in EDID information
Feb 1 14:25:47 uos-PC kernel: [ 2.352145] [drm] Detect:SHOW
Feb 1 14:25:47 uos-PC kernel: [ 2.352147] [drm] encoder1(BIOS) i2c:7
Feb 1 14:25:47 uos-PC kernel: [ 2.352149] [drm] connector1:
Feb 1 14:25:47 uos-PC kernel: [ 2.352151] [drm] Reading EDID via built-in I2C
Feb 1 14:25:47 uos-PC kernel: [ 2.352152] [drm] Detect:POLL
Feb 1 14:25:47 uos-PC kernel: [ 2.352172] [drm] io: 0x90000e0010010000, mmio: 0x45450000, size: 0x10000
refer to
- https://harttle.land/2019/10/13/archlinux-backlight.html
- https://wiki.archlinux.org/index.php/backlight