1 | /* |
2 | * Copyright (C) 2010 Francisco Jerez. |
3 | * All Rights Reserved. |
4 | * |
5 | * Permission is hereby granted, free of charge, to any person obtaining |
6 | * a copy of this software and associated documentation files (the |
7 | * "Software"), to deal in the Software without restriction, including |
8 | * without limitation the rights to use, copy, modify, merge, publish, |
9 | * distribute, sublicense, and/or sell copies of the Software, and to |
10 | * permit persons to whom the Software is furnished to do so, subject to |
11 | * the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice (including the |
14 | * next paragraph) shall be included in all copies or substantial |
15 | * portions of the Software. |
16 | * |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE |
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
24 | * |
25 | */ |
26 | |
27 | #include <linux/module.h> |
28 | |
29 | #include <drm/drm_drv.h> |
30 | #include <drm/drm_encoder_slave.h> |
31 | #include <drm/drm_print.h> |
32 | #include <drm/drm_probe_helper.h> |
33 | #include <drm/i2c/sil164.h> |
34 | |
35 | struct sil164_priv { |
36 | struct sil164_encoder_params config; |
37 | struct i2c_client *duallink_slave; |
38 | |
39 | uint8_t saved_state[0x10]; |
40 | uint8_t saved_slave_state[0x10]; |
41 | }; |
42 | |
43 | #define to_sil164_priv(x) \ |
44 | ((struct sil164_priv *)to_encoder_slave(x)->slave_priv) |
45 | |
46 | #define sil164_dbg(client, format, ...) do { \ |
47 | if (drm_debug_enabled(DRM_UT_KMS)) \ |
48 | dev_printk(KERN_DEBUG, &client->dev, \ |
49 | "%s: " format, __func__, ## __VA_ARGS__); \ |
50 | } while (0) |
51 | #define sil164_info(client, format, ...) \ |
52 | dev_info(&client->dev, format, __VA_ARGS__) |
53 | #define sil164_err(client, format, ...) \ |
54 | dev_err(&client->dev, format, __VA_ARGS__) |
55 | |
56 | #define SIL164_I2C_ADDR_MASTER 0x38 |
57 | #define SIL164_I2C_ADDR_SLAVE 0x39 |
58 | |
59 | /* HW register definitions */ |
60 | |
61 | #define SIL164_VENDOR_LO 0x0 |
62 | #define SIL164_VENDOR_HI 0x1 |
63 | #define SIL164_DEVICE_LO 0x2 |
64 | #define SIL164_DEVICE_HI 0x3 |
65 | #define SIL164_REVISION 0x4 |
66 | #define SIL164_FREQ_MIN 0x6 |
67 | #define SIL164_FREQ_MAX 0x7 |
68 | #define SIL164_CONTROL0 0x8 |
69 | # define SIL164_CONTROL0_POWER_ON 0x01 |
70 | # define SIL164_CONTROL0_EDGE_RISING 0x02 |
71 | # define SIL164_CONTROL0_INPUT_24BIT 0x04 |
72 | # define SIL164_CONTROL0_DUAL_EDGE 0x08 |
73 | # define SIL164_CONTROL0_HSYNC_ON 0x10 |
74 | # define SIL164_CONTROL0_VSYNC_ON 0x20 |
75 | #define SIL164_DETECT 0x9 |
76 | # define SIL164_DETECT_INTR_STAT 0x01 |
77 | # define SIL164_DETECT_HOTPLUG_STAT 0x02 |
78 | # define SIL164_DETECT_RECEIVER_STAT 0x04 |
79 | # define SIL164_DETECT_INTR_MODE_RECEIVER 0x00 |
80 | # define SIL164_DETECT_INTR_MODE_HOTPLUG 0x08 |
81 | # define SIL164_DETECT_OUT_MODE_HIGH 0x00 |
82 | # define SIL164_DETECT_OUT_MODE_INTR 0x10 |
83 | # define SIL164_DETECT_OUT_MODE_RECEIVER 0x20 |
84 | # define SIL164_DETECT_OUT_MODE_HOTPLUG 0x30 |
85 | # define SIL164_DETECT_VSWING_STAT 0x80 |
86 | #define SIL164_CONTROL1 0xa |
87 | # define SIL164_CONTROL1_DESKEW_ENABLE 0x10 |
88 | # define SIL164_CONTROL1_DESKEW_INCR_SHIFT 5 |
89 | #define SIL164_GPIO 0xb |
90 | #define SIL164_CONTROL2 0xc |
91 | # define SIL164_CONTROL2_FILTER_ENABLE 0x01 |
92 | # define SIL164_CONTROL2_FILTER_SETTING_SHIFT 1 |
93 | # define SIL164_CONTROL2_DUALLINK_MASTER 0x40 |
94 | # define SIL164_CONTROL2_SYNC_CONT 0x80 |
95 | #define SIL164_DUALLINK 0xd |
96 | # define SIL164_DUALLINK_ENABLE 0x10 |
97 | # define SIL164_DUALLINK_SKEW_SHIFT 5 |
98 | #define SIL164_PLLZONE 0xe |
99 | # define SIL164_PLLZONE_STAT 0x08 |
100 | # define SIL164_PLLZONE_FORCE_ON 0x10 |
101 | # define SIL164_PLLZONE_FORCE_HIGH 0x20 |
102 | |
103 | /* HW access functions */ |
104 | |
105 | static void |
106 | sil164_write(struct i2c_client *client, uint8_t addr, uint8_t val) |
107 | { |
108 | uint8_t buf[] = {addr, val}; |
109 | int ret; |
110 | |
111 | ret = i2c_master_send(client, buf, ARRAY_SIZE(buf)); |
112 | if (ret < 0) |
113 | sil164_err(client, "Error %d writing to subaddress 0x%x\n" , |
114 | ret, addr); |
115 | } |
116 | |
117 | static uint8_t |
118 | sil164_read(struct i2c_client *client, uint8_t addr) |
119 | { |
120 | uint8_t val; |
121 | int ret; |
122 | |
123 | ret = i2c_master_send(client, buf: &addr, count: sizeof(addr)); |
124 | if (ret < 0) |
125 | goto fail; |
126 | |
127 | ret = i2c_master_recv(client, buf: &val, count: sizeof(val)); |
128 | if (ret < 0) |
129 | goto fail; |
130 | |
131 | return val; |
132 | |
133 | fail: |
134 | sil164_err(client, "Error %d reading from subaddress 0x%x\n" , |
135 | ret, addr); |
136 | return 0; |
137 | } |
138 | |
139 | static void |
140 | sil164_save_state(struct i2c_client *client, uint8_t *state) |
141 | { |
142 | int i; |
143 | |
144 | for (i = 0x8; i <= 0xe; i++) |
145 | state[i] = sil164_read(client, addr: i); |
146 | } |
147 | |
148 | static void |
149 | sil164_restore_state(struct i2c_client *client, uint8_t *state) |
150 | { |
151 | int i; |
152 | |
153 | for (i = 0x8; i <= 0xe; i++) |
154 | sil164_write(client, addr: i, val: state[i]); |
155 | } |
156 | |
157 | static void |
158 | sil164_set_power_state(struct i2c_client *client, bool on) |
159 | { |
160 | uint8_t control0 = sil164_read(client, SIL164_CONTROL0); |
161 | |
162 | if (on) |
163 | control0 |= SIL164_CONTROL0_POWER_ON; |
164 | else |
165 | control0 &= ~SIL164_CONTROL0_POWER_ON; |
166 | |
167 | sil164_write(client, SIL164_CONTROL0, val: control0); |
168 | } |
169 | |
170 | static void |
171 | sil164_init_state(struct i2c_client *client, |
172 | struct sil164_encoder_params *config, |
173 | bool duallink) |
174 | { |
175 | sil164_write(client, SIL164_CONTROL0, |
176 | SIL164_CONTROL0_HSYNC_ON | |
177 | SIL164_CONTROL0_VSYNC_ON | |
178 | (config->input_edge ? SIL164_CONTROL0_EDGE_RISING : 0) | |
179 | (config->input_width ? SIL164_CONTROL0_INPUT_24BIT : 0) | |
180 | (config->input_dual ? SIL164_CONTROL0_DUAL_EDGE : 0)); |
181 | |
182 | sil164_write(client, SIL164_DETECT, |
183 | SIL164_DETECT_INTR_STAT | |
184 | SIL164_DETECT_OUT_MODE_RECEIVER); |
185 | |
186 | sil164_write(client, SIL164_CONTROL1, |
187 | val: (config->input_skew ? SIL164_CONTROL1_DESKEW_ENABLE : 0) | |
188 | (((config->input_skew + 4) & 0x7) |
189 | << SIL164_CONTROL1_DESKEW_INCR_SHIFT)); |
190 | |
191 | sil164_write(client, SIL164_CONTROL2, |
192 | SIL164_CONTROL2_SYNC_CONT | |
193 | (config->pll_filter ? 0 : SIL164_CONTROL2_FILTER_ENABLE) | |
194 | (4 << SIL164_CONTROL2_FILTER_SETTING_SHIFT)); |
195 | |
196 | sil164_write(client, SIL164_PLLZONE, val: 0); |
197 | |
198 | if (duallink) |
199 | sil164_write(client, SIL164_DUALLINK, |
200 | SIL164_DUALLINK_ENABLE | |
201 | (((config->duallink_skew + 4) & 0x7) |
202 | << SIL164_DUALLINK_SKEW_SHIFT)); |
203 | else |
204 | sil164_write(client, SIL164_DUALLINK, val: 0); |
205 | } |
206 | |
207 | /* DRM encoder functions */ |
208 | |
209 | static void |
210 | sil164_encoder_set_config(struct drm_encoder *encoder, void *params) |
211 | { |
212 | struct sil164_priv *priv = to_sil164_priv(encoder); |
213 | |
214 | priv->config = *(struct sil164_encoder_params *)params; |
215 | } |
216 | |
217 | static void |
218 | sil164_encoder_dpms(struct drm_encoder *encoder, int mode) |
219 | { |
220 | struct sil164_priv *priv = to_sil164_priv(encoder); |
221 | bool on = (mode == DRM_MODE_DPMS_ON); |
222 | bool duallink = (on && encoder->crtc->mode.clock > 165000); |
223 | |
224 | sil164_set_power_state(client: drm_i2c_encoder_get_client(encoder), on); |
225 | |
226 | if (priv->duallink_slave) |
227 | sil164_set_power_state(client: priv->duallink_slave, on: duallink); |
228 | } |
229 | |
230 | static void |
231 | sil164_encoder_save(struct drm_encoder *encoder) |
232 | { |
233 | struct sil164_priv *priv = to_sil164_priv(encoder); |
234 | |
235 | sil164_save_state(client: drm_i2c_encoder_get_client(encoder), |
236 | state: priv->saved_state); |
237 | |
238 | if (priv->duallink_slave) |
239 | sil164_save_state(client: priv->duallink_slave, |
240 | state: priv->saved_slave_state); |
241 | } |
242 | |
243 | static void |
244 | sil164_encoder_restore(struct drm_encoder *encoder) |
245 | { |
246 | struct sil164_priv *priv = to_sil164_priv(encoder); |
247 | |
248 | sil164_restore_state(client: drm_i2c_encoder_get_client(encoder), |
249 | state: priv->saved_state); |
250 | |
251 | if (priv->duallink_slave) |
252 | sil164_restore_state(client: priv->duallink_slave, |
253 | state: priv->saved_slave_state); |
254 | } |
255 | |
256 | static int |
257 | sil164_encoder_mode_valid(struct drm_encoder *encoder, |
258 | struct drm_display_mode *mode) |
259 | { |
260 | struct sil164_priv *priv = to_sil164_priv(encoder); |
261 | |
262 | if (mode->clock < 32000) |
263 | return MODE_CLOCK_LOW; |
264 | |
265 | if (mode->clock > 330000 || |
266 | (mode->clock > 165000 && !priv->duallink_slave)) |
267 | return MODE_CLOCK_HIGH; |
268 | |
269 | return MODE_OK; |
270 | } |
271 | |
272 | static void |
273 | sil164_encoder_mode_set(struct drm_encoder *encoder, |
274 | struct drm_display_mode *mode, |
275 | struct drm_display_mode *adjusted_mode) |
276 | { |
277 | struct sil164_priv *priv = to_sil164_priv(encoder); |
278 | bool duallink = adjusted_mode->clock > 165000; |
279 | |
280 | sil164_init_state(client: drm_i2c_encoder_get_client(encoder), |
281 | config: &priv->config, duallink); |
282 | |
283 | if (priv->duallink_slave) |
284 | sil164_init_state(client: priv->duallink_slave, |
285 | config: &priv->config, duallink); |
286 | |
287 | sil164_encoder_dpms(encoder, DRM_MODE_DPMS_ON); |
288 | } |
289 | |
290 | static enum drm_connector_status |
291 | sil164_encoder_detect(struct drm_encoder *encoder, |
292 | struct drm_connector *connector) |
293 | { |
294 | struct i2c_client *client = drm_i2c_encoder_get_client(encoder); |
295 | |
296 | if (sil164_read(client, SIL164_DETECT) & SIL164_DETECT_HOTPLUG_STAT) |
297 | return connector_status_connected; |
298 | else |
299 | return connector_status_disconnected; |
300 | } |
301 | |
302 | static int |
303 | sil164_encoder_get_modes(struct drm_encoder *encoder, |
304 | struct drm_connector *connector) |
305 | { |
306 | return 0; |
307 | } |
308 | |
309 | static int |
310 | sil164_encoder_create_resources(struct drm_encoder *encoder, |
311 | struct drm_connector *connector) |
312 | { |
313 | return 0; |
314 | } |
315 | |
316 | static int |
317 | sil164_encoder_set_property(struct drm_encoder *encoder, |
318 | struct drm_connector *connector, |
319 | struct drm_property *property, |
320 | uint64_t val) |
321 | { |
322 | return 0; |
323 | } |
324 | |
325 | static void |
326 | sil164_encoder_destroy(struct drm_encoder *encoder) |
327 | { |
328 | struct sil164_priv *priv = to_sil164_priv(encoder); |
329 | |
330 | i2c_unregister_device(client: priv->duallink_slave); |
331 | |
332 | kfree(objp: priv); |
333 | drm_i2c_encoder_destroy(encoder); |
334 | } |
335 | |
336 | static const struct drm_encoder_slave_funcs sil164_encoder_funcs = { |
337 | .set_config = sil164_encoder_set_config, |
338 | .destroy = sil164_encoder_destroy, |
339 | .dpms = sil164_encoder_dpms, |
340 | .save = sil164_encoder_save, |
341 | .restore = sil164_encoder_restore, |
342 | .mode_valid = sil164_encoder_mode_valid, |
343 | .mode_set = sil164_encoder_mode_set, |
344 | .detect = sil164_encoder_detect, |
345 | .get_modes = sil164_encoder_get_modes, |
346 | .create_resources = sil164_encoder_create_resources, |
347 | .set_property = sil164_encoder_set_property, |
348 | }; |
349 | |
350 | /* I2C driver functions */ |
351 | |
352 | static int |
353 | sil164_probe(struct i2c_client *client) |
354 | { |
355 | int vendor = sil164_read(client, SIL164_VENDOR_HI) << 8 | |
356 | sil164_read(client, SIL164_VENDOR_LO); |
357 | int device = sil164_read(client, SIL164_DEVICE_HI) << 8 | |
358 | sil164_read(client, SIL164_DEVICE_LO); |
359 | int rev = sil164_read(client, SIL164_REVISION); |
360 | |
361 | if (vendor != 0x1 || device != 0x6) { |
362 | sil164_dbg(client, "Unknown device %x:%x.%x\n" , |
363 | vendor, device, rev); |
364 | return -ENODEV; |
365 | } |
366 | |
367 | sil164_info(client, "Detected device %x:%x.%x\n" , |
368 | vendor, device, rev); |
369 | |
370 | return 0; |
371 | } |
372 | |
373 | static struct i2c_client * |
374 | sil164_detect_slave(struct i2c_client *client) |
375 | { |
376 | struct i2c_adapter *adap = client->adapter; |
377 | struct i2c_msg msg = { |
378 | .addr = SIL164_I2C_ADDR_SLAVE, |
379 | .len = 0, |
380 | }; |
381 | const struct i2c_board_info info = { |
382 | I2C_BOARD_INFO("sil164" , SIL164_I2C_ADDR_SLAVE) |
383 | }; |
384 | |
385 | if (i2c_transfer(adap, msgs: &msg, num: 1) != 1) { |
386 | sil164_dbg(adap, "No dual-link slave found." ); |
387 | return NULL; |
388 | } |
389 | |
390 | return i2c_new_client_device(adap, info: &info); |
391 | } |
392 | |
393 | static int |
394 | sil164_encoder_init(struct i2c_client *client, |
395 | struct drm_device *dev, |
396 | struct drm_encoder_slave *encoder) |
397 | { |
398 | struct sil164_priv *priv; |
399 | struct i2c_client *slave_client; |
400 | |
401 | priv = kzalloc(size: sizeof(*priv), GFP_KERNEL); |
402 | if (!priv) |
403 | return -ENOMEM; |
404 | |
405 | encoder->slave_priv = priv; |
406 | encoder->slave_funcs = &sil164_encoder_funcs; |
407 | |
408 | slave_client = sil164_detect_slave(client); |
409 | if (!IS_ERR(ptr: slave_client)) |
410 | priv->duallink_slave = slave_client; |
411 | |
412 | return 0; |
413 | } |
414 | |
415 | static const struct i2c_device_id sil164_ids[] = { |
416 | { "sil164" , 0 }, |
417 | { } |
418 | }; |
419 | MODULE_DEVICE_TABLE(i2c, sil164_ids); |
420 | |
421 | static struct drm_i2c_encoder_driver sil164_driver = { |
422 | .i2c_driver = { |
423 | .probe = sil164_probe, |
424 | .driver = { |
425 | .name = "sil164" , |
426 | }, |
427 | .id_table = sil164_ids, |
428 | }, |
429 | .encoder_init = sil164_encoder_init, |
430 | }; |
431 | |
432 | /* Module initialization */ |
433 | |
434 | static int __init |
435 | sil164_init(void) |
436 | { |
437 | return drm_i2c_encoder_register(THIS_MODULE, driver: &sil164_driver); |
438 | } |
439 | |
440 | static void __exit |
441 | sil164_exit(void) |
442 | { |
443 | drm_i2c_encoder_unregister(driver: &sil164_driver); |
444 | } |
445 | |
446 | MODULE_AUTHOR("Francisco Jerez <currojerez@riseup.net>" ); |
447 | MODULE_DESCRIPTION("Silicon Image sil164 TMDS transmitter driver" ); |
448 | MODULE_LICENSE("GPL and additional rights" ); |
449 | |
450 | module_init(sil164_init); |
451 | module_exit(sil164_exit); |
452 | |