drm/nouveau/i2c: add interfaces to support handling aux channel interrupts
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
parent
c26fe84356
commit
3668a339d6
4 changed files with 94 additions and 0 deletions
|
@ -14,12 +14,24 @@
|
||||||
#define NV_I2C_TYPE_EXTDDC(e) (0x0005 | (e) << 8)
|
#define NV_I2C_TYPE_EXTDDC(e) (0x0005 | (e) << 8)
|
||||||
#define NV_I2C_TYPE_EXTAUX(e) (0x0006 | (e) << 8)
|
#define NV_I2C_TYPE_EXTAUX(e) (0x0006 | (e) << 8)
|
||||||
|
|
||||||
|
enum nvkm_i2c_event {
|
||||||
|
NVKM_I2C_PLUG = 1,
|
||||||
|
NVKM_I2C_UNPLUG = 2,
|
||||||
|
NVKM_I2C_IRQ = 4,
|
||||||
|
NVKM_I2C_DONE = 8,
|
||||||
|
NVKM_I2C_ANY = (NVKM_I2C_PLUG |
|
||||||
|
NVKM_I2C_UNPLUG |
|
||||||
|
NVKM_I2C_IRQ |
|
||||||
|
NVKM_I2C_DONE),
|
||||||
|
};
|
||||||
|
|
||||||
struct nouveau_i2c_port {
|
struct nouveau_i2c_port {
|
||||||
struct nouveau_object base;
|
struct nouveau_object base;
|
||||||
struct i2c_adapter adapter;
|
struct i2c_adapter adapter;
|
||||||
|
|
||||||
struct list_head head;
|
struct list_head head;
|
||||||
u8 index;
|
u8 index;
|
||||||
|
int aux;
|
||||||
|
|
||||||
const struct nouveau_i2c_func *func;
|
const struct nouveau_i2c_func *func;
|
||||||
};
|
};
|
||||||
|
@ -46,6 +58,7 @@ struct nouveau_i2c_board_info {
|
||||||
|
|
||||||
struct nouveau_i2c {
|
struct nouveau_i2c {
|
||||||
struct nouveau_subdev base;
|
struct nouveau_subdev base;
|
||||||
|
struct nouveau_event *ntfy;
|
||||||
|
|
||||||
struct nouveau_i2c_port *(*find)(struct nouveau_i2c *, u8 index);
|
struct nouveau_i2c_port *(*find)(struct nouveau_i2c *, u8 index);
|
||||||
struct nouveau_i2c_port *(*find_type)(struct nouveau_i2c *, u16 type);
|
struct nouveau_i2c_port *(*find_type)(struct nouveau_i2c *, u16 type);
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <core/option.h>
|
#include <core/option.h>
|
||||||
|
#include <core/event.h>
|
||||||
|
|
||||||
#include <subdev/bios.h>
|
#include <subdev/bios.h>
|
||||||
#include <subdev/bios/dcb.h>
|
#include <subdev/bios/dcb.h>
|
||||||
|
@ -114,6 +115,7 @@ nouveau_i2c_port_create_(struct nouveau_object *parent,
|
||||||
port->adapter.owner = THIS_MODULE;
|
port->adapter.owner = THIS_MODULE;
|
||||||
port->adapter.dev.parent = nv_device_base(device);
|
port->adapter.dev.parent = nv_device_base(device);
|
||||||
port->index = index;
|
port->index = index;
|
||||||
|
port->aux = -1;
|
||||||
port->func = func;
|
port->func = func;
|
||||||
|
|
||||||
if ( algo == &nouveau_i2c_bit_algo &&
|
if ( algo == &nouveau_i2c_bit_algo &&
|
||||||
|
@ -236,11 +238,59 @@ nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what,
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nouveau_i2c_intr_disable(struct nouveau_event *event, int type, int index)
|
||||||
|
{
|
||||||
|
struct nouveau_i2c *i2c = nouveau_i2c(event->priv);
|
||||||
|
struct nouveau_i2c_port *port = i2c->find(i2c, index);
|
||||||
|
const struct nouveau_i2c_impl *impl = (void *)nv_object(i2c)->oclass;
|
||||||
|
if (port && port->aux >= 0)
|
||||||
|
impl->aux_mask(i2c, type, 1 << port->aux, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nouveau_i2c_intr_enable(struct nouveau_event *event, int type, int index)
|
||||||
|
{
|
||||||
|
struct nouveau_i2c *i2c = nouveau_i2c(event->priv);
|
||||||
|
struct nouveau_i2c_port *port = i2c->find(i2c, index);
|
||||||
|
const struct nouveau_i2c_impl *impl = (void *)nv_object(i2c)->oclass;
|
||||||
|
if (port && port->aux >= 0)
|
||||||
|
impl->aux_mask(i2c, type, 1 << port->aux, 1 << port->aux);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nouveau_i2c_intr(struct nouveau_subdev *subdev)
|
||||||
|
{
|
||||||
|
struct nouveau_i2c_impl *impl = (void *)nv_oclass(subdev);
|
||||||
|
struct nouveau_i2c *i2c = nouveau_i2c(subdev);
|
||||||
|
struct nouveau_i2c_port *port;
|
||||||
|
u32 hi, lo, rq, tx, e;
|
||||||
|
|
||||||
|
if (impl->aux_stat) {
|
||||||
|
impl->aux_stat(i2c, &hi, &lo, &rq, &tx);
|
||||||
|
if (hi || lo || rq || tx) {
|
||||||
|
list_for_each_entry(port, &i2c->ports, head) {
|
||||||
|
if (e = 0, port->aux < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (hi & (1 << port->aux)) e |= NVKM_I2C_PLUG;
|
||||||
|
if (lo & (1 << port->aux)) e |= NVKM_I2C_UNPLUG;
|
||||||
|
if (rq & (1 << port->aux)) e |= NVKM_I2C_IRQ;
|
||||||
|
if (tx & (1 << port->aux)) e |= NVKM_I2C_DONE;
|
||||||
|
|
||||||
|
nouveau_event_trigger(i2c->ntfy, e, port->index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
_nouveau_i2c_fini(struct nouveau_object *object, bool suspend)
|
_nouveau_i2c_fini(struct nouveau_object *object, bool suspend)
|
||||||
{
|
{
|
||||||
|
struct nouveau_i2c_impl *impl = (void *)nv_oclass(object);
|
||||||
struct nouveau_i2c *i2c = (void *)object;
|
struct nouveau_i2c *i2c = (void *)object;
|
||||||
struct nouveau_i2c_port *port;
|
struct nouveau_i2c_port *port;
|
||||||
|
u32 mask;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
list_for_each_entry(port, &i2c->ports, head) {
|
list_for_each_entry(port, &i2c->ports, head) {
|
||||||
|
@ -249,6 +299,11 @@ _nouveau_i2c_fini(struct nouveau_object *object, bool suspend)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((mask = (1 << impl->aux) - 1), impl->aux_stat) {
|
||||||
|
impl->aux_mask(i2c, NVKM_I2C_ANY, mask, 0);
|
||||||
|
impl->aux_stat(i2c, &mask, &mask, &mask, &mask);
|
||||||
|
}
|
||||||
|
|
||||||
return nouveau_subdev_fini(&i2c->base, suspend);
|
return nouveau_subdev_fini(&i2c->base, suspend);
|
||||||
fail:
|
fail:
|
||||||
list_for_each_entry_continue_reverse(port, &i2c->ports, head) {
|
list_for_each_entry_continue_reverse(port, &i2c->ports, head) {
|
||||||
|
@ -289,6 +344,8 @@ _nouveau_i2c_dtor(struct nouveau_object *object)
|
||||||
struct nouveau_i2c *i2c = (void *)object;
|
struct nouveau_i2c *i2c = (void *)object;
|
||||||
struct nouveau_i2c_port *port, *temp;
|
struct nouveau_i2c_port *port, *temp;
|
||||||
|
|
||||||
|
nouveau_event_destroy(&i2c->ntfy);
|
||||||
|
|
||||||
list_for_each_entry_safe(port, temp, &i2c->ports, head) {
|
list_for_each_entry_safe(port, temp, &i2c->ports, head) {
|
||||||
nouveau_object_ref(NULL, (struct nouveau_object **)&port);
|
nouveau_object_ref(NULL, (struct nouveau_object **)&port);
|
||||||
}
|
}
|
||||||
|
@ -323,6 +380,7 @@ nouveau_i2c_create_(struct nouveau_object *parent,
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
nv_subdev(i2c)->intr = nouveau_i2c_intr;
|
||||||
i2c->find = nouveau_i2c_find;
|
i2c->find = nouveau_i2c_find;
|
||||||
i2c->find_type = nouveau_i2c_find_type;
|
i2c->find_type = nouveau_i2c_find_type;
|
||||||
i2c->identify = nouveau_i2c_identify;
|
i2c->identify = nouveau_i2c_identify;
|
||||||
|
@ -379,6 +437,13 @@ nouveau_i2c_create_(struct nouveau_object *parent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = nouveau_event_create(4, index, &i2c->ntfy);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
i2c->ntfy->priv = i2c;
|
||||||
|
i2c->ntfy->enable = nouveau_i2c_intr_enable;
|
||||||
|
i2c->ntfy->disable = nouveau_i2c_intr_disable;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -232,6 +232,7 @@ nv94_aux_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
port->base.aux = info->drive;
|
||||||
port->addr = info->drive;
|
port->addr = info->drive;
|
||||||
if (info->share != DCB_I2C_UNUSED) {
|
if (info->share != DCB_I2C_UNUSED) {
|
||||||
port->ctrl = 0x00e500 + (info->drive * 0x50);
|
port->ctrl = 0x00e500 + (info->drive * 0x50);
|
||||||
|
|
|
@ -55,7 +55,22 @@ extern const struct i2c_algorithm nouveau_i2c_aux_algo;
|
||||||
|
|
||||||
struct nouveau_i2c_impl {
|
struct nouveau_i2c_impl {
|
||||||
struct nouveau_oclass base;
|
struct nouveau_oclass base;
|
||||||
|
|
||||||
|
/* supported i2c port classes */
|
||||||
struct nouveau_oclass *sclass;
|
struct nouveau_oclass *sclass;
|
||||||
|
|
||||||
|
/* number of native dp aux channels present */
|
||||||
|
int aux;
|
||||||
|
|
||||||
|
/* read and ack pending interrupts, returning only data
|
||||||
|
* for ports that have not been masked off, while still
|
||||||
|
* performing the ack for anything that was pending.
|
||||||
|
*/
|
||||||
|
void (*aux_stat)(struct nouveau_i2c *, u32 *, u32 *, u32 *, u32 *);
|
||||||
|
|
||||||
|
/* mask on/off interrupt types for a given set of auxch
|
||||||
|
*/
|
||||||
|
void (*aux_mask)(struct nouveau_i2c *, u32, u32, u32);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Reference in a new issue