diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_i2c.c')
-rw-r--r-- | drivers/gpu/drm/i915/intel_i2c.c | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index d1c1e0f7f262..2ca17b14b6c1 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c | |||
@@ -34,6 +34,11 @@ | |||
34 | #include <drm/i915_drm.h> | 34 | #include <drm/i915_drm.h> |
35 | #include "i915_drv.h" | 35 | #include "i915_drv.h" |
36 | 36 | ||
37 | enum disp_clk { | ||
38 | CDCLK, | ||
39 | CZCLK | ||
40 | }; | ||
41 | |||
37 | struct gmbus_port { | 42 | struct gmbus_port { |
38 | const char *name; | 43 | const char *name; |
39 | int reg; | 44 | int reg; |
@@ -58,10 +63,69 @@ to_intel_gmbus(struct i2c_adapter *i2c) | |||
58 | return container_of(i2c, struct intel_gmbus, adapter); | 63 | return container_of(i2c, struct intel_gmbus, adapter); |
59 | } | 64 | } |
60 | 65 | ||
66 | static int get_disp_clk_div(struct drm_i915_private *dev_priv, | ||
67 | enum disp_clk clk) | ||
68 | { | ||
69 | u32 reg_val; | ||
70 | int clk_ratio; | ||
71 | |||
72 | reg_val = I915_READ(CZCLK_CDCLK_FREQ_RATIO); | ||
73 | |||
74 | if (clk == CDCLK) | ||
75 | clk_ratio = | ||
76 | ((reg_val & CDCLK_FREQ_MASK) >> CDCLK_FREQ_SHIFT) + 1; | ||
77 | else | ||
78 | clk_ratio = (reg_val & CZCLK_FREQ_MASK) + 1; | ||
79 | |||
80 | return clk_ratio; | ||
81 | } | ||
82 | |||
83 | static void gmbus_set_freq(struct drm_i915_private *dev_priv) | ||
84 | { | ||
85 | int vco_freq[] = { 800, 1600, 2000, 2400 }; | ||
86 | int gmbus_freq = 0, cdclk_div, hpll_freq; | ||
87 | |||
88 | BUG_ON(!IS_VALLEYVIEW(dev_priv->dev)); | ||
89 | |||
90 | /* Skip setting the gmbus freq if BIOS has already programmed it */ | ||
91 | if (I915_READ(GMBUSFREQ_VLV) != 0xA0) | ||
92 | return; | ||
93 | |||
94 | /* Obtain SKU information */ | ||
95 | mutex_lock(&dev_priv->dpio_lock); | ||
96 | hpll_freq = | ||
97 | vlv_cck_read(dev_priv, CCK_FUSE_REG) & CCK_FUSE_HPLL_FREQ_MASK; | ||
98 | mutex_unlock(&dev_priv->dpio_lock); | ||
99 | |||
100 | /* Get the CDCLK divide ratio */ | ||
101 | cdclk_div = get_disp_clk_div(dev_priv, CDCLK); | ||
102 | |||
103 | /* | ||
104 | * Program the gmbus_freq based on the cdclk frequency. | ||
105 | * BSpec erroneously claims we should aim for 4MHz, but | ||
106 | * in fact 1MHz is the correct frequency. | ||
107 | */ | ||
108 | if (cdclk_div) | ||
109 | gmbus_freq = (vco_freq[hpll_freq] << 1) / cdclk_div; | ||
110 | |||
111 | if (WARN_ON(gmbus_freq == 0)) | ||
112 | return; | ||
113 | |||
114 | I915_WRITE(GMBUSFREQ_VLV, gmbus_freq); | ||
115 | } | ||
116 | |||
61 | void | 117 | void |
62 | intel_i2c_reset(struct drm_device *dev) | 118 | intel_i2c_reset(struct drm_device *dev) |
63 | { | 119 | { |
64 | struct drm_i915_private *dev_priv = dev->dev_private; | 120 | struct drm_i915_private *dev_priv = dev->dev_private; |
121 | |||
122 | /* | ||
123 | * In BIOS-less system, program the correct gmbus frequency | ||
124 | * before reading edid. | ||
125 | */ | ||
126 | if (IS_VALLEYVIEW(dev)) | ||
127 | gmbus_set_freq(dev_priv); | ||
128 | |||
65 | I915_WRITE(dev_priv->gpio_mmio_base + GMBUS0, 0); | 129 | I915_WRITE(dev_priv->gpio_mmio_base + GMBUS0, 0); |
66 | I915_WRITE(dev_priv->gpio_mmio_base + GMBUS4, 0); | 130 | I915_WRITE(dev_priv->gpio_mmio_base + GMBUS4, 0); |
67 | } | 131 | } |