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:
parent
d2f4e89254
commit
4a9f822fe1
9 changed files with 93 additions and 21 deletions
|
@ -5982,7 +5982,13 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case OUTPUT_TMDS:
|
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;
|
break;
|
||||||
case 0xe:
|
case 0xe:
|
||||||
/* weird g80 mobile type that "nv" treats as a terminator */
|
/* 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];
|
dcb->i2c_table = &bios->data[i2ctabptr];
|
||||||
if (dcb->version >= 0x30)
|
if (dcb->version >= 0x30)
|
||||||
dcb->i2c_default_indices = dcb->i2c_table[4];
|
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)
|
if (entries > DCB_MAX_NUM_ENTRIES)
|
||||||
|
|
|
@ -131,6 +131,7 @@ struct dcb_entry {
|
||||||
} dpconf;
|
} dpconf;
|
||||||
struct {
|
struct {
|
||||||
struct sor_conf sor;
|
struct sor_conf sor;
|
||||||
|
int slave_addr;
|
||||||
} tmdsconf;
|
} tmdsconf;
|
||||||
};
|
};
|
||||||
bool i2c_upper_default;
|
bool i2c_upper_default;
|
||||||
|
|
|
@ -37,12 +37,6 @@
|
||||||
#include "nouveau_connector.h"
|
#include "nouveau_connector.h"
|
||||||
#include "nouveau_hw.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 *
|
static struct nouveau_encoder *
|
||||||
find_encoder_by_type(struct drm_connector *connector, int type)
|
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_connector *nv_connector = nouveau_connector(connector);
|
||||||
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
|
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
|
||||||
|
struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
|
||||||
struct drm_device *dev = connector->dev;
|
struct drm_device *dev = connector->dev;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -432,8 +427,8 @@ nouveau_connector_set_property(struct drm_connector *connector,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV)
|
if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV)
|
||||||
return get_slave_funcs(nv_encoder)->
|
return get_slave_funcs(encoder)->set_property(
|
||||||
set_property(to_drm_encoder(nv_encoder), connector, property, value);
|
encoder, connector, property, value);
|
||||||
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -545,6 +540,7 @@ nouveau_connector_get_modes(struct drm_connector *connector)
|
||||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||||
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
||||||
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
|
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
|
||||||
|
struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/* destroy the native mode, the attached monitor could have changed.
|
/* 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)
|
if (nv_encoder->dcb->type == OUTPUT_TV)
|
||||||
ret = get_slave_funcs(nv_encoder)->
|
ret = get_slave_funcs(encoder)->get_modes(encoder, connector);
|
||||||
get_modes(to_drm_encoder(nv_encoder), connector);
|
|
||||||
|
|
||||||
if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS ||
|
if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS ||
|
||||||
nv_connector->dcb->type == DCB_CONNECTOR_eDP)
|
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 drm_nouveau_private *dev_priv = connector->dev->dev_private;
|
||||||
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
||||||
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
|
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 min_clock = 25000, max_clock = min_clock;
|
||||||
unsigned clock = mode->clock;
|
unsigned clock = mode->clock;
|
||||||
|
|
||||||
|
@ -623,8 +619,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
|
||||||
max_clock = 350000;
|
max_clock = 350000;
|
||||||
break;
|
break;
|
||||||
case OUTPUT_TV:
|
case OUTPUT_TV:
|
||||||
return get_slave_funcs(nv_encoder)->
|
return get_slave_funcs(encoder)->mode_valid(encoder, mode);
|
||||||
mode_valid(to_drm_encoder(nv_encoder), mode);
|
|
||||||
case OUTPUT_DP:
|
case OUTPUT_DP:
|
||||||
if (nv_encoder->dp.link_bw == DP_LINK_BW_2_7)
|
if (nv_encoder->dp.link_bw == DP_LINK_BW_2_7)
|
||||||
max_clock = nv_encoder->dp.link_nr * 270000;
|
max_clock = nv_encoder->dp.link_nr * 270000;
|
||||||
|
|
|
@ -410,7 +410,7 @@ enum nv04_fp_display_regs {
|
||||||
|
|
||||||
struct nv04_crtc_reg {
|
struct nv04_crtc_reg {
|
||||||
unsigned char MiscOutReg; /* */
|
unsigned char MiscOutReg; /* */
|
||||||
uint8_t CRTC[0x9f];
|
uint8_t CRTC[0xa0];
|
||||||
uint8_t CR58[0x10];
|
uint8_t CR58[0x10];
|
||||||
uint8_t Sequencer[5];
|
uint8_t Sequencer[5];
|
||||||
uint8_t Graphics[9];
|
uint8_t Graphics[9];
|
||||||
|
|
|
@ -71,6 +71,12 @@ static inline struct drm_encoder *to_drm_encoder(struct nouveau_encoder *enc)
|
||||||
return &enc->base.base;
|
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 *
|
struct nouveau_connector *
|
||||||
nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
|
nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
|
||||||
int nv50_sor_create(struct drm_connector *, struct dcb_entry *);
|
int nv50_sor_create(struct drm_connector *, struct dcb_entry *);
|
||||||
|
|
|
@ -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_FF_INDEX);
|
||||||
rd_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX);
|
rd_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX);
|
||||||
rd_cio_state(dev, head, regp, NV_CIO_CRE_21);
|
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, 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_49);
|
||||||
rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
|
rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
|
||||||
rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_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_ENH_INDEX);
|
||||||
wr_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX);
|
wr_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX);
|
||||||
wr_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__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, 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_49);
|
||||||
wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
|
wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
|
||||||
|
|
|
@ -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 */
|
* 1 << 30 on 0x60.830), for no apparent reason */
|
||||||
regp->CRTC[NV_CIO_CRE_59] = off_chip_digital;
|
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_830 = mode->crtc_vdisplay - 3;
|
||||||
regp->crtc_834 = mode->crtc_vdisplay - 1;
|
regp->crtc_834 = mode->crtc_vdisplay - 1;
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
#include "nouveau_hw.h"
|
#include "nouveau_hw.h"
|
||||||
#include "nvreg.h"
|
#include "nvreg.h"
|
||||||
|
|
||||||
|
#include "i2c/sil164.h"
|
||||||
|
|
||||||
#define FP_TG_CONTROL_ON (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | \
|
#define FP_TG_CONTROL_ON (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | \
|
||||||
NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS | \
|
NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS | \
|
||||||
NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS)
|
NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS)
|
||||||
|
@ -429,6 +431,11 @@ static void nv04_dfp_commit(struct drm_encoder *encoder)
|
||||||
else
|
else
|
||||||
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000);
|
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);
|
helper->dpms(encoder, DRM_MODE_DPMS_ON);
|
||||||
|
|
||||||
NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
|
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");
|
NV_DEBUG_KMS(encoder->dev, "\n");
|
||||||
|
|
||||||
|
if (get_slave_funcs(encoder))
|
||||||
|
get_slave_funcs(encoder)->destroy(encoder);
|
||||||
|
|
||||||
drm_encoder_cleanup(encoder);
|
drm_encoder_cleanup(encoder);
|
||||||
kfree(nv_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 = {
|
static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = {
|
||||||
.dpms = nv04_lvds_dpms,
|
.dpms = nv04_lvds_dpms,
|
||||||
.save = nv04_dfp_save,
|
.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_crtcs = entry->heads;
|
||||||
encoder->possible_clones = 0;
|
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);
|
drm_mode_connector_attach_encoder(connector, encoder);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
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)
|
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_vskew = 1;
|
||||||
regp->tv_vsync_delay = 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)
|
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)
|
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);
|
drm_encoder_cleanup(encoder);
|
||||||
|
|
||||||
kfree(encoder->helper_private);
|
kfree(encoder->helper_private);
|
||||||
|
@ -229,7 +228,7 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry)
|
||||||
goto fail_cleanup;
|
goto fail_cleanup;
|
||||||
|
|
||||||
/* Fill the function pointers */
|
/* Fill the function pointers */
|
||||||
sfuncs = to_encoder_slave(encoder)->slave_funcs;
|
sfuncs = get_slave_funcs(encoder);
|
||||||
|
|
||||||
*hfuncs = (struct drm_encoder_helper_funcs) {
|
*hfuncs = (struct drm_encoder_helper_funcs) {
|
||||||
.dpms = nv04_tv_dpms,
|
.dpms = nv04_tv_dpms,
|
||||||
|
|
Loading…
Add table
Reference in a new issue