目录
1.2 struct drm_mode_object base
1.4 uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER]
1.5 struct drm_encoder *encoder
1.6 struct list_head probed_modes
1.8 const struct drm_connector_helper_funcs *helper_private
1.9 const struct drm_connector_funcs *funcs
2.1.2 (2)创建struct drm_mode_object base结构体
2.1.4 (4)生成drm_mode_config的connector_ida并作为该connector的index索引
2.1.5 (5)生成该connector的type的connector_ida
2.1.7 (7)调用drm_connector_get_cmdline_mode
2.1.8 (8)把改connector放到drm_mode_config的connector_list链表中。
2.2 drm_mode_connector_attach_encoder
本文主要分析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 3
uint32_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_init
list_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_init
ret=__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_type
return ret;
注意, connector->base.id会在用户态接口drmModeGetResource调用时, 作为connector_id返回给用户态,后续用户态可以通过connector_id,调用drmModeGetConnector找到drm_connector,并获取其相关参数。
用户态获取connector_id逻辑如下:
//用户态
drmModeGetResources
drmIoctl(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_getresources
drm_connector_list_iter_begin(dev, &conn_iter);
drm_for_each_connector_iter(connector, &conn_iter); //递归获取connector
put_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_getconnector
connector = 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_init
drmm_mode_config_init
drm_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_init
connector->base.properties = &connector->properties
drm_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实例obj
obj = 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_noedid
mode = drm_mode_duplicate(dev, ptr);
drm_mode_probed_add(connector, mode);
list_add_tail(&mode->head, &connector->probed_modes);
2. drm_add_edid_modes
add_standard_modes
.....
3.drm_helper_probe_add_cmdline_mode
mode = 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_getconnector
connector = 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;
else
out_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。
本文详细分析了LinuxDRM框架中的drm_connector结构体,包括其各个字段的作用,如head、base.properties、encoder_ids、encoder等,并介绍了drm_connector_init函数的执行流程,如创建base对象、初始化属性、附加encoder等。此外,还提到了相关的API,如drm_mode_getconnector用于获取connector信息。
248

被折叠的 条评论
为什么被折叠?



