mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 19:00:25 +00:00
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;
|
||||
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)
|
||||
|
|
|
@ -131,6 +131,7 @@ struct dcb_entry {
|
|||
} dpconf;
|
||||
struct {
|
||||
struct sor_conf sor;
|
||||
int slave_addr;
|
||||
} tmdsconf;
|
||||
};
|
||||
bool i2c_upper_default;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue