summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/intel_cdclk.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/i915/intel_cdclk.c')
-rw-r--r--drivers/gpu/drm/i915/intel_cdclk.c94
1 files changed, 70 insertions, 24 deletions
diff --git a/drivers/gpu/drm/i915/intel_cdclk.c b/drivers/gpu/drm/i915/intel_cdclk.c
index d77e2bec1e29..ee788d5be5e3 100644
--- a/drivers/gpu/drm/i915/intel_cdclk.c
+++ b/drivers/gpu/drm/i915/intel_cdclk.c
@@ -858,7 +858,7 @@ static void skl_get_cdclk(struct drm_i915_private *dev_priv,
skl_dpll0_update(dev_priv, cdclk_state);
- cdclk_state->cdclk = cdclk_state->ref;
+ cdclk_state->cdclk = cdclk_state->bypass = cdclk_state->ref;
if (cdclk_state->vco == 0)
goto out;
@@ -1006,7 +1006,7 @@ static void skl_set_cdclk(struct drm_i915_private *dev_priv,
/* Choose frequency for this cdclk */
switch (cdclk) {
default:
- WARN_ON(cdclk != dev_priv->cdclk.hw.ref);
+ WARN_ON(cdclk != dev_priv->cdclk.hw.bypass);
WARN_ON(vco != 0);
/* fall through */
case 308571:
@@ -1085,7 +1085,7 @@ static void skl_sanitize_cdclk(struct drm_i915_private *dev_priv)
/* Is PLL enabled and locked ? */
if (dev_priv->cdclk.hw.vco == 0 ||
- dev_priv->cdclk.hw.cdclk == dev_priv->cdclk.hw.ref)
+ dev_priv->cdclk.hw.cdclk == dev_priv->cdclk.hw.bypass)
goto sanitize;
/* DPLL okay; verify the cdclock
@@ -1159,7 +1159,7 @@ void skl_uninit_cdclk(struct drm_i915_private *dev_priv)
{
struct intel_cdclk_state cdclk_state = dev_priv->cdclk.hw;
- cdclk_state.cdclk = cdclk_state.ref;
+ cdclk_state.cdclk = cdclk_state.bypass;
cdclk_state.vco = 0;
cdclk_state.voltage_level = skl_calc_voltage_level(cdclk_state.cdclk);
@@ -1199,7 +1199,7 @@ static int bxt_de_pll_vco(struct drm_i915_private *dev_priv, int cdclk)
{
int ratio;
- if (cdclk == dev_priv->cdclk.hw.ref)
+ if (cdclk == dev_priv->cdclk.hw.bypass)
return 0;
switch (cdclk) {
@@ -1224,7 +1224,7 @@ static int glk_de_pll_vco(struct drm_i915_private *dev_priv, int cdclk)
{
int ratio;
- if (cdclk == dev_priv->cdclk.hw.ref)
+ if (cdclk == dev_priv->cdclk.hw.bypass)
return 0;
switch (cdclk) {
@@ -1268,7 +1268,7 @@ static void bxt_get_cdclk(struct drm_i915_private *dev_priv,
bxt_de_pll_update(dev_priv, cdclk_state);
- cdclk_state->cdclk = cdclk_state->ref;
+ cdclk_state->cdclk = cdclk_state->bypass = cdclk_state->ref;
if (cdclk_state->vco == 0)
goto out;
@@ -1352,7 +1352,7 @@ static void bxt_set_cdclk(struct drm_i915_private *dev_priv,
/* cdclk = vco / 2 / div{1,1.5,2,4} */
switch (DIV_ROUND_CLOSEST(vco, cdclk)) {
default:
- WARN_ON(cdclk != dev_priv->cdclk.hw.ref);
+ WARN_ON(cdclk != dev_priv->cdclk.hw.bypass);
WARN_ON(vco != 0);
/* fall through */
case 2:
@@ -1370,10 +1370,15 @@ static void bxt_set_cdclk(struct drm_i915_private *dev_priv,
break;
}
- /* Inform power controller of upcoming frequency change */
+ /*
+ * Inform power controller of upcoming frequency change. BSpec
+ * requires us to wait up to 150usec, but that leads to timeouts;
+ * the 2ms used here is based on experiment.
+ */
mutex_lock(&dev_priv->pcu_lock);
- ret = sandybridge_pcode_write(dev_priv, HSW_PCODE_DE_WRITE_FREQ_REQ,
- 0x80000000);
+ ret = sandybridge_pcode_write_timeout(dev_priv,
+ HSW_PCODE_DE_WRITE_FREQ_REQ,
+ 0x80000000, 150, 2);
mutex_unlock(&dev_priv->pcu_lock);
if (ret) {
@@ -1404,8 +1409,15 @@ static void bxt_set_cdclk(struct drm_i915_private *dev_priv,
I915_WRITE(CDCLK_CTL, val);
mutex_lock(&dev_priv->pcu_lock);
- ret = sandybridge_pcode_write(dev_priv, HSW_PCODE_DE_WRITE_FREQ_REQ,
- cdclk_state->voltage_level);
+ /*
+ * The timeout isn't specified, the 2ms used here is based on
+ * experiment.
+ * FIXME: Waiting for the request completion could be delayed until
+ * the next PCODE request based on BSpec.
+ */
+ ret = sandybridge_pcode_write_timeout(dev_priv,
+ HSW_PCODE_DE_WRITE_FREQ_REQ,
+ cdclk_state->voltage_level, 150, 2);
mutex_unlock(&dev_priv->pcu_lock);
if (ret) {
@@ -1425,7 +1437,7 @@ static void bxt_sanitize_cdclk(struct drm_i915_private *dev_priv)
intel_dump_cdclk_state(&dev_priv->cdclk.hw, "Current CDCLK");
if (dev_priv->cdclk.hw.vco == 0 ||
- dev_priv->cdclk.hw.cdclk == dev_priv->cdclk.hw.ref)
+ dev_priv->cdclk.hw.cdclk == dev_priv->cdclk.hw.bypass)
goto sanitize;
/* DPLL okay; verify the cdclock
@@ -1514,7 +1526,7 @@ void bxt_uninit_cdclk(struct drm_i915_private *dev_priv)
{
struct intel_cdclk_state cdclk_state = dev_priv->cdclk.hw;
- cdclk_state.cdclk = cdclk_state.ref;
+ cdclk_state.cdclk = cdclk_state.bypass;
cdclk_state.vco = 0;
cdclk_state.voltage_level = bxt_calc_voltage_level(cdclk_state.cdclk);
@@ -1574,7 +1586,7 @@ static void cnl_get_cdclk(struct drm_i915_private *dev_priv,
cnl_cdclk_pll_update(dev_priv, cdclk_state);
- cdclk_state->cdclk = cdclk_state->ref;
+ cdclk_state->cdclk = cdclk_state->bypass = cdclk_state->ref;
if (cdclk_state->vco == 0)
goto out;
@@ -1660,7 +1672,7 @@ static void cnl_set_cdclk(struct drm_i915_private *dev_priv,
/* cdclk = vco / 2 / div{1,2} */
switch (DIV_ROUND_CLOSEST(vco, cdclk)) {
default:
- WARN_ON(cdclk != dev_priv->cdclk.hw.ref);
+ WARN_ON(cdclk != dev_priv->cdclk.hw.bypass);
WARN_ON(vco != 0);
/* fall through */
case 2:
@@ -1705,7 +1717,7 @@ static int cnl_cdclk_pll_vco(struct drm_i915_private *dev_priv, int cdclk)
{
int ratio;
- if (cdclk == dev_priv->cdclk.hw.ref)
+ if (cdclk == dev_priv->cdclk.hw.bypass)
return 0;
switch (cdclk) {
@@ -1732,7 +1744,7 @@ static void cnl_sanitize_cdclk(struct drm_i915_private *dev_priv)
intel_dump_cdclk_state(&dev_priv->cdclk.hw, "Current CDCLK");
if (dev_priv->cdclk.hw.vco == 0 ||
- dev_priv->cdclk.hw.cdclk == dev_priv->cdclk.hw.ref)
+ dev_priv->cdclk.hw.cdclk == dev_priv->cdclk.hw.bypass)
goto sanitize;
/* DPLL okay; verify the cdclock
@@ -1805,7 +1817,7 @@ void cnl_uninit_cdclk(struct drm_i915_private *dev_priv)
{
struct intel_cdclk_state cdclk_state = dev_priv->cdclk.hw;
- cdclk_state.cdclk = cdclk_state.ref;
+ cdclk_state.cdclk = cdclk_state.bypass;
cdclk_state.vco = 0;
cdclk_state.voltage_level = cnl_calc_voltage_level(cdclk_state.cdclk);
@@ -1846,9 +1858,10 @@ bool intel_cdclk_changed(const struct intel_cdclk_state *a,
void intel_dump_cdclk_state(const struct intel_cdclk_state *cdclk_state,
const char *context)
{
- DRM_DEBUG_DRIVER("%s %d kHz, VCO %d kHz, ref %d kHz, voltage level %d\n",
+ DRM_DEBUG_DRIVER("%s %d kHz, VCO %d kHz, ref %d kHz, bypass %d kHz, voltage level %d\n",
context, cdclk_state->cdclk, cdclk_state->vco,
- cdclk_state->ref, cdclk_state->voltage_level);
+ cdclk_state->ref, cdclk_state->bypass,
+ cdclk_state->voltage_level);
}
/**
@@ -1940,6 +1953,14 @@ int intel_crtc_compute_min_cdclk(const struct intel_crtc_state *crtc_state)
if (crtc_state->has_audio && INTEL_GEN(dev_priv) >= 9)
min_cdclk = max(2 * 96000, min_cdclk);
+ /*
+ * On Valleyview some DSI panels lose (v|h)sync when the clock is lower
+ * than 320000KHz.
+ */
+ if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DSI) &&
+ IS_VALLEYVIEW(dev_priv))
+ min_cdclk = max(320000, min_cdclk);
+
if (min_cdclk > dev_priv->max_cdclk_freq) {
DRM_DEBUG_KMS("required cdclk (%d kHz) exceeds max (%d kHz)\n",
min_cdclk, dev_priv->max_cdclk_freq);
@@ -2334,6 +2355,30 @@ static int cnp_rawclk(struct drm_i915_private *dev_priv)
return divider + fraction;
}
+static int icp_rawclk(struct drm_i915_private *dev_priv)
+{
+ u32 rawclk;
+ int divider, numerator, denominator, frequency;
+
+ if (I915_READ(SFUSE_STRAP) & SFUSE_STRAP_RAW_FREQUENCY) {
+ frequency = 24000;
+ divider = 23;
+ numerator = 0;
+ denominator = 0;
+ } else {
+ frequency = 19200;
+ divider = 18;
+ numerator = 1;
+ denominator = 4;
+ }
+
+ rawclk = CNP_RAWCLK_DIV(divider) | ICP_RAWCLK_NUM(numerator) |
+ ICP_RAWCLK_DEN(denominator);
+
+ I915_WRITE(PCH_RAWCLK_FREQ, rawclk);
+ return frequency;
+}
+
static int pch_rawclk(struct drm_i915_private *dev_priv)
{
return (I915_READ(PCH_RAWCLK_FREQ) & RAWCLK_FREQ_MASK) * 1000;
@@ -2381,8 +2426,9 @@ static int g4x_hrawclk(struct drm_i915_private *dev_priv)
*/
void intel_update_rawclk(struct drm_i915_private *dev_priv)
{
-
- if (HAS_PCH_CNP(dev_priv))
+ if (HAS_PCH_ICP(dev_priv))
+ dev_priv->rawclk_freq = icp_rawclk(dev_priv);
+ else if (HAS_PCH_CNP(dev_priv))
dev_priv->rawclk_freq = cnp_rawclk(dev_priv);
else if (HAS_PCH_SPLIT(dev_priv))
dev_priv->rawclk_freq = pch_rawclk(dev_priv);
OpenPOWER on IntegriCloud