2015-05-19 22:29:30 +08:00
/*
* hdac_i915 . c - routines for sync between HD - A core and i915 display driver
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation ; either version 2 of the License , or ( at your option )
* any later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License
* for more details .
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/pci.h>
# include <linux/component.h>
# include <drm/i915_component.h>
# include <sound/core.h>
# include <sound/hdaudio.h>
# include <sound/hda_i915.h>
2016-04-21 16:39:17 +02:00
# include <sound/hda_register.h>
2015-05-19 22:29:30 +08:00
static struct i915_audio_component * hdac_acomp ;
2015-10-28 12:26:48 +01:00
/**
* snd_hdac_set_codec_wakeup - Enable / disable HDMI / DP codec wakeup
* @ bus : HDA core bus
* @ enable : enable or disable the wakeup
*
* This function is supposed to be used only by a HD - audio controller
* driver that needs the interaction with i915 graphics .
*
* This function should be called during the chip reset , also called at
* resume for updating STATESTS register read .
*
* Returns zero for success or a negative error code .
*/
2015-05-19 22:29:30 +08:00
int snd_hdac_set_codec_wakeup ( struct hdac_bus * bus , bool enable )
{
struct i915_audio_component * acomp = bus - > audio_component ;
2015-06-10 12:15:27 +02:00
if ( ! acomp | | ! acomp - > ops )
2015-05-19 22:29:30 +08:00
return - ENODEV ;
if ( ! acomp - > ops - > codec_wake_override ) {
dev_warn ( bus - > dev ,
" Invalid codec wake callback \n " ) ;
return 0 ;
}
dev_dbg ( bus - > dev , " %s codec wakeup \n " ,
enable ? " enable " : " disable " ) ;
acomp - > ops - > codec_wake_override ( acomp - > dev , enable ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_set_codec_wakeup ) ;
2015-10-28 12:26:48 +01:00
/**
* snd_hdac_display_power - Power up / down the power refcount
* @ bus : HDA core bus
* @ enable : power up or down
*
* This function is supposed to be used only by a HD - audio controller
* driver that needs the interaction with i915 graphics .
*
* This function manages a refcount and calls the i915 get_power ( ) and
* put_power ( ) ops accordingly , toggling the codec wakeup , too .
*
* Returns zero for success or a negative error code .
*/
2015-05-19 22:29:30 +08:00
int snd_hdac_display_power ( struct hdac_bus * bus , bool enable )
{
struct i915_audio_component * acomp = bus - > audio_component ;
2015-06-10 12:15:27 +02:00
if ( ! acomp | | ! acomp - > ops )
2015-05-19 22:29:30 +08:00
return - ENODEV ;
dev_dbg ( bus - > dev , " display power %s \n " ,
enable ? " enable " : " disable " ) ;
if ( enable ) {
2015-07-16 10:39:24 +02:00
if ( ! bus - > i915_power_refcount + + ) {
2015-05-19 22:29:30 +08:00
acomp - > ops - > get_power ( acomp - > dev ) ;
2015-07-16 10:39:24 +02:00
snd_hdac_set_codec_wakeup ( bus , true ) ;
snd_hdac_set_codec_wakeup ( bus , false ) ;
}
2015-05-19 22:29:30 +08:00
} else {
WARN_ON ( ! bus - > i915_power_refcount ) ;
if ( ! - - bus - > i915_power_refcount )
acomp - > ops - > put_power ( acomp - > dev ) ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_display_power ) ;
2016-04-21 16:39:17 +02:00
# define CONTROLLER_IN_GPU(pci) (((pci)->device == 0x0a0c) || \
( ( pci ) - > device = = 0x0c0c ) | | \
( ( pci ) - > device = = 0x0d0c ) | | \
( ( pci ) - > device = = 0x160c ) )
2015-10-28 12:26:48 +01:00
/**
2016-04-21 16:39:17 +02:00
* snd_hdac_i915_set_bclk - Reprogram BCLK for HSW / BDW
2015-10-28 12:26:48 +01:00
* @ bus : HDA core bus
*
2016-04-21 16:39:17 +02:00
* Intel HSW / BDW display HDA controller is in GPU . Both its power and link BCLK
* depends on GPU . Two Extended Mode registers EM4 ( M value ) and EM5 ( N Value )
* are used to convert CDClk ( Core Display Clock ) to 24 MHz BCLK :
* BCLK = CDCLK * M / N
* The values will be lost when the display power well is disabled and need to
* be restored to avoid abnormal playback speed .
2015-10-28 12:26:48 +01:00
*
2016-04-21 16:39:17 +02:00
* Call this function at initializing and changing power well , as well as
* at ELD notifier for the hotplug .
2015-10-28 12:26:48 +01:00
*/
2016-04-21 16:39:17 +02:00
void snd_hdac_i915_set_bclk ( struct hdac_bus * bus )
2015-05-19 22:29:30 +08:00
{
struct i915_audio_component * acomp = bus - > audio_component ;
2016-04-21 16:39:17 +02:00
struct pci_dev * pci = to_pci_dev ( bus - > dev ) ;
int cdclk_freq ;
unsigned int bclk_m , bclk_n ;
if ( ! acomp | | ! acomp - > ops | | ! acomp - > ops - > get_cdclk_freq )
return ; /* only for i915 binding */
if ( ! CONTROLLER_IN_GPU ( pci ) )
return ; /* only HSW/BDW */
cdclk_freq = acomp - > ops - > get_cdclk_freq ( acomp - > dev ) ;
switch ( cdclk_freq ) {
case 337500 :
bclk_m = 16 ;
bclk_n = 225 ;
break ;
case 450000 :
default : /* default CDCLK 450MHz */
bclk_m = 4 ;
bclk_n = 75 ;
break ;
case 540000 :
bclk_m = 4 ;
bclk_n = 90 ;
break ;
case 675000 :
bclk_m = 8 ;
bclk_n = 225 ;
break ;
}
2015-05-19 22:29:30 +08:00
2016-04-21 16:39:17 +02:00
snd_hdac_chip_writew ( bus , HSW_EM4 , bclk_m ) ;
snd_hdac_chip_writew ( bus , HSW_EM5 , bclk_n ) ;
2015-05-19 22:29:30 +08:00
}
2016-04-21 16:39:17 +02:00
EXPORT_SYMBOL_GPL ( snd_hdac_i915_set_bclk ) ;
2015-05-19 22:29:30 +08:00
2016-03-21 14:41:58 +01:00
/* There is a fixed mapping between audio pin node and display port.
* on SNB , IVY , HSW , BSW , SKL , BXT , KBL :
2015-12-01 12:39:38 +01:00
* Pin Widget 5 - PORT B ( port = 1 in i915 driver )
* Pin Widget 6 - PORT C ( port = 2 in i915 driver )
* Pin Widget 7 - PORT D ( port = 3 in i915 driver )
2016-03-21 14:41:58 +01:00
*
* on VLV , ILK :
* Pin Widget 4 - PORT B ( port = 1 in i915 driver )
* Pin Widget 5 - PORT C ( port = 2 in i915 driver )
* Pin Widget 6 - PORT D ( port = 3 in i915 driver )
2015-12-01 12:39:38 +01:00
*/
2016-03-21 14:41:58 +01:00
static int pin2port ( struct hdac_device * codec , hda_nid_t pin_nid )
2015-12-01 12:39:38 +01:00
{
2016-03-21 14:41:58 +01:00
int base_nid ;
switch ( codec - > vendor_id ) {
case 0x80860054 : /* ILK */
case 0x80862804 : /* ILK */
case 0x80862882 : /* VLV */
base_nid = 3 ;
break ;
default :
base_nid = 4 ;
break ;
}
if ( WARN_ON ( pin_nid < = base_nid | | pin_nid > base_nid + 3 ) )
2016-03-10 15:49:35 +01:00
return - 1 ;
2016-03-21 14:41:58 +01:00
return pin_nid - base_nid ;
2015-12-01 12:39:38 +01:00
}
/**
* snd_hdac_sync_audio_rate - Set N / CTS based on the sample rate
2016-03-21 14:41:58 +01:00
* @ codec : HDA codec
2015-12-01 12:39:38 +01:00
* @ nid : the pin widget NID
* @ rate : the sample rate to set
*
* This function is supposed to be used only by a HD - audio controller
* driver that needs the interaction with i915 graphics .
*
* This function sets N / CTS value based on the given sample rate .
* Returns zero for success , or a negative error code .
*/
2016-03-21 14:41:58 +01:00
int snd_hdac_sync_audio_rate ( struct hdac_device * codec , hda_nid_t nid , int rate )
2015-12-01 12:39:38 +01:00
{
2016-03-21 14:41:58 +01:00
struct hdac_bus * bus = codec - > bus ;
2015-12-01 12:39:38 +01:00
struct i915_audio_component * acomp = bus - > audio_component ;
2016-03-10 15:49:35 +01:00
int port ;
2015-12-01 12:39:38 +01:00
if ( ! acomp | | ! acomp - > ops | | ! acomp - > ops - > sync_audio_rate )
return - ENODEV ;
2016-03-21 14:41:58 +01:00
port = pin2port ( codec , nid ) ;
2016-03-10 15:49:35 +01:00
if ( port < 0 )
return - EINVAL ;
return acomp - > ops - > sync_audio_rate ( acomp - > dev , port , rate ) ;
2015-12-01 12:39:38 +01:00
}
EXPORT_SYMBOL_GPL ( snd_hdac_sync_audio_rate ) ;
/**
* snd_hdac_acomp_get_eld - Get the audio state and ELD via component
2016-03-21 14:41:58 +01:00
* @ codec : HDA codec
2015-12-01 12:39:38 +01:00
* @ nid : the pin widget NID
* @ audio_enabled : the pointer to store the current audio state
* @ buffer : the buffer pointer to store ELD bytes
* @ max_bytes : the max bytes to be stored on @ buffer
*
* This function is supposed to be used only by a HD - audio controller
* driver that needs the interaction with i915 graphics .
*
* This function queries the current state of the audio on the given
* digital port and fetches the ELD bytes onto the given buffer .
* It returns the number of bytes for the total ELD data , zero for
* invalid ELD , or a negative error code .
*
* The return size is the total bytes required for the whole ELD bytes ,
* thus it may be over @ max_bytes . If it ' s over @ max_bytes , it implies
* that only a part of ELD bytes have been fetched .
*/
2016-03-21 14:41:58 +01:00
int snd_hdac_acomp_get_eld ( struct hdac_device * codec , hda_nid_t nid ,
2015-12-01 12:39:38 +01:00
bool * audio_enabled , char * buffer , int max_bytes )
{
2016-03-21 14:41:58 +01:00
struct hdac_bus * bus = codec - > bus ;
2015-12-01 12:39:38 +01:00
struct i915_audio_component * acomp = bus - > audio_component ;
2016-03-10 15:49:35 +01:00
int port ;
2015-12-01 12:39:38 +01:00
if ( ! acomp | | ! acomp - > ops | | ! acomp - > ops - > get_eld )
return - ENODEV ;
2016-03-21 14:41:58 +01:00
port = pin2port ( codec , nid ) ;
2016-03-10 15:49:35 +01:00
if ( port < 0 )
return - EINVAL ;
return acomp - > ops - > get_eld ( acomp - > dev , port , audio_enabled ,
2015-12-01 12:39:38 +01:00
buffer , max_bytes ) ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_acomp_get_eld ) ;
2015-05-19 22:29:30 +08:00
static int hdac_component_master_bind ( struct device * dev )
{
struct i915_audio_component * acomp = hdac_acomp ;
int ret ;
ret = component_bind_all ( dev , acomp ) ;
if ( ret < 0 )
return ret ;
if ( WARN_ON ( ! ( acomp - > dev & & acomp - > ops & & acomp - > ops - > get_power & &
acomp - > ops - > put_power & & acomp - > ops - > get_cdclk_freq ) ) ) {
ret = - EINVAL ;
goto out_unbind ;
}
/*
* Atm , we don ' t support dynamic unbinding initiated by the child
* component , so pin its containing module until we unbind .
*/
if ( ! try_module_get ( acomp - > ops - > owner ) ) {
ret = - ENODEV ;
goto out_unbind ;
}
return 0 ;
out_unbind :
component_unbind_all ( dev , acomp ) ;
return ret ;
}
static void hdac_component_master_unbind ( struct device * dev )
{
struct i915_audio_component * acomp = hdac_acomp ;
module_put ( acomp - > ops - > owner ) ;
component_unbind_all ( dev , acomp ) ;
WARN_ON ( acomp - > ops | | acomp - > dev ) ;
}
static const struct component_master_ops hdac_component_master_ops = {
. bind = hdac_component_master_bind ,
. unbind = hdac_component_master_unbind ,
} ;
static int hdac_component_master_match ( struct device * dev , void * data )
{
/* i915 is the only supported component */
return ! strcmp ( dev - > driver - > name , " i915 " ) ;
}
2015-10-28 12:26:48 +01:00
/**
* snd_hdac_i915_register_notifier - Register i915 audio component ops
* @ aops : i915 audio component ops
*
* This function is supposed to be used only by a HD - audio controller
* driver that needs the interaction with i915 graphics .
*
* This function sets the given ops to be called by the i915 graphics driver .
*
* Returns zero for success or a negative error code .
*/
2015-08-19 10:48:57 +02:00
int snd_hdac_i915_register_notifier ( const struct i915_audio_component_audio_ops * aops )
{
if ( WARN_ON ( ! hdac_acomp ) )
return - ENODEV ;
hdac_acomp - > audio_ops = aops ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_i915_register_notifier ) ;
2016-03-29 15:03:06 +02:00
/* check whether intel graphics is present */
static bool i915_gfx_present ( void )
{
static struct pci_device_id ids [ ] = {
{ PCI_DEVICE ( PCI_VENDOR_ID_INTEL , PCI_ANY_ID ) ,
. class = PCI_BASE_CLASS_DISPLAY < < 16 ,
. class_mask = 0xff < < 16 } ,
{ }
} ;
return pci_dev_present ( ids ) ;
}
2015-10-28 12:26:48 +01:00
/**
* snd_hdac_i915_init - Initialize i915 audio component
* @ bus : HDA core bus
*
* This function is supposed to be used only by a HD - audio controller
* driver that needs the interaction with i915 graphics .
*
* This function initializes and sets up the audio component to communicate
* with i915 graphics driver .
*
* Returns zero for success or a negative error code .
*/
2015-05-19 22:29:30 +08:00
int snd_hdac_i915_init ( struct hdac_bus * bus )
{
struct component_match * match = NULL ;
struct device * dev = bus - > dev ;
struct i915_audio_component * acomp ;
int ret ;
2016-03-21 14:41:58 +01:00
if ( WARN_ON ( hdac_acomp ) )
return - EBUSY ;
2016-03-29 15:03:06 +02:00
if ( ! i915_gfx_present ( ) )
return - ENODEV ;
2015-05-19 22:29:30 +08:00
acomp = kzalloc ( sizeof ( * acomp ) , GFP_KERNEL ) ;
if ( ! acomp )
return - ENOMEM ;
bus - > audio_component = acomp ;
hdac_acomp = acomp ;
component_match_add ( dev , & match , hdac_component_master_match , bus ) ;
ret = component_master_add_with_match ( dev , & hdac_component_master_ops ,
match ) ;
if ( ret < 0 )
goto out_err ;
/*
* Atm , we don ' t support deferring the component binding , so make sure
* i915 is loaded and that the binding successfully completes .
*/
request_module ( " i915 " ) ;
if ( ! acomp - > ops ) {
ret = - ENODEV ;
goto out_master_del ;
}
dev_dbg ( dev , " bound to i915 component master \n " ) ;
return 0 ;
out_master_del :
component_master_del ( dev , & hdac_component_master_ops ) ;
out_err :
kfree ( acomp ) ;
bus - > audio_component = NULL ;
2016-03-29 18:48:07 +02:00
hdac_acomp = NULL ;
2016-01-20 15:00:26 +01:00
dev_info ( dev , " failed to add i915 component master (%d) \n " , ret ) ;
2015-05-19 22:29:30 +08:00
return ret ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_i915_init ) ;
2015-10-28 12:26:48 +01:00
/**
* snd_hdac_i915_exit - Finalize i915 audio component
* @ bus : HDA core bus
*
* This function is supposed to be used only by a HD - audio controller
* driver that needs the interaction with i915 graphics .
*
* This function releases the i915 audio component that has been used .
*
* Returns zero for success or a negative error code .
*/
2015-05-19 22:29:30 +08:00
int snd_hdac_i915_exit ( struct hdac_bus * bus )
{
struct device * dev = bus - > dev ;
struct i915_audio_component * acomp = bus - > audio_component ;
2015-06-10 12:15:27 +02:00
if ( ! acomp )
return 0 ;
2015-05-19 22:29:30 +08:00
WARN_ON ( bus - > i915_power_refcount ) ;
2015-06-10 12:15:27 +02:00
if ( bus - > i915_power_refcount > 0 & & acomp - > ops )
2015-05-19 22:29:30 +08:00
acomp - > ops - > put_power ( acomp - > dev ) ;
component_master_del ( dev , & hdac_component_master_ops ) ;
kfree ( acomp ) ;
bus - > audio_component = NULL ;
2016-03-29 12:29:24 +02:00
hdac_acomp = NULL ;
2015-05-19 22:29:30 +08:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_i915_exit ) ;