2019-05-19 13:08:55 +01:00
// SPDX-License-Identifier: GPL-2.0-only
2015-04-14 12:15:47 +02:00
/*
* HD - audio stream operations
*/
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/export.h>
2015-04-16 11:31:12 +02:00
# include <linux/clocksource.h>
2022-12-02 16:28:28 +01:00
# include <sound/compress_driver.h>
2015-04-14 12:15:47 +02:00
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/hdaudio.h>
# include <sound/hda_register.h>
2015-05-12 09:43:20 +08:00
# include "trace.h"
2015-04-14 12:15:47 +02:00
2022-09-19 14:10:35 +02:00
/*
* the hdac_stream library is intended to be used with the following
* transitions . The states are not formally defined in the code but loosely
* inspired by boolean variables . Note that the ' prepared ' field is not used
* in this library but by the callers during the hw_params / prepare transitions
*
* |
* stream_init ( ) |
* v
* + - - + - - - - - - - +
* | unused |
* + - - + - - - - + - - +
* | ^
* stream_assign ( ) | | stream_release ( )
* v |
* + - - + - - - - + - - +
* | opened |
* + - - + - - - - + - - +
* | ^
* stream_reset ( ) | |
* stream_setup ( ) | | stream_cleanup ( )
* v |
* + - - + - - - - + - - +
* | prepared |
* + - - + - - - - + - - +
* | ^
* stream_start ( ) | | stream_stop ( )
* v |
* + - - + - - - - + - - +
* | running |
* + - - - - - - - - - - +
*/
2019-01-14 23:51:09 +05:30
/**
* snd_hdac_get_stream_stripe_ctl - get stripe control value
* @ bus : HD - audio core bus
* @ substream : PCM substream
*/
int snd_hdac_get_stream_stripe_ctl ( struct hdac_bus * bus ,
struct snd_pcm_substream * substream )
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
unsigned int channels = runtime - > channels ,
rate = runtime - > rate ,
bits_per_sample = runtime - > sample_bits ,
max_sdo_lines , value , sdo_line ;
/* T_AZA_GCAP_NSDO is 1:2 bitfields in GCAP */
max_sdo_lines = snd_hdac_chip_readl ( bus , GCAP ) & AZX_GCAP_NSDO ;
/* following is from HD audio spec */
for ( sdo_line = max_sdo_lines ; sdo_line > 0 ; sdo_line > > = 1 ) {
if ( rate > 48000 )
value = ( channels * bits_per_sample *
( rate / 48000 ) ) / sdo_line ;
else
value = ( channels * bits_per_sample ) / sdo_line ;
2020-05-04 13:46:15 +05:30
if ( value > = bus - > sdo_limit )
2019-01-14 23:51:09 +05:30
break ;
}
/* stripe value: 0 for 1SDO, 1 for 2SDO, 2 for 4SDO lines */
return sdo_line > > 1 ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_get_stream_stripe_ctl ) ;
2015-04-14 12:15:47 +02:00
/**
* snd_hdac_stream_init - initialize each stream ( aka device )
* @ bus : HD - audio core bus
* @ azx_dev : HD - audio core stream object to initialize
* @ idx : stream index number
* @ direction : stream direction ( SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE )
* @ tag : the tag id to assign
*
* Assign the starting bdl address to each stream ( device ) and initialize .
*/
void snd_hdac_stream_init ( struct hdac_bus * bus , struct hdac_stream * azx_dev ,
int idx , int direction , int tag )
{
azx_dev - > bus = bus ;
/* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
azx_dev - > sd_addr = bus - > remap_addr + ( 0x20 * idx + 0x80 ) ;
/* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
azx_dev - > sd_int_sta_mask = 1 < < idx ;
azx_dev - > index = idx ;
azx_dev - > direction = direction ;
azx_dev - > stream_tag = tag ;
2015-04-14 12:53:28 +02:00
snd_hdac_dsp_lock_init ( azx_dev ) ;
2015-04-14 12:15:47 +02:00
list_add_tail ( & azx_dev - > list , & bus - > stream_list ) ;
2022-10-19 11:21:15 -05:00
if ( bus - > spbcap ) {
azx_dev - > spib_addr = bus - > spbcap + AZX_SPB_BASE +
AZX_SPB_INTERVAL * idx +
AZX_SPB_SPIB ;
azx_dev - > fifo_addr = bus - > spbcap + AZX_SPB_BASE +
AZX_SPB_INTERVAL * idx +
AZX_SPB_MAXFIFO ;
}
if ( bus - > drsmcap )
azx_dev - > dpibr_addr = bus - > drsmcap + AZX_DRSM_BASE +
AZX_DRSM_INTERVAL * idx ;
2015-04-14 12:15:47 +02:00
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_init ) ;
/**
* snd_hdac_stream_start - start a stream
* @ azx_dev : HD - audio core stream to start
*
* Start a stream , set start_wallclk and set the running flag .
*/
2023-02-09 20:17:23 +08:00
void snd_hdac_stream_start ( struct hdac_stream * azx_dev )
2015-04-14 12:15:47 +02:00
{
struct hdac_bus * bus = azx_dev - > bus ;
2019-01-14 23:51:11 +05:30
int stripe_ctl ;
2015-04-14 12:15:47 +02:00
2015-05-12 09:43:20 +08:00
trace_snd_hdac_stream_start ( bus , azx_dev ) ;
2015-04-14 12:15:47 +02:00
azx_dev - > start_wallclk = snd_hdac_chip_readl ( bus , WALLCLK ) ;
/* enable SIE */
2019-01-09 16:20:51 +08:00
snd_hdac_chip_updatel ( bus , INTCTL ,
1 < < azx_dev - > index ,
1 < < azx_dev - > index ) ;
2019-01-14 23:51:11 +05:30
/* set stripe control */
2019-12-02 08:49:47 +01:00
if ( azx_dev - > stripe ) {
if ( azx_dev - > substream )
stripe_ctl = snd_hdac_get_stream_stripe_ctl ( bus , azx_dev - > substream ) ;
else
stripe_ctl = 0 ;
snd_hdac_stream_updateb ( azx_dev , SD_CTL_3B , SD_CTL_STRIPE_MASK ,
stripe_ctl ) ;
}
2015-04-14 12:15:47 +02:00
/* set DMA start and interrupt mask */
snd_hdac_stream_updateb ( azx_dev , SD_CTL ,
0 , SD_CTL_DMA_START | SD_INT_MASK ) ;
azx_dev - > running = true ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_start ) ;
/**
2022-09-19 14:10:34 +02:00
* snd_hdac_stream_clear - helper to clear stream registers and stop DMA transfers
2015-04-14 12:15:47 +02:00
* @ azx_dev : HD - audio core stream to stop
*/
2022-09-19 14:10:34 +02:00
static void snd_hdac_stream_clear ( struct hdac_stream * azx_dev )
2015-04-14 12:15:47 +02:00
{
snd_hdac_stream_updateb ( azx_dev , SD_CTL ,
SD_CTL_DMA_START | SD_INT_MASK , 0 ) ;
snd_hdac_stream_writeb ( azx_dev , SD_STS , SD_INT_MASK ) ; /* to be sure */
2019-12-14 18:52:17 +01:00
if ( azx_dev - > stripe )
2019-12-02 08:49:47 +01:00
snd_hdac_stream_updateb ( azx_dev , SD_CTL_3B , SD_CTL_STRIPE_MASK , 0 ) ;
2015-04-14 12:15:47 +02:00
azx_dev - > running = false ;
}
/**
* snd_hdac_stream_stop - stop a stream
* @ azx_dev : HD - audio core stream to stop
*
* Stop a stream DMA and disable stream interrupt
*/
void snd_hdac_stream_stop ( struct hdac_stream * azx_dev )
{
2015-05-12 09:43:20 +08:00
trace_snd_hdac_stream_stop ( azx_dev - > bus , azx_dev ) ;
2015-04-14 12:15:47 +02:00
snd_hdac_stream_clear ( azx_dev ) ;
/* disable SIE */
snd_hdac_chip_updatel ( azx_dev - > bus , INTCTL , 1 < < azx_dev - > index , 0 ) ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_stop ) ;
2022-09-19 14:10:38 +02:00
/**
* snd_hdac_stop_streams - stop all streams
* @ bus : HD - audio core bus
*/
void snd_hdac_stop_streams ( struct hdac_bus * bus )
{
struct hdac_stream * stream ;
list_for_each_entry ( stream , & bus - > stream_list , list )
snd_hdac_stream_stop ( stream ) ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_stop_streams ) ;
2021-12-16 17:11:27 -06:00
/**
* snd_hdac_stop_streams_and_chip - stop all streams and chip if running
* @ bus : HD - audio core bus
*/
void snd_hdac_stop_streams_and_chip ( struct hdac_bus * bus )
{
if ( bus - > chip_init ) {
2022-09-19 14:10:38 +02:00
snd_hdac_stop_streams ( bus ) ;
2021-12-16 17:11:27 -06:00
snd_hdac_bus_stop_chip ( bus ) ;
}
}
EXPORT_SYMBOL_GPL ( snd_hdac_stop_streams_and_chip ) ;
2015-04-14 12:15:47 +02:00
/**
* snd_hdac_stream_reset - reset a stream
* @ azx_dev : HD - audio core stream to reset
*/
void snd_hdac_stream_reset ( struct hdac_stream * azx_dev )
{
unsigned char val ;
2020-08-05 15:22:20 +05:30
int dma_run_state ;
2015-04-14 12:15:47 +02:00
snd_hdac_stream_clear ( azx_dev ) ;
2020-08-05 15:22:20 +05:30
dma_run_state = snd_hdac_stream_readb ( azx_dev , SD_CTL ) & SD_CTL_DMA_START ;
2015-04-14 12:15:47 +02:00
snd_hdac_stream_updateb ( azx_dev , SD_CTL , 0 , SD_CTL_STREAM_RESET ) ;
2022-08-18 16:15:15 +02:00
/* wait for hardware to report that the stream entered reset */
snd_hdac_stream_readb_poll ( azx_dev , SD_CTL , val , ( val & SD_CTL_STREAM_RESET ) , 3 , 300 ) ;
2020-08-05 15:22:20 +05:30
if ( azx_dev - > bus - > dma_stop_delay & & dma_run_state )
udelay ( azx_dev - > bus - > dma_stop_delay ) ;
2022-08-18 16:15:15 +02:00
snd_hdac_stream_updateb ( azx_dev , SD_CTL , SD_CTL_STREAM_RESET , 0 ) ;
2015-04-14 12:15:47 +02:00
2022-08-18 16:15:15 +02:00
/* wait for hardware to report that the stream is out of reset */
snd_hdac_stream_readb_poll ( azx_dev , SD_CTL , val , ! ( val & SD_CTL_STREAM_RESET ) , 3 , 300 ) ;
2015-04-14 12:15:47 +02:00
/* reset first position - may not be synced with hw at this time */
if ( azx_dev - > posbuf )
* azx_dev - > posbuf = 0 ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_reset ) ;
/**
* snd_hdac_stream_setup - set up the SD for streaming
* @ azx_dev : HD - audio core stream to set up
*/
int snd_hdac_stream_setup ( struct hdac_stream * azx_dev )
{
struct hdac_bus * bus = azx_dev - > bus ;
2015-05-19 11:35:13 +02:00
struct snd_pcm_runtime * runtime ;
2015-04-14 12:15:47 +02:00
unsigned int val ;
2015-05-19 11:35:13 +02:00
if ( azx_dev - > substream )
runtime = azx_dev - > substream - > runtime ;
else
runtime = NULL ;
2015-04-14 12:15:47 +02:00
/* make sure the run bit is zero for SD */
snd_hdac_stream_clear ( azx_dev ) ;
/* program the stream_tag */
val = snd_hdac_stream_readl ( azx_dev , SD_CTL ) ;
val = ( val & ~ SD_CTL_STREAM_TAG_MASK ) |
( azx_dev - > stream_tag < < SD_CTL_STREAM_TAG_SHIFT ) ;
if ( ! bus - > snoop )
val | = SD_CTL_TRAFFIC_PRIO ;
snd_hdac_stream_writel ( azx_dev , SD_CTL , val ) ;
/* program the length of samples in cyclic buffer */
snd_hdac_stream_writel ( azx_dev , SD_CBL , azx_dev - > bufsize ) ;
/* program the stream format */
/* this value needs to be the same as the one programmed */
snd_hdac_stream_writew ( azx_dev , SD_FORMAT , azx_dev - > format_val ) ;
/* program the stream LVI (last valid index) of the BDL */
snd_hdac_stream_writew ( azx_dev , SD_LVI , azx_dev - > frags - 1 ) ;
/* program the BDL address */
/* lower BDL address */
snd_hdac_stream_writel ( azx_dev , SD_BDLPL , ( u32 ) azx_dev - > bdl . addr ) ;
/* upper BDL address */
snd_hdac_stream_writel ( azx_dev , SD_BDLPU ,
upper_32_bits ( azx_dev - > bdl . addr ) ) ;
/* enable the position buffer */
if ( bus - > use_posbuf & & bus - > posbuf . addr ) {
if ( ! ( snd_hdac_chip_readl ( bus , DPLBASE ) & AZX_DPLBASE_ENABLE ) )
snd_hdac_chip_writel ( bus , DPLBASE ,
( u32 ) bus - > posbuf . addr | AZX_DPLBASE_ENABLE ) ;
}
/* set the interrupt enable bits in the descriptor control register */
snd_hdac_stream_updatel ( azx_dev , SD_CTL , 0 , SD_INT_MASK ) ;
2019-08-12 18:01:47 +02:00
azx_dev - > fifo_size = snd_hdac_stream_readw ( azx_dev , SD_FIFOSIZE ) + 1 ;
2015-04-14 12:15:47 +02:00
/* when LPIB delay correction gives a small negative value,
* we ignore it ; currently set the threshold statically to
* 64 frames
*/
2015-05-19 11:35:13 +02:00
if ( runtime & & runtime - > period_size > 64 )
2015-04-14 12:15:47 +02:00
azx_dev - > delay_negative_threshold =
- frames_to_bytes ( runtime , 64 ) ;
else
azx_dev - > delay_negative_threshold = 0 ;
/* wallclk has 24Mhz clock source */
2015-05-19 11:35:13 +02:00
if ( runtime )
azx_dev - > period_wallclk = ( ( ( runtime - > period_size * 24000 ) /
2015-04-14 12:15:47 +02:00
runtime - > rate ) * 1000 ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_setup ) ;
/**
* snd_hdac_stream_cleanup - cleanup a stream
* @ azx_dev : HD - audio core stream to clean up
*/
void snd_hdac_stream_cleanup ( struct hdac_stream * azx_dev )
{
snd_hdac_stream_writel ( azx_dev , SD_BDLPL , 0 ) ;
snd_hdac_stream_writel ( azx_dev , SD_BDLPU , 0 ) ;
snd_hdac_stream_writel ( azx_dev , SD_CTL , 0 ) ;
azx_dev - > bufsize = 0 ;
azx_dev - > period_bytes = 0 ;
azx_dev - > format_val = 0 ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_cleanup ) ;
/**
* snd_hdac_stream_assign - assign a stream for the PCM
* @ bus : HD - audio core bus
* @ substream : PCM substream to assign
*
* Look for an unused stream for the given PCM substream , assign it
* and return the stream object . If no stream is free , returns NULL .
* The function tries to keep using the same stream object when it ' s used
* beforehand . Also , when bus - > reverse_assign flag is set , the last free
* or matching entry is returned . This is needed for some strange codecs .
*/
struct hdac_stream * snd_hdac_stream_assign ( struct hdac_bus * bus ,
struct snd_pcm_substream * substream )
{
struct hdac_stream * azx_dev ;
struct hdac_stream * res = NULL ;
/* make a non-zero unique key for the substream */
int key = ( substream - > pcm - > device < < 16 ) | ( substream - > number < < 2 ) |
( substream - > stream + 1 ) ;
2021-09-24 14:24:14 -05:00
spin_lock_irq ( & bus - > reg_lock ) ;
2015-04-14 12:15:47 +02:00
list_for_each_entry ( azx_dev , & bus - > stream_list , list ) {
if ( azx_dev - > direction ! = substream - > stream )
continue ;
if ( azx_dev - > opened )
continue ;
if ( azx_dev - > assigned_key = = key ) {
res = azx_dev ;
break ;
}
if ( ! res | | bus - > reverse_assign )
res = azx_dev ;
}
if ( res ) {
res - > opened = 1 ;
res - > running = 0 ;
res - > assigned_key = key ;
res - > substream = substream ;
}
2021-09-24 14:24:14 -05:00
spin_unlock_irq ( & bus - > reg_lock ) ;
2015-04-14 12:15:47 +02:00
return res ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_assign ) ;
2022-09-19 14:10:40 +02:00
/**
* snd_hdac_stream_release_locked - release the assigned stream
* @ azx_dev : HD - audio core stream to release
*
* Release the stream that has been assigned by snd_hdac_stream_assign ( ) .
* The bus - > reg_lock needs to be taken at a higher level
*/
void snd_hdac_stream_release_locked ( struct hdac_stream * azx_dev )
{
azx_dev - > opened = 0 ;
azx_dev - > running = 0 ;
azx_dev - > substream = NULL ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_release_locked ) ;
2015-04-14 12:15:47 +02:00
/**
* snd_hdac_stream_release - release the assigned stream
* @ azx_dev : HD - audio core stream to release
*
* Release the stream that has been assigned by snd_hdac_stream_assign ( ) .
*/
void snd_hdac_stream_release ( struct hdac_stream * azx_dev )
{
struct hdac_bus * bus = azx_dev - > bus ;
spin_lock_irq ( & bus - > reg_lock ) ;
2022-09-19 14:10:40 +02:00
snd_hdac_stream_release_locked ( azx_dev ) ;
2015-04-14 12:15:47 +02:00
spin_unlock_irq ( & bus - > reg_lock ) ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_release ) ;
2015-08-23 11:52:51 +05:30
/**
* snd_hdac_get_stream - return hdac_stream based on stream_tag and
* direction
*
* @ bus : HD - audio core bus
* @ dir : direction for the stream to be found
* @ stream_tag : stream tag for stream to be found
*/
struct hdac_stream * snd_hdac_get_stream ( struct hdac_bus * bus ,
int dir , int stream_tag )
{
struct hdac_stream * s ;
list_for_each_entry ( s , & bus - > stream_list , list ) {
if ( s - > direction = = dir & & s - > stream_tag = = stream_tag )
return s ;
}
return NULL ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_get_stream ) ;
2015-04-14 12:15:47 +02:00
/*
* set up a BDL entry
*/
static int setup_bdle ( struct hdac_bus * bus ,
struct snd_dma_buffer * dmab ,
struct hdac_stream * azx_dev , __le32 * * bdlp ,
int ofs , int size , int with_ioc )
{
__le32 * bdl = * bdlp ;
while ( size > 0 ) {
dma_addr_t addr ;
int chunk ;
if ( azx_dev - > frags > = AZX_MAX_BDL_ENTRIES )
return - EINVAL ;
addr = snd_sgbuf_get_addr ( dmab , ofs ) ;
/* program the address field of the BDL entry */
bdl [ 0 ] = cpu_to_le32 ( ( u32 ) addr ) ;
bdl [ 1 ] = cpu_to_le32 ( upper_32_bits ( addr ) ) ;
/* program the size field of the BDL entry */
chunk = snd_sgbuf_get_chunk_size ( dmab , ofs , size ) ;
/* one BDLE cannot cross 4K boundary on CTHDA chips */
if ( bus - > align_bdle_4k ) {
u32 remain = 0x1000 - ( ofs & 0xfff ) ;
if ( chunk > remain )
chunk = remain ;
}
bdl [ 2 ] = cpu_to_le32 ( chunk ) ;
/* program the IOC to enable interrupt
* only when the whole fragment is processed
*/
size - = chunk ;
bdl [ 3 ] = ( size | | ! with_ioc ) ? 0 : cpu_to_le32 ( 0x01 ) ;
bdl + = 4 ;
azx_dev - > frags + + ;
ofs + = chunk ;
}
* bdlp = bdl ;
return ofs ;
}
/**
* snd_hdac_stream_setup_periods - set up BDL entries
* @ azx_dev : HD - audio core stream to set up
*
* Set up the buffer descriptor table of the given stream based on the
* period and buffer sizes of the assigned PCM substream .
*/
int snd_hdac_stream_setup_periods ( struct hdac_stream * azx_dev )
{
struct hdac_bus * bus = azx_dev - > bus ;
struct snd_pcm_substream * substream = azx_dev - > substream ;
2022-12-02 16:28:28 +01:00
struct snd_compr_stream * cstream = azx_dev - > cstream ;
struct snd_pcm_runtime * runtime = NULL ;
2022-12-02 16:28:27 +01:00
struct snd_dma_buffer * dmab ;
2015-04-14 12:15:47 +02:00
__le32 * bdl ;
int i , ofs , periods , period_bytes ;
int pos_adj , pos_align ;
2022-12-02 16:28:28 +01:00
if ( substream ) {
runtime = substream - > runtime ;
dmab = snd_pcm_get_dma_buf ( substream ) ;
} else if ( cstream ) {
dmab = snd_pcm_get_dma_buf ( cstream ) ;
2022-12-08 15:26:35 +01:00
} else {
WARN ( 1 , " No substream or cstream assigned \n " ) ;
return - EINVAL ;
2022-12-02 16:28:28 +01:00
}
2022-12-02 16:28:27 +01:00
2015-04-14 12:15:47 +02:00
/* reset BDL address */
snd_hdac_stream_writel ( azx_dev , SD_BDLPL , 0 ) ;
snd_hdac_stream_writel ( azx_dev , SD_BDLPU , 0 ) ;
period_bytes = azx_dev - > period_bytes ;
periods = azx_dev - > bufsize / period_bytes ;
/* program the initial BDL entries */
bdl = ( __le32 * ) azx_dev - > bdl . area ;
ofs = 0 ;
azx_dev - > frags = 0 ;
pos_adj = bus - > bdl_pos_adj ;
2022-12-02 16:28:27 +01:00
if ( runtime & & ! azx_dev - > no_period_wakeup & & pos_adj > 0 ) {
2015-04-14 12:15:47 +02:00
pos_align = pos_adj ;
2020-12-23 18:22:20 +01:00
pos_adj = DIV_ROUND_UP ( pos_adj * runtime - > rate , 48000 ) ;
2015-04-14 12:15:47 +02:00
if ( ! pos_adj )
pos_adj = pos_align ;
else
2020-12-23 18:22:20 +01:00
pos_adj = roundup ( pos_adj , pos_align ) ;
2015-04-14 12:15:47 +02:00
pos_adj = frames_to_bytes ( runtime , pos_adj ) ;
if ( pos_adj > = period_bytes ) {
dev_warn ( bus - > dev , " Too big adjustment %d \n " ,
pos_adj ) ;
pos_adj = 0 ;
} else {
2022-12-02 16:28:27 +01:00
ofs = setup_bdle ( bus , dmab , azx_dev ,
2015-04-14 12:15:47 +02:00
& bdl , ofs , pos_adj , true ) ;
if ( ofs < 0 )
goto error ;
}
} else
pos_adj = 0 ;
for ( i = 0 ; i < periods ; i + + ) {
if ( i = = periods - 1 & & pos_adj )
2022-12-02 16:28:27 +01:00
ofs = setup_bdle ( bus , dmab , azx_dev ,
& bdl , ofs , period_bytes - pos_adj , 0 ) ;
2015-04-14 12:15:47 +02:00
else
2022-12-02 16:28:27 +01:00
ofs = setup_bdle ( bus , dmab , azx_dev ,
& bdl , ofs , period_bytes ,
2015-04-14 12:15:47 +02:00
! azx_dev - > no_period_wakeup ) ;
if ( ofs < 0 )
goto error ;
}
return 0 ;
error :
dev_err ( bus - > dev , " Too many BDL entries: buffer=%d, period=%d \n " ,
azx_dev - > bufsize , period_bytes ) ;
return - EINVAL ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_setup_periods ) ;
2015-10-28 12:26:48 +01:00
/**
* snd_hdac_stream_set_params - set stream parameters
2015-04-17 17:58:58 +05:30
* @ azx_dev : HD - audio core stream for which parameters are to be set
* @ format_val : format value parameter
*
* Setup the HD - audio core stream parameters from substream of the stream
* and passed format value
*/
int snd_hdac_stream_set_params ( struct hdac_stream * azx_dev ,
unsigned int format_val )
{
struct snd_pcm_substream * substream = azx_dev - > substream ;
2022-12-02 16:28:28 +01:00
struct snd_compr_stream * cstream = azx_dev - > cstream ;
2022-12-02 16:28:27 +01:00
unsigned int bufsize , period_bytes ;
unsigned int no_period_wakeup ;
2015-04-17 17:58:58 +05:30
int err ;
2022-12-02 16:28:28 +01:00
if ( substream ) {
bufsize = snd_pcm_lib_buffer_bytes ( substream ) ;
period_bytes = snd_pcm_lib_period_bytes ( substream ) ;
no_period_wakeup = substream - > runtime - > no_period_wakeup ;
} else if ( cstream ) {
bufsize = cstream - > runtime - > buffer_size ;
period_bytes = cstream - > runtime - > fragment_size ;
no_period_wakeup = 0 ;
} else {
2015-04-17 17:58:58 +05:30
return - EINVAL ;
2022-12-02 16:28:28 +01:00
}
2015-04-17 17:58:58 +05:30
if ( bufsize ! = azx_dev - > bufsize | |
period_bytes ! = azx_dev - > period_bytes | |
format_val ! = azx_dev - > format_val | |
2022-12-02 16:28:27 +01:00
no_period_wakeup ! = azx_dev - > no_period_wakeup ) {
2015-04-17 17:58:58 +05:30
azx_dev - > bufsize = bufsize ;
azx_dev - > period_bytes = period_bytes ;
azx_dev - > format_val = format_val ;
2022-12-02 16:28:27 +01:00
azx_dev - > no_period_wakeup = no_period_wakeup ;
2015-04-17 17:58:58 +05:30
err = snd_hdac_stream_setup_periods ( azx_dev ) ;
if ( err < 0 )
return err ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_set_params ) ;
2016-12-21 20:32:01 +01:00
static u64 azx_cc_read ( const struct cyclecounter * cc )
2015-04-14 12:15:47 +02:00
{
struct hdac_stream * azx_dev = container_of ( cc , struct hdac_stream , cc ) ;
return snd_hdac_chip_readl ( azx_dev - > bus , WALLCLK ) ;
}
static void azx_timecounter_init ( struct hdac_stream * azx_dev ,
2016-12-21 20:32:01 +01:00
bool force , u64 last )
2015-04-14 12:15:47 +02:00
{
struct timecounter * tc = & azx_dev - > tc ;
struct cyclecounter * cc = & azx_dev - > cc ;
u64 nsec ;
cc - > read = azx_cc_read ;
cc - > mask = CLOCKSOURCE_MASK ( 32 ) ;
/*
2021-11-24 23:40:01 +01:00
* Calculate the optimal mult / shift values . The counter wraps
* around after ~ 178.9 seconds .
2015-04-14 12:15:47 +02:00
*/
2021-11-24 23:40:01 +01:00
clocks_calc_mult_shift ( & cc - > mult , & cc - > shift , 24000000 ,
NSEC_PER_SEC , 178 ) ;
2015-04-14 12:15:47 +02:00
nsec = 0 ; /* audio time is elapsed time since trigger */
timecounter_init ( tc , cc , nsec ) ;
if ( force ) {
/*
* force timecounter to use predefined value ,
* used for synchronized starts
*/
tc - > cycle_last = last ;
}
}
/**
* snd_hdac_stream_timecounter_init - initialize time counter
* @ azx_dev : HD - audio core stream ( master stream )
* @ streams : bit flags of streams to set up
*
* Initializes the time counter of streams marked by the bit flags ( each
* bit corresponds to the stream index ) .
* The trigger timestamp of PCM substream assigned to the given stream is
* updated accordingly , too .
*/
void snd_hdac_stream_timecounter_init ( struct hdac_stream * azx_dev ,
unsigned int streams )
{
struct hdac_bus * bus = azx_dev - > bus ;
struct snd_pcm_runtime * runtime = azx_dev - > substream - > runtime ;
struct hdac_stream * s ;
bool inited = false ;
2016-12-21 20:32:01 +01:00
u64 cycle_last = 0 ;
2015-04-14 12:15:47 +02:00
int i = 0 ;
list_for_each_entry ( s , & bus - > stream_list , list ) {
if ( streams & ( 1 < < i ) ) {
azx_timecounter_init ( s , inited , cycle_last ) ;
if ( ! inited ) {
inited = true ;
cycle_last = s - > tc . cycle_last ;
}
}
i + + ;
}
snd_pcm_gettime ( runtime , & runtime - > trigger_tstamp ) ;
runtime - > trigger_tstamp_latched = true ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_timecounter_init ) ;
/**
* snd_hdac_stream_sync_trigger - turn on / off stream sync register
* @ azx_dev : HD - audio core stream ( master stream )
2020-01-13 14:56:38 -06:00
* @ set : true = set , false = clear
2015-04-14 12:15:47 +02:00
* @ streams : bit flags of streams to sync
2020-01-13 14:56:38 -06:00
* @ reg : the stream sync register address
2015-04-14 12:15:47 +02:00
*/
void snd_hdac_stream_sync_trigger ( struct hdac_stream * azx_dev , bool set ,
unsigned int streams , unsigned int reg )
{
struct hdac_bus * bus = azx_dev - > bus ;
unsigned int val ;
if ( ! reg )
reg = AZX_REG_SSYNC ;
2017-03-29 08:27:15 +02:00
val = _snd_hdac_chip_readl ( bus , reg ) ;
2015-04-14 12:15:47 +02:00
if ( set )
val | = streams ;
else
val & = ~ streams ;
2017-03-29 08:27:15 +02:00
_snd_hdac_chip_writel ( bus , reg , val ) ;
2015-04-14 12:15:47 +02:00
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_sync_trigger ) ;
/**
2021-03-19 09:38:54 +08:00
* snd_hdac_stream_sync - sync with start / stop trigger operation
2015-04-14 12:15:47 +02:00
* @ azx_dev : HD - audio core stream ( master stream )
* @ start : true = start , false = stop
* @ streams : bit flags of streams to sync
*
* For @ start = true , wait until all FIFOs get ready .
* For @ start = false , wait until all RUN bits are cleared .
*/
void snd_hdac_stream_sync ( struct hdac_stream * azx_dev , bool start ,
unsigned int streams )
{
struct hdac_bus * bus = azx_dev - > bus ;
int i , nwait , timeout ;
struct hdac_stream * s ;
for ( timeout = 5000 ; timeout ; timeout - - ) {
nwait = 0 ;
i = 0 ;
list_for_each_entry ( s , & bus - > stream_list , list ) {
2020-01-28 10:45:08 +05:30
if ( ! ( streams & ( 1 < < i + + ) ) )
continue ;
if ( start ) {
/* check FIFO gets ready */
if ( ! ( snd_hdac_stream_readb ( s , SD_STS ) &
SD_STS_FIFO_READY ) )
nwait + + ;
} else {
/* check RUN bit is cleared */
if ( snd_hdac_stream_readb ( s , SD_CTL ) &
SD_CTL_DMA_START ) {
nwait + + ;
/*
* Perform stream reset if DMA RUN
* bit not cleared within given timeout
*/
if ( timeout = = 1 )
snd_hdac_stream_reset ( s ) ;
2015-04-14 12:15:47 +02:00
}
}
}
if ( ! nwait )
break ;
cpu_relax ( ) ;
}
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_sync ) ;
2015-04-14 12:53:28 +02:00
2022-10-19 11:21:15 -05:00
/**
* snd_hdac_stream_spbcap_enable - enable SPIB for a stream
* @ bus : HD - audio core bus
* @ enable : flag to enable / disable SPIB
* @ index : stream index for which SPIB need to be enabled
*/
void snd_hdac_stream_spbcap_enable ( struct hdac_bus * bus ,
bool enable , int index )
{
u32 mask = 0 ;
if ( ! bus - > spbcap ) {
dev_err ( bus - > dev , " Address of SPB capability is NULL \n " ) ;
return ;
}
mask | = ( 1 < < index ) ;
if ( enable )
snd_hdac_updatel ( bus - > spbcap , AZX_REG_SPB_SPBFCCTL , mask , mask ) ;
else
snd_hdac_updatel ( bus - > spbcap , AZX_REG_SPB_SPBFCCTL , mask , 0 ) ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_spbcap_enable ) ;
/**
* snd_hdac_stream_set_spib - sets the spib value of a stream
* @ bus : HD - audio core bus
* @ azx_dev : hdac_stream
* @ value : spib value to set
*/
int snd_hdac_stream_set_spib ( struct hdac_bus * bus ,
struct hdac_stream * azx_dev , u32 value )
{
if ( ! bus - > spbcap ) {
dev_err ( bus - > dev , " Address of SPB capability is NULL \n " ) ;
return - EINVAL ;
}
writel ( value , azx_dev - > spib_addr ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_set_spib ) ;
/**
* snd_hdac_stream_get_spbmaxfifo - gets the spib value of a stream
* @ bus : HD - audio core bus
* @ azx_dev : hdac_stream
*
* Return maxfifo for the stream
*/
int snd_hdac_stream_get_spbmaxfifo ( struct hdac_bus * bus ,
struct hdac_stream * azx_dev )
{
if ( ! bus - > spbcap ) {
dev_err ( bus - > dev , " Address of SPB capability is NULL \n " ) ;
return - EINVAL ;
}
return readl ( azx_dev - > fifo_addr ) ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_get_spbmaxfifo ) ;
/**
* snd_hdac_stream_drsm_enable - enable DMA resume for a stream
* @ bus : HD - audio core bus
* @ enable : flag to enable / disable DRSM
* @ index : stream index for which DRSM need to be enabled
*/
void snd_hdac_stream_drsm_enable ( struct hdac_bus * bus ,
bool enable , int index )
{
u32 mask = 0 ;
if ( ! bus - > drsmcap ) {
dev_err ( bus - > dev , " Address of DRSM capability is NULL \n " ) ;
return ;
}
mask | = ( 1 < < index ) ;
if ( enable )
snd_hdac_updatel ( bus - > drsmcap , AZX_REG_DRSM_CTL , mask , mask ) ;
else
snd_hdac_updatel ( bus - > drsmcap , AZX_REG_DRSM_CTL , mask , 0 ) ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_drsm_enable ) ;
2022-10-27 14:46:56 +02:00
/*
* snd_hdac_stream_wait_drsm - wait for HW to clear RSM for a stream
* @ azx_dev : HD - audio core stream to await RSM for
*
* Returns 0 on success and - ETIMEDOUT upon a timeout .
*/
int snd_hdac_stream_wait_drsm ( struct hdac_stream * azx_dev )
{
struct hdac_bus * bus = azx_dev - > bus ;
u32 mask , reg ;
int ret ;
mask = 1 < < azx_dev - > index ;
ret = read_poll_timeout ( snd_hdac_reg_readl , reg , ! ( reg & mask ) , 250 , 2000 , false , bus ,
bus - > drsmcap + AZX_REG_DRSM_CTL ) ;
if ( ret )
dev_dbg ( bus - > dev , " polling RSM 0x%08x failed: %d \n " , mask , ret ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_wait_drsm ) ;
2022-10-19 11:21:15 -05:00
/**
* snd_hdac_stream_set_dpibr - sets the dpibr value of a stream
* @ bus : HD - audio core bus
* @ azx_dev : hdac_stream
* @ value : dpib value to set
*/
int snd_hdac_stream_set_dpibr ( struct hdac_bus * bus ,
struct hdac_stream * azx_dev , u32 value )
{
if ( ! bus - > drsmcap ) {
dev_err ( bus - > dev , " Address of DRSM capability is NULL \n " ) ;
return - EINVAL ;
}
writel ( value , azx_dev - > dpibr_addr ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_set_dpibr ) ;
/**
* snd_hdac_stream_set_lpib - sets the lpib value of a stream
* @ azx_dev : hdac_stream
* @ value : lpib value to set
*/
int snd_hdac_stream_set_lpib ( struct hdac_stream * azx_dev , u32 value )
{
snd_hdac_stream_writel ( azx_dev , SD_LPIB , value ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_stream_set_lpib ) ;
2015-04-14 12:53:28 +02:00
# ifdef CONFIG_SND_HDA_DSP_LOADER
/**
* snd_hdac_dsp_prepare - prepare for DSP loading
* @ azx_dev : HD - audio core stream used for DSP loading
* @ format : HD - audio stream format
* @ byte_size : data chunk byte size
* @ bufp : allocated buffer
*
* Allocate the buffer for the given size and set up the given stream for
* DSP loading . Returns the stream tag ( > = 0 ) , or a negative error code .
*/
int snd_hdac_dsp_prepare ( struct hdac_stream * azx_dev , unsigned int format ,
unsigned int byte_size , struct snd_dma_buffer * bufp )
{
struct hdac_bus * bus = azx_dev - > bus ;
2018-07-25 23:24:03 +02:00
__le32 * bdl ;
2015-04-14 12:53:28 +02:00
int err ;
snd_hdac_dsp_lock ( azx_dev ) ;
spin_lock_irq ( & bus - > reg_lock ) ;
if ( azx_dev - > running | | azx_dev - > locked ) {
spin_unlock_irq ( & bus - > reg_lock ) ;
err = - EBUSY ;
goto unlock ;
}
azx_dev - > locked = true ;
spin_unlock_irq ( & bus - > reg_lock ) ;
2019-08-07 20:02:31 +02:00
err = snd_dma_alloc_pages ( SNDRV_DMA_TYPE_DEV_SG , bus - > dev ,
byte_size , bufp ) ;
2015-04-14 12:53:28 +02:00
if ( err < 0 )
goto err_alloc ;
2015-05-19 11:35:13 +02:00
azx_dev - > substream = NULL ;
2015-04-14 12:53:28 +02:00
azx_dev - > bufsize = byte_size ;
azx_dev - > period_bytes = byte_size ;
azx_dev - > format_val = format ;
snd_hdac_stream_reset ( azx_dev ) ;
/* reset BDL address */
snd_hdac_stream_writel ( azx_dev , SD_BDLPL , 0 ) ;
snd_hdac_stream_writel ( azx_dev , SD_BDLPU , 0 ) ;
azx_dev - > frags = 0 ;
2018-07-25 23:24:03 +02:00
bdl = ( __le32 * ) azx_dev - > bdl . area ;
2015-04-14 12:53:28 +02:00
err = setup_bdle ( bus , bufp , azx_dev , & bdl , 0 , byte_size , 0 ) ;
if ( err < 0 )
goto error ;
snd_hdac_stream_setup ( azx_dev ) ;
snd_hdac_dsp_unlock ( azx_dev ) ;
return azx_dev - > stream_tag ;
error :
2019-08-07 20:02:31 +02:00
snd_dma_free_pages ( bufp ) ;
2015-04-14 12:53:28 +02:00
err_alloc :
spin_lock_irq ( & bus - > reg_lock ) ;
azx_dev - > locked = false ;
spin_unlock_irq ( & bus - > reg_lock ) ;
unlock :
snd_hdac_dsp_unlock ( azx_dev ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_dsp_prepare ) ;
/**
* snd_hdac_dsp_trigger - start / stop DSP loading
* @ azx_dev : HD - audio core stream used for DSP loading
* @ start : trigger start or stop
*/
void snd_hdac_dsp_trigger ( struct hdac_stream * azx_dev , bool start )
{
if ( start )
2023-02-09 20:17:23 +08:00
snd_hdac_stream_start ( azx_dev ) ;
2015-04-14 12:53:28 +02:00
else
snd_hdac_stream_stop ( azx_dev ) ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_dsp_trigger ) ;
/**
* snd_hdac_dsp_cleanup - clean up the stream from DSP loading to normal
* @ azx_dev : HD - audio core stream used for DSP loading
* @ dmab : buffer used by DSP loading
*/
void snd_hdac_dsp_cleanup ( struct hdac_stream * azx_dev ,
struct snd_dma_buffer * dmab )
{
struct hdac_bus * bus = azx_dev - > bus ;
if ( ! dmab - > area | | ! azx_dev - > locked )
return ;
snd_hdac_dsp_lock ( azx_dev ) ;
/* reset BDL address */
snd_hdac_stream_writel ( azx_dev , SD_BDLPL , 0 ) ;
snd_hdac_stream_writel ( azx_dev , SD_BDLPU , 0 ) ;
snd_hdac_stream_writel ( azx_dev , SD_CTL , 0 ) ;
azx_dev - > bufsize = 0 ;
azx_dev - > period_bytes = 0 ;
azx_dev - > format_val = 0 ;
2019-08-07 20:02:31 +02:00
snd_dma_free_pages ( dmab ) ;
2015-04-14 12:53:28 +02:00
dmab - > area = NULL ;
spin_lock_irq ( & bus - > reg_lock ) ;
azx_dev - > locked = false ;
spin_unlock_irq ( & bus - > reg_lock ) ;
snd_hdac_dsp_unlock ( azx_dev ) ;
}
EXPORT_SYMBOL_GPL ( snd_hdac_dsp_cleanup ) ;
# endif /* CONFIG_SND_HDA_DSP_LOADER */