diff options
Diffstat (limited to 'drivers/gpu')
29 files changed, 1347 insertions, 555 deletions
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 414b2e8b810..40ce4e18e7e 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -22,6 +22,7 @@ nouveau-y += core/subdev/bios/base.o 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 @@ -45,6 +46,8 @@ nouveau-y += core/subdev/gpio/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 @@ -61,6 +64,7 @@ nouveau-y += core/engine/copy/nva3.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 diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/vga.c b/drivers/gpu/drm/nouveau/core/engine/disp/vga.c new file mode 100644 index 00000000000..cec2110cc53 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/vga.c @@ -0,0 +1,214 @@ +/* + * 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"); +} diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/i2c.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/i2c.h new file mode 100644 index 00000000000..5079bedfd98 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/i2c.h @@ -0,0 +1,25 @@ +#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 diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h b/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h index 1d083893a4d..b93ab01e378 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h @@ -1,39 +1,18 @@ -/* - * 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; @@ -44,16 +23,38 @@ struct nouveau_i2c_chan { 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 diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/vga.h b/drivers/gpu/drm/nouveau/core/include/subdev/vga.h new file mode 100644 index 00000000000..d81df1ae442 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/subdev/vga.h @@ -0,0 +1,28 @@ +#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 diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/i2c.c b/drivers/gpu/drm/nouveau/core/subdev/bios/i2c.c new file mode 100644 index 00000000000..ad577db8376 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/i2c.c @@ -0,0 +1,129 @@ +/* + * 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; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv04.c index d54daa3f0df..e0ebbe184c9 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv04.c @@ -24,6 +24,7 @@ #include <subdev/device.h> #include <subdev/bios.h> +#include <subdev/i2c.h> int nv04_identify(struct nouveau_device *device) @@ -31,9 +32,11 @@ 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"); diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv10.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv10.c index c0c40cddaa2..19b1de60db2 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nv10.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv10.c @@ -25,6 +25,7 @@ #include <subdev/device.h> #include <subdev/bios.h> #include <subdev/gpio.h> +#include <subdev/i2c.h> int nv10_identify(struct nouveau_device *device) @@ -33,34 +34,42 @@ 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"); diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv20.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv20.c index 1215e3ff47f..8f735275b88 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nv20.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv20.c @@ -25,6 +25,7 @@ #include <subdev/device.h> #include <subdev/bios.h> #include <subdev/gpio.h> +#include <subdev/i2c.h> int nv20_identify(struct nouveau_device *device) @@ -33,18 +34,22 @@ 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"); diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv30.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv30.c index 43eb94e7768..eefc3455bd2 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nv30.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv30.c @@ -25,6 +25,7 @@ #include <subdev/device.h> #include <subdev/bios.h> #include <subdev/gpio.h> +#include <subdev/i2c.h> int nv30_identify(struct nouveau_device *device) @@ -33,22 +34,27 @@ 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"); diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv40.c index 7c10a3c68d5..63047c5bfdc 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nv40.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv40.c @@ -25,6 +25,7 @@ #include <subdev/device.h> #include <subdev/bios.h> #include <subdev/gpio.h> +#include <subdev/i2c.h> int nv40_identify(struct nouveau_device *device) @@ -33,66 +34,82 @@ 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"); diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv50.c index 581dcf19988..9f4f6eff443 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv50.c @@ -25,6 +25,7 @@ #include <subdev/device.h> #include <subdev/bios.h> #include <subdev/gpio.h> +#include <subdev/i2c.h> int nv50_identify(struct nouveau_device *device) @@ -33,58 +34,72 @@ 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"); diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c index df31111965c..f941024723c 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c @@ -25,6 +25,7 @@ #include <subdev/device.h> #include <subdev/bios.h> #include <subdev/gpio.h> +#include <subdev/i2c.h> int nvc0_identify(struct nouveau_device *device) @@ -33,34 +34,42 @@ 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"); diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/device/nve0.c index d321cb4ec13..21763cf39ec 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nve0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nve0.c @@ -25,6 +25,7 @@ #include <subdev/device.h> #include <subdev/bios.h> #include <subdev/gpio.h> +#include <subdev/i2c.h> int nve0_identify(struct nouveau_device *device) @@ -33,10 +34,12 @@ 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"); diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c new file mode 100644 index 00000000000..fe1ebf199ba --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c @@ -0,0 +1,212 @@ +/* + * 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 +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c index 740e399bc7a..3d2c88310f9 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c @@ -1,5 +1,5 @@ /* - * 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"), @@ -22,204 +22,277 @@ * 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; @@ -228,167 +301,107 @@ nouveau_i2c_init(struct drm_device *dev) 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, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c new file mode 100644 index 00000000000..1c4c9a5c8e2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c @@ -0,0 +1,230 @@ +/* + * 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 +}; diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 4f0d9bd1914..35b0a8f9c00 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -528,7 +528,7 @@ static int dcb_entry_idx_from_crtchead(struct drm_device *dev) 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) { @@ -537,9 +537,9 @@ init_i2c_device_find(struct drm_device *dev, int i2c_index) /* 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); @@ -920,7 +920,7 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) 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); @@ -929,7 +929,7 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) 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; @@ -1166,7 +1166,7 @@ init_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) 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; @@ -1189,7 +1189,7 @@ init_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) 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) { @@ -1206,7 +1206,7 @@ init_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) 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) { @@ -1241,7 +1241,7 @@ init_zm_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) 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; @@ -1270,7 +1270,7 @@ init_zm_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) 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) { @@ -1304,7 +1304,7 @@ init_zm_i2c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) 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; @@ -1333,7 +1333,7 @@ init_zm_i2c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) 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; @@ -1769,7 +1769,7 @@ init_i2c_if(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) 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; @@ -1782,7 +1782,7 @@ init_i2c_if(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) 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) { @@ -3167,7 +3167,7 @@ init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) */ 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; @@ -3192,7 +3192,7 @@ init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) 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; @@ -3201,7 +3201,7 @@ init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) 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; @@ -3226,7 +3226,7 @@ init_zm_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) */ 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; @@ -3249,7 +3249,7 @@ init_zm_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) 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; @@ -3285,7 +3285,7 @@ init_i2c_long_if(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) 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] = { @@ -3304,7 +3304,7 @@ init_i2c_long_if(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) 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", @@ -6270,10 +6270,6 @@ nouveau_bios_init(struct drm_device *dev) if (ret) return ret; - ret = nouveau_i2c_init(dev); - if (ret) - return ret; - ret = nouveau_mxm_init(dev); if (ret) return ret; @@ -6318,8 +6314,5 @@ nouveau_bios_init(struct drm_device *dev) 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); } diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index b4529a0b35a..52fce11e9d2 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -25,7 +25,6 @@ #define __NOUVEAU_BIOS_H__ #include "nvreg.h" -#include <subdev/i2c.h> #define DCB_MAX_NUM_ENTRIES 16 #define DCB_MAX_NUM_I2C_ENTRIES 16 diff --git a/drivers/gpu/drm/nouveau/nouveau_compat.c b/drivers/gpu/drm/nouveau/nouveau_compat.c index 81fc8494e76..76582b0a9df 100644 --- a/drivers/gpu/drm/nouveau/nouveau_compat.c +++ b/drivers/gpu/drm/nouveau/nouveau_compat.c @@ -3,6 +3,7 @@ #include <subdev/bios.h> #include <subdev/gpio.h> +#include <subdev/i2c.h> void *nouveau_newpriv(struct drm_device *); @@ -130,3 +131,52 @@ nouveau_gpio_isr_del(struct drm_device *dev, int idx, u8 tag, u8 line, 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); +} diff --git a/drivers/gpu/drm/nouveau/nouveau_compat.h b/drivers/gpu/drm/nouveau/nouveau_compat.h index f1143c362db..9b3298bfe4e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_compat.h +++ b/drivers/gpu/drm/nouveau/nouveau_compat.h @@ -20,4 +20,17 @@ int nouveau_gpio_isr_add(struct drm_device *, int idx, u8 tag, u8 line, 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 diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index cbf8348ca22..f9cc3b38606 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -105,7 +105,7 @@ nouveau_connector_destroy(struct drm_connector *connector) 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) { @@ -113,7 +113,7 @@ nouveau_connector_ddc_detect(struct drm_connector *connector, 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; @@ -217,7 +217,7 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) 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. */ @@ -229,7 +229,7 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) 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) { diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index bb0faf9f5d1..9503cfa0492 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -28,7 +28,8 @@ #define __NOUVEAU_CONNECTOR_H__ #include "drm_edid.h" -#include <subdev/i2c.h> + +struct nouveau_i2c_port; enum nouveau_underscan_type { UNDERSCAN_OFF, diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 63c0e827c30..9e18b35803a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -23,143 +23,13 @@ */ #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) { @@ -208,9 +78,9 @@ 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; @@ -236,7 +106,7 @@ dp_set_link_config(struct drm_device *dev, struct dp_state *dp) 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 @@ -248,10 +118,10 @@ dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 pattern) 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 @@ -274,7 +144,7 @@ dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) 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 @@ -284,7 +154,7 @@ dp_link_train_update(struct drm_device *dev, struct dp_state *dp, u32 delay) 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; @@ -417,19 +287,17 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate, 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 */ @@ -491,7 +359,7 @@ nouveau_dp_dpms(struct drm_encoder *encoder, int mode, u32 datarate, 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); @@ -503,14 +371,14 @@ nouveau_dp_dpms(struct drm_encoder *encoder, int mode, u32 datarate, 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]; @@ -518,11 +386,11 @@ nouveau_dp_probe_oui(struct drm_device *dev, struct nouveau_i2c_chan *auxch, 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]); @@ -533,7 +401,7 @@ nouveau_dp_detect(struct drm_encoder *encoder) { 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; @@ -541,7 +409,7 @@ nouveau_dp_detect(struct drm_encoder *encoder) 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; @@ -566,58 +434,3 @@ nouveau_dp_detect(struct drm_encoder *encoder) 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 -}; diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index 3dc14a3dcc4..1e79ee5fb89 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -32,6 +32,8 @@ #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); @@ -87,8 +89,6 @@ get_slave_funcs(struct drm_encoder *enc) } /* 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 *); diff --git a/drivers/gpu/drm/nouveau/nouveau_mxm.c b/drivers/gpu/drm/nouveau/nouveau_mxm.c index b2b326b83ed..7ca129d5583 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mxm.c +++ b/drivers/gpu/drm/nouveau/nouveau_mxm.c @@ -471,7 +471,7 @@ mxm_dcb_sanitise(struct drm_device *dev) } 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[] = { @@ -479,14 +479,14 @@ mxm_shadow_rom_fetch(struct nouveau_i2c_chan *i2c, u8 addr, { .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; diff --git a/drivers/gpu/drm/nouveau/nouveau_temp.c b/drivers/gpu/drm/nouveau/nouveau_temp.c index 0f5a3016055..d00b9012a8f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_temp.c +++ b/drivers/gpu/drm/nouveau/nouveau_temp.c @@ -264,14 +264,14 @@ nouveau_temp_safety_checks(struct drm_device *dev) } 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; @@ -296,7 +296,7 @@ nouveau_temp_probe_i2c(struct drm_device *dev) }; nouveau_i2c_identify(dev, "monitoring device", info, - probe_monitoring_device, NV_I2C_DEFAULT(0)); + probe_monitoring_device, 0x80); //NV_I2C_DEFAULT(0)); } void diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c index c2675623b7c..c9835b9bd63 100644 --- a/drivers/gpu/drm/nouveau/nv04_dfp.c +++ b/drivers/gpu/drm/nouveau/nv04_dfp.c @@ -624,7 +624,7 @@ static void nv04_tmds_slave_init(struct drm_encoder *encoder) { 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", @@ -646,7 +646,7 @@ static void nv04_tmds_slave_init(struct drm_encoder *encoder) 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 = { diff --git a/drivers/gpu/drm/nouveau/nv04_tv.c b/drivers/gpu/drm/nouveau/nv04_tv.c index 3eb605ddfd0..3d7dd22879b 100644 --- a/drivers/gpu/drm/nouveau/nv04_tv.c +++ b/drivers/gpu/drm/nouveau/nv04_tv.c @@ -188,7 +188,7 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry) 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; @@ -221,7 +221,7 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry) /* 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; |