drm/nouveau: Add some generic I2C gadget detection code.
authorFrancisco Jerez <currojerez@riseup.net>
Mon, 19 Jul 2010 13:55:08 +0000 (15:55 +0200)
committerBen Skeggs <bskeggs@redhat.com>
Mon, 26 Jul 2010 01:42:30 +0000 (11:42 +1000)
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>
drivers/gpu/drm/nouveau/nouveau_connector.c
drivers/gpu/drm/nouveau/nouveau_i2c.c
drivers/gpu/drm/nouveau/nouveau_i2c.h
drivers/gpu/drm/nouveau/nv04_tv.c

index 7f749d2..27df006 100644 (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;
                }
index 316a3c7..97ba89e 100644 (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;
+}
index c8eaf7a..6dd2f87 100644 (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);
index 84b5954..8de1eef 100644 (file)
 
 #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;
 }