nouveau-y += core/subdev/bios/bit.o
nouveau-y += core/subdev/bios/dcb.o
nouveau-y += core/subdev/bios/gpio.o
+nouveau-y += core/subdev/bios/i2c.o
nouveau-y += core/subdev/device/base.o
nouveau-y += core/subdev/device/nv04.o
nouveau-y += core/subdev/device/nv10.o
nouveau-y += core/subdev/gpio/nv50.o
nouveau-y += core/subdev/gpio/nvd0.o
nouveau-y += core/subdev/i2c/base.o
+nouveau-y += core/subdev/i2c/aux.o
+nouveau-y += core/subdev/i2c/bit.o
nouveau-y += core/subdev/instmem/nv04.o
nouveau-y += core/subdev/instmem/nv50.o
nouveau-y += core/subdev/instmem/nvc0.o
nouveau-y += core/engine/copy/nvc0.o
nouveau-y += core/engine/crypt/nv84.o
nouveau-y += core/engine/crypt/nv98.o
+nouveau-y += core/engine/disp/vga.o
nouveau-y += core/engine/fifo/nv04.o
nouveau-y += core/engine/fifo/nv10.o
nouveau-y += core/engine/fifo/nv17.o
--- /dev/null
+/*
+ * Copyright 2012 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 <core/subdev.h>
+#include <core/device.h>
+
+u8
+nv_rdport(void *obj, int head, u16 port)
+{
+ struct nouveau_device *device = nv_device(obj);
+
+ if (device->card_type >= NV_50)
+ return nv_rd08(obj, 0x601000 + port);
+
+ if (port == 0x03c0 || port == 0x03c1 || /* AR */
+ port == 0x03c2 || port == 0x03da || /* INP0 */
+ port == 0x03d4 || port == 0x03d5) /* CR */
+ return nv_rd08(obj, 0x601000 + (head * 0x2000) + port);
+
+ if (port == 0x03c2 || port == 0x03cc || /* MISC */
+ port == 0x03c4 || port == 0x03c5 || /* SR */
+ port == 0x03ce || port == 0x03cf) { /* GR */
+ if (device->card_type < NV_40)
+ head = 0; /* CR44 selects head */
+ return nv_rd08(obj, 0x0c0000 + (head * 0x2000) + port);
+ }
+
+ nv_error(obj, "unknown vga port 0x%04x\n", port);
+ return 0x00;
+}
+
+void
+nv_wrport(void *obj, int head, u16 port, u8 data)
+{
+ struct nouveau_device *device = nv_device(obj);
+
+ if (device->card_type >= NV_50)
+ nv_wr08(obj, 0x601000 + port, data);
+ else
+ if (port == 0x03c0 || port == 0x03c1 || /* AR */
+ port == 0x03c2 || port == 0x03da || /* INP0 */
+ port == 0x03d4 || port == 0x03d5) /* CR */
+ nv_wr08(obj, 0x601000 + (head * 0x2000) + port, data);
+ else
+ if (port == 0x03c2 || port == 0x03cc || /* MISC */
+ port == 0x03c4 || port == 0x03c5 || /* SR */
+ port == 0x03ce || port == 0x03cf) { /* GR */
+ if (device->card_type < NV_40)
+ head = 0; /* CR44 selects head */
+ nv_wr08(obj, 0x0c0000 + (head * 0x2000) + port, data);
+ } else
+ nv_error(obj, "unknown vga port 0x%04x\n", port);
+}
+
+u8
+nv_rdvgas(void *obj, int head, u8 index)
+{
+ nv_wrport(obj, head, 0x03c4, index);
+ return nv_rdport(obj, head, 0x03c5);
+}
+
+void
+nv_wrvgas(void *obj, int head, u8 index, u8 value)
+{
+ nv_wrport(obj, head, 0x03c4, index);
+ nv_wrport(obj, head, 0x03c5, value);
+}
+
+u8
+nv_rdvgag(void *obj, int head, u8 index)
+{
+ nv_wrport(obj, head, 0x03ce, index);
+ return nv_rdport(obj, head, 0x03cf);
+}
+
+void
+nv_wrvgag(void *obj, int head, u8 index, u8 value)
+{
+ nv_wrport(obj, head, 0x03ce, index);
+ nv_wrport(obj, head, 0x03cf, value);
+}
+
+u8
+nv_rdvgac(void *obj, int head, u8 index)
+{
+ nv_wrport(obj, head, 0x03d4, index);
+ return nv_rdport(obj, head, 0x03d5);
+}
+
+void
+nv_wrvgac(void *obj, int head, u8 index, u8 value)
+{
+ nv_wrport(obj, head, 0x03d4, index);
+ nv_wrport(obj, head, 0x03d5, value);
+}
+
+u8
+nv_rdvgai(void *obj, int head, u16 port, u8 index)
+{
+ if (port == 0x03c4) return nv_rdvgas(obj, head, index);
+ if (port == 0x03ce) return nv_rdvgag(obj, head, index);
+ if (port == 0x03d4) return nv_rdvgac(obj, head, index);
+ nv_error(obj, "unknown indexed vga port 0x%04x\n", port);
+ return 0x00;
+}
+
+void
+nv_wrvgai(void *obj, int head, u16 port, u8 index, u8 value)
+{
+ if (port == 0x03c4) nv_wrvgas(obj, head, index, value);
+ else if (port == 0x03ce) nv_wrvgag(obj, head, index, value);
+ else if (port == 0x03d4) nv_wrvgac(obj, head, index, value);
+ else nv_error(obj, "unknown indexed vga port 0x%04x\n", port);
+}
+
+bool
+nv_lockvgac(void *obj, bool lock)
+{
+ bool locked = !nv_rdvgac(obj, 0, 0x1f);
+ u8 data = lock ? 0x99 : 0x57;
+ nv_wrvgac(obj, 0, 0x1f, data);
+ if (nv_device(obj)->chipset == 0x11) {
+ if (!(nv_rd32(obj, 0x001084) & 0x10000000))
+ nv_wrvgac(obj, 1, 0x1f, data);
+ }
+ return locked;
+}
+
+/* CR44 takes values 0 (head A), 3 (head B) and 4 (heads tied)
+ * it affects only the 8 bit vga io regs, which we access using mmio at
+ * 0xc{0,2}3c*, 0x60{1,3}3*, and 0x68{1,3}3d*
+ * in general, the set value of cr44 does not matter: reg access works as
+ * expected and values can be set for the appropriate head by using a 0x2000
+ * offset as required
+ * however:
+ * a) pre nv40, the head B range of PRMVIO regs at 0xc23c* was not exposed and
+ * cr44 must be set to 0 or 3 for accessing values on the correct head
+ * through the common 0xc03c* addresses
+ * b) in tied mode (4) head B is programmed to the values set on head A, and
+ * access using the head B addresses can have strange results, ergo we leave
+ * tied mode in init once we know to what cr44 should be restored on exit
+ *
+ * the owner parameter is slightly abused:
+ * 0 and 1 are treated as head values and so the set value is (owner * 3)
+ * other values are treated as literal values to set
+ */
+u8
+nv_rdvgaowner(void *obj)
+{
+ if (nv_device(obj)->card_type < NV_50) {
+ if (nv_device(obj)->chipset == 0x11) {
+ u32 tied = nv_rd32(obj, 0x001084) & 0x10000000;
+ if (tied == 0) {
+ u8 slA = nv_rdvgac(obj, 0, 0x28) & 0x80;
+ u8 tvA = nv_rdvgac(obj, 0, 0x33) & 0x01;
+ u8 slB = nv_rdvgac(obj, 1, 0x28) & 0x80;
+ u8 tvB = nv_rdvgac(obj, 1, 0x33) & 0x01;
+ if (slA && !tvA) return 0x00;
+ if (slB && !tvB) return 0x03;
+ if (slA) return 0x00;
+ if (slB) return 0x03;
+ return 0x00;
+ }
+ return 0x04;
+ }
+
+ return nv_rdvgac(obj, 0, 0x44);
+ }
+
+ nv_error(obj, "rdvgaowner after nv4x\n");
+ return 0x00;
+}
+
+void
+nv_wrvgaowner(void *obj, u8 select)
+{
+ if (nv_device(obj)->card_type < NV_50) {
+ u8 owner = (select == 1) ? 3 : select;
+ if (nv_device(obj)->chipset == 0x11) {
+ /* workaround hw lockup bug */
+ nv_rdvgac(obj, 0, 0x1f);
+ nv_rdvgac(obj, 1, 0x1f);
+ }
+
+ nv_wrvgac(obj, 0, 0x44, owner);
+
+ if (nv_device(obj)->chipset == 0x11) {
+ nv_wrvgac(obj, 0, 0x2e, owner);
+ nv_wrvgac(obj, 0, 0x2e, owner);
+ }
+ } else
+ nv_error(obj, "wrvgaowner after nv4x\n");
+}
--- /dev/null
+#ifndef __NVBIOS_I2C_H__
+#define __NVBIOS_I2C_H__
+
+struct nouveau_bios;
+
+enum dcb_i2c_type {
+ DCB_I2C_NV04_BIT = 0,
+ DCB_I2C_NV4E_BIT = 4,
+ DCB_I2C_NVIO_BIT = 5,
+ DCB_I2C_NVIO_AUX = 6,
+ DCB_I2C_UNUSED = 0xff
+};
+
+struct dcb_i2c_entry {
+ enum dcb_i2c_type type;
+ u8 drive;
+ u8 sense;
+ u32 data;
+};
+
+u16 dcb_i2c_table(struct nouveau_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
+u16 dcb_i2c_entry(struct nouveau_bios *, u8 index, u8 *ver, u8 *len);
+int dcb_i2c_parse(struct nouveau_bios *, u8 index, struct dcb_i2c_entry *);
+
+#endif
-/*
- * Copyright 2009 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.
- */
-
#ifndef __NOUVEAU_I2C_H__
#define __NOUVEAU_I2C_H__
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-#include "drm_dp_helper.h"
+#include <core/subdev.h>
+#include <core/device.h>
+
+#include <subdev/bios.h>
+#include <subdev/bios/i2c.h>
#define NV_I2C_PORT(n) (0x00 + (n))
-#define NV_I2C_PORT_NUM 0x10
#define NV_I2C_DEFAULT(n) (0x80 + (n))
-struct nouveau_i2c_chan {
+struct nouveau_i2c_port {
struct i2c_adapter adapter;
- struct drm_device *dev;
+ struct nouveau_i2c *i2c;
struct i2c_algo_bit_data bit;
struct list_head head;
u8 index;
u32 state;
};
-int nouveau_i2c_init(struct drm_device *);
-void nouveau_i2c_fini(struct drm_device *);
-struct nouveau_i2c_chan *nouveau_i2c_find(struct drm_device *, u8 index);
-bool nouveau_probe_i2c_addr(struct nouveau_i2c_chan *i2c, int addr);
-int nouveau_i2c_identify(struct drm_device *dev, const char *what,
- struct i2c_board_info *info,
- bool (*match)(struct nouveau_i2c_chan *,
- struct i2c_board_info *),
- int index);
+struct nouveau_i2c {
+ struct nouveau_subdev base;
+
+ struct nouveau_i2c_port *(*find)(struct nouveau_i2c *, u8 index);
+ int (*identify)(struct nouveau_i2c *, int index,
+ const char *what, struct i2c_board_info *,
+ bool (*match)(struct nouveau_i2c_port *,
+ struct i2c_board_info *));
+ struct list_head ports;
+};
+
+static inline struct nouveau_i2c *
+nouveau_i2c(void *obj)
+{
+ return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_I2C];
+}
+
+extern struct nouveau_oclass nouveau_i2c_oclass;
+
+void nouveau_i2c_drive_scl(void *, int);
+void nouveau_i2c_drive_sda(void *, int);
+int nouveau_i2c_sense_scl(void *);
+int nouveau_i2c_sense_sda(void *);
+
+int nv_rdi2cr(struct nouveau_i2c_port *, u8 addr, u8 reg);
+int nv_wri2cr(struct nouveau_i2c_port *, u8 addr, u8 reg, u8 val);
+bool nv_probe_i2c(struct nouveau_i2c_port *, u8 addr);
+
+int nv_rdaux(struct nouveau_i2c_port *, u32 addr, u8 *data, u8 size);
+int nv_wraux(struct nouveau_i2c_port *, u32 addr, u8 *data, u8 size);
-extern const struct i2c_algorithm nouveau_dp_i2c_algo;
+extern const struct i2c_algorithm nouveau_i2c_bit_algo;
+extern const struct i2c_algorithm nouveau_i2c_aux_algo;
-#endif /* __NOUVEAU_I2C_H__ */
+#endif
--- /dev/null
+#ifndef __NOUVEAU_VGA_H__
+#define __NOUVEAU_VGA_H__
+
+/* access to various legacy io ports */
+u8 nv_rdport(void *obj, int head, u16 port);
+void nv_wrport(void *obj, int head, u16 port, u8 value);
+
+/* VGA Sequencer */
+u8 nv_rdvgas(void *obj, int head, u8 index);
+void nv_wrvgas(void *obj, int head, u8 index, u8 value);
+
+/* VGA Graphics */
+u8 nv_rdvgag(void *obj, int head, u8 index);
+void nv_wrvgag(void *obj, int head, u8 index, u8 value);
+
+/* VGA CRTC */
+u8 nv_rdvgac(void *obj, int head, u8 index);
+void nv_wrvgac(void *obj, int head, u8 index, u8 value);
+
+/* VGA indexed port access dispatcher */
+u8 nv_rdvgai(void *obj, int head, u16 port, u8 index);
+void nv_wrvgai(void *obj, int head, u16 port, u8 index, u8 value);
+
+bool nv_lockvgac(void *obj, bool lock);
+u8 nv_rdvgaowner(void *obj);
+void nv_wrvgaowner(void *obj, u8);
+
+#endif
--- /dev/null
+/*
+ * Copyright 2012 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 "subdev/bios.h"
+#include "subdev/bios/dcb.h"
+#include "subdev/bios/i2c.h"
+
+u16
+dcb_i2c_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ u16 i2c = 0x0000;
+ u16 dcb = dcb_table(bios, ver, hdr, cnt, len);
+ if (dcb) {
+ if (*ver >= 0x15)
+ i2c = nv_ro16(bios, dcb + 2);
+ if (*ver >= 0x30)
+ i2c = nv_ro16(bios, dcb + 4);
+ }
+
+ if (i2c && *ver >= 0x30) {
+ *ver = nv_ro08(bios, i2c + 0);
+ *hdr = nv_ro08(bios, i2c + 1);
+ *cnt = nv_ro08(bios, i2c + 2);
+ *len = nv_ro08(bios, i2c + 3);
+ } else {
+ *ver = *ver; /* use DCB version */
+ *hdr = 0;
+ *cnt = 16;
+ *len = 4;
+ }
+
+ return i2c;
+}
+
+u16
+dcb_i2c_entry(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len)
+{
+ u8 hdr, cnt;
+ u16 i2c = dcb_i2c_table(bios, ver, &hdr, &cnt, len);
+ if (i2c && idx < cnt)
+ return i2c + hdr + (idx * *len);
+ return 0x0000;
+}
+
+int
+dcb_i2c_parse(struct nouveau_bios *bios, u8 idx, struct dcb_i2c_entry *info)
+{
+ u8 ver, len;
+ u16 ent = dcb_i2c_entry(bios, idx, &ver, &len);
+ if (ent) {
+ info->data = nv_ro32(bios, ent + 0);
+ info->type = nv_ro08(bios, ent + 3);
+ if (ver < 0x30) {
+ info->type &= 0x07;
+ if (info->type == 0x07)
+ info->type = 0xff;
+ }
+
+ switch (info->type) {
+ case DCB_I2C_NV04_BIT:
+ info->drive = nv_ro08(bios, ent + 0);
+ info->sense = nv_ro08(bios, ent + 1);
+ return 0;
+ case DCB_I2C_NV4E_BIT:
+ info->drive = nv_ro08(bios, ent + 1);
+ return 0;
+ case DCB_I2C_NVIO_BIT:
+ case DCB_I2C_NVIO_AUX:
+ info->drive = nv_ro08(bios, ent + 0);
+ return 0;
+ case DCB_I2C_UNUSED:
+ return 0;
+ default:
+ nv_warn(bios, "unknown i2c type %d\n", info->type);
+ info->type = DCB_I2C_UNUSED;
+ return 0;
+ }
+ }
+
+ if (bios->bmp_offset && idx < 2) {
+ /* BMP (from v4.0 has i2c info in the structure, it's in a
+ * fixed location on earlier VBIOS
+ */
+ if (nv_ro08(bios, bios->bmp_offset + 5) < 4)
+ ent = 0x0048;
+ else
+ ent = 0x0036 + bios->bmp_offset;
+
+ if (idx == 0) {
+ info->drive = nv_ro08(bios, ent + 4);
+ if (!info->drive) info->drive = 0x3f;
+ info->sense = nv_ro08(bios, ent + 5);
+ if (!info->sense) info->sense = 0x3e;
+ } else
+ if (idx == 1) {
+ info->drive = nv_ro08(bios, ent + 6);
+ if (!info->drive) info->drive = 0x37;
+ info->sense = nv_ro08(bios, ent + 7);
+ if (!info->sense) info->sense = 0x36;
+ }
+
+ info->type = DCB_I2C_NV04_BIT;
+ return 0;
+ }
+
+ return -ENOENT;
+}
#include <subdev/device.h>
#include <subdev/bios.h>
+#include <subdev/i2c.h>
int
nv04_identify(struct nouveau_device *device)
switch (device->chipset) {
case 0x04:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x05:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
default:
nv_fatal(device, "unknown RIVA chipset\n");
#include <subdev/device.h>
#include <subdev/bios.h>
#include <subdev/gpio.h>
+#include <subdev/i2c.h>
int
nv10_identify(struct nouveau_device *device)
case 0x10:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x15:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x16:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x1a:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x11:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x17:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x1f:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x18:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
default:
nv_fatal(device, "unknown Celsius chipset\n");
#include <subdev/device.h>
#include <subdev/bios.h>
#include <subdev/gpio.h>
+#include <subdev/i2c.h>
int
nv20_identify(struct nouveau_device *device)
case 0x20:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x25:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x28:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x2a:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
default:
nv_fatal(device, "unknown Kelvin chipset\n");
#include <subdev/device.h>
#include <subdev/bios.h>
#include <subdev/gpio.h>
+#include <subdev/i2c.h>
int
nv30_identify(struct nouveau_device *device)
case 0x30:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x35:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x31:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x36:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x34:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
default:
nv_fatal(device, "unknown Rankine chipset\n");
#include <subdev/device.h>
#include <subdev/bios.h>
#include <subdev/gpio.h>
+#include <subdev/i2c.h>
int
nv40_identify(struct nouveau_device *device)
case 0x40:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x41:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x42:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x43:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x45:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x47:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x49:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x4b:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x44:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x46:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x4a:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x4c:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x4e:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x63:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x67:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x68:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
default:
nv_fatal(device, "unknown Curie chipset\n");
#include <subdev/device.h>
#include <subdev/bios.h>
#include <subdev/gpio.h>
+#include <subdev/i2c.h>
int
nv50_identify(struct nouveau_device *device)
case 0x50:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x84:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x86:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x92:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x94:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x96:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0x98:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0xa0:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0xaa:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0xac:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0xa3:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0xa5:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0xa8:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0xaf:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
default:
nv_fatal(device, "unknown Tesla chipset\n");
#include <subdev/device.h>
#include <subdev/bios.h>
#include <subdev/gpio.h>
+#include <subdev/i2c.h>
int
nvc0_identify(struct nouveau_device *device)
case 0xc0:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0xc4:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0xc3:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0xce:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0xcf:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0xc1:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0xc8:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0xd9:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nvd0_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
default:
nv_fatal(device, "unknown Fermi chipset\n");
#include <subdev/device.h>
#include <subdev/bios.h>
#include <subdev/gpio.h>
+#include <subdev/i2c.h>
int
nve0_identify(struct nouveau_device *device)
case 0xe4:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nvd0_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
case 0xe7:
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = &nvd0_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = &nouveau_i2c_oclass;
break;
default:
nv_fatal(device, "unknown Kepler chipset\n");
--- /dev/null
+/*
+ * Copyright 2009 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 <subdev/i2c.h>
+
+/******************************************************************************
+ * aux channel util functions
+ *****************************************************************************/
+#define AUX_DBG(fmt, args...) nv_debug(aux, "AUXCH(%d): " fmt, ch, ##args)
+#define AUX_ERR(fmt, args...) nv_error(aux, "AUXCH(%d): " fmt, ch, ##args)
+
+static void
+auxch_fini(struct nouveau_i2c *aux, int ch)
+{
+ nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00310000, 0x00000000);
+}
+
+static int
+auxch_init(struct nouveau_i2c *aux, int ch)
+{
+ const u32 unksel = 1; /* nfi which to use, or if it matters.. */
+ const u32 ureq = unksel ? 0x00100000 : 0x00200000;
+ const u32 urep = unksel ? 0x01000000 : 0x02000000;
+ u32 ctrl, timeout;
+
+ /* wait up to 1ms for any previous transaction to be done... */
+ timeout = 1000;
+ do {
+ ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
+ udelay(1);
+ if (!timeout--) {
+ AUX_ERR("begin idle timeout 0x%08x", ctrl);
+ return -EBUSY;
+ }
+ } while (ctrl & 0x03010000);
+
+ /* set some magic, and wait up to 1ms for it to appear */
+ nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00300000, ureq);
+ timeout = 1000;
+ do {
+ ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
+ udelay(1);
+ if (!timeout--) {
+ AUX_ERR("magic wait 0x%08x\n", ctrl);
+ auxch_fini(aux, ch);
+ return -EBUSY;
+ }
+ } while ((ctrl & 0x03000000) != urep);
+
+ return 0;
+}
+
+static int
+auxch_tx(struct nouveau_i2c *aux, int ch, u8 type, u32 addr, u8 *data, u8 size)
+{
+ u32 ctrl, stat, timeout, retries;
+ u32 xbuf[4] = {};
+ int ret, i;
+
+ AUX_DBG("%d: 0x%08x %d\n", type, addr, size);
+
+ ret = auxch_init(aux, ch);
+ if (ret)
+ goto out;
+
+ stat = nv_rd32(aux, 0x00e4e8 + (ch * 0x50));
+ if (!(stat & 0x10000000)) {
+ AUX_DBG("sink not detected\n");
+ ret = -ENXIO;
+ goto out;
+ }
+
+ if (!(type & 1)) {
+ memcpy(xbuf, data, size);
+ for (i = 0; i < 16; i += 4) {
+ AUX_DBG("wr 0x%08x\n", xbuf[i / 4]);
+ nv_wr32(aux, 0x00e4c0 + (ch * 0x50) + i, xbuf[i / 4]);
+ }
+ }
+
+ ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
+ ctrl &= ~0x0001f0ff;
+ ctrl |= type << 12;
+ ctrl |= size - 1;
+ nv_wr32(aux, 0x00e4e0 + (ch * 0x50), addr);
+
+ /* retry transaction a number of times on failure... */
+ ret = -EREMOTEIO;
+ for (retries = 0; retries < 32; retries++) {
+ /* reset, and delay a while if this is a retry */
+ nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x80000000 | ctrl);
+ nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00000000 | ctrl);
+ if (retries)
+ udelay(400);
+
+ /* transaction request, wait up to 1ms for it to complete */
+ nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00010000 | ctrl);
+
+ timeout = 1000;
+ do {
+ ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50));
+ udelay(1);
+ if (!timeout--) {
+ AUX_ERR("tx req timeout 0x%08x\n", ctrl);
+ goto out;
+ }
+ } while (ctrl & 0x00010000);
+
+ /* read status, and check if transaction completed ok */
+ stat = nv_mask(aux, 0x00e4e8 + (ch * 0x50), 0, 0);
+ if (!(stat & 0x000f0f00)) {
+ ret = 0;
+ break;
+ }
+
+ AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat);
+ }
+
+ if (type & 1) {
+ for (i = 0; i < 16; i += 4) {
+ xbuf[i / 4] = nv_rd32(aux, 0x00e4d0 + (ch * 0x50) + i);
+ AUX_DBG("rd 0x%08x\n", xbuf[i / 4]);
+ }
+ memcpy(data, xbuf, size);
+ }
+
+out:
+ auxch_fini(aux, ch);
+ return ret;
+}
+
+int
+nv_rdaux(struct nouveau_i2c_port *auxch, u32 addr, u8 *data, u8 size)
+{
+ return auxch_tx(auxch->i2c, auxch->drive, 9, addr, data, size);
+}
+
+int
+nv_wraux(struct nouveau_i2c_port *auxch, u32 addr, u8 *data, u8 size)
+{
+ return auxch_tx(auxch->i2c, auxch->drive, 8, addr, data, size);
+}
+
+static int
+aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ struct nouveau_i2c_port *auxch = (struct nouveau_i2c_port *)adap;
+ struct i2c_msg *msg = msgs;
+ int ret, mcnt = num;
+
+ while (mcnt--) {
+ u8 remaining = msg->len;
+ u8 *ptr = msg->buf;
+
+ while (remaining) {
+ u8 cnt = (remaining > 16) ? 16 : remaining;
+ u8 cmd;
+
+ if (msg->flags & I2C_M_RD)
+ cmd = 1;
+ else
+ cmd = 0;
+
+ if (mcnt || remaining > 16)
+ cmd |= 4; /* MOT */
+
+ ret = auxch_tx(auxch->i2c, auxch->drive, cmd,
+ msg->addr, ptr, cnt);
+ if (ret < 0)
+ return ret;
+
+ ptr += cnt;
+ remaining -= cnt;
+ }
+
+ msg++;
+ }
+
+ return num;
+}
+
+static u32
+aux_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+const struct i2c_algorithm nouveau_i2c_aux_algo = {
+ .master_xfer = aux_xfer,
+ .functionality = aux_func
+};
/*
- * Copyright 2009 Red Hat Inc.
+ * Copyright 2012 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"),
* Authors: Ben Skeggs
*/
-#include <linux/module.h>
+#include "core/option.h"
-#include "drmP.h"
-#include "nouveau_drv.h"
-#include <subdev/i2c.h>
-#include "nouveau_hw.h"
+#include "subdev/i2c.h"
+#include "subdev/vga.h"
-static void
-i2c_drive_scl(void *data, int state)
+int
+nv_rdi2cr(struct nouveau_i2c_port *port, u8 addr, u8 reg)
+{
+ u8 val;
+ struct i2c_msg msgs[] = {
+ { .addr = addr, .flags = 0, .len = 1, .buf = ® },
+ { .addr = addr, .flags = I2C_M_RD, .len = 1, .buf = &val },
+ };
+
+ int ret = i2c_transfer(&port->adapter, msgs, 2);
+ if (ret != 2)
+ return -EIO;
+
+ return val;
+}
+
+int
+nv_wri2cr(struct nouveau_i2c_port *port, u8 addr, u8 reg, u8 val)
+{
+ struct i2c_msg msgs[] = {
+ { .addr = addr, .flags = 0, .len = 1, .buf = ® },
+ { .addr = addr, .flags = 0, .len = 1, .buf = &val },
+ };
+
+ int ret = i2c_transfer(&port->adapter, msgs, 2);
+ if (ret != 2)
+ return -EIO;
+
+ return 0;
+}
+
+bool
+nv_probe_i2c(struct nouveau_i2c_port *port, u8 addr)
+{
+ u8 buf[] = { 0 };
+ struct i2c_msg msgs[] = {
+ {
+ .addr = addr,
+ .flags = 0,
+ .len = 1,
+ .buf = buf,
+ },
+ {
+ .addr = addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = buf,
+ }
+ };
+
+ return i2c_transfer(&port->adapter, msgs, 2) == 2;
+}
+
+static struct nouveau_i2c_port *
+nouveau_i2c_find(struct nouveau_i2c *i2c, u8 index)
+{
+ struct nouveau_bios *bios = nouveau_bios(i2c);
+ struct nouveau_i2c_port *port;
+
+ if (index == NV_I2C_DEFAULT(0) ||
+ index == NV_I2C_DEFAULT(1)) {
+ 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;
+ else
+ index = (auxidx & 0xf0) >> 4;
+ } else {
+ index = 2;
+ }
+ }
+
+ list_for_each_entry(port, &i2c->ports, head) {
+ if (port->index == index)
+ break;
+ }
+
+ if (&port->head == &i2c->ports)
+ return NULL;
+
+ if (nv_device(i2c)->card_type >= NV_50 && (port->dcb & 0x00000100)) {
+ u32 reg = 0x00e500, val;
+ if (port->type == 6) {
+ reg += port->drive * 0x50;
+ val = 0x2002;
+ } else {
+ reg += ((port->dcb & 0x1e00) >> 9) * 0x50;
+ val = 0xe001;
+ }
+
+ /* nfi, but neither auxch or i2c work if it's 1 */
+ nv_mask(i2c, reg + 0x0c, 0x00000001, 0x00000000);
+ /* nfi, but switches auxch vs normal i2c */
+ nv_mask(i2c, reg + 0x00, 0x0000f003, val);
+ }
+
+ return port;
+}
+
+static int
+nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what,
+ struct i2c_board_info *info,
+ bool (*match)(struct nouveau_i2c_port *,
+ struct i2c_board_info *))
{
- struct nouveau_i2c_chan *port = data;
- if (port->type == 0) {
- u8 val = NVReadVgaCrtc(port->dev, 0, port->drive);
+ struct nouveau_i2c_port *port = nouveau_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].addr; i++) {
+ if (nv_probe_i2c(port, info[i].addr) &&
+ (!match || match(port, &info[i]))) {
+ nv_info(i2c, "detected %s: %s\n", what, info[i].type);
+ return i;
+ }
+ }
+
+ nv_debug(i2c, "no devices found.\n");
+ return -ENODEV;
+}
+
+void
+nouveau_i2c_drive_scl(void *data, int state)
+{
+ struct nouveau_i2c_port *port = data;
+
+ if (port->type == DCB_I2C_NV04_BIT) {
+ u8 val = nv_rdvgac(port->i2c, 0, port->drive);
if (state) val |= 0x20;
else val &= 0xdf;
- NVWriteVgaCrtc(port->dev, 0, port->drive, val | 0x01);
+ nv_wrvgac(port->i2c, 0, port->drive, val | 0x01);
} else
- if (port->type == 4) {
- nv_mask(port->dev, port->drive, 0x2f, state ? 0x21 : 0x01);
+ if (port->type == DCB_I2C_NV4E_BIT) {
+ nv_mask(port->i2c, port->drive, 0x2f, state ? 0x21 : 0x01);
} else
- if (port->type == 5) {
+ if (port->type == DCB_I2C_NVIO_BIT) {
if (state) port->state |= 0x01;
else port->state &= 0xfe;
- nv_wr32(port->dev, port->drive, 4 | port->state);
+ nv_wr32(port->i2c, port->drive, 4 | port->state);
}
}
-static void
-i2c_drive_sda(void *data, int state)
+void
+nouveau_i2c_drive_sda(void *data, int state)
{
- struct nouveau_i2c_chan *port = data;
- if (port->type == 0) {
- u8 val = NVReadVgaCrtc(port->dev, 0, port->drive);
+ struct nouveau_i2c_port *port = data;
+
+ if (port->type == DCB_I2C_NV04_BIT) {
+ u8 val = nv_rdvgac(port->i2c, 0, port->drive);
if (state) val |= 0x10;
else val &= 0xef;
- NVWriteVgaCrtc(port->dev, 0, port->drive, val | 0x01);
+ nv_wrvgac(port->i2c, 0, port->drive, val | 0x01);
} else
- if (port->type == 4) {
- nv_mask(port->dev, port->drive, 0x1f, state ? 0x11 : 0x01);
+ if (port->type == DCB_I2C_NV4E_BIT) {
+ nv_mask(port->i2c, port->drive, 0x1f, state ? 0x11 : 0x01);
} else
- if (port->type == 5) {
+ if (port->type == DCB_I2C_NVIO_BIT) {
if (state) port->state |= 0x02;
else port->state &= 0xfd;
- nv_wr32(port->dev, port->drive, 4 | port->state);
+ nv_wr32(port->i2c, port->drive, 4 | port->state);
}
}
-static int
-i2c_sense_scl(void *data)
+int
+nouveau_i2c_sense_scl(void *data)
{
- struct nouveau_i2c_chan *port = data;
- struct drm_nouveau_private *dev_priv = port->dev->dev_private;
- if (port->type == 0) {
- return !!(NVReadVgaCrtc(port->dev, 0, port->sense) & 0x04);
+ struct nouveau_i2c_port *port = data;
+ struct nouveau_device *device = nv_device(port->i2c);
+
+ if (port->type == DCB_I2C_NV04_BIT) {
+ return !!(nv_rdvgac(port->i2c, 0, port->sense) & 0x04);
} else
- if (port->type == 4) {
- return !!(nv_rd32(port->dev, port->sense) & 0x00040000);
+ if (port->type == DCB_I2C_NV4E_BIT) {
+ return !!(nv_rd32(port->i2c, port->sense) & 0x00040000);
} else
- if (port->type == 5) {
- if (dev_priv->card_type < NV_D0)
- return !!(nv_rd32(port->dev, port->sense) & 0x01);
+ if (port->type == DCB_I2C_NVIO_BIT) {
+ if (device->card_type < NV_D0)
+ return !!(nv_rd32(port->i2c, port->sense) & 0x01);
else
- return !!(nv_rd32(port->dev, port->sense) & 0x10);
+ return !!(nv_rd32(port->i2c, port->sense) & 0x10);
}
+
return 0;
}
-static int
-i2c_sense_sda(void *data)
+int
+nouveau_i2c_sense_sda(void *data)
{
- struct nouveau_i2c_chan *port = data;
- struct drm_nouveau_private *dev_priv = port->dev->dev_private;
- if (port->type == 0) {
- return !!(NVReadVgaCrtc(port->dev, 0, port->sense) & 0x08);
+ struct nouveau_i2c_port *port = data;
+ struct nouveau_device *device = nv_device(port->i2c);
+
+ if (port->type == DCB_I2C_NV04_BIT) {
+ return !!(nv_rdvgac(port->i2c, 0, port->sense) & 0x08);
} else
- if (port->type == 4) {
- return !!(nv_rd32(port->dev, port->sense) & 0x00080000);
+ if (port->type == DCB_I2C_NV4E_BIT) {
+ return !!(nv_rd32(port->i2c, port->sense) & 0x00080000);
} else
- if (port->type == 5) {
- if (dev_priv->card_type < NV_D0)
- return !!(nv_rd32(port->dev, port->sense) & 0x02);
+ if (port->type == DCB_I2C_NVIO_BIT) {
+ if (device->card_type < NV_D0)
+ return !!(nv_rd32(port->i2c, port->sense) & 0x02);
else
- return !!(nv_rd32(port->dev, port->sense) & 0x20);
+ return !!(nv_rd32(port->i2c, port->sense) & 0x20);
}
+
return 0;
}
-static const uint32_t nv50_i2c_port[] = {
+static const u32 nv50_i2c_port[] = {
0x00e138, 0x00e150, 0x00e168, 0x00e180,
0x00e254, 0x00e274, 0x00e764, 0x00e780,
0x00e79c, 0x00e7b8
};
-static u8 *
-i2c_table(struct drm_device *dev, u8 *version)
-{
- u8 *dcb = olddcb_table(dev), *i2c = NULL;
- if (dcb) {
- if (dcb[0] >= 0x15)
- i2c = ROMPTR(dev, dcb[2]);
- if (dcb[0] >= 0x30)
- i2c = ROMPTR(dev, dcb[4]);
- }
-
- /* early revisions had no version number, use dcb version */
- if (i2c) {
- *version = dcb[0];
- if (*version >= 0x30)
- *version = i2c[0];
- }
-
- return i2c;
-}
-
-int
-nouveau_i2c_init(struct drm_device *dev)
+static int
+nouveau_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nvbios *bios = &dev_priv->vbios;
- struct nouveau_i2c_chan *port;
- u8 version = 0x00, entries, recordlen;
- u8 *i2c, *entry, legacy[2][4] = {};
- int ret, i;
-
- INIT_LIST_HEAD(&dev_priv->i2c_ports);
-
- i2c = i2c_table(dev, &version);
- if (!i2c) {
- u8 *bmp = &bios->data[bios->offset];
- if (bios->type != NVBIOS_BMP)
- return -ENODEV;
-
- legacy[0][0] = NV_CIO_CRE_DDC_WR__INDEX;
- legacy[0][1] = NV_CIO_CRE_DDC_STATUS__INDEX;
- legacy[1][0] = NV_CIO_CRE_DDC0_WR__INDEX;
- legacy[1][1] = NV_CIO_CRE_DDC0_STATUS__INDEX;
-
- /* BMP (from v4.0) has i2c info in the structure, it's in a
- * fixed location on earlier VBIOS
- */
- if (bmp[5] < 4)
- i2c = &bios->data[0x48];
- else
- i2c = &bmp[0x36];
-
- if (i2c[4]) legacy[0][0] = i2c[4];
- if (i2c[5]) legacy[0][1] = i2c[5];
- if (i2c[6]) legacy[1][0] = i2c[6];
- if (i2c[7]) legacy[1][1] = i2c[7];
- }
-
- if (version >= 0x30) {
- entry = i2c[1] + i2c;
- entries = i2c[2];
- recordlen = i2c[3];
- } else
- if (version) {
- entry = i2c;
- entries = 16;
- recordlen = 4;
- } else {
- entry = legacy[0];
- entries = 2;
- recordlen = 4;
- }
+ struct nouveau_device *device = nv_device(parent);
+ struct nouveau_bios *bios = nouveau_bios(parent);
+ struct nouveau_i2c_port *port;
+ struct nouveau_i2c *i2c;
+ struct dcb_i2c_entry info;
+ int ret, i = -1;
+
+ ret = nouveau_subdev_create(parent, engine, oclass, 0,
+ "I2C", "i2c", &i2c);
+ *pobject = nv_object(i2c);
+ if (ret)
+ return ret;
+
+ i2c->find = nouveau_i2c_find;
+ i2c->identify = nouveau_i2c_identify;
+ INIT_LIST_HEAD(&i2c->ports);
+
+ while (!dcb_i2c_parse(bios, ++i, &info)) {
+ if (info.type == DCB_I2C_UNUSED)
+ continue;
- for (i = 0; i < entries; i++, entry += recordlen) {
port = kzalloc(sizeof(*port), GFP_KERNEL);
- if (port == NULL) {
- nouveau_i2c_fini(dev);
- return -ENOMEM;
- }
-
- port->type = entry[3];
- if (version < 0x30) {
- port->type &= 0x07;
- if (port->type == 0x07)
- port->type = 0xff;
- }
-
- if (port->type == 0xff) {
- kfree(port);
- continue;
+ if (!port) {
+ nv_error(i2c, "failed port memory alloc at %d\n", i);
+ break;
}
+ port->type = info.type;
switch (port->type) {
- case 0: /* NV04:NV50 */
- port->drive = entry[0];
- port->sense = entry[1];
+ case DCB_I2C_NV04_BIT:
+ port->drive = info.drive;
+ port->sense = info.sense;
break;
- case 4: /* NV4E */
- port->drive = 0x600800 + entry[1];
+ case DCB_I2C_NV4E_BIT:
+ port->drive = 0x600800 + info.drive;
port->sense = port->drive;
break;
- case 5: /* NV50- */
- port->drive = entry[0] & 0x0f;
- if (dev_priv->card_type < NV_D0) {
- if (port->drive >= ARRAY_SIZE(nv50_i2c_port))
+ case DCB_I2C_NVIO_BIT:
+ port->drive = info.drive & 0x0f;
+ if (device->card_type < NV_D0) {
+ if (info.drive >= ARRAY_SIZE(nv50_i2c_port))
break;
port->drive = nv50_i2c_port[port->drive];
port->sense = port->drive;
port->sense = port->drive;
}
break;
- case 6: /* NV50- DP AUX */
- port->drive = entry[0] & 0x0f;
+ case DCB_I2C_NVIO_AUX:
+ port->drive = info.drive & 0x0f;
port->sense = port->drive;
- port->adapter.algo = &nouveau_dp_i2c_algo;
+ port->adapter.algo = &nouveau_i2c_aux_algo;
break;
default:
break;
}
if (!port->adapter.algo && !port->drive) {
- NV_ERROR(dev, "I2C%d: type %d index %x/%x unknown\n",
+ nv_error(i2c, "I2C%d: type %d index %x/%x unknown\n",
i, port->type, port->drive, port->sense);
kfree(port);
continue;
}
snprintf(port->adapter.name, sizeof(port->adapter.name),
- "nouveau-%s-%d", pci_name(dev->pdev), i);
+ "nouveau-%s-%d", device->name, i);
port->adapter.owner = THIS_MODULE;
- port->adapter.dev.parent = &dev->pdev->dev;
- port->dev = dev;
+ port->adapter.dev.parent = &device->pdev->dev;
+ port->i2c = i2c;
port->index = i;
- port->dcb = ROM32(entry[0]);
+ port->dcb = info.data;
i2c_set_adapdata(&port->adapter, i2c);
- if (port->adapter.algo != &nouveau_dp_i2c_algo) {
- port->adapter.algo_data = &port->bit;
- port->bit.udelay = 10;
- port->bit.timeout = usecs_to_jiffies(2200);
- port->bit.data = port;
- port->bit.setsda = i2c_drive_sda;
- port->bit.setscl = i2c_drive_scl;
- port->bit.getsda = i2c_sense_sda;
- port->bit.getscl = i2c_sense_scl;
-
- i2c_drive_scl(port, 0);
- i2c_drive_sda(port, 1);
- i2c_drive_scl(port, 1);
-
- ret = i2c_bit_add_bus(&port->adapter);
+ if (port->adapter.algo != &nouveau_i2c_aux_algo) {
+ nouveau_i2c_drive_scl(port, 0);
+ nouveau_i2c_drive_sda(port, 1);
+ nouveau_i2c_drive_scl(port, 1);
+
+#ifdef CONFIG_NOUVEAU_I2C_INTERNAL_DEFAULT
+ if (nouveau_boolopt(device->cfgopt, "NvI2C", true)) {
+#else
+ if (nouveau_boolopt(device->cfgopt, "NvI2C", false)) {
+#endif
+ port->adapter.algo = &nouveau_i2c_bit_algo;
+ ret = i2c_add_adapter(&port->adapter);
+ } else {
+ port->adapter.algo_data = &port->bit;
+ port->bit.udelay = 10;
+ port->bit.timeout = usecs_to_jiffies(2200);
+ port->bit.data = port;
+ port->bit.setsda = nouveau_i2c_drive_sda;
+ port->bit.setscl = nouveau_i2c_drive_scl;
+ port->bit.getsda = nouveau_i2c_sense_sda;
+ port->bit.getscl = nouveau_i2c_sense_scl;
+ ret = i2c_bit_add_bus(&port->adapter);
+ }
} else {
- port->adapter.algo = &nouveau_dp_i2c_algo;
+ port->adapter.algo = &nouveau_i2c_aux_algo;
ret = i2c_add_adapter(&port->adapter);
}
if (ret) {
- NV_ERROR(dev, "I2C%d: failed register: %d\n", i, ret);
+ nv_error(i2c, "I2C%d: failed register: %d\n", i, ret);
kfree(port);
continue;
}
- list_add_tail(&port->head, &dev_priv->i2c_ports);
+ list_add_tail(&port->head, &i2c->ports);
}
return 0;
}
-void
-nouveau_i2c_fini(struct drm_device *dev)
+static void
+nouveau_i2c_dtor(struct nouveau_object *object)
{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_i2c_chan *port, *tmp;
+ struct nouveau_i2c *i2c = (void *)object;
+ struct nouveau_i2c_port *port, *temp;
- list_for_each_entry_safe(port, tmp, &dev_priv->i2c_ports, head) {
+ list_for_each_entry_safe(port, temp, &i2c->ports, head) {
i2c_del_adapter(&port->adapter);
+ list_del(&port->head);
kfree(port);
}
-}
-
-struct nouveau_i2c_chan *
-nouveau_i2c_find(struct drm_device *dev, u8 index)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_i2c_chan *port;
-
- if (index == NV_I2C_DEFAULT(0) ||
- index == NV_I2C_DEFAULT(1)) {
- u8 version, *i2c = i2c_table(dev, &version);
- if (i2c && version >= 0x30) {
- if (index == NV_I2C_DEFAULT(0))
- index = (i2c[4] & 0x0f);
- else
- index = (i2c[4] & 0xf0) >> 4;
- } else {
- index = 2;
- }
- }
-
- list_for_each_entry(port, &dev_priv->i2c_ports, head) {
- if (port->index == index)
- break;
- }
-
- if (&port->head == &dev_priv->i2c_ports)
- return NULL;
- if (dev_priv->card_type >= NV_50 && (port->dcb & 0x00000100)) {
- u32 reg = 0x00e500, val;
- if (port->type == 6) {
- reg += port->drive * 0x50;
- val = 0x2002;
- } else {
- reg += ((port->dcb & 0x1e00) >> 9) * 0x50;
- val = 0xe001;
- }
-
- /* nfi, but neither auxch or i2c work if it's 1 */
- nv_mask(dev, reg + 0x0c, 0x00000001, 0x00000000);
- /* nfi, but switches auxch vs normal i2c */
- nv_mask(dev, reg + 0x00, 0x0000f003, val);
- }
-
- return port;
+ nouveau_subdev_destroy(&i2c->base);
}
-bool
-nouveau_probe_i2c_addr(struct nouveau_i2c_chan *i2c, int addr)
+static int
+nouveau_i2c_init(struct nouveau_object *object)
{
- uint8_t buf[] = { 0 };
- struct i2c_msg msgs[] = {
- {
- .addr = addr,
- .flags = 0,
- .len = 1,
- .buf = buf,
- },
- {
- .addr = addr,
- .flags = I2C_M_RD,
- .len = 1,
- .buf = buf,
- }
- };
-
- return i2c_transfer(&i2c->adapter, msgs, 2) == 2;
+ struct nouveau_i2c *i2c = (void *)object;
+ return nouveau_subdev_init(&i2c->base);
}
-int
-nouveau_i2c_identify(struct drm_device *dev, const char *what,
- struct i2c_board_info *info,
- bool (*match)(struct nouveau_i2c_chan *,
- struct i2c_board_info *),
- int index)
+static int
+nouveau_i2c_fini(struct nouveau_object *object, bool suspend)
{
- struct nouveau_i2c_chan *i2c = nouveau_i2c_find(dev, index);
- int i;
-
- if (!i2c) {
- NV_DEBUG(dev, "No bus when probing %s on %d\n", what, index);
- return -ENODEV;
- }
-
- NV_DEBUG(dev, "Probing %ss on I2C bus: %d\n", what, i2c->index);
- for (i = 0; info[i].addr; i++) {
- if (nouveau_probe_i2c_addr(i2c, info[i].addr) &&
- (!match || match(i2c, &info[i]))) {
- NV_INFO(dev, "Detected %s: %s\n", what, info[i].type);
- return i;
- }
- }
-
- NV_DEBUG(dev, "No devices found.\n");
- return -ENODEV;
+ struct nouveau_i2c *i2c = (void *)object;
+ return nouveau_subdev_fini(&i2c->base, suspend);
}
+
+struct nouveau_oclass
+nouveau_i2c_oclass = {
+ .handle = NV_SUBDEV(I2C, 0x00),
+ .ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = nouveau_i2c_ctor,
+ .dtor = nouveau_i2c_dtor,
+ .init = nouveau_i2c_init,
+ .fini = nouveau_i2c_fini,
+ },
+};
--- /dev/null
+/*
+ * Copyright 2012 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 "subdev/i2c.h"
+
+#ifdef CONFIG_NOUVEAU_I2C_INTERNAL
+#define T_TIMEOUT 2200000
+#define T_RISEFALL 1000
+#define T_HOLD 5000
+
+static inline void
+i2c_drive_scl(struct nouveau_i2c_port *port, int state)
+{
+ nouveau_i2c_drive_scl(port, state);
+}
+
+static inline void
+i2c_drive_sda(struct nouveau_i2c_port *port, int state)
+{
+ nouveau_i2c_drive_sda(port, state);
+}
+
+static inline int
+i2c_sense_scl(struct nouveau_i2c_port *port)
+{
+ return nouveau_i2c_sense_scl(port);
+}
+
+static inline int
+i2c_sense_sda(struct nouveau_i2c_port *port)
+{
+ return nouveau_i2c_sense_sda(port);
+}
+
+static void
+i2c_delay(struct nouveau_i2c_port *port, u32 nsec)
+{
+ udelay((nsec + 500) / 1000);
+}
+
+static bool
+i2c_raise_scl(struct nouveau_i2c_port *port)
+{
+ u32 timeout = T_TIMEOUT / T_RISEFALL;
+
+ i2c_drive_scl(port, 1);
+ do {
+ i2c_delay(port, T_RISEFALL);
+ } while (!i2c_sense_scl(port) && --timeout);
+
+ return timeout != 0;
+}
+
+static int
+i2c_start(struct nouveau_i2c_port *port)
+{
+ int ret = 0;
+
+ port->state = i2c_sense_scl(port);
+ port->state |= i2c_sense_sda(port) << 1;
+ if (port->state != 3) {
+ i2c_drive_scl(port, 0);
+ i2c_drive_sda(port, 1);
+ if (!i2c_raise_scl(port))
+ ret = -EBUSY;
+ }
+
+ i2c_drive_sda(port, 0);
+ i2c_delay(port, T_HOLD);
+ i2c_drive_scl(port, 0);
+ i2c_delay(port, T_HOLD);
+ return ret;
+}
+
+static void
+i2c_stop(struct nouveau_i2c_port *port)
+{
+ i2c_drive_scl(port, 0);
+ i2c_drive_sda(port, 0);
+ i2c_delay(port, T_RISEFALL);
+
+ i2c_drive_scl(port, 1);
+ i2c_delay(port, T_HOLD);
+ i2c_drive_sda(port, 1);
+ i2c_delay(port, T_HOLD);
+}
+
+static int
+i2c_bitw(struct nouveau_i2c_port *port, int sda)
+{
+ i2c_drive_sda(port, sda);
+ i2c_delay(port, T_RISEFALL);
+
+ if (!i2c_raise_scl(port))
+ return -ETIMEDOUT;
+ i2c_delay(port, T_HOLD);
+
+ i2c_drive_scl(port, 0);
+ i2c_delay(port, T_HOLD);
+ return 0;
+}
+
+static int
+i2c_bitr(struct nouveau_i2c_port *port)
+{
+ int sda;
+
+ i2c_drive_sda(port, 1);
+ i2c_delay(port, T_RISEFALL);
+
+ if (!i2c_raise_scl(port))
+ return -ETIMEDOUT;
+ i2c_delay(port, T_HOLD);
+
+ sda = i2c_sense_sda(port);
+
+ i2c_drive_scl(port, 0);
+ i2c_delay(port, T_HOLD);
+ return sda;
+}
+
+static int
+i2c_get_byte(struct nouveau_i2c_port *port, u8 *byte, bool last)
+{
+ int i, bit;
+
+ *byte = 0;
+ for (i = 7; i >= 0; i--) {
+ bit = i2c_bitr(port);
+ if (bit < 0)
+ return bit;
+ *byte |= bit << i;
+ }
+
+ return i2c_bitw(port, last ? 1 : 0);
+}
+
+static int
+i2c_put_byte(struct nouveau_i2c_port *port, u8 byte)
+{
+ int i, ret;
+ for (i = 7; i >= 0; i--) {
+ ret = i2c_bitw(port, !!(byte & (1 << i)));
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = i2c_bitr(port);
+ if (ret == 1) /* nack */
+ ret = -EIO;
+ return ret;
+}
+
+static int
+i2c_addr(struct nouveau_i2c_port *port, struct i2c_msg *msg)
+{
+ u32 addr = msg->addr << 1;
+ if (msg->flags & I2C_M_RD)
+ addr |= 1;
+ return i2c_put_byte(port, addr);
+}
+
+static int
+i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ struct nouveau_i2c_port *port = (struct nouveau_i2c_port *)adap;
+ struct i2c_msg *msg = msgs;
+ int ret = 0, mcnt = num;
+
+ while (!ret && mcnt--) {
+ u8 remaining = msg->len;
+ u8 *ptr = msg->buf;
+
+ ret = i2c_start(port);
+ if (ret == 0)
+ ret = i2c_addr(port, msg);
+
+ if (msg->flags & I2C_M_RD) {
+ while (!ret && remaining--)
+ ret = i2c_get_byte(port, ptr++, !remaining);
+ } else {
+ while (!ret && remaining--)
+ ret = i2c_put_byte(port, *ptr++);
+ }
+
+ msg++;
+ }
+
+ i2c_stop(port);
+ return (ret < 0) ? ret : num;
+}
+#else
+static int
+i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ return -ENODEV;
+}
+#endif
+
+static u32
+i2c_bit_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+const struct i2c_algorithm nouveau_i2c_bit_algo = {
+ .master_xfer = i2c_bit_xfer,
+ .functionality = i2c_bit_func
+};
return dcb_entry;
}
-static struct nouveau_i2c_chan *
+static struct nouveau_i2c_port *
init_i2c_device_find(struct drm_device *dev, int i2c_index)
{
if (i2c_index == 0xff) {
/* note: dcb_entry_idx_from_crtchead needs pre-script set-up */
int idx = dcb_entry_idx_from_crtchead(dev);
- i2c_index = NV_I2C_DEFAULT(0);
+ i2c_index = 0x80; //NV_I2C_DEFAULT(0);
if (idx != 0x7f && dcb->entry[idx].i2c_upper_default)
- i2c_index = NV_I2C_DEFAULT(1);
+ i2c_index = 0x81; //NV_I2C_DEFAULT(1);
}
return nouveau_i2c_find(dev, i2c_index);
break;
case 5:
{
- struct nouveau_i2c_chan *auxch;
+ struct nouveau_i2c_port *auxch;
int ret;
auxch = nouveau_i2c_find(dev, bios->display.output->i2c_index);
return 3;
}
- ret = nouveau_dp_auxch(auxch, 9, 0xd, &cond, 1);
+ ret = auxch_rd(dev, auxch, 0xd, &cond, 1);
if (ret) {
NV_ERROR(dev, "0x%04X: auxch rd fail: %d\n", offset, ret);
return 3;
uint8_t i2c_index = bios->data[offset + 1];
uint8_t i2c_address = bios->data[offset + 2] >> 1;
uint8_t count = bios->data[offset + 3];
- struct nouveau_i2c_chan *chan;
+ struct nouveau_i2c_port *chan;
int len = 4 + count * 3;
int ret, i;
uint8_t data = bios->data[offset + 6 + i * 3];
union i2c_smbus_data val;
- ret = i2c_smbus_xfer(&chan->adapter, i2c_address, 0,
+ ret = i2c_smbus_xfer(nouveau_i2c_adapter(chan), i2c_address, 0,
I2C_SMBUS_READ, reg,
I2C_SMBUS_BYTE_DATA, &val);
if (ret < 0) {
val.byte &= mask;
val.byte |= data;
- ret = i2c_smbus_xfer(&chan->adapter, i2c_address, 0,
+ ret = i2c_smbus_xfer(nouveau_i2c_adapter(chan), i2c_address, 0,
I2C_SMBUS_WRITE, reg,
I2C_SMBUS_BYTE_DATA, &val);
if (ret < 0) {
uint8_t i2c_index = bios->data[offset + 1];
uint8_t i2c_address = bios->data[offset + 2] >> 1;
uint8_t count = bios->data[offset + 3];
- struct nouveau_i2c_chan *chan;
+ struct nouveau_i2c_port *chan;
int len = 4 + count * 2;
int ret, i;
if (!bios->execute)
continue;
- ret = i2c_smbus_xfer(&chan->adapter, i2c_address, 0,
+ ret = i2c_smbus_xfer(nouveau_i2c_adapter(chan), i2c_address, 0,
I2C_SMBUS_WRITE, reg,
I2C_SMBUS_BYTE_DATA, &val);
if (ret < 0) {
uint8_t i2c_address = bios->data[offset + 2] >> 1;
uint8_t count = bios->data[offset + 3];
int len = 4 + count;
- struct nouveau_i2c_chan *chan;
+ struct nouveau_i2c_port *chan;
struct i2c_msg msg;
uint8_t data[256];
int ret, i;
msg.flags = 0;
msg.len = count;
msg.buf = data;
- ret = i2c_transfer(&chan->adapter, &msg, 1);
+ ret = i2c_transfer(nouveau_i2c_adapter(chan), &msg, 1);
if (ret != 1) {
NV_ERROR(dev, "0x%04X: i2c wr fail: %d\n", offset, ret);
return len;
uint8_t reg = bios->data[offset + 3];
uint8_t mask = bios->data[offset + 4];
uint8_t data = bios->data[offset + 5];
- struct nouveau_i2c_chan *chan;
+ struct nouveau_i2c_port *chan;
union i2c_smbus_data val;
int ret;
if (!chan)
return -ENODEV;
- ret = i2c_smbus_xfer(&chan->adapter, i2c_address, 0,
+ ret = i2c_smbus_xfer(nouveau_i2c_adapter(chan), i2c_address, 0,
I2C_SMBUS_READ, reg,
I2C_SMBUS_BYTE_DATA, &val);
if (ret < 0) {
*/
struct drm_device *dev = bios->dev;
- struct nouveau_i2c_chan *auxch;
+ struct nouveau_i2c_port *auxch;
uint32_t addr = ROM32(bios->data[offset + 1]);
uint8_t count = bios->data[offset + 5];
int len = 6 + count * 2;
for (i = 0; i < count; i++, offset += 2) {
uint8_t data;
- ret = nouveau_dp_auxch(auxch, 9, addr, &data, 1);
+ ret = auxch_rd(dev, auxch, addr, &data, 1);
if (ret) {
NV_ERROR(dev, "INIT_AUXCH: rd auxch fail %d\n", ret);
return len;
data &= bios->data[offset + 0];
data |= bios->data[offset + 1];
- ret = nouveau_dp_auxch(auxch, 8, addr, &data, 1);
+ ret = auxch_wr(dev, auxch, addr, &data, 1);
if (ret) {
NV_ERROR(dev, "INIT_AUXCH: wr auxch fail %d\n", ret);
return len;
*/
struct drm_device *dev = bios->dev;
- struct nouveau_i2c_chan *auxch;
+ struct nouveau_i2c_port *auxch;
uint32_t addr = ROM32(bios->data[offset + 1]);
uint8_t count = bios->data[offset + 5];
int len = 6 + count;
offset += 6;
for (i = 0; i < count; i++, offset++) {
- ret = nouveau_dp_auxch(auxch, 8, addr, &bios->data[offset], 1);
+ ret = auxch_wr(dev, auxch, addr, &bios->data[offset], 1);
if (ret) {
NV_ERROR(dev, "INIT_ZM_AUXCH: wr auxch fail %d\n", ret);
return len;
uint8_t reghi = bios->data[offset + 4];
uint8_t mask = bios->data[offset + 5];
uint8_t data = bios->data[offset + 6];
- struct nouveau_i2c_chan *chan;
+ struct nouveau_i2c_port *chan;
uint8_t buf0[2] = { reghi, reglo };
uint8_t buf1[1];
struct i2c_msg msg[2] = {
return -ENODEV;
- ret = i2c_transfer(&chan->adapter, msg, 2);
+ ret = i2c_transfer(nouveau_i2c_adapter(chan), msg, 2);
if (ret < 0) {
BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X:0x%02X, Value: [no device], "
"Mask: 0x%02X, Data: 0x%02X\n",
if (ret)
return ret;
- ret = nouveau_i2c_init(dev);
- if (ret)
- return ret;
-
ret = nouveau_mxm_init(dev);
if (ret)
return ret;
void
nouveau_bios_takedown(struct drm_device *dev)
{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
-
nouveau_mxm_fini(dev);
- nouveau_i2c_fini(dev);
}
#define __NOUVEAU_BIOS_H__
#include "nvreg.h"
-#include <subdev/i2c.h>
#define DCB_MAX_NUM_ENTRIES 16
#define DCB_MAX_NUM_I2C_ENTRIES 16
#include <subdev/bios.h>
#include <subdev/gpio.h>
+#include <subdev/i2c.h>
void *nouveau_newpriv(struct drm_device *);
if (gpio && gpio->isr_del)
gpio->isr_del(gpio, idx, tag, line, exec, data);
}
+
+struct nouveau_i2c_port *
+nouveau_i2c_find(struct drm_device *dev, u8 index)
+{
+ struct nouveau_drm *drm = nouveau_newpriv(dev);
+ struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
+
+ return i2c->find(i2c, index);
+}
+
+bool
+nouveau_probe_i2c_addr(struct nouveau_i2c_port *port, int addr)
+{
+ return nv_probe_i2c(port, addr);
+}
+
+struct i2c_adapter *
+nouveau_i2c_adapter(struct nouveau_i2c_port *port)
+{
+ return &port->adapter;
+}
+
+
+int
+nouveau_i2c_identify(struct drm_device *dev, const char *what,
+ struct i2c_board_info *info,
+ bool (*match)(struct nouveau_i2c_port *,
+ struct i2c_board_info *),
+ int index)
+{
+ struct nouveau_drm *drm = nouveau_newpriv(dev);
+ struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
+
+ return i2c->identify(i2c, index, what, info, match);
+}
+
+int
+auxch_rd(struct drm_device *dev, struct nouveau_i2c_port *port,
+ u32 addr, u8 *data, u8 size)
+{
+ return nv_rdaux(port, addr, data, size);
+}
+
+int
+auxch_wr(struct drm_device *dev, struct nouveau_i2c_port *port,
+ u32 addr, u8 *data, u8 size)
+{
+ return nv_wraux(port, addr, data, size);
+}
void (*)(void *, int state), void *data);
void nouveau_gpio_isr_del(struct drm_device *, int idx, u8 tag, u8 line,
void (*)(void *, int state), void *data);
+
+struct nouveau_i2c_port *nouveau_i2c_find(struct drm_device *, u8);
+bool nouveau_probe_i2c_addr(struct nouveau_i2c_port *, int addr);
+struct i2c_adapter *nouveau_i2c_adapter(struct nouveau_i2c_port *);
+int nouveau_i2c_identify(struct drm_device *dev, const char *what,
+ struct i2c_board_info *info,
+ bool (*match)(struct nouveau_i2c_port *,
+ struct i2c_board_info *), int index);
+
+int auxch_rd(struct drm_device *, struct nouveau_i2c_port *, u32, u8 *, u8);
+int auxch_wr(struct drm_device *, struct nouveau_i2c_port *, u32, u8 *, u8);
+
+
#endif
kfree(connector);
}
-static struct nouveau_i2c_chan *
+static struct nouveau_i2c_port *
nouveau_connector_ddc_detect(struct drm_connector *connector,
struct nouveau_encoder **pnv_encoder)
{
int i;
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
- struct nouveau_i2c_chan *i2c = NULL;
+ struct nouveau_i2c_port *i2c = NULL;
struct nouveau_encoder *nv_encoder;
struct drm_mode_object *obj;
int id;
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = NULL;
struct nouveau_encoder *nv_partner;
- struct nouveau_i2c_chan *i2c;
+ struct nouveau_i2c_port *i2c;
int type;
/* Cleanup the previous EDID block. */
i2c = nouveau_connector_ddc_detect(connector, &nv_encoder);
if (i2c) {
- nv_connector->edid = drm_get_edid(connector, &i2c->adapter);
+ nv_connector->edid = drm_get_edid(connector, nouveau_i2c_adapter(i2c));
drm_mode_connector_update_edid_property(connector,
nv_connector->edid);
if (!nv_connector->edid) {
#define __NOUVEAU_CONNECTOR_H__
#include "drm_edid.h"
-#include <subdev/i2c.h>
+
+struct nouveau_i2c_port;
enum nouveau_underscan_type {
UNDERSCAN_OFF,
*/
#include "drmP.h"
+#include "drm_dp_helper.h"
#include "nouveau_drv.h"
-#include <subdev/i2c.h>
#include "nouveau_connector.h"
#include "nouveau_encoder.h"
#include "nouveau_crtc.h"
-/******************************************************************************
- * aux channel util functions
- *****************************************************************************/
-#define AUX_DBG(fmt, args...) do { \
- if (nouveau_reg_debug & NOUVEAU_REG_DEBUG_AUXCH) { \
- NV_PRINTK(KERN_DEBUG, dev, "AUXCH(%d): " fmt, ch, ##args); \
- } \
-} while (0)
-#define AUX_ERR(fmt, args...) NV_ERROR(dev, "AUXCH(%d): " fmt, ch, ##args)
-
-static void
-auxch_fini(struct drm_device *dev, int ch)
-{
- nv_mask(dev, 0x00e4e4 + (ch * 0x50), 0x00310000, 0x00000000);
-}
-
-static int
-auxch_init(struct drm_device *dev, int ch)
-{
- const u32 unksel = 1; /* nfi which to use, or if it matters.. */
- const u32 ureq = unksel ? 0x00100000 : 0x00200000;
- const u32 urep = unksel ? 0x01000000 : 0x02000000;
- u32 ctrl, timeout;
-
- /* wait up to 1ms for any previous transaction to be done... */
- timeout = 1000;
- do {
- ctrl = nv_rd32(dev, 0x00e4e4 + (ch * 0x50));
- udelay(1);
- if (!timeout--) {
- AUX_ERR("begin idle timeout 0x%08x", ctrl);
- return -EBUSY;
- }
- } while (ctrl & 0x03010000);
-
- /* set some magic, and wait up to 1ms for it to appear */
- nv_mask(dev, 0x00e4e4 + (ch * 0x50), 0x00300000, ureq);
- timeout = 1000;
- do {
- ctrl = nv_rd32(dev, 0x00e4e4 + (ch * 0x50));
- udelay(1);
- if (!timeout--) {
- AUX_ERR("magic wait 0x%08x\n", ctrl);
- auxch_fini(dev, ch);
- return -EBUSY;
- }
- } while ((ctrl & 0x03000000) != urep);
-
- return 0;
-}
-
-static int
-auxch_tx(struct drm_device *dev, int ch, u8 type, u32 addr, u8 *data, u8 size)
-{
- u32 ctrl, stat, timeout, retries;
- u32 xbuf[4] = {};
- int ret, i;
-
- AUX_DBG("%d: 0x%08x %d\n", type, addr, size);
-
- ret = auxch_init(dev, ch);
- if (ret)
- goto out;
-
- stat = nv_rd32(dev, 0x00e4e8 + (ch * 0x50));
- if (!(stat & 0x10000000)) {
- AUX_DBG("sink not detected\n");
- ret = -ENXIO;
- goto out;
- }
-
- if (!(type & 1)) {
- memcpy(xbuf, data, size);
- for (i = 0; i < 16; i += 4) {
- AUX_DBG("wr 0x%08x\n", xbuf[i / 4]);
- nv_wr32(dev, 0x00e4c0 + (ch * 0x50) + i, xbuf[i / 4]);
- }
- }
-
- ctrl = nv_rd32(dev, 0x00e4e4 + (ch * 0x50));
- ctrl &= ~0x0001f0ff;
- ctrl |= type << 12;
- ctrl |= size - 1;
- nv_wr32(dev, 0x00e4e0 + (ch * 0x50), addr);
-
- /* retry transaction a number of times on failure... */
- ret = -EREMOTEIO;
- for (retries = 0; retries < 32; retries++) {
- /* reset, and delay a while if this is a retry */
- nv_wr32(dev, 0x00e4e4 + (ch * 0x50), 0x80000000 | ctrl);
- nv_wr32(dev, 0x00e4e4 + (ch * 0x50), 0x00000000 | ctrl);
- if (retries)
- udelay(400);
-
- /* transaction request, wait up to 1ms for it to complete */
- nv_wr32(dev, 0x00e4e4 + (ch * 0x50), 0x00010000 | ctrl);
-
- timeout = 1000;
- do {
- ctrl = nv_rd32(dev, 0x00e4e4 + (ch * 0x50));
- udelay(1);
- if (!timeout--) {
- AUX_ERR("tx req timeout 0x%08x\n", ctrl);
- goto out;
- }
- } while (ctrl & 0x00010000);
-
- /* read status, and check if transaction completed ok */
- stat = nv_mask(dev, 0x00e4e8 + (ch * 0x50), 0, 0);
- if (!(stat & 0x000f0f00)) {
- ret = 0;
- break;
- }
-
- AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat);
- }
-
- if (type & 1) {
- for (i = 0; i < 16; i += 4) {
- xbuf[i / 4] = nv_rd32(dev, 0x00e4d0 + (ch * 0x50) + i);
- AUX_DBG("rd 0x%08x\n", xbuf[i / 4]);
- }
- memcpy(data, xbuf, size);
- }
-
-out:
- auxch_fini(dev, ch);
- return ret;
-}
-
u8 *
nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry)
{
* link training
*****************************************************************************/
struct dp_state {
+ struct nouveau_i2c_port *auxch;
struct dp_train_func *func;
struct dcb_entry *dcb;
- int auxch;
int crtc;
u8 *dpcd;
int link_nr;
if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP)
sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
- auxch_tx(dev, dp->auxch, 8, DP_LINK_BW_SET, sink, 2);
+ auxch_wr(dev, dp->auxch, DP_LINK_BW_SET, sink, 2);
}
static void
dp->func->train_set(dev, dp->dcb, pattern);
- auxch_tx(dev, dp->auxch, 9, DP_TRAINING_PATTERN_SET, &sink_tp, 1);
+ auxch_rd(dev, dp->auxch, DP_TRAINING_PATTERN_SET, &sink_tp, 1);
sink_tp &= ~DP_TRAINING_PATTERN_MASK;
sink_tp |= pattern;
- auxch_tx(dev, dp->auxch, 8, DP_TRAINING_PATTERN_SET, &sink_tp, 1);
+ auxch_wr(dev, dp->auxch, DP_TRAINING_PATTERN_SET, &sink_tp, 1);
}
static int
dp->func->train_adj(dev, dp->dcb, i, lvsw, lpre);
}
- return auxch_tx(dev, dp->auxch, 8, DP_TRAINING_LANE0_SET, dp->conf, 4);
+ return auxch_wr(dev, dp->auxch, DP_TRAINING_LANE0_SET, dp->conf, 4);
}
static int
udelay(delay);
- ret = auxch_tx(dev, dp->auxch, 9, DP_LANE0_1_STATUS, dp->stat, 6);
+ ret = auxch_rd(dev, dp->auxch, DP_LANE0_1_STATUS, dp->stat, 6);
if (ret)
return ret;
struct nouveau_connector *nv_connector =
nouveau_encoder_connector_get(nv_encoder);
struct drm_device *dev = encoder->dev;
- struct nouveau_i2c_chan *auxch;
const u32 bw_list[] = { 270000, 162000, 0 };
const u32 *link_bw = bw_list;
struct dp_state dp;
- auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
- if (!auxch)
+ dp.auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
+ if (!dp.auxch)
return false;
dp.func = func;
dp.dcb = nv_encoder->dcb;
dp.crtc = nv_crtc->index;
- dp.auxch = auxch->drive;
dp.dpcd = nv_encoder->dp.dpcd;
/* adjust required bandwidth for 8B/10B coding overhead */
struct dp_train_func *func)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct nouveau_i2c_chan *auxch;
+ struct nouveau_i2c_port *auxch;
u8 status;
auxch = nouveau_i2c_find(encoder->dev, nv_encoder->dcb->i2c_index);
else
status = DP_SET_POWER_D3;
- nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1);
+ auxch_wr(encoder->dev, auxch, DP_SET_POWER, &status, 1);
if (mode == DRM_MODE_DPMS_ON)
nouveau_dp_link_train(encoder, datarate, func);
}
static void
-nouveau_dp_probe_oui(struct drm_device *dev, struct nouveau_i2c_chan *auxch,
+nouveau_dp_probe_oui(struct drm_device *dev, struct nouveau_i2c_port *auxch,
u8 *dpcd)
{
u8 buf[3];
if (!(dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT))
return;
- if (!auxch_tx(dev, auxch->drive, 9, DP_SINK_OUI, buf, 3))
+ if (!auxch_rd(dev, auxch, DP_SINK_OUI, buf, 3))
NV_DEBUG_KMS(dev, "Sink OUI: %02hx%02hx%02hx\n",
buf[0], buf[1], buf[2]);
- if (!auxch_tx(dev, auxch->drive, 9, DP_BRANCH_OUI, buf, 3))
+ if (!auxch_rd(dev, auxch, DP_BRANCH_OUI, buf, 3))
NV_DEBUG_KMS(dev, "Branch OUI: %02hx%02hx%02hx\n",
buf[0], buf[1], buf[2]);
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_device *dev = encoder->dev;
- struct nouveau_i2c_chan *auxch;
+ struct nouveau_i2c_port *auxch;
u8 *dpcd = nv_encoder->dp.dpcd;
int ret;
if (!auxch)
return false;
- ret = auxch_tx(dev, auxch->drive, 9, DP_DPCD_REV, dpcd, 8);
+ ret = auxch_rd(dev, auxch, DP_DPCD_REV, dpcd, 8);
if (ret)
return false;
return true;
}
-
-int
-nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
- uint8_t *data, int data_nr)
-{
- return auxch_tx(auxch->dev, auxch->drive, cmd, addr, data, data_nr);
-}
-
-static int
-nouveau_dp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
-{
- struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adap;
- struct i2c_msg *msg = msgs;
- int ret, mcnt = num;
-
- while (mcnt--) {
- u8 remaining = msg->len;
- u8 *ptr = msg->buf;
-
- while (remaining) {
- u8 cnt = (remaining > 16) ? 16 : remaining;
- u8 cmd;
-
- if (msg->flags & I2C_M_RD)
- cmd = AUX_I2C_READ;
- else
- cmd = AUX_I2C_WRITE;
-
- if (mcnt || remaining > 16)
- cmd |= AUX_I2C_MOT;
-
- ret = nouveau_dp_auxch(auxch, cmd, msg->addr, ptr, cnt);
- if (ret < 0)
- return ret;
-
- ptr += cnt;
- remaining -= cnt;
- }
-
- msg++;
- }
-
- return num;
-}
-
-static u32
-nouveau_dp_i2c_func(struct i2c_adapter *adap)
-{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
-}
-
-const struct i2c_algorithm nouveau_dp_i2c_algo = {
- .master_xfer = nouveau_dp_i2c_xfer,
- .functionality = nouveau_dp_i2c_func
-};
#define NV_DPMS_CLEARED 0x80
+struct nouveau_i2c_port;
+
struct dp_train_func {
void (*link_set)(struct drm_device *, struct dcb_entry *, int crtc,
int nr, u32 bw, bool enhframe);
}
/* nouveau_dp.c */
-int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
- uint8_t *data, int data_nr);
bool nouveau_dp_detect(struct drm_encoder *);
void nouveau_dp_dpms(struct drm_encoder *, int mode, u32 datarate,
struct dp_train_func *);
}
static bool
-mxm_shadow_rom_fetch(struct nouveau_i2c_chan *i2c, u8 addr,
+mxm_shadow_rom_fetch(struct nouveau_i2c_port *i2c, u8 addr,
u8 offset, u8 size, u8 *data)
{
struct i2c_msg msgs[] = {
{ .addr = addr, .flags = I2C_M_RD, .len = size, .buf = data, },
};
- return i2c_transfer(&i2c->adapter, msgs, 2) == 2;
+ return i2c_transfer(nouveau_i2c_adapter(i2c), msgs, 2) == 2;
}
static bool
mxm_shadow_rom(struct drm_device *dev, u8 version)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_i2c_chan *i2c = NULL;
+ struct nouveau_i2c_port *i2c = NULL;
u8 i2cidx, mxms[6], addr, size;
i2cidx = mxm_ddc_map(dev, 1 /* LVDS_DDC */) & 0x0f;
}
static bool
-probe_monitoring_device(struct nouveau_i2c_chan *i2c,
+probe_monitoring_device(struct nouveau_i2c_port *i2c,
struct i2c_board_info *info)
{
struct i2c_client *client;
request_module("%s%s", I2C_MODULE_PREFIX, info->type);
- client = i2c_new_device(&i2c->adapter, info);
+ client = i2c_new_device(nouveau_i2c_adapter(i2c), info);
if (!client)
return false;
};
nouveau_i2c_identify(dev, "monitoring device", info,
- probe_monitoring_device, NV_I2C_DEFAULT(0));
+ probe_monitoring_device, 0x80); //NV_I2C_DEFAULT(0));
}
void
{
struct drm_device *dev = encoder->dev;
struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
- struct nouveau_i2c_chan *i2c = nouveau_i2c_find(dev, 2);
+ struct nouveau_i2c_port *i2c = nouveau_i2c_find(dev, 2);
struct i2c_board_info info[] = {
{
.type = "sil164",
return;
drm_i2c_encoder_init(dev, to_encoder_slave(encoder),
- &i2c->adapter, &info[type]);
+ nouveau_i2c_adapter(i2c), &info[type]);
}
static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = {
struct drm_device *dev = connector->dev;
struct drm_encoder_helper_funcs *hfuncs;
struct drm_encoder_slave_funcs *sfuncs;
- struct nouveau_i2c_chan *i2c =
+ struct nouveau_i2c_port *i2c =
nouveau_i2c_find(dev, entry->i2c_index);
int type, ret;
/* Run the slave-specific initialization */
ret = drm_i2c_encoder_init(dev, to_encoder_slave(encoder),
- &i2c->adapter, &nv04_tv_encoder_info[type]);
+ nouveau_i2c_adapter(i2c), &nv04_tv_encoder_info[type]);
if (ret < 0)
goto fail_cleanup;