Path: blob/master/drivers/gpu/drm/i915/intel_panel.c
15113 views
/*1* Copyright © 2006-2010 Intel Corporation2* Copyright (c) 2006 Dave Airlie <[email protected]>3*4* Permission is hereby granted, free of charge, to any person obtaining a5* copy of this software and associated documentation files (the "Software"),6* to deal in the Software without restriction, including without limitation7* the rights to use, copy, modify, merge, publish, distribute, sublicense,8* and/or sell copies of the Software, and to permit persons to whom the9* Software is furnished to do so, subject to the following conditions:10*11* The above copyright notice and this permission notice (including the next12* paragraph) shall be included in all copies or substantial portions of the13* Software.14*15* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR16* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,17* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL18* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER19* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING20* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER21* DEALINGS IN THE SOFTWARE.22*23* Authors:24* Eric Anholt <[email protected]>25* Dave Airlie <[email protected]>26* Jesse Barnes <[email protected]>27* Chris Wilson <[email protected]>28*/2930#include "intel_drv.h"3132#define PCI_LBPC 0xf4 /* legacy/combination backlight modes */3334void35intel_fixed_panel_mode(struct drm_display_mode *fixed_mode,36struct drm_display_mode *adjusted_mode)37{38adjusted_mode->hdisplay = fixed_mode->hdisplay;39adjusted_mode->hsync_start = fixed_mode->hsync_start;40adjusted_mode->hsync_end = fixed_mode->hsync_end;41adjusted_mode->htotal = fixed_mode->htotal;4243adjusted_mode->vdisplay = fixed_mode->vdisplay;44adjusted_mode->vsync_start = fixed_mode->vsync_start;45adjusted_mode->vsync_end = fixed_mode->vsync_end;46adjusted_mode->vtotal = fixed_mode->vtotal;4748adjusted_mode->clock = fixed_mode->clock;4950drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);51}5253/* adjusted_mode has been preset to be the panel's fixed mode */54void55intel_pch_panel_fitting(struct drm_device *dev,56int fitting_mode,57struct drm_display_mode *mode,58struct drm_display_mode *adjusted_mode)59{60struct drm_i915_private *dev_priv = dev->dev_private;61int x, y, width, height;6263x = y = width = height = 0;6465/* Native modes don't need fitting */66if (adjusted_mode->hdisplay == mode->hdisplay &&67adjusted_mode->vdisplay == mode->vdisplay)68goto done;6970switch (fitting_mode) {71case DRM_MODE_SCALE_CENTER:72width = mode->hdisplay;73height = mode->vdisplay;74x = (adjusted_mode->hdisplay - width + 1)/2;75y = (adjusted_mode->vdisplay - height + 1)/2;76break;7778case DRM_MODE_SCALE_ASPECT:79/* Scale but preserve the aspect ratio */80{81u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;82u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;83if (scaled_width > scaled_height) { /* pillar */84width = scaled_height / mode->vdisplay;85x = (adjusted_mode->hdisplay - width + 1) / 2;86y = 0;87height = adjusted_mode->vdisplay;88} else if (scaled_width < scaled_height) { /* letter */89height = scaled_width / mode->hdisplay;90y = (adjusted_mode->vdisplay - height + 1) / 2;91x = 0;92width = adjusted_mode->hdisplay;93} else {94x = y = 0;95width = adjusted_mode->hdisplay;96height = adjusted_mode->vdisplay;97}98}99break;100101default:102case DRM_MODE_SCALE_FULLSCREEN:103x = y = 0;104width = adjusted_mode->hdisplay;105height = adjusted_mode->vdisplay;106break;107}108109done:110dev_priv->pch_pf_pos = (x << 16) | y;111dev_priv->pch_pf_size = (width << 16) | height;112}113114static int is_backlight_combination_mode(struct drm_device *dev)115{116struct drm_i915_private *dev_priv = dev->dev_private;117118if (INTEL_INFO(dev)->gen >= 4)119return I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE;120121if (IS_GEN2(dev))122return I915_READ(BLC_PWM_CTL) & BLM_LEGACY_MODE;123124return 0;125}126127static u32 i915_read_blc_pwm_ctl(struct drm_i915_private *dev_priv)128{129u32 val;130131/* Restore the CTL value if it lost, e.g. GPU reset */132133if (HAS_PCH_SPLIT(dev_priv->dev)) {134val = I915_READ(BLC_PWM_PCH_CTL2);135if (dev_priv->saveBLC_PWM_CTL2 == 0) {136dev_priv->saveBLC_PWM_CTL2 = val;137} else if (val == 0) {138I915_WRITE(BLC_PWM_PCH_CTL2,139dev_priv->saveBLC_PWM_CTL);140val = dev_priv->saveBLC_PWM_CTL;141}142} else {143val = I915_READ(BLC_PWM_CTL);144if (dev_priv->saveBLC_PWM_CTL == 0) {145dev_priv->saveBLC_PWM_CTL = val;146dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_CTL2);147} else if (val == 0) {148I915_WRITE(BLC_PWM_CTL,149dev_priv->saveBLC_PWM_CTL);150I915_WRITE(BLC_PWM_CTL2,151dev_priv->saveBLC_PWM_CTL2);152val = dev_priv->saveBLC_PWM_CTL;153}154}155156return val;157}158159u32 intel_panel_get_max_backlight(struct drm_device *dev)160{161struct drm_i915_private *dev_priv = dev->dev_private;162u32 max;163164max = i915_read_blc_pwm_ctl(dev_priv);165if (max == 0) {166/* XXX add code here to query mode clock or hardware clock167* and program max PWM appropriately.168*/169printk_once(KERN_WARNING "fixme: max PWM is zero.\n");170return 1;171}172173if (HAS_PCH_SPLIT(dev)) {174max >>= 16;175} else {176if (IS_PINEVIEW(dev)) {177max >>= 17;178} else {179max >>= 16;180if (INTEL_INFO(dev)->gen < 4)181max &= ~1;182}183184if (is_backlight_combination_mode(dev))185max *= 0xff;186}187188DRM_DEBUG_DRIVER("max backlight PWM = %d\n", max);189return max;190}191192u32 intel_panel_get_backlight(struct drm_device *dev)193{194struct drm_i915_private *dev_priv = dev->dev_private;195u32 val;196197if (HAS_PCH_SPLIT(dev)) {198val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;199} else {200val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;201if (IS_PINEVIEW(dev))202val >>= 1;203204if (is_backlight_combination_mode(dev)){205u8 lbpc;206207val &= ~1;208pci_read_config_byte(dev->pdev, PCI_LBPC, &lbpc);209val *= lbpc;210}211}212213DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val);214return val;215}216217static void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level)218{219struct drm_i915_private *dev_priv = dev->dev_private;220u32 val = I915_READ(BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;221I915_WRITE(BLC_PWM_CPU_CTL, val | level);222}223224void intel_panel_set_backlight(struct drm_device *dev, u32 level)225{226struct drm_i915_private *dev_priv = dev->dev_private;227u32 tmp;228229DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level);230231if (HAS_PCH_SPLIT(dev))232return intel_pch_panel_set_backlight(dev, level);233234if (is_backlight_combination_mode(dev)){235u32 max = intel_panel_get_max_backlight(dev);236u8 lbpc;237238lbpc = level * 0xfe / max + 1;239level /= lbpc;240pci_write_config_byte(dev->pdev, PCI_LBPC, lbpc);241}242243tmp = I915_READ(BLC_PWM_CTL);244if (IS_PINEVIEW(dev)) {245tmp &= ~(BACKLIGHT_DUTY_CYCLE_MASK - 1);246level <<= 1;247} else248tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK;249I915_WRITE(BLC_PWM_CTL, tmp | level);250}251252void intel_panel_disable_backlight(struct drm_device *dev)253{254struct drm_i915_private *dev_priv = dev->dev_private;255256if (dev_priv->backlight_enabled) {257dev_priv->backlight_level = intel_panel_get_backlight(dev);258dev_priv->backlight_enabled = false;259}260261intel_panel_set_backlight(dev, 0);262}263264void intel_panel_enable_backlight(struct drm_device *dev)265{266struct drm_i915_private *dev_priv = dev->dev_private;267268if (dev_priv->backlight_level == 0)269dev_priv->backlight_level = intel_panel_get_max_backlight(dev);270271intel_panel_set_backlight(dev, dev_priv->backlight_level);272dev_priv->backlight_enabled = true;273}274275void intel_panel_setup_backlight(struct drm_device *dev)276{277struct drm_i915_private *dev_priv = dev->dev_private;278279dev_priv->backlight_level = intel_panel_get_backlight(dev);280dev_priv->backlight_enabled = dev_priv->backlight_level != 0;281}282283enum drm_connector_status284intel_panel_detect(struct drm_device *dev)285{286#if 0287struct drm_i915_private *dev_priv = dev->dev_private;288#endif289290if (i915_panel_ignore_lid)291return i915_panel_ignore_lid > 0 ?292connector_status_connected :293connector_status_disconnected;294295/* opregion lid state on HP 2540p is wrong at boot up,296* appears to be either the BIOS or Linux ACPI fault */297#if 0298/* Assume that the BIOS does not lie through the OpRegion... */299if (dev_priv->opregion.lid_state)300return ioread32(dev_priv->opregion.lid_state) & 0x1 ?301connector_status_connected :302connector_status_disconnected;303#endif304305return connector_status_unknown;306}307308309