From: Zhao Yakui yakui.zhao@intel.com
Currently when trying to call the DPMS off again for one CRTC with DPMS off, it will firstly disable the SR and can't enable it again because of the incorrect check/logic. In such case the self refresh is still disabled although one CRTC pipe is active. This is wrong.
Signed-off-by: Zhao Yakui yakui.zhao@intel.com [Ported to in kernel driver] Signed-off-by: Alan Cox alan@linux.intel.com ---
drivers/gpu/drm/gma500/cdv_intel_display.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c index a68509b..5c3a312 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_display.c +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c @@ -791,7 +791,7 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode) case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: if (psb_intel_crtc->active) - return; + break;
psb_intel_crtc->active = true;
@@ -835,7 +835,6 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode) REG_WRITE(map->status, temp); REG_READ(map->status);
- cdv_intel_update_watermark(dev, crtc); cdv_intel_crtc_load_lut(crtc);
/* Give the overlay scaler a chance to enable @@ -845,7 +844,7 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode) break; case DRM_MODE_DPMS_OFF: if (!psb_intel_crtc->active) - return; + break;
psb_intel_crtc->active = false;
@@ -892,10 +891,10 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode)
/* Wait for the clocks to turn off. */ udelay(150); - cdv_intel_update_watermark(dev, crtc); psb_intel_crtc->crtc_enable = false; break; } + cdv_intel_update_watermark(dev, crtc); /*Set FIFO Watermarks*/ REG_WRITE(DSPARB, 0x3F3E); }
From: Zhao Yakui yakui.zhao@intel.com
Based on the spec, the CRT output doesn't use the lane. And the HDMI B output uses the Lane0/1 while the HDMI C output uses the Lane 2/3. But currently it will program all the four lanes for the CRT/HDMI.
Signed-off-by: Zhao Yakui yakui.zhao@intel.com [Ported to the in-kernel driver] Signed-off-by: Alan Cox alan@linux.intel.com ---
drivers/gpu/drm/gma500/cdv_intel_display.c | 82 +++++++++++++++++----------- drivers/gpu/drm/gma500/cdv_intel_hdmi.c | 2 + drivers/gpu/drm/gma500/psb_intel_drv.h | 5 ++ 3 files changed, 58 insertions(+), 31 deletions(-)
diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c index 5c3a312..f16169c 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_display.c +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c @@ -57,8 +57,14 @@ struct cdv_intel_clock_t { struct cdv_intel_limit_t { struct cdv_intel_range_t dot, vco, n, m, m1, m2, p, p1; struct cdv_intel_p2_t p2; + bool (*find_pll)(const struct cdv_intel_limit_t *, struct drm_crtc *, + int, int, struct cdv_intel_clock_t *); };
+static bool cdv_intel_find_best_PLL(const struct cdv_intel_limit_t *limit, + struct drm_crtc *crtc, int target, int refclk, + struct cdv_intel_clock_t *best_clock); + #define CDV_LIMIT_SINGLE_LVDS_96 0 #define CDV_LIMIT_SINGLE_LVDS_100 1 #define CDV_LIMIT_DAC_HDMI_27 2 @@ -76,6 +82,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = { .p1 = {.min = 2, .max = 10}, .p2 = {.dot_limit = 200000, .p2_slow = 14, .p2_fast = 14}, + .find_pll = cdv_intel_find_best_PLL, }, { /* CDV_SINGLE_LVDS_100MHz */ .dot = {.min = 20000, .max = 115500}, @@ -90,6 +97,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = { * is 80-224Mhz. Prefer single channel as much as possible. */ .p2 = {.dot_limit = 200000, .p2_slow = 14, .p2_fast = 14}, + .find_pll = cdv_intel_find_best_PLL, }, { /* CDV_DAC_HDMI_27MHz */ .dot = {.min = 20000, .max = 400000}, @@ -101,6 +109,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = { .p = {.min = 5, .max = 90}, .p1 = {.min = 1, .max = 9}, .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 5}, + .find_pll = cdv_intel_find_best_PLL, }, { /* CDV_DAC_HDMI_96MHz */ .dot = {.min = 20000, .max = 400000}, @@ -112,6 +121,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = { .p = {.min = 5, .max = 100}, .p1 = {.min = 1, .max = 10}, .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 5}, + .find_pll = cdv_intel_find_best_PLL, }, };
@@ -216,7 +226,7 @@ static void cdv_sb_reset(struct drm_device *dev) */ static int cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc, - struct cdv_intel_clock_t *clock, bool is_lvds) + struct cdv_intel_clock_t *clock, bool is_lvds, u32 ddi_select) { struct psb_intel_crtc *psb_crtc = to_psb_intel_crtc(crtc); int pipe = psb_crtc->pipe; @@ -336,30 +346,33 @@ cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc, if (ret) return ret;
- lane_reg = PSB_LANE0; - cdv_sb_read(dev, lane_reg, &lane_value); - lane_value &= ~(LANE_PLL_MASK); - lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe); - cdv_sb_write(dev, lane_reg, lane_value); - - lane_reg = PSB_LANE1; - cdv_sb_read(dev, lane_reg, &lane_value); - lane_value &= ~(LANE_PLL_MASK); - lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe); - cdv_sb_write(dev, lane_reg, lane_value); - - lane_reg = PSB_LANE2; - cdv_sb_read(dev, lane_reg, &lane_value); - lane_value &= ~(LANE_PLL_MASK); - lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe); - cdv_sb_write(dev, lane_reg, lane_value); - - lane_reg = PSB_LANE3; - cdv_sb_read(dev, lane_reg, &lane_value); - lane_value &= ~(LANE_PLL_MASK); - lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe); - cdv_sb_write(dev, lane_reg, lane_value); - + if (ddi_select) { + if ((ddi_select & DDI_MASK) == DDI0_SELECT) { + lane_reg = PSB_LANE0; + cdv_sb_read(dev, lane_reg, &lane_value); + lane_value &= ~(LANE_PLL_MASK); + lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe); + cdv_sb_write(dev, lane_reg, lane_value); + + lane_reg = PSB_LANE1; + cdv_sb_read(dev, lane_reg, &lane_value); + lane_value &= ~(LANE_PLL_MASK); + lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe); + cdv_sb_write(dev, lane_reg, lane_value); + } else { + lane_reg = PSB_LANE2; + cdv_sb_read(dev, lane_reg, &lane_value); + lane_value &= ~(LANE_PLL_MASK); + lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe); + cdv_sb_write(dev, lane_reg, lane_value); + + lane_reg = PSB_LANE3; + cdv_sb_read(dev, lane_reg, &lane_value); + lane_value &= ~(LANE_PLL_MASK); + lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe); + cdv_sb_write(dev, lane_reg, lane_value); + } + } return 0; }
@@ -438,13 +451,12 @@ static bool cdv_intel_PLL_is_valid(struct drm_crtc *crtc, return true; }
-static bool cdv_intel_find_best_PLL(struct drm_crtc *crtc, int target, - int refclk, - struct cdv_intel_clock_t *best_clock) +static bool cdv_intel_find_best_PLL(const struct cdv_intel_limit_t *limit, + struct drm_crtc *crtc, int target, int refclk, + struct cdv_intel_clock_t *best_clock) { struct drm_device *dev = crtc->dev; struct cdv_intel_clock_t clock; - const struct cdv_intel_limit_t *limit = cdv_intel_limit(crtc, refclk); int err = target;
@@ -954,6 +966,8 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, bool is_hdmi = false; struct drm_mode_config *mode_config = &dev->mode_config; struct drm_connector *connector; + const struct cdv_intel_limit_t *limit; + u32 ddi_select = 0;
list_for_each_entry(connector, &mode_config->connector_list, head) { struct psb_intel_encoder *psb_intel_encoder = @@ -963,6 +977,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, || connector->encoder->crtc != crtc) continue;
+ ddi_select = psb_intel_encoder->ddi_select; switch (psb_intel_encoder->type) { case INTEL_OUTPUT_LVDS: is_lvds = true; @@ -976,6 +991,9 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, case INTEL_OUTPUT_HDMI: is_hdmi = true; break; + default: + DRM_ERROR("invalid output type.\n"); + return 0; } }
@@ -992,8 +1010,10 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, }
drm_mode_debug_printmodeline(adjusted_mode); + + limit = cdv_intel_limit(crtc, refclk);
- ok = cdv_intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, + ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock); if (!ok) { dev_err(dev->dev, "Couldn't find PLL settings for mode!\n"); @@ -1032,7 +1052,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, REG_WRITE(map->dpll, dpll | DPLL_VGA_MODE_DIS | DPLL_SYNCLOCK_ENABLE); REG_READ(map->dpll);
- cdv_dpll_set_clock_cdv(dev, crtc, &clock, is_lvds); + cdv_dpll_set_clock_cdv(dev, crtc, &clock, is_lvds, ddi_select);
udelay(150);
diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c index a86f87b..0c90f03 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c +++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c @@ -352,9 +352,11 @@ void cdv_hdmi_init(struct drm_device *dev, switch (reg) { case SDVOB: ddc_bus = GPIOE; + psb_intel_encoder->ddi_select = DDI0_SELECT; break; case SDVOC: ddc_bus = GPIOD; + psb_intel_encoder->ddi_select = DDI1_SELECT; break; default: DRM_ERROR("unknown reg 0x%x for HDMI\n", reg); diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h index ebe1a28..7b357b0 100644 --- a/drivers/gpu/drm/gma500/psb_intel_drv.h +++ b/drivers/gpu/drm/gma500/psb_intel_drv.h @@ -133,6 +133,11 @@ struct psb_intel_encoder { void (*hot_plug)(struct psb_intel_encoder *); int crtc_mask; int clone_mask; + u32 ddi_select; /* Channel info */ +#define DDI0_SELECT 0x01 +#define DDI1_SELECT 0x02 +#define DP_MASK 0x8000; +#define DDI_MASK 0x03 void *dev_priv; /* For sdvo_priv, lvds_priv, etc... */
/* FIXME: Either make SDVO and LVDS store it's i2c here or give CDV it's
From: Alan Cox alan@linux.intel.com
Add the support of display port on CDV
Import the pieces we need in order to do DisplayPort. Don't wire them up yet as there is work to do to integrate them.
Signed-off-by: Zhao Yakui yakui.zhao@intel.com Signed-off-by: Alan Cox alan@linux.intel.com ---
drivers/gpu/drm/gma500/cdv_intel_dp.c | 1504 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/gma500/psb_intel_drv.h | 6 drivers/gpu/drm/gma500/psb_intel_reg.h | 155 +++ 3 files changed, 1661 insertions(+), 4 deletions(-) create mode 100644 drivers/gpu/drm/gma500/cdv_intel_dp.c
diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c new file mode 100644 index 0000000..c6d545d --- /dev/null +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -0,0 +1,1504 @@ +/* + * Copyright © 2012 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 + * + */ + +#include <linux/i2c.h> +#include <linux/slab.h> +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "drm_crtc_helper.h" +#include "psb_drv.h" +#include "psb_intel_drv.h" +#include "psb_drm.h" +#include "psb_intel_reg.h" +#include "drm_dp_helper.h" + + +#define DP_LINK_STATUS_SIZE 6 +#define DP_LINK_CHECK_TIMEOUT (10 * 1000) + +#define DP_LINK_CONFIGURATION_SIZE 9 + +#define CDV_FAST_LINK_TRAIN 1 + +struct psb_intel_dp { + uint32_t output_reg; + uint32_t DP; + uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE]; + bool has_audio; + int force_audio; + uint32_t color_range; + uint8_t link_bw; + uint8_t lane_count; + uint8_t dpcd[4]; + struct psb_intel_output *output; + struct i2c_adapter adapter; + struct i2c_algo_dp_aux_data algo; + uint8_t train_set[4]; + uint8_t link_status[DP_LINK_STATUS_SIZE]; +}; + +struct ddi_regoff { + uint32_t PreEmph1; + uint32_t PreEmph2; + uint32_t VSwing1; + uint32_t VSwing2; + uint32_t VSwing3; + uint32_t VSwing4; + uint32_t VSwing5; +}; + +static struct ddi_regoff ddi_DP_train_table[] = { + {.PreEmph1 = 0x812c, .PreEmph2 = 0x8124, .VSwing1 = 0x8154, + .VSwing2 = 0x8148, .VSwing3 = 0x814C, .VSwing4 = 0x8150, + .VSwing5 = 0x8158,}, + {.PreEmph1 = 0x822c, .PreEmph2 = 0x8224, .VSwing1 = 0x8254, + .VSwing2 = 0x8248, .VSwing3 = 0x824C, .VSwing4 = 0x8250, + .VSwing5 = 0x8258,}, +}; + +static uint32_t dp_vswing_premph_table[] = { + 0x55338954, 0x4000, + 0x554d8954, 0x2000, + 0x55668954, 0, + 0x559ac0d4, 0x6000, +}; +/** + * 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 true, and false otherwise. + */ +static bool is_edp(struct psb_intel_output *output) +{ + return output->type == INTEL_OUTPUT_EDP; +} + + +static void psb_intel_dp_start_link_train(struct psb_intel_output *output); +static void psb_intel_dp_complete_link_train(struct psb_intel_output *output); +static void psb_intel_dp_link_down(struct psb_intel_output *output); + +static int +psb_intel_dp_max_lane_count(struct psb_intel_output *output) +{ + struct psb_intel_dp *intel_dp = output->dev_priv; + int max_lane_count = 4; + + if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) { + max_lane_count = intel_dp->dpcd[DP_MAX_LANE_COUNT] & 0x1f; + switch (max_lane_count) { + case 1: case 2: case 4: + break; + default: + max_lane_count = 4; + } + } + return max_lane_count; +} + +static int +psb_intel_dp_max_link_bw(struct psb_intel_output *output) +{ + struct psb_intel_dp *intel_dp = output->dev_priv; + int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE]; + + switch (max_link_bw) { + case DP_LINK_BW_1_62: + case DP_LINK_BW_2_7: + break; + default: + max_link_bw = DP_LINK_BW_1_62; + break; + } + return max_link_bw; +} + +static int +psb_intel_dp_link_clock(uint8_t link_bw) +{ + if (link_bw == DP_LINK_BW_2_7) + return 270000; + else + return 162000; +} + +static int +psb_intel_dp_link_required(int pixel_clock, int bpp) +{ + return (pixel_clock * bpp + 7) / 8; +} + +static int +psb_intel_dp_max_data_rate(int max_link_clock, int max_lanes) +{ + return (max_link_clock * max_lanes * 19) / 20; +} + +static int +psb_intel_dp_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct psb_intel_output *output = to_psb_intel_output(connector); + struct drm_device *dev = connector->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + int max_link_clock = psb_intel_dp_link_clock(psb_intel_dp_max_link_bw(output)); + int max_lanes = psb_intel_dp_max_lane_count(output); + + if (is_edp(output) && dev_priv->panel_fixed_mode) { + if (mode->hdisplay > dev_priv->panel_fixed_mode->hdisplay) + return MODE_PANEL; + + if (mode->vdisplay > dev_priv->panel_fixed_mode->vdisplay) + return MODE_PANEL; + } + + /* only refuse the mode on non eDP since we have seen some weird eDP panels + which are outside spec tolerances but somehow work by magic */ + if (!is_edp(output) && + (psb_intel_dp_link_required(mode->clock, 24) + > psb_intel_dp_max_data_rate(max_link_clock, max_lanes))) + return MODE_CLOCK_HIGH; + + if (mode->clock < 10000) + return MODE_CLOCK_LOW; + + return MODE_OK; +} + +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; +} + +static 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 +psb_intel_dp_aux_ch(struct psb_intel_output *output, + uint8_t *send, int send_bytes, + uint8_t *recv, int recv_size) +{ + struct psb_intel_dp *intel_dp = output->dev_priv; + uint32_t output_reg = intel_dp->output_reg; + struct drm_device *dev = output->base.dev; + uint32_t ch_ctl = output_reg + 0x10; + uint32_t ch_data = ch_ctl + 4; + int i; + int recv_bytes; + uint32_t status; + uint32_t aux_clock_divider; + int try, precharge; + + /* The clock divider is based off the hrawclk, + * and would like to run at 2MHz. So, take the + * hrawclk value and divide by 2 and use that + * On CDV platform it uses 200MHz as hrawclk. + * + */ + aux_clock_divider = 200 / 2; + + precharge = 4; + + if (REG_READ(ch_ctl) & DP_AUX_CH_CTL_SEND_BUSY) { + DRM_ERROR("dp_aux_ch not started status 0x%08x\n", + REG_READ(ch_ctl)); + return -EBUSY; + } + + /* 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) + REG_WRITE(ch_data + i, + pack_aux(send + i, send_bytes - i)); + + /* Send the command and wait for it to complete */ + REG_WRITE(ch_ctl, + DP_AUX_CH_CTL_SEND_BUSY | + DP_AUX_CH_CTL_TIME_OUT_400us | + (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) | + (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | + (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); + for (;;) { + status = REG_READ(ch_ctl); + if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) + break; + udelay(100); + } + + /* Clear done status and any errors */ + REG_WRITE(ch_ctl, + status | + 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) + break; + } + + if ((status & DP_AUX_CH_CTL_DONE) == 0) { + DRM_ERROR("dp_aux_ch not done status 0x%08x\n", status); + return -EBUSY; + } + + /* Check for timeout or receive error. + * Timeouts occur when the sink is not connected + */ + if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) { + DRM_ERROR("dp_aux_ch receive error status 0x%08x\n", status); + return -EIO; + } + + /* 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) { + DRM_DEBUG_KMS("dp_aux_ch timeout status 0x%08x\n", status); + return -ETIMEDOUT; + } + + /* 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(REG_READ(ch_data + i), + recv + i, recv_bytes - i); + + return recv_bytes; +} + +/* Write data to the aux channel in native mode */ +static int +psb_intel_dp_aux_native_write(struct psb_intel_output *output, + 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 = psb_intel_dp_aux_ch(output, 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 -EIO; + } + return send_bytes; +} + +/* Write a single byte to the aux channel in native mode */ +static int +psb_intel_dp_aux_native_write_1(struct psb_intel_output *output, + uint16_t address, uint8_t byte) +{ + return psb_intel_dp_aux_native_write(output, address, &byte, 1); +} + +/* read bytes from a native aux channel */ +static int +psb_intel_dp_aux_native_read(struct psb_intel_output *output, + 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 = psb_intel_dp_aux_ch(output, msg, msg_bytes, + reply, reply_bytes); + if (ret == 0) + return -EPROTO; + 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 -EIO; + } +} + +static int +psb_intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, + uint8_t write_byte, uint8_t *read_byte) +{ + struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; + struct psb_intel_dp *intel_dp = container_of(adapter, + struct psb_intel_dp, + adapter); + struct psb_intel_output *output = intel_dp->output; + uint16_t address = algo_data->address; + 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] = address >> 8; + msg[2] = 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 = psb_intel_dp_aux_ch(output, + msg, msg_bytes, + reply, reply_bytes); + if (ret < 0) { + DRM_DEBUG_KMS("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: + DRM_DEBUG_KMS("aux_ch native nack\n"); + return -EREMOTEIO; + case AUX_NATIVE_REPLY_DEFER: + udelay(100); + continue; + default: + DRM_ERROR("aux_ch invalid native reply 0x%02x\n", + reply[0]); + return -EREMOTEIO; + } + + 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: + DRM_DEBUG_KMS("aux_i2c nack\n"); + return -EREMOTEIO; + case AUX_I2C_REPLY_DEFER: + DRM_DEBUG_KMS("aux_i2c defer\n"); + udelay(100); + break; + default: + DRM_ERROR("aux_i2c invalid reply 0x%02x\n", reply[0]); + return -EREMOTEIO; + } + } + + DRM_ERROR("too many retries, giving up\n"); + return -EREMOTEIO; +} + +static int +psb_intel_dp_i2c_init(struct psb_intel_output *output, const char *name) +{ + struct psb_intel_dp *intel_dp = output->dev_priv; + DRM_DEBUG_KMS("i2c_init %s\n", name); + intel_dp->algo.running = false; + intel_dp->algo.address = 0; + intel_dp->algo.aux_ch = psb_intel_dp_i2c_aux_ch; + + memset(&intel_dp->adapter, '\0', sizeof (intel_dp->adapter)); + intel_dp->adapter.owner = THIS_MODULE; + intel_dp->adapter.class = I2C_CLASS_DDC; + strncpy (intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1); + intel_dp->adapter.name[sizeof(intel_dp->adapter.name) - 1] = '\0'; + intel_dp->adapter.algo_data = &intel_dp->algo; + intel_dp->adapter.dev.parent = &output->base.kdev; + + return i2c_dp_aux_add_bus(&intel_dp->adapter); +} + +static bool +psb_intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + struct psb_intel_dp *intel_dp = output->dev_priv; + int lane_count, clock; + int max_lane_count = psb_intel_dp_max_lane_count(output); + int max_clock = psb_intel_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 = max_clock; clock >= 0; clock--) { + int link_avail = psb_intel_dp_max_data_rate(psb_intel_dp_link_clock(bws[clock]), lane_count); + + if (psb_intel_dp_link_required(mode->clock, 24) + <= link_avail) { + intel_dp->link_bw = bws[clock]; + intel_dp->lane_count = lane_count; + adjusted_mode->clock = psb_intel_dp_link_clock(intel_dp->link_bw); + DRM_DEBUG_KMS("Display port link bw %02x lane " + "count %d clock %d\n", + intel_dp->link_bw, intel_dp->lane_count, + adjusted_mode->clock); + return true; + } + } + } + + return false; +} + +struct psb_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 +psb_intel_reduce_ratio(uint32_t *num, uint32_t *den) +{ + /* + while (*num > 0xffffff || *den > 0xffffff) { + *num >>= 1; + *den >>= 1; + }*/ + uint64_t value, m; + m = *num; + value = m * (0x800000); + m = do_div(value, *den); + *num = value; + *den = 0x800000; +} + +static void +psb_intel_dp_compute_m_n(int bpp, + int nlanes, + int pixel_clock, + int link_clock, + struct psb_intel_dp_m_n *m_n) +{ + m_n->tu = 64; + m_n->gmch_m = (pixel_clock * bpp + 7) >> 3; + m_n->gmch_n = link_clock * nlanes; + psb_intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); + m_n->link_m = pixel_clock; + m_n->link_n = link_clock; + psb_intel_reduce_ratio(&m_n->link_m, &m_n->link_n); +} + +void +psb_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = crtc->dev; + struct drm_mode_config *mode_config = &dev->mode_config; + struct drm_encoder *encoder; + struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc); + int lane_count = 4, bpp = 24; + struct psb_intel_dp_m_n m_n; + int pipe = intel_crtc->pipe; + + /* + * Find the lane count in the intel_encoder private + */ + list_for_each_entry(encoder, &mode_config->encoder_list, head) { + struct psb_intel_output *intel_output; + struct psb_intel_dp *intel_dp; + + if (encoder->crtc != crtc) + continue; + + intel_output = enc_to_psb_intel_output(encoder); + intel_dp = intel_output->dev_priv; + if (intel_output->type == INTEL_OUTPUT_DISPLAYPORT) { + lane_count = intel_dp->lane_count; + break; + } else if (is_edp(intel_output)) { + lane_count = intel_dp->lane_count; + break; + } + } + + /* + * 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. + */ + psb_intel_dp_compute_m_n(bpp, lane_count, + mode->clock, adjusted_mode->clock, &m_n); + + { + REG_WRITE(PIPE_GMCH_DATA_M(pipe), + ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) | + m_n.gmch_m); + REG_WRITE(PIPE_GMCH_DATA_N(pipe), m_n.gmch_n); + REG_WRITE(PIPE_DP_LINK_M(pipe), m_n.link_m); + REG_WRITE(PIPE_DP_LINK_N(pipe), m_n.link_n); + } +} + +static void +psb_intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct psb_intel_output *intel_output = enc_to_psb_intel_output(encoder); + struct drm_crtc *crtc = encoder->crtc; + struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc); + struct psb_intel_dp *intel_dp = intel_output->dev_priv; + + + intel_dp->DP = DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; + intel_dp->DP |= intel_dp->color_range; + + 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; + + 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; + } + if (intel_dp->has_audio) + intel_dp->DP |= DP_AUDIO_OUTPUT_ENABLE; + + 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; + + /* + * 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; + intel_dp->DP |= DP_ENHANCED_FRAMING; + } + + /* CPT DP's pipe select is decided in TRANS_DP_CTL */ + if (intel_crtc->pipe == 1) + intel_dp->DP |= DP_PIPEB_SELECT; + + DRM_DEBUG_KMS("DP expected reg is %x\n", intel_dp->DP); +} + + +/* If the sink supports it, try to set the power state appropriately */ +static void psb_intel_dp_sink_dpms(struct psb_intel_output *output, int mode) +{ + struct psb_intel_dp *intel_dp = output->dev_priv; + int ret, i; + + /* Should have a valid DPCD by this point */ + if (intel_dp->dpcd[DP_DPCD_REV] < 0x11) + return; + + if (mode != DRM_MODE_DPMS_ON) { + ret = psb_intel_dp_aux_native_write_1(output, DP_SET_POWER, + DP_SET_POWER_D3); + if (ret != 1) + DRM_DEBUG_DRIVER("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 = psb_intel_dp_aux_native_write_1(output, + DP_SET_POWER, + DP_SET_POWER_D0); + if (ret == 1) + break; + udelay(1000); + } + } +} + +static void psb_intel_dp_prepare(struct drm_encoder *encoder) +{ + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + + /* Wake up the sink first */ + psb_intel_dp_sink_dpms(output, DRM_MODE_DPMS_ON); + + psb_intel_dp_link_down(output); +} + +static void psb_intel_dp_commit(struct drm_encoder *encoder) +{ + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + + psb_intel_dp_start_link_train(output); + + psb_intel_dp_complete_link_train(output); + +} + +static void +psb_intel_dp_dpms(struct drm_encoder *encoder, int mode) +{ + struct psb_intel_output *intel_output = enc_to_psb_intel_output(encoder); + struct psb_intel_dp *intel_dp = intel_output->dev_priv; + struct drm_device *dev = encoder->dev; + uint32_t dp_reg = REG_READ(intel_dp->output_reg); + + if (mode != DRM_MODE_DPMS_ON) { + psb_intel_dp_sink_dpms(intel_output, mode); + psb_intel_dp_link_down(intel_output); + } else { + psb_intel_dp_sink_dpms(intel_output, mode); + if (!(dp_reg & DP_PORT_EN)) { + psb_intel_dp_start_link_train(intel_output); + psb_intel_dp_complete_link_train(intel_output); + } + } +} + +/* + * Native read with retry for link status and receiver capability reads for + * cases where the sink may still be asleep. + */ +static bool +psb_intel_dp_aux_native_read_retry(struct psb_intel_output *output, 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 = psb_intel_dp_aux_native_read(output, address, recv, + recv_bytes); + if (ret == recv_bytes) + return true; + udelay(1000); + } + + return false; +} + +/* + * Fetch AUX CH registers 0x202 - 0x207 which contain + * link status information + */ +static bool +psb_intel_dp_get_link_status(struct psb_intel_output *output) +{ + struct psb_intel_dp *intel_dp = output->dev_priv; + return psb_intel_dp_aux_native_read_retry(output, + DP_LANE0_1_STATUS, + intel_dp->link_status, + DP_LINK_STATUS_SIZE); +} + +static uint8_t +psb_intel_dp_link_status(uint8_t link_status[DP_LINK_STATUS_SIZE], + int r) +{ + return link_status[r - DP_LANE0_1_STATUS]; +} + +static uint8_t +psb_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 = psb_intel_dp_link_status(link_status, i); + + return ((l >> s) & 3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; +} + +static uint8_t +psb_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 = psb_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 + +#define CDV_DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_1200 +/* +static uint8_t +psb_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 +psb_intel_get_adjust_train(struct psb_intel_output *output) +{ + struct psb_intel_dp *intel_dp = output->dev_priv; + uint8_t v = 0; + uint8_t p = 0; + int lane; + + for (lane = 0; lane < intel_dp->lane_count; lane++) { + uint8_t this_v = psb_intel_get_adjust_request_voltage(intel_dp->link_status, lane); + uint8_t this_p = psb_intel_get_adjust_request_pre_emphasis(intel_dp->link_status, lane); + + if (this_v > v) + v = this_v; + if (this_p > p) + p = this_p; + } + + if (v >= CDV_DP_VOLTAGE_MAX) + v = CDV_DP_VOLTAGE_MAX | DP_TRAIN_MAX_SWING_REACHED; + + if (p == DP_TRAIN_PRE_EMPHASIS_MASK) + p |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; + + for (lane = 0; lane < 4; lane++) + intel_dp->train_set[lane] = v | p; +} + + +static uint8_t +psb_intel_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE], + int lane) +{ + int i = DP_LANE0_1_STATUS + (lane >> 1); + int s = (lane & 1) * 4; + uint8_t l = psb_intel_dp_link_status(link_status, i); + + return (l >> s) & 0xf; +} + +/* Check for clock recovery is done on all channels */ +static bool +psb_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 = psb_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 +psb_intel_channel_eq_ok(struct psb_intel_output *output) +{ + struct psb_intel_dp *intel_dp = output->dev_priv; + uint8_t lane_align; + uint8_t lane_status; + int lane; + + lane_align = psb_intel_dp_link_status(intel_dp->link_status, + DP_LANE_ALIGN_STATUS_UPDATED); + if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) + return false; + for (lane = 0; lane < intel_dp->lane_count; lane++) { + lane_status = psb_intel_get_lane_status(intel_dp->link_status, lane); + if ((lane_status & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS) + return false; + } + return true; +} + +static bool +psb_intel_dp_set_link_train(struct psb_intel_output *output, + uint32_t dp_reg_value, + uint8_t dp_train_pat) +{ + + struct drm_device *dev = output->base.dev; + int ret; + struct psb_intel_dp *intel_dp = output->dev_priv; + + REG_WRITE(intel_dp->output_reg, dp_reg_value); + REG_READ(intel_dp->output_reg); + + ret = psb_intel_dp_aux_native_write_1(output, + DP_TRAINING_PATTERN_SET, + dp_train_pat); + + if (ret != 1) { + DRM_DEBUG_KMS("Failure in setting link pattern %x\n", + dp_train_pat); + return false; + } + + return true; +} + + +static bool +psb_intel_dplink_set_level(struct psb_intel_output *output, + uint8_t dp_train_pat) +{ + + int ret; + struct psb_intel_dp *intel_dp = output->dev_priv; + + ret = psb_intel_dp_aux_native_write(output, + DP_TRAINING_LANE0_SET, + intel_dp->train_set, + intel_dp->lane_count); + + if (ret != intel_dp->lane_count) { + DRM_DEBUG_KMS("Failure in setting level %d, lane_cnt= %d\n", + intel_dp->train_set[0], intel_dp->lane_count); + return false; + } + return true; +} + +static void +psb_intel_dp_set_vswing_premph(struct psb_intel_output *output, uint8_t signal_level) +{ + struct drm_device *dev = output->base.dev; + struct psb_intel_dp *intel_dp = output->dev_priv; + struct ddi_regoff *ddi_reg; + int vswing, premph, index; + + if (intel_dp->output_reg == DP_B) + ddi_reg = &ddi_DP_train_table[0]; + else + ddi_reg = &ddi_DP_train_table[1]; + + vswing = (signal_level & DP_TRAIN_VOLTAGE_SWING_MASK); + premph = ((signal_level & DP_TRAIN_PRE_EMPHASIS_MASK)) >> + DP_TRAIN_PRE_EMPHASIS_SHIFT; + + if (vswing + premph > 3) + return; +#ifdef CDV_FAST_LINK_TRAIN + return; +#endif + DRM_DEBUG_KMS("Test2\n"); + //return ; + psb_sb_reset(dev); + /* ;Swing voltage programming + ;gfx_dpio_set_reg(0xc058, 0x0505313A) */ + psb_sb_write(dev, ddi_reg->VSwing5, 0x0505313A); + + /* ;gfx_dpio_set_reg(0x8154, 0x43406055) */ + psb_sb_write(dev, ddi_reg->VSwing1, 0x43406055); + + /* ;gfx_dpio_set_reg(0x8148, 0x55338954) + * The VSwing_PreEmph table is also considered based on the vswing/premp + */ + index = (vswing + premph) * 2; + if (premph == 1 && vswing == 1) { + psb_sb_write(dev, ddi_reg->VSwing2, 0x055738954); + } else + psb_sb_write(dev, ddi_reg->VSwing2, dp_vswing_premph_table[index]); + + /* ;gfx_dpio_set_reg(0x814c, 0x40802040) */ + if ((vswing + premph) == DP_TRAIN_VOLTAGE_SWING_1200) + psb_sb_write(dev, ddi_reg->VSwing3, 0x70802040); + else + psb_sb_write(dev, ddi_reg->VSwing3, 0x40802040); + + /* ;gfx_dpio_set_reg(0x8150, 0x2b405555) */ + //psb_sb_write(dev, ddi_reg->VSwing4, 0x2b405555); + + /* ;gfx_dpio_set_reg(0x8154, 0xc3406055) */ + psb_sb_write(dev, ddi_reg->VSwing1, 0xc3406055); + + /* ;Pre emphasis programming + * ;gfx_dpio_set_reg(0xc02c, 0x1f030040) + */ + psb_sb_write(dev, ddi_reg->PreEmph1, 0x1f030040); + + /* ;gfx_dpio_set_reg(0x8124, 0x00004000) */ + index = 2 * premph + 1; + psb_sb_write(dev, ddi_reg->PreEmph2, dp_vswing_premph_table[index]); + return; +} + + +/* Enable corresponding port and start training pattern 1 */ +static void +psb_intel_dp_start_link_train(struct psb_intel_output *output) +{ + struct drm_device *dev = output->base.dev; + struct psb_intel_dp *intel_dp = output->dev_priv; + int i; + uint8_t voltage; + bool clock_recovery = false; + int tries; + u32 reg; + uint32_t DP = intel_dp->DP; + + DP |= DP_PORT_EN; + DP &= ~DP_LINK_TRAIN_MASK; + + reg = DP; + reg |= DP_LINK_TRAIN_PAT_1; + /* Enable output, wait for it to become active */ + REG_WRITE(intel_dp->output_reg, reg); + REG_READ(intel_dp->output_reg); + psb_intel_wait_for_vblank(dev); + + DRM_DEBUG_KMS("Link config\n"); + /* Write the link configuration data */ + psb_intel_dp_aux_native_write(output, DP_LINK_BW_SET, + intel_dp->link_configuration, + 2); + + memset(intel_dp->train_set, 0, 4); + voltage = 0; + tries = 0; + clock_recovery = false; + + DRM_DEBUG_KMS("Start train\n"); + reg = DP | DP_LINK_TRAIN_PAT_1; + + + for (;;) { + /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ + + if (!psb_intel_dp_set_link_train(output, reg, DP_TRAINING_PATTERN_1)) { + DRM_DEBUG_KMS("Failure in aux-transfer setting pattern 1\n"); + } + psb_intel_dp_set_vswing_premph(output, intel_dp->train_set[0]); + /* Set training pattern 1 */ + + psb_intel_dplink_set_level(output, DP_TRAINING_PATTERN_1); + + udelay(200); + if (!psb_intel_dp_get_link_status(output)) + break; + + if (psb_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { + DRM_DEBUG_KMS("PT1 train is done\n"); + clock_recovery = true; + 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) + break; + + /* Check to see if we've tried the same voltage 5 times */ + if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) { + ++tries; + if (tries == 5) + break; + } else + tries = 0; + voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; + + /* Compute new intel_dp->train_set as requested by target */ + psb_intel_get_adjust_train(output); + + } + + if (!clock_recovery) { + DRM_DEBUG_KMS("failure in DP patter 1 training, train set %x\n", intel_dp->train_set[0]); + } + + intel_dp->DP = DP; +} + +static void +psb_intel_dp_complete_link_train(struct psb_intel_output *output) +{ + struct drm_device *dev = output->base.dev; + struct psb_intel_dp *intel_dp = output->dev_priv; + bool channel_eq = false; + int tries, cr_tries; + u32 reg; + uint32_t DP = intel_dp->DP; + + /* channel equalization */ + tries = 0; + cr_tries = 0; + channel_eq = false; + + DRM_DEBUG_KMS("\n"); + reg = DP | DP_LINK_TRAIN_PAT_2; + + for (;;) { + /* channel eq pattern */ + if (!psb_intel_dp_set_link_train(output, reg, + DP_TRAINING_PATTERN_2)) { + DRM_DEBUG_KMS("Failure in aux-transfer setting pattern 2\n"); + } + /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ + + if (cr_tries > 5) { + DRM_ERROR("failed to train DP, aborting\n"); + psb_intel_dp_link_down(output); + break; + } + + psb_intel_dp_set_vswing_premph(output, intel_dp->train_set[0]); + + psb_intel_dplink_set_level(output, DP_TRAINING_PATTERN_2); + + udelay(1000); + if (!psb_intel_dp_get_link_status(output)) + break; + + /* Make sure clock is still ok */ + if (!psb_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { + psb_intel_dp_start_link_train(output); + cr_tries++; + continue; + } + + if (psb_intel_channel_eq_ok(output)) { + DRM_DEBUG_KMS("PT2 train is done\n"); + channel_eq = true; + break; + } + + /* Try 5 times, then try clock recovery if that fails */ + if (tries > 5) { + psb_intel_dp_link_down(output); + psb_intel_dp_start_link_train(output); + tries = 0; + cr_tries++; + continue; + } + + /* Compute new intel_dp->train_set as requested by target */ + psb_intel_get_adjust_train(output); + ++tries; + + } + + reg = DP | DP_LINK_TRAIN_OFF; + + REG_WRITE(intel_dp->output_reg, reg); + REG_READ(intel_dp->output_reg); + psb_intel_dp_aux_native_write_1(output, + DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); +} + +static void +psb_intel_dp_link_down(struct psb_intel_output *output) +{ + struct drm_device *dev = output->base.dev; + struct psb_intel_dp *intel_dp = output->dev_priv; + uint32_t DP = intel_dp->DP; + + if ((REG_READ(intel_dp->output_reg) & DP_PORT_EN) == 0) + return; + + DRM_DEBUG_KMS("\n"); + + + { + DP &= ~DP_LINK_TRAIN_MASK; + REG_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE); + } + REG_READ(intel_dp->output_reg); + + msleep(17); + + REG_WRITE(intel_dp->output_reg, DP & ~DP_PORT_EN); + REG_READ(intel_dp->output_reg); +} + +static enum drm_connector_status +cdv_dp_detect(struct psb_intel_output *output) +{ + struct psb_intel_dp *intel_dp = output->dev_priv; + enum drm_connector_status status; + + status = connector_status_disconnected; + if (psb_intel_dp_aux_native_read(output, 0x000, intel_dp->dpcd, + sizeof (intel_dp->dpcd)) == sizeof (intel_dp->dpcd)) + { + if (intel_dp->dpcd[DP_DPCD_REV] != 0) + status = connector_status_connected; + } + if (status == connector_status_connected) + DRM_DEBUG_KMS("DPCD: Rev=%x LN_Rate=%x LN_CNT=%x LN_DOWNSP=%x\n", + intel_dp->dpcd[0], intel_dp->dpcd[1], + intel_dp->dpcd[2], intel_dp->dpcd[3]); + return status; +} + +/** + * 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 +psb_intel_dp_detect(struct drm_connector *connector, bool force) +{ + struct psb_intel_output *output = to_psb_intel_output(connector); + struct psb_intel_dp *intel_dp = output->dev_priv; + enum drm_connector_status status; + struct edid *edid = NULL; + + intel_dp->has_audio = false; + + status = cdv_dp_detect(output); + if (status != connector_status_connected) + return status; + + if (intel_dp->force_audio) { + intel_dp->has_audio = intel_dp->force_audio > 0; + } else { + edid = drm_get_edid(connector, &intel_dp->adapter); + if (edid) { + intel_dp->has_audio = drm_detect_monitor_audio(edid); + connector->display_info.raw_edid = NULL; + kfree(edid); + } + } + + return connector_status_connected; +} + +static int psb_intel_dp_get_modes(struct drm_connector *connector) +{ + struct psb_intel_output *intel_output = to_psb_intel_output(connector); + struct psb_intel_dp *intel_dp = intel_output->dev_priv; + struct edid *edid = NULL; + int ret = 0; + + + edid = drm_get_edid(&intel_output->base, + &intel_dp->adapter); + if (edid) { + drm_mode_connector_update_edid_property(&intel_output-> + base, edid); + ret = drm_add_edid_modes(&intel_output->base, edid); + kfree(edid); + } + + return ret; +} + +static bool +psb_intel_dp_detect_audio(struct drm_connector *connector) +{ + struct psb_intel_output *output = to_psb_intel_output(connector); + struct psb_intel_dp *intel_dp = output->dev_priv; + struct edid *edid; + bool has_audio = false; + + edid = drm_get_edid(connector, &intel_dp->adapter); + if (edid) { + has_audio = drm_detect_monitor_audio(edid); + + connector->display_info.raw_edid = NULL; + kfree(edid); + } + + return has_audio; +} + +static int +psb_intel_dp_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t val) +{ + struct drm_psb_private *dev_priv = connector->dev->dev_private; + struct psb_intel_output *output = to_psb_intel_output(connector); + struct psb_intel_dp *intel_dp = output->dev_priv; + int ret; + + ret = drm_connector_property_set_value(connector, property, val); + if (ret) + return ret; + + if (property == dev_priv->force_audio_property) { + int i = val; + bool has_audio; + + if (i == intel_dp->force_audio) + return 0; + + intel_dp->force_audio = i; + + if (i == 0) + has_audio = psb_intel_dp_detect_audio(connector); + else + has_audio = i > 0; + + if (has_audio == intel_dp->has_audio) + return 0; + + intel_dp->has_audio = has_audio; + goto done; + } + + if (property == dev_priv->broadcast_rgb_property) { + if (val == !!intel_dp->color_range) + return 0; + + intel_dp->color_range = val ? DP_COLOR_RANGE_16_235 : 0; + goto done; + } + + return -EINVAL; + +done: + if (output->enc.crtc) { + struct drm_crtc *crtc = output->enc.crtc; + drm_crtc_helper_set_mode(crtc, &crtc->mode, + crtc->x, crtc->y, + crtc->fb); + } + + return 0; +} + +static void +psb_intel_dp_destroy (struct drm_connector *connector) +{ + struct psb_intel_output *output = to_psb_intel_output(connector); + struct psb_intel_dp *intel_dp = output->dev_priv; + + i2c_del_adapter(&intel_dp->adapter); + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + kfree(connector); +} + +static void psb_intel_dp_encoder_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_helper_funcs psb_intel_dp_helper_funcs = { + .dpms = psb_intel_dp_dpms, + .mode_fixup = psb_intel_dp_mode_fixup, + .prepare = psb_intel_dp_prepare, + .mode_set = psb_intel_dp_mode_set, + .commit = psb_intel_dp_commit, +}; + +static const struct drm_connector_funcs psb_intel_dp_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = psb_intel_dp_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = psb_intel_dp_set_property, + .destroy = psb_intel_dp_destroy, +}; + +static const struct drm_connector_helper_funcs psb_intel_dp_connector_helper_funcs = { + .get_modes = psb_intel_dp_get_modes, + .mode_valid = psb_intel_dp_mode_valid, + .best_encoder = psb_intel_best_encoder, +}; + +static const struct drm_encoder_funcs psb_intel_dp_enc_funcs = { + .destroy = psb_intel_dp_encoder_destroy, +}; + + +static void +psb_intel_dp_add_properties(struct psb_intel_output *output, struct drm_connector *connector) +{ + psb_intel_attach_force_audio_property(connector); + psb_intel_attach_broadcast_rgb_property(connector); +} + +void +psb_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int output_reg) +{ + struct drm_connector *connector; + struct drm_encoder *encoder; + struct psb_intel_output *psb_intel_output; + struct psb_intel_dp *intel_dp; + const char *name = NULL; + int type; + + psb_intel_output = kzalloc(sizeof(struct psb_intel_output) + + sizeof(struct psb_intel_dp), GFP_KERNEL); + if (!psb_intel_output) + return; + + intel_dp = (struct psb_intel_dp *)(psb_intel_output + 1); + psb_intel_output->mode_dev = mode_dev; + connector = &psb_intel_output->base; + encoder = &psb_intel_output->enc; + psb_intel_output->dev_priv=intel_dp; + intel_dp->output = psb_intel_output; + + intel_dp->output_reg = output_reg; + + type = DRM_MODE_CONNECTOR_DisplayPort; + psb_intel_output->type = INTEL_OUTPUT_DISPLAYPORT; + + drm_connector_init(dev, connector, &psb_intel_dp_connector_funcs, type); + drm_connector_helper_add(connector, &psb_intel_dp_connector_helper_funcs); + + connector->polled = DRM_CONNECTOR_POLL_HPD; + + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + + drm_encoder_init(dev, encoder, &psb_intel_dp_enc_funcs, + DRM_MODE_ENCODER_TMDS); + drm_encoder_helper_add(encoder, &psb_intel_dp_helper_funcs); + + drm_mode_connector_attach_encoder(&psb_intel_output->base, + &psb_intel_output->enc); + + drm_sysfs_connector_add(connector); + + /* Set up the DDC bus. */ + switch (output_reg) { + case DP_B: + name = "DPDDC-B"; + psb_intel_output->ddi_select = (DP_MASK | DDI0_SELECT); + break; + case DP_C: + name = "DPDDC-C"; + psb_intel_output->ddi_select = (DP_MASK | DDI1_SELECT); + break; + } + + psb_intel_dp_i2c_init(psb_intel_output, name); + psb_intel_dp_add_properties(psb_intel_output, connector); + +} diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h index 7b357b0..73214e2 100644 --- a/drivers/gpu/drm/gma500/psb_intel_drv.h +++ b/drivers/gpu/drm/gma500/psb_intel_drv.h @@ -29,10 +29,6 @@ * Display related stuff */
-/* store information about an Ixxx DVO */ -/* The i830->i865 use multiple DVOs with multiple i2cs */ -/* the i915, i945 have a single sDVO i2c bus - which is different */ -#define MAX_OUTPUTS 6 /* maximum connectors per crtcs in the mode set */ #define INTELFB_CONN_LIMIT 4
@@ -69,6 +65,8 @@ #define INTEL_OUTPUT_HDMI 6 #define INTEL_OUTPUT_MIPI 7 #define INTEL_OUTPUT_MIPI2 8 +#define INTEL_OUTPUT_DISPLAYPORT 9 +#define INTEL_OUTPUT_EDP 10
#define INTEL_DVO_CHIP_NONE 0 #define INTEL_DVO_CHIP_LVDS 1 diff --git a/drivers/gpu/drm/gma500/psb_intel_reg.h b/drivers/gpu/drm/gma500/psb_intel_reg.h index 8e8c8ef..d3091fc 100644 --- a/drivers/gpu/drm/gma500/psb_intel_reg.h +++ b/drivers/gpu/drm/gma500/psb_intel_reg.h @@ -1347,5 +1347,160 @@ No status bits are changed. #define LANE_PLL_ENABLE (0x3 << 20) #define LANE_PLL_PIPE(p) (((p) == 0) ? (1 << 21) : (0 << 21))
+#define DP_B 0x64100 +#define DP_C 0x64200 + +#define DP_PORT_EN (1 << 31) +#define DP_PIPEB_SELECT (1 << 30) +#define DP_PIPE_MASK (1 << 30) + +/* Link training mode - select a suitable mode for each stage */ +#define DP_LINK_TRAIN_PAT_1 (0 << 28) +#define DP_LINK_TRAIN_PAT_2 (1 << 28) +#define DP_LINK_TRAIN_PAT_IDLE (2 << 28) +#define DP_LINK_TRAIN_OFF (3 << 28) +#define DP_LINK_TRAIN_MASK (3 << 28) +#define DP_LINK_TRAIN_SHIFT 28 + +/* Signal voltages. These are mostly controlled by the other end */ +#define DP_VOLTAGE_0_4 (0 << 25) +#define DP_VOLTAGE_0_6 (1 << 25) +#define DP_VOLTAGE_0_8 (2 << 25) +#define DP_VOLTAGE_1_2 (3 << 25) +#define DP_VOLTAGE_MASK (7 << 25) +#define DP_VOLTAGE_SHIFT 25 + +/* Signal pre-emphasis levels, like voltages, the other end tells us what + * they want + */ +#define DP_PRE_EMPHASIS_0 (0 << 22) +#define DP_PRE_EMPHASIS_3_5 (1 << 22) +#define DP_PRE_EMPHASIS_6 (2 << 22) +#define DP_PRE_EMPHASIS_9_5 (3 << 22) +#define DP_PRE_EMPHASIS_MASK (7 << 22) +#define DP_PRE_EMPHASIS_SHIFT 22 + +/* How many wires to use. I guess 3 was too hard */ +#define DP_PORT_WIDTH_1 (0 << 19) +#define DP_PORT_WIDTH_2 (1 << 19) +#define DP_PORT_WIDTH_4 (3 << 19) +#define DP_PORT_WIDTH_MASK (7 << 19) + +/* Mystic DPCD version 1.1 special mode */ +#define DP_ENHANCED_FRAMING (1 << 18) + +/** locked once port is enabled */ +#define DP_PORT_REVERSAL (1 << 15) + +/** sends the clock on lane 15 of the PEG for debug */ +#define DP_CLOCK_OUTPUT_ENABLE (1 << 13) + +#define DP_SCRAMBLING_DISABLE (1 << 12) +#define DP_SCRAMBLING_DISABLE_IRONLAKE (1 << 7) + +/** limit RGB values to avoid confusing TVs */ +#define DP_COLOR_RANGE_16_235 (1 << 8) + +/** Turn on the audio link */ +#define DP_AUDIO_OUTPUT_ENABLE (1 << 6) + +/** vs and hs sync polarity */ +#define DP_SYNC_VS_HIGH (1 << 4) +#define DP_SYNC_HS_HIGH (1 << 3) + +/** A fantasy */ +#define DP_DETECTED (1 << 2) + +/** The aux channel provides a way to talk to the + * signal sink for DDC etc. Max packet size supported + * is 20 bytes in each direction, hence the 5 fixed + * data registers + */ +#define DPB_AUX_CH_CTL 0x64110 +#define DPB_AUX_CH_DATA1 0x64114 +#define DPB_AUX_CH_DATA2 0x64118 +#define DPB_AUX_CH_DATA3 0x6411c +#define DPB_AUX_CH_DATA4 0x64120 +#define DPB_AUX_CH_DATA5 0x64124 + +#define DPC_AUX_CH_CTL 0x64210 +#define DPC_AUX_CH_DATA1 0x64214 +#define DPC_AUX_CH_DATA2 0x64218 +#define DPC_AUX_CH_DATA3 0x6421c +#define DPC_AUX_CH_DATA4 0x64220 +#define DPC_AUX_CH_DATA5 0x64224 + +#define DP_AUX_CH_CTL_SEND_BUSY (1 << 31) +#define DP_AUX_CH_CTL_DONE (1 << 30) +#define DP_AUX_CH_CTL_INTERRUPT (1 << 29) +#define DP_AUX_CH_CTL_TIME_OUT_ERROR (1 << 28) +#define DP_AUX_CH_CTL_TIME_OUT_400us (0 << 26) +#define DP_AUX_CH_CTL_TIME_OUT_600us (1 << 26) +#define DP_AUX_CH_CTL_TIME_OUT_800us (2 << 26) +#define DP_AUX_CH_CTL_TIME_OUT_1600us (3 << 26) +#define DP_AUX_CH_CTL_TIME_OUT_MASK (3 << 26) +#define DP_AUX_CH_CTL_RECEIVE_ERROR (1 << 25) +#define DP_AUX_CH_CTL_MESSAGE_SIZE_MASK (0x1f << 20) +#define DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT 20 +#define DP_AUX_CH_CTL_PRECHARGE_2US_MASK (0xf << 16) +#define DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT 16 +#define DP_AUX_CH_CTL_AUX_AKSV_SELECT (1 << 15) +#define DP_AUX_CH_CTL_MANCHESTER_TEST (1 << 14) +#define DP_AUX_CH_CTL_SYNC_TEST (1 << 13) +#define DP_AUX_CH_CTL_DEGLITCH_TEST (1 << 12) +#define DP_AUX_CH_CTL_PRECHARGE_TEST (1 << 11) +#define DP_AUX_CH_CTL_BIT_CLOCK_2X_MASK (0x7ff) +#define DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT 0 + +/* + * Computing GMCH M and N values for the Display Port link + * + * GMCH M/N = dot clock * bytes per pixel / ls_clk * # of lanes + * + * ls_clk (we assume) is the DP link clock (1.62 or 2.7 GHz) + * + * The GMCH value is used internally + * + * bytes_per_pixel is the number of bytes coming out of the plane, + * which is after the LUTs, so we want the bytes for our color format. + * For our current usage, this is always 3, one byte for R, G and B. + */ + +#define _PIPEA_GMCH_DATA_M 0x70050 +#define _PIPEB_GMCH_DATA_M 0x71050 + +/* Transfer unit size for display port - 1, default is 0x3f (for TU size 64) */ +#define PIPE_GMCH_DATA_M_TU_SIZE_MASK (0x3f << 25) +#define PIPE_GMCH_DATA_M_TU_SIZE_SHIFT 25 + +#define PIPE_GMCH_DATA_M_MASK (0xffffff) + +#define _PIPEA_GMCH_DATA_N 0x70054 +#define _PIPEB_GMCH_DATA_N 0x71054 +#define PIPE_GMCH_DATA_N_MASK (0xffffff) + +/* + * Computing Link M and N values for the Display Port link + * + * Link M / N = pixel_clock / ls_clk + * + * (the DP spec calls pixel_clock the 'strm_clk') + * + * The Link value is transmitted in the Main Stream + * Attributes and VB-ID. + */ + +#define _PIPEA_DP_LINK_M 0x70060 +#define _PIPEB_DP_LINK_M 0x71060 +#define PIPEA_DP_LINK_M_MASK (0xffffff) + +#define _PIPEA_DP_LINK_N 0x70064 +#define _PIPEB_DP_LINK_N 0x71064 +#define PIPEA_DP_LINK_N_MASK (0xffffff) + +#define PIPE_GMCH_DATA_M(pipe) _PIPE(pipe, _PIPEA_GMCH_DATA_M, _PIPEB_GMCH_DATA_M) +#define PIPE_GMCH_DATA_N(pipe) _PIPE(pipe, _PIPEA_GMCH_DATA_N, _PIPEB_GMCH_DATA_N) +#define PIPE_DP_LINK_M(pipe) _PIPE(pipe, _PIPEA_DP_LINK_M, _PIPEB_DP_LINK_M) +#define PIPE_DP_LINK_N(pipe) _PIPE(pipe, _PIPEA_DP_LINK_N, _PIPEB_DP_LINK_N)
#endif
From: Alan Cox alan@linux.intel.com
Based on bits from Yakui yakui.zhao@intel.com
We can import various little bits of code before we plumb it all in and hopefully this way catch any regressions more easily.
Signed-off-by: Alan Cox alan@linux.intel.com ---
drivers/gpu/drm/gma500/cdv_device.c | 59 +++++++++++++++++ drivers/gpu/drm/gma500/cdv_intel_display.c | 99 +++++++++++++++++++++++++++- drivers/gpu/drm/gma500/framebuffer.c | 4 + drivers/gpu/drm/gma500/psb_intel_drv.h | 2 - 4 files changed, 161 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/gma500/cdv_device.c b/drivers/gpu/drm/gma500/cdv_device.c index b7e7b49..e2fff24 100644 --- a/drivers/gpu/drm/gma500/cdv_device.c +++ b/drivers/gpu/drm/gma500/cdv_device.c @@ -488,6 +488,65 @@ static void cdv_hotplug_enable(struct drm_device *dev, bool on) } }
+static const char *force_audio_names[] = { + "off", + "auto", + "on", +}; + +void cdv_intel_attach_force_audio_property(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct drm_property *prop; + int i; + + prop = dev_priv->force_audio_property; + if (prop == NULL) { + prop = drm_property_create(dev, DRM_MODE_PROP_ENUM, + "audio", + ARRAY_SIZE(force_audio_names)); + if (prop == NULL) + return; + + for (i = 0; i < ARRAY_SIZE(force_audio_names); i++) + drm_property_add_enum(prop, i, i-1, force_audio_names[i]); + + dev_priv->force_audio_property = prop; + } + drm_connector_attach_property(connector, prop, 0); +} + + +static const char *broadcast_rgb_names[] = { + "Full", + "Limited 16:235", +}; + +void cdv_intel_attach_broadcast_rgb_property(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct drm_property *prop; + int i; + + prop = dev_priv->broadcast_rgb_property; + if (prop == NULL) { + prop = drm_property_create(dev, DRM_MODE_PROP_ENUM, + "Broadcast RGB", + ARRAY_SIZE(broadcast_rgb_names)); + if (prop == NULL) + return; + + for (i = 0; i < ARRAY_SIZE(broadcast_rgb_names); i++) + drm_property_add_enum(prop, i, i, broadcast_rgb_names[i]); + + dev_priv->broadcast_rgb_property = prop; + } + + drm_connector_attach_property(connector, prop, 0); +} + /* Cedarview */ static const struct psb_offset cdv_regmap[2] = { { diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c index f16169c..2e0231e 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_display.c +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c @@ -64,11 +64,16 @@ struct cdv_intel_limit_t { static bool cdv_intel_find_best_PLL(const struct cdv_intel_limit_t *limit, struct drm_crtc *crtc, int target, int refclk, struct cdv_intel_clock_t *best_clock); +static bool cdv_intel_find_dp_pll(const struct cdv_intel_limit_t *limit, struct drm_crtc *crtc, int target, + int refclk, + struct cdv_intel_clock_t *best_clock);
#define CDV_LIMIT_SINGLE_LVDS_96 0 #define CDV_LIMIT_SINGLE_LVDS_100 1 #define CDV_LIMIT_DAC_HDMI_27 2 #define CDV_LIMIT_DAC_HDMI_96 3 +#define CDV_LIMIT_DP_27 4 +#define CDV_LIMIT_DP_100 5
static const struct cdv_intel_limit_t cdv_intel_limits[] = { { /* CDV_SIGNLE_LVDS_96MHz */ @@ -123,6 +128,30 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = { .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 5}, .find_pll = cdv_intel_find_best_PLL, }, + { /* CDV_DP_27MHz */ + .dot = {.min = 160000, .max = 272000}, + .vco = {.min = 1809000, .max = 3564000}, + .n = {.min = 1, .max = 1}, + .m = {.min = 67, .max = 132}, + .m1 = {.min = 0, .max = 0}, + .m2 = {.min = 65, .max = 130}, + .p = {.min = 5, .max = 90}, + .p1 = {.min = 1, .max = 9}, + .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 10}, + .find_pll = cdv_intel_find_dp_pll, + }, + { /* CDV_DP_100MHz */ + .dot = {.min = 160000, .max = 272000}, + .vco = {.min = 1800000, .max = 3600000}, + .n = {.min = 2, .max = 6}, + .m = {.min = 60, .max = 164}, + .m1 = {.min = 0, .max = 0}, + .m2 = {.min = 58, .max = 162}, + .p = {.min = 5, .max = 100}, + .p1 = {.min = 1, .max = 10}, + .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 10}, + .find_pll = cdv_intel_find_dp_pll, + } };
#define _wait_for(COND, MS, W) ({ \ @@ -269,7 +298,7 @@ cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc, ref_value &= ~(REF_CLK_MASK);
/* use DPLL_A for pipeB on CRT/HDMI */ - if (pipe == 1 && !is_lvds) { + if (pipe == 1 && !is_lvds && !(ddi_select & DP_MASK)) { DRM_DEBUG_KMS("use DPLLA for pipe B\n"); ref_value |= REF_CLK_DPLLA; } else { @@ -409,6 +438,11 @@ static const struct cdv_intel_limit_t *cdv_intel_limit(struct drm_crtc *crtc, limit = &cdv_intel_limits[CDV_LIMIT_SINGLE_LVDS_96]; else limit = &cdv_intel_limits[CDV_LIMIT_SINGLE_LVDS_100]; + } else if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { + if (refclk == 27000) + limit = &cdv_intel_limits[CDV_LIMIT_DP_27]; + else + limit = &cdv_intel_limits[CDV_LIMIT_DP_100]; } else { if (refclk == 27000) limit = &cdv_intel_limits[CDV_LIMIT_DAC_HDMI_27]; @@ -510,6 +544,49 @@ static bool cdv_intel_find_best_PLL(const struct cdv_intel_limit_t *limit, return err != target; }
+static bool cdv_intel_find_dp_pll(const struct cdv_intel_limit_t *limit, struct drm_crtc *crtc, int target, + int refclk, + struct cdv_intel_clock_t *best_clock) +{ + struct cdv_intel_clock_t clock; + if (refclk == 27000) { + if (target < 200000) { + clock.p1 = 2; + clock.p2 = 10; + clock.n = 1; + clock.m1 = 0; + clock.m2 = 118; + } else { + clock.p1 = 1; + clock.p2 = 10; + clock.n = 1; + clock.m1 = 0; + clock.m2 = 98; + } + } else if (refclk == 100000) { + if (target < 200000) { + clock.p1 = 2; + clock.p2 = 10; + clock.n = 5; + clock.m1 = 0; + clock.m2 = 160; + } else { + clock.p1 = 1; + clock.p2 = 10; + clock.n = 5; + clock.m1 = 0; + clock.m2 = 133; + } + } else + return false; + clock.m = clock.m2 + 2; + clock.p = clock.p1 * clock.p2; + clock.vco = (refclk * clock.m) / clock.n; + clock.dot = clock.vco / clock.p; + memcpy(best_clock, &clock, sizeof(struct cdv_intel_clock_t)); + return true; +} + static int cdv_intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { @@ -963,7 +1040,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, u32 dpll = 0, dspcntr, pipeconf; bool ok; bool is_crt = false, is_lvds = false, is_tv = false; - bool is_hdmi = false; + bool is_hdmi = false, is_dp = false; struct drm_mode_config *mode_config = &dev->mode_config; struct drm_connector *connector; const struct cdv_intel_limit_t *limit; @@ -991,6 +1068,9 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, case INTEL_OUTPUT_HDMI: is_hdmi = true; break; + case INTEL_OUTPUT_DISPLAYPORT: + is_dp = true; + break; default: DRM_ERROR("invalid output type.\n"); return 0; @@ -1003,6 +1083,12 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, else /* high-end sku, 27/100 mhz */ refclk = 27000; + if (is_dp) { + if (pipe == 0) + refclk = 27000; + else + refclk = 100000; + }
if (is_lvds && dev_priv->lvds_use_ssc) { refclk = dev_priv->lvds_ssc_freq * 1000; @@ -1028,6 +1114,15 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, } /* dpll |= PLL_REF_INPUT_DREFCLK; */
+ if (is_dp) { +/*FIXME cdv_intel_dp_set_m_n(crtc, mode, adjusted_mode); */ + } else { + REG_WRITE(PIPE_GMCH_DATA_M(pipe), 0); + REG_WRITE(PIPE_GMCH_DATA_N(pipe), 0); + REG_WRITE(PIPE_DP_LINK_M(pipe), 0); + REG_WRITE(PIPE_DP_LINK_N(pipe), 0); + } + dpll |= DPLL_SYNCLOCK_ENABLE; /* if (is_lvds) dpll |= DPLLB_MODE_LVDS; diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 5732b57..2de6b1f 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -764,6 +764,10 @@ static void psb_setup_outputs(struct drm_device *dev) crtc_mask = dev_priv->ops->hdmi_mask; clone_mask = (1 << INTEL_OUTPUT_HDMI); break; + case INTEL_OUTPUT_DISPLAYPORT: + crtc_mask = (1 << 0) | (1 << 1); + clone_mask = (1 << INTEL_OUTPUT_DISPLAYPORT); + break; } encoder->possible_crtcs = crtc_mask; encoder->possible_clones = diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h index 73214e2..c574c01 100644 --- a/drivers/gpu/drm/gma500/psb_intel_drv.h +++ b/drivers/gpu/drm/gma500/psb_intel_drv.h @@ -134,7 +134,7 @@ struct psb_intel_encoder { u32 ddi_select; /* Channel info */ #define DDI0_SELECT 0x01 #define DDI1_SELECT 0x02 -#define DP_MASK 0x8000; +#define DP_MASK 0x8000 #define DDI_MASK 0x03 void *dev_priv; /* For sdvo_priv, lvds_priv, etc... */
From: Alan Cox alan@linux.intel.com
This is mostly just aligning bits of behaviour
Signed-off-by: Alan Cox alan@linux.intel.com ---
drivers/gpu/drm/gma500/Makefile | 3 drivers/gpu/drm/gma500/cdv_intel_display.c | 6 drivers/gpu/drm/gma500/cdv_intel_dp.c | 480 ++++++++++++++-------------- drivers/gpu/drm/gma500/psb_intel_drv.h | 16 + 4 files changed, 260 insertions(+), 245 deletions(-)
diff --git a/drivers/gpu/drm/gma500/Makefile b/drivers/gpu/drm/gma500/Makefile index 81ff7e2..7a2d40a 100644 --- a/drivers/gpu/drm/gma500/Makefile +++ b/drivers/gpu/drm/gma500/Makefile @@ -30,7 +30,8 @@ gma500_gfx-$(CONFIG_DRM_GMA3600) += cdv_device.o \ cdv_intel_crt.o \ cdv_intel_display.o \ cdv_intel_hdmi.o \ - cdv_intel_lvds.o + cdv_intel_lvds.o \ + cdv_intel_dp.o
gma500_gfx-$(CONFIG_DRM_GMA600) += oaktrail_device.o \ oaktrail_crtc.o \ diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c index 2e0231e..3f11060 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_display.c +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c @@ -171,7 +171,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = { #define wait_for(COND, MS) _wait_for(COND, MS, 1)
-static int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val) +int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val) { int ret;
@@ -198,7 +198,7 @@ static int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val) return 0; }
-static int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val) +int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val) { int ret; static bool dpio_debug = true; @@ -240,7 +240,7 @@ static int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val) /* Reset the DPIO configuration register. The BIOS does this at every * mode set. */ -static void cdv_sb_reset(struct drm_device *dev) +void cdv_sb_reset(struct drm_device *dev) {
REG_WRITE(DPIO_CFG, 0); diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index c6d545d..b29b602 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -33,7 +33,6 @@ #include "drm_crtc_helper.h" #include "psb_drv.h" #include "psb_intel_drv.h" -#include "psb_drm.h" #include "psb_intel_reg.h" #include "drm_dp_helper.h"
@@ -45,7 +44,7 @@
#define CDV_FAST_LINK_TRAIN 1
-struct psb_intel_dp { +struct cdv_intel_dp { uint32_t output_reg; uint32_t DP; uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE]; @@ -55,7 +54,7 @@ struct psb_intel_dp { uint8_t link_bw; uint8_t lane_count; uint8_t dpcd[4]; - struct psb_intel_output *output; + struct psb_intel_encoder *encoder; struct i2c_adapter adapter; struct i2c_algo_dp_aux_data algo; uint8_t train_set[4]; @@ -94,20 +93,20 @@ static uint32_t dp_vswing_premph_table[] = { * If a CPU or PCH DP output is attached to an eDP panel, this function * will return true, and false otherwise. */ -static bool is_edp(struct psb_intel_output *output) +static bool is_edp(struct psb_intel_encoder *encoder) { - return output->type == INTEL_OUTPUT_EDP; + return encoder->type == INTEL_OUTPUT_EDP; }
-static void psb_intel_dp_start_link_train(struct psb_intel_output *output); -static void psb_intel_dp_complete_link_train(struct psb_intel_output *output); -static void psb_intel_dp_link_down(struct psb_intel_output *output); +static void cdv_intel_dp_start_link_train(struct psb_intel_encoder *encoder); +static void cdv_intel_dp_complete_link_train(struct psb_intel_encoder *encoder); +static void cdv_intel_dp_link_down(struct psb_intel_encoder *encoder);
static int -psb_intel_dp_max_lane_count(struct psb_intel_output *output) +cdv_intel_dp_max_lane_count(struct psb_intel_encoder *encoder) { - struct psb_intel_dp *intel_dp = output->dev_priv; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; int max_lane_count = 4;
if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) { @@ -123,9 +122,9 @@ psb_intel_dp_max_lane_count(struct psb_intel_output *output) }
static int -psb_intel_dp_max_link_bw(struct psb_intel_output *output) +cdv_intel_dp_max_link_bw(struct psb_intel_encoder *encoder) { - struct psb_intel_dp *intel_dp = output->dev_priv; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE];
switch (max_link_bw) { @@ -140,7 +139,7 @@ psb_intel_dp_max_link_bw(struct psb_intel_output *output) }
static int -psb_intel_dp_link_clock(uint8_t link_bw) +cdv_intel_dp_link_clock(uint8_t link_bw) { if (link_bw == DP_LINK_BW_2_7) return 270000; @@ -149,28 +148,28 @@ psb_intel_dp_link_clock(uint8_t link_bw) }
static int -psb_intel_dp_link_required(int pixel_clock, int bpp) +cdv_intel_dp_link_required(int pixel_clock, int bpp) { return (pixel_clock * bpp + 7) / 8; }
static int -psb_intel_dp_max_data_rate(int max_link_clock, int max_lanes) +cdv_intel_dp_max_data_rate(int max_link_clock, int max_lanes) { return (max_link_clock * max_lanes * 19) / 20; }
static int -psb_intel_dp_mode_valid(struct drm_connector *connector, +cdv_intel_dp_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - struct psb_intel_output *output = to_psb_intel_output(connector); + struct psb_intel_encoder *encoder = psb_intel_attached_encoder(connector); struct drm_device *dev = connector->dev; struct drm_psb_private *dev_priv = dev->dev_private; - int max_link_clock = psb_intel_dp_link_clock(psb_intel_dp_max_link_bw(output)); - int max_lanes = psb_intel_dp_max_lane_count(output); + int max_link_clock = cdv_intel_dp_link_clock(cdv_intel_dp_max_link_bw(encoder)); + int max_lanes = cdv_intel_dp_max_lane_count(encoder);
- if (is_edp(output) && dev_priv->panel_fixed_mode) { + if (is_edp(encoder) && dev_priv->panel_fixed_mode) { if (mode->hdisplay > dev_priv->panel_fixed_mode->hdisplay) return MODE_PANEL;
@@ -180,9 +179,9 @@ psb_intel_dp_mode_valid(struct drm_connector *connector,
/* only refuse the mode on non eDP since we have seen some weird eDP panels which are outside spec tolerances but somehow work by magic */ - if (!is_edp(output) && - (psb_intel_dp_link_required(mode->clock, 24) - > psb_intel_dp_max_data_rate(max_link_clock, max_lanes))) + if (!is_edp(encoder) && + (cdv_intel_dp_link_required(mode->clock, 24) + > cdv_intel_dp_max_data_rate(max_link_clock, max_lanes))) return MODE_CLOCK_HIGH;
if (mode->clock < 10000) @@ -215,13 +214,13 @@ unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes) }
static int -psb_intel_dp_aux_ch(struct psb_intel_output *output, +cdv_intel_dp_aux_ch(struct psb_intel_encoder *encoder, uint8_t *send, int send_bytes, uint8_t *recv, int recv_size) { - struct psb_intel_dp *intel_dp = output->dev_priv; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; uint32_t output_reg = intel_dp->output_reg; - struct drm_device *dev = output->base.dev; + struct drm_device *dev = encoder->base.dev; uint32_t ch_ctl = output_reg + 0x10; uint32_t ch_data = ch_ctl + 4; int i; @@ -315,7 +314,7 @@ psb_intel_dp_aux_ch(struct psb_intel_output *output,
/* Write data to the aux channel in native mode */ static int -psb_intel_dp_aux_native_write(struct psb_intel_output *output, +cdv_intel_dp_aux_native_write(struct psb_intel_encoder *encoder, uint16_t address, uint8_t *send, int send_bytes) { int ret; @@ -332,7 +331,7 @@ psb_intel_dp_aux_native_write(struct psb_intel_output *output, memcpy(&msg[4], send, send_bytes); msg_bytes = send_bytes + 4; for (;;) { - ret = psb_intel_dp_aux_ch(output, msg, msg_bytes, &ack, 1); + ret = cdv_intel_dp_aux_ch(encoder, msg, msg_bytes, &ack, 1); if (ret < 0) return ret; if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK) @@ -347,15 +346,15 @@ psb_intel_dp_aux_native_write(struct psb_intel_output *output,
/* Write a single byte to the aux channel in native mode */ static int -psb_intel_dp_aux_native_write_1(struct psb_intel_output *output, +cdv_intel_dp_aux_native_write_1(struct psb_intel_encoder *encoder, uint16_t address, uint8_t byte) { - return psb_intel_dp_aux_native_write(output, address, &byte, 1); + return cdv_intel_dp_aux_native_write(encoder, address, &byte, 1); }
/* read bytes from a native aux channel */ static int -psb_intel_dp_aux_native_read(struct psb_intel_output *output, +cdv_intel_dp_aux_native_read(struct psb_intel_encoder *encoder, uint16_t address, uint8_t *recv, int recv_bytes) { uint8_t msg[4]; @@ -374,7 +373,7 @@ psb_intel_dp_aux_native_read(struct psb_intel_output *output, reply_bytes = recv_bytes + 1;
for (;;) { - ret = psb_intel_dp_aux_ch(output, msg, msg_bytes, + ret = cdv_intel_dp_aux_ch(encoder, msg, msg_bytes, reply, reply_bytes); if (ret == 0) return -EPROTO; @@ -393,14 +392,14 @@ psb_intel_dp_aux_native_read(struct psb_intel_output *output, }
static int -psb_intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, +cdv_intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, uint8_t write_byte, uint8_t *read_byte) { struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; - struct psb_intel_dp *intel_dp = container_of(adapter, - struct psb_intel_dp, + struct cdv_intel_dp *intel_dp = container_of(adapter, + struct cdv_intel_dp, adapter); - struct psb_intel_output *output = intel_dp->output; + struct psb_intel_encoder *encoder = intel_dp->encoder; uint16_t address = algo_data->address; uint8_t msg[5]; uint8_t reply[2]; @@ -440,7 +439,7 @@ psb_intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, }
for (retry = 0; retry < 5; retry++) { - ret = psb_intel_dp_aux_ch(output, + ret = cdv_intel_dp_aux_ch(encoder, msg, msg_bytes, reply, reply_bytes); if (ret < 0) { @@ -490,13 +489,13 @@ psb_intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, }
static int -psb_intel_dp_i2c_init(struct psb_intel_output *output, const char *name) +cdv_intel_dp_i2c_init(struct psb_intel_connector *connector, struct psb_intel_encoder *encoder, const char *name) { - struct psb_intel_dp *intel_dp = output->dev_priv; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; DRM_DEBUG_KMS("i2c_init %s\n", name); intel_dp->algo.running = false; intel_dp->algo.address = 0; - intel_dp->algo.aux_ch = psb_intel_dp_i2c_aux_ch; + intel_dp->algo.aux_ch = cdv_intel_dp_i2c_aux_ch;
memset(&intel_dp->adapter, '\0', sizeof (intel_dp->adapter)); intel_dp->adapter.owner = THIS_MODULE; @@ -504,32 +503,32 @@ psb_intel_dp_i2c_init(struct psb_intel_output *output, const char *name) strncpy (intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1); intel_dp->adapter.name[sizeof(intel_dp->adapter.name) - 1] = '\0'; intel_dp->adapter.algo_data = &intel_dp->algo; - intel_dp->adapter.dev.parent = &output->base.kdev; + intel_dp->adapter.dev.parent = &connector->base.kdev;
return i2c_dp_aux_add_bus(&intel_dp->adapter); }
static bool -psb_intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, +cdv_intel_dp_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - struct psb_intel_output *output = enc_to_psb_intel_output(encoder); - struct psb_intel_dp *intel_dp = output->dev_priv; + struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder); + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; int lane_count, clock; - int max_lane_count = psb_intel_dp_max_lane_count(output); - int max_clock = psb_intel_dp_max_link_bw(output) == DP_LINK_BW_2_7 ? 1 : 0; + int max_lane_count = cdv_intel_dp_max_lane_count(intel_encoder); + int max_clock = cdv_intel_dp_max_link_bw(intel_encoder) == 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 = max_clock; clock >= 0; clock--) { - int link_avail = psb_intel_dp_max_data_rate(psb_intel_dp_link_clock(bws[clock]), lane_count); + int link_avail = cdv_intel_dp_max_data_rate(cdv_intel_dp_link_clock(bws[clock]), lane_count);
- if (psb_intel_dp_link_required(mode->clock, 24) + if (cdv_intel_dp_link_required(mode->clock, 24) <= link_avail) { intel_dp->link_bw = bws[clock]; intel_dp->lane_count = lane_count; - adjusted_mode->clock = psb_intel_dp_link_clock(intel_dp->link_bw); + adjusted_mode->clock = cdv_intel_dp_link_clock(intel_dp->link_bw); DRM_DEBUG_KMS("Display port link bw %02x lane " "count %d clock %d\n", intel_dp->link_bw, intel_dp->lane_count, @@ -542,7 +541,7 @@ psb_intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mo return false; }
-struct psb_intel_dp_m_n { +struct cdv_intel_dp_m_n { uint32_t tu; uint32_t gmch_m; uint32_t gmch_n; @@ -567,11 +566,11 @@ psb_intel_reduce_ratio(uint32_t *num, uint32_t *den) }
static void -psb_intel_dp_compute_m_n(int bpp, +cdv_intel_dp_compute_m_n(int bpp, int nlanes, int pixel_clock, int link_clock, - struct psb_intel_dp_m_n *m_n) + struct cdv_intel_dp_m_n *m_n) { m_n->tu = 64; m_n->gmch_m = (pixel_clock * bpp + 7) >> 3; @@ -583,7 +582,7 @@ psb_intel_dp_compute_m_n(int bpp, }
void -psb_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, +cdv_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = crtc->dev; @@ -591,25 +590,25 @@ psb_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_encoder *encoder; struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc); int lane_count = 4, bpp = 24; - struct psb_intel_dp_m_n m_n; + struct cdv_intel_dp_m_n m_n; int pipe = intel_crtc->pipe;
/* * Find the lane count in the intel_encoder private */ list_for_each_entry(encoder, &mode_config->encoder_list, head) { - struct psb_intel_output *intel_output; - struct psb_intel_dp *intel_dp; + struct psb_intel_encoder *intel_encoder; + struct cdv_intel_dp *intel_dp;
if (encoder->crtc != crtc) continue;
- intel_output = enc_to_psb_intel_output(encoder); - intel_dp = intel_output->dev_priv; - if (intel_output->type == INTEL_OUTPUT_DISPLAYPORT) { + intel_encoder = to_psb_intel_encoder(encoder); + intel_dp = intel_encoder->dev_priv; + if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT) { lane_count = intel_dp->lane_count; break; - } else if (is_edp(intel_output)) { + } else if (is_edp(intel_encoder)) { lane_count = intel_dp->lane_count; break; } @@ -620,7 +619,7 @@ psb_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, * the number of bytes_per_pixel post-LUT, which we always * set up for 8-bits of R/G/B, or 3 bytes total. */ - psb_intel_dp_compute_m_n(bpp, lane_count, + cdv_intel_dp_compute_m_n(bpp, lane_count, mode->clock, adjusted_mode->clock, &m_n);
{ @@ -634,13 +633,13 @@ psb_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, }
static void -psb_intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, +cdv_intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - struct psb_intel_output *intel_output = enc_to_psb_intel_output(encoder); + struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder); struct drm_crtc *crtc = encoder->crtc; struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc); - struct psb_intel_dp *intel_dp = intel_output->dev_priv; + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv;
intel_dp->DP = DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; @@ -689,9 +688,9 @@ psb_intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode
/* If the sink supports it, try to set the power state appropriately */ -static void psb_intel_dp_sink_dpms(struct psb_intel_output *output, int mode) +static void cdv_intel_dp_sink_dpms(struct psb_intel_encoder *encoder, int mode) { - struct psb_intel_dp *intel_dp = output->dev_priv; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; int ret, i;
/* Should have a valid DPCD by this point */ @@ -699,7 +698,7 @@ static void psb_intel_dp_sink_dpms(struct psb_intel_output *output, int mode) return;
if (mode != DRM_MODE_DPMS_ON) { - ret = psb_intel_dp_aux_native_write_1(output, DP_SET_POWER, + ret = cdv_intel_dp_aux_native_write_1(encoder, DP_SET_POWER, DP_SET_POWER_D3); if (ret != 1) DRM_DEBUG_DRIVER("failed to write sink power state\n"); @@ -709,7 +708,7 @@ static void psb_intel_dp_sink_dpms(struct psb_intel_output *output, int mode) * time to wake up. */ for (i = 0; i < 3; i++) { - ret = psb_intel_dp_aux_native_write_1(output, + ret = cdv_intel_dp_aux_native_write_1(encoder, DP_SET_POWER, DP_SET_POWER_D0); if (ret == 1) @@ -719,42 +718,39 @@ static void psb_intel_dp_sink_dpms(struct psb_intel_output *output, int mode) } }
-static void psb_intel_dp_prepare(struct drm_encoder *encoder) +static void cdv_intel_dp_prepare(struct drm_encoder *encoder) { - struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder);
/* Wake up the sink first */ - psb_intel_dp_sink_dpms(output, DRM_MODE_DPMS_ON); - - psb_intel_dp_link_down(output); + cdv_intel_dp_sink_dpms(intel_encoder, DRM_MODE_DPMS_ON); + cdv_intel_dp_link_down(intel_encoder); }
-static void psb_intel_dp_commit(struct drm_encoder *encoder) +static void cdv_intel_dp_commit(struct drm_encoder *encoder) { - struct psb_intel_output *output = enc_to_psb_intel_output(encoder); - - psb_intel_dp_start_link_train(output); - - psb_intel_dp_complete_link_train(output); + struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder);
+ cdv_intel_dp_start_link_train(intel_encoder); + cdv_intel_dp_complete_link_train(intel_encoder); }
static void -psb_intel_dp_dpms(struct drm_encoder *encoder, int mode) +cdv_intel_dp_dpms(struct drm_encoder *encoder, int mode) { - struct psb_intel_output *intel_output = enc_to_psb_intel_output(encoder); - struct psb_intel_dp *intel_dp = intel_output->dev_priv; + struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder); + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; struct drm_device *dev = encoder->dev; uint32_t dp_reg = REG_READ(intel_dp->output_reg);
if (mode != DRM_MODE_DPMS_ON) { - psb_intel_dp_sink_dpms(intel_output, mode); - psb_intel_dp_link_down(intel_output); + cdv_intel_dp_sink_dpms(intel_encoder, mode); + cdv_intel_dp_link_down(intel_encoder); } else { - psb_intel_dp_sink_dpms(intel_output, mode); + cdv_intel_dp_sink_dpms(intel_encoder, mode); if (!(dp_reg & DP_PORT_EN)) { - psb_intel_dp_start_link_train(intel_output); - psb_intel_dp_complete_link_train(intel_output); + cdv_intel_dp_start_link_train(intel_encoder); + cdv_intel_dp_complete_link_train(intel_encoder); } } } @@ -764,7 +760,7 @@ psb_intel_dp_dpms(struct drm_encoder *encoder, int mode) * cases where the sink may still be asleep. */ static bool -psb_intel_dp_aux_native_read_retry(struct psb_intel_output *output, uint16_t address, +cdv_intel_dp_aux_native_read_retry(struct psb_intel_encoder *encoder, uint16_t address, uint8_t *recv, int recv_bytes) { int ret, i; @@ -774,7 +770,7 @@ psb_intel_dp_aux_native_read_retry(struct psb_intel_output *output, uint16_t add * but we're also supposed to retry 3 times per the spec. */ for (i = 0; i < 3; i++) { - ret = psb_intel_dp_aux_native_read(output, address, recv, + ret = cdv_intel_dp_aux_native_read(encoder, address, recv, recv_bytes); if (ret == recv_bytes) return true; @@ -789,44 +785,44 @@ psb_intel_dp_aux_native_read_retry(struct psb_intel_output *output, uint16_t add * link status information */ static bool -psb_intel_dp_get_link_status(struct psb_intel_output *output) +cdv_intel_dp_get_link_status(struct psb_intel_encoder *encoder) { - struct psb_intel_dp *intel_dp = output->dev_priv; - return psb_intel_dp_aux_native_read_retry(output, + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + return cdv_intel_dp_aux_native_read_retry(encoder, DP_LANE0_1_STATUS, intel_dp->link_status, DP_LINK_STATUS_SIZE); }
static uint8_t -psb_intel_dp_link_status(uint8_t link_status[DP_LINK_STATUS_SIZE], +cdv_intel_dp_link_status(uint8_t link_status[DP_LINK_STATUS_SIZE], int r) { return link_status[r - DP_LANE0_1_STATUS]; }
static uint8_t -psb_intel_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE], +cdv_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 = psb_intel_dp_link_status(link_status, i); + uint8_t l = cdv_intel_dp_link_status(link_status, i);
return ((l >> s) & 3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; }
static uint8_t -psb_intel_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_SIZE], +cdv_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 = psb_intel_dp_link_status(link_status, i); + uint8_t l = cdv_intel_dp_link_status(link_status, i);
return ((l >> s) & 3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; } @@ -847,7 +843,7 @@ static char *link_train_names[] = { #define CDV_DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_1200 /* static uint8_t -psb_intel_dp_pre_emphasis_max(uint8_t voltage_swing) +cdv_intel_dp_pre_emphasis_max(uint8_t voltage_swing) { switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { case DP_TRAIN_VOLTAGE_SWING_400: @@ -863,16 +859,16 @@ psb_intel_dp_pre_emphasis_max(uint8_t voltage_swing) } */ static void -psb_intel_get_adjust_train(struct psb_intel_output *output) +cdv_intel_get_adjust_train(struct psb_intel_encoder *encoder) { - struct psb_intel_dp *intel_dp = output->dev_priv; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; uint8_t v = 0; uint8_t p = 0; int lane;
for (lane = 0; lane < intel_dp->lane_count; lane++) { - uint8_t this_v = psb_intel_get_adjust_request_voltage(intel_dp->link_status, lane); - uint8_t this_p = psb_intel_get_adjust_request_pre_emphasis(intel_dp->link_status, lane); + uint8_t this_v = cdv_intel_get_adjust_request_voltage(intel_dp->link_status, lane); + uint8_t this_p = cdv_intel_get_adjust_request_pre_emphasis(intel_dp->link_status, lane);
if (this_v > v) v = this_v; @@ -892,25 +888,25 @@ psb_intel_get_adjust_train(struct psb_intel_output *output)
static uint8_t -psb_intel_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE], +cdv_intel_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane) { int i = DP_LANE0_1_STATUS + (lane >> 1); int s = (lane & 1) * 4; - uint8_t l = psb_intel_dp_link_status(link_status, i); + uint8_t l = cdv_intel_dp_link_status(link_status, i);
return (l >> s) & 0xf; }
/* Check for clock recovery is done on all channels */ static bool -psb_intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count) +cdv_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 = psb_intel_get_lane_status(link_status, lane); + lane_status = cdv_intel_get_lane_status(link_status, lane); if ((lane_status & DP_LANE_CR_DONE) == 0) return false; } @@ -922,19 +918,19 @@ psb_intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_c DP_LANE_CHANNEL_EQ_DONE|\ DP_LANE_SYMBOL_LOCKED) static bool -psb_intel_channel_eq_ok(struct psb_intel_output *output) +cdv_intel_channel_eq_ok(struct psb_intel_encoder *encoder) { - struct psb_intel_dp *intel_dp = output->dev_priv; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; uint8_t lane_align; uint8_t lane_status; int lane;
- lane_align = psb_intel_dp_link_status(intel_dp->link_status, + lane_align = cdv_intel_dp_link_status(intel_dp->link_status, DP_LANE_ALIGN_STATUS_UPDATED); if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) return false; for (lane = 0; lane < intel_dp->lane_count; lane++) { - lane_status = psb_intel_get_lane_status(intel_dp->link_status, lane); + lane_status = cdv_intel_get_lane_status(intel_dp->link_status, lane); if ((lane_status & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS) return false; } @@ -942,19 +938,19 @@ psb_intel_channel_eq_ok(struct psb_intel_output *output) }
static bool -psb_intel_dp_set_link_train(struct psb_intel_output *output, +cdv_intel_dp_set_link_train(struct psb_intel_encoder *encoder, uint32_t dp_reg_value, uint8_t dp_train_pat) { - struct drm_device *dev = output->base.dev; + struct drm_device *dev = encoder->base.dev; int ret; - struct psb_intel_dp *intel_dp = output->dev_priv; + struct cdv_intel_dp *intel_dp = encoder->dev_priv;
REG_WRITE(intel_dp->output_reg, dp_reg_value); REG_READ(intel_dp->output_reg);
- ret = psb_intel_dp_aux_native_write_1(output, + ret = cdv_intel_dp_aux_native_write_1(encoder, DP_TRAINING_PATTERN_SET, dp_train_pat);
@@ -969,14 +965,14 @@ psb_intel_dp_set_link_train(struct psb_intel_output *output,
static bool -psb_intel_dplink_set_level(struct psb_intel_output *output, +cdv_intel_dplink_set_level(struct psb_intel_encoder *encoder, uint8_t dp_train_pat) { int ret; - struct psb_intel_dp *intel_dp = output->dev_priv; + struct cdv_intel_dp *intel_dp = encoder->dev_priv;
- ret = psb_intel_dp_aux_native_write(output, + ret = cdv_intel_dp_aux_native_write(encoder, DP_TRAINING_LANE0_SET, intel_dp->train_set, intel_dp->lane_count); @@ -990,10 +986,10 @@ psb_intel_dplink_set_level(struct psb_intel_output *output, }
static void -psb_intel_dp_set_vswing_premph(struct psb_intel_output *output, uint8_t signal_level) +cdv_intel_dp_set_vswing_premph(struct psb_intel_encoder *encoder, uint8_t signal_level) { - struct drm_device *dev = output->base.dev; - struct psb_intel_dp *intel_dp = output->dev_priv; + struct drm_device *dev = encoder->base.dev; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; struct ddi_regoff *ddi_reg; int vswing, premph, index;
@@ -1013,53 +1009,53 @@ psb_intel_dp_set_vswing_premph(struct psb_intel_output *output, uint8_t signal_l #endif DRM_DEBUG_KMS("Test2\n"); //return ; - psb_sb_reset(dev); + cdv_sb_reset(dev); /* ;Swing voltage programming ;gfx_dpio_set_reg(0xc058, 0x0505313A) */ - psb_sb_write(dev, ddi_reg->VSwing5, 0x0505313A); + cdv_sb_write(dev, ddi_reg->VSwing5, 0x0505313A);
/* ;gfx_dpio_set_reg(0x8154, 0x43406055) */ - psb_sb_write(dev, ddi_reg->VSwing1, 0x43406055); + cdv_sb_write(dev, ddi_reg->VSwing1, 0x43406055);
/* ;gfx_dpio_set_reg(0x8148, 0x55338954) * The VSwing_PreEmph table is also considered based on the vswing/premp */ index = (vswing + premph) * 2; if (premph == 1 && vswing == 1) { - psb_sb_write(dev, ddi_reg->VSwing2, 0x055738954); + cdv_sb_write(dev, ddi_reg->VSwing2, 0x055738954); } else - psb_sb_write(dev, ddi_reg->VSwing2, dp_vswing_premph_table[index]); + cdv_sb_write(dev, ddi_reg->VSwing2, dp_vswing_premph_table[index]);
/* ;gfx_dpio_set_reg(0x814c, 0x40802040) */ if ((vswing + premph) == DP_TRAIN_VOLTAGE_SWING_1200) - psb_sb_write(dev, ddi_reg->VSwing3, 0x70802040); + cdv_sb_write(dev, ddi_reg->VSwing3, 0x70802040); else - psb_sb_write(dev, ddi_reg->VSwing3, 0x40802040); + cdv_sb_write(dev, ddi_reg->VSwing3, 0x40802040);
/* ;gfx_dpio_set_reg(0x8150, 0x2b405555) */ - //psb_sb_write(dev, ddi_reg->VSwing4, 0x2b405555); + /* cdv_sb_write(dev, ddi_reg->VSwing4, 0x2b405555); */
/* ;gfx_dpio_set_reg(0x8154, 0xc3406055) */ - psb_sb_write(dev, ddi_reg->VSwing1, 0xc3406055); + cdv_sb_write(dev, ddi_reg->VSwing1, 0xc3406055);
/* ;Pre emphasis programming * ;gfx_dpio_set_reg(0xc02c, 0x1f030040) */ - psb_sb_write(dev, ddi_reg->PreEmph1, 0x1f030040); + cdv_sb_write(dev, ddi_reg->PreEmph1, 0x1f030040);
/* ;gfx_dpio_set_reg(0x8124, 0x00004000) */ index = 2 * premph + 1; - psb_sb_write(dev, ddi_reg->PreEmph2, dp_vswing_premph_table[index]); + cdv_sb_write(dev, ddi_reg->PreEmph2, dp_vswing_premph_table[index]); return; }
/* Enable corresponding port and start training pattern 1 */ static void -psb_intel_dp_start_link_train(struct psb_intel_output *output) +cdv_intel_dp_start_link_train(struct psb_intel_encoder *encoder) { - struct drm_device *dev = output->base.dev; - struct psb_intel_dp *intel_dp = output->dev_priv; + struct drm_device *dev = encoder->base.dev; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; int i; uint8_t voltage; bool clock_recovery = false; @@ -1068,10 +1064,10 @@ psb_intel_dp_start_link_train(struct psb_intel_output *output) uint32_t DP = intel_dp->DP;
DP |= DP_PORT_EN; - DP &= ~DP_LINK_TRAIN_MASK; + DP &= ~DP_LINK_TRAIN_MASK; - reg = DP; - reg |= DP_LINK_TRAIN_PAT_1; + reg = DP; + reg |= DP_LINK_TRAIN_PAT_1; /* Enable output, wait for it to become active */ REG_WRITE(intel_dp->output_reg, reg); REG_READ(intel_dp->output_reg); @@ -1079,7 +1075,7 @@ psb_intel_dp_start_link_train(struct psb_intel_output *output)
DRM_DEBUG_KMS("Link config\n"); /* Write the link configuration data */ - psb_intel_dp_aux_native_write(output, DP_LINK_BW_SET, + cdv_intel_dp_aux_native_write(encoder, DP_LINK_BW_SET, intel_dp->link_configuration, 2);
@@ -1095,19 +1091,19 @@ psb_intel_dp_start_link_train(struct psb_intel_output *output) for (;;) { /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */
- if (!psb_intel_dp_set_link_train(output, reg, DP_TRAINING_PATTERN_1)) { + if (!cdv_intel_dp_set_link_train(encoder, reg, DP_TRAINING_PATTERN_1)) { DRM_DEBUG_KMS("Failure in aux-transfer setting pattern 1\n"); } - psb_intel_dp_set_vswing_premph(output, intel_dp->train_set[0]); + cdv_intel_dp_set_vswing_premph(encoder, intel_dp->train_set[0]); /* Set training pattern 1 */
- psb_intel_dplink_set_level(output, DP_TRAINING_PATTERN_1); + cdv_intel_dplink_set_level(encoder, DP_TRAINING_PATTERN_1);
udelay(200); - if (!psb_intel_dp_get_link_status(output)) + if (!cdv_intel_dp_get_link_status(encoder)) break;
- if (psb_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { + if (cdv_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { DRM_DEBUG_KMS("PT1 train is done\n"); clock_recovery = true; break; @@ -1130,7 +1126,7 @@ psb_intel_dp_start_link_train(struct psb_intel_output *output) voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
/* Compute new intel_dp->train_set as requested by target */ - psb_intel_get_adjust_train(output); + cdv_intel_get_adjust_train(encoder);
}
@@ -1142,10 +1138,10 @@ psb_intel_dp_start_link_train(struct psb_intel_output *output) }
static void -psb_intel_dp_complete_link_train(struct psb_intel_output *output) +cdv_intel_dp_complete_link_train(struct psb_intel_encoder *encoder) { - struct drm_device *dev = output->base.dev; - struct psb_intel_dp *intel_dp = output->dev_priv; + struct drm_device *dev = encoder->base.dev; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; bool channel_eq = false; int tries, cr_tries; u32 reg; @@ -1161,7 +1157,7 @@ psb_intel_dp_complete_link_train(struct psb_intel_output *output)
for (;;) { /* channel eq pattern */ - if (!psb_intel_dp_set_link_train(output, reg, + if (!cdv_intel_dp_set_link_train(encoder, reg, DP_TRAINING_PATTERN_2)) { DRM_DEBUG_KMS("Failure in aux-transfer setting pattern 2\n"); } @@ -1169,26 +1165,26 @@ psb_intel_dp_complete_link_train(struct psb_intel_output *output)
if (cr_tries > 5) { DRM_ERROR("failed to train DP, aborting\n"); - psb_intel_dp_link_down(output); + cdv_intel_dp_link_down(encoder); break; }
- psb_intel_dp_set_vswing_premph(output, intel_dp->train_set[0]); + cdv_intel_dp_set_vswing_premph(encoder, intel_dp->train_set[0]);
- psb_intel_dplink_set_level(output, DP_TRAINING_PATTERN_2); + cdv_intel_dplink_set_level(encoder, DP_TRAINING_PATTERN_2);
udelay(1000); - if (!psb_intel_dp_get_link_status(output)) + if (!cdv_intel_dp_get_link_status(encoder)) break;
/* Make sure clock is still ok */ - if (!psb_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { - psb_intel_dp_start_link_train(output); + if (!cdv_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { + cdv_intel_dp_start_link_train(encoder); cr_tries++; continue; }
- if (psb_intel_channel_eq_ok(output)) { + if (cdv_intel_channel_eq_ok(encoder)) { DRM_DEBUG_KMS("PT2 train is done\n"); channel_eq = true; break; @@ -1196,15 +1192,15 @@ psb_intel_dp_complete_link_train(struct psb_intel_output *output)
/* Try 5 times, then try clock recovery if that fails */ if (tries > 5) { - psb_intel_dp_link_down(output); - psb_intel_dp_start_link_train(output); + cdv_intel_dp_link_down(encoder); + cdv_intel_dp_start_link_train(encoder); tries = 0; cr_tries++; continue; }
/* Compute new intel_dp->train_set as requested by target */ - psb_intel_get_adjust_train(output); + cdv_intel_get_adjust_train(encoder); ++tries;
} @@ -1213,15 +1209,15 @@ psb_intel_dp_complete_link_train(struct psb_intel_output *output)
REG_WRITE(intel_dp->output_reg, reg); REG_READ(intel_dp->output_reg); - psb_intel_dp_aux_native_write_1(output, + cdv_intel_dp_aux_native_write_1(encoder, DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); }
static void -psb_intel_dp_link_down(struct psb_intel_output *output) +cdv_intel_dp_link_down(struct psb_intel_encoder *encoder) { - struct drm_device *dev = output->base.dev; - struct psb_intel_dp *intel_dp = output->dev_priv; + struct drm_device *dev = encoder->base.dev; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; uint32_t DP = intel_dp->DP;
if ((REG_READ(intel_dp->output_reg) & DP_PORT_EN) == 0) @@ -1243,13 +1239,13 @@ psb_intel_dp_link_down(struct psb_intel_output *output) }
static enum drm_connector_status -cdv_dp_detect(struct psb_intel_output *output) +cdv_dp_detect(struct psb_intel_encoder *encoder) { - struct psb_intel_dp *intel_dp = output->dev_priv; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; enum drm_connector_status status;
status = connector_status_disconnected; - if (psb_intel_dp_aux_native_read(output, 0x000, intel_dp->dpcd, + if (cdv_intel_dp_aux_native_read(encoder, 0x000, intel_dp->dpcd, sizeof (intel_dp->dpcd)) == sizeof (intel_dp->dpcd)) { if (intel_dp->dpcd[DP_DPCD_REV] != 0) @@ -1269,16 +1265,16 @@ cdv_dp_detect(struct psb_intel_output *output) * \return false if DP port is disconnected. */ static enum drm_connector_status -psb_intel_dp_detect(struct drm_connector *connector, bool force) +cdv_intel_dp_detect(struct drm_connector *connector, bool force) { - struct psb_intel_output *output = to_psb_intel_output(connector); - struct psb_intel_dp *intel_dp = output->dev_priv; + struct psb_intel_encoder *encoder = psb_intel_attached_encoder(connector); + struct cdv_intel_dp *intel_dp = encoder->dev_priv; enum drm_connector_status status; struct edid *edid = NULL;
intel_dp->has_audio = false;
- status = cdv_dp_detect(output); + status = cdv_dp_detect(encoder); if (status != connector_status_connected) return status;
@@ -1296,20 +1292,18 @@ psb_intel_dp_detect(struct drm_connector *connector, bool force) return connector_status_connected; }
-static int psb_intel_dp_get_modes(struct drm_connector *connector) +static int cdv_intel_dp_get_modes(struct drm_connector *connector) { - struct psb_intel_output *intel_output = to_psb_intel_output(connector); - struct psb_intel_dp *intel_dp = intel_output->dev_priv; + struct psb_intel_encoder *intel_encoder = psb_intel_attached_encoder(connector); + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; struct edid *edid = NULL; int ret = 0;
- edid = drm_get_edid(&intel_output->base, - &intel_dp->adapter); + edid = drm_get_edid(connector, &intel_dp->adapter); if (edid) { - drm_mode_connector_update_edid_property(&intel_output-> - base, edid); - ret = drm_add_edid_modes(&intel_output->base, edid); + drm_mode_connector_update_edid_property(connector, edid); + ret = drm_add_edid_modes(connector, edid); kfree(edid); }
@@ -1317,10 +1311,10 @@ static int psb_intel_dp_get_modes(struct drm_connector *connector) }
static bool -psb_intel_dp_detect_audio(struct drm_connector *connector) +cdv_intel_dp_detect_audio(struct drm_connector *connector) { - struct psb_intel_output *output = to_psb_intel_output(connector); - struct psb_intel_dp *intel_dp = output->dev_priv; + struct psb_intel_encoder *encoder = psb_intel_attached_encoder(connector); + struct cdv_intel_dp *intel_dp = encoder->dev_priv; struct edid *edid; bool has_audio = false;
@@ -1336,13 +1330,13 @@ psb_intel_dp_detect_audio(struct drm_connector *connector) }
static int -psb_intel_dp_set_property(struct drm_connector *connector, +cdv_intel_dp_set_property(struct drm_connector *connector, struct drm_property *property, uint64_t val) { struct drm_psb_private *dev_priv = connector->dev->dev_private; - struct psb_intel_output *output = to_psb_intel_output(connector); - struct psb_intel_dp *intel_dp = output->dev_priv; + struct psb_intel_encoder *encoder = psb_intel_attached_encoder(connector); + struct cdv_intel_dp *intel_dp = encoder->dev_priv; int ret;
ret = drm_connector_property_set_value(connector, property, val); @@ -1359,7 +1353,7 @@ psb_intel_dp_set_property(struct drm_connector *connector, intel_dp->force_audio = i;
if (i == 0) - has_audio = psb_intel_dp_detect_audio(connector); + has_audio = cdv_intel_dp_detect_audio(connector); else has_audio = i > 0;
@@ -1381,8 +1375,8 @@ psb_intel_dp_set_property(struct drm_connector *connector, return -EINVAL;
done: - if (output->enc.crtc) { - struct drm_crtc *crtc = output->enc.crtc; + if (encoder->base.crtc) { + struct drm_crtc *crtc = encoder->base.crtc; drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x, crtc->y, crtc->fb); @@ -1392,10 +1386,11 @@ done: }
static void -psb_intel_dp_destroy (struct drm_connector *connector) +cdv_intel_dp_destroy (struct drm_connector *connector) { - struct psb_intel_output *output = to_psb_intel_output(connector); - struct psb_intel_dp *intel_dp = output->dev_priv; + struct psb_intel_encoder *psb_intel_encoder = + psb_intel_attached_encoder(connector); + struct cdv_intel_dp *intel_dp = psb_intel_encoder->dev_priv;
i2c_del_adapter(&intel_dp->adapter); drm_sysfs_connector_remove(connector); @@ -1403,86 +1398,83 @@ psb_intel_dp_destroy (struct drm_connector *connector) kfree(connector); }
-static void psb_intel_dp_encoder_destroy(struct drm_encoder *encoder) +static void cdv_intel_dp_encoder_destroy(struct drm_encoder *encoder) { drm_encoder_cleanup(encoder); }
-static const struct drm_encoder_helper_funcs psb_intel_dp_helper_funcs = { - .dpms = psb_intel_dp_dpms, - .mode_fixup = psb_intel_dp_mode_fixup, - .prepare = psb_intel_dp_prepare, - .mode_set = psb_intel_dp_mode_set, - .commit = psb_intel_dp_commit, +static const struct drm_encoder_helper_funcs cdv_intel_dp_helper_funcs = { + .dpms = cdv_intel_dp_dpms, + .mode_fixup = cdv_intel_dp_mode_fixup, + .prepare = cdv_intel_dp_prepare, + .mode_set = cdv_intel_dp_mode_set, + .commit = cdv_intel_dp_commit, };
-static const struct drm_connector_funcs psb_intel_dp_connector_funcs = { +static const struct drm_connector_funcs cdv_intel_dp_connector_funcs = { .dpms = drm_helper_connector_dpms, - .detect = psb_intel_dp_detect, + .detect = cdv_intel_dp_detect, .fill_modes = drm_helper_probe_single_connector_modes, - .set_property = psb_intel_dp_set_property, - .destroy = psb_intel_dp_destroy, + .set_property = cdv_intel_dp_set_property, + .destroy = cdv_intel_dp_destroy, };
-static const struct drm_connector_helper_funcs psb_intel_dp_connector_helper_funcs = { - .get_modes = psb_intel_dp_get_modes, - .mode_valid = psb_intel_dp_mode_valid, +static const struct drm_connector_helper_funcs cdv_intel_dp_connector_helper_funcs = { + .get_modes = cdv_intel_dp_get_modes, + .mode_valid = cdv_intel_dp_mode_valid, .best_encoder = psb_intel_best_encoder, };
-static const struct drm_encoder_funcs psb_intel_dp_enc_funcs = { - .destroy = psb_intel_dp_encoder_destroy, +static const struct drm_encoder_funcs cdv_intel_dp_enc_funcs = { + .destroy = cdv_intel_dp_encoder_destroy, };
-static void -psb_intel_dp_add_properties(struct psb_intel_output *output, struct drm_connector *connector) +static void cdv_intel_dp_add_properties(struct drm_connector *connector) { - psb_intel_attach_force_audio_property(connector); - psb_intel_attach_broadcast_rgb_property(connector); + cdv_intel_attach_force_audio_property(connector); + cdv_intel_attach_broadcast_rgb_property(connector); }
void -psb_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int output_reg) +cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int output_reg) { + struct psb_intel_encoder *psb_intel_encoder; + struct psb_intel_connector *psb_intel_connector; struct drm_connector *connector; struct drm_encoder *encoder; - struct psb_intel_output *psb_intel_output; - struct psb_intel_dp *intel_dp; + struct cdv_intel_dp *intel_dp; const char *name = NULL; - int type;
- psb_intel_output = kzalloc(sizeof(struct psb_intel_output) + - sizeof(struct psb_intel_dp), GFP_KERNEL); - if (!psb_intel_output) + psb_intel_encoder = kzalloc(sizeof(struct psb_intel_encoder), GFP_KERNEL); + if (!psb_intel_encoder) return; + psb_intel_connector = kzalloc(sizeof(struct psb_intel_connector), GFP_KERNEL); + if (!psb_intel_connector) + goto err_connector; + intel_dp = kzalloc(sizeof(struct cdv_intel_dp), GFP_KERNEL); + if (!intel_dp) + goto err_priv; + + connector = &psb_intel_connector->base; + encoder = &psb_intel_encoder->base;
- intel_dp = (struct psb_intel_dp *)(psb_intel_output + 1); - psb_intel_output->mode_dev = mode_dev; - connector = &psb_intel_output->base; - encoder = &psb_intel_output->enc; - psb_intel_output->dev_priv=intel_dp; - intel_dp->output = psb_intel_output; + drm_connector_init(dev, connector, &cdv_intel_dp_connector_funcs, DRM_MODE_CONNECTOR_DisplayPort); + drm_encoder_init(dev, encoder, &cdv_intel_dp_enc_funcs, DRM_MODE_ENCODER_TMDS);
+ psb_intel_connector_attach_encoder(psb_intel_connector, psb_intel_encoder); + psb_intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; + + psb_intel_encoder->dev_priv=intel_dp; + intel_dp->encoder = psb_intel_encoder; intel_dp->output_reg = output_reg; - type = DRM_MODE_CONNECTOR_DisplayPort; - psb_intel_output->type = INTEL_OUTPUT_DISPLAYPORT; - - drm_connector_init(dev, connector, &psb_intel_dp_connector_funcs, type); - drm_connector_helper_add(connector, &psb_intel_dp_connector_helper_funcs); + drm_encoder_helper_add(encoder, &cdv_intel_dp_helper_funcs); + drm_connector_helper_add(connector, &cdv_intel_dp_connector_helper_funcs);
connector->polled = DRM_CONNECTOR_POLL_HPD; - - connector->interlace_allowed = 0; - connector->doublescan_allowed = 0; - - drm_encoder_init(dev, encoder, &psb_intel_dp_enc_funcs, - DRM_MODE_ENCODER_TMDS); - drm_encoder_helper_add(encoder, &psb_intel_dp_helper_funcs); - - drm_mode_connector_attach_encoder(&psb_intel_output->base, - &psb_intel_output->enc); + connector->interlace_allowed = false; + connector->doublescan_allowed = false;
drm_sysfs_connector_add(connector);
@@ -1490,15 +1482,21 @@ psb_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev switch (output_reg) { case DP_B: name = "DPDDC-B"; - psb_intel_output->ddi_select = (DP_MASK | DDI0_SELECT); + psb_intel_encoder->ddi_select = (DP_MASK | DDI0_SELECT); break; case DP_C: name = "DPDDC-C"; - psb_intel_output->ddi_select = (DP_MASK | DDI1_SELECT); + psb_intel_encoder->ddi_select = (DP_MASK | DDI1_SELECT); break; }
- psb_intel_dp_i2c_init(psb_intel_output, name); - psb_intel_dp_add_properties(psb_intel_output, connector); + cdv_intel_dp_i2c_init(psb_intel_connector, psb_intel_encoder, name); + /* FIXME:fail check */ + cdv_intel_dp_add_properties(connector); + return;
+err_priv: + kfree(psb_intel_connector); +err_connector: + kfree(psb_intel_encoder); } diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h index c574c01..c53e592 100644 --- a/drivers/gpu/drm/gma500/psb_intel_drv.h +++ b/drivers/gpu/drm/gma500/psb_intel_drv.h @@ -288,4 +288,20 @@ extern void gma_intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed); extern void gma_intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit); extern void gma_intel_teardown_gmbus(struct drm_device *dev);
+/* DP support */ +extern void cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int output_reg); +extern void psb_intel_dp_set_m_n(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + +extern void psb_intel_attach_force_audio_property(struct drm_connector *connector); +extern void psb_intel_attach_broadcast_rgb_property(struct drm_connector *connector); + +extern int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val); +extern int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val); +extern void cdv_sb_reset(struct drm_device *dev); + +extern void cdv_intel_attach_force_audio_property(struct drm_connector *connector); +extern void cdv_intel_attach_broadcast_rgb_property(struct drm_connector *connector); + #endif /* __INTEL_DRV_H__ */
From: Alan Cox alan@linux.intel.com
This will give the basic support only
Signed-off-by: Alan Cox alan@linux.intel.com ---
drivers/gpu/drm/gma500/cdv_device.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/gma500/cdv_device.c b/drivers/gpu/drm/gma500/cdv_device.c index e2fff24..68f100e 100644 --- a/drivers/gpu/drm/gma500/cdv_device.c +++ b/drivers/gpu/drm/gma500/cdv_device.c @@ -58,10 +58,17 @@ static int cdv_output_init(struct drm_device *dev) cdv_intel_lvds_init(dev, &dev_priv->mode_dev);
/* These bits indicate HDMI not SDVO on CDV */ - if (REG_READ(SDVOB) & SDVO_DETECTED) + if (REG_READ(SDVOB) & SDVO_DETECTED) { cdv_hdmi_init(dev, &dev_priv->mode_dev, SDVOB); - if (REG_READ(SDVOC) & SDVO_DETECTED) + if (REG_READ(DP_B) & DP_DETECTED) + cdv_intel_dp_init(dev, &dev_priv->mode_dev, DP_B); + } + + if (REG_READ(SDVOC) & SDVO_DETECTED) { cdv_hdmi_init(dev, &dev_priv->mode_dev, SDVOC); + if (REG_READ(DP_C) & DP_DETECTED) + cdv_intel_dp_init(dev, &dev_priv->mode_dev, DP_C); + } return 0; }
From: Zhao Yakui yakui.zhao@intel.com
Introduce the eDP support into the driver.
This has been reworked a bit because kernel driver proper uses encoder/connectors while the legacy Intel driver uses the old output stuff.
It also diverges on the backlight handling. The legacy Intel driver adds a panel abstraction based upon the i915 one. It's only really used for backlight bits and we have a perfectly good backlight abstraction which can extend instead.
Signed-off-by: Zhao Yakui yakui.zhao@intel.com [ported to upstream driver, redid backlight abstraction] Signed-off-by: Alan Cox alan@linux.intel.com ---
drivers/gpu/drm/gma500/backlight.c | 45 +++ drivers/gpu/drm/gma500/cdv_device.c | 1 drivers/gpu/drm/gma500/cdv_intel_display.c | 42 ++- drivers/gpu/drm/gma500/cdv_intel_dp.c | 468 +++++++++++++++++++++++++++- drivers/gpu/drm/gma500/cdv_intel_lvds.c | 12 - drivers/gpu/drm/gma500/framebuffer.c | 3 drivers/gpu/drm/gma500/intel_bios.c | 101 ++++++ drivers/gpu/drm/gma500/intel_bios.h | 46 +++ drivers/gpu/drm/gma500/mdfld_dsi_output.c | 13 - drivers/gpu/drm/gma500/mid_bios.c | 8 drivers/gpu/drm/gma500/opregion.c | 3 drivers/gpu/drm/gma500/psb_drv.h | 19 + drivers/gpu/drm/gma500/psb_intel_lvds.c | 13 - drivers/gpu/drm/gma500/psb_intel_reg.h | 38 ++ 14 files changed, 751 insertions(+), 61 deletions(-)
diff --git a/drivers/gpu/drm/gma500/backlight.c b/drivers/gpu/drm/gma500/backlight.c index 2079395..143eba3 100644 --- a/drivers/gpu/drm/gma500/backlight.c +++ b/drivers/gpu/drm/gma500/backlight.c @@ -26,10 +26,55 @@ #include "intel_bios.h" #include "power.h"
+static void do_gma_backlight_set(struct drm_device *dev) +{ +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + struct drm_psb_private *dev_priv = dev->dev_private; + backlight_update_status(dev_priv->backlight_device); +#endif +} + +void gma_backlight_enable(struct drm_device *dev) +{ +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + struct drm_psb_private *dev_priv = dev->dev_private; + dev_priv->backlight_enabled = true; + if (dev_priv->backlight_device) { + dev_priv->backlight_device->props.brightness = dev_priv->backlight_level; + do_gma_backlight_set(dev); + } +#endif +} + +void gma_backlight_disable(struct drm_device *dev) +{ +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + struct drm_psb_private *dev_priv = dev->dev_private; + dev_priv->backlight_enabled = false; + if (dev_priv->backlight_device) { + dev_priv->backlight_device->props.brightness = 0; + do_gma_backlight_set(dev); + } +#endif +} + +void gma_backlight_set(struct drm_device *dev, int v) +{ +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + struct drm_psb_private *dev_priv = dev->dev_private; + dev_priv->backlight_level = v; + if (dev_priv->backlight_device && dev_priv->backlight_enabled) { + dev_priv->backlight_device->props.brightness = v; + do_gma_backlight_set(dev); + } +#endif +} + int gma_backlight_init(struct drm_device *dev) { #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE struct drm_psb_private *dev_priv = dev->dev_private; + dev_priv->backlight_enabled = true; return dev_priv->ops->backlight_init(dev); #else return 0; diff --git a/drivers/gpu/drm/gma500/cdv_device.c b/drivers/gpu/drm/gma500/cdv_device.c index 68f100e..4882e64 100644 --- a/drivers/gpu/drm/gma500/cdv_device.c +++ b/drivers/gpu/drm/gma500/cdv_device.c @@ -170,6 +170,7 @@ static int cdv_backlight_init(struct drm_device *dev) cdv_get_brightness(cdv_backlight_device); backlight_update_status(cdv_backlight_device); dev_priv->backlight_device = cdv_backlight_device; + dev_priv->backlight_enabled = true; return 0; }
diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c index 3f11060..bfb0565 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_display.c +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c @@ -438,7 +438,8 @@ static const struct cdv_intel_limit_t *cdv_intel_limit(struct drm_crtc *crtc, limit = &cdv_intel_limits[CDV_LIMIT_SINGLE_LVDS_96]; else limit = &cdv_intel_limits[CDV_LIMIT_SINGLE_LVDS_100]; - } else if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { + } else if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) || + psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) { if (refclk == 27000) limit = &cdv_intel_limits[CDV_LIMIT_DP_27]; else @@ -1045,6 +1046,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, struct drm_connector *connector; const struct cdv_intel_limit_t *limit; u32 ddi_select = 0; + bool is_edp = false;
list_for_each_entry(connector, &mode_config->connector_list, head) { struct psb_intel_encoder *psb_intel_encoder = @@ -1071,6 +1073,9 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, case INTEL_OUTPUT_DISPLAYPORT: is_dp = true; break; + case INTEL_OUTPUT_EDP: + is_edp = true; + break; default: DRM_ERROR("invalid output type.\n"); return 0; @@ -1083,7 +1088,15 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, else /* high-end sku, 27/100 mhz */ refclk = 27000; - if (is_dp) { + if (is_dp || is_edp) { + /* + * Based on the spec the low-end SKU has only CRT/LVDS. So it is + * unnecessary to consider it for DP/eDP. + * On the high-end SKU, it will use the 27/100M reference clk + * for DP/eDP. When using SSC clock, the ref clk is 100MHz.Otherwise + * it will be 27MHz. From the VBIOS code it seems that the pipe A choose + * 27MHz for DP/eDP while the Pipe B chooses the 100MHz. + */ if (pipe == 0) refclk = 27000; else @@ -1133,6 +1146,31 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, /* setup pipeconf */ pipeconf = REG_READ(map->conf);
+ pipeconf &= ~(PIPE_BPC_MASK); + if (is_edp) { + switch (dev_priv->edp.bpp) { + case 24: + pipeconf |= PIPE_8BPC; + break; + case 18: + pipeconf |= PIPE_6BPC; + break; + case 30: + pipeconf |= PIPE_10BPC; + break; + default: + pipeconf |= PIPE_8BPC; + break; + } + } else if (is_lvds) { + /* the BPC will be 6 if it is 18-bit LVDS panel */ + if ((REG_READ(LVDS) & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP) + pipeconf |= PIPE_8BPC; + else + pipeconf |= PIPE_6BPC; + } else + pipeconf |= PIPE_8BPC; + /* Set up the display plane register */ dspcntr = DISPPLANE_GAMMA_ENABLE;
diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index b29b602..0571ef9b 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -36,6 +36,20 @@ #include "psb_intel_reg.h" #include "drm_dp_helper.h"
+#define _wait_for(COND, MS, W) ({ \ + unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \ + int ret__ = 0; \ + while (! (COND)) { \ + if (time_after(jiffies, timeout__)) { \ + ret__ = -ETIMEDOUT; \ + break; \ + } \ + if (W && !in_dbg_master()) msleep(W); \ + } \ + ret__; \ +}) + +#define wait_for(COND, MS) _wait_for(COND, MS, 1)
#define DP_LINK_STATUS_SIZE 6 #define DP_LINK_CHECK_TIMEOUT (10 * 1000) @@ -59,6 +73,13 @@ struct cdv_intel_dp { struct i2c_algo_dp_aux_data algo; uint8_t train_set[4]; uint8_t link_status[DP_LINK_STATUS_SIZE]; + int panel_power_up_delay; + int panel_power_down_delay; + int panel_power_cycle_delay; + int backlight_on_delay; + int backlight_off_delay; + struct drm_display_mode *panel_fixed_mode; /* for eDP */ + bool panel_on; };
struct ddi_regoff { @@ -159,31 +180,166 @@ cdv_intel_dp_max_data_rate(int max_link_clock, int max_lanes) return (max_link_clock * max_lanes * 19) / 20; }
+static void cdv_intel_edp_panel_vdd_on(struct psb_intel_encoder *intel_encoder) +{ + struct drm_device *dev = intel_encoder->base.dev; + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; + u32 pp; + + if (intel_dp->panel_on) { + DRM_DEBUG_KMS("Skip VDD on because of panel on\n"); + return; + } + DRM_DEBUG_KMS("\n"); + + pp = REG_READ(PP_CONTROL); + + pp |= EDP_FORCE_VDD; + REG_WRITE(PP_CONTROL, pp); + REG_READ(PP_CONTROL); + msleep(intel_dp->panel_power_up_delay); +} + +static void cdv_intel_edp_panel_vdd_off(struct psb_intel_encoder *intel_encoder) +{ + struct drm_device *dev = intel_encoder->base.dev; + u32 pp; + + DRM_DEBUG_KMS("\n"); + pp = REG_READ(PP_CONTROL); + + pp &= ~EDP_FORCE_VDD; + REG_WRITE(PP_CONTROL, pp); + REG_READ(PP_CONTROL); + +} + +/* Returns true if the panel was already on when called */ +static bool cdv_intel_edp_panel_on(struct psb_intel_encoder *intel_encoder) +{ + struct drm_device *dev = intel_encoder->base.dev; + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; + u32 pp, idle_on_mask = PP_ON | PP_SEQUENCE_NONE; + + if (intel_dp->panel_on) + return true; + + DRM_DEBUG_KMS("\n"); + pp = REG_READ(PP_CONTROL); + pp &= ~PANEL_UNLOCK_MASK; + + pp |= (PANEL_UNLOCK_REGS | POWER_TARGET_ON); + REG_WRITE(PP_CONTROL, pp); + REG_READ(PP_CONTROL); + + if (wait_for(((REG_READ(PP_STATUS) & idle_on_mask) == idle_on_mask), 1000)) { + DRM_DEBUG_KMS("Error in Powering up eDP panel, status %x\n", REG_READ(PP_STATUS)); + intel_dp->panel_on = false; + } else + intel_dp->panel_on = true; + msleep(intel_dp->panel_power_up_delay); + + return false; +} + +static void cdv_intel_edp_panel_off (struct psb_intel_encoder *intel_encoder) +{ + struct drm_device *dev = intel_encoder->base.dev; + u32 pp, idle_off_mask = PP_ON ; + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; + + DRM_DEBUG_KMS("\n"); + + pp = REG_READ(PP_CONTROL); + + if ((pp & POWER_TARGET_ON) == 0) + return; + + intel_dp->panel_on = false; + pp &= ~PANEL_UNLOCK_MASK; + /* ILK workaround: disable reset around power sequence */ + + pp &= ~POWER_TARGET_ON; + pp &= ~EDP_FORCE_VDD; + pp &= ~EDP_BLC_ENABLE; + REG_WRITE(PP_CONTROL, pp); + REG_READ(PP_CONTROL); + DRM_DEBUG_KMS("PP_STATUS %x\n", REG_READ(PP_STATUS)); + + if (wait_for((REG_READ(PP_STATUS) & idle_off_mask) == 0, 1000)) { + DRM_DEBUG_KMS("Error in turning off Panel\n"); + } + + msleep(intel_dp->panel_power_cycle_delay); + DRM_DEBUG_KMS("Over\n"); +} + +static void cdv_intel_edp_backlight_on (struct psb_intel_encoder *intel_encoder) +{ + struct drm_device *dev = intel_encoder->base.dev; + u32 pp; + + DRM_DEBUG_KMS("\n"); + /* + * 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. + */ + msleep(300); + pp = REG_READ(PP_CONTROL); + + pp |= EDP_BLC_ENABLE; + REG_WRITE(PP_CONTROL, pp); + gma_backlight_enable(dev); +} + +static void cdv_intel_edp_backlight_off (struct psb_intel_encoder *intel_encoder) +{ + struct drm_device *dev = intel_encoder->base.dev; + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; + u32 pp; + + DRM_DEBUG_KMS("\n"); + gma_backlight_disable(dev); + msleep(10); + pp = REG_READ(PP_CONTROL); + + pp &= ~EDP_BLC_ENABLE; + REG_WRITE(PP_CONTROL, pp); + msleep(intel_dp->backlight_off_delay); +} + static int cdv_intel_dp_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct psb_intel_encoder *encoder = psb_intel_attached_encoder(connector); - struct drm_device *dev = connector->dev; - struct drm_psb_private *dev_priv = dev->dev_private; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; int max_link_clock = cdv_intel_dp_link_clock(cdv_intel_dp_max_link_bw(encoder)); int max_lanes = cdv_intel_dp_max_lane_count(encoder); + struct drm_psb_private *dev_priv = connector->dev->dev_private;
- if (is_edp(encoder) && dev_priv->panel_fixed_mode) { - if (mode->hdisplay > dev_priv->panel_fixed_mode->hdisplay) + if (is_edp(encoder) && intel_dp->panel_fixed_mode) { + if (mode->hdisplay > intel_dp->panel_fixed_mode->hdisplay) return MODE_PANEL; - - if (mode->vdisplay > dev_priv->panel_fixed_mode->vdisplay) + if (mode->vdisplay > intel_dp->panel_fixed_mode->vdisplay) return MODE_PANEL; }
/* only refuse the mode on non eDP since we have seen some weird eDP panels which are outside spec tolerances but somehow work by magic */ if (!is_edp(encoder) && - (cdv_intel_dp_link_required(mode->clock, 24) + (cdv_intel_dp_link_required(mode->clock, dev_priv->edp.bpp) > cdv_intel_dp_max_data_rate(max_link_clock, max_lanes))) return MODE_CLOCK_HIGH;
+ if (is_edp(encoder)) { + if (cdv_intel_dp_link_required(mode->clock, 24) + > cdv_intel_dp_max_data_rate(max_link_clock, max_lanes)) + return MODE_CLOCK_HIGH; + + } if (mode->clock < 10000) return MODE_CLOCK_LOW;
@@ -238,6 +394,8 @@ cdv_intel_dp_aux_ch(struct psb_intel_encoder *encoder, aux_clock_divider = 200 / 2;
precharge = 4; + if (is_edp(encoder)) + precharge = 10;
if (REG_READ(ch_ctl) & DP_AUX_CH_CTL_SEND_BUSY) { DRM_ERROR("dp_aux_ch not started status 0x%08x\n", @@ -492,7 +650,10 @@ static int cdv_intel_dp_i2c_init(struct psb_intel_connector *connector, struct psb_intel_encoder *encoder, const char *name) { struct cdv_intel_dp *intel_dp = encoder->dev_priv; + int ret; + DRM_DEBUG_KMS("i2c_init %s\n", name); + intel_dp->algo.running = false; intel_dp->algo.address = 0; intel_dp->algo.aux_ch = cdv_intel_dp_i2c_aux_ch; @@ -505,27 +666,58 @@ cdv_intel_dp_i2c_init(struct psb_intel_connector *connector, struct psb_intel_en intel_dp->adapter.algo_data = &intel_dp->algo; intel_dp->adapter.dev.parent = &connector->base.kdev;
- return i2c_dp_aux_add_bus(&intel_dp->adapter); + if (is_edp(encoder)) + cdv_intel_edp_panel_vdd_on(encoder); + ret = i2c_dp_aux_add_bus(&intel_dp->adapter); + if (is_edp(encoder)) + cdv_intel_edp_panel_vdd_off(encoder); + + return ret; +} + +void cdv_intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, + struct drm_display_mode *adjusted_mode) +{ + adjusted_mode->hdisplay = fixed_mode->hdisplay; + adjusted_mode->hsync_start = fixed_mode->hsync_start; + adjusted_mode->hsync_end = fixed_mode->hsync_end; + adjusted_mode->htotal = fixed_mode->htotal; + + adjusted_mode->vdisplay = fixed_mode->vdisplay; + adjusted_mode->vsync_start = fixed_mode->vsync_start; + adjusted_mode->vsync_end = fixed_mode->vsync_end; + adjusted_mode->vtotal = fixed_mode->vtotal; + + adjusted_mode->clock = fixed_mode->clock; + + drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); }
static bool cdv_intel_dp_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { + struct drm_psb_private *dev_priv = encoder->dev->dev_private; struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder); struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; int lane_count, clock; int max_lane_count = cdv_intel_dp_max_lane_count(intel_encoder); int max_clock = cdv_intel_dp_max_link_bw(intel_encoder) == DP_LINK_BW_2_7 ? 1 : 0; static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 }; + int refclock = mode->clock; + int bpp = 24;
+ if (is_edp(intel_encoder) && intel_dp->panel_fixed_mode) { + cdv_intel_fixed_panel_mode(intel_dp->panel_fixed_mode, adjusted_mode); + refclock = intel_dp->panel_fixed_mode->clock; + bpp = dev_priv->edp.bpp; + }
for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) { for (clock = max_clock; clock >= 0; clock--) { int link_avail = cdv_intel_dp_max_data_rate(cdv_intel_dp_link_clock(bws[clock]), lane_count);
- if (cdv_intel_dp_link_required(mode->clock, 24) - <= link_avail) { + if (cdv_intel_dp_link_required(refclock, bpp) <= link_avail) { intel_dp->link_bw = bws[clock]; intel_dp->lane_count = lane_count; adjusted_mode->clock = cdv_intel_dp_link_clock(intel_dp->link_bw); @@ -537,7 +729,18 @@ cdv_intel_dp_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mo } } } - + if (is_edp(intel_encoder)) { + /* okay we failed just pick the highest */ + intel_dp->lane_count = max_lane_count; + intel_dp->link_bw = bws[max_clock]; + adjusted_mode->clock = cdv_intel_dp_link_clock(intel_dp->link_bw); + DRM_DEBUG_KMS("Force picking display port link bw %02x lane " + "count %d clock %d\n", + intel_dp->link_bw, intel_dp->lane_count, + adjusted_mode->clock); + + return true; + } return false; }
@@ -550,7 +753,7 @@ struct cdv_intel_dp_m_n { };
static void -psb_intel_reduce_ratio(uint32_t *num, uint32_t *den) +cdv_intel_reduce_ratio(uint32_t *num, uint32_t *den) { /* while (*num > 0xffffff || *den > 0xffffff) { @@ -575,10 +778,10 @@ cdv_intel_dp_compute_m_n(int bpp, m_n->tu = 64; m_n->gmch_m = (pixel_clock * bpp + 7) >> 3; m_n->gmch_n = link_clock * nlanes; - psb_intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); + cdv_intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); m_n->link_m = pixel_clock; m_n->link_n = link_clock; - psb_intel_reduce_ratio(&m_n->link_m, &m_n->link_n); + cdv_intel_reduce_ratio(&m_n->link_m, &m_n->link_n); }
void @@ -586,6 +789,7 @@ cdv_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; struct drm_mode_config *mode_config = &dev->mode_config; struct drm_encoder *encoder; struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc); @@ -610,6 +814,7 @@ cdv_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, break; } else if (is_edp(intel_encoder)) { lane_count = intel_dp->lane_count; + bpp = dev_priv->edp.bpp; break; } } @@ -640,7 +845,7 @@ cdv_intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode struct drm_crtc *crtc = encoder->crtc; struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc); struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; - + struct drm_device *dev = encoder->dev;
intel_dp->DP = DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; intel_dp->DP |= intel_dp->color_range; @@ -683,7 +888,22 @@ cdv_intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode if (intel_crtc->pipe == 1) intel_dp->DP |= DP_PIPEB_SELECT;
+ REG_WRITE(intel_dp->output_reg, (intel_dp->DP | DP_PORT_EN)); DRM_DEBUG_KMS("DP expected reg is %x\n", intel_dp->DP); + if (is_edp(intel_encoder)) { + uint32_t pfit_control; + cdv_intel_edp_panel_on(intel_encoder); + + if (mode->hdisplay != adjusted_mode->hdisplay || + mode->vdisplay != adjusted_mode->vdisplay) + pfit_control = PFIT_ENABLE; + else + pfit_control = 0; + + pfit_control |= intel_crtc->pipe << PFIT_PIPE_SHIFT; + + REG_WRITE(PFIT_CONTROL, pfit_control); + } }
@@ -721,18 +941,31 @@ static void cdv_intel_dp_sink_dpms(struct psb_intel_encoder *encoder, int mode) static void cdv_intel_dp_prepare(struct drm_encoder *encoder) { struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder); + int edp = is_edp(intel_encoder);
+ if (edp) { + cdv_intel_edp_backlight_off(intel_encoder); + cdv_intel_edp_panel_off(intel_encoder); + cdv_intel_edp_panel_vdd_on(intel_encoder); + } /* Wake up the sink first */ cdv_intel_dp_sink_dpms(intel_encoder, DRM_MODE_DPMS_ON); cdv_intel_dp_link_down(intel_encoder); + if (edp) + cdv_intel_edp_panel_vdd_off(intel_encoder); }
static void cdv_intel_dp_commit(struct drm_encoder *encoder) { struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder); + int edp = is_edp(intel_encoder);
+ if (edp) + cdv_intel_edp_panel_on(intel_encoder); cdv_intel_dp_start_link_train(intel_encoder); cdv_intel_dp_complete_link_train(intel_encoder); + if (edp) + cdv_intel_edp_backlight_on(intel_encoder); }
static void @@ -742,16 +975,29 @@ cdv_intel_dp_dpms(struct drm_encoder *encoder, int mode) struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; struct drm_device *dev = encoder->dev; uint32_t dp_reg = REG_READ(intel_dp->output_reg); + int edp = is_edp(intel_encoder);
if (mode != DRM_MODE_DPMS_ON) { + if (edp) { + cdv_intel_edp_backlight_off(intel_encoder); + cdv_intel_edp_panel_vdd_on(intel_encoder); + } cdv_intel_dp_sink_dpms(intel_encoder, mode); cdv_intel_dp_link_down(intel_encoder); + if (edp) { + cdv_intel_edp_panel_vdd_off(intel_encoder); + cdv_intel_edp_panel_off(intel_encoder); + } } else { + if (edp) + cdv_intel_edp_panel_on(intel_encoder); cdv_intel_dp_sink_dpms(intel_encoder, mode); if (!(dp_reg & DP_PORT_EN)) { cdv_intel_dp_start_link_train(intel_encoder); cdv_intel_dp_complete_link_train(intel_encoder); } + if (edp) + cdv_intel_edp_backlight_on(intel_encoder); } }
@@ -1090,6 +1336,10 @@ cdv_intel_dp_start_link_train(struct psb_intel_encoder *encoder)
for (;;) { /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ + DRM_DEBUG_KMS("DP Link Train Set %x, Link_config %x, %x\n", + intel_dp->train_set[0], + intel_dp->link_configuration[0], + intel_dp->link_configuration[1]);
if (!cdv_intel_dp_set_link_train(encoder, reg, DP_TRAINING_PATTERN_1)) { DRM_DEBUG_KMS("Failure in aux-transfer setting pattern 1\n"); @@ -1103,6 +1353,10 @@ cdv_intel_dp_start_link_train(struct psb_intel_encoder *encoder) if (!cdv_intel_dp_get_link_status(encoder)) break;
+ DRM_DEBUG_KMS("DP Link status %x, %x, %x, %x, %x, %x\n", + intel_dp->link_status[0], intel_dp->link_status[1], intel_dp->link_status[2], + intel_dp->link_status[3], intel_dp->link_status[4], intel_dp->link_status[5]); + if (cdv_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { DRM_DEBUG_KMS("PT1 train is done\n"); clock_recovery = true; @@ -1156,7 +1410,13 @@ cdv_intel_dp_complete_link_train(struct psb_intel_encoder *encoder) reg = DP | DP_LINK_TRAIN_PAT_2;
for (;;) { - /* channel eq pattern */ + + DRM_DEBUG_KMS("DP Link Train Set %x, Link_config %x, %x\n", + intel_dp->train_set[0], + intel_dp->link_configuration[0], + intel_dp->link_configuration[1]); + /* channel eq pattern */ + if (!cdv_intel_dp_set_link_train(encoder, reg, DP_TRAINING_PATTERN_2)) { DRM_DEBUG_KMS("Failure in aux-transfer setting pattern 2\n"); @@ -1177,6 +1437,10 @@ cdv_intel_dp_complete_link_train(struct psb_intel_encoder *encoder) if (!cdv_intel_dp_get_link_status(encoder)) break;
+ DRM_DEBUG_KMS("DP Link status %x, %x, %x, %x, %x, %x\n", + intel_dp->link_status[0], intel_dp->link_status[1], intel_dp->link_status[2], + intel_dp->link_status[3], intel_dp->link_status[4], intel_dp->link_status[5]); + /* Make sure clock is still ok */ if (!cdv_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { cdv_intel_dp_start_link_train(encoder); @@ -1271,12 +1535,18 @@ cdv_intel_dp_detect(struct drm_connector *connector, bool force) struct cdv_intel_dp *intel_dp = encoder->dev_priv; enum drm_connector_status status; struct edid *edid = NULL; + int edp = is_edp(encoder);
intel_dp->has_audio = false;
+ if (edp) + cdv_intel_edp_panel_vdd_on(encoder); status = cdv_dp_detect(encoder); - if (status != connector_status_connected) + if (status != connector_status_connected) { + if (edp) + cdv_intel_edp_panel_vdd_off(encoder); return status; + }
if (intel_dp->force_audio) { intel_dp->has_audio = intel_dp->force_audio > 0; @@ -1288,6 +1558,8 @@ cdv_intel_dp_detect(struct drm_connector *connector, bool force) kfree(edid); } } + if (edp) + cdv_intel_edp_panel_vdd_off(encoder);
return connector_status_connected; } @@ -1298,6 +1570,7 @@ static int cdv_intel_dp_get_modes(struct drm_connector *connector) struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; struct edid *edid = NULL; int ret = 0; + int edp = is_edp(intel_encoder);
edid = drm_get_edid(connector, &intel_dp->adapter); @@ -1307,6 +1580,42 @@ static int cdv_intel_dp_get_modes(struct drm_connector *connector) kfree(edid); }
+ if (is_edp(intel_encoder)) { + struct drm_device *dev = connector->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + + cdv_intel_edp_panel_vdd_off(intel_encoder); + if (ret) { + if (edp && !intel_dp->panel_fixed_mode) { + struct drm_display_mode *newmode; + list_for_each_entry(newmode, &connector->probed_modes, + head) { + if (newmode->type & DRM_MODE_TYPE_PREFERRED) { + intel_dp->panel_fixed_mode = + drm_mode_duplicate(dev, newmode); + break; + } + } + } + + return ret; + } + if (!intel_dp->panel_fixed_mode && dev_priv->lfp_lvds_vbt_mode) { + intel_dp->panel_fixed_mode = + drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); + if (intel_dp->panel_fixed_mode) { + intel_dp->panel_fixed_mode->type |= + DRM_MODE_TYPE_PREFERRED; + } + } + if (intel_dp->panel_fixed_mode != NULL) { + struct drm_display_mode *mode; + mode = drm_mode_duplicate(dev, intel_dp->panel_fixed_mode); + drm_mode_probed_add(connector, mode); + return 1; + } + } + return ret; }
@@ -1317,6 +1626,10 @@ cdv_intel_dp_detect_audio(struct drm_connector *connector) struct cdv_intel_dp *intel_dp = encoder->dev_priv; struct edid *edid; bool has_audio = false; + int edp = is_edp(encoder); + + if (edp) + cdv_intel_edp_panel_vdd_on(encoder);
edid = drm_get_edid(connector, &intel_dp->adapter); if (edid) { @@ -1325,6 +1638,8 @@ cdv_intel_dp_detect_audio(struct drm_connector *connector) connector->display_info.raw_edid = NULL; kfree(edid); } + if (edp) + cdv_intel_edp_panel_vdd_off(encoder);
return has_audio; } @@ -1386,12 +1701,19 @@ done: }
static void -cdv_intel_dp_destroy (struct drm_connector *connector) +cdv_intel_dp_destroy(struct drm_connector *connector) { struct psb_intel_encoder *psb_intel_encoder = psb_intel_attached_encoder(connector); struct cdv_intel_dp *intel_dp = psb_intel_encoder->dev_priv;
+ if (is_edp(psb_intel_encoder)) { + /* cdv_intel_panel_destroy_backlight(connector->dev); */ + if (intel_dp->panel_fixed_mode) { + kfree(intel_dp->panel_fixed_mode); + intel_dp->panel_fixed_mode = NULL; + } + } i2c_del_adapter(&intel_dp->adapter); drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); @@ -1436,6 +1758,26 @@ static void cdv_intel_dp_add_properties(struct drm_connector *connector) cdv_intel_attach_broadcast_rgb_property(connector); }
+/* check the VBT to see whether the eDP is on DP-D port */ +static bool cdv_intel_dpc_is_edp(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct child_device_config *p_child; + int i; + + if (!dev_priv->child_dev_num) + return false; + + for (i = 0; i < dev_priv->child_dev_num; i++) { + p_child = dev_priv->child_dev + i; + + if (p_child->dvo_port == PORT_IDPC && + p_child->device_type == DEVICE_TYPE_eDP) + return true; + } + return false; +} + void cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int output_reg) { @@ -1445,6 +1787,7 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev struct drm_encoder *encoder; struct cdv_intel_dp *intel_dp; const char *name = NULL; + int type = DRM_MODE_CONNECTOR_DisplayPort;
psb_intel_encoder = kzalloc(sizeof(struct psb_intel_encoder), GFP_KERNEL); if (!psb_intel_encoder) @@ -1456,14 +1799,22 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev if (!intel_dp) goto err_priv;
+ if ((output_reg == DP_C) && cdv_intel_dpc_is_edp(dev)) + type = DRM_MODE_CONNECTOR_eDP; + connector = &psb_intel_connector->base; encoder = &psb_intel_encoder->base;
- drm_connector_init(dev, connector, &cdv_intel_dp_connector_funcs, DRM_MODE_CONNECTOR_DisplayPort); + drm_connector_init(dev, connector, &cdv_intel_dp_connector_funcs, type); drm_encoder_init(dev, encoder, &cdv_intel_dp_enc_funcs, DRM_MODE_ENCODER_TMDS);
psb_intel_connector_attach_encoder(psb_intel_connector, psb_intel_encoder); - psb_intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; + + if (type == DRM_MODE_CONNECTOR_DisplayPort) + psb_intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; + else + psb_intel_encoder->type = INTEL_OUTPUT_EDP; +
psb_intel_encoder->dev_priv=intel_dp; intel_dp->encoder = psb_intel_encoder; @@ -1493,6 +1844,83 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev cdv_intel_dp_i2c_init(psb_intel_connector, psb_intel_encoder, name); /* FIXME:fail check */ cdv_intel_dp_add_properties(connector); + + if (is_edp(psb_intel_encoder)) { + int ret; + struct edp_power_seq cur; + u32 pp_on, pp_off, pp_div; + u32 pwm_ctrl; + + pp_on = REG_READ(PP_CONTROL); + pp_on &= ~PANEL_UNLOCK_MASK; + pp_on |= PANEL_UNLOCK_REGS; + + REG_WRITE(PP_CONTROL, pp_on); + + pwm_ctrl = REG_READ(BLC_PWM_CTL2); + pwm_ctrl |= PWM_PIPE_B; + REG_WRITE(BLC_PWM_CTL2, pwm_ctrl); + + pp_on = REG_READ(PP_ON_DELAYS); + pp_off = REG_READ(PP_OFF_DELAYS); + pp_div = REG_READ(PP_DIVISOR); + + /* Pull timing values out of registers */ + cur.t1_t3 = (pp_on & PANEL_POWER_UP_DELAY_MASK) >> + PANEL_POWER_UP_DELAY_SHIFT; + + cur.t8 = (pp_on & PANEL_LIGHT_ON_DELAY_MASK) >> + PANEL_LIGHT_ON_DELAY_SHIFT; + + cur.t9 = (pp_off & PANEL_LIGHT_OFF_DELAY_MASK) >> + PANEL_LIGHT_OFF_DELAY_SHIFT; + + cur.t10 = (pp_off & PANEL_POWER_DOWN_DELAY_MASK) >> + PANEL_POWER_DOWN_DELAY_SHIFT; + + cur.t11_t12 = ((pp_div & PANEL_POWER_CYCLE_DELAY_MASK) >> + PANEL_POWER_CYCLE_DELAY_SHIFT); + + DRM_DEBUG_KMS("cur t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n", + cur.t1_t3, cur.t8, cur.t9, cur.t10, cur.t11_t12); + + + intel_dp->panel_power_up_delay = cur.t1_t3 / 10; + intel_dp->backlight_on_delay = cur.t8 / 10; + intel_dp->backlight_off_delay = cur.t9 / 10; + intel_dp->panel_power_down_delay = cur.t10 / 10; + intel_dp->panel_power_cycle_delay = (cur.t11_t12 - 1) * 100; + + DRM_DEBUG_KMS("panel power up delay %d, power down delay %d, power cycle delay %d\n", + intel_dp->panel_power_up_delay, intel_dp->panel_power_down_delay, + intel_dp->panel_power_cycle_delay); + + DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n", + intel_dp->backlight_on_delay, intel_dp->backlight_off_delay); + + + cdv_intel_edp_panel_vdd_on(psb_intel_encoder); + ret = cdv_intel_dp_aux_native_read(psb_intel_encoder, DP_DPCD_REV, + intel_dp->dpcd, + sizeof(intel_dp->dpcd)); + cdv_intel_edp_panel_vdd_off(psb_intel_encoder); + if (ret == 0) { + /* if this fails, presume the device is a ghost */ + DRM_INFO("failed to retrieve link info, disabling eDP\n"); + cdv_intel_dp_encoder_destroy(encoder); + cdv_intel_dp_destroy(connector); + goto err_priv; + } else { + DRM_DEBUG_KMS("DPCD: Rev=%x LN_Rate=%x LN_CNT=%x LN_DOWNSP=%x\n", + intel_dp->dpcd[0], intel_dp->dpcd[1], + intel_dp->dpcd[2], intel_dp->dpcd[3]); + + } + /* The CDV reference driver moves pnale backlight setup into the displays that + have a backlight: this is a good idea and one we should probably adopt, however + we need to migrate all the drivers before we can do that */ + /*cdv_intel_panel_setup_backlight(dev); */ + } return;
err_priv: diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c index c7f9468..b362dd3 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c +++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c @@ -506,16 +506,8 @@ static int cdv_intel_lvds_set_property(struct drm_connector *connector, property, value)) return -1; - else { -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - struct drm_psb_private *dev_priv = - encoder->dev->dev_private; - struct backlight_device *bd = - dev_priv->backlight_device; - bd->props.brightness = value; - backlight_update_status(bd); -#endif - } + else + gma_backlight_set(encoder->dev, value); } else if (!strcmp(property->name, "DPMS") && encoder) { struct drm_encoder_helper_funcs *helpers = encoder->helper_private; diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 2de6b1f..884ba73 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -768,6 +768,9 @@ static void psb_setup_outputs(struct drm_device *dev) crtc_mask = (1 << 0) | (1 << 1); clone_mask = (1 << INTEL_OUTPUT_DISPLAYPORT); break; + case INTEL_OUTPUT_EDP: + crtc_mask = (1 << 1); + clone_mask = (1 << INTEL_OUTPUT_EDP); } encoder->possible_crtcs = crtc_mask; encoder->possible_clones = diff --git a/drivers/gpu/drm/gma500/intel_bios.c b/drivers/gpu/drm/gma500/intel_bios.c index 8d7caf0..4fb79cf 100644 --- a/drivers/gpu/drm/gma500/intel_bios.c +++ b/drivers/gpu/drm/gma500/intel_bios.c @@ -54,6 +54,98 @@ static void *find_section(struct bdb_header *bdb, int section_id) return NULL; }
+static void +parse_edp(struct drm_psb_private *dev_priv, struct bdb_header *bdb) +{ + struct bdb_edp *edp; + struct edp_power_seq *edp_pps; + struct edp_link_params *edp_link_params; + uint8_t panel_type; + + edp = find_section(bdb, BDB_EDP); + + dev_priv->edp.bpp = 18; + if (!edp) { + if (dev_priv->edp.support) { + DRM_DEBUG_KMS("No eDP BDB found but eDP panel supported, assume %dbpp panel color depth.\n", + dev_priv->edp.bpp); + } + return; + } + + panel_type = dev_priv->panel_type; + switch ((edp->color_depth >> (panel_type * 2)) & 3) { + case EDP_18BPP: + dev_priv->edp.bpp = 18; + break; + case EDP_24BPP: + dev_priv->edp.bpp = 24; + break; + case EDP_30BPP: + dev_priv->edp.bpp = 30; + break; + } + + /* Get the eDP sequencing and link info */ + edp_pps = &edp->power_seqs[panel_type]; + edp_link_params = &edp->link_params[panel_type]; + + dev_priv->edp.pps = *edp_pps; + + DRM_DEBUG_KMS("EDP timing in vbt t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n", + dev_priv->edp.pps.t1_t3, dev_priv->edp.pps.t8, + dev_priv->edp.pps.t9, dev_priv->edp.pps.t10, + dev_priv->edp.pps.t11_t12); + + dev_priv->edp.rate = edp_link_params->rate ? DP_LINK_BW_2_7 : + DP_LINK_BW_1_62; + switch (edp_link_params->lanes) { + case 0: + dev_priv->edp.lanes = 1; + break; + case 1: + dev_priv->edp.lanes = 2; + break; + case 3: + default: + dev_priv->edp.lanes = 4; + break; + } + DRM_DEBUG_KMS("VBT reports EDP: Lane_count %d, Lane_rate %d, Bpp %d\n", + dev_priv->edp.lanes, dev_priv->edp.rate, dev_priv->edp.bpp); + + switch (edp_link_params->preemphasis) { + case 0: + dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_0; + break; + case 1: + dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_3_5; + break; + case 2: + dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_6; + break; + case 3: + dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_9_5; + break; + } + switch (edp_link_params->vswing) { + case 0: + dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_400; + break; + case 1: + dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_600; + break; + case 2: + dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_800; + break; + case 3: + dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_1200; + break; + } + DRM_DEBUG_KMS("VBT reports EDP: VSwing %d, Preemph %d\n", + dev_priv->edp.vswing, dev_priv->edp.preemphasis); +} + static u16 get_blocksize(void *p) { @@ -154,6 +246,8 @@ static void parse_lfp_panel_data(struct drm_psb_private *dev_priv, return;
dev_priv->lvds_dither = lvds_options->pixel_dither; + dev_priv->panel_type = lvds_options->panel_type; + if (lvds_options->panel_type == 0xff) return;
@@ -340,6 +434,9 @@ parse_driver_features(struct drm_psb_private *dev_priv, if (!driver) return;
+ if (driver->lvds_config == BDB_DRIVER_FEATURE_EDP) + dev_priv->edp.support = 1; + /* This bit means to use 96Mhz for DPLL_A or not */ if (driver->primary_lfp_id) dev_priv->dplla_96mhz = true; @@ -437,6 +534,9 @@ int psb_intel_init_bios(struct drm_device *dev) size_t size; int i;
+ + dev_priv->panel_type = 0xff; + /* XXX Should this validation be moved to intel_opregion.c? */ if (dev_priv->opregion.vbt) { struct vbt_header *vbt = dev_priv->opregion.vbt; @@ -477,6 +577,7 @@ int psb_intel_init_bios(struct drm_device *dev) parse_sdvo_device_mapping(dev_priv, bdb); parse_device_mapping(dev_priv, bdb); parse_backlight_data(dev_priv, bdb); + parse_edp(dev_priv, bdb);
if (bios) pci_unmap_rom(pdev, bios); diff --git a/drivers/gpu/drm/gma500/intel_bios.h b/drivers/gpu/drm/gma500/intel_bios.h index 2e95523..c6267c9 100644 --- a/drivers/gpu/drm/gma500/intel_bios.h +++ b/drivers/gpu/drm/gma500/intel_bios.h @@ -23,6 +23,7 @@ #define _I830_BIOS_H_
#include <drm/drmP.h> +#include <drm/drm_dp_helper.h>
struct vbt_header { u8 signature[20]; /**< Always starts with 'VBT$' */ @@ -93,6 +94,7 @@ struct vbios_data { #define BDB_SDVO_LVDS_PNP_IDS 24 #define BDB_SDVO_LVDS_POWER_SEQ 25 #define BDB_TV_OPTIONS 26 +#define BDB_EDP 27 #define BDB_LVDS_OPTIONS 40 #define BDB_LVDS_LFP_DATA_PTRS 41 #define BDB_LVDS_LFP_DATA 42 @@ -391,6 +393,11 @@ struct bdb_sdvo_lvds_options { u8 panel_misc_bits_4; } __attribute__((packed));
+#define BDB_DRIVER_FEATURE_NO_LVDS 0 +#define BDB_DRIVER_FEATURE_INT_LVDS 1 +#define BDB_DRIVER_FEATURE_SDVO_LVDS 2 +#define BDB_DRIVER_FEATURE_EDP 3 + struct bdb_driver_features { u8 boot_dev_algorithm:1; u8 block_display_switch:1; @@ -431,6 +438,45 @@ struct bdb_driver_features { u8 custom_vbt_version; } __attribute__((packed));
+#define EDP_18BPP 0 +#define EDP_24BPP 1 +#define EDP_30BPP 2 +#define EDP_RATE_1_62 0 +#define EDP_RATE_2_7 1 +#define EDP_LANE_1 0 +#define EDP_LANE_2 1 +#define EDP_LANE_4 3 +#define EDP_PREEMPHASIS_NONE 0 +#define EDP_PREEMPHASIS_3_5dB 1 +#define EDP_PREEMPHASIS_6dB 2 +#define EDP_PREEMPHASIS_9_5dB 3 +#define EDP_VSWING_0_4V 0 +#define EDP_VSWING_0_6V 1 +#define EDP_VSWING_0_8V 2 +#define EDP_VSWING_1_2V 3 + +struct edp_power_seq { + u16 t1_t3; + u16 t8; + u16 t9; + u16 t10; + u16 t11_t12; +} __attribute__ ((packed)); + +struct edp_link_params { + u8 rate:4; + u8 lanes:4; + u8 preemphasis:4; + u8 vswing:4; +} __attribute__ ((packed)); + +struct bdb_edp { + struct edp_power_seq power_seqs[16]; + u32 color_depth; + u32 sdrrs_msa_timing_delay; + struct edp_link_params link_params[16]; +} __attribute__ ((packed)); + extern int psb_intel_init_bios(struct drm_device *dev); extern void psb_intel_destroy_bios(struct drm_device *dev);
diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_output.c b/drivers/gpu/drm/gma500/mdfld_dsi_output.c index 5675d93..32dba2a 100644 --- a/drivers/gpu/drm/gma500/mdfld_dsi_output.c +++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.c @@ -299,17 +299,8 @@ static int mdfld_dsi_connector_set_property(struct drm_connector *connector, if (drm_connector_property_set_value(connector, property, value)) goto set_prop_error; - else { -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - struct backlight_device *psb_bd; - - psb_bd = mdfld_get_backlight_device(); - if (psb_bd) { - psb_bd->props.brightness = value; - mdfld_set_brightness(psb_bd); - } -#endif - } + else + gma_backlight_set(encoder->dev, value); } set_prop_done: return 0; diff --git a/drivers/gpu/drm/gma500/mid_bios.c b/drivers/gpu/drm/gma500/mid_bios.c index b2a790b..850cd3f 100644 --- a/drivers/gpu/drm/gma500/mid_bios.c +++ b/drivers/gpu/drm/gma500/mid_bios.c @@ -118,20 +118,20 @@ static void mid_get_pci_revID(struct drm_psb_private *dev_priv) dev_priv->platform_rev_id); }
-struct vbt_header { +struct mid_vbt_header { u32 signature; u8 revision; } __packed;
/* The same for r0 and r1 */ struct vbt_r0 { - struct vbt_header vbt_header; + struct mid_vbt_header vbt_header; u8 size; u8 checksum; } __packed;
struct vbt_r10 { - struct vbt_header vbt_header; + struct mid_vbt_header vbt_header; u8 checksum; u16 size; u8 panel_count; @@ -281,7 +281,7 @@ static void mid_get_vbt_data(struct drm_psb_private *dev_priv) struct drm_device *dev = dev_priv->dev; u32 addr; u8 __iomem *vbt_virtual; - struct vbt_header vbt_header; + struct mid_vbt_header vbt_header; struct pci_dev *pci_gfx_root = pci_get_bus_and_slot(0, PCI_DEVFN(2, 0)); int ret = -1;
diff --git a/drivers/gpu/drm/gma500/opregion.c b/drivers/gpu/drm/gma500/opregion.c index c430bd4..ad0d6de 100644 --- a/drivers/gpu/drm/gma500/opregion.c +++ b/drivers/gpu/drm/gma500/opregion.c @@ -166,8 +166,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
if (config_enabled(CONFIG_BACKLIGHT_CLASS_DEVICE)) { int max = bd->props.max_brightness; - bd->props.brightness = bclp * max / 255; - backlight_update_status(bd); + gma_backlight_set(dev, bclp * max / 255); }
asle->cblv = (bclp * 0x64) / 0xff | ASLE_CBLV_VALID; diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index bc1bc17..223ff5b 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h @@ -27,6 +27,7 @@ #include "gma_drm.h" #include "psb_reg.h" #include "psb_intel_drv.h" +#include "intel_bios.h" #include "gtt.h" #include "power.h" #include "opregion.h" @@ -612,6 +613,8 @@ struct drm_psb_private { */ struct backlight_device *backlight_device; struct drm_property *backlight_property; + bool backlight_enabled; + int backlight_level; uint32_t blc_adj1; uint32_t blc_adj2;
@@ -639,6 +642,19 @@ struct drm_psb_private { int mdfld_panel_id;
bool dplla_96mhz; /* DPLL data from the VBT */ + + struct { + int rate; + int lanes; + int preemphasis; + int vswing; + + bool initialized; + bool support; + int bpp; + struct edp_power_seq pps; + } edp; + uint8_t panel_type; };
@@ -795,6 +811,9 @@ extern int psb_fbdev_init(struct drm_device *dev); /* backlight.c */ int gma_backlight_init(struct drm_device *dev); void gma_backlight_exit(struct drm_device *dev); +void gma_backlight_disable(struct drm_device *dev); +void gma_backlight_enable(struct drm_device *dev); +void gma_backlight_set(struct drm_device *dev, int v);
/* oaktrail_crtc.c */ extern const struct drm_crtc_helper_funcs oaktrail_helper_funcs; diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c index 37adc9e..2a4c3a9 100644 --- a/drivers/gpu/drm/gma500/psb_intel_lvds.c +++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c @@ -630,17 +630,8 @@ int psb_intel_lvds_set_property(struct drm_connector *connector, property, value)) goto set_prop_error; - else { -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - struct drm_psb_private *devp = - encoder->dev->dev_private; - struct backlight_device *bd = devp->backlight_device; - if (bd) { - bd->props.brightness = value; - backlight_update_status(bd); - } -#endif - } + else + gma_backlight_set(encoder->dev, value); } else if (!strcmp(property->name, "DPMS")) { struct drm_encoder_helper_funcs *hfuncs = encoder->helper_private; diff --git a/drivers/gpu/drm/gma500/psb_intel_reg.h b/drivers/gpu/drm/gma500/psb_intel_reg.h index d3091fc..389e969 100644 --- a/drivers/gpu/drm/gma500/psb_intel_reg.h +++ b/drivers/gpu/drm/gma500/psb_intel_reg.h @@ -173,15 +173,46 @@ #define PP_SEQUENCE_ON (1 << 28) #define PP_SEQUENCE_OFF (2 << 28) #define PP_SEQUENCE_MASK 0x30000000 +#define PP_CYCLE_DELAY_ACTIVE (1 << 27) +#define PP_SEQUENCE_STATE_ON_IDLE (1 << 3) +#define PP_SEQUENCE_STATE_MASK 0x0000000f + #define PP_CONTROL 0x61204 #define POWER_TARGET_ON (1 << 0) - +#define PANEL_UNLOCK_REGS (0xabcd << 16) +#define PANEL_UNLOCK_MASK (0xffff << 16) +#define EDP_FORCE_VDD (1 << 3) +#define EDP_BLC_ENABLE (1 << 2) +#define PANEL_POWER_RESET (1 << 1) +#define PANEL_POWER_OFF (0 << 0) +#define PANEL_POWER_ON (1 << 0) + +/* Poulsbo/Oaktrail */ #define LVDSPP_ON 0x61208 #define LVDSPP_OFF 0x6120c #define PP_CYCLE 0x61210
+/* Cedartrail */ #define PP_ON_DELAYS 0x61208 /* Cedartrail */ +#define PANEL_PORT_SELECT_MASK (3 << 30) +#define PANEL_PORT_SELECT_LVDS (0 << 30) +#define PANEL_PORT_SELECT_EDP (1 << 30) +#define PANEL_POWER_UP_DELAY_MASK (0x1fff0000) +#define PANEL_POWER_UP_DELAY_SHIFT 16 +#define PANEL_LIGHT_ON_DELAY_MASK (0x1fff) +#define PANEL_LIGHT_ON_DELAY_SHIFT 0 + #define PP_OFF_DELAYS 0x6120c /* Cedartrail */ +#define PANEL_POWER_DOWN_DELAY_MASK (0x1fff0000) +#define PANEL_POWER_DOWN_DELAY_SHIFT 16 +#define PANEL_LIGHT_OFF_DELAY_MASK (0x1fff) +#define PANEL_LIGHT_OFF_DELAY_SHIFT 0 + +#define PP_DIVISOR 0x61210 /* Cedartrail */ +#define PP_REFERENCE_DIVIDER_MASK (0xffffff00) +#define PP_REFERENCE_DIVIDER_SHIFT 8 +#define PANEL_POWER_CYCLE_DELAY_MASK (0x1f) +#define PANEL_POWER_CYCLE_DELAY_SHIFT 0
#define PFIT_CONTROL 0x61230 #define PFIT_ENABLE (1 << 31) @@ -1503,4 +1534,9 @@ No status bits are changed. #define PIPE_DP_LINK_M(pipe) _PIPE(pipe, _PIPEA_DP_LINK_M, _PIPEB_DP_LINK_M) #define PIPE_DP_LINK_N(pipe) _PIPE(pipe, _PIPEA_DP_LINK_N, _PIPEB_DP_LINK_N)
+#define PIPE_BPC_MASK (7 << 5) +#define PIPE_8BPC (0 << 5) +#define PIPE_10BPC (1 << 5) +#define PIPE_6BPC (2 << 5) + #endif
From: Zhao Yakui yakui.zhao@intel.com
Disable the clock gating of display controller to make DP/eDP work well
I don't know why the DP/eDP is affected by the clock gating. But the test shows that it really fixes the DP/eDP clock issue during enabling DP/eDP.
Signed-off-by: Zhao Yakui yakui.zhao@intel.com [Updated to only apply the workaround if the device has DP. We don't want to do this on netbooks] Signed-off-by: Alan Cox alan@linux.intel.com ---
drivers/gpu/drm/gma500/cdv_intel_dp.c | 24 ++++++++++++++++++++++++ drivers/gpu/drm/gma500/psb_intel_reg.h | 4 ++++ 2 files changed, 28 insertions(+)
diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index 0571ef9b..9bacce3 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -1778,6 +1778,28 @@ static bool cdv_intel_dpc_is_edp(struct drm_device *dev) return false; }
+/* Cedarview display clock gating + + We need this disable dot get correct behaviour while enabling + DP/eDP. TODO - investigate if we can turn it back to normality + after enabling */ +static void cdv_disable_intel_clock_gating(struct drm_device *dev) +{ + u32 reg_value; + reg_value = REG_READ(DSPCLK_GATE_D); + + reg_value |= (DPUNIT_PIPEB_GATE_DISABLE | + DPUNIT_PIPEA_GATE_DISABLE | + DPCUNIT_CLOCK_GATE_DISABLE | + DPLSUNIT_CLOCK_GATE_DISABLE | + DPOUNIT_CLOCK_GATE_DISABLE | + DPIOUNIT_CLOCK_GATE_DISABLE); + + REG_WRITE(DSPCLK_GATE_D, reg_value); + + udelay(500); +} + void cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int output_reg) { @@ -1841,6 +1863,8 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev break; }
+ cdv_disable_intel_clock_gating(dev); + cdv_intel_dp_i2c_init(psb_intel_connector, psb_intel_encoder, name); /* FIXME:fail check */ cdv_intel_dp_add_properties(connector); diff --git a/drivers/gpu/drm/gma500/psb_intel_reg.h b/drivers/gpu/drm/gma500/psb_intel_reg.h index 389e969..d914719 100644 --- a/drivers/gpu/drm/gma500/psb_intel_reg.h +++ b/drivers/gpu/drm/gma500/psb_intel_reg.h @@ -1313,6 +1313,10 @@ No status bits are changed. # define VRHUNIT_CLOCK_GATE_DISABLE (1 << 28) /* Fixed value on CDV */ # define DPOUNIT_CLOCK_GATE_DISABLE (1 << 11) # define DPIOUNIT_CLOCK_GATE_DISABLE (1 << 6) +# define DPUNIT_PIPEB_GATE_DISABLE (1 << 30) +# define DPUNIT_PIPEA_GATE_DISABLE (1 << 25) +# define DPCUNIT_CLOCK_GATE_DISABLE (1 << 24) +# define DPLSUNIT_CLOCK_GATE_DISABLE (1 << 13)
#define RAMCLK_GATE_D 0x6210
dri-devel@lists.freedesktop.org