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

Commit 5eb08b69 authored by Zhenyu Wang's avatar Zhenyu Wang Committed by Eric Anholt
Browse files

drm/i915: enable DisplayPort support on IGDNG

parent eebc863e
Loading
Loading
Loading
Loading
+25 −0
Original line number Original line Diff line number Diff line
@@ -1444,6 +1444,7 @@
#define   DP_CLOCK_OUTPUT_ENABLE	(1 << 13)
#define   DP_CLOCK_OUTPUT_ENABLE	(1 << 13)


#define   DP_SCRAMBLING_DISABLE		(1 << 12)
#define   DP_SCRAMBLING_DISABLE		(1 << 12)
#define   DP_SCRAMBLING_DISABLE_IGDNG	(1 << 7)


/** limit RGB values to avoid confusing TVs */
/** limit RGB values to avoid confusing TVs */
#define   DP_COLOR_RANGE_16_235		(1 << 8)
#define   DP_COLOR_RANGE_16_235		(1 << 8)
@@ -2210,4 +2211,28 @@
#define PCH_PP_OFF_DELAYS	0xc720c
#define PCH_PP_OFF_DELAYS	0xc720c
#define PCH_PP_DIVISOR		0xc7210
#define PCH_PP_DIVISOR		0xc7210


#define PCH_DP_B		0xe4100
#define PCH_DPB_AUX_CH_CTL	0xe4110
#define PCH_DPB_AUX_CH_DATA1	0xe4114
#define PCH_DPB_AUX_CH_DATA2	0xe4118
#define PCH_DPB_AUX_CH_DATA3	0xe411c
#define PCH_DPB_AUX_CH_DATA4	0xe4120
#define PCH_DPB_AUX_CH_DATA5	0xe4124

#define PCH_DP_C		0xe4200
#define PCH_DPC_AUX_CH_CTL	0xe4210
#define PCH_DPC_AUX_CH_DATA1	0xe4214
#define PCH_DPC_AUX_CH_DATA2	0xe4218
#define PCH_DPC_AUX_CH_DATA3	0xe421c
#define PCH_DPC_AUX_CH_DATA4	0xe4220
#define PCH_DPC_AUX_CH_DATA5	0xe4224

#define PCH_DP_D		0xe4300
#define PCH_DPD_AUX_CH_CTL	0xe4310
#define PCH_DPD_AUX_CH_DATA1	0xe4314
#define PCH_DPD_AUX_CH_DATA2	0xe4318
#define PCH_DPD_AUX_CH_DATA3	0xe431c
#define PCH_DPD_AUX_CH_DATA4	0xe4320
#define PCH_DPD_AUX_CH_DATA5	0xe4324

#endif /* _I915_REG_H_ */
#endif /* _I915_REG_H_ */
+49 −2
Original line number Original line Diff line number Diff line
@@ -268,6 +268,9 @@ intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
static bool
static bool
intel_find_pll_g4x_dp(const intel_limit_t *, struct drm_crtc *crtc,
intel_find_pll_g4x_dp(const intel_limit_t *, struct drm_crtc *crtc,
		      int target, int refclk, intel_clock_t *best_clock);
		      int target, int refclk, intel_clock_t *best_clock);
static bool
intel_find_pll_igdng_dp(const intel_limit_t *, struct drm_crtc *crtc,
		      int target, int refclk, intel_clock_t *best_clock);


static const intel_limit_t intel_limits_i8xx_dvo = {
static const intel_limit_t intel_limits_i8xx_dvo = {
        .dot = { .min = I8XX_DOT_MIN,		.max = I8XX_DOT_MAX },
        .dot = { .min = I8XX_DOT_MIN,		.max = I8XX_DOT_MAX },
@@ -751,6 +754,30 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
	return found;
	return found;
}
}


static bool
intel_find_pll_igdng_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
		      int target, int refclk, intel_clock_t *best_clock)
{
	struct drm_device *dev = crtc->dev;
	intel_clock_t clock;
	if (target < 200000) {
		clock.n = 1;
		clock.p1 = 2;
		clock.p2 = 10;
		clock.m1 = 12;
		clock.m2 = 9;
	} else {
		clock.n = 2;
		clock.p1 = 1;
		clock.p2 = 10;
		clock.m1 = 14;
		clock.m2 = 8;
	}
	intel_clock(dev, refclk, &clock);
	memcpy(best_clock, &clock, sizeof(intel_clock_t));
	return true;
}

static bool
static bool
intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
			int target, int refclk, intel_clock_t *best_clock)
			int target, int refclk, intel_clock_t *best_clock)
@@ -763,6 +790,10 @@ intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
	int err_most = 47;
	int err_most = 47;
	found = false;
	found = false;


	if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT))
		return intel_find_pll_igdng_dp(limit, crtc, target,
					       refclk, best_clock);

	if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
	if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
		if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
		if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
		    LVDS_CLKB_POWER_UP)
		    LVDS_CLKB_POWER_UP)
@@ -2136,6 +2167,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
	int lvds_reg = LVDS;
	int lvds_reg = LVDS;
	u32 temp;
	u32 temp;
	int sdvo_pixel_multiply;
	int sdvo_pixel_multiply;
	int target_clock;


	drm_vblank_pre_modeset(dev, pipe);
	drm_vblank_pre_modeset(dev, pipe);


@@ -2218,11 +2250,18 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
	}
	}


	/* FDI link */
	/* FDI link */
	if (IS_IGDNG(dev))
	if (IS_IGDNG(dev)) {
		/* DP over FDI requires target mode clock
		   instead of link clock */
		if (is_dp)
			target_clock = mode->clock;
		else
			target_clock = adjusted_mode->clock;
		igdng_compute_m_n(3, 4, /* lane num 4 */
		igdng_compute_m_n(3, 4, /* lane num 4 */
				adjusted_mode->clock,
				target_clock,
				270000, /* lane clock */
				270000, /* lane clock */
				&m_n);
				&m_n);
	}


	if (IS_IGD(dev))
	if (IS_IGD(dev))
		fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2;
		fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2;
@@ -3050,6 +3089,8 @@ static void intel_setup_outputs(struct drm_device *dev)
			found = 0;
			found = 0;
			if (!found)
			if (!found)
				intel_hdmi_init(dev, HDMIB);
				intel_hdmi_init(dev, HDMIB);
			if (!found && (I915_READ(PCH_DP_B) & DP_DETECTED))
				intel_dp_init(dev, PCH_DP_B);
		}
		}


		if (I915_READ(HDMIC) & PORT_DETECTED)
		if (I915_READ(HDMIC) & PORT_DETECTED)
@@ -3058,6 +3099,12 @@ static void intel_setup_outputs(struct drm_device *dev)
		if (I915_READ(HDMID) & PORT_DETECTED)
		if (I915_READ(HDMID) & PORT_DETECTED)
			intel_hdmi_init(dev, HDMID);
			intel_hdmi_init(dev, HDMID);


		if (I915_READ(PCH_DP_C) & DP_DETECTED)
			intel_dp_init(dev, PCH_DP_C);

		if (I915_READ(PCH_DP_D) & DP_DETECTED)
			intel_dp_init(dev, PCH_DP_D);

	} else if (IS_I9XX(dev)) {
	} else if (IS_I9XX(dev)) {
		int found;
		int found;
		u32 reg;
		u32 reg;
+83 −19
Original line number Original line Diff line number Diff line
@@ -206,7 +206,12 @@ intel_dp_aux_ch(struct intel_output *intel_output,
	 * and would like to run at 2MHz. So, take the
	 * and would like to run at 2MHz. So, take the
	 * hrawclk value and divide by 2 and use that
	 * hrawclk value and divide by 2 and use that
	 */
	 */
	/* IGDNG: input clock fixed at 125Mhz, so aux_bit_clk always 62 */
	if (IS_IGDNG(dev))
		aux_clock_divider = 62;
	else
		aux_clock_divider = intel_hrawclk(dev) / 2;
		aux_clock_divider = intel_hrawclk(dev) / 2;

	/* Must try at least 3 times according to DP spec */
	/* Must try at least 3 times according to DP spec */
	for (try = 0; try < 5; try++) {
	for (try = 0; try < 5; try++) {
		/* Load the send data into the aux channel data registers */
		/* Load the send data into the aux channel data registers */
@@ -493,6 +498,23 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
	intel_dp_compute_m_n(3, lane_count,
	intel_dp_compute_m_n(3, lane_count,
			     mode->clock, adjusted_mode->clock, &m_n);
			     mode->clock, adjusted_mode->clock, &m_n);


	if (IS_IGDNG(dev)) {
		if (intel_crtc->pipe == 0) {
			I915_WRITE(TRANSA_DATA_M1,
				   ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
				   m_n.gmch_m);
			I915_WRITE(TRANSA_DATA_N1, m_n.gmch_n);
			I915_WRITE(TRANSA_DP_LINK_M1, m_n.link_m);
			I915_WRITE(TRANSA_DP_LINK_N1, m_n.link_n);
		} else {
			I915_WRITE(TRANSB_DATA_M1,
				   ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
				   m_n.gmch_m);
			I915_WRITE(TRANSB_DATA_N1, m_n.gmch_n);
			I915_WRITE(TRANSB_DP_LINK_M1, m_n.link_m);
			I915_WRITE(TRANSB_DP_LINK_N1, m_n.link_n);
		}
	} else {
		if (intel_crtc->pipe == 0) {
		if (intel_crtc->pipe == 0) {
			I915_WRITE(PIPEA_GMCH_DATA_M,
			I915_WRITE(PIPEA_GMCH_DATA_M,
				   ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
				   ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
@@ -511,6 +533,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
			I915_WRITE(PIPEB_DP_LINK_N, m_n.link_n);
			I915_WRITE(PIPEB_DP_LINK_N, m_n.link_n);
		}
		}
	}
	}
}


static void
static void
intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
@@ -935,6 +958,12 @@ intel_dp_link_down(struct intel_output *intel_output, uint32_t DP)
	struct drm_i915_private *dev_priv = dev->dev_private;
	struct drm_i915_private *dev_priv = dev->dev_private;
	struct intel_dp_priv *dp_priv = intel_output->dev_priv;
	struct intel_dp_priv *dp_priv = intel_output->dev_priv;


	DP &= ~DP_LINK_TRAIN_MASK;
	I915_WRITE(dp_priv->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE);
	POSTING_READ(dp_priv->output_reg);

	udelay(17000);

	I915_WRITE(dp_priv->output_reg, DP & ~DP_PORT_EN);
	I915_WRITE(dp_priv->output_reg, DP & ~DP_PORT_EN);
	POSTING_READ(dp_priv->output_reg);
	POSTING_READ(dp_priv->output_reg);
}
}
@@ -978,6 +1007,24 @@ intel_dp_check_link_status(struct intel_output *intel_output)
		intel_dp_link_train(intel_output, dp_priv->DP, dp_priv->link_configuration);
		intel_dp_link_train(intel_output, dp_priv->DP, dp_priv->link_configuration);
}
}


static enum drm_connector_status
igdng_dp_detect(struct drm_connector *connector)
{
	struct intel_output *intel_output = to_intel_output(connector);
	struct intel_dp_priv *dp_priv = intel_output->dev_priv;
	enum drm_connector_status status;

	status = connector_status_disconnected;
	if (intel_dp_aux_native_read(intel_output,
				     0x000, dp_priv->dpcd,
				     sizeof (dp_priv->dpcd)) == sizeof (dp_priv->dpcd))
	{
		if (dp_priv->dpcd[0] != 0)
			status = connector_status_connected;
	}
	return status;
}

/**
/**
 * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect DP connection.
 * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect DP connection.
 *
 *
@@ -996,6 +1043,9 @@ intel_dp_detect(struct drm_connector *connector)


	dp_priv->has_audio = false;
	dp_priv->has_audio = false;


	if (IS_IGDNG(dev))
		return igdng_dp_detect(connector);

	temp = I915_READ(PORT_HOTPLUG_EN);
	temp = I915_READ(PORT_HOTPLUG_EN);


	I915_WRITE(PORT_HOTPLUG_EN,
	I915_WRITE(PORT_HOTPLUG_EN,
@@ -1106,6 +1156,7 @@ intel_dp_init(struct drm_device *dev, int output_reg)
	struct drm_connector *connector;
	struct drm_connector *connector;
	struct intel_output *intel_output;
	struct intel_output *intel_output;
	struct intel_dp_priv *dp_priv;
	struct intel_dp_priv *dp_priv;
	const char *name = NULL;


	intel_output = kcalloc(sizeof(struct intel_output) + 
	intel_output = kcalloc(sizeof(struct intel_output) + 
			       sizeof(struct intel_dp_priv), 1, GFP_KERNEL);
			       sizeof(struct intel_dp_priv), 1, GFP_KERNEL);
@@ -1139,9 +1190,22 @@ intel_dp_init(struct drm_device *dev, int output_reg)
	drm_sysfs_connector_add(connector);
	drm_sysfs_connector_add(connector);


	/* Set up the DDC bus. */
	/* Set up the DDC bus. */
	intel_dp_i2c_init(intel_output,
	switch (output_reg) {
			  (output_reg == DP_B) ? "DPDDC-B" :
		case DP_B:
			  (output_reg == DP_C) ? "DPDDC-C" : "DPDDC-D");
		case PCH_DP_B:
			name = "DPDDC-B";
			break;
		case DP_C:
		case PCH_DP_C:
			name = "DPDDC-C";
			break;
		case DP_D:
		case PCH_DP_D:
			name = "DPDDC-D";
			break;
	}

	intel_dp_i2c_init(intel_output, name);
	intel_output->ddc_bus = &dp_priv->adapter;
	intel_output->ddc_bus = &dp_priv->adapter;
	intel_output->hot_plug = intel_dp_hot_plug;
	intel_output->hot_plug = intel_dp_hot_plug;