diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/subdev/i2c')
34 files changed, 2165 insertions, 1958 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild index d68307409980..1f730613c237 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild @@ -1,16 +1,30 @@  nvkm-y += nvkm/subdev/i2c/base.o -nvkm-y += nvkm/subdev/i2c/anx9805.o -nvkm-y += nvkm/subdev/i2c/aux.o -nvkm-y += nvkm/subdev/i2c/bit.o -nvkm-y += nvkm/subdev/i2c/pad.o -nvkm-y += nvkm/subdev/i2c/padnv04.o -nvkm-y += nvkm/subdev/i2c/padg94.o -nvkm-y += nvkm/subdev/i2c/padgm204.o  nvkm-y += nvkm/subdev/i2c/nv04.o  nvkm-y += nvkm/subdev/i2c/nv4e.o  nvkm-y += nvkm/subdev/i2c/nv50.o  nvkm-y += nvkm/subdev/i2c/g94.o -nvkm-y += nvkm/subdev/i2c/gf110.o  nvkm-y += nvkm/subdev/i2c/gf117.o +nvkm-y += nvkm/subdev/i2c/gf119.o  nvkm-y += nvkm/subdev/i2c/gk104.o  nvkm-y += nvkm/subdev/i2c/gm204.o + +nvkm-y += nvkm/subdev/i2c/pad.o +nvkm-y += nvkm/subdev/i2c/padnv04.o +nvkm-y += nvkm/subdev/i2c/padnv4e.o +nvkm-y += nvkm/subdev/i2c/padnv50.o +nvkm-y += nvkm/subdev/i2c/padg94.o +nvkm-y += nvkm/subdev/i2c/padgf119.o +nvkm-y += nvkm/subdev/i2c/padgm204.o + +nvkm-y += nvkm/subdev/i2c/bus.o +nvkm-y += nvkm/subdev/i2c/busnv04.o +nvkm-y += nvkm/subdev/i2c/busnv4e.o +nvkm-y += nvkm/subdev/i2c/busnv50.o +nvkm-y += nvkm/subdev/i2c/busgf119.o +nvkm-y += nvkm/subdev/i2c/bit.o + +nvkm-y += nvkm/subdev/i2c/aux.o +nvkm-y += nvkm/subdev/i2c/auxg94.o +nvkm-y += nvkm/subdev/i2c/auxgm204.o + +nvkm-y += nvkm/subdev/i2c/anx9805.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c index d17dd1cf3c34..b7b01c3f7037 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c @@ -21,272 +21,258 @@   *   * Authors: Ben Skeggs <[email protected]>   */ -#include "port.h" +#define anx9805_pad(p) container_of((p), struct anx9805_pad, base) +#define anx9805_bus(p) container_of((p), struct anx9805_bus, base) +#define anx9805_aux(p) container_of((p), struct anx9805_aux, base) +#include "aux.h" +#include "bus.h" + +struct anx9805_pad { +	struct nvkm_i2c_pad base; +	struct nvkm_i2c_bus *bus; +	u8 addr; +}; -struct anx9805_i2c_port { -	struct nvkm_i2c_port base; -	u32 addr; -	u32 ctrl; +struct anx9805_bus { +	struct nvkm_i2c_bus base; +	struct anx9805_pad *pad; +	u8 addr;  };  static int -anx9805_train(struct nvkm_i2c_port *port, int link_nr, int link_bw, bool enh) +anx9805_bus_xfer(struct nvkm_i2c_bus *base, struct i2c_msg *msgs, int num)  { -	struct anx9805_i2c_port *chan = (void *)port; -	struct nvkm_i2c_port *mast = (void *)nv_object(chan)->parent; -	u8 tmp, i; - -	DBG("ANX9805 train %d 0x%02x %d\n", link_nr, link_bw, enh); +	struct anx9805_bus *bus = anx9805_bus(base); +	struct anx9805_pad *pad = bus->pad; +	struct i2c_adapter *adap = &pad->bus->i2c; +	struct i2c_msg *msg = msgs; +	int ret = -ETIMEDOUT; +	int i, j, cnt = num; +	u8 seg = 0x00, off = 0x00, tmp; -	nv_wri2cr(mast, chan->addr, 0xa0, link_bw); -	nv_wri2cr(mast, chan->addr, 0xa1, link_nr | (enh ? 0x80 : 0x00)); -	nv_wri2cr(mast, chan->addr, 0xa2, 0x01); -	nv_wri2cr(mast, chan->addr, 0xa8, 0x01); +	tmp = nvkm_rdi2cr(adap, pad->addr, 0x07) & ~0x10; +	nvkm_wri2cr(adap, pad->addr, 0x07, tmp | 0x10); +	nvkm_wri2cr(adap, pad->addr, 0x07, tmp); +	nvkm_wri2cr(adap, bus->addr, 0x43, 0x05); +	mdelay(5); -	i = 0; -	while ((tmp = nv_rdi2cr(mast, chan->addr, 0xa8)) & 0x01) { -		mdelay(5); -		if (i++ == 100) { -			nv_error(port, "link training timed out\n"); -			return -ETIMEDOUT; +	while (cnt--) { +		if ( (msg->flags & I2C_M_RD) && msg->addr == 0x50) { +			nvkm_wri2cr(adap, bus->addr, 0x40, msg->addr << 1); +			nvkm_wri2cr(adap, bus->addr, 0x41, seg); +			nvkm_wri2cr(adap, bus->addr, 0x42, off); +			nvkm_wri2cr(adap, bus->addr, 0x44, msg->len); +			nvkm_wri2cr(adap, bus->addr, 0x45, 0x00); +			nvkm_wri2cr(adap, bus->addr, 0x43, 0x01); +			for (i = 0; i < msg->len; i++) { +				j = 0; +				while (nvkm_rdi2cr(adap, bus->addr, 0x46) & 0x10) { +					mdelay(5); +					if (j++ == 32) +						goto done; +				} +				msg->buf[i] = nvkm_rdi2cr(adap, bus->addr, 0x47); +			} +		} else +		if (!(msg->flags & I2C_M_RD)) { +			if (msg->addr == 0x50 && msg->len == 0x01) { +				off = msg->buf[0]; +			} else +			if (msg->addr == 0x30 && msg->len == 0x01) { +				seg = msg->buf[0]; +			} else +				goto done; +		} else { +			goto done;  		} +		msg++;  	} -	if (tmp & 0x70) { -		nv_error(port, "link training failed: 0x%02x\n", tmp); -		return -EIO; +	ret = num; +done: +	nvkm_wri2cr(adap, bus->addr, 0x43, 0x00); +	return ret; +} + +static const struct nvkm_i2c_bus_func +anx9805_bus_func = { +	.xfer = anx9805_bus_xfer, +}; + +static int +anx9805_bus_new(struct nvkm_i2c_pad *base, int id, u8 drive, +		struct nvkm_i2c_bus **pbus) +{ +	struct anx9805_pad *pad = anx9805_pad(base); +	struct anx9805_bus *bus; +	int ret; + +	if (!(bus = kzalloc(sizeof(*bus), GFP_KERNEL))) +		return -ENOMEM; +	*pbus = &bus->base; +	bus->pad = pad; + +	ret = nvkm_i2c_bus_ctor(&anx9805_bus_func, &pad->base, id, &bus->base); +	if (ret) +		return ret; + +	switch (pad->addr) { +	case 0x39: bus->addr = 0x3d; break; +	case 0x3b: bus->addr = 0x3f; break; +	default: +		return -ENOSYS;  	} -	return 1; +	return 0;  } +struct anx9805_aux { +	struct nvkm_i2c_aux base; +	struct anx9805_pad *pad; +	u8 addr; +}; +  static int -anx9805_aux(struct nvkm_i2c_port *port, bool retry, -	    u8 type, u32 addr, u8 *data, u8 size) +anx9805_aux_xfer(struct nvkm_i2c_aux *base, bool retry, +		 u8 type, u32 addr, u8 *data, u8 size)  { -	struct anx9805_i2c_port *chan = (void *)port; -	struct nvkm_i2c_port *mast = (void *)nv_object(chan)->parent; +	struct anx9805_aux *aux = anx9805_aux(base); +	struct anx9805_pad *pad = aux->pad; +	struct i2c_adapter *adap = &pad->bus->i2c;  	int i, ret = -ETIMEDOUT;  	u8 buf[16] = {};  	u8 tmp; -	DBG("%02x %05x %d\n", type, addr, size); +	AUX_DBG(&aux->base, "%02x %05x %d", type, addr, size); -	tmp = nv_rdi2cr(mast, chan->ctrl, 0x07) & ~0x04; -	nv_wri2cr(mast, chan->ctrl, 0x07, tmp | 0x04); -	nv_wri2cr(mast, chan->ctrl, 0x07, tmp); -	nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01); +	tmp = nvkm_rdi2cr(adap, pad->addr, 0x07) & ~0x04; +	nvkm_wri2cr(adap, pad->addr, 0x07, tmp | 0x04); +	nvkm_wri2cr(adap, pad->addr, 0x07, tmp); +	nvkm_wri2cr(adap, pad->addr, 0xf7, 0x01); -	nv_wri2cr(mast, chan->addr, 0xe4, 0x80); +	nvkm_wri2cr(adap, aux->addr, 0xe4, 0x80);  	if (!(type & 1)) {  		memcpy(buf, data, size); -		DBG("%16ph", buf); +		AUX_DBG(&aux->base, "%16ph", buf);  		for (i = 0; i < size; i++) -			nv_wri2cr(mast, chan->addr, 0xf0 + i, buf[i]); +			nvkm_wri2cr(adap, aux->addr, 0xf0 + i, buf[i]);  	} -	nv_wri2cr(mast, chan->addr, 0xe5, ((size - 1) << 4) | type); -	nv_wri2cr(mast, chan->addr, 0xe6, (addr & 0x000ff) >>  0); -	nv_wri2cr(mast, chan->addr, 0xe7, (addr & 0x0ff00) >>  8); -	nv_wri2cr(mast, chan->addr, 0xe8, (addr & 0xf0000) >> 16); -	nv_wri2cr(mast, chan->addr, 0xe9, 0x01); +	nvkm_wri2cr(adap, aux->addr, 0xe5, ((size - 1) << 4) | type); +	nvkm_wri2cr(adap, aux->addr, 0xe6, (addr & 0x000ff) >>  0); +	nvkm_wri2cr(adap, aux->addr, 0xe7, (addr & 0x0ff00) >>  8); +	nvkm_wri2cr(adap, aux->addr, 0xe8, (addr & 0xf0000) >> 16); +	nvkm_wri2cr(adap, aux->addr, 0xe9, 0x01);  	i = 0; -	while ((tmp = nv_rdi2cr(mast, chan->addr, 0xe9)) & 0x01) { +	while ((tmp = nvkm_rdi2cr(adap, aux->addr, 0xe9)) & 0x01) {  		mdelay(5);  		if (i++ == 32)  			goto done;  	} -	if ((tmp = nv_rdi2cr(mast, chan->ctrl, 0xf7)) & 0x01) { +	if ((tmp = nvkm_rdi2cr(adap, pad->addr, 0xf7)) & 0x01) {  		ret = -EIO;  		goto done;  	}  	if (type & 1) {  		for (i = 0; i < size; i++) -			buf[i] = nv_rdi2cr(mast, chan->addr, 0xf0 + i); -		DBG("%16ph", buf); +			buf[i] = nvkm_rdi2cr(adap, aux->addr, 0xf0 + i); +		AUX_DBG(&aux->base, "%16ph", buf);  		memcpy(data, buf, size);  	}  	ret = 0;  done: -	nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01); +	nvkm_wri2cr(adap, pad->addr, 0xf7, 0x01);  	return ret;  } -static const struct nvkm_i2c_func -anx9805_aux_func = { -	.aux = anx9805_aux, -	.lnk_ctl = anx9805_train, -}; -  static int -anx9805_aux_chan_ctor(struct nvkm_object *parent, -		      struct nvkm_object *engine, -		      struct nvkm_oclass *oclass, void *data, u32 index, -		      struct nvkm_object **pobject) +anx9805_aux_lnk_ctl(struct nvkm_i2c_aux *base, +		    int link_nr, int link_bw, bool enh)  { -	struct nvkm_i2c_port *mast = (void *)parent; -	struct anx9805_i2c_port *chan; -	int ret; - -	ret = nvkm_i2c_port_create(parent, engine, oclass, index, -				   &nvkm_i2c_aux_algo, &anx9805_aux_func, -				   &chan); -	*pobject = nv_object(chan); -	if (ret) -		return ret; - -	switch ((oclass->handle & 0xff00) >> 8) { -	case 0x0d: -		chan->addr = 0x38; -		chan->ctrl = 0x39; -		break; -	case 0x0e: -		chan->addr = 0x3c; -		chan->ctrl = 0x3b; -		break; -	default: -		BUG_ON(1); -	} - -	if (mast->adapter.algo == &i2c_bit_algo) { -		struct i2c_algo_bit_data *algo = mast->adapter.algo_data; -		algo->udelay = max(algo->udelay, 40); -	} - -	return 0; -} - -static struct nvkm_ofuncs -anx9805_aux_ofuncs = { -	.ctor =  anx9805_aux_chan_ctor, -	.dtor = _nvkm_i2c_port_dtor, -	.init = _nvkm_i2c_port_init, -	.fini = _nvkm_i2c_port_fini, -}; +	struct anx9805_aux *aux = anx9805_aux(base); +	struct anx9805_pad *pad = aux->pad; +	struct i2c_adapter *adap = &pad->bus->i2c; +	u8 tmp, i; -static int -anx9805_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) -{ -	struct anx9805_i2c_port *port = adap->algo_data; -	struct nvkm_i2c_port *mast = (void *)nv_object(port)->parent; -	struct i2c_msg *msg = msgs; -	int ret = -ETIMEDOUT; -	int i, j, cnt = num; -	u8 seg = 0x00, off = 0x00, tmp; +	AUX_DBG(&aux->base, "ANX9805 train %d %02x %d", +		link_nr, link_bw, enh); -	tmp = nv_rdi2cr(mast, port->ctrl, 0x07) & ~0x10; -	nv_wri2cr(mast, port->ctrl, 0x07, tmp | 0x10); -	nv_wri2cr(mast, port->ctrl, 0x07, tmp); -	nv_wri2cr(mast, port->addr, 0x43, 0x05); -	mdelay(5); +	nvkm_wri2cr(adap, aux->addr, 0xa0, link_bw); +	nvkm_wri2cr(adap, aux->addr, 0xa1, link_nr | (enh ? 0x80 : 0x00)); +	nvkm_wri2cr(adap, aux->addr, 0xa2, 0x01); +	nvkm_wri2cr(adap, aux->addr, 0xa8, 0x01); -	while (cnt--) { -		if ( (msg->flags & I2C_M_RD) && msg->addr == 0x50) { -			nv_wri2cr(mast, port->addr, 0x40, msg->addr << 1); -			nv_wri2cr(mast, port->addr, 0x41, seg); -			nv_wri2cr(mast, port->addr, 0x42, off); -			nv_wri2cr(mast, port->addr, 0x44, msg->len); -			nv_wri2cr(mast, port->addr, 0x45, 0x00); -			nv_wri2cr(mast, port->addr, 0x43, 0x01); -			for (i = 0; i < msg->len; i++) { -				j = 0; -				while (nv_rdi2cr(mast, port->addr, 0x46) & 0x10) { -					mdelay(5); -					if (j++ == 32) -						goto done; -				} -				msg->buf[i] = nv_rdi2cr(mast, port->addr, 0x47); -			} -		} else -		if (!(msg->flags & I2C_M_RD)) { -			if (msg->addr == 0x50 && msg->len == 0x01) { -				off = msg->buf[0]; -			} else -			if (msg->addr == 0x30 && msg->len == 0x01) { -				seg = msg->buf[0]; -			} else -				goto done; -		} else { -			goto done; +	i = 0; +	while ((tmp = nvkm_rdi2cr(adap, aux->addr, 0xa8)) & 0x01) { +		mdelay(5); +		if (i++ == 100) { +			AUX_ERR(&aux->base, "link training timeout"); +			return -ETIMEDOUT;  		} -		msg++;  	} -	ret = num; -done: -	nv_wri2cr(mast, port->addr, 0x43, 0x00); -	return ret; -} +	if (tmp & 0x70) { +		AUX_ERR(&aux->base, "link training failed"); +		return -EIO; +	} -static u32 -anx9805_func(struct i2c_adapter *adap) -{ -	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +	return 0;  } -static const struct i2c_algorithm -anx9805_i2c_algo = { -	.master_xfer = anx9805_xfer, -	.functionality = anx9805_func -}; - -static const struct nvkm_i2c_func -anx9805_i2c_func = { +static const struct nvkm_i2c_aux_func +anx9805_aux_func = { +	.xfer = anx9805_aux_xfer, +	.lnk_ctl = anx9805_aux_lnk_ctl,  };  static int -anx9805_ddc_port_ctor(struct nvkm_object *parent, -		      struct nvkm_object *engine, -		      struct nvkm_oclass *oclass, void *data, u32 index, -		      struct nvkm_object **pobject) +anx9805_aux_new(struct nvkm_i2c_pad *base, int id, u8 drive, +		struct nvkm_i2c_aux **pbus)  { -	struct nvkm_i2c_port *mast = (void *)parent; -	struct anx9805_i2c_port *port; +	struct anx9805_pad *pad = anx9805_pad(base); +	struct anx9805_aux *aux;  	int ret; -	ret = nvkm_i2c_port_create(parent, engine, oclass, index, -				   &anx9805_i2c_algo, &anx9805_i2c_func, &port); -	*pobject = nv_object(port); +	if (!(aux = kzalloc(sizeof(*aux), GFP_KERNEL))) +		return -ENOMEM; +	*pbus = &aux->base; +	aux->pad = pad; + +	ret = nvkm_i2c_aux_ctor(&anx9805_aux_func, &pad->base, id, &aux->base);  	if (ret)  		return ret; -	switch ((oclass->handle & 0xff00) >> 8) { -	case 0x0d: -		port->addr = 0x3d; -		port->ctrl = 0x39; -		break; -	case 0x0e: -		port->addr = 0x3f; -		port->ctrl = 0x3b; -		break; +	switch (pad->addr) { +	case 0x39: aux->addr = 0x38; break; +	case 0x3b: aux->addr = 0x3c; break;  	default: -		BUG_ON(1); -	} - -	if (mast->adapter.algo == &i2c_bit_algo) { -		struct i2c_algo_bit_data *algo = mast->adapter.algo_data; -		algo->udelay = max(algo->udelay, 40); +		return -ENOSYS;  	}  	return 0;  } -static struct nvkm_ofuncs -anx9805_ddc_ofuncs = { -	.ctor =  anx9805_ddc_port_ctor, -	.dtor = _nvkm_i2c_port_dtor, -	.init = _nvkm_i2c_port_init, -	.fini = _nvkm_i2c_port_fini, +static const struct nvkm_i2c_pad_func +anx9805_pad_func = { +	.bus_new_4 = anx9805_bus_new, +	.aux_new_6 = anx9805_aux_new,  }; -struct nvkm_oclass -nvkm_anx9805_sclass[] = { -	{ .handle = NV_I2C_TYPE_EXTDDC(0x0d), .ofuncs = &anx9805_ddc_ofuncs }, -	{ .handle = NV_I2C_TYPE_EXTAUX(0x0d), .ofuncs = &anx9805_aux_ofuncs }, -	{ .handle = NV_I2C_TYPE_EXTDDC(0x0e), .ofuncs = &anx9805_ddc_ofuncs }, -	{ .handle = NV_I2C_TYPE_EXTAUX(0x0e), .ofuncs = &anx9805_aux_ofuncs }, -	{} -}; +int +anx9805_pad_new(struct nvkm_i2c_bus *bus, int id, u8 addr, +		struct nvkm_i2c_pad **ppad) +{ +	struct anx9805_pad *pad; + +	if (!(pad = kzalloc(sizeof(*pad), GFP_KERNEL))) +		return -ENOMEM; +	*ppad = &pad->base; + +	nvkm_i2c_pad_ctor(&anx9805_pad_func, bus->pad->i2c, id, &pad->base); +	pad->bus = bus; +	pad->addr = addr; +	return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c index 1c18860f80d1..f0851d57df2f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c @@ -21,50 +21,17 @@   *   * Authors: Ben Skeggs   */ -#include "priv.h" - -int -nv_rdaux(struct nvkm_i2c_port *port, u32 addr, u8 *data, u8 size) -{ -	struct nvkm_i2c *i2c = nvkm_i2c(port); -	if (port->func->aux) { -		int ret = i2c->acquire(port, 0); -		if (ret == 0) { -			ret = port->func->aux(port, true, 9, addr, data, size); -			i2c->release(port); -		} -		return ret; -	} -	return -ENODEV; -} - -int -nv_wraux(struct nvkm_i2c_port *port, u32 addr, u8 *data, u8 size) -{ -	struct nvkm_i2c *i2c = nvkm_i2c(port); -	if (port->func->aux) { -		int ret = i2c->acquire(port, 0); -		if (ret == 0) { -			ret = port->func->aux(port, true, 8, addr, data, size); -			i2c->release(port); -		} -		return ret; -	} -	return -ENODEV; -} +#include "aux.h" +#include "pad.h"  static int -aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +nvkm_i2c_aux_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)  { -	struct nvkm_i2c_port *port = adap->algo_data; -	struct nvkm_i2c *i2c = nvkm_i2c(port); +	struct nvkm_i2c_aux *aux = container_of(adap, typeof(*aux), i2c);  	struct i2c_msg *msg = msgs;  	int ret, mcnt = num; -	if (!port->func->aux) -		return -ENODEV; - -	ret = i2c->acquire(port, 0); +	ret = nvkm_i2c_aux_acquire(aux);  	if (ret)  		return ret; @@ -84,9 +51,9 @@ aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)  			if (mcnt || remaining > 16)  				cmd |= 4; /* MOT */ -			ret = port->func->aux(port, true, cmd, msg->addr, ptr, cnt); +			ret = aux->func->xfer(aux, true, cmd, msg->addr, ptr, cnt);  			if (ret < 0) { -				i2c->release(port); +				nvkm_i2c_aux_release(aux);  				return ret;  			} @@ -97,17 +64,111 @@ aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)  		msg++;  	} -	i2c->release(port); +	nvkm_i2c_aux_release(aux);  	return num;  }  static u32 -aux_func(struct i2c_adapter *adap) +nvkm_i2c_aux_i2c_func(struct i2c_adapter *adap)  {  	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;  } -const struct i2c_algorithm nvkm_i2c_aux_algo = { -	.master_xfer = aux_xfer, -	.functionality = aux_func +const struct i2c_algorithm +nvkm_i2c_aux_i2c_algo = { +	.master_xfer = nvkm_i2c_aux_i2c_xfer, +	.functionality = nvkm_i2c_aux_i2c_func  }; + +void +nvkm_i2c_aux_monitor(struct nvkm_i2c_aux *aux, bool monitor) +{ +	struct nvkm_i2c_pad *pad = aux->pad; +	AUX_TRACE(aux, "monitor: %s", monitor ? "yes" : "no"); +	if (monitor) +		nvkm_i2c_pad_mode(pad, NVKM_I2C_PAD_AUX); +	else +		nvkm_i2c_pad_mode(pad, NVKM_I2C_PAD_OFF); +} + +void +nvkm_i2c_aux_release(struct nvkm_i2c_aux *aux) +{ +	struct nvkm_i2c_pad *pad = aux->pad; +	AUX_TRACE(aux, "release"); +	nvkm_i2c_pad_release(pad); +	mutex_unlock(&aux->mutex); +} + +int +nvkm_i2c_aux_acquire(struct nvkm_i2c_aux *aux) +{ +	struct nvkm_i2c_pad *pad = aux->pad; +	int ret; +	AUX_TRACE(aux, "acquire"); +	mutex_lock(&aux->mutex); +	ret = nvkm_i2c_pad_acquire(pad, NVKM_I2C_PAD_AUX); +	if (ret) +		mutex_unlock(&aux->mutex); +	return ret; +} + +int +nvkm_i2c_aux_xfer(struct nvkm_i2c_aux *aux, bool retry, u8 type, +		  u32 addr, u8 *data, u8 size) +{ +	return aux->func->xfer(aux, retry, type, addr, data, size); +} + +int +nvkm_i2c_aux_lnk_ctl(struct nvkm_i2c_aux *aux, int nr, int bw, bool ef) +{ +	if (aux->func->lnk_ctl) +		return aux->func->lnk_ctl(aux, nr, bw, ef); +	return -ENODEV; +} + +void +nvkm_i2c_aux_del(struct nvkm_i2c_aux **paux) +{ +	struct nvkm_i2c_aux *aux = *paux; +	if (aux && !WARN_ON(!aux->func)) { +		AUX_TRACE(aux, "dtor"); +		list_del(&aux->head); +		i2c_del_adapter(&aux->i2c); +		kfree(*paux); +		*paux = NULL; +	} +} + +int +nvkm_i2c_aux_ctor(const struct nvkm_i2c_aux_func *func, +		  struct nvkm_i2c_pad *pad, int id, +		  struct nvkm_i2c_aux *aux) +{ +	struct nvkm_device *device = pad->i2c->subdev.device; + +	aux->func = func; +	aux->pad = pad; +	aux->id = id; +	mutex_init(&aux->mutex); +	list_add_tail(&aux->head, &pad->i2c->aux); +	AUX_TRACE(aux, "ctor"); + +	snprintf(aux->i2c.name, sizeof(aux->i2c.name), "nvkm-%s-aux-%04x", +		 dev_name(device->dev), id); +	aux->i2c.owner = THIS_MODULE; +	aux->i2c.dev.parent = device->dev; +	aux->i2c.algo = &nvkm_i2c_aux_i2c_algo; +	return i2c_add_adapter(&aux->i2c); +} + +int +nvkm_i2c_aux_new_(const struct nvkm_i2c_aux_func *func, +		  struct nvkm_i2c_pad *pad, int id, +		  struct nvkm_i2c_aux **paux) +{ +	if (!(*paux = kzalloc(sizeof(**paux), GFP_KERNEL))) +		return -ENOMEM; +	return nvkm_i2c_aux_ctor(func, pad, id, *paux); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h new file mode 100644 index 000000000000..35a892e4a4c3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h @@ -0,0 +1,30 @@ +#ifndef __NVKM_I2C_AUX_H__ +#define __NVKM_I2C_AUX_H__ +#include "pad.h" + +struct nvkm_i2c_aux_func { +	int  (*xfer)(struct nvkm_i2c_aux *, bool retry, u8 type, +		     u32 addr, u8 *data, u8 size); +	int  (*lnk_ctl)(struct nvkm_i2c_aux *, int link_nr, int link_bw, +			bool enhanced_framing); +}; + +int nvkm_i2c_aux_ctor(const struct nvkm_i2c_aux_func *, struct nvkm_i2c_pad *, +		      int id, struct nvkm_i2c_aux *); +int nvkm_i2c_aux_new_(const struct nvkm_i2c_aux_func *, struct nvkm_i2c_pad *, +		      int id, struct nvkm_i2c_aux **); +void nvkm_i2c_aux_del(struct nvkm_i2c_aux **); +int nvkm_i2c_aux_xfer(struct nvkm_i2c_aux *, bool retry, u8 type, +		      u32 addr, u8 *data, u8 size); + +int g94_i2c_aux_new(struct nvkm_i2c_pad *, int, u8, struct nvkm_i2c_aux **); +int gm204_i2c_aux_new(struct nvkm_i2c_pad *, int, u8, struct nvkm_i2c_aux **); + +#define AUX_MSG(b,l,f,a...) do {                                               \ +	struct nvkm_i2c_aux *_aux = (b);                                       \ +	nvkm_##l(&_aux->pad->i2c->subdev, "aux %04x: "f"\n", _aux->id, ##a);   \ +} while(0) +#define AUX_ERR(b,f,a...) AUX_MSG((b), error, f, ##a) +#define AUX_DBG(b,f,a...) AUX_MSG((b), debug, f, ##a) +#define AUX_TRACE(b,f,a...) AUX_MSG((b), trace, f, ##a) +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c new file mode 100644 index 000000000000..954f5b76bfcf --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c @@ -0,0 +1,181 @@ +/* + * Copyright 2015 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 busions 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 <[email protected]> + */ +#define g94_i2c_aux(p) container_of((p), struct g94_i2c_aux, base) +#include "aux.h" + +struct g94_i2c_aux { +	struct nvkm_i2c_aux base; +	int ch; +}; + +static void +g94_i2c_aux_fini(struct g94_i2c_aux *aux) +{ +	struct nvkm_device *device = aux->base.pad->i2c->subdev.device; +	nvkm_mask(device, 0x00e4e4 + (aux->ch * 0x50), 0x00310000, 0x00000000); +} + +static int +g94_i2c_aux_init(struct g94_i2c_aux *aux) +{ +	struct nvkm_device *device = aux->base.pad->i2c->subdev.device; +	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 = nvkm_rd32(device, 0x00e4e4 + (aux->ch * 0x50)); +		udelay(1); +		if (!timeout--) { +			AUX_ERR(&aux->base, "begin idle timeout %08x", ctrl); +			return -EBUSY; +		} +	} while (ctrl & 0x03010000); + +	/* set some magic, and wait up to 1ms for it to appear */ +	nvkm_mask(device, 0x00e4e4 + (aux->ch * 0x50), 0x00300000, ureq); +	timeout = 1000; +	do { +		ctrl = nvkm_rd32(device, 0x00e4e4 + (aux->ch * 0x50)); +		udelay(1); +		if (!timeout--) { +			AUX_ERR(&aux->base, "magic wait %08x", ctrl); +			g94_i2c_aux_fini(aux); +			return -EBUSY; +		} +	} while ((ctrl & 0x03000000) != urep); + +	return 0; +} + +static int +g94_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry, +		 u8 type, u32 addr, u8 *data, u8 size) +{ +	struct g94_i2c_aux *aux = g94_i2c_aux(obj); +	struct nvkm_device *device = aux->base.pad->i2c->subdev.device; +	const u32 base = aux->ch * 0x50; +	u32 ctrl, stat, timeout, retries; +	u32 xbuf[4] = {}; +	int ret, i; + +	AUX_TRACE(&aux->base, "%d: %08x %d", type, addr, size); + +	ret = g94_i2c_aux_init(aux); +	if (ret < 0) +		goto out; + +	stat = nvkm_rd32(device, 0x00e4e8 + base); +	if (!(stat & 0x10000000)) { +		AUX_TRACE(&aux->base, "sink not detected"); +		ret = -ENXIO; +		goto out; +	} + +	if (!(type & 1)) { +		memcpy(xbuf, data, size); +		for (i = 0; i < 16; i += 4) { +			AUX_TRACE(&aux->base, "wr %08x", xbuf[i / 4]); +			nvkm_wr32(device, 0x00e4c0 + base + i, xbuf[i / 4]); +		} +	} + +	ctrl  = nvkm_rd32(device, 0x00e4e4 + base); +	ctrl &= ~0x0001f0ff; +	ctrl |= type << 12; +	ctrl |= size - 1; +	nvkm_wr32(device, 0x00e4e0 + base, addr); + +	/* (maybe) retry transaction a number of times on failure... */ +	for (retries = 0; !ret && retries < 32; retries++) { +		/* reset, and delay a while if this is a retry */ +		nvkm_wr32(device, 0x00e4e4 + base, 0x80000000 | ctrl); +		nvkm_wr32(device, 0x00e4e4 + base, 0x00000000 | ctrl); +		if (retries) +			udelay(400); + +		/* transaction request, wait up to 1ms for it to complete */ +		nvkm_wr32(device, 0x00e4e4 + base, 0x00010000 | ctrl); + +		timeout = 1000; +		do { +			ctrl = nvkm_rd32(device, 0x00e4e4 + base); +			udelay(1); +			if (!timeout--) { +				AUX_ERR(&aux->base, "timeout %08x", ctrl); +				ret = -EIO; +				goto out; +			} +		} while (ctrl & 0x00010000); +		ret = 1; + +		/* read status, and check if transaction completed ok */ +		stat = nvkm_mask(device, 0x00e4e8 + base, 0, 0); +		if ((stat & 0x000f0000) == 0x00080000 || +		    (stat & 0x000f0000) == 0x00020000) +			ret = retry ? 0 : 1; +		if ((stat & 0x00000100)) +			ret = -ETIMEDOUT; +		if ((stat & 0x00000e00)) +			ret = -EIO; + +		AUX_TRACE(&aux->base, "%02d %08x %08x", retries, ctrl, stat); +	} + +	if (type & 1) { +		for (i = 0; i < 16; i += 4) { +			xbuf[i / 4] = nvkm_rd32(device, 0x00e4d0 + base + i); +			AUX_TRACE(&aux->base, "rd %08x", xbuf[i / 4]); +		} +		memcpy(data, xbuf, size); +	} + +out: +	g94_i2c_aux_fini(aux); +	return ret < 0 ? ret : (stat & 0x000f0000) >> 16; +} + +static const struct nvkm_i2c_aux_func +g94_i2c_aux_func = { +	.xfer = g94_i2c_aux_xfer, +}; + +int +g94_i2c_aux_new(struct nvkm_i2c_pad *pad, int index, u8 drive, +		struct nvkm_i2c_aux **paux) +{ +	struct g94_i2c_aux *aux; + +	if (!(aux = kzalloc(sizeof(*aux), GFP_KERNEL))) +		return -ENOMEM; +	*paux = &aux->base; + +	nvkm_i2c_aux_ctor(&g94_i2c_aux_func, pad, index, &aux->base); +	aux->ch = drive; +	aux->base.intr = 1 << aux->ch; +	return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm204.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm204.c new file mode 100644 index 000000000000..bed231b56dbd --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm204.c @@ -0,0 +1,181 @@ +/* + * Copyright 2015 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 busions 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 <[email protected]> + */ +#define gm204_i2c_aux(p) container_of((p), struct gm204_i2c_aux, base) +#include "aux.h" + +struct gm204_i2c_aux { +	struct nvkm_i2c_aux base; +	int ch; +}; + +static void +gm204_i2c_aux_fini(struct gm204_i2c_aux *aux) +{ +	struct nvkm_device *device = aux->base.pad->i2c->subdev.device; +	nvkm_mask(device, 0x00d954 + (aux->ch * 0x50), 0x00310000, 0x00000000); +} + +static int +gm204_i2c_aux_init(struct gm204_i2c_aux *aux) +{ +	struct nvkm_device *device = aux->base.pad->i2c->subdev.device; +	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 = nvkm_rd32(device, 0x00d954 + (aux->ch * 0x50)); +		udelay(1); +		if (!timeout--) { +			AUX_ERR(&aux->base, "begin idle timeout %08x", ctrl); +			return -EBUSY; +		} +	} while (ctrl & 0x03010000); + +	/* set some magic, and wait up to 1ms for it to appear */ +	nvkm_mask(device, 0x00d954 + (aux->ch * 0x50), 0x00300000, ureq); +	timeout = 1000; +	do { +		ctrl = nvkm_rd32(device, 0x00d954 + (aux->ch * 0x50)); +		udelay(1); +		if (!timeout--) { +			AUX_ERR(&aux->base, "magic wait %08x", ctrl); +			gm204_i2c_aux_fini(aux); +			return -EBUSY; +		} +	} while ((ctrl & 0x03000000) != urep); + +	return 0; +} + +static int +gm204_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry, +		   u8 type, u32 addr, u8 *data, u8 size) +{ +	struct gm204_i2c_aux *aux = gm204_i2c_aux(obj); +	struct nvkm_device *device = aux->base.pad->i2c->subdev.device; +	const u32 base = aux->ch * 0x50; +	u32 ctrl, stat, timeout, retries; +	u32 xbuf[4] = {}; +	int ret, i; + +	AUX_TRACE(&aux->base, "%d: %08x %d", type, addr, size); + +	ret = gm204_i2c_aux_init(aux); +	if (ret < 0) +		goto out; + +	stat = nvkm_rd32(device, 0x00d958 + base); +	if (!(stat & 0x10000000)) { +		AUX_TRACE(&aux->base, "sink not detected"); +		ret = -ENXIO; +		goto out; +	} + +	if (!(type & 1)) { +		memcpy(xbuf, data, size); +		for (i = 0; i < 16; i += 4) { +			AUX_TRACE(&aux->base, "wr %08x", xbuf[i / 4]); +			nvkm_wr32(device, 0x00d930 + base + i, xbuf[i / 4]); +		} +	} + +	ctrl  = nvkm_rd32(device, 0x00d954 + base); +	ctrl &= ~0x0001f0ff; +	ctrl |= type << 12; +	ctrl |= size - 1; +	nvkm_wr32(device, 0x00d950 + base, addr); + +	/* (maybe) retry transaction a number of times on failure... */ +	for (retries = 0; !ret && retries < 32; retries++) { +		/* reset, and delay a while if this is a retry */ +		nvkm_wr32(device, 0x00d954 + base, 0x80000000 | ctrl); +		nvkm_wr32(device, 0x00d954 + base, 0x00000000 | ctrl); +		if (retries) +			udelay(400); + +		/* transaction request, wait up to 1ms for it to complete */ +		nvkm_wr32(device, 0x00d954 + base, 0x00010000 | ctrl); + +		timeout = 1000; +		do { +			ctrl = nvkm_rd32(device, 0x00d954 + base); +			udelay(1); +			if (!timeout--) { +				AUX_ERR(&aux->base, "timeout %08x", ctrl); +				ret = -EIO; +				goto out; +			} +		} while (ctrl & 0x00010000); +		ret = 1; + +		/* read status, and check if transaction completed ok */ +		stat = nvkm_mask(device, 0x00d958 + base, 0, 0); +		if ((stat & 0x000f0000) == 0x00080000 || +		    (stat & 0x000f0000) == 0x00020000) +			ret = retry ? 0 : 1; +		if ((stat & 0x00000100)) +			ret = -ETIMEDOUT; +		if ((stat & 0x00000e00)) +			ret = -EIO; + +		AUX_TRACE(&aux->base, "%02d %08x %08x", retries, ctrl, stat); +	} + +	if (type & 1) { +		for (i = 0; i < 16; i += 4) { +			xbuf[i / 4] = nvkm_rd32(device, 0x00d940 + base + i); +			AUX_TRACE(&aux->base, "rd %08x", xbuf[i / 4]); +		} +		memcpy(data, xbuf, size); +	} + +out: +	gm204_i2c_aux_fini(aux); +	return ret < 0 ? ret : (stat & 0x000f0000) >> 16; +} + +static const struct nvkm_i2c_aux_func +gm204_i2c_aux_func = { +	.xfer = gm204_i2c_aux_xfer, +}; + +int +gm204_i2c_aux_new(struct nvkm_i2c_pad *pad, int index, u8 drive, +		struct nvkm_i2c_aux **paux) +{ +	struct gm204_i2c_aux *aux; + +	if (!(aux = kzalloc(sizeof(*aux), GFP_KERNEL))) +		return -ENOMEM; +	*paux = &aux->base; + +	nvkm_i2c_aux_ctor(&gm204_i2c_aux_func, pad, index, &aux->base); +	aux->ch = drive; +	aux->base.intr = 1 << aux->ch; +	return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c index 9200f122c02c..243a71ff0a0d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/base.c @@ -22,328 +22,91 @@   * Authors: Ben Skeggs   */  #include "priv.h" +#include "aux.h" +#include "bus.h"  #include "pad.h" -#include <core/device.h>  #include <core/notify.h>  #include <core/option.h>  #include <subdev/bios.h>  #include <subdev/bios/dcb.h> +#include <subdev/bios/i2c.h> -/****************************************************************************** - * interface to linux i2c bit-banging algorithm - *****************************************************************************/ - -#ifdef CONFIG_NOUVEAU_I2C_INTERNAL_DEFAULT -#define CSTMSEL true -#else -#define CSTMSEL false -#endif - -static int -nvkm_i2c_pre_xfer(struct i2c_adapter *adap) +static struct nvkm_i2c_pad * +nvkm_i2c_pad_find(struct nvkm_i2c *i2c, int id)  { -	struct i2c_algo_bit_data *bit = adap->algo_data; -	struct nvkm_i2c_port *port = bit->data; -	return nvkm_i2c(port)->acquire(port, bit->timeout); -} +	struct nvkm_i2c_pad *pad; -static void -nvkm_i2c_post_xfer(struct i2c_adapter *adap) -{ -	struct i2c_algo_bit_data *bit = adap->algo_data; -	struct nvkm_i2c_port *port = bit->data; -	return nvkm_i2c(port)->release(port); -} - -static void -nvkm_i2c_setscl(void *data, int state) -{ -	struct nvkm_i2c_port *port = data; -	port->func->drive_scl(port, state); -} - -static void -nvkm_i2c_setsda(void *data, int state) -{ -	struct nvkm_i2c_port *port = data; -	port->func->drive_sda(port, state); -} - -static int -nvkm_i2c_getscl(void *data) -{ -	struct nvkm_i2c_port *port = data; -	return port->func->sense_scl(port); -} - -static int -nvkm_i2c_getsda(void *data) -{ -	struct nvkm_i2c_port *port = data; -	return port->func->sense_sda(port); -} - -/****************************************************************************** - * base i2c "port" class implementation - *****************************************************************************/ - -int -_nvkm_i2c_port_fini(struct nvkm_object *object, bool suspend) -{ -	struct nvkm_i2c_port *port = (void *)object; -	struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port); -	nv_ofuncs(pad)->fini(nv_object(pad), suspend); -	return nvkm_object_fini(&port->base, suspend); -} - -void -_nvkm_i2c_port_dtor(struct nvkm_object *object) -{ -	struct nvkm_i2c_port *port = (void *)object; -	i2c_del_adapter(&port->adapter); -	nvkm_object_destroy(&port->base); -} - -int -nvkm_i2c_port_create_(struct nvkm_object *parent, struct nvkm_object *engine, -		      struct nvkm_oclass *oclass, u8 index, -		      const struct i2c_algorithm *algo, -		      const struct nvkm_i2c_func *func, -		      int size, void **pobject) -{ -	struct nvkm_device *device = nv_device(parent); -	struct nvkm_i2c *i2c = nvkm_i2c(parent); -	struct nvkm_i2c_port *port; -	int ret; - -	ret = nvkm_object_create_(parent, engine, oclass, 0, size, pobject); -	port = *pobject; -	if (ret) -		return ret; - -	snprintf(port->adapter.name, sizeof(port->adapter.name), -		 "nvkm-%s-%d", device->name, index); -	port->adapter.owner = THIS_MODULE; -	port->adapter.dev.parent = nv_device_base(device); -	port->index = index; -	port->aux = -1; -	port->func = func; -	mutex_init(&port->mutex); - -	if ( algo == &nvkm_i2c_bit_algo && -	    !nvkm_boolopt(device->cfgopt, "NvI2C", CSTMSEL)) { -		struct i2c_algo_bit_data *bit; - -		bit = kzalloc(sizeof(*bit), GFP_KERNEL); -		if (!bit) -			return -ENOMEM; - -		bit->udelay = 10; -		bit->timeout = usecs_to_jiffies(2200); -		bit->data = port; -		bit->pre_xfer = nvkm_i2c_pre_xfer; -		bit->post_xfer = nvkm_i2c_post_xfer; -		bit->setsda = nvkm_i2c_setsda; -		bit->setscl = nvkm_i2c_setscl; -		bit->getsda = nvkm_i2c_getsda; -		bit->getscl = nvkm_i2c_getscl; - -		port->adapter.algo_data = bit; -		ret = i2c_bit_add_bus(&port->adapter); -	} else { -		port->adapter.algo_data = port; -		port->adapter.algo = algo; -		ret = i2c_add_adapter(&port->adapter); +	list_for_each_entry(pad, &i2c->pad, head) { +		if (pad->id == id) +			return pad;  	} -	if (ret == 0) -		list_add_tail(&port->head, &i2c->ports); -	return ret; +	return NULL;  } -/****************************************************************************** - * base i2c subdev class implementation - *****************************************************************************/ - -static struct nvkm_i2c_port * -nvkm_i2c_find(struct nvkm_i2c *i2c, u8 index) +struct nvkm_i2c_bus * +nvkm_i2c_bus_find(struct nvkm_i2c *i2c, int id)  { -	struct nvkm_bios *bios = nvkm_bios(i2c); -	struct nvkm_i2c_port *port; +	struct nvkm_bios *bios = i2c->subdev.device->bios; +	struct nvkm_i2c_bus *bus; -	if (index == NV_I2C_DEFAULT(0) || -	    index == NV_I2C_DEFAULT(1)) { +	if (id == NVKM_I2C_BUS_PRI || id == NVKM_I2C_BUS_SEC) {  		u8  ver, hdr, cnt, len;  		u16 i2c = dcb_i2c_table(bios, &ver, &hdr, &cnt, &len);  		if (i2c && ver >= 0x30) { -			u8 auxidx = nv_ro08(bios, i2c + 4); -			if (index == NV_I2C_DEFAULT(0)) -				index = (auxidx & 0x0f) >> 0; +			u8 auxidx = nvbios_rd08(bios, i2c + 4); +			if (id == NVKM_I2C_BUS_PRI) +				id = NVKM_I2C_BUS_CCB((auxidx & 0x0f) >> 0);  			else -				index = (auxidx & 0xf0) >> 4; +				id = NVKM_I2C_BUS_CCB((auxidx & 0xf0) >> 4);  		} else { -			index = 2; +			id = NVKM_I2C_BUS_CCB(2);  		}  	} -	list_for_each_entry(port, &i2c->ports, head) { -		if (port->index == index) -			return port; +	list_for_each_entry(bus, &i2c->bus, head) { +		if (bus->id == id) +			return bus;  	}  	return NULL;  } -static struct nvkm_i2c_port * -nvkm_i2c_find_type(struct nvkm_i2c *i2c, u16 type) +struct nvkm_i2c_aux * +nvkm_i2c_aux_find(struct nvkm_i2c *i2c, int id)  { -	struct nvkm_i2c_port *port; +	struct nvkm_i2c_aux *aux; -	list_for_each_entry(port, &i2c->ports, head) { -		if (nv_hclass(port) == type) -			return port; +	list_for_each_entry(aux, &i2c->aux, head) { +		if (aux->id == id) +			return aux;  	}  	return NULL;  }  static void -nvkm_i2c_release_pad(struct nvkm_i2c_port *port) -{ -	struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port); -	struct nvkm_i2c *i2c = nvkm_i2c(port); - -	if (atomic_dec_and_test(&nv_object(pad)->usecount)) { -		nv_ofuncs(pad)->fini(nv_object(pad), false); -		wake_up_all(&i2c->wait); -	} -} - -static int -nvkm_i2c_try_acquire_pad(struct nvkm_i2c_port *port) -{ -	struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port); - -	if (atomic_add_return(1, &nv_object(pad)->usecount) != 1) { -		struct nvkm_object *owner = (void *)pad->port; -		do { -			if (owner == (void *)port) -				return 0; -			owner = owner->parent; -		} while(owner); -		nvkm_i2c_release_pad(port); -		return -EBUSY; -	} - -	pad->next = port; -	nv_ofuncs(pad)->init(nv_object(pad)); -	return 0; -} - -static int -nvkm_i2c_acquire_pad(struct nvkm_i2c_port *port, unsigned long timeout) -{ -	struct nvkm_i2c *i2c = nvkm_i2c(port); - -	if (timeout) { -		if (wait_event_timeout(i2c->wait, -				       nvkm_i2c_try_acquire_pad(port) == 0, -				       timeout) == 0) -			return -EBUSY; -	} else { -		wait_event(i2c->wait, nvkm_i2c_try_acquire_pad(port) == 0); -	} - -	return 0; -} - -static void -nvkm_i2c_release(struct nvkm_i2c_port *port) -__releases(pad->mutex) -{ -	nvkm_i2c(port)->release_pad(port); -	mutex_unlock(&port->mutex); -} - -static int -nvkm_i2c_acquire(struct nvkm_i2c_port *port, unsigned long timeout) -__acquires(pad->mutex) -{ -	int ret; -	mutex_lock(&port->mutex); -	if ((ret = nvkm_i2c(port)->acquire_pad(port, timeout))) -		mutex_unlock(&port->mutex); -	return ret; -} - -static int -nvkm_i2c_identify(struct nvkm_i2c *i2c, int index, const char *what, -		  struct nvkm_i2c_board_info *info, -		  bool (*match)(struct nvkm_i2c_port *, -				struct i2c_board_info *, void *), void *data) -{ -	struct nvkm_i2c_port *port = nvkm_i2c_find(i2c, index); -	int i; - -	if (!port) { -		nv_debug(i2c, "no bus when probing %s on %d\n", what, index); -		return -ENODEV; -	} - -	nv_debug(i2c, "probing %ss on bus: %d\n", what, port->index); -	for (i = 0; info[i].dev.addr; i++) { -		u8 orig_udelay = 0; - -		if ((port->adapter.algo == &i2c_bit_algo) && -		    (info[i].udelay != 0)) { -			struct i2c_algo_bit_data *algo = port->adapter.algo_data; -			nv_debug(i2c, "using custom udelay %d instead of %d\n", -			         info[i].udelay, algo->udelay); -			orig_udelay = algo->udelay; -			algo->udelay = info[i].udelay; -		} - -		if (nv_probe_i2c(port, info[i].dev.addr) && -		    (!match || match(port, &info[i].dev, data))) { -			nv_info(i2c, "detected %s: %s\n", what, -				info[i].dev.type); -			return i; -		} - -		if (orig_udelay) { -			struct i2c_algo_bit_data *algo = port->adapter.algo_data; -			algo->udelay = orig_udelay; -		} -	} - -	nv_debug(i2c, "no devices found.\n"); -	return -ENODEV; -} - -static void -nvkm_i2c_intr_fini(struct nvkm_event *event, int type, int index) +nvkm_i2c_intr_fini(struct nvkm_event *event, int type, int id)  {  	struct nvkm_i2c *i2c = container_of(event, typeof(*i2c), event); -	struct nvkm_i2c_port *port = i2c->find(i2c, index); -	const struct nvkm_i2c_impl *impl = (void *)nv_object(i2c)->oclass; -	if (port && port->aux >= 0) -		impl->aux_mask(i2c, type, 1 << port->aux, 0); +	struct nvkm_i2c_aux *aux = nvkm_i2c_aux_find(i2c, id); +	if (aux) +		i2c->func->aux_mask(i2c, type, aux->intr, 0);  }  static void -nvkm_i2c_intr_init(struct nvkm_event *event, int type, int index) +nvkm_i2c_intr_init(struct nvkm_event *event, int type, int id)  {  	struct nvkm_i2c *i2c = container_of(event, typeof(*i2c), event); -	struct nvkm_i2c_port *port = i2c->find(i2c, index); -	const struct nvkm_i2c_impl *impl = (void *)nv_object(i2c)->oclass; -	if (port && port->aux >= 0) -		impl->aux_mask(i2c, type, 1 << port->aux, 1 << port->aux); +	struct nvkm_i2c_aux *aux = nvkm_i2c_aux_find(i2c, id); +	if (aux) +		i2c->func->aux_mask(i2c, type, aux->intr, aux->intr);  }  static int  nvkm_i2c_intr_ctor(struct nvkm_object *object, void *data, u32 size, -		      struct nvkm_notify *notify) +		   struct nvkm_notify *notify)  {  	struct nvkm_i2c_ntfy_req *req = data;  	if (!WARN_ON(size != sizeof(*req))) { @@ -355,38 +118,6 @@ nvkm_i2c_intr_ctor(struct nvkm_object *object, void *data, u32 size,  	return -EINVAL;  } -static void -nvkm_i2c_intr(struct nvkm_subdev *subdev) -{ -	struct nvkm_i2c_impl *impl = (void *)nv_oclass(subdev); -	struct nvkm_i2c *i2c = nvkm_i2c(subdev); -	struct nvkm_i2c_port *port; -	u32 hi, lo, rq, tx, e; - -	if (impl->aux_stat) { -		impl->aux_stat(i2c, &hi, &lo, &rq, &tx); -		if (hi || lo || rq || tx) { -			list_for_each_entry(port, &i2c->ports, head) { -				if (e = 0, port->aux < 0) -					continue; - -				if (hi & (1 << port->aux)) e |= NVKM_I2C_PLUG; -				if (lo & (1 << port->aux)) e |= NVKM_I2C_UNPLUG; -				if (rq & (1 << port->aux)) e |= NVKM_I2C_IRQ; -				if (tx & (1 << port->aux)) e |= NVKM_I2C_DONE; -				if (e) { -					struct nvkm_i2c_ntfy_rep rep = { -						.mask = e, -					}; -					nvkm_event_send(&i2c->event, rep.mask, -							port->index, &rep, -							sizeof(rep)); -				} -			} -		} -	} -} -  static const struct nvkm_event_func  nvkm_i2c_intr_func = {  	.ctor = nvkm_i2c_intr_ctor, @@ -394,229 +125,272 @@ nvkm_i2c_intr_func = {  	.fini = nvkm_i2c_intr_fini,  }; -int -_nvkm_i2c_fini(struct nvkm_object *object, bool suspend) +static void +nvkm_i2c_intr(struct nvkm_subdev *subdev)  { -	struct nvkm_i2c_impl *impl = (void *)nv_oclass(object); -	struct nvkm_i2c *i2c = (void *)object; -	struct nvkm_i2c_port *port; -	u32 mask; -	int ret; +	struct nvkm_i2c *i2c = nvkm_i2c(subdev); +	struct nvkm_i2c_aux *aux; +	u32 hi, lo, rq, tx; + +	if (!i2c->func->aux_stat) +		return; + +	i2c->func->aux_stat(i2c, &hi, &lo, &rq, &tx); +	if (!hi && !lo && !rq && !tx) +		return; -	list_for_each_entry(port, &i2c->ports, head) { -		ret = nv_ofuncs(port)->fini(nv_object(port), suspend); -		if (ret && suspend) -			goto fail; +	list_for_each_entry(aux, &i2c->aux, head) { +		u32 mask = 0; +		if (hi & aux->intr) mask |= NVKM_I2C_PLUG; +		if (lo & aux->intr) mask |= NVKM_I2C_UNPLUG; +		if (rq & aux->intr) mask |= NVKM_I2C_IRQ; +		if (tx & aux->intr) mask |= NVKM_I2C_DONE; +		if (mask) { +			struct nvkm_i2c_ntfy_rep rep = { +				.mask = mask, +			}; +			nvkm_event_send(&i2c->event, rep.mask, aux->id, +					&rep, sizeof(rep)); +		}  	} +} + +static int +nvkm_i2c_fini(struct nvkm_subdev *subdev, bool suspend) +{ +	struct nvkm_i2c *i2c = nvkm_i2c(subdev); +	struct nvkm_i2c_pad *pad; +	u32 mask; -	if ((mask = (1 << impl->aux) - 1), impl->aux_stat) { -		impl->aux_mask(i2c, NVKM_I2C_ANY, mask, 0); -		impl->aux_stat(i2c, &mask, &mask, &mask, &mask); +	if ((mask = (1 << i2c->func->aux) - 1), i2c->func->aux_stat) { +		i2c->func->aux_mask(i2c, NVKM_I2C_ANY, mask, 0); +		i2c->func->aux_stat(i2c, &mask, &mask, &mask, &mask);  	} -	return nvkm_subdev_fini(&i2c->base, suspend); -fail: -	list_for_each_entry_continue_reverse(port, &i2c->ports, head) { -		nv_ofuncs(port)->init(nv_object(port)); +	list_for_each_entry(pad, &i2c->pad, head) { +		nvkm_i2c_pad_fini(pad);  	} -	return ret; +	return 0;  } -int -_nvkm_i2c_init(struct nvkm_object *object) +static int +nvkm_i2c_init(struct nvkm_subdev *subdev)  { -	struct nvkm_i2c *i2c = (void *)object; -	struct nvkm_i2c_port *port; -	int ret; - -	ret = nvkm_subdev_init(&i2c->base); -	if (ret == 0) { -		list_for_each_entry(port, &i2c->ports, head) { -			ret = nv_ofuncs(port)->init(nv_object(port)); -			if (ret) -				goto fail; -		} +	struct nvkm_i2c *i2c = nvkm_i2c(subdev); +	struct nvkm_i2c_bus *bus; +	struct nvkm_i2c_pad *pad; + +	list_for_each_entry(pad, &i2c->pad, head) { +		nvkm_i2c_pad_init(pad);  	} -	return ret; -fail: -	list_for_each_entry_continue_reverse(port, &i2c->ports, head) { -		nv_ofuncs(port)->fini(nv_object(port), false); +	list_for_each_entry(bus, &i2c->bus, head) { +		nvkm_i2c_bus_init(bus);  	} -	return ret; +	return 0;  } -void -_nvkm_i2c_dtor(struct nvkm_object *object) +static void * +nvkm_i2c_dtor(struct nvkm_subdev *subdev)  { -	struct nvkm_i2c *i2c = (void *)object; -	struct nvkm_i2c_port *port, *temp; +	struct nvkm_i2c *i2c = nvkm_i2c(subdev);  	nvkm_event_fini(&i2c->event); -	list_for_each_entry_safe(port, temp, &i2c->ports, head) { -		nvkm_object_ref(NULL, (struct nvkm_object **)&port); +	while (!list_empty(&i2c->aux)) { +		struct nvkm_i2c_aux *aux = +			list_first_entry(&i2c->aux, typeof(*aux), head); +		nvkm_i2c_aux_del(&aux);  	} -	nvkm_subdev_destroy(&i2c->base); -} - -static struct nvkm_oclass * -nvkm_i2c_extdev_sclass[] = { -	nvkm_anx9805_sclass, -}; +	while (!list_empty(&i2c->bus)) { +		struct nvkm_i2c_bus *bus = +			list_first_entry(&i2c->bus, typeof(*bus), head); +		nvkm_i2c_bus_del(&bus); +	} -static void -nvkm_i2c_create_port(struct nvkm_i2c *i2c, int index, u8 type, -		     struct dcb_i2c_entry *info) -{ -	const struct nvkm_i2c_impl *impl = (void *)nv_oclass(i2c); -	struct nvkm_oclass *oclass; -	struct nvkm_object *parent; -	struct nvkm_object *object; -	int ret, pad; - -	if (info->share != DCB_I2C_UNUSED) { -		pad    = info->share; -		oclass = impl->pad_s; -	} else { -		if (type != DCB_I2C_NVIO_AUX) -			pad = 0x100 + info->drive; -		else -			pad = 0x100 + info->auxch; -		oclass = impl->pad_x; +	while (!list_empty(&i2c->pad)) { +		struct nvkm_i2c_pad *pad = +			list_first_entry(&i2c->pad, typeof(*pad), head); +		nvkm_i2c_pad_del(&pad);  	} -	ret = nvkm_object_ctor(nv_object(i2c), NULL, oclass, -			       NULL, pad, &parent); -	if (ret < 0) -		return; +	return i2c; +} -	oclass = impl->sclass; -	do { -		ret = -EINVAL; -		if (oclass->handle == type) { -			ret = nvkm_object_ctor(parent, NULL, oclass, -					       info, index, &object); -		} -	} while (ret && (++oclass)->handle); +static const struct nvkm_subdev_func +nvkm_i2c = { +	.dtor = nvkm_i2c_dtor, +	.init = nvkm_i2c_init, +	.fini = nvkm_i2c_fini, +	.intr = nvkm_i2c_intr, +}; -	nvkm_object_ref(NULL, &parent); +static const struct nvkm_i2c_drv { +	u8 bios; +	u8 addr; +	int (*pad_new)(struct nvkm_i2c_bus *, int id, u8 addr, +		       struct nvkm_i2c_pad **);  } +nvkm_i2c_drv[] = { +	{ 0x0d, 0x39, anx9805_pad_new }, +	{ 0x0e, 0x3b, anx9805_pad_new }, +	{} +};  int -nvkm_i2c_create_(struct nvkm_object *parent, struct nvkm_object *engine, -		 struct nvkm_oclass *oclass, int length, void **pobject) +nvkm_i2c_new_(const struct nvkm_i2c_func *func, struct nvkm_device *device, +	      int index, struct nvkm_i2c **pi2c)  { -	struct nvkm_bios *bios = nvkm_bios(parent); +	struct nvkm_bios *bios = device->bios;  	struct nvkm_i2c *i2c; -	struct nvkm_object *object; -	struct dcb_i2c_entry info; -	int ret, i, j, index = -1; -	struct dcb_output outp; -	u8  ver, hdr; -	u32 data; - -	ret = nvkm_subdev_create(parent, engine, oclass, 0, "I2C", "i2c", &i2c); -	*pobject = nv_object(i2c); -	if (ret) -		return ret; - -	nv_subdev(i2c)->intr = nvkm_i2c_intr; -	i2c->find = nvkm_i2c_find; -	i2c->find_type = nvkm_i2c_find_type; -	i2c->acquire_pad = nvkm_i2c_acquire_pad; -	i2c->release_pad = nvkm_i2c_release_pad; -	i2c->acquire = nvkm_i2c_acquire; -	i2c->release = nvkm_i2c_release; -	i2c->identify = nvkm_i2c_identify; -	init_waitqueue_head(&i2c->wait); -	INIT_LIST_HEAD(&i2c->ports); - -	while (!dcb_i2c_parse(bios, ++index, &info)) { -		switch (info.type) { -		case DCB_I2C_NV04_BIT: -		case DCB_I2C_NV4E_BIT: -		case DCB_I2C_NVIO_BIT: -			nvkm_i2c_create_port(i2c, NV_I2C_PORT(index), -					     info.type, &info); -			break; -		case DCB_I2C_NVIO_AUX: -			nvkm_i2c_create_port(i2c, NV_I2C_AUX(index), -					     info.type, &info); -			break; -		case DCB_I2C_PMGR: -			if (info.drive != DCB_I2C_UNUSED) { -				nvkm_i2c_create_port(i2c, NV_I2C_PORT(index), -						     DCB_I2C_NVIO_BIT, &info); -			} -			if (info.auxch != DCB_I2C_UNUSED) { -				nvkm_i2c_create_port(i2c, NV_I2C_AUX(index), -						     DCB_I2C_NVIO_AUX, &info); -			} -			break; -		case DCB_I2C_UNUSED: -		default: +	struct dcb_i2c_entry ccbE; +	struct dcb_output dcbE; +	u8 ver, hdr; +	int ret, i; + +	if (!(i2c = *pi2c = kzalloc(sizeof(*i2c), GFP_KERNEL))) +		return -ENOMEM; + +	nvkm_subdev_ctor(&nvkm_i2c, device, index, 0, &i2c->subdev); +	i2c->func = func; +	INIT_LIST_HEAD(&i2c->pad); +	INIT_LIST_HEAD(&i2c->bus); +	INIT_LIST_HEAD(&i2c->aux); + +	i = -1; +	while (!dcb_i2c_parse(bios, ++i, &ccbE)) { +		struct nvkm_i2c_pad *pad = NULL; +		struct nvkm_i2c_bus *bus = NULL; +		struct nvkm_i2c_aux *aux = NULL; + +		nvkm_debug(&i2c->subdev, "ccb %02x: type %02x drive %02x " +			   "sense %02x share %02x auxch %02x\n", i, ccbE.type, +			   ccbE.drive, ccbE.sense, ccbE.share, ccbE.auxch); + +		if (ccbE.share != DCB_I2C_UNUSED) { +			const int id = NVKM_I2C_PAD_HYBRID(ccbE.share); +			if (!(pad = nvkm_i2c_pad_find(i2c, id))) +				ret = func->pad_s_new(i2c, id, &pad); +			else +				ret = 0; +		} else { +			ret = func->pad_x_new(i2c, NVKM_I2C_PAD_CCB(i), &pad); +		} + +		if (ret) { +			nvkm_error(&i2c->subdev, "ccb %02x pad, %d\n", i, ret); +			nvkm_i2c_pad_del(&pad); +			continue; +		} + +		if (pad->func->bus_new_0 && ccbE.type == DCB_I2C_NV04_BIT) { +			ret = pad->func->bus_new_0(pad, NVKM_I2C_BUS_CCB(i), +						   ccbE.drive, +						   ccbE.sense, &bus); +		} else +		if (pad->func->bus_new_4 && +		    ( ccbE.type == DCB_I2C_NV4E_BIT || +		      ccbE.type == DCB_I2C_NVIO_BIT || +		     (ccbE.type == DCB_I2C_PMGR && +		      ccbE.drive != DCB_I2C_UNUSED))) { +			ret = pad->func->bus_new_4(pad, NVKM_I2C_BUS_CCB(i), +						   ccbE.drive, &bus); +		} + +		if (ret) { +			nvkm_error(&i2c->subdev, "ccb %02x bus, %d\n", i, ret); +			nvkm_i2c_bus_del(&bus); +		} + +		if (pad->func->aux_new_6 && +		    ( ccbE.type == DCB_I2C_NVIO_AUX || +		     (ccbE.type == DCB_I2C_PMGR && +		      ccbE.auxch != DCB_I2C_UNUSED))) { +			ret = pad->func->aux_new_6(pad, NVKM_I2C_BUS_CCB(i), +						   ccbE.auxch, &aux); +		} else { +			ret = 0; +		} + +		if (ret) { +			nvkm_error(&i2c->subdev, "ccb %02x aux, %d\n", i, ret); +			nvkm_i2c_aux_del(&aux); +		} + +		if (ccbE.type != DCB_I2C_UNUSED && !bus && !aux) { +			nvkm_warn(&i2c->subdev, "ccb %02x was ignored\n", i);  			continue;  		}  	} -	/* in addition to the busses specified in the i2c table, there -	 * may be ddc/aux channels hiding behind external tmds/dp/etc -	 * transmitters. -	 */ -	index = NV_I2C_EXT(0);  	i = -1; -	while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &outp))) { -		if (!outp.location || !outp.extdev) +	while (dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE)) { +		const struct nvkm_i2c_drv *drv = nvkm_i2c_drv; +		struct nvkm_i2c_bus *bus; +		struct nvkm_i2c_pad *pad; + +		/* internal outputs handled by native i2c busses (above) */ +		if (!dcbE.location)  			continue; -		switch (outp.type) { -		case DCB_OUTPUT_TMDS: -			info.type = NV_I2C_TYPE_EXTDDC(outp.extdev); -			break; -		case DCB_OUTPUT_DP: -			info.type = NV_I2C_TYPE_EXTAUX(outp.extdev); -			break; -		default: +		/* we need an i2c bus to talk to the external encoder */ +		bus = nvkm_i2c_bus_find(i2c, dcbE.i2c_index); +		if (!bus) { +			nvkm_debug(&i2c->subdev, "dcb %02x no bus\n", i);  			continue;  		} -		ret = -ENODEV; -		j = -1; -		while (ret && ++j < ARRAY_SIZE(nvkm_i2c_extdev_sclass)) { -			parent = nv_object(i2c->find(i2c, outp.i2c_index)); -			oclass = nvkm_i2c_extdev_sclass[j]; -			do { -				if (oclass->handle != info.type) -					continue; -				ret = nvkm_object_ctor(parent, NULL, oclass, -						       NULL, index++, &object); -			} while (ret && (++oclass)->handle); +		/* ... and a driver for it */ +		while (drv->pad_new) { +			if (drv->bios == dcbE.extdev) +				break; +			drv++;  		} -	} -	ret = nvkm_event_init(&nvkm_i2c_intr_func, 4, index, &i2c->event); -	if (ret) -		return ret; - -	return 0; -} +		if (!drv->pad_new) { +			nvkm_debug(&i2c->subdev, "dcb %02x drv %02x unknown\n", +				   i, dcbE.extdev); +			continue; +		} -int -_nvkm_i2c_ctor(struct nvkm_object *parent, struct nvkm_object *engine, -	       struct nvkm_oclass *oclass, void *data, u32 size, -	       struct nvkm_object **pobject) -{ -	struct nvkm_i2c *i2c; -	int ret; +		/* find/create an instance of the driver */ +		pad = nvkm_i2c_pad_find(i2c, NVKM_I2C_PAD_EXT(dcbE.extdev)); +		if (!pad) { +			const int id = NVKM_I2C_PAD_EXT(dcbE.extdev); +			ret = drv->pad_new(bus, id, drv->addr, &pad); +			if (ret) { +				nvkm_error(&i2c->subdev, "dcb %02x pad, %d\n", +					   i, ret); +				nvkm_i2c_pad_del(&pad); +				continue; +			} +		} -	ret = nvkm_i2c_create(parent, engine, oclass, &i2c); -	*pobject = nv_object(i2c); -	if (ret) -		return ret; +		/* create any i2c bus / aux channel required by the output */ +		if (pad->func->aux_new_6 && dcbE.type == DCB_OUTPUT_DP) { +			const int id = NVKM_I2C_AUX_EXT(dcbE.extdev); +			struct nvkm_i2c_aux *aux = NULL; +			ret = pad->func->aux_new_6(pad, id, 0, &aux); +			if (ret) { +				nvkm_error(&i2c->subdev, "dcb %02x aux, %d\n", +					   i, ret); +				nvkm_i2c_aux_del(&aux); +			} +		} else +		if (pad->func->bus_new_4) { +			const int id = NVKM_I2C_BUS_EXT(dcbE.extdev); +			struct nvkm_i2c_bus *bus = NULL; +			ret = pad->func->bus_new_4(pad, id, 0, &bus); +			if (ret) { +				nvkm_error(&i2c->subdev, "dcb %02x bus, %d\n", +					   i, ret); +				nvkm_i2c_bus_del(&bus); +			} +		} +	} -	return 0; +	return nvkm_event_init(&nvkm_i2c_intr_func, 4, i, &i2c->event);  } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c index 861a453d2a67..cdce11bbabe5 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bit.c @@ -9,7 +9,7 @@   * 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. + * all copies or substantial busions 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, @@ -21,7 +21,7 @@   *   * Authors: Ben Skeggs   */ -#include "priv.h" +#include "bus.h"  #ifdef CONFIG_NOUVEAU_I2C_INTERNAL  #define T_TIMEOUT  2200000 @@ -29,205 +29,188 @@  #define T_HOLD     5000  static inline void -i2c_drive_scl(struct nvkm_i2c_port *port, int state) +nvkm_i2c_drive_scl(struct nvkm_i2c_bus *bus, int state)  { -	port->func->drive_scl(port, state); +	bus->func->drive_scl(bus, state);  }  static inline void -i2c_drive_sda(struct nvkm_i2c_port *port, int state) +nvkm_i2c_drive_sda(struct nvkm_i2c_bus *bus, int state)  { -	port->func->drive_sda(port, state); +	bus->func->drive_sda(bus, state);  }  static inline int -i2c_sense_scl(struct nvkm_i2c_port *port) +nvkm_i2c_sense_scl(struct nvkm_i2c_bus *bus)  { -	return port->func->sense_scl(port); +	return bus->func->sense_scl(bus);  }  static inline int -i2c_sense_sda(struct nvkm_i2c_port *port) +nvkm_i2c_sense_sda(struct nvkm_i2c_bus *bus)  { -	return port->func->sense_sda(port); +	return bus->func->sense_sda(bus);  }  static void -i2c_delay(struct nvkm_i2c_port *port, u32 nsec) +nvkm_i2c_delay(struct nvkm_i2c_bus *bus, u32 nsec)  {  	udelay((nsec + 500) / 1000);  }  static bool -i2c_raise_scl(struct nvkm_i2c_port *port) +nvkm_i2c_raise_scl(struct nvkm_i2c_bus *bus)  {  	u32 timeout = T_TIMEOUT / T_RISEFALL; -	i2c_drive_scl(port, 1); +	nvkm_i2c_drive_scl(bus, 1);  	do { -		i2c_delay(port, T_RISEFALL); -	} while (!i2c_sense_scl(port) && --timeout); +		nvkm_i2c_delay(bus, T_RISEFALL); +	} while (!nvkm_i2c_sense_scl(bus) && --timeout);  	return timeout != 0;  }  static int -i2c_start(struct nvkm_i2c_port *port) +i2c_start(struct nvkm_i2c_bus *bus)  {  	int ret = 0; -	if (!i2c_sense_scl(port) || -	    !i2c_sense_sda(port)) { -		i2c_drive_scl(port, 0); -		i2c_drive_sda(port, 1); -		if (!i2c_raise_scl(port)) +	if (!nvkm_i2c_sense_scl(bus) || +	    !nvkm_i2c_sense_sda(bus)) { +		nvkm_i2c_drive_scl(bus, 0); +		nvkm_i2c_drive_sda(bus, 1); +		if (!nvkm_i2c_raise_scl(bus))  			ret = -EBUSY;  	} -	i2c_drive_sda(port, 0); -	i2c_delay(port, T_HOLD); -	i2c_drive_scl(port, 0); -	i2c_delay(port, T_HOLD); +	nvkm_i2c_drive_sda(bus, 0); +	nvkm_i2c_delay(bus, T_HOLD); +	nvkm_i2c_drive_scl(bus, 0); +	nvkm_i2c_delay(bus, T_HOLD);  	return ret;  }  static void -i2c_stop(struct nvkm_i2c_port *port) +i2c_stop(struct nvkm_i2c_bus *bus)  { -	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); +	nvkm_i2c_drive_scl(bus, 0); +	nvkm_i2c_drive_sda(bus, 0); +	nvkm_i2c_delay(bus, T_RISEFALL); + +	nvkm_i2c_drive_scl(bus, 1); +	nvkm_i2c_delay(bus, T_HOLD); +	nvkm_i2c_drive_sda(bus, 1); +	nvkm_i2c_delay(bus, T_HOLD);  }  static int -i2c_bitw(struct nvkm_i2c_port *port, int sda) +i2c_bitw(struct nvkm_i2c_bus *bus, int sda)  { -	i2c_drive_sda(port, sda); -	i2c_delay(port, T_RISEFALL); +	nvkm_i2c_drive_sda(bus, sda); +	nvkm_i2c_delay(bus, T_RISEFALL); -	if (!i2c_raise_scl(port)) +	if (!nvkm_i2c_raise_scl(bus))  		return -ETIMEDOUT; -	i2c_delay(port, T_HOLD); +	nvkm_i2c_delay(bus, T_HOLD); -	i2c_drive_scl(port, 0); -	i2c_delay(port, T_HOLD); +	nvkm_i2c_drive_scl(bus, 0); +	nvkm_i2c_delay(bus, T_HOLD);  	return 0;  }  static int -i2c_bitr(struct nvkm_i2c_port *port) +i2c_bitr(struct nvkm_i2c_bus *bus)  {  	int sda; -	i2c_drive_sda(port, 1); -	i2c_delay(port, T_RISEFALL); +	nvkm_i2c_drive_sda(bus, 1); +	nvkm_i2c_delay(bus, T_RISEFALL); -	if (!i2c_raise_scl(port)) +	if (!nvkm_i2c_raise_scl(bus))  		return -ETIMEDOUT; -	i2c_delay(port, T_HOLD); +	nvkm_i2c_delay(bus, T_HOLD); -	sda = i2c_sense_sda(port); +	sda = nvkm_i2c_sense_sda(bus); -	i2c_drive_scl(port, 0); -	i2c_delay(port, T_HOLD); +	nvkm_i2c_drive_scl(bus, 0); +	nvkm_i2c_delay(bus, T_HOLD);  	return sda;  }  static int -i2c_get_byte(struct nvkm_i2c_port *port, u8 *byte, bool last) +nvkm_i2c_get_byte(struct nvkm_i2c_bus *bus, u8 *byte, bool last)  {  	int i, bit;  	*byte = 0;  	for (i = 7; i >= 0; i--) { -		bit = i2c_bitr(port); +		bit = i2c_bitr(bus);  		if (bit < 0)  			return bit;  		*byte |= bit << i;  	} -	return i2c_bitw(port, last ? 1 : 0); +	return i2c_bitw(bus, last ? 1 : 0);  }  static int -i2c_put_byte(struct nvkm_i2c_port *port, u8 byte) +nvkm_i2c_put_byte(struct nvkm_i2c_bus *bus, u8 byte)  {  	int i, ret;  	for (i = 7; i >= 0; i--) { -		ret = i2c_bitw(port, !!(byte & (1 << i))); +		ret = i2c_bitw(bus, !!(byte & (1 << i)));  		if (ret < 0)  			return ret;  	} -	ret = i2c_bitr(port); +	ret = i2c_bitr(bus);  	if (ret == 1) /* nack */  		ret = -EIO;  	return ret;  }  static int -i2c_addr(struct nvkm_i2c_port *port, struct i2c_msg *msg) +i2c_addr(struct nvkm_i2c_bus *bus, struct i2c_msg *msg)  {  	u32 addr = msg->addr << 1;  	if (msg->flags & I2C_M_RD)  		addr |= 1; -	return i2c_put_byte(port, addr); +	return nvkm_i2c_put_byte(bus, addr);  } -static int -i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +int +nvkm_i2c_bit_xfer(struct nvkm_i2c_bus *bus, struct i2c_msg *msgs, int num)  { -	struct nvkm_i2c_port *port = adap->algo_data;  	struct i2c_msg *msg = msgs;  	int ret = 0, mcnt = num; -	ret = nvkm_i2c(port)->acquire(port, nsecs_to_jiffies(T_TIMEOUT)); -	if (ret) -		return ret; -  	while (!ret && mcnt--) {  		u8 remaining = msg->len;  		u8 *ptr = msg->buf; -		ret = i2c_start(port); +		ret = i2c_start(bus);  		if (ret == 0) -			ret = i2c_addr(port, msg); +			ret = i2c_addr(bus, msg);  		if (msg->flags & I2C_M_RD) {  			while (!ret && remaining--) -				ret = i2c_get_byte(port, ptr++, !remaining); +				ret = nvkm_i2c_get_byte(bus, ptr++, !remaining);  		} else {  			while (!ret && remaining--) -				ret = i2c_put_byte(port, *ptr++); +				ret = nvkm_i2c_put_byte(bus, *ptr++);  		}  		msg++;  	} -	i2c_stop(port); -	nvkm_i2c(port)->release(port); +	i2c_stop(bus);  	return (ret < 0) ? ret : num;  }  #else -static int -i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +int +nvkm_i2c_bit_xfer(struct nvkm_i2c_bus *bus, 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 nvkm_i2c_bit_algo = { -	.master_xfer = i2c_bit_xfer, -	.functionality = i2c_bit_func -}; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.c new file mode 100644 index 000000000000..807a2b67bd64 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.c @@ -0,0 +1,245 @@ +/* + * Copyright 2015 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 <[email protected]> + */ +#include "bus.h" +#include "pad.h" + +#include <core/option.h> + +/******************************************************************************* + * i2c-algo-bit + ******************************************************************************/ +static int +nvkm_i2c_bus_pre_xfer(struct i2c_adapter *adap) +{ +	struct nvkm_i2c_bus *bus = container_of(adap, typeof(*bus), i2c); +	return nvkm_i2c_bus_acquire(bus); +} + +static void +nvkm_i2c_bus_post_xfer(struct i2c_adapter *adap) +{ +	struct nvkm_i2c_bus *bus = container_of(adap, typeof(*bus), i2c); +	return nvkm_i2c_bus_release(bus); +} + +static void +nvkm_i2c_bus_setscl(void *data, int state) +{ +	struct nvkm_i2c_bus *bus = data; +	bus->func->drive_scl(bus, state); +} + +static void +nvkm_i2c_bus_setsda(void *data, int state) +{ +	struct nvkm_i2c_bus *bus = data; +	bus->func->drive_sda(bus, state); +} + +static int +nvkm_i2c_bus_getscl(void *data) +{ +	struct nvkm_i2c_bus *bus = data; +	return bus->func->sense_scl(bus); +} + +static int +nvkm_i2c_bus_getsda(void *data) +{ +	struct nvkm_i2c_bus *bus = data; +	return bus->func->sense_sda(bus); +} + +/******************************************************************************* + * !i2c-algo-bit (off-chip i2c bus / hw i2c / internal bit-banging algo) + ******************************************************************************/ +static int +nvkm_i2c_bus_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ +	struct nvkm_i2c_bus *bus = container_of(adap, typeof(*bus), i2c); +	int ret; + +	ret = nvkm_i2c_bus_acquire(bus); +	if (ret) +		return ret; + +	ret = bus->func->xfer(bus, msgs, num); +	nvkm_i2c_bus_release(bus); +	return ret; +} + +static u32 +nvkm_i2c_bus_func(struct i2c_adapter *adap) +{ +	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm +nvkm_i2c_bus_algo = { +	.master_xfer = nvkm_i2c_bus_xfer, +	.functionality = nvkm_i2c_bus_func, +}; + +/******************************************************************************* + * nvkm_i2c_bus base + ******************************************************************************/ +void +nvkm_i2c_bus_init(struct nvkm_i2c_bus *bus) +{ +	BUS_TRACE(bus, "init"); +	if (bus->func->init) +		bus->func->init(bus); +} + +void +nvkm_i2c_bus_release(struct nvkm_i2c_bus *bus) +{ +	struct nvkm_i2c_pad *pad = bus->pad; +	BUS_TRACE(bus, "release"); +	nvkm_i2c_pad_release(pad); +	mutex_unlock(&bus->mutex); +} + +int +nvkm_i2c_bus_acquire(struct nvkm_i2c_bus *bus) +{ +	struct nvkm_i2c_pad *pad = bus->pad; +	int ret; +	BUS_TRACE(bus, "acquire"); +	mutex_lock(&bus->mutex); +	ret = nvkm_i2c_pad_acquire(pad, NVKM_I2C_PAD_I2C); +	if (ret) +		mutex_unlock(&bus->mutex); +	return ret; +} + +int +nvkm_i2c_bus_probe(struct nvkm_i2c_bus *bus, const char *what, +		   struct nvkm_i2c_bus_probe *info, +		   bool (*match)(struct nvkm_i2c_bus *, +				 struct i2c_board_info *, void *), void *data) +{ +	int i; + +	BUS_DBG(bus, "probing %ss", what); +	for (i = 0; info[i].dev.addr; i++) { +		u8 orig_udelay = 0; + +		if ((bus->i2c.algo == &i2c_bit_algo) && (info[i].udelay != 0)) { +			struct i2c_algo_bit_data *algo = bus->i2c.algo_data; +			BUS_DBG(bus, "%dms delay instead of %dms", +				     info[i].udelay, algo->udelay); +			orig_udelay = algo->udelay; +			algo->udelay = info[i].udelay; +		} + +		if (nvkm_probe_i2c(&bus->i2c, info[i].dev.addr) && +		    (!match || match(bus, &info[i].dev, data))) { +			BUS_DBG(bus, "detected %s: %s", +				what, info[i].dev.type); +			return i; +		} + +		if (orig_udelay) { +			struct i2c_algo_bit_data *algo = bus->i2c.algo_data; +			algo->udelay = orig_udelay; +		} +	} + +	BUS_DBG(bus, "no devices found."); +	return -ENODEV; +} + +void +nvkm_i2c_bus_del(struct nvkm_i2c_bus **pbus) +{ +	struct nvkm_i2c_bus *bus = *pbus; +	if (bus && !WARN_ON(!bus->func)) { +		BUS_TRACE(bus, "dtor"); +		list_del(&bus->head); +		i2c_del_adapter(&bus->i2c); +		kfree(bus->i2c.algo_data); +		kfree(*pbus); +		*pbus = NULL; +	} +} + +int +nvkm_i2c_bus_ctor(const struct nvkm_i2c_bus_func *func, +		  struct nvkm_i2c_pad *pad, int id, +		  struct nvkm_i2c_bus *bus) +{ +	struct nvkm_device *device = pad->i2c->subdev.device; +	struct i2c_algo_bit_data *bit; +#ifndef CONFIG_NOUVEAU_I2C_INTERNAL_DEFAULT +	const bool internal = false; +#else +	const bool internal = true; +#endif +	int ret; + +	bus->func = func; +	bus->pad = pad; +	bus->id = id; +	mutex_init(&bus->mutex); +	list_add_tail(&bus->head, &pad->i2c->bus); +	BUS_TRACE(bus, "ctor"); + +	snprintf(bus->i2c.name, sizeof(bus->i2c.name), "nvkm-%s-bus-%04x", +		 dev_name(device->dev), id); +	bus->i2c.owner = THIS_MODULE; +	bus->i2c.dev.parent = device->dev; + +	if ( bus->func->drive_scl && +	    !nvkm_boolopt(device->cfgopt, "NvI2C", internal)) { +		if (!(bit = kzalloc(sizeof(*bit), GFP_KERNEL))) +			return -ENOMEM; +		bit->udelay = 10; +		bit->timeout = usecs_to_jiffies(2200); +		bit->data = bus; +		bit->pre_xfer = nvkm_i2c_bus_pre_xfer; +		bit->post_xfer = nvkm_i2c_bus_post_xfer; +		bit->setscl = nvkm_i2c_bus_setscl; +		bit->setsda = nvkm_i2c_bus_setsda; +		bit->getscl = nvkm_i2c_bus_getscl; +		bit->getsda = nvkm_i2c_bus_getsda; +		bus->i2c.algo_data = bit; +		ret = i2c_bit_add_bus(&bus->i2c); +	} else { +		bus->i2c.algo = &nvkm_i2c_bus_algo; +		ret = i2c_add_adapter(&bus->i2c); +	} + +	return ret; +} + +int +nvkm_i2c_bus_new_(const struct nvkm_i2c_bus_func *func, +		  struct nvkm_i2c_pad *pad, int id, +		  struct nvkm_i2c_bus **pbus) +{ +	if (!(*pbus = kzalloc(sizeof(**pbus), GFP_KERNEL))) +		return -ENOMEM; +	return nvkm_i2c_bus_ctor(func, pad, id, *pbus); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.h b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.h new file mode 100644 index 000000000000..e1be14c23e54 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/bus.h @@ -0,0 +1,37 @@ +#ifndef __NVKM_I2C_BUS_H__ +#define __NVKM_I2C_BUS_H__ +#include "pad.h" + +struct nvkm_i2c_bus_func { +	void (*init)(struct nvkm_i2c_bus *); +	void (*drive_scl)(struct nvkm_i2c_bus *, int state); +	void (*drive_sda)(struct nvkm_i2c_bus *, int state); +	int (*sense_scl)(struct nvkm_i2c_bus *); +	int (*sense_sda)(struct nvkm_i2c_bus *); +	int (*xfer)(struct nvkm_i2c_bus *, struct i2c_msg *, int num); +}; + +int nvkm_i2c_bus_ctor(const struct nvkm_i2c_bus_func *, struct nvkm_i2c_pad *, +		      int id, struct nvkm_i2c_bus *); +int nvkm_i2c_bus_new_(const struct nvkm_i2c_bus_func *, struct nvkm_i2c_pad *, +		      int id, struct nvkm_i2c_bus **); +void nvkm_i2c_bus_del(struct nvkm_i2c_bus **); +void nvkm_i2c_bus_init(struct nvkm_i2c_bus *); + +int nvkm_i2c_bit_xfer(struct nvkm_i2c_bus *, struct i2c_msg *, int); + +int nv04_i2c_bus_new(struct nvkm_i2c_pad *, int, u8, u8, +		     struct nvkm_i2c_bus **); + +int nv4e_i2c_bus_new(struct nvkm_i2c_pad *, int, u8, struct nvkm_i2c_bus **); +int nv50_i2c_bus_new(struct nvkm_i2c_pad *, int, u8, struct nvkm_i2c_bus **); +int gf119_i2c_bus_new(struct nvkm_i2c_pad *, int, u8, struct nvkm_i2c_bus **); + +#define BUS_MSG(b,l,f,a...) do {                                               \ +	struct nvkm_i2c_bus *_bus = (b);                                       \ +	nvkm_##l(&_bus->pad->i2c->subdev, "bus %04x: "f"\n", _bus->id, ##a);   \ +} while(0) +#define BUS_ERR(b,f,a...) BUS_MSG((b), error, f, ##a) +#define BUS_DBG(b,f,a...) BUS_MSG((b), debug, f, ##a) +#define BUS_TRACE(b,f,a...) BUS_MSG((b), trace, f, ##a) +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busgf119.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busgf119.c new file mode 100644 index 000000000000..96bbdda0f439 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busgf119.c @@ -0,0 +1,95 @@ +/* + * Copyright 2015 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 busions 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 <[email protected]> + */ +#define gf119_i2c_bus(p) container_of((p), struct gf119_i2c_bus, base) +#include "bus.h" + +struct gf119_i2c_bus { +	struct nvkm_i2c_bus base; +	u32 addr; +}; + +static void +gf119_i2c_bus_drive_scl(struct nvkm_i2c_bus *base, int state) +{ +	struct gf119_i2c_bus *bus = gf119_i2c_bus(base); +	struct nvkm_device *device = bus->base.pad->i2c->subdev.device; +	nvkm_mask(device, bus->addr, 0x00000001, state ? 0x00000001 : 0); +} + +static void +gf119_i2c_bus_drive_sda(struct nvkm_i2c_bus *base, int state) +{ +	struct gf119_i2c_bus *bus = gf119_i2c_bus(base); +	struct nvkm_device *device = bus->base.pad->i2c->subdev.device; +	nvkm_mask(device, bus->addr, 0x00000002, state ? 0x00000002 : 0); +} + +static int +gf119_i2c_bus_sense_scl(struct nvkm_i2c_bus *base) +{ +	struct gf119_i2c_bus *bus = gf119_i2c_bus(base); +	struct nvkm_device *device = bus->base.pad->i2c->subdev.device; +	return !!(nvkm_rd32(device, bus->addr) & 0x00000010); +} + +static int +gf119_i2c_bus_sense_sda(struct nvkm_i2c_bus *base) +{ +	struct gf119_i2c_bus *bus = gf119_i2c_bus(base); +	struct nvkm_device *device = bus->base.pad->i2c->subdev.device; +	return !!(nvkm_rd32(device, bus->addr) & 0x00000020); +} + +static void +gf119_i2c_bus_init(struct nvkm_i2c_bus *base) +{ +	struct gf119_i2c_bus *bus = gf119_i2c_bus(base); +	struct nvkm_device *device = bus->base.pad->i2c->subdev.device; +	nvkm_wr32(device, bus->addr, 0x00000007); +} + +static const struct nvkm_i2c_bus_func +gf119_i2c_bus_func = { +	.init = gf119_i2c_bus_init, +	.drive_scl = gf119_i2c_bus_drive_scl, +	.drive_sda = gf119_i2c_bus_drive_sda, +	.sense_scl = gf119_i2c_bus_sense_scl, +	.sense_sda = gf119_i2c_bus_sense_sda, +	.xfer = nvkm_i2c_bit_xfer, +}; + +int +gf119_i2c_bus_new(struct nvkm_i2c_pad *pad, int id, u8 drive, +		 struct nvkm_i2c_bus **pbus) +{ +	struct gf119_i2c_bus *bus; + +	if (!(bus = kzalloc(sizeof(*bus), GFP_KERNEL))) +		return -ENOMEM; +	*pbus = &bus->base; + +	nvkm_i2c_bus_ctor(&gf119_i2c_bus_func, pad, id, &bus->base); +	bus->addr = 0x00d014 + (drive * 0x20); +	return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv04.c new file mode 100644 index 000000000000..a58db159231f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv04.c @@ -0,0 +1,96 @@ +/* + * Copyright 2015 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 busions 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 <[email protected]> + */ +#define nv04_i2c_bus(p) container_of((p), struct nv04_i2c_bus, base) +#include "bus.h" + +#include <subdev/vga.h> + +struct nv04_i2c_bus { +	struct nvkm_i2c_bus base; +	u8 drive; +	u8 sense; +}; + +static void +nv04_i2c_bus_drive_scl(struct nvkm_i2c_bus *base, int state) +{ +	struct nv04_i2c_bus *bus = nv04_i2c_bus(base); +	struct nvkm_device *device = bus->base.pad->i2c->subdev.device; +	u8 val = nvkm_rdvgac(device, 0, bus->drive); +	if (state) val |= 0x20; +	else	   val &= 0xdf; +	nvkm_wrvgac(device, 0, bus->drive, val | 0x01); +} + +static void +nv04_i2c_bus_drive_sda(struct nvkm_i2c_bus *base, int state) +{ +	struct nv04_i2c_bus *bus = nv04_i2c_bus(base); +	struct nvkm_device *device = bus->base.pad->i2c->subdev.device; +	u8 val = nvkm_rdvgac(device, 0, bus->drive); +	if (state) val |= 0x10; +	else	   val &= 0xef; +	nvkm_wrvgac(device, 0, bus->drive, val | 0x01); +} + +static int +nv04_i2c_bus_sense_scl(struct nvkm_i2c_bus *base) +{ +	struct nv04_i2c_bus *bus = nv04_i2c_bus(base); +	struct nvkm_device *device = bus->base.pad->i2c->subdev.device; +	return !!(nvkm_rdvgac(device, 0, bus->sense) & 0x04); +} + +static int +nv04_i2c_bus_sense_sda(struct nvkm_i2c_bus *base) +{ +	struct nv04_i2c_bus *bus = nv04_i2c_bus(base); +	struct nvkm_device *device = bus->base.pad->i2c->subdev.device; +	return !!(nvkm_rdvgac(device, 0, bus->sense) & 0x08); +} + +static const struct nvkm_i2c_bus_func +nv04_i2c_bus_func = { +	.drive_scl = nv04_i2c_bus_drive_scl, +	.drive_sda = nv04_i2c_bus_drive_sda, +	.sense_scl = nv04_i2c_bus_sense_scl, +	.sense_sda = nv04_i2c_bus_sense_sda, +	.xfer = nvkm_i2c_bit_xfer, +}; + +int +nv04_i2c_bus_new(struct nvkm_i2c_pad *pad, int id, u8 drive, u8 sense, +		 struct nvkm_i2c_bus **pbus) +{ +	struct nv04_i2c_bus *bus; + +	if (!(bus = kzalloc(sizeof(*bus), GFP_KERNEL))) +		return -ENOMEM; +	*pbus = &bus->base; + +	nvkm_i2c_bus_ctor(&nv04_i2c_bus_func, pad, id, &bus->base); +	bus->drive = drive; +	bus->sense = sense; +	return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv4e.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv4e.c new file mode 100644 index 000000000000..cdd73dcb1197 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv4e.c @@ -0,0 +1,86 @@ +/* + * Copyright 2015 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 busions 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 <[email protected]> + */ +#define nv4e_i2c_bus(p) container_of((p), struct nv4e_i2c_bus, base) +#include "bus.h" + +struct nv4e_i2c_bus { +	struct nvkm_i2c_bus base; +	u32 addr; +}; + +static void +nv4e_i2c_bus_drive_scl(struct nvkm_i2c_bus *base, int state) +{ +	struct nv4e_i2c_bus *bus = nv4e_i2c_bus(base); +	struct nvkm_device *device = bus->base.pad->i2c->subdev.device; +	nvkm_mask(device, bus->addr, 0x2f, state ? 0x21 : 0x01); +} + +static void +nv4e_i2c_bus_drive_sda(struct nvkm_i2c_bus *base, int state) +{ +	struct nv4e_i2c_bus *bus = nv4e_i2c_bus(base); +	struct nvkm_device *device = bus->base.pad->i2c->subdev.device; +	nvkm_mask(device, bus->addr, 0x1f, state ? 0x11 : 0x01); +} + +static int +nv4e_i2c_bus_sense_scl(struct nvkm_i2c_bus *base) +{ +	struct nv4e_i2c_bus *bus = nv4e_i2c_bus(base); +	struct nvkm_device *device = bus->base.pad->i2c->subdev.device; +	return !!(nvkm_rd32(device, bus->addr) & 0x00040000); +} + +static int +nv4e_i2c_bus_sense_sda(struct nvkm_i2c_bus *base) +{ +	struct nv4e_i2c_bus *bus = nv4e_i2c_bus(base); +	struct nvkm_device *device = bus->base.pad->i2c->subdev.device; +	return !!(nvkm_rd32(device, bus->addr) & 0x00080000); +} + +static const struct nvkm_i2c_bus_func +nv4e_i2c_bus_func = { +	.drive_scl = nv4e_i2c_bus_drive_scl, +	.drive_sda = nv4e_i2c_bus_drive_sda, +	.sense_scl = nv4e_i2c_bus_sense_scl, +	.sense_sda = nv4e_i2c_bus_sense_sda, +	.xfer = nvkm_i2c_bit_xfer, +}; + +int +nv4e_i2c_bus_new(struct nvkm_i2c_pad *pad, int id, u8 drive, +		 struct nvkm_i2c_bus **pbus) +{ +	struct nv4e_i2c_bus *bus; + +	if (!(bus = kzalloc(sizeof(*bus), GFP_KERNEL))) +		return -ENOMEM; +	*pbus = &bus->base; + +	nvkm_i2c_bus_ctor(&nv4e_i2c_bus_func, pad, id, &bus->base); +	bus->addr = 0x600800 + drive; +	return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv50.c new file mode 100644 index 000000000000..8db8399381ca --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/busnv50.c @@ -0,0 +1,113 @@ +/* + * Copyright 2015 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 busions 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 <[email protected]> + */ +#define nv50_i2c_bus(p) container_of((p), struct nv50_i2c_bus, base) +#include "bus.h" + +#include <subdev/vga.h> + +struct nv50_i2c_bus { +	struct nvkm_i2c_bus base; +	u32 addr; +	u32 data; +}; + +static void +nv50_i2c_bus_drive_scl(struct nvkm_i2c_bus *base, int state) +{ +	struct nv50_i2c_bus *bus = nv50_i2c_bus(base); +	struct nvkm_device *device = bus->base.pad->i2c->subdev.device; +	if (state) bus->data |= 0x01; +	else	   bus->data &= 0xfe; +	nvkm_wr32(device, bus->addr, bus->data); +} + +static void +nv50_i2c_bus_drive_sda(struct nvkm_i2c_bus *base, int state) +{ +	struct nv50_i2c_bus *bus = nv50_i2c_bus(base); +	struct nvkm_device *device = bus->base.pad->i2c->subdev.device; +	if (state) bus->data |= 0x02; +	else	   bus->data &= 0xfd; +	nvkm_wr32(device, bus->addr, bus->data); +} + +static int +nv50_i2c_bus_sense_scl(struct nvkm_i2c_bus *base) +{ +	struct nv50_i2c_bus *bus = nv50_i2c_bus(base); +	struct nvkm_device *device = bus->base.pad->i2c->subdev.device; +	return !!(nvkm_rd32(device, bus->addr) & 0x00000001); +} + +static int +nv50_i2c_bus_sense_sda(struct nvkm_i2c_bus *base) +{ +	struct nv50_i2c_bus *bus = nv50_i2c_bus(base); +	struct nvkm_device *device = bus->base.pad->i2c->subdev.device; +	return !!(nvkm_rd32(device, bus->addr) & 0x00000002); +} + +static void +nv50_i2c_bus_init(struct nvkm_i2c_bus *base) +{ +	struct nv50_i2c_bus *bus = nv50_i2c_bus(base); +	struct nvkm_device *device = bus->base.pad->i2c->subdev.device; +	nvkm_wr32(device, bus->addr, (bus->data = 0x00000007)); +} + +static const struct nvkm_i2c_bus_func +nv50_i2c_bus_func = { +	.init = nv50_i2c_bus_init, +	.drive_scl = nv50_i2c_bus_drive_scl, +	.drive_sda = nv50_i2c_bus_drive_sda, +	.sense_scl = nv50_i2c_bus_sense_scl, +	.sense_sda = nv50_i2c_bus_sense_sda, +	.xfer = nvkm_i2c_bit_xfer, +}; + +int +nv50_i2c_bus_new(struct nvkm_i2c_pad *pad, int id, u8 drive, +		 struct nvkm_i2c_bus **pbus) +{ +	static const u32 addr[] = { +		0x00e138, 0x00e150, 0x00e168, 0x00e180, +		0x00e254, 0x00e274, 0x00e764, 0x00e780, +		0x00e79c, 0x00e7b8 +	}; +	struct nv50_i2c_bus *bus; + +	if (drive >= ARRAY_SIZE(addr)) { +		nvkm_warn(&pad->i2c->subdev, "bus %d unknown\n", drive); +		return -ENODEV; +	} + +	if (!(bus = kzalloc(sizeof(*bus), GFP_KERNEL))) +		return -ENOMEM; +	*pbus = &bus->base; + +	nvkm_i2c_bus_ctor(&nv50_i2c_bus_func, pad, id, &bus->base); +	bus->addr = addr[drive]; +	bus->data = 0x00000007; +	return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/g94.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/g94.c index 2a2dd47b9835..bb2a31d88161 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/g94.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/g94.c @@ -21,26 +21,29 @@   *   * Authors: Ben Skeggs   */ -#include "nv50.h" +#include "priv.h" +#include "pad.h"  void  g94_aux_stat(struct nvkm_i2c *i2c, u32 *hi, u32 *lo, u32 *rq, u32 *tx)  { -	u32 intr = nv_rd32(i2c, 0x00e06c); -	u32 stat = nv_rd32(i2c, 0x00e068) & intr, i; +	struct nvkm_device *device = i2c->subdev.device; +	u32 intr = nvkm_rd32(device, 0x00e06c); +	u32 stat = nvkm_rd32(device, 0x00e068) & intr, i;  	for (i = 0, *hi = *lo = *rq = *tx = 0; i < 8; i++) {  		if ((stat & (1 << (i * 4)))) *hi |= 1 << i;  		if ((stat & (2 << (i * 4)))) *lo |= 1 << i;  		if ((stat & (4 << (i * 4)))) *rq |= 1 << i;  		if ((stat & (8 << (i * 4)))) *tx |= 1 << i;  	} -	nv_wr32(i2c, 0x00e06c, intr); +	nvkm_wr32(device, 0x00e06c, intr);  }  void  g94_aux_mask(struct nvkm_i2c *i2c, u32 type, u32 mask, u32 data)  { -	u32 temp = nv_rd32(i2c, 0x00e068), i; +	struct nvkm_device *device = i2c->subdev.device; +	u32 temp = nvkm_rd32(device, 0x00e068), i;  	for (i = 0; i < 8; i++) {  		if (mask & (1 << i)) {  			if (!(data & (1 << i))) { @@ -50,230 +53,20 @@ g94_aux_mask(struct nvkm_i2c *i2c, u32 type, u32 mask, u32 data)  			temp |= type << (i * 4);  		}  	} -	nv_wr32(i2c, 0x00e068, temp); -} - -#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 nvkm_i2c *aux, int ch) -{ -	nv_mask(aux, 0x00e4e4 + (ch * 0x50), 0x00310000, 0x00000000); -} - -static int -auxch_init(struct nvkm_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\n", 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; -} - -int -g94_aux(struct nvkm_i2c_port *base, bool retry, -	 u8 type, u32 addr, u8 *data, u8 size) -{ -	struct nvkm_i2c *aux = nvkm_i2c(base); -	struct nv50_i2c_port *port = (void *)base; -	u32 ctrl, stat, timeout, retries; -	u32 xbuf[4] = {}; -	int ch = port->addr; -	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); - -	/* (maybe) retry transaction a number of times on failure... */ -	for (retries = 0; !ret && 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); -				ret = -EIO; -				goto out; -			} -		} while (ctrl & 0x00010000); -		ret = 1; - -		/* read status, and check if transaction completed ok */ -		stat = nv_mask(aux, 0x00e4e8 + (ch * 0x50), 0, 0); -		if ((stat & 0x000f0000) == 0x00080000 || -		    (stat & 0x000f0000) == 0x00020000) -			ret = retry ? 0 : 1; -		if ((stat & 0x00000100)) -			ret = -ETIMEDOUT; -		if ((stat & 0x00000e00)) -			ret = -EIO; - -		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 < 0 ? ret : (stat & 0x000f0000) >> 16; +	nvkm_wr32(device, 0x00e068, temp);  }  static const struct nvkm_i2c_func -g94_i2c_func = { -	.drive_scl = nv50_i2c_drive_scl, -	.drive_sda = nv50_i2c_drive_sda, -	.sense_scl = nv50_i2c_sense_scl, -	.sense_sda = nv50_i2c_sense_sda, -}; - -static int -g94_i2c_port_ctor(struct nvkm_object *parent, struct nvkm_object *engine, -		  struct nvkm_oclass *oclass, void *data, u32 index, -		  struct nvkm_object **pobject) -{ -	struct dcb_i2c_entry *info = data; -	struct nv50_i2c_port *port; -	int ret; - -	ret = nvkm_i2c_port_create(parent, engine, oclass, index, -				   &nvkm_i2c_bit_algo, &g94_i2c_func, &port); -	*pobject = nv_object(port); -	if (ret) -		return ret; - -	if (info->drive >= nv50_i2c_addr_nr) -		return -EINVAL; - -	port->state = 7; -	port->addr = nv50_i2c_addr[info->drive]; -	return 0; -} - -static const struct nvkm_i2c_func -g94_aux_func = { -	.aux       = g94_aux, +g94_i2c = { +	.pad_x_new = g94_i2c_pad_x_new, +	.pad_s_new = g94_i2c_pad_s_new, +	.aux = 4, +	.aux_stat = g94_aux_stat, +	.aux_mask = g94_aux_mask,  };  int -g94_aux_port_ctor(struct nvkm_object *parent, struct nvkm_object *engine, -		  struct nvkm_oclass *oclass, void *data, u32 index, -		  struct nvkm_object **pobject) +g94_i2c_new(struct nvkm_device *device, int index, struct nvkm_i2c **pi2c)  { -	struct dcb_i2c_entry *info = data; -	struct nv50_i2c_port *port; -	int ret; - -	ret = nvkm_i2c_port_create(parent, engine, oclass, index, -				   &nvkm_i2c_aux_algo, &g94_aux_func, &port); -	*pobject = nv_object(port); -	if (ret) -		return ret; - -	port->base.aux = info->auxch; -	port->addr = info->auxch; -	return 0; +	return nvkm_i2c_new_(&g94_i2c, device, index, pi2c);  } - -static struct nvkm_oclass -g94_i2c_sclass[] = { -	{ .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT), -	  .ofuncs = &(struct nvkm_ofuncs) { -		  .ctor = g94_i2c_port_ctor, -		  .dtor = _nvkm_i2c_port_dtor, -		  .init = nv50_i2c_port_init, -		  .fini = _nvkm_i2c_port_fini, -	  }, -	}, -	{ .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX), -	  .ofuncs = &(struct nvkm_ofuncs) { -		  .ctor = g94_aux_port_ctor, -		  .dtor = _nvkm_i2c_port_dtor, -		  .init = _nvkm_i2c_port_init, -		  .fini = _nvkm_i2c_port_fini, -	  }, -	}, -	{} -}; - -struct nvkm_oclass * -g94_i2c_oclass = &(struct nvkm_i2c_impl) { -	.base.handle = NV_SUBDEV(I2C, 0x94), -	.base.ofuncs = &(struct nvkm_ofuncs) { -		.ctor = _nvkm_i2c_ctor, -		.dtor = _nvkm_i2c_dtor, -		.init = _nvkm_i2c_init, -		.fini = _nvkm_i2c_fini, -	}, -	.sclass = g94_i2c_sclass, -	.pad_x = &nv04_i2c_pad_oclass, -	.pad_s = &g94_i2c_pad_oclass, -	.aux = 4, -	.aux_stat = g94_aux_stat, -	.aux_mask = g94_aux_mask, -}.base; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf110.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf110.c deleted file mode 100644 index 4d4ac6638140..000000000000 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf110.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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 "nv50.h" - -static int -gf110_i2c_sense_scl(struct nvkm_i2c_port *base) -{ -	struct nv50_i2c_priv *priv = (void *)nvkm_i2c(base); -	struct nv50_i2c_port *port = (void *)base; -	return !!(nv_rd32(priv, port->addr) & 0x00000010); -} - -static int -gf110_i2c_sense_sda(struct nvkm_i2c_port *base) -{ -	struct nv50_i2c_priv *priv = (void *)nvkm_i2c(base); -	struct nv50_i2c_port *port = (void *)base; -	return !!(nv_rd32(priv, port->addr) & 0x00000020); -} - -static const struct nvkm_i2c_func -gf110_i2c_func = { -	.drive_scl = nv50_i2c_drive_scl, -	.drive_sda = nv50_i2c_drive_sda, -	.sense_scl = gf110_i2c_sense_scl, -	.sense_sda = gf110_i2c_sense_sda, -}; - -int -gf110_i2c_port_ctor(struct nvkm_object *parent, struct nvkm_object *engine, -		    struct nvkm_oclass *oclass, void *data, u32 index, -		    struct nvkm_object **pobject) -{ -	struct dcb_i2c_entry *info = data; -	struct nv50_i2c_port *port; -	int ret; - -	ret = nvkm_i2c_port_create(parent, engine, oclass, index, -				   &nvkm_i2c_bit_algo, &gf110_i2c_func, &port); -	*pobject = nv_object(port); -	if (ret) -		return ret; - -	port->state = 0x00000007; -	port->addr = 0x00d014 + (info->drive * 0x20); -	return 0; -} - -struct nvkm_oclass -gf110_i2c_sclass[] = { -	{ .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT), -	  .ofuncs = &(struct nvkm_ofuncs) { -		  .ctor = gf110_i2c_port_ctor, -		  .dtor = _nvkm_i2c_port_dtor, -		  .init = nv50_i2c_port_init, -		  .fini = _nvkm_i2c_port_fini, -	  }, -	}, -	{ .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX), -	  .ofuncs = &(struct nvkm_ofuncs) { -		  .ctor = g94_aux_port_ctor, -		  .dtor = _nvkm_i2c_port_dtor, -		  .init = _nvkm_i2c_port_init, -		  .fini = _nvkm_i2c_port_fini, -	  }, -	}, -	{} -}; - -struct nvkm_oclass * -gf110_i2c_oclass = &(struct nvkm_i2c_impl) { -	.base.handle = NV_SUBDEV(I2C, 0xd0), -	.base.ofuncs = &(struct nvkm_ofuncs) { -		.ctor = _nvkm_i2c_ctor, -		.dtor = _nvkm_i2c_dtor, -		.init = _nvkm_i2c_init, -		.fini = _nvkm_i2c_fini, -	}, -	.sclass = gf110_i2c_sclass, -	.pad_x = &nv04_i2c_pad_oclass, -	.pad_s = &g94_i2c_pad_oclass, -	.aux = 4, -	.aux_stat = g94_aux_stat, -	.aux_mask = g94_aux_mask, -}.base; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf117.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf117.c index e290b40f2d13..ae4aad3fcd2e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf117.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf117.c @@ -21,18 +21,16 @@   *   * Authors: Ben Skeggs   */ -#include "nv50.h" +#include "priv.h" +#include "pad.h" -struct nvkm_oclass * -gf117_i2c_oclass = &(struct nvkm_i2c_impl) { -	.base.handle = NV_SUBDEV(I2C, 0xd7), -	.base.ofuncs = &(struct nvkm_ofuncs) { -		.ctor = _nvkm_i2c_ctor, -		.dtor = _nvkm_i2c_dtor, -		.init = _nvkm_i2c_init, -		.fini = _nvkm_i2c_fini, -	}, -	.sclass = gf110_i2c_sclass, -	.pad_x = &nv04_i2c_pad_oclass, -	.pad_s = &nv04_i2c_pad_oclass, -}.base; +static const struct nvkm_i2c_func +gf117_i2c = { +	.pad_x_new = gf119_i2c_pad_x_new, +}; + +int +gf117_i2c_new(struct nvkm_device *device, int index, struct nvkm_i2c **pi2c) +{ +	return nvkm_i2c_new_(&gf117_i2c, device, index, pi2c); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf119.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf119.c new file mode 100644 index 000000000000..6f2b02af42c8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gf119.c @@ -0,0 +1,40 @@ +/* + * 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 "priv.h" +#include "pad.h" + +static const struct nvkm_i2c_func +gf119_i2c = { +	.pad_x_new = gf119_i2c_pad_x_new, +	.pad_s_new = gf119_i2c_pad_s_new, +	.aux = 4, +	.aux_stat = g94_aux_stat, +	.aux_mask = g94_aux_mask, +}; + +int +gf119_i2c_new(struct nvkm_device *device, int index, struct nvkm_i2c **pi2c) +{ +	return nvkm_i2c_new_(&gf119_i2c, device, index, pi2c); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk104.c index 1a464903a992..f9f6bf4b66c9 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk104.c @@ -21,26 +21,29 @@   *   * Authors: Ben Skeggs   */ -#include "nv50.h" +#include "priv.h" +#include "pad.h"  void  gk104_aux_stat(struct nvkm_i2c *i2c, u32 *hi, u32 *lo, u32 *rq, u32 *tx)  { -	u32 intr = nv_rd32(i2c, 0x00dc60); -	u32 stat = nv_rd32(i2c, 0x00dc68) & intr, i; +	struct nvkm_device *device = i2c->subdev.device; +	u32 intr = nvkm_rd32(device, 0x00dc60); +	u32 stat = nvkm_rd32(device, 0x00dc68) & intr, i;  	for (i = 0, *hi = *lo = *rq = *tx = 0; i < 8; i++) {  		if ((stat & (1 << (i * 4)))) *hi |= 1 << i;  		if ((stat & (2 << (i * 4)))) *lo |= 1 << i;  		if ((stat & (4 << (i * 4)))) *rq |= 1 << i;  		if ((stat & (8 << (i * 4)))) *tx |= 1 << i;  	} -	nv_wr32(i2c, 0x00dc60, intr); +	nvkm_wr32(device, 0x00dc60, intr);  }  void  gk104_aux_mask(struct nvkm_i2c *i2c, u32 type, u32 mask, u32 data)  { -	u32 temp = nv_rd32(i2c, 0x00dc68), i; +	struct nvkm_device *device = i2c->subdev.device; +	u32 temp = nvkm_rd32(device, 0x00dc68), i;  	for (i = 0; i < 8; i++) {  		if (mask & (1 << i)) {  			if (!(data & (1 << i))) { @@ -50,22 +53,20 @@ gk104_aux_mask(struct nvkm_i2c *i2c, u32 type, u32 mask, u32 data)  			temp |= type << (i * 4);  		}  	} -	nv_wr32(i2c, 0x00dc68, temp); +	nvkm_wr32(device, 0x00dc68, temp);  } -struct nvkm_oclass * -gk104_i2c_oclass = &(struct nvkm_i2c_impl) { -	.base.handle = NV_SUBDEV(I2C, 0xe0), -	.base.ofuncs = &(struct nvkm_ofuncs) { -		.ctor = _nvkm_i2c_ctor, -		.dtor = _nvkm_i2c_dtor, -		.init = _nvkm_i2c_init, -		.fini = _nvkm_i2c_fini, -	}, -	.sclass = gf110_i2c_sclass, -	.pad_x = &nv04_i2c_pad_oclass, -	.pad_s = &g94_i2c_pad_oclass, +static const struct nvkm_i2c_func +gk104_i2c = { +	.pad_x_new = gf119_i2c_pad_x_new, +	.pad_s_new = gf119_i2c_pad_s_new,  	.aux = 4,  	.aux_stat = gk104_aux_stat,  	.aux_mask = gk104_aux_mask, -}.base; +}; + +int +gk104_i2c_new(struct nvkm_device *device, int index, struct nvkm_i2c **pi2c) +{ +	return nvkm_i2c_new_(&gk104_i2c, device, index, pi2c); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gm204.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gm204.c index ab64237b3842..ff9f7d62f6be 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gm204.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gm204.c @@ -21,199 +21,20 @@   *   * Authors: Ben Skeggs   */ -#include "nv50.h" - -#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 nvkm_i2c *aux, int ch) -{ -	nv_mask(aux, 0x00d954 + (ch * 0x50), 0x00310000, 0x00000000); -} - -static int -auxch_init(struct nvkm_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, 0x00d954 + (ch * 0x50)); -		udelay(1); -		if (!timeout--) { -			AUX_ERR("begin idle timeout 0x%08x\n", ctrl); -			return -EBUSY; -		} -	} while (ctrl & 0x03010000); - -	/* set some magic, and wait up to 1ms for it to appear */ -	nv_mask(aux, 0x00d954 + (ch * 0x50), 0x00300000, ureq); -	timeout = 1000; -	do { -		ctrl = nv_rd32(aux, 0x00d954 + (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; -} - -int -gm204_aux(struct nvkm_i2c_port *base, bool retry, -	 u8 type, u32 addr, u8 *data, u8 size) -{ -	struct nvkm_i2c *aux = nvkm_i2c(base); -	struct nv50_i2c_port *port = (void *)base; -	u32 ctrl, stat, timeout, retries; -	u32 xbuf[4] = {}; -	int ch = port->addr; -	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, 0x00d958 + (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, 0x00d930 + (ch * 0x50) + i, xbuf[i / 4]); -		} -	} - -	ctrl  = nv_rd32(aux, 0x00d954 + (ch * 0x50)); -	ctrl &= ~0x0001f0ff; -	ctrl |= type << 12; -	ctrl |= size - 1; -	nv_wr32(aux, 0x00d950 + (ch * 0x50), addr); - -	/* (maybe) retry transaction a number of times on failure... */ -	for (retries = 0; !ret && retries < 32; retries++) { -		/* reset, and delay a while if this is a retry */ -		nv_wr32(aux, 0x00d954 + (ch * 0x50), 0x80000000 | ctrl); -		nv_wr32(aux, 0x00d954 + (ch * 0x50), 0x00000000 | ctrl); -		if (retries) -			udelay(400); - -		/* transaction request, wait up to 1ms for it to complete */ -		nv_wr32(aux, 0x00d954 + (ch * 0x50), 0x00010000 | ctrl); - -		timeout = 1000; -		do { -			ctrl = nv_rd32(aux, 0x00d954 + (ch * 0x50)); -			udelay(1); -			if (!timeout--) { -				AUX_ERR("tx req timeout 0x%08x\n", ctrl); -				ret = -EIO; -				goto out; -			} -		} while (ctrl & 0x00010000); -		ret = 1; - -		/* read status, and check if transaction completed ok */ -		stat = nv_mask(aux, 0x00d958 + (ch * 0x50), 0, 0); -		if ((stat & 0x000f0000) == 0x00080000 || -		    (stat & 0x000f0000) == 0x00020000) -			ret = retry ? 0 : 1; -		if ((stat & 0x00000100)) -			ret = -ETIMEDOUT; -		if ((stat & 0x00000e00)) -			ret = -EIO; - -		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, 0x00d940 + (ch * 0x50) + i); -			AUX_DBG("rd 0x%08x\n", xbuf[i / 4]); -		} -		memcpy(data, xbuf, size); -	} - -out: -	auxch_fini(aux, ch); -	return ret < 0 ? ret : (stat & 0x000f0000) >> 16; -} +#include "priv.h" +#include "pad.h"  static const struct nvkm_i2c_func -gm204_aux_func = { -	.aux       = gm204_aux, +gm204_i2c = { +	.pad_x_new = gf119_i2c_pad_x_new, +	.pad_s_new = gm204_i2c_pad_s_new, +	.aux = 8, +	.aux_stat = gk104_aux_stat, +	.aux_mask = gk104_aux_mask,  };  int -gm204_aux_port_ctor(struct nvkm_object *parent, -		    struct nvkm_object *engine, -		    struct nvkm_oclass *oclass, void *data, u32 index, -		    struct nvkm_object **pobject) +gm204_i2c_new(struct nvkm_device *device, int index, struct nvkm_i2c **pi2c)  { -	struct dcb_i2c_entry *info = data; -	struct nv50_i2c_port *port; -	int ret; - -	ret = nvkm_i2c_port_create(parent, engine, oclass, index, -				   &nvkm_i2c_aux_algo, &gm204_aux_func, &port); -	*pobject = nv_object(port); -	if (ret) -		return ret; - -	port->base.aux = info->auxch; -	port->addr = info->auxch; -	return 0; +	return nvkm_i2c_new_(&gm204_i2c, device, index, pi2c);  } - -struct nvkm_oclass -gm204_i2c_sclass[] = { -	{ .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT), -	  .ofuncs = &(struct nvkm_ofuncs) { -		  .ctor = gf110_i2c_port_ctor, -		  .dtor = _nvkm_i2c_port_dtor, -		  .init = nv50_i2c_port_init, -		  .fini = _nvkm_i2c_port_fini, -	  }, -	}, -	{ .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX), -	  .ofuncs = &(struct nvkm_ofuncs) { -		  .ctor = gm204_aux_port_ctor, -		  .dtor = _nvkm_i2c_port_dtor, -		  .init = _nvkm_i2c_port_init, -		  .fini = _nvkm_i2c_port_fini, -	  }, -	}, -	{} -}; - -struct nvkm_oclass * -gm204_i2c_oclass = &(struct nvkm_i2c_impl) { -	.base.handle = NV_SUBDEV(I2C, 0x24), -	.base.ofuncs = &(struct nvkm_ofuncs) { -		.ctor = _nvkm_i2c_ctor, -		.dtor = _nvkm_i2c_dtor, -		.init = _nvkm_i2c_init, -		.fini = _nvkm_i2c_fini, -	}, -	.sclass = gm204_i2c_sclass, -	.pad_x = &nv04_i2c_pad_oclass, -	.pad_s = &gm204_i2c_pad_oclass, -	.aux = 8, -	.aux_stat = gk104_aux_stat, -	.aux_mask = gk104_aux_mask, -}.base; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv04.c index 4cdf1c489353..18776f49355c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv04.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv04.c @@ -22,107 +22,15 @@   * Authors: Ben Skeggs   */  #include "priv.h" - -#include <subdev/vga.h> - -struct nv04_i2c_priv { -	struct nvkm_i2c base; -}; - -struct nv04_i2c_port { -	struct nvkm_i2c_port base; -	u8 drive; -	u8 sense; -}; - -static void -nv04_i2c_drive_scl(struct nvkm_i2c_port *base, int state) -{ -	struct nv04_i2c_priv *priv = (void *)nvkm_i2c(base); -	struct nv04_i2c_port *port = (void *)base; -	u8 val = nv_rdvgac(priv, 0, port->drive); -	if (state) val |= 0x20; -	else	   val &= 0xdf; -	nv_wrvgac(priv, 0, port->drive, val | 0x01); -} - -static void -nv04_i2c_drive_sda(struct nvkm_i2c_port *base, int state) -{ -	struct nv04_i2c_priv *priv = (void *)nvkm_i2c(base); -	struct nv04_i2c_port *port = (void *)base; -	u8 val = nv_rdvgac(priv, 0, port->drive); -	if (state) val |= 0x10; -	else	   val &= 0xef; -	nv_wrvgac(priv, 0, port->drive, val | 0x01); -} - -static int -nv04_i2c_sense_scl(struct nvkm_i2c_port *base) -{ -	struct nv04_i2c_priv *priv = (void *)nvkm_i2c(base); -	struct nv04_i2c_port *port = (void *)base; -	return !!(nv_rdvgac(priv, 0, port->sense) & 0x04); -} - -static int -nv04_i2c_sense_sda(struct nvkm_i2c_port *base) -{ -	struct nv04_i2c_priv *priv = (void *)nvkm_i2c(base); -	struct nv04_i2c_port *port = (void *)base; -	return !!(nv_rdvgac(priv, 0, port->sense) & 0x08); -} +#include "pad.h"  static const struct nvkm_i2c_func -nv04_i2c_func = { -	.drive_scl = nv04_i2c_drive_scl, -	.drive_sda = nv04_i2c_drive_sda, -	.sense_scl = nv04_i2c_sense_scl, -	.sense_sda = nv04_i2c_sense_sda, +nv04_i2c = { +	.pad_x_new = nv04_i2c_pad_new,  }; -static int -nv04_i2c_port_ctor(struct nvkm_object *parent, struct nvkm_object *engine, -		   struct nvkm_oclass *oclass, void *data, u32 index, -		   struct nvkm_object **pobject) +int +nv04_i2c_new(struct nvkm_device *device, int index, struct nvkm_i2c **pi2c)  { -	struct dcb_i2c_entry *info = data; -	struct nv04_i2c_port *port; -	int ret; - -	ret = nvkm_i2c_port_create(parent, engine, oclass, index, -				   &nvkm_i2c_bit_algo, &nv04_i2c_func, &port); -	*pobject = nv_object(port); -	if (ret) -		return ret; - -	port->drive = info->drive; -	port->sense = info->sense; -	return 0; +	return nvkm_i2c_new_(&nv04_i2c, device, index, pi2c);  } - -static struct nvkm_oclass -nv04_i2c_sclass[] = { -	{ .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NV04_BIT), -	  .ofuncs = &(struct nvkm_ofuncs) { -		  .ctor = nv04_i2c_port_ctor, -		  .dtor = _nvkm_i2c_port_dtor, -		  .init = _nvkm_i2c_port_init, -		  .fini = _nvkm_i2c_port_fini, -	  }, -	}, -	{} -}; - -struct nvkm_oclass * -nv04_i2c_oclass = &(struct nvkm_i2c_impl) { -	.base.handle = NV_SUBDEV(I2C, 0x04), -	.base.ofuncs = &(struct nvkm_ofuncs) { -		.ctor = _nvkm_i2c_ctor, -		.dtor = _nvkm_i2c_dtor, -		.init = _nvkm_i2c_init, -		.fini = _nvkm_i2c_fini, -	}, -	.sclass = nv04_i2c_sclass, -	.pad_x = &nv04_i2c_pad_oclass, -}.base; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv4e.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv4e.c index 046fe5e2ea19..6b762f7cee9e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv4e.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv4e.c @@ -22,99 +22,15 @@   * Authors: Ben Skeggs   */  #include "priv.h" - -#include <subdev/vga.h> - -struct nv4e_i2c_priv { -	struct nvkm_i2c base; -}; - -struct nv4e_i2c_port { -	struct nvkm_i2c_port base; -	u32 addr; -}; - -static void -nv4e_i2c_drive_scl(struct nvkm_i2c_port *base, int state) -{ -	struct nv4e_i2c_priv *priv = (void *)nvkm_i2c(base); -	struct nv4e_i2c_port *port = (void *)base; -	nv_mask(priv, port->addr, 0x2f, state ? 0x21 : 0x01); -} - -static void -nv4e_i2c_drive_sda(struct nvkm_i2c_port *base, int state) -{ -	struct nv4e_i2c_priv *priv = (void *)nvkm_i2c(base); -	struct nv4e_i2c_port *port = (void *)base; -	nv_mask(priv, port->addr, 0x1f, state ? 0x11 : 0x01); -} - -static int -nv4e_i2c_sense_scl(struct nvkm_i2c_port *base) -{ -	struct nv4e_i2c_priv *priv = (void *)nvkm_i2c(base); -	struct nv4e_i2c_port *port = (void *)base; -	return !!(nv_rd32(priv, port->addr) & 0x00040000); -} - -static int -nv4e_i2c_sense_sda(struct nvkm_i2c_port *base) -{ -	struct nv4e_i2c_priv *priv = (void *)nvkm_i2c(base); -	struct nv4e_i2c_port *port = (void *)base; -	return !!(nv_rd32(priv, port->addr) & 0x00080000); -} +#include "pad.h"  static const struct nvkm_i2c_func -nv4e_i2c_func = { -	.drive_scl = nv4e_i2c_drive_scl, -	.drive_sda = nv4e_i2c_drive_sda, -	.sense_scl = nv4e_i2c_sense_scl, -	.sense_sda = nv4e_i2c_sense_sda, +nv4e_i2c = { +	.pad_x_new = nv4e_i2c_pad_new,  }; -static int -nv4e_i2c_port_ctor(struct nvkm_object *parent, struct nvkm_object *engine, -		   struct nvkm_oclass *oclass, void *data, u32 index, -		   struct nvkm_object **pobject) +int +nv4e_i2c_new(struct nvkm_device *device, int index, struct nvkm_i2c **pi2c)  { -	struct dcb_i2c_entry *info = data; -	struct nv4e_i2c_port *port; -	int ret; - -	ret = nvkm_i2c_port_create(parent, engine, oclass, index, -				   &nvkm_i2c_bit_algo, &nv4e_i2c_func, &port); -	*pobject = nv_object(port); -	if (ret) -		return ret; - -	port->addr = 0x600800 + info->drive; -	return 0; +	return nvkm_i2c_new_(&nv4e_i2c, device, index, pi2c);  } - -static struct nvkm_oclass -nv4e_i2c_sclass[] = { -	{ .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NV4E_BIT), -	  .ofuncs = &(struct nvkm_ofuncs) { -		  .ctor = nv4e_i2c_port_ctor, -		  .dtor = _nvkm_i2c_port_dtor, -		  .init = _nvkm_i2c_port_init, -		  .fini = _nvkm_i2c_port_fini, -	  }, -	}, -	{} -}; - -struct nvkm_oclass * -nv4e_i2c_oclass = &(struct nvkm_i2c_impl) { -	.base.handle = NV_SUBDEV(I2C, 0x4e), -	.base.ofuncs = &(struct nvkm_ofuncs) { -		.ctor = _nvkm_i2c_ctor, -		.dtor = _nvkm_i2c_dtor, -		.init = _nvkm_i2c_init, -		.fini = _nvkm_i2c_fini, -	}, -	.sclass = nv4e_i2c_sclass, -	.pad_x = &nv04_i2c_pad_oclass, -}.base; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.c index fba5b26a5682..75640ab97d6a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.c @@ -21,113 +21,16 @@   *   * Authors: Ben Skeggs   */ -#include "nv50.h" - -void -nv50_i2c_drive_scl(struct nvkm_i2c_port *base, int state) -{ -	struct nv50_i2c_priv *priv = (void *)nvkm_i2c(base); -	struct nv50_i2c_port *port = (void *)base; -	if (state) port->state |= 0x01; -	else	   port->state &= 0xfe; -	nv_wr32(priv, port->addr, port->state); -} - -void -nv50_i2c_drive_sda(struct nvkm_i2c_port *base, int state) -{ -	struct nv50_i2c_priv *priv = (void *)nvkm_i2c(base); -	struct nv50_i2c_port *port = (void *)base; -	if (state) port->state |= 0x02; -	else	   port->state &= 0xfd; -	nv_wr32(priv, port->addr, port->state); -} - -int -nv50_i2c_sense_scl(struct nvkm_i2c_port *base) -{ -	struct nv50_i2c_priv *priv = (void *)nvkm_i2c(base); -	struct nv50_i2c_port *port = (void *)base; -	return !!(nv_rd32(priv, port->addr) & 0x00000001); -} - -int -nv50_i2c_sense_sda(struct nvkm_i2c_port *base) -{ -	struct nv50_i2c_priv *priv = (void *)nvkm_i2c(base); -	struct nv50_i2c_port *port = (void *)base; -	return !!(nv_rd32(priv, port->addr) & 0x00000002); -} +#include "priv.h" +#include "pad.h"  static const struct nvkm_i2c_func -nv50_i2c_func = { -	.drive_scl = nv50_i2c_drive_scl, -	.drive_sda = nv50_i2c_drive_sda, -	.sense_scl = nv50_i2c_sense_scl, -	.sense_sda = nv50_i2c_sense_sda, -}; - -const u32 nv50_i2c_addr[] = { -	0x00e138, 0x00e150, 0x00e168, 0x00e180, -	0x00e254, 0x00e274, 0x00e764, 0x00e780, -	0x00e79c, 0x00e7b8 +nv50_i2c = { +	.pad_x_new = nv50_i2c_pad_new,  }; -const int nv50_i2c_addr_nr = ARRAY_SIZE(nv50_i2c_addr); - -static int -nv50_i2c_port_ctor(struct nvkm_object *parent, struct nvkm_object *engine, -		   struct nvkm_oclass *oclass, void *data, u32 index, -		   struct nvkm_object **pobject) -{ -	struct dcb_i2c_entry *info = data; -	struct nv50_i2c_port *port; -	int ret; - -	ret = nvkm_i2c_port_create(parent, engine, oclass, index, -				   &nvkm_i2c_bit_algo, &nv50_i2c_func, &port); -	*pobject = nv_object(port); -	if (ret) -		return ret; - -	if (info->drive >= nv50_i2c_addr_nr) -		return -EINVAL; - -	port->state = 0x00000007; -	port->addr = nv50_i2c_addr[info->drive]; -	return 0; -}  int -nv50_i2c_port_init(struct nvkm_object *object) +nv50_i2c_new(struct nvkm_device *device, int index, struct nvkm_i2c **pi2c)  { -	struct nv50_i2c_priv *priv = (void *)nvkm_i2c(object); -	struct nv50_i2c_port *port = (void *)object; -	nv_wr32(priv, port->addr, port->state); -	return nvkm_i2c_port_init(&port->base); +	return nvkm_i2c_new_(&nv50_i2c, device, index, pi2c);  } - -static struct nvkm_oclass -nv50_i2c_sclass[] = { -	{ .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT), -	  .ofuncs = &(struct nvkm_ofuncs) { -		  .ctor = nv50_i2c_port_ctor, -		  .dtor = _nvkm_i2c_port_dtor, -		  .init = nv50_i2c_port_init, -		  .fini = _nvkm_i2c_port_fini, -	  }, -	}, -	{} -}; - -struct nvkm_oclass * -nv50_i2c_oclass = &(struct nvkm_i2c_impl) { -	.base.handle = NV_SUBDEV(I2C, 0x50), -	.base.ofuncs = &(struct nvkm_ofuncs) { -		.ctor = _nvkm_i2c_ctor, -		.dtor = _nvkm_i2c_dtor, -		.init = _nvkm_i2c_init, -		.fini = _nvkm_i2c_fini, -	}, -	.sclass = nv50_i2c_sclass, -	.pad_x = &nv04_i2c_pad_oclass, -}.base; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.h b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.h deleted file mode 100644 index b3139e721b02..000000000000 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/nv50.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef __NV50_I2C_H__ -#define __NV50_I2C_H__ -#include "priv.h" - -struct nv50_i2c_priv { -	struct nvkm_i2c base; -}; - -struct nv50_i2c_port { -	struct nvkm_i2c_port base; -	u32 addr; -	u32 state; -}; - -extern const u32 nv50_i2c_addr[]; -extern const int nv50_i2c_addr_nr; -int  nv50_i2c_port_init(struct nvkm_object *); -int  nv50_i2c_sense_scl(struct nvkm_i2c_port *); -int  nv50_i2c_sense_sda(struct nvkm_i2c_port *); -void nv50_i2c_drive_scl(struct nvkm_i2c_port *, int state); -void nv50_i2c_drive_sda(struct nvkm_i2c_port *, int state); - -int  g94_aux_port_ctor(struct nvkm_object *, struct nvkm_object *, -			struct nvkm_oclass *, void *, u32, -			struct nvkm_object **); -void g94_i2c_acquire(struct nvkm_i2c_port *); -void g94_i2c_release(struct nvkm_i2c_port *); - -int  gf110_i2c_port_ctor(struct nvkm_object *, struct nvkm_object *, -			struct nvkm_oclass *, void *, u32, -			struct nvkm_object **); -#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.c index a242eeb67829..2c5fcb9c504b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.c @@ -1,5 +1,5 @@  /* - * Copyright 2014 Red Hat Inc. + * Copyright 2015 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"), @@ -19,65 +19,98 @@   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR   * OTHER DEALINGS IN THE SOFTWARE.   * - * Authors: Ben Skeggs + * Authors: Ben Skeggs <[email protected]>   */  #include "pad.h" -int -_nvkm_i2c_pad_fini(struct nvkm_object *object, bool suspend) +static void +nvkm_i2c_pad_mode_locked(struct nvkm_i2c_pad *pad, enum nvkm_i2c_pad_mode mode)  { -	struct nvkm_i2c_pad *pad = (void *)object; -	DBG("-> NULL\n"); -	pad->port = NULL; -	return nvkm_object_fini(&pad->base, suspend); +	PAD_TRACE(pad, "-> %s", (mode == NVKM_I2C_PAD_AUX) ? "aux" : +			      (mode == NVKM_I2C_PAD_I2C) ? "i2c" : "off"); +	if (pad->func->mode) +		pad->func->mode(pad, mode);  } -int -_nvkm_i2c_pad_init(struct nvkm_object *object) +void +nvkm_i2c_pad_mode(struct nvkm_i2c_pad *pad, enum nvkm_i2c_pad_mode mode)  { -	struct nvkm_i2c_pad *pad = (void *)object; -	DBG("-> PORT:%02x\n", pad->next->index); -	pad->port = pad->next; -	return nvkm_object_init(&pad->base); +	PAD_TRACE(pad, "mode %d", mode); +	mutex_lock(&pad->mutex); +	nvkm_i2c_pad_mode_locked(pad, mode); +	pad->mode = mode; +	mutex_unlock(&pad->mutex);  } -int -nvkm_i2c_pad_create_(struct nvkm_object *parent, -		     struct nvkm_object *engine, -		     struct nvkm_oclass *oclass, int index, -		     int size, void **pobject) +void +nvkm_i2c_pad_release(struct nvkm_i2c_pad *pad)  { -	struct nvkm_i2c *i2c = nvkm_i2c(parent); -	struct nvkm_i2c_port *port; -	struct nvkm_i2c_pad *pad; -	int ret; +	PAD_TRACE(pad, "release"); +	if (pad->mode == NVKM_I2C_PAD_OFF) +		nvkm_i2c_pad_mode_locked(pad, pad->mode); +	mutex_unlock(&pad->mutex); +} -	list_for_each_entry(port, &i2c->ports, head) { -		pad = nvkm_i2c_pad(port); -		if (pad->index == index) { -			atomic_inc(&nv_object(pad)->refcount); -			*pobject = pad; -			return 1; +int +nvkm_i2c_pad_acquire(struct nvkm_i2c_pad *pad, enum nvkm_i2c_pad_mode mode) +{ +	PAD_TRACE(pad, "acquire"); +	mutex_lock(&pad->mutex); +	if (pad->mode != mode) { +		if (pad->mode != NVKM_I2C_PAD_OFF) { +			mutex_unlock(&pad->mutex); +			return -EBUSY;  		} +		nvkm_i2c_pad_mode_locked(pad, mode);  	} +	return 0; +} + +void +nvkm_i2c_pad_fini(struct nvkm_i2c_pad *pad) +{ +	PAD_TRACE(pad, "fini"); +	nvkm_i2c_pad_mode_locked(pad, NVKM_I2C_PAD_OFF); +} -	ret = nvkm_object_create_(parent, engine, oclass, 0, size, pobject); -	pad = *pobject; -	if (ret) -		return ret; +void +nvkm_i2c_pad_init(struct nvkm_i2c_pad *pad) +{ +	PAD_TRACE(pad, "init"); +	nvkm_i2c_pad_mode_locked(pad, pad->mode); +} -	pad->index = index; -	return 0; +void +nvkm_i2c_pad_del(struct nvkm_i2c_pad **ppad) +{ +	struct nvkm_i2c_pad *pad = *ppad; +	if (pad) { +		PAD_TRACE(pad, "dtor"); +		list_del(&pad->head); +		kfree(pad); +		pad = NULL; +	} +} + +void +nvkm_i2c_pad_ctor(const struct nvkm_i2c_pad_func *func, struct nvkm_i2c *i2c, +		  int id, struct nvkm_i2c_pad *pad) +{ +	pad->func = func; +	pad->i2c = i2c; +	pad->id = id; +	pad->mode = NVKM_I2C_PAD_OFF; +	mutex_init(&pad->mutex); +	list_add_tail(&pad->head, &i2c->pad); +	PAD_TRACE(pad, "ctor");  }  int -_nvkm_i2c_pad_ctor(struct nvkm_object *parent, struct nvkm_object *engine, -		   struct nvkm_oclass *oclass, void *data, u32 index, -		   struct nvkm_object **pobject) +nvkm_i2c_pad_new_(const struct nvkm_i2c_pad_func *func, struct nvkm_i2c *i2c, +		  int id, struct nvkm_i2c_pad **ppad)  { -	struct nvkm_i2c_pad *pad; -	int ret; -	ret = nvkm_i2c_pad_create(parent, engine, oclass, index, &pad); -	*pobject = nv_object(pad); -	return ret; +	if (!(*ppad = kzalloc(sizeof(**ppad), GFP_KERNEL))) +		return -ENOMEM; +	nvkm_i2c_pad_ctor(func, i2c, id, *ppad); +	return 0;  } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.h b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.h index f3422cc6f8db..9eeb992944c6 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.h @@ -1,56 +1,67 @@  #ifndef __NVKM_I2C_PAD_H__  #define __NVKM_I2C_PAD_H__ -#include "priv.h" +#include <subdev/i2c.h>  struct nvkm_i2c_pad { -	struct nvkm_object base; -	int index; -	struct nvkm_i2c_port *port; -	struct nvkm_i2c_port *next; +	const struct nvkm_i2c_pad_func *func; +	struct nvkm_i2c *i2c; +#define NVKM_I2C_PAD_HYBRID(n) /* 'n' is hw pad index */                     (n) +#define NVKM_I2C_PAD_CCB(n) /* 'n' is ccb index */                 ((n) + 0x100) +#define NVKM_I2C_PAD_EXT(n) /* 'n' is dcb external encoder type */ ((n) + 0x200) +	int id; + +	enum nvkm_i2c_pad_mode { +		NVKM_I2C_PAD_OFF, +		NVKM_I2C_PAD_I2C, +		NVKM_I2C_PAD_AUX, +	} mode; +	struct mutex mutex; +	struct list_head head; +}; + +struct nvkm_i2c_pad_func { +	int (*bus_new_0)(struct nvkm_i2c_pad *, int id, u8 drive, u8 sense, +			 struct nvkm_i2c_bus **); +	int (*bus_new_4)(struct nvkm_i2c_pad *, int id, u8 drive, +			 struct nvkm_i2c_bus **); + +	int (*aux_new_6)(struct nvkm_i2c_pad *, int id, u8 drive, +			 struct nvkm_i2c_aux **); + +	void (*mode)(struct nvkm_i2c_pad *, enum nvkm_i2c_pad_mode);  }; -static inline struct nvkm_i2c_pad * -nvkm_i2c_pad(struct nvkm_i2c_port *port) -{ -	struct nvkm_object *pad = nv_object(port); -	while (!nv_iclass(pad->parent, NV_SUBDEV_CLASS)) -		pad = pad->parent; -	return (void *)pad; -} - -#define nvkm_i2c_pad_create(p,e,o,i,d)                                         \ -	nvkm_i2c_pad_create_((p), (e), (o), (i), sizeof(**d), (void **)d) -#define nvkm_i2c_pad_destroy(p) ({                                             \ -	struct nvkm_i2c_pad *_p = (p);                                         \ -	_nvkm_i2c_pad_dtor(nv_object(_p));                                     \ -}) -#define nvkm_i2c_pad_init(p) ({                                                \ -	struct nvkm_i2c_pad *_p = (p);                                         \ -	_nvkm_i2c_pad_init(nv_object(_p));                                     \ -}) -#define nvkm_i2c_pad_fini(p,s) ({                                              \ -	struct nvkm_i2c_pad *_p = (p);                                         \ -	_nvkm_i2c_pad_fini(nv_object(_p), (s));                                \ -}) - -int nvkm_i2c_pad_create_(struct nvkm_object *, struct nvkm_object *, -			 struct nvkm_oclass *, int index, int, void **); - -int _nvkm_i2c_pad_ctor(struct nvkm_object *, struct nvkm_object *, -		       struct nvkm_oclass *, void *, u32, -		       struct nvkm_object **); -#define _nvkm_i2c_pad_dtor nvkm_object_destroy -int _nvkm_i2c_pad_init(struct nvkm_object *); -int _nvkm_i2c_pad_fini(struct nvkm_object *, bool); - -#ifndef MSG -#define MSG(l,f,a...) do {                                                     \ -	struct nvkm_i2c_pad *_pad = (void *)pad;                               \ -	nv_##l(_pad, "PAD:%c:%02x: "f,                                         \ -	       _pad->index >= 0x100 ? 'X' : 'S',                               \ -	       _pad->index >= 0x100 ? _pad->index - 0x100 : _pad->index, ##a); \ +void nvkm_i2c_pad_ctor(const struct nvkm_i2c_pad_func *, struct nvkm_i2c *, +		       int id, struct nvkm_i2c_pad *); +int nvkm_i2c_pad_new_(const struct nvkm_i2c_pad_func *, struct nvkm_i2c *, +		      int id, struct nvkm_i2c_pad **); +void nvkm_i2c_pad_del(struct nvkm_i2c_pad **); +void nvkm_i2c_pad_init(struct nvkm_i2c_pad *); +void nvkm_i2c_pad_fini(struct nvkm_i2c_pad *); +void nvkm_i2c_pad_mode(struct nvkm_i2c_pad *, enum nvkm_i2c_pad_mode); +int nvkm_i2c_pad_acquire(struct nvkm_i2c_pad *, enum nvkm_i2c_pad_mode); +void nvkm_i2c_pad_release(struct nvkm_i2c_pad *); + +void g94_i2c_pad_mode(struct nvkm_i2c_pad *, enum nvkm_i2c_pad_mode); + +int nv04_i2c_pad_new(struct nvkm_i2c *, int, struct nvkm_i2c_pad **); +int nv4e_i2c_pad_new(struct nvkm_i2c *, int, struct nvkm_i2c_pad **); +int nv50_i2c_pad_new(struct nvkm_i2c *, int, struct nvkm_i2c_pad **); +int g94_i2c_pad_x_new(struct nvkm_i2c *, int, struct nvkm_i2c_pad **); +int gf119_i2c_pad_x_new(struct nvkm_i2c *, int, struct nvkm_i2c_pad **); +int gm204_i2c_pad_x_new(struct nvkm_i2c *, int, struct nvkm_i2c_pad **); + +int g94_i2c_pad_s_new(struct nvkm_i2c *, int, struct nvkm_i2c_pad **); +int gf119_i2c_pad_s_new(struct nvkm_i2c *, int, struct nvkm_i2c_pad **); +int gm204_i2c_pad_s_new(struct nvkm_i2c *, int, struct nvkm_i2c_pad **); + +int anx9805_pad_new(struct nvkm_i2c_bus *, int, u8, struct nvkm_i2c_pad **); + +#define PAD_MSG(p,l,f,a...) do {                                               \ +	struct nvkm_i2c_pad *_pad = (p);                                       \ +	nvkm_##l(&_pad->i2c->subdev, "pad %04x: "f"\n", _pad->id, ##a);        \  } while(0) -#define DBG(f,a...) MSG(debug, f, ##a) -#define ERR(f,a...) MSG(error, f, ##a) -#endif +#define PAD_ERR(p,f,a...) PAD_MSG((p), error, f, ##a) +#define PAD_DBG(p,f,a...) PAD_MSG((p), debug, f, ##a) +#define PAD_TRACE(p,f,a...) PAD_MSG((p), trace, f, ##a)  #endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padg94.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padg94.c index e9832f7a7e38..5904bc5f2d2a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padg94.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padg94.c @@ -22,64 +22,55 @@   * Authors: Ben Skeggs   */  #include "pad.h" +#include "aux.h" +#include "bus.h" -struct g94_i2c_pad { -	struct nvkm_i2c_pad base; -	int addr; -}; - -static int -g94_i2c_pad_fini(struct nvkm_object *object, bool suspend) -{ -	struct nvkm_i2c *i2c = (void *)nvkm_i2c(object); -	struct g94_i2c_pad *pad = (void *)object; -	nv_mask(i2c, 0x00e50c + pad->addr, 0x00000001, 0x00000001); -	return nvkm_i2c_pad_fini(&pad->base, suspend); -} - -static int -g94_i2c_pad_init(struct nvkm_object *object) +void +g94_i2c_pad_mode(struct nvkm_i2c_pad *pad, enum nvkm_i2c_pad_mode mode)  { -	struct nvkm_i2c *i2c = (void *)nvkm_i2c(object); -	struct g94_i2c_pad *pad = (void *)object; +	struct nvkm_subdev *subdev = &pad->i2c->subdev; +	struct nvkm_device *device = subdev->device; +	const u32 base = (pad->id - NVKM_I2C_PAD_HYBRID(0)) * 0x50; -	switch (nv_oclass(pad->base.next)->handle) { -	case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX): -		nv_mask(i2c, 0x00e500 + pad->addr, 0x0000c003, 0x00000002); +	switch (mode) { +	case NVKM_I2C_PAD_OFF: +		nvkm_mask(device, 0x00e50c + base, 0x00000001, 0x00000001); +		break; +	case NVKM_I2C_PAD_I2C: +		nvkm_mask(device, 0x00e500 + base, 0x0000c003, 0x0000c001); +		nvkm_mask(device, 0x00e50c + base, 0x00000001, 0x00000000); +		break; +	case NVKM_I2C_PAD_AUX: +		nvkm_mask(device, 0x00e500 + base, 0x0000c003, 0x00000002); +		nvkm_mask(device, 0x00e50c + base, 0x00000001, 0x00000000);  		break; -	case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT):  	default: -		nv_mask(i2c, 0x00e500 + pad->addr, 0x0000c003, 0x0000c001); +		WARN_ON(1);  		break;  	} - -	nv_mask(i2c, 0x00e50c + pad->addr, 0x00000001, 0x00000000); -	return nvkm_i2c_pad_init(&pad->base);  } -static int -g94_i2c_pad_ctor(struct nvkm_object *parent, struct nvkm_object *engine, -		 struct nvkm_oclass *oclass, void *data, u32 index, -		 struct nvkm_object **pobject) -{ -	struct g94_i2c_pad *pad; -	int ret; - -	ret = nvkm_i2c_pad_create(parent, engine, oclass, index, &pad); -	*pobject = nv_object(pad); -	if (ret) -		return ret; +static const struct nvkm_i2c_pad_func +g94_i2c_pad_s_func = { +	.bus_new_4 = nv50_i2c_bus_new, +	.aux_new_6 = g94_i2c_aux_new, +	.mode = g94_i2c_pad_mode, +}; -	pad->addr = index * 0x50;; -	return 0; +int +g94_i2c_pad_s_new(struct nvkm_i2c *i2c, int id, struct nvkm_i2c_pad **ppad) +{ +	return nvkm_i2c_pad_new_(&g94_i2c_pad_s_func, i2c, id, ppad);  } -struct nvkm_oclass -g94_i2c_pad_oclass = { -	.ofuncs = &(struct nvkm_ofuncs) { -		.ctor = g94_i2c_pad_ctor, -		.dtor = _nvkm_i2c_pad_dtor, -		.init = g94_i2c_pad_init, -		.fini = g94_i2c_pad_fini, -	}, +static const struct nvkm_i2c_pad_func +g94_i2c_pad_x_func = { +	.bus_new_4 = nv50_i2c_bus_new, +	.aux_new_6 = g94_i2c_aux_new,  }; + +int +g94_i2c_pad_x_new(struct nvkm_i2c *i2c, int id, struct nvkm_i2c_pad **ppad) +{ +	return nvkm_i2c_pad_new_(&g94_i2c_pad_x_func, i2c, id, ppad); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgf119.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgf119.c new file mode 100644 index 000000000000..d53212f1aa52 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgf119.c @@ -0,0 +1,51 @@ +/* + * Copyright 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "pad.h" +#include "aux.h" +#include "bus.h" + +static const struct nvkm_i2c_pad_func +gf119_i2c_pad_s_func = { +	.bus_new_4 = gf119_i2c_bus_new, +	.aux_new_6 = g94_i2c_aux_new, +	.mode = g94_i2c_pad_mode, +}; + +int +gf119_i2c_pad_s_new(struct nvkm_i2c *i2c, int id, struct nvkm_i2c_pad **ppad) +{ +	return nvkm_i2c_pad_new_(&gf119_i2c_pad_s_func, i2c, id, ppad); +} + +static const struct nvkm_i2c_pad_func +gf119_i2c_pad_x_func = { +	.bus_new_4 = gf119_i2c_bus_new, +	.aux_new_6 = g94_i2c_aux_new, +}; + +int +gf119_i2c_pad_x_new(struct nvkm_i2c *i2c, int id, struct nvkm_i2c_pad **ppad) +{ +	return nvkm_i2c_pad_new_(&gf119_i2c_pad_x_func, i2c, id, ppad); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgm204.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgm204.c index be590405444d..24a4d760c67b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgm204.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgm204.c @@ -22,64 +22,55 @@   * Authors: Ben Skeggs   */  #include "pad.h" +#include "aux.h" +#include "bus.h" -struct gm204_i2c_pad { -	struct nvkm_i2c_pad base; -	int addr; -}; - -static int -gm204_i2c_pad_fini(struct nvkm_object *object, bool suspend) -{ -	struct nvkm_i2c *i2c = (void *)nvkm_i2c(object); -	struct gm204_i2c_pad *pad = (void *)object; -	nv_mask(i2c, 0x00d97c + pad->addr, 0x00000001, 0x00000001); -	return nvkm_i2c_pad_fini(&pad->base, suspend); -} - -static int -gm204_i2c_pad_init(struct nvkm_object *object) +static void +gm204_i2c_pad_mode(struct nvkm_i2c_pad *pad, enum nvkm_i2c_pad_mode mode)  { -	struct nvkm_i2c *i2c = (void *)nvkm_i2c(object); -	struct gm204_i2c_pad *pad = (void *)object; +	struct nvkm_subdev *subdev = &pad->i2c->subdev; +	struct nvkm_device *device = subdev->device; +	const u32 base = (pad->id - NVKM_I2C_PAD_HYBRID(0)) * 0x50; -	switch (nv_oclass(pad->base.next)->handle) { -	case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX): -		nv_mask(i2c, 0x00d970 + pad->addr, 0x0000c003, 0x00000002); +	switch (mode) { +	case NVKM_I2C_PAD_OFF: +		nvkm_mask(device, 0x00d97c + base, 0x00000001, 0x00000001); +		break; +	case NVKM_I2C_PAD_I2C: +		nvkm_mask(device, 0x00d970 + base, 0x0000c003, 0x0000c001); +		nvkm_mask(device, 0x00d97c + base, 0x00000001, 0x00000000); +		break; +	case NVKM_I2C_PAD_AUX: +		nvkm_mask(device, 0x00d970 + base, 0x0000c003, 0x00000002); +		nvkm_mask(device, 0x00d97c + base, 0x00000001, 0x00000000);  		break; -	case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT):  	default: -		nv_mask(i2c, 0x00d970 + pad->addr, 0x0000c003, 0x0000c001); +		WARN_ON(1);  		break;  	} - -	nv_mask(i2c, 0x00d97c + pad->addr, 0x00000001, 0x00000000); -	return nvkm_i2c_pad_init(&pad->base);  } -static int -gm204_i2c_pad_ctor(struct nvkm_object *parent, struct nvkm_object *engine, -		   struct nvkm_oclass *oclass, void *data, u32 index, -		   struct nvkm_object **pobject) -{ -	struct gm204_i2c_pad *pad; -	int ret; - -	ret = nvkm_i2c_pad_create(parent, engine, oclass, index, &pad); -	*pobject = nv_object(pad); -	if (ret) -		return ret; +static const struct nvkm_i2c_pad_func +gm204_i2c_pad_s_func = { +	.bus_new_4 = gf119_i2c_bus_new, +	.aux_new_6 = gm204_i2c_aux_new, +	.mode = gm204_i2c_pad_mode, +}; -	pad->addr = index * 0x50;; -	return 0; +int +gm204_i2c_pad_s_new(struct nvkm_i2c *i2c, int id, struct nvkm_i2c_pad **ppad) +{ +	return nvkm_i2c_pad_new_(&gm204_i2c_pad_s_func, i2c, id, ppad);  } -struct nvkm_oclass -gm204_i2c_pad_oclass = { -	.ofuncs = &(struct nvkm_ofuncs) { -		.ctor = gm204_i2c_pad_ctor, -		.dtor = _nvkm_i2c_pad_dtor, -		.init = gm204_i2c_pad_init, -		.fini = gm204_i2c_pad_fini, -	}, +static const struct nvkm_i2c_pad_func +gm204_i2c_pad_x_func = { +	.bus_new_4 = gf119_i2c_bus_new, +	.aux_new_6 = gm204_i2c_aux_new,  }; + +int +gm204_i2c_pad_x_new(struct nvkm_i2c *i2c, int id, struct nvkm_i2c_pad **ppad) +{ +	return nvkm_i2c_pad_new_(&gm204_i2c_pad_x_func, i2c, id, ppad); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv04.c index 22c7daaad3a0..310046ad9c61 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv04.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv04.c @@ -22,13 +22,15 @@   * Authors: Ben Skeggs   */  #include "pad.h" +#include "bus.h" -struct nvkm_oclass -nv04_i2c_pad_oclass = { -	.ofuncs = &(struct nvkm_ofuncs) { -		.ctor = _nvkm_i2c_pad_ctor, -		.dtor = _nvkm_i2c_pad_dtor, -		.init = _nvkm_i2c_pad_init, -		.fini = _nvkm_i2c_pad_fini, -	}, +static const struct nvkm_i2c_pad_func +nv04_i2c_pad_func = { +	.bus_new_0 = nv04_i2c_bus_new,  }; + +int +nv04_i2c_pad_new(struct nvkm_i2c *i2c, int id, struct nvkm_i2c_pad **ppad) +{ +	return nvkm_i2c_pad_new_(&nv04_i2c_pad_func, i2c, id, ppad); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv4e.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv4e.c new file mode 100644 index 000000000000..dda6fc0b089d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv4e.c @@ -0,0 +1,36 @@ +/* + * Copyright 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "pad.h" +#include "bus.h" + +static const struct nvkm_i2c_pad_func +nv4e_i2c_pad_func = { +	.bus_new_4 = nv4e_i2c_bus_new, +}; + +int +nv4e_i2c_pad_new(struct nvkm_i2c *i2c, int id, struct nvkm_i2c_pad **ppad) +{ +	return nvkm_i2c_pad_new_(&nv4e_i2c_pad_func, i2c, id, ppad); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv50.c new file mode 100644 index 000000000000..a03f25b1914f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padnv50.c @@ -0,0 +1,36 @@ +/* + * Copyright 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include "pad.h" +#include "bus.h" + +static const struct nvkm_i2c_pad_func +nv50_i2c_pad_func = { +	.bus_new_4 = nv50_i2c_bus_new, +}; + +int +nv50_i2c_pad_new(struct nvkm_i2c *i2c, int id, struct nvkm_i2c_pad **ppad) +{ +	return nvkm_i2c_pad_new_(&nv50_i2c_pad_func, i2c, id, ppad); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/port.h b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/port.h deleted file mode 100644 index 586f53dad813..000000000000 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/port.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __NVKM_I2C_PORT_H__ -#define __NVKM_I2C_PORT_H__ -#include "priv.h" - -#ifndef MSG -#define MSG(l,f,a...) do {                                                     \ -	struct nvkm_i2c_port *_port = (void *)port;                         \ -	nv_##l(_port, "PORT:%02x: "f, _port->index, ##a);                      \ -} while(0) -#define DBG(f,a...) MSG(debug, f, ##a) -#define ERR(f,a...) MSG(error, f, ##a) -#endif -#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/priv.h index 6586e1567fcf..bf655a66ef40 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/priv.h @@ -1,69 +1,14 @@  #ifndef __NVKM_I2C_PRIV_H__  #define __NVKM_I2C_PRIV_H__ +#define nvkm_i2c(p) container_of((p), struct nvkm_i2c, subdev)  #include <subdev/i2c.h> -extern struct nvkm_oclass nv04_i2c_pad_oclass; -extern struct nvkm_oclass g94_i2c_pad_oclass; -extern struct nvkm_oclass gm204_i2c_pad_oclass; +int nvkm_i2c_new_(const struct nvkm_i2c_func *, struct nvkm_device *, +		  int index, struct nvkm_i2c **); -#define nvkm_i2c_port_create(p,e,o,i,a,f,d)                                 \ -	nvkm_i2c_port_create_((p), (e), (o), (i), (a), (f),                 \ -				 sizeof(**d), (void **)d) -#define nvkm_i2c_port_destroy(p) ({                                         \ -	struct nvkm_i2c_port *port = (p);                                   \ -	_nvkm_i2c_port_dtor(nv_object(i2c));                                \ -}) -#define nvkm_i2c_port_init(p)                                               \ -	nvkm_object_init(&(p)->base) -#define nvkm_i2c_port_fini(p,s)                                             \ -	nvkm_object_fini(&(p)->base, (s)) - -int nvkm_i2c_port_create_(struct nvkm_object *, struct nvkm_object *, -			     struct nvkm_oclass *, u8, -			     const struct i2c_algorithm *, -			     const struct nvkm_i2c_func *, -			     int, void **); -void _nvkm_i2c_port_dtor(struct nvkm_object *); -#define _nvkm_i2c_port_init nvkm_object_init -int  _nvkm_i2c_port_fini(struct nvkm_object *, bool); - -#define nvkm_i2c_create(p,e,o,d)                                            \ -	nvkm_i2c_create_((p), (e), (o), sizeof(**d), (void **)d) -#define nvkm_i2c_destroy(p) ({                                              \ -	struct nvkm_i2c *i2c = (p);                                         \ -	_nvkm_i2c_dtor(nv_object(i2c));                                     \ -}) -#define nvkm_i2c_init(p) ({                                                 \ -	struct nvkm_i2c *i2c = (p);                                         \ -	_nvkm_i2c_init(nv_object(i2c));                                     \ -}) -#define nvkm_i2c_fini(p,s) ({                                               \ -	struct nvkm_i2c *i2c = (p);                                         \ -	_nvkm_i2c_fini(nv_object(i2c), (s));                                \ -}) - -int nvkm_i2c_create_(struct nvkm_object *, struct nvkm_object *, -			struct nvkm_oclass *, int, void **); -int  _nvkm_i2c_ctor(struct nvkm_object *, struct nvkm_object *, -		       struct nvkm_oclass *, void *, u32, -		       struct nvkm_object **); -void _nvkm_i2c_dtor(struct nvkm_object *); -int  _nvkm_i2c_init(struct nvkm_object *); -int  _nvkm_i2c_fini(struct nvkm_object *, bool); - -extern struct nvkm_oclass nvkm_anx9805_sclass[]; -extern struct nvkm_oclass gf110_i2c_sclass[]; - -extern const struct i2c_algorithm nvkm_i2c_bit_algo; -extern const struct i2c_algorithm nvkm_i2c_aux_algo; - -struct nvkm_i2c_impl { -	struct nvkm_oclass base; - -	/* supported i2c port classes */ -	struct nvkm_oclass *sclass; -	struct nvkm_oclass *pad_x; -	struct nvkm_oclass *pad_s; +struct nvkm_i2c_func { +	int (*pad_x_new)(struct nvkm_i2c *, int id, struct nvkm_i2c_pad **); +	int (*pad_s_new)(struct nvkm_i2c *, int id, struct nvkm_i2c_pad **);  	/* number of native dp aux channels present */  	int aux;  |