Linux内核4.14版本——drm框架分析(2)——connector分析
目录
1. drm_connector结构体
1.1 struct list_head head
1.2 struct drm_mode_object base
1.3 base.properties
1.4 uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER]
1.5 struct drm_encoder *encoder
1.6 struct list_head probed_modes
1.7 struct list_head modes
1.8 const struct drm_connector_helper_funcs *helper_private
1.9 const struct drm_connector_funcs *funcs
2. connector的相关API
2.1 drm_connector_init
2.1.1 (1)拿到drm_mode_config结构体
2.1.2 (2)创建struct drm_mode_object base结构体
2.1.3 (3)得到properties,后面填充
2.1.4 (4)生成drm_mode_config的connector_ida并作为该connector的index索引
2.1.5 (5)生成该connector的type的connector_ida
2.1.6 (6)初始化一些变量
2.1.7 (7)调用drm_connector_get_cmdline_mode
2.1.8 (8)把改connector放到drm_mode_config的connector_list链表中。
2.1.9 (9)~(12)attach property
2.2 drm_mode_connector_attach_encoder
2.3 drm_connector_register
2.4 drm_connector_helper_add
2.5 drm_mode_getconnector
本文主要分析connector的初始化和配置。
1. drm_connector结构体
/*** struct drm_connector - central DRM connector control structure* @dev: parent DRM device* @kdev: kernel device for sysfs attributes* @attr: sysfs attributes* @head: list management* @base: base KMS object* @name: human readable name, can be overwritten by the driver* @connector_type: one of the DRM_MODE_CONNECTOR_<foo> types from drm_mode.h* @connector_type_id: index into connector type enum* @interlace_allowed: can this connector handle interlaced modes?* @doublescan_allowed: can this connector handle doublescan?* @stereo_allowed: can this connector handle stereo modes?* @funcs: connector control functions* @edid_blob_ptr: DRM property containing EDID if present* @properties: property tracking for this connector* @dpms: current dpms state* @helper_private: mid-layer private data* @cmdline_mode: mode line parsed from the kernel cmdline for this connector* @force: a DRM_FORCE_<foo> state for forced mode sets* @override_edid: has the EDID been overwritten through debugfs for testing?* @encoder_ids: valid encoders for this connector* @encoder: encoder driving this connector, if any* @eld: EDID-like data, if present* @latency_present: AV delay info from ELD, if found* @video_latency: video latency info from ELD, if found* @audio_latency: audio latency info from ELD, if found* @null_edid_counter: track sinks that give us all zeros for the EDID* @bad_edid_counter: track sinks that give us an EDID with invalid checksum* @edid_corrupt: indicates whether the last read EDID was corrupt* @debugfs_entry: debugfs directory for this connector* @has_tile: is this connector connected to a tiled monitor* @tile_group: tile group for the connected monitor* @tile_is_single_monitor: whether the tile is one monitor housing* @num_h_tile: number of horizontal tiles in the tile group* @num_v_tile: number of vertical tiles in the tile group* @tile_h_loc: horizontal location of this tile* @tile_v_loc: vertical location of this tile* @tile_h_size: horizontal size of this tile.* @tile_v_size: vertical size of this tile.* @scaling_mode_property: Optional atomic property to control the upscaling.** Each connector may be connected to one or more CRTCs, or may be clonable by* another connector if they can share a CRTC. Each connector also has a specific* position in the broader display (referred to as a 'screen' though it could* span multiple monitors).*/
struct drm_connector {struct drm_device *dev;struct device *kdev;struct device_attribute *attr;struct list_head head;struct drm_mode_object base;char *name;/*** @mutex: Lock for general connector state, but currently only protects* @registered. Most of the connector state is still protected by* &drm_mode_config.mutex.*/struct mutex mutex;/*** @index: Compacted connector index, which matches the position inside* the mode_config.list for drivers not supporting hot-add/removing. Can* be used as an array index. It is invariant over the lifetime of the* connector.*/unsigned index;int connector_type;int connector_type_id;bool interlace_allowed;bool doublescan_allowed;bool stereo_allowed;/*** @ycbcr_420_allowed : This bool indicates if this connector is* capable of handling YCBCR 420 output. While parsing the EDID* blocks, its very helpful to know, if the source is capable of* handling YCBCR 420 outputs.*/bool ycbcr_420_allowed;/*** @registered: Is this connector exposed (registered) with userspace?* Protected by @mutex.*/bool registered;/*** @modes:* Modes available on this connector (from fill_modes() + user).* Protected by &drm_mode_config.mutex.*/struct list_head modes;/*** @status:* One of the drm_connector_status enums (connected, not, or unknown).* Protected by &drm_mode_config.mutex.*/enum drm_connector_status status;/*** @probed_modes:* These are modes added by probing with DDC or the BIOS, before* filtering is applied. Used by the probe helpers. Protected by* &drm_mode_config.mutex.*/struct list_head probed_modes;/*** @display_info: Display information is filled from EDID information* when a display is detected. For non hot-pluggable displays such as* flat panels in embedded systems, the driver should initialize the* &drm_display_info.width_mm and &drm_display_info.height_mm fields* with the physical size of the display.** Protected by &drm_mode_config.mutex.*/struct drm_display_info display_info;const struct drm_connector_funcs *funcs;struct drm_property_blob *edid_blob_ptr;struct drm_object_properties properties;struct drm_property *scaling_mode_property;/*** @path_blob_ptr:** DRM blob property data for the DP MST path property.*/struct drm_property_blob *path_blob_ptr;/*** @tile_blob_ptr:** DRM blob property data for the tile property (used mostly by DP MST).* This is meant for screens which are driven through separate display* pipelines represented by &drm_crtc, which might not be running with* genlocked clocks. For tiled panels which are genlocked, like* dual-link LVDS or dual-link DSI, the driver should try to not expose* the tiling and virtualize both &drm_crtc and &drm_plane if needed.*/struct drm_property_blob *tile_blob_ptr;/* should we poll this connector for connects and disconnects */
/* hot plug detectable */
#define DRM_CONNECTOR_POLL_HPD (1 << 0)
/* poll for connections */
#define DRM_CONNECTOR_POLL_CONNECT (1 << 1)
/* can cleanly poll for disconnections without flickering the screen */
/* DACs should rarely do this without a lot of testing */
#define DRM_CONNECTOR_POLL_DISCONNECT (1 << 2)/*** @polled:** Connector polling mode, a combination of** DRM_CONNECTOR_POLL_HPD* The connector generates hotplug events and doesn't need to be* periodically polled. The CONNECT and DISCONNECT flags must not* be set together with the HPD flag.** DRM_CONNECTOR_POLL_CONNECT* Periodically poll the connector for connection.** DRM_CONNECTOR_POLL_DISCONNECT* Periodically poll the connector for disconnection.** Set to 0 for connectors that don't support connection status* discovery.*/uint8_t polled;/* requested DPMS state */int dpms;const struct drm_connector_helper_funcs *helper_private;/* forced on connector */struct drm_cmdline_mode cmdline_mode;enum drm_connector_force force;bool override_edid;#define DRM_CONNECTOR_MAX_ENCODER 3uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER];struct drm_encoder *encoder; /* currently active encoder */#define MAX_ELD_BYTES 128/* EDID bits */uint8_t eld[MAX_ELD_BYTES];bool latency_present[2];int video_latency[2]; /* [0]: progressive, [1]: interlaced */int audio_latency[2];int null_edid_counter; /* needed to workaround some HW bugs where we get all 0s */unsigned bad_edid_counter;/* Flag for raw EDID header corruption - used in Displayport* compliance testing - * Displayport Link CTS Core 1.2 rev1.1 4.2.2.6*/bool edid_corrupt;struct dentry *debugfs_entry;/*** @state:** Current atomic state for this connector.** This is protected by @drm_mode_config.connection_mutex. Note that* nonblocking atomic commits access the current connector state without* taking locks. Either by going through the &struct drm_atomic_state* pointers, see for_each_connector_in_state(),* for_each_oldnew_connector_in_state(),* for_each_old_connector_in_state() and* for_each_new_connector_in_state(). Or through careful ordering of* atomic commit operations as implemented in the atomic helpers, see* &struct drm_crtc_commit.*/struct drm_connector_state *state;/* DisplayID bits */bool has_tile;struct drm_tile_group *tile_group;bool tile_is_single_monitor;uint8_t num_h_tile, num_v_tile;uint8_t tile_h_loc, tile_v_loc;uint16_t tile_h_size, tile_v_size;
};
drm_connector的主要初始化接口为drm_connector_init,可以在下面的API中看到,下面我们分析一些参数。
1.1 struct list_head head
connecotr实例初始化完成后,通过head挂在drm_device.mode_config.connector_list上。
drm_connector_initlist_add_tail(&connector->head, &config->connector_list)config->num_connector++
注意每个drm_device设备只有一个mode_config成员,因此可以通过mode_config.connector_list遍历出这个设备的所有connector实例,也可以通过如下方式访问。
drm_connector_list_iter_begin(dev, &conn_iter);
drm_for_each_connector_iter(connector, &conn_iter);// ... use connector to process
drm_connector_list_iter_end(&conn_iter);
1.2 struct drm_mode_object base
通过__drm_mode_object_add向dev->mode_config.object_idr申请id(base.id=id, base.type=DRM_MODE_OBJECT_CONNECTOR)
drm_connector_initret=__drm_mode_object_add(dev, &connector->base,DRM_MODE_OBJECT_CONNECTOR, false, drm_connector_free);ret=idr_alloc(&dev->mode_config.object_idr, register_obj ? obj : NULL,1, 0, GFP_KERNEL);0bj->id = ret;obj->type = obj_typereturn ret;
注意, connector->base.id会在用户态接口drmModeGetResource调用时, 作为connector_id返回给用户态,后续用户态可以通过connector_id,调用drmModeGetConnector找到drm_connector,并获取其相关参数。
用户态获取connector_id逻辑如下:
//用户态
drmModeGetResourcesdrmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res)//res.crtc_id_ptr指向设备的所有crtc_id
//内核态
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, 0)drm_mode_getresourcesdrm_connector_list_iter_begin(dev, &conn_iter);drm_for_each_connector_iter(connector, &conn_iter); //递归获取connectorput_user(connector->base.id, connector_id+count); //拷贝id到用户态drm_connector_list_iter_end(&conn_iter);
用户态根据connector_id获取drm_connector的逻辑如下:
//用户态
drmModeGetConnector()conn.connector_id = connector_id;drmIoctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn)
//内核态
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, 0)drm_mode_getconnectorconnector = drm_connector_lookup(dev, file_priv, out_resp->connector_id)//根据用户态传进来的id, 和type(DRM_MODE_OBJECT_CONNECTOR查找drm mode obj类型)mo = drm_mode_object_find(dev, file_priv, id, DRM_MODE_OBJECT_CONNECTOR);return obj_to_connector(mo)
1.3 base.properties
记录connector的属性(如CRTC_ID/EDID/DPMS/link-status/non-desktop/TITLE等),这些属性首先在drm_mode_create_standard_properties创建初始化并保存在dev->mode_config中,如下:
//以'CRTC_ID'属性为例
drm_mode_config_initdrmm_mode_config_initdrm_mode_create_standard_properties//创建名称为“CRTC_ID”的属性prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC, "CRTC_ID", DRM_MODE_OBJECT_CRTTC);//创建属性drm_property_create//将该property保存在mode_config的property_list链表找list_add_tail(&property->head, &dev->mode_config.property_list)//保存到mode_config.prop_crtc_id中dev->mode_config.prop_crtc_id = prop
在drm_connector_init中会将初始化的属性attach到base.properties
drm_connector_initconnector->base.properties = &connector->propertiesdrm_object_attach_property(&connector->base,config->dpms_property)drm_object_attach_property(&connector->base,config->link_status_property)drm_object_attach_property(&connector->base, config->prop_crtc_id,0)......
properties的获取:
//用户态
drmModeObjectGetProperties//通过connector_id以及DM_MODE_OBJECT_CONNECTOR获取其所有属性drmIoctl(fd, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, &properties)
//内核态DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl,0)//根据obj_id(connector_id),和obj_type(DRM_MODE_OBJECT_CONNECTOR)获取connector的drm_mode_object实例objobj = drm_mode_object_find(dev, file_priv, arg->obj_id, arg->obj_type)//遍历connector->base.properties并拷贝到用户态,//这里仅拷贝了properties的id和value,并没有拷贝名称drm_mode_object_get_properties(obj, file_priv->atomic,arg->props_ptr, arg->prop_values_ptr, ...);//上述获取的是connector的所有属性id、value,如果要获取特定名称的属性,还需要如下操作:
//用户态
drmModeGetProperty//根据prop_id获取属性实例对象drmIoctl(fd, DRM_IOCTL_MODE_GETPROPERTY, &prop)
//内核态DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getProperty_ioctl,0)//根据prop_id获取属性实例并拷贝到用户态
1.4 uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER]
标识其连接的encoders,在drm设备的connector和encoder初始化结束后,调用drm_connector_attach_encoder绑定。
1.5 struct drm_encoder *encoder
当前使用的encoder。
1.6 struct list_head probed_modes
通过DDC或者BIOS等获取的探测出来的drm_display_mode类型实例mode,通过drm_mode_probed_add,接口添加到connector->probed_modes链表,其常用的调用有三种如下:
1. drm_add_modes_noedidmode = drm_mode_duplicate(dev, ptr);drm_mode_probed_add(connector, mode);list_add_tail(&mode->head, &connector->probed_modes);
2. drm_add_edid_modesadd_standard_modes.....
3.drm_helper_probe_add_cmdline_modemode = drm_mode_create_from_cmdline_mode(connector->dev, cmdline_mode);drm_mode_probed_add(connector,mode)
1.7 struct list_head modes
connector有效的drm_display_mode类型实例mode链表,该链表的更新,目前仅找到一处,如下:
connector->funcs->fill_modes在drm_mode_getconnector中调用
.fill_modes = drm_helper_probe_single_connector_modes //从probed_modes链表中的modes 添加到connector->modes链表中drm_connector_list_update
connector->modes链表中的mode实例会在drm_mode_getconnector中拷贝到用户态,用户态根据获取的modes的宽高信息创建fb。
//用户态
drmModeGetConnector()conn.connector_id = connector_id;drmIoctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn)
//内核态
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, 0)drm_mode_getconnectorconnector = drm_connector_lookup(dev, file_priv, out_resp->connector_id)//根据用户态传进来的id, 和type(DRM_MODE_OBJECT_CONNECTOR查找drm mode obj类型)list_for_each_entry(mode, &connector->modes, head)//用户态是drm_mode_modeinfo类型对象, 内核态是drm_display_mode类型对象//这里需要做转换drm_mode_convert_to_umode(&u_mode, mode);
1.8 const struct drm_connector_helper_funcs *helper_private
connector->func在drm_connector_init时初始化, func都是一些标准的helper函数,其内部会最终通过connector->helper_private调用drm驱动自定的接口,以下为例(drivers\gpu\drm\bridge\synopsys\dw-hdmi.c)。
static const struct drm_connector_funcs dw_hdmi_connector_funcs = {.fill_modes = drm_helper_probe_single_connector_modes,.detect = dw_hdmi_connector_detect,.destroy = drm_connector_cleanup,.force = dw_hdmi_connector_force,.reset = drm_atomic_helper_connector_reset,.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};drm_connector_init(bridge->dev, connector, &dw_hdmi_connector_funcs,DRM_MODE_CONNECTOR_HDMIA);
1.9 const struct drm_connector_funcs *funcs
static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {.get_modes = dw_hdmi_connector_get_modes,.best_encoder = drm_atomic_helper_best_encoder,
};drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs);
2. connector的相关API
2.1 drm_connector_init
已下面的调用分析改函数:
drm_connector_init(bridge->dev, connector, &dw_hdmi_connector_funcs,DRM_MODE_CONNECTOR_HDMIA);
/** Connector and encoder types.*/
static struct drm_conn_prop_enum_list drm_connector_enum_list[] = {{ DRM_MODE_CONNECTOR_Unknown, "Unknown" },{ DRM_MODE_CONNECTOR_VGA, "VGA" },{ DRM_MODE_CONNECTOR_DVII, "DVI-I" },{ DRM_MODE_CONNECTOR_DVID, "DVI-D" },{ DRM_MODE_CONNECTOR_DVIA, "DVI-A" },{ DRM_MODE_CONNECTOR_Composite, "Composite" },{ DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" },{ DRM_MODE_CONNECTOR_LVDS, "LVDS" },{ DRM_MODE_CONNECTOR_Component, "Component" },{ DRM_MODE_CONNECTOR_9PinDIN, "DIN" },{ DRM_MODE_CONNECTOR_DisplayPort, "DP" },{ DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },{ DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },{ DRM_MODE_CONNECTOR_TV, "TV" },{ DRM_MODE_CONNECTOR_eDP, "eDP" },{ DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" },{ DRM_MODE_CONNECTOR_DSI, "DSI" },{ DRM_MODE_CONNECTOR_DPI, "DPI" },
};/*** drm_connector_init - Init a preallocated connector* @dev: DRM device* @connector: the connector to init* @funcs: callbacks for this connector* @connector_type: user visible type of the connector** Initialises a preallocated connector. Connectors should be* subclassed as part of driver connector objects.** Returns:* Zero on success, error code on failure.*/
int drm_connector_init(struct drm_device *dev,struct drm_connector *connector,const struct drm_connector_funcs *funcs,int connector_type)
{struct drm_mode_config *config = &dev->mode_config; // (1)int ret;struct ida *connector_ida =&drm_connector_enum_list[connector_type].ida;ret = __drm_mode_object_add(dev, &connector->base, // (2)DRM_MODE_OBJECT_CONNECTOR,false, drm_connector_free);if (ret)return ret;connector->base.properties = &connector->properties; // (3)connector->dev = dev;connector->funcs = funcs;ret = ida_simple_get(&config->connector_ida, 0, 0, GFP_KERNEL); // (4)if (ret < 0)goto out_put;connector->index = ret;ret = 0;connector->connector_type = connector_type; // (5) connector->connector_type_id =ida_simple_get(connector_ida, 1, 0, GFP_KERNEL);if (connector->connector_type_id < 0) {ret = connector->connector_type_id;goto out_put_id;}connector->name = // (6) kasprintf(GFP_KERNEL, "%s-%d",drm_connector_enum_list[connector_type].name,connector->connector_type_id);if (!connector->name) {ret = -ENOMEM;goto out_put_type_id;}INIT_LIST_HEAD(&connector->probed_modes);INIT_LIST_HEAD(&connector->modes);mutex_init(&connector->mutex);connector->edid_blob_ptr = NULL;connector->status = connector_status_unknown;drm_connector_get_cmdline_mode(connector); // (7) /* We should add connectors at the end to avoid upsetting the connector* index too much. */spin_lock_irq(&config->connector_list_lock); // (8)list_add_tail(&connector->head, &config->connector_list);config->num_connector++;spin_unlock_irq(&config->connector_list_lock);if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL) // (9)drm_object_attach_property(&connector->base,config->edid_property,0);drm_object_attach_property(&connector->base, // (10)config->dpms_property, 0);drm_object_attach_property(&connector->base, // (11)config->link_status_property,0);if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { // (12)drm_object_attach_property(&connector->base, config->prop_crtc_id, 0);}connector->debugfs_entry = NULL;
out_put_type_id:if (ret)ida_simple_remove(connector_ida, connector->connector_type_id);
out_put_id:if (ret)ida_simple_remove(&config->connector_ida, connector->index);
out_put:if (ret)drm_mode_object_unregister(dev, &connector->base);return ret;
}
2.1.1 (1)拿到drm_mode_config结构体
struct drm_mode_config *config = &dev->mode_config; // (1)int ret;struct ida *connector_ida =&drm_connector_enum_list[connector_type].ida;
(1.1)一个drm设备只有一个struct drm_mode_config结构体,里面配置了一些参数,后面会讲。
(1.2)拿到struct ida *connector_ida,后面会分配。
2.1.2 (2)创建struct drm_mode_object base结构体
ret = __drm_mode_object_add(dev, &connector->base, // (2)DRM_MODE_OBJECT_CONNECTOR,false, drm_connector_free);
(2.1)生成一个类型为DRM_MODE_OBJECT_CONNECTOR的struct drm_mode_object结构体,这个在1.2节中已经提到过。
2.1.3 (3)得到properties,后面填充
connector->base.properties = &connector->properties; // (3)connector->dev = dev;connector->funcs = funcs;
2.1.4 (4)生成drm_mode_config的connector_ida并作为该connector的index索引
ret = ida_simple_get(&config->connector_ida, 0, 0, GFP_KERNEL); // (4)if (ret < 0)goto out_put;connector->index = ret;ret = 0;
2.1.5 (5)生成该connector的type的connector_ida
connector->connector_type = connector_type; // (5) connector->connector_type_id =ida_simple_get(connector_ida, 1, 0, GFP_KERNEL);if (connector->connector_type_id < 0) {ret = connector->connector_type_id;goto out_put_id;}
生成该connector的type的connector_ida,后面该connector命名会用到。
2.1.6 (6)初始化一些变量
connector->name = // (6) kasprintf(GFP_KERNEL, "%s-%d",drm_connector_enum_list[connector_type].name,connector->connector_type_id);if (!connector->name) {ret = -ENOMEM;goto out_put_type_id;}INIT_LIST_HEAD(&connector->probed_modes);INIT_LIST_HEAD(&connector->modes);mutex_init(&connector->mutex);connector->edid_blob_ptr = NULL;connector->status = connector_status_unknown;
2.1.7 (7)调用drm_connector_get_cmdline_mode
drm_connector_get_cmdline_mode(connector); // (7)
2.1.8 (8)把改connector放到drm_mode_config的connector_list链表中。
spin_lock_irq(&config->connector_list_lock); // (8)list_add_tail(&connector->head, &config->connector_list);config->num_connector++;spin_unlock_irq(&config->connector_list_lock);
2.1.9 (9)~(12)attach property
if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL) // (9)drm_object_attach_property(&connector->base,config->edid_property,0);drm_object_attach_property(&connector->base, // (10)config->dpms_property, 0);drm_object_attach_property(&connector->base, // (11)config->link_status_property,0);if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { // (12)drm_object_attach_property(&connector->base, config->prop_crtc_id, 0);
2.2 drm_mode_connector_attach_encoder
/*** drm_mode_connector_attach_encoder - attach a connector to an encoder* @connector: connector to attach* @encoder: encoder to attach @connector to** This function links up a connector to an encoder. Note that the routing* restrictions between encoders and crtcs are exposed to userspace through the* possible_clones and possible_crtcs bitmasks.** Returns:* Zero on success, negative errno on failure.*/
int drm_mode_connector_attach_encoder(struct drm_connector *connector,struct drm_encoder *encoder)
{int i;/** In the past, drivers have attempted to model the static association* of connector to encoder in simple connector/encoder devices using a* direct assignment of connector->encoder = encoder. This connection* is a logical one and the responsibility of the core, so drivers are* expected not to mess with this.** Note that the error return should've been enough here, but a large* majority of drivers ignores the return value, so add in a big WARN* to get people's attention.*/if (WARN_ON(connector->encoder))return -EINVAL;for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {if (connector->encoder_ids[i] == 0) {connector->encoder_ids[i] = encoder->base.id;return 0;}}return -ENOMEM;
}
该connector attach一个encoder。
2.3 drm_connector_register
/*** drm_connector_register - register a connector* @connector: the connector to register** Register userspace interfaces for a connector** Returns:* Zero on success, error code on failure.*/
int drm_connector_register(struct drm_connector *connector)
{int ret = 0;if (!connector->dev->registered)return 0;mutex_lock(&connector->mutex);if (connector->registered)goto unlock;ret = drm_sysfs_connector_add(connector);if (ret)goto unlock;ret = drm_debugfs_connector_add(connector);if (ret) {goto err_sysfs;}if (connector->funcs->late_register) {ret = connector->funcs->late_register(connector);if (ret)goto err_debugfs;}drm_mode_object_register(connector->dev, &connector->base);connector->registered = true;goto unlock;err_debugfs:drm_debugfs_connector_remove(connector);
err_sysfs:drm_sysfs_connector_remove(connector);
unlock:mutex_unlock(&connector->mutex);return ret;
}
2.4 drm_connector_helper_add
/*** drm_connector_helper_add - sets the helper vtable for a connector* @connector: DRM connector* @funcs: helper vtable to set for @connector*/
static inline void drm_connector_helper_add(struct drm_connector *connector,const struct drm_connector_helper_funcs *funcs)
{connector->helper_private = funcs;
}
2.5 drm_mode_getconnector
int drm_mode_getconnector(struct drm_device *dev, void *data,struct drm_file *file_priv)
{struct drm_mode_get_connector *out_resp = data;struct drm_connector *connector;struct drm_encoder *encoder;struct drm_display_mode *mode;int mode_count = 0;int encoders_count = 0;int ret = 0;int copied = 0;int i;struct drm_mode_modeinfo u_mode;struct drm_mode_modeinfo __user *mode_ptr;uint32_t __user *encoder_ptr;if (!drm_core_check_feature(dev, DRIVER_MODESET))return -EINVAL;memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));connector = drm_connector_lookup(dev, out_resp->connector_id); // (1)if (!connector)return -ENOENT;for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) // (2)if (connector->encoder_ids[i] != 0)encoders_count++;if ((out_resp->count_encoders >= encoders_count) && encoders_count) { copied = 0;encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr);for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {if (connector->encoder_ids[i] != 0) {if (put_user(connector->encoder_ids[i], // (3)encoder_ptr + copied)) {ret = -EFAULT;goto out;}copied++;}}}out_resp->count_encoders = encoders_count; // (4)out_resp->connector_id = connector->base.id;out_resp->connector_type = connector->connector_type;out_resp->connector_type_id = connector->connector_type_id;mutex_lock(&dev->mode_config.mutex);if (out_resp->count_modes == 0) {connector->funcs->fill_modes(connector,dev->mode_config.max_width,dev->mode_config.max_height);}out_resp->mm_width = connector->display_info.width_mm; // (5)out_resp->mm_height = connector->display_info.height_mm;out_resp->subpixel = connector->display_info.subpixel_order;out_resp->connection = connector->status;/* delayed so we get modes regardless of pre-fill_modes state */list_for_each_entry(mode, &connector->modes, head)if (drm_mode_expose_to_userspace(mode, file_priv))mode_count++;/** This ioctl is called twice, once to determine how much space is* needed, and the 2nd time to fill it.*/if ((out_resp->count_modes >= mode_count) && mode_count) { // (6)copied = 0;mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr;list_for_each_entry(mode, &connector->modes, head) {if (!drm_mode_expose_to_userspace(mode, file_priv))continue;drm_mode_convert_to_umode(&u_mode, mode);if (copy_to_user(mode_ptr + copied,&u_mode, sizeof(u_mode))) {ret = -EFAULT;mutex_unlock(&dev->mode_config.mutex);goto out;}copied++;}}out_resp->count_modes = mode_count;mutex_unlock(&dev->mode_config.mutex);drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);encoder = drm_connector_get_encoder(connector); // (7)if (encoder)out_resp->encoder_id = encoder->base.id;elseout_resp->encoder_id = 0;/* Only grab properties after probing, to make sure EDID and other* properties reflect the latest status. */ // (8)ret = drm_mode_object_get_properties(&connector->base, file_priv->atomic,(uint32_t __user *)(unsigned long)(out_resp->props_ptr),(uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr),&out_resp->count_props);drm_modeset_unlock(&dev->mode_config.connection_mutex);out:drm_connector_put(connector);return ret;
}
该函数一般用户态使用ioctl调用。
//用户态
drmModeGetConnector()conn.connector_id = connector_id;drmIoctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn)
//内核态
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, 0)drm_mode_getconnector
(1)通过out_resp->connector_id找到需要的connector。
(2)计算该connector支持多少个encoder。
(3)把该connector支持的encoder拷贝到用户态。
(4~5)赋值一些用户态结构体一些变量
(6)mode暂时不知道干啥的
(7)得到该connector目前正在使用的encoder。
(8)得到properties。