2013-08-01 00:51:33 +04:00
/*
* Copyright 2013 Advanced Micro Devices , Inc .
*
* 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 , sublicense ,
* 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 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 NONINFRINGEMENT . IN NO EVENT SHALL
* THE COPYRIGHT HOLDER ( S ) OR AUTHOR ( S ) 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/hdmi.h>
# include <drm/drmP.h>
# include "radeon.h"
# include "sid.h"
static u32 dce6_endpoint_rreg ( struct radeon_device * rdev ,
u32 block_offset , u32 reg )
{
2013-09-04 03:00:09 +04:00
unsigned long flags ;
2013-08-01 00:51:33 +04:00
u32 r ;
2013-09-04 03:00:09 +04:00
spin_lock_irqsave ( & rdev - > end_idx_lock , flags ) ;
2013-08-01 00:51:33 +04:00
WREG32 ( AZ_F0_CODEC_ENDPOINT_INDEX + block_offset , reg ) ;
r = RREG32 ( AZ_F0_CODEC_ENDPOINT_DATA + block_offset ) ;
2013-09-04 03:00:09 +04:00
spin_unlock_irqrestore ( & rdev - > end_idx_lock , flags ) ;
2013-08-01 00:51:33 +04:00
return r ;
}
static void dce6_endpoint_wreg ( struct radeon_device * rdev ,
u32 block_offset , u32 reg , u32 v )
{
2013-09-04 03:00:09 +04:00
unsigned long flags ;
spin_lock_irqsave ( & rdev - > end_idx_lock , flags ) ;
2013-08-01 00:51:33 +04:00
if ( ASIC_IS_DCE8 ( rdev ) )
WREG32 ( AZ_F0_CODEC_ENDPOINT_INDEX + block_offset , reg ) ;
else
WREG32 ( AZ_F0_CODEC_ENDPOINT_INDEX + block_offset ,
AZ_ENDPOINT_REG_WRITE_EN | AZ_ENDPOINT_REG_INDEX ( reg ) ) ;
WREG32 ( AZ_F0_CODEC_ENDPOINT_DATA + block_offset , v ) ;
2013-09-04 03:00:09 +04:00
spin_unlock_irqrestore ( & rdev - > end_idx_lock , flags ) ;
2013-08-01 00:51:33 +04:00
}
# define RREG32_ENDPOINT(block, reg) dce6_endpoint_rreg(rdev, (block), (reg))
# define WREG32_ENDPOINT(block, reg, v) dce6_endpoint_wreg(rdev, (block), (reg), (v))
static void dce6_afmt_get_connected_pins ( struct radeon_device * rdev )
{
int i ;
u32 offset , tmp ;
for ( i = 0 ; i < rdev - > audio . num_pins ; i + + ) {
offset = rdev - > audio . pin [ i ] . offset ;
tmp = RREG32_ENDPOINT ( offset ,
AZ_F0_CODEC_PIN_CONTROL_RESPONSE_CONFIGURATION_DEFAULT ) ;
if ( ( ( tmp & PORT_CONNECTIVITY_MASK ) > > PORT_CONNECTIVITY_SHIFT ) = = 1 )
rdev - > audio . pin [ i ] . connected = false ;
else
rdev - > audio . pin [ i ] . connected = true ;
}
}
struct r600_audio_pin * dce6_audio_get_pin ( struct radeon_device * rdev )
{
int i ;
dce6_afmt_get_connected_pins ( rdev ) ;
for ( i = 0 ; i < rdev - > audio . num_pins ; i + + ) {
if ( rdev - > audio . pin [ i ] . connected )
return & rdev - > audio . pin [ i ] ;
}
DRM_ERROR ( " No connected audio pins found! \n " ) ;
return NULL ;
}
void dce6_afmt_select_pin ( struct drm_encoder * encoder )
{
struct radeon_device * rdev = encoder - > dev - > dev_private ;
struct radeon_encoder * radeon_encoder = to_radeon_encoder ( encoder ) ;
struct radeon_encoder_atom_dig * dig = radeon_encoder - > enc_priv ;
2013-12-04 02:45:14 +04:00
u32 offset ;
2013-08-01 00:51:33 +04:00
2013-12-04 02:45:14 +04:00
if ( ! dig | | ! dig - > afmt | | ! dig - > afmt - > pin )
2013-08-01 00:51:33 +04:00
return ;
2013-12-04 02:45:14 +04:00
offset = dig - > afmt - > offset ;
2013-09-03 22:03:21 +04:00
WREG32 ( AFMT_AUDIO_SRC_CONTROL + offset ,
AFMT_AUDIO_SRC_SELECT ( dig - > afmt - > pin - > id ) ) ;
2013-08-01 00:51:33 +04:00
}
2013-10-11 02:03:06 +04:00
void dce6_afmt_write_latency_fields ( struct drm_encoder * encoder ,
struct drm_display_mode * mode )
{
struct radeon_device * rdev = encoder - > dev - > dev_private ;
struct radeon_encoder * radeon_encoder = to_radeon_encoder ( encoder ) ;
struct radeon_encoder_atom_dig * dig = radeon_encoder - > enc_priv ;
struct drm_connector * connector ;
struct radeon_connector * radeon_connector = NULL ;
u32 tmp = 0 , offset ;
2013-12-04 02:45:14 +04:00
if ( ! dig | | ! dig - > afmt | | ! dig - > afmt - > pin )
2013-10-11 02:03:06 +04:00
return ;
offset = dig - > afmt - > pin - > offset ;
list_for_each_entry ( connector , & encoder - > dev - > mode_config . connector_list , head ) {
if ( connector - > encoder = = encoder ) {
radeon_connector = to_radeon_connector ( connector ) ;
break ;
}
}
if ( ! radeon_connector ) {
DRM_ERROR ( " Couldn't find encoder's connector \n " ) ;
return ;
}
if ( mode - > flags & DRM_MODE_FLAG_INTERLACE ) {
if ( connector - > latency_present [ 1 ] )
tmp = VIDEO_LIPSYNC ( connector - > video_latency [ 1 ] ) |
AUDIO_LIPSYNC ( connector - > audio_latency [ 1 ] ) ;
else
2014-07-13 03:47:14 +04:00
tmp = VIDEO_LIPSYNC ( 0 ) | AUDIO_LIPSYNC ( 0 ) ;
2013-10-11 02:03:06 +04:00
} else {
if ( connector - > latency_present [ 0 ] )
tmp = VIDEO_LIPSYNC ( connector - > video_latency [ 0 ] ) |
AUDIO_LIPSYNC ( connector - > audio_latency [ 0 ] ) ;
else
2014-07-13 03:47:14 +04:00
tmp = VIDEO_LIPSYNC ( 0 ) | AUDIO_LIPSYNC ( 0 ) ;
2013-10-11 02:03:06 +04:00
}
WREG32_ENDPOINT ( offset , AZ_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC , tmp ) ;
}
2013-08-15 13:16:30 +04:00
void dce6_afmt_write_speaker_allocation ( struct drm_encoder * encoder )
2013-08-01 00:51:33 +04:00
{
struct radeon_device * rdev = encoder - > dev - > dev_private ;
struct radeon_encoder * radeon_encoder = to_radeon_encoder ( encoder ) ;
struct radeon_encoder_atom_dig * dig = radeon_encoder - > enc_priv ;
2013-08-15 13:16:30 +04:00
struct drm_connector * connector ;
struct radeon_connector * radeon_connector = NULL ;
2013-08-01 00:51:33 +04:00
u32 offset , tmp ;
2014-10-13 21:23:48 +04:00
u8 * sadb = NULL ;
2013-08-15 13:16:30 +04:00
int sad_count ;
2013-12-04 02:45:14 +04:00
if ( ! dig | | ! dig - > afmt | | ! dig - > afmt - > pin )
2013-08-15 13:16:30 +04:00
return ;
offset = dig - > afmt - > pin - > offset ;
list_for_each_entry ( connector , & encoder - > dev - > mode_config . connector_list , head ) {
2014-07-13 03:47:15 +04:00
if ( connector - > encoder = = encoder ) {
2013-08-15 13:16:30 +04:00
radeon_connector = to_radeon_connector ( connector ) ;
2014-07-13 03:47:15 +04:00
break ;
}
2013-08-15 13:16:30 +04:00
}
if ( ! radeon_connector ) {
DRM_ERROR ( " Couldn't find encoder's connector \n " ) ;
return ;
}
2014-07-15 19:00:47 +04:00
sad_count = drm_edid_to_speaker_allocation ( radeon_connector_edid ( connector ) , & sadb ) ;
2014-10-13 19:51:50 +04:00
if ( sad_count < 0 ) {
DRM_DEBUG ( " Couldn't read Speaker Allocation Data Block: %d \n " , sad_count ) ;
sad_count = 0 ;
2013-08-15 13:16:30 +04:00
}
/* program the speaker allocation */
tmp = RREG32_ENDPOINT ( offset , AZ_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER ) ;
tmp & = ~ ( DP_CONNECTION | SPEAKER_ALLOCATION_MASK ) ;
/* set HDMI mode */
tmp | = HDMI_CONNECTION ;
if ( sad_count )
tmp | = SPEAKER_ALLOCATION ( sadb [ 0 ] ) ;
else
tmp | = SPEAKER_ALLOCATION ( 5 ) ; /* stereo */
WREG32_ENDPOINT ( offset , AZ_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER , tmp ) ;
kfree ( sadb ) ;
}
void dce6_afmt_write_sad_regs ( struct drm_encoder * encoder )
{
struct radeon_device * rdev = encoder - > dev - > dev_private ;
struct radeon_encoder * radeon_encoder = to_radeon_encoder ( encoder ) ;
struct radeon_encoder_atom_dig * dig = radeon_encoder - > enc_priv ;
u32 offset ;
2013-08-01 00:51:33 +04:00
struct drm_connector * connector ;
struct radeon_connector * radeon_connector = NULL ;
struct cea_sad * sads ;
2013-08-15 13:16:30 +04:00
int i , sad_count ;
2013-08-01 00:51:33 +04:00
static const u16 eld_reg_to_type [ ] [ 2 ] = {
{ AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0 , HDMI_AUDIO_CODING_TYPE_PCM } ,
{ AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR1 , HDMI_AUDIO_CODING_TYPE_AC3 } ,
{ AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR2 , HDMI_AUDIO_CODING_TYPE_MPEG1 } ,
{ AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR3 , HDMI_AUDIO_CODING_TYPE_MP3 } ,
{ AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR4 , HDMI_AUDIO_CODING_TYPE_MPEG2 } ,
{ AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR5 , HDMI_AUDIO_CODING_TYPE_AAC_LC } ,
{ AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR6 , HDMI_AUDIO_CODING_TYPE_DTS } ,
{ AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR7 , HDMI_AUDIO_CODING_TYPE_ATRAC } ,
{ AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR9 , HDMI_AUDIO_CODING_TYPE_EAC3 } ,
{ AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR10 , HDMI_AUDIO_CODING_TYPE_DTS_HD } ,
{ AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR11 , HDMI_AUDIO_CODING_TYPE_MLP } ,
{ AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR13 , HDMI_AUDIO_CODING_TYPE_WMA_PRO } ,
} ;
2013-12-04 02:45:14 +04:00
if ( ! dig | | ! dig - > afmt | | ! dig - > afmt - > pin )
2013-08-01 00:51:33 +04:00
return ;
offset = dig - > afmt - > pin - > offset ;
list_for_each_entry ( connector , & encoder - > dev - > mode_config . connector_list , head ) {
2014-07-13 03:47:15 +04:00
if ( connector - > encoder = = encoder ) {
2013-08-01 00:51:33 +04:00
radeon_connector = to_radeon_connector ( connector ) ;
2014-07-13 03:47:15 +04:00
break ;
}
2013-08-01 00:51:33 +04:00
}
if ( ! radeon_connector ) {
DRM_ERROR ( " Couldn't find encoder's connector \n " ) ;
return ;
}
2014-07-15 19:00:47 +04:00
sad_count = drm_edid_to_sad ( radeon_connector_edid ( connector ) , & sads ) ;
2013-12-13 18:05:49 +04:00
if ( sad_count < = 0 ) {
2013-08-01 00:51:33 +04:00
DRM_ERROR ( " Couldn't read SADs: %d \n " , sad_count ) ;
return ;
}
BUG_ON ( ! sads ) ;
for ( i = 0 ; i < ARRAY_SIZE ( eld_reg_to_type ) ; i + + ) {
u32 value = 0 ;
2013-10-29 03:19:16 +04:00
u8 stereo_freqs = 0 ;
int max_channels = - 1 ;
2013-08-01 00:51:33 +04:00
int j ;
for ( j = 0 ; j < sad_count ; j + + ) {
struct cea_sad * sad = & sads [ j ] ;
if ( sad - > format = = eld_reg_to_type [ i ] [ 1 ] ) {
2013-10-29 03:19:16 +04:00
if ( sad - > channels > max_channels ) {
value = MAX_CHANNELS ( sad - > channels ) |
DESCRIPTOR_BYTE_2 ( sad - > byte2 ) |
SUPPORTED_FREQUENCIES ( sad - > freq ) ;
max_channels = sad - > channels ;
}
2013-08-01 00:51:33 +04:00
if ( sad - > format = = HDMI_AUDIO_CODING_TYPE_PCM )
2013-10-29 03:19:16 +04:00
stereo_freqs | = sad - > freq ;
else
break ;
2013-08-01 00:51:33 +04:00
}
}
2013-10-29 03:19:16 +04:00
value | = SUPPORTED_FREQUENCIES_STEREO ( stereo_freqs ) ;
2013-08-01 00:51:33 +04:00
WREG32_ENDPOINT ( offset , eld_reg_to_type [ i ] [ 0 ] , value ) ;
}
kfree ( sads ) ;
}
static int dce6_audio_chipset_supported ( struct radeon_device * rdev )
{
return ! ASIC_IS_NODCE ( rdev ) ;
}
2014-02-18 20:07:55 +04:00
void dce6_audio_enable ( struct radeon_device * rdev ,
struct r600_audio_pin * pin ,
2014-09-19 01:26:39 +04:00
u8 enable_mask )
2013-08-01 00:51:33 +04:00
{
2014-02-18 20:07:55 +04:00
if ( ! pin )
return ;
2014-09-19 00:19:04 +04:00
WREG32_ENDPOINT ( pin - > offset , AZ_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL ,
2014-09-19 01:26:39 +04:00
enable_mask ? AUDIO_ENABLED : 0 ) ;
2013-08-01 00:51:33 +04:00
}
static const u32 pin_offsets [ 7 ] =
{
( 0x5e00 - 0x5e00 ) ,
( 0x5e18 - 0x5e00 ) ,
( 0x5e30 - 0x5e00 ) ,
( 0x5e48 - 0x5e00 ) ,
( 0x5e60 - 0x5e00 ) ,
( 0x5e78 - 0x5e00 ) ,
( 0x5e90 - 0x5e00 ) ,
} ;
int dce6_audio_init ( struct radeon_device * rdev )
{
int i ;
if ( ! radeon_audio | | ! dce6_audio_chipset_supported ( rdev ) )
return 0 ;
rdev - > audio . enabled = true ;
2014-04-08 19:28:54 +04:00
if ( ASIC_IS_DCE81 ( rdev ) ) /* KV: 4 streams, 7 endpoints */
rdev - > audio . num_pins = 7 ;
else if ( ASIC_IS_DCE83 ( rdev ) ) /* KB: 2 streams, 3 endpoints */
rdev - > audio . num_pins = 3 ;
else if ( ASIC_IS_DCE8 ( rdev ) ) /* BN/HW: 6 streams, 7 endpoints */
rdev - > audio . num_pins = 7 ;
else if ( ASIC_IS_DCE61 ( rdev ) ) /* TN: 4 streams, 6 endpoints */
2013-12-12 21:13:17 +04:00
rdev - > audio . num_pins = 6 ;
2014-04-08 19:28:54 +04:00
else if ( ASIC_IS_DCE64 ( rdev ) ) /* OL: 2 streams, 2 endpoints */
rdev - > audio . num_pins = 2 ;
else /* SI: 6 streams, 6 endpoints */
2013-08-01 00:51:33 +04:00
rdev - > audio . num_pins = 6 ;
for ( i = 0 ; i < rdev - > audio . num_pins ; i + + ) {
rdev - > audio . pin [ i ] . channels = - 1 ;
rdev - > audio . pin [ i ] . rate = - 1 ;
rdev - > audio . pin [ i ] . bits_per_sample = - 1 ;
rdev - > audio . pin [ i ] . status_bits = 0 ;
rdev - > audio . pin [ i ] . category_code = 0 ;
rdev - > audio . pin [ i ] . connected = false ;
rdev - > audio . pin [ i ] . offset = pin_offsets [ i ] ;
rdev - > audio . pin [ i ] . id = i ;
2014-02-18 20:07:55 +04:00
/* disable audio. it will be set up later */
dce6_audio_enable ( rdev , & rdev - > audio . pin [ i ] , false ) ;
2013-08-01 00:51:33 +04:00
}
return 0 ;
}
void dce6_audio_fini ( struct radeon_device * rdev )
{
int i ;
if ( ! rdev - > audio . enabled )
return ;
for ( i = 0 ; i < rdev - > audio . num_pins ; i + + )
dce6_audio_enable ( rdev , & rdev - > audio . pin [ i ] , false ) ;
rdev - > audio . enabled = false ;
}