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

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
...@@ -219,6 +219,7 @@ typedef struct drm_i915_private { ...@@ -219,6 +219,7 @@ typedef struct drm_i915_private {
unsigned int lvds_vbt:1; unsigned int lvds_vbt:1;
unsigned int int_crt_support:1; unsigned int int_crt_support:1;
unsigned int lvds_use_ssc:1; unsigned int lvds_use_ssc:1;
unsigned int edp_support:1;
int lvds_ssc_freq; int lvds_ssc_freq;
struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */ 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); ...@@ -889,6 +890,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
IS_I915GM(dev))) IS_I915GM(dev)))
#define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev) || IS_IGDNG(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_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)) #define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_I965G(dev))
/* dsparb controlled by hw only */ /* dsparb controlled by hw only */
#define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IGDNG(dev)) #define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IGDNG(dev))
......
...@@ -1395,6 +1395,7 @@ ...@@ -1395,6 +1395,7 @@
#define TV_V_CHROMA_42 0x684a8 #define TV_V_CHROMA_42 0x684a8
/* Display Port */ /* Display Port */
#define DP_A 0x64000 /* eDP */
#define DP_B 0x64100 #define DP_B 0x64100
#define DP_C 0x64200 #define DP_C 0x64200
#define DP_D 0x64300 #define DP_D 0x64300
...@@ -1437,9 +1438,17 @@ ...@@ -1437,9 +1438,17 @@
/* Mystic DPCD version 1.1 special mode */ /* Mystic DPCD version 1.1 special mode */
#define DP_ENHANCED_FRAMING (1 << 18) #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 */ /** locked once port is enabled */
#define DP_PORT_REVERSAL (1 << 15) #define DP_PORT_REVERSAL (1 << 15)
/* eDP */
#define DP_PLL_ENABLE (1 << 14)
/** sends the clock on lane 15 of the PEG for debug */ /** sends the clock on lane 15 of the PEG for debug */
#define DP_CLOCK_OUTPUT_ENABLE (1 << 13) #define DP_CLOCK_OUTPUT_ENABLE (1 << 13)
...@@ -1464,6 +1473,13 @@ ...@@ -1464,6 +1473,13 @@
* is 20 bytes in each direction, hence the 5 fixed * is 20 bytes in each direction, hence the 5 fixed
* data registers * 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_CTL 0x64110
#define DPB_AUX_CH_DATA1 0x64114 #define DPB_AUX_CH_DATA1 0x64114
#define DPB_AUX_CH_DATA2 0x64118 #define DPB_AUX_CH_DATA2 0x64118
......
...@@ -296,6 +296,25 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv, ...@@ -296,6 +296,25 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
} }
return; 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 * intel_init_bios - initialize VBIOS settings & find VBT
* @dev: DRM device * @dev: DRM device
...@@ -346,6 +365,8 @@ intel_init_bios(struct drm_device *dev) ...@@ -346,6 +365,8 @@ intel_init_bios(struct drm_device *dev)
parse_lfp_panel_data(dev_priv, bdb); parse_lfp_panel_data(dev_priv, bdb);
parse_sdvo_panel_data(dev_priv, bdb); parse_sdvo_panel_data(dev_priv, bdb);
parse_sdvo_device_mapping(dev_priv, bdb); parse_sdvo_device_mapping(dev_priv, bdb);
parse_driver_features(dev_priv, bdb);
pci_unmap_rom(pdev, bios); pci_unmap_rom(pdev, bios);
return 0; return 0;
......
...@@ -381,6 +381,51 @@ struct bdb_sdvo_lvds_options { ...@@ -381,6 +381,51 @@ struct bdb_sdvo_lvds_options {
} __attribute__((packed)); } __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); bool intel_init_bios(struct drm_device *dev);
/* /*
......
This diff is collapsed.
...@@ -40,6 +40,8 @@ ...@@ -40,6 +40,8 @@
#define DP_LINK_CONFIGURATION_SIZE 9 #define DP_LINK_CONFIGURATION_SIZE 9
#define IS_eDP(i) ((i)->type == INTEL_OUTPUT_EDP)
struct intel_dp_priv { struct intel_dp_priv {
uint32_t output_reg; uint32_t output_reg;
uint32_t DP; uint32_t DP;
...@@ -63,6 +65,19 @@ intel_dp_link_train(struct intel_output *intel_output, uint32_t DP, ...@@ -63,6 +65,19 @@ intel_dp_link_train(struct intel_output *intel_output, uint32_t DP,
static void static void
intel_dp_link_down(struct intel_output *intel_output, uint32_t DP); intel_dp_link_down(struct intel_output *intel_output, uint32_t DP);
void
intel_edp_link_config (struct intel_output *intel_output,
int *lane_num, int *link_bw)
{
struct intel_dp_priv *dp_priv = intel_output->dev_priv;
*lane_num = dp_priv->lane_count;
if (dp_priv->link_bw == DP_LINK_BW_1_62)
*link_bw = 162000;
else if (dp_priv->link_bw == DP_LINK_BW_2_7)
*link_bw = 270000;
}
static int static int
intel_dp_max_lane_count(struct intel_output *intel_output) intel_dp_max_lane_count(struct intel_output *intel_output)
{ {
...@@ -206,9 +221,10 @@ intel_dp_aux_ch(struct intel_output *intel_output, ...@@ -206,9 +221,10 @@ 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_eDP(intel_output))
if (IS_IGDNG(dev)) aux_clock_divider = 225; /* eDP input clock at 450Mhz */
aux_clock_divider = 62; else if (IS_IGDNG(dev))
aux_clock_divider = 62; /* IGDNG: input clock fixed at 125Mhz */
else else
aux_clock_divider = intel_hrawclk(dev) / 2; aux_clock_divider = intel_hrawclk(dev) / 2;
...@@ -579,8 +595,38 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, ...@@ -579,8 +595,38 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
if (intel_crtc->pipe == 1) if (intel_crtc->pipe == 1)
dp_priv->DP |= DP_PIPEB_SELECT; dp_priv->DP |= DP_PIPEB_SELECT;
if (IS_eDP(intel_output)) {
/* don't miss out required setting for eDP */
dp_priv->DP |= DP_PLL_ENABLE;
if (adjusted_mode->clock < 200000)
dp_priv->DP |= DP_PLL_FREQ_160MHZ;
else
dp_priv->DP |= DP_PLL_FREQ_270MHZ;
}
} }
static void igdng_edp_backlight_on (struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 pp;
DRM_DEBUG("\n");
pp = I915_READ(PCH_PP_CONTROL);
pp |= EDP_BLC_ENABLE;
I915_WRITE(PCH_PP_CONTROL, pp);
}
static void igdng_edp_backlight_off (struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 pp;
DRM_DEBUG("\n");
pp = I915_READ(PCH_PP_CONTROL);
pp &= ~EDP_BLC_ENABLE;
I915_WRITE(PCH_PP_CONTROL, pp);
}
static void static void
intel_dp_dpms(struct drm_encoder *encoder, int mode) intel_dp_dpms(struct drm_encoder *encoder, int mode)
...@@ -592,11 +638,17 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode) ...@@ -592,11 +638,17 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode)
uint32_t dp_reg = I915_READ(dp_priv->output_reg); uint32_t dp_reg = I915_READ(dp_priv->output_reg);
if (mode != DRM_MODE_DPMS_ON) { if (mode != DRM_MODE_DPMS_ON) {
if (dp_reg & DP_PORT_EN) if (dp_reg & DP_PORT_EN) {
intel_dp_link_down(intel_output, dp_priv->DP); intel_dp_link_down(intel_output, dp_priv->DP);
if (IS_eDP(intel_output))
igdng_edp_backlight_off(dev);
}
} else { } else {
if (!(dp_reg & DP_PORT_EN)) if (!(dp_reg & DP_PORT_EN)) {
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);
if (IS_eDP(intel_output))
igdng_edp_backlight_on(dev);
}
} }
dp_priv->dpms_mode = mode; dp_priv->dpms_mode = mode;
} }
...@@ -958,12 +1010,23 @@ intel_dp_link_down(struct intel_output *intel_output, uint32_t DP) ...@@ -958,12 +1010,23 @@ 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;
DRM_DEBUG("\n");
if (IS_eDP(intel_output)) {
DP &= ~DP_PLL_ENABLE;
I915_WRITE(dp_priv->output_reg, DP);
POSTING_READ(dp_priv->output_reg);
udelay(100);
}
DP &= ~DP_LINK_TRAIN_MASK; DP &= ~DP_LINK_TRAIN_MASK;
I915_WRITE(dp_priv->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE); I915_WRITE(dp_priv->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE);
POSTING_READ(dp_priv->output_reg); POSTING_READ(dp_priv->output_reg);
udelay(17000); udelay(17000);
if (IS_eDP(intel_output))
DP |= DP_LINK_TRAIN_OFF;
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);
} }
...@@ -1089,11 +1152,27 @@ intel_dp_detect(struct drm_connector *connector) ...@@ -1089,11 +1152,27 @@ intel_dp_detect(struct drm_connector *connector)
static int intel_dp_get_modes(struct drm_connector *connector) static int intel_dp_get_modes(struct drm_connector *connector)
{ {
struct intel_output *intel_output = to_intel_output(connector); struct intel_output *intel_output = to_intel_output(connector);
struct drm_device *dev = intel_output->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
/* We should parse the EDID data and find out if it has an audio sink /* We should parse the EDID data and find out if it has an audio sink
*/ */
return intel_ddc_get_modes(intel_output); ret = intel_ddc_get_modes(intel_output);
if (ret)
return ret;
/* if eDP has no EDID, try to use fixed panel mode from VBT */
if (IS_eDP(intel_output)) {
if (dev_priv->panel_fixed_mode != NULL) {
struct drm_display_mode *mode;
mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode);
drm_mode_probed_add(connector, mode);
return 1;
}
}
return 0;
} }
static void static void
...@@ -1170,7 +1249,10 @@ intel_dp_init(struct drm_device *dev, int output_reg) ...@@ -1170,7 +1249,10 @@ intel_dp_init(struct drm_device *dev, int output_reg)
DRM_MODE_CONNECTOR_DisplayPort); DRM_MODE_CONNECTOR_DisplayPort);
drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs); drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs);
intel_output->type = INTEL_OUTPUT_DISPLAYPORT; if (output_reg == DP_A)
intel_output->type = INTEL_OUTPUT_EDP;
else
intel_output->type = INTEL_OUTPUT_DISPLAYPORT;
connector->interlace_allowed = true; connector->interlace_allowed = true;
connector->doublescan_allowed = 0; connector->doublescan_allowed = 0;
...@@ -1191,6 +1273,9 @@ intel_dp_init(struct drm_device *dev, int output_reg) ...@@ -1191,6 +1273,9 @@ intel_dp_init(struct drm_device *dev, int output_reg)
/* Set up the DDC bus. */ /* Set up the DDC bus. */
switch (output_reg) { switch (output_reg) {
case DP_A:
name = "DPDDC-A";
break;
case DP_B: case DP_B:
case PCH_DP_B: case PCH_DP_B:
name = "DPDDC-B"; name = "DPDDC-B";
...@@ -1206,9 +1291,22 @@ intel_dp_init(struct drm_device *dev, int output_reg) ...@@ -1206,9 +1291,22 @@ intel_dp_init(struct drm_device *dev, int output_reg)
} }
intel_dp_i2c_init(intel_output, name); 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;
if (output_reg == DP_A) {
/* initialize panel mode from VBT if available for eDP */
if (dev_priv->lfp_lvds_vbt_mode) {
dev_priv->panel_fixed_mode =
drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode);
if (dev_priv->panel_fixed_mode) {
dev_priv->panel_fixed_mode->type |=
DRM_MODE_TYPE_PREFERRED;
}
}
}
/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written /* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
* 0xd. Failure to do so will result in spurious interrupts being * 0xd. Failure to do so will result in spurious interrupts being
* generated on the port when a cable is not attached. * generated on the port when a cable is not attached.
......
...@@ -55,6 +55,7 @@ ...@@ -55,6 +55,7 @@
#define INTEL_OUTPUT_TVOUT 5 #define INTEL_OUTPUT_TVOUT 5
#define INTEL_OUTPUT_HDMI 6 #define INTEL_OUTPUT_HDMI 6
#define INTEL_OUTPUT_DISPLAYPORT 7 #define INTEL_OUTPUT_DISPLAYPORT 7
#define INTEL_OUTPUT_EDP 8
#define INTEL_DVO_CHIP_NONE 0 #define INTEL_DVO_CHIP_NONE 0
#define INTEL_DVO_CHIP_LVDS 1 #define INTEL_DVO_CHIP_LVDS 1
...@@ -121,6 +122,8 @@ extern void intel_dp_init(struct drm_device *dev, int dp_reg); ...@@ -121,6 +122,8 @@ extern void intel_dp_init(struct drm_device *dev, int dp_reg);
void void
intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode); struct drm_display_mode *adjusted_mode);
extern void intel_edp_link_config (struct intel_output *, int *, int *);
extern void intel_crtc_load_lut(struct drm_crtc *crtc); extern void intel_crtc_load_lut(struct drm_crtc *crtc);
extern void intel_encoder_prepare (struct drm_encoder *encoder); extern void intel_encoder_prepare (struct drm_encoder *encoder);
......
...@@ -892,6 +892,10 @@ void intel_lvds_init(struct drm_device *dev) ...@@ -892,6 +892,10 @@ void intel_lvds_init(struct drm_device *dev)
if (IS_IGDNG(dev)) { if (IS_IGDNG(dev)) {
if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0) if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0)
return; return;
if (dev_priv->edp_support) {
DRM_DEBUG("disable LVDS for eDP support\n");
return;
}
gpio = PCH_GPIOC; gpio = PCH_GPIOC;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment