intel_panel.c revision 254797
1235783Skib/* 2235783Skib * Copyright �� 2006-2010 Intel Corporation 3235783Skib * Copyright (c) 2006 Dave Airlie <airlied@linux.ie> 4235783Skib * 5235783Skib * Permission is hereby granted, free of charge, to any person obtaining a 6235783Skib * copy of this software and associated documentation files (the "Software"), 7235783Skib * to deal in the Software without restriction, including without limitation 8235783Skib * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9235783Skib * and/or sell copies of the Software, and to permit persons to whom the 10235783Skib * Software is furnished to do so, subject to the following conditions: 11235783Skib * 12235783Skib * The above copyright notice and this permission notice (including the next 13235783Skib * paragraph) shall be included in all copies or substantial portions of the 14235783Skib * Software. 15235783Skib * 16235783Skib * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17235783Skib * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18235783Skib * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19235783Skib * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20235783Skib * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21235783Skib * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22235783Skib * DEALINGS IN THE SOFTWARE. 23235783Skib * 24235783Skib * Authors: 25235783Skib * Eric Anholt <eric@anholt.net> 26235783Skib * Dave Airlie <airlied@linux.ie> 27235783Skib * Jesse Barnes <jesse.barnes@intel.com> 28235783Skib * Chris Wilson <chris@chris-wilson.co.uk> 29235783Skib */ 30235783Skib 31235783Skib#include <sys/cdefs.h> 32235783Skib__FBSDID("$FreeBSD: head/sys/dev/drm2/i915/intel_panel.c 254797 2013-08-24 16:50:47Z dumbbell $"); 33235783Skib 34235783Skib#include <dev/drm2/drmP.h> 35235783Skib#include <dev/drm2/drm.h> 36235783Skib#include <dev/drm2/i915/i915_drm.h> 37235783Skib#include <dev/drm2/i915/intel_drv.h> 38235783Skib 39235783Skib#define PCI_LBPC 0xf4 /* legacy/combination backlight modes */ 40235783Skib 41235783Skibvoid 42235783Skibintel_fixed_panel_mode(struct drm_display_mode *fixed_mode, 43235783Skib struct drm_display_mode *adjusted_mode) 44235783Skib{ 45235783Skib adjusted_mode->hdisplay = fixed_mode->hdisplay; 46235783Skib adjusted_mode->hsync_start = fixed_mode->hsync_start; 47235783Skib adjusted_mode->hsync_end = fixed_mode->hsync_end; 48235783Skib adjusted_mode->htotal = fixed_mode->htotal; 49235783Skib 50235783Skib adjusted_mode->vdisplay = fixed_mode->vdisplay; 51235783Skib adjusted_mode->vsync_start = fixed_mode->vsync_start; 52235783Skib adjusted_mode->vsync_end = fixed_mode->vsync_end; 53235783Skib adjusted_mode->vtotal = fixed_mode->vtotal; 54235783Skib 55235783Skib adjusted_mode->clock = fixed_mode->clock; 56235783Skib} 57235783Skib 58235783Skib/* adjusted_mode has been preset to be the panel's fixed mode */ 59235783Skibvoid 60235783Skibintel_pch_panel_fitting(struct drm_device *dev, 61235783Skib int fitting_mode, 62254797Sdumbbell const struct drm_display_mode *mode, 63235783Skib struct drm_display_mode *adjusted_mode) 64235783Skib{ 65235783Skib struct drm_i915_private *dev_priv = dev->dev_private; 66235783Skib int x, y, width, height; 67235783Skib 68235783Skib x = y = width = height = 0; 69235783Skib 70235783Skib /* Native modes don't need fitting */ 71235783Skib if (adjusted_mode->hdisplay == mode->hdisplay && 72235783Skib adjusted_mode->vdisplay == mode->vdisplay) 73235783Skib goto done; 74235783Skib 75235783Skib switch (fitting_mode) { 76235783Skib case DRM_MODE_SCALE_CENTER: 77235783Skib width = mode->hdisplay; 78235783Skib height = mode->vdisplay; 79235783Skib x = (adjusted_mode->hdisplay - width + 1)/2; 80235783Skib y = (adjusted_mode->vdisplay - height + 1)/2; 81235783Skib break; 82235783Skib 83235783Skib case DRM_MODE_SCALE_ASPECT: 84235783Skib /* Scale but preserve the aspect ratio */ 85235783Skib { 86235783Skib u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay; 87235783Skib u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay; 88235783Skib if (scaled_width > scaled_height) { /* pillar */ 89235783Skib width = scaled_height / mode->vdisplay; 90235783Skib if (width & 1) 91235783Skib width++; 92235783Skib x = (adjusted_mode->hdisplay - width + 1) / 2; 93235783Skib y = 0; 94235783Skib height = adjusted_mode->vdisplay; 95235783Skib } else if (scaled_width < scaled_height) { /* letter */ 96235783Skib height = scaled_width / mode->hdisplay; 97235783Skib if (height & 1) 98235783Skib height++; 99235783Skib y = (adjusted_mode->vdisplay - height + 1) / 2; 100235783Skib x = 0; 101235783Skib width = adjusted_mode->hdisplay; 102235783Skib } else { 103235783Skib x = y = 0; 104235783Skib width = adjusted_mode->hdisplay; 105235783Skib height = adjusted_mode->vdisplay; 106235783Skib } 107235783Skib } 108235783Skib break; 109235783Skib 110235783Skib default: 111235783Skib case DRM_MODE_SCALE_FULLSCREEN: 112235783Skib x = y = 0; 113235783Skib width = adjusted_mode->hdisplay; 114235783Skib height = adjusted_mode->vdisplay; 115235783Skib break; 116235783Skib } 117235783Skib 118235783Skibdone: 119235783Skib dev_priv->pch_pf_pos = (x << 16) | y; 120235783Skib dev_priv->pch_pf_size = (width << 16) | height; 121235783Skib} 122235783Skib 123235783Skibstatic int is_backlight_combination_mode(struct drm_device *dev) 124235783Skib{ 125235783Skib struct drm_i915_private *dev_priv = dev->dev_private; 126235783Skib 127235783Skib if (INTEL_INFO(dev)->gen >= 4) 128235783Skib return I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE; 129235783Skib 130235783Skib if (IS_GEN2(dev)) 131235783Skib return I915_READ(BLC_PWM_CTL) & BLM_LEGACY_MODE; 132235783Skib 133235783Skib return 0; 134235783Skib} 135235783Skib 136235783Skibstatic u32 i915_read_blc_pwm_ctl(struct drm_i915_private *dev_priv) 137235783Skib{ 138235783Skib u32 val; 139235783Skib 140235783Skib /* Restore the CTL value if it lost, e.g. GPU reset */ 141235783Skib 142235783Skib if (HAS_PCH_SPLIT(dev_priv->dev)) { 143235783Skib val = I915_READ(BLC_PWM_PCH_CTL2); 144235783Skib if (dev_priv->saveBLC_PWM_CTL2 == 0) { 145235783Skib dev_priv->saveBLC_PWM_CTL2 = val; 146235783Skib } else if (val == 0) { 147235783Skib I915_WRITE(BLC_PWM_PCH_CTL2, 148235783Skib dev_priv->saveBLC_PWM_CTL2); 149235783Skib val = dev_priv->saveBLC_PWM_CTL2; 150235783Skib } 151235783Skib } else { 152235783Skib val = I915_READ(BLC_PWM_CTL); 153235783Skib if (dev_priv->saveBLC_PWM_CTL == 0) { 154235783Skib dev_priv->saveBLC_PWM_CTL = val; 155235783Skib dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_CTL2); 156235783Skib } else if (val == 0) { 157235783Skib I915_WRITE(BLC_PWM_CTL, 158235783Skib dev_priv->saveBLC_PWM_CTL); 159235783Skib I915_WRITE(BLC_PWM_CTL2, 160235783Skib dev_priv->saveBLC_PWM_CTL2); 161235783Skib val = dev_priv->saveBLC_PWM_CTL; 162235783Skib } 163235783Skib } 164235783Skib 165235783Skib return val; 166235783Skib} 167235783Skib 168235783Skibu32 intel_panel_get_max_backlight(struct drm_device *dev) 169235783Skib{ 170235783Skib struct drm_i915_private *dev_priv = dev->dev_private; 171235783Skib u32 max; 172235783Skib 173235783Skib max = i915_read_blc_pwm_ctl(dev_priv); 174235783Skib if (max == 0) { 175235783Skib /* XXX add code here to query mode clock or hardware clock 176235783Skib * and program max PWM appropriately. 177235783Skib */ 178235783Skib#if 0 179235783Skib printf("fixme: max PWM is zero.\n"); 180235783Skib#endif 181235783Skib return 1; 182235783Skib } 183235783Skib 184235783Skib if (HAS_PCH_SPLIT(dev)) { 185235783Skib max >>= 16; 186235783Skib } else { 187235783Skib if (INTEL_INFO(dev)->gen < 4) 188235783Skib max >>= 17; 189235783Skib else 190235783Skib max >>= 16; 191235783Skib 192235783Skib if (is_backlight_combination_mode(dev)) 193235783Skib max *= 0xff; 194235783Skib } 195235783Skib 196235783Skib DRM_DEBUG("max backlight PWM = %d\n", max); 197235783Skib return max; 198235783Skib} 199235783Skib 200235783Skibu32 intel_panel_get_backlight(struct drm_device *dev) 201235783Skib{ 202235783Skib struct drm_i915_private *dev_priv = dev->dev_private; 203235783Skib u32 val; 204235783Skib 205235783Skib if (HAS_PCH_SPLIT(dev)) { 206235783Skib val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; 207235783Skib } else { 208235783Skib val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; 209235783Skib if (INTEL_INFO(dev)->gen < 4) 210235783Skib val >>= 1; 211235783Skib 212235783Skib if (is_backlight_combination_mode(dev)) { 213235783Skib u8 lbpc; 214235783Skib 215235783Skib lbpc = pci_read_config(dev->device, PCI_LBPC, 1); 216235783Skib val *= lbpc; 217235783Skib } 218235783Skib } 219235783Skib 220235783Skib DRM_DEBUG("get backlight PWM = %d\n", val); 221235783Skib return val; 222235783Skib} 223235783Skib 224235783Skibstatic void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level) 225235783Skib{ 226235783Skib struct drm_i915_private *dev_priv = dev->dev_private; 227235783Skib u32 val = I915_READ(BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK; 228235783Skib I915_WRITE(BLC_PWM_CPU_CTL, val | level); 229235783Skib} 230235783Skib 231235783Skibstatic void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level) 232235783Skib{ 233235783Skib struct drm_i915_private *dev_priv = dev->dev_private; 234235783Skib u32 tmp; 235235783Skib 236235783Skib DRM_DEBUG("set backlight PWM = %d\n", level); 237235783Skib 238235783Skib if (HAS_PCH_SPLIT(dev)) 239235783Skib return intel_pch_panel_set_backlight(dev, level); 240235783Skib 241235783Skib if (is_backlight_combination_mode(dev)) { 242235783Skib u32 max = intel_panel_get_max_backlight(dev); 243235783Skib u8 lbpc; 244235783Skib 245235783Skib lbpc = level * 0xfe / max + 1; 246235783Skib level /= lbpc; 247235783Skib pci_write_config(dev->device, PCI_LBPC, lbpc, 4); 248235783Skib } 249235783Skib 250235783Skib tmp = I915_READ(BLC_PWM_CTL); 251235783Skib if (INTEL_INFO(dev)->gen < 4) 252235783Skib level <<= 1; 253235783Skib tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK; 254235783Skib I915_WRITE(BLC_PWM_CTL, tmp | level); 255235783Skib} 256235783Skib 257235783Skibvoid intel_panel_set_backlight(struct drm_device *dev, u32 level) 258235783Skib{ 259235783Skib struct drm_i915_private *dev_priv = dev->dev_private; 260235783Skib 261235783Skib dev_priv->backlight_level = level; 262235783Skib if (dev_priv->backlight_enabled) 263235783Skib intel_panel_actually_set_backlight(dev, level); 264235783Skib} 265235783Skib 266235783Skibvoid intel_panel_disable_backlight(struct drm_device *dev) 267235783Skib{ 268235783Skib struct drm_i915_private *dev_priv = dev->dev_private; 269235783Skib 270235783Skib dev_priv->backlight_enabled = false; 271235783Skib intel_panel_actually_set_backlight(dev, 0); 272235783Skib} 273235783Skib 274235783Skibvoid intel_panel_enable_backlight(struct drm_device *dev) 275235783Skib{ 276235783Skib struct drm_i915_private *dev_priv = dev->dev_private; 277235783Skib 278235783Skib if (dev_priv->backlight_level == 0) 279235783Skib dev_priv->backlight_level = intel_panel_get_max_backlight(dev); 280235783Skib 281235783Skib dev_priv->backlight_enabled = true; 282235783Skib intel_panel_actually_set_backlight(dev, dev_priv->backlight_level); 283235783Skib} 284235783Skib 285235783Skibstatic void intel_panel_init_backlight(struct drm_device *dev) 286235783Skib{ 287235783Skib struct drm_i915_private *dev_priv = dev->dev_private; 288235783Skib 289235783Skib dev_priv->backlight_level = intel_panel_get_backlight(dev); 290235783Skib dev_priv->backlight_enabled = dev_priv->backlight_level != 0; 291235783Skib} 292235783Skib 293235783Skibenum drm_connector_status 294235783Skibintel_panel_detect(struct drm_device *dev) 295235783Skib{ 296235783Skib#if 0 297235783Skib struct drm_i915_private *dev_priv = dev->dev_private; 298235783Skib#endif 299235783Skib 300235783Skib if (i915_panel_ignore_lid) 301235783Skib return i915_panel_ignore_lid > 0 ? 302235783Skib connector_status_connected : 303235783Skib connector_status_disconnected; 304235783Skib 305235783Skib /* opregion lid state on HP 2540p is wrong at boot up, 306235783Skib * appears to be either the BIOS or Linux ACPI fault */ 307235783Skib#if 0 308235783Skib /* Assume that the BIOS does not lie through the OpRegion... */ 309235783Skib if (dev_priv->opregion.lid_state) 310235783Skib return ioread32(dev_priv->opregion.lid_state) & 0x1 ? 311235783Skib connector_status_connected : 312235783Skib connector_status_disconnected; 313235783Skib#endif 314235783Skib 315235783Skib return connector_status_unknown; 316235783Skib} 317235783Skib 318235783Skibint intel_panel_setup_backlight(struct drm_device *dev) 319235783Skib{ 320235783Skib intel_panel_init_backlight(dev); 321235783Skib return 0; 322235783Skib} 323235783Skib 324235783Skibvoid intel_panel_destroy_backlight(struct drm_device *dev) 325235783Skib{ 326235783Skib return; 327235783Skib} 328