Rev 4560 | Rev 5271 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 4560 | Rev 5060 | ||
---|---|---|---|
1 | /* |
1 | /* |
2 | * Copyright © 2009 Keith Packard |
2 | * Copyright © 2009 Keith Packard |
3 | * |
3 | * |
4 | * Permission to use, copy, modify, distribute, and sell this software and its |
4 | * Permission to use, copy, modify, distribute, and sell this software and its |
5 | * documentation for any purpose is hereby granted without fee, provided that |
5 | * documentation for any purpose is hereby granted without fee, provided that |
6 | * the above copyright notice appear in all copies and that both that copyright |
6 | * the above copyright notice appear in all copies and that both that copyright |
7 | * notice and this permission notice appear in supporting documentation, and |
7 | * notice and this permission notice appear in supporting documentation, and |
8 | * that the name of the copyright holders not be used in advertising or |
8 | * that the name of the copyright holders not be used in advertising or |
9 | * publicity pertaining to distribution of the software without specific, |
9 | * publicity pertaining to distribution of the software without specific, |
10 | * written prior permission. The copyright holders make no representations |
10 | * written prior permission. The copyright holders make no representations |
11 | * about the suitability of this software for any purpose. It is provided "as |
11 | * about the suitability of this software for any purpose. It is provided "as |
12 | * is" without express or implied warranty. |
12 | * is" without express or implied warranty. |
13 | * |
13 | * |
14 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
14 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
15 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO |
15 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO |
16 | * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
16 | * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
17 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, |
17 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, |
18 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
18 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
19 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE |
19 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE |
20 | * OF THIS SOFTWARE. |
20 | * OF THIS SOFTWARE. |
21 | */ |
21 | */ |
22 | 22 | ||
23 | #include |
23 | #include |
24 | #include |
24 | #include |
25 | //#include |
25 | //#include |
26 | //#include |
26 | //#include |
27 | #include |
27 | #include |
28 | #include |
28 | #include |
29 | #include |
29 | #include |
30 | #include |
- | |
31 | #include |
30 | #include |
- | 31 | #include |
|
32 | 32 | ||
33 | /** |
33 | /** |
34 | * DOC: dp helpers |
34 | * DOC: dp helpers |
35 | * |
35 | * |
36 | * These functions contain some common logic and helpers at various abstraction |
36 | * These functions contain some common logic and helpers at various abstraction |
37 | * levels to deal with Display Port sink devices and related things like DP aux |
37 | * levels to deal with Display Port sink devices and related things like DP aux |
38 | * channel transfers, EDID reading over DP aux channels, decoding certain DPCD |
38 | * channel transfers, EDID reading over DP aux channels, decoding certain DPCD |
39 | * blocks, ... |
39 | * blocks, ... |
40 | */ |
40 | */ |
41 | 41 | ||
42 | /* Run a single AUX_CH I2C transaction, writing/reading data as necessary */ |
42 | /* Run a single AUX_CH I2C transaction, writing/reading data as necessary */ |
43 | static int |
43 | static int |
44 | i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode, |
44 | i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode, |
45 | uint8_t write_byte, uint8_t *read_byte) |
45 | uint8_t write_byte, uint8_t *read_byte) |
46 | { |
46 | { |
47 | struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; |
47 | struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; |
48 | int ret; |
48 | int ret; |
49 | 49 | ||
50 | ret = (*algo_data->aux_ch)(adapter, mode, |
50 | ret = (*algo_data->aux_ch)(adapter, mode, |
51 | write_byte, read_byte); |
51 | write_byte, read_byte); |
52 | return ret; |
52 | return ret; |
53 | } |
53 | } |
54 | 54 | ||
55 | /* |
55 | /* |
56 | * I2C over AUX CH |
56 | * I2C over AUX CH |
57 | */ |
57 | */ |
58 | 58 | ||
59 | /* |
59 | /* |
60 | * Send the address. If the I2C link is running, this 'restarts' |
60 | * Send the address. If the I2C link is running, this 'restarts' |
61 | * the connection with the new address, this is used for doing |
61 | * the connection with the new address, this is used for doing |
62 | * a write followed by a read (as needed for DDC) |
62 | * a write followed by a read (as needed for DDC) |
63 | */ |
63 | */ |
64 | static int |
64 | static int |
65 | i2c_algo_dp_aux_address(struct i2c_adapter *adapter, u16 address, bool reading) |
65 | i2c_algo_dp_aux_address(struct i2c_adapter *adapter, u16 address, bool reading) |
66 | { |
66 | { |
67 | struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; |
67 | struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; |
68 | int mode = MODE_I2C_START; |
68 | int mode = MODE_I2C_START; |
69 | int ret; |
69 | int ret; |
70 | 70 | ||
71 | if (reading) |
71 | if (reading) |
72 | mode |= MODE_I2C_READ; |
72 | mode |= MODE_I2C_READ; |
73 | else |
73 | else |
74 | mode |= MODE_I2C_WRITE; |
74 | mode |= MODE_I2C_WRITE; |
75 | algo_data->address = address; |
75 | algo_data->address = address; |
76 | algo_data->running = true; |
76 | algo_data->running = true; |
77 | ret = i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL); |
77 | ret = i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL); |
78 | return ret; |
78 | return ret; |
79 | } |
79 | } |
80 | 80 | ||
81 | /* |
81 | /* |
82 | * Stop the I2C transaction. This closes out the link, sending |
82 | * Stop the I2C transaction. This closes out the link, sending |
83 | * a bare address packet with the MOT bit turned off |
83 | * a bare address packet with the MOT bit turned off |
84 | */ |
84 | */ |
85 | static void |
85 | static void |
86 | i2c_algo_dp_aux_stop(struct i2c_adapter *adapter, bool reading) |
86 | i2c_algo_dp_aux_stop(struct i2c_adapter *adapter, bool reading) |
87 | { |
87 | { |
88 | struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; |
88 | struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; |
89 | int mode = MODE_I2C_STOP; |
89 | int mode = MODE_I2C_STOP; |
90 | 90 | ||
91 | if (reading) |
91 | if (reading) |
92 | mode |= MODE_I2C_READ; |
92 | mode |= MODE_I2C_READ; |
93 | else |
93 | else |
94 | mode |= MODE_I2C_WRITE; |
94 | mode |= MODE_I2C_WRITE; |
95 | if (algo_data->running) { |
95 | if (algo_data->running) { |
96 | (void) i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL); |
96 | (void) i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL); |
97 | algo_data->running = false; |
97 | algo_data->running = false; |
98 | } |
98 | } |
99 | } |
99 | } |
100 | 100 | ||
101 | /* |
101 | /* |
102 | * Write a single byte to the current I2C address, the |
102 | * Write a single byte to the current I2C address, the |
103 | * the I2C link must be running or this returns -EIO |
103 | * the I2C link must be running or this returns -EIO |
104 | */ |
104 | */ |
105 | static int |
105 | static int |
106 | i2c_algo_dp_aux_put_byte(struct i2c_adapter *adapter, u8 byte) |
106 | i2c_algo_dp_aux_put_byte(struct i2c_adapter *adapter, u8 byte) |
107 | { |
107 | { |
108 | struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; |
108 | struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; |
109 | int ret; |
109 | int ret; |
110 | 110 | ||
111 | if (!algo_data->running) |
111 | if (!algo_data->running) |
112 | return -EIO; |
112 | return -EIO; |
113 | 113 | ||
114 | ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_WRITE, byte, NULL); |
114 | ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_WRITE, byte, NULL); |
115 | return ret; |
115 | return ret; |
116 | } |
116 | } |
117 | 117 | ||
118 | /* |
118 | /* |
119 | * Read a single byte from the current I2C address, the |
119 | * Read a single byte from the current I2C address, the |
120 | * I2C link must be running or this returns -EIO |
120 | * I2C link must be running or this returns -EIO |
121 | */ |
121 | */ |
122 | static int |
122 | static int |
123 | i2c_algo_dp_aux_get_byte(struct i2c_adapter *adapter, u8 *byte_ret) |
123 | i2c_algo_dp_aux_get_byte(struct i2c_adapter *adapter, u8 *byte_ret) |
124 | { |
124 | { |
125 | struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; |
125 | struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; |
126 | int ret; |
126 | int ret; |
127 | 127 | ||
128 | if (!algo_data->running) |
128 | if (!algo_data->running) |
129 | return -EIO; |
129 | return -EIO; |
130 | 130 | ||
131 | ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_READ, 0, byte_ret); |
131 | ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_READ, 0, byte_ret); |
132 | return ret; |
132 | return ret; |
133 | } |
133 | } |
134 | 134 | ||
135 | static int |
135 | static int |
136 | i2c_algo_dp_aux_xfer(struct i2c_adapter *adapter, |
136 | i2c_algo_dp_aux_xfer(struct i2c_adapter *adapter, |
137 | struct i2c_msg *msgs, |
137 | struct i2c_msg *msgs, |
138 | int num) |
138 | int num) |
139 | { |
139 | { |
140 | int ret = 0; |
140 | int ret = 0; |
141 | bool reading = false; |
141 | bool reading = false; |
142 | int m; |
142 | int m; |
143 | int b; |
143 | int b; |
144 | 144 | ||
145 | for (m = 0; m < num; m++) { |
145 | for (m = 0; m < num; m++) { |
146 | u16 len = msgs[m].len; |
146 | u16 len = msgs[m].len; |
147 | u8 *buf = msgs[m].buf; |
147 | u8 *buf = msgs[m].buf; |
148 | reading = (msgs[m].flags & I2C_M_RD) != 0; |
148 | reading = (msgs[m].flags & I2C_M_RD) != 0; |
149 | ret = i2c_algo_dp_aux_address(adapter, msgs[m].addr, reading); |
149 | ret = i2c_algo_dp_aux_address(adapter, msgs[m].addr, reading); |
150 | if (ret < 0) |
150 | if (ret < 0) |
151 | break; |
151 | break; |
152 | if (reading) { |
152 | if (reading) { |
153 | for (b = 0; b < len; b++) { |
153 | for (b = 0; b < len; b++) { |
154 | ret = i2c_algo_dp_aux_get_byte(adapter, &buf[b]); |
154 | ret = i2c_algo_dp_aux_get_byte(adapter, &buf[b]); |
155 | if (ret < 0) |
155 | if (ret < 0) |
156 | break; |
156 | break; |
157 | } |
157 | } |
158 | } else { |
158 | } else { |
159 | for (b = 0; b < len; b++) { |
159 | for (b = 0; b < len; b++) { |
160 | ret = i2c_algo_dp_aux_put_byte(adapter, buf[b]); |
160 | ret = i2c_algo_dp_aux_put_byte(adapter, buf[b]); |
161 | if (ret < 0) |
161 | if (ret < 0) |
162 | break; |
162 | break; |
163 | } |
163 | } |
164 | } |
164 | } |
165 | if (ret < 0) |
165 | if (ret < 0) |
166 | break; |
166 | break; |
167 | } |
167 | } |
168 | if (ret >= 0) |
168 | if (ret >= 0) |
169 | ret = num; |
169 | ret = num; |
170 | i2c_algo_dp_aux_stop(adapter, reading); |
170 | i2c_algo_dp_aux_stop(adapter, reading); |
171 | DRM_DEBUG_KMS("dp_aux_xfer return %d\n", ret); |
171 | DRM_DEBUG_KMS("dp_aux_xfer return %d\n", ret); |
172 | return ret; |
172 | return ret; |
173 | } |
173 | } |
174 | 174 | ||
175 | static u32 |
175 | static u32 |
176 | i2c_algo_dp_aux_functionality(struct i2c_adapter *adapter) |
176 | i2c_algo_dp_aux_functionality(struct i2c_adapter *adapter) |
177 | { |
177 | { |
178 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | |
178 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | |
179 | I2C_FUNC_SMBUS_READ_BLOCK_DATA | |
179 | I2C_FUNC_SMBUS_READ_BLOCK_DATA | |
180 | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | |
180 | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | |
181 | I2C_FUNC_10BIT_ADDR; |
181 | I2C_FUNC_10BIT_ADDR; |
182 | } |
182 | } |
183 | 183 | ||
184 | static const struct i2c_algorithm i2c_dp_aux_algo = { |
184 | static const struct i2c_algorithm i2c_dp_aux_algo = { |
185 | .master_xfer = i2c_algo_dp_aux_xfer, |
185 | .master_xfer = i2c_algo_dp_aux_xfer, |
186 | .functionality = i2c_algo_dp_aux_functionality, |
186 | .functionality = i2c_algo_dp_aux_functionality, |
187 | }; |
187 | }; |
188 | 188 | ||
189 | static void |
189 | static void |
190 | i2c_dp_aux_reset_bus(struct i2c_adapter *adapter) |
190 | i2c_dp_aux_reset_bus(struct i2c_adapter *adapter) |
191 | { |
191 | { |
192 | (void) i2c_algo_dp_aux_address(adapter, 0, false); |
192 | (void) i2c_algo_dp_aux_address(adapter, 0, false); |
193 | (void) i2c_algo_dp_aux_stop(adapter, false); |
193 | (void) i2c_algo_dp_aux_stop(adapter, false); |
194 | } |
194 | } |
195 | 195 | ||
196 | static int |
196 | static int |
197 | i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter) |
197 | i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter) |
198 | { |
198 | { |
199 | adapter->algo = &i2c_dp_aux_algo; |
199 | adapter->algo = &i2c_dp_aux_algo; |
200 | adapter->retries = 3; |
200 | adapter->retries = 3; |
201 | i2c_dp_aux_reset_bus(adapter); |
201 | i2c_dp_aux_reset_bus(adapter); |
202 | return 0; |
202 | return 0; |
203 | } |
203 | } |
204 | 204 | ||
205 | /** |
205 | /** |
206 | * i2c_dp_aux_add_bus() - register an i2c adapter using the aux ch helper |
206 | * i2c_dp_aux_add_bus() - register an i2c adapter using the aux ch helper |
207 | * @adapter: i2c adapter to register |
207 | * @adapter: i2c adapter to register |
208 | * |
208 | * |
209 | * This registers an i2c adapater that uses dp aux channel as it's underlaying |
209 | * This registers an i2c adapter that uses dp aux channel as it's underlaying |
210 | * transport. The driver needs to fill out the &i2c_algo_dp_aux_data structure |
210 | * transport. The driver needs to fill out the &i2c_algo_dp_aux_data structure |
211 | * and store it in the algo_data member of the @adapter argument. This will be |
211 | * and store it in the algo_data member of the @adapter argument. This will be |
212 | * used by the i2c over dp aux algorithm to drive the hardware. |
212 | * used by the i2c over dp aux algorithm to drive the hardware. |
213 | * |
213 | * |
214 | * RETURNS: |
214 | * RETURNS: |
215 | * 0 on success, -ERRNO on failure. |
215 | * 0 on success, -ERRNO on failure. |
- | 216 | * |
|
- | 217 | * IMPORTANT: |
|
- | 218 | * This interface is deprecated, please switch to the new dp aux helpers and |
|
- | 219 | * drm_dp_aux_register(). |
|
216 | */ |
220 | */ |
217 | int |
221 | int |
218 | i2c_dp_aux_add_bus(struct i2c_adapter *adapter) |
222 | i2c_dp_aux_add_bus(struct i2c_adapter *adapter) |
219 | { |
223 | { |
220 | int error; |
224 | int error; |
221 | 225 | ||
222 | error = i2c_dp_aux_prepare_bus(adapter); |
226 | error = i2c_dp_aux_prepare_bus(adapter); |
223 | if (error) |
227 | if (error) |
224 | return error; |
228 | return error; |
225 | error = i2c_add_adapter(adapter); |
229 | error = i2c_add_adapter(adapter); |
226 | return error; |
230 | return error; |
227 | } |
231 | } |
228 | EXPORT_SYMBOL(i2c_dp_aux_add_bus); |
232 | EXPORT_SYMBOL(i2c_dp_aux_add_bus); |
229 | 233 | ||
230 | /* Helpers for DP link training */ |
234 | /* Helpers for DP link training */ |
231 | static u8 dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r) |
235 | static u8 dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r) |
232 | { |
236 | { |
233 | return link_status[r - DP_LANE0_1_STATUS]; |
237 | return link_status[r - DP_LANE0_1_STATUS]; |
234 | } |
238 | } |
235 | 239 | ||
236 | static u8 dp_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE], |
240 | static u8 dp_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE], |
237 | int lane) |
241 | int lane) |
238 | { |
242 | { |
239 | int i = DP_LANE0_1_STATUS + (lane >> 1); |
243 | int i = DP_LANE0_1_STATUS + (lane >> 1); |
240 | int s = (lane & 1) * 4; |
244 | int s = (lane & 1) * 4; |
241 | u8 l = dp_link_status(link_status, i); |
245 | u8 l = dp_link_status(link_status, i); |
242 | return (l >> s) & 0xf; |
246 | return (l >> s) & 0xf; |
243 | } |
247 | } |
244 | 248 | ||
245 | bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE], |
249 | bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE], |
246 | int lane_count) |
250 | int lane_count) |
247 | { |
251 | { |
248 | u8 lane_align; |
252 | u8 lane_align; |
249 | u8 lane_status; |
253 | u8 lane_status; |
250 | int lane; |
254 | int lane; |
251 | 255 | ||
252 | lane_align = dp_link_status(link_status, |
256 | lane_align = dp_link_status(link_status, |
253 | DP_LANE_ALIGN_STATUS_UPDATED); |
257 | DP_LANE_ALIGN_STATUS_UPDATED); |
254 | if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) |
258 | if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) |
255 | return false; |
259 | return false; |
256 | for (lane = 0; lane < lane_count; lane++) { |
260 | for (lane = 0; lane < lane_count; lane++) { |
257 | lane_status = dp_get_lane_status(link_status, lane); |
261 | lane_status = dp_get_lane_status(link_status, lane); |
258 | if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS) |
262 | if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS) |
259 | return false; |
263 | return false; |
260 | } |
264 | } |
261 | return true; |
265 | return true; |
262 | } |
266 | } |
263 | EXPORT_SYMBOL(drm_dp_channel_eq_ok); |
267 | EXPORT_SYMBOL(drm_dp_channel_eq_ok); |
264 | 268 | ||
265 | bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE], |
269 | bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE], |
266 | int lane_count) |
270 | int lane_count) |
267 | { |
271 | { |
268 | int lane; |
272 | int lane; |
269 | u8 lane_status; |
273 | u8 lane_status; |
270 | 274 | ||
271 | for (lane = 0; lane < lane_count; lane++) { |
275 | for (lane = 0; lane < lane_count; lane++) { |
272 | lane_status = dp_get_lane_status(link_status, lane); |
276 | lane_status = dp_get_lane_status(link_status, lane); |
273 | if ((lane_status & DP_LANE_CR_DONE) == 0) |
277 | if ((lane_status & DP_LANE_CR_DONE) == 0) |
274 | return false; |
278 | return false; |
275 | } |
279 | } |
276 | return true; |
280 | return true; |
277 | } |
281 | } |
278 | EXPORT_SYMBOL(drm_dp_clock_recovery_ok); |
282 | EXPORT_SYMBOL(drm_dp_clock_recovery_ok); |
279 | 283 | ||
280 | u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE], |
284 | u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE], |
281 | int lane) |
285 | int lane) |
282 | { |
286 | { |
283 | int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); |
287 | int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); |
284 | int s = ((lane & 1) ? |
288 | int s = ((lane & 1) ? |
285 | DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : |
289 | DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : |
286 | DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); |
290 | DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); |
287 | u8 l = dp_link_status(link_status, i); |
291 | u8 l = dp_link_status(link_status, i); |
288 | 292 | ||
289 | return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; |
293 | return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; |
290 | } |
294 | } |
291 | EXPORT_SYMBOL(drm_dp_get_adjust_request_voltage); |
295 | EXPORT_SYMBOL(drm_dp_get_adjust_request_voltage); |
292 | 296 | ||
293 | u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE], |
297 | u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE], |
294 | int lane) |
298 | int lane) |
295 | { |
299 | { |
296 | int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); |
300 | int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); |
297 | int s = ((lane & 1) ? |
301 | int s = ((lane & 1) ? |
298 | DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : |
302 | DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : |
299 | DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); |
303 | DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); |
300 | u8 l = dp_link_status(link_status, i); |
304 | u8 l = dp_link_status(link_status, i); |
301 | 305 | ||
302 | return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; |
306 | return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; |
303 | } |
307 | } |
304 | EXPORT_SYMBOL(drm_dp_get_adjust_request_pre_emphasis); |
308 | EXPORT_SYMBOL(drm_dp_get_adjust_request_pre_emphasis); |
305 | 309 | ||
306 | void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) { |
310 | void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) { |
307 | if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0) |
311 | if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0) |
308 | udelay(100); |
312 | udelay(100); |
309 | else |
313 | else |
310 | mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4); |
314 | mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4); |
311 | } |
315 | } |
312 | EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay); |
316 | EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay); |
313 | 317 | ||
314 | void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) { |
318 | void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) { |
315 | if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0) |
319 | if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0) |
316 | udelay(400); |
320 | udelay(400); |
317 | else |
321 | else |
318 | mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4); |
322 | mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4); |
319 | } |
323 | } |
320 | EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay); |
324 | EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay); |
321 | 325 | ||
322 | u8 drm_dp_link_rate_to_bw_code(int link_rate) |
326 | u8 drm_dp_link_rate_to_bw_code(int link_rate) |
323 | { |
327 | { |
324 | switch (link_rate) { |
328 | switch (link_rate) { |
325 | case 162000: |
329 | case 162000: |
326 | default: |
330 | default: |
327 | return DP_LINK_BW_1_62; |
331 | return DP_LINK_BW_1_62; |
328 | case 270000: |
332 | case 270000: |
329 | return DP_LINK_BW_2_7; |
333 | return DP_LINK_BW_2_7; |
330 | case 540000: |
334 | case 540000: |
331 | return DP_LINK_BW_5_4; |
335 | return DP_LINK_BW_5_4; |
332 | } |
336 | } |
333 | } |
337 | } |
334 | EXPORT_SYMBOL(drm_dp_link_rate_to_bw_code); |
338 | EXPORT_SYMBOL(drm_dp_link_rate_to_bw_code); |
335 | 339 | ||
336 | int drm_dp_bw_code_to_link_rate(u8 link_bw) |
340 | int drm_dp_bw_code_to_link_rate(u8 link_bw) |
337 | { |
341 | { |
338 | switch (link_bw) { |
342 | switch (link_bw) { |
339 | case DP_LINK_BW_1_62: |
343 | case DP_LINK_BW_1_62: |
340 | default: |
344 | default: |
341 | return 162000; |
345 | return 162000; |
342 | case DP_LINK_BW_2_7: |
346 | case DP_LINK_BW_2_7: |
343 | return 270000; |
347 | return 270000; |
344 | case DP_LINK_BW_5_4: |
348 | case DP_LINK_BW_5_4: |
345 | return 540000; |
349 | return 540000; |
346 | } |
350 | } |
347 | } |
351 | } |
348 | EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate);><>><>>>>>>>>>> |
352 | EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate); |
- | 353 | ||
- | 354 | /** |
|
- | 355 | * DOC: dp helpers |
|
- | 356 | * |
|
- | 357 | * The DisplayPort AUX channel is an abstraction to allow generic, driver- |
|
- | 358 | * independent access to AUX functionality. Drivers can take advantage of |
|
- | 359 | * this by filling in the fields of the drm_dp_aux structure. |
|
- | 360 | * |
|
- | 361 | * Transactions are described using a hardware-independent drm_dp_aux_msg |
|
- | 362 | * structure, which is passed into a driver's .transfer() implementation. |
|
- | 363 | * Both native and I2C-over-AUX transactions are supported. |
|
- | 364 | */ |
|
- | 365 | ||
- | 366 | static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request, |
|
- | 367 | unsigned int offset, void *buffer, size_t size) |
|
- | 368 | { |
|
- | 369 | struct drm_dp_aux_msg msg; |
|
- | 370 | unsigned int retry; |
|
- | 371 | int err; |
|
- | 372 | ||
- | 373 | memset(&msg, 0, sizeof(msg)); |
|
- | 374 | msg.address = offset; |
|
- | 375 | msg.request = request; |
|
- | 376 | msg.buffer = buffer; |
|
- | 377 | msg.size = size; |
|
- | 378 | ||
- | 379 | /* |
|
- | 380 | * The specification doesn't give any recommendation on how often to |
|
- | 381 | * retry native transactions, so retry 7 times like for I2C-over-AUX |
|
- | 382 | * transactions. |
|
- | 383 | */ |
|
- | 384 | for (retry = 0; retry < 7; retry++) { |
|
- | 385 | ||
- | 386 | mutex_lock(&aux->hw_mutex); |
|
- | 387 | err = aux->transfer(aux, &msg); |
|
- | 388 | mutex_unlock(&aux->hw_mutex); |
|
- | 389 | if (err < 0) { |
|
- | 390 | if (err == -EBUSY) |
|
- | 391 | continue; |
|
- | 392 | ||
- | 393 | return err; |
|
- | 394 | } |
|
- | 395 | ||
- | 396 | ||
- | 397 | switch (msg.reply & DP_AUX_NATIVE_REPLY_MASK) { |
|
- | 398 | case DP_AUX_NATIVE_REPLY_ACK: |
|
- | 399 | if (err < size) |
|
- | 400 | return -EPROTO; |
|
- | 401 | return err; |
|
- | 402 | ||
- | 403 | case DP_AUX_NATIVE_REPLY_NACK: |
|
- | 404 | return -EIO; |
|
- | 405 | ||
- | 406 | case DP_AUX_NATIVE_REPLY_DEFER: |
|
- | 407 | usleep(500); |
|
- | 408 | break; |
|
- | 409 | } |
|
- | 410 | } |
|
- | 411 | ||
- | 412 | DRM_DEBUG_KMS("too many retries, giving up\n"); |
|
- | 413 | return -EIO; |
|
- | 414 | } |
|
- | 415 | ||
- | 416 | /** |
|
- | 417 | * drm_dp_dpcd_read() - read a series of bytes from the DPCD |
|
- | 418 | * @aux: DisplayPort AUX channel |
|
- | 419 | * @offset: address of the (first) register to read |
|
- | 420 | * @buffer: buffer to store the register values |
|
- | 421 | * @size: number of bytes in @buffer |
|
- | 422 | * |
|
- | 423 | * Returns the number of bytes transferred on success, or a negative error |
|
- | 424 | * code on failure. -EIO is returned if the request was NAKed by the sink or |
|
- | 425 | * if the retry count was exceeded. If not all bytes were transferred, this |
|
- | 426 | * function returns -EPROTO. Errors from the underlying AUX channel transfer |
|
- | 427 | * function, with the exception of -EBUSY (which causes the transaction to |
|
- | 428 | * be retried), are propagated to the caller. |
|
- | 429 | */ |
|
- | 430 | ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset, |
|
- | 431 | void *buffer, size_t size) |
|
- | 432 | { |
|
- | 433 | return drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset, buffer, |
|
- | 434 | size); |
|
- | 435 | } |
|
- | 436 | EXPORT_SYMBOL(drm_dp_dpcd_read); |
|
- | 437 | ||
- | 438 | /** |
|
- | 439 | * drm_dp_dpcd_write() - write a series of bytes to the DPCD |
|
- | 440 | * @aux: DisplayPort AUX channel |
|
- | 441 | * @offset: address of the (first) register to write |
|
- | 442 | * @buffer: buffer containing the values to write |
|
- | 443 | * @size: number of bytes in @buffer |
|
- | 444 | * |
|
- | 445 | * Returns the number of bytes transferred on success, or a negative error |
|
- | 446 | * code on failure. -EIO is returned if the request was NAKed by the sink or |
|
- | 447 | * if the retry count was exceeded. If not all bytes were transferred, this |
|
- | 448 | * function returns -EPROTO. Errors from the underlying AUX channel transfer |
|
- | 449 | * function, with the exception of -EBUSY (which causes the transaction to |
|
- | 450 | * be retried), are propagated to the caller. |
|
- | 451 | */ |
|
- | 452 | ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset, |
|
- | 453 | void *buffer, size_t size) |
|
- | 454 | { |
|
- | 455 | return drm_dp_dpcd_access(aux, DP_AUX_NATIVE_WRITE, offset, buffer, |
|
- | 456 | size); |
|
- | 457 | } |
|
- | 458 | EXPORT_SYMBOL(drm_dp_dpcd_write); |
|
- | 459 | ||
- | 460 | /** |
|
- | 461 | * drm_dp_dpcd_read_link_status() - read DPCD link status (bytes 0x202-0x207) |
|
- | 462 | * @aux: DisplayPort AUX channel |
|
- | 463 | * @status: buffer to store the link status in (must be at least 6 bytes) |
|
- | 464 | * |
|
- | 465 | * Returns the number of bytes transferred on success or a negative error |
|
- | 466 | * code on failure. |
|
- | 467 | */ |
|
- | 468 | int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux, |
|
- | 469 | u8 status[DP_LINK_STATUS_SIZE]) |
|
- | 470 | { |
|
- | 471 | return drm_dp_dpcd_read(aux, DP_LANE0_1_STATUS, status, |
|
- | 472 | DP_LINK_STATUS_SIZE); |
|
- | 473 | } |
|
- | 474 | EXPORT_SYMBOL(drm_dp_dpcd_read_link_status); |
|
- | 475 | ||
- | 476 | /** |
|
- | 477 | * drm_dp_link_probe() - probe a DisplayPort link for capabilities |
|
- | 478 | * @aux: DisplayPort AUX channel |
|
- | 479 | * @link: pointer to structure in which to return link capabilities |
|
- | 480 | * |
|
- | 481 | * The structure filled in by this function can usually be passed directly |
|
- | 482 | * into drm_dp_link_power_up() and drm_dp_link_configure() to power up and |
|
- | 483 | * configure the link based on the link's capabilities. |
|
- | 484 | * |
|
- | 485 | * Returns 0 on success or a negative error code on failure. |
|
- | 486 | */ |
|
- | 487 | int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) |
|
- | 488 | { |
|
- | 489 | u8 values[3]; |
|
- | 490 | int err; |
|
- | 491 | ||
- | 492 | memset(link, 0, sizeof(*link)); |
|
- | 493 | ||
- | 494 | err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values)); |
|
- | 495 | if (err < 0) |
|
- | 496 | return err; |
|
- | 497 | ||
- | 498 | link->revision = values[0]; |
|
- | 499 | link->rate = drm_dp_bw_code_to_link_rate(values[1]); |
|
- | 500 | link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK; |
|
- | 501 | ||
- | 502 | if (values[2] & DP_ENHANCED_FRAME_CAP) |
|
- | 503 | link->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING; |
|
- | 504 | ||
- | 505 | return 0; |
|
- | 506 | } |
|
- | 507 | EXPORT_SYMBOL(drm_dp_link_probe); |
|
- | 508 | ||
- | 509 | /** |
|
- | 510 | * drm_dp_link_power_up() - power up a DisplayPort link |
|
- | 511 | * @aux: DisplayPort AUX channel |
|
- | 512 | * @link: pointer to a structure containing the link configuration |
|
- | 513 | * |
|
- | 514 | * Returns 0 on success or a negative error code on failure. |
|
- | 515 | */ |
|
- | 516 | int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link) |
|
- | 517 | { |
|
- | 518 | u8 value; |
|
- | 519 | int err; |
|
- | 520 | ||
- | 521 | /* DP_SET_POWER register is only available on DPCD v1.1 and later */ |
|
- | 522 | if (link->revision < 0x11) |
|
- | 523 | return 0; |
|
- | 524 | ||
- | 525 | err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value); |
|
- | 526 | if (err < 0) |
|
- | 527 | return err; |
|
- | 528 | ||
- | 529 | value &= ~DP_SET_POWER_MASK; |
|
- | 530 | value |= DP_SET_POWER_D0; |
|
- | 531 | ||
- | 532 | err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value); |
|
- | 533 | if (err < 0) |
|
- | 534 | return err; |
|
- | 535 | ||
- | 536 | /* |
|
- | 537 | * According to the DP 1.1 specification, a "Sink Device must exit the |
|
- | 538 | * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink |
|
- | 539 | * Control Field" (register 0x600). |
|
- | 540 | */ |
|
- | 541 | usleep(2000); |
|
- | 542 | ||
- | 543 | return 0; |
|
- | 544 | } |
|
- | 545 | EXPORT_SYMBOL(drm_dp_link_power_up); |
|
- | 546 | ||
- | 547 | /** |
|
- | 548 | * drm_dp_link_configure() - configure a DisplayPort link |
|
- | 549 | * @aux: DisplayPort AUX channel |
|
- | 550 | * @link: pointer to a structure containing the link configuration |
|
- | 551 | * |
|
- | 552 | * Returns 0 on success or a negative error code on failure. |
|
- | 553 | */ |
|
- | 554 | int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link) |
|
- | 555 | { |
|
- | 556 | u8 values[2]; |
|
- | 557 | int err; |
|
- | 558 | ||
- | 559 | values[0] = drm_dp_link_rate_to_bw_code(link->rate); |
|
- | 560 | values[1] = link->num_lanes; |
|
- | 561 | ||
- | 562 | if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING) |
|
- | 563 | values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; |
|
- | 564 | ||
- | 565 | err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values)); |
|
- | 566 | if (err < 0) |
|
- | 567 | return err; |
|
- | 568 | ||
- | 569 | return 0; |
|
- | 570 | } |
|
- | 571 | EXPORT_SYMBOL(drm_dp_link_configure); |
|
- | 572 | ||
- | 573 | /* |
|
- | 574 | * I2C-over-AUX implementation |
|
- | 575 | */ |
|
- | 576 | ||
- | 577 | static u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter) |
|
- | 578 | { |
|
- | 579 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | |
|
- | 580 | I2C_FUNC_SMBUS_READ_BLOCK_DATA | |
|
- | 581 | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | |
|
- | 582 | I2C_FUNC_10BIT_ADDR; |
|
- | 583 | } |
|
- | 584 | ||
- | 585 | /* |
|
- | 586 | * Transfer a single I2C-over-AUX message and handle various error conditions, |
|
- | 587 | * retrying the transaction as appropriate. It is assumed that the |
|
- | 588 | * aux->transfer function does not modify anything in the msg other than the |
|
- | 589 | * reply field. |
|
- | 590 | */ |
|
- | 591 | static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) |
|
- | 592 | { |
|
- | 593 | unsigned int retry; |
|
- | 594 | int err; |
|
- | 595 | ||
- | 596 | /* |
|
- | 597 | * DP1.2 sections 2.7.7.1.5.6.1 and 2.7.7.1.6.6.1: A DP Source device |
|
- | 598 | * is required to retry at least seven times upon receiving AUX_DEFER |
|
- | 599 | * before giving up the AUX transaction. |
|
- | 600 | */ |
|
- | 601 | for (retry = 0; retry < 7; retry++) { |
|
- | 602 | mutex_lock(&aux->hw_mutex); |
|
- | 603 | err = aux->transfer(aux, msg); |
|
- | 604 | mutex_unlock(&aux->hw_mutex); |
|
- | 605 | if (err < 0) { |
|
- | 606 | if (err == -EBUSY) |
|
- | 607 | continue; |
|
- | 608 | ||
- | 609 | DRM_DEBUG_KMS("transaction failed: %d\n", err); |
|
- | 610 | return err; |
|
- | 611 | } |
|
- | 612 | ||
- | 613 | ||
- | 614 | switch (msg->reply & DP_AUX_NATIVE_REPLY_MASK) { |
|
- | 615 | case DP_AUX_NATIVE_REPLY_ACK: |
|
- | 616 | /* |
|
- | 617 | * For I2C-over-AUX transactions this isn't enough, we |
|
- | 618 | * need to check for the I2C ACK reply. |
|
- | 619 | */ |
|
- | 620 | break; |
|
- | 621 | ||
- | 622 | case DP_AUX_NATIVE_REPLY_NACK: |
|
- | 623 | DRM_DEBUG_KMS("native nack\n"); |
|
- | 624 | return -EREMOTEIO; |
|
- | 625 | ||
- | 626 | case DP_AUX_NATIVE_REPLY_DEFER: |
|
- | 627 | DRM_DEBUG_KMS("native defer"); |
|
- | 628 | /* |
|
- | 629 | * We could check for I2C bit rate capabilities and if |
|
- | 630 | * available adjust this interval. We could also be |
|
- | 631 | * more careful with DP-to-legacy adapters where a |
|
- | 632 | * long legacy cable may force very low I2C bit rates. |
|
- | 633 | * |
|
- | 634 | * For now just defer for long enough to hopefully be |
|
- | 635 | * safe for all use-cases. |
|
- | 636 | */ |
|
- | 637 | usleep_range(500, 600); |
|
- | 638 | continue; |
|
- | 639 | ||
- | 640 | default: |
|
- | 641 | DRM_ERROR("invalid native reply %#04x\n", msg->reply); |
|
- | 642 | return -EREMOTEIO; |
|
- | 643 | } |
|
- | 644 | ||
- | 645 | switch (msg->reply & DP_AUX_I2C_REPLY_MASK) { |
|
- | 646 | case DP_AUX_I2C_REPLY_ACK: |
|
- | 647 | /* |
|
- | 648 | * Both native ACK and I2C ACK replies received. We |
|
- | 649 | * can assume the transfer was successful. |
|
- | 650 | */ |
|
- | 651 | if (err < msg->size) |
|
- | 652 | return -EPROTO; |
|
- | 653 | return 0; |
|
- | 654 | ||
- | 655 | case DP_AUX_I2C_REPLY_NACK: |
|
- | 656 | DRM_DEBUG_KMS("I2C nack\n"); |
|
- | 657 | return -EREMOTEIO; |
|
- | 658 | ||
- | 659 | case DP_AUX_I2C_REPLY_DEFER: |
|
- | 660 | DRM_DEBUG_KMS("I2C defer\n"); |
|
- | 661 | usleep_range(400, 500); |
|
- | 662 | continue; |
|
- | 663 | ||
- | 664 | default: |
|
- | 665 | DRM_ERROR("invalid I2C reply %#04x\n", msg->reply); |
|
- | 666 | return -EREMOTEIO; |
|
- | 667 | } |
|
- | 668 | } |
|
- | 669 | ||
- | 670 | DRM_DEBUG_KMS("too many retries, giving up\n"); |
|
- | 671 | return -EREMOTEIO; |
|
- | 672 | } |
|
- | 673 | ||
- | 674 | static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, |
|
- | 675 | int num) |
|
- | 676 | { |
|
- | 677 | struct drm_dp_aux *aux = adapter->algo_data; |
|
- | 678 | unsigned int i, j; |
|
- | 679 | struct drm_dp_aux_msg msg; |
|
- | 680 | int err = 0; |
|
- | 681 | ||
- | 682 | memset(&msg, 0, sizeof(msg)); |
|
- | 683 | ||
- | 684 | for (i = 0; i < num; i++) { |
|
- | 685 | msg.address = msgs[i].addr; |
|
- | 686 | msg.request = (msgs[i].flags & I2C_M_RD) ? |
|
- | 687 | DP_AUX_I2C_READ : |
|
- | 688 | DP_AUX_I2C_WRITE; |
|
- | 689 | msg.request |= DP_AUX_I2C_MOT; |
|
- | 690 | /* Send a bare address packet to start the transaction. |
|
- | 691 | * Zero sized messages specify an address only (bare |
|
- | 692 | * address) transaction. |
|
- | 693 | */ |
|
- | 694 | msg.buffer = NULL; |
|
- | 695 | msg.size = 0; |
|
- | 696 | err = drm_dp_i2c_do_msg(aux, &msg); |
|
- | 697 | if (err < 0) |
|
- | 698 | break; |
|
- | 699 | /* |
|
- | 700 | * Many hardware implementations support FIFOs larger than a |
|
- | 701 | * single byte, but it has been empirically determined that |
|
- | 702 | * transferring data in larger chunks can actually lead to |
|
- | 703 | * decreased performance. Therefore each message is simply |
|
- | 704 | * transferred byte-by-byte. |
|
- | 705 | */ |
|
- | 706 | for (j = 0; j < msgs[i].len; j++) { |
|
- | 707 | msg.buffer = msgs[i].buf + j; |
|
- | 708 | msg.size = 1; |
|
- | 709 | ||
- | 710 | err = drm_dp_i2c_do_msg(aux, &msg); |
|
- | 711 | if (err < 0) |
|
- | 712 | break; |
|
- | 713 | } |
|
- | 714 | if (err < 0) |
|
- | 715 | break; |
|
- | 716 | } |
|
- | 717 | if (err >= 0) |
|
- | 718 | err = num; |
|
- | 719 | /* Send a bare address packet to close out the transaction. |
|
- | 720 | * Zero sized messages specify an address only (bare |
|
- | 721 | * address) transaction. |
|
- | 722 | */ |
|
- | 723 | msg.request &= ~DP_AUX_I2C_MOT; |
|
- | 724 | msg.buffer = NULL; |
|
- | 725 | msg.size = 0; |
|
- | 726 | (void)drm_dp_i2c_do_msg(aux, &msg); |
|
- | 727 | ||
- | 728 | return err; |
|
- | 729 | } |
|
- | 730 | ||
- | 731 | static const struct i2c_algorithm drm_dp_i2c_algo = { |
|
- | 732 | .functionality = drm_dp_i2c_functionality, |
|
- | 733 | .master_xfer = drm_dp_i2c_xfer, |
|
- | 734 | }; |
|
- | 735 | ||
- | 736 | /** |
|
- | 737 | * drm_dp_aux_register() - initialise and register aux channel |
|
- | 738 | * @aux: DisplayPort AUX channel |
|
- | 739 | * |
|
- | 740 | * Returns 0 on success or a negative error code on failure. |
|
- | 741 | */ |
|
- | 742 | int drm_dp_aux_register(struct drm_dp_aux *aux) |
|
- | 743 | { |
|
- | 744 | mutex_init(&aux->hw_mutex); |
|
- | 745 | ||
- | 746 | aux->ddc.algo = &drm_dp_i2c_algo; |
|
- | 747 | aux->ddc.algo_data = aux; |
|
- | 748 | aux->ddc.retries = 3; |
|
- | 749 | ||
- | 750 | aux->ddc.class = I2C_CLASS_DDC; |
|
- | 751 | aux->ddc.owner = THIS_MODULE; |
|
- | 752 | aux->ddc.dev.parent = aux->dev; |
|
- | 753 | // aux->ddc.dev.of_node = aux->dev->of_node; |
|
- | 754 | ||
- | 755 | strlcpy(aux->ddc.name, aux->name ? aux->name : "aux", |
|
- | 756 | sizeof(aux->ddc.name)); |
|
- | 757 | ||
- | 758 | return i2c_add_adapter(&aux->ddc); |
|
- | 759 | } |
|
- | 760 | EXPORT_SYMBOL(drm_dp_aux_register); |
|
- | 761 | ||
- | 762 | /** |
|
- | 763 | * drm_dp_aux_unregister() - unregister an AUX adapter |
|
- | 764 | * @aux: DisplayPort AUX channel |
|
- | 765 | */ |
|
- | 766 | void drm_dp_aux_unregister(struct drm_dp_aux *aux) |
|
- | 767 | { |
|
- | 768 | i2c_del_adapter(&aux->ddc); |
|
- | 769 | } |
|
- | 770 | EXPORT_SYMBOL(drm_dp_aux_unregister);>>>>>>>>>>>>>>>>><>><>>>>>>>>>> |