注:文章都是通过阅读各位前辈总结的资料 Android 11.0 && Linux(Kernel 4.19)Rockchip平台源码、加上自己的思考分析总结出来的,其中难免有理解不对的地方,欢迎大家批评指正。文章为个人学习、研究、欣赏之用,图文内容整理自互联网,如有侵权,请联系删除(◕‿◕),转载请注明出处(©Rockchip ©Android @Linux 版权所有),谢谢。

(==文章基于 Kernel-4.19==)&&(==文章基于 Android 11.0==)

【zhoujinjian.com博客原图链接】

【开发板 RockPi4bPlusV1.6】

【开发板 RockPi4bPlusV1.6 Android 11.0 && Linux(Kernel 4.19)源码链接】:(repo init -u https://github.com/radxa/manifests.git -b Android11_Radxa_rk11.1 -m rockchip-r-release.xml)

【开发板 RockPi4bPlusV1.6 Android 11.0 && Linux(Kernel 4.19)编译指南】

正是由于前人(各位大神)的分析和总结,帮助我节约了大量的时间和精力,特别感谢,由于不喜欢图片水印,去除了水印,敬请谅解!!!

本文转自Rockchip RK3399 - DRM encoder、bridge、connector基础知识 ,如有侵权,请联系删除。


开发板 :ROCK Pi 4B+开发板
eMMC32GB
LPDDR44GB
显示屏 :7英寸HDMI接口显示屏
u-boot2017.09
linux4.19


一、encoder数据结构

1.1 struct drm_encoder

linux内核使用struct drm_encoder来表示一个编码器,将crtc输出的图像信号转换成一定格式的数字信号,如HDMIDisplayPortMIPI等;定义在include/drm/drm_encoder.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/**
* struct drm_encoder - central DRM encoder structure
* @dev: parent DRM device
* @head: list management
* @base: base KMS object
* @name: human readable name, can be overwritten by the driver
* @funcs: control functions, can be NULL for simple managed encoders
* @helper_private: mid-layer private data
*
* CRTCs drive pixels to encoders, which convert them into signals
* appropriate for a given connector or set of connectors.
*/
struct drm_encoder {
struct drm_device *dev;
struct list_head head;

struct drm_mode_object base;
char *name;
/**
* @encoder_type:
*
* One of the DRM_MODE_ENCODER_<foo> types in drm_mode.h. The following
* encoder types are defined thus far:
*
* - DRM_MODE_ENCODER_DAC for VGA and analog on DVI-I/DVI-A.
*
* - DRM_MODE_ENCODER_TMDS for DVI, HDMI and (embedded) DisplayPort.
*
* - DRM_MODE_ENCODER_LVDS for display panels, or in general any panel
* with a proprietary parallel connector.
*
* - DRM_MODE_ENCODER_TVDAC for TV output (Composite, S-Video,
* Component, SCART).
*
* - DRM_MODE_ENCODER_VIRTUAL for virtual machine displays
*
* - DRM_MODE_ENCODER_DSI for panels connected using the DSI serial bus.
*
* - DRM_MODE_ENCODER_DPI for panels connected using the DPI parallel
* bus.
*
* - DRM_MODE_ENCODER_DPMST for special fake encoders used to allow
* mutliple DP MST streams to share one physical encoder.
*/
int encoder_type;

/**
* @index: Position inside the mode_config.list, can be used as an array
* index. It is invariant over the lifetime of the encoder.
*/
unsigned index;
/**
* @possible_crtcs: Bitmask of potential CRTC bindings, using
* drm_crtc_index() as the index into the bitfield. The driver must set
* the bits for all &drm_crtc objects this encoder can be connected to
* before calling drm_dev_register().
*
* You will get a WARN if you get this wrong in the driver.
*
* Note that since CRTC objects can't be hotplugged the assigned indices
* are stable and hence known before registering all objects.
*/
uint32_t possible_crtcs;

/**
* @possible_clones: Bitmask of potential sibling encoders for cloning,
* using drm_encoder_index() as the index into the bitfield. The driver
* must set the bits for all &drm_encoder objects which can clone a
* &drm_crtc together with this encoder before calling
* drm_dev_register(). Drivers should set the bit representing the
* encoder itself, too. Cloning bits should be set such that when two
* encoders can be used in a cloned configuration, they both should have
* each another bits set.
*
* As an exception to the above rule if the driver doesn't implement
* any cloning it can leave @possible_clones set to 0. The core will
* automagically fix this up by setting the bit for the encoder itself.
*
* You will get a WARN if you get this wrong in the driver.
*
* Note that since encoder objects can't be hotplugged the assigned indices
* are stable and hence known before registering all objects.
*/
uint32_t possible_clones;

/**
* @crtc: Currently bound CRTC, only really meaningful for non-atomic
* drivers. Atomic drivers should instead check
* &drm_connector_state.crtc.
*/
struct drm_crtc *crtc;

/**
* @bridge_chain: Bridges attached to this encoder. Drivers shall not
* access this field directly.
*/
struct list_head bridge_chain;

const struct drm_encoder_funcs *funcs;
const struct drm_encoder_helper_funcs *helper_private;
};

1.2 操作函数

1.2.1 struct drm_encoder_funcs

struct drm_encoder_funcs用于描述encoder的控制函数,定义在include/drm/drm_encoder.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* struct drm_encoder_funcs - encoder controls
*
* Encoders sit between CRTCs and connectors.
*/
struct drm_encoder_funcs {
/**
* @reset:
*
* Reset encoder hardware and software state to off. This function isn't
* called by the core directly, only through drm_mode_config_reset().
* It's not a helper hook only for historical reasons.
*/
void (*reset)(struct drm_encoder *encoder);

/**
* @destroy:
*
* Clean up encoder resources. This is only called at driver unload time
* through drm_mode_config_cleanup() since an encoder cannot be
* hotplugged in DRM.
*/
void (*destroy)(struct drm_encoder *encoder);

/**
* @late_register:
*
* This optional hook can be used to register additional userspace
* interfaces attached to the encoder like debugfs interfaces.
* It is called late in the driver load sequence from drm_dev_register().
* Everything added from this callback should be unregistered in
* the early_unregister callback.
*
* Returns:
*
* 0 on success, or a negative error code on failure.
*/
int (*late_register)(struct drm_encoder *encoder);

/**
* @early_unregister:
*
* This optional hook should be used to unregister the additional
* userspace interfaces attached to the encoder from
* @late_register. It is called from drm_dev_unregister(),
* early in the driver unload sequence to disable userspace access
* before data structures are torndown.
*/
void (*early_unregister)(struct drm_encoder *encoder);
};
1.2.2 struct drm_encoder_helper_funcs

struct drm_encoder_helper_funcs定义了一些常用的encoder辅助操作函数,定义在include/drm/drm_modeset_helper_vtables.h

Hidden Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
/**
* struct drm_encoder_helper_funcs - helper operations for encoders
*
* These hooks are used by the legacy CRTC helpers, the transitional plane
* helpers and the new atomic modesetting helpers.
*/
struct drm_encoder_helper_funcs {
/**
* @dpms:
*
* Callback to control power levels on the encoder. If the mode passed in
* is unsupported, the provider must use the next lowest power level.
* This is used by the legacy encoder helpers to implement DPMS
* functionality in drm_helper_connector_dpms().
*
* This callback is also used to disable an encoder by calling it with
* DRM_MODE_DPMS_OFF if the @disable hook isn't used.
*
* This callback is used by the legacy CRTC helpers. Atomic helpers
* also support using this hook for enabling and disabling an encoder to
* facilitate transitions to atomic, but it is deprecated. Instead
* @enable and @disable should be used.
*/
void (*dpms)(struct drm_encoder *encoder, int mode);

/**
* @mode_valid:
*
* This callback is used to check if a specific mode is valid in this
* encoder. This should be implemented if the encoder has some sort
* of restriction in the modes it can display. For example, a given
* encoder may be responsible to set a clock value. If the clock can
* not produce all the values for the available modes then this callback
* can be used to restrict the number of modes to only the ones that
* can be displayed.
*
* This hook is used by the probe helpers to filter the mode list in
* drm_helper_probe_single_connector_modes(), and it is used by the
* atomic helpers to validate modes supplied by userspace in
* drm_atomic_helper_check_modeset().
*
* This function is optional.
*
* NOTE:
*
* Since this function is both called from the check phase of an atomic
* commit, and the mode validation in the probe paths it is not allowed
* to look at anything else but the passed-in mode, and validate it
* against configuration-invariant hardward constraints. Any further
* limits which depend upon the configuration can only be checked in
* @mode_fixup or @atomic_check.
*
* RETURNS:
*
* drm_mode_status Enum
*/
enum drm_mode_status (*mode_valid)(struct drm_encoder *crtc,
const struct drm_display_mode *mode);

/**
* @mode_fixup:
*
* This callback is used to validate and adjust a mode. The parameter
* mode is the display mode that should be fed to the next element in
* the display chain, either the final &drm_connector or a &drm_bridge.
* The parameter adjusted_mode is the input mode the encoder requires. It
* can be modified by this callback and does not need to match mode. See
* also &drm_crtc_state.adjusted_mode for more details.
*
* This function is used by both legacy CRTC helpers and atomic helpers.
* This hook is optional.
*
* NOTE:
*
* This function is called in the check phase of atomic modesets, which
* can be aborted for any reason (including on userspace's request to
* just check whether a configuration would be possible). Atomic drivers
* MUST NOT touch any persistent state (hardware or software) or data
* structures except the passed in adjusted_mode parameter.
*
* This is in contrast to the legacy CRTC helpers where this was
* allowed.
*
* Atomic drivers which need to inspect and adjust more state should
* instead use the @atomic_check callback. If @atomic_check is used,
* this hook isn't called since @atomic_check allows a strict superset
* of the functionality of @mode_fixup.
*
* Also beware that userspace can request its own custom modes, neither
* core nor helpers filter modes to the list of probe modes reported by
* the GETCONNECTOR IOCTL and stored in &drm_connector.modes. To ensure
* that modes are filtered consistently put any encoder constraints and
* limits checks into @mode_valid.
*
* RETURNS:
*
* True if an acceptable configuration is possible, false if the modeset
* operation should be rejected.
*/
bool (*mode_fixup)(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);

/**
* @prepare:
*
* This callback should prepare the encoder for a subsequent modeset,
* which in practice means the driver should disable the encoder if it
* is running. Most drivers ended up implementing this by calling their
* @dpms hook with DRM_MODE_DPMS_OFF.
*
* This callback is used by the legacy CRTC helpers. Atomic helpers
* also support using this hook for disabling an encoder to facilitate
* transitions to atomic, but it is deprecated. Instead @disable should
* be used.
*/
void (*prepare)(struct drm_encoder *encoder);
/**
* @commit:
*
* This callback should commit the new mode on the encoder after a modeset,
* which in practice means the driver should enable the encoder. Most
* drivers ended up implementing this by calling their @dpms hook with
* DRM_MODE_DPMS_ON.
*
* This callback is used by the legacy CRTC helpers. Atomic helpers
* also support using this hook for enabling an encoder to facilitate
* transitions to atomic, but it is deprecated. Instead @enable should
* be used.
*/
void (*commit)(struct drm_encoder *encoder);

/**
* @mode_set:
*
* This callback is used to update the display mode of an encoder.
*
* Note that the display pipe is completely off when this function is
* called. Drivers which need hardware to be running before they program
* the new display mode (because they implement runtime PM) should not
* use this hook, because the helper library calls it only once and not
* every time the display pipeline is suspend using either DPMS or the
* new "ACTIVE" property. Such drivers should instead move all their
* encoder setup into the @enable callback.
*
* This callback is used both by the legacy CRTC helpers and the atomic
* modeset helpers. It is optional in the atomic helpers.
*
* NOTE:
*
* If the driver uses the atomic modeset helpers and needs to inspect
* the connector state or connector display info during mode setting,
* @atomic_mode_set can be used instead.
*/
void (*mode_set)(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);

/**
* @atomic_mode_set:
*
* This callback is used to update the display mode of an encoder.
*
* Note that the display pipe is completely off when this function is
* called. Drivers which need hardware to be running before they program
* the new display mode (because they implement runtime PM) should not
* use this hook, because the helper library calls it only once and not
* every time the display pipeline is suspended using either DPMS or the
* new "ACTIVE" property. Such drivers should instead move all their
* encoder setup into the @enable callback.
*
* This callback is used by the atomic modeset helpers in place of the
* @mode_set callback, if set by the driver. It is optional and should
* be used instead of @mode_set if the driver needs to inspect the
* connector state or display info, since there is no direct way to
* go from the encoder to the current connector.
*/
void (*atomic_mode_set)(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state);
/**
* @detect:
*
* This callback can be used by drivers who want to do detection on the
* encoder object instead of in connector functions.
*
* It is not used by any helper and therefore has purely driver-specific
* semantics. New drivers shouldn't use this and instead just implement
* their own private callbacks.
*
* FIXME:
*
* This should just be converted into a pile of driver vfuncs.
* Currently radeon, amdgpu and nouveau are using it.
*/
enum drm_connector_status (*detect)(struct drm_encoder *encoder,
struct drm_connector *connector);

/**
* @atomic_disable:
*
* This callback should be used to disable the encoder. With the atomic
* drivers it is called before this encoder's CRTC has been shut off
* using their own &drm_crtc_helper_funcs.atomic_disable hook. If that
* sequence is too simple drivers can just add their own driver private
* encoder hooks and call them from CRTC's callback by looping over all
* encoders connected to it using for_each_encoder_on_crtc().
*
* This callback is a variant of @disable that provides the atomic state
* to the driver. If @atomic_disable is implemented, @disable is not
* called by the helpers.
*
* This hook is only used by atomic helpers. Atomic drivers don't need
* to implement it if there's no need to disable anything at the encoder
* level. To ensure that runtime PM handling (using either DPMS or the
* new "ACTIVE" property) works @atomic_disable must be the inverse of
* @atomic_enable.
*/
void (*atomic_disable)(struct drm_encoder *encoder,
struct drm_atomic_state *state);

/**
* @atomic_enable:
*
* This callback should be used to enable the encoder. It is called
* after this encoder's CRTC has been enabled using their own
* &drm_crtc_helper_funcs.atomic_enable hook. If that sequence is
* too simple drivers can just add their own driver private encoder
* hooks and call them from CRTC's callback by looping over all encoders
* connected to it using for_each_encoder_on_crtc().
*
* This callback is a variant of @enable that provides the atomic state
* to the driver. If @atomic_enable is implemented, @enable is not
* called by the helpers.
*
* This hook is only used by atomic helpers, it is the opposite of
* @atomic_disable. Atomic drivers don't need to implement it if there's
* no need to enable anything at the encoder level. To ensure that
* runtime PM handling works @atomic_enable must be the inverse of
* @atomic_disable.
*/
void (*atomic_enable)(struct drm_encoder *encoder,
struct drm_atomic_state *state);
/**
* @disable:
*
* This callback should be used to disable the encoder. With the atomic
* drivers it is called before this encoder's CRTC has been shut off
* using their own &drm_crtc_helper_funcs.disable hook. If that
* sequence is too simple drivers can just add their own driver private
* encoder hooks and call them from CRTC's callback by looping over all
* encoders connected to it using for_each_encoder_on_crtc().
*
* This hook is used both by legacy CRTC helpers and atomic helpers.
* Atomic drivers don't need to implement it if there's no need to
* disable anything at the encoder level. To ensure that runtime PM
* handling (using either DPMS or the new "ACTIVE" property) works
* @disable must be the inverse of @enable for atomic drivers.
*
* For atomic drivers also consider @atomic_disable and save yourself
* from having to read the NOTE below!
*
* NOTE:
*
* With legacy CRTC helpers there's a big semantic difference between
* @disable and other hooks (like @prepare or @dpms) used to shut down a
* encoder: @disable is only called when also logically disabling the
* display pipeline and needs to release any resources acquired in
* @mode_set (like shared PLLs, or again release pinned framebuffers).
*
* Therefore @disable must be the inverse of @mode_set plus @commit for
* drivers still using legacy CRTC helpers, which is different from the
* rules under atomic.
*/
void (*disable)(struct drm_encoder *encoder);

/**
* @enable:
*
* This callback should be used to enable the encoder. With the atomic
* drivers it is called after this encoder's CRTC has been enabled using
* their own &drm_crtc_helper_funcs.enable hook. If that sequence is
* too simple drivers can just add their own driver private encoder
* hooks and call them from CRTC's callback by looping over all encoders
* connected to it using for_each_encoder_on_crtc().
*
* This hook is only used by atomic helpers, it is the opposite of
* @disable. Atomic drivers don't need to implement it if there's no
* need to enable anything at the encoder level. To ensure that
* runtime PM handling (using either DPMS or the new "ACTIVE" property)
* works @enable must be the inverse of @disable for atomic drivers.
*/
void (*enable)(struct drm_encoder *encoder);

/**
* @atomic_check:
*
* This callback is used to validate encoder state for atomic drivers.
* Since the encoder is the object connecting the CRTC and connector it
* gets passed both states, to be able to validate interactions and
* update the CRTC to match what the encoder needs for the requested
* connector.
*
* Since this provides a strict superset of the functionality of
* @mode_fixup (the requested and adjusted modes are both available
* through the passed in &struct drm_crtc_state) @mode_fixup is not
* called when @atomic_check is implemented.
*
* This function is used by the atomic helpers, but it is optional.
*
* NOTE:
*
* This function is called in the check phase of an atomic update. The
* driver is not allowed to change anything outside of the free-standing
* state objects passed-in or assembled in the overall &drm_atomic_state
* update tracking structure.
*
* Also beware that userspace can request its own custom modes, neither
* core nor helpers filter modes to the list of probe modes reported by
* the GETCONNECTOR IOCTL and stored in &drm_connector.modes. To ensure
* that modes are filtered consistently put any encoder constraints and
* limits checks into @mode_valid.
*
* RETURNS:
*
* 0 on success, -EINVAL if the state or the transition can't be
* supported, -ENOMEM on memory allocation failure and -EDEADLK if an
* attempt to obtain another state object ran into a &drm_modeset_lock
* deadlock.
*/
int (*atomic_check)(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state);
};

二、bridge数据结构

2.1 struct drm_bridge

linux内核使用struct drm_bridge来表示一个桥接设备,一般用于注册encoder后面另外再接的转换芯片;定义在include/drm/drm_bridge.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/**
* struct drm_bridge - central DRM bridge control structure
*/
struct drm_bridge {
/** @base: inherit from &drm_private_object */
struct drm_private_obj base;
/** @dev: DRM device this bridge belongs to */
struct drm_device *dev;
/** @encoder: encoder to which this bridge is connected */
struct drm_encoder *encoder;
/** @chain_node: used to form a bridge chain */
struct list_head chain_node;
#ifdef CONFIG_OF
/** @of_node: device node pointer to the bridge */
struct device_node *of_node;
#endif
/** @list: to keep track of all added bridges */
struct list_head list;
/**
* @timings:
*
* the timing specification for the bridge, if any (may be NULL)
*/
const struct drm_bridge_timings *timings;
/** @funcs: control functions */
const struct drm_bridge_funcs *funcs;
/** @driver_private: pointer to the bridge driver's internal context */
void *driver_private;
/** @ops: bitmask of operations supported by the bridge */
enum drm_bridge_ops ops;
/**
* @type: Type of the connection at the bridge output
* (DRM_MODE_CONNECTOR_*). For bridges at the end of this chain this
* identifies the type of connected display.
*/
int type;
/**
* @interlace_allowed: Indicate that the bridge can handle interlaced
* modes.
*/
bool interlace_allowed;
/**
* @pre_enable_prev_first: The bridge requires that the prev
* bridge @pre_enable function is called before its @pre_enable,
* and conversely for post_disable. This is most frequently a
* requirement for DSI devices which need the host to be initialised
* before the peripheral.
*/
bool pre_enable_prev_first;
/**
* @ddc: Associated I2C adapter for DDC access, if any.
*/
struct i2c_adapter *ddc;
/** private: */
/**
* @hpd_mutex: Protects the @hpd_cb and @hpd_data fields.
*/
struct mutex hpd_mutex;
/**
* @hpd_cb: Hot plug detection callback, registered with
* drm_bridge_hpd_enable().
*/
void (*hpd_cb)(void *data, enum drm_connector_status status);
/**
* @hpd_data: Private data passed to the Hot plug detection callback
* @hpd_cb.
*/
void *hpd_data;
};

2.2struct drm_bridge_funcs

struct drm_bridge_funcs用于描述bridge的控制函数,定义在include/drm/drm_bridge.h

Hidden Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
/**
* struct drm_bridge_funcs - drm_bridge control functions
*/
struct drm_bridge_funcs {
/**
* @attach:
*
* This callback is invoked whenever our bridge is being attached to a
* &drm_encoder. The flags argument tunes the behaviour of the attach
* operation (see DRM_BRIDGE_ATTACH_*).
*
* The @attach callback is optional.
*
* RETURNS:
*
* Zero on success, error code on failure.
*/
int (*attach)(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags);

/**
* @detach:
*
* This callback is invoked whenever our bridge is being detached from a
* &drm_encoder.
*
* The @detach callback is optional.
*/
void (*detach)(struct drm_bridge *bridge);

/**
* @mode_valid:
*
* This callback is used to check if a specific mode is valid in this
* bridge. This should be implemented if the bridge has some sort of
* restriction in the modes it can display. For example, a given bridge
* may be responsible to set a clock value. If the clock can not
* produce all the values for the available modes then this callback
* can be used to restrict the number of modes to only the ones that
* can be displayed.
*
* This hook is used by the probe helpers to filter the mode list in
* drm_helper_probe_single_connector_modes(), and it is used by the
* atomic helpers to validate modes supplied by userspace in
* drm_atomic_helper_check_modeset().
*
* The @mode_valid callback is optional.
*
* NOTE:
*
* Since this function is both called from the check phase of an atomic
* commit, and the mode validation in the probe paths it is not allowed
* to look at anything else but the passed-in mode, and validate it
* against configuration-invariant hardward constraints. Any further
* limits which depend upon the configuration can only be checked in
* @mode_fixup.
*
* RETURNS:
*
* drm_mode_status Enum
*/
enum drm_mode_status (*mode_valid)(struct drm_bridge *bridge,
const struct drm_display_info *info,
const struct drm_display_mode *mode);
/**
* @mode_fixup:
*
* This callback is used to validate and adjust a mode. The parameter
* mode is the display mode that should be fed to the next element in
* the display chain, either the final &drm_connector or the next
* &drm_bridge. The parameter adjusted_mode is the input mode the bridge
* requires. It can be modified by this callback and does not need to
* match mode. See also &drm_crtc_state.adjusted_mode for more details.
*
* This is the only hook that allows a bridge to reject a modeset. If
* this function passes all other callbacks must succeed for this
* configuration.
*
* The mode_fixup callback is optional. &drm_bridge_funcs.mode_fixup()
* is not called when &drm_bridge_funcs.atomic_check() is implemented,
* so only one of them should be provided.
*
* NOTE:
*
* This function is called in the check phase of atomic modesets, which
* can be aborted for any reason (including on userspace's request to
* just check whether a configuration would be possible). Drivers MUST
* NOT touch any persistent state (hardware or software) or data
* structures except the passed in @state parameter.
*
* Also beware that userspace can request its own custom modes, neither
* core nor helpers filter modes to the list of probe modes reported by
* the GETCONNECTOR IOCTL and stored in &drm_connector.modes. To ensure
* that modes are filtered consistently put any bridge constraints and
* limits checks into @mode_valid.
*
* RETURNS:
*
* True if an acceptable configuration is possible, false if the modeset
* operation should be rejected.
*/
bool (*mode_fixup)(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
/**
* @disable:
*
* This callback should disable the bridge. It is called right before
* the preceding element in the display pipe is disabled. If the
* preceding element is a bridge this means it's called before that
* bridge's @disable vfunc. If the preceding element is a &drm_encoder
* it's called right before the &drm_encoder_helper_funcs.disable,
* &drm_encoder_helper_funcs.prepare or &drm_encoder_helper_funcs.dpms
* hook.
*
* The bridge can assume that the display pipe (i.e. clocks and timing
* signals) feeding it is still running when this callback is called.
*
* The @disable callback is optional.
*
* NOTE:
*
* This is deprecated, do not use!
* New drivers shall use &drm_bridge_funcs.atomic_disable.
*/
void (*disable)(struct drm_bridge *bridge);

/**
* @post_disable:
*
* This callback should disable the bridge. It is called right after the
* preceding element in the display pipe is disabled. If the preceding
* element is a bridge this means it's called after that bridge's
* @post_disable function. If the preceding element is a &drm_encoder
* it's called right after the encoder's
* &drm_encoder_helper_funcs.disable, &drm_encoder_helper_funcs.prepare
* or &drm_encoder_helper_funcs.dpms hook.
*
* The bridge must assume that the display pipe (i.e. clocks and timing
* singals) feeding it is no longer running when this callback is
* called.
*
* The @post_disable callback is optional.
*
* NOTE:
*
* This is deprecated, do not use!
* New drivers shall use &drm_bridge_funcs.atomic_post_disable.
*/
void (*post_disable)(struct drm_bridge *bridge);

/**
* @mode_set:
*
* This callback should set the given mode on the bridge. It is called
* after the @mode_set callback for the preceding element in the display
* pipeline has been called already. If the bridge is the first element
* then this would be &drm_encoder_helper_funcs.mode_set. The display
* pipe (i.e. clocks and timing signals) is off when this function is
* called.
*
* The adjusted_mode parameter is the mode output by the CRTC for the
* first bridge in the chain. It can be different from the mode
* parameter that contains the desired mode for the connector at the end
* of the bridges chain, for instance when the first bridge in the chain
* performs scaling. The adjusted mode is mostly useful for the first
* bridge in the chain and is likely irrelevant for the other bridges.
*
* For atomic drivers the adjusted_mode is the mode stored in
* &drm_crtc_state.adjusted_mode.
*
* NOTE:
*
* This is deprecated, do not use!
* New drivers shall set their mode in the
* &drm_bridge_funcs.atomic_enable operation.
*/
void (*mode_set)(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode);
/**
* @pre_enable:
*
* This callback should enable the bridge. It is called right before
* the preceding element in the display pipe is enabled. If the
* preceding element is a bridge this means it's called before that
* bridge's @pre_enable function. If the preceding element is a
* &drm_encoder it's called right before the encoder's
* &drm_encoder_helper_funcs.enable, &drm_encoder_helper_funcs.commit or
* &drm_encoder_helper_funcs.dpms hook.
*
* The display pipe (i.e. clocks and timing signals) feeding this bridge
* will not yet be running when this callback is called. The bridge must
* not enable the display link feeding the next bridge in the chain (if
* there is one) when this callback is called.
*
* The @pre_enable callback is optional.
*
* NOTE:
*
* This is deprecated, do not use!
* New drivers shall use &drm_bridge_funcs.atomic_pre_enable.
*/
void (*pre_enable)(struct drm_bridge *bridge);

/**
* @enable:
*
* This callback should enable the bridge. It is called right after
* the preceding element in the display pipe is enabled. If the
* preceding element is a bridge this means it's called after that
* bridge's @enable function. If the preceding element is a
* &drm_encoder it's called right after the encoder's
* &drm_encoder_helper_funcs.enable, &drm_encoder_helper_funcs.commit or
* &drm_encoder_helper_funcs.dpms hook.
*
* The bridge can assume that the display pipe (i.e. clocks and timing
* signals) feeding it is running when this callback is called. This
* callback must enable the display link feeding the next bridge in the
* chain if there is one.
*
* The @enable callback is optional.
*
* NOTE:
*
* This is deprecated, do not use!
* New drivers shall use &drm_bridge_funcs.atomic_enable.
*/
void (*enable)(struct drm_bridge *bridge);

/**
* @atomic_pre_enable:
*
* This callback should enable the bridge. It is called right before
* the preceding element in the display pipe is enabled. If the
* preceding element is a bridge this means it's called before that
* bridge's @atomic_pre_enable or @pre_enable function. If the preceding
* element is a &drm_encoder it's called right before the encoder's
* &drm_encoder_helper_funcs.atomic_enable hook.
*
* The display pipe (i.e. clocks and timing signals) feeding this bridge
* will not yet be running when this callback is called. The bridge must
* not enable the display link feeding the next bridge in the chain (if
* there is one) when this callback is called.
*
* The @atomic_pre_enable callback is optional.
*/
void (*atomic_pre_enable)(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state);

/**
* @atomic_enable:
*
* This callback should enable the bridge. It is called right after
* the preceding element in the display pipe is enabled. If the
* preceding element is a bridge this means it's called after that
* bridge's @atomic_enable or @enable function. If the preceding element
* is a &drm_encoder it's called right after the encoder's
* &drm_encoder_helper_funcs.atomic_enable hook.
*
* The bridge can assume that the display pipe (i.e. clocks and timing
* signals) feeding it is running when this callback is called. This
* callback must enable the display link feeding the next bridge in the
* chain if there is one.
*
* The @atomic_enable callback is optional.
*/
void (*atomic_enable)(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state);
/**
* @atomic_disable:
*
* This callback should disable the bridge. It is called right before
* the preceding element in the display pipe is disabled. If the
* preceding element is a bridge this means it's called before that
* bridge's @atomic_disable or @disable vfunc. If the preceding element
* is a &drm_encoder it's called right before the
* &drm_encoder_helper_funcs.atomic_disable hook.
*
* The bridge can assume that the display pipe (i.e. clocks and timing
* signals) feeding it is still running when this callback is called.
*
* The @atomic_disable callback is optional.
*/
void (*atomic_disable)(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state);

/**
* @atomic_post_disable:
*
* This callback should disable the bridge. It is called right after the
* preceding element in the display pipe is disabled. If the preceding
* element is a bridge this means it's called after that bridge's
* @atomic_post_disable or @post_disable function. If the preceding
* element is a &drm_encoder it's called right after the encoder's
* &drm_encoder_helper_funcs.atomic_disable hook.
*
* The bridge must assume that the display pipe (i.e. clocks and timing
* signals) feeding it is no longer running when this callback is
* called.
*
* The @atomic_post_disable callback is optional.
*/
void (*atomic_post_disable)(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state);

/**
* @atomic_duplicate_state:
*
* Duplicate the current bridge state object (which is guaranteed to be
* non-NULL).
*
* The atomic_duplicate_state hook is mandatory if the bridge
* implements any of the atomic hooks, and should be left unassigned
* otherwise. For bridges that don't subclass &drm_bridge_state, the
* drm_atomic_helper_bridge_duplicate_state() helper function shall be
* used to implement this hook.
*
* RETURNS:
* A valid drm_bridge_state object or NULL if the allocation fails.
*/
struct drm_bridge_state *(*atomic_duplicate_state)(struct drm_bridge *bridge);

/**
* @atomic_destroy_state:
*
* Destroy a bridge state object previously allocated by
* &drm_bridge_funcs.atomic_duplicate_state().
*
* The atomic_destroy_state hook is mandatory if the bridge implements
* any of the atomic hooks, and should be left unassigned otherwise.
* For bridges that don't subclass &drm_bridge_state, the
* drm_atomic_helper_bridge_destroy_state() helper function shall be
* used to implement this hook.
*/
void (*atomic_destroy_state)(struct drm_bridge *bridge,
struct drm_bridge_state *state);

/**
* @atomic_get_output_bus_fmts:
*
* Return the supported bus formats on the output end of a bridge.
* The returned array must be allocated with kmalloc() and will be
* freed by the caller. If the allocation fails, NULL should be
* returned. num_output_fmts must be set to the returned array size.
* Formats listed in the returned array should be listed in decreasing
* preference order (the core will try all formats until it finds one
* that works).
*
* This method is only called on the last element of the bridge chain
* as part of the bus format negotiation process that happens in
* &drm_atomic_bridge_chain_select_bus_fmts().
* This method is optional. When not implemented, the core will
* fall back to &drm_connector.display_info.bus_formats[0] if
* &drm_connector.display_info.num_bus_formats > 0,
* or to MEDIA_BUS_FMT_FIXED otherwise.
*/
u32 *(*atomic_get_output_bus_fmts)(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state,
unsigned int *num_output_fmts);

/**
* @atomic_get_input_bus_fmts:
*
* Return the supported bus formats on the input end of a bridge for
* a specific output bus format.
*
* The returned array must be allocated with kmalloc() and will be
* freed by the caller. If the allocation fails, NULL should be
* returned. num_input_fmts must be set to the returned array size.
* Formats listed in the returned array should be listed in decreasing
* preference order (the core will try all formats until it finds one
* that works). When the format is not supported NULL should be
* returned and num_input_fmts should be set to 0.
*
* This method is called on all elements of the bridge chain as part of
* the bus format negotiation process that happens in
* drm_atomic_bridge_chain_select_bus_fmts().
* This method is optional. When not implemented, the core will bypass
* bus format negotiation on this element of the bridge without
* failing, and the previous element in the chain will be passed
* MEDIA_BUS_FMT_FIXED as its output bus format.
*
* Bridge drivers that need to support being linked to bridges that are
* not supporting bus format negotiation should handle the
* output_fmt == MEDIA_BUS_FMT_FIXED case appropriately, by selecting a
* sensible default value or extracting this information from somewhere
* else (FW property, &drm_display_mode, &drm_display_info, ...)
*
* Note: Even if input format selection on the first bridge has no
* impact on the negotiation process (bus format negotiation stops once
* we reach the first element of the chain), drivers are expected to
* return accurate input formats as the input format may be used to
* configure the CRTC output appropriately.
*/
u32 *(*atomic_get_input_bus_fmts)(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state,
u32 output_fmt,
unsigned int *num_input_fmts);

/**
* @atomic_check:
*
* This method is responsible for checking bridge state correctness.
* It can also check the state of the surrounding components in chain
* to make sure the whole pipeline can work properly.
*
* &drm_bridge_funcs.atomic_check() hooks are called in reverse
* order (from the last to the first bridge).
*
* This method is optional. &drm_bridge_funcs.mode_fixup() is not
* called when &drm_bridge_funcs.atomic_check() is implemented, so only
* one of them should be provided.
*
* If drivers need to tweak &drm_bridge_state.input_bus_cfg.flags or
* &drm_bridge_state.output_bus_cfg.flags it should happen in
* this function. By default the &drm_bridge_state.output_bus_cfg.flags
* field is set to the next bridge
* &drm_bridge_state.input_bus_cfg.flags value or
* &drm_connector.display_info.bus_flags if the bridge is the last
* element in the chain.
*
* RETURNS:
* zero if the check passed, a negative error code otherwise.
*/
int (*atomic_check)(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state);

/**
* @atomic_reset:
*
* Reset the bridge to a predefined state (or retrieve its current
* state) and return a &drm_bridge_state object matching this state.
* This function is called at attach time.
*
* The atomic_reset hook is mandatory if the bridge implements any of
* the atomic hooks, and should be left unassigned otherwise. For
* bridges that don't subclass &drm_bridge_state, the
* drm_atomic_helper_bridge_reset() helper function shall be used to
* implement this hook.
*
* Note that the atomic_reset() semantics is not exactly matching the
* reset() semantics found on other components (connector, plane, ...).
*
* 1. The reset operation happens when the bridge is attached, not when
* drm_mode_config_reset() is called
* 2. It's meant to be used exclusively on bridges that have been
* converted to the ATOMIC API
*
* RETURNS:
* A valid drm_bridge_state object in case of success, an ERR_PTR()
* giving the reason of the failure otherwise.
*/
struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge);

/**
* @detect:
*
* Check if anything is attached to the bridge output.
*
* This callback is optional, if not implemented the bridge will be
* considered as always having a component attached to its output.
* Bridges that implement this callback shall set the
* DRM_BRIDGE_OP_DETECT flag in their &drm_bridge->ops.
*
* RETURNS:
*
* drm_connector_status indicating the bridge output status.
*/
enum drm_connector_status (*detect)(struct drm_bridge *bridge);

/**
* @get_modes:
*
* Fill all modes currently valid for the sink into the &drm_connector
* with drm_mode_probed_add().
*
* The @get_modes callback is mostly intended to support non-probeable
* displays such as many fixed panels. Bridges that support reading
* EDID shall leave @get_modes unimplemented and implement the
* &drm_bridge_funcs->get_edid callback instead.
*
* This callback is optional. Bridges that implement it shall set the
* DRM_BRIDGE_OP_MODES flag in their &drm_bridge->ops.
*
* The connector parameter shall be used for the sole purpose of
* filling modes, and shall not be stored internally by bridge drivers
* for future usage.
*
* RETURNS:
*
* The number of modes added by calling drm_mode_probed_add().
*/
int (*get_modes)(struct drm_bridge *bridge,
struct drm_connector *connector);

/**
* @get_edid:
*
* Read and parse the EDID data of the connected display.
*
* The @get_edid callback is the preferred way of reporting mode
* information for a display connected to the bridge output. Bridges
* that support reading EDID shall implement this callback and leave
* the @get_modes callback unimplemented.
*
* The caller of this operation shall first verify the output
* connection status and refrain from reading EDID from a disconnected
* output.
*
* This callback is optional. Bridges that implement it shall set the
* DRM_BRIDGE_OP_EDID flag in their &drm_bridge->ops.
*
* The connector parameter shall be used for the sole purpose of EDID
* retrieval and parsing, and shall not be stored internally by bridge
* drivers for future usage.
*
* RETURNS:
*
* An edid structure newly allocated with kmalloc() (or similar) on
* success, or NULL otherwise. The caller is responsible for freeing
* the returned edid structure with kfree().
*/
struct edid *(*get_edid)(struct drm_bridge *bridge,
struct drm_connector *connector);

/**
* @hpd_notify:
*
* Notify the bridge of hot plug detection.
*
* This callback is optional, it may be implemented by bridges that
* need to be notified of display connection or disconnection for
* internal reasons. One use case is to reset the internal state of CEC
* controllers for HDMI bridges.
*/
void (*hpd_notify)(struct drm_bridge *bridge,
enum drm_connector_status status);

/**
* @hpd_enable:
*
* Enable hot plug detection. From now on the bridge shall call
* drm_bridge_hpd_notify() each time a change is detected in the output
* connection status, until hot plug detection gets disabled with
* @hpd_disable.
*
* This callback is optional and shall only be implemented by bridges
* that support hot-plug notification without polling. Bridges that
* implement it shall also implement the @hpd_disable callback and set
* the DRM_BRIDGE_OP_HPD flag in their &drm_bridge->ops.
*/
void (*hpd_enable)(struct drm_bridge *bridge);

/**
* @hpd_disable:
*
* Disable hot plug detection. Once this function returns the bridge
* shall not call drm_bridge_hpd_notify() when a change in the output
* connection status occurs.
*
* This callback is optional and shall only be implemented by bridges
* that support hot-plug notification without polling. Bridges that
* implement it shall also implement the @hpd_enable callback and set
* the DRM_BRIDGE_OP_HPD flag in their &drm_bridge->ops.
*/
void (*hpd_disable)(struct drm_bridge *bridge);

/**
* @debugfs_init:
*
* Allows bridges to create bridge-specific debugfs files.
*/
void (*debugfs_init)(struct drm_bridge *bridge, struct dentry *root);
};

三、connector数据结构

3.1 struct drm_connector

linux内核使用struct drm_connector来表示一个连接器,用于将encoder输出的信号传递给显示器,并与显示器建立连接;定义在include/drm/drm_connector.h:

Hidden Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
/**
* 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;

/**
* @edid_override: Override EDID set via debugfs.
*
* Do not modify or access outside of the drm_edid_override_* family of
* functions.
*/
const struct drm_edid *edid_override;

/**
* @edid_override_mutex: Protect access to edid_override.
*/
struct mutex edid_override_mutex;

/** @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;
};
3.1.1 struct drm_display_info

linux内核使用struct drm_display_info来表示当前连接的显示设备的显示信息,它包含了一系列成员变量,用来描述显示设备的物理尺寸、颜色格式、总线格式等信息。定义在include/drm/drm_connector.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/**
* struct drm_display_info - runtime data about the connected sink
*
* Describes a given display (e.g. CRT or flat panel) and its limitations. For
* fixed display sinks like built-in panels there's not much difference between
* this and &struct drm_connector. But for sinks with a real cable this
* structure is meant to describe all the things at the other end of the cable.
*
* For sinks which provide an EDID this can be filled out by calling
* drm_add_edid_modes().
*/
struct drm_display_info {
/**
* @width_mm: Physical width in mm.
*/
unsigned int width_mm;

/**
* @height_mm: Physical height in mm.
*/
unsigned int height_mm;

/**
* @bpc: Maximum bits per color channel. Used by HDMI and DP outputs.
*/
unsigned int bpc;

/**
* @subpixel_order: Subpixel order of LCD panels.
*/
enum subpixel_order subpixel_order;

#define DRM_COLOR_FORMAT_RGB444 (1<<0)
#define DRM_COLOR_FORMAT_YCBCR444 (1<<1)
#define DRM_COLOR_FORMAT_YCBCR422 (1<<2)
#define DRM_COLOR_FORMAT_YCBCR420 (1<<3)

/**
* @panel_orientation: Read only connector property for built-in panels,
* indicating the orientation of the panel vs the device's casing.
* drm_connector_init() sets this to DRM_MODE_PANEL_ORIENTATION_UNKNOWN.
* When not UNKNOWN this gets used by the drm_fb_helpers to rotate the
* fb to compensate and gets exported as prop to userspace.
*/
int panel_orientation;

/**
* @color_formats: HDMI Color formats, selects between RGB and YCrCb
* modes. Used DRM_COLOR_FORMAT\_ defines, which are _not_ the same ones
* as used to describe the pixel format in framebuffers, and also don't
* match the formats in @bus_formats which are shared with v4l.
*/
u32 color_formats;

/**
* @bus_formats: Pixel data format on the wire, somewhat redundant with
* @color_formats. Array of size @num_bus_formats encoded using
* MEDIA_BUS_FMT\_ defines shared with v4l and media drivers.
*/
const u32 *bus_formats;

/**
* @bus_formats: Pixel data format on the wire, somewhat redundant with
* @color_formats. Array of size @num_bus_formats encoded using
* MEDIA_BUS_FMT\_ defines shared with v4l and media drivers.
*/
const u32 *bus_formats;
/**
* @num_bus_formats: Size of @bus_formats array.
*/
unsigned int num_bus_formats;

/**
* @bus_flags: Additional information (like pixel signal polarity) for
* the pixel data on the bus, using &enum drm_bus_flags values
* DRM_BUS_FLAGS\_.
*/
u32 bus_flags;

/**
* @max_tmds_clock: Maximum TMDS clock rate supported by the
* sink in kHz. 0 means undefined.
*/
int max_tmds_clock;

/**
* @dvi_dual: Dual-link DVI sink?
*/
bool dvi_dual;

/**
* @is_hdmi: True if the sink is an HDMI device.
*
* This field shall be used instead of calling
* drm_detect_hdmi_monitor() when possible.
*/
bool is_hdmi;

/**
* @has_hdmi_infoframe: Does the sink support the HDMI infoframe?
*/
bool has_hdmi_infoframe;

/**
* @rgb_quant_range_selectable: Does the sink support selecting
* the RGB quantization range?
*/
bool rgb_quant_range_selectable;

/**
* @edid_hdmi_rgb444_dc_modes: Mask of supported hdmi deep color modes
* in RGB 4:4:4. Even more stuff redundant with @bus_formats.
*/
u8 edid_hdmi_rgb444_dc_modes;

/**
* @edid_hdmi_ycbcr444_dc_modes: Mask of supported hdmi deep color
* modes in YCbCr 4:4:4. Even more stuff redundant with @bus_formats.
*/
u8 edid_hdmi_ycbcr444_dc_modes;

/**
* @cea_rev: CEA revision of the HDMI sink.
*/
u8 cea_rev;

/**
* @hdmi: advance features of a HDMI sink.
*/
struct drm_hdmi_info hdmi;

/**
* @non_desktop: Non desktop display (HMD).
*/
bool non_desktop;

/**
* @monitor_range: Frequency range supported by monitor range descriptor
*/
struct drm_monitor_range_info monitor_range;

/**
* @luminance_range: Luminance range supported by panel
*/
struct drm_luminance_range_info luminance_range;

/**
* @mso_stream_count: eDP Multi-SST Operation (MSO) stream count from
* the DisplayID VESA vendor block. 0 for conventional Single-Stream
* Transport (SST), or 2 or 4 MSO streams.
*/
u8 mso_stream_count;

/**
* @mso_pixel_overlap: eDP MSO segment pixel overlap, 0-8 pixels.
*/
u8 mso_pixel_overlap;

/**
* @max_dsc_bpp: Maximum DSC target bitrate, if it is set to 0 the
* monitor's default value is used instead.
*/
u32 max_dsc_bpp;

/**
* @vics: Array of vics_len VICs. Internal to EDID parsing.
*/
u8 *vics;

/**
* @vics_len: Number of elements in vics. Internal to EDID parsing.
*/
int vics_len;

/**
* @quirks: EDID based quirks. Internal to EDID parsing.
*/
u32 quirks;
};

其中:

  • width_mm:物理宽度,单位mm
  • height_mm:物理高度、单位mm
  • bpc: 每个颜色通道最大位数,适用于HDMIDP输出;
  • panel_orientation:一个只读属性,用于描述内置面板的方向与设备外壳的方向之间的关系。在调用drm_connector_init()函数时,这个属性会被设置为DRM_MODE_PANEL_ORIENTATION_UNKNOWN,表示方向未知。当方向不是未知时,图形驱动程序中的drm_fb_helpers会根据该属性来旋转帧缓冲以进行补偿,并将其作为属性导出给用户空间;
  • color_formats:用于选择HDMI的颜色格式,支持RGBYCrCb模式;这里使用了DRM_COLOR_FORMAT_定义的值来表示不同的颜色格式,这些值与用于描述framebuffer中像素格式的值是不同的。此外,它们也与与v4l共享的bus_formats中的格式不匹配;
  • bus_formats:表示在传输介质上的像素数据格式,与color_formats相比有些冗余。bus_formats是一个数组,其大小由num_bus_formats确定,使用MEDIA_BUS_FMT_定义对其进行编码,这些定义是和v4l(以及媒体驱动程序共享的;
  • num_bus_formatsbus_formats数组的长度;
  • bus_flags:它包含了有关总线上像素数据的附加信息,比如像素信号的极性。这些信息使用enum drm_bus_flags中定义的数值,其中包括了DRM_BUS_FLAGS_前缀;
  • max_tmds_clock:表示显示设备支持的TMDS时钟的最大速率,单位是千赫兹(kHz)。如果值为0,则表示未定义;
  • dvi_dual:是否支持双链DVI
  • is_hdmi:用于指示显示设备是否为HDMI设备,通过设置该字段来避免不必要地调用drm_detect_hdmi_monitor函数;
  • has_hdmi_infoframe:显示设备是否支持HDMI信息帧;
  • rgb_quant_range_selectable:表示显示设备是否支持选择RGB量化范围;
  • edid_hdmi_rgb444_dc_modes:用于表示支持的HDMI深色模式的位掩码,适用于RGB 4:4:4格式;
  • edid_hdmi_ycbcr444_dc_modes:用于表示支持的HDMI深色模式的位掩码,适用于YCbCr 4:4:4格式;
  • cea_rev:在HDMI接口中,CEA规范定义了一系列的视频和音频格式;该字段表示HDMI显示设备支持的CEA规范版本;
  • hdmiHDMI显示设备支持的高级功能;
  • non_desktop:表示非桌面显示;
  • monitor_range:存储从edid的``Detailed Monitor Descriptor`解析出的面板支持的频率范围;
  • luminance_range:存储从edid的``static hdr metadata.`解析出的面板支持的亮度范围;
  • mso_stream_counteDP Multi-SST Operation(MSO)流计数是从DisplayID VESA供应商块中获取的信息。它用于指示eDP(Embedded DisplayPort)接口支持的传输模式;
    • eDP接口使用传统的单流传输(SST)时,MSO流计数为0;
    • eDP接口支持多流操作(MSO)时,MSO流计数可以是2或4;
  • mso_pixel_overlap
  • max_dsc_bpp:最大DSC目标比特率;
  • vicsArray of vics_len VICs. Internal to EDID parsing
  • vics_lenNumber of elements in vics. Internal to EDID parsing
  • quirksEDID based quirks. Internal to EDID parsing.
3.1.2 struct drm_hdmi_info

linux内核使用struct drm_hdmi_info来表示当前连接的HDMI显示设备的信息,定义在include/drm/drm_connector.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* struct drm_hdmi_info - runtime information about the connected HDMI sink
*
* Describes if a given display supports advanced HDMI 2.0 features.
* This information is available in CEA-861-F extension blocks (like HF-VSDB).
*/
struct drm_hdmi_info {
/** @scdc: sink's scdc support and capabilities */
struct drm_scdc scdc;

/**
* @y420_vdb_modes: bitmap of modes which can support ycbcr420
* output only (not normal RGB/YCBCR444/422 outputs). The max VIC
* defined by the CEA-861-G spec is 219, so the size is 256 bits to map
* up to 256 VICs.
*/
unsigned long y420_vdb_modes[BITS_TO_LONGS(256)];

/**
* @y420_cmdb_modes: bitmap of modes which can support ycbcr420
* output also, along with normal HDMI outputs. The max VIC defined by
* the CEA-861-G spec is 219, so the size is 256 bits to map up to 256
* VICs.
*/
unsigned long y420_cmdb_modes[BITS_TO_LONGS(256)];

/** @y420_dc_modes: bitmap of deep color support index */
u8 y420_dc_modes;

/** @max_frl_rate_per_lane: support fixed rate link */
u8 max_frl_rate_per_lane;

/** @max_lanes: supported by sink */
u8 max_lanes;

/** @dsc_cap: DSC capabilities of the sink */
struct drm_hdmi_dsc_cap dsc_cap;
};

3.2 操作函数

3.2.1 struct drm_connector_funcs

struct drm_connector_funcs用于描述connector的控制函数,定义在include/drm/drm_connector.h

Hidden Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
/**
* struct drm_connector_funcs - control connectors on a given device
*
* Each CRTC may have one or more connectors attached to it. The functions
* below allow the core DRM code to control connectors, enumerate available modes,
* etc.
*/
struct drm_connector_funcs {
/**
* @dpms:
*
* Legacy entry point to set the per-connector DPMS state. Legacy DPMS
* is exposed as a standard property on the connector, but diverted to
* this callback in the drm core. Note that atomic drivers don't
* implement the 4 level DPMS support on the connector any more, but
* instead only have an on/off "ACTIVE" property on the CRTC object.
*
* This hook is not used by atomic drivers, remapping of the legacy DPMS
* property is entirely handled in the DRM core.
*
* RETURNS:
*
* 0 on success or a negative error code on failure.
*/
int (*dpms)(struct drm_connector *connector, int mode);

/**
* @reset:
*
* Reset connector hardware and software state to off. This function isn't
* called by the core directly, only through drm_mode_config_reset().
* It's not a helper hook only for historical reasons.
*
* Atomic drivers can use drm_atomic_helper_connector_reset() to reset
* atomic state using this hook.
*/
void (*reset)(struct drm_connector *connector);

/**
* @detect:
*
* Check to see if anything is attached to the connector. The parameter
* force is set to false whilst polling, true when checking the
* connector due to a user request. force can be used by the driver to
* avoid expensive, destructive operations during automated probing.
*
* This callback is optional, if not implemented the connector will be
* considered as always being attached.
*
* FIXME:
*
* Note that this hook is only called by the probe helper. It's not in
* the helper library vtable purely for historical reasons. The only DRM
* core entry point to probe connector state is @fill_modes.
*
* Note that the helper library will already hold
* &drm_mode_config.connection_mutex. Drivers which need to grab additional
* locks to avoid races with concurrent modeset changes need to use
* &drm_connector_helper_funcs.detect_ctx instead.
*
* Also note that this callback can be called no matter the
* state the connector is in. Drivers that need the underlying
* device to be powered to perform the detection will first need
* to make sure it's been properly enabled.
*
* RETURNS:
*
* drm_connector_status indicating the connector's status.
*/
enum drm_connector_status (*detect)(struct drm_connector *connector,
bool force);

/**
* @force:
*
* This function is called to update internal encoder state when the
* connector is forced to a certain state by userspace, either through
* the sysfs interfaces or on the kernel cmdline. In that case the
* @detect callback isn't called.
*
* FIXME:
*
* Note that this hook is only called by the probe helper. It's not in
* the helper library vtable purely for historical reasons. The only DRM
* core entry point to probe connector state is @fill_modes.
*/
void (*force)(struct drm_connector *connector);

/**
* @fill_modes:
*
* Entry point for output detection and basic mode validation. The
* driver should reprobe the output if needed (e.g. when hotplug
* handling is unreliable), add all detected modes to &drm_connector.modes
* and filter out any the device can't support in any configuration. It
* also needs to filter out any modes wider or higher than the
* parameters max_width and max_height indicate.
*
* The drivers must also prune any modes no longer valid from
* &drm_connector.modes. Furthermore it must update
* &drm_connector.status and &drm_connector.edid. If no EDID has been
* received for this output connector->edid must be NULL.
*
* Drivers using the probe helpers should use
* drm_helper_probe_single_connector_modes() to implement this
* function.
*
* RETURNS:
*
* The number of modes detected and filled into &drm_connector.modes.
*/
int (*fill_modes)(struct drm_connector *connector, uint32_t max_width, uint32_t max_height);

/**
* @set_property:
*
* This is the legacy entry point to update a property attached to the
* connector.
*
* This callback is optional if the driver does not support any legacy
* driver-private properties. For atomic drivers it is not used because
* property handling is done entirely in the DRM core.
*
* RETURNS:
*
* 0 on success or a negative error code on failure.
*/
int (*set_property)(struct drm_connector *connector, struct drm_property *property,
uint64_t val);

/**
* @late_register:
*
* This optional hook can be used to register additional userspace
* interfaces attached to the connector, light backlight control, i2c,
* DP aux or similar interfaces. It is called late in the driver load
* sequence from drm_connector_register() when registering all the
* core drm connector interfaces. Everything added from this callback
* should be unregistered in the early_unregister callback.
* should be unregistered in the early_unregister callback.
*
* This is called while holding &drm_connector.mutex.
*
* Returns:
*
* 0 on success, or a negative error code on failure.
*/
int (*late_register)(struct drm_connector *connector);

/**
* @early_unregister:
*
* This optional hook should be used to unregister the additional
* userspace interfaces attached to the connector from
* late_register(). It is called from drm_connector_unregister(),
* early in the driver unload sequence to disable userspace access
* before data structures are torndown.
*
* This is called while holding &drm_connector.mutex.
*/
void (*early_unregister)(struct drm_connector *connector);

/**
* @destroy:
*
* Clean up connector resources. This is called at driver unload time
* through drm_mode_config_cleanup(). It can also be called at runtime
* when a connector is being hot-unplugged for drivers that support
* connector hotplugging (e.g. DisplayPort MST).
*/
void (*destroy)(struct drm_connector *connector);

/**
* @atomic_duplicate_state:
*
* Duplicate the current atomic state for this connector and return it.
* The core and helpers guarantee that any atomic state duplicated with
* this hook and still owned by the caller (i.e. not transferred to the
* driver by calling &drm_mode_config_funcs.atomic_commit) will be
* cleaned up by calling the @atomic_destroy_state hook in this
* structure.
*
* This callback is mandatory for atomic drivers.
*
* Atomic drivers which don't subclass &struct drm_connector_state should use
* drm_atomic_helper_connector_duplicate_state(). Drivers that subclass the
* state structure to extend it with driver-private state should use
* __drm_atomic_helper_connector_duplicate_state() to make sure shared state is
* duplicated in a consistent fashion across drivers.
*
* It is an error to call this hook before &drm_connector.state has been
* initialized correctly.
*
* NOTE:
*
* If the duplicate state references refcounted resources this hook must
* acquire a reference for each of them. The driver must release these
* references again in @atomic_destroy_state.
*
* RETURNS:
*
* Duplicated atomic state or NULL when the allocation failed.
*/
struct drm_connector_state *(*atomic_duplicate_state)(struct drm_connector *connector);

/**
* @atomic_destroy_state:
*
* Destroy a state duplicated with @atomic_duplicate_state and release
* or unreference all resources it references
*
* This callback is mandatory for atomic drivers.
*/
void (*atomic_destroy_state)(struct drm_connector *connector,
struct drm_connector_state *state);

/**
* @atomic_set_property:
*
* Decode a driver-private property value and store the decoded value
* into the passed-in state structure. Since the atomic core decodes all
* standardized properties (even for extensions beyond the core set of
* properties which might not be implemented by all drivers) this
* requires drivers to subclass the state structure.
*
* Such driver-private properties should really only be implemented for
* truly hardware/vendor specific state. Instead it is preferred to
* standardize atomic extension and decode the properties used to expose
* such an extension in the core.
*
* Do not call this function directly, use
* drm_atomic_connector_set_property() instead.
*
* This callback is optional if the driver does not support any
* driver-private atomic properties.
*
* NOTE:
*
* This function is called in the state assembly phase of atomic
* modesets, which can be aborted for any reason (including on
* userspace's request to just check whether a configuration would be
* possible). Drivers MUST NOT touch any persistent state (hardware or
* software) or data structures except the passed in @state parameter.
*
* Also since userspace controls in which order properties are set this
* function must not do any input validation (since the state update is
* incomplete and hence likely inconsistent). Instead any such input
* validation must be done in the various atomic_check callbacks.
*
* RETURNS:
*
* 0 if the property has been found, -EINVAL if the property isn't
* implemented by the driver (which shouldn't ever happen, the core only
* asks for properties attached to this connector). No other validation
* is allowed by the driver. The core already checks that the property
* value is within the range (integer, valid enum value, ...) the driver
* set when registering the property.
*/
int (*atomic_set_property)(struct drm_connector *connector,
struct drm_connector_state *state,
struct drm_property *property,
uint64_t val);

/**
* @atomic_get_property:
*
* Reads out the decoded driver-private property. This is used to
* implement the GETCONNECTOR IOCTL.
*
* Do not call this function directly, use
* drm_atomic_connector_get_property() instead.
*
* This callback is optional if the driver does not support any
* driver-private atomic properties.
*
* RETURNS:
*
* 0 on success, -EINVAL if the property isn't implemented by the
* driver (which shouldn't ever happen, the core only asks for
* properties attached to this connector).
*/
int (*atomic_get_property)(struct drm_connector *connector,
const struct drm_connector_state *state,
struct drm_property *property,
uint64_t *val);

/**
* @atomic_print_state:
*
* If driver subclasses &struct drm_connector_state, it should implement
* this optional hook for printing additional driver specific state.
*
* Do not call this directly, use drm_atomic_connector_print_state()
* instead.
*/
void (*atomic_print_state)(struct drm_printer *p,
const struct drm_connector_state *state);

/**
* @oob_hotplug_event:
*
* This will get called when a hotplug-event for a drm-connector
* has been received from a source outside the display driver / device.
*/
void (*oob_hotplug_event)(struct drm_connector *connector);

/**
* @debugfs_init:
*
* Allows connectors to create connector-specific debugfs files.
*/
void (*debugfs_init)(struct drm_connector *connector, struct dentry *root);
};
3.2.2 struct drm_connector_helper_funcs

struct drm_connector_helper_funcs定义了一些常用的connector辅助操作函数,定义在include/drm/drm_modeset_helper_vtables.h

Hidden Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
/**
* struct drm_connector_helper_funcs - helper operations for connectors
*
* These functions are used by the atomic and legacy modeset helpers and by the
* probe helpers.
*/
struct drm_connector_helper_funcs {
/**
* @get_modes:
*
* This function should fill in all modes currently valid for the sink
* into the &drm_connector.probed_modes list. It should also update the
* EDID property by calling drm_connector_update_edid_property().
*
* The usual way to implement this is to cache the EDID retrieved in the
* probe callback somewhere in the driver-private connector structure.
* In this function drivers then parse the modes in the EDID and add
* them by calling drm_add_edid_modes(). But connectors that drive a
* fixed panel can also manually add specific modes using
* drm_mode_probed_add(). Drivers which manually add modes should also
* make sure that the &drm_connector.display_info,
* &drm_connector.width_mm and &drm_connector.height_mm fields are
* filled in.
*
* Note that the caller function will automatically add standard VESA
* DMT modes up to 1024x768 if the .get_modes() helper operation returns
* no mode and if the connector status is connector_status_connected or
* connector_status_unknown. There is no need to call
* drm_add_modes_noedid() manually in that case.
*
* Virtual drivers that just want some standard VESA mode with a given
* resolution can call drm_add_modes_noedid(), and mark the preferred
* one using drm_set_preferred_mode().
*
* This function is only called after the @detect hook has indicated
* that a sink is connected and when the EDID isn't overridden through
* sysfs or the kernel commandline.
*
* This callback is used by the probe helpers in e.g.
* drm_helper_probe_single_connector_modes().
*
* To avoid races with concurrent connector state updates, the helper
* libraries always call this with the &drm_mode_config.connection_mutex
* held. Because of this it's safe to inspect &drm_connector->state.
*
* RETURNS:
*
* The number of modes added by calling drm_mode_probed_add().
*/
int (*get_modes)(struct drm_connector *connector);

/**
* @detect_ctx:
*
* Check to see if anything is attached to the connector. The parameter
* force is set to false whilst polling, true when checking the
* connector due to a user request. force can be used by the driver to
* avoid expensive, destructive operations during automated probing.
*
* This callback is optional, if not implemented the connector will be
* considered as always being attached.
*
* This is the atomic version of &drm_connector_funcs.detect.
*
* To avoid races against concurrent connector state updates, the
* helper libraries always call this with ctx set to a valid context,
* and &drm_mode_config.connection_mutex will always be locked with
* the ctx parameter set to this ctx. This allows taking additional
* locks as required.
*
*
* RETURNS:
*
* &drm_connector_status indicating the connector's status,
* or the error code returned by drm_modeset_lock(), -EDEADLK.
*/
int (*detect_ctx)(struct drm_connector *connector,
struct drm_modeset_acquire_ctx *ctx,
bool force);

/**
* @mode_valid:
*
* Callback to validate a mode for a connector, irrespective of the
* specific display configuration.
*
* This callback is used by the probe helpers to filter the mode list
* (which is usually derived from the EDID data block from the sink).
* See e.g. drm_helper_probe_single_connector_modes().
*
* This function is optional.
*
* NOTE:
*
* This only filters the mode list supplied to userspace in the
* GETCONNECTOR IOCTL. Compared to &drm_encoder_helper_funcs.mode_valid,
* &drm_crtc_helper_funcs.mode_valid and &drm_bridge_funcs.mode_valid,
* which are also called by the atomic helpers from
* drm_atomic_helper_check_modeset(). This allows userspace to force and
* ignore sink constraint (like the pixel clock limits in the screen's
* EDID), which is useful for e.g. testing, or working around a broken
* EDID. Any source hardware constraint (which always need to be
* enforced) therefore should be checked in one of the above callbacks,
* and not this one here.
*
* To avoid races with concurrent connector state updates, the helper
* libraries always call this with the &drm_mode_config.connection_mutex
* held. Because of this it's safe to inspect &drm_connector->state.
*
* RETURNS:
*
* Either &drm_mode_status.MODE_OK or one of the failure reasons in &enum
* drm_mode_status.
*/
enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
struct drm_display_mode *mode);

/**
* @mode_valid_ctx:
*
* Callback to validate a mode for a connector, irrespective of the
* specific display configuration.
*
* This callback is used by the probe helpers to filter the mode list
* (which is usually derived from the EDID data block from the sink).
* See e.g. drm_helper_probe_single_connector_modes().
*
* This function is optional, and is the atomic version of
* &drm_connector_helper_funcs.mode_valid.
*
* To allow for accessing the atomic state of modesetting objects, the
* helper libraries always call this with ctx set to a valid context,
* and &drm_mode_config.connection_mutex will always be locked with
* the ctx parameter set to @ctx. This allows for taking additional
* locks as required.
*
* Even though additional locks may be acquired, this callback is
* still expected not to take any constraints into account which would
* be influenced by the currently set display state - such constraints
* should be handled in the driver's atomic check. For example, if a
* connector shares display bandwidth with other connectors then it
* would be ok to validate the minimum bandwidth requirement of a mode
* against the maximum possible bandwidth of the connector. But it
* wouldn't be ok to take the current bandwidth usage of other
* connectors into account, as this would change depending on the
* display state.
*
* Returns:
* 0 if &drm_connector_helper_funcs.mode_valid_ctx succeeded and wrote
* the &enum drm_mode_status value to @status, or a negative error
* code otherwise.
*
*/
int (*mode_valid_ctx)(struct drm_connector *connector,
struct drm_display_mode *mode,
struct drm_modeset_acquire_ctx *ctx,
enum drm_mode_status *status);

/**
* @best_encoder:
*
* This function should select the best encoder for the given connector.
*
* This function is used by both the atomic helpers (in the
* drm_atomic_helper_check_modeset() function) and in the legacy CRTC
* helpers.
*
* NOTE:
*
* In atomic drivers this function is called in the check phase of an
* atomic update. The driver is not allowed to change or inspect
* anything outside of arguments passed-in. Atomic drivers which need to
* inspect dynamic configuration state should instead use
* @atomic_best_encoder.
*
* You can leave this function to NULL if the connector is only
* attached to a single encoder. In this case, the core will call
* drm_connector_get_single_encoder() for you.
*
* RETURNS:
*
* Encoder that should be used for the given connector and connector
* state, or NULL if no suitable encoder exists. Note that the helpers
* will ensure that encoders aren't used twice, drivers should not check
* for this.
*/
struct drm_encoder *(*best_encoder)(struct drm_connector *connector);
/**
* @atomic_best_encoder:
*
* This is the atomic version of @best_encoder for atomic drivers which
* need to select the best encoder depending upon the desired
* configuration and can't select it statically.
*
* This function is used by drm_atomic_helper_check_modeset().
* If it is not implemented, the core will fallback to @best_encoder
* (or drm_connector_get_single_encoder() if @best_encoder is NULL).
*
* NOTE:
*
* This function is called in the check phase of an atomic update. The
* driver is not allowed to change anything outside of the
* &drm_atomic_state update tracking structure passed in.
*
* RETURNS:
*
* Encoder that should be used for the given connector and connector
* state, or NULL if no suitable encoder exists. Note that the helpers
* will ensure that encoders aren't used twice, drivers should not check
* for this.
*/
struct drm_encoder *(*atomic_best_encoder)(struct drm_connector *connector,
struct drm_atomic_state *state);

/**
* @atomic_check:
*
* This hook is used to validate connector state. This function is
* called from &drm_atomic_helper_check_modeset, and is called when
* a connector property is set, or a modeset on the crtc is forced.
*
* Because &drm_atomic_helper_check_modeset may be called multiple times,
* this function should handle being called multiple times as well.
*
* This function is also allowed to inspect any other object's state and
* can add more state objects to the atomic commit if needed. Care must
* be taken though to ensure that state check and compute functions for
* these added states are all called, and derived state in other objects
* all updated. Again the recommendation is to just call check helpers
* until a maximal configuration is reached.
*
* NOTE:
*
* This function is called in the check phase of an atomic update. The
* driver is not allowed to change anything outside of the free-standing
* state objects passed-in or assembled in the overall &drm_atomic_state
* update tracking structure.
*
* RETURNS:
*
* 0 on success, -EINVAL if the state or the transition can't be
* supported, -ENOMEM on memory allocation failure and -EDEADLK if an
* attempt to obtain another state object ran into a &drm_modeset_lock
* deadlock.
*/
int (*atomic_check)(struct drm_connector *connector,
struct drm_atomic_state *state);
/**
* @atomic_commit:
*
* This hook is to be used by drivers implementing writeback connectors
* that need a point when to commit the writeback job to the hardware.
* The writeback_job to commit is available in the new connector state,
* in &drm_connector_state.writeback_job.
*
* This hook is optional.
*
* This callback is used by the atomic modeset helpers.
*/
void (*atomic_commit)(struct drm_connector *connector,
struct drm_atomic_state *state);

/**
* @prepare_writeback_job:
*
* As writeback jobs contain a framebuffer, drivers may need to
* prepare and clean them up the same way they can prepare and
* clean up framebuffers for planes. This optional connector operation
* is used to support the preparation of writeback jobs. The job
* prepare operation is called from drm_atomic_helper_prepare_planes()
* for struct &drm_writeback_connector connectors only.
*
* This operation is optional.
*
* This callback is used by the atomic modeset helpers.
*/
int (*prepare_writeback_job)(struct drm_writeback_connector *connector,
struct drm_writeback_job *job);
/**
* @cleanup_writeback_job:
*
* This optional connector operation is used to support the
* cleanup of writeback jobs. The job cleanup operation is called
* from the existing drm_writeback_cleanup_job() function, invoked
* both when destroying the job as part of an aborted commit, or when
* the job completes.
*
* This operation is optional.
*
* This callback is used by the atomic modeset helpers.
*/
void (*cleanup_writeback_job)(struct drm_writeback_connector *connector,
struct drm_writeback_job *job);

/**
* @enable_hpd:
*
* Enable hot-plug detection for the connector.
*
* This operation is optional.
*
* This callback is used by the drm_kms_helper_poll_enable() helpers.
*/
void (*enable_hpd)(struct drm_connector *connector);

/**
* @disable_hpd:
*
* Disable hot-plug detection for the connector.
*
* This operation is optional.
*
* This callback is used by the drm_kms_helper_poll_disable() helpers.
*/
void (*disable_hpd)(struct drm_connector *connector);
};

四、核心API

4.1 encoder初始化

drm_encoder_init函数用于初始化encoder对象,定义在drivers/gpu/drm/drm_encoder.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* drm_encoder_init - Init a preallocated encoder
* @dev: drm device
* @encoder: the encoder to init
* @funcs: callbacks for this encoder
* @encoder_type: user visible type of the encoder
* @name: printf style format string for the encoder name, or NULL for default name
*
* Initializes a preallocated encoder. Encoder should be subclassed as part of
* driver encoder objects. At driver unload time the driver's
* &drm_encoder_funcs.destroy hook should call drm_encoder_cleanup() and kfree()
* the encoder structure. The encoder structure should not be allocated with
* devm_kzalloc().
*
* Note: consider using drmm_encoder_alloc() or drmm_encoder_init()
* instead of drm_encoder_init() to let the DRM managed resource
* infrastructure take care of cleanup and deallocation.
*
* Returns:
* Zero on success, error code on failure.
*/
int drm_encoder_init(struct drm_device *dev,
struct drm_encoder *encoder,
const struct drm_encoder_funcs *funcs,
int encoder_type, const char *name, ...)
{
va_list ap;
int ret;

WARN_ON(!funcs->destroy);

va_start(ap, name);
ret = __drm_encoder_init(dev, encoder, funcs, encoder_type, name, ap);
va_end(ap);

return ret;
}

具体实现位于函数__drm_encoder_init

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
__printf(5, 0)
static int __drm_encoder_init(struct drm_device *dev,
struct drm_encoder *encoder,
const struct drm_encoder_funcs *funcs,
int encoder_type, const char *name, va_list ap)
{
int ret;

/* encoder index is used with 32bit bitmasks */
if (WARN_ON(dev->mode_config.num_encoder >= 32))
return -EINVAL;

ret = drm_mode_object_add(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
if (ret)
return ret;

encoder->dev = dev;
encoder->encoder_type = encoder_type;
encoder->funcs = funcs;
if (name) {
encoder->name = kvasprintf(GFP_KERNEL, name, ap);
} else {
encoder->name = kasprintf(GFP_KERNEL, "%s-%d",
drm_encoder_enum_list[encoder_type].name,
encoder->base.id);
}
if (!encoder->name) {
ret = -ENOMEM;
goto out_put;
}

INIT_LIST_HEAD(&encoder->bridge_chain);
list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
encoder->index = dev->mode_config.num_encoder++;

out_put:
if (ret)
drm_mode_object_unregister(dev, &encoder->base);

return ret;
}

4.2 connector初始化

drm_connector_init函数用于初始化connector对象,定义在drivers/gpu/drm/drm_connector.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* 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.
*
* At driver unload time the driver's &drm_connector_funcs.destroy hook
* should call drm_connector_cleanup() and free the connector structure.
* The connector structure should not be allocated with devm_kzalloc().
*
* Note: consider using drmm_connector_init() instead of
* drm_connector_init() to let the DRM managed resource infrastructure
* take care of cleanup and deallocation.
*
* 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)
{
if (drm_WARN_ON(dev, !(funcs && funcs->destroy)))
return -EINVAL;

return __drm_connector_init(dev, connector, funcs, connector_type, NULL);
}

具体实现位于函数__drm_connector_init

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
static int __drm_connector_init(struct drm_device *dev,
struct drm_connector *connector,
const struct drm_connector_funcs *funcs,
int connector_type,
struct i2c_adapter *ddc)
{
struct drm_mode_config *config = &dev->mode_config;
int ret;
struct ida *connector_ida =
&drm_connector_enum_list[connector_type].ida;

WARN_ON(drm_drv_uses_atomic_modeset(dev) &&
(!funcs->atomic_destroy_state ||
!funcs->atomic_duplicate_state));

ret = __drm_mode_object_add(dev, &connector->base,
DRM_MODE_OBJECT_CONNECTOR,
false, drm_connector_free);
if (ret)
return ret;

connector->base.properties = &connector->properties;
connector->dev = dev;
connector->funcs = funcs;

/* connector index is used with 32bit bitmasks */
ret = ida_alloc_max(&config->connector_ida, 31, GFP_KERNEL);
if (ret < 0) {
DRM_DEBUG_KMS("Failed to allocate %s connector index: %d\n",
drm_connector_enum_list[connector_type].name,
ret);
goto out_put;
}
connector->index = ret;
ret = 0;

connector->connector_type = connector_type;
connector->connector_type_id =
ida_alloc_min(connector_ida, 1, GFP_KERNEL);
if (connector->connector_type_id < 0) {
ret = connector->connector_type_id;
goto out_put_id;
}
connector->name =
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;
}

/* provide ddc symlink in sysfs */
connector->ddc = ddc;

INIT_LIST_HEAD(&connector->global_connector_list_entry);
INIT_LIST_HEAD(&connector->probed_modes);
INIT_LIST_HEAD(&connector->modes);
mutex_init(&connector->mutex);
mutex_init(&connector->edid_override_mutex);
connector->edid_blob_ptr = NULL;
connector->epoch_counter = 0;
connector->tile_blob_ptr = NULL;
connector->status = connector_status_unknown;
connector->display_info.panel_orientation =
DRM_MODE_PANEL_ORIENTATION_UNKNOWN;

drm_connector_get_cmdline_mode(connector);

/* We should add connectors at the end to avoid upsetting the connector
* index too much.
*/
spin_lock_irq(&config->connector_list_lock);
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 &&
connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
drm_connector_attach_edid_property(connector);

drm_object_attach_property(&connector->base,
config->dpms_property, 0);

drm_object_attach_property(&connector->base,
config->link_status_property,
0);

drm_object_attach_property(&connector->base,
config->non_desktop_property,
0);
drm_object_attach_property(&connector->base,
config->tile_property,
0);

if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
drm_object_attach_property(&connector->base, config->prop_crtc_id, 0);
}

connector->debugfs_entry = NULL;
out_put_type_id:
if (ret)
ida_free(connector_ida, connector->connector_type_id);
out_put_id:
if (ret)
ida_free(&config->connector_ida, connector->index);
out_put:
if (ret)
drm_mode_object_unregister(dev, &connector->base);

return ret;
}

参考文章

[1] RK3399驱动开发 | 20 - 阿美林7寸mipi屏幕调试

[2] Linux MIPI DSI驱动开发 | 基于RK3399

[3] RK3399 LINUX-SDK MIPI屏幕驱动及调试