diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2014-05-13 00:47:36 -0400 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2014-06-11 02:09:16 -0400 |
commit | 3668a339d6e7d47f777b8ce86bd7ca53451df6bb (patch) | |
tree | e5b5f3a569c34511602f1bcf2ae95125e5ec1431 | |
parent | c26fe843560fe01b4b652ee21952d33f60f4e699 (diff) |
drm/nouveau/i2c: add interfaces to support handling aux channel interrupts
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
-rw-r--r-- | drivers/gpu/drm/nouveau/core/include/subdev/i2c.h | 13 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/core/subdev/i2c/base.c | 65 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h | 15 |
4 files changed, 94 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h b/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h index 857a11025dac..a4c9c8b5cb70 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h | |||
@@ -14,12 +14,24 @@ | |||
14 | #define NV_I2C_TYPE_EXTDDC(e) (0x0005 | (e) << 8) | 14 | #define NV_I2C_TYPE_EXTDDC(e) (0x0005 | (e) << 8) |
15 | #define NV_I2C_TYPE_EXTAUX(e) (0x0006 | (e) << 8) | 15 | #define NV_I2C_TYPE_EXTAUX(e) (0x0006 | (e) << 8) |
16 | 16 | ||
17 | enum nvkm_i2c_event { | ||
18 | NVKM_I2C_PLUG = 1, | ||
19 | NVKM_I2C_UNPLUG = 2, | ||
20 | NVKM_I2C_IRQ = 4, | ||
21 | NVKM_I2C_DONE = 8, | ||
22 | NVKM_I2C_ANY = (NVKM_I2C_PLUG | | ||
23 | NVKM_I2C_UNPLUG | | ||
24 | NVKM_I2C_IRQ | | ||
25 | NVKM_I2C_DONE), | ||
26 | }; | ||
27 | |||
17 | struct nouveau_i2c_port { | 28 | struct nouveau_i2c_port { |
18 | struct nouveau_object base; | 29 | struct nouveau_object base; |
19 | struct i2c_adapter adapter; | 30 | struct i2c_adapter adapter; |
20 | 31 | ||
21 | struct list_head head; | 32 | struct list_head head; |
22 | u8 index; | 33 | u8 index; |
34 | int aux; | ||
23 | 35 | ||
24 | const struct nouveau_i2c_func *func; | 36 | const struct nouveau_i2c_func *func; |
25 | }; | 37 | }; |
@@ -46,6 +58,7 @@ struct nouveau_i2c_board_info { | |||
46 | 58 | ||
47 | struct nouveau_i2c { | 59 | struct nouveau_i2c { |
48 | struct nouveau_subdev base; | 60 | struct nouveau_subdev base; |
61 | struct nouveau_event *ntfy; | ||
49 | 62 | ||
50 | struct nouveau_i2c_port *(*find)(struct nouveau_i2c *, u8 index); | 63 | struct nouveau_i2c_port *(*find)(struct nouveau_i2c *, u8 index); |
51 | struct nouveau_i2c_port *(*find_type)(struct nouveau_i2c *, u16 type); | 64 | struct nouveau_i2c_port *(*find_type)(struct nouveau_i2c *, u16 type); |
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c index 1c6fa86e9425..0e36057609e7 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c | |||
@@ -23,6 +23,7 @@ | |||
23 | */ | 23 | */ |
24 | 24 | ||
25 | #include <core/option.h> | 25 | #include <core/option.h> |
26 | #include <core/event.h> | ||
26 | 27 | ||
27 | #include <subdev/bios.h> | 28 | #include <subdev/bios.h> |
28 | #include <subdev/bios/dcb.h> | 29 | #include <subdev/bios/dcb.h> |
@@ -114,6 +115,7 @@ nouveau_i2c_port_create_(struct nouveau_object *parent, | |||
114 | port->adapter.owner = THIS_MODULE; | 115 | port->adapter.owner = THIS_MODULE; |
115 | port->adapter.dev.parent = nv_device_base(device); | 116 | port->adapter.dev.parent = nv_device_base(device); |
116 | port->index = index; | 117 | port->index = index; |
118 | port->aux = -1; | ||
117 | port->func = func; | 119 | port->func = func; |
118 | 120 | ||
119 | if ( algo == &nouveau_i2c_bit_algo && | 121 | if ( algo == &nouveau_i2c_bit_algo && |
@@ -236,11 +238,59 @@ nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what, | |||
236 | return -ENODEV; | 238 | return -ENODEV; |
237 | } | 239 | } |
238 | 240 | ||
241 | static void | ||
242 | nouveau_i2c_intr_disable(struct nouveau_event *event, int type, int index) | ||
243 | { | ||
244 | struct nouveau_i2c *i2c = nouveau_i2c(event->priv); | ||
245 | struct nouveau_i2c_port *port = i2c->find(i2c, index); | ||
246 | const struct nouveau_i2c_impl *impl = (void *)nv_object(i2c)->oclass; | ||
247 | if (port && port->aux >= 0) | ||
248 | impl->aux_mask(i2c, type, 1 << port->aux, 0); | ||
249 | } | ||
250 | |||
251 | static void | ||
252 | nouveau_i2c_intr_enable(struct nouveau_event *event, int type, int index) | ||
253 | { | ||
254 | struct nouveau_i2c *i2c = nouveau_i2c(event->priv); | ||
255 | struct nouveau_i2c_port *port = i2c->find(i2c, index); | ||
256 | const struct nouveau_i2c_impl *impl = (void *)nv_object(i2c)->oclass; | ||
257 | if (port && port->aux >= 0) | ||
258 | impl->aux_mask(i2c, type, 1 << port->aux, 1 << port->aux); | ||
259 | } | ||
260 | |||
261 | static void | ||
262 | nouveau_i2c_intr(struct nouveau_subdev *subdev) | ||
263 | { | ||
264 | struct nouveau_i2c_impl *impl = (void *)nv_oclass(subdev); | ||
265 | struct nouveau_i2c *i2c = nouveau_i2c(subdev); | ||
266 | struct nouveau_i2c_port *port; | ||
267 | u32 hi, lo, rq, tx, e; | ||
268 | |||
269 | if (impl->aux_stat) { | ||
270 | impl->aux_stat(i2c, &hi, &lo, &rq, &tx); | ||
271 | if (hi || lo || rq || tx) { | ||
272 | list_for_each_entry(port, &i2c->ports, head) { | ||
273 | if (e = 0, port->aux < 0) | ||
274 | continue; | ||
275 | |||
276 | if (hi & (1 << port->aux)) e |= NVKM_I2C_PLUG; | ||
277 | if (lo & (1 << port->aux)) e |= NVKM_I2C_UNPLUG; | ||
278 | if (rq & (1 << port->aux)) e |= NVKM_I2C_IRQ; | ||
279 | if (tx & (1 << port->aux)) e |= NVKM_I2C_DONE; | ||
280 | |||
281 | nouveau_event_trigger(i2c->ntfy, e, port->index); | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | } | ||
286 | |||
239 | int | 287 | int |
240 | _nouveau_i2c_fini(struct nouveau_object *object, bool suspend) | 288 | _nouveau_i2c_fini(struct nouveau_object *object, bool suspend) |
241 | { | 289 | { |
290 | struct nouveau_i2c_impl *impl = (void *)nv_oclass(object); | ||
242 | struct nouveau_i2c *i2c = (void *)object; | 291 | struct nouveau_i2c *i2c = (void *)object; |
243 | struct nouveau_i2c_port *port; | 292 | struct nouveau_i2c_port *port; |
293 | u32 mask; | ||
244 | int ret; | 294 | int ret; |
245 | 295 | ||
246 | list_for_each_entry(port, &i2c->ports, head) { | 296 | list_for_each_entry(port, &i2c->ports, head) { |
@@ -249,6 +299,11 @@ _nouveau_i2c_fini(struct nouveau_object *object, bool suspend) | |||
249 | goto fail; | 299 | goto fail; |
250 | } | 300 | } |
251 | 301 | ||
302 | if ((mask = (1 << impl->aux) - 1), impl->aux_stat) { | ||
303 | impl->aux_mask(i2c, NVKM_I2C_ANY, mask, 0); | ||
304 | impl->aux_stat(i2c, &mask, &mask, &mask, &mask); | ||
305 | } | ||
306 | |||
252 | return nouveau_subdev_fini(&i2c->base, suspend); | 307 | return nouveau_subdev_fini(&i2c->base, suspend); |
253 | fail: | 308 | fail: |
254 | list_for_each_entry_continue_reverse(port, &i2c->ports, head) { | 309 | list_for_each_entry_continue_reverse(port, &i2c->ports, head) { |
@@ -289,6 +344,8 @@ _nouveau_i2c_dtor(struct nouveau_object *object) | |||
289 | struct nouveau_i2c *i2c = (void *)object; | 344 | struct nouveau_i2c *i2c = (void *)object; |
290 | struct nouveau_i2c_port *port, *temp; | 345 | struct nouveau_i2c_port *port, *temp; |
291 | 346 | ||
347 | nouveau_event_destroy(&i2c->ntfy); | ||
348 | |||
292 | list_for_each_entry_safe(port, temp, &i2c->ports, head) { | 349 | list_for_each_entry_safe(port, temp, &i2c->ports, head) { |
293 | nouveau_object_ref(NULL, (struct nouveau_object **)&port); | 350 | nouveau_object_ref(NULL, (struct nouveau_object **)&port); |
294 | } | 351 | } |
@@ -323,6 +380,7 @@ nouveau_i2c_create_(struct nouveau_object *parent, | |||
323 | if (ret) | 380 | if (ret) |
324 | return ret; | 381 | return ret; |
325 | 382 | ||
383 | nv_subdev(i2c)->intr = nouveau_i2c_intr; | ||
326 | i2c->find = nouveau_i2c_find; | 384 | i2c->find = nouveau_i2c_find; |
327 | i2c->find_type = nouveau_i2c_find_type; | 385 | i2c->find_type = nouveau_i2c_find_type; |
328 | i2c->identify = nouveau_i2c_identify; | 386 | i2c->identify = nouveau_i2c_identify; |
@@ -379,6 +437,13 @@ nouveau_i2c_create_(struct nouveau_object *parent, | |||
379 | } | 437 | } |
380 | } | 438 | } |
381 | 439 | ||
440 | ret = nouveau_event_create(4, index, &i2c->ntfy); | ||
441 | if (ret) | ||
442 | return ret; | ||
443 | |||
444 | i2c->ntfy->priv = i2c; | ||
445 | i2c->ntfy->enable = nouveau_i2c_intr_enable; | ||
446 | i2c->ntfy->disable = nouveau_i2c_intr_disable; | ||
382 | return 0; | 447 | return 0; |
383 | } | 448 | } |
384 | 449 | ||
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c index 768b849a7370..900f464c716c 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c | |||
@@ -232,6 +232,7 @@ nv94_aux_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine, | |||
232 | if (ret) | 232 | if (ret) |
233 | return ret; | 233 | return ret; |
234 | 234 | ||
235 | port->base.aux = info->drive; | ||
235 | port->addr = info->drive; | 236 | port->addr = info->drive; |
236 | if (info->share != DCB_I2C_UNUSED) { | 237 | if (info->share != DCB_I2C_UNUSED) { |
237 | port->ctrl = 0x00e500 + (info->drive * 0x50); | 238 | port->ctrl = 0x00e500 + (info->drive * 0x50); |
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h b/drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h index ec1934205cf5..27b9a9717229 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h | |||
@@ -55,7 +55,22 @@ extern const struct i2c_algorithm nouveau_i2c_aux_algo; | |||
55 | 55 | ||
56 | struct nouveau_i2c_impl { | 56 | struct nouveau_i2c_impl { |
57 | struct nouveau_oclass base; | 57 | struct nouveau_oclass base; |
58 | |||
59 | /* supported i2c port classes */ | ||
58 | struct nouveau_oclass *sclass; | 60 | struct nouveau_oclass *sclass; |
61 | |||
62 | /* number of native dp aux channels present */ | ||
63 | int aux; | ||
64 | |||
65 | /* read and ack pending interrupts, returning only data | ||
66 | * for ports that have not been masked off, while still | ||
67 | * performing the ack for anything that was pending. | ||
68 | */ | ||
69 | void (*aux_stat)(struct nouveau_i2c *, u32 *, u32 *, u32 *, u32 *); | ||
70 | |||
71 | /* mask on/off interrupt types for a given set of auxch | ||
72 | */ | ||
73 | void (*aux_mask)(struct nouveau_i2c *, u32, u32, u32); | ||
59 | }; | 74 | }; |
60 | 75 | ||
61 | #endif | 76 | #endif |