diff options
Diffstat (limited to 'drivers/video/nvidia/nv_i2c.c')
-rw-r--r-- | drivers/video/nvidia/nv_i2c.c | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/drivers/video/nvidia/nv_i2c.c b/drivers/video/nvidia/nv_i2c.c new file mode 100644 index 000000000000..3757c1407c19 --- /dev/null +++ b/drivers/video/nvidia/nv_i2c.c | |||
@@ -0,0 +1,215 @@ | |||
1 | /* | ||
2 | * linux/drivers/video/nvidia/nvidia-i2c.c - nVidia i2c | ||
3 | * | ||
4 | * Copyright 2004 Antonino A. Daplas <adaplas @pol.net> | ||
5 | * | ||
6 | * Based on rivafb-i2c.c | ||
7 | * | ||
8 | * This file is subject to the terms and conditions of the GNU General Public | ||
9 | * License. See the file COPYING in the main directory of this archive | ||
10 | * for more details. | ||
11 | */ | ||
12 | |||
13 | #include <linux/config.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/pci.h> | ||
19 | #include <linux/fb.h> | ||
20 | |||
21 | #include <asm/io.h> | ||
22 | |||
23 | #include "nv_type.h" | ||
24 | #include "nv_local.h" | ||
25 | #include "nv_proto.h" | ||
26 | |||
27 | #include "../edid.h" | ||
28 | |||
29 | static void nvidia_gpio_setscl(void *data, int state) | ||
30 | { | ||
31 | struct nvidia_i2c_chan *chan = data; | ||
32 | struct nvidia_par *par = chan->par; | ||
33 | u32 val; | ||
34 | |||
35 | VGA_WR08(par->PCIO, 0x3d4, chan->ddc_base + 1); | ||
36 | val = VGA_RD08(par->PCIO, 0x3d5) & 0xf0; | ||
37 | |||
38 | if (state) | ||
39 | val |= 0x20; | ||
40 | else | ||
41 | val &= ~0x20; | ||
42 | |||
43 | VGA_WR08(par->PCIO, 0x3d4, chan->ddc_base + 1); | ||
44 | VGA_WR08(par->PCIO, 0x3d5, val | 0x1); | ||
45 | } | ||
46 | |||
47 | static void nvidia_gpio_setsda(void *data, int state) | ||
48 | { | ||
49 | struct nvidia_i2c_chan *chan = (struct nvidia_i2c_chan *)data; | ||
50 | struct nvidia_par *par = chan->par; | ||
51 | u32 val; | ||
52 | |||
53 | VGA_WR08(par->PCIO, 0x3d4, chan->ddc_base + 1); | ||
54 | val = VGA_RD08(par->PCIO, 0x3d5) & 0xf0; | ||
55 | |||
56 | if (state) | ||
57 | val |= 0x10; | ||
58 | else | ||
59 | val &= ~0x10; | ||
60 | |||
61 | VGA_WR08(par->PCIO, 0x3d4, chan->ddc_base + 1); | ||
62 | VGA_WR08(par->PCIO, 0x3d5, val | 0x1); | ||
63 | } | ||
64 | |||
65 | static int nvidia_gpio_getscl(void *data) | ||
66 | { | ||
67 | struct nvidia_i2c_chan *chan = (struct nvidia_i2c_chan *)data; | ||
68 | struct nvidia_par *par = chan->par; | ||
69 | u32 val = 0; | ||
70 | |||
71 | VGA_WR08(par->PCIO, 0x3d4, chan->ddc_base); | ||
72 | if (VGA_RD08(par->PCIO, 0x3d5) & 0x04) | ||
73 | val = 1; | ||
74 | |||
75 | val = VGA_RD08(par->PCIO, 0x3d5); | ||
76 | |||
77 | return val; | ||
78 | } | ||
79 | |||
80 | static int nvidia_gpio_getsda(void *data) | ||
81 | { | ||
82 | struct nvidia_i2c_chan *chan = (struct nvidia_i2c_chan *)data; | ||
83 | struct nvidia_par *par = chan->par; | ||
84 | u32 val = 0; | ||
85 | |||
86 | VGA_WR08(par->PCIO, 0x3d4, chan->ddc_base); | ||
87 | if (VGA_RD08(par->PCIO, 0x3d5) & 0x08) | ||
88 | val = 1; | ||
89 | |||
90 | return val; | ||
91 | } | ||
92 | |||
93 | #define I2C_ALGO_NVIDIA 0x0e0000 | ||
94 | static int nvidia_setup_i2c_bus(struct nvidia_i2c_chan *chan, const char *name) | ||
95 | { | ||
96 | int rc; | ||
97 | |||
98 | strcpy(chan->adapter.name, name); | ||
99 | chan->adapter.owner = THIS_MODULE; | ||
100 | chan->adapter.id = I2C_ALGO_NVIDIA; | ||
101 | chan->adapter.algo_data = &chan->algo; | ||
102 | chan->adapter.dev.parent = &chan->par->pci_dev->dev; | ||
103 | chan->algo.setsda = nvidia_gpio_setsda; | ||
104 | chan->algo.setscl = nvidia_gpio_setscl; | ||
105 | chan->algo.getsda = nvidia_gpio_getsda; | ||
106 | chan->algo.getscl = nvidia_gpio_getscl; | ||
107 | chan->algo.udelay = 40; | ||
108 | chan->algo.timeout = msecs_to_jiffies(2); | ||
109 | chan->algo.data = chan; | ||
110 | |||
111 | i2c_set_adapdata(&chan->adapter, chan); | ||
112 | |||
113 | /* Raise SCL and SDA */ | ||
114 | nvidia_gpio_setsda(chan, 1); | ||
115 | nvidia_gpio_setscl(chan, 1); | ||
116 | udelay(20); | ||
117 | |||
118 | rc = i2c_bit_add_bus(&chan->adapter); | ||
119 | if (rc == 0) | ||
120 | dev_dbg(&chan->par->pci_dev->dev, | ||
121 | "I2C bus %s registered.\n", name); | ||
122 | else { | ||
123 | dev_warn(&chan->par->pci_dev->dev, | ||
124 | "Failed to register I2C bus %s.\n", name); | ||
125 | chan->par = NULL; | ||
126 | } | ||
127 | |||
128 | return rc; | ||
129 | } | ||
130 | |||
131 | void nvidia_create_i2c_busses(struct nvidia_par *par) | ||
132 | { | ||
133 | par->bus = 3; | ||
134 | |||
135 | par->chan[0].par = par; | ||
136 | par->chan[1].par = par; | ||
137 | par->chan[2].par = par; | ||
138 | |||
139 | par->chan[0].ddc_base = 0x3e; | ||
140 | nvidia_setup_i2c_bus(&par->chan[0], "BUS1"); | ||
141 | |||
142 | par->chan[1].ddc_base = 0x36; | ||
143 | nvidia_setup_i2c_bus(&par->chan[1], "BUS2"); | ||
144 | |||
145 | par->chan[2].ddc_base = 0x50; | ||
146 | nvidia_setup_i2c_bus(&par->chan[2], "BUS3"); | ||
147 | } | ||
148 | |||
149 | void nvidia_delete_i2c_busses(struct nvidia_par *par) | ||
150 | { | ||
151 | if (par->chan[0].par) | ||
152 | i2c_bit_del_bus(&par->chan[0].adapter); | ||
153 | par->chan[0].par = NULL; | ||
154 | |||
155 | if (par->chan[1].par) | ||
156 | i2c_bit_del_bus(&par->chan[1].adapter); | ||
157 | par->chan[1].par = NULL; | ||
158 | |||
159 | if (par->chan[2].par) | ||
160 | i2c_bit_del_bus(&par->chan[2].adapter); | ||
161 | par->chan[2].par = NULL; | ||
162 | |||
163 | } | ||
164 | |||
165 | static u8 *nvidia_do_probe_i2c_edid(struct nvidia_i2c_chan *chan) | ||
166 | { | ||
167 | u8 start = 0x0; | ||
168 | struct i2c_msg msgs[] = { | ||
169 | { | ||
170 | .addr = 0x50, | ||
171 | .len = 1, | ||
172 | .buf = &start, | ||
173 | }, { | ||
174 | .addr = 0x50, | ||
175 | .flags = I2C_M_RD, | ||
176 | .len = EDID_LENGTH, | ||
177 | }, | ||
178 | }; | ||
179 | u8 *buf; | ||
180 | |||
181 | if (!chan->par) | ||
182 | return NULL; | ||
183 | |||
184 | buf = kmalloc(EDID_LENGTH, GFP_KERNEL); | ||
185 | if (!buf) { | ||
186 | dev_warn(&chan->par->pci_dev->dev, "Out of memory!\n"); | ||
187 | return NULL; | ||
188 | } | ||
189 | msgs[1].buf = buf; | ||
190 | |||
191 | if (i2c_transfer(&chan->adapter, msgs, 2) == 2) | ||
192 | return buf; | ||
193 | dev_dbg(&chan->par->pci_dev->dev, "Unable to read EDID block.\n"); | ||
194 | kfree(buf); | ||
195 | return NULL; | ||
196 | } | ||
197 | |||
198 | int nvidia_probe_i2c_connector(struct nvidia_par *par, int conn, u8 **out_edid) | ||
199 | { | ||
200 | u8 *edid = NULL; | ||
201 | int i; | ||
202 | |||
203 | for (i = 0; i < 3; i++) { | ||
204 | /* Do the real work */ | ||
205 | edid = nvidia_do_probe_i2c_edid(&par->chan[conn - 1]); | ||
206 | if (edid) | ||
207 | break; | ||
208 | } | ||
209 | if (out_edid) | ||
210 | *out_edid = edid; | ||
211 | if (!edid) | ||
212 | return 1; | ||
213 | |||
214 | return 0; | ||
215 | } | ||