aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrancisco Jerez <currojerez@riseup.net>2010-07-22 11:06:18 -0400
committerDave Airlie <airlied@redhat.com>2010-08-01 20:21:13 -0400
commite190bfe56841551b1ad5abb42ebd0c4798cc8c01 (patch)
tree5a4759222056f95e0ffb085b267f8edb949579d4
parenta7b98b6748efdddd832b39662801c9f828df1813 (diff)
drm: Import driver for the sil164 I2C TMDS transmitter.
sil164 transmitters are used for DVI outputs on Intel/nvidia and ATI setups. So far only nouveau can use this driver. Signed-off-by: Francisco Jerez <currojerez@riseup.net> Tested-by: Patrice Mandin <patmandin@gmail.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r--drivers/gpu/drm/i2c/Makefile3
-rw-r--r--drivers/gpu/drm/i2c/sil164_drv.c462
-rw-r--r--drivers/gpu/drm/nouveau/Kconfig9
-rw-r--r--include/drm/i2c/sil164.h63
4 files changed, 537 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile
index 6d2abaf35ba..92862563e7e 100644
--- a/drivers/gpu/drm/i2c/Makefile
+++ b/drivers/gpu/drm/i2c/Makefile
@@ -2,3 +2,6 @@ ccflags-y := -Iinclude/drm
2 2
3ch7006-y := ch7006_drv.o ch7006_mode.o 3ch7006-y := ch7006_drv.o ch7006_mode.o
4obj-$(CONFIG_DRM_I2C_CH7006) += ch7006.o 4obj-$(CONFIG_DRM_I2C_CH7006) += ch7006.o
5
6sil164-y := sil164_drv.o
7obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o
diff --git a/drivers/gpu/drm/i2c/sil164_drv.c b/drivers/gpu/drm/i2c/sil164_drv.c
new file mode 100644
index 00000000000..0b6773290c0
--- /dev/null
+++ b/drivers/gpu/drm/i2c/sil164_drv.c
@@ -0,0 +1,462 @@
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 "drmP.h"
28#include "drm_crtc_helper.h"
29#include "drm_encoder_slave.h"
30#include "i2c/sil164.h"
31
32struct sil164_priv {
33 struct sil164_encoder_params config;
34 struct i2c_client *duallink_slave;
35
36 uint8_t saved_state[0x10];
37 uint8_t saved_slave_state[0x10];
38};
39
40#define to_sil164_priv(x) \
41 ((struct sil164_priv *)to_encoder_slave(x)->slave_priv)
42
43#define sil164_dbg(client, format, ...) do { \
44 if (drm_debug & DRM_UT_KMS) \
45 dev_printk(KERN_DEBUG, &client->dev, \
46 "%s: " format, __func__, ## __VA_ARGS__); \
47 } while (0)
48#define sil164_info(client, format, ...) \
49 dev_info(&client->dev, format, __VA_ARGS__)
50#define sil164_err(client, format, ...) \
51 dev_err(&client->dev, format, __VA_ARGS__)
52
53#define SIL164_I2C_ADDR_MASTER 0x38
54#define SIL164_I2C_ADDR_SLAVE 0x39
55
56/* HW register definitions */
57
58#define SIL164_VENDOR_LO 0x0
59#define SIL164_VENDOR_HI 0x1
60#define SIL164_DEVICE_LO 0x2
61#define SIL164_DEVICE_HI 0x3
62#define SIL164_REVISION 0x4
63#define SIL164_FREQ_MIN 0x6
64#define SIL164_FREQ_MAX 0x7
65#define SIL164_CONTROL0 0x8
66# define SIL164_CONTROL0_POWER_ON 0x01
67# define SIL164_CONTROL0_EDGE_RISING 0x02
68# define SIL164_CONTROL0_INPUT_24BIT 0x04
69# define SIL164_CONTROL0_DUAL_EDGE 0x08
70# define SIL164_CONTROL0_HSYNC_ON 0x10
71# define SIL164_CONTROL0_VSYNC_ON 0x20
72#define SIL164_DETECT 0x9
73# define SIL164_DETECT_INTR_STAT 0x01
74# define SIL164_DETECT_HOTPLUG_STAT 0x02
75# define SIL164_DETECT_RECEIVER_STAT 0x04
76# define SIL164_DETECT_INTR_MODE_RECEIVER 0x00
77# define SIL164_DETECT_INTR_MODE_HOTPLUG 0x08
78# define SIL164_DETECT_OUT_MODE_HIGH 0x00
79# define SIL164_DETECT_OUT_MODE_INTR 0x10
80# define SIL164_DETECT_OUT_MODE_RECEIVER 0x20
81# define SIL164_DETECT_OUT_MODE_HOTPLUG 0x30
82# define SIL164_DETECT_VSWING_STAT 0x80
83#define SIL164_CONTROL1 0xa
84# define SIL164_CONTROL1_DESKEW_ENABLE 0x10
85# define SIL164_CONTROL1_DESKEW_INCR_SHIFT 5
86#define SIL164_GPIO 0xb
87#define SIL164_CONTROL2 0xc
88# define SIL164_CONTROL2_FILTER_ENABLE 0x01
89# define SIL164_CONTROL2_FILTER_SETTING_SHIFT 1
90# define SIL164_CONTROL2_DUALLINK_MASTER 0x40
91# define SIL164_CONTROL2_SYNC_CONT 0x80
92#define SIL164_DUALLINK 0xd
93# define SIL164_DUALLINK_ENABLE 0x10
94# define SIL164_DUALLINK_SKEW_SHIFT 5
95#define SIL164_PLLZONE 0xe
96# define SIL164_PLLZONE_STAT 0x08
97# define SIL164_PLLZONE_FORCE_ON 0x10
98# define SIL164_PLLZONE_FORCE_HIGH 0x20
99
100/* HW access functions */
101
102static void
103sil164_write(struct i2c_client *client, uint8_t addr, uint8_t val)
104{
105 uint8_t buf[] = {addr, val};
106 int ret;
107
108 ret = i2c_master_send(client, buf, ARRAY_SIZE(buf));
109 if (ret < 0)
110 sil164_err(client, "Error %d writing to subaddress 0x%x\n",
111 ret, addr);
112}
113
114static uint8_t
115sil164_read(struct i2c_client *client, uint8_t addr)
116{
117 uint8_t val;
118 int ret;
119
120 ret = i2c_master_send(client, &addr, sizeof(addr));
121 if (ret < 0)
122 goto fail;
123
124 ret = i2c_master_recv(client, &val, sizeof(val));
125 if (ret < 0)
126 goto fail;
127
128 return val;
129
130fail:
131 sil164_err(client, "Error %d reading from subaddress 0x%x\n",
132 ret, addr);
133 return 0;
134}
135
136static void
137sil164_save_state(struct i2c_client *client, uint8_t *state)
138{
139 int i;
140
141 for (i = 0x8; i <= 0xe; i++)
142 state[i] = sil164_read(client, i);
143}
144
145static void
146sil164_restore_state(struct i2c_client *client, uint8_t *state)
147{
148 int i;
149
150 for (i = 0x8; i <= 0xe; i++)
151 sil164_write(client, i, state[i]);
152}
153
154static void
155sil164_set_power_state(struct i2c_client *client, bool on)
156{
157 uint8_t control0 = sil164_read(client, SIL164_CONTROL0);
158
159 if (on)
160 control0 |= SIL164_CONTROL0_POWER_ON;
161 else
162 control0 &= ~SIL164_CONTROL0_POWER_ON;
163
164 sil164_write(client, SIL164_CONTROL0, control0);
165}
166
167static void
168sil164_init_state(struct i2c_client *client,
169 struct sil164_encoder_params *config,
170 bool duallink)
171{
172 sil164_write(client, SIL164_CONTROL0,
173 SIL164_CONTROL0_HSYNC_ON |
174 SIL164_CONTROL0_VSYNC_ON |
175 (config->input_edge ? SIL164_CONTROL0_EDGE_RISING : 0) |
176 (config->input_width ? SIL164_CONTROL0_INPUT_24BIT : 0) |
177 (config->input_dual ? SIL164_CONTROL0_DUAL_EDGE : 0));
178
179 sil164_write(client, SIL164_DETECT,
180 SIL164_DETECT_INTR_STAT |
181 SIL164_DETECT_OUT_MODE_RECEIVER);
182
183 sil164_write(client, SIL164_CONTROL1,
184 (config->input_skew ? SIL164_CONTROL1_DESKEW_ENABLE : 0) |
185 (((config->input_skew + 4) & 0x7)
186 << SIL164_CONTROL1_DESKEW_INCR_SHIFT));
187
188 sil164_write(client, SIL164_CONTROL2,
189 SIL164_CONTROL2_SYNC_CONT |
190 (config->pll_filter ? 0 : SIL164_CONTROL2_FILTER_ENABLE) |
191 (4 << SIL164_CONTROL2_FILTER_SETTING_SHIFT));
192
193 sil164_write(client, SIL164_PLLZONE, 0);
194
195 if (duallink)
196 sil164_write(client, SIL164_DUALLINK,
197 SIL164_DUALLINK_ENABLE |
198 (((config->duallink_skew + 4) & 0x7)
199 << SIL164_DUALLINK_SKEW_SHIFT));
200 else
201 sil164_write(client, SIL164_DUALLINK, 0);
202}
203
204/* DRM encoder functions */
205
206static void
207sil164_encoder_set_config(struct drm_encoder *encoder, void *params)
208{
209 struct sil164_priv *priv = to_sil164_priv(encoder);
210
211 priv->config = *(struct sil164_encoder_params *)params;
212}
213
214static void
215sil164_encoder_dpms(struct drm_encoder *encoder, int mode)
216{
217 struct sil164_priv *priv = to_sil164_priv(encoder);
218 bool on = (mode == DRM_MODE_DPMS_ON);
219 bool duallink = (on && encoder->crtc->mode.clock > 165000);
220
221 sil164_set_power_state(drm_i2c_encoder_get_client(encoder), on);
222
223 if (priv->duallink_slave)
224 sil164_set_power_state(priv->duallink_slave, duallink);
225}
226
227static void
228sil164_encoder_save(struct drm_encoder *encoder)
229{
230 struct sil164_priv *priv = to_sil164_priv(encoder);
231
232 sil164_save_state(drm_i2c_encoder_get_client(encoder),
233 priv->saved_state);
234
235 if (priv->duallink_slave)
236 sil164_save_state(priv->duallink_slave,
237 priv->saved_slave_state);
238}
239
240static void
241sil164_encoder_restore(struct drm_encoder *encoder)
242{
243 struct sil164_priv *priv = to_sil164_priv(encoder);
244
245 sil164_restore_state(drm_i2c_encoder_get_client(encoder),
246 priv->saved_state);
247
248 if (priv->duallink_slave)
249 sil164_restore_state(priv->duallink_slave,
250 priv->saved_slave_state);
251}
252
253static bool
254sil164_encoder_mode_fixup(struct drm_encoder *encoder,
255 struct drm_display_mode *mode,
256 struct drm_display_mode *adjusted_mode)
257{
258 return true;
259}
260
261static int
262sil164_encoder_mode_valid(struct drm_encoder *encoder,
263 struct drm_display_mode *mode)
264{
265 struct sil164_priv *priv = to_sil164_priv(encoder);
266
267 if (mode->clock < 32000)
268 return MODE_CLOCK_LOW;
269
270 if (mode->clock > 330000 ||
271 (mode->clock > 165000 && !priv->duallink_slave))
272 return MODE_CLOCK_HIGH;
273
274 return MODE_OK;
275}
276
277static void
278sil164_encoder_mode_set(struct drm_encoder *encoder,
279 struct drm_display_mode *mode,
280 struct drm_display_mode *adjusted_mode)
281{
282 struct sil164_priv *priv = to_sil164_priv(encoder);
283 bool duallink = adjusted_mode->clock > 165000;
284
285 sil164_init_state(drm_i2c_encoder_get_client(encoder),
286 &priv->config, duallink);
287
288 if (priv->duallink_slave)
289 sil164_init_state(priv->duallink_slave,
290 &priv->config, duallink);
291
292 sil164_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
293}
294
295static enum drm_connector_status
296sil164_encoder_detect(struct drm_encoder *encoder,
297 struct drm_connector *connector)
298{
299 struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
300
301 if (sil164_read(client, SIL164_DETECT) & SIL164_DETECT_HOTPLUG_STAT)
302 return connector_status_connected;
303 else
304 return connector_status_disconnected;
305}
306
307static int
308sil164_encoder_get_modes(struct drm_encoder *encoder,
309 struct drm_connector *connector)
310{
311 return 0;
312}
313
314static int
315sil164_encoder_create_resources(struct drm_encoder *encoder,
316 struct drm_connector *connector)
317{
318 return 0;
319}
320
321static int
322sil164_encoder_set_property(struct drm_encoder *encoder,
323 struct drm_connector *connector,
324 struct drm_property *property,
325 uint64_t val)
326{
327 return 0;
328}
329
330static void
331sil164_encoder_destroy(struct drm_encoder *encoder)
332{
333 struct sil164_priv *priv = to_sil164_priv(encoder);
334
335 if (priv->duallink_slave)
336 i2c_unregister_device(priv->duallink_slave);
337
338 kfree(priv);
339 drm_i2c_encoder_destroy(encoder);
340}
341
342static struct drm_encoder_slave_funcs sil164_encoder_funcs = {
343 .set_config = sil164_encoder_set_config,
344 .destroy = sil164_encoder_destroy,
345 .dpms = sil164_encoder_dpms,
346 .save = sil164_encoder_save,
347 .restore = sil164_encoder_restore,
348 .mode_fixup = sil164_encoder_mode_fixup,
349 .mode_valid = sil164_encoder_mode_valid,
350 .mode_set = sil164_encoder_mode_set,
351 .detect = sil164_encoder_detect,
352 .get_modes = sil164_encoder_get_modes,
353 .create_resources = sil164_encoder_create_resources,
354 .set_property = sil164_encoder_set_property,
355};
356
357/* I2C driver functions */
358
359static int
360sil164_probe(struct i2c_client *client, const struct i2c_device_id *id)
361{
362 int vendor = sil164_read(client, SIL164_VENDOR_HI) << 8 |
363 sil164_read(client, SIL164_VENDOR_LO);
364 int device = sil164_read(client, SIL164_DEVICE_HI) << 8 |
365 sil164_read(client, SIL164_DEVICE_LO);
366 int rev = sil164_read(client, SIL164_REVISION);
367
368 if (vendor != 0x1 || device != 0x6) {
369 sil164_dbg(client, "Unknown device %x:%x.%x\n",
370 vendor, device, rev);
371 return -ENODEV;
372 }
373
374 sil164_info(client, "Detected device %x:%x.%x\n",
375 vendor, device, rev);
376
377 return 0;
378}
379
380static int
381sil164_remove(struct i2c_client *client)
382{
383 return 0;
384}
385
386static struct i2c_client *
387sil164_detect_slave(struct i2c_client *client)
388{
389 struct i2c_adapter *adap = client->adapter;
390 struct i2c_msg msg = {
391 .addr = SIL164_I2C_ADDR_SLAVE,
392 .len = 0,
393 };
394 const struct i2c_board_info info = {
395 I2C_BOARD_INFO("sil164", SIL164_I2C_ADDR_SLAVE)
396 };
397
398 if (i2c_transfer(adap, &msg, 1) != 1) {
399 sil164_dbg(adap, "No dual-link slave found.");
400 return NULL;
401 }
402
403 return i2c_new_device(adap, &info);
404}
405
406static int
407sil164_encoder_init(struct i2c_client *client,
408 struct drm_device *dev,
409 struct drm_encoder_slave *encoder)
410{
411 struct sil164_priv *priv;
412
413 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
414 if (!priv)
415 return -ENOMEM;
416
417 encoder->slave_priv = priv;
418 encoder->slave_funcs = &sil164_encoder_funcs;
419
420 priv->duallink_slave = sil164_detect_slave(client);
421
422 return 0;
423}
424
425static struct i2c_device_id sil164_ids[] = {
426 { "sil164", 0 },
427 { }
428};
429MODULE_DEVICE_TABLE(i2c, sil164_ids);
430
431static struct drm_i2c_encoder_driver sil164_driver = {
432 .i2c_driver = {
433 .probe = sil164_probe,
434 .remove = sil164_remove,
435 .driver = {
436 .name = "sil164",
437 },
438 .id_table = sil164_ids,
439 },
440 .encoder_init = sil164_encoder_init,
441};
442
443/* Module initialization */
444
445static int __init
446sil164_init(void)
447{
448 return drm_i2c_encoder_register(THIS_MODULE, &sil164_driver);
449}
450
451static void __exit
452sil164_exit(void)
453{
454 drm_i2c_encoder_unregister(&sil164_driver);
455}
456
457MODULE_AUTHOR("Francisco Jerez <currojerez@riseup.net>");
458MODULE_DESCRIPTION("Silicon Image sil164 TMDS transmitter driver");
459MODULE_LICENSE("GPL and additional rights");
460
461module_init(sil164_init);
462module_exit(sil164_exit);
diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig
index b6f5239c2ef..d2d28048efb 100644
--- a/drivers/gpu/drm/nouveau/Kconfig
+++ b/drivers/gpu/drm/nouveau/Kconfig
@@ -41,4 +41,13 @@ config DRM_I2C_CH7006
41 41
42 This driver is currently only useful if you're also using 42 This driver is currently only useful if you're also using
43 the nouveau driver. 43 the nouveau driver.
44
45config DRM_I2C_SIL164
46 tristate "Silicon Image sil164 TMDS transmitter"
47 default m if DRM_NOUVEAU
48 help
49 Support for sil164 and similar single-link (or dual-link
50 when used in pairs) TMDS transmitters, used in some nVidia
51 video cards.
52
44endmenu 53endmenu
diff --git a/include/drm/i2c/sil164.h b/include/drm/i2c/sil164.h
new file mode 100644
index 00000000000..205e27384c8
--- /dev/null
+++ b/include/drm/i2c/sil164.h
@@ -0,0 +1,63 @@
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#ifndef __DRM_I2C_SIL164_H__
28#define __DRM_I2C_SIL164_H__
29
30/**
31 * struct sil164_encoder_params
32 *
33 * Describes how the sil164 is connected to the GPU. It should be used
34 * as the @params parameter of its @set_config method.
35 *
36 * See "http://www.siliconimage.com/docs/SiI-DS-0021-E-164.pdf".
37 */
38struct sil164_encoder_params {
39 enum {
40 SIL164_INPUT_EDGE_FALLING = 0,
41 SIL164_INPUT_EDGE_RISING
42 } input_edge;
43
44 enum {
45 SIL164_INPUT_WIDTH_12BIT = 0,
46 SIL164_INPUT_WIDTH_24BIT
47 } input_width;
48
49 enum {
50 SIL164_INPUT_SINGLE_EDGE = 0,
51 SIL164_INPUT_DUAL_EDGE
52 } input_dual;
53
54 enum {
55 SIL164_PLL_FILTER_ON = 0,
56 SIL164_PLL_FILTER_OFF,
57 } pll_filter;
58
59 int input_skew; /** < Allowed range [-4, 3], use 0 for no de-skew. */
60 int duallink_skew; /** < Allowed range [-4, 3]. */
61};
62
63#endif