diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_panel.c')
-rw-r--r-- | drivers/gpu/drm/i915/intel_panel.c | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index e7f5299d9d57..a06ff07a4d3b 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c | |||
@@ -30,6 +30,8 @@ | |||
30 | 30 | ||
31 | #include "intel_drv.h" | 31 | #include "intel_drv.h" |
32 | 32 | ||
33 | #define PCI_LBPC 0xf4 /* legacy/combination backlight modes */ | ||
34 | |||
33 | void | 35 | void |
34 | intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, | 36 | intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, |
35 | struct drm_display_mode *adjusted_mode) | 37 | struct drm_display_mode *adjusted_mode) |
@@ -109,3 +111,197 @@ done: | |||
109 | dev_priv->pch_pf_pos = (x << 16) | y; | 111 | dev_priv->pch_pf_pos = (x << 16) | y; |
110 | dev_priv->pch_pf_size = (width << 16) | height; | 112 | dev_priv->pch_pf_size = (width << 16) | height; |
111 | } | 113 | } |
114 | |||
115 | static int is_backlight_combination_mode(struct drm_device *dev) | ||
116 | { | ||
117 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
118 | |||
119 | if (INTEL_INFO(dev)->gen >= 4) | ||
120 | return I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE; | ||
121 | |||
122 | if (IS_GEN2(dev)) | ||
123 | return I915_READ(BLC_PWM_CTL) & BLM_LEGACY_MODE; | ||
124 | |||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static u32 i915_read_blc_pwm_ctl(struct drm_i915_private *dev_priv) | ||
129 | { | ||
130 | u32 val; | ||
131 | |||
132 | /* Restore the CTL value if it lost, e.g. GPU reset */ | ||
133 | |||
134 | if (HAS_PCH_SPLIT(dev_priv->dev)) { | ||
135 | val = I915_READ(BLC_PWM_PCH_CTL2); | ||
136 | if (dev_priv->saveBLC_PWM_CTL2 == 0) { | ||
137 | dev_priv->saveBLC_PWM_CTL2 = val; | ||
138 | } else if (val == 0) { | ||
139 | I915_WRITE(BLC_PWM_PCH_CTL2, | ||
140 | dev_priv->saveBLC_PWM_CTL); | ||
141 | val = dev_priv->saveBLC_PWM_CTL; | ||
142 | } | ||
143 | } else { | ||
144 | val = I915_READ(BLC_PWM_CTL); | ||
145 | if (dev_priv->saveBLC_PWM_CTL == 0) { | ||
146 | dev_priv->saveBLC_PWM_CTL = val; | ||
147 | dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_CTL2); | ||
148 | } else if (val == 0) { | ||
149 | I915_WRITE(BLC_PWM_CTL, | ||
150 | dev_priv->saveBLC_PWM_CTL); | ||
151 | I915_WRITE(BLC_PWM_CTL2, | ||
152 | dev_priv->saveBLC_PWM_CTL2); | ||
153 | val = dev_priv->saveBLC_PWM_CTL; | ||
154 | } | ||
155 | } | ||
156 | |||
157 | return val; | ||
158 | } | ||
159 | |||
160 | u32 intel_panel_get_max_backlight(struct drm_device *dev) | ||
161 | { | ||
162 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
163 | u32 max; | ||
164 | |||
165 | max = i915_read_blc_pwm_ctl(dev_priv); | ||
166 | if (max == 0) { | ||
167 | /* XXX add code here to query mode clock or hardware clock | ||
168 | * and program max PWM appropriately. | ||
169 | */ | ||
170 | printk_once(KERN_WARNING "fixme: max PWM is zero.\n"); | ||
171 | return 1; | ||
172 | } | ||
173 | |||
174 | if (HAS_PCH_SPLIT(dev)) { | ||
175 | max >>= 16; | ||
176 | } else { | ||
177 | if (IS_PINEVIEW(dev)) { | ||
178 | max >>= 17; | ||
179 | } else { | ||
180 | max >>= 16; | ||
181 | if (INTEL_INFO(dev)->gen < 4) | ||
182 | max &= ~1; | ||
183 | } | ||
184 | |||
185 | if (is_backlight_combination_mode(dev)) | ||
186 | max *= 0xff; | ||
187 | } | ||
188 | |||
189 | DRM_DEBUG_DRIVER("max backlight PWM = %d\n", max); | ||
190 | return max; | ||
191 | } | ||
192 | |||
193 | u32 intel_panel_get_backlight(struct drm_device *dev) | ||
194 | { | ||
195 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
196 | u32 val; | ||
197 | |||
198 | if (HAS_PCH_SPLIT(dev)) { | ||
199 | val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; | ||
200 | } else { | ||
201 | val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; | ||
202 | if (IS_PINEVIEW(dev)) | ||
203 | val >>= 1; | ||
204 | |||
205 | if (is_backlight_combination_mode(dev)){ | ||
206 | u8 lbpc; | ||
207 | |||
208 | val &= ~1; | ||
209 | pci_read_config_byte(dev->pdev, PCI_LBPC, &lbpc); | ||
210 | val *= lbpc; | ||
211 | } | ||
212 | } | ||
213 | |||
214 | DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val); | ||
215 | return val; | ||
216 | } | ||
217 | |||
218 | static void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level) | ||
219 | { | ||
220 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
221 | u32 val = I915_READ(BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK; | ||
222 | I915_WRITE(BLC_PWM_CPU_CTL, val | level); | ||
223 | } | ||
224 | |||
225 | void intel_panel_set_backlight(struct drm_device *dev, u32 level) | ||
226 | { | ||
227 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
228 | u32 tmp; | ||
229 | |||
230 | DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level); | ||
231 | |||
232 | if (HAS_PCH_SPLIT(dev)) | ||
233 | return intel_pch_panel_set_backlight(dev, level); | ||
234 | |||
235 | if (is_backlight_combination_mode(dev)){ | ||
236 | u32 max = intel_panel_get_max_backlight(dev); | ||
237 | u8 lbpc; | ||
238 | |||
239 | lbpc = level * 0xfe / max + 1; | ||
240 | level /= lbpc; | ||
241 | pci_write_config_byte(dev->pdev, PCI_LBPC, lbpc); | ||
242 | } | ||
243 | |||
244 | tmp = I915_READ(BLC_PWM_CTL); | ||
245 | if (IS_PINEVIEW(dev)) { | ||
246 | tmp &= ~(BACKLIGHT_DUTY_CYCLE_MASK - 1); | ||
247 | level <<= 1; | ||
248 | } else | ||
249 | tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK; | ||
250 | I915_WRITE(BLC_PWM_CTL, tmp | level); | ||
251 | } | ||
252 | |||
253 | void intel_panel_disable_backlight(struct drm_device *dev) | ||
254 | { | ||
255 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
256 | |||
257 | if (dev_priv->backlight_enabled) { | ||
258 | dev_priv->backlight_level = intel_panel_get_backlight(dev); | ||
259 | dev_priv->backlight_enabled = false; | ||
260 | } | ||
261 | |||
262 | intel_panel_set_backlight(dev, 0); | ||
263 | } | ||
264 | |||
265 | void intel_panel_enable_backlight(struct drm_device *dev) | ||
266 | { | ||
267 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
268 | |||
269 | if (dev_priv->backlight_level == 0) | ||
270 | dev_priv->backlight_level = intel_panel_get_max_backlight(dev); | ||
271 | |||
272 | intel_panel_set_backlight(dev, dev_priv->backlight_level); | ||
273 | dev_priv->backlight_enabled = true; | ||
274 | } | ||
275 | |||
276 | void intel_panel_setup_backlight(struct drm_device *dev) | ||
277 | { | ||
278 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
279 | |||
280 | dev_priv->backlight_level = intel_panel_get_backlight(dev); | ||
281 | dev_priv->backlight_enabled = dev_priv->backlight_level != 0; | ||
282 | } | ||
283 | |||
284 | enum drm_connector_status | ||
285 | intel_panel_detect(struct drm_device *dev) | ||
286 | { | ||
287 | #if 0 | ||
288 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
289 | #endif | ||
290 | |||
291 | if (i915_panel_ignore_lid) | ||
292 | return i915_panel_ignore_lid > 0 ? | ||
293 | connector_status_connected : | ||
294 | connector_status_disconnected; | ||
295 | |||
296 | /* opregion lid state on HP 2540p is wrong at boot up, | ||
297 | * appears to be either the BIOS or Linux ACPI fault */ | ||
298 | #if 0 | ||
299 | /* Assume that the BIOS does not lie through the OpRegion... */ | ||
300 | if (dev_priv->opregion.lid_state) | ||
301 | return ioread32(dev_priv->opregion.lid_state) & 0x1 ? | ||
302 | connector_status_connected : | ||
303 | connector_status_disconnected; | ||
304 | #endif | ||
305 | |||
306 | return connector_status_unknown; | ||
307 | } | ||