2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-23 04:34:11 +08:00

drm/nouveau/i2c: port to subdev interfaces

v2/v3: Ben Skeggs <bskeggs@redhat.com>
- fix typo in default bus selection
- fix accidental loss of destructor

v4: Dmitry Eremin-Solenikov <dmitry_eremin@mentor.com>
- fix typo causing incorrect default i2c port settings when no BMP data

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
Ben Skeggs 2012-07-10 14:36:38 +10:00
parent e0996aea4c
commit 4196faa862
29 changed files with 1405 additions and 613 deletions

View File

@ -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

View File

@ -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");
}

View File

@ -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

View File

@ -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;
extern const struct i2c_algorithm nouveau_dp_i2c_algo;
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;
};
#endif /* __NOUVEAU_I2C_H__ */
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_i2c_bit_algo;
extern const struct i2c_algorithm nouveau_i2c_aux_algo;
#endif

View File

@ -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

View File

@ -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;
}

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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
};

View File

@ -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,331 +22,46 @@
* 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)
{
struct nouveau_i2c_chan *port = data;
if (port->type == 0) {
u8 val = NVReadVgaCrtc(port->dev, 0, port->drive);
if (state) val |= 0x20;
else val &= 0xdf;
NVWriteVgaCrtc(port->dev, 0, port->drive, val | 0x01);
} else
if (port->type == 4) {
nv_mask(port->dev, port->drive, 0x2f, state ? 0x21 : 0x01);
} else
if (port->type == 5) {
if (state) port->state |= 0x01;
else port->state &= 0xfe;
nv_wr32(port->dev, port->drive, 4 | port->state);
}
}
u8 val;
struct i2c_msg msgs[] = {
{ .addr = addr, .flags = 0, .len = 1, .buf = &reg },
{ .addr = addr, .flags = I2C_M_RD, .len = 1, .buf = &val },
};
static void
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);
if (state) val |= 0x10;
else val &= 0xef;
NVWriteVgaCrtc(port->dev, 0, port->drive, val | 0x01);
} else
if (port->type == 4) {
nv_mask(port->dev, port->drive, 0x1f, state ? 0x11 : 0x01);
} else
if (port->type == 5) {
if (state) port->state |= 0x02;
else port->state &= 0xfd;
nv_wr32(port->dev, port->drive, 4 | port->state);
}
}
int ret = i2c_transfer(&port->adapter, msgs, 2);
if (ret != 2)
return -EIO;
static int
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);
} else
if (port->type == 4) {
return !!(nv_rd32(port->dev, port->sense) & 0x00040000);
} else
if (port->type == 5) {
if (dev_priv->card_type < NV_D0)
return !!(nv_rd32(port->dev, port->sense) & 0x01);
else
return !!(nv_rd32(port->dev, port->sense) & 0x10);
}
return 0;
}
static int
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);
} else
if (port->type == 4) {
return !!(nv_rd32(port->dev, port->sense) & 0x00080000);
} else
if (port->type == 5) {
if (dev_priv->card_type < NV_D0)
return !!(nv_rd32(port->dev, port->sense) & 0x02);
else
return !!(nv_rd32(port->dev, port->sense) & 0x20);
}
return 0;
}
static const uint32_t 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;
return val;
}
int
nouveau_i2c_init(struct drm_device *dev)
nv_wri2cr(struct nouveau_i2c_port *port, u8 addr, u8 reg, u8 val)
{
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;
struct i2c_msg msgs[] = {
{ .addr = addr, .flags = 0, .len = 1, .buf = &reg },
{ .addr = addr, .flags = 0, .len = 1, .buf = &val },
};
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;
}
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;
}
switch (port->type) {
case 0: /* NV04:NV50 */
port->drive = entry[0];
port->sense = entry[1];
break;
case 4: /* NV4E */
port->drive = 0x600800 + entry[1];
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))
break;
port->drive = nv50_i2c_port[port->drive];
port->sense = port->drive;
} else {
port->drive = 0x00d014 + (port->drive * 0x20);
port->sense = port->drive;
}
break;
case 6: /* NV50- DP AUX */
port->drive = entry[0] & 0x0f;
port->sense = port->drive;
port->adapter.algo = &nouveau_dp_i2c_algo;
break;
default:
break;
}
if (!port->adapter.algo && !port->drive) {
NV_ERROR(dev, "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);
port->adapter.owner = THIS_MODULE;
port->adapter.dev.parent = &dev->pdev->dev;
port->dev = dev;
port->index = i;
port->dcb = ROM32(entry[0]);
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);
} else {
port->adapter.algo = &nouveau_dp_i2c_algo;
ret = i2c_add_adapter(&port->adapter);
}
if (ret) {
NV_ERROR(dev, "I2C%d: failed register: %d\n", i, ret);
kfree(port);
continue;
}
list_add_tail(&port->head, &dev_priv->i2c_ports);
}
int ret = i2c_transfer(&port->adapter, msgs, 2);
if (ret != 2)
return -EIO;
return 0;
}
void
nouveau_i2c_fini(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_i2c_chan *port, *tmp;
list_for_each_entry_safe(port, tmp, &dev_priv->i2c_ports, head) {
i2c_del_adapter(&port->adapter);
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;
}
bool
nouveau_probe_i2c_addr(struct nouveau_i2c_chan *i2c, int addr)
nv_probe_i2c(struct nouveau_i2c_port *port, u8 addr)
{
uint8_t buf[] = { 0 };
u8 buf[] = { 0 };
struct i2c_msg msgs[] = {
{
.addr = addr,
@ -362,33 +77,331 @@ nouveau_probe_i2c_addr(struct nouveau_i2c_chan *i2c, int addr)
}
};
return i2c_transfer(&i2c->adapter, msgs, 2) == 2;
return i2c_transfer(&port->adapter, msgs, 2) == 2;
}
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 struct nouveau_i2c_port *
nouveau_i2c_find(struct nouveau_i2c *i2c, u8 index)
{
struct nouveau_i2c_chan *i2c = nouveau_i2c_find(dev, 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_port *port = nouveau_i2c_find(i2c, index);
int i;
if (!i2c) {
NV_DEBUG(dev, "No bus when probing %s on %d\n", what, index);
if (!port) {
nv_debug(i2c, "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);
nv_debug(i2c, "probing %ss on bus: %d\n", what, port->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);
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(dev, "No devices found.\n");
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;
nv_wrvgac(port->i2c, 0, port->drive, val | 0x01);
} else
if (port->type == DCB_I2C_NV4E_BIT) {
nv_mask(port->i2c, port->drive, 0x2f, state ? 0x21 : 0x01);
} else
if (port->type == DCB_I2C_NVIO_BIT) {
if (state) port->state |= 0x01;
else port->state &= 0xfe;
nv_wr32(port->i2c, port->drive, 4 | port->state);
}
}
void
nouveau_i2c_drive_sda(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 |= 0x10;
else val &= 0xef;
nv_wrvgac(port->i2c, 0, port->drive, val | 0x01);
} else
if (port->type == DCB_I2C_NV4E_BIT) {
nv_mask(port->i2c, port->drive, 0x1f, state ? 0x11 : 0x01);
} else
if (port->type == DCB_I2C_NVIO_BIT) {
if (state) port->state |= 0x02;
else port->state &= 0xfd;
nv_wr32(port->i2c, port->drive, 4 | port->state);
}
}
int
nouveau_i2c_sense_scl(void *data)
{
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 == DCB_I2C_NV4E_BIT) {
return !!(nv_rd32(port->i2c, port->sense) & 0x00040000);
} else
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->i2c, port->sense) & 0x10);
}
return 0;
}
int
nouveau_i2c_sense_sda(void *data)
{
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 == DCB_I2C_NV4E_BIT) {
return !!(nv_rd32(port->i2c, port->sense) & 0x00080000);
} else
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->i2c, port->sense) & 0x20);
}
return 0;
}
static const u32 nv50_i2c_port[] = {
0x00e138, 0x00e150, 0x00e168, 0x00e180,
0x00e254, 0x00e274, 0x00e764, 0x00e780,
0x00e79c, 0x00e7b8
};
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 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;
port = kzalloc(sizeof(*port), GFP_KERNEL);
if (!port) {
nv_error(i2c, "failed port memory alloc at %d\n", i);
break;
}
port->type = info.type;
switch (port->type) {
case DCB_I2C_NV04_BIT:
port->drive = info.drive;
port->sense = info.sense;
break;
case DCB_I2C_NV4E_BIT:
port->drive = 0x600800 + info.drive;
port->sense = port->drive;
break;
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;
} else {
port->drive = 0x00d014 + (port->drive * 0x20);
port->sense = port->drive;
}
break;
case DCB_I2C_NVIO_AUX:
port->drive = info.drive & 0x0f;
port->sense = port->drive;
port->adapter.algo = &nouveau_i2c_aux_algo;
break;
default:
break;
}
if (!port->adapter.algo && !port->drive) {
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", device->name, i);
port->adapter.owner = THIS_MODULE;
port->adapter.dev.parent = &device->pdev->dev;
port->i2c = i2c;
port->index = i;
port->dcb = info.data;
i2c_set_adapdata(&port->adapter, i2c);
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_i2c_aux_algo;
ret = i2c_add_adapter(&port->adapter);
}
if (ret) {
nv_error(i2c, "I2C%d: failed register: %d\n", i, ret);
kfree(port);
continue;
}
list_add_tail(&port->head, &i2c->ports);
}
return 0;
}
static void
nouveau_i2c_dtor(struct nouveau_object *object)
{
struct nouveau_i2c *i2c = (void *)object;
struct nouveau_i2c_port *port, *temp;
list_for_each_entry_safe(port, temp, &i2c->ports, head) {
i2c_del_adapter(&port->adapter);
list_del(&port->head);
kfree(port);
}
nouveau_subdev_destroy(&i2c->base);
}
static int
nouveau_i2c_init(struct nouveau_object *object)
{
struct nouveau_i2c *i2c = (void *)object;
return nouveau_subdev_init(&i2c->base);
}
static int
nouveau_i2c_fini(struct nouveau_object *object, bool suspend)
{
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,
},
};

View File

@ -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
};

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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) {

View File

@ -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,

View File

@ -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
};

View File

@ -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 *);

View File

@ -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;

View File

@ -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

View File

@ -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 = {

View File

@ -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;