DRM全解析 —— connector详解(1)

本文参考以下博文:

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

特此致谢!

1. 简介

connector是指encoder和panel之间交互的接口部分,更通俗地说就是连接物理设备的连接器,对应于物理连接器 (如VGA、DVI、FPD-Link、HDMI、DisplayPort、S-Video等) 。connector并不是指物理线,在DRM中,connector 是一个抽象的数据结构,代表连接的显示设备,从connector中可以得到当前物理连接的输出设备相关的信息。

connector通常和encoder驱动绑定在一起。

connector在系统中的位置和作用如下所示:

2. 核心结构

在Linux内核的DRM中,connector对应的核心结构体为:struct drm_connector。该结构体在include/drm/drm_connector.h中定义,代码如下(Linux内核版本:6.1):

cpp 复制代码
/**
 * struct drm_connector - central DRM connector control structure
 *
 * 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 {
	/** @dev: parent DRM device */
	struct drm_device *dev;
	/** @kdev: kernel device for sysfs attributes */
	struct device *kdev;
	/** @attr: sysfs attributes */
	struct device_attribute *attr;
	/**
	 * @fwnode: associated fwnode supplied by platform firmware
	 *
	 * Drivers can set this to associate a fwnode with a connector, drivers
	 * are expected to get a reference on the fwnode when setting this.
	 * drm_connector_cleanup() will call fwnode_handle_put() on this.
	 */
	struct fwnode_handle *fwnode;

	/**
	 * @head:
	 *
	 * List of all connectors on a @dev, linked from
	 * &drm_mode_config.connector_list. Protected by
	 * &drm_mode_config.connector_list_lock, but please only use
	 * &drm_connector_list_iter to walk this list.
	 */
	struct list_head head;

	/**
	 * @global_connector_list_entry:
	 *
	 * Connector entry in the global connector-list, used by
	 * drm_connector_find_by_fwnode().
	 */
	struct list_head global_connector_list_entry;

	/** @base: base KMS object */
	struct drm_mode_object base;

	/** @name: human readable name, can be overwritten by the driver */
	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;

	/**
	 * @connector_type:
	 * one of the DRM_MODE_CONNECTOR_<foo> types from drm_mode.h
	 */
	int connector_type;
	/** @connector_type_id: index into connector type enum */
	int connector_type_id;
	/**
	 * @interlace_allowed:
	 * Can this connector handle interlaced modes? Only used by
	 * drm_helper_probe_single_connector_modes() for mode filtering.
	 */
	bool interlace_allowed;
	/**
	 * @doublescan_allowed:
	 * Can this connector handle doublescan? Only used by
	 * drm_helper_probe_single_connector_modes() for mode filtering.
	 */
	bool doublescan_allowed;
	/**
	 * @stereo_allowed:
	 * Can this connector handle stereo modes? Only used by
	 * drm_helper_probe_single_connector_modes() for mode filtering.
	 */
	bool stereo_allowed;

	/**
	 * @ycbcr_420_allowed : This bool indicates if this connector is
	 * capable of handling YCBCR 420 output. While parsing the EDID
	 * blocks it's very helpful to know if the source is capable of
	 * handling YCBCR 420 outputs.
	 */
	bool ycbcr_420_allowed;

	/**
	 * @registration_state: Is this connector initializing, exposed
	 * (registered) with userspace, or unregistered?
	 *
	 * Protected by @mutex.
	 */
	enum drm_connector_registration_state registration_state;

	/**
	 * @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;

	/** @funcs: connector control functions */
	const struct drm_connector_funcs *funcs;

	/**
	 * @edid_blob_ptr: DRM property containing EDID if present. Protected by
	 * &drm_mode_config.mutex. This should be updated only by calling
	 * drm_connector_update_edid_property().
	 */
	struct drm_property_blob *edid_blob_ptr;

	/** @properties: property tracking for this connector */
	struct drm_object_properties properties;

	/**
	 * @scaling_mode_property: Optional atomic property to control the
	 * upscaling. See drm_connector_attach_content_protection_property().
	 */
	struct drm_property *scaling_mode_property;

	/**
	 * @vrr_capable_property: Optional property to help userspace
	 * query hardware support for variable refresh rate on a connector.
	 * connector. Drivers can add the property to a connector by
	 * calling drm_connector_attach_vrr_capable_property().
	 *
	 * This should be updated only by calling
	 * drm_connector_set_vrr_capable_property().
	 */
	struct drm_property *vrr_capable_property;

	/**
	 * @colorspace_property: Connector property to set the suitable
	 * colorspace supported by the sink.
	 */
	struct drm_property *colorspace_property;

	/**
	 * @path_blob_ptr:
	 *
	 * DRM blob property data for the DP MST path property. This should only
	 * be updated by calling drm_connector_set_path_property().
	 */
	struct drm_property_blob *path_blob_ptr;

	/**
	 * @max_bpc_property: Default connector property for the max bpc to be
	 * driven out of the connector.
	 */
	struct drm_property *max_bpc_property;

	/** @privacy_screen: drm_privacy_screen for this connector, or NULL. */
	struct drm_privacy_screen *privacy_screen;

	/** @privacy_screen_notifier: privacy-screen notifier_block */
	struct notifier_block privacy_screen_notifier;

	/**
	 * @privacy_screen_sw_state_property: Optional atomic property for the
	 * connector to control the integrated privacy screen.
	 */
	struct drm_property *privacy_screen_sw_state_property;

	/**
	 * @privacy_screen_hw_state_property: Optional atomic property for the
	 * connector to report the actual integrated privacy screen state.
	 */
	struct drm_property *privacy_screen_hw_state_property;

#define DRM_CONNECTOR_POLL_HPD (1 << 0)
#define DRM_CONNECTOR_POLL_CONNECT (1 << 1)
#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, without
	 *     causing flickering even when the connector is in use. DACs should
	 *     rarely do this without a lot of testing.
	 *
	 * Set to 0 for connectors that don't support connection status
	 * discovery.
	 */
	uint8_t polled;

	/**
	 * @dpms: Current dpms state. For legacy drivers the
	 * &drm_connector_funcs.dpms callback must update this. For atomic
	 * drivers, this is handled by the core atomic code, and drivers must
	 * only take &drm_crtc_state.active into account.
	 */
	int dpms;

	/** @helper_private: mid-layer private data */
	const struct drm_connector_helper_funcs *helper_private;

	/** @cmdline_mode: mode line parsed from the kernel cmdline for this connector */
	struct drm_cmdline_mode cmdline_mode;
	/** @force: a DRM_FORCE_<foo> state for forced mode sets */
	enum drm_connector_force force;
	/**
	 * @override_edid: has the EDID been overwritten through debugfs for
	 * testing? Do not modify outside of drm_edid_override_set() and
	 * drm_edid_override_reset().
	 */
	bool override_edid;
	/** @epoch_counter: used to detect any other changes in connector, besides status */
	u64 epoch_counter;

	/**
	 * @possible_encoders: Bit mask of encoders that can drive this
	 * connector, drm_encoder_index() determines the index into the bitfield
	 * and the bits are set with drm_connector_attach_encoder().
	 */
	u32 possible_encoders;

	/**
	 * @encoder: Currently bound encoder driving this connector, if any.
	 * Only really meaningful for non-atomic drivers. Atomic drivers should
	 * instead look at &drm_connector_state.best_encoder, and in case they
	 * need the CRTC driving this output, &drm_connector_state.crtc.
	 */
	struct drm_encoder *encoder;

#define MAX_ELD_BYTES	128
	/** @eld: EDID-like data, if present */
	uint8_t eld[MAX_ELD_BYTES];
	/** @latency_present: AV delay info from ELD, if found */
	bool latency_present[2];
	/**
	 * @video_latency: Video latency info from ELD, if found.
	 * [0]: progressive, [1]: interlaced
	 */
	int video_latency[2];
	/**
	 * @audio_latency: audio latency info from ELD, if found
	 * [0]: progressive, [1]: interlaced
	 */
	int audio_latency[2];

	/**
	 * @ddc: associated ddc adapter.
	 * A connector usually has its associated ddc adapter. If a driver uses
	 * this field, then an appropriate symbolic link is created in connector
	 * sysfs directory to make it easy for the user to tell which i2c
	 * adapter is for a particular display.
	 *
	 * The field should be set by calling drm_connector_init_with_ddc().
	 */
	struct i2c_adapter *ddc;

	/**
	 * @null_edid_counter: track sinks that give us all zeros for the EDID.
	 * Needed to workaround some HW bugs where we get all 0s
	 */
	int null_edid_counter;

	/** @bad_edid_counter: track sinks that give us an EDID with invalid checksum */
	unsigned bad_edid_counter;

	/**
	 * @edid_corrupt: Indicates whether the last read EDID was corrupt. Used
	 * in Displayport compliance testing - Displayport Link CTS Core 1.2
	 * rev1.1 4.2.2.6
	 */
	bool edid_corrupt;
	/**
	 * @real_edid_checksum: real edid checksum for corrupted edid block.
	 * Required in Displayport 1.4 compliance testing
	 * rev1.1 4.2.2.6
	 */
	u8 real_edid_checksum;

	/** @debugfs_entry: debugfs directory for this connector */
	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_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. FIXME: Extract into a substruct? */

	/**
	 * @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.
	 *
	 * This should only be updated by calling
	 * drm_connector_set_tile_property().
	 */
	struct drm_property_blob *tile_blob_ptr;

	/** @has_tile: is this connector connected to a tiled monitor */
	bool has_tile;
	/** @tile_group: tile group for the connected monitor */
	struct drm_tile_group *tile_group;
	/** @tile_is_single_monitor: whether the tile is one monitor housing */
	bool tile_is_single_monitor;

	/** @num_h_tile: number of horizontal tiles in the tile group */
	/** @num_v_tile: number of vertical tiles in the tile group */
	uint8_t num_h_tile, num_v_tile;
	/** @tile_h_loc: horizontal location of this tile */
	/** @tile_v_loc: vertical location of this tile */
	uint8_t tile_h_loc, tile_v_loc;
	/** @tile_h_size: horizontal size of this tile. */
	/** @tile_v_size: vertical size of this tile. */
	uint16_t tile_h_size, tile_v_size;

	/**
	 * @free_node:
	 *
	 * List used only by &drm_connector_list_iter to be able to clean up a
	 * connector from any context, in conjunction with
	 * &drm_mode_config.connector_free_work.
	 */
	struct llist_node free_node;

	/** @hdr_sink_metadata: HDR Metadata Information read from sink */
	struct hdr_sink_metadata hdr_sink_metadata;
};

可以看到,这个结构比plane、CRTC和encoder大得多。

3. drm_connector结构释义

(0)总述

cpp 复制代码
/**
 * struct drm_connector - central DRM connector control structure
 *
 * 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 ------ 核心的DRM connector控制结构。

每个connector可以连接到一个或多个CRTC,或者如果它们可以共享CRTC,则可以由另一个connector克隆。每个connector在更宽的显示中也有一个特定的位置(称为"屏幕",尽管它可以跨越多个监视器)。

(1)struct drm_device *dev

cpp 复制代码
    /** @dev: parent DRM device */
	struct drm_device *dev;

父DRM设备。

(2)struct device *kdev

cpp 复制代码
    /** @kdev: kernel device for sysfs attributes */
	struct device *kdev;

sysfs属性的内核设备。

(3)struct device_attribute *attr

cpp 复制代码
    /** @attr: sysfs attributes */
	struct device_attribute *attr;

sysfs属性。

(4)struct fwnode_handle *fwnode

cpp 复制代码
    /**
	 * @fwnode: associated fwnode supplied by platform firmware
	 *
	 * Drivers can set this to associate a fwnode with a connector, drivers
	 * are expected to get a reference on the fwnode when setting this.
	 * drm_connector_cleanup() will call fwnode_handle_put() on this.
	 */
	struct fwnode_handle *fwnode;

由平台固件提供的关联fwnode。

驱动程序可以将此项设置为将fwnode与一个connector相关联,在设置此项时,驱动程序需要在fwnode上获得引用。在如此设置后,drm_connector_cleanup()将调用fwnode_handle_put()。

(5)struct list_head head

cpp 复制代码
    /**
	 * @head:
	 *
	 * List of all connectors on a @dev, linked from
	 * &drm_mode_config.connector_list. Protected by
	 * &drm_mode_config.connector_list_lock, but please only use
	 * &drm_connector_list_iter to walk this list.
	 */
	struct list_head head;

从&drm_mode_config.connecter_List链接的@dev上所有连接器的列表。受&drm_mode_config.connector_list_lock保护,但请仅使用&drm_conconnector_list_iter遍历此链表。

(6)struct list_head global_connector_list_entry

cpp 复制代码
    /**
	 * @global_connector_list_entry:
	 *
	 * Connector entry in the global connector-list, used by
	 * drm_connector_find_by_fwnode().
	 */
	struct list_head global_connector_list_entry;

全局connector-list中的connector条目,由drm_connector_find_by_fwnode()使用。

(7)struct drm_mode_object base

cpp 复制代码
    /** @base: base KMS object */
	struct drm_mode_object base;

基本KMS对象。

(8)char *name

cpp 复制代码
    /** @name: human readable name, can be overwritten by the driver */
	char *name;

人类可读的名称(名字),可以被驱动程序覆盖。

drm_connector结构的其余成员将在下一篇文章中继续深入释义。

相关推荐
DeeplyMind14 天前
AMD KFD的BO设计分析系列3-4:Linux DRM GEM mmap 与 drm_vma_offset_node 机制详解
linux·drm·opengl驱动·drm_gem_object
林政硕(Cohen0415)6 个月前
Linux驱动开发进阶(七)- DRM驱动程序设计
linux·驱动开发·drm
songze_lee8 个月前
openharmony系统移植之显示驱动框架从framebuffer升级为drm(linux-5.10)
linux·openharmony·drm
AlfredZhao9 个月前
永远不要轻易设置Oracle的隐藏参数,哪怕是DRM
oracle·drm·_gc_undo_affinity
芒果黑1 年前
Qt WebEngine播放DRM音视频
drm·qt webengine
炭烤毛蛋2 年前
modetest
drm·framebuffer
wenshizhang2 年前
AMD显卡休眠唤醒流程分析
linux·内核·drm·amdgpu
Android系统攻城狮2 年前
Android12之DRM架构(一)
drm
蓝天居士2 年前
DRM全解析 —— encoder详解(3)
drm