Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 46959b77 authored by Ben Skeggs's avatar Ben Skeggs
Browse files

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



Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 43720133
Loading
Loading
Loading
Loading
+110 −1
Original line number Diff line number Diff line
@@ -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;

+1 −0
Original line number Diff line number Diff line
@@ -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 *);
+0 −3
Original line number Diff line number Diff line
@@ -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;
+1 −1
Original line number Diff line number Diff line
@@ -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)
+7 −32
Original line number Diff line number Diff line
@@ -701,37 +701,6 @@ nv50_display_unk10_handler(struct drm_device *dev)
	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));
Loading