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: Francisco Jerez <currojerez@riseup.net>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
Francisco Jerez 2010-07-19 15:55:08 +02:00 committed by Ben Skeggs
parent 6d6a413aa2
commit 6d416d80f7
4 changed files with 75 additions and 99 deletions

View file

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

View file

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

View file

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

View file

@ -34,69 +34,26 @@
#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;
}
}
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;
return nouveau_i2c_identify(dev, "TV encoder",
nv04_tv_encoder_info, i2c_index);
}
#define PLLSEL_TV_CRTC1_MASK \
(NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \
| NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1)
@ -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;
}