drm/nv17-nv4x: Attempt to init some external TMDS transmitters.

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>
This commit is contained in:
Francisco Jerez 2010-07-20 16:48:08 +02:00 committed by Ben Skeggs
parent d2f4e89254
commit 4a9f822fe1
9 changed files with 93 additions and 21 deletions

View 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)

View file

@ -131,6 +131,7 @@ struct dcb_entry {
} dpconf;
struct {
struct sor_conf sor;
int slave_addr;
} tmdsconf;
};
bool i2c_upper_default;

View file

@ -37,12 +37,6 @@
#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;

View 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];

View 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 *);

View 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);

View 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;

View 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;
}

View 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,