参考内核代码:

Linux-6.1/driver/gpu/drm/radeon

Linux-6.1/driver/gpu/drm/amd

在AMD显卡驱动初始化了GPU之后,第二步便是负责显示器有关(模式设置)的初始化。AMD显卡的驱动程序主要有二种,一种是radeon驱动(radeon目录),另一种是amdgpu驱动(amd目录);它们的模式设置相关代码差异还挺大。

一、Radeon驱动的模式设置

初始化时的首次模式设置

在Radeo显卡初始化时会进行首次模式设置,在radeon_modeset_init中实现:

int radeon_modeset_init(struct radeon_device *rdev)/*初始化类型为struct drm_mode_config的mode_config各个字段*/|--drm_mode_config_init(rdev->ddev);/*模式设置操作函数funcs赋值为radeon_mode_funcs*/|--rdev->ddev->mode_config.funcs = &radeon_mode_funcs;/*将支持分辨率的最大宽和高*/|--rdev->ddev->mode_config.max_width = 8192;|--rdev->ddev->mode_config.max_height = 8192;/*将首选色深设为24*/|--rdev->ddev->mode_config.preferred_depth = 24;/*FrameBuffer的基地址fb_base赋值为显存基地址rdev->mc.aper_base*/|--rdev->ddev->mode_config.fb_base = rdev->mc.aper_base;/*初始化radeon_device内嵌的类型为struct radeon_mode_info的二级数据结构mode_info,该结构是上面drm_mode_config的一些补充,是radeon显卡所特有的信息*/|--radeon_modeset_create_props(rdev);/*根据VBIOS类型来决定走if或else;用于初始化内置I2C总线信息。显卡内置的I2C总线是用来获取显示器EDID信息的(里面包含了显示器所包含的分辨率列表)*/|--radeon_i2c_init(rdev);|--if (rdev->is_atom_bios) radeon_atombios_i2c_init(rdev);else radeon_combios_i2c_init(rdev);/*根据crtc的个数调用radeon_crtc_init(),来完成crtc的初始化*/|--for (i = 0; i < rdev->num_crtc; i++) radeon_crtc_init(rdev->ddev, i);/*ctrc的通用部分的初始化由drm_crtc_init完成,每个ctrc的主操作函数集struct drm_crtc_funcs被赋值为radeon_crtc_funcs*/|--drm_crtc_init(dev, &radeon_crtc->base, &radeon_crtc_funcs);/*对于RS780显卡来说,设置宽高分别为64*/|--radeon_crtc->max_cursor_width = CURSOR_WIDTH;|--radeon_crtc->max_cursor_height = CURSOR_HEIGHT;/*设置了mode_config中光标的最大宽高*/|--dev->mode_config.cursor_width = radeon_crtc->max_cursor_width;|--dev->mode_config.cursor_height = radeon_crtc->max_cursor_height;/*Radeon特有的部分根据VBIOS类型由radeon_atombios_init_crtc或者radeon_legacy_init_crtc完成,它们会将每个ctrc的辅助操作函数集struct drm_crtc_helper_funcs赋值为atombios_helper_funcs或者legacy_helper_funcs*/|--CASE1: radeon_atombios_init_crtc(dev, radeon_crtc);|--drm_crtc_helper_add(&radeon_crtc->base, &atombios_helper_funcs);|--CASE2: radeon_legacy_init_crtc(dev, radeon_crtc);|--drm_crtc_helper_add(&radeon_crtc->base, &legacy_helper_funcs);/*用于根据VBiOS类型,分别初始化encoder和connector,这里仅分析ATOMBIOS的情况*/|--radeon_setup_enc_conn(rdev->ddev);|--radeon_get_atom_connector_info_from_supported_devices_table(dev);/*由radeon_add_atom_encoder来添加每个encoder*/|--for (i = 0; i < max_device; i++) radeon_add_atom_encoder();/*根据不同的radeon_encoder->encoder_id来各自分别执行以下二个函数,encoder的初始化由drm_encoder_init来完成,每个encoder的主操作函数集struct drm_encoder_funcs都被赋值为radeon_atom_enc_funcs,而辅助操作函数集struct drm_encoder_helper_funcs可能是radeon_atom_dig_helper_funcs(数字信号)或者radeon_atom_dac_helper_funcs(模拟信号)*/|--drm_encoder_init();|--drm_encoder_helper_add();/*由radeon_add_atom_connector来添加每个connector*/|--for (i = 0; i < max_device; i++) radeon_add_atom_connector();/*其中每个connector的主操作函数集struct drm_connector_funcs和辅助操作函数集struct drm_connector_helper_funcs根据类型的不一样,分别各自被初始化为不同的值*/|--drm_connector_init_with_ddc();|--drm_connector_helper_add();|-- radeon_link_encoder_connector(dev); /*如果VBIOS采用ATOMBIOS,还会进一步调用radeon_atom_encoder_init对encoder进行初始化*/  |--if (rdev->is_atom_bios) radeon_atom_encoder_init(rdev);|--if (rdev->is_atom_bios) radeon_atom_disp_eng_pll_init(rdev);/*hpd是hotplug detect的缩写,用于探测显示器的热插拔,该函数会设置相关的寄存器饼调用radeon_irq_kms_enable_hpd使能HPD中断。这里需要注意并非所有的显示器都支持HPD,比如VGA显示器就不支持HPD*/|--radeon_hpd_init(rdev);|--(rdev)->asic->hpd.init((rdev));|--r600_hpd_init();|--radeon_irq_kms_enable_hpd(rdev, enable);/*初始化framebuffer,并创建/dev/fbX设备文件(如果只有一个显卡则X=0)*/|--radeon_fbdev_init(rdev);/*将radeon_device::rfbdev::helper(framebuffer帮手)赋值为radeon_fb_helper_funcs*/|--drm_fb_helper_prepare(rdev->ddev, &rfbdev->helper,&radeon_fb_helper_funcs);/*初始化framebuffer帮手的一些字段,其中会调用drm_client_init对DRM framebuffer核内用户(drm_client)进行初始化*/|--drm_fb_helper_init(rdev->ddev, &rfbdev->helper);/*禁用所有未使用的功能,主要指的是没有连接显示器的ctrc和encoder*/|--drm_helper_disable_unused_functions(rdev->ddev);/*非常重要的函数;会探测、创建framebuffer设备并进行首次模式设置*/|--drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel);/*也是处理显示器热插拔的。VGA等类型的显示器不支持HPD中断,因此采用定时器触发的轮询方式来处理。*/|--drm_kms_helper_poll_init(rdev->ddev);/*设置工作队列项output_poll_work的处理函数为output_poll_execute*/|--INIT_DELAYED_WORK(&dev->mode_config.output_poll_work, output_poll_execute);|--dev->mode_config.poll_enabled = true;/*启用轮询,轮询周期为10s*/|--drm_kms_helper_poll_enable(dev);/*创建电源管理有关的sysfs接口*/|--radeon_pm_late_init(rdev);

上面流程中会调用到drm_fb_helper_initial_config()函数,它会探测、创建framebuffer设备并进行首次模式设置。

int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)|--__drm_fb_helper_initial_config_and_unlock(fb_helper, bpp_sel);|--width = dev->mode_config.max_width;|--height = dev->mode_config.max_height;|--drm_client_modeset_probe(&fb_helper->client, width, height);/*用于记录每个ctrc在framebuffer中的偏移(屏幕起始点在整个FB缓冲区中的坐标,同一个crtc上各个connector的输出内容是完全相同的)*/|--struct drm_client_offset *offsets;/*modes[]数组用保存每个connector所期望的显示模式*/|--struct drm_display_mode **modes;/*enable[]数组用于指示每个connector是否启用*/|--bool *enabled;/*首先通过一个循环探测所有显示器的显示模式,实际工作由每个connector的主操作函数集中的fill_modes()回调函数完成,对于Radeon显卡就是drm_helper_probe_single_connector_modes*/|--for (i = 0; i < connector_count; i++)|--connectors[i]->funcs->fill_modes(connectors[i], width, height);|--drm_helper_probe_single_connector_modes();/*调用connector主操作函数集中的detect()回调函数探测显示器是否已连接,具体实现根据connector类型的不同而不同,比如对于VGA显示器就是radeon_vga_detect*/|--connector->funcs->detect(connector, force);|--radeon_vga_detect()/radeon_dvi_detect()/*对于连接了显示器的connector,调用connector辅助操作函数集中的get_modes回调函数探测显示模式,具体实现根据不同connector的不同而不同,对于vga显示器就是radeon_vga_get_modes*/|--connector_funcs->get_modes(connector);|--radeon_vga_get_modes()/radeon_dp_get_modes()/*对于连接了显示器的connector,调用以下函数加入内核启动参数“video=”所指定的显示模式*/|--drm_helper_probe_add_cmdline_mode(connector);/*填充enable[]数组,"启用"基本等同于连接了显示器*/|--drm_client_connectors_enabled(connectors, connector_count, enabled);/*试图获取并使用BIOS所建立的初始显示模式*/|--drm_client_firmware_config();/*drm_client_target_cloned判断是否能够在所有的显示器上使用镜像模式(克隆模式,每个显示器使用相同的显示模式,该显示模式要么是内核启动参数指定,要么是复合1024x768的分辨率),如果可以则modes[]数组被填充为相同的公共显示模式;如果不能使用镜像模式则通过drm_client_target_preferred选择每个显示器的最优显示模式并填充到modes[]数组。*/|--drm_client_target_cloned()/drm_client_target_preferred();/*drm_client_pick_crtcs在多路显示输出(多个ctrc里)选择最优的一路,该函数实现比较复杂,但基本原则是有显示器连接的优先、复合内核启动草书配置的优先、复合最优分辨率的优先*/|--drm_client_pick_crtcs();|--for (i = 0; i < connector_count; i++)|--struct drm_display_mode *mode = modes[i];|--struct drm_crtc *crtc = crtcs[i];|--struct drm_client_offset *offset = &offsets[i];|--modeset->mode = drm_mode_duplicate(dev, mode);|--modeset->connectors[modeset->num_connectors++] = connector;|--modeset->x = offset->x;|--modeset->y = offset->y;/*该函数先初始化一个类型为drm_fb_helper_surface_size的局部变量sizes(包括显示平面的宽高、显示帧的宽高和色深等信息),然后探测并创建一个系统级的framebuffer。*/|--drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);|--struct drm_fb_helper_surface_size sizes/*探测和创建framebuffer由FB辅助函数集中的fb_probe回调函数负责,具体到radeon显卡就是radeonfb_create*/|--(*fb_helper->funcs->fb_probe)(fb_helper, &sizes);|--radeonfb_create()/*接下来完成一些framebuffer相关的赋值操作,然后注册刚刚创建的framebuffer设备(/dev/fbX)。注册framebuffer设备和首次模式设置是由register_framebuffer完成的。这里需要注意sizes的内部信息是通过各个crtc上的显示器特征汇总而来的,因此当内核启动时如果没有连接任何显示器,则显示平面和显示帧分辨率都会设置成1024x768*/|--drm_setup_crtcs_fb(fb_helper);|--drm_client_for_each_modeset(modeset, client);|--modeset->fb = fb_helper->fb;|--info = fb_helper->fbdev;/**/|--register_framebuffer(info);

radeonfb_create()和register_framebuffer()是drm_fb_helper_initial_config()调用的二个很重要的函数,接下来先看radeonfb_create()

1)radeonfb_create()

int radeonfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes)/*在显存vram里分配一块BO,用来作为帧缓存framebuffer, BO的大小取决于mode_cmd中的宽度width(显示平面宽度,即所有ctrc期望分辨率的最大宽度)、高度height(显示平面高度,即所有ctrc期望分辨率的最大高度)和包括色深信息在内的pixel_format*/|--radeonfb_create_pinned_object(rfbdev, &mode_cmd, &gobj);/*分配一个类型为struct fb_info的FB描述信息info*/|--drm_fb_helper_alloc_fbi(helper);/*初始化radeon_fbdev中的各个成员;其子函数drm_framebuffer_init会初始化一个通用的framebuffer,类型为struct drm_framebuffer;并加入到drm_mode_config的framebuffer列表(drm_device::mode_config::fb_list)。此处创建的是永久存在的系统级framebuffer,后续每次创建的用户级framebuffer都会调用drm_framebuffer_init()往列表里添加,每次删除旧的用户级framebuffer都会调用drm_framebuffer_cleanup()从列表里删除*/|--radeon_framebuffer_init(rdev->ddev, &rfbdev->fb, &mode_cmd, gobj);|--drm_framebuffer_init(dev, fb, &radeon_fb_funcs);|--list_add(&fb->head, &dev->mode_config.fb_list);/*帧缓存的起始物理地址*/|--info->fix.smem_start = rdev->mc.aper_base + tmp;/*帧缓存的起始虚拟地址*/|--info->screen_base = rbo->kptr;|--drm_fb_helper_fill_info(info, &rfbdev->helper, sizes);/*填充FB描述信息中的固定参数*/|--drm_fb_helper_fill_fix(info, fb->pitches[0],fb->format->is_color_indexed);/*填充FB描述信息中的可变参数(比如帧缓存framebuffer的宽度xres_virtual和高度yres_virtual,来自sizes的surface_width和surface_height; 显示内容区域的宽度xres和高度yres, 来自sizes的fb_width和fb_height)*/|--drm_fb_helper_fill_var(info, fb_helper,sizes->fb_width, sizes->fb_height);

2)register_framebuffer()

接下来再看radeonfb_create()

int register_framebuffer(struct fb_info *fb_info)|--do_register_framebuffer(fb_info);/*创建/dev/fbX设备*/|--fb_info->dev = device_create(fb_class, fb_info->device,MKDEV(FB_MAJOR, i), NULL, "fb%d", i);/*创建sysfs下的控制接口*/|--fb_init_device(fb_info);/*将fb_var_screeninfo转换成FB显示模式fb_videomode*/|--fb_var_to_videomode(&mode, &fb_info->var);/*添加这个显示模式*/|--fb_add_videomode(&mode, &fb_info->modelist);/*在大多数情况下radeon显卡是系统的首选,因此注册的这个fb设备就是就是主fb设备(通常就是/dev/fb0),那么fbcon_fb_registered经过层层调用最后会调到fb_con接口体的con_startup()函数指针和con_init()函数指针。*/|--fbcon_fb_registered(fb_info);|--do_fb_registered(info);|--fbcon_select_primary(info);|--do_fbcon_takeover(1);|--do_take_over_console(&fb_con, first_fb_vc, last_fb_vc,fbcon_is_default);|--do_register_con_driver(csw, first, last);|--do_bind_con_driver(csw, first, last, deflt);|--csw->con_startup();/*进行一些打开设备文件、设置字体之类的准备工作*/|--fbcon_startup();|--visual_init(vc, i, 0);|--vc->vc_sw->con_init(vc, init);/*调用radeonfb_ops的fs_set_par()函数指针执行进行首次模式设置。其具体实现是drm_fb_helper_set_par*/|--fbcon_init();|--info->fbops->fb_set_par(info)|--drm_fb_helper_set_par()       

drm_fb_helper_set_par()是register_framebuffer中一个很重要的函数,其调用关系如下所示。

其中的drm_crtc_helper_set_config()是drm_fb_helper_set_par()流程中重要的函数。该函数非常复杂,但也有规可循。首先要记住,该函数的操作对象是一个crtc,而一个framebuffer可以连接多个crtc(多路输出)。采用镜像模式显示时,每个crtc对于framebuffer中的同一块区域;采用扩展模式显示时,每个crtc对应frambuffer中不同区域。输入参数set中的x和y就是本crtc在framebuffer中的相对位置。

函数中有二个非常重要的局部变量,mode_changed和fb_changed。其中前者的含义是"crtc显示模式发生改变";后者的含义是"crtc在framebuffer中相对位置发生改变。当mode_changed为真时(包括二者都为真)需要执行一次"完全模式设置";而仅当fb_changed为真时,可以仅执行一次"基本模式设置";二者均为假时,无需任何操作。该行函数的大部分逻辑都是在处理这二个变量的取值,距离如下:

1)如果该crtc所关联的framebuffer发生改变,那么不管是该crtc之前未关联framebuffer或之后不再关联framebuffer,mode_changed均为真;如果像素格式有变mode_changed同样为真;其他情况下fb_changed为真。

2)如果该crtc在framebuffer中的相对位置发生改变,则fb_changed为真。

3)如果分辨率或者刷新率(时钟)发生改变,mode_changed为真。

4)如果该crtc上的最优connector发生改变,或者最优connecotor没有启用,则mode_changed为真。

5)如果fb_changed为真,但是该crtc没有提供基本模式设置的方法,则视同为mode_changed为真。

int drm_fb_helper_set_par(struct fb_info *info)|--__drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force);/*从LINUX3.19开始,DRM子系统引入了原子KMS(Atomic KMS),其基本概念就是把模式设置所需要完成的各项工作按”全或无“的原子方式一次性完成,避免画面分裂现象。DRM核心中的原子KMS在Linux4.2中已经完善,但目前三大桌面显卡供应商中只有intel完整地实现了原子KMS,Nvidia和AMD还在使用旧的API。*/|--drm_client_modeset_commit_locked(&fb_helper->client);/*如果实现了原子KMS执行如下函数*/|--CASE A: drm_client_modeset_commit_atomic(client, true, false);/*如果没有实现原子KMS执行如下函数,它会调用若干个子函数*/|--CASE B: drm_client_modeset_commit_legacy(client);|--drm_client_for_each_modeset(mode_set, client);/*在每个ctrc上都会执行以下操作*/|--crtc->funcs->cursor_set2()/crtc->funcs->cursor_set()|--drm_mode_set_config_internal(mode_set);|--__drm_mode_set_config_internal(set, NULL);|--crtc->funcs->set_config(set, ctx);|--radeon_crtc_set_config()|--drm_crtc_helper_set_config(set, ctx);/*基本模式设置, 由以下回调完成,具体到radeon显卡和ATOMBIOS就是atombios_crtc_set_base*/;*/|--crtc_funcs->mode_set_base();|--atombios_crtc_set_base();/*完全模式设置*/|--drm_crtc_helper_set_mode();

完全模式设置时由drm_crtc_helper_set_mode()来完成,由于基本模式设置是完全模式设置的子集,因此着重分析完全模式设置。其函数的原型如下所示:

/*函数参数指定了目标crtc,而mode指定了目标显示模式。*/
bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,struct drm_display_mode *mode, int x, int y, struct drm_framebuffer *old_fb)
{/*局部变量adjusted_mode是以mode为模板,根据具体的硬件时序要求进行微调后的硬件目标显示模式;mode和adjust_mode会赋值给目标crtc的mode字段(新显示模式)和hwmode字段(新硬件显示模式)。局部变量saved_mode和saved_hwmode保存目标crtc的原显示模式和原硬件显示模式,以便模式设置失败时恢复过来。*/struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode;
}

该函数的比较长,但是主干实现比较清晰,基本就是如下几点(旧版本内核还有Bridge相关的部分,但是实际上只有原子KMS的API支持Bridge,因此像比较新的内核版本的Legacy API里面已经没有了Bridge API相关部分):

1)对于crtc上的每个encoder,执行encoder辅助函数集的mode_fixup()回调函数,对于使用ATOMBIOS的Radeon显卡一般就是radeon_atom_mode_fixup();负责在encoder层面对硬件目标显示模式adjust_mode进行必要的修正(主要是时序信息)。

2)对于crtc本身,执行crtc辅助函数集的mode_fixup()回调函数,对于使用ATOMBIOS的Radeon显卡一般就是atombios_crtc_mode_fixup();负责在crtc层面对硬件目标显示模式adjust_mode进行必要的修正(主要是时序信息)。

3)对于crtc上的每个encoder,执行encoder辅助函数集中的prepare()回调函数,对于使用ATOMBIOS的Radeon显卡一般就是radeon_atom_encoder_prepare();负责一些encoder层面的准备工作,比如一些必要的加锁。

4)对于crtc本身,执行crtc辅助函数集的prepare()回调函数,对于使用ATOMBIOS的radeon显卡就是atombios_crtc_prepare(),负责一些crtc层面的准备工作,比如一些必要的加锁和关闭供电(DPMS OFF)。

5)对于crtc本身,执行crtc辅助函数集中的mode_set()回调函数,对于使用ATOMBIOS的radeon显卡就是atombios_crtc_mode_set(),负责crtc层面的核心模式设置工作。这个步骤最重要:

/*大部分的代码都是根据新的显示模式调整各个功能部件的时钟/时序,当然还有atombios_crtc_set_base
和radeon_cursor_reset。前者调整framebuffer(相当于一次基本模式设置,在RS780上具体功能由
avivo_crtc_do_set_base实现,涉及大量显卡特定寄存器的设置);后者重置光标*/
int atombios_crtc_mode_set(struct drm_crtc *crtc,struct drm_display_mode *mode,struct drm_display_mode *adjusted_mode,int x, int y, struct drm_framebuffer *old_fb)
{struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);struct drm_device *dev = crtc->dev;struct radeon_device *rdev = dev->dev_private;struct radeon_encoder *radeon_encoder =to_radeon_encoder(radeon_crtc->encoder);bool is_tvcv = false;if (radeon_encoder->active_device &(ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT))is_tvcv = true;if (!radeon_crtc->adjusted_clock)return -EINVAL;atombios_crtc_set_pll(crtc, adjusted_mode);if (ASIC_IS_DCE4(rdev))atombios_set_crtc_dtd_timing(crtc, adjusted_mode);else if (ASIC_IS_AVIVO(rdev)) {if (is_tvcv)atombios_crtc_set_timing(crtc, adjusted_mode);elseatombios_set_crtc_dtd_timing(crtc, adjusted_mode);} else {atombios_crtc_set_timing(crtc, adjusted_mode);if (radeon_crtc->crtc_id == 0)atombios_set_crtc_dtd_timing(crtc, adjusted_mode);radeon_legacy_atom_fixup(crtc);}atombios_crtc_set_base(crtc, x, y, old_fb);atombios_overscan_setup(crtc, mode, adjusted_mode);atombios_scaler_setup(crtc);radeon_cursor_reset(crtc);/* update the hw version fpr dpm */radeon_crtc->hw_mode = *adjusted_mode;return 0;
}

6)对于crtc上的每个encoder,执行encoder辅助函数集中mode_set()回调函数,对于使用ATOMBIOS的radeon显卡一般就是radeon_atom_encoder_mode_set(),负责encoder层面的核心模式设置工作。

7)对于crtc本身,执行crtc辅助函数集中的commit()回调函数,对于使用ATOMBIOS的radeon显卡就是atombios_crtc_commit(),负责一些crtc层面的交付操作,比如一些必要的解锁和重新供电(DPMS ON)。

8)对于crtc上的每个encoder,执行encoder辅助函数集中的commit()回调函数,对于使用ATOMBIOS的radeon显卡一般就是radeon_atom_encoder_commit(),负责一些enocoder层面的交付操作,比如一些iyao的解锁和重新供电(DPMS ON)

3)显卡初始化时首次模式设置的核心调用链总结

radeon_modeset_init()
-> radeon_fbdev_init()
-> drm_fb_helper_initial_config()
-> drm_fb_helper_single_fb_probe()
-> register_framebuffer()
-> do_register_framebuffer()
-> fbcon_fb_registered()
-> do_fbcon_takeover()
-> do_take_over_console()
-> do_bind_con_driver()
-> visual_init()
-> fbcon_init()
-> drm_fb_helper_set_par()
-> drm_fb_helper_restore_fbdev_mode_unlocked()
-> drm_client_modeset_commit_legacy()
-> drm_mode_set_config_internal()
-> __drm_mode_set_config_internal()
-> radeon_crtc_set_config()
-> drm_crtc_helper_set_config()
-> drm_crtc_helper_set_mode()

运行时的模式设置

虽然显卡初始化的时候会进行首次模式设置,但系统在运行过程中同样会做一些模式设置的工作,最典型的一种情况就是切换分辨率。运行时模式设置通过类型为DRM_IOCTL_MODE_SETCRTC的ioctl()系统调用完成,其具体实现函数为drm_mode_setcrtc()。其内部逻辑和初始化时的首次模式设置基本相同。其核心调用链如下:

int drm_mode_setcrtc(struct drm_device *dev, void *data,struct drm_file *file_priv)|--__drm_mode_set_config_internal()|--crtc->funcs->set_config()|--radeon_crtc_set_config()|--drm_crtc_helper_set_config()|--drm_crtc_helper_set_mode()

二、amdgpu驱动的模式设置

初始化时的首次模式设置

待续......

运行时的模式设置

待续......

AMD GPU模式设置(2):初始化和运行时相关推荐

  1. AMD GPU模式设置(1):核心数据结构

    参考内核代码:Linux-6.1/driver/gpu/drm/amd DRM框架 DRM框架包括DRM core和DRM驱动而部分,其中DRM驱动包含GEM(Graphics Execution M ...

  2. Angular开发模式下的编译器和运行时的代码比较

    IDE里的index.html里的app-root: 在浏览器里打开后,能看到app-root下面的几个子节点:app-top-bar和router-outlet, 以及app-product-lis ...

  3. 【嵌入式开发】时钟初始化 ( 时钟相关概念 | 嵌入式时钟体系 | Lock Time | 分频参数设置 | CPU 异步模式设置 | APLL MPLL 时钟频率设置 )

    文章目录 一. 时钟相关概念解析 1. 相关概念术语 ( 1 ) 时钟脉冲信号 ( 概念 : 电压幅度 时间间隔 形成脉冲 | 作用 : 时序逻辑基础 间隔固定 根据脉冲数量可计算出时间 ) ( 2 ...

  4. RK3588 CPU GPU DDR NPU定频和性能模式设置

    RK3588 CPU GPU NPU DDR定频和性能模式设置方法 文章目录 RK3588 CPU GPU NPU DDR定频和性能模式设置方法 查看RK3588 CPU GPU DDR NPU的频率 ...

  5. jvm的类加载和运行时数据区和垃圾回收

    类加载过程 加载(loading) 引导类加载器 扩展类加载器 系统类加载器 1.通过一个类的全限定名获取此类的二进制字节流 2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构 3.在内 ...

  6. 3.内存分配、逃逸分析与栈上分配、直接内存和运行时常量池、基本类型的包装类和常量池、TLAB、可达性分析算法(学习笔记)

    3.JVM内存分配 3.1.内存分配概述 3.2.内存分配–Eden区域 3.3.内存分配–大对象直接进老年代 3.3.1.背景 3.3.2.解析 3.4.内存分配–长期存活的对象进去老年代 3.5. ...

  7. java中判断undefined_Java虚拟机系列一:一文搞懂 JVM 架构和运行时数据区

    前言 之前写博客一直比较随性,主题也很随意,就是想到什么写什么,对什么感兴趣就写什么.虽然写起来无拘无束,自在随意,但也带来了一些问题,每次写完一篇后就要去纠结下一篇到底写什么,看来选择太多也不是好事 ...

  8. AMD GPU任务调度(3) —— fence机制

    文章目录 GPU fence command format EOP event DMA fence 数据结构 dma-fence amdgpu-fence dma_fence_ops amdgpu_f ...

  9. Go 语言的垃圾回收演化历程:垃圾回收和运行时问题

    Google Go 团队的成员 Richard L. Hudson (Rick) 近日在 Go 的官方博客和大家分享了他在2018年6月18日国际内存管理研讨会(ISMM)上发表的主题演讲稿.在过去的 ...

最新文章

  1. AssertValid函数学习
  2. 入门NLP最优解:从项目实践轻松入手!
  3. c语言序列sequence,Sequence
  4. 实现做出html的上标以及下标
  5. 【网络安全】一个堆题inndy_notepad的练习笔记
  6. servlet如何使用session把用户的手机号修改_SpringBoot源码学习系列之嵌入式Servlet容器...
  7. db2导入发生错误显示不是绝对路径_python编程常见错误总结
  8. java 一些容易忽视的小点-数据类型和运算符篇
  9. web电商、商城pc端、商城、购物车、订单、线上支付、web商城、pc商城、登录注册、人工客服、收货地址、现金券、优惠券、礼品卡、团购订单、评价晒单、消息通知、电子产品商城、手机商城、电脑商城
  10. [转] 文件内容查看 cat,less,more,tail,head,sed
  11. 探讨【IGE】的源代码【五】。
  12. 利用google搜索自己的博客
  13. oracle中alter用法,Oraclealter用法
  14. 将源码打包成deb软件包
  15. 【DevOps研发管理方案】一:方案简介
  16. 【C语言】'\0'、'0'、' '、“0”、0详解
  17. u盘chk文件恢复图文教程
  18. 十二星座、超完美解析!
  19. 关于多个ul标签并列时下对齐的问题
  20. 接外包有哪些渠道呢?

热门文章

  1. MySQL存储过程不带参数
  2. 我有故事,你有酒么?----观魔兽有感,记我过去的10年
  3. vue + vue-print-nb 实现打印 以及 样式布局问题
  4. 对不起,腾讯我也拒绝了
  5. 几十行代码实现Java爬虫,结合jsoup爬取网名昵称
  6. 阿里妈妈2018的ESMM(CVR预估)
  7. 寄语:别过于在意他人的眼光
  8. MMoE ESSM PLE对比
  9. 永恒之塔打BOSS的故事
  10. ArcMap中3D符号化显示