aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nv04_dfp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv04_dfp.c')
-rw-r--r--drivers/gpu/drm/nouveau/nv04_dfp.c73
1 files changed, 73 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c
index 3311f3a8c818..a5dcf7685800 100644
--- a/drivers/gpu/drm/nouveau/nv04_dfp.c
+++ b/drivers/gpu/drm/nouveau/nv04_dfp.c
@@ -34,6 +34,8 @@
34#include "nouveau_hw.h" 34#include "nouveau_hw.h"
35#include "nvreg.h" 35#include "nvreg.h"
36 36
37#include "i2c/sil164.h"
38
37#define FP_TG_CONTROL_ON (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | \ 39#define FP_TG_CONTROL_ON (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | \
38 NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS | \ 40 NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS | \
39 NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS) 41 NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS)
@@ -144,6 +146,36 @@ void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode)
144 } 146 }
145} 147}
146 148
149static struct drm_encoder *get_tmds_slave(struct drm_encoder *encoder)
150{
151 struct drm_device *dev = encoder->dev;
152 struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
153 struct drm_encoder *slave;
154
155 if (dcb->type != OUTPUT_TMDS || dcb->location == DCB_LOC_ON_CHIP)
156 return NULL;
157
158 /* Some BIOSes (e.g. the one in a Quadro FX1000) report several
159 * TMDS transmitters at the same I2C address, in the same I2C
160 * bus. This can still work because in that case one of them is
161 * always hard-wired to a reasonable configuration using straps,
162 * and the other one needs to be programmed.
163 *
164 * I don't think there's a way to know which is which, even the
165 * blob programs the one exposed via I2C for *both* heads, so
166 * let's do the same.
167 */
168 list_for_each_entry(slave, &dev->mode_config.encoder_list, head) {
169 struct dcb_entry *slave_dcb = nouveau_encoder(slave)->dcb;
170
171 if (slave_dcb->type == OUTPUT_TMDS && get_slave_funcs(slave) &&
172 slave_dcb->tmdsconf.slave_addr == dcb->tmdsconf.slave_addr)
173 return slave;
174 }
175
176 return NULL;
177}
178
147static bool nv04_dfp_mode_fixup(struct drm_encoder *encoder, 179static bool nv04_dfp_mode_fixup(struct drm_encoder *encoder,
148 struct drm_display_mode *mode, 180 struct drm_display_mode *mode,
149 struct drm_display_mode *adjusted_mode) 181 struct drm_display_mode *adjusted_mode)
@@ -429,6 +461,11 @@ static void nv04_dfp_commit(struct drm_encoder *encoder)
429 else 461 else
430 NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000); 462 NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000);
431 463
464 /* Init external transmitters */
465 if (get_tmds_slave(encoder))
466 get_slave_funcs(get_tmds_slave(encoder))->mode_set(
467 encoder, &nv_encoder->mode, &nv_encoder->mode);
468
432 helper->dpms(encoder, DRM_MODE_DPMS_ON); 469 helper->dpms(encoder, DRM_MODE_DPMS_ON);
433 470
434 NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n", 471 NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
@@ -550,10 +587,42 @@ static void nv04_dfp_destroy(struct drm_encoder *encoder)
550 587
551 NV_DEBUG_KMS(encoder->dev, "\n"); 588 NV_DEBUG_KMS(encoder->dev, "\n");
552 589
590 if (get_slave_funcs(encoder))
591 get_slave_funcs(encoder)->destroy(encoder);
592
553 drm_encoder_cleanup(encoder); 593 drm_encoder_cleanup(encoder);
554 kfree(nv_encoder); 594 kfree(nv_encoder);
555} 595}
556 596
597static void nv04_tmds_slave_init(struct drm_encoder *encoder)
598{
599 struct drm_device *dev = encoder->dev;
600 struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
601 struct nouveau_i2c_chan *i2c = nouveau_i2c_find(dev, 2);
602 struct i2c_board_info info[] = {
603 {
604 .type = "sil164",
605 .addr = (dcb->tmdsconf.slave_addr == 0x7 ? 0x3a : 0x38),
606 .platform_data = &(struct sil164_encoder_params) {
607 SIL164_INPUT_EDGE_RISING
608 }
609 },
610 { }
611 };
612 int type;
613
614 if (!nv_gf4_disp_arch(dev) || !i2c ||
615 get_tmds_slave(encoder))
616 return;
617
618 type = nouveau_i2c_identify(dev, "TMDS transmitter", info, 2);
619 if (type < 0)
620 return;
621
622 drm_i2c_encoder_init(dev, to_encoder_slave(encoder),
623 &i2c->adapter, &info[type]);
624}
625
557static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = { 626static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = {
558 .dpms = nv04_lvds_dpms, 627 .dpms = nv04_lvds_dpms,
559 .save = nv04_dfp_save, 628 .save = nv04_dfp_save,
@@ -616,6 +685,10 @@ nv04_dfp_create(struct drm_connector *connector, struct dcb_entry *entry)
616 encoder->possible_crtcs = entry->heads; 685 encoder->possible_crtcs = entry->heads;
617 encoder->possible_clones = 0; 686 encoder->possible_clones = 0;
618 687
688 if (entry->type == OUTPUT_TMDS &&
689 entry->location != DCB_LOC_ON_CHIP)
690 nv04_tmds_slave_init(encoder);
691
619 drm_mode_connector_attach_encoder(connector, encoder); 692 drm_mode_connector_attach_encoder(connector, encoder);
620 return 0; 693 return 0;
621} 694}