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

Commit 32f9d658 authored by Zhenyu Wang's avatar Zhenyu Wang Committed by Eric Anholt
Browse files

drm/i915: Add eDP support on IGDNG mobile chip



This adds embedded DisplayPort support on next mobile chip which
aims to replace origin LVDS port. VBT's driver feature block has
been used to determine the type of current internal panel for eDP
or LVDS.

Currently no panel fitting support for eDP and backlight control
would be added in future.

Signed-off-by: default avatarZhenyu Wang <zhenyuw@linux.intel.com>
Signed-off-by: default avatarEric Anholt <eric@anholt.net>
parent 5eb08b69
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -219,6 +219,7 @@ typedef struct drm_i915_private {
	unsigned int lvds_vbt:1;
	unsigned int int_crt_support:1;
	unsigned int lvds_use_ssc:1;
	unsigned int edp_support:1;
	int lvds_ssc_freq;

	struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */
@@ -889,6 +890,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
						      IS_I915GM(dev)))
#define SUPPORTS_INTEGRATED_HDMI(dev)	(IS_G4X(dev) || IS_IGDNG(dev))
#define SUPPORTS_INTEGRATED_DP(dev)	(IS_G4X(dev) || IS_IGDNG(dev))
#define SUPPORTS_EDP(dev)		(IS_IGDNG_M(dev))
#define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_I965G(dev))
/* dsparb controlled by hw only */
#define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IGDNG(dev))
+16 −0
Original line number Diff line number Diff line
@@ -1395,6 +1395,7 @@
#define TV_V_CHROMA_42		0x684a8

/* Display Port */
#define DP_A				0x64000 /* eDP */
#define DP_B				0x64100
#define DP_C				0x64200
#define DP_D				0x64300
@@ -1437,9 +1438,17 @@
/* Mystic DPCD version 1.1 special mode */
#define   DP_ENHANCED_FRAMING		(1 << 18)

/* eDP */
#define   DP_PLL_FREQ_270MHZ		(0 << 16)
#define   DP_PLL_FREQ_160MHZ		(1 << 16)
#define   DP_PLL_FREQ_MASK		(3 << 16)

/** locked once port is enabled */
#define   DP_PORT_REVERSAL		(1 << 15)

/* eDP */
#define   DP_PLL_ENABLE			(1 << 14)

/** sends the clock on lane 15 of the PEG for debug */
#define   DP_CLOCK_OUTPUT_ENABLE	(1 << 13)

@@ -1464,6 +1473,13 @@
 * is 20 bytes in each direction, hence the 5 fixed
 * data registers
 */
#define DPA_AUX_CH_CTL			0x64010
#define DPA_AUX_CH_DATA1		0x64014
#define DPA_AUX_CH_DATA2		0x64018
#define DPA_AUX_CH_DATA3		0x6401c
#define DPA_AUX_CH_DATA4		0x64020
#define DPA_AUX_CH_DATA5		0x64024

#define DPB_AUX_CH_CTL			0x64110
#define DPB_AUX_CH_DATA1		0x64114
#define DPB_AUX_CH_DATA2		0x64118
+21 −0
Original line number Diff line number Diff line
@@ -296,6 +296,25 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
	}
	return;
}

static void
parse_driver_features(struct drm_i915_private *dev_priv,
		       struct bdb_header *bdb)
{
	struct drm_device *dev = dev_priv->dev;
	struct bdb_driver_features *driver;

	/* set default for chips without eDP */
	if (!SUPPORTS_EDP(dev)) {
		dev_priv->edp_support = 0;
		return;
	}

	driver = find_section(bdb, BDB_DRIVER_FEATURES);
	if (driver && driver->lvds_config == BDB_DRIVER_FEATURE_EDP)
		dev_priv->edp_support = 1;
}

/**
 * intel_init_bios - initialize VBIOS settings & find VBT
 * @dev: DRM device
@@ -346,6 +365,8 @@ intel_init_bios(struct drm_device *dev)
	parse_lfp_panel_data(dev_priv, bdb);
	parse_sdvo_panel_data(dev_priv, bdb);
	parse_sdvo_device_mapping(dev_priv, bdb);
	parse_driver_features(dev_priv, bdb);

	pci_unmap_rom(pdev, bios);

	return 0;
+45 −0
Original line number Diff line number Diff line
@@ -381,6 +381,51 @@ struct bdb_sdvo_lvds_options {
} __attribute__((packed));


#define BDB_DRIVER_FEATURE_NO_LVDS		0
#define BDB_DRIVER_FEATURE_INT_LVDS		1
#define BDB_DRIVER_FEATURE_SDVO_LVDS		2
#define BDB_DRIVER_FEATURE_EDP			3

struct bdb_driver_features {
	u8 boot_dev_algorithm:1;
	u8 block_display_switch:1;
	u8 allow_display_switch:1;
	u8 hotplug_dvo:1;
	u8 dual_view_zoom:1;
	u8 int15h_hook:1;
	u8 sprite_in_clone:1;
	u8 primary_lfp_id:1;

	u16 boot_mode_x;
	u16 boot_mode_y;
	u8 boot_mode_bpp;
	u8 boot_mode_refresh;

	u16 enable_lfp_primary:1;
	u16 selective_mode_pruning:1;
	u16 dual_frequency:1;
	u16 render_clock_freq:1; /* 0: high freq; 1: low freq */
	u16 nt_clone_support:1;
	u16 power_scheme_ui:1; /* 0: CUI; 1: 3rd party */
	u16 sprite_display_assign:1; /* 0: secondary; 1: primary */
	u16 cui_aspect_scaling:1;
	u16 preserve_aspect_ratio:1;
	u16 sdvo_device_power_down:1;
	u16 crt_hotplug:1;
	u16 lvds_config:2;
	u16 tv_hotplug:1;
	u16 hdmi_config:2;

	u8 static_display:1;
	u8 reserved2:7;
	u16 legacy_crt_max_x;
	u16 legacy_crt_max_y;
	u8 legacy_crt_max_refresh;

	u8 hdmi_termination;
	u8 custom_vbt_version;
} __attribute__((packed));

bool intel_init_bios(struct drm_device *dev);

/*
+272 −161
Original line number Diff line number Diff line
@@ -34,6 +34,8 @@

#include "drm_crtc_helper.h"

#define HAS_eDP (intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))

bool intel_pipe_has_type (struct drm_crtc *crtc, int type);
static void intel_update_watermarks(struct drm_device *dev);

@@ -601,6 +603,23 @@ bool intel_pipe_has_type (struct drm_crtc *crtc, int type)
    return false;
}

struct drm_connector *
intel_pipe_get_output (struct drm_crtc *crtc)
{
    struct drm_device *dev = crtc->dev;
    struct drm_mode_config *mode_config = &dev->mode_config;
    struct drm_connector *l_entry, *ret = NULL;

    list_for_each_entry(l_entry, &mode_config->connector_list, head) {
	    if (l_entry->encoder &&
	        l_entry->encoder->crtc == crtc) {
		    ret = l_entry;
		    break;
	    }
    }
    return ret;
}

#define INTELPllInvalid(s)   do { /* DRM_DEBUG(s); */ return false; } while (0)
/**
 * Returns whether the given set of divisors are valid for a given refclk with
@@ -790,6 +809,10 @@ intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
	int err_most = 47;
	found = false;

	/* eDP has only 2 clock choice, no n/m/p setting */
	if (HAS_eDP)
		return true;

	if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT))
		return intel_find_pll_igdng_dp(limit, crtc, target,
					       refclk, best_clock);
@@ -1052,6 +1075,67 @@ static void i915_disable_vga (struct drm_device *dev)
	I915_WRITE(vga_reg, VGA_DISP_DISABLE);
}

static void igdng_disable_pll_edp (struct drm_crtc *crtc)
{
	struct drm_device *dev = crtc->dev;
	struct drm_i915_private *dev_priv = dev->dev_private;
	u32 dpa_ctl;

	DRM_DEBUG("\n");
	dpa_ctl = I915_READ(DP_A);
	dpa_ctl &= ~DP_PLL_ENABLE;
	I915_WRITE(DP_A, dpa_ctl);
}

static void igdng_enable_pll_edp (struct drm_crtc *crtc)
{
	struct drm_device *dev = crtc->dev;
	struct drm_i915_private *dev_priv = dev->dev_private;
	u32 dpa_ctl;

	dpa_ctl = I915_READ(DP_A);
	dpa_ctl |= DP_PLL_ENABLE;
	I915_WRITE(DP_A, dpa_ctl);
	udelay(200);
}


static void igdng_set_pll_edp (struct drm_crtc *crtc, int clock)
{
	struct drm_device *dev = crtc->dev;
	struct drm_i915_private *dev_priv = dev->dev_private;
	u32 dpa_ctl;

	DRM_DEBUG("eDP PLL enable for clock %d\n", clock);
	dpa_ctl = I915_READ(DP_A);
	dpa_ctl &= ~DP_PLL_FREQ_MASK;

	if (clock < 200000) {
		u32 temp;
		dpa_ctl |= DP_PLL_FREQ_160MHZ;
		/* workaround for 160Mhz:
		   1) program 0x4600c bits 15:0 = 0x8124
		   2) program 0x46010 bit 0 = 1
		   3) program 0x46034 bit 24 = 1
		   4) program 0x64000 bit 14 = 1
		   */
		temp = I915_READ(0x4600c);
		temp &= 0xffff0000;
		I915_WRITE(0x4600c, temp | 0x8124);

		temp = I915_READ(0x46010);
		I915_WRITE(0x46010, temp | 1);

		temp = I915_READ(0x46034);
		I915_WRITE(0x46034, temp | (1 << 24));
	} else {
		dpa_ctl |= DP_PLL_FREQ_270MHZ;
	}
	I915_WRITE(DP_A, dpa_ctl);

	udelay(500);
}

static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
{
	struct drm_device *dev = crtc->dev;
@@ -1093,6 +1177,10 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
	case DRM_MODE_DPMS_STANDBY:
	case DRM_MODE_DPMS_SUSPEND:
		DRM_DEBUG("crtc %d dpms on\n", pipe);
		if (HAS_eDP) {
			/* enable eDP PLL */
			igdng_enable_pll_edp(crtc);
		} else {
			/* enable PCH DPLL */
			temp = I915_READ(pch_dpll_reg);
			if ((temp & DPLL_VCO_ENABLE) == 0) {
@@ -1115,6 +1203,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
				I915_READ(fdi_tx_reg);
				udelay(100);
			}
		}

		/* Enable CPU pipe */
		temp = I915_READ(pipeconf_reg);
@@ -1132,6 +1221,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
			I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
		}

		if (!HAS_eDP) {
			/* enable CPU FDI TX and PCH FDI RX */
			temp = I915_READ(fdi_tx_reg);
			temp |= FDI_TX_ENABLE;
@@ -1210,7 +1300,8 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
				} else
					DRM_DEBUG("train 2 fail\n");
			} else {
			I915_WRITE(fdi_rx_iir_reg, temp | FDI_RX_SYMBOL_LOCK);
				I915_WRITE(fdi_rx_iir_reg,
						temp | FDI_RX_SYMBOL_LOCK);
				DRM_DEBUG("train 2 ok 2!\n");
			}
			DRM_DEBUG("train done\n");
@@ -1249,6 +1340,8 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
			/* wait one idle pattern time */
			udelay(100);

		}

		intel_crtc_load_lut(crtc);

	break;
@@ -1286,6 +1379,10 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
		} else
			DRM_DEBUG("crtc %d is disabled\n", pipe);

		if (HAS_eDP) {
			igdng_disable_pll_edp(crtc);
		}

		/* disable CPU FDI tx and PCH FDI rx */
		temp = I915_READ(fdi_tx_reg);
		I915_WRITE(fdi_tx_reg, temp & ~FDI_TX_ENABLE);
@@ -2152,6 +2249,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
	u32 dpll = 0, fp = 0, dspcntr, pipeconf;
	bool ok, is_sdvo = false, is_dvo = false;
	bool is_crt = false, is_lvds = false, is_tv = false, is_dp = false;
	bool is_edp = false;
	struct drm_mode_config *mode_config = &dev->mode_config;
	struct drm_connector *connector;
	const intel_limit_t *limit;
@@ -2199,6 +2297,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
		case INTEL_OUTPUT_DISPLAYPORT:
			is_dp = true;
			break;
		case INTEL_OUTPUT_EDP:
			is_edp = true;
			break;
		}

		num_outputs++;
@@ -2251,16 +2352,27 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,

	/* FDI link */
	if (IS_IGDNG(dev)) {
		int lane, link_bw;
		/* eDP doesn't require FDI link, so just set DP M/N
		   according to current link config */
		if (is_edp) {
			struct drm_connector *edp;
			target_clock = mode->clock;
			edp = intel_pipe_get_output(crtc);
			intel_edp_link_config(to_intel_output(edp),
					&lane, &link_bw);
		} else {
			/* 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 */
				target_clock,
				270000, /* lane clock */
				&m_n);
			lane = 4;
			link_bw = 270000;
		}
		igdng_compute_m_n(3, lane, target_clock,
				  link_bw, &m_n);
	}

	if (IS_IGD(dev))
@@ -2382,29 +2494,15 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
		dpll_reg = pch_dpll_reg;
	}

	if (dpll & DPLL_VCO_ENABLE) {
	if (is_edp) {
		igdng_disable_pll_edp(crtc);
	} else if ((dpll & DPLL_VCO_ENABLE)) {
		I915_WRITE(fp_reg, fp);
		I915_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE);
		I915_READ(dpll_reg);
		udelay(150);
	}

	if (IS_IGDNG(dev)) {
		/* enable PCH clock reference source */
		/* XXX need to change the setting for other outputs */
		u32 temp;
		temp = I915_READ(PCH_DREF_CONTROL);
		temp &= ~DREF_NONSPREAD_SOURCE_MASK;
		temp |= DREF_NONSPREAD_CK505_ENABLE;
		temp &= ~DREF_SSC_SOURCE_MASK;
		temp |= DREF_SSC_SOURCE_ENABLE;
		temp &= ~DREF_SSC1_ENABLE;
		/* if no eDP, disable source output to CPU */
		temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
		temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
		I915_WRITE(PCH_DREF_CONTROL, temp);
	}

	/* The LVDS pin pair needs to be on before the DPLLs are enabled.
	 * This is an exception to the general rule that mode_set doesn't turn
	 * things on.
@@ -2436,6 +2534,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
	if (is_dp)
		intel_dp_set_m_n(crtc, mode, adjusted_mode);

	if (!is_edp) {
		I915_WRITE(fp_reg, fp);
		I915_WRITE(dpll_reg, dpll);
		I915_READ(dpll_reg);
@@ -2453,6 +2552,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
		I915_READ(dpll_reg);
		/* Wait for the clocks to stabilize. */
		udelay(150);
	}

	I915_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) |
		   ((adjusted_mode->crtc_htotal - 1) << 16));
@@ -2482,11 +2582,15 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
		I915_WRITE(link_m1_reg, m_n.link_m);
		I915_WRITE(link_n1_reg, m_n.link_n);

		if (is_edp) {
			igdng_set_pll_edp(crtc, adjusted_mode->clock);
		} else {
			/* enable FDI RX PLL too */
			temp = I915_READ(fdi_rx_reg);
			I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE);
			udelay(200);
		}
	}

	I915_WRITE(pipeconf_reg, pipeconf);
	I915_READ(pipeconf_reg);
@@ -3083,6 +3187,9 @@ static void intel_setup_outputs(struct drm_device *dev)
	if (IS_IGDNG(dev)) {
		int found;

		if (IS_MOBILE(dev) && (I915_READ(DP_A) & DP_DETECTED))
			intel_dp_init(dev, DP_A);

		if (I915_READ(HDMIB) & PORT_DETECTED) {
			/* check SDVOB */
			/* found = intel_sdvo_init(dev, HDMIB); */
@@ -3179,6 +3286,10 @@ static void intel_setup_outputs(struct drm_device *dev)
				     (1 << 1));
			clone_mask = (1 << INTEL_OUTPUT_DISPLAYPORT);
			break;
		case INTEL_OUTPUT_EDP:
			crtc_mask = (1 << 1);
			clone_mask = (1 << INTEL_OUTPUT_EDP);
			break;
		}
		encoder->possible_crtcs = crtc_mask;
		encoder->possible_clones = intel_connector_clones(dev, clone_mask);
Loading