Commit | Line | Data |
---|---|---|
6ee73861 BS |
1 | /* |
2 | * Copyright 2009 Red Hat Inc. | |
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, sublicense, | |
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 shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
20 | * OTHER DEALINGS IN THE SOFTWARE. | |
21 | * | |
22 | * Authors: Ben Skeggs | |
23 | */ | |
24 | ||
e0cd3608 PG |
25 | #include <linux/module.h> |
26 | ||
760285e7 | 27 | #include <drm/drmP.h> |
6ee73861 BS |
28 | #include "nouveau_drv.h" |
29 | #include "nouveau_i2c.h" | |
30 | #include "nouveau_hw.h" | |
31 | ||
32 | static void | |
2bdb06e3 | 33 | i2c_drive_scl(void *data, int state) |
6ee73861 | 34 | { |
2bdb06e3 BS |
35 | struct nouveau_i2c_chan *port = data; |
36 | if (port->type == 0) { | |
37 | u8 val = NVReadVgaCrtc(port->dev, 0, port->drive); | |
38 | if (state) val |= 0x20; | |
39 | else val &= 0xdf; | |
40 | NVWriteVgaCrtc(port->dev, 0, port->drive, val | 0x01); | |
41 | } else | |
42 | if (port->type == 4) { | |
43 | nv_mask(port->dev, port->drive, 0x2f, state ? 0x21 : 0x01); | |
44 | } else | |
45 | if (port->type == 5) { | |
46 | if (state) port->state |= 0x01; | |
47 | else port->state &= 0xfe; | |
48 | nv_wr32(port->dev, port->drive, 4 | port->state); | |
49 | } | |
6ee73861 BS |
50 | } |
51 | ||
52 | static void | |
2bdb06e3 | 53 | i2c_drive_sda(void *data, int state) |
6ee73861 | 54 | { |
2bdb06e3 BS |
55 | struct nouveau_i2c_chan *port = data; |
56 | if (port->type == 0) { | |
57 | u8 val = NVReadVgaCrtc(port->dev, 0, port->drive); | |
58 | if (state) val |= 0x10; | |
59 | else val &= 0xef; | |
60 | NVWriteVgaCrtc(port->dev, 0, port->drive, val | 0x01); | |
61 | } else | |
62 | if (port->type == 4) { | |
63 | nv_mask(port->dev, port->drive, 0x1f, state ? 0x11 : 0x01); | |
64 | } else | |
65 | if (port->type == 5) { | |
66 | if (state) port->state |= 0x02; | |
67 | else port->state &= 0xfd; | |
68 | nv_wr32(port->dev, port->drive, 4 | port->state); | |
69 | } | |
6ee73861 BS |
70 | } |
71 | ||
eeb3ca12 | 72 | static int |
2bdb06e3 | 73 | i2c_sense_scl(void *data) |
eeb3ca12 | 74 | { |
2bdb06e3 BS |
75 | struct nouveau_i2c_chan *port = data; |
76 | struct drm_nouveau_private *dev_priv = port->dev->dev_private; | |
77 | if (port->type == 0) { | |
78 | return !!(NVReadVgaCrtc(port->dev, 0, port->sense) & 0x04); | |
79 | } else | |
80 | if (port->type == 4) { | |
81 | return !!(nv_rd32(port->dev, port->sense) & 0x00040000); | |
82 | } else | |
83 | if (port->type == 5) { | |
84 | if (dev_priv->card_type < NV_D0) | |
85 | return !!(nv_rd32(port->dev, port->sense) & 0x01); | |
86 | else | |
87 | return !!(nv_rd32(port->dev, port->sense) & 0x10); | |
88 | } | |
89 | return 0; | |
eeb3ca12 BS |
90 | } |
91 | ||
92 | static int | |
2bdb06e3 | 93 | i2c_sense_sda(void *data) |
eeb3ca12 | 94 | { |
2bdb06e3 BS |
95 | struct nouveau_i2c_chan *port = data; |
96 | struct drm_nouveau_private *dev_priv = port->dev->dev_private; | |
97 | if (port->type == 0) { | |
98 | return !!(NVReadVgaCrtc(port->dev, 0, port->sense) & 0x08); | |
99 | } else | |
100 | if (port->type == 4) { | |
101 | return !!(nv_rd32(port->dev, port->sense) & 0x00080000); | |
102 | } else | |
103 | if (port->type == 5) { | |
104 | if (dev_priv->card_type < NV_D0) | |
105 | return !!(nv_rd32(port->dev, port->sense) & 0x02); | |
106 | else | |
107 | return !!(nv_rd32(port->dev, port->sense) & 0x20); | |
108 | } | |
109 | return 0; | |
eeb3ca12 | 110 | } |
6ee73861 | 111 | |
486a45c2 BS |
112 | static const uint32_t nv50_i2c_port[] = { |
113 | 0x00e138, 0x00e150, 0x00e168, 0x00e180, | |
114 | 0x00e254, 0x00e274, 0x00e764, 0x00e780, | |
115 | 0x00e79c, 0x00e7b8 | |
116 | }; | |
117 | ||
118 | static u8 * | |
119 | i2c_table(struct drm_device *dev, u8 *version) | |
120 | { | |
121 | u8 *dcb = dcb_table(dev), *i2c = NULL; | |
122 | if (dcb) { | |
123 | if (dcb[0] >= 0x15) | |
124 | i2c = ROMPTR(dev, dcb[2]); | |
125 | if (dcb[0] >= 0x30) | |
126 | i2c = ROMPTR(dev, dcb[4]); | |
127 | } | |
128 | ||
129 | /* early revisions had no version number, use dcb version */ | |
130 | if (i2c) { | |
131 | *version = dcb[0]; | |
132 | if (*version >= 0x30) | |
133 | *version = i2c[0]; | |
134 | } | |
135 | ||
136 | return i2c; | |
137 | } | |
138 | ||
6ee73861 | 139 | int |
486a45c2 | 140 | nouveau_i2c_init(struct drm_device *dev) |
6ee73861 BS |
141 | { |
142 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
486a45c2 BS |
143 | struct nvbios *bios = &dev_priv->vbios; |
144 | struct nouveau_i2c_chan *port; | |
59365671 | 145 | u8 version = 0x00, entries, recordlen; |
486a45c2 | 146 | u8 *i2c, *entry, legacy[2][4] = {}; |
486a45c2 BS |
147 | int ret, i; |
148 | ||
149 | INIT_LIST_HEAD(&dev_priv->i2c_ports); | |
150 | ||
151 | i2c = i2c_table(dev, &version); | |
152 | if (!i2c) { | |
153 | u8 *bmp = &bios->data[bios->offset]; | |
154 | if (bios->type != NVBIOS_BMP) | |
155 | return -ENODEV; | |
156 | ||
157 | legacy[0][0] = NV_CIO_CRE_DDC_WR__INDEX; | |
158 | legacy[0][1] = NV_CIO_CRE_DDC_STATUS__INDEX; | |
159 | legacy[1][0] = NV_CIO_CRE_DDC0_WR__INDEX; | |
160 | legacy[1][1] = NV_CIO_CRE_DDC0_STATUS__INDEX; | |
161 | ||
162 | /* BMP (from v4.0) has i2c info in the structure, it's in a | |
163 | * fixed location on earlier VBIOS | |
164 | */ | |
165 | if (bmp[5] < 4) | |
166 | i2c = &bios->data[0x48]; | |
167 | else | |
168 | i2c = &bmp[0x36]; | |
169 | ||
170 | if (i2c[4]) legacy[0][0] = i2c[4]; | |
171 | if (i2c[5]) legacy[0][1] = i2c[5]; | |
172 | if (i2c[6]) legacy[1][0] = i2c[6]; | |
173 | if (i2c[7]) legacy[1][1] = i2c[7]; | |
174 | } | |
6ee73861 | 175 | |
59365671 | 176 | if (version >= 0x30) { |
486a45c2 BS |
177 | entry = i2c[1] + i2c; |
178 | entries = i2c[2]; | |
179 | recordlen = i2c[3]; | |
180 | } else | |
59365671 | 181 | if (version) { |
486a45c2 BS |
182 | entry = i2c; |
183 | entries = 16; | |
184 | recordlen = 4; | |
185 | } else { | |
186 | entry = legacy[0]; | |
187 | entries = 2; | |
188 | recordlen = 4; | |
6ee73861 BS |
189 | } |
190 | ||
486a45c2 BS |
191 | for (i = 0; i < entries; i++, entry += recordlen) { |
192 | port = kzalloc(sizeof(*port), GFP_KERNEL); | |
193 | if (port == NULL) { | |
194 | nouveau_i2c_fini(dev); | |
195 | return -ENOMEM; | |
196 | } | |
197 | ||
198 | port->type = entry[3]; | |
199 | if (version < 0x30) { | |
200 | port->type &= 0x07; | |
201 | if (port->type == 0x07) | |
202 | port->type = 0xff; | |
203 | } | |
204 | ||
205 | if (port->type == 0xff) { | |
206 | kfree(port); | |
207 | continue; | |
208 | } | |
209 | ||
210 | switch (port->type) { | |
211 | case 0: /* NV04:NV50 */ | |
2bdb06e3 BS |
212 | port->drive = entry[0]; |
213 | port->sense = entry[1]; | |
486a45c2 BS |
214 | break; |
215 | case 4: /* NV4E */ | |
2bdb06e3 BS |
216 | port->drive = 0x600800 + entry[1]; |
217 | port->sense = port->drive; | |
486a45c2 BS |
218 | break; |
219 | case 5: /* NV50- */ | |
2bdb06e3 | 220 | port->drive = entry[0] & 0x0f; |
486a45c2 | 221 | if (dev_priv->card_type < NV_D0) { |
2bdb06e3 | 222 | if (port->drive >= ARRAY_SIZE(nv50_i2c_port)) |
486a45c2 | 223 | break; |
2bdb06e3 BS |
224 | port->drive = nv50_i2c_port[port->drive]; |
225 | port->sense = port->drive; | |
486a45c2 | 226 | } else { |
2bdb06e3 BS |
227 | port->drive = 0x00d014 + (port->drive * 0x20); |
228 | port->sense = port->drive; | |
486a45c2 | 229 | } |
486a45c2 BS |
230 | break; |
231 | case 6: /* NV50- DP AUX */ | |
3d7a1da2 | 232 | port->drive = entry[0] & 0x0f; |
2bdb06e3 | 233 | port->sense = port->drive; |
486a45c2 BS |
234 | port->adapter.algo = &nouveau_dp_i2c_algo; |
235 | break; | |
236 | default: | |
237 | break; | |
238 | } | |
239 | ||
ec9b3a9d | 240 | if (!port->adapter.algo && !port->drive) { |
486a45c2 | 241 | NV_ERROR(dev, "I2C%d: type %d index %x/%x unknown\n", |
2bdb06e3 | 242 | i, port->type, port->drive, port->sense); |
486a45c2 BS |
243 | kfree(port); |
244 | continue; | |
245 | } | |
246 | ||
247 | snprintf(port->adapter.name, sizeof(port->adapter.name), | |
248 | "nouveau-%s-%d", pci_name(dev->pdev), i); | |
249 | port->adapter.owner = THIS_MODULE; | |
250 | port->adapter.dev.parent = &dev->pdev->dev; | |
251 | port->dev = dev; | |
252 | port->index = i; | |
253 | port->dcb = ROM32(entry[0]); | |
254 | i2c_set_adapdata(&port->adapter, i2c); | |
255 | ||
ec9b3a9d BS |
256 | if (port->adapter.algo != &nouveau_dp_i2c_algo) { |
257 | port->adapter.algo_data = &port->bit; | |
258 | port->bit.udelay = 10; | |
259 | port->bit.timeout = usecs_to_jiffies(2200); | |
260 | port->bit.data = port; | |
261 | port->bit.setsda = i2c_drive_sda; | |
262 | port->bit.setscl = i2c_drive_scl; | |
263 | port->bit.getsda = i2c_sense_sda; | |
264 | port->bit.getscl = i2c_sense_scl; | |
265 | ||
266 | i2c_drive_scl(port, 0); | |
267 | i2c_drive_sda(port, 1); | |
268 | i2c_drive_scl(port, 1); | |
269 | ||
270 | ret = i2c_bit_add_bus(&port->adapter); | |
271 | } else { | |
272 | port->adapter.algo = &nouveau_dp_i2c_algo; | |
273 | ret = i2c_add_adapter(&port->adapter); | |
274 | } | |
275 | ||
486a45c2 BS |
276 | if (ret) { |
277 | NV_ERROR(dev, "I2C%d: failed register: %d\n", i, ret); | |
278 | kfree(port); | |
279 | continue; | |
280 | } | |
6ee73861 | 281 | |
486a45c2 | 282 | list_add_tail(&port->head, &dev_priv->i2c_ports); |
6ee73861 BS |
283 | } |
284 | ||
6ee73861 BS |
285 | return 0; |
286 | } | |
287 | ||
288 | void | |
486a45c2 | 289 | nouveau_i2c_fini(struct drm_device *dev) |
6ee73861 | 290 | { |
486a45c2 BS |
291 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
292 | struct nouveau_i2c_chan *port, *tmp; | |
6ee73861 | 293 | |
486a45c2 BS |
294 | list_for_each_entry_safe(port, tmp, &dev_priv->i2c_ports, head) { |
295 | i2c_del_adapter(&port->adapter); | |
296 | kfree(port); | |
297 | } | |
6ee73861 BS |
298 | } |
299 | ||
300 | struct nouveau_i2c_chan * | |
486a45c2 | 301 | nouveau_i2c_find(struct drm_device *dev, u8 index) |
6ee73861 BS |
302 | { |
303 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
486a45c2 BS |
304 | struct nouveau_i2c_chan *port; |
305 | ||
306 | if (index == NV_I2C_DEFAULT(0) || | |
307 | index == NV_I2C_DEFAULT(1)) { | |
308 | u8 version, *i2c = i2c_table(dev, &version); | |
309 | if (i2c && version >= 0x30) { | |
310 | if (index == NV_I2C_DEFAULT(0)) | |
311 | index = (i2c[4] & 0x0f); | |
312 | else | |
313 | index = (i2c[4] & 0xf0) >> 4; | |
314 | } else { | |
315 | index = 2; | |
316 | } | |
317 | } | |
6ee73861 | 318 | |
486a45c2 BS |
319 | list_for_each_entry(port, &dev_priv->i2c_ports, head) { |
320 | if (port->index == index) | |
321 | break; | |
322 | } | |
6ee73861 | 323 | |
486a45c2 BS |
324 | if (&port->head == &dev_priv->i2c_ports) |
325 | return NULL; | |
7e99a9b2 | 326 | |
486a45c2 BS |
327 | if (dev_priv->card_type >= NV_50 && (port->dcb & 0x00000100)) { |
328 | u32 reg = 0x00e500, val; | |
329 | if (port->type == 6) { | |
2bdb06e3 | 330 | reg += port->drive * 0x50; |
7e99a9b2 BS |
331 | val = 0x2002; |
332 | } else { | |
486a45c2 | 333 | reg += ((port->dcb & 0x1e00) >> 9) * 0x50; |
7e99a9b2 BS |
334 | val = 0xe001; |
335 | } | |
336 | ||
6b70e481 BS |
337 | /* nfi, but neither auxch or i2c work if it's 1 */ |
338 | nv_mask(dev, reg + 0x0c, 0x00000001, 0x00000000); | |
339 | /* nfi, but switches auxch vs normal i2c */ | |
340 | nv_mask(dev, reg + 0x00, 0x0000f003, val); | |
6ee73861 BS |
341 | } |
342 | ||
486a45c2 | 343 | return port; |
6ee73861 BS |
344 | } |
345 | ||
6d416d80 FJ |
346 | bool |
347 | nouveau_probe_i2c_addr(struct nouveau_i2c_chan *i2c, int addr) | |
348 | { | |
f1feda70 FJ |
349 | uint8_t buf[] = { 0 }; |
350 | struct i2c_msg msgs[] = { | |
351 | { | |
352 | .addr = addr, | |
353 | .flags = 0, | |
354 | .len = 1, | |
355 | .buf = buf, | |
356 | }, | |
357 | { | |
358 | .addr = addr, | |
359 | .flags = I2C_M_RD, | |
360 | .len = 1, | |
361 | .buf = buf, | |
362 | } | |
6d416d80 FJ |
363 | }; |
364 | ||
f1feda70 | 365 | return i2c_transfer(&i2c->adapter, msgs, 2) == 2; |
6d416d80 FJ |
366 | } |
367 | ||
368 | int | |
369 | nouveau_i2c_identify(struct drm_device *dev, const char *what, | |
66146da0 FJ |
370 | struct i2c_board_info *info, |
371 | bool (*match)(struct nouveau_i2c_chan *, | |
372 | struct i2c_board_info *), | |
373 | int index) | |
6d416d80 FJ |
374 | { |
375 | struct nouveau_i2c_chan *i2c = nouveau_i2c_find(dev, index); | |
946fd35f | 376 | int i; |
6d416d80 | 377 | |
9e3b6b99 BS |
378 | if (!i2c) { |
379 | NV_DEBUG(dev, "No bus when probing %s on %d\n", what, index); | |
380 | return -ENODEV; | |
381 | } | |
6d416d80 | 382 | |
9e3b6b99 BS |
383 | NV_DEBUG(dev, "Probing %ss on I2C bus: %d\n", what, i2c->index); |
384 | for (i = 0; info[i].addr; i++) { | |
66146da0 FJ |
385 | if (nouveau_probe_i2c_addr(i2c, info[i].addr) && |
386 | (!match || match(i2c, &info[i]))) { | |
6d416d80 | 387 | NV_INFO(dev, "Detected %s: %s\n", what, info[i].type); |
946fd35f | 388 | return i; |
6d416d80 FJ |
389 | } |
390 | } | |
391 | ||
392 | NV_DEBUG(dev, "No devices found.\n"); | |
946fd35f | 393 | return -ENODEV; |
6d416d80 | 394 | } |