Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6d416d80 authored by Francisco Jerez's avatar Francisco Jerez Committed by Ben Skeggs
Browse files

drm/nouveau: Add some generic I2C gadget detection code.



Clean up and move the external TV encoder detection code to
nouveau_i2c.c, it's also going to be useful for external TMDS and DDC
detection.

Signed-off-by: default avatarFrancisco Jerez <currojerez@riseup.net>
Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 6d6a413a
Loading
Loading
Loading
Loading
+3 −19
Original line number Diff line number Diff line
@@ -139,26 +139,10 @@ nouveau_connector_ddc_detect(struct drm_connector *connector,
			     struct nouveau_encoder **pnv_encoder)
{
	struct drm_device *dev = connector->dev;
	uint8_t out_buf[] = { 0x0, 0x0}, buf[2];
	int ret, flags, i;

	struct i2c_msg msgs[] = {
		{
			.addr = 0x50,
			.flags = 0,
			.len = 1,
			.buf = out_buf,
		},
		{
			.addr = 0x50,
			.flags = I2C_M_RD,
			.len = 1,
			.buf = buf,
		}
	};

	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
		struct nouveau_i2c_chan *i2c = NULL;
		struct nouveau_i2c_chan *i2c;
		struct nouveau_encoder *nv_encoder;
		struct drm_mode_object *obj;
		int id;
@@ -178,10 +162,10 @@ nouveau_connector_ddc_detect(struct drm_connector *connector,
			continue;

		nouveau_connector_ddc_prepare(connector, &flags);
		ret = i2c_transfer(&i2c->adapter, msgs, 2);
		ret = nouveau_probe_i2c_addr(i2c, 0x50);
		nouveau_connector_ddc_finish(connector, flags);

		if (ret == 2) {
		if (ret) {
			*pnv_encoder = nv_encoder;
			return i2c;
		}
+34 −0
Original line number Diff line number Diff line
@@ -278,3 +278,37 @@ nouveau_i2c_find(struct drm_device *dev, int index)
	return i2c->chan;
}

bool
nouveau_probe_i2c_addr(struct nouveau_i2c_chan *i2c, int addr)
{
	struct i2c_msg msg = {
		.addr = addr,
		.len = 0,
	};

	return i2c_transfer(&i2c->adapter, &msg, 1) == 1;
}

int
nouveau_i2c_identify(struct drm_device *dev, const char *what,
		     struct i2c_board_info *info, int index)
{
	struct nouveau_i2c_chan *i2c = nouveau_i2c_find(dev, index);
	int was_locked, i;

	was_locked = NVLockVgaCrtcs(dev, false);
	NV_DEBUG(dev, "Probing %ss on I2C bus: %d\n", what, index);

	for (i = 0; info[i].addr; i++) {
		if (nouveau_probe_i2c_addr(i2c, info[i].addr)) {
			NV_INFO(dev, "Detected %s: %s\n", what, info[i].type);
			goto out;
		}
	}

	NV_DEBUG(dev, "No devices found.\n");
out:
	NVLockVgaCrtcs(dev, was_locked);

	return info[i].addr ? i : -ENODEV;
}
+3 −0
Original line number Diff line number Diff line
@@ -45,6 +45,9 @@ struct nouveau_i2c_chan {
int nouveau_i2c_init(struct drm_device *, struct dcb_i2c_entry *, int index);
void nouveau_i2c_fini(struct drm_device *, struct dcb_i2c_entry *);
struct nouveau_i2c_chan *nouveau_i2c_find(struct drm_device *, int index);
bool nouveau_probe_i2c_addr(struct nouveau_i2c_chan *i2c, int addr);
int nouveau_i2c_identify(struct drm_device *dev, const char *what,
			 struct i2c_board_info *info, int index);

int nouveau_dp_i2c_aux_ch(struct i2c_adapter *, int mode, uint8_t write_byte,
			  uint8_t *read_byte);
+35 −80
Original line number Diff line number Diff line
@@ -34,68 +34,25 @@

#include "i2c/ch7006.h"

static struct {
	struct i2c_board_info board_info;
	struct drm_encoder_funcs funcs;
	struct drm_encoder_helper_funcs hfuncs;
	void *params;

} nv04_tv_encoder_info[] = {
static struct i2c_board_info nv04_tv_encoder_info[] = {
	{
		.board_info = { I2C_BOARD_INFO("ch7006", 0x75) },
		.params = &(struct ch7006_encoder_params) {
		I2C_BOARD_INFO("ch7006", 0x75),
		.platform_data = &(struct ch7006_encoder_params) {
			CH7006_FORMAT_RGB24m12I, CH7006_CLOCK_MASTER,
			0, 0, 0,
			CH7006_SYNC_SLAVE, CH7006_SYNC_SEPARATED,
			CH7006_POUT_3_3V, CH7006_ACTIVE_HSYNC
		}
	},
	},
};

static bool probe_i2c_addr(struct i2c_adapter *adapter, int addr)
{
	struct i2c_msg msg = {
		.addr = addr,
		.len = 0,
	{ }
};

	return i2c_transfer(adapter, &msg, 1) == 1;
}

int nv04_tv_identify(struct drm_device *dev, int i2c_index)
{
	struct nouveau_i2c_chan *i2c;
	bool was_locked;
	int i, ret;

	NV_TRACE(dev, "Probing TV encoders on I2C bus: %d\n", i2c_index);

	i2c = nouveau_i2c_find(dev, i2c_index);
	if (!i2c)
		return -ENODEV;

	was_locked = NVLockVgaCrtcs(dev, false);

	for (i = 0; i < ARRAY_SIZE(nv04_tv_encoder_info); i++) {
		if (probe_i2c_addr(&i2c->adapter,
				   nv04_tv_encoder_info[i].board_info.addr)) {
			ret = i;
			break;
		}
	return nouveau_i2c_identify(dev, "TV encoder",
				    nv04_tv_encoder_info, i2c_index);
}

	if (i < ARRAY_SIZE(nv04_tv_encoder_info)) {
		NV_TRACE(dev, "Detected TV encoder: %s\n",
			 nv04_tv_encoder_info[i].board_info.type);

	} else {
		NV_TRACE(dev, "No TV encoders found.\n");
		i = -ENODEV;
	}

	NVLockVgaCrtcs(dev, was_locked);
	return i;
}

#define PLLSEL_TV_CRTC1_MASK				\
	(NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1		\
@@ -214,32 +171,33 @@ static void nv04_tv_commit(struct drm_encoder *encoder)

static void nv04_tv_destroy(struct drm_encoder *encoder)
{
	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);

	to_encoder_slave(encoder)->slave_funcs->destroy(encoder);

	drm_encoder_cleanup(encoder);

	kfree(nv_encoder);
	kfree(encoder->helper_private);
	kfree(nouveau_encoder(encoder));
}

static const struct drm_encoder_funcs nv04_tv_funcs = {
	.destroy = nv04_tv_destroy,
};

int
nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry)
{
	struct nouveau_encoder *nv_encoder;
	struct drm_encoder *encoder;
	struct drm_device *dev = connector->dev;
	struct drm_nouveau_private *dev_priv = dev->dev_private;
	struct i2c_adapter *adap;
	struct drm_encoder_funcs *funcs = NULL;
	struct drm_encoder_helper_funcs *hfuncs = NULL;
	struct drm_encoder_slave_funcs *sfuncs = NULL;
	int i2c_index = entry->i2c_index;
	struct drm_encoder_helper_funcs *hfuncs;
	struct drm_encoder_slave_funcs *sfuncs;
	struct nouveau_i2c_chan *i2c =
		nouveau_i2c_find(dev, entry->i2c_index);
	int type, ret;
	bool was_locked;

	/* Ensure that we can talk to this encoder */
	type = nv04_tv_identify(dev, i2c_index);
	type = nv04_tv_identify(dev, entry->i2c_index);
	if (type < 0)
		return type;

@@ -248,41 +206,37 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry)
	if (!nv_encoder)
		return -ENOMEM;

	hfuncs = kzalloc(sizeof(*hfuncs), GFP_KERNEL);
	if (!hfuncs) {
		ret = -ENOMEM;
		goto fail_free;
	}

	/* Initialize the common members */
	encoder = to_drm_encoder(nv_encoder);

	funcs = &nv04_tv_encoder_info[type].funcs;
	hfuncs = &nv04_tv_encoder_info[type].hfuncs;

	drm_encoder_init(dev, encoder, funcs, DRM_MODE_ENCODER_TVDAC);
	drm_encoder_init(dev, encoder, &nv04_tv_funcs, DRM_MODE_ENCODER_TVDAC);
	drm_encoder_helper_add(encoder, hfuncs);

	encoder->possible_crtcs = entry->heads;
	encoder->possible_clones = 0;

	nv_encoder->dcb = entry;
	nv_encoder->or = ffs(entry->or) - 1;

	/* Run the slave-specific initialization */
	adap = &dev_priv->vbios.dcb.i2c[i2c_index].chan->adapter;

	was_locked = NVLockVgaCrtcs(dev, false);

	ret = drm_i2c_encoder_init(dev, to_encoder_slave(encoder), adap,
				   &nv04_tv_encoder_info[type].board_info);
	ret = drm_i2c_encoder_init(dev, to_encoder_slave(encoder),
				   &i2c->adapter, &nv04_tv_encoder_info[type]);

	NVLockVgaCrtcs(dev, was_locked);

	if (ret < 0)
		goto fail;
		goto fail_cleanup;

	/* Fill the function pointers */
	sfuncs = to_encoder_slave(encoder)->slave_funcs;

	*funcs = (struct drm_encoder_funcs) {
		.destroy = nv04_tv_destroy,
	};

	*hfuncs = (struct drm_encoder_helper_funcs) {
		.dpms = nv04_tv_dpms,
		.save = sfuncs->save,
@@ -294,16 +248,17 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry)
		.detect = sfuncs->detect,
	};

	/* Set the slave encoder configuration */
	sfuncs->set_config(encoder, nv04_tv_encoder_info[type].params);
	/* Attach it to the specified connector. */
	sfuncs->set_config(encoder, nv04_tv_encoder_info[type].platform_data);
	sfuncs->create_resources(encoder, connector);

	drm_mode_connector_attach_encoder(connector, encoder);

	return 0;

fail:
fail_cleanup:
	drm_encoder_cleanup(encoder);

	kfree(hfuncs);
fail_free:
	kfree(nv_encoder);
	return ret;
}