drm/nv17-nv4x: Attempt to init some external TMDS transmitters.
authorFrancisco Jerez <currojerez@riseup.net>
Tue, 20 Jul 2010 14:48:08 +0000 (16:48 +0200)
committerBen Skeggs <bskeggs@redhat.com>
Thu, 5 Aug 2010 22:34:59 +0000 (08:34 +1000)
sil164 and friends are the most common, usually they just need to be
poked once because a fixed configuration is enough for any modes and
clocks, so they worked without this patch if the BIOS had done a good
job on POST. Display couldn't survive a suspend/resume cycle though.
Unfortunately, BIOS scripts are useless here.

Signed-off-by: Francisco Jerez <currojerez@riseup.net>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/nouveau_bios.c
drivers/gpu/drm/nouveau/nouveau_bios.h
drivers/gpu/drm/nouveau/nouveau_connector.c
drivers/gpu/drm/nouveau/nouveau_drv.h
drivers/gpu/drm/nouveau/nouveau_encoder.h
drivers/gpu/drm/nouveau/nouveau_hw.c
drivers/gpu/drm/nouveau/nv04_crtc.c
drivers/gpu/drm/nouveau/nv04_dfp.c
drivers/gpu/drm/nouveau/nv04_tv.c

index c608b0b29a3680b309369e89bf6f1175874b8bde..e86f46cf883710d042cd93728e4ddc2eab5eb7d5 100644 (file)
@@ -5982,7 +5982,13 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
                }
                break;
        case OUTPUT_TMDS:
-               entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4;
+               if (dcb->version >= 0x22)
+                       entry->tmdsconf.slave_addr = (conf & 0x00000070) >> 4;
+               else if (dcb->version >= 0x30)
+                       entry->tmdsconf.slave_addr = (conf & 0x00000700) >> 8;
+               else if (dcb->version >= 0x40)
+                       entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4;
+
                break;
        case 0xe:
                /* weird g80 mobile type that "nv" treats as a terminator */
@@ -6272,6 +6278,19 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
                dcb->i2c_table = &bios->data[i2ctabptr];
                if (dcb->version >= 0x30)
                        dcb->i2c_default_indices = dcb->i2c_table[4];
+
+               /*
+                * Parse the "management" I2C bus, used for hardware
+                * monitoring and some external TMDS transmitters.
+                */
+               if (dcb->version >= 0x22) {
+                       int idx = (dcb->version >= 0x40 ?
+                                  dcb->i2c_default_indices & 0xf :
+                                  2);
+
+                       read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table,
+                                          idx, &dcb->i2c[idx]);
+               }
        }
 
        if (entries > DCB_MAX_NUM_ENTRIES)
index 024458a8d060193043852670b12beba84efa3c01..fd14dfd3d780f6b45a280d1cf4c4aed6eb41ed20 100644 (file)
@@ -131,6 +131,7 @@ struct dcb_entry {
                } dpconf;
                struct {
                        struct sor_conf sor;
+                       int slave_addr;
                } tmdsconf;
        };
        bool i2c_upper_default;
index 734e92635e83f103885cecbf8674d077a9df5cd9..b1b22baf14284a6d2105a320f7602f3e81b00529 100644 (file)
 #include "nouveau_connector.h"
 #include "nouveau_hw.h"
 
-static inline struct drm_encoder_slave_funcs *
-get_slave_funcs(struct nouveau_encoder *enc)
-{
-       return to_encoder_slave(to_drm_encoder(enc))->slave_funcs;
-}
-
 static struct nouveau_encoder *
 find_encoder_by_type(struct drm_connector *connector, int type)
 {
@@ -360,6 +354,7 @@ nouveau_connector_set_property(struct drm_connector *connector,
 {
        struct nouveau_connector *nv_connector = nouveau_connector(connector);
        struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
+       struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
        struct drm_device *dev = connector->dev;
        int ret;
 
@@ -432,8 +427,8 @@ nouveau_connector_set_property(struct drm_connector *connector,
        }
 
        if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV)
-               return get_slave_funcs(nv_encoder)->
-                       set_property(to_drm_encoder(nv_encoder), connector, property, value);
+               return get_slave_funcs(encoder)->set_property(
+                       encoder, connector, property, value);
 
        return -EINVAL;
 }
@@ -545,6 +540,7 @@ nouveau_connector_get_modes(struct drm_connector *connector)
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        struct nouveau_connector *nv_connector = nouveau_connector(connector);
        struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
+       struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
        int ret = 0;
 
        /* destroy the native mode, the attached monitor could have changed.
@@ -580,8 +576,7 @@ nouveau_connector_get_modes(struct drm_connector *connector)
        }
 
        if (nv_encoder->dcb->type == OUTPUT_TV)
-               ret = get_slave_funcs(nv_encoder)->
-                       get_modes(to_drm_encoder(nv_encoder), connector);
+               ret = get_slave_funcs(encoder)->get_modes(encoder, connector);
 
        if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS ||
            nv_connector->dcb->type == DCB_CONNECTOR_eDP)
@@ -597,6 +592,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
        struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
        struct nouveau_connector *nv_connector = nouveau_connector(connector);
        struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
+       struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
        unsigned min_clock = 25000, max_clock = min_clock;
        unsigned clock = mode->clock;
 
@@ -623,8 +619,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
                        max_clock = 350000;
                break;
        case OUTPUT_TV:
-               return get_slave_funcs(nv_encoder)->
-                       mode_valid(to_drm_encoder(nv_encoder), mode);
+               return get_slave_funcs(encoder)->mode_valid(encoder, mode);
        case OUTPUT_DP:
                if (nv_encoder->dp.link_bw == DP_LINK_BW_2_7)
                        max_clock = nv_encoder->dp.link_nr * 270000;
index db10809e91311897059b6277c87c04dd34b1f31d..9e17d88bc890c2b320a0ede023b3be5a0d0657b9 100644 (file)
@@ -410,7 +410,7 @@ enum nv04_fp_display_regs {
 
 struct nv04_crtc_reg {
        unsigned char MiscOutReg;     /* */
-       uint8_t CRTC[0x9f];
+       uint8_t CRTC[0xa0];
        uint8_t CR58[0x10];
        uint8_t Sequencer[5];
        uint8_t Graphics[9];
index a1a0d48ae70c04b95fd6e75b3d494e5a16e8ba12..7c82d68bc155d7d8a738d206a1c795b79849cc05 100644 (file)
@@ -71,6 +71,12 @@ static inline struct drm_encoder *to_drm_encoder(struct nouveau_encoder *enc)
        return &enc->base.base;
 }
 
+static inline struct drm_encoder_slave_funcs *
+get_slave_funcs(struct drm_encoder *enc)
+{
+       return to_encoder_slave(enc)->slave_funcs;
+}
+
 struct nouveau_connector *
 nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
 int nv50_sor_create(struct drm_connector *, struct dcb_entry *);
index 7855b35effc357e2a6db34db2f181a9ea5663f0f..7b613682e400b88cc387c83261987b6259026b98 100644 (file)
@@ -865,8 +865,12 @@ nv_save_state_ext(struct drm_device *dev, int head,
        rd_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX);
        rd_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX);
        rd_cio_state(dev, head, regp, NV_CIO_CRE_21);
-       if (dev_priv->card_type >= NV_30)
+
+       if (dev_priv->card_type >= NV_30) {
                rd_cio_state(dev, head, regp, NV_CIO_CRE_47);
+               rd_cio_state(dev, head, regp, 0x9f);
+       }
+
        rd_cio_state(dev, head, regp, NV_CIO_CRE_49);
        rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
        rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX);
@@ -971,8 +975,11 @@ nv_load_state_ext(struct drm_device *dev, int head,
        wr_cio_state(dev, head, regp, NV_CIO_CRE_ENH_INDEX);
        wr_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX);
        wr_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX);
-       if (dev_priv->card_type >= NV_30)
+
+       if (dev_priv->card_type >= NV_30) {
                wr_cio_state(dev, head, regp, NV_CIO_CRE_47);
+               wr_cio_state(dev, head, regp, 0x9f);
+       }
 
        wr_cio_state(dev, head, regp, NV_CIO_CRE_49);
        wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
index 1c20c08ce67c33685eb433644679209bf31874ba..08c7e073edce266887480c46aee61a0e9b3bb1b0 100644 (file)
@@ -542,6 +542,9 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode)
         * 1 << 30 on 0x60.830), for no apparent reason */
        regp->CRTC[NV_CIO_CRE_59] = off_chip_digital;
 
+       if (dev_priv->card_type >= NV_30)
+               regp->CRTC[0x9f] = off_chip_digital ? 0x11 : 0x1;
+
        regp->crtc_830 = mode->crtc_vdisplay - 3;
        regp->crtc_834 = mode->crtc_vdisplay - 1;
 
index 3311f3a8c8180b9bd3eeee6e5571f7703a6d9bdf..9871570d2ff41f03a19ac53f3e2260f1000c1e7d 100644 (file)
@@ -34,6 +34,8 @@
 #include "nouveau_hw.h"
 #include "nvreg.h"
 
+#include "i2c/sil164.h"
+
 #define FP_TG_CONTROL_ON  (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |       \
                           NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS |         \
                           NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS)
@@ -429,6 +431,11 @@ static void nv04_dfp_commit(struct drm_encoder *encoder)
        else
                NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000);
 
+       /* Init external transmitters */
+       if (get_slave_funcs(encoder))
+               get_slave_funcs(encoder)->mode_set(encoder, &nv_encoder->mode,
+                                                  &nv_encoder->mode);
+
        helper->dpms(encoder, DRM_MODE_DPMS_ON);
 
        NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
@@ -550,10 +557,41 @@ static void nv04_dfp_destroy(struct drm_encoder *encoder)
 
        NV_DEBUG_KMS(encoder->dev, "\n");
 
+       if (get_slave_funcs(encoder))
+               get_slave_funcs(encoder)->destroy(encoder);
+
        drm_encoder_cleanup(encoder);
        kfree(nv_encoder);
 }
 
+static void nv04_tmds_slave_init(struct drm_encoder *encoder)
+{
+       struct drm_device *dev = encoder->dev;
+       struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
+       struct nouveau_i2c_chan *i2c = nouveau_i2c_find(dev, 2);
+       struct i2c_board_info info[] = {
+               {
+                       .type = "sil164",
+                       .addr = (dcb->tmdsconf.slave_addr == 0x7 ? 0x3a : 0x38),
+                       .platform_data = &(struct sil164_encoder_params) {
+                               SIL164_INPUT_EDGE_RISING
+                       }
+               },
+               { }
+       };
+       int type;
+
+       if (!nv_gf4_disp_arch(dev) || !i2c)
+               return;
+
+       type = nouveau_i2c_identify(dev, "TMDS transmitter", info, 2);
+       if (type < 0)
+               return;
+
+       drm_i2c_encoder_init(dev, to_encoder_slave(encoder),
+                            &i2c->adapter, &info[type]);
+}
+
 static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = {
        .dpms = nv04_lvds_dpms,
        .save = nv04_dfp_save,
@@ -616,6 +654,10 @@ nv04_dfp_create(struct drm_connector *connector, struct dcb_entry *entry)
        encoder->possible_crtcs = entry->heads;
        encoder->possible_clones = 0;
 
+       if (entry->type == OUTPUT_TMDS &&
+           entry->location != DCB_LOC_ON_CHIP)
+               nv04_tmds_slave_init(encoder);
+
        drm_mode_connector_attach_encoder(connector, encoder);
        return 0;
 }
index 94e299cef0b2624521b69251e4e1452945238728..4e73c4461a22afbd3742b41f4f225d1bdcbcefb9 100644 (file)
@@ -89,7 +89,7 @@ static void nv04_tv_dpms(struct drm_encoder *encoder, int mode)
 
        NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel);
 
-       to_encoder_slave(encoder)->slave_funcs->dpms(encoder, mode);
+       get_slave_funcs(encoder)->dpms(encoder, mode);
 }
 
 static void nv04_tv_bind(struct drm_device *dev, int head, bool bind)
@@ -152,7 +152,7 @@ static void nv04_tv_mode_set(struct drm_encoder *encoder,
        regp->tv_vskew = 1;
        regp->tv_vsync_delay = 1;
 
-       to_encoder_slave(encoder)->slave_funcs->mode_set(encoder, mode, adjusted_mode);
+       get_slave_funcs(encoder)->mode_set(encoder, mode, adjusted_mode);
 }
 
 static void nv04_tv_commit(struct drm_encoder *encoder)
@@ -171,8 +171,7 @@ static void nv04_tv_commit(struct drm_encoder *encoder)
 
 static void nv04_tv_destroy(struct drm_encoder *encoder)
 {
-       to_encoder_slave(encoder)->slave_funcs->destroy(encoder);
-
+       get_slave_funcs(encoder)->destroy(encoder);
        drm_encoder_cleanup(encoder);
 
        kfree(encoder->helper_private);
@@ -229,7 +228,7 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry)
                goto fail_cleanup;
 
        /* Fill the function pointers */
-       sfuncs = to_encoder_slave(encoder)->slave_funcs;
+       sfuncs = get_slave_funcs(encoder);
 
        *hfuncs = (struct drm_encoder_helper_funcs) {
                .dpms = nv04_tv_dpms,