1 /* 2 * Copyright © 2006-2010 Intel Corporation 3 * Copyright (c) 2006 Dave Airlie <airlied@linux.ie> 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the next 13 * paragraph) shall be included in all copies or substantial portions of the 14 * Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 * DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: 25 * Eric Anholt <eric@anholt.net> 26 * Dave Airlie <airlied@linux.ie> 27 * Jesse Barnes <jesse.barnes@intel.com> 28 * Chris Wilson <chris@chris-wilson.co.uk> 29 * 30 * $FreeBSD: src/sys/dev/drm2/i915/intel_panel.c,v 1.1 2012/05/22 11:07:44 kib Exp $ 31 */ 32 33 #include <drm/drmP.h> 34 #include <drm/i915_drm.h> 35 #include "intel_drv.h" 36 37 #define PCI_LBPC 0xf4 /* legacy/combination backlight modes */ 38 39 void 40 intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, 41 struct drm_display_mode *adjusted_mode) 42 { 43 adjusted_mode->hdisplay = fixed_mode->hdisplay; 44 adjusted_mode->hsync_start = fixed_mode->hsync_start; 45 adjusted_mode->hsync_end = fixed_mode->hsync_end; 46 adjusted_mode->htotal = fixed_mode->htotal; 47 48 adjusted_mode->vdisplay = fixed_mode->vdisplay; 49 adjusted_mode->vsync_start = fixed_mode->vsync_start; 50 adjusted_mode->vsync_end = fixed_mode->vsync_end; 51 adjusted_mode->vtotal = fixed_mode->vtotal; 52 53 adjusted_mode->clock = fixed_mode->clock; 54 } 55 56 /* adjusted_mode has been preset to be the panel's fixed mode */ 57 void 58 intel_pch_panel_fitting(struct drm_device *dev, 59 int fitting_mode, 60 const struct drm_display_mode *mode, 61 struct drm_display_mode *adjusted_mode) 62 { 63 struct drm_i915_private *dev_priv = dev->dev_private; 64 int x, y, width, height; 65 66 x = y = width = height = 0; 67 68 /* Native modes don't need fitting */ 69 if (adjusted_mode->hdisplay == mode->hdisplay && 70 adjusted_mode->vdisplay == mode->vdisplay) 71 goto done; 72 73 switch (fitting_mode) { 74 case DRM_MODE_SCALE_CENTER: 75 width = mode->hdisplay; 76 height = mode->vdisplay; 77 x = (adjusted_mode->hdisplay - width + 1)/2; 78 y = (adjusted_mode->vdisplay - height + 1)/2; 79 break; 80 81 case DRM_MODE_SCALE_ASPECT: 82 /* Scale but preserve the aspect ratio */ 83 { 84 u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay; 85 u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay; 86 if (scaled_width > scaled_height) { /* pillar */ 87 width = scaled_height / mode->vdisplay; 88 if (width & 1) 89 width++; 90 x = (adjusted_mode->hdisplay - width + 1) / 2; 91 y = 0; 92 height = adjusted_mode->vdisplay; 93 } else if (scaled_width < scaled_height) { /* letter */ 94 height = scaled_width / mode->hdisplay; 95 if (height & 1) 96 height++; 97 y = (adjusted_mode->vdisplay - height + 1) / 2; 98 x = 0; 99 width = adjusted_mode->hdisplay; 100 } else { 101 x = y = 0; 102 width = adjusted_mode->hdisplay; 103 height = adjusted_mode->vdisplay; 104 } 105 } 106 break; 107 108 default: 109 case DRM_MODE_SCALE_FULLSCREEN: 110 x = y = 0; 111 width = adjusted_mode->hdisplay; 112 height = adjusted_mode->vdisplay; 113 break; 114 } 115 116 done: 117 dev_priv->pch_pf_pos = (x << 16) | y; 118 dev_priv->pch_pf_size = (width << 16) | height; 119 } 120 121 static int is_backlight_combination_mode(struct drm_device *dev) 122 { 123 struct drm_i915_private *dev_priv = dev->dev_private; 124 125 if (INTEL_INFO(dev)->gen >= 4) 126 return I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE; 127 128 if (IS_GEN2(dev)) 129 return I915_READ(BLC_PWM_CTL) & BLM_LEGACY_MODE; 130 131 return 0; 132 } 133 134 static u32 i915_read_blc_pwm_ctl(struct drm_i915_private *dev_priv) 135 { 136 u32 val; 137 138 /* Restore the CTL value if it lost, e.g. GPU reset */ 139 140 if (HAS_PCH_SPLIT(dev_priv->dev)) { 141 val = I915_READ(BLC_PWM_PCH_CTL2); 142 if (dev_priv->saveBLC_PWM_CTL2 == 0) { 143 dev_priv->saveBLC_PWM_CTL2 = val; 144 } else if (val == 0) { 145 I915_WRITE(BLC_PWM_PCH_CTL2, 146 dev_priv->saveBLC_PWM_CTL2); 147 val = dev_priv->saveBLC_PWM_CTL2; 148 } 149 } else { 150 val = I915_READ(BLC_PWM_CTL); 151 if (dev_priv->saveBLC_PWM_CTL == 0) { 152 dev_priv->saveBLC_PWM_CTL = val; 153 dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_CTL2); 154 } else if (val == 0) { 155 I915_WRITE(BLC_PWM_CTL, 156 dev_priv->saveBLC_PWM_CTL); 157 I915_WRITE(BLC_PWM_CTL2, 158 dev_priv->saveBLC_PWM_CTL2); 159 val = dev_priv->saveBLC_PWM_CTL; 160 } 161 } 162 163 return val; 164 } 165 166 u32 intel_panel_get_max_backlight(struct drm_device *dev) 167 { 168 struct drm_i915_private *dev_priv = dev->dev_private; 169 u32 max; 170 171 max = i915_read_blc_pwm_ctl(dev_priv); 172 if (max == 0) { 173 /* XXX add code here to query mode clock or hardware clock 174 * and program max PWM appropriately. 175 */ 176 #if 0 177 printf("fixme: max PWM is zero.\n"); 178 #endif 179 return 1; 180 } 181 182 if (HAS_PCH_SPLIT(dev)) { 183 max >>= 16; 184 } else { 185 if (INTEL_INFO(dev)->gen < 4) 186 max >>= 17; 187 else 188 max >>= 16; 189 190 if (is_backlight_combination_mode(dev)) 191 max *= 0xff; 192 } 193 194 DRM_DEBUG("max backlight PWM = %d\n", max); 195 return max; 196 } 197 198 u32 intel_panel_get_backlight(struct drm_device *dev) 199 { 200 struct drm_i915_private *dev_priv = dev->dev_private; 201 u32 val; 202 203 if (HAS_PCH_SPLIT(dev)) { 204 val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; 205 } else { 206 val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; 207 if (INTEL_INFO(dev)->gen < 4) 208 val >>= 1; 209 210 if (is_backlight_combination_mode(dev)) { 211 u8 lbpc; 212 213 lbpc = pci_read_config(dev->dev, PCI_LBPC, 1); 214 val *= lbpc; 215 } 216 } 217 218 DRM_DEBUG("get backlight PWM = %d\n", val); 219 return val; 220 } 221 222 static void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level) 223 { 224 struct drm_i915_private *dev_priv = dev->dev_private; 225 u32 val = I915_READ(BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK; 226 I915_WRITE(BLC_PWM_CPU_CTL, val | level); 227 } 228 229 static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level) 230 { 231 struct drm_i915_private *dev_priv = dev->dev_private; 232 u32 tmp; 233 234 DRM_DEBUG("set backlight PWM = %d\n", level); 235 236 if (HAS_PCH_SPLIT(dev)) 237 return intel_pch_panel_set_backlight(dev, level); 238 239 if (is_backlight_combination_mode(dev)) { 240 u32 max = intel_panel_get_max_backlight(dev); 241 u8 lbpc; 242 243 lbpc = level * 0xfe / max + 1; 244 level /= lbpc; 245 pci_write_config(dev->dev, PCI_LBPC, lbpc, 4); 246 } 247 248 tmp = I915_READ(BLC_PWM_CTL); 249 if (INTEL_INFO(dev)->gen < 4) 250 level <<= 1; 251 tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK; 252 I915_WRITE(BLC_PWM_CTL, tmp | level); 253 } 254 255 void intel_panel_set_backlight(struct drm_device *dev, u32 level) 256 { 257 struct drm_i915_private *dev_priv = dev->dev_private; 258 259 dev_priv->backlight_level = level; 260 if (dev_priv->backlight_enabled) 261 intel_panel_actually_set_backlight(dev, level); 262 } 263 264 void intel_panel_disable_backlight(struct drm_device *dev) 265 { 266 struct drm_i915_private *dev_priv = dev->dev_private; 267 268 dev_priv->backlight_enabled = false; 269 intel_panel_actually_set_backlight(dev, 0); 270 } 271 272 void intel_panel_enable_backlight(struct drm_device *dev) 273 { 274 struct drm_i915_private *dev_priv = dev->dev_private; 275 276 if (dev_priv->backlight_level == 0) 277 dev_priv->backlight_level = intel_panel_get_max_backlight(dev); 278 279 dev_priv->backlight_enabled = true; 280 intel_panel_actually_set_backlight(dev, dev_priv->backlight_level); 281 } 282 283 static void intel_panel_init_backlight(struct drm_device *dev) 284 { 285 struct drm_i915_private *dev_priv = dev->dev_private; 286 287 dev_priv->backlight_level = intel_panel_get_backlight(dev); 288 dev_priv->backlight_enabled = dev_priv->backlight_level != 0; 289 } 290 291 enum drm_connector_status 292 intel_panel_detect(struct drm_device *dev) 293 { 294 #if 0 295 struct drm_i915_private *dev_priv = dev->dev_private; 296 #endif 297 298 if (i915_panel_ignore_lid) 299 return i915_panel_ignore_lid > 0 ? 300 connector_status_connected : 301 connector_status_disconnected; 302 303 /* opregion lid state on HP 2540p is wrong at boot up, 304 * appears to be either the BIOS or Linux ACPI fault */ 305 #if 0 306 /* Assume that the BIOS does not lie through the OpRegion... */ 307 if (dev_priv->opregion.lid_state) 308 return ioread32(dev_priv->opregion.lid_state) & 0x1 ? 309 connector_status_connected : 310 connector_status_disconnected; 311 #endif 312 313 return connector_status_unknown; 314 } 315 316 int intel_panel_setup_backlight(struct drm_device *dev) 317 { 318 intel_panel_init_backlight(dev); 319 return 0; 320 } 321 322 void intel_panel_destroy_backlight(struct drm_device *dev) 323 { 324 return; 325 } 326