Commit d2ae2eb4 authored by Ben Skeggs's avatar Ben Skeggs
Browse files

drm/nouveau/i2c: balance port acquire/release



This was a half-finished hack before, just enough to handle the shared
aux/i2c pad thing on G94 and up.

We got lucky with locking etc up until now, as this was (generally) all
protected by the DRM mode_config lock.  It's about to become a lot more
likely to hit the races.

Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 0ff32977
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -62,6 +62,8 @@ struct nouveau_i2c {

	struct nouveau_i2c_port *(*find)(struct nouveau_i2c *, u8 index);
	struct nouveau_i2c_port *(*find_type)(struct nouveau_i2c *, u16 type);
	int  (*acquire)(struct nouveau_i2c_port *, unsigned long timeout);
	void (*release)(struct nouveau_i2c_port *);
	int (*identify)(struct nouveau_i2c *, int index,
			const char *what, struct nouveau_i2c_board_info *,
			bool (*match)(struct nouveau_i2c_port *,
+23 −9
Original line number Diff line number Diff line
@@ -27,10 +27,14 @@
int
nv_rdaux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size)
{
	struct nouveau_i2c *i2c = nouveau_i2c(port);
	if (port->func->aux) {
		if (port->func->acquire)
			port->func->acquire(port);
		return port->func->aux(port, true, 9, addr, data, size);
		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;
}
@@ -38,10 +42,14 @@ nv_rdaux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size)
int
nv_wraux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size)
{
	struct nouveau_i2c *i2c = nouveau_i2c(port);
	if (port->func->aux) {
		if (port->func->acquire)
			port->func->acquire(port);
		return port->func->aux(port, true, 8, addr, data, size);
		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;
}
@@ -50,13 +58,16 @@ static int
aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
	struct nouveau_i2c_port *port = adap->algo_data;
	struct nouveau_i2c *i2c = nouveau_i2c(port);
	struct i2c_msg *msg = msgs;
	int ret, mcnt = num;

	if (!port->func->aux)
		return -ENODEV;
	if ( port->func->acquire)
		port->func->acquire(port);

	ret = i2c->acquire(port, 0);
	if (ret)
		return ret;

	while (mcnt--) {
		u8 remaining = msg->len;
@@ -75,8 +86,10 @@ aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
				cmd |= 4; /* MOT */

			ret = port->func->aux(port, true, cmd, msg->addr, ptr, cnt);
			if (ret < 0)
			if (ret < 0) {
				i2c->release(port);
				return ret;
			}

			ptr += cnt;
			remaining -= cnt;
@@ -85,6 +98,7 @@ aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
		msg++;
	}

	i2c->release(port);
	return num;
}

+27 −3
Original line number Diff line number Diff line
@@ -47,9 +47,15 @@ nouveau_i2c_pre_xfer(struct i2c_adapter *adap)
{
	struct i2c_algo_bit_data *bit = adap->algo_data;
	struct nouveau_i2c_port *port = bit->data;
	if (port->func->acquire)
		port->func->acquire(port);
	return 0;
	return nouveau_i2c(port)->acquire(port, bit->timeout);
}

static void
nouveau_i2c_post_xfer(struct i2c_adapter *adap)
{
	struct i2c_algo_bit_data *bit = adap->algo_data;
	struct nouveau_i2c_port *port = bit->data;
	return nouveau_i2c(port)->release(port);
}

static void
@@ -130,6 +136,7 @@ nouveau_i2c_port_create_(struct nouveau_object *parent,
		bit->timeout = usecs_to_jiffies(2200);
		bit->data = port;
		bit->pre_xfer = nouveau_i2c_pre_xfer;
		bit->post_xfer = nouveau_i2c_post_xfer;
		bit->setsda = nouveau_i2c_setsda;
		bit->setscl = nouveau_i2c_setscl;
		bit->getsda = nouveau_i2c_getsda;
@@ -194,6 +201,21 @@ nouveau_i2c_find_type(struct nouveau_i2c *i2c, u16 type)
	return NULL;
}

static void
nouveau_i2c_release(struct nouveau_i2c_port *port)
{
	if (port->func->release)
		port->func->release(port);
}

static int
nouveau_i2c_acquire(struct nouveau_i2c_port *port, unsigned long timeout)
{
	if (port->func->acquire)
		port->func->acquire(port);
	return 0;
}

static int
nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what,
		     struct nouveau_i2c_board_info *info,
@@ -383,6 +405,8 @@ nouveau_i2c_create_(struct nouveau_object *parent,
	nv_subdev(i2c)->intr = nouveau_i2c_intr;
	i2c->find = nouveau_i2c_find;
	i2c->find_type = nouveau_i2c_find_type;
	i2c->acquire = nouveau_i2c_acquire;
	i2c->release = nouveau_i2c_release;
	i2c->identify = nouveau_i2c_identify;
	INIT_LIST_HEAD(&i2c->ports);

+4 −2
Original line number Diff line number Diff line
@@ -187,8 +187,9 @@ i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
	struct i2c_msg *msg = msgs;
	int ret = 0, mcnt = num;

	if (port->func->acquire)
		port->func->acquire(port);
	ret = nouveau_i2c(port)->acquire(port, nsecs_to_jiffies(T_TIMEOUT));
	if (ret)
		return ret;

	while (!ret && mcnt--) {
		u8 remaining = msg->len;
@@ -210,6 +211,7 @@ i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
	}

	i2c_stop(port);
	nouveau_i2c(port)->release(port);
	return (ret < 0) ? ret : num;
}
#else