[Intel-gfx] [PATCH 12/17] drm/i915: Import DisplayPort code
Keith Packard
keithp at keithp.com
Sun May 31 05:42:36 CEST 2009
---
drivers/gpu/drm/i915/Makefile | 1 +
drivers/gpu/drm/i915/i915_drv.h | 1 +
drivers/gpu/drm/i915/intel_display.c | 11 +-
drivers/gpu/drm/i915/intel_dp.c | 967 +++++++++++++++++++++-------------
drivers/gpu/drm/i915/intel_dp.h | 11 +
drivers/gpu/drm/i915/intel_dp_i2c.c | 244 +++++++++
drivers/gpu/drm/i915/intel_drv.h | 1 +
7 files changed, 874 insertions(+), 362 deletions(-)
create mode 100644 drivers/gpu/drm/i915/intel_dp_i2c.c
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 184b8bf..30d6b99 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -14,6 +14,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \
intel_lvds.o \
intel_bios.o \
intel_dp.o \
+ intel_dp_i2c.o \
intel_hdmi.o \
intel_sdvo.o \
intel_modes.o \
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index df43e88..0771cae 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -832,6 +832,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
#define HAS_128_BYTE_Y_TILING(dev) (IS_I9XX(dev) && !(IS_I915G(dev) || \
IS_I915GM(dev)))
#define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev))
+#define SUPPORTS_INTEGRATED_DP(dev) (IS_G4X(dev))
#define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_I965G(dev))
#define PRIMARY_RINGBUFFER_SIZE (128*1024)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 1fc7bb0..2b042ea 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -1276,11 +1276,6 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
I915_READ(LVDS);
}
- /* The Display Port M/N ratio needs to be set before the DPLL is enabled
- */
- if (is_dp)
- intel_dp_set_m_n(crtc, mode, adjusted_mode);
-
I915_WRITE(fp_reg, fp);
I915_WRITE(dpll_reg, dpll);
I915_READ(dpll_reg);
@@ -1907,6 +1902,8 @@ static void intel_setup_outputs(struct drm_device *dev)
found = intel_sdvo_init(dev, SDVOB);
if (!found && SUPPORTS_INTEGRATED_HDMI(dev))
intel_hdmi_init(dev, SDVOB);
+ if (!found && SUPPORTS_INTEGRATED_DP(dev))
+ intel_dp_init(dev, DP_B);
}
/* Before G4X SDVOC doesn't have its own detect register */
@@ -1919,7 +1916,11 @@ static void intel_setup_outputs(struct drm_device *dev)
found = intel_sdvo_init(dev, SDVOC);
if (!found && SUPPORTS_INTEGRATED_HDMI(dev))
intel_hdmi_init(dev, SDVOC);
+ if (!found && SUPPORTS_INTEGRATED_DP(dev))
+ intel_dp_init(dev, DP_C);
}
+ if (SUPPORTS_INTEGRATED_DP(dev) && (I915_READ(DP_D) & DP_DETECTED))
+ intel_dp_init(dev, DP_C);
} else
intel_dvo_init(dev);
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 5d07f14..50a75a1 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -26,16 +26,14 @@
*/
#include <linux/i2c.h>
-#include <linux/delay.h>
#include "drmP.h"
#include "drm.h"
#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
#include "intel_drv.h"
#include "i915_drm.h"
#include "i915_drv.h"
-#include "intel_dp_regs.h"
-
-#undef SDVO_DEBUG
+#include "intel_dp.h"
#define DP_LINK_STATUS_SIZE 6
#define DP_LINK_CHECK_TIMEOUT (10 * 1000)
@@ -43,49 +41,31 @@
#define DP_LINK_CONFIGURATION_SIZE 9
struct intel_dp_priv {
- /* I2C over AUX */
- uint16_t i2c_address;
- Bool i2c_running;
-
- /* Output config register */
uint32_t output_reg;
-
- /* Desired output config register state */
uint32_t DP;
- /* Desire link config state */
uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE];
-
- /* Saved output config state */
uint32_t save_DP;
uint8_t save_link_configuration[DP_LINK_CONFIGURATION_SIZE];
-
- /* whether the sink supports audio (currently unset) */
- Bool has_audio;
-
- /* available link bandwidth */
+ bool has_audio;
uint8_t link_bw;
-
- /* available lane count */
uint8_t lane_count;
-
- /* Device configuration information */
uint8_t dpcd[4];
+ struct intel_output *intel_output;
+ struct i2c_adapter adapter;
+ struct i2c_algo_dp_aux_data algo;
};
static void
-intel_dp_link_train(xf86OutputPtr output, uint32_t DP,
+intel_dp_link_train(struct intel_output *intel_output, uint32_t DP,
uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE]);
static void
-intel_dp_link_down(xf86OutputPtr output, uint32_t DP);
+intel_dp_link_down(struct intel_output *intel_output, uint32_t DP);
static int
-intel_dp_max_lane_count(xf86OutputPtr output)
+intel_dp_max_lane_count(struct intel_output *intel_output)
{
- struct drm_device *dev = encoder->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_output *intel_output = enc_to_intel_output(encoder);
- struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+ struct intel_dp_priv *dp_priv = intel_output->dev_priv;
int max_lane_count = 4;
if (dp_priv->dpcd[0] >= 0x11) {
@@ -101,12 +81,9 @@ intel_dp_max_lane_count(xf86OutputPtr output)
}
static int
-intel_dp_max_link_bw(xf86OutputPtr output)
+intel_dp_max_link_bw(struct intel_output *intel_output)
{
- struct drm_device *dev = encoder->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_output *intel_output = enc_to_intel_output(encoder);
- struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+ struct intel_dp_priv *dp_priv = intel_output->dev_priv;
int max_link_bw = dp_priv->dpcd[1];
switch (max_link_bw) {
@@ -129,20 +106,32 @@ intel_dp_link_clock(uint8_t link_bw)
return 162000;
}
-/* Return DP link clock rate.
- *
- * This driver always encodes 3 bytes per pixel (RGB)
- */
+/* I think this is a fiction */
static int
intel_dp_link_required(int pixel_clock)
{
return pixel_clock * 3;
}
-/* AUX channel communication in native and I2C modes */
+static int
+intel_dp_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_output));
+ int max_lanes = intel_dp_max_lane_count(intel_output);
+
+ if (intel_dp_link_required(mode->clock) > max_link_clock * max_lanes)
+ return MODE_CLOCK_HIGH;
+
+ if (mode->clock < 10000)
+ return MODE_CLOCK_LOW;
+
+ return MODE_OK;
+}
static uint32_t
-intel_pack_aux(uint8_t *src, int src_bytes)
+pack_aux(uint8_t *src, int src_bytes)
{
int i;
uint32_t v = 0;
@@ -155,7 +144,7 @@ intel_pack_aux(uint8_t *src, int src_bytes)
}
static void
-intel_unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes)
+unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes)
{
int i;
if (dst_bytes > 4)
@@ -165,24 +154,24 @@ intel_unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes)
}
static int
-intel_dp_aux_ch(struct drm_encoder *encoder, uint32_t output_reg,
+intel_dp_aux_ch(struct intel_output *intel_output,
uint8_t *send, int send_bytes,
uint8_t *recv, int recv_size)
{
- struct drm_device *dev = encoder->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_output *intel_output = enc_to_intel_output(encoder);
struct intel_dp_priv *dp_priv = intel_output->dev_priv;
- uint32_t ch_ctl = output_reg + 0x10;
- uint32_t ch_data = ch_ctl + 4;
- int i;
- int recv_bytes;
- uint32_t ctl;
- uint32_t status;
+ uint32_t output_reg = dp_priv->output_reg;
+ struct drm_device *dev = intel_output->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ uint32_t ch_ctl = output_reg + 0x10;
+ uint32_t ch_data = ch_ctl + 4;
+ int i;
+ int recv_bytes;
+ uint32_t ctl;
+ uint32_t status;
/* Load the send data into the aux channel data registers */
for (i = 0; i < send_bytes; i += 4) {
- uint32_t d = intel_pack_aux(send + i, send_bytes - i);;
+ uint32_t d = pack_aux(send + i, send_bytes - i);;
I915_WRITE(ch_data + i, d);
}
@@ -210,19 +199,19 @@ intel_dp_aux_ch(struct drm_encoder *encoder, uint32_t output_reg,
}
/* Clear done status and any errors */
- I915_READ(ch_ctl, (ctl |
- DP_AUX_CH_CTL_DONE |
- DP_AUX_CH_CTL_TIME_OUT_ERROR |
- DP_AUX_CH_CTL_RECEIVE_ERROR));
+ I915_WRITE(ch_ctl, (ctl |
+ DP_AUX_CH_CTL_DONE |
+ DP_AUX_CH_CTL_TIME_OUT_ERROR |
+ DP_AUX_CH_CTL_RECEIVE_ERROR));
if ((status & DP_AUX_CH_CTL_DONE) == 0)
- return -EIO;
+ return -1;
/* Check for timeout or receive error.
* Timeouts occur when the sink is not connected
*/
if (status & (DP_AUX_CH_CTL_TIME_OUT_ERROR | DP_AUX_CH_CTL_RECEIVE_ERROR))
- return -EIO;
+ return -1;
/* Unload any bytes sent back from the other side */
recv_bytes = ((status & DP_AUX_CH_CTL_MESSAGE_SIZE_MASK) >>
@@ -231,9 +220,9 @@ intel_dp_aux_ch(struct drm_encoder *encoder, uint32_t output_reg,
if (recv_bytes > recv_size)
recv_bytes = recv_size;
for (i = 0; i < recv_bytes; i += 4) {
- uint32_t d = INREG(ch_data + i);
+ uint32_t d = I915_READ(ch_data + i);
- intel_unpack_aux(d, recv + i, recv_bytes - i);
+ unpack_aux(d, recv + i, recv_bytes - i);
}
return recv_bytes;
@@ -241,7 +230,7 @@ intel_dp_aux_ch(struct drm_encoder *encoder, uint32_t output_reg,
/* Write data to the aux channel in native mode */
static int
-intel_dp_aux_native_write(struct drm_encoder *encoder, uint32_t output_reg,
+intel_dp_aux_native_write(struct intel_output *intel_output,
uint16_t address, uint8_t *send, int send_bytes)
{
int ret;
@@ -249,7 +238,8 @@ intel_dp_aux_native_write(struct drm_encoder *encoder, uint32_t output_reg,
int msg_bytes;
uint8_t ack;
- assert(send_bytes <= 16);
+ if (send_bytes > 16)
+ return -1;
msg[0] = AUX_NATIVE_WRITE << 4;
msg[1] = address >> 8;
msg[2] = address;
@@ -257,7 +247,7 @@ intel_dp_aux_native_write(struct drm_encoder *encoder, uint32_t output_reg,
memcpy(&msg[4], send, send_bytes);
msg_bytes = send_bytes + 4;
for (;;) {
- ret = intel_dp_aux_ch(encoder, output_reg, msg, msg_bytes, &ack, 1);
+ ret = intel_dp_aux_ch(intel_output, msg, msg_bytes, &ack, 1);
if (ret < 0)
return ret;
if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK)
@@ -265,23 +255,23 @@ intel_dp_aux_native_write(struct drm_encoder *encoder, uint32_t output_reg,
else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER)
udelay(100);
else
- return -EIO;
+ 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 drm_encoder *encoder, uint32_t output_reg,
+intel_dp_aux_native_write_1(struct intel_output *intel_output,
uint16_t address, uint8_t byte)
{
- return intel_dp_aux_native_write(encoder, output_reg, address, &byte, 1);
+ return intel_dp_aux_native_write(intel_output, address, &byte, 1);
}
/* read bytes from a native aux channel */
static int
-i830_dp_aux_native_read(struct drm_encoder *encoder, uint32_t output_reg,
- uint16_t address, uint8_t *recv, int recv_bytes)
+intel_dp_aux_native_read(struct intel_output *intel_output,
+ uint16_t address, uint8_t *recv, int recv_bytes)
{
uint8_t msg[4];
int msg_bytes;
@@ -299,7 +289,7 @@ i830_dp_aux_native_read(struct drm_encoder *encoder, uint32_t output_reg,
reply_bytes = recv_bytes + 1;
for (;;) {
- ret = intel_dp_aux_ch(encoder, output_reg, msg, msg_bytes,
+ ret = intel_dp_aux_ch(intel_output, msg, msg_bytes,
reply, reply_bytes);
if (ret <= 0)
return ret;
@@ -309,236 +299,168 @@ i830_dp_aux_native_read(struct drm_encoder *encoder, uint32_t output_reg,
return ret - 1;
}
else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER)
- usleep(100);
+ udelay(100);
else
- return -EIO;
+ return -1;
}
}
-/* Run a single AUX_CH I2C transaction, writing/reading data as necessary */
-
-enum dp_aux_i2c_mode {
- aux_i2c_start,
- aux_i2c_write,
- aux_i2c_read,
- aux_i2c_stop
-};
-
static int
-intel_dp_aux_i2c_transaction(struct drm_encoder *encoder, uint32_t output_reg,
- uint16_t address,
- enum dp_aux_i2c_mode mode,
- uint8_t write_byte, uint8_t *read_byte)
+intel_dp_i2c_aux_ch(struct i2c_adapter *adapter,
+ uint8_t *send, int send_bytes,
+ uint8_t *recv, int recv_bytes)
{
- uint8_t msg[5];
- uint8_t reply[2];
- int msg_bytes;
- int reply_bytes;
- int ret;
-
- /* Set up the command byte */
- if (address & 1)
- msg[0] = AUX_I2C_READ << 4;
- else
- msg[0] = AUX_I2C_WRITE << 4;
-
- if (mode != aux_i2c_stop)
- msg[0] |= AUX_I2C_MOT << 4;
-
- /* Note that the AUX_CH I2C stuff wants the read/write
- * bit stripped off
- */
- msg[1] = address >> 9;
- msg[2] = address >> 1;
-
- switch (mode) {
- case aux_i2c_start:
- case aux_i2c_stop:
- default:
- msg_bytes = 3;
- reply_bytes = 1;
- break;
- case aux_i2c_write:
- msg[3] = 0;
- msg[4] = write_byte;
- msg_bytes = 5;
- reply_bytes = 1;
- break;
- case aux_i2c_read:
- msg[3] = 0;
- msg_bytes = 4;
- reply_bytes = 2;
- break;
- }
-
- for (;;) {
- ret = intel_dp_aux_ch(encoder, output_reg, msg, msg_bytes,
- reply, reply_bytes);
- if (ret <= 0)
- return ret;
+ struct intel_dp_priv *dp_priv = container_of(adapter,
+ struct intel_dp_priv,
+ adapter);
+ struct intel_output *intel_output = dp_priv->intel_output;
- if ((reply[0] & AUX_I2C_REPLY_MASK) == AUX_I2C_REPLY_ACK) {
- if (mode == aux_i2c_read)
- *read_byte = reply[1];
- return reply_bytes - 1;
- }
- else if ((reply[0] & AUX_I2C_REPLY_MASK) == AUX_I2C_REPLY_DEFER)
- usleep(100);
- else
- return -EIO;
- }
+ return intel_dp_aux_ch(intel_output,
+ send, send_bytes, recv, recv_bytes);
}
-/*
- * I2C over AUX CH
- */
-
-/*
- * Send the address. If the I2C link is running, this 'restarts'
- * the connection with the new address, this is used for doing
- * a write followed by a read (as needed for DDC)
- */
-static Bool
-i830_dp_i2c_address(I2CDevPtr dev, I2CSlaveAddr addr)
+static int
+intel_dp_i2c_init(struct intel_output *intel_output, const char *name)
{
- I2CBusPtr bus = dev->pI2CBus;
- xf86OutputPtr output = bus->DriverPrivate.ptr;
- I830OutputPrivatePtr intel_output = output->driver_private;
- struct i830_dp_priv *dev_priv = intel_output->dev_priv;
- ScrnInfoPtr scrn = output->scrn;
-
- dev_priv->i2c_address = addr;
- dev_priv->i2c_running = TRUE;
- return i830_dp_aux_i2c_transaction(scrn, dev_priv->output_reg, addr,
- aux_i2c_start, 0, NULL) >= 0;
+ struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+
+ dp_priv->algo.running = false;
+ dp_priv->algo.address = 0;
+ dp_priv->algo.aux_ch = intel_dp_i2c_aux_ch;
+
+ memset(&dp_priv->adapter, '\0', sizeof (dp_priv->adapter));
+ dp_priv->adapter.owner = THIS_MODULE;
+ dp_priv->adapter.class = I2C_CLASS_DDC;
+ strncpy (dp_priv->adapter.name, name, sizeof dp_priv->adapter.name - 1);
+ dp_priv->adapter.name[sizeof dp_priv->adapter.name - 1] = '\0';
+ dp_priv->adapter.algo_data = &dp_priv->algo;
+ dp_priv->adapter.dev.parent = &intel_output->base.kdev;
+
+ return i2c_dp_aux_add_bus(&dp_priv->adapter);
}
-/* DIX never even calls this function, so it better not be necessary */
-static Bool
-i830_dp_i2c_start(I2CBusPtr bus, int timeout)
+static bool
+intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
{
- return TRUE;
-}
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+ int lane_count, clock;
+ int max_lane_count = intel_dp_max_lane_count(intel_output);
+ int max_clock = intel_dp_max_link_bw(intel_output) == DP_LINK_BW_2_7 ? 1 : 0;
+ static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 };
-/*
- * Stop the I2C transaction. This closes out the link, sending
- * a bare address packet with the MOT bit turned off
- */
-static void
-i830_dp_i2c_stop(I2CDevPtr dev)
-{
- I2CBusPtr bus = dev->pI2CBus;
- xf86OutputPtr output = bus->DriverPrivate.ptr;
- ScrnInfoPtr scrn = output->scrn;
- I830OutputPrivatePtr intel_output = output->driver_private;
- struct i830_dp_priv *dev_priv = intel_output->dev_priv;
+ for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) {
+ for (clock = 0; clock <= max_clock; clock++) {
+ int link_avail = intel_dp_link_clock(bws[clock]) * lane_count;
- if (dev_priv->i2c_running)
- (void) i830_dp_aux_i2c_transaction(scrn, dev_priv->output_reg,
- dev_priv->i2c_address,
- aux_i2c_stop, 0, NULL);
- dev_priv->i2c_running = FALSE;
+ if (intel_dp_link_required(mode->clock) <= link_avail) {
+ dp_priv->link_bw = bws[clock];
+ dp_priv->lane_count = lane_count;
+ adjusted_mode->clock = intel_dp_link_clock(dp_priv->link_bw);
+ return true;
+ }
+ }
+ }
+ return false;
}
-/*
- * Write a single byte to the current I2C address, this assumes
- * that the I2C link is running (or presumably it won't work).
- */
-static Bool
-i830_dp_i2c_put_byte(I2CDevPtr dev, I2CByte byte)
-{
- I2CBusPtr bus = dev->pI2CBus;
- xf86OutputPtr output = bus->DriverPrivate.ptr;
- ScrnInfoPtr scrn = output->scrn;
- I830OutputPrivatePtr intel_output = output->driver_private;
- struct i830_dp_priv *dev_priv = intel_output->dev_priv;
+struct intel_dp_m_n {
+ uint32_t tu;
+ uint32_t gmch_m;
+ uint32_t gmch_n;
+ uint32_t link_m;
+ uint32_t link_n;
+};
- return i830_dp_aux_i2c_transaction(scrn, dev_priv->output_reg,
- dev_priv->i2c_address,
- aux_i2c_write, byte, NULL) >= 0;
+static void
+intel_reduce_ratio(uint32_t *num, uint32_t *den)
+{
+ while (*num > 0xffffff || *den > 0xffffff) {
+ *num >>= 1;
+ *den >>= 1;
+ }
}
-static Bool
-i830_dp_i2c_get_byte(I2CDevPtr dev, I2CByte *byte_ret, Bool last)
+static void
+intel_dp_compute_m_n(int bytes_per_pixel,
+ int nlanes,
+ int pixel_clock,
+ int link_clock,
+ struct intel_dp_m_n *m_n)
{
- I2CBusPtr bus = dev->pI2CBus;
- xf86OutputPtr output = bus->DriverPrivate.ptr;
- I830OutputPrivatePtr intel_output = output->driver_private;
- struct i830_dp_priv *dev_priv = intel_output->dev_priv;
- ScrnInfoPtr scrn = output->scrn;
-
- return i830_dp_aux_i2c_transaction(scrn, dev_priv->output_reg,
- dev_priv->i2c_address,
- aux_i2c_read, 0, byte_ret) == 1;
+ m_n->tu = 64;
+ m_n->gmch_m = pixel_clock * bytes_per_pixel;
+ 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);
}
-static Bool
-i830_dp_i2c_init(ScrnInfoPtr pScrn, I2CBusPtr *bus_ptr,
- xf86OutputPtr output, char *name)
+void
+intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
{
- I2CBusPtr pI2CBus;
-
- pI2CBus = xf86CreateI2CBusRec();
-
- if (!pI2CBus)
- return FALSE;
-
- pI2CBus->BusName = name;
- pI2CBus->scrnIndex = pScrn->scrnIndex;
- pI2CBus->I2CGetByte = i830_dp_i2c_get_byte;
- pI2CBus->I2CPutByte = i830_dp_i2c_put_byte;
- pI2CBus->I2CAddress = i830_dp_i2c_address;
- pI2CBus->I2CStart = i830_dp_i2c_start;
- pI2CBus->I2CStop = i830_dp_i2c_stop;
- pI2CBus->DriverPrivate.ptr = output;
-
- /* Assume all busses are used for DDCish stuff */
+ struct drm_device *dev = crtc->dev;
+ struct drm_mode_config *mode_config = &dev->mode_config;
+ struct drm_connector *connector;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int lane_count = 4;
+ struct intel_dp_m_n m_n;
/*
- * These were set incorrectly in the server pre-1.3, Having
- * duplicate settings is sub-optimal, but this lets the driver
- * work with older servers
+ * Find the lane count in the intel_output private
*/
- pI2CBus->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */
- pI2CBus->StartTimeout = 550;
- pI2CBus->BitTimeout = 40;
- pI2CBus->AcknTimeout = 40;
- pI2CBus->RiseFallTime = 20;
+ list_for_each_entry(connector, &mode_config->connector_list, head) {
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dp_priv *dp_priv = intel_output->dev_priv;
- if (!xf86I2CBusInit(pI2CBus))
- return FALSE;
+ if (!connector->encoder || connector->encoder->crtc != crtc)
+ continue;
- *bus_ptr = pI2CBus;
- return TRUE;
-}
-
-static int
-intel_dp_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
-{
- int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(output));
- int max_lanes = intel_dp_max_lane_count(output);
-
- if (intel_dp_link_required(mode->Clock) > max_link_clock * max_lanes)
- return MODE_CLOCK_HIGH;
-
- if (mode->Clock < 10000)
- return MODE_CLOCK_LOW;
+ if (intel_output->type == INTEL_OUTPUT_DISPLAYPORT) {
+ lane_count = dp_priv->lane_count;
+ break;
+ }
+ }
- return MODE_OK;
+ /*
+ * 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(3, lane_count,
+ mode->clock, adjusted_mode->clock, &m_n);
+
+ if (intel_crtc->pipe == 0) {
+ I915_WRITE(PIPEA_GMCH_DATA_M,
+ ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
+ m_n.gmch_m);
+ I915_WRITE(PIPEA_GMCH_DATA_N,
+ m_n.gmch_n);
+ I915_WRITE(PIPEA_DP_LINK_M, m_n.link_m);
+ I915_WRITE(PIPEA_DP_LINK_N, m_n.link_n);
+ } else {
+ I915_WRITE(PIPEB_GMCH_DATA_M,
+ ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
+ m_n.gmch_m);
+ I915_WRITE(PIPEB_GMCH_DATA_N,
+ m_n.gmch_n);
+ I915_WRITE(PIPEB_DP_LINK_M, m_n.link_m);
+ I915_WRITE(PIPEB_DP_LINK_N, m_n.link_n);
+ }
}
-static void intel_dp_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
+static void
+intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
{
- struct drm_device *dev = encoder->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_output *intel_output = enc_to_intel_output(encoder);
struct intel_dp_priv *dp_priv = intel_output->dev_priv;
- I830OutputPrivatePtr intel_output = output->driver_private;
- struct i830_dp_priv *dev_priv = intel_output->dev_priv;
- xf86CrtcPtr crtc = output->crtc;
- I830CrtcPrivatePtr intel_crtc = crtc->driver_private;
+ struct drm_crtc *crtc = intel_output->enc.crtc;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
dp_priv->DP = (DP_LINK_TRAIN_OFF |
DP_VOLTAGE_0_4 |
@@ -577,118 +499,464 @@ static void intel_dp_mode_set(struct drm_encoder *encoder,
dp_priv->DP |= DP_PIPEB_SELECT;
}
-static void intel_dp_dpms(struct drm_encoder *encoder, int mode)
+
+static void
+intel_dp_dpms(struct drm_encoder *encoder, int mode)
{
- struct drm_device *dev = encoder->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_output *intel_output = enc_to_intel_output(encoder);
struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+ struct drm_device *dev = intel_output->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t dp_reg = I915_READ(dp_priv->output_reg);
- if (mode == DPMSModeOff) {
+ if (mode != DRM_MODE_DPMS_ON) {
if (dp_reg & DP_PORT_EN)
- i830_dp_link_down(output, dp_priv->DP);
+ intel_dp_link_down(intel_output, dp_priv->DP);
} else {
if (!(dp_reg & DP_PORT_EN))
- i830_dp_link_train(output, dp_priv->DP, dp_priv->link_configuration);
+ intel_dp_link_train(intel_output, dp_priv->DP, dp_priv->link_configuration);
}
}
-static void intel_dp_save(struct drm_connector *connector)
+/*
+ * Fetch AUX CH registers 0x202 - 0x207 which contain
+ * link status information
+ */
+static bool
+intel_dp_get_link_status(struct intel_output *intel_output,
+ uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+ int ret;
+
+ ret = intel_dp_aux_native_read(intel_output,
+ DP_LANE0_1_STATUS,
+ link_status, DP_LINK_STATUS_SIZE);
+ if (ret != DP_LINK_STATUS_SIZE)
+ return false;
+ return true;
+}
+
+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];
+}
+
+static void
+intel_dp_save(struct drm_connector *connector)
{
- struct drm_device *dev = connector->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
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;
struct intel_dp_priv *dp_priv = intel_output->dev_priv;
dp_priv->save_DP = I915_READ(dp_priv->output_reg);
+ intel_dp_aux_native_read(intel_output, 0x100,
+ dp_priv->save_link_configuration, sizeof (dp_priv->save_link_configuration));
+}
- /* Save the link configuration */
- intel_dp_aux_native_read(pScrn, dp_priv->output_reg, DP_LINK_BW_SET,
- dp_priv->save_link_configuration,
- sizeof (dp_priv->save_link_configuration));
-
+static uint8_t
+intel_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE],
+ int lane)
+{
+ int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
+ int s = ((lane & 1) ?
+ DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
+ DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
+ uint8_t l = intel_dp_link_status(link_status, i);
+
+ return ((l >> s) & 3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
+}
+
+static uint8_t
+intel_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_SIZE],
+ int lane)
+{
+ int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
+ int s = ((lane & 1) ?
+ DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
+ DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
+ uint8_t l = intel_dp_link_status(link_status, i);
+
+ return ((l >> s) & 3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+}
+
+
+#if 0
+static char *voltage_names[] = {
+ "0.4V", "0.6V", "0.8V", "1.2V"
+};
+static char *pre_emph_names[] = {
+ "0dB", "3.5dB", "6dB", "9.5dB"
+};
+static char *link_train_names[] = {
+ "pattern 1", "pattern 2", "idle", "off"
+};
+#endif
+
+/*
+ * These are source-specific values; current Intel hardware supports
+ * a maximum voltage of 800mV and a maximum pre-emphasis of 6dB
+ */
+#define I830_DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_800
+
+static uint8_t
+intel_dp_pre_emphasis_max(uint8_t voltage_swing)
+{
+ 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_output *intel_output,
+ uint8_t link_status[DP_LINK_STATUS_SIZE],
+ int lane_count,
+ uint8_t train_set[4])
+{
+ uint8_t v = 0;
+ uint8_t p = 0;
+ int lane;
+
+ for (lane = 0; lane < lane_count; lane++) {
+ uint8_t this_v = intel_get_adjust_request_voltage(link_status, lane);
+ uint8_t this_p = intel_get_adjust_request_pre_emphasis(link_status, lane);
+
+ if (this_v > v)
+ v = this_v;
+ if (this_p > p)
+ p = this_p;
+ }
+
+ if (v >= I830_DP_VOLTAGE_MAX)
+ v = I830_DP_VOLTAGE_MAX | DP_TRAIN_MAX_SWING_REACHED;
+
+ if (p >= intel_dp_pre_emphasis_max(v))
+ p = intel_dp_pre_emphasis_max(v) | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+
+ for (lane = 0; lane < 4; lane++)
+ train_set[lane] = v | p;
+}
+
+static uint32_t
+intel_dp_signal_levels(uint8_t train_set, int lane_count)
+{
+ 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;
}
-static void intel_dp_restore(struct drm_connector *connector)
+static uint8_t
+intel_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE],
+ int lane)
{
- struct drm_device *dev = connector->dev;
+ int i = DP_LANE0_1_STATUS + (lane >> 1);
+ int s = (lane & 1) * 4;
+ uint8_t l = intel_dp_link_status(link_status, i);
+
+ return (l >> s) & 0xf;
+}
+
+/* Check for clock recovery is done on all channels */
+static bool
+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);
+ if ((lane_status & DP_LANE_CR_DONE) == 0)
+ return false;
+ }
+ return true;
+}
+
+/* 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 bool
+intel_channel_eq_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count)
+{
+ 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 false;
+ for (lane = 0; lane < lane_count; lane++) {
+ lane_status = intel_get_lane_status(link_status, lane);
+ if ((lane_status & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS)
+ return false;
+ }
+ return true;
+}
+
+static bool
+intel_dp_set_link_train(struct intel_output *intel_output,
+ uint32_t dp_reg_value,
+ uint8_t dp_train_pat,
+ uint8_t train_set[4],
+ bool first)
+{
+ struct drm_device *dev = intel_output->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_output *intel_output = to_intel_output(connector);
struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+ int ret;
+
+ I915_WRITE(dp_priv->output_reg, dp_reg_value);
+ POSTING_READ(dp_priv->output_reg);
+ if (first)
+ intel_wait_for_vblank(dev);
+
+ intel_dp_aux_native_write_1(intel_output,
+ DP_TRAINING_PATTERN_SET,
+ dp_train_pat);
- I915_WRITE(dp_priv->output_reg, dp_priv->save_DP);
+ ret = intel_dp_aux_native_write(intel_output,
+ DP_TRAINING_LANE0_SET, train_set, 4);
+ if (ret != 4)
+ return false;
+
+ return true;
}
-static int intel_dp_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
+static void
+intel_dp_link_train(struct intel_output *intel_output, uint32_t DP,
+ uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE])
{
- int max_link_clock = i830_dp_link_clock(i830_dp_max_link_bw(output));
- int max_lanes = i830_dp_max_lane_count(output);
+ struct drm_device *dev = intel_output->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+ uint8_t train_set[4];
+ uint8_t link_status[DP_LINK_STATUS_SIZE];
+ int i;
+ uint8_t voltage;
+ bool clock_recovery = false;
+ bool channel_eq = false;
+ bool first = true;
+ int tries;
+
+ /* Write the link configuration data */
+ intel_dp_aux_native_write(intel_output, 0x100,
+ link_configuration, DP_LINK_CONFIGURATION_SIZE);
+
+ DP |= DP_PORT_EN;
+ DP &= ~DP_LINK_TRAIN_MASK;
+ memset(train_set, 0, 4);
+ voltage = 0xff;
+ tries = 0;
+ clock_recovery = false;
+ for (;;) {
+ /* Use train_set[0] to set the voltage and pre emphasis values */
+ uint32_t signal_levels = intel_dp_signal_levels(train_set[0], dp_priv->lane_count);
+ DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels;
- if (intel_dp_link_required(mode->Clock) > max_link_clock * max_lanes)
- return MODE_CLOCK_HIGH;
+ if (!intel_dp_set_link_train(intel_output, DP | DP_LINK_TRAIN_PAT_1,
+ DP_TRAINING_PATTERN_1, train_set, first))
+ break;
+ first = false;
+ /* Set training pattern 1 */
- if (mode->Clock < 10000)
- return MODE_CLOCK_LOW;
+ udelay(100);
+ if (!intel_dp_get_link_status(intel_output, link_status))
+ break;
- return MODE_OK;
+ if (intel_clock_recovery_ok(link_status, dp_priv->lane_count)) {
+ clock_recovery = true;
+ break;
+ }
+
+ /* Check to see if we've tried the max voltage */
+ for (i = 0; i < dp_priv->lane_count; i++)
+ if ((train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
+ break;
+ if (i == dp_priv->lane_count)
+ break;
+
+ /* Check to see if we've tried the same voltage 5 times */
+ if ((train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
+ ++tries;
+ if (tries == 5)
+ break;
+ } else
+ tries = 0;
+ voltage = train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
+
+ /* Compute new train_set as requested by target */
+ intel_get_adjust_train(intel_output, link_status, dp_priv->lane_count, train_set);
+ }
+
+ /* channel equalization */
+ tries = 0;
+ channel_eq = false;
+ for (;;) {
+ /* Use train_set[0] to set the voltage and pre emphasis values */
+ uint32_t signal_levels = intel_dp_signal_levels(train_set[0], dp_priv->lane_count);
+ DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels;
+
+ /* channel eq pattern */
+ if (!intel_dp_set_link_train(intel_output, DP | DP_LINK_TRAIN_PAT_2,
+ DP_TRAINING_PATTERN_2, train_set,
+ false))
+ break;
+
+ udelay(400);
+ if (!intel_dp_get_link_status(intel_output, link_status))
+ break;
+
+ if (intel_channel_eq_ok(link_status, dp_priv->lane_count)) {
+ channel_eq = true;
+ break;
+ }
+
+ /* Try 5 times */
+ if (tries > 5)
+ break;
+
+ /* Compute new train_set as requested by target */
+ intel_get_adjust_train(intel_output, link_status, dp_priv->lane_count, train_set);
+ ++tries;
+ }
+
+ I915_WRITE(dp_priv->output_reg, DP | DP_LINK_TRAIN_OFF);
+ POSTING_READ(dp_priv->output_reg);
+ intel_dp_aux_native_write_1(intel_output,
+ DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE);
}
-static bool intel_dp_mode_fixup(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
+static void
+intel_dp_link_down(struct intel_output *intel_output, uint32_t DP)
{
- struct drm_device *dev = connector->dev;
+ struct drm_device *dev = intel_output->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+
+ I915_WRITE(dp_priv->output_reg, DP & ~DP_PORT_EN);
+ POSTING_READ(dp_priv->output_reg);
+}
+
+static void
+intel_dp_restore(struct drm_connector *connector)
+{
struct intel_output *intel_output = to_intel_output(connector);
struct intel_dp_priv *dp_priv = intel_output->dev_priv;
- int lane_count, clock;
- int max_lane_count = i830_dp_max_lane_count(output);
- int max_clock = i830_dp_max_link_bw(output) == DP_LINK_BW_2_7 ? 1 : 0;;
- static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 };
- for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1)
- {
- for (clock = 0; clock <= max_clock; clock++)
- {
- int link_avail = intel_dp_link_clock(bws[clock]) * lane_count;
+ if (dp_priv->save_DP & DP_PORT_EN)
+ intel_dp_link_train(intel_output, dp_priv->save_DP, dp_priv->save_link_configuration);
+ else
+ intel_dp_link_down(intel_output, dp_priv->save_DP);
+}
- if (intel_dp_link_required(mode->Clock) <= link_avail)
- {
- dev_priv->link_bw = bws[clock];
- dev_priv->lane_count = lane_count;
- adjusted_mode->Clock = i830_dp_link_clock(dev_priv->link_bw);
- return TRUE;
- }
- }
+#if 0
+/*
+ * According to DP spec
+ * 5.1.2:
+ * 1. Read DPCD
+ * 2. Configure link according to Receiver Capabilities
+ * 3. Use Link Training from 2.5.3.3 and 3.5.1.3
+ * 4. Check link status on receipt of hot-plug interrupt
+ */
+
+static void
+intel_dp_check_link_status(struct intel_output *intel_output)
+{
+ struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+ uint8_t link_status[DP_LINK_STATUS_SIZE];
+
+ if (!intel_output->enc.crtc)
+ return;
+
+ if (!intel_dp_get_link_status(intel_output, link_status)) {
+ intel_dp_link_down(intel_output, dp_priv->DP);
+ return;
}
- return FALSE;
+
+ if (!intel_channel_eq_ok(link_status, dp_priv->lane_count))
+ intel_dp_link_train(intel_output, dp_priv->DP, dp_priv->link_configuration);
}
+#endif
+/**
+ * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect DP connection.
+ *
+ * \return true if DP port is connected.
+ * \return false if DP port is disconnected.
+ */
static enum drm_connector_status
intel_dp_detect(struct drm_connector *connector)
{
- struct drm_device *dev = connector->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
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;
struct intel_dp_priv *dp_priv = intel_output->dev_priv;
- u32 temp, bit;
+ uint32_t temp, bit;
enum drm_connector_status status;
- dp_priv->has_audio = FALSE;
+ dp_priv->has_audio = false;
+
+ /* 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
+ * generated on the port when a cable is not attached.
+ */
+ if (IS_G4X(dev) && !IS_GM45(dev)) {
+ temp = I915_READ(PEG_BAND_GAP_DATA);
+ I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd);
+ }
temp = I915_READ(PORT_HOTPLUG_EN);
I915_WRITE(PORT_HOTPLUG_EN,
- temp |
- DPB_HOTPLUG_INT_EN |
- DPC_HOTPLUG_INT_EN |
- DPD_HOTPLUG_INT_EN);
+ temp |
+ DPB_HOTPLUG_INT_EN |
+ DPC_HOTPLUG_INT_EN |
+ DPD_HOTPLUG_INT_EN);
POSTING_READ(PORT_HOTPLUG_EN);
- switch (dev_priv->output_reg) {
+ switch (dp_priv->output_reg) {
case DP_B:
bit = DPB_HOTPLUG_INT_STATUS;
break;
@@ -702,11 +970,13 @@ intel_dp_detect(struct drm_connector *connector)
return connector_status_unknown;
}
- if ((I915_READ(PORT_HOTPLUG_STAT) & bit) == 0)
+ temp = I915_READ(PORT_HOTPLUG_STAT);
+
+ if ((temp & bit) == 0)
return connector_status_disconnected;
-
+
status = connector_status_disconnected;
- if (intel_dp_aux_native_read(pScrn, dp_priv->output_reg,
+ if (intel_dp_aux_native_read(intel_output,
0, dp_priv->dpcd,
sizeof (dp_priv->dpcd)) == sizeof (dp_priv->dpcd))
{
@@ -720,14 +990,14 @@ static int intel_dp_get_modes(struct drm_connector *connector)
{
struct intel_output *intel_output = to_intel_output(connector);
- /* We should parse the EDID data and find out if it's an HDMI sink so
- * we can send audio to it.
+ /* We should parse the EDID data and find out if it has an audio sink
*/
return intel_ddc_get_modes(intel_output);
}
-static void intel_dp_destroy(struct drm_connector *connector)
+static void
+intel_dp_destroy (struct drm_connector *connector)
{
struct intel_output *intel_output = to_intel_output(connector);
@@ -769,48 +1039,39 @@ static const struct drm_encoder_funcs intel_dp_enc_funcs = {
.destroy = intel_dp_enc_destroy,
};
-struct intel_i2c_chan *intel_dp_i2c_create(struct drm_device *dev, const char *name)
-{
-}
-
-void intel_dp_init(struct drm_device *dev, int output_reg)
+void
+intel_dp_init(struct drm_device *dev, int output_reg)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_connector *connector;
struct intel_output *intel_output;
struct intel_dp_priv *dp_priv;
- intel_output = kcalloc(sizeof(struct intel_output) +
+ intel_output = kcalloc(sizeof(struct intel_output) +
sizeof(struct intel_dp_priv), 1, GFP_KERNEL);
if (!intel_output)
return;
+
dp_priv = (struct intel_dp_priv *)(intel_output + 1);
connector = &intel_output->base;
drm_connector_init(dev, connector, &intel_dp_connector_funcs,
- DRM_MODE_CONNECTOR_DVID);
+ DRM_MODE_CONNECTOR_DisplayPort);
drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs);
intel_output->type = INTEL_OUTPUT_DISPLAYPORT;
- connector->interlace_allowed = 0;
+ connector->interlace_allowed = true;
connector->doublescan_allowed = 0;
/* Set up the DDC bus. */
- switch (output_reg) {
- case DP_B:
- default:
- intel_output->ddc_bus = intel_i2c_create(dev, "DP_B");
- case DP_C:
- intel_output->ddc_bus = intel_i2c_create(dev, "DP_C");
- case DP_D:
- intel_output->ddc_bus = intel_i2c_create(dev, "DP_D");
- }
-
- if (!intel_output->ddc_bus)
- goto err_connector;
+ intel_dp_i2c_init(intel_output,
+ (output_reg == DP_B) ? "DPDDC-B" :
+ (output_reg == DP_C) ? "DPDDC-C" : "DPDDC_D");
+ dp_priv->intel_output = intel_output;
dp_priv->output_reg = output_reg;
+ dp_priv->has_audio = false;
intel_output->dev_priv = dp_priv;
drm_encoder_init(dev, &intel_output->enc, &intel_dp_enc_funcs,
@@ -829,12 +1090,4 @@ void intel_dp_init(struct drm_device *dev, int output_reg)
u32 temp = I915_READ(PEG_BAND_GAP_DATA);
I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd);
}
-
- return;
-
-err_connector:
- drm_connector_cleanup(connector);
- kfree(intel_output);
-
- return;
}
diff --git a/drivers/gpu/drm/i915/intel_dp.h b/drivers/gpu/drm/i915/intel_dp.h
index cfce2c5..89310b9 100644
--- a/drivers/gpu/drm/i915/intel_dp.h
+++ b/drivers/gpu/drm/i915/intel_dp.h
@@ -130,4 +130,15 @@
#define DP_ADJUST_PRE_EMPHASIS_LANE1_MASK 0xc0
#define DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT 6
+struct i2c_algo_dp_aux_data {
+ bool running;
+ u16 address;
+ int (*aux_ch) (struct i2c_adapter *adapter,
+ uint8_t *send, int send_bytes,
+ uint8_t *recv, int recv_bytes);
+};
+
+int
+i2c_dp_aux_add_bus(struct i2c_adapter *adapter);
+
#endif /* _INTEL_DP_H_ */
diff --git a/drivers/gpu/drm/i915/intel_dp_i2c.c b/drivers/gpu/drm/i915/intel_dp_i2c.c
new file mode 100644
index 0000000..4e4611c
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_dp_i2c.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright © 2009 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/i2c.h>
+#include "intel_dp.h"
+
+/* Run a single AUX_CH I2C transaction, writing/reading data as necessary */
+
+enum dp_aux_i2c_mode {
+ aux_i2c_start,
+ aux_i2c_write,
+ aux_i2c_read,
+ aux_i2c_stop
+};
+
+static int
+i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter,
+ enum dp_aux_i2c_mode mode,
+ uint8_t write_byte, uint8_t *read_byte)
+{
+ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+ uint16_t address = algo_data->address;
+ uint8_t msg[5];
+ uint8_t reply[2];
+ int msg_bytes;
+ int reply_bytes;
+ int ret;
+
+ /* Set up the command byte */
+ if (address & 1)
+ msg[0] = AUX_I2C_READ << 4;
+ else
+ msg[0] = AUX_I2C_WRITE << 4;
+
+ if (mode != aux_i2c_stop)
+ msg[0] |= AUX_I2C_MOT << 4;
+
+ /* Note that the AUX_CH I2C stuff wants the read/write
+ * bit stripped off
+ */
+ msg[1] = address >> 9;
+ msg[2] = address >> 1;
+
+ switch (mode) {
+ case aux_i2c_start:
+ case aux_i2c_stop:
+ default:
+ msg_bytes = 3;
+ reply_bytes = 1;
+ break;
+ case aux_i2c_write:
+ msg[3] = 0;
+ msg[4] = write_byte;
+ msg_bytes = 5;
+ reply_bytes = 1;
+ break;
+ case aux_i2c_read:
+ msg[3] = 0;
+ msg_bytes = 4;
+ reply_bytes = 2;
+ break;
+ }
+
+ for (;;) {
+ ret = (*algo_data->aux_ch)(adapter,
+ msg, msg_bytes,
+ reply, reply_bytes);
+ if (ret <= 0)
+ return -1;
+ if ((reply[0] & AUX_I2C_REPLY_MASK) == AUX_I2C_REPLY_ACK) {
+ if (mode == aux_i2c_read)
+ *read_byte = reply[1];
+ return reply_bytes - 1;
+ }
+ else if ((reply[0] & AUX_I2C_REPLY_MASK) == AUX_I2C_REPLY_DEFER)
+ udelay(100);
+ else
+ return -1;
+ }
+}
+
+/*
+ * I2C over AUX CH
+ */
+
+/*
+ * Send the address. If the I2C link is running, this 'restarts'
+ * the connection with the new address, this is used for doing
+ * a write followed by a read (as needed for DDC)
+ */
+static int
+i2c_algo_dp_aux_address(struct i2c_adapter *adapter, u16 address)
+{
+ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+
+ algo_data->address = address;
+ algo_data->running = true;
+ return i2c_algo_dp_aux_transaction(adapter, aux_i2c_start,
+ 0, NULL);
+}
+
+/*
+ * Stop the I2C transaction. This closes out the link, sending
+ * a bare address packet with the MOT bit turned off
+ */
+static void
+i2c_algo_dp_aux_stop(struct i2c_adapter *adapter)
+{
+ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+
+ if (algo_data->running) {
+ (void) i2c_algo_dp_aux_transaction(adapter, aux_i2c_stop,
+ 0, NULL);
+ algo_data->running = false;
+ }
+}
+
+/*
+ * Write a single byte to the current I2C address, the
+ * the I2C link must be running or this returns -EIO
+ */
+static int
+i2c_algo_dp_aux_put_byte(struct i2c_adapter *adapter, u8 byte)
+{
+ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+
+ if (!algo_data->running)
+ return -EIO;
+
+ return i2c_algo_dp_aux_transaction(adapter, aux_i2c_write,
+ byte, NULL);
+}
+
+/*
+ * Read a single byte from the current I2C address, the
+ * I2C link must be running or this returns -EIO
+ */
+static int
+i2c_algo_dp_aux_get_byte(struct i2c_adapter *adapter, u8 *byte_ret)
+{
+ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+
+ if (!algo_data->running)
+ return -EIO;
+
+ return i2c_algo_dp_aux_transaction(adapter, aux_i2c_read,
+ 0, byte_ret);
+}
+
+static int
+i2c_algo_dp_aux_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs,
+ int num)
+{
+ int orig_num = num;
+ int ret = 0;
+ while (num--) {
+ u16 len = msgs->len;
+ u8 *buf = msgs->buf;
+ ret = i2c_algo_dp_aux_address(adapter, msgs->addr);
+ if (ret < 0)
+ break;
+ if (msgs->flags & I2C_M_RD) {
+ while (len--) {
+ ret = i2c_algo_dp_aux_get_byte(adapter, buf++);
+ if (ret < 0)
+ break;
+ }
+ } else {
+ while (len--) {
+ ret = i2c_algo_dp_aux_put_byte(adapter, *buf++);
+ if (ret < 0)
+ break;
+ }
+ }
+ if (ret < 0)
+ break;
+ msgs++;
+ }
+ if (ret == 0)
+ ret = orig_num;
+ i2c_algo_dp_aux_stop(adapter);
+ return ret;
+}
+
+static u32
+i2c_algo_dp_aux_functionality(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+ I2C_FUNC_SMBUS_READ_BLOCK_DATA |
+ I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
+ I2C_FUNC_10BIT_ADDR;
+}
+
+static const struct i2c_algorithm i2c_dp_aux_algo = {
+ .master_xfer = i2c_algo_dp_aux_xfer,
+ .functionality = i2c_algo_dp_aux_functionality,
+};
+
+static int
+i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter)
+{
+ adapter->algo = &i2c_dp_aux_algo;
+ adapter->retries = 3;
+ return 0;
+}
+
+int
+i2c_dp_aux_add_bus(struct i2c_adapter *adapter)
+{
+ int error;
+
+ error = i2c_dp_aux_prepare_bus(adapter);
+ if (error)
+ return error;
+ return i2c_add_adapter(adapter);
+}
+EXPORT_SYMBOL(i2c_dp_aux_add_bus);
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 0937112..9b0882c 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -117,6 +117,7 @@ extern bool intel_sdvo_init(struct drm_device *dev, int output_device);
extern void intel_dvo_init(struct drm_device *dev);
extern void intel_tv_init(struct drm_device *dev);
extern void intel_lvds_init(struct drm_device *dev);
+extern void intel_dp_init(struct drm_device *dev, int dp_reg);
extern void intel_crtc_load_lut(struct drm_crtc *crtc);
extern void intel_encoder_prepare (struct drm_encoder *encoder);
--
1.6.3.1
More information about the Intel-gfx
mailing list