aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c')
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c742
1 files changed, 258 insertions, 484 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c
index 9200f122c02c..243a71ff0a0d 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c
@@ -22,328 +22,91 @@
* Authors: Ben Skeggs
*/
#include "priv.h"
+#include "aux.h"
+#include "bus.h"
#include "pad.h"
-#include <core/device.h>
#include <core/notify.h>
#include <core/option.h>
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
+#include <subdev/bios/i2c.h>
-/******************************************************************************
- * interface to linux i2c bit-banging algorithm
- *****************************************************************************/
-
-#ifdef CONFIG_NOUVEAU_I2C_INTERNAL_DEFAULT
-#define CSTMSEL true
-#else
-#define CSTMSEL false
-#endif
-
-static int
-nvkm_i2c_pre_xfer(struct i2c_adapter *adap)
+static struct nvkm_i2c_pad *
+nvkm_i2c_pad_find(struct nvkm_i2c *i2c, int id)
{
- struct i2c_algo_bit_data *bit = adap->algo_data;
- struct nvkm_i2c_port *port = bit->data;
- return nvkm_i2c(port)->acquire(port, bit->timeout);
-}
+ struct nvkm_i2c_pad *pad;
-static void
-nvkm_i2c_post_xfer(struct i2c_adapter *adap)
-{
- struct i2c_algo_bit_data *bit = adap->algo_data;
- struct nvkm_i2c_port *port = bit->data;
- return nvkm_i2c(port)->release(port);
-}
-
-static void
-nvkm_i2c_setscl(void *data, int state)
-{
- struct nvkm_i2c_port *port = data;
- port->func->drive_scl(port, state);
-}
-
-static void
-nvkm_i2c_setsda(void *data, int state)
-{
- struct nvkm_i2c_port *port = data;
- port->func->drive_sda(port, state);
-}
-
-static int
-nvkm_i2c_getscl(void *data)
-{
- struct nvkm_i2c_port *port = data;
- return port->func->sense_scl(port);
-}
-
-static int
-nvkm_i2c_getsda(void *data)
-{
- struct nvkm_i2c_port *port = data;
- return port->func->sense_sda(port);
-}
-
-/******************************************************************************
- * base i2c "port" class implementation
- *****************************************************************************/
-
-int
-_nvkm_i2c_port_fini(struct nvkm_object *object, bool suspend)
-{
- struct nvkm_i2c_port *port = (void *)object;
- struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port);
- nv_ofuncs(pad)->fini(nv_object(pad), suspend);
- return nvkm_object_fini(&port->base, suspend);
-}
-
-void
-_nvkm_i2c_port_dtor(struct nvkm_object *object)
-{
- struct nvkm_i2c_port *port = (void *)object;
- i2c_del_adapter(&port->adapter);
- nvkm_object_destroy(&port->base);
-}
-
-int
-nvkm_i2c_port_create_(struct nvkm_object *parent, struct nvkm_object *engine,
- struct nvkm_oclass *oclass, u8 index,
- const struct i2c_algorithm *algo,
- const struct nvkm_i2c_func *func,
- int size, void **pobject)
-{
- struct nvkm_device *device = nv_device(parent);
- struct nvkm_i2c *i2c = nvkm_i2c(parent);
- struct nvkm_i2c_port *port;
- int ret;
-
- ret = nvkm_object_create_(parent, engine, oclass, 0, size, pobject);
- port = *pobject;
- if (ret)
- return ret;
-
- snprintf(port->adapter.name, sizeof(port->adapter.name),
- "nvkm-%s-%d", device->name, index);
- port->adapter.owner = THIS_MODULE;
- port->adapter.dev.parent = nv_device_base(device);
- port->index = index;
- port->aux = -1;
- port->func = func;
- mutex_init(&port->mutex);
-
- if ( algo == &nvkm_i2c_bit_algo &&
- !nvkm_boolopt(device->cfgopt, "NvI2C", CSTMSEL)) {
- struct i2c_algo_bit_data *bit;
-
- bit = kzalloc(sizeof(*bit), GFP_KERNEL);
- if (!bit)
- return -ENOMEM;
-
- bit->udelay = 10;
- bit->timeout = usecs_to_jiffies(2200);
- bit->data = port;
- bit->pre_xfer = nvkm_i2c_pre_xfer;
- bit->post_xfer = nvkm_i2c_post_xfer;
- bit->setsda = nvkm_i2c_setsda;
- bit->setscl = nvkm_i2c_setscl;
- bit->getsda = nvkm_i2c_getsda;
- bit->getscl = nvkm_i2c_getscl;
-
- port->adapter.algo_data = bit;
- ret = i2c_bit_add_bus(&port->adapter);
- } else {
- port->adapter.algo_data = port;
- port->adapter.algo = algo;
- ret = i2c_add_adapter(&port->adapter);
+ list_for_each_entry(pad, &i2c->pad, head) {
+ if (pad->id == id)
+ return pad;
}
- if (ret == 0)
- list_add_tail(&port->head, &i2c->ports);
- return ret;
+ return NULL;
}
-/******************************************************************************
- * base i2c subdev class implementation
- *****************************************************************************/
-
-static struct nvkm_i2c_port *
-nvkm_i2c_find(struct nvkm_i2c *i2c, u8 index)
+struct nvkm_i2c_bus *
+nvkm_i2c_bus_find(struct nvkm_i2c *i2c, int id)
{
- struct nvkm_bios *bios = nvkm_bios(i2c);
- struct nvkm_i2c_port *port;
+ struct nvkm_bios *bios = i2c->subdev.device->bios;
+ struct nvkm_i2c_bus *bus;
- if (index == NV_I2C_DEFAULT(0) ||
- index == NV_I2C_DEFAULT(1)) {
+ if (id == NVKM_I2C_BUS_PRI || id == NVKM_I2C_BUS_SEC) {
u8 ver, hdr, cnt, len;
u16 i2c = dcb_i2c_table(bios, &ver, &hdr, &cnt, &len);
if (i2c && ver >= 0x30) {
- u8 auxidx = nv_ro08(bios, i2c + 4);
- if (index == NV_I2C_DEFAULT(0))
- index = (auxidx & 0x0f) >> 0;
+ u8 auxidx = nvbios_rd08(bios, i2c + 4);
+ if (id == NVKM_I2C_BUS_PRI)
+ id = NVKM_I2C_BUS_CCB((auxidx & 0x0f) >> 0);
else
- index = (auxidx & 0xf0) >> 4;
+ id = NVKM_I2C_BUS_CCB((auxidx & 0xf0) >> 4);
} else {
- index = 2;
+ id = NVKM_I2C_BUS_CCB(2);
}
}
- list_for_each_entry(port, &i2c->ports, head) {
- if (port->index == index)
- return port;
+ list_for_each_entry(bus, &i2c->bus, head) {
+ if (bus->id == id)
+ return bus;
}
return NULL;
}
-static struct nvkm_i2c_port *
-nvkm_i2c_find_type(struct nvkm_i2c *i2c, u16 type)
+struct nvkm_i2c_aux *
+nvkm_i2c_aux_find(struct nvkm_i2c *i2c, int id)
{
- struct nvkm_i2c_port *port;
+ struct nvkm_i2c_aux *aux;
- list_for_each_entry(port, &i2c->ports, head) {
- if (nv_hclass(port) == type)
- return port;
+ list_for_each_entry(aux, &i2c->aux, head) {
+ if (aux->id == id)
+ return aux;
}
return NULL;
}
static void
-nvkm_i2c_release_pad(struct nvkm_i2c_port *port)
-{
- struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port);
- struct nvkm_i2c *i2c = nvkm_i2c(port);
-
- if (atomic_dec_and_test(&nv_object(pad)->usecount)) {
- nv_ofuncs(pad)->fini(nv_object(pad), false);
- wake_up_all(&i2c->wait);
- }
-}
-
-static int
-nvkm_i2c_try_acquire_pad(struct nvkm_i2c_port *port)
-{
- struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port);
-
- if (atomic_add_return(1, &nv_object(pad)->usecount) != 1) {
- struct nvkm_object *owner = (void *)pad->port;
- do {
- if (owner == (void *)port)
- return 0;
- owner = owner->parent;
- } while(owner);
- nvkm_i2c_release_pad(port);
- return -EBUSY;
- }
-
- pad->next = port;
- nv_ofuncs(pad)->init(nv_object(pad));
- return 0;
-}
-
-static int
-nvkm_i2c_acquire_pad(struct nvkm_i2c_port *port, unsigned long timeout)
-{
- struct nvkm_i2c *i2c = nvkm_i2c(port);
-
- if (timeout) {
- if (wait_event_timeout(i2c->wait,
- nvkm_i2c_try_acquire_pad(port) == 0,
- timeout) == 0)
- return -EBUSY;
- } else {
- wait_event(i2c->wait, nvkm_i2c_try_acquire_pad(port) == 0);
- }
-
- return 0;
-}
-
-static void
-nvkm_i2c_release(struct nvkm_i2c_port *port)
-__releases(pad->mutex)
-{
- nvkm_i2c(port)->release_pad(port);
- mutex_unlock(&port->mutex);
-}
-
-static int
-nvkm_i2c_acquire(struct nvkm_i2c_port *port, unsigned long timeout)
-__acquires(pad->mutex)
-{
- int ret;
- mutex_lock(&port->mutex);
- if ((ret = nvkm_i2c(port)->acquire_pad(port, timeout)))
- mutex_unlock(&port->mutex);
- return ret;
-}
-
-static int
-nvkm_i2c_identify(struct nvkm_i2c *i2c, int index, const char *what,
- struct nvkm_i2c_board_info *info,
- bool (*match)(struct nvkm_i2c_port *,
- struct i2c_board_info *, void *), void *data)
-{
- struct nvkm_i2c_port *port = nvkm_i2c_find(i2c, index);
- int i;
-
- if (!port) {
- nv_debug(i2c, "no bus when probing %s on %d\n", what, index);
- return -ENODEV;
- }
-
- nv_debug(i2c, "probing %ss on bus: %d\n", what, port->index);
- for (i = 0; info[i].dev.addr; i++) {
- u8 orig_udelay = 0;
-
- if ((port->adapter.algo == &i2c_bit_algo) &&
- (info[i].udelay != 0)) {
- struct i2c_algo_bit_data *algo = port->adapter.algo_data;
- nv_debug(i2c, "using custom udelay %d instead of %d\n",
- info[i].udelay, algo->udelay);
- orig_udelay = algo->udelay;
- algo->udelay = info[i].udelay;
- }
-
- if (nv_probe_i2c(port, info[i].dev.addr) &&
- (!match || match(port, &info[i].dev, data))) {
- nv_info(i2c, "detected %s: %s\n", what,
- info[i].dev.type);
- return i;
- }
-
- if (orig_udelay) {
- struct i2c_algo_bit_data *algo = port->adapter.algo_data;
- algo->udelay = orig_udelay;
- }
- }
-
- nv_debug(i2c, "no devices found.\n");
- return -ENODEV;
-}
-
-static void
-nvkm_i2c_intr_fini(struct nvkm_event *event, int type, int index)
+nvkm_i2c_intr_fini(struct nvkm_event *event, int type, int id)
{
struct nvkm_i2c *i2c = container_of(event, typeof(*i2c), event);
- struct nvkm_i2c_port *port = i2c->find(i2c, index);
- const struct nvkm_i2c_impl *impl = (void *)nv_object(i2c)->oclass;
- if (port && port->aux >= 0)
- impl->aux_mask(i2c, type, 1 << port->aux, 0);
+ struct nvkm_i2c_aux *aux = nvkm_i2c_aux_find(i2c, id);
+ if (aux)
+ i2c->func->aux_mask(i2c, type, aux->intr, 0);
}
static void
-nvkm_i2c_intr_init(struct nvkm_event *event, int type, int index)
+nvkm_i2c_intr_init(struct nvkm_event *event, int type, int id)
{
struct nvkm_i2c *i2c = container_of(event, typeof(*i2c), event);
- struct nvkm_i2c_port *port = i2c->find(i2c, index);
- const struct nvkm_i2c_impl *impl = (void *)nv_object(i2c)->oclass;
- if (port && port->aux >= 0)
- impl->aux_mask(i2c, type, 1 << port->aux, 1 << port->aux);
+ struct nvkm_i2c_aux *aux = nvkm_i2c_aux_find(i2c, id);
+ if (aux)
+ i2c->func->aux_mask(i2c, type, aux->intr, aux->intr);
}
static int
nvkm_i2c_intr_ctor(struct nvkm_object *object, void *data, u32 size,
- struct nvkm_notify *notify)
+ struct nvkm_notify *notify)
{
struct nvkm_i2c_ntfy_req *req = data;
if (!WARN_ON(size != sizeof(*req))) {
@@ -355,38 +118,6 @@ nvkm_i2c_intr_ctor(struct nvkm_object *object, void *data, u32 size,
return -EINVAL;
}
-static void
-nvkm_i2c_intr(struct nvkm_subdev *subdev)
-{
- struct nvkm_i2c_impl *impl = (void *)nv_oclass(subdev);
- struct nvkm_i2c *i2c = nvkm_i2c(subdev);
- struct nvkm_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;
- if (e) {
- struct nvkm_i2c_ntfy_rep rep = {
- .mask = e,
- };
- nvkm_event_send(&i2c->event, rep.mask,
- port->index, &rep,
- sizeof(rep));
- }
- }
- }
- }
-}
-
static const struct nvkm_event_func
nvkm_i2c_intr_func = {
.ctor = nvkm_i2c_intr_ctor,
@@ -394,229 +125,272 @@ nvkm_i2c_intr_func = {
.fini = nvkm_i2c_intr_fini,
};
-int
-_nvkm_i2c_fini(struct nvkm_object *object, bool suspend)
+static void
+nvkm_i2c_intr(struct nvkm_subdev *subdev)
{
- struct nvkm_i2c_impl *impl = (void *)nv_oclass(object);
- struct nvkm_i2c *i2c = (void *)object;
- struct nvkm_i2c_port *port;
- u32 mask;
- int ret;
+ struct nvkm_i2c *i2c = nvkm_i2c(subdev);
+ struct nvkm_i2c_aux *aux;
+ u32 hi, lo, rq, tx;
+
+ if (!i2c->func->aux_stat)
+ return;
+
+ i2c->func->aux_stat(i2c, &hi, &lo, &rq, &tx);
+ if (!hi && !lo && !rq && !tx)
+ return;
- list_for_each_entry(port, &i2c->ports, head) {
- ret = nv_ofuncs(port)->fini(nv_object(port), suspend);
- if (ret && suspend)
- goto fail;
+ list_for_each_entry(aux, &i2c->aux, head) {
+ u32 mask = 0;
+ if (hi & aux->intr) mask |= NVKM_I2C_PLUG;
+ if (lo & aux->intr) mask |= NVKM_I2C_UNPLUG;
+ if (rq & aux->intr) mask |= NVKM_I2C_IRQ;
+ if (tx & aux->intr) mask |= NVKM_I2C_DONE;
+ if (mask) {
+ struct nvkm_i2c_ntfy_rep rep = {
+ .mask = mask,
+ };
+ nvkm_event_send(&i2c->event, rep.mask, aux->id,
+ &rep, sizeof(rep));
+ }
}
+}
+
+static int
+nvkm_i2c_fini(struct nvkm_subdev *subdev, bool suspend)
+{
+ struct nvkm_i2c *i2c = nvkm_i2c(subdev);
+ struct nvkm_i2c_pad *pad;
+ u32 mask;
- 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);
+ if ((mask = (1 << i2c->func->aux) - 1), i2c->func->aux_stat) {
+ i2c->func->aux_mask(i2c, NVKM_I2C_ANY, mask, 0);
+ i2c->func->aux_stat(i2c, &mask, &mask, &mask, &mask);
}
- return nvkm_subdev_fini(&i2c->base, suspend);
-fail:
- list_for_each_entry_continue_reverse(port, &i2c->ports, head) {
- nv_ofuncs(port)->init(nv_object(port));
+ list_for_each_entry(pad, &i2c->pad, head) {
+ nvkm_i2c_pad_fini(pad);
}
- return ret;
+ return 0;
}
-int
-_nvkm_i2c_init(struct nvkm_object *object)
+static int
+nvkm_i2c_init(struct nvkm_subdev *subdev)
{
- struct nvkm_i2c *i2c = (void *)object;
- struct nvkm_i2c_port *port;
- int ret;
-
- ret = nvkm_subdev_init(&i2c->base);
- if (ret == 0) {
- list_for_each_entry(port, &i2c->ports, head) {
- ret = nv_ofuncs(port)->init(nv_object(port));
- if (ret)
- goto fail;
- }
+ struct nvkm_i2c *i2c = nvkm_i2c(subdev);
+ struct nvkm_i2c_bus *bus;
+ struct nvkm_i2c_pad *pad;
+
+ list_for_each_entry(pad, &i2c->pad, head) {
+ nvkm_i2c_pad_init(pad);
}
- return ret;
-fail:
- list_for_each_entry_continue_reverse(port, &i2c->ports, head) {
- nv_ofuncs(port)->fini(nv_object(port), false);
+ list_for_each_entry(bus, &i2c->bus, head) {
+ nvkm_i2c_bus_init(bus);
}
- return ret;
+ return 0;
}
-void
-_nvkm_i2c_dtor(struct nvkm_object *object)
+static void *
+nvkm_i2c_dtor(struct nvkm_subdev *subdev)
{
- struct nvkm_i2c *i2c = (void *)object;
- struct nvkm_i2c_port *port, *temp;
+ struct nvkm_i2c *i2c = nvkm_i2c(subdev);
nvkm_event_fini(&i2c->event);
- list_for_each_entry_safe(port, temp, &i2c->ports, head) {
- nvkm_object_ref(NULL, (struct nvkm_object **)&port);
+ while (!list_empty(&i2c->aux)) {
+ struct nvkm_i2c_aux *aux =
+ list_first_entry(&i2c->aux, typeof(*aux), head);
+ nvkm_i2c_aux_del(&aux);
}
- nvkm_subdev_destroy(&i2c->base);
-}
-
-static struct nvkm_oclass *
-nvkm_i2c_extdev_sclass[] = {
- nvkm_anx9805_sclass,
-};
+ while (!list_empty(&i2c->bus)) {
+ struct nvkm_i2c_bus *bus =
+ list_first_entry(&i2c->bus, typeof(*bus), head);
+ nvkm_i2c_bus_del(&bus);
+ }
-static void
-nvkm_i2c_create_port(struct nvkm_i2c *i2c, int index, u8 type,
- struct dcb_i2c_entry *info)
-{
- const struct nvkm_i2c_impl *impl = (void *)nv_oclass(i2c);
- struct nvkm_oclass *oclass;
- struct nvkm_object *parent;
- struct nvkm_object *object;
- int ret, pad;
-
- if (info->share != DCB_I2C_UNUSED) {
- pad = info->share;
- oclass = impl->pad_s;
- } else {
- if (type != DCB_I2C_NVIO_AUX)
- pad = 0x100 + info->drive;
- else
- pad = 0x100 + info->auxch;
- oclass = impl->pad_x;
+ while (!list_empty(&i2c->pad)) {
+ struct nvkm_i2c_pad *pad =
+ list_first_entry(&i2c->pad, typeof(*pad), head);
+ nvkm_i2c_pad_del(&pad);
}
- ret = nvkm_object_ctor(nv_object(i2c), NULL, oclass,
- NULL, pad, &parent);
- if (ret < 0)
- return;
+ return i2c;
+}
- oclass = impl->sclass;
- do {
- ret = -EINVAL;
- if (oclass->handle == type) {
- ret = nvkm_object_ctor(parent, NULL, oclass,
- info, index, &object);
- }
- } while (ret && (++oclass)->handle);
+static const struct nvkm_subdev_func
+nvkm_i2c = {
+ .dtor = nvkm_i2c_dtor,
+ .init = nvkm_i2c_init,
+ .fini = nvkm_i2c_fini,
+ .intr = nvkm_i2c_intr,
+};
- nvkm_object_ref(NULL, &parent);
+static const struct nvkm_i2c_drv {
+ u8 bios;
+ u8 addr;
+ int (*pad_new)(struct nvkm_i2c_bus *, int id, u8 addr,
+ struct nvkm_i2c_pad **);
}
+nvkm_i2c_drv[] = {
+ { 0x0d, 0x39, anx9805_pad_new },
+ { 0x0e, 0x3b, anx9805_pad_new },
+ {}
+};
int
-nvkm_i2c_create_(struct nvkm_object *parent, struct nvkm_object *engine,
- struct nvkm_oclass *oclass, int length, void **pobject)
+nvkm_i2c_new_(const struct nvkm_i2c_func *func, struct nvkm_device *device,
+ int index, struct nvkm_i2c **pi2c)
{
- struct nvkm_bios *bios = nvkm_bios(parent);
+ struct nvkm_bios *bios = device->bios;
struct nvkm_i2c *i2c;
- struct nvkm_object *object;
- struct dcb_i2c_entry info;
- int ret, i, j, index = -1;
- struct dcb_output outp;
- u8 ver, hdr;
- u32 data;
-
- ret = nvkm_subdev_create(parent, engine, oclass, 0, "I2C", "i2c", &i2c);
- *pobject = nv_object(i2c);
- if (ret)
- return ret;
-
- nv_subdev(i2c)->intr = nvkm_i2c_intr;
- i2c->find = nvkm_i2c_find;
- i2c->find_type = nvkm_i2c_find_type;
- i2c->acquire_pad = nvkm_i2c_acquire_pad;
- i2c->release_pad = nvkm_i2c_release_pad;
- i2c->acquire = nvkm_i2c_acquire;
- i2c->release = nvkm_i2c_release;
- i2c->identify = nvkm_i2c_identify;
- init_waitqueue_head(&i2c->wait);
- INIT_LIST_HEAD(&i2c->ports);
-
- while (!dcb_i2c_parse(bios, ++index, &info)) {
- switch (info.type) {
- case DCB_I2C_NV04_BIT:
- case DCB_I2C_NV4E_BIT:
- case DCB_I2C_NVIO_BIT:
- nvkm_i2c_create_port(i2c, NV_I2C_PORT(index),
- info.type, &info);
- break;
- case DCB_I2C_NVIO_AUX:
- nvkm_i2c_create_port(i2c, NV_I2C_AUX(index),
- info.type, &info);
- break;
- case DCB_I2C_PMGR:
- if (info.drive != DCB_I2C_UNUSED) {
- nvkm_i2c_create_port(i2c, NV_I2C_PORT(index),
- DCB_I2C_NVIO_BIT, &info);
- }
- if (info.auxch != DCB_I2C_UNUSED) {
- nvkm_i2c_create_port(i2c, NV_I2C_AUX(index),
- DCB_I2C_NVIO_AUX, &info);
- }
- break;
- case DCB_I2C_UNUSED:
- default:
+ struct dcb_i2c_entry ccbE;
+ struct dcb_output dcbE;
+ u8 ver, hdr;
+ int ret, i;
+
+ if (!(i2c = *pi2c = kzalloc(sizeof(*i2c), GFP_KERNEL)))
+ return -ENOMEM;
+
+ nvkm_subdev_ctor(&nvkm_i2c, device, index, 0, &i2c->subdev);
+ i2c->func = func;
+ INIT_LIST_HEAD(&i2c->pad);
+ INIT_LIST_HEAD(&i2c->bus);
+ INIT_LIST_HEAD(&i2c->aux);
+
+ i = -1;
+ while (!dcb_i2c_parse(bios, ++i, &ccbE)) {
+ struct nvkm_i2c_pad *pad = NULL;
+ struct nvkm_i2c_bus *bus = NULL;
+ struct nvkm_i2c_aux *aux = NULL;
+
+ nvkm_debug(&i2c->subdev, "ccb %02x: type %02x drive %02x "
+ "sense %02x share %02x auxch %02x\n", i, ccbE.type,
+ ccbE.drive, ccbE.sense, ccbE.share, ccbE.auxch);
+
+ if (ccbE.share != DCB_I2C_UNUSED) {
+ const int id = NVKM_I2C_PAD_HYBRID(ccbE.share);
+ if (!(pad = nvkm_i2c_pad_find(i2c, id)))
+ ret = func->pad_s_new(i2c, id, &pad);
+ else
+ ret = 0;
+ } else {
+ ret = func->pad_x_new(i2c, NVKM_I2C_PAD_CCB(i), &pad);
+ }
+
+ if (ret) {
+ nvkm_error(&i2c->subdev, "ccb %02x pad, %d\n", i, ret);
+ nvkm_i2c_pad_del(&pad);
+ continue;
+ }
+
+ if (pad->func->bus_new_0 && ccbE.type == DCB_I2C_NV04_BIT) {
+ ret = pad->func->bus_new_0(pad, NVKM_I2C_BUS_CCB(i),
+ ccbE.drive,
+ ccbE.sense, &bus);
+ } else
+ if (pad->func->bus_new_4 &&
+ ( ccbE.type == DCB_I2C_NV4E_BIT ||
+ ccbE.type == DCB_I2C_NVIO_BIT ||
+ (ccbE.type == DCB_I2C_PMGR &&
+ ccbE.drive != DCB_I2C_UNUSED))) {
+ ret = pad->func->bus_new_4(pad, NVKM_I2C_BUS_CCB(i),
+ ccbE.drive, &bus);
+ }
+
+ if (ret) {
+ nvkm_error(&i2c->subdev, "ccb %02x bus, %d\n", i, ret);
+ nvkm_i2c_bus_del(&bus);
+ }
+
+ if (pad->func->aux_new_6 &&
+ ( ccbE.type == DCB_I2C_NVIO_AUX ||
+ (ccbE.type == DCB_I2C_PMGR &&
+ ccbE.auxch != DCB_I2C_UNUSED))) {
+ ret = pad->func->aux_new_6(pad, NVKM_I2C_BUS_CCB(i),
+ ccbE.auxch, &aux);
+ } else {
+ ret = 0;
+ }
+
+ if (ret) {
+ nvkm_error(&i2c->subdev, "ccb %02x aux, %d\n", i, ret);
+ nvkm_i2c_aux_del(&aux);
+ }
+
+ if (ccbE.type != DCB_I2C_UNUSED && !bus && !aux) {
+ nvkm_warn(&i2c->subdev, "ccb %02x was ignored\n", i);
continue;
}
}
- /* in addition to the busses specified in the i2c table, there
- * may be ddc/aux channels hiding behind external tmds/dp/etc
- * transmitters.
- */
- index = NV_I2C_EXT(0);
i = -1;
- while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &outp))) {
- if (!outp.location || !outp.extdev)
+ while (dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE)) {
+ const struct nvkm_i2c_drv *drv = nvkm_i2c_drv;
+ struct nvkm_i2c_bus *bus;
+ struct nvkm_i2c_pad *pad;
+
+ /* internal outputs handled by native i2c busses (above) */
+ if (!dcbE.location)
continue;
- switch (outp.type) {
- case DCB_OUTPUT_TMDS:
- info.type = NV_I2C_TYPE_EXTDDC(outp.extdev);
- break;
- case DCB_OUTPUT_DP:
- info.type = NV_I2C_TYPE_EXTAUX(outp.extdev);
- break;
- default:
+ /* we need an i2c bus to talk to the external encoder */
+ bus = nvkm_i2c_bus_find(i2c, dcbE.i2c_index);
+ if (!bus) {
+ nvkm_debug(&i2c->subdev, "dcb %02x no bus\n", i);
continue;
}
- ret = -ENODEV;
- j = -1;
- while (ret && ++j < ARRAY_SIZE(nvkm_i2c_extdev_sclass)) {
- parent = nv_object(i2c->find(i2c, outp.i2c_index));
- oclass = nvkm_i2c_extdev_sclass[j];
- do {
- if (oclass->handle != info.type)
- continue;
- ret = nvkm_object_ctor(parent, NULL, oclass,
- NULL, index++, &object);
- } while (ret && (++oclass)->handle);
+ /* ... and a driver for it */
+ while (drv->pad_new) {
+ if (drv->bios == dcbE.extdev)
+ break;
+ drv++;
}
- }
- ret = nvkm_event_init(&nvkm_i2c_intr_func, 4, index, &i2c->event);
- if (ret)
- return ret;
-
- return 0;
-}
+ if (!drv->pad_new) {
+ nvkm_debug(&i2c->subdev, "dcb %02x drv %02x unknown\n",
+ i, dcbE.extdev);
+ continue;
+ }
-int
-_nvkm_i2c_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
- struct nvkm_oclass *oclass, void *data, u32 size,
- struct nvkm_object **pobject)
-{
- struct nvkm_i2c *i2c;
- int ret;
+ /* find/create an instance of the driver */
+ pad = nvkm_i2c_pad_find(i2c, NVKM_I2C_PAD_EXT(dcbE.extdev));
+ if (!pad) {
+ const int id = NVKM_I2C_PAD_EXT(dcbE.extdev);
+ ret = drv->pad_new(bus, id, drv->addr, &pad);
+ if (ret) {
+ nvkm_error(&i2c->subdev, "dcb %02x pad, %d\n",
+ i, ret);
+ nvkm_i2c_pad_del(&pad);
+ continue;
+ }
+ }
- ret = nvkm_i2c_create(parent, engine, oclass, &i2c);
- *pobject = nv_object(i2c);
- if (ret)
- return ret;
+ /* create any i2c bus / aux channel required by the output */
+ if (pad->func->aux_new_6 && dcbE.type == DCB_OUTPUT_DP) {
+ const int id = NVKM_I2C_AUX_EXT(dcbE.extdev);
+ struct nvkm_i2c_aux *aux = NULL;
+ ret = pad->func->aux_new_6(pad, id, 0, &aux);
+ if (ret) {
+ nvkm_error(&i2c->subdev, "dcb %02x aux, %d\n",
+ i, ret);
+ nvkm_i2c_aux_del(&aux);
+ }
+ } else
+ if (pad->func->bus_new_4) {
+ const int id = NVKM_I2C_BUS_EXT(dcbE.extdev);
+ struct nvkm_i2c_bus *bus = NULL;
+ ret = pad->func->bus_new_4(pad, id, 0, &bus);
+ if (ret) {
+ nvkm_error(&i2c->subdev, "dcb %02x bus, %d\n",
+ i, ret);
+ nvkm_i2c_bus_del(&bus);
+ }
+ }
+ }
- return 0;
+ return nvkm_event_init(&nvkm_i2c_intr_func, 4, i, &i2c->event);
}