aboutsummaryrefslogtreecommitdiff
path: root/src/drivers/intel/gma/intel_dp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/intel/gma/intel_dp.c')
-rw-r--r--src/drivers/intel/gma/intel_dp.c1707
1 files changed, 1707 insertions, 0 deletions
diff --git a/src/drivers/intel/gma/intel_dp.c b/src/drivers/intel/gma/intel_dp.c
new file mode 100644
index 0000000000..4b5f7c49c1
--- /dev/null
+++ b/src/drivers/intel/gma/intel_dp.c
@@ -0,0 +1,1707 @@
+/*
+ * Copyright 2013 Google Inc.
+ * Copyright © 2008 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,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * 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:
+ * Keith Packard <keithp@keithp.com>
+ *
+ */
+
+/* This code was created by the coccinnelle filters in the i915tool,
+ * with some final hand filtering.
+ */
+
+#include <console/console.h>
+#include <stdint.h>
+#include <delay.h>
+#include <drivers/intel/gma/i915.h>
+#include <string.h>
+
+/**
+ * is_edp - is the given port attached to an eDP panel (either CPU or PCH)
+ * @intel_dp: DP struct
+ *
+ * If a CPU or PCH DP output is attached to an eDP panel, this function
+ * will return 1, and 0 otherwise.
+ */
+static int is_edp(struct intel_dp *intel_dp)
+{
+ return intel_dp->type == INTEL_OUTPUT_EDP;
+}
+
+/**
+ * is_pch_edp - is the port on the PCH and attached to an eDP panel?
+ * @intel_dp: DP struct
+ *
+ * Returns 1 if the given DP struct corresponds to a PCH DP port attached
+ * to an eDP panel, 0 otherwise. Helpful for determining whether we
+ * may need FDI resources for a given DP output or not.
+ */
+static int is_pch_edp(struct intel_dp *intel_dp)
+{
+ return intel_dp->is_pch_edp;
+}
+
+/**
+ * is_cpu_edp - is the port on the CPU and attached to an eDP panel?
+ * @intel_dp: DP struct
+ *
+ * Returns 1 if the given DP struct corresponds to a CPU eDP port.
+ */
+static int is_cpu_edp(struct intel_dp *intel_dp)
+{
+ return is_edp(intel_dp) && !is_pch_edp(intel_dp);
+}
+
+static uint32_t
+pack_aux(uint8_t *src, int src_bytes)
+{
+ int i;
+ uint32_t v = 0;
+
+ if (src_bytes > 4)
+ src_bytes = 4;
+ for (i = 0; i < src_bytes; i++)
+ v |= ((uint32_t) src[i]) << ((3-i) * 8);
+ return v;
+}
+
+void
+unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes)
+{
+ int i;
+ if (dst_bytes > 4)
+ dst_bytes = 4;
+ for (i = 0; i < dst_bytes; i++)
+ dst[i] = src >> ((3-i) * 8);
+}
+
+static int ironlake_edp_have_panel_power(struct intel_dp *intel_dp)
+{
+
+ return (io_i915_read32(PCH_PP_STATUS) & PP_ON) != 0;
+}
+
+static int ironlake_edp_have_panel_vdd(struct intel_dp *intel_dp)
+{
+ return (io_i915_read32(PCH_PP_CONTROL) & EDP_FORCE_VDD) != 0;
+}
+
+int
+intel_dp_aux_ch(struct intel_dp *intel_dp,
+ uint8_t *send, int send_bytes,
+ uint8_t *recv, int recv_size)
+{
+ uint32_t output_reg = intel_dp->output_reg;
+ uint32_t ch_ctl = output_reg + 0x10;
+ uint32_t ch_data = ch_ctl + 4;
+ int i;
+ int recv_bytes;
+ uint32_t status;
+ int try;
+
+ /* Try to wait for any previous AUX channel activity */
+ for (try = 0; try < 3; try++) {
+ status = io_i915_read32(ch_ctl);
+ if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0)
+ break;
+ mdelay(1);
+ }
+
+ if (try == 3) {
+ if (1) {
+ status = io_i915_read32(ch_ctl);
+ printk(BIOS_ERR,
+ "dp_aux_ch not started status 0x%08x\n",
+ status);
+ }
+ return -1;
+ }
+
+ /* Must try at least 3 times according to DP spec */
+ for (try = 0; try < 5; try++) {
+ /* Load the send data into the aux channel data registers */
+ for (i = 0; i < send_bytes; i += 4){
+ u32 val, addr;
+ val = pack_aux(send + i, send_bytes - i);
+ addr = ch_data + i;
+ io_i915_write32(val, addr);
+ }
+
+ /* Send the command and wait for it to complete */
+ io_i915_write32(DP_AUX_CH_CTL_SEND_BUSY |
+ DP_AUX_CH_CTL_TIME_OUT_400us |
+ (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
+ (intel_dp->precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
+ (intel_dp->aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) |
+ DP_AUX_CH_CTL_DONE |
+ DP_AUX_CH_CTL_TIME_OUT_ERROR |
+ DP_AUX_CH_CTL_RECEIVE_ERROR, ch_ctl);
+ for (;;) {
+ status = io_i915_read32(ch_ctl);
+ if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0)
+ break;
+ udelay(100);
+ }
+
+ /* Clear done status and any errors */
+ io_i915_write32(status |
+ DP_AUX_CH_CTL_DONE |
+ DP_AUX_CH_CTL_TIME_OUT_ERROR |
+ DP_AUX_CH_CTL_RECEIVE_ERROR, ch_ctl);
+
+ if (status & (DP_AUX_CH_CTL_TIME_OUT_ERROR |
+ DP_AUX_CH_CTL_RECEIVE_ERROR))
+ continue;
+ if (status & DP_AUX_CH_CTL_DONE)
+ break;
+ }
+
+ if ((status & DP_AUX_CH_CTL_DONE) == 0) {
+ printk(BIOS_ERR, "dp_aux_ch not done status 0x%08x\n", status);
+ return -1;
+ }
+
+ /* Check for timeout or receive error.
+ * Timeouts occur when the sink is not connected
+ */
+ if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) {
+ printk(BIOS_ERR,
+ "dp_aux_ch receive error status 0x%08x\n", status);
+ return -1;
+ }
+
+ /* Timeouts occur when the device isn't connected, so they're
+ * "normal" -- don't fill the kernel log with these */
+ if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) {
+ printk(BIOS_ERR, "dp_aux_ch timeout status 0x%08x\n", status);
+ return -1;
+ }
+
+ /* Unload any bytes sent back from the other side */
+ recv_bytes = ((status & DP_AUX_CH_CTL_MESSAGE_SIZE_MASK) >>
+ DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT);
+ if (recv_bytes > recv_size)
+ recv_bytes = recv_size;
+
+ for (i = 0; i < recv_bytes; i += 4)
+ unpack_aux(io_i915_read32(ch_data + i),
+ recv + i, recv_bytes - i);
+
+ return recv_bytes;
+}
+
+/* Write data to the aux channel in native mode */
+static int
+intel_dp_aux_native_write(struct intel_dp *intel_dp,
+ uint16_t address, uint8_t *send, int send_bytes)
+{
+ int ret;
+ uint8_t msg[20];
+ int msg_bytes;
+ uint8_t ack;
+
+ if (send_bytes > 16)
+ return -1;
+ msg[0] = AUX_NATIVE_WRITE << 4;
+ msg[1] = address >> 8;
+ msg[2] = address & 0xff;
+ msg[3] = send_bytes - 1;
+ memcpy(&msg[4], send, send_bytes);
+ msg_bytes = send_bytes + 4;
+ for (;;) {
+ ret = intel_dp_aux_ch(intel_dp, msg, msg_bytes, &ack, 1);
+ if (ret < 0)
+ return ret;
+ if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK)
+ break;
+ else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER)
+ udelay(100);
+ else
+ return -1;
+ }
+ return send_bytes;
+}
+
+/* Write a single byte to the aux channel in native mode */
+static int
+intel_dp_aux_native_write_1(struct intel_dp *intel_dp,
+ uint16_t address, uint8_t byte)
+{
+ return intel_dp_aux_native_write(intel_dp, address, &byte, 1);
+}
+
+/* read bytes from a native aux channel */
+static int
+intel_dp_aux_native_read(struct intel_dp *intel_dp,
+ uint16_t address, uint8_t *recv, int recv_bytes)
+{
+ uint8_t msg[4];
+ int msg_bytes;
+ uint8_t reply[20];
+ int reply_bytes;
+ uint8_t ack;
+ int ret;
+
+ msg[0] = AUX_NATIVE_READ << 4;
+ msg[1] = address >> 8;
+ msg[2] = address & 0xff;
+ msg[3] = recv_bytes - 1;
+
+ msg_bytes = 4;
+ reply_bytes = recv_bytes + 1;
+
+ for (;;) {
+ ret = intel_dp_aux_ch(intel_dp, msg, msg_bytes,
+ reply, reply_bytes);
+ if (ret == 0)
+ return -1;
+ if (ret < 0)
+ return ret;
+ ack = reply[0];
+ if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK) {
+ memcpy(recv, reply + 1, ret - 1);
+ return ret - 1;
+ }
+ else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER)
+ udelay(100);
+ else
+ return -1;
+ }
+}
+
+int
+intel_dp_i2c_aux_ch(struct intel_dp *intel_dp,
+ int mode, uint8_t write_byte, uint8_t *read_byte)
+{
+ uint8_t msg[5];
+ uint8_t reply[2];
+ unsigned retry;
+ int msg_bytes;
+ int reply_bytes;
+ int ret;
+
+ /* Set up the command byte */
+ if (mode & MODE_I2C_READ)
+ msg[0] = AUX_I2C_READ << 4;
+ else
+ msg[0] = AUX_I2C_WRITE << 4;
+
+ if (!(mode & MODE_I2C_STOP))
+ msg[0] |= AUX_I2C_MOT << 4;
+
+ msg[1] = intel_dp->address >> 8;
+ msg[2] = intel_dp->address;
+
+ switch (mode) {
+ case MODE_I2C_WRITE:
+ msg[3] = 0;
+ msg[4] = write_byte;
+ msg_bytes = 5;
+ reply_bytes = 1;
+ break;
+ case MODE_I2C_READ:
+ msg[3] = 0;
+ msg_bytes = 4;
+ reply_bytes = 2;
+ break;
+ default:
+ msg_bytes = 3;
+ reply_bytes = 1;
+ break;
+ }
+
+ for (retry = 0; retry < 5; retry++) {
+ ret = intel_dp_aux_ch(intel_dp,
+ msg, msg_bytes,
+ reply, reply_bytes);
+ if (ret < 0) {
+ printk(BIOS_ERR, "aux_ch failed %d\n", ret);
+ return ret;
+ }
+
+ switch (reply[0] & AUX_NATIVE_REPLY_MASK) {
+ case AUX_NATIVE_REPLY_ACK:
+ /* I2C-over-AUX Reply field is only valid
+ * when paired with AUX ACK.
+ */
+ break;
+ case AUX_NATIVE_REPLY_NACK:
+ printk(BIOS_ERR, "aux_ch native nack\n");
+ return -1;
+ case AUX_NATIVE_REPLY_DEFER:
+ udelay(100);
+ continue;
+ default:
+ printk(BIOS_ERR, "aux_ch invalid native reply 0x%02x\n",
+ reply[0]);
+ return -1;
+ }
+
+ switch (reply[0] & AUX_I2C_REPLY_MASK) {
+ case AUX_I2C_REPLY_ACK:
+ if (mode == MODE_I2C_READ) {
+ *read_byte = reply[1];
+ }
+ return reply_bytes - 1;
+ case AUX_I2C_REPLY_NACK:
+ printk(BIOS_ERR, "aux_i2c nack\n");
+ return -1;
+ case AUX_I2C_REPLY_DEFER:
+ printk(BIOS_ERR, "aux_i2c defer\n");
+ udelay(100);
+ break;
+ default:
+ printk(BIOS_ERR,
+ "aux_i2c invalid reply 0x%02x\n", reply[0]);
+ return -1;
+ }
+ }
+
+ printk(BIOS_ERR, "too many retries, giving up\n");
+ return -1;
+}
+
+int
+intel_dp_i2c_init(struct intel_dp *intel_dp)
+{
+ int ret = 0;
+
+ /* not clear what we need to do here, if anything.
+ * this function was more about setting up the kernel.
+ * it's a handy placeholder, so we leave it in for now.
+ */
+ return ret;
+}
+
+struct intel_dp_m_n {
+ uint32_t tu;
+ uint32_t gmch_m;
+ uint32_t gmch_n;
+ uint32_t link_m;
+ uint32_t link_n;
+};
+
+static void
+intel_reduce_ratio(uint32_t *num, uint32_t *den)
+{
+ while (*num > 0xffffff || *den > 0xffffff) {
+ *num >>= 1;
+ *den >>= 1;
+ }
+}
+
+static void
+intel_dp_compute_m_n(int bpp,
+ int nlanes,
+ int pixel_clock,
+ int link_clock,
+ struct intel_dp_m_n *m_n)
+{
+ m_n->tu = 64;
+ m_n->gmch_m = (pixel_clock * bpp) >> 3;
+ m_n->gmch_n = link_clock * nlanes;
+ intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n);
+ m_n->link_m = pixel_clock;
+ m_n->link_n = link_clock;
+ intel_reduce_ratio(&m_n->link_m, &m_n->link_n);
+}
+
+/* not sure. */
+void intel_dp_set_m_n(struct intel_dp *intel_dp);
+
+void
+intel_dp_set_m_n(struct intel_dp *intel_dp)
+{
+ int lane_count;
+ struct intel_dp_m_n m_n;
+ int pipe = intel_dp->pipe;
+
+ lane_count = intel_dp->lane_count;
+
+ /*
+ * Compute the GMCH and Link ratios. The '3' here is
+ * the number of bytes_per_pixel post-LUT, which we always
+ * set up for 8-bits of R/G/B, or 3 bytes total.
+ */
+ intel_dp_compute_m_n(intel_dp->bpp, lane_count,
+ intel_dp->clock, intel_dp->clock, &m_n);
+
+ {
+ io_i915_write32((
+ (m_n.tu - 1) <<
+ PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
+ m_n.gmch_m, TRANSDATA_M1(pipe));
+ io_i915_write32(m_n.gmch_n, TRANSDATA_N1(pipe));
+ io_i915_write32(m_n.link_m, TRANSDPLINK_M1(pipe));
+ io_i915_write32(m_n.link_n, TRANSDPLINK_N1(pipe));
+ }
+}
+
+static void ironlake_edp_pll_off(void);
+
+void
+intel_dp_mode_set(struct intel_dp *intel_dp)
+{
+ /* Turn on the eDP PLL if needed */
+ if (is_edp(intel_dp)) {
+ if (!is_pch_edp(intel_dp))
+ ironlake_edp_pll_on();
+ else
+ ironlake_edp_pll_off();
+ }
+
+ /*
+ * There are four kinds of DP registers:
+ *
+ * IBX PCH
+ * SNB CPU
+ * IVB CPU
+ * CPT PCH
+ *
+ * IBX PCH and CPU are the same for almost everything,
+ * except that the CPU DP PLL is configured in this
+ * register
+ *
+ * CPT PCH is quite different, having many bits moved
+ * to the TRANS_DP_CTL register instead. That
+ * configuration happens (oddly) in ironlake_pch_enable
+ */
+
+ /* Preserve the BIOS-computed detected bit. This is
+ * supposed to be read-only.
+ */
+ intel_dp->DP = io_i915_read32(intel_dp->output_reg) & DP_DETECTED;
+ printk(BIOS_SPEW, "%s: initial value is %08lx\n", __func__,
+ (unsigned long)intel_dp->DP);
+ /* | 0 essentially */
+ intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0;
+
+ /* Handle DP bits in common between all three register formats */
+
+ switch (intel_dp->lane_count) {
+ case 1:
+ intel_dp->DP |= DP_PORT_WIDTH_1;
+ break;
+ case 2:
+ intel_dp->DP |= DP_PORT_WIDTH_2;
+ break;
+ case 4:
+ intel_dp->DP |= DP_PORT_WIDTH_4;
+ break;
+ }
+ memset(intel_dp->link_configuration, 0, DP_LINK_CONFIGURATION_SIZE);
+ intel_dp->link_configuration[0] = intel_dp->link_bw;
+ intel_dp->link_configuration[1] = intel_dp->lane_count;
+ intel_dp->link_configuration[8] = DP_SET_ANSI_8B10B;
+ /*
+ * Check for DPCD version > 1.1 and enhanced framing support
+ */
+ if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 &&
+ (intel_dp->dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP)) {
+ intel_dp->link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+ }
+
+ /* Split out the IBX/CPU vs CPT settings */
+
+ if (is_cpu_edp(intel_dp) && (intel_dp->gen == 7)) {
+ /* what are these? We're not sure.
+ if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
+ intel_dp->DP |= DP_SYNC_HS_HIGH;
+
+ if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
+ */
+ intel_dp->DP |= DP_SYNC_VS_HIGH;
+ /* */
+ intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT;
+
+ if (intel_dp->link_configuration[1] &
+ DP_LANE_COUNT_ENHANCED_FRAME_EN)
+ intel_dp->DP |= DP_ENHANCED_FRAMING;
+
+ intel_dp->DP |= intel_dp->pipe << 29;
+
+ /* don't miss out required setting for eDP */
+ intel_dp->DP |= DP_PLL_ENABLE;
+ if (intel_dp->clock < 200000)
+ intel_dp->DP |= DP_PLL_FREQ_160MHZ;
+ else
+ intel_dp->DP |= DP_PLL_FREQ_270MHZ;
+ } else if (!intel_dp->has_pch_cpt || is_cpu_edp(intel_dp)) {
+ intel_dp->DP |= intel_dp->color_range;
+ intel_dp->DP |= DP_LINK_TRAIN_OFF;
+
+ if (intel_dp->link_configuration[1] &
+ DP_LANE_COUNT_ENHANCED_FRAME_EN)
+ intel_dp->DP |= DP_ENHANCED_FRAMING;
+
+ if (intel_dp->pipe == 1)
+ intel_dp->DP |= DP_PIPEB_SELECT;
+
+ if (is_cpu_edp(intel_dp)) {
+ /* don't miss out required setting for eDP */
+ intel_dp->DP |= DP_PLL_ENABLE;
+ if (intel_dp->clock < 200000)
+ intel_dp->DP |= DP_PLL_FREQ_160MHZ;
+ else
+ intel_dp->DP |= DP_PLL_FREQ_270MHZ;
+ }
+ } else {
+ intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT;
+ }
+}
+
+#define IDLE_ON_MASK (PP_ON | 0 | PP_SEQUENCE_MASK | 0 | PP_SEQUENCE_STATE_MASK)
+#define IDLE_ON_VALUE (PP_ON | 0 | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_ON_IDLE)
+
+#define IDLE_OFF_MASK (PP_ON | 0 | PP_SEQUENCE_MASK | 0 | PP_SEQUENCE_STATE_MASK)
+#define IDLE_OFF_VALUE (0 | 0 | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_OFF_IDLE)
+
+#define IDLE_CYCLE_MASK (PP_ON | 0 | PP_SEQUENCE_MASK | PP_CYCLE_DELAY_ACTIVE | PP_SEQUENCE_STATE_MASK)
+#define IDLE_CYCLE_VALUE (0 | 0 | PP_SEQUENCE_NONE | 0 | PP_SEQUENCE_STATE_OFF_IDLE)
+
+static void ironlake_wait_panel_status(struct intel_dp *intel_dp,
+ u32 mask,
+ u32 value)
+{
+ int i;
+ u32 status;
+ printk(BIOS_ERR, "[000000.0] [drm:%s], ", __func__);
+ printk(BIOS_ERR, "mask %08lx value %08lx status %08lx control %08lx\n",
+ (unsigned long) mask, (unsigned long) value,
+ (unsigned long)io_i915_read32(PCH_PP_STATUS),
+ (unsigned long)io_i915_read32(PCH_PP_CONTROL));
+
+ for(i = 0, status = io_i915_read32(PCH_PP_STATUS); ((status & mask) != value) && (i < 5000);
+ status = io_i915_read32(PCH_PP_STATUS)){
+ udelay(10);
+ }
+ if (i > 5000){
+ printk(BIOS_ERR,
+ "Panel status timeout: status %08lx control %08lx\n",
+ (unsigned long)io_i915_read32(PCH_PP_STATUS),
+ (unsigned long)io_i915_read32(PCH_PP_CONTROL));
+ }
+}
+
+static void ironlake_wait_panel_on(struct intel_dp *intel_dp)
+{
+ printk(BIOS_ERR, "Wait for panel power on\n");
+ ironlake_wait_panel_status(intel_dp, IDLE_ON_MASK, IDLE_ON_VALUE);
+}
+
+static void ironlake_wait_panel_off(struct intel_dp *intel_dp)
+{
+ printk(BIOS_ERR, "Wait for panel power off time\n");
+ ironlake_wait_panel_status(intel_dp, IDLE_OFF_MASK, IDLE_OFF_VALUE);
+}
+
+static void ironlake_wait_panel_power_cycle(struct intel_dp *intel_dp)
+{
+ printk(BIOS_ERR, "Wait for panel power cycle\n");
+ ironlake_wait_panel_status(intel_dp, IDLE_CYCLE_MASK, IDLE_CYCLE_VALUE);
+}
+
+
+/* Read the current pp_control value, unlocking the register if it
+ * is locked
+ */
+
+static u32 ironlake_get_pp_control(void)
+{
+ u32 control = io_i915_read32(PCH_PP_CONTROL);
+
+ control &= ~PANEL_UNLOCK_MASK;
+ control |= PANEL_UNLOCK_REGS;
+ return control;
+}
+
+void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp)
+{
+ u32 pp;
+
+ if (!is_edp(intel_dp))
+ return;
+ printk(BIOS_ERR, "Turn eDP VDD on\n");
+
+ if (intel_dp->want_panel_vdd) {
+ printk(BIOS_ERR, "eDP VDD already requested on\n");
+ }
+
+ intel_dp->want_panel_vdd = 1;
+
+ if (ironlake_edp_have_panel_vdd(intel_dp)) {
+ printk(BIOS_ERR, "eDP VDD already on\n");
+ return;
+ }
+
+ if (!ironlake_edp_have_panel_power(intel_dp))
+ ironlake_wait_panel_power_cycle(intel_dp);
+
+ pp = ironlake_get_pp_control();
+ pp |= EDP_FORCE_VDD;
+ io_i915_write32(pp, PCH_PP_CONTROL);
+ // remember this if we need it later. Not sure yet.
+ ////POSTING_READ(PCH_PP_CONTROL);
+ printk(BIOS_ERR, "PCH_PP_STATUS: 0x%08lx PCH_PP_CONTROL: 0x%08lx\n",
+ io_i915_read32(PCH_PP_STATUS), io_i915_read32(PCH_PP_CONTROL));
+
+ /*
+ * If the panel wasn't on, delay before accessing aux channel
+ */
+ if (!ironlake_edp_have_panel_power(intel_dp)) {
+ printk(BIOS_ERR, "eDP was not running\n");
+ mdelay(intel_dp->panel_power_up_delay);
+ }
+}
+
+static void ironlake_panel_vdd_off_sync(struct intel_dp *intel_dp)
+{
+ u32 pp;
+
+ if (!intel_dp->want_panel_vdd && ironlake_edp_have_panel_vdd(intel_dp)) {
+ pp = ironlake_get_pp_control();
+ pp &= ~EDP_FORCE_VDD;
+ io_i915_write32(pp, PCH_PP_CONTROL);
+ ////POSTING_READ(PCH_PP_CONTROL);
+
+ /* Make sure sequencer is idle before allowing subsequent activity */
+ printk(BIOS_ERR, "PCH_PP_STATUS: 0x%08lx PCH_PP_CONTROL: 0x%08lx\n",
+ io_i915_read32(PCH_PP_STATUS), io_i915_read32(PCH_PP_CONTROL));
+
+ mdelay(intel_dp->panel_power_down_delay);
+ }
+}
+
+void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, int sync)
+{
+ if (!is_edp(intel_dp))
+ return;
+
+ printk(BIOS_ERR, "Turn eDP VDD off %d\n", intel_dp->want_panel_vdd);
+ if (!intel_dp->want_panel_vdd) {
+ printk(BIOS_ERR, "eDP VDD not forced on");
+ }
+
+ intel_dp->want_panel_vdd = 0;
+
+ if (sync)
+ ironlake_panel_vdd_off_sync(intel_dp);
+}
+
+void ironlake_edp_panel_on(struct intel_dp *intel_dp)
+{
+ u32 pp;
+
+ if (!is_edp(intel_dp))
+ return;
+
+ printk(BIOS_ERR, "Turn eDP power on\n");
+
+ if (ironlake_edp_have_panel_power(intel_dp)) {
+ printk(BIOS_ERR, "eDP power already on\n");
+ return;
+ }
+
+ ironlake_wait_panel_power_cycle(intel_dp);
+
+ pp = ironlake_get_pp_control();
+ if ((intel_dp->gen == 5)) {
+ /* ILK workaround: disable reset around power sequence */
+ pp &= ~PANEL_POWER_RESET;
+ io_i915_write32(pp, PCH_PP_CONTROL);
+ ////POSTING_READ(PCH_PP_CONTROL);
+ }
+
+ pp |= POWER_TARGET_ON;
+ if (!(intel_dp->gen == 5))
+ pp |= PANEL_POWER_RESET;
+
+ io_i915_write32(pp, PCH_PP_CONTROL);
+ ////POSTING_READ(PCH_PP_CONTROL);
+
+ ironlake_wait_panel_on(intel_dp);
+
+ if ((intel_dp->gen == 5)) {
+ pp |= PANEL_POWER_RESET; /* restore panel reset bit */
+ io_i915_write32(pp, PCH_PP_CONTROL);
+ ////POSTING_READ(PCH_PP_CONTROL);
+ }
+}
+
+static void ironlake_edp_panel_off(struct intel_dp *intel_dp)
+{
+ u32 pp;
+
+ if (!is_edp(intel_dp))
+ return;
+
+ printk(BIOS_ERR, "Turn eDP power off\n");
+
+ if (intel_dp->want_panel_vdd) {
+ printk(BIOS_ERR, "Cannot turn power off while VDD is on\n");
+ }
+
+ pp = ironlake_get_pp_control();
+ pp &= ~(POWER_TARGET_ON | EDP_FORCE_VDD |
+ PANEL_POWER_RESET | EDP_BLC_ENABLE);
+ io_i915_write32(pp, PCH_PP_CONTROL);
+ ////POSTING_READ(PCH_PP_CONTROL);
+
+ ironlake_wait_panel_off(intel_dp);
+}
+
+void ironlake_edp_backlight_on(struct intel_dp *intel_dp)
+{
+ u32 pp;
+
+ if (!is_edp(intel_dp))
+ return;
+
+ /*
+ * If we enable the backlight right away following a panel power
+ * on, we may see slight flicker as the panel syncs with the eDP
+ * link. So delay a bit to make sure the image is solid before
+ * allowing it to appear.
+ */
+ mdelay(intel_dp->backlight_on_delay);
+ pp = ironlake_get_pp_control();
+ pp |= EDP_BLC_ENABLE;
+ io_i915_write32(pp, PCH_PP_CONTROL);
+ ////POSTING_READ(PCH_PP_CONTROL);
+}
+
+static void ironlake_edp_backlight_off(struct intel_dp *intel_dp)
+{
+ u32 pp;
+
+ if (!is_edp(intel_dp))
+ return;
+
+ pp = ironlake_get_pp_control();
+ pp &= ~EDP_BLC_ENABLE;
+ io_i915_write32(pp, PCH_PP_CONTROL);
+ ////POSTING_READ(PCH_PP_CONTROL);
+ mdelay(intel_dp->backlight_off_delay);
+}
+
+void ironlake_edp_pll_on(void)
+{
+ u32 dpa_ctl;
+
+ dpa_ctl = io_i915_read32(DP_A);
+ dpa_ctl |= DP_PLL_ENABLE;
+ io_i915_write32(dpa_ctl, DP_A);
+ ////POSTING_READ(DP_A);
+ udelay(200);
+}
+
+static void ironlake_edp_pll_off(void)
+{
+ u32 dpa_ctl;
+
+ dpa_ctl = io_i915_read32(DP_A);
+ dpa_ctl &= ~DP_PLL_ENABLE;
+ io_i915_write32(dpa_ctl, DP_A);
+ ////POSTING_READ(DP_A);
+ udelay(200);
+}
+
+/* If the sink supports it, try to set the power state appropriately */
+void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode)
+{
+ int ret, i;
+
+ /* Should have a valid DPCD by this point */
+ if (intel_dp->dpcd[DP_DPCD_REV] < 0x11)
+ return;
+
+ /* the convention, for whatever reason, is that mode > 0 means 'off' */
+ if (mode) {
+ ret = intel_dp_aux_native_write_1(intel_dp, DP_SET_POWER,
+ DP_SET_POWER_D3);
+ if (ret != 1) {
+ printk(BIOS_ERR, "failed to write sink power state\n");
+ }
+ } else {
+ /*
+ * When turning on, we need to retry for 1ms to give the sink
+ * time to wake up.
+ */
+ for (i = 0; i < 3; i++) {
+ ret = intel_dp_aux_native_write_1(intel_dp,
+ DP_SET_POWER,
+ DP_SET_POWER_D0);
+ if (ret == 1)
+ break;
+ mdelay(1);
+ }
+ }
+}
+// not sure if needed yet or not.
+void intel_dp_prepare(struct intel_dp *intel_dp);
+void intel_dp_prepare(struct intel_dp *intel_dp)
+{
+
+ ironlake_edp_backlight_off(intel_dp);
+ ironlake_edp_panel_off(intel_dp);
+
+ /* Wake up the sink first */
+ ironlake_edp_panel_vdd_on(intel_dp);
+ intel_dp_sink_dpms(intel_dp, 0);
+ intel_dp_link_down(intel_dp);
+ ironlake_edp_panel_vdd_off(intel_dp, 0);
+
+ /* Make sure the panel is off before trying to
+ * change the mode
+ */
+}
+
+// might be useful.
+void intel_dp_commit(struct intel_dp *intel_dp);
+void intel_dp_commit(struct intel_dp *intel_dp)
+{
+
+ ironlake_edp_panel_vdd_on(intel_dp);
+ intel_dp_sink_dpms(intel_dp, 0);
+ intel_dp_start_link_train(intel_dp);
+ ironlake_edp_panel_on(intel_dp);
+ ironlake_edp_panel_vdd_off(intel_dp, 1);
+ intel_dp_complete_link_train(intel_dp);
+ ironlake_edp_backlight_on(intel_dp);
+
+ intel_dp->dpms_mode = 0;
+}
+
+void
+intel_dp_dpms(struct intel_dp *intel_dp, int mode)
+{
+ uint32_t dp_reg = io_i915_read32(intel_dp->output_reg);
+
+ printk(BIOS_SPEW, "%s: power %s\n", __func__, mode ? "off" : "on");
+ if (mode){
+ ironlake_edp_backlight_off(intel_dp);
+ ironlake_edp_panel_off(intel_dp);
+
+ ironlake_edp_panel_vdd_on(intel_dp);
+ intel_dp_sink_dpms(intel_dp, mode);
+ intel_dp_link_down(intel_dp);
+ ironlake_edp_panel_vdd_off(intel_dp, 0);
+
+ if (is_cpu_edp(intel_dp))
+ ironlake_edp_pll_off();
+ } else {
+ if (is_cpu_edp(intel_dp))
+ ironlake_edp_pll_on();
+
+ ironlake_edp_panel_vdd_on(intel_dp);
+ intel_dp_sink_dpms(intel_dp, mode);
+ if (!(dp_reg & DP_PORT_EN)) {
+ intel_dp_start_link_train(intel_dp);
+ ironlake_edp_panel_on(intel_dp);
+ ironlake_edp_panel_vdd_off(intel_dp, 1);
+ intel_dp_complete_link_train(intel_dp);
+ } else
+ ironlake_edp_panel_vdd_off(intel_dp, 0);
+ ironlake_edp_backlight_on(intel_dp);
+ }
+ intel_dp->dpms_mode = mode;
+}
+
+/*
+ * Native read with retry for link status and receiver capability reads for
+ * cases where the sink may still be asleep.
+ */
+static int
+intel_dp_aux_native_read_retry(struct intel_dp *intel_dp, uint16_t address,
+ uint8_t *recv, int recv_bytes)
+{
+ int ret, i;
+
+ /*
+ * Sinks are *supposed* to come up within 1ms from an off state,
+ * but we're also supposed to retry 3 times per the spec.
+ */
+ for (i = 0; i < 3; i++) {
+ ret = intel_dp_aux_native_read(intel_dp, address, recv,
+ recv_bytes);
+ if (ret == recv_bytes)
+ return ret;
+ mdelay(1);
+ }
+
+ return ret;
+}
+
+/*
+ * Fetch AUX CH registers 0x202 - 0x207 which contain
+ * link status information
+ */
+static int
+intel_dp_get_link_status(struct intel_dp *intel_dp,
+ uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+ int ret, i;
+
+ ret = intel_dp_aux_native_read_retry(intel_dp,
+ DP_LANE0_1_STATUS,
+ link_status,
+ DP_LINK_STATUS_SIZE);
+
+ printk(BIOS_SPEW, "%s:", __func__);
+ for(i = 0; i < /* !!sizeof(link_status) == 4*/
+ DP_LINK_STATUS_SIZE; i++)
+ printk(BIOS_SPEW, " %02x", link_status[i]);
+ printk(BIOS_SPEW, "\n");
+ return ret;
+}
+
+static uint8_t
+intel_dp_link_status(uint8_t link_status[DP_LINK_STATUS_SIZE],
+ int r)
+{
+ return link_status[r - DP_LANE0_1_STATUS];
+}
+
+#if 0
+static uint8_t
+intel_get_adjust_request_voltage(uint8_t adjust_request[2],
+ int lane)
+{
+ int s = ((lane & 1) ?
+ DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
+ DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
+ uint8_t l = adjust_request[lane>>1];
+
+ return ((l >> s) & 3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
+}
+
+static uint8_t
+intel_get_adjust_request_pre_emphasis(uint8_t adjust_request[2],
+ int lane)
+{
+ int s = ((lane & 1) ?
+ DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
+ DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
+ uint8_t l = adjust_request[lane>>1];
+
+ return ((l >> s) & 3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+}
+#endif
+const char *voltage_names[] = {
+ "0.4V", "0.6V", "0.8V", "1.2V"
+};
+const char *pre_emph_names[] = {
+ "0dB", "3.5dB", "6dB", "9.5dB"
+};
+const char *link_train_names[] = {
+ "pattern 1", "pattern 2", "idle", "off"
+};
+
+/*
+ * These are source-specific values; current Intel hardware supports
+ * a maximum voltage of 800mV and a maximum pre-emphasis of 6dB
+ */
+
+static uint8_t
+intel_dp_voltage_max(struct intel_dp *intel_dp)
+{
+
+ if ((intel_dp->gen == 7) && is_cpu_edp(intel_dp))
+ return DP_TRAIN_VOLTAGE_SWING_800;
+ else if (intel_dp->has_pch_cpt && !is_cpu_edp(intel_dp))
+ return DP_TRAIN_VOLTAGE_SWING_1200;
+ else
+ return DP_TRAIN_VOLTAGE_SWING_800;
+}
+
+static uint8_t
+intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing)
+{
+
+ if (intel_dp->is_haswell){
+ switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
+ case DP_TRAIN_VOLTAGE_SWING_400:
+ return DP_TRAIN_PRE_EMPHASIS_9_5;
+ case DP_TRAIN_VOLTAGE_SWING_600:
+ return DP_TRAIN_PRE_EMPHASIS_6;
+ case DP_TRAIN_VOLTAGE_SWING_800:
+ return DP_TRAIN_PRE_EMPHASIS_3_5;
+ case DP_TRAIN_VOLTAGE_SWING_1200:
+ default:
+ return DP_TRAIN_PRE_EMPHASIS_0;
+ }
+ } else if ((intel_dp->gen == 7) && is_cpu_edp(intel_dp)) {
+ switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
+ case DP_TRAIN_VOLTAGE_SWING_400:
+ return DP_TRAIN_PRE_EMPHASIS_6;
+ case DP_TRAIN_VOLTAGE_SWING_600:
+ case DP_TRAIN_VOLTAGE_SWING_800:
+ return DP_TRAIN_PRE_EMPHASIS_3_5;
+ default:
+ return DP_TRAIN_PRE_EMPHASIS_0;
+ }
+ } else {
+ switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
+ case DP_TRAIN_VOLTAGE_SWING_400:
+ return DP_TRAIN_PRE_EMPHASIS_6;
+ case DP_TRAIN_VOLTAGE_SWING_600:
+ return DP_TRAIN_PRE_EMPHASIS_6;
+ case DP_TRAIN_VOLTAGE_SWING_800:
+ return DP_TRAIN_PRE_EMPHASIS_3_5;
+ case DP_TRAIN_VOLTAGE_SWING_1200:
+ default:
+ return DP_TRAIN_PRE_EMPHASIS_0;
+ }
+ }
+}
+
+static void
+intel_get_adjust_train(struct intel_dp *intel_dp,
+ uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+ uint8_t v = 0;
+ uint8_t p = 0;
+ int lane;
+ uint8_t voltage_max;
+ uint8_t preemph_max;
+
+ for (lane = 0; lane < intel_dp->lane_count; lane++) {
+ uint8_t this_v = drm_dp_get_adjust_request_voltage(
+ link_status, lane);
+ uint8_t this_p = drm_dp_get_adjust_request_pre_emphasis(
+ link_status, lane);
+
+ if (this_v > v)
+ v = this_v;
+ if (this_p > p)
+ p = this_p;
+ }
+
+ voltage_max = intel_dp_voltage_max(intel_dp);
+ if (v >= voltage_max)
+ v = voltage_max | DP_TRAIN_MAX_SWING_REACHED;
+
+ preemph_max = intel_dp_pre_emphasis_max(intel_dp, v);
+ if (p >= preemph_max)
+ p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+
+ printk(BIOS_SPEW, "%s: set to %s %s%s %s\n",
+ __func__,
+ voltage_names[v&3], pre_emph_names[p&3],
+ v & DP_TRAIN_MAX_SWING_REACHED ? ",max volt swing reached":"",
+ p & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED ?
+ ", max pre emph reached" : "");
+ for (lane = 0; lane < 4; lane++)
+ intel_dp->train_set[lane] = v | p;
+}
+
+static uint32_t
+intel_dp_signal_levels(uint8_t train_set)
+{
+ uint32_t signal_levels = 0;
+
+ switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
+ case DP_TRAIN_VOLTAGE_SWING_400:
+ default:
+ signal_levels |= DP_VOLTAGE_0_4;
+ break;
+ case DP_TRAIN_VOLTAGE_SWING_600:
+ signal_levels |= DP_VOLTAGE_0_6;
+ break;
+ case DP_TRAIN_VOLTAGE_SWING_800:
+ signal_levels |= DP_VOLTAGE_0_8;
+ break;
+ case DP_TRAIN_VOLTAGE_SWING_1200:
+ signal_levels |= DP_VOLTAGE_1_2;
+ break;
+ }
+ switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) {
+ case DP_TRAIN_PRE_EMPHASIS_0:
+ default:
+ signal_levels |= DP_PRE_EMPHASIS_0;
+ break;
+ case DP_TRAIN_PRE_EMPHASIS_3_5:
+ signal_levels |= DP_PRE_EMPHASIS_3_5;
+ break;
+ case DP_TRAIN_PRE_EMPHASIS_6:
+ signal_levels |= DP_PRE_EMPHASIS_6;
+ break;
+ case DP_TRAIN_PRE_EMPHASIS_9_5:
+ signal_levels |= DP_PRE_EMPHASIS_9_5;
+ break;
+ }
+ return signal_levels;
+}
+
+/* Gen6's DP voltage swing and pre-emphasis control */
+static uint32_t
+intel_gen6_edp_signal_levels(uint8_t train_set)
+{
+ int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK |
+ DP_TRAIN_PRE_EMPHASIS_MASK);
+ switch (signal_levels) {
+ case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0:
+ case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0:
+ return EDP_LINK_TRAIN_400_600MV_0DB_SNB_B;
+ case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5:
+ return EDP_LINK_TRAIN_400MV_3_5DB_SNB_B;
+ case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6:
+ case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_6:
+ return EDP_LINK_TRAIN_400_600MV_6DB_SNB_B;
+ case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5:
+ case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5:
+ return EDP_LINK_TRAIN_600_800MV_3_5DB_SNB_B;
+ case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0:
+ case DP_TRAIN_VOLTAGE_SWING_1200 | DP_TRAIN_PRE_EMPHASIS_0:
+ return EDP_LINK_TRAIN_800_1200MV_0DB_SNB_B;
+ default:
+ printk(BIOS_ERR, "[000000.0] [drm:%s], ", __func__);
+ printk(BIOS_ERR, "Unsupported voltage swing/pre-emphasis level:"
+ "0x%x\n", signal_levels);
+ return EDP_LINK_TRAIN_400_600MV_0DB_SNB_B;
+ }
+}
+
+/* Gen7's DP voltage swing and pre-emphasis control */
+static uint32_t
+intel_gen7_edp_signal_levels(uint8_t train_set)
+{
+ int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK |
+ DP_TRAIN_PRE_EMPHASIS_MASK);
+ switch (signal_levels) {
+ case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0:
+ return EDP_LINK_TRAIN_400MV_0DB_IVB;
+ case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5:
+ return EDP_LINK_TRAIN_400MV_3_5DB_IVB;
+ case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6:
+ return EDP_LINK_TRAIN_400MV_6DB_IVB;
+
+ case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0:
+ return EDP_LINK_TRAIN_600MV_0DB_IVB;
+ case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5:
+ return EDP_LINK_TRAIN_600MV_3_5DB_IVB;
+
+ case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0:
+ return EDP_LINK_TRAIN_800MV_0DB_IVB;
+ case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5:
+ return EDP_LINK_TRAIN_800MV_3_5DB_IVB;
+
+ default:
+ printk(BIOS_ERR, "[000000.0] [drm:%s], ", __func__);
+ printk(BIOS_ERR, "Unsupported voltage swing/pre-emphasis level:"
+ "0x%x\n", signal_levels);
+ return EDP_LINK_TRAIN_500MV_0DB_IVB;
+ }
+}
+
+/* Gen7.5's (HSW) DP voltage swing and pre-emphasis control */
+static uint32_t
+intel_dp_signal_levels_hsw(uint8_t train_set)
+{
+ int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK |
+ DP_TRAIN_PRE_EMPHASIS_MASK);
+ switch (signal_levels) {
+ case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0:
+ return DDI_BUF_EMP_400MV_0DB_HSW;
+ case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5:
+ return DDI_BUF_EMP_400MV_3_5DB_HSW;
+ case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6:
+ return DDI_BUF_EMP_400MV_6DB_HSW;
+ case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_9_5:
+ return DDI_BUF_EMP_400MV_9_5DB_HSW;
+
+ case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0:
+ return DDI_BUF_EMP_600MV_0DB_HSW;
+ case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5:
+ return DDI_BUF_EMP_600MV_3_5DB_HSW;
+ case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_6:
+ return DDI_BUF_EMP_600MV_6DB_HSW;
+
+ case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0:
+ return DDI_BUF_EMP_800MV_0DB_HSW;
+ case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5:
+ return DDI_BUF_EMP_800MV_3_5DB_HSW;
+ default:
+ printk(BIOS_SPEW,
+ "Unsupported voltage swing/pre-emphasis level:"
+ "0x%x\n", signal_levels);
+ return DDI_BUF_EMP_400MV_0DB_HSW;
+ }
+}
+
+
+static uint8_t
+intel_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE],
+ int lane)
+{
+ int s = (lane & 1) * 4;
+ uint8_t l = link_status[lane>>1];
+
+ return (l >> s) & 0xf;
+}
+
+/* Check for clock recovery is done on all channels */
+static int
+intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE],
+ int lane_count)
+{
+ int lane;
+ uint8_t lane_status;
+
+ for (lane = 0; lane < lane_count; lane++) {
+ lane_status = intel_get_lane_status(link_status, lane);
+ printk(BIOS_SPEW,
+ "%s: Lane %d, status %02x\n", __func__,
+ lane, lane_status);
+ if ((lane_status & DP_LANE_CR_DONE) == 0)
+ return 0;
+ }
+ return 1;
+}
+
+/* Check to see if channel eq is done on all channels */
+#define CHANNEL_EQ_BITS (DP_LANE_CR_DONE| \
+ DP_LANE_CHANNEL_EQ_DONE| \
+ DP_LANE_SYMBOL_LOCKED)
+static int
+intel_channel_eq_ok(struct intel_dp *intel_dp,
+ uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+ uint8_t lane_align;
+ uint8_t lane_status;
+ int lane;
+
+ lane_align = intel_dp_link_status(link_status,
+ DP_LANE_ALIGN_STATUS_UPDATED);
+ if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0)
+ return 0;
+ for (lane = 0; lane < intel_dp->lane_count; lane++) {
+ lane_status = intel_get_lane_status(link_status, lane);
+ if ((lane_status & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS)
+ return 0;
+ }
+ return 1;
+}
+
+static int
+intel_dp_set_link_train(struct intel_dp *intel_dp,
+ uint32_t dp_reg_value,
+ uint8_t dp_train_pat)
+{
+ int ret;
+ u32 temp;
+ int port = intel_dp->port;
+ int i;
+
+ printk(BIOS_SPEW, "%s: dp_reg_value %08lx dp_train_pat %02x\n",
+ __func__, (unsigned long) dp_reg_value, dp_train_pat);
+ if (intel_dp->is_haswell){
+ temp = io_i915_read32(DP_TP_CTL(port));
+
+ if (dp_train_pat & DP_LINK_SCRAMBLING_DISABLE)
+ temp |= DP_TP_CTL_SCRAMBLE_DISABLE;
+ else
+ temp &= ~DP_TP_CTL_SCRAMBLE_DISABLE;
+
+ temp &= ~DP_TP_CTL_LINK_TRAIN_MASK;
+ switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
+ case DP_TRAINING_PATTERN_DISABLE:
+ temp |= DP_TP_CTL_LINK_TRAIN_IDLE;
+ io_i915_write32( temp, DP_TP_CTL(port));
+
+ for(i = 0; i < 10; i++){
+ u32 status;
+ status = io_i915_read32(DP_TP_STATUS(port));
+ if (status & DP_TP_STATUS_IDLE_DONE)
+ break;
+ }
+
+ if (i == 10)
+ printk(BIOS_ERR,
+ "Timed out waiting for DP idle patterns\n");
+
+ temp &= ~DP_TP_CTL_LINK_TRAIN_MASK;
+ temp |= DP_TP_CTL_LINK_TRAIN_NORMAL;
+
+ break;
+ case DP_TRAINING_PATTERN_1:
+ temp |= DP_TP_CTL_LINK_TRAIN_PAT1;
+ break;
+ case DP_TRAINING_PATTERN_2:
+ temp |= DP_TP_CTL_LINK_TRAIN_PAT2;
+ break;
+ case DP_TRAINING_PATTERN_3:
+ temp |= DP_TP_CTL_LINK_TRAIN_PAT3;
+ break;
+ }
+ io_i915_write32( temp, DP_TP_CTL(port));
+
+ } else if (intel_dp->has_pch_cpt &&
+ (intel_dp->gen != 7 || !is_cpu_edp(intel_dp))) {
+ dp_reg_value &= ~DP_LINK_TRAIN_MASK_CPT;
+
+ switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
+ case DP_TRAINING_PATTERN_DISABLE:
+ dp_reg_value |= DP_LINK_TRAIN_OFF_CPT;
+ break;
+ case DP_TRAINING_PATTERN_1:
+ dp_reg_value |= DP_LINK_TRAIN_PAT_1_CPT;
+ break;
+ case DP_TRAINING_PATTERN_2:
+ dp_reg_value |= DP_LINK_TRAIN_PAT_2_CPT;
+ break;
+ case DP_TRAINING_PATTERN_3:
+ printk(BIOS_ERR,
+ "DP training pattern 3 not supported\n");
+ dp_reg_value |= DP_LINK_TRAIN_PAT_2_CPT;
+ break;
+ }
+
+ } else {
+ dp_reg_value &= ~DP_LINK_TRAIN_MASK;
+
+ switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
+ case DP_TRAINING_PATTERN_DISABLE:
+ dp_reg_value |= DP_LINK_TRAIN_OFF;
+ break;
+ case DP_TRAINING_PATTERN_1:
+ dp_reg_value |= DP_LINK_TRAIN_PAT_1;
+ break;
+ case DP_TRAINING_PATTERN_2:
+ dp_reg_value |= DP_LINK_TRAIN_PAT_2;
+ break;
+ case DP_TRAINING_PATTERN_3:
+ printk(BIOS_ERR,"DP training pattern 3 not supported\n");
+ dp_reg_value |= DP_LINK_TRAIN_PAT_2;
+ break;
+ }
+ }
+
+ io_i915_write32( dp_reg_value, intel_dp->output_reg);
+ //POSTING_READ(intel_dp->output_reg);
+
+ intel_dp_aux_native_write_1(intel_dp,
+ DP_TRAINING_PATTERN_SET,
+ dp_train_pat);
+
+ if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) !=
+ DP_TRAINING_PATTERN_DISABLE) {
+ ret = intel_dp_aux_native_write(intel_dp,
+ DP_TRAINING_LANE0_SET,
+ intel_dp->train_set,
+ intel_dp->lane_count);
+ if (ret != intel_dp->lane_count){
+ printk(BIOS_ERR, "%s: wanted %d, got %d\n", __func__,
+ intel_dp->lane_count, ret);
+ return 0;
+ }
+ }
+
+ printk(BIOS_SPEW, "%s: success\n", __func__);
+ return 1;
+}
+
+/* Enable corresponding port and start training pattern 1 */
+void
+intel_dp_start_link_train(struct intel_dp *intel_dp)
+{
+ int i;
+ uint8_t voltage;
+ int clock_recovery = 0;
+ int voltage_tries, loop_tries;
+ u32 reg;
+ uint32_t DP = intel_dp->DP;
+
+ if (intel_dp->is_haswell)
+ intel_ddi_prepare_link_retrain(intel_dp, intel_dp->port);
+
+ /* Write the link configuration data */
+ printk(BIOS_SPEW, "Write the link configuration data\n");
+ intel_dp_aux_native_write(intel_dp, DP_LINK_BW_SET,
+ intel_dp->link_configuration,
+ DP_LINK_CONFIGURATION_SIZE);
+ printk(BIOS_SPEW, "Written\n");
+
+ DP |= DP_PORT_EN;
+
+ memset(intel_dp->train_set, 0, 4);
+ voltage = 0xff;
+ voltage_tries = 0;
+ loop_tries = 0;
+ clock_recovery = 0;
+
+ for (;;) {
+ /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */
+ uint8_t link_status[DP_LINK_STATUS_SIZE];
+ uint32_t signal_levels;
+
+ if (intel_dp->is_haswell){
+ signal_levels =
+ intel_dp_signal_levels_hsw(intel_dp->train_set[0]);
+ DP = (DP & ~DDI_BUF_EMP_MASK) | signal_levels;
+ printk(BIOS_SPEW, "Haswell: levels %08x DP %08x\n", signal_levels, DP);
+ } else if ((intel_dp->gen == 7) && is_cpu_edp(intel_dp)) {
+ signal_levels = intel_gen7_edp_signal_levels(intel_dp->train_set[0]);
+ DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_IVB) | signal_levels;
+ } else if ((intel_dp->gen == 6) && is_cpu_edp(intel_dp)) {
+ signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]);
+ DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels;
+ } else {
+ signal_levels = intel_dp_signal_levels(intel_dp->train_set[0]);
+ DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels;
+ }
+ printk(BIOS_ERR, "training pattern 1 signal levels %08x\n", signal_levels);
+
+ if (intel_dp->has_pch_cpt && ((intel_dp->gen == 7) || !is_cpu_edp(intel_dp)))
+ reg = DP | DP_LINK_TRAIN_PAT_1_CPT;
+ else
+ reg = DP | DP_LINK_TRAIN_PAT_1;
+
+ if (!intel_dp_set_link_train(intel_dp, reg,
+ DP_TRAINING_PATTERN_1 |
+ DP_LINK_SCRAMBLING_DISABLE))
+ break;
+ /* Set training pattern 1 */
+
+ udelay(100);
+ if (!intel_dp_get_link_status(intel_dp, link_status)) {
+ printk(BIOS_ERR, "failed to get link status\n");
+ break;
+ }
+
+ if (intel_clock_recovery_ok(link_status, intel_dp->lane_count)) {
+ printk(BIOS_ERR, "clock recovery OK\n");
+ clock_recovery = 1;
+ break;
+ }
+
+ /* Check to see if we've tried the max voltage */
+ for (i = 0; i < intel_dp->lane_count; i++)
+ if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
+ break;
+ if (i == intel_dp->lane_count) {
+ ++loop_tries;
+ if (loop_tries == 5) {
+ printk(BIOS_ERR, "too many full retries, give up\n");
+ break;
+ }
+ printk(BIOS_SPEW, "%s: reset train set\n", __func__);
+ memset(intel_dp->train_set, 0, 4);
+ voltage_tries = 0;
+ continue;
+ }
+
+ /* Check to see if we've tried the same voltage 5 times */
+ if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
+ ++voltage_tries;
+ if (voltage_tries == 5) {
+ printk(BIOS_ERR, "too many voltage retries, give up\n");
+ break;
+ }
+ } else
+ voltage_tries = 0;
+ voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
+ printk(BIOS_SPEW, "%s: voltage now %s\n", __func__,
+ voltage_names[voltage]);
+
+ /* Compute new intel_dp->train_set as requested by target */
+ intel_get_adjust_train(intel_dp, link_status);
+ printk(BIOS_SPEW, "%s: new intel train set is "
+ "%02x%02x%02x%02x",
+ __func__,
+ intel_dp->train_set[0], intel_dp->train_set[1],
+ intel_dp->train_set[2], intel_dp->train_set[3]);
+ }
+
+ intel_dp->DP = DP;
+}
+
+void
+intel_dp_complete_link_train(struct intel_dp *intel_dp)
+{
+ int channel_eq = 0;
+ int tries, cr_tries;
+ uint32_t DP = intel_dp->DP;
+
+ /* channel equalization */
+ tries = 0;
+ cr_tries = 0;
+ channel_eq = 0;
+ for (;;) {
+ /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */
+ uint32_t signal_levels;
+ uint8_t link_status[DP_LINK_STATUS_SIZE];
+
+ if (cr_tries > 5) {
+ printk(BIOS_ERR, "failed to train DP, aborting\n");
+ intel_dp_link_down(intel_dp);
+ break;
+ }
+
+ if (intel_dp->is_haswell) {
+ signal_levels = intel_dp_signal_levels_hsw(intel_dp->train_set[0]);
+ DP = (DP & ~DDI_BUF_EMP_MASK) | signal_levels;
+ printk(BIOS_SPEW, "%s: new DP %08x\n", __func__,
+ DP);
+ } else if ((intel_dp->gen == 7) && is_cpu_edp(intel_dp)) {
+ signal_levels = intel_gen7_edp_signal_levels(intel_dp->train_set[0]);
+ DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_IVB) | signal_levels;
+ } else if ((intel_dp->gen == 6) && is_cpu_edp(intel_dp)) {
+ signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]);
+ DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels;
+ } else {
+ signal_levels = intel_dp_signal_levels(intel_dp->train_set[0]);
+ DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels;
+ }
+
+ /* channel eq pattern */
+ if (!intel_dp_set_link_train(intel_dp, DP,
+ DP_TRAINING_PATTERN_2 |
+ DP_LINK_SCRAMBLING_DISABLE))
+ break;
+
+ udelay(40000); /* was 400 */
+ if (!intel_dp_get_link_status(intel_dp, link_status))
+ break;
+
+ /* Make sure clock is still ok */
+ if (!intel_clock_recovery_ok(link_status, intel_dp->lane_count)) {
+ printk(BIOS_SPEW, "%s: Clock recovery is NOT ok"
+ ": restarting from the start\n", __func__);
+ intel_dp_start_link_train(intel_dp);
+ cr_tries++;
+ continue;
+ }
+
+ if (intel_channel_eq_ok(intel_dp, link_status)) {
+ printk(BIOS_SPEW, "%s: success\n", __func__);
+ channel_eq = 1;
+ break;
+ }
+
+ /* Try 5 times, then try clock recovery if that fails */
+ if (tries > 5) {
+ printk(BIOS_SPEW, "%s: tries > 5,recovering. \n",
+ __func__);
+ intel_dp_link_down(intel_dp);
+ intel_dp_start_link_train(intel_dp);
+ tries = 0;
+ cr_tries++;
+ continue;
+ }
+
+ /* Compute new intel_dp->train_set as requested by target */
+ printk(BIOS_SPEW, "%s: adjust the train\n", __func__);
+ intel_get_adjust_train(intel_dp, link_status);
+ ++tries;
+ }
+
+ //io_i915_write32(reg, intel_dp->output_reg);
+ ////POSTING_READ(intel_dp->output_reg);
+ intel_dp_set_link_train(intel_dp, DP, DP_TRAINING_PATTERN_DISABLE);
+}
+
+void
+intel_dp_link_down(struct intel_dp *intel_dp)
+{
+ uint32_t DP = intel_dp->DP;
+
+ if ((io_i915_read32(intel_dp->output_reg) & DP_PORT_EN) == 0)
+ return;
+
+ if (intel_dp->is_haswell){
+ printk(BIOS_SPEW, "%s: It's a haswell, skip this\n", __func__);
+ return;
+ }
+ if (is_edp(intel_dp)) {
+ DP &= ~DP_PLL_ENABLE;
+ io_i915_write32(DP, intel_dp->output_reg);
+ ////POSTING_READ(intel_dp->output_reg);
+ udelay(100);
+ }
+
+ if (intel_dp->has_pch_cpt && ((intel_dp->gen == 7) || !is_cpu_edp(intel_dp))) {
+ DP &= ~DP_LINK_TRAIN_MASK_CPT;
+ io_i915_write32(DP | DP_LINK_TRAIN_PAT_IDLE_CPT, intel_dp->output_reg);
+ } else {
+ DP &= ~DP_LINK_TRAIN_MASK;
+ io_i915_write32(DP | DP_LINK_TRAIN_PAT_IDLE, intel_dp->output_reg);
+ }
+ ////POSTING_READ(intel_dp->output_reg);
+
+ mdelay(17);
+
+ if (is_edp(intel_dp)) {
+ if (intel_dp->has_pch_cpt && ((intel_dp->gen == 7) || !is_cpu_edp(intel_dp)))
+ DP |= DP_LINK_TRAIN_OFF_CPT;
+ else
+ DP |= DP_LINK_TRAIN_OFF;
+ }
+
+ DP &= ~DP_AUDIO_OUTPUT_ENABLE;
+ io_i915_write32(DP & ~DP_PORT_EN, intel_dp->output_reg);
+ ////POSTING_READ(intel_dp->output_reg);
+ mdelay(600000); //intel_dp->panel_power_down_delay);
+}
+
+int
+intel_dp_get_dpcd(struct intel_dp *intel_dp)
+{
+ int got = 0, want = sizeof(intel_dp->dpcd), rev;
+ got = intel_dp_aux_native_read_retry(intel_dp, 0x000, intel_dp->dpcd,
+ want);
+ if (got < want) {
+ printk(BIOS_SPEW, "%s: got %d, wanted %d\n", __func__, got, want);
+ return 0;
+ }
+
+ rev = intel_dp->dpcd[DP_DPCD_REV];
+ if (!rev){
+ printk(BIOS_SPEW, "%s: intel->dp[DP_DPCD_REV] is 0\n",
+ __func__);
+ return 0;
+ }
+
+ printk(BIOS_SPEW, "DPCD: %02hx%02hx%02hx%02hx%02hx%02hx%02hx%02hx\n",
+ intel_dp->dpcd[0], intel_dp->dpcd[1], intel_dp->dpcd[2],
+ intel_dp->dpcd[3], intel_dp->dpcd[4], intel_dp->dpcd[5],
+ intel_dp->dpcd[6], intel_dp->dpcd[7]);
+ return 1;
+}
+
+/* I have no idea how big max downspread is. 1 byte? Hard as hell to find. */
+int
+intel_dp_get_max_downspread(struct intel_dp *intel_dp, u8 *max_downspread)
+{
+ int got, want = 1;
+ got = intel_dp_aux_native_read_retry(intel_dp, 0x000, max_downspread,
+ want);
+ if (got < want) {
+ printk(BIOS_SPEW, "%s: got %d, wanted %d\n", __func__, got, want);
+ return 0;
+ }
+
+ printk(BIOS_SPEW, "%s: max_downspread is %02x\n", __func__, *max_downspread);
+ return 1;
+}
+