This paper mainly analyzes the initialization and configuration of connector, DRM_ The connector structure is as follows:
/** * 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; /** * @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; /** @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; #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? */ 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; };
drm_ The main initialization interface of connector is drm_connector_init
Here we analyze some common parameters.
(1)connector->head
After the connecotr instance is initialized, it is hung on the DRM through the head_ device. mode_ config. connector_ On the list
drm_connector_init list_add_tail(&connector->head, &config->connector_list) config->num_connector++
, note each DRM_ The device has only one mode_config member, so you can use mode_config.connector_list traverses all connector instances of the device, which can also be accessed in the following ways
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);
(2) Connector - > base (drm_mode_object type)
Pass__ drm_mode_object_add to dev - > mode_ config. object_ IDR application 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;
Note that connector - > base ID will be used as the connector when the user interface drmModeGetResource is called_ The ID is returned to the user status, and the subsequent user status can be through the connector_id, call drmModeGetConnector to find drm_connector and get its related parameters
Get connector in user mode_ The ID logic is as follows:
//User state drmModeGetResources drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res) //res.crtc_id_ptr points to all crtcs of the device_ id //Kernel state 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); //Get connector recursively put_user(connector->base.id, connector_id+count); //Copy id to user status drm_connector_list_iter_end(&conn_iter);
User status according to connector_id get DRM_ The logic of the connector is as follows:
//User state drmModeGetConnector() conn.connector_id = connector_id; drmIoctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn) //Kernel state 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) //According to the id and type passed in from the user status (drm_mode_object_connector finds the drm mode obj type) mo = drm_mode_object_find(dev, file_priv, id, DRM_MODE_OBJECT_CONNECTOR); return obj_to_connector(mo)
(3)connector->base.properties
Record the attributes of the connector (such as crtc_id / EDID / DPMS / link status / non desktop / title, etc.). These attributes are first recorded in drm_mode_create_standard_properties are created, initialized and saved in dev - > mode_ Config, as follows:
//With 'CRTC'_ 'ID' attribute as an example drm_mode_config_init drmm_mode_config_init drm_mode_create_standard_properties //Create an attribute named "CRTC_ID" prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC, "CRTC_ID", DRM_MODE_OBJECT_CRTTC); //Create attribute drm_property_create //Save the property in mode_config property_list linked list list_add_tail(&property->head, &dev->mode_config.property_list) //Save to mode_ config. prop_ crtc_ In ID dev->mode_config.prop_crtc_id = prop
In drm_connector_init will attach the initialized attribute to 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) ......
Getting properties
//User state drmModeObjectGetProperties //Via connector_id and DM_MODE_OBJECT_CONNECTOR gets all its properties drmIoctl(fd, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, &properties) //Kernel state DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl,0) //According to obj_id(connector_id), and obj_type(DRM_MODE_OBJECT_CONNECTOR) gets the DRM of the connector_ mode_ Object instance obj = drm_mode_object_find(dev, file_priv, arg->obj_id, arg->obj_type) //Traverse connector - > base Properties and copy them to the user status, //Here, only the id and value of the properties are copied, and no name is copied drm_mode_object_get_properties(obj, file_priv->atomic, arg->props_ptr, arg->prop_values_ptr, ...); //All attribute IDs and value s of the connector are obtained above. If you want to obtain an attribute with a specific name, you also need to do the following: //User state drmModeGetProperty //According to prop_id get property instance object drmIoctl(fd, DRM_IOCTL_MODE_GETPROPERTY, &prop) //Kernel state DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getProperty_ioctl,0) //According to prop_id gets the attribute instance and copies it to the user state
There are three methods to set properties:
1) All properties can be set directly through prop through drmModeObjSetProperty_ Find the prop instance ID and set it
drmModeObjectSetProperty DRM_IOCTL(fd, DRM_IOCTL_MODE_OBJ_SETPROPERTY, &prop)
2) The properties of the connector can be set through drmmodiconnectorsetproperty. This method will first pass through the connector_id found drm_connector instance, and then traverse the connector base. Properties by matching prop_ Find the corresponding prop instance by ID
drmModeConnectorSetProperty DRM_IOCTL(fd, DRM_IOCTL_MODE_SETPROPERTY, &osp)
3) Atomic operation, drmmodeatomicaddroperty. This method is analyzed separately later. Of course, the above two methods also modify attributes through atomic operation in the kernel state
(4)connector->cmdline_mode
It is used to save the display mode set by cmdline. The initialization process is as follows. How does cmdline write the reference document documentation / FB / modedb txt:
drm_connector_init drm_connector_get_cmdline_mode(connector)
cmdline_mode is mainly used to create the framebuffer of fbdev. This logic can be sorted out later. Please refer to the blog:
How does the DRM driver create fb device_ Additional reading notes - CSDN blog_ drm fb
(5)connector->possible_encoders
The encoders that identifies its connection is called drm_ after the connector and encoder initialization of the DRM device. connector_ attach_ Encoder binding.
Through this parameter, you can find the encoder connected by the connector. Through the macro
drm_connector_for_each_possible_encoder
(6)connector->probed_modesĀ
The detected DRM obtained through DDC or BIOS_ display_ Mode type instance mode, through drm_mode_probed_add, add the interface to connector - > probe_ Modes linked list. There are three common calls as follows:
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)
(7)connector->modes
connector valid drm_display_mode type instance mode linked list. There is only one update of the linked list, as follows:
//connector->funcs->fill_ Modes in DRM_ mode_ Call in getconnector //.fill_modes = drm_helper_probe_single_connector_modes //From probe_ The modes in the modes linked list are added to the connector - > modes linked list drm_connector_list_update
The mode instance in the connector - > modes linked list will be displayed in DRM_ mode_ Copy the getconnector to the user state, and the user state creates fb according to the width and height information of the obtained modes
//User state drmModeGetConnector() conn.connector_id = connector_id; drmIoctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn) //Kernel state 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) //According to the id and type passed in from the user status (drm_mode_object_connector finds the drm mode obj type) list_for_each_entry(mode, &connector->modes, head) //The user status is drm_mode_modeinfo type object. The kernel state is drm_display_mode type object //There needs to be a conversion drm_mode_convert_to_umode(&u_mode, mode);
(8)connector->func && connector->helper_private
These two are both function pointers. Together, the helper function implements atomic calls.
Connector - > func in drm_ connector_ When init is initialized, func is some standard helper functions, and its internal will be finally through connector - > helper_ Private calls the interface customized by drm driver
static const struct drm_connector_funcs vkms_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, .destroy = vkms_connector_destroy, .reset = drm_atomic_helper_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; vkms_output_init ret = drm_connector_init(dev, connector, &vkms_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL) connector->funcs =funcs ---->vkms_connector_funcs
connector->helper_ The initialization of private is as follows
static const struct drm_connector_helper_funcs vkms_conn_helper_funcs = { .get_modes = vkms_conn_get_modes, }; vkms_output_init ret = drm_connector_init(dev, connector, &vkms_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL); drm_connector_helper_add(connector, &vkms_conn_helper_funcs); connector->helper_private = funcs-----> vkms_conn_helper_funcs
connector->func && connector->helper_ The usage logic of private is analyzed in the next section