2008-08-05 19:37:25 +01:00
/*
* Copyright 2008 Intel Corporation < hong . liu @ intel . com >
* Copyright 2008 Red Hat < mjg @ redhat . com >
*
* Permission is hereby granted , free of charge , to any person obtaining
* a copy of this software and associated documentation files ( the
* " Software " ) , to deal in the Software without restriction , including
* without limitation the rights to use , copy , modify , merge , publish ,
* distribute , sub license , and / or sell copies of the Software , and to
* permit persons to whom the Software is furnished to do so , subject to
* the following conditions :
*
* The above copyright notice and this permission notice ( including the
* next paragraph ) shall be included in all copies or substantial
* portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND ,
* EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND
* NON - INFRINGEMENT . IN NO EVENT SHALL INTEL AND / OR ITS SUPPLIERS BE
* LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN
* ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE .
*
*/
# include <linux/acpi.h>
2015-12-14 12:50:46 +02:00
# include <linux/dmi.h>
2009-03-19 21:35:39 +00:00
# include <acpi/video.h>
2008-08-05 19:37:25 +01:00
2012-10-02 18:01:07 +01:00
# include <drm/drmP.h>
# include <drm/i915_drm.h>
2008-08-05 19:37:25 +01:00
# include "i915_drv.h"
2010-08-22 13:18:16 +01:00
# include "intel_drv.h"
2008-08-05 19:37:25 +01:00
# define OPREGION_HEADER_OFFSET 0
# define OPREGION_ACPI_OFFSET 0x100
2011-02-24 18:13:42 +00:00
# define ACPI_CLID 0x01ac /* current lid state indicator */
# define ACPI_CDCK 0x01b0 /* current docking state indicator */
2008-08-05 19:37:25 +01:00
# define OPREGION_SWSCI_OFFSET 0x200
# define OPREGION_ASLE_OFFSET 0x300
2010-08-19 16:09:23 +01:00
# define OPREGION_VBT_OFFSET 0x400
2015-12-14 12:50:45 +02:00
# define OPREGION_ASLE_EXT_OFFSET 0x1C00
2008-08-05 19:37:25 +01:00
# define OPREGION_SIGNATURE "IntelGraphicsMem"
# define MBOX_ACPI (1<<0)
# define MBOX_SWSCI (1<<1)
# define MBOX_ASLE (1<<2)
2015-07-02 17:43:22 +03:00
# define MBOX_ASLE_EXT (1<<4)
2008-08-05 19:37:25 +01:00
struct opregion_header {
2011-08-16 15:34:10 -04:00
u8 signature [ 16 ] ;
u32 size ;
u32 opregion_ver ;
u8 bios_ver [ 32 ] ;
u8 vbios_ver [ 16 ] ;
u8 driver_ver [ 16 ] ;
u32 mboxes ;
2015-07-02 17:43:22 +03:00
u32 driver_model ;
u32 pcon ;
u8 dver [ 32 ] ;
u8 rsvd [ 124 ] ;
2013-12-02 11:26:09 -02:00
} __packed ;
2008-08-05 19:37:25 +01:00
/* OpRegion mailbox #1: public ACPI methods */
struct opregion_acpi {
2011-08-16 15:34:10 -04:00
u32 drdy ; /* driver readiness */
u32 csts ; /* notification status */
u32 cevt ; /* current event */
u8 rsvd1 [ 20 ] ;
u32 didl [ 8 ] ; /* supported display devices ID list */
u32 cpdl [ 8 ] ; /* currently presented display list */
u32 cadl [ 8 ] ; /* currently active display list */
u32 nadl [ 8 ] ; /* next active devices list */
u32 aslp ; /* ASL sleep time-out */
u32 tidx ; /* toggle table index */
u32 chpd ; /* current hotplug enable indicator */
u32 clid ; /* current lid state*/
u32 cdck ; /* current docking state */
u32 sxsw ; /* Sx state resume */
u32 evts ; /* ASL supported events */
u32 cnot ; /* current OS notification */
u32 nrdy ; /* driver status */
2015-07-02 17:43:22 +03:00
u32 did2 [ 7 ] ; /* extended supported display devices ID list */
u32 cpd2 [ 7 ] ; /* extended attached display devices list */
u8 rsvd2 [ 4 ] ;
2013-12-02 11:26:09 -02:00
} __packed ;
2008-08-05 19:37:25 +01:00
/* OpRegion mailbox #2: SWSCI */
struct opregion_swsci {
2011-08-16 15:34:10 -04:00
u32 scic ; /* SWSCI command|status|data */
u32 parm ; /* command parameters */
u32 dslp ; /* driver sleep time-out */
u8 rsvd [ 244 ] ;
2013-12-02 11:26:09 -02:00
} __packed ;
2008-08-05 19:37:25 +01:00
/* OpRegion mailbox #3: ASLE */
struct opregion_asle {
2011-08-16 15:34:10 -04:00
u32 ardy ; /* driver readiness */
u32 aslc ; /* ASLE interrupt command */
u32 tche ; /* technology enabled indicator */
u32 alsi ; /* current ALS illuminance reading */
u32 bclp ; /* backlight brightness to set */
u32 pfit ; /* panel fitting state */
u32 cblv ; /* current brightness level */
u16 bclm [ 20 ] ; /* backlight level duty cycle mapping table */
u32 cpfm ; /* current panel fitting mode */
u32 epfm ; /* enabled panel fitting modes */
u8 plut [ 74 ] ; /* panel LUT and identifier */
u32 pfmb ; /* PWM freq and min brightness */
2013-09-17 11:14:11 -03:00
u32 cddv ; /* color correction default values */
u32 pcft ; /* power conservation features */
u32 srot ; /* supported rotation angles */
u32 iuer ; /* IUER events */
2015-07-02 17:43:22 +03:00
u64 fdss ;
u32 fdsp ;
u32 stat ;
2015-12-01 04:17:05 +05:30
u64 rvda ; /* Physical address of raw vbt data */
u32 rvds ; /* Size of raw vbt data */
u8 rsvd [ 58 ] ;
2013-12-02 11:26:09 -02:00
} __packed ;
2008-08-05 19:37:25 +01:00
2015-12-14 12:50:45 +02:00
/* OpRegion mailbox #5: ASLE ext */
struct opregion_asle_ext {
u32 phed ; /* Panel Header */
u8 bddc [ 256 ] ; /* Panel EDID */
u8 rsvd [ 764 ] ;
} __packed ;
2013-04-29 13:02:51 +03:00
/* Driver readiness indicator */
# define ASLE_ARDY_READY (1 << 0)
# define ASLE_ARDY_NOT_READY (0 << 0)
2013-09-17 11:14:11 -03:00
/* ASLE Interrupt Command (ASLC) bits */
# define ASLC_SET_ALS_ILLUM (1 << 0)
# define ASLC_SET_BACKLIGHT (1 << 1)
# define ASLC_SET_PFIT (1 << 2)
# define ASLC_SET_PWM_FREQ (1 << 3)
# define ASLC_SUPPORTED_ROTATION_ANGLES (1 << 4)
# define ASLC_BUTTON_ARRAY (1 << 5)
# define ASLC_CONVERTIBLE_INDICATOR (1 << 6)
# define ASLC_DOCKING_INDICATOR (1 << 7)
# define ASLC_ISCT_STATE_CHANGE (1 << 8)
# define ASLC_REQ_MSK 0x1ff
/* response bits */
# define ASLC_ALS_ILLUM_FAILED (1 << 10)
# define ASLC_BACKLIGHT_FAILED (1 << 12)
# define ASLC_PFIT_FAILED (1 << 14)
# define ASLC_PWM_FREQ_FAILED (1 << 16)
# define ASLC_ROTATION_ANGLES_FAILED (1 << 18)
# define ASLC_BUTTON_ARRAY_FAILED (1 << 20)
# define ASLC_CONVERTIBLE_FAILED (1 << 22)
# define ASLC_DOCKING_FAILED (1 << 24)
# define ASLC_ISCT_STATE_FAILED (1 << 26)
2008-08-05 19:37:25 +01:00
2013-04-29 13:02:50 +03:00
/* Technology enabled indicator */
# define ASLE_TCHE_ALS_EN (1 << 0)
# define ASLE_TCHE_BLC_EN (1 << 1)
# define ASLE_TCHE_PFIT_EN (1 << 2)
# define ASLE_TCHE_PFMB_EN (1 << 3)
2008-08-05 19:37:25 +01:00
/* ASLE backlight brightness to set */
# define ASLE_BCLP_VALID (1<<31)
# define ASLE_BCLP_MSK (~(1<<31))
/* ASLE panel fitting request */
# define ASLE_PFIT_VALID (1<<31)
# define ASLE_PFIT_CENTER (1<<0)
# define ASLE_PFIT_STRETCH_TEXT (1<<1)
# define ASLE_PFIT_STRETCH_GFX (1<<2)
/* PWM frequency and minimum brightness */
# define ASLE_PFMB_BRIGHTNESS_MASK (0xff)
# define ASLE_PFMB_BRIGHTNESS_VALID (1<<8)
# define ASLE_PFMB_PWM_MASK (0x7ffffe00)
# define ASLE_PFMB_PWM_VALID (1<<31)
# define ASLE_CBLV_VALID (1<<31)
2013-09-17 11:14:11 -03:00
/* IUER */
# define ASLE_IUER_DOCKING (1 << 7)
# define ASLE_IUER_CONVERTIBLE (1 << 6)
# define ASLE_IUER_ROTATION_LOCK_BTN (1 << 4)
# define ASLE_IUER_VOLUME_DOWN_BTN (1 << 3)
# define ASLE_IUER_VOLUME_UP_BTN (1 << 2)
# define ASLE_IUER_WINDOWS_BTN (1 << 1)
# define ASLE_IUER_POWER_BTN (1 << 0)
2013-09-02 10:38:59 +03:00
/* Software System Control Interrupt (SWSCI) */
# define SWSCI_SCIC_INDICATOR (1 << 0)
# define SWSCI_SCIC_MAIN_FUNCTION_SHIFT 1
# define SWSCI_SCIC_MAIN_FUNCTION_MASK (0xf << 1)
# define SWSCI_SCIC_SUB_FUNCTION_SHIFT 8
# define SWSCI_SCIC_SUB_FUNCTION_MASK (0xff << 8)
# define SWSCI_SCIC_EXIT_PARAMETER_SHIFT 8
# define SWSCI_SCIC_EXIT_PARAMETER_MASK (0xff << 8)
# define SWSCI_SCIC_EXIT_STATUS_SHIFT 5
# define SWSCI_SCIC_EXIT_STATUS_MASK (7 << 5)
# define SWSCI_SCIC_EXIT_STATUS_SUCCESS 1
# define SWSCI_FUNCTION_CODE(main, sub) \
( ( main ) < < SWSCI_SCIC_MAIN_FUNCTION_SHIFT | \
( sub ) < < SWSCI_SCIC_SUB_FUNCTION_SHIFT )
/* SWSCI: Get BIOS Data (GBDA) */
# define SWSCI_GBDA 4
# define SWSCI_GBDA_SUPPORTED_CALLS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 0)
# define SWSCI_GBDA_REQUESTED_CALLBACKS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 1)
# define SWSCI_GBDA_BOOT_DISPLAY_PREF SWSCI_FUNCTION_CODE(SWSCI_GBDA, 4)
# define SWSCI_GBDA_PANEL_DETAILS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 5)
# define SWSCI_GBDA_TV_STANDARD SWSCI_FUNCTION_CODE(SWSCI_GBDA, 6)
# define SWSCI_GBDA_INTERNAL_GRAPHICS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 7)
# define SWSCI_GBDA_SPREAD_SPECTRUM SWSCI_FUNCTION_CODE(SWSCI_GBDA, 10)
/* SWSCI: System BIOS Callbacks (SBCB) */
# define SWSCI_SBCB 6
# define SWSCI_SBCB_SUPPORTED_CALLBACKS SWSCI_FUNCTION_CODE(SWSCI_SBCB, 0)
# define SWSCI_SBCB_INIT_COMPLETION SWSCI_FUNCTION_CODE(SWSCI_SBCB, 1)
# define SWSCI_SBCB_PRE_HIRES_SET_MODE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 3)
# define SWSCI_SBCB_POST_HIRES_SET_MODE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 4)
# define SWSCI_SBCB_DISPLAY_SWITCH SWSCI_FUNCTION_CODE(SWSCI_SBCB, 5)
# define SWSCI_SBCB_SET_TV_FORMAT SWSCI_FUNCTION_CODE(SWSCI_SBCB, 6)
# define SWSCI_SBCB_ADAPTER_POWER_STATE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 7)
# define SWSCI_SBCB_DISPLAY_POWER_STATE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 8)
# define SWSCI_SBCB_SET_BOOT_DISPLAY SWSCI_FUNCTION_CODE(SWSCI_SBCB, 9)
# define SWSCI_SBCB_SET_PANEL_DETAILS SWSCI_FUNCTION_CODE(SWSCI_SBCB, 10)
# define SWSCI_SBCB_SET_INTERNAL_GFX SWSCI_FUNCTION_CODE(SWSCI_SBCB, 11)
# define SWSCI_SBCB_POST_HIRES_TO_DOS_FS SWSCI_FUNCTION_CODE(SWSCI_SBCB, 16)
# define SWSCI_SBCB_SUSPEND_RESUME SWSCI_FUNCTION_CODE(SWSCI_SBCB, 17)
# define SWSCI_SBCB_SET_SPREAD_SPECTRUM SWSCI_FUNCTION_CODE(SWSCI_SBCB, 18)
# define SWSCI_SBCB_POST_VBE_PM SWSCI_FUNCTION_CODE(SWSCI_SBCB, 19)
# define SWSCI_SBCB_ENABLE_DISABLE_AUDIO SWSCI_FUNCTION_CODE(SWSCI_SBCB, 21)
2009-03-19 21:35:39 +00:00
# define ACPI_OTHER_OUTPUT (0<<8)
# define ACPI_VGA_OUTPUT (1<<8)
# define ACPI_TV_OUTPUT (2<<8)
# define ACPI_DIGITAL_OUTPUT (3<<8)
# define ACPI_LVDS_OUTPUT (4<<8)
2014-01-31 15:49:08 +02:00
# define MAX_DSLP 1500
2013-09-02 10:38:59 +03:00
static int swsci ( struct drm_device * dev , u32 function , u32 parm , u32 * parm_out )
{
struct drm_i915_private * dev_priv = dev - > dev_private ;
2015-10-12 21:12:57 +00:00
struct opregion_swsci * swsci = dev_priv - > opregion . swsci ;
2013-09-02 10:38:59 +03:00
u32 main_function , sub_function , scic ;
2016-04-15 12:03:39 +03:00
u16 swsci_val ;
2013-09-02 10:38:59 +03:00
u32 dslp ;
if ( ! swsci )
return - ENODEV ;
main_function = ( function & SWSCI_SCIC_MAIN_FUNCTION_MASK ) > >
SWSCI_SCIC_MAIN_FUNCTION_SHIFT ;
sub_function = ( function & SWSCI_SCIC_SUB_FUNCTION_MASK ) > >
SWSCI_SCIC_SUB_FUNCTION_SHIFT ;
/* Check if we can call the function. See swsci_setup for details. */
if ( main_function = = SWSCI_SBCB ) {
if ( ( dev_priv - > opregion . swsci_sbcb_sub_functions &
( 1 < < sub_function ) ) = = 0 )
return - EINVAL ;
} else if ( main_function = = SWSCI_GBDA ) {
if ( ( dev_priv - > opregion . swsci_gbda_sub_functions &
( 1 < < sub_function ) ) = = 0 )
return - EINVAL ;
}
/* Driver sleep timeout in ms. */
2015-10-12 21:12:57 +00:00
dslp = swsci - > dslp ;
2013-09-02 10:38:59 +03:00
if ( ! dslp ) {
2013-10-09 16:39:57 -03:00
/* The spec says 2ms should be the default, but it's too small
* for some machines . */
dslp = 50 ;
2014-01-31 15:49:08 +02:00
} else if ( dslp > MAX_DSLP ) {
2013-09-02 10:38:59 +03:00
/* Hey bios, trust must be earned. */
2014-01-31 15:49:08 +02:00
DRM_INFO_ONCE ( " ACPI BIOS requests an excessive sleep of %u ms, "
" using %u ms instead \n " , dslp , MAX_DSLP ) ;
dslp = MAX_DSLP ;
2013-09-02 10:38:59 +03:00
}
/* The spec tells us to do this, but we are the only user... */
2015-10-12 21:12:57 +00:00
scic = swsci - > scic ;
2013-09-02 10:38:59 +03:00
if ( scic & SWSCI_SCIC_INDICATOR ) {
DRM_DEBUG_DRIVER ( " SWSCI request already in progress \n " ) ;
return - EBUSY ;
}
scic = function | SWSCI_SCIC_INDICATOR ;
2015-10-12 21:12:57 +00:00
swsci - > parm = parm ;
swsci - > scic = scic ;
2013-09-02 10:38:59 +03:00
/* Ensure SCI event is selected and event trigger is cleared. */
2016-04-15 12:03:39 +03:00
pci_read_config_word ( dev - > pdev , SWSCI , & swsci_val ) ;
if ( ! ( swsci_val & SWSCI_SCISEL ) | | ( swsci_val & SWSCI_GSSCIE ) ) {
swsci_val | = SWSCI_SCISEL ;
swsci_val & = ~ SWSCI_GSSCIE ;
pci_write_config_word ( dev - > pdev , SWSCI , swsci_val ) ;
2013-09-02 10:38:59 +03:00
}
/* Use event trigger to tell bios to check the mail. */
2016-04-15 12:03:39 +03:00
swsci_val | = SWSCI_GSSCIE ;
pci_write_config_word ( dev - > pdev , SWSCI , swsci_val ) ;
2013-09-02 10:38:59 +03:00
/* Poll for the result. */
2015-10-12 21:12:57 +00:00
# define C (((scic = swsci->scic) & SWSCI_SCIC_INDICATOR) == 0)
2013-09-02 10:38:59 +03:00
if ( wait_for ( C , dslp ) ) {
DRM_DEBUG_DRIVER ( " SWSCI request timed out \n " ) ;
return - ETIMEDOUT ;
}
scic = ( scic & SWSCI_SCIC_EXIT_STATUS_MASK ) > >
SWSCI_SCIC_EXIT_STATUS_SHIFT ;
/* Note: scic == 0 is an error! */
if ( scic ! = SWSCI_SCIC_EXIT_STATUS_SUCCESS ) {
DRM_DEBUG_DRIVER ( " SWSCI request error %u \n " , scic ) ;
return - EIO ;
}
if ( parm_out )
2015-10-12 21:12:57 +00:00
* parm_out = swsci - > parm ;
2013-09-02 10:38:59 +03:00
return 0 ;
# undef C
}
2013-08-30 19:40:30 +03:00
# define DISPLAY_TYPE_CRT 0
# define DISPLAY_TYPE_TV 1
# define DISPLAY_TYPE_EXTERNAL_FLAT_PANEL 2
# define DISPLAY_TYPE_INTERNAL_FLAT_PANEL 3
int intel_opregion_notify_encoder ( struct intel_encoder * intel_encoder ,
bool enable )
{
struct drm_device * dev = intel_encoder - > base . dev ;
u32 parm = 0 ;
u32 type = 0 ;
u32 port ;
/* don't care about old stuff for now */
if ( ! HAS_DDI ( dev ) )
return 0 ;
2015-10-01 22:23:49 +05:30
if ( intel_encoder - > type = = INTEL_OUTPUT_DSI )
port = 0 ;
else
port = intel_ddi_get_encoder_port ( intel_encoder ) ;
if ( port = = PORT_E ) {
2013-08-30 19:40:30 +03:00
port = 0 ;
} else {
parm | = 1 < < port ;
port + + ;
}
if ( ! enable )
parm | = 4 < < 8 ;
switch ( intel_encoder - > type ) {
case INTEL_OUTPUT_ANALOG :
type = DISPLAY_TYPE_CRT ;
break ;
case INTEL_OUTPUT_UNKNOWN :
case INTEL_OUTPUT_DISPLAYPORT :
case INTEL_OUTPUT_HDMI :
2014-05-02 14:02:48 +10:00
case INTEL_OUTPUT_DP_MST :
2013-08-30 19:40:30 +03:00
type = DISPLAY_TYPE_EXTERNAL_FLAT_PANEL ;
break ;
case INTEL_OUTPUT_EDP :
2015-10-01 22:23:49 +05:30
case INTEL_OUTPUT_DSI :
2013-08-30 19:40:30 +03:00
type = DISPLAY_TYPE_INTERNAL_FLAT_PANEL ;
break ;
default :
WARN_ONCE ( 1 , " unsupported intel_encoder type %d \n " ,
intel_encoder - > type ) ;
return - EINVAL ;
}
parm | = type < < ( 16 + port * 3 ) ;
return swsci ( dev , SWSCI_SBCB_DISPLAY_POWER_STATE , parm , NULL ) ;
}
2013-08-30 19:40:31 +03:00
static const struct {
pci_power_t pci_power_state ;
u32 parm ;
} power_state_map [ ] = {
{ PCI_D0 , 0x00 } ,
{ PCI_D1 , 0x01 } ,
{ PCI_D2 , 0x02 } ,
{ PCI_D3hot , 0x04 } ,
{ PCI_D3cold , 0x04 } ,
} ;
int intel_opregion_notify_adapter ( struct drm_device * dev , pci_power_t state )
{
int i ;
if ( ! HAS_DDI ( dev ) )
return 0 ;
for ( i = 0 ; i < ARRAY_SIZE ( power_state_map ) ; i + + ) {
if ( state = = power_state_map [ i ] . pci_power_state )
return swsci ( dev , SWSCI_SBCB_ADAPTER_POWER_STATE ,
power_state_map [ i ] . parm , NULL ) ;
}
return - EINVAL ;
}
2008-08-05 19:37:25 +01:00
static u32 asle_set_backlight ( struct drm_device * dev , u32 bclp )
{
struct drm_i915_private * dev_priv = dev - > dev_private ;
2015-12-16 12:48:16 +02:00
struct intel_connector * connector ;
2015-10-12 21:12:57 +00:00
struct opregion_asle * asle = dev_priv - > opregion . asle ;
2008-08-05 19:37:25 +01:00
2012-10-01 14:25:30 +03:00
DRM_DEBUG_DRIVER ( " bclp = 0x%08x \n " , bclp ) ;
2015-06-16 16:27:50 +02:00
if ( acpi_video_get_backlight_type ( ) = = acpi_backlight_native ) {
2014-07-07 15:43:51 +08:00
DRM_DEBUG_KMS ( " opregion backlight request ignored \n " ) ;
return 0 ;
}
2008-08-05 19:37:25 +01:00
if ( ! ( bclp & ASLE_BCLP_VALID ) )
2013-09-17 11:14:11 -03:00
return ASLC_BACKLIGHT_FAILED ;
2008-08-05 19:37:25 +01:00
bclp & = ASLE_BCLP_MSK ;
2010-08-22 13:18:16 +01:00
if ( bclp > 255 )
2013-09-17 11:14:11 -03:00
return ASLC_BACKLIGHT_FAILED ;
2008-08-05 19:37:25 +01:00
2013-11-19 12:10:12 -05:00
drm_modeset_lock ( & dev - > mode_config . connection_mutex , NULL ) ;
2013-11-08 16:48:55 +02:00
2013-10-31 18:55:49 +02:00
/*
2013-11-08 16:48:55 +02:00
* Update backlight on all connectors that support backlight ( usually
* only one ) .
2013-10-31 18:55:49 +02:00
*/
2013-10-13 12:56:31 +01:00
DRM_DEBUG_KMS ( " updating opregion backlight %d/255 \n " , bclp ) ;
2015-12-16 12:48:16 +02:00
for_each_intel_connector ( dev , connector )
intel_panel_set_backlight_acpi ( connector , bclp , 255 ) ;
2015-10-12 21:12:57 +00:00
asle - > cblv = DIV_ROUND_UP ( bclp * 100 , 255 ) | ASLE_CBLV_VALID ;
2008-08-05 19:37:25 +01:00
2013-11-19 12:10:12 -05:00
drm_modeset_unlock ( & dev - > mode_config . connection_mutex ) ;
2013-10-31 18:55:49 +02:00
2013-11-08 16:48:55 +02:00
return 0 ;
2008-08-05 19:37:25 +01:00
}
static u32 asle_set_als_illum ( struct drm_device * dev , u32 alsi )
{
/* alsi is the current ALS reading in lux. 0 indicates below sensor
range , 0xffff indicates above sensor range . 1 - 0xfffe are valid */
2013-04-12 15:20:57 +03:00
DRM_DEBUG_DRIVER ( " Illum is not supported \n " ) ;
2013-09-17 11:14:11 -03:00
return ASLC_ALS_ILLUM_FAILED ;
2008-08-05 19:37:25 +01:00
}
static u32 asle_set_pwm_freq ( struct drm_device * dev , u32 pfmb )
{
2013-04-12 15:20:57 +03:00
DRM_DEBUG_DRIVER ( " PWM freq is not supported \n " ) ;
2013-09-17 11:14:11 -03:00
return ASLC_PWM_FREQ_FAILED ;
2008-08-05 19:37:25 +01:00
}
static u32 asle_set_pfit ( struct drm_device * dev , u32 pfit )
{
/* Panel fitting is currently controlled by the X code, so this is a
noop until modesetting support works fully */
2013-04-12 15:20:57 +03:00
DRM_DEBUG_DRIVER ( " Pfit is not supported \n " ) ;
2013-09-17 11:14:11 -03:00
return ASLC_PFIT_FAILED ;
}
static u32 asle_set_supported_rotation_angles ( struct drm_device * dev , u32 srot )
{
DRM_DEBUG_DRIVER ( " SROT is not supported \n " ) ;
return ASLC_ROTATION_ANGLES_FAILED ;
}
static u32 asle_set_button_array ( struct drm_device * dev , u32 iuer )
{
if ( ! iuer )
DRM_DEBUG_DRIVER ( " Button array event is not supported (nothing) \n " ) ;
if ( iuer & ASLE_IUER_ROTATION_LOCK_BTN )
DRM_DEBUG_DRIVER ( " Button array event is not supported (rotation lock) \n " ) ;
if ( iuer & ASLE_IUER_VOLUME_DOWN_BTN )
DRM_DEBUG_DRIVER ( " Button array event is not supported (volume down) \n " ) ;
if ( iuer & ASLE_IUER_VOLUME_UP_BTN )
DRM_DEBUG_DRIVER ( " Button array event is not supported (volume up) \n " ) ;
if ( iuer & ASLE_IUER_WINDOWS_BTN )
DRM_DEBUG_DRIVER ( " Button array event is not supported (windows) \n " ) ;
if ( iuer & ASLE_IUER_POWER_BTN )
DRM_DEBUG_DRIVER ( " Button array event is not supported (power) \n " ) ;
return ASLC_BUTTON_ARRAY_FAILED ;
}
static u32 asle_set_convertible ( struct drm_device * dev , u32 iuer )
{
if ( iuer & ASLE_IUER_CONVERTIBLE )
DRM_DEBUG_DRIVER ( " Convertible is not supported (clamshell) \n " ) ;
else
DRM_DEBUG_DRIVER ( " Convertible is not supported (slate) \n " ) ;
return ASLC_CONVERTIBLE_FAILED ;
}
static u32 asle_set_docking ( struct drm_device * dev , u32 iuer )
{
if ( iuer & ASLE_IUER_DOCKING )
DRM_DEBUG_DRIVER ( " Docking is not supported (docked) \n " ) ;
else
DRM_DEBUG_DRIVER ( " Docking is not supported (undocked) \n " ) ;
return ASLC_DOCKING_FAILED ;
}
static u32 asle_isct_state ( struct drm_device * dev )
{
DRM_DEBUG_DRIVER ( " ISCT is not supported \n " ) ;
return ASLC_ISCT_STATE_FAILED ;
2008-08-05 19:37:25 +01:00
}
2013-10-31 18:55:48 +02:00
static void asle_work ( struct work_struct * work )
2008-08-05 19:37:25 +01:00
{
2013-10-31 18:55:48 +02:00
struct intel_opregion * opregion =
container_of ( work , struct intel_opregion , asle_work ) ;
struct drm_i915_private * dev_priv =
container_of ( opregion , struct drm_i915_private , opregion ) ;
struct drm_device * dev = dev_priv - > dev ;
2015-10-12 21:12:57 +00:00
struct opregion_asle * asle = dev_priv - > opregion . asle ;
2013-09-17 11:14:11 -03:00
u32 aslc_stat = 0 ;
u32 aslc_req ;
2008-08-05 19:37:25 +01:00
if ( ! asle )
return ;
2015-10-12 21:12:57 +00:00
aslc_req = asle - > aslc ;
2008-08-05 19:37:25 +01:00
2013-09-17 11:14:11 -03:00
if ( ! ( aslc_req & ASLC_REQ_MSK ) ) {
DRM_DEBUG_DRIVER ( " No request on ASLC interrupt 0x%08x \n " ,
aslc_req ) ;
2008-08-05 19:37:25 +01:00
return ;
}
2013-09-17 11:14:11 -03:00
if ( aslc_req & ASLC_SET_ALS_ILLUM )
2015-10-12 21:12:57 +00:00
aslc_stat | = asle_set_als_illum ( dev , asle - > alsi ) ;
2013-09-17 11:14:11 -03:00
if ( aslc_req & ASLC_SET_BACKLIGHT )
2015-10-12 21:12:57 +00:00
aslc_stat | = asle_set_backlight ( dev , asle - > bclp ) ;
2013-09-17 11:14:11 -03:00
if ( aslc_req & ASLC_SET_PFIT )
2015-10-12 21:12:57 +00:00
aslc_stat | = asle_set_pfit ( dev , asle - > pfit ) ;
2013-09-17 11:14:11 -03:00
if ( aslc_req & ASLC_SET_PWM_FREQ )
2015-10-12 21:12:57 +00:00
aslc_stat | = asle_set_pwm_freq ( dev , asle - > pfmb ) ;
2013-09-17 11:14:11 -03:00
if ( aslc_req & ASLC_SUPPORTED_ROTATION_ANGLES )
aslc_stat | = asle_set_supported_rotation_angles ( dev ,
2015-10-12 21:12:57 +00:00
asle - > srot ) ;
2013-09-17 11:14:11 -03:00
if ( aslc_req & ASLC_BUTTON_ARRAY )
2015-10-12 21:12:57 +00:00
aslc_stat | = asle_set_button_array ( dev , asle - > iuer ) ;
2008-08-05 19:37:25 +01:00
2013-09-17 11:14:11 -03:00
if ( aslc_req & ASLC_CONVERTIBLE_INDICATOR )
2015-10-12 21:12:57 +00:00
aslc_stat | = asle_set_convertible ( dev , asle - > iuer ) ;
2008-08-05 19:37:25 +01:00
2013-09-17 11:14:11 -03:00
if ( aslc_req & ASLC_DOCKING_INDICATOR )
2015-10-12 21:12:57 +00:00
aslc_stat | = asle_set_docking ( dev , asle - > iuer ) ;
2008-08-05 19:37:25 +01:00
2013-09-17 11:14:11 -03:00
if ( aslc_req & ASLC_ISCT_STATE_CHANGE )
aslc_stat | = asle_isct_state ( dev ) ;
2008-08-05 19:37:25 +01:00
2015-10-12 21:12:57 +00:00
asle - > aslc = aslc_stat ;
2008-08-05 19:37:25 +01:00
}
2016-05-06 14:48:28 +01:00
void intel_opregion_asle_intr ( struct drm_i915_private * dev_priv )
2013-10-31 18:55:48 +02:00
{
if ( dev_priv - > opregion . asle )
schedule_work ( & dev_priv - > opregion . asle_work ) ;
}
2008-08-05 19:37:25 +01:00
# define ACPI_EV_DISPLAY_SWITCH (1<<0)
# define ACPI_EV_LID (1<<1)
# define ACPI_EV_DOCK (1<<2)
static struct intel_opregion * system_opregion ;
2008-12-18 21:18:47 +01:00
static int intel_opregion_video_event ( struct notifier_block * nb ,
unsigned long val , void * data )
2008-08-05 19:37:25 +01:00
{
/* The only video events relevant to opregion are 0x80. These indicate
either a docking event , lid switch or display switch request . In
Linux , these are handled by the dock , button and video drivers .
2011-07-12 18:30:52 -04:00
*/
2008-08-05 19:37:25 +01:00
2011-07-12 18:30:52 -04:00
struct acpi_bus_event * event = data ;
2015-10-12 21:12:57 +00:00
struct opregion_acpi * acpi ;
2011-07-12 18:30:52 -04:00
int ret = NOTIFY_OK ;
if ( strcmp ( event - > device_class , ACPI_VIDEO_CLASS ) ! = 0 )
return NOTIFY_DONE ;
2008-08-05 19:37:25 +01:00
if ( ! system_opregion )
return NOTIFY_DONE ;
acpi = system_opregion - > acpi ;
2011-07-12 18:30:52 -04:00
2015-10-12 21:12:57 +00:00
if ( event - > type = = 0x80 & & ( ( acpi - > cevt & 1 ) = = 0 ) )
2011-07-12 18:30:52 -04:00
ret = NOTIFY_BAD ;
2015-10-12 21:12:57 +00:00
acpi - > csts = 0 ;
2008-08-05 19:37:25 +01:00
2011-07-12 18:30:52 -04:00
return ret ;
2008-08-05 19:37:25 +01:00
}
static struct notifier_block intel_opregion_notifier = {
. notifier_call = intel_opregion_video_event ,
} ;
2009-03-19 21:35:39 +00:00
/*
* Initialise the DIDL field in opregion . This passes a list of devices to
* the firmware . Values are defined by section B .4 .2 of the ACPI specification
* ( version 3 )
*/
2015-07-02 17:43:24 +03:00
static u32 get_did ( struct intel_opregion * opregion , int i )
{
u32 did ;
if ( i < ARRAY_SIZE ( opregion - > acpi - > didl ) ) {
2015-10-12 21:12:57 +00:00
did = opregion - > acpi - > didl [ i ] ;
2015-07-02 17:43:24 +03:00
} else {
i - = ARRAY_SIZE ( opregion - > acpi - > didl ) ;
if ( WARN_ON ( i > = ARRAY_SIZE ( opregion - > acpi - > did2 ) ) )
return 0 ;
2015-10-12 21:12:57 +00:00
did = opregion - > acpi - > did2 [ i ] ;
2015-07-02 17:43:24 +03:00
}
return did ;
}
static void set_did ( struct intel_opregion * opregion , int i , u32 val )
{
if ( i < ARRAY_SIZE ( opregion - > acpi - > didl ) ) {
2015-10-12 21:12:57 +00:00
opregion - > acpi - > didl [ i ] = val ;
2015-07-02 17:43:24 +03:00
} else {
i - = ARRAY_SIZE ( opregion - > acpi - > didl ) ;
if ( WARN_ON ( i > = ARRAY_SIZE ( opregion - > acpi - > did2 ) ) )
return ;
2015-10-12 21:12:57 +00:00
opregion - > acpi - > did2 [ i ] = val ;
2015-07-02 17:43:24 +03:00
}
}
2009-03-19 21:35:39 +00:00
static void intel_didl_outputs ( struct drm_device * dev )
{
struct drm_i915_private * dev_priv = dev - > dev_private ;
struct intel_opregion * opregion = & dev_priv - > opregion ;
struct drm_connector * connector ;
2010-03-29 15:12:16 +08:00
acpi_handle handle ;
struct acpi_device * acpi_dev , * acpi_cdev , * acpi_video_bus = NULL ;
unsigned long long device_id ;
acpi_status status ;
2015-07-02 17:43:25 +03:00
u32 temp , max_outputs ;
2009-03-19 21:35:39 +00:00
int i = 0 ;
2013-11-14 23:17:21 +01:00
handle = ACPI_HANDLE ( & dev - > pdev - > dev ) ;
2013-02-01 10:14:20 +09:00
if ( ! handle | | acpi_bus_get_device ( handle , & acpi_dev ) )
2010-03-29 15:12:16 +08:00
return ;
2013-03-04 21:30:41 +00:00
if ( acpi_is_video_device ( handle ) )
2010-03-29 15:12:16 +08:00
acpi_video_bus = acpi_dev ;
else {
list_for_each_entry ( acpi_cdev , & acpi_dev - > children , node ) {
2013-03-04 21:30:41 +00:00
if ( acpi_is_video_device ( acpi_cdev - > handle ) ) {
2010-03-29 15:12:16 +08:00
acpi_video_bus = acpi_cdev ;
break ;
}
}
}
if ( ! acpi_video_bus ) {
2015-10-23 11:00:06 +02:00
DRM_DEBUG_KMS ( " No ACPI video bus found \n " ) ;
2010-03-29 15:12:16 +08:00
return ;
}
2015-07-02 17:43:25 +03:00
/*
* In theory , did2 , the extended didl , gets added at opregion version
* 3.0 . In practice , however , we ' re supposed to set it for earlier
* versions as well , since a BIOS that doesn ' t understand did2 should
* not look at it anyway . Use a variable so we can tweak this if a need
* arises later .
*/
max_outputs = ARRAY_SIZE ( opregion - > acpi - > didl ) +
ARRAY_SIZE ( opregion - > acpi - > did2 ) ;
2010-03-29 15:12:16 +08:00
list_for_each_entry ( acpi_cdev , & acpi_video_bus - > children , node ) {
2015-07-02 17:43:25 +03:00
if ( i > = max_outputs ) {
DRM_DEBUG_KMS ( " More than %u outputs detected via ACPI \n " ,
max_outputs ) ;
2010-03-29 15:12:16 +08:00
return ;
}
2015-07-02 17:43:24 +03:00
status = acpi_evaluate_integer ( acpi_cdev - > handle , " _ADR " ,
NULL , & device_id ) ;
2010-03-29 15:12:16 +08:00
if ( ACPI_SUCCESS ( status ) ) {
if ( ! device_id )
goto blind_set ;
2015-07-02 17:43:24 +03:00
set_did ( opregion , i + + , ( u32 ) ( device_id & 0x0f0f ) ) ;
2010-03-29 15:12:16 +08:00
}
}
end :
2015-07-02 17:43:25 +03:00
DRM_DEBUG_KMS ( " %d outputs detected \n " , i ) ;
/* If fewer than max outputs, the list must be null terminated */
if ( i < max_outputs )
2015-07-02 17:43:24 +03:00
set_did ( opregion , i , 0 ) ;
2010-03-29 15:12:16 +08:00
return ;
blind_set :
i = 0 ;
2009-03-19 21:35:39 +00:00
list_for_each_entry ( connector , & dev - > mode_config . connector_list , head ) {
int output_type = ACPI_OTHER_OUTPUT ;
2015-07-02 17:43:25 +03:00
if ( i > = max_outputs ) {
DRM_DEBUG_KMS ( " More than %u outputs in connector list \n " ,
max_outputs ) ;
2009-03-19 21:35:39 +00:00
return ;
}
switch ( connector - > connector_type ) {
case DRM_MODE_CONNECTOR_VGA :
case DRM_MODE_CONNECTOR_DVIA :
output_type = ACPI_VGA_OUTPUT ;
break ;
case DRM_MODE_CONNECTOR_Composite :
case DRM_MODE_CONNECTOR_SVIDEO :
case DRM_MODE_CONNECTOR_Component :
case DRM_MODE_CONNECTOR_9PinDIN :
output_type = ACPI_TV_OUTPUT ;
break ;
case DRM_MODE_CONNECTOR_DVII :
case DRM_MODE_CONNECTOR_DVID :
case DRM_MODE_CONNECTOR_DisplayPort :
case DRM_MODE_CONNECTOR_HDMIA :
case DRM_MODE_CONNECTOR_HDMIB :
output_type = ACPI_DIGITAL_OUTPUT ;
break ;
case DRM_MODE_CONNECTOR_LVDS :
output_type = ACPI_LVDS_OUTPUT ;
break ;
}
2015-07-02 17:43:24 +03:00
temp = get_did ( opregion , i ) ;
set_did ( opregion , i , temp | ( 1 < < 31 ) | output_type | i ) ;
2009-03-19 21:35:39 +00:00
i + + ;
}
2010-03-29 15:12:16 +08:00
goto end ;
2009-03-19 21:35:39 +00:00
}
2012-06-26 00:36:24 +02:00
static void intel_setup_cadls ( struct drm_device * dev )
{
struct drm_i915_private * dev_priv = dev - > dev_private ;
struct intel_opregion * opregion = & dev_priv - > opregion ;
int i = 0 ;
u32 disp_id ;
/* Initialize the CADL field by duplicating the DIDL values.
* Technically , this is not always correct as display outputs may exist ,
* but not active . This initialization is necessary for some Clevo
* laptops that check this field before processing the brightness and
* display switching hotkeys . Just like DIDL , CADL is NULL - terminated if
* there are less than eight devices . */
do {
2015-07-02 17:43:24 +03:00
disp_id = get_did ( opregion , i ) ;
2015-10-12 21:12:57 +00:00
opregion - > acpi - > cadl [ i ] = disp_id ;
2012-06-26 00:36:24 +02:00
} while ( + + i < 8 & & disp_id ! = 0 ) ;
}
2010-08-19 16:09:23 +01:00
void intel_opregion_init ( struct drm_device * dev )
{
struct drm_i915_private * dev_priv = dev - > dev_private ;
struct intel_opregion * opregion = & dev_priv - > opregion ;
if ( ! opregion - > header )
return ;
if ( opregion - > acpi ) {
2015-02-23 12:03:31 +01:00
intel_didl_outputs ( dev ) ;
intel_setup_cadls ( dev ) ;
2010-08-19 16:09:23 +01:00
/* Notify BIOS we are ready to handle ACPI video ext notifs.
* Right now , all the events are handled by the ACPI video module .
* We don ' t actually need to do anything with them . */
2015-10-12 21:12:57 +00:00
opregion - > acpi - > csts = 0 ;
opregion - > acpi - > drdy = 1 ;
2010-08-19 16:09:23 +01:00
system_opregion = opregion ;
register_acpi_notifier ( & intel_opregion_notifier ) ;
}
2013-04-29 13:02:51 +03:00
if ( opregion - > asle ) {
2015-10-12 21:12:57 +00:00
opregion - > asle - > tche = ASLE_TCHE_BLC_EN ;
opregion - > asle - > ardy = ASLE_ARDY_READY ;
2013-04-29 13:02:51 +03:00
}
2010-08-19 16:09:23 +01:00
}
void intel_opregion_fini ( struct drm_device * dev )
{
struct drm_i915_private * dev_priv = dev - > dev_private ;
struct intel_opregion * opregion = & dev_priv - > opregion ;
if ( ! opregion - > header )
return ;
2013-04-29 13:02:51 +03:00
if ( opregion - > asle )
2015-10-12 21:12:57 +00:00
opregion - > asle - > ardy = ASLE_ARDY_NOT_READY ;
2013-04-29 13:02:51 +03:00
2013-10-31 18:55:48 +02:00
cancel_work_sync ( & dev_priv - > opregion . asle_work ) ;
2010-08-19 16:09:23 +01:00
if ( opregion - > acpi ) {
2015-10-12 21:12:57 +00:00
opregion - > acpi - > drdy = 0 ;
2010-08-19 16:09:23 +01:00
system_opregion = NULL ;
unregister_acpi_notifier ( & intel_opregion_notifier ) ;
}
/* just clear all opregion memory pointers now */
2015-10-12 21:12:57 +00:00
memunmap ( opregion - > header ) ;
2015-12-15 13:18:00 +02:00
if ( opregion - > rvda ) {
memunmap ( opregion - > rvda ) ;
opregion - > rvda = NULL ;
}
2010-08-19 16:09:23 +01:00
opregion - > header = NULL ;
opregion - > acpi = NULL ;
opregion - > swsci = NULL ;
opregion - > asle = NULL ;
opregion - > vbt = NULL ;
2013-09-12 13:58:20 -03:00
opregion - > lid_state = NULL ;
2010-08-19 16:09:23 +01:00
}
2013-09-02 10:38:59 +03:00
static void swsci_setup ( struct drm_device * dev )
{
struct drm_i915_private * dev_priv = dev - > dev_private ;
struct intel_opregion * opregion = & dev_priv - > opregion ;
bool requested_callbacks = false ;
u32 tmp ;
/* Sub-function code 0 is okay, let's allow them. */
opregion - > swsci_gbda_sub_functions = 1 ;
opregion - > swsci_sbcb_sub_functions = 1 ;
/* We use GBDA to ask for supported GBDA calls. */
if ( swsci ( dev , SWSCI_GBDA_SUPPORTED_CALLS , 0 , & tmp ) = = 0 ) {
/* make the bits match the sub-function codes */
tmp < < = 1 ;
opregion - > swsci_gbda_sub_functions | = tmp ;
}
/*
* We also use GBDA to ask for _requested_ SBCB callbacks . The driver
* must not call interfaces that are not specifically requested by the
* bios .
*/
if ( swsci ( dev , SWSCI_GBDA_REQUESTED_CALLBACKS , 0 , & tmp ) = = 0 ) {
/* here, the bits already match sub-function codes */
opregion - > swsci_sbcb_sub_functions | = tmp ;
requested_callbacks = true ;
}
/*
* But we use SBCB to ask for _supported_ SBCB calls . This does not mean
* the callback is _requested_ . But we still can ' t call interfaces that
* are not requested .
*/
if ( swsci ( dev , SWSCI_SBCB_SUPPORTED_CALLBACKS , 0 , & tmp ) = = 0 ) {
/* make the bits match the sub-function codes */
u32 low = tmp & 0x7ff ;
u32 high = tmp & ~ 0xfff ; /* bit 11 is reserved */
tmp = ( high < < 4 ) | ( low < < 1 ) | 1 ;
/* best guess what to do with supported wrt requested */
if ( requested_callbacks ) {
u32 req = opregion - > swsci_sbcb_sub_functions ;
if ( ( req & tmp ) ! = req )
DRM_DEBUG_DRIVER ( " SWSCI BIOS requested (%08x) SBCB callbacks that are not supported (%08x) \n " , req , tmp ) ;
/* XXX: for now, trust the requested callbacks */
/* opregion->swsci_sbcb_sub_functions &= tmp; */
} else {
opregion - > swsci_sbcb_sub_functions | = tmp ;
}
}
DRM_DEBUG_DRIVER ( " SWSCI GBDA callbacks %08x, SBCB callbacks %08x \n " ,
opregion - > swsci_gbda_sub_functions ,
opregion - > swsci_sbcb_sub_functions ) ;
}
2010-08-19 16:09:23 +01:00
2015-12-14 12:50:46 +02:00
static int intel_no_opregion_vbt_callback ( const struct dmi_system_id * id )
{
DRM_DEBUG_KMS ( " Falling back to manually reading VBT from "
" VBIOS ROM for %s \n " , id - > ident ) ;
return 1 ;
}
static const struct dmi_system_id intel_no_opregion_vbt [ ] = {
{
. callback = intel_no_opregion_vbt_callback ,
. ident = " ThinkCentre A57 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " LENOVO " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " 97027RG " ) ,
} ,
} ,
{ }
} ;
2010-08-19 16:09:23 +01:00
int intel_opregion_setup ( struct drm_device * dev )
2008-08-05 19:37:25 +01:00
{
struct drm_i915_private * dev_priv = dev - > dev_private ;
struct intel_opregion * opregion = & dev_priv - > opregion ;
u32 asls , mboxes ;
2012-04-16 14:07:42 -07:00
char buf [ sizeof ( OPREGION_SIGNATURE ) ] ;
2008-08-05 19:37:25 +01:00
int err = 0 ;
2015-10-12 21:12:57 +00:00
void * base ;
2008-08-05 19:37:25 +01:00
2015-07-02 17:43:21 +03:00
BUILD_BUG_ON ( sizeof ( struct opregion_header ) ! = 0x100 ) ;
BUILD_BUG_ON ( sizeof ( struct opregion_acpi ) ! = 0x100 ) ;
BUILD_BUG_ON ( sizeof ( struct opregion_swsci ) ! = 0x100 ) ;
BUILD_BUG_ON ( sizeof ( struct opregion_asle ) ! = 0x100 ) ;
2015-12-14 12:50:45 +02:00
BUILD_BUG_ON ( sizeof ( struct opregion_asle_ext ) ! = 0x400 ) ;
2015-07-02 17:43:21 +03:00
2016-04-15 12:03:39 +03:00
pci_read_config_dword ( dev - > pdev , ASLS , & asls ) ;
2009-10-09 11:39:40 +08:00
DRM_DEBUG_DRIVER ( " graphic opregion physical addr: 0x%x \n " , asls ) ;
2008-08-05 19:37:25 +01:00
if ( asls = = 0 ) {
2009-10-09 11:39:40 +08:00
DRM_DEBUG_DRIVER ( " ACPI OpRegion not supported! \n " ) ;
2008-08-05 19:37:25 +01:00
return - ENOTSUPP ;
}
2013-10-31 18:55:48 +02:00
INIT_WORK ( & opregion - > asle_work , asle_work ) ;
2015-10-12 21:12:57 +00:00
base = memremap ( asls , OPREGION_SIZE , MEMREMAP_WB ) ;
2008-08-05 19:37:25 +01:00
if ( ! base )
return - ENOMEM ;
2015-10-12 21:12:57 +00:00
memcpy ( buf , base , sizeof ( buf ) ) ;
2012-04-16 14:07:42 -07:00
if ( memcmp ( buf , OPREGION_SIGNATURE , 16 ) ) {
2009-10-09 11:39:40 +08:00
DRM_DEBUG_DRIVER ( " opregion signature mismatch \n " ) ;
2008-08-05 19:37:25 +01:00
err = - EINVAL ;
goto err_out ;
}
2010-08-19 16:09:23 +01:00
opregion - > header = base ;
2011-02-24 18:13:42 +00:00
opregion - > lid_state = base + ACPI_CLID ;
2011-01-16 19:37:30 +00:00
2015-10-12 21:12:57 +00:00
mboxes = opregion - > header - > mboxes ;
2008-08-05 19:37:25 +01:00
if ( mboxes & MBOX_ACPI ) {
2009-10-09 11:39:40 +08:00
DRM_DEBUG_DRIVER ( " Public ACPI methods supported \n " ) ;
2008-08-05 19:37:25 +01:00
opregion - > acpi = base + OPREGION_ACPI_OFFSET ;
}
if ( mboxes & MBOX_SWSCI ) {
2009-10-09 11:39:40 +08:00
DRM_DEBUG_DRIVER ( " SWSCI supported \n " ) ;
2008-08-05 19:37:25 +01:00
opregion - > swsci = base + OPREGION_SWSCI_OFFSET ;
2013-09-02 10:38:59 +03:00
swsci_setup ( dev ) ;
2008-08-05 19:37:25 +01:00
}
2015-12-14 12:50:45 +02:00
2008-08-05 19:37:25 +01:00
if ( mboxes & MBOX_ASLE ) {
2009-10-09 11:39:40 +08:00
DRM_DEBUG_DRIVER ( " ASLE supported \n " ) ;
2008-08-05 19:37:25 +01:00
opregion - > asle = base + OPREGION_ASLE_OFFSET ;
2013-04-29 13:02:51 +03:00
2015-10-12 21:12:57 +00:00
opregion - > asle - > ardy = ASLE_ARDY_NOT_READY ;
2008-08-05 19:37:25 +01:00
}
2015-12-14 12:50:45 +02:00
if ( mboxes & MBOX_ASLE_EXT )
DRM_DEBUG_DRIVER ( " ASLE extension supported \n " ) ;
2015-12-15 13:16:15 +02:00
if ( ! dmi_check_system ( intel_no_opregion_vbt ) ) {
2015-12-15 13:18:00 +02:00
const void * vbt = NULL ;
u32 vbt_size = 0 ;
if ( opregion - > header - > opregion_ver > = 2 & & opregion - > asle & &
opregion - > asle - > rvda & & opregion - > asle - > rvds ) {
opregion - > rvda = memremap ( opregion - > asle - > rvda ,
opregion - > asle - > rvds ,
MEMREMAP_WB ) ;
vbt = opregion - > rvda ;
vbt_size = opregion - > asle - > rvds ;
}
2015-12-15 13:16:15 +02:00
if ( intel_bios_is_valid_vbt ( vbt , vbt_size ) ) {
2015-12-15 13:18:00 +02:00
DRM_DEBUG_KMS ( " Found valid VBT in ACPI OpRegion (RVDA) \n " ) ;
2015-12-15 13:16:15 +02:00
opregion - > vbt = vbt ;
2015-12-15 13:17:12 +02:00
opregion - > vbt_size = vbt_size ;
2015-12-15 13:18:00 +02:00
} else {
vbt = base + OPREGION_VBT_OFFSET ;
vbt_size = OPREGION_ASLE_EXT_OFFSET - OPREGION_VBT_OFFSET ;
if ( intel_bios_is_valid_vbt ( vbt , vbt_size ) ) {
DRM_DEBUG_KMS ( " Found valid VBT in ACPI OpRegion (Mailbox #4) \n " ) ;
opregion - > vbt = vbt ;
opregion - > vbt_size = vbt_size ;
}
2015-12-15 13:16:15 +02:00
}
}
2015-12-14 12:50:46 +02:00
2008-08-05 19:37:25 +01:00
return 0 ;
err_out :
2015-10-12 21:12:57 +00:00
memunmap ( base ) ;
2008-08-05 19:37:25 +01:00
return err ;
}
2016-04-11 10:23:51 +03:00
int
intel_opregion_get_panel_type ( struct drm_device * dev )
{
u32 panel_details ;
int ret ;
ret = swsci ( dev , SWSCI_GBDA_PANEL_DETAILS , 0x0 , & panel_details ) ;
if ( ret ) {
DRM_DEBUG_KMS ( " Failed to get panel details from OpRegion (%d) \n " ,
ret ) ;
return ret ;
}
ret = ( panel_details > > 8 ) & 0xff ;
if ( ret > 0x10 ) {
DRM_DEBUG_KMS ( " Invalid OpRegion panel type 0x%x \n " , ret ) ;
return - EINVAL ;
}
/* fall back to VBT panel type? */
if ( ret = = 0x0 ) {
DRM_DEBUG_KMS ( " No panel type in OpRegion \n " ) ;
return - ENODEV ;
}
return ret - 1 ;
}