Commit 2bdb06e3 authored by Ben Skeggs's avatar Ben Skeggs
Browse files

drm/nouveau/i2c: tidy up bit-bang helpers, also fixing nv50 setsda bug



Was using nv_mask, which is bad.  Reading the reg senses the current line
states, which aren't necessarily the states we're trying to drive the
lines to.

Fixed to store SCL driver state just as we already do for SDA.

Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 486a45c2
Loading
Loading
Loading
Loading
+3 −3
Original line number Original line Diff line number Diff line
@@ -578,7 +578,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate)


	dp.dcb = nv_encoder->dcb;
	dp.dcb = nv_encoder->dcb;
	dp.crtc = nv_crtc->index;
	dp.crtc = nv_crtc->index;
	dp.auxch = auxch->rd;
	dp.auxch = auxch->drive;
	dp.or = nv_encoder->or;
	dp.or = nv_encoder->or;
	dp.link = !(nv_encoder->dcb->sorconf.link & 1);
	dp.link = !(nv_encoder->dcb->sorconf.link & 1);
	dp.dpcd = nv_encoder->dp.dpcd;
	dp.dpcd = nv_encoder->dp.dpcd;
@@ -653,7 +653,7 @@ nouveau_dp_detect(struct drm_encoder *encoder)
	if (!auxch)
	if (!auxch)
		return false;
		return false;


	ret = auxch_tx(dev, auxch->rd, 9, DP_DPCD_REV, dpcd, 8);
	ret = auxch_tx(dev, auxch->drive, 9, DP_DPCD_REV, dpcd, 8);
	if (ret)
	if (ret)
		return false;
		return false;


@@ -681,7 +681,7 @@ int
nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
		 uint8_t *data, int data_nr)
		 uint8_t *data, int data_nr)
{
{
	return auxch_tx(auxch->dev, auxch->rd, cmd, addr, data, data_nr);
	return auxch_tx(auxch->dev, auxch->drive, cmd, addr, data, data_nr);
}
}


static int
static int
+88 −142
Original line number Original line Diff line number Diff line
@@ -30,132 +30,83 @@
#include "nouveau_hw.h"
#include "nouveau_hw.h"


static void
static void
nv04_i2c_setscl(void *data, int state)
i2c_drive_scl(void *data, int state)
{
{
	struct nouveau_i2c_chan *i2c = data;
	struct nouveau_i2c_chan *port = data;
	struct drm_device *dev = i2c->dev;
	if (port->type == 0) {
	uint8_t val;
		u8 val = NVReadVgaCrtc(port->dev, 0, port->drive);

		if (state) val |= 0x20;
	val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xd0) | (state ? 0x20 : 0);
		else	   val &= 0xdf;
	NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01);
		NVWriteVgaCrtc(port->dev, 0, port->drive, val | 0x01);
}
	} else

	if (port->type == 4) {
static void
		nv_mask(port->dev, port->drive, 0x2f, state ? 0x21 : 0x01);
nv04_i2c_setsda(void *data, int state)
	} else
{
	if (port->type == 5) {
	struct nouveau_i2c_chan *i2c = data;
		if (state) port->state |= 0x01;
	struct drm_device *dev = i2c->dev;
		else	   port->state &= 0xfe;
	uint8_t val;
		nv_wr32(port->dev, port->drive, 4 | port->state);

	val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xe0) | (state ? 0x10 : 0);
	NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01);
}

static int
nv04_i2c_getscl(void *data)
{
	struct nouveau_i2c_chan *i2c = data;
	struct drm_device *dev = i2c->dev;

	return !!(NVReadVgaCrtc(dev, 0, i2c->rd) & 4);
}

static int
nv04_i2c_getsda(void *data)
{
	struct nouveau_i2c_chan *i2c = data;
	struct drm_device *dev = i2c->dev;

	return !!(NVReadVgaCrtc(dev, 0, i2c->rd) & 8);
	}
	}

static void
nv4e_i2c_setscl(void *data, int state)
{
	struct nouveau_i2c_chan *i2c = data;
	struct drm_device *dev = i2c->dev;
	uint8_t val;

	val = (nv_rd32(dev, i2c->wr) & 0xd0) | (state ? 0x20 : 0);
	nv_wr32(dev, i2c->wr, val | 0x01);
}
}


static void
static void
nv4e_i2c_setsda(void *data, int state)
i2c_drive_sda(void *data, int state)
{
	struct nouveau_i2c_chan *i2c = data;
	struct drm_device *dev = i2c->dev;
	uint8_t val;

	val = (nv_rd32(dev, i2c->wr) & 0xe0) | (state ? 0x10 : 0);
	nv_wr32(dev, i2c->wr, val | 0x01);
}

static int
nv4e_i2c_getscl(void *data)
{
	struct nouveau_i2c_chan *i2c = data;
	struct drm_device *dev = i2c->dev;

	return !!((nv_rd32(dev, i2c->rd) >> 16) & 4);
}

static int
nv4e_i2c_getsda(void *data)
{
{
	struct nouveau_i2c_chan *i2c = data;
	struct nouveau_i2c_chan *port = data;
	struct drm_device *dev = i2c->dev;
	if (port->type == 0) {

		u8 val = NVReadVgaCrtc(port->dev, 0, port->drive);
	return !!((nv_rd32(dev, i2c->rd) >> 16) & 8);
		if (state) val |= 0x10;
		else	   val &= 0xef;
		NVWriteVgaCrtc(port->dev, 0, port->drive, val | 0x01);
	} else
	if (port->type == 4) {
		nv_mask(port->dev, port->drive, 0x1f, state ? 0x11 : 0x01);
	} else
	if (port->type == 5) {
		if (state) port->state |= 0x02;
		else	   port->state &= 0xfd;
		nv_wr32(port->dev, port->drive, 4 | port->state);
	}
	}

static int
nv50_i2c_getscl(void *data)
{
	struct nouveau_i2c_chan *i2c = data;
	struct drm_device *dev = i2c->dev;

	return !!(nv_rd32(dev, i2c->rd) & 1);
}
}


static int
static int
nv50_i2c_getsda(void *data)
i2c_sense_scl(void *data)
{
{
	struct nouveau_i2c_chan *i2c = data;
	struct nouveau_i2c_chan *port = data;
	struct drm_device *dev = i2c->dev;
	struct drm_nouveau_private *dev_priv = port->dev->dev_private;

	if (port->type == 0) {
	return !!(nv_rd32(dev, i2c->rd) & 2);
		return !!(NVReadVgaCrtc(port->dev, 0, port->sense) & 0x04);
}
	} else

	if (port->type == 4) {
static void
		return !!(nv_rd32(port->dev, port->sense) & 0x00040000);
nv50_i2c_setscl(void *data, int state)
	} else
{
	if (port->type == 5) {
	struct nouveau_i2c_chan *i2c = data;
		if (dev_priv->card_type < NV_D0)

			return !!(nv_rd32(port->dev, port->sense) & 0x01);
	nv_wr32(i2c->dev, i2c->wr, 4 | (i2c->data ? 2 : 0) | (state ? 1 : 0));
		else
			return !!(nv_rd32(port->dev, port->sense) & 0x10);
	}
	}

	return 0;
static void
nv50_i2c_setsda(void *data, int state)
{
	struct nouveau_i2c_chan *i2c = data;

	nv_mask(i2c->dev, i2c->wr, 0x00000006, 4 | (state ? 2 : 0));
	i2c->data = state;
}
}


static int
static int
nvd0_i2c_getscl(void *data)
i2c_sense_sda(void *data)
{
{
	struct nouveau_i2c_chan *i2c = data;
	struct nouveau_i2c_chan *port = data;
	return !!(nv_rd32(i2c->dev, i2c->rd) & 0x10);
	struct drm_nouveau_private *dev_priv = port->dev->dev_private;
	if (port->type == 0) {
		return !!(NVReadVgaCrtc(port->dev, 0, port->sense) & 0x08);
	} else
	if (port->type == 4) {
		return !!(nv_rd32(port->dev, port->sense) & 0x00080000);
	} else
	if (port->type == 5) {
		if (dev_priv->card_type < NV_D0)
			return !!(nv_rd32(port->dev, port->sense) & 0x02);
		else
			return !!(nv_rd32(port->dev, port->sense) & 0x20);
	}
	}

	return 0;
static int
nvd0_i2c_getsda(void *data)
{
	struct nouveau_i2c_chan *i2c = data;
	return !!(nv_rd32(i2c->dev, i2c->rd) & 0x20);
}
}


static const uint32_t nv50_i2c_port[] = {
static const uint32_t nv50_i2c_port[] = {
@@ -258,51 +209,37 @@ nouveau_i2c_init(struct drm_device *dev)


		switch (port->type) {
		switch (port->type) {
		case 0: /* NV04:NV50 */
		case 0: /* NV04:NV50 */
			port->wr = entry[0];
			port->drive = entry[0];
			port->rd = entry[1];
			port->sense = entry[1];
			port->bit.setsda = nv04_i2c_setsda;
			port->bit.setscl = nv04_i2c_setscl;
			port->bit.getsda = nv04_i2c_getsda;
			port->bit.getscl = nv04_i2c_getscl;
			break;
			break;
		case 4: /* NV4E */
		case 4: /* NV4E */
			port->wr = 0x600800 + entry[1];
			port->drive = 0x600800 + entry[1];
			port->rd = port->wr;
			port->sense = port->drive;
			port->bit.setsda = nv4e_i2c_setsda;
			port->bit.setscl = nv4e_i2c_setscl;
			port->bit.getsda = nv4e_i2c_getsda;
			port->bit.getscl = nv4e_i2c_getscl;
			break;
			break;
		case 5: /* NV50- */
		case 5: /* NV50- */
			port->wr = entry[0] & 0x0f;
			port->drive = entry[0] & 0x0f;
			if (dev_priv->card_type < NV_D0) {
			if (dev_priv->card_type < NV_D0) {
				if (port->wr >= ARRAY_SIZE(nv50_i2c_port))
				if (port->drive >= ARRAY_SIZE(nv50_i2c_port))
					break;
					break;
				port->wr = nv50_i2c_port[port->wr];
				port->drive = nv50_i2c_port[port->drive];
				port->rd = port->wr;
				port->sense = port->drive;
				port->bit.getsda = nv50_i2c_getsda;
				port->bit.getscl = nv50_i2c_getscl;
			} else {
			} else {
				port->wr = 0x00d014 + (port->wr * 0x20);
				port->drive = 0x00d014 + (port->drive * 0x20);
				port->rd = port->wr;
				port->sense = port->drive;
				port->bit.getsda = nvd0_i2c_getsda;
				port->bit.getscl = nvd0_i2c_getscl;
			}
			}
			port->bit.setsda = nv50_i2c_setsda;
			port->bit.setscl = nv50_i2c_setscl;
			break;
			break;
		case 6: /* NV50- DP AUX */
		case 6: /* NV50- DP AUX */
			port->wr = entry[0];
			port->drive = entry[0];
			port->rd = port->wr;
			port->sense = port->drive;
			port->adapter.algo = &nouveau_dp_i2c_algo;
			port->adapter.algo = &nouveau_dp_i2c_algo;
			break;
			break;
		default:
		default:
			break;
			break;
		}
		}


		if (!port->adapter.algo && !port->wr) {
		if (!port->adapter.algo && !port->drive) {
			NV_ERROR(dev, "I2C%d: type %d index %x/%x unknown\n",
			NV_ERROR(dev, "I2C%d: type %d index %x/%x unknown\n",
				 i, port->type, port->wr, port->rd);
				 i, port->type, port->drive, port->sense);
			kfree(port);
			kfree(port);
			continue;
			continue;
		}
		}
@@ -321,6 +258,15 @@ nouveau_i2c_init(struct drm_device *dev)
			port->bit.udelay = 40;
			port->bit.udelay = 40;
			port->bit.timeout = usecs_to_jiffies(5000);
			port->bit.timeout = usecs_to_jiffies(5000);
			port->bit.data = port;
			port->bit.data = port;
			port->bit.setsda = i2c_drive_sda;
			port->bit.setscl = i2c_drive_scl;
			port->bit.getsda = i2c_sense_sda;
			port->bit.getscl = i2c_sense_scl;

			i2c_drive_scl(port, 0);
			i2c_drive_sda(port, 1);
			i2c_drive_scl(port, 1);

			ret = i2c_bit_add_bus(&port->adapter);
			ret = i2c_bit_add_bus(&port->adapter);
		} else {
		} else {
			port->adapter.algo = &nouveau_dp_i2c_algo;
			port->adapter.algo = &nouveau_dp_i2c_algo;
@@ -381,7 +327,7 @@ nouveau_i2c_find(struct drm_device *dev, u8 index)
	if (dev_priv->card_type >= NV_50 && (port->dcb & 0x00000100)) {
	if (dev_priv->card_type >= NV_50 && (port->dcb & 0x00000100)) {
		u32 reg = 0x00e500, val;
		u32 reg = 0x00e500, val;
		if (port->type == 6) {
		if (port->type == 6) {
			reg += port->rd * 0x50;
			reg += port->drive * 0x50;
			val  = 0x2002;
			val  = 0x2002;
		} else {
		} else {
			reg += ((port->dcb & 0x1e00) >> 9) * 0x50;
			reg += ((port->dcb & 0x1e00) >> 9) * 0x50;
+3 −3
Original line number Original line Diff line number Diff line
@@ -39,9 +39,9 @@ struct nouveau_i2c_chan {
	u8  index;
	u8  index;
	u8  type;
	u8  type;
	u32 dcb;
	u32 dcb;
	unsigned rd;
	u32 drive;
	unsigned wr;
	u32 sense;
	unsigned data;
	u32 state;
};
};


int  nouveau_i2c_init(struct drm_device *);
int  nouveau_i2c_init(struct drm_device *);