2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-23 12:43:55 +08:00

drm/nouveau/dp: remove reliance on vbios for native displayport

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
Ben Skeggs 2011-07-01 15:51:49 +10:00
parent 4372013388
commit 46959b7790
6 changed files with 126 additions and 63 deletions

View File

@ -194,6 +194,116 @@ auxch_wr(struct drm_encoder *encoder, int address, uint8_t *buf, int size)
return ret;
}
static u32
dp_link_bw_get(struct drm_device *dev, int or, int link)
{
u32 ctrl = nv_rd32(dev, 0x614300 + (or * 0x800));
if (!(ctrl & 0x000c0000))
return 162000;
return 270000;
}
static int
dp_lane_count_get(struct drm_device *dev, int or, int link)
{
u32 ctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
switch (ctrl & 0x000f0000) {
case 0x00010000: return 1;
case 0x00030000: return 2;
default:
return 4;
}
}
void
nouveau_dp_tu_update(struct drm_device *dev, int or, int link, u32 clk, u32 bpp)
{
const u32 symbol = 100000;
int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0;
int TU, VTUi, VTUf, VTUa;
u64 link_data_rate, link_ratio, unk;
u32 best_diff = 64 * symbol;
u32 link_nr, link_bw, r;
/* calculate packed data rate for each lane */
link_nr = dp_lane_count_get(dev, or, link);
link_data_rate = (clk * bpp / 8) / link_nr;
/* calculate ratio of packed data rate to link symbol rate */
link_bw = dp_link_bw_get(dev, or, link);
link_ratio = link_data_rate * symbol;
r = do_div(link_ratio, link_bw);
for (TU = 64; TU >= 32; TU--) {
/* calculate average number of valid symbols in each TU */
u32 tu_valid = link_ratio * TU;
u32 calc, diff;
/* find a hw representation for the fraction.. */
VTUi = tu_valid / symbol;
calc = VTUi * symbol;
diff = tu_valid - calc;
if (diff) {
if (diff >= (symbol / 2)) {
VTUf = symbol / (symbol - diff);
if (symbol - (VTUf * diff))
VTUf++;
if (VTUf <= 15) {
VTUa = 1;
calc += symbol - (symbol / VTUf);
} else {
VTUa = 0;
VTUf = 1;
calc += symbol;
}
} else {
VTUa = 0;
VTUf = min((int)(symbol / diff), 15);
calc += symbol / VTUf;
}
diff = calc - tu_valid;
} else {
/* no remainder, but the hw doesn't like the fractional
* part to be zero. decrement the integer part and
* have the fraction add a whole symbol back
*/
VTUa = 0;
VTUf = 1;
VTUi--;
}
if (diff < best_diff) {
best_diff = diff;
bestTU = TU;
bestVTUa = VTUa;
bestVTUf = VTUf;
bestVTUi = VTUi;
if (diff == 0)
break;
}
}
if (!bestTU) {
NV_ERROR(dev, "DP: unable to find suitable config\n");
return;
}
/* XXX close to vbios numbers, but not right */
unk = (symbol - link_ratio) * bestTU;
unk *= link_ratio;
r = do_div(unk, symbol);
r = do_div(unk, symbol);
unk += 6;
nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2);
nv_mask(dev, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 |
bestVTUf << 16 |
bestVTUi << 8 |
unk);
}
static int
nouveau_dp_lane_count_set(struct drm_encoder *encoder, uint8_t cmd)
{
@ -617,7 +727,6 @@ static int
nouveau_dp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adap;
struct drm_device *dev = auxch->dev;
struct i2c_msg *msg = msgs;
int ret, mcnt = num;

View File

@ -1101,6 +1101,7 @@ int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
uint8_t *data, int data_nr);
bool nouveau_dp_detect(struct drm_encoder *);
bool nouveau_dp_link_train(struct drm_encoder *);
void nouveau_dp_tu_update(struct drm_device *, int, int, u32, u32);
/* nv04_fb.c */
extern int nv04_fb_init(struct drm_device *);

View File

@ -49,9 +49,6 @@ struct nouveau_encoder {
union {
struct {
int mc_unknown;
uint32_t unk0;
uint32_t unk1;
int dpcd_version;
int link_nr;
int link_bw;

View File

@ -843,7 +843,7 @@
#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2 0x02000000
#define NV50_SOR_DP_UNK118(i, l) (0x0061c118 + (i) * 0x800 + (l) * 0x80)
#define NV50_SOR_DP_UNK120(i, l) (0x0061c120 + (i) * 0x800 + (l) * 0x80)
#define NV50_SOR_DP_UNK128(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80)
#define NV50_SOR_DP_SCFG(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80)
#define NV50_SOR_DP_UNK130(i, l) (0x0061c130 + (i) * 0x800 + (l) * 0x80)
#define NV50_PDISPLAY_USER(i) ((i) * 0x1000 + 0x00640000)

View File

@ -701,37 +701,6 @@ ack:
nv_wr32(dev, 0x610030, 0x80000000);
}
static void
nv50_display_unk20_dp_hack(struct drm_device *dev, struct dcb_entry *dcb)
{
int or = ffs(dcb->or) - 1, link = !(dcb->dpconf.sor.link & 1);
struct drm_encoder *encoder;
uint32_t tmp, unk0 = 0, unk1 = 0;
if (dcb->type != OUTPUT_DP)
return;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
if (nv_encoder->dcb == dcb) {
unk0 = nv_encoder->dp.unk0;
unk1 = nv_encoder->dp.unk1;
break;
}
}
if (unk0 || unk1) {
tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
tmp &= 0xfffffe03;
nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp | unk0);
tmp = nv_rd32(dev, NV50_SOR_DP_UNK128(or, link));
tmp &= 0xfef080c0;
nv_wr32(dev, NV50_SOR_DP_UNK128(or, link), tmp | unk1);
}
}
static void
nv50_display_unk20_handler(struct drm_device *dev)
{
@ -830,7 +799,13 @@ nv50_display_unk20_handler(struct drm_device *dev)
script = nv50_display_script_select(dev, dcb, mc, pclk);
nouveau_bios_run_display_table(dev, script, pclk, dcb, -1);
nv50_display_unk20_dp_hack(dev, dcb);
if (type == OUTPUT_DP) {
int link = !(dcb->dpconf.sor.link & 1);
if ((mc & 0x000f0000) == 0x00020000)
nouveau_dp_tu_update(dev, or, link, pclk, 18);
else
nouveau_dp_tu_update(dev, or, link, pclk, 24);
}
if (dcb->type != OUTPUT_ANALOG) {
tmp = nv_rd32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or));

View File

@ -187,6 +187,7 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_device *dev = encoder->dev;
struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc);
struct nouveau_connector *nv_connector;
uint32_t mode_ctl = 0;
int ret;
@ -206,7 +207,12 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
mode_ctl = 0x0200;
break;
case OUTPUT_DP:
mode_ctl |= (nv_encoder->dp.mc_unknown << 16);
nv_connector = nouveau_encoder_connector_get(nv_encoder);
if (nv_connector && nv_connector->base.display_info.bpc == 6)
mode_ctl |= 0x00020000;
else
mode_ctl |= 0x00050000;
if (nv_encoder->dcb->sorconf.link & 1)
mode_ctl |= 0x00000800;
else
@ -313,31 +319,6 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_entry *entry)
encoder->possible_crtcs = entry->heads;
encoder->possible_clones = 0;
if (nv_encoder->dcb->type == OUTPUT_DP) {
int or = nv_encoder->or, link = !(entry->dpconf.sor.link & 1);
uint32_t tmp;
tmp = nv_rd32(dev, 0x61c700 + (or * 0x800));
if (!tmp)
tmp = nv_rd32(dev, 0x610798 + (or * 8));
switch ((tmp & 0x00000f00) >> 8) {
case 8:
case 9:
nv_encoder->dp.mc_unknown = (tmp & 0x000f0000) >> 16;
tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
nv_encoder->dp.unk0 = tmp & 0x000001fc;
tmp = nv_rd32(dev, NV50_SOR_DP_UNK128(or, link));
nv_encoder->dp.unk1 = tmp & 0x010f7f3f;
break;
default:
break;
}
if (!nv_encoder->dp.mc_unknown)
nv_encoder->dp.mc_unknown = 5;
}
drm_mode_connector_attach_encoder(connector, encoder);
return 0;
}