Linux内核4.14版本——drm框架分析(2)——connector分析

本文详细分析了LinuxDRM框架中的drm_connector结构体,包括其各个字段的作用,如head、base.properties、encoder_ids、encoder等,并介绍了drm_connector_init函数的执行流程,如创建base对象、初始化属性、附加encoder等。此外,还提到了相关的API,如drm_mode_getconnector用于获取connector信息。
摘要由CSDN通过智能技术生成

目录

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 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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值