drm/nouveau/i2c: introduce locking at a per-port level
authorBen Skeggs <bskeggs@redhat.com>
Thu, 29 May 2014 01:35:10 +0000 (11:35 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Wed, 11 Jun 2014 06:10:34 +0000 (16:10 +1000)
There's also provisions to allow a pad to be locked with a specific
routing, for an indefinite period of time.  This will be used in
future patches.

The G94+ pad driver will now also power-down pads when not required.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
14 files changed:
drivers/gpu/drm/nouveau/Makefile
drivers/gpu/drm/nouveau/core/include/subdev/i2c.h
drivers/gpu/drm/nouveau/core/subdev/i2c/base.c
drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c
drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c
drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c
drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c
drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c
drivers/gpu/drm/nouveau/core/subdev/i2c/nve0.c
drivers/gpu/drm/nouveau/core/subdev/i2c/pad.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/pad.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/padnv04.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/padnv94.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h

index ee4a557ec86a0f1b029112c4ac10a5b4d339db6c..d647e7487a0dc23e4a918d4189c8d5cdd408475f 100644 (file)
@@ -132,6 +132,9 @@ nouveau-y += core/subdev/i2c/base.o
 nouveau-y += core/subdev/i2c/anx9805.o
 nouveau-y += core/subdev/i2c/aux.o
 nouveau-y += core/subdev/i2c/bit.o
+nouveau-y += core/subdev/i2c/pad.o
+nouveau-y += core/subdev/i2c/padnv04.o
+nouveau-y += core/subdev/i2c/padnv94.o
 nouveau-y += core/subdev/i2c/nv04.o
 nouveau-y += core/subdev/i2c/nv4e.o
 nouveau-y += core/subdev/i2c/nv50.o
index 9c150568dbb350151a293b2b4409dfb489c79b35..db1b39d080135f66094debd56149a8b175f15940 100644 (file)
@@ -28,6 +28,7 @@ enum nvkm_i2c_event {
 struct nouveau_i2c_port {
        struct nouveau_object base;
        struct i2c_adapter adapter;
+       struct mutex mutex;
 
        struct list_head head;
        u8  index;
@@ -37,9 +38,6 @@ struct nouveau_i2c_port {
 };
 
 struct nouveau_i2c_func {
-       void (*acquire)(struct nouveau_i2c_port *);
-       void (*release)(struct nouveau_i2c_port *);
-
        void (*drive_scl)(struct nouveau_i2c_port *, int);
        void (*drive_sda)(struct nouveau_i2c_port *, int);
        int  (*sense_scl)(struct nouveau_i2c_port *);
@@ -62,12 +60,16 @@ struct nouveau_i2c {
 
        struct nouveau_i2c_port *(*find)(struct nouveau_i2c *, u8 index);
        struct nouveau_i2c_port *(*find_type)(struct nouveau_i2c *, u16 type);
+       int  (*acquire_pad)(struct nouveau_i2c_port *, unsigned long timeout);
+       void (*release_pad)(struct nouveau_i2c_port *);
        int  (*acquire)(struct nouveau_i2c_port *, unsigned long timeout);
        void (*release)(struct nouveau_i2c_port *);
        int (*identify)(struct nouveau_i2c *, int index,
                        const char *what, struct nouveau_i2c_board_info *,
                        bool (*match)(struct nouveau_i2c_port *,
                                      struct i2c_board_info *, void *), void *);
+
+       wait_queue_head_t wait;
        struct list_head ports;
 };
 
index a230465f5e65d64966a78b373f795d8f6835ddfb..09ba2cc851cfa31e0e24629219ffab13402d74ea 100644 (file)
@@ -31,6 +31,7 @@
 #include <subdev/vga.h>
 
 #include "priv.h"
+#include "pad.h"
 
 /******************************************************************************
  * interface to linux i2c bit-banging algorithm
@@ -90,6 +91,15 @@ nouveau_i2c_getsda(void *data)
  * base i2c "port" class implementation
  *****************************************************************************/
 
+int
+_nouveau_i2c_port_fini(struct nouveau_object *object, bool suspend)
+{
+       struct nouveau_i2c_port *port = (void *)object;
+       struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port);
+       nv_ofuncs(pad)->fini(nv_object(pad), suspend);
+       return nouveau_object_fini(&port->base, suspend);
+}
+
 void
 _nouveau_i2c_port_dtor(struct nouveau_object *object)
 {
@@ -106,7 +116,7 @@ nouveau_i2c_port_create_(struct nouveau_object *parent,
                         const struct nouveau_i2c_func *func,
                         int size, void **pobject)
 {
-       struct nouveau_device *device = nv_device(parent);
+       struct nouveau_device *device = nv_device(engine);
        struct nouveau_i2c *i2c = (void *)engine;
        struct nouveau_i2c_port *port;
        int ret;
@@ -123,6 +133,7 @@ nouveau_i2c_port_create_(struct nouveau_object *parent,
        port->index = index;
        port->aux = -1;
        port->func = func;
+       mutex_init(&port->mutex);
 
        if ( algo == &nouveau_i2c_bit_algo &&
            !nouveau_boolopt(device->cfgopt, "NvI2C", CSTMSEL)) {
@@ -201,19 +212,73 @@ nouveau_i2c_find_type(struct nouveau_i2c *i2c, u16 type)
        return NULL;
 }
 
+static void
+nouveau_i2c_release_pad(struct nouveau_i2c_port *port)
+{
+       struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port);
+       struct nouveau_i2c *i2c = nouveau_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
+nouveau_i2c_try_acquire_pad(struct nouveau_i2c_port *port)
+{
+       struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port);
+
+       if (atomic_add_return(1, &nv_object(pad)->usecount) != 1) {
+               struct nouveau_object *owner = (void *)pad->port;
+               do {
+                       if (owner == (void *)port)
+                               return 0;
+                       owner = owner->parent;
+               } while(owner);
+               nouveau_i2c_release_pad(port);
+               return -EBUSY;
+       }
+
+       pad->next = port;
+       nv_ofuncs(pad)->init(nv_object(pad));
+       return 0;
+}
+
+static int
+nouveau_i2c_acquire_pad(struct nouveau_i2c_port *port, unsigned long timeout)
+{
+       struct nouveau_i2c *i2c = nouveau_i2c(port);
+
+       if (timeout) {
+               if (wait_event_timeout(i2c->wait,
+                                      nouveau_i2c_try_acquire_pad(port) == 0,
+                                      timeout) == 0)
+                       return -EBUSY;
+       } else {
+               wait_event(i2c->wait, nouveau_i2c_try_acquire_pad(port) == 0);
+       }
+
+       return 0;
+}
+
 static void
 nouveau_i2c_release(struct nouveau_i2c_port *port)
+__releases(pad->mutex)
 {
-       if (port->func->release)
-               port->func->release(port);
+       nouveau_i2c(port)->release_pad(port);
+       mutex_unlock(&port->mutex);
 }
 
 static int
 nouveau_i2c_acquire(struct nouveau_i2c_port *port, unsigned long timeout)
+__acquires(pad->mutex)
 {
-       if (port->func->acquire)
-               port->func->acquire(port);
-       return 0;
+       int ret;
+       mutex_lock(&port->mutex);
+       if ((ret = nouveau_i2c(port)->acquire_pad(port, timeout)))
+               mutex_unlock(&port->mutex);
+       return ret;
 }
 
 static int
@@ -391,7 +456,7 @@ nouveau_i2c_create_(struct nouveau_object *parent,
        struct nouveau_i2c *i2c;
        struct nouveau_object *object;
        struct dcb_i2c_entry info;
-       int ret, i, j, index = -1;
+       int ret, i, j, index = -1, pad;
        struct dcb_output outp;
        u8  ver, hdr;
        u32 data;
@@ -405,24 +470,45 @@ nouveau_i2c_create_(struct nouveau_object *parent,
        nv_subdev(i2c)->intr = nouveau_i2c_intr;
        i2c->find = nouveau_i2c_find;
        i2c->find_type = nouveau_i2c_find_type;
+       i2c->acquire_pad = nouveau_i2c_acquire_pad;
+       i2c->release_pad = nouveau_i2c_release_pad;
        i2c->acquire = nouveau_i2c_acquire;
        i2c->release = nouveau_i2c_release;
        i2c->identify = nouveau_i2c_identify;
+       init_waitqueue_head(&i2c->wait);
        INIT_LIST_HEAD(&i2c->ports);
 
        while (!dcb_i2c_parse(bios, ++index, &info)) {
                if (info.type == DCB_I2C_UNUSED)
                        continue;
 
+               if (info.share != DCB_I2C_UNUSED) {
+                       if (info.type == DCB_I2C_NVIO_AUX)
+                               pad = info.drive;
+                       else
+                               pad = info.share;
+                       oclass = impl->pad_s;
+               } else {
+                       pad = 0x100 + info.drive;
+                       oclass = impl->pad_x;
+               }
+
+               ret = nouveau_object_ctor(NULL, *pobject, oclass,
+                                         NULL, pad, &parent);
+               if (ret < 0)
+                       continue;
+
                oclass = impl->sclass;
                do {
                        ret = -EINVAL;
                        if (oclass->handle == info.type) {
-                               ret = nouveau_object_ctor(*pobject, *pobject,
+                               ret = nouveau_object_ctor(parent, *pobject,
                                                          oclass, &info,
                                                          index, &object);
                        }
                } while (ret && (++oclass)->handle);
+
+               nouveau_object_ref(NULL, &parent);
        }
 
        /* in addition to the busses specified in the i2c table, there
index a37528d5cc9410357c9f4dc5d00e3aa8c2214c66..b1725bdea967e79cb3315d37b11bdecf098203a6 100644 (file)
@@ -126,4 +126,5 @@ nv04_i2c_oclass = &(struct nouveau_i2c_impl) {
                .fini = _nouveau_i2c_fini,
        },
        .sclass = nv04_i2c_sclass,
+       .pad_x = &nv04_i2c_pad_oclass,
 }.base;
index d97101b3c695b61f850d4b8e5568b90bc74ea29f..f16c87ce5ba1fe88194e00461c0b5dd89408a7fd 100644 (file)
@@ -118,4 +118,5 @@ nv4e_i2c_oclass = &(struct nouveau_i2c_impl) {
                .fini = _nouveau_i2c_fini,
        },
        .sclass = nv4e_i2c_sclass,
+       .pad_x = &nv04_i2c_pad_oclass,
 }.base;
index bb854981a24bbec9bf572ad28fb89267a8b6e566..7b8756d4df083b0e3e0e8ede4f3582ffd7537501 100644 (file)
@@ -131,4 +131,5 @@ nv50_i2c_oclass = &(struct nouveau_i2c_impl) {
                .fini = _nouveau_i2c_fini,
        },
        .sclass = nv50_i2c_sclass,
+       .pad_x = &nv04_i2c_pad_oclass,
 }.base;
index ec0bd68a9027dd56dd97b53e9d20c6f2148fed72..f59c3a25546285dfe9304cbc7a86b5319d56a836 100644 (file)
@@ -185,26 +185,8 @@ out:
        return ret < 0 ? ret : (stat & 0x000f0000) >> 16;
 }
 
-void
-nv94_i2c_acquire(struct nouveau_i2c_port *base)
-{
-       struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine;
-       struct nv50_i2c_port *port = (void *)base;
-       if (port->ctrl) {
-               nv_mask(priv, port->ctrl + 0x0c, 0x00000001, 0x00000000);
-               nv_mask(priv, port->ctrl + 0x00, 0x0000f003, port->data);
-       }
-}
-
-void
-nv94_i2c_release(struct nouveau_i2c_port *base)
-{
-}
-
 static const struct nouveau_i2c_func
 nv94_i2c_func = {
-       .acquire   = nv94_i2c_acquire,
-       .release   = nv94_i2c_release,
        .drive_scl = nv50_i2c_drive_scl,
        .drive_sda = nv50_i2c_drive_sda,
        .sense_scl = nv50_i2c_sense_scl,
@@ -241,8 +223,6 @@ nv94_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
 
 static const struct nouveau_i2c_func
 nv94_aux_func = {
-       .acquire   = nv94_i2c_acquire,
-       .release   = nv94_i2c_release,
        .aux       = nv94_aux,
 };
 
@@ -303,6 +283,8 @@ nv94_i2c_oclass = &(struct nouveau_i2c_impl) {
                .fini = _nouveau_i2c_fini,
        },
        .sclass = nv94_i2c_sclass,
+       .pad_x = &nv04_i2c_pad_oclass,
+       .pad_s = &nv94_i2c_pad_oclass,
        .aux = 4,
        .aux_stat = nv94_aux_stat,
        .aux_mask = nv94_aux_mask,
index 35b8253f324d9f8eb31794b005c9707508dc27a7..364ddb1c5f034d527d0f4e8566018ca242e14c10 100644 (file)
@@ -42,8 +42,6 @@ nvd0_i2c_sense_sda(struct nouveau_i2c_port *base)
 
 static const struct nouveau_i2c_func
 nvd0_i2c_func = {
-       .acquire   = nv94_i2c_acquire,
-       .release   = nv94_i2c_release,
        .drive_scl = nv50_i2c_drive_scl,
        .drive_sda = nv50_i2c_drive_sda,
        .sense_scl = nvd0_i2c_sense_scl,
@@ -106,6 +104,8 @@ nvd0_i2c_oclass = &(struct nouveau_i2c_impl) {
                .fini = _nouveau_i2c_fini,
        },
        .sclass = nvd0_i2c_sclass,
+       .pad_x = &nv04_i2c_pad_oclass,
+       .pad_s = &nv94_i2c_pad_oclass,
        .aux = 4,
        .aux_stat = nv94_aux_stat,
        .aux_mask = nv94_aux_mask,
index fb53389af89ed4fbd7933a9cf55125f4a11765fb..cae77e1ad8dc9b24c689b3b29669d946372dbcdf 100644 (file)
@@ -64,6 +64,8 @@ nve0_i2c_oclass = &(struct nouveau_i2c_impl) {
                .fini = _nouveau_i2c_fini,
        },
        .sclass = nvd0_i2c_sclass,
+       .pad_x = &nv04_i2c_pad_oclass,
+       .pad_s = &nv94_i2c_pad_oclass,
        .aux = 4,
        .aux_stat = nve0_aux_stat,
        .aux_mask = nve0_aux_mask,
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.c
new file mode 100644 (file)
index 0000000..e9e4124
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "pad.h"
+
+int
+_nvkm_i2c_pad_fini(struct nouveau_object *object, bool suspend)
+{
+       struct nvkm_i2c_pad *pad = (void *)object;
+       DBG("-> NULL\n");
+       pad->port = NULL;
+       return nouveau_object_fini(&pad->base, suspend);
+}
+
+int
+_nvkm_i2c_pad_init(struct nouveau_object *object)
+{
+       struct nvkm_i2c_pad *pad = (void *)object;
+       DBG("-> PORT:%02x\n", pad->next->index);
+       pad->port = pad->next;
+       return nouveau_object_init(&pad->base);
+}
+
+int
+nvkm_i2c_pad_create_(struct nouveau_object *parent,
+                    struct nouveau_object *engine,
+                    struct nouveau_oclass *oclass, int index,
+                    int size, void **pobject)
+{
+       struct nouveau_i2c *i2c = (void *)engine;
+       struct nouveau_i2c_port *port;
+       struct nvkm_i2c_pad *pad;
+       int ret;
+
+       list_for_each_entry(port, &i2c->ports, head) {
+               pad = nvkm_i2c_pad(port);
+               if (pad->index == index) {
+                       atomic_inc(&nv_object(pad)->refcount);
+                       *pobject = pad;
+                       return 1;
+               }
+       }
+
+       ret = nouveau_object_create_(parent, engine, oclass, 0, size, pobject);
+       pad = *pobject;
+       if (ret)
+               return ret;
+
+       pad->index = index;
+       return 0;
+}
+
+int
+_nvkm_i2c_pad_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                  struct nouveau_oclass *oclass, void *data, u32 index,
+                  struct nouveau_object **pobject)
+{
+       struct nvkm_i2c_pad *pad;
+       int ret;
+       ret = nvkm_i2c_pad_create(parent, engine, oclass, index, &pad);
+       *pobject = nv_object(pad);
+       return ret;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.h b/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.h
new file mode 100644 (file)
index 0000000..452ac10
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef __NVKM_I2C_PAD_H__
+#define __NVKM_I2C_PAD_H__
+
+#include "priv.h"
+
+struct nvkm_i2c_pad {
+       struct nouveau_object base;
+       int index;
+       struct nouveau_i2c_port *port;
+       struct nouveau_i2c_port *next;
+};
+
+static inline struct nvkm_i2c_pad *
+nvkm_i2c_pad(struct nouveau_i2c_port *port)
+{
+       struct nouveau_object *pad = nv_object(port);
+       while (pad->parent)
+               pad = pad->parent;
+       return (void *)pad;
+}
+
+#define nvkm_i2c_pad_create(p,e,o,i,d)                                         \
+       nvkm_i2c_pad_create_((p), (e), (o), (i), sizeof(**d), (void **)d)
+#define nvkm_i2c_pad_destroy(p) ({                                             \
+       struct nvkm_i2c_pad *_p = (p);                                         \
+       _nvkm_i2c_pad_dtor(nv_object(_p));                                     \
+})
+#define nvkm_i2c_pad_init(p) ({                                                \
+       struct nvkm_i2c_pad *_p = (p);                                         \
+       _nvkm_i2c_pad_init(nv_object(_p));                                     \
+})
+#define nvkm_i2c_pad_fini(p,s) ({                                              \
+       struct nvkm_i2c_pad *_p = (p);                                         \
+       _nvkm_i2c_pad_fini(nv_object(_p), (s));                                \
+})
+
+int nvkm_i2c_pad_create_(struct nouveau_object *, struct nouveau_object *,
+                        struct nouveau_oclass *, int index, int, void **);
+
+int _nvkm_i2c_pad_ctor(struct nouveau_object *, struct nouveau_object *,
+                      struct nouveau_oclass *, void *, u32,
+                      struct nouveau_object **);
+#define _nvkm_i2c_pad_dtor nouveau_object_destroy
+int _nvkm_i2c_pad_init(struct nouveau_object *);
+int _nvkm_i2c_pad_fini(struct nouveau_object *, bool);
+
+#ifndef MSG
+#define MSG(l,f,a...) do {                                                     \
+       struct nvkm_i2c_pad *_pad = (void *)pad;                               \
+       nv_##l(nv_object(_pad)->engine, "PAD:%c:%02x: "f,                      \
+              _pad->index >= 0x100 ? 'X' : 'S',                               \
+              _pad->index >= 0x100 ? _pad->index - 0x100 : _pad->index, ##a); \
+} while(0)
+#define DBG(f,a...) MSG(debug, f, ##a)
+#define ERR(f,a...) MSG(error, f, ##a)
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv04.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv04.c
new file mode 100644 (file)
index 0000000..2c4b612
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "pad.h"
+
+struct nouveau_oclass
+nv04_i2c_pad_oclass = {
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = _nvkm_i2c_pad_ctor,
+               .dtor = _nvkm_i2c_pad_dtor,
+               .init = _nvkm_i2c_pad_init,
+               .fini = _nvkm_i2c_pad_fini,
+       },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv94.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv94.c
new file mode 100644 (file)
index 0000000..0dc6753
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "pad.h"
+
+struct nv94_i2c_pad {
+       struct nvkm_i2c_pad base;
+       int addr;
+};
+
+static int
+nv94_i2c_pad_fini(struct nouveau_object *object, bool suspend)
+{
+       struct nouveau_i2c *i2c = (void *)object->engine;
+       struct nv94_i2c_pad *pad = (void *)object;
+       nv_mask(i2c, 0x00e50c + pad->addr, 0x00000001, 0x00000001);
+       return nvkm_i2c_pad_fini(&pad->base, suspend);
+}
+
+static int
+nv94_i2c_pad_init(struct nouveau_object *object)
+{
+       struct nouveau_i2c *i2c = (void *)object->engine;
+       struct nv94_i2c_pad *pad = (void *)object;
+
+       switch (nv_oclass(pad->base.next)->handle) {
+       case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX):
+               nv_mask(i2c, 0x00e500 + pad->addr, 0x0000c003, 0x00000002);
+               break;
+       case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT):
+       default:
+               nv_mask(i2c, 0x00e500 + pad->addr, 0x0000c003, 0x0000c001);
+               break;
+       }
+
+       nv_mask(i2c, 0x00e50c + pad->addr, 0x00000001, 0x00000000);
+       return nvkm_i2c_pad_init(&pad->base);
+}
+
+static int
+nv94_i2c_pad_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+                 struct nouveau_oclass *oclass, void *data, u32 index,
+                 struct nouveau_object **pobject)
+{
+       struct nv94_i2c_pad *pad;
+       int ret;
+
+       ret = nvkm_i2c_pad_create(parent, engine, oclass, index, &pad);
+       *pobject = nv_object(pad);
+       if (ret)
+               return ret;
+
+       pad->addr = index * 0x50;;
+       return 0;
+}
+
+struct nouveau_oclass
+nv94_i2c_pad_oclass = {
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = nv94_i2c_pad_ctor,
+               .dtor = _nvkm_i2c_pad_dtor,
+               .init = nv94_i2c_pad_init,
+               .fini = nv94_i2c_pad_fini,
+       },
+};
index 9a1e420e2fd1825f9a61c8c94181c37fa40f0eab..780090b6425a19026b4302962d87d04291b9c34f 100644 (file)
@@ -3,6 +3,9 @@
 
 #include <subdev/i2c.h>
 
+extern struct nouveau_oclass nv04_i2c_pad_oclass;
+extern struct nouveau_oclass nv94_i2c_pad_oclass;
+
 #define nouveau_i2c_port_create(p,e,o,i,a,f,d)                                 \
        nouveau_i2c_port_create_((p), (e), (o), (i), (a), (f),                 \
                                 sizeof(**d), (void **)d)
@@ -22,7 +25,7 @@ int nouveau_i2c_port_create_(struct nouveau_object *, struct nouveau_object *,
                             int, void **);
 void _nouveau_i2c_port_dtor(struct nouveau_object *);
 #define _nouveau_i2c_port_init nouveau_object_init
-#define _nouveau_i2c_port_fini nouveau_object_fini
+int  _nouveau_i2c_port_fini(struct nouveau_object *, bool);
 
 #define nouveau_i2c_create(p,e,o,d)                                            \
        nouveau_i2c_create_((p), (e), (o), sizeof(**d), (void **)d)
@@ -59,6 +62,8 @@ struct nouveau_i2c_impl {
 
        /* supported i2c port classes */
        struct nouveau_oclass *sclass;
+       struct nouveau_oclass *pad_x;
+       struct nouveau_oclass *pad_s;
 
        /* number of native dp aux channels present */
        int aux;