Commit | Line | Data |
---|---|---|
6ee73861 | 1 | /* |
7dcd060c | 2 | * Copyright 2013 Red Hat Inc. |
6ee73861 BS |
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 | ||
7dcd060c | 25 | #include <core/option.h> |
e0cd3608 | 26 | |
5effecd4 BS |
27 | #include <subdev/bios.h> |
28 | #include <subdev/bios/dcb.h> | |
29 | #include <subdev/bios/i2c.h> | |
7dcd060c BS |
30 | #include <subdev/i2c.h> |
31 | #include <subdev/vga.h> | |
6ee73861 | 32 | |
7dcd060c BS |
33 | /****************************************************************************** |
34 | * interface to linux i2c bit-banging algorithm | |
35 | *****************************************************************************/ | |
36 | ||
37 | #ifdef CONFIG_NOUVEAU_I2C_INTERNAL_DEFAULT | |
38 | #define CSTMSEL true | |
39 | #else | |
40 | #define CSTMSEL false | |
41 | #endif | |
42 | ||
43 | static int | |
44 | nouveau_i2c_pre_xfer(struct i2c_adapter *adap) | |
4196faa8 | 45 | { |
7dcd060c BS |
46 | struct i2c_algo_bit_data *bit = adap->algo_data; |
47 | struct nouveau_i2c_port *port = bit->data; | |
48 | if (port->func->acquire) | |
49 | port->func->acquire(port); | |
50 | return 0; | |
51 | } | |
4196faa8 | 52 | |
7dcd060c BS |
53 | static void |
54 | nouveau_i2c_setscl(void *data, int state) | |
55 | { | |
56 | struct nouveau_i2c_port *port = data; | |
57 | port->func->drive_scl(port, state); | |
58 | } | |
4196faa8 | 59 | |
7dcd060c BS |
60 | static void |
61 | nouveau_i2c_setsda(void *data, int state) | |
62 | { | |
63 | struct nouveau_i2c_port *port = data; | |
64 | port->func->drive_sda(port, state); | |
4196faa8 BS |
65 | } |
66 | ||
7dcd060c BS |
67 | static int |
68 | nouveau_i2c_getscl(void *data) | |
4196faa8 | 69 | { |
7dcd060c BS |
70 | struct nouveau_i2c_port *port = data; |
71 | return port->func->sense_scl(port); | |
72 | } | |
4196faa8 | 73 | |
7dcd060c BS |
74 | static int |
75 | nouveau_i2c_getsda(void *data) | |
76 | { | |
77 | struct nouveau_i2c_port *port = data; | |
78 | return port->func->sense_sda(port); | |
79 | } | |
4196faa8 | 80 | |
7dcd060c BS |
81 | /****************************************************************************** |
82 | * base i2c "port" class implementation | |
83 | *****************************************************************************/ | |
84 | ||
85 | void | |
86 | _nouveau_i2c_port_dtor(struct nouveau_object *object) | |
87 | { | |
88 | struct nouveau_i2c_port *port = (void *)object; | |
89 | i2c_del_adapter(&port->adapter); | |
90 | nouveau_object_destroy(&port->base); | |
4196faa8 BS |
91 | } |
92 | ||
7dcd060c BS |
93 | int |
94 | nouveau_i2c_port_create_(struct nouveau_object *parent, | |
95 | struct nouveau_object *engine, | |
96 | struct nouveau_oclass *oclass, u8 index, | |
97 | const struct i2c_algorithm *algo, | |
c865534f | 98 | const struct nouveau_i2c_func *func, |
7dcd060c | 99 | int size, void **pobject) |
4196faa8 | 100 | { |
7dcd060c BS |
101 | struct nouveau_device *device = nv_device(parent); |
102 | struct nouveau_i2c *i2c = (void *)engine; | |
103 | struct nouveau_i2c_port *port; | |
104 | int ret; | |
105 | ||
106 | ret = nouveau_object_create_(parent, engine, oclass, 0, size, pobject); | |
107 | port = *pobject; | |
108 | if (ret) | |
109 | return ret; | |
110 | ||
111 | snprintf(port->adapter.name, sizeof(port->adapter.name), | |
112 | "nouveau-%s-%d", device->name, index); | |
113 | port->adapter.owner = THIS_MODULE; | |
420b9469 | 114 | port->adapter.dev.parent = nv_device_base(device); |
7dcd060c | 115 | port->index = index; |
c865534f | 116 | port->func = func; |
7dcd060c BS |
117 | i2c_set_adapdata(&port->adapter, i2c); |
118 | ||
119 | if ( algo == &nouveau_i2c_bit_algo && | |
120 | !nouveau_boolopt(device->cfgopt, "NvI2C", CSTMSEL)) { | |
121 | struct i2c_algo_bit_data *bit; | |
122 | ||
123 | bit = kzalloc(sizeof(*bit), GFP_KERNEL); | |
124 | if (!bit) | |
125 | return -ENOMEM; | |
126 | ||
127 | bit->udelay = 10; | |
128 | bit->timeout = usecs_to_jiffies(2200); | |
129 | bit->data = port; | |
130 | bit->pre_xfer = nouveau_i2c_pre_xfer; | |
131 | bit->setsda = nouveau_i2c_setsda; | |
132 | bit->setscl = nouveau_i2c_setscl; | |
133 | bit->getsda = nouveau_i2c_getsda; | |
134 | bit->getscl = nouveau_i2c_getscl; | |
135 | ||
136 | port->adapter.algo_data = bit; | |
137 | ret = i2c_bit_add_bus(&port->adapter); | |
138 | } else { | |
139 | port->adapter.algo_data = port; | |
140 | port->adapter.algo = algo; | |
141 | ret = i2c_add_adapter(&port->adapter); | |
142 | } | |
143 | ||
144 | /* drop port's i2c subdev refcount, i2c handles this itself */ | |
d395f1e4 | 145 | if (ret == 0) |
7dcd060c | 146 | list_add_tail(&port->head, &i2c->ports); |
7dcd060c | 147 | return ret; |
4196faa8 BS |
148 | } |
149 | ||
7dcd060c BS |
150 | /****************************************************************************** |
151 | * base i2c subdev class implementation | |
152 | *****************************************************************************/ | |
153 | ||
4196faa8 BS |
154 | static struct nouveau_i2c_port * |
155 | nouveau_i2c_find(struct nouveau_i2c *i2c, u8 index) | |
156 | { | |
157 | struct nouveau_bios *bios = nouveau_bios(i2c); | |
158 | struct nouveau_i2c_port *port; | |
159 | ||
160 | if (index == NV_I2C_DEFAULT(0) || | |
161 | index == NV_I2C_DEFAULT(1)) { | |
162 | u8 ver, hdr, cnt, len; | |
163 | u16 i2c = dcb_i2c_table(bios, &ver, &hdr, &cnt, &len); | |
164 | if (i2c && ver >= 0x30) { | |
165 | u8 auxidx = nv_ro08(bios, i2c + 4); | |
166 | if (index == NV_I2C_DEFAULT(0)) | |
167 | index = (auxidx & 0x0f) >> 0; | |
168 | else | |
169 | index = (auxidx & 0xf0) >> 4; | |
170 | } else { | |
171 | index = 2; | |
172 | } | |
173 | } | |
174 | ||
175 | list_for_each_entry(port, &i2c->ports, head) { | |
176 | if (port->index == index) | |
df3ef6a1 | 177 | return port; |
4196faa8 BS |
178 | } |
179 | ||
df3ef6a1 | 180 | return NULL; |
4196faa8 BS |
181 | } |
182 | ||
548ddb6d BS |
183 | static struct nouveau_i2c_port * |
184 | nouveau_i2c_find_type(struct nouveau_i2c *i2c, u16 type) | |
185 | { | |
186 | struct nouveau_i2c_port *port; | |
187 | ||
188 | list_for_each_entry(port, &i2c->ports, head) { | |
7dcd060c | 189 | if (nv_hclass(port) == type) |
548ddb6d BS |
190 | return port; |
191 | } | |
192 | ||
193 | return NULL; | |
194 | } | |
195 | ||
4196faa8 BS |
196 | static int |
197 | nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what, | |
9e2b734f | 198 | struct nouveau_i2c_board_info *info, |
4196faa8 | 199 | bool (*match)(struct nouveau_i2c_port *, |
fdd239ac | 200 | struct i2c_board_info *, void *), void *data) |
6ee73861 | 201 | { |
4196faa8 BS |
202 | struct nouveau_i2c_port *port = nouveau_i2c_find(i2c, index); |
203 | int i; | |
204 | ||
205 | if (!port) { | |
206 | nv_debug(i2c, "no bus when probing %s on %d\n", what, index); | |
207 | return -ENODEV; | |
208 | } | |
209 | ||
210 | nv_debug(i2c, "probing %ss on bus: %d\n", what, port->index); | |
9e2b734f MP |
211 | for (i = 0; info[i].dev.addr; i++) { |
212 | u8 orig_udelay = 0; | |
213 | ||
214 | if ((port->adapter.algo == &i2c_bit_algo) && | |
215 | (info[i].udelay != 0)) { | |
216 | struct i2c_algo_bit_data *algo = port->adapter.algo_data; | |
217 | nv_debug(i2c, "using custom udelay %d instead of %d\n", | |
218 | info[i].udelay, algo->udelay); | |
219 | orig_udelay = algo->udelay; | |
220 | algo->udelay = info[i].udelay; | |
221 | } | |
222 | ||
223 | if (nv_probe_i2c(port, info[i].dev.addr) && | |
fdd239ac | 224 | (!match || match(port, &info[i].dev, data))) { |
9e2b734f MP |
225 | nv_info(i2c, "detected %s: %s\n", what, |
226 | info[i].dev.type); | |
4196faa8 BS |
227 | return i; |
228 | } | |
9e2b734f MP |
229 | |
230 | if (orig_udelay) { | |
231 | struct i2c_algo_bit_data *algo = port->adapter.algo_data; | |
232 | algo->udelay = orig_udelay; | |
233 | } | |
4196faa8 BS |
234 | } |
235 | ||
236 | nv_debug(i2c, "no devices found.\n"); | |
237 | return -ENODEV; | |
238 | } | |
239 | ||
7dcd060c BS |
240 | int |
241 | _nouveau_i2c_fini(struct nouveau_object *object, bool suspend) | |
4196faa8 | 242 | { |
7dcd060c BS |
243 | struct nouveau_i2c *i2c = (void *)object; |
244 | struct nouveau_i2c_port *port; | |
245 | int ret; | |
4196faa8 | 246 | |
7dcd060c BS |
247 | list_for_each_entry(port, &i2c->ports, head) { |
248 | ret = nv_ofuncs(port)->fini(nv_object(port), suspend); | |
249 | if (ret && suspend) | |
250 | goto fail; | |
2bdb06e3 | 251 | } |
4196faa8 | 252 | |
7dcd060c BS |
253 | return nouveau_subdev_fini(&i2c->base, suspend); |
254 | fail: | |
255 | list_for_each_entry_continue_reverse(port, &i2c->ports, head) { | |
256 | nv_ofuncs(port)->init(nv_object(port)); | |
2bdb06e3 | 257 | } |
7dcd060c BS |
258 | |
259 | return ret; | |
6ee73861 BS |
260 | } |
261 | ||
4196faa8 | 262 | int |
7dcd060c | 263 | _nouveau_i2c_init(struct nouveau_object *object) |
eeb3ca12 | 264 | { |
7dcd060c BS |
265 | struct nouveau_i2c *i2c = (void *)object; |
266 | struct nouveau_i2c_port *port; | |
267 | int ret; | |
268 | ||
269 | ret = nouveau_subdev_init(&i2c->base); | |
270 | if (ret == 0) { | |
271 | list_for_each_entry(port, &i2c->ports, head) { | |
272 | ret = nv_ofuncs(port)->init(nv_object(port)); | |
273 | if (ret) | |
274 | goto fail; | |
275 | } | |
2bdb06e3 | 276 | } |
4196faa8 | 277 | |
7dcd060c BS |
278 | return ret; |
279 | fail: | |
280 | list_for_each_entry_continue_reverse(port, &i2c->ports, head) { | |
281 | nv_ofuncs(port)->fini(nv_object(port), false); | |
2bdb06e3 | 282 | } |
4196faa8 | 283 | |
7dcd060c | 284 | return ret; |
eeb3ca12 | 285 | } |
6ee73861 | 286 | |
7dcd060c BS |
287 | void |
288 | _nouveau_i2c_dtor(struct nouveau_object *object) | |
df3ef6a1 | 289 | { |
7dcd060c BS |
290 | struct nouveau_i2c *i2c = (void *)object; |
291 | struct nouveau_i2c_port *port, *temp; | |
292 | ||
293 | list_for_each_entry_safe(port, temp, &i2c->ports, head) { | |
294 | nouveau_object_ref(NULL, (struct nouveau_object **)&port); | |
df3ef6a1 BS |
295 | } |
296 | ||
7dcd060c | 297 | nouveau_subdev_destroy(&i2c->base); |
df3ef6a1 BS |
298 | } |
299 | ||
5effecd4 BS |
300 | static struct nouveau_oclass * |
301 | nouveau_i2c_extdev_sclass[] = { | |
302 | nouveau_anx9805_sclass, | |
303 | }; | |
304 | ||
7dcd060c BS |
305 | int |
306 | nouveau_i2c_create_(struct nouveau_object *parent, | |
307 | struct nouveau_object *engine, | |
308 | struct nouveau_oclass *oclass, | |
309 | struct nouveau_oclass *sclass, | |
310 | int length, void **pobject) | |
6ee73861 | 311 | { |
4196faa8 | 312 | struct nouveau_bios *bios = nouveau_bios(parent); |
4196faa8 | 313 | struct nouveau_i2c *i2c; |
7dcd060c | 314 | struct nouveau_object *object; |
4196faa8 | 315 | struct dcb_i2c_entry info; |
5effecd4 BS |
316 | int ret, i, j, index = -1; |
317 | struct dcb_output outp; | |
318 | u8 ver, hdr; | |
319 | u32 data; | |
4196faa8 BS |
320 | |
321 | ret = nouveau_subdev_create(parent, engine, oclass, 0, | |
322 | "I2C", "i2c", &i2c); | |
323 | *pobject = nv_object(i2c); | |
324 | if (ret) | |
325 | return ret; | |
326 | ||
327 | i2c->find = nouveau_i2c_find; | |
548ddb6d | 328 | i2c->find_type = nouveau_i2c_find_type; |
4196faa8 BS |
329 | i2c->identify = nouveau_i2c_identify; |
330 | INIT_LIST_HEAD(&i2c->ports); | |
331 | ||
7dcd060c | 332 | while (!dcb_i2c_parse(bios, ++index, &info)) { |
4196faa8 BS |
333 | if (info.type == DCB_I2C_UNUSED) |
334 | continue; | |
6ee73861 | 335 | |
7dcd060c BS |
336 | oclass = sclass; |
337 | do { | |
338 | ret = -EINVAL; | |
339 | if (oclass->handle == info.type) { | |
340 | ret = nouveau_object_ctor(*pobject, *pobject, | |
341 | oclass, &info, | |
342 | index, &object); | |
4196faa8 | 343 | } |
7dcd060c | 344 | } while (ret && (++oclass)->handle); |
6ee73861 BS |
345 | } |
346 | ||
5effecd4 BS |
347 | /* in addition to the busses specified in the i2c table, there |
348 | * may be ddc/aux channels hiding behind external tmds/dp/etc | |
349 | * transmitters. | |
350 | */ | |
351 | index = ((index + 0x0f) / 0x10) * 0x10; | |
352 | i = -1; | |
353 | while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &outp))) { | |
354 | if (!outp.location || !outp.extdev) | |
355 | continue; | |
356 | ||
357 | switch (outp.type) { | |
358 | case DCB_OUTPUT_TMDS: | |
359 | info.type = NV_I2C_TYPE_EXTDDC(outp.extdev); | |
360 | break; | |
361 | case DCB_OUTPUT_DP: | |
362 | info.type = NV_I2C_TYPE_EXTAUX(outp.extdev); | |
363 | break; | |
364 | default: | |
365 | continue; | |
366 | } | |
367 | ||
368 | ret = -ENODEV; | |
369 | j = -1; | |
370 | while (ret && ++j < ARRAY_SIZE(nouveau_i2c_extdev_sclass)) { | |
371 | parent = nv_object(i2c->find(i2c, outp.i2c_index)); | |
372 | oclass = nouveau_i2c_extdev_sclass[j]; | |
373 | do { | |
374 | if (oclass->handle != info.type) | |
375 | continue; | |
376 | ret = nouveau_object_ctor(parent, *pobject, | |
377 | oclass, NULL, | |
378 | index++, &object); | |
379 | } while (ret && (++oclass)->handle); | |
380 | } | |
381 | } | |
382 | ||
6ee73861 BS |
383 | return 0; |
384 | } |