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:
parent
4372013388
commit
46959b7790
@ -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;
|
||||
|
||||
|
@ -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 *);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user