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

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
...@@ -139,26 +139,10 @@ nouveau_connector_ddc_detect(struct drm_connector *connector, ...@@ -139,26 +139,10 @@ nouveau_connector_ddc_detect(struct drm_connector *connector,
struct nouveau_encoder **pnv_encoder) struct nouveau_encoder **pnv_encoder)
{ {
struct drm_device *dev = connector->dev; struct drm_device *dev = connector->dev;
uint8_t out_buf[] = { 0x0, 0x0}, buf[2];
int ret, flags, i; 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++) { 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 nouveau_encoder *nv_encoder;
struct drm_mode_object *obj; struct drm_mode_object *obj;
int id; int id;
...@@ -178,10 +162,10 @@ nouveau_connector_ddc_detect(struct drm_connector *connector, ...@@ -178,10 +162,10 @@ nouveau_connector_ddc_detect(struct drm_connector *connector,
continue; continue;
nouveau_connector_ddc_prepare(connector, &flags); 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); nouveau_connector_ddc_finish(connector, flags);
if (ret == 2) { if (ret) {
*pnv_encoder = nv_encoder; *pnv_encoder = nv_encoder;
return i2c; return i2c;
} }
......
...@@ -278,3 +278,37 @@ nouveau_i2c_find(struct drm_device *dev, int index) ...@@ -278,3 +278,37 @@ nouveau_i2c_find(struct drm_device *dev, int index)
return i2c->chan; 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;
}
...@@ -45,6 +45,9 @@ struct nouveau_i2c_chan { ...@@ -45,6 +45,9 @@ struct nouveau_i2c_chan {
int nouveau_i2c_init(struct drm_device *, struct dcb_i2c_entry *, int index); int nouveau_i2c_init(struct drm_device *, struct dcb_i2c_entry *, int index);
void nouveau_i2c_fini(struct drm_device *, struct dcb_i2c_entry *); void nouveau_i2c_fini(struct drm_device *, struct dcb_i2c_entry *);
struct nouveau_i2c_chan *nouveau_i2c_find(struct drm_device *, int index); 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, int nouveau_dp_i2c_aux_ch(struct i2c_adapter *, int mode, uint8_t write_byte,
uint8_t *read_byte); uint8_t *read_byte);
......
...@@ -34,69 +34,26 @@ ...@@ -34,69 +34,26 @@
#include "i2c/ch7006.h" #include "i2c/ch7006.h"
static struct { static struct i2c_board_info nv04_tv_encoder_info[] = {
struct i2c_board_info board_info;
struct drm_encoder_funcs funcs;
struct drm_encoder_helper_funcs hfuncs;
void *params;
} nv04_tv_encoder_info[] = {
{ {
.board_info = { I2C_BOARD_INFO("ch7006", 0x75) }, I2C_BOARD_INFO("ch7006", 0x75),
.params = &(struct ch7006_encoder_params) { .platform_data = &(struct ch7006_encoder_params) {
CH7006_FORMAT_RGB24m12I, CH7006_CLOCK_MASTER, CH7006_FORMAT_RGB24m12I, CH7006_CLOCK_MASTER,
0, 0, 0, 0, 0, 0,
CH7006_SYNC_SLAVE, CH7006_SYNC_SEPARATED, CH7006_SYNC_SLAVE, CH7006_SYNC_SEPARATED,
CH7006_POUT_3_3V, CH7006_ACTIVE_HSYNC 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) int nv04_tv_identify(struct drm_device *dev, int i2c_index)
{ {
struct nouveau_i2c_chan *i2c; return nouveau_i2c_identify(dev, "TV encoder",
bool was_locked; nv04_tv_encoder_info, i2c_index);
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;
} }
#define PLLSEL_TV_CRTC1_MASK \ #define PLLSEL_TV_CRTC1_MASK \
(NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \ (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \
| NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1) | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1)
...@@ -214,32 +171,33 @@ static void nv04_tv_commit(struct drm_encoder *encoder) ...@@ -214,32 +171,33 @@ static void nv04_tv_commit(struct drm_encoder *encoder)
static void nv04_tv_destroy(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); to_encoder_slave(encoder)->slave_funcs->destroy(encoder);
drm_encoder_cleanup(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 int
nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry) nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry)
{ {
struct nouveau_encoder *nv_encoder; struct nouveau_encoder *nv_encoder;
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct drm_device *dev = connector->dev; struct drm_device *dev = connector->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_encoder_helper_funcs *hfuncs;
struct i2c_adapter *adap; struct drm_encoder_slave_funcs *sfuncs;
struct drm_encoder_funcs *funcs = NULL; struct nouveau_i2c_chan *i2c =
struct drm_encoder_helper_funcs *hfuncs = NULL; nouveau_i2c_find(dev, entry->i2c_index);
struct drm_encoder_slave_funcs *sfuncs = NULL;
int i2c_index = entry->i2c_index;
int type, ret; int type, ret;
bool was_locked; bool was_locked;
/* Ensure that we can talk to this encoder */ /* 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) if (type < 0)
return type; return type;
...@@ -248,41 +206,37 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry) ...@@ -248,41 +206,37 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry)
if (!nv_encoder) if (!nv_encoder)
return -ENOMEM; return -ENOMEM;
hfuncs = kzalloc(sizeof(*hfuncs), GFP_KERNEL);
if (!hfuncs) {
ret = -ENOMEM;
goto fail_free;
}
/* Initialize the common members */ /* Initialize the common members */
encoder = to_drm_encoder(nv_encoder); encoder = to_drm_encoder(nv_encoder);
funcs = &nv04_tv_encoder_info[type].funcs; drm_encoder_init(dev, encoder, &nv04_tv_funcs, DRM_MODE_ENCODER_TVDAC);
hfuncs = &nv04_tv_encoder_info[type].hfuncs;
drm_encoder_init(dev, encoder, funcs, DRM_MODE_ENCODER_TVDAC);
drm_encoder_helper_add(encoder, hfuncs); drm_encoder_helper_add(encoder, hfuncs);
encoder->possible_crtcs = entry->heads; encoder->possible_crtcs = entry->heads;
encoder->possible_clones = 0; encoder->possible_clones = 0;
nv_encoder->dcb = entry; nv_encoder->dcb = entry;
nv_encoder->or = ffs(entry->or) - 1; nv_encoder->or = ffs(entry->or) - 1;
/* Run the slave-specific initialization */ /* Run the slave-specific initialization */
adap = &dev_priv->vbios.dcb.i2c[i2c_index].chan->adapter;
was_locked = NVLockVgaCrtcs(dev, false); was_locked = NVLockVgaCrtcs(dev, false);
ret = drm_i2c_encoder_init(dev, to_encoder_slave(encoder), adap, ret = drm_i2c_encoder_init(dev, to_encoder_slave(encoder),
&nv04_tv_encoder_info[type].board_info); &i2c->adapter, &nv04_tv_encoder_info[type]);
NVLockVgaCrtcs(dev, was_locked); NVLockVgaCrtcs(dev, was_locked);
if (ret < 0) if (ret < 0)
goto fail; goto fail_cleanup;
/* Fill the function pointers */ /* Fill the function pointers */
sfuncs = to_encoder_slave(encoder)->slave_funcs; sfuncs = to_encoder_slave(encoder)->slave_funcs;
*funcs = (struct drm_encoder_funcs) {
.destroy = nv04_tv_destroy,
};
*hfuncs = (struct drm_encoder_helper_funcs) { *hfuncs = (struct drm_encoder_helper_funcs) {
.dpms = nv04_tv_dpms, .dpms = nv04_tv_dpms,
.save = sfuncs->save, .save = sfuncs->save,
...@@ -294,16 +248,17 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry) ...@@ -294,16 +248,17 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry)
.detect = sfuncs->detect, .detect = sfuncs->detect,
}; };
/* Set the slave encoder configuration */ /* Attach it to the specified connector. */
sfuncs->set_config(encoder, nv04_tv_encoder_info[type].params); sfuncs->set_config(encoder, nv04_tv_encoder_info[type].platform_data);
sfuncs->create_resources(encoder, connector); sfuncs->create_resources(encoder, connector);
drm_mode_connector_attach_encoder(connector, encoder); drm_mode_connector_attach_encoder(connector, encoder);
return 0; return 0;
fail: fail_cleanup:
drm_encoder_cleanup(encoder); drm_encoder_cleanup(encoder);
kfree(hfuncs);
fail_free:
kfree(nv_encoder); kfree(nv_encoder);
return ret; return ret;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment