/*
* Copyright © 2012-2013 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Authors:
* Eugeni Dodonov <eugeni.dodonov@intel.com>
*
*/
/*
*/
#include "i915_drv.h"
#include "intel_drv.h"
* them for both DP and FDI transports, allowing those ports to
* automatically adapt to HDMI connections as well
*/
0x00FFFFFF, 0x0006000E, /* DP parameters */
0x00D75FFF, 0x0005000A,
0x00C30FFF, 0x00040006,
0x80AAAFFF, 0x000B0000,
0x00FFFFFF, 0x0005000A,
0x00D75FFF, 0x000C0004,
0x80C30FFF, 0x000B0000,
0x00FFFFFF, 0x00040006,
0x80D75FFF, 0x000B0000,
0x00FFFFFF, 0x00040006 /* HDMI parameters */
};
0x00FFFFFF, 0x0007000E, /* FDI parameters */
0x00D75FFF, 0x000F000A,
0x00C30FFF, 0x00060006,
0x00AAAFFF, 0x001E0000,
0x00FFFFFF, 0x000F000A,
0x00D75FFF, 0x00160004,
0x00C30FFF, 0x001E0000,
0x00FFFFFF, 0x00060006,
0x00D75FFF, 0x001E0000,
0x00FFFFFF, 0x00040006 /* HDMI parameters */
};
{
return intel_dig_port->port;
} else if (type == INTEL_OUTPUT_ANALOG) {
return PORT_E;
} else {
BUG();
}
return I915_MAX_PORTS;
}
/* On Haswell, DDI port buffers must be programmed with correct values
* in advance. The buffer values are different for FDI and DP modes,
* in either FDI or DP modes only, as HDMI connections will work with both
* of those
*/
bool use_fdi_mode)
{
int i;
DRM_DEBUG_DRIVER("Initializing DDI buffers for port %c in %s mode\n",
DRM_ERROR("Programming port %c in FDI mode, this probably will not work.",
reg += 4;
}
}
/* Program DDI buffers translations for DP. By default, program ports A-D in DP
* mode and port E for FDI.
*/
{
int port;
return;
/* DDI E is the suggested one to work in FDI mode, so program is as such
* by default. It will have to be re-programmed in case a digital DP
* output will be detected on it
*/
}
static const long hsw_ddi_buf_ctl_values[] = {
};
{
int i;
for (i = 0; i < 8; i++) {
udelay(1);
return;
}
}
/* Starting with Haswell, different DDI ports can work in FDI mode for
* connection to the PCH-located connectors. For this, it is necessary to train
* both the DDI port and PCH receiver for the desired DDI buffer settings.
*
* The recommended port to work in FDI mode is DDI E, which we use here. Also,
* please note that when FDI mode is active on DDI E, it shares 2 lines with
* DDI A (which is used for eDP)
*/
{
/* Set the FDI_RX_MISC pwrdn lanes and the 2 workarounds listed at the
* mode set "sequence for CRT port" document:
* - TP1 to TP2 time with the default value
* - FDI delay to 90h
*
* WaFDIAutoLinkSetTimingOverrride:hsw
*/
/* Enable the PCH Receiver FDI PLL */
udelay(220);
/* Switch from Rawclk to PCDclk */
rx_ctl_val |= FDI_PCDCLK;
/* Configure Port Clock Select */
/* Start the training iterating through available voltages and emphasis,
* testing each value twice. */
/* Configure DP_TP_CTL with auto-training */
/* Configure and enable DDI_BUF_CTL for DDI E with next voltage.
* DDI E does not support port reversal, the functionality is
* achieved on the PCH side in FDI_RX_CTL, so no need to set the
* port reversal bit */
hsw_ddi_buf_ctl_values[i / 2]);
udelay(600);
/* Program PCH FDI Receiver TU */
/* Enable PCH FDI Receiver with auto-training */
/* Wait for FDI receiver lane calibration */
udelay(30);
/* Unset FDI_RX_MISC pwrdn lanes */
/* Wait for FDI auto training time */
udelay(5);
if (temp & DP_TP_STATUS_AUTOTRAIN_DONE) {
DRM_DEBUG_KMS("FDI link training done on step %d\n", i);
/* Enable normal pixel sending for FDI */
return;
}
temp &= ~DDI_BUF_CTL_ENABLE;
/* Disable DP_TP_CTL and FDI_RX_CTL and retry */
rx_ctl_val &= ~FDI_RX_ENABLE;
/* Reset FDI_RX_MISC pwrdn lanes */
}
DRM_ERROR("FDI link training failed!\n");
}
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
DRM_DEBUG_KMS("Preparing DDI mode on port %c, pipe %c\n",
intel_crtc->eld_vld = false;
DRM_DEBUG_DRIVER("DP audio on pipe %c on DDI\n",
/* write eld */
DRM_DEBUG_DRIVER("DP audio: write eld information\n");
}
} else if (type == INTEL_OUTPUT_HDMI) {
if (intel_hdmi->has_audio) {
/* Proper support for digital audio needs a new logic
* and a new set of registers, so we leave it for future
* patch bombing.
*/
DRM_DEBUG_DRIVER("HDMI audio on pipe %c on DDI\n",
/* write eld */
DRM_DEBUG_DRIVER("HDMI audio: write eld information\n");
}
}
}
static struct intel_encoder *
{
int num_encoders = 0;
ret = intel_encoder;
num_encoders++;
}
if (num_encoders != 1) {
}
return ret;
}
{
switch (intel_crtc->ddi_pll_sel) {
case PORT_CLK_SEL_SPLL:
plls->spll_refcount--;
if (plls->spll_refcount == 0) {
DRM_DEBUG_KMS("Disabling SPLL\n");
}
break;
case PORT_CLK_SEL_WRPLL1:
plls->wrpll1_refcount--;
if (plls->wrpll1_refcount == 0) {
DRM_DEBUG_KMS("Disabling WRPLL 1\n");
}
break;
case PORT_CLK_SEL_WRPLL2:
plls->wrpll2_refcount--;
if (plls->wrpll2_refcount == 0) {
DRM_DEBUG_KMS("Disabling WRPLL 2\n");
}
break;
}
if(plls->spll_refcount < 0)
DRM_ERROR("Invalid SPLL refcount\n");
if(plls->wrpll1_refcount < 0)
DRM_ERROR("Invalid WRPLL1 refcount\n");
if(plls->wrpll2_refcount < 0)
DRM_ERROR("Invalid WRPLL2 refcount\n");
}
/* Constraints for PLL good behavior */
#define ABS_DIFF(a, b) ((a > b) ? (a - b) : (b - a))
struct wrpll_rnp {
};
{
unsigned budget;
switch (clock) {
case 25175000:
case 25200000:
case 27000000:
case 27027000:
case 37762500:
case 37800000:
case 40500000:
case 40541000:
case 54000000:
case 54054000:
case 59341000:
case 59400000:
case 72000000:
case 74176000:
case 74250000:
case 81000000:
case 81081000:
case 89012000:
case 89100000:
case 108000000:
case 108108000:
case 111264000:
case 111375000:
case 148352000:
case 148500000:
case 162000000:
case 162162000:
case 222525000:
case 222750000:
case 296703000:
case 297000000:
budget = 0;
break;
case 233500000:
case 245250000:
case 247750000:
case 253250000:
case 298000000:
budget = 1500;
break;
case 169128000:
case 169500000:
case 179500000:
case 202000000:
budget = 2000;
break;
case 256250000:
case 262500000:
case 270000000:
case 272500000:
case 273750000:
case 280750000:
case 281250000:
case 286000000:
case 291750000:
budget = 4000;
break;
case 267250000:
case 268500000:
budget = 5000;
break;
default:
budget = 1000;
break;
}
return budget;
}
{
/* No best (r,n,p) yet */
if (best->p == 0) {
best->p = p;
return;
}
/*
* Output clock is (LC_FREQ_2K / 2000) * N / (P * R), which compares to
* freq2k.
*
* delta = 1e6 *
* abs(freq2k - (LC_FREQ_2K * n2/(p * r2))) /
* freq2k;
*
* and we would like delta <= budget.
*
* If the discrepancy is above the PPM-based budget, always prefer to
* improve upon the previous solution. However, if you're within the
* budget, try to maximize Ref * VCO, that is N / (P * R^2).
*/
c = 1000000 * diff;
d = 1000000 * diff_best;
if (a < c && b < d) {
/* If both are above the budget, pick the closer */
best->p = p;
}
} else if (a >= c && b < d) {
/* If A is below the threshold but B is above it? Update. */
best->p = p;
} else if (a >= c && b >= d) {
/* Both are below the limit, so pick the higher n2/(r2*r2) */
best->p = p;
}
}
/* Otherwise a < c && b >= d, do nothing */
}
static void
{
unsigned budget;
/* Special case handling for 540 pixel clock: bypass WR PLL entirely
* and directly pass the LC PLL to it. */
if (freq2k == 5400000) {
*n2_out = 2;
*p_out = 1;
*r2_out = 2;
return;
}
/*
* Ref = LC_FREQ / R, where Ref is the actual reference input seen by
* the WR PLL.
*
* We want R so that REF_MIN <= Ref <= REF_MAX.
* Injecting R2 = 2 * R gives:
* REF_MAX * r2 > LC_FREQ * 2 and
* REF_MIN * r2 < LC_FREQ * 2
*
* Which means the desired boundaries for r2 are:
* LC_FREQ * 2 / REF_MAX < r2 < LC_FREQ * 2 / REF_MIN
*
*/
r2++) {
/*
* VCO = N * Ref, that is: VCO = N * LC_FREQ / R
*
* Once again we want VCO_MIN <= VCO <= VCO_MAX.
* Injecting R2 = 2 * R and N2 = 2 * N, we get:
* VCO_MAX * r2 > n2 * LC_FREQ and
* VCO_MIN * r2 < n2 * LC_FREQ)
*
* Which means the desired boundaries for n2 are:
* VCO_MIN * r2 / LC_FREQ < n2 < VCO_MAX * r2 / LC_FREQ
*/
n2++) {
}
}
DRM_DEBUG_KMS("WRPLL: %dHz refresh rate with p=%d, n2=%d r2=%d\n",
}
{
/* TODO: reuse PLLs when possible (compare values) */
case DP_LINK_BW_1_62:
break;
case DP_LINK_BW_2_7:
break;
case DP_LINK_BW_5_4:
break;
default:
DRM_ERROR("Link bandwidth %d unsupported\n",
return false;
}
/* We don't need to turn any PLL on because we'll use LCPLL. */
return true;
} else if (type == INTEL_OUTPUT_HDMI) {
if (plls->wrpll1_refcount == 0) {
DRM_DEBUG_KMS("Using WRPLL 1 on pipe %c\n",
plls->wrpll1_refcount++;
reg = WRPLL_CTL1;
} else if (plls->wrpll2_refcount == 0) {
DRM_DEBUG_KMS("Using WRPLL 2 on pipe %c\n",
plls->wrpll2_refcount++;
reg = WRPLL_CTL2;
} else {
DRM_ERROR("No WRPLLs available!\n");
return false;
}
DRM_ERROR("WRPLL already enabled\n");
} else if (type == INTEL_OUTPUT_ANALOG) {
if (plls->spll_refcount == 0) {
DRM_DEBUG_KMS("Using SPLL on pipe %c\n",
plls->spll_refcount++;
} else {
DRM_ERROR("SPLL already in use\n");
return false;
}
DRM_ERROR("SPLL already enabled\n");
} else {
BUG_ON(1);
return false;
}
udelay(20);
return true;
}
{
case 18:
temp |= TRANS_MSA_6_BPC;
break;
case 24:
temp |= TRANS_MSA_8_BPC;
break;
case 30:
temp |= TRANS_MSA_10_BPC;
break;
case 36:
temp |= TRANS_MSA_12_BPC;
break;
default:
BUG_ON(1);
}
}
}
{
/* Enable TRANS_DDI_FUNC_CTL for the pipe to work in HDMI mode */
case 18:
temp |= TRANS_DDI_BPC_6;
break;
case 24:
temp |= TRANS_DDI_BPC_8;
break;
case 30:
temp |= TRANS_DDI_BPC_10;
break;
case 36:
temp |= TRANS_DDI_BPC_12;
break;
default:
BUG_ON(1);
}
temp |= TRANS_DDI_PVSYNC;
temp |= TRANS_DDI_PHSYNC;
if (cpu_transcoder == TRANSCODER_EDP) {
switch (pipe) {
case PIPE_A:
/* Can only use the always-on power well for eDP when
* not using the panel fitter, and when not using motion
* blur mitigation (which we don't support). */
else
break;
case PIPE_B:
break;
case PIPE_C:
break;
default:
BUG();
break;
}
}
if (type == INTEL_OUTPUT_HDMI) {
if (intel_hdmi->has_hdmi_sink)
else
} else if (type == INTEL_OUTPUT_ANALOG) {
} else if (type == INTEL_OUTPUT_DISPLAYPORT ||
type == INTEL_OUTPUT_EDP) {
} else {
DRM_ERROR("Invalid encoder type %d for pipe %c\n",
BUG_ON(1);
}
}
enum transcoder cpu_transcoder)
{
}
{
return false;
else
switch (tmp & TRANS_DDI_MODE_SELECT_MASK) {
return (type == DRM_MODE_CONNECTOR_HDMIA);
if (type == DRM_MODE_CONNECTOR_eDP)
return true;
return (type == DRM_MODE_CONNECTOR_DisplayPort);
return (type == DRM_MODE_CONNECTOR_VGA);
default:
return false;
}
}
{
int i;
if (!(tmp & DDI_BUF_CTL_ENABLE))
return false;
switch (tmp & TRANS_DDI_EDP_INPUT_MASK) {
case TRANS_DDI_EDP_INPUT_A_ON:
break;
break;
break;
}
return true;
} else {
for (i = TRANSCODER_A; i <= TRANSCODER_C; i++) {
if ((tmp & TRANS_DDI_PORT_MASK)
== TRANS_DDI_SELECT_PORT(port)) {
*pipe = i;
return true;
}
}
}
return false;
}
{
pipe);
int i;
if (cpu_transcoder == TRANSCODER_EDP) {
} else {
if (temp == TRANS_DDI_SELECT_PORT(i))
port = i;
}
if (port == I915_MAX_PORTS) {
DRM_ERROR("Pipe %c enabled on an unknown port\n",
} else {
DRM_DEBUG_KMS("Pipe %c connected to port %c using clock "
ret);
}
return ret;
}
{
if (!intel_crtc->active)
continue;
pipe);
switch (intel_crtc->ddi_pll_sel) {
case PORT_CLK_SEL_SPLL:
break;
case PORT_CLK_SEL_WRPLL1:
break;
case PORT_CLK_SEL_WRPLL2:
break;
}
}
}
{
if (cpu_transcoder != TRANSCODER_EDP)
}
{
if (cpu_transcoder != TRANSCODER_EDP)
}
{
if (type == INTEL_OUTPUT_EDP) {
ironlake_edp_panel_vdd_off(intel_dp, true);
}
}
}
{
bool wait = false;
if (val & DDI_BUF_CTL_ENABLE) {
val &= ~DDI_BUF_CTL_ENABLE;
wait = true;
}
if (wait)
if (type == INTEL_OUTPUT_EDP) {
}
}
{
if (type == INTEL_OUTPUT_HDMI) {
* are ignored so nothing special needs to be done besides
* enabling the port.
*/
} else if (type == INTEL_OUTPUT_EDP) {
}
}
}
{
(pipe * 4));
}
if (type == INTEL_OUTPUT_EDP) {
}
}
{
return 450000;
return 450000;
return 337500;
else
return 540000;
}
{
/* The LCPLL register should be turned on by the BIOS. For now let's
* just check its state and print errors in case something is wrong.
* Don't even try to turn it on.
*/
DRM_DEBUG_KMS("CDCLK running at %dKHz\n",
if (val & LCPLL_CD_SOURCE_FCLK)
DRM_ERROR("CDCLK source is not LCPLL\n");
if (val & LCPLL_PLL_DISABLE)
DRM_ERROR("LCPLL is disabled\n");
}
{
bool wait = false;
if (val & DDI_BUF_CTL_ENABLE) {
val &= ~DDI_BUF_CTL_ENABLE;
wait = true;
}
if (wait)
}
udelay(600);
}
{
val &= ~FDI_RX_ENABLE;
val &= ~FDI_PCDCLK;
val &= ~FDI_RX_PLL_ENABLE;
}
{
}
struct intel_crtc_config *pipe_config)
{
if (temp & TRANS_DDI_PHSYNC)
else
if (temp & TRANS_DDI_PVSYNC)
else
}
{
/* HDMI has nothing special to destroy, so we can go with this. */
}
struct intel_crtc_config *pipe_config)
{
if(type == INTEL_OUTPUT_UNKNOWN)
DRM_ERROR("mode_fixup() on unknown output!");
if (type == INTEL_OUTPUT_HDMI)
else
}
};
};
{
if (!intel_dig_port)
return;
if (!dp_connector) {
return;
}
intel_encoder->cloneable = false;
return;
}
if (!hdmi_connector) {
return;
}
}
}