mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 19:00:25 +00:00
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:
parent
6d6a413aa2
commit
6d416d80f7
4 changed files with 75 additions and 99 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue