1 | /* |
2 | * Copyright (c) 2015 NVIDIA Corporation. All rights reserved. |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation |
7 | * the rights to use, copy, modify, merge, publish, distribute, sub license, |
8 | * and/or sell copies of the Software, and to permit persons to whom the |
9 | * Software is furnished to do so, subject to the following conditions: |
10 | * |
11 | * The above copyright notice and this permission notice (including the |
12 | * next paragraph) shall be included in all copies or substantial portions |
13 | * of the Software. |
14 | * |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL |
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
21 | * DEALINGS IN THE SOFTWARE. |
22 | */ |
23 | |
24 | #include <linux/i2c.h> |
25 | #include <linux/slab.h> |
26 | #include <linux/delay.h> |
27 | |
28 | #include <drm/display/drm_scdc_helper.h> |
29 | #include <drm/drm_connector.h> |
30 | #include <drm/drm_device.h> |
31 | #include <drm/drm_print.h> |
32 | |
33 | /** |
34 | * DOC: scdc helpers |
35 | * |
36 | * Status and Control Data Channel (SCDC) is a mechanism introduced by the |
37 | * HDMI 2.0 specification. It is a point-to-point protocol that allows the |
38 | * HDMI source and HDMI sink to exchange data. The same I2C interface that |
39 | * is used to access EDID serves as the transport mechanism for SCDC. |
40 | * |
41 | * Note: The SCDC status is going to be lost when the display is |
42 | * disconnected. This can happen physically when the user disconnects |
43 | * the cable, but also when a display is switched on (such as waking up |
44 | * a TV). |
45 | * |
46 | * This is further complicated by the fact that, upon a disconnection / |
47 | * reconnection, KMS won't change the mode on its own. This means that |
48 | * one can't just rely on setting the SCDC status on enable, but also |
49 | * has to track the connector status changes using interrupts and |
50 | * restore the SCDC status. The typical solution for this is to trigger an |
51 | * empty modeset in drm_connector_helper_funcs.detect_ctx(), like what vc4 does |
52 | * in vc4_hdmi_reset_link(). |
53 | */ |
54 | |
55 | #define SCDC_I2C_SLAVE_ADDRESS 0x54 |
56 | |
57 | /** |
58 | * drm_scdc_read - read a block of data from SCDC |
59 | * @adapter: I2C controller |
60 | * @offset: start offset of block to read |
61 | * @buffer: return location for the block to read |
62 | * @size: size of the block to read |
63 | * |
64 | * Reads a block of data from SCDC, starting at a given offset. |
65 | * |
66 | * Returns: |
67 | * 0 on success, negative error code on failure. |
68 | */ |
69 | ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer, |
70 | size_t size) |
71 | { |
72 | int ret; |
73 | struct i2c_msg msgs[2] = { |
74 | { |
75 | .addr = SCDC_I2C_SLAVE_ADDRESS, |
76 | .flags = 0, |
77 | .len = 1, |
78 | .buf = &offset, |
79 | }, { |
80 | .addr = SCDC_I2C_SLAVE_ADDRESS, |
81 | .flags = I2C_M_RD, |
82 | .len = size, |
83 | .buf = buffer, |
84 | } |
85 | }; |
86 | |
87 | ret = i2c_transfer(adap: adapter, msgs, ARRAY_SIZE(msgs)); |
88 | if (ret < 0) |
89 | return ret; |
90 | if (ret != ARRAY_SIZE(msgs)) |
91 | return -EPROTO; |
92 | |
93 | return 0; |
94 | } |
95 | EXPORT_SYMBOL(drm_scdc_read); |
96 | |
97 | /** |
98 | * drm_scdc_write - write a block of data to SCDC |
99 | * @adapter: I2C controller |
100 | * @offset: start offset of block to write |
101 | * @buffer: block of data to write |
102 | * @size: size of the block to write |
103 | * |
104 | * Writes a block of data to SCDC, starting at a given offset. |
105 | * |
106 | * Returns: |
107 | * 0 on success, negative error code on failure. |
108 | */ |
109 | ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset, |
110 | const void *buffer, size_t size) |
111 | { |
112 | struct i2c_msg msg = { |
113 | .addr = SCDC_I2C_SLAVE_ADDRESS, |
114 | .flags = 0, |
115 | .len = 1 + size, |
116 | .buf = NULL, |
117 | }; |
118 | void *data; |
119 | int err; |
120 | |
121 | data = kmalloc(size: 1 + size, GFP_KERNEL); |
122 | if (!data) |
123 | return -ENOMEM; |
124 | |
125 | msg.buf = data; |
126 | |
127 | memcpy(data, &offset, sizeof(offset)); |
128 | memcpy(data + 1, buffer, size); |
129 | |
130 | err = i2c_transfer(adap: adapter, msgs: &msg, num: 1); |
131 | |
132 | kfree(objp: data); |
133 | |
134 | if (err < 0) |
135 | return err; |
136 | if (err != 1) |
137 | return -EPROTO; |
138 | |
139 | return 0; |
140 | } |
141 | EXPORT_SYMBOL(drm_scdc_write); |
142 | |
143 | /** |
144 | * drm_scdc_get_scrambling_status - what is status of scrambling? |
145 | * @connector: connector |
146 | * |
147 | * Reads the scrambler status over SCDC, and checks the |
148 | * scrambling status. |
149 | * |
150 | * Returns: |
151 | * True if the scrambling is enabled, false otherwise. |
152 | */ |
153 | bool drm_scdc_get_scrambling_status(struct drm_connector *connector) |
154 | { |
155 | u8 status; |
156 | int ret; |
157 | |
158 | ret = drm_scdc_readb(adapter: connector->ddc, SCDC_SCRAMBLER_STATUS, value: &status); |
159 | if (ret < 0) { |
160 | drm_dbg_kms(connector->dev, |
161 | "[CONNECTOR:%d:%s] Failed to read scrambling status: %d\n" , |
162 | connector->base.id, connector->name, ret); |
163 | return false; |
164 | } |
165 | |
166 | return status & SCDC_SCRAMBLING_STATUS; |
167 | } |
168 | EXPORT_SYMBOL(drm_scdc_get_scrambling_status); |
169 | |
170 | /** |
171 | * drm_scdc_set_scrambling - enable scrambling |
172 | * @connector: connector |
173 | * @enable: bool to indicate if scrambling is to be enabled/disabled |
174 | * |
175 | * Writes the TMDS config register over SCDC channel, and: |
176 | * enables scrambling when enable = 1 |
177 | * disables scrambling when enable = 0 |
178 | * |
179 | * Returns: |
180 | * True if scrambling is set/reset successfully, false otherwise. |
181 | */ |
182 | bool drm_scdc_set_scrambling(struct drm_connector *connector, |
183 | bool enable) |
184 | { |
185 | u8 config; |
186 | int ret; |
187 | |
188 | ret = drm_scdc_readb(adapter: connector->ddc, SCDC_TMDS_CONFIG, value: &config); |
189 | if (ret < 0) { |
190 | drm_dbg_kms(connector->dev, |
191 | "[CONNECTOR:%d:%s] Failed to read TMDS config: %d\n" , |
192 | connector->base.id, connector->name, ret); |
193 | return false; |
194 | } |
195 | |
196 | if (enable) |
197 | config |= SCDC_SCRAMBLING_ENABLE; |
198 | else |
199 | config &= ~SCDC_SCRAMBLING_ENABLE; |
200 | |
201 | ret = drm_scdc_writeb(adapter: connector->ddc, SCDC_TMDS_CONFIG, value: config); |
202 | if (ret < 0) { |
203 | drm_dbg_kms(connector->dev, |
204 | "[CONNECTOR:%d:%s] Failed to enable scrambling: %d\n" , |
205 | connector->base.id, connector->name, ret); |
206 | return false; |
207 | } |
208 | |
209 | return true; |
210 | } |
211 | EXPORT_SYMBOL(drm_scdc_set_scrambling); |
212 | |
213 | /** |
214 | * drm_scdc_set_high_tmds_clock_ratio - set TMDS clock ratio |
215 | * @connector: connector |
216 | * @set: ret or reset the high clock ratio |
217 | * |
218 | * |
219 | * TMDS clock ratio calculations go like this: |
220 | * TMDS character = 10 bit TMDS encoded value |
221 | * |
222 | * TMDS character rate = The rate at which TMDS characters are |
223 | * transmitted (Mcsc) |
224 | * |
225 | * TMDS bit rate = 10x TMDS character rate |
226 | * |
227 | * As per the spec: |
228 | * TMDS clock rate for pixel clock < 340 MHz = 1x the character |
229 | * rate = 1/10 pixel clock rate |
230 | * |
231 | * TMDS clock rate for pixel clock > 340 MHz = 0.25x the character |
232 | * rate = 1/40 pixel clock rate |
233 | * |
234 | * Writes to the TMDS config register over SCDC channel, and: |
235 | * sets TMDS clock ratio to 1/40 when set = 1 |
236 | * |
237 | * sets TMDS clock ratio to 1/10 when set = 0 |
238 | * |
239 | * Returns: |
240 | * True if write is successful, false otherwise. |
241 | */ |
242 | bool drm_scdc_set_high_tmds_clock_ratio(struct drm_connector *connector, |
243 | bool set) |
244 | { |
245 | u8 config; |
246 | int ret; |
247 | |
248 | ret = drm_scdc_readb(adapter: connector->ddc, SCDC_TMDS_CONFIG, value: &config); |
249 | if (ret < 0) { |
250 | drm_dbg_kms(connector->dev, |
251 | "[CONNECTOR:%d:%s] Failed to read TMDS config: %d\n" , |
252 | connector->base.id, connector->name, ret); |
253 | return false; |
254 | } |
255 | |
256 | if (set) |
257 | config |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40; |
258 | else |
259 | config &= ~SCDC_TMDS_BIT_CLOCK_RATIO_BY_40; |
260 | |
261 | ret = drm_scdc_writeb(adapter: connector->ddc, SCDC_TMDS_CONFIG, value: config); |
262 | if (ret < 0) { |
263 | drm_dbg_kms(connector->dev, |
264 | "[CONNECTOR:%d:%s] Failed to set TMDS clock ratio: %d\n" , |
265 | connector->base.id, connector->name, ret); |
266 | return false; |
267 | } |
268 | |
269 | /* |
270 | * The spec says that a source should wait minimum 1ms and maximum |
271 | * 100ms after writing the TMDS config for clock ratio. Lets allow a |
272 | * wait of up to 2ms here. |
273 | */ |
274 | usleep_range(min: 1000, max: 2000); |
275 | return true; |
276 | } |
277 | EXPORT_SYMBOL(drm_scdc_set_high_tmds_clock_ratio); |
278 | |