2019-06-03 07:44:50 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2013-12-11 14:44:02 -05:00
/*
* Copyright ( C ) 2013 Red Hat
* Author : Rob Clark < robdclark @ gmail . com >
*/
# include <linux/hdmi.h>
# include "hdmi.h"
/* maps MSM_HDMI_AUDIO_CHANNEL_n consts used by audio driver to # of channels: */
static int nchannels [ ] = { 2 , 4 , 6 , 8 } ;
/* Supported HDMI Audio sample rates */
# define MSM_HDMI_SAMPLE_RATE_32KHZ 0
# define MSM_HDMI_SAMPLE_RATE_44_1KHZ 1
# define MSM_HDMI_SAMPLE_RATE_48KHZ 2
# define MSM_HDMI_SAMPLE_RATE_88_2KHZ 3
# define MSM_HDMI_SAMPLE_RATE_96KHZ 4
# define MSM_HDMI_SAMPLE_RATE_176_4KHZ 5
# define MSM_HDMI_SAMPLE_RATE_192KHZ 6
# define MSM_HDMI_SAMPLE_RATE_MAX 7
struct hdmi_msm_audio_acr {
uint32_t n ; /* N parameter for clock regeneration */
uint32_t cts ; /* CTS parameter for clock regeneration */
} ;
struct hdmi_msm_audio_arcs {
unsigned long int pixclock ;
struct hdmi_msm_audio_acr lut [ MSM_HDMI_SAMPLE_RATE_MAX ] ;
} ;
# define HDMI_MSM_AUDIO_ARCS(pclk, ...) { (1000 * (pclk)), __VA_ARGS__ }
/* Audio constants lookup table for hdmi_msm_audio_acr_setup */
/* Valid Pixel-Clock rates: 25.2MHz, 27MHz, 27.03MHz, 74.25MHz, 148.5MHz */
static const struct hdmi_msm_audio_arcs acr_lut [ ] = {
/* 25.200MHz */
HDMI_MSM_AUDIO_ARCS ( 25200 , {
{ 4096 , 25200 } , { 6272 , 28000 } , { 6144 , 25200 } , { 12544 , 28000 } ,
{ 12288 , 25200 } , { 25088 , 28000 } , { 24576 , 25200 } } ) ,
/* 27.000MHz */
HDMI_MSM_AUDIO_ARCS ( 27000 , {
{ 4096 , 27000 } , { 6272 , 30000 } , { 6144 , 27000 } , { 12544 , 30000 } ,
{ 12288 , 27000 } , { 25088 , 30000 } , { 24576 , 27000 } } ) ,
/* 27.027MHz */
HDMI_MSM_AUDIO_ARCS ( 27030 , {
{ 4096 , 27027 } , { 6272 , 30030 } , { 6144 , 27027 } , { 12544 , 30030 } ,
{ 12288 , 27027 } , { 25088 , 30030 } , { 24576 , 27027 } } ) ,
/* 74.250MHz */
HDMI_MSM_AUDIO_ARCS ( 74250 , {
{ 4096 , 74250 } , { 6272 , 82500 } , { 6144 , 74250 } , { 12544 , 82500 } ,
{ 12288 , 74250 } , { 25088 , 82500 } , { 24576 , 74250 } } ) ,
/* 148.500MHz */
HDMI_MSM_AUDIO_ARCS ( 148500 , {
{ 4096 , 148500 } , { 6272 , 165000 } , { 6144 , 148500 } , { 12544 , 165000 } ,
{ 12288 , 148500 } , { 25088 , 165000 } , { 24576 , 148500 } } ) ,
} ;
static const struct hdmi_msm_audio_arcs * get_arcs ( unsigned long int pixclock )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( acr_lut ) ; i + + ) {
const struct hdmi_msm_audio_arcs * arcs = & acr_lut [ i ] ;
if ( arcs - > pixclock = = pixclock )
return arcs ;
}
return NULL ;
}
2016-02-22 22:08:35 +01:00
int msm_hdmi_audio_update ( struct hdmi * hdmi )
2013-12-11 14:44:02 -05:00
{
struct hdmi_audio * audio = & hdmi - > audio ;
struct hdmi_audio_infoframe * info = & audio - > infoframe ;
const struct hdmi_msm_audio_arcs * arcs = NULL ;
bool enabled = audio - > enabled ;
uint32_t acr_pkt_ctrl , vbi_pkt_ctrl , aud_pkt_ctrl ;
uint32_t infofrm_ctrl , audio_config ;
DBG ( " audio: enabled=%d, channels=%d, channel_allocation=0x%x, "
" level_shift_value=%d, downmix_inhibit=%d, rate=%d " ,
audio - > enabled , info - > channels , info - > channel_allocation ,
info - > level_shift_value , info - > downmix_inhibit , audio - > rate ) ;
DBG ( " video: power_on=%d, pixclock=%lu " , hdmi - > power_on , hdmi - > pixclock ) ;
if ( enabled & & ! ( hdmi - > power_on & & hdmi - > pixclock ) ) {
DBG ( " disabling audio: no video " ) ;
enabled = false ;
}
if ( enabled ) {
arcs = get_arcs ( hdmi - > pixclock ) ;
if ( ! arcs ) {
DBG ( " disabling audio: unsupported pixclock: %lu " ,
hdmi - > pixclock ) ;
enabled = false ;
}
}
/* Read first before writing */
acr_pkt_ctrl = hdmi_read ( hdmi , REG_HDMI_ACR_PKT_CTRL ) ;
vbi_pkt_ctrl = hdmi_read ( hdmi , REG_HDMI_VBI_PKT_CTRL ) ;
aud_pkt_ctrl = hdmi_read ( hdmi , REG_HDMI_AUDIO_PKT_CTRL1 ) ;
infofrm_ctrl = hdmi_read ( hdmi , REG_HDMI_INFOFRAME_CTRL0 ) ;
audio_config = hdmi_read ( hdmi , REG_HDMI_AUDIO_CFG ) ;
/* Clear N/CTS selection bits */
acr_pkt_ctrl & = ~ HDMI_ACR_PKT_CTRL_SELECT__MASK ;
if ( enabled ) {
uint32_t n , cts , multiplier ;
enum hdmi_acr_cts select ;
uint8_t buf [ 14 ] ;
n = arcs - > lut [ audio - > rate ] . n ;
cts = arcs - > lut [ audio - > rate ] . cts ;
if ( ( MSM_HDMI_SAMPLE_RATE_192KHZ = = audio - > rate ) | |
( MSM_HDMI_SAMPLE_RATE_176_4KHZ = = audio - > rate ) ) {
multiplier = 4 ;
n > > = 2 ; /* divide N by 4 and use multiplier */
} else if ( ( MSM_HDMI_SAMPLE_RATE_96KHZ = = audio - > rate ) | |
( MSM_HDMI_SAMPLE_RATE_88_2KHZ = = audio - > rate ) ) {
multiplier = 2 ;
n > > = 1 ; /* divide N by 2 and use multiplier */
} else {
multiplier = 1 ;
}
DBG ( " n=%u, cts=%u, multiplier=%u " , n , cts , multiplier ) ;
acr_pkt_ctrl | = HDMI_ACR_PKT_CTRL_SOURCE ;
acr_pkt_ctrl | = HDMI_ACR_PKT_CTRL_AUDIO_PRIORITY ;
acr_pkt_ctrl | = HDMI_ACR_PKT_CTRL_N_MULTIPLIER ( multiplier ) ;
if ( ( MSM_HDMI_SAMPLE_RATE_48KHZ = = audio - > rate ) | |
( MSM_HDMI_SAMPLE_RATE_96KHZ = = audio - > rate ) | |
( MSM_HDMI_SAMPLE_RATE_192KHZ = = audio - > rate ) )
select = ACR_48 ;
else if ( ( MSM_HDMI_SAMPLE_RATE_44_1KHZ = = audio - > rate ) | |
( MSM_HDMI_SAMPLE_RATE_88_2KHZ = = audio - > rate ) | |
( MSM_HDMI_SAMPLE_RATE_176_4KHZ = = audio - > rate ) )
select = ACR_44 ;
else /* default to 32k */
select = ACR_32 ;
acr_pkt_ctrl | = HDMI_ACR_PKT_CTRL_SELECT ( select ) ;
hdmi_write ( hdmi , REG_HDMI_ACR_0 ( select - 1 ) ,
HDMI_ACR_0_CTS ( cts ) ) ;
hdmi_write ( hdmi , REG_HDMI_ACR_1 ( select - 1 ) ,
HDMI_ACR_1_N ( n ) ) ;
hdmi_write ( hdmi , REG_HDMI_AUDIO_PKT_CTRL2 ,
COND ( info - > channels ! = 2 , HDMI_AUDIO_PKT_CTRL2_LAYOUT ) |
HDMI_AUDIO_PKT_CTRL2_OVERRIDE ) ;
acr_pkt_ctrl | = HDMI_ACR_PKT_CTRL_CONT ;
acr_pkt_ctrl | = HDMI_ACR_PKT_CTRL_SEND ;
/* configure infoframe: */
hdmi_audio_infoframe_pack ( info , buf , sizeof ( buf ) ) ;
hdmi_write ( hdmi , REG_HDMI_AUDIO_INFO0 ,
2017-06-15 15:13:46 +01:00
( buf [ 3 ] < < 0 ) | ( buf [ 4 ] < < 8 ) |
( buf [ 5 ] < < 16 ) | ( buf [ 6 ] < < 24 ) ) ;
2013-12-11 14:44:02 -05:00
hdmi_write ( hdmi , REG_HDMI_AUDIO_INFO1 ,
2017-06-15 15:13:46 +01:00
( buf [ 7 ] < < 0 ) | ( buf [ 8 ] < < 8 ) ) ;
2013-12-11 14:44:02 -05:00
hdmi_write ( hdmi , REG_HDMI_GC , 0 ) ;
vbi_pkt_ctrl | = HDMI_VBI_PKT_CTRL_GC_ENABLE ;
vbi_pkt_ctrl | = HDMI_VBI_PKT_CTRL_GC_EVERY_FRAME ;
aud_pkt_ctrl | = HDMI_AUDIO_PKT_CTRL1_AUDIO_SAMPLE_SEND ;
infofrm_ctrl | = HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND ;
infofrm_ctrl | = HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT ;
infofrm_ctrl | = HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE ;
infofrm_ctrl | = HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE ;
audio_config & = ~ HDMI_AUDIO_CFG_FIFO_WATERMARK__MASK ;
audio_config | = HDMI_AUDIO_CFG_FIFO_WATERMARK ( 4 ) ;
audio_config | = HDMI_AUDIO_CFG_ENGINE_ENABLE ;
} else {
acr_pkt_ctrl & = ~ HDMI_ACR_PKT_CTRL_CONT ;
acr_pkt_ctrl & = ~ HDMI_ACR_PKT_CTRL_SEND ;
vbi_pkt_ctrl & = ~ HDMI_VBI_PKT_CTRL_GC_ENABLE ;
vbi_pkt_ctrl & = ~ HDMI_VBI_PKT_CTRL_GC_EVERY_FRAME ;
aud_pkt_ctrl & = ~ HDMI_AUDIO_PKT_CTRL1_AUDIO_SAMPLE_SEND ;
infofrm_ctrl & = ~ HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND ;
infofrm_ctrl & = ~ HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT ;
infofrm_ctrl & = ~ HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE ;
infofrm_ctrl & = ~ HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE ;
audio_config & = ~ HDMI_AUDIO_CFG_ENGINE_ENABLE ;
}
hdmi_write ( hdmi , REG_HDMI_ACR_PKT_CTRL , acr_pkt_ctrl ) ;
hdmi_write ( hdmi , REG_HDMI_VBI_PKT_CTRL , vbi_pkt_ctrl ) ;
hdmi_write ( hdmi , REG_HDMI_AUDIO_PKT_CTRL1 , aud_pkt_ctrl ) ;
hdmi_write ( hdmi , REG_HDMI_INFOFRAME_CTRL0 , infofrm_ctrl ) ;
hdmi_write ( hdmi , REG_HDMI_AUD_INT ,
COND ( enabled , HDMI_AUD_INT_AUD_FIFO_URUN_INT ) |
COND ( enabled , HDMI_AUD_INT_AUD_SAM_DROP_INT ) ) ;
hdmi_write ( hdmi , REG_HDMI_AUDIO_CFG , audio_config ) ;
DBG ( " audio %sabled " , enabled ? " en " : " dis " ) ;
return 0 ;
}
2016-02-22 22:08:35 +01:00
int msm_hdmi_audio_info_setup ( struct hdmi * hdmi , bool enabled ,
2013-12-11 14:44:02 -05:00
uint32_t num_of_channels , uint32_t channel_allocation ,
uint32_t level_shift , bool down_mix )
{
struct hdmi_audio * audio ;
if ( ! hdmi )
return - ENXIO ;
audio = & hdmi - > audio ;
if ( num_of_channels > = ARRAY_SIZE ( nchannels ) )
return - EINVAL ;
audio - > enabled = enabled ;
audio - > infoframe . channels = nchannels [ num_of_channels ] ;
audio - > infoframe . channel_allocation = channel_allocation ;
audio - > infoframe . level_shift_value = level_shift ;
audio - > infoframe . downmix_inhibit = down_mix ;
2016-02-22 22:08:35 +01:00
return msm_hdmi_audio_update ( hdmi ) ;
2013-12-11 14:44:02 -05:00
}
2016-02-22 22:08:35 +01:00
void msm_hdmi_audio_set_sample_rate ( struct hdmi * hdmi , int rate )
2013-12-11 14:44:02 -05:00
{
struct hdmi_audio * audio ;
if ( ! hdmi )
return ;
audio = & hdmi - > audio ;
if ( ( rate < 0 ) | | ( rate > = MSM_HDMI_SAMPLE_RATE_MAX ) )
return ;
audio - > rate = rate ;
2016-02-22 22:08:35 +01:00
msm_hdmi_audio_update ( hdmi ) ;
2013-12-11 14:44:02 -05:00
}