2020-05-01 09:58:50 -05:00
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2019-04-12 11:08:54 -05:00
//
// This file is provided under a dual BSD/GPLv2 license. When using or
// redistributing this file, you may do so under either license.
//
// Copyright(c) 2018 Intel Corporation. All rights reserved.
//
// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com>
// Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
// Rander Wang <rander.wang@intel.com>
// Keyon Jie <yang.jie@linux.intel.com>
//
/*
* Hardware interface for generic Intel audio DSP HDA IP
*/
# include <linux/pm_runtime.h>
# include <sound/hdaudio_ext.h>
# include <sound/hda_register.h>
# include <sound/sof.h>
2022-09-19 14:21:06 +02:00
# include <trace/events/sof_intel.h>
2019-04-12 11:08:54 -05:00
# include "../ops.h"
2019-12-04 15:15:51 -06:00
# include "../sof-audio.h"
2019-04-12 11:08:54 -05:00
# include "hda.h"
2020-08-26 11:45:27 -07:00
# define HDA_LTRP_GB_VALUE_US 95
2021-09-28 11:17:44 +03:00
static inline const char * hda_hstream_direction_str ( struct hdac_stream * hstream )
{
if ( hstream - > direction = = SNDRV_PCM_STREAM_PLAYBACK )
return " Playback " ;
else
return " Capture " ;
}
static char * hda_hstream_dbg_get_stream_info_str ( struct hdac_stream * hstream )
{
struct snd_soc_pcm_runtime * rtd ;
if ( hstream - > substream )
rtd = asoc_substream_to_rtd ( hstream - > substream ) ;
else if ( hstream - > cstream )
rtd = hstream - > cstream - > private_data ;
else
/* Non audio DMA user, like dma-trace */
return kasprintf ( GFP_KERNEL , " -- (%s, stream_tag: %u) " ,
hda_hstream_direction_str ( hstream ) ,
hstream - > stream_tag ) ;
return kasprintf ( GFP_KERNEL , " dai_link \" %s \" (%s, stream_tag: %u) " ,
rtd - > dai_link - > name , hda_hstream_direction_str ( hstream ) ,
hstream - > stream_tag ) ;
}
2019-04-12 11:08:54 -05:00
/*
* set up one of BDL entries for a stream
*/
static int hda_setup_bdle ( struct snd_sof_dev * sdev ,
struct snd_dma_buffer * dmab ,
2022-02-09 08:31:04 +02:00
struct hdac_stream * hstream ,
2019-04-12 11:08:54 -05:00
struct sof_intel_dsp_bdl * * bdlp ,
int offset , int size , int ioc )
{
struct hdac_bus * bus = sof_to_bus ( sdev ) ;
struct sof_intel_dsp_bdl * bdl = * bdlp ;
while ( size > 0 ) {
dma_addr_t addr ;
int chunk ;
2022-02-09 08:31:04 +02:00
if ( hstream - > frags > = HDA_DSP_MAX_BDL_ENTRIES ) {
2019-04-12 11:08:54 -05:00
dev_err ( sdev - > dev , " error: stream frags exceeded \n " ) ;
return - EINVAL ;
}
addr = snd_sgbuf_get_addr ( dmab , offset ) ;
/* program BDL addr */
bdl - > addr_l = cpu_to_le32 ( lower_32_bits ( addr ) ) ;
bdl - > addr_h = cpu_to_le32 ( upper_32_bits ( addr ) ) ;
/* program BDL size */
chunk = snd_sgbuf_get_chunk_size ( dmab , offset , size ) ;
/* one BDLE should not cross 4K boundary */
if ( bus - > align_bdle_4k ) {
u32 remain = 0x1000 - ( offset & 0xfff ) ;
if ( chunk > remain )
chunk = remain ;
}
bdl - > size = cpu_to_le32 ( chunk ) ;
/* only program IOC when the whole segment is processed */
size - = chunk ;
bdl - > ioc = ( size | | ! ioc ) ? 0 : cpu_to_le32 ( 0x01 ) ;
bdl + + ;
2022-02-09 08:31:04 +02:00
hstream - > frags + + ;
2019-04-12 11:08:54 -05:00
offset + = chunk ;
}
* bdlp = bdl ;
return offset ;
}
/*
* set up Buffer Descriptor List ( BDL ) for host memory transfer
* BDL describes the location of the individual buffers and is little endian .
*/
int hda_dsp_stream_setup_bdl ( struct snd_sof_dev * sdev ,
struct snd_dma_buffer * dmab ,
2022-02-09 08:31:04 +02:00
struct hdac_stream * hstream )
2019-04-12 11:08:54 -05:00
{
struct sof_intel_hda_dev * hda = sdev - > pdata - > hw_pdata ;
struct sof_intel_dsp_bdl * bdl ;
int i , offset , period_bytes , periods ;
int remain , ioc ;
2022-02-09 08:31:04 +02:00
period_bytes = hstream - > period_bytes ;
2022-06-16 16:53:42 -05:00
dev_dbg ( sdev - > dev , " period_bytes:0x%x \n " , period_bytes ) ;
2019-04-12 11:08:54 -05:00
if ( ! period_bytes )
2022-02-09 08:31:04 +02:00
period_bytes = hstream - > bufsize ;
2019-04-12 11:08:54 -05:00
2022-02-09 08:31:04 +02:00
periods = hstream - > bufsize / period_bytes ;
2019-04-12 11:08:54 -05:00
2022-06-16 16:53:42 -05:00
dev_dbg ( sdev - > dev , " periods:%d \n " , periods ) ;
2019-04-12 11:08:54 -05:00
2022-02-09 08:31:04 +02:00
remain = hstream - > bufsize % period_bytes ;
2019-04-12 11:08:54 -05:00
if ( remain )
periods + + ;
/* program the initial BDL entries */
2022-02-09 08:31:04 +02:00
bdl = ( struct sof_intel_dsp_bdl * ) hstream - > bdl . area ;
2019-04-12 11:08:54 -05:00
offset = 0 ;
2022-02-09 08:31:04 +02:00
hstream - > frags = 0 ;
2019-04-12 11:08:54 -05:00
/*
* set IOC if don ' t use position IPC
* and period_wakeup needed .
*/
ioc = hda - > no_ipc_position ?
2022-02-09 08:31:04 +02:00
! hstream - > no_period_wakeup : 0 ;
2019-04-12 11:08:54 -05:00
for ( i = 0 ; i < periods ; i + + ) {
if ( i = = ( periods - 1 ) & & remain )
/* set the last small entry */
offset = hda_setup_bdle ( sdev , dmab ,
2022-02-09 08:31:04 +02:00
hstream , & bdl , offset ,
2019-04-12 11:08:54 -05:00
remain , 0 ) ;
else
offset = hda_setup_bdle ( sdev , dmab ,
2022-02-09 08:31:04 +02:00
hstream , & bdl , offset ,
2019-04-12 11:08:54 -05:00
period_bytes , ioc ) ;
}
return offset ;
}
int hda_dsp_stream_spib_config ( struct snd_sof_dev * sdev ,
2022-02-09 08:31:04 +02:00
struct hdac_ext_stream * hext_stream ,
2019-04-12 11:08:54 -05:00
int enable , u32 size )
{
2022-02-09 08:31:04 +02:00
struct hdac_stream * hstream = & hext_stream - > hstream ;
2019-04-12 11:08:54 -05:00
u32 mask ;
if ( ! sdev - > bar [ HDA_DSP_SPIB_BAR ] ) {
dev_err ( sdev - > dev , " error: address of spib capability is NULL \n " ) ;
return - EINVAL ;
}
mask = ( 1 < < hstream - > index ) ;
/* enable/disable SPIB for the stream */
snd_sof_dsp_update_bits ( sdev , HDA_DSP_SPIB_BAR ,
SOF_HDA_ADSP_REG_CL_SPBFIFO_SPBFCCTL , mask ,
enable < < hstream - > index ) ;
/* set the SPIB value */
2022-10-19 11:21:15 -05:00
sof_io_write ( sdev , hstream - > spib_addr , size ) ;
2019-04-12 11:08:54 -05:00
return 0 ;
}
/* get next unused stream */
struct hdac_ext_stream *
2021-01-26 18:07:36 -08:00
hda_dsp_stream_get ( struct snd_sof_dev * sdev , int direction , u32 flags )
2019-04-12 11:08:54 -05:00
{
2023-02-20 09:58:03 +02:00
const struct sof_intel_dsp_desc * chip_info = get_chip_info ( sdev - > pdata ) ;
2023-02-20 09:58:02 +02:00
struct sof_intel_hda_dev * hda = sdev - > pdata - > hw_pdata ;
2019-04-12 11:08:54 -05:00
struct hdac_bus * bus = sof_to_bus ( sdev ) ;
2019-06-12 12:23:37 -05:00
struct sof_intel_hda_stream * hda_stream ;
2022-02-09 08:31:04 +02:00
struct hdac_ext_stream * hext_stream = NULL ;
2019-04-12 11:08:54 -05:00
struct hdac_stream * s ;
spin_lock_irq ( & bus - > reg_lock ) ;
/* get an unused stream */
list_for_each_entry ( s , & bus - > stream_list , list ) {
if ( s - > direction = = direction & & ! s - > opened ) {
2022-02-09 08:31:04 +02:00
hext_stream = stream_to_hdac_ext_stream ( s ) ;
hda_stream = container_of ( hext_stream ,
2019-06-12 12:23:37 -05:00
struct sof_intel_hda_stream ,
2022-02-09 08:31:04 +02:00
hext_stream ) ;
2019-06-12 12:23:37 -05:00
/* check if the host DMA channel is reserved */
if ( hda_stream - > host_reserved )
continue ;
s - > opened = true ;
2019-04-12 11:08:54 -05:00
break ;
}
}
spin_unlock_irq ( & bus - > reg_lock ) ;
/* stream found ? */
2022-02-09 08:31:04 +02:00
if ( ! hext_stream ) {
2019-04-12 11:08:54 -05:00
dev_err ( sdev - > dev , " error: no free %s streams \n " ,
direction = = SNDRV_PCM_STREAM_PLAYBACK ?
" playback " : " capture " ) ;
2022-02-09 08:31:04 +02:00
return hext_stream ;
2021-01-26 18:07:36 -08:00
}
hda_stream - > flags = flags ;
2019-04-12 11:08:54 -05:00
2019-09-27 15:05:38 -05:00
/*
2021-01-26 18:07:36 -08:00
* Prevent DMI Link L1 entry for streams that don ' t support it .
2019-09-27 15:05:38 -05:00
* Workaround to address a known issue with host DMA that results
2023-02-20 09:58:03 +02:00
* in xruns during pause / release in capture scenarios . This is not needed for the ACE IP .
2019-09-27 15:05:38 -05:00
*/
2023-02-20 09:58:03 +02:00
if ( chip_info - > hw_ip_version < SOF_INTEL_ACE_1_0 & &
! ( flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE ) ) {
2021-08-12 18:19:39 -05:00
snd_sof_dsp_update_bits ( sdev , HDA_DSP_HDA_BAR ,
HDA_VS_INTEL_EM2 ,
HDA_VS_INTEL_EM2_L1SEN , 0 ) ;
2023-02-20 09:58:02 +02:00
hda - > l1_disabled = true ;
}
2019-09-27 15:05:38 -05:00
2022-02-09 08:31:04 +02:00
return hext_stream ;
2019-04-12 11:08:54 -05:00
}
/* free a stream */
int hda_dsp_stream_put ( struct snd_sof_dev * sdev , int direction , int stream_tag )
{
2023-02-20 09:58:03 +02:00
const struct sof_intel_dsp_desc * chip_info = get_chip_info ( sdev - > pdata ) ;
2023-02-20 09:58:02 +02:00
struct sof_intel_hda_dev * hda = sdev - > pdata - > hw_pdata ;
2019-04-12 11:08:54 -05:00
struct hdac_bus * bus = sof_to_bus ( sdev ) ;
2021-01-26 18:07:36 -08:00
struct sof_intel_hda_stream * hda_stream ;
2022-02-09 08:31:04 +02:00
struct hdac_ext_stream * hext_stream ;
2019-04-12 11:08:54 -05:00
struct hdac_stream * s ;
2021-01-26 18:07:36 -08:00
bool dmi_l1_enable = true ;
2019-09-27 15:05:38 -05:00
bool found = false ;
2019-04-12 11:08:54 -05:00
spin_lock_irq ( & bus - > reg_lock ) ;
2019-09-27 15:05:38 -05:00
/*
2021-01-26 18:07:36 -08:00
* close stream matching the stream tag and check if there are any open streams
* that are DMI L1 incompatible .
2019-09-27 15:05:38 -05:00
*/
2019-04-12 11:08:54 -05:00
list_for_each_entry ( s , & bus - > stream_list , list ) {
2022-02-09 08:31:04 +02:00
hext_stream = stream_to_hdac_ext_stream ( s ) ;
hda_stream = container_of ( hext_stream , struct sof_intel_hda_stream , hext_stream ) ;
2021-01-26 18:07:36 -08:00
2019-09-27 15:05:38 -05:00
if ( ! s - > opened )
continue ;
if ( s - > direction = = direction & & s - > stream_tag = = stream_tag ) {
2019-04-12 11:08:54 -05:00
s - > opened = false ;
2019-09-27 15:05:38 -05:00
found = true ;
2021-01-26 18:07:36 -08:00
} else if ( ! ( hda_stream - > flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE ) ) {
dmi_l1_enable = false ;
2019-04-12 11:08:54 -05:00
}
}
spin_unlock_irq ( & bus - > reg_lock ) ;
2021-01-26 18:07:36 -08:00
/* Enable DMI L1 if permitted */
2023-02-20 09:58:03 +02:00
if ( chip_info - > hw_ip_version < SOF_INTEL_ACE_1_0 & & dmi_l1_enable ) {
2021-01-26 18:07:36 -08:00
snd_sof_dsp_update_bits ( sdev , HDA_DSP_HDA_BAR , HDA_VS_INTEL_EM2 ,
HDA_VS_INTEL_EM2_L1SEN , HDA_VS_INTEL_EM2_L1SEN ) ;
2023-02-20 09:58:02 +02:00
hda - > l1_disabled = false ;
}
2019-09-27 15:05:38 -05:00
if ( ! found ) {
2022-06-16 16:53:40 -05:00
dev_err ( sdev - > dev , " %s: stream_tag %d not opened! \n " ,
2021-09-28 13:26:35 +03:00
__func__ , stream_tag ) ;
2019-09-27 15:05:38 -05:00
return - ENODEV ;
}
return 0 ;
2019-04-12 11:08:54 -05:00
}
2021-11-25 12:15:12 +02:00
static int hda_dsp_stream_reset ( struct snd_sof_dev * sdev , struct hdac_stream * hstream )
{
int sd_offset = SOF_STREAM_SD_OFFSET ( hstream ) ;
int timeout = HDA_DSP_STREAM_RESET_TIMEOUT ;
u32 val ;
/* enter stream reset */
snd_sof_dsp_update_bits ( sdev , HDA_DSP_HDA_BAR , sd_offset , SOF_STREAM_SD_OFFSET_CRST ,
SOF_STREAM_SD_OFFSET_CRST ) ;
do {
val = snd_sof_dsp_read ( sdev , HDA_DSP_HDA_BAR , sd_offset ) ;
if ( val & SOF_STREAM_SD_OFFSET_CRST )
break ;
} while ( - - timeout ) ;
if ( timeout = = 0 ) {
dev_err ( sdev - > dev , " timeout waiting for stream reset \n " ) ;
return - ETIMEDOUT ;
}
timeout = HDA_DSP_STREAM_RESET_TIMEOUT ;
/* exit stream reset and wait to read a zero before reading any other register */
snd_sof_dsp_update_bits ( sdev , HDA_DSP_HDA_BAR , sd_offset , SOF_STREAM_SD_OFFSET_CRST , 0x0 ) ;
/* wait for hardware to report that stream is out of reset */
udelay ( 3 ) ;
do {
val = snd_sof_dsp_read ( sdev , HDA_DSP_HDA_BAR , sd_offset ) ;
if ( ( val & SOF_STREAM_SD_OFFSET_CRST ) = = 0 )
break ;
} while ( - - timeout ) ;
if ( timeout = = 0 ) {
dev_err ( sdev - > dev , " timeout waiting for stream to exit reset \n " ) ;
return - ETIMEDOUT ;
}
return 0 ;
}
2019-04-12 11:08:54 -05:00
int hda_dsp_stream_trigger ( struct snd_sof_dev * sdev ,
2022-02-09 08:31:04 +02:00
struct hdac_ext_stream * hext_stream , int cmd )
2019-04-12 11:08:54 -05:00
{
2022-02-09 08:31:04 +02:00
struct hdac_stream * hstream = & hext_stream - > hstream ;
2019-04-12 11:08:54 -05:00
int sd_offset = SOF_STREAM_SD_OFFSET ( hstream ) ;
2019-06-12 12:23:46 -05:00
u32 dma_start = SOF_HDA_SD_CTL_DMA_START ;
2021-09-28 11:17:44 +03:00
int ret = 0 ;
2019-06-12 12:23:46 -05:00
u32 run ;
2019-04-12 11:08:54 -05:00
/* cmd must be for audio stream */
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
case SNDRV_PCM_TRIGGER_START :
snd_sof_dsp_update_bits ( sdev , HDA_DSP_HDA_BAR , SOF_HDA_INTCTL ,
1 < < hstream - > index ,
1 < < hstream - > index ) ;
snd_sof_dsp_update_bits ( sdev , HDA_DSP_HDA_BAR ,
sd_offset ,
SOF_HDA_SD_CTL_DMA_START |
SOF_HDA_CL_DMA_SD_INT_MASK ,
SOF_HDA_SD_CTL_DMA_START |
SOF_HDA_CL_DMA_SD_INT_MASK ) ;
2019-06-12 12:23:46 -05:00
ret = snd_sof_dsp_read_poll_timeout ( sdev ,
HDA_DSP_HDA_BAR ,
sd_offset , run ,
( ( run & dma_start ) = = dma_start ) ,
HDA_DSP_REG_POLL_INTERVAL_US ,
HDA_DSP_STREAM_RUN_TIMEOUT ) ;
2021-09-28 11:17:44 +03:00
if ( ret > = 0 )
hstream - > running = true ;
2019-06-12 12:23:46 -05:00
2019-04-12 11:08:54 -05:00
break ;
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
case SNDRV_PCM_TRIGGER_STOP :
snd_sof_dsp_update_bits ( sdev , HDA_DSP_HDA_BAR ,
sd_offset ,
SOF_HDA_SD_CTL_DMA_START |
SOF_HDA_CL_DMA_SD_INT_MASK , 0x0 ) ;
2019-06-12 12:23:46 -05:00
ret = snd_sof_dsp_read_poll_timeout ( sdev , HDA_DSP_HDA_BAR ,
sd_offset , run ,
! ( run & dma_start ) ,
HDA_DSP_REG_POLL_INTERVAL_US ,
HDA_DSP_STREAM_RUN_TIMEOUT ) ;
2021-09-28 11:17:44 +03:00
if ( ret > = 0 ) {
snd_sof_dsp_write ( sdev , HDA_DSP_HDA_BAR ,
2022-10-24 11:53:07 -05:00
sd_offset + SOF_HDA_ADSP_REG_SD_STS ,
2021-09-28 11:17:44 +03:00
SOF_HDA_CL_DMA_SD_INT_MASK ) ;
2019-04-12 11:08:54 -05:00
2021-09-28 11:17:44 +03:00
hstream - > running = false ;
snd_sof_dsp_update_bits ( sdev , HDA_DSP_HDA_BAR ,
SOF_HDA_INTCTL ,
1 < < hstream - > index , 0x0 ) ;
}
2019-04-12 11:08:54 -05:00
break ;
default :
dev_err ( sdev - > dev , " error: unknown command: %d \n " , cmd ) ;
return - EINVAL ;
}
2021-09-28 11:17:44 +03:00
if ( ret < 0 ) {
char * stream_name = hda_hstream_dbg_get_stream_info_str ( hstream ) ;
dev_err ( sdev - > dev ,
" %s: cmd %d on %s: timeout on STREAM_SD_OFFSET read \n " ,
__func__ , cmd , stream_name ? stream_name : " unknown stream " ) ;
kfree ( stream_name ) ;
}
return ret ;
2019-04-12 11:08:54 -05:00
}
2020-08-26 11:45:27 -07:00
/* minimal recommended programming for ICCMAX stream */
2022-02-09 08:31:04 +02:00
int hda_dsp_iccmax_stream_hw_params ( struct snd_sof_dev * sdev , struct hdac_ext_stream * hext_stream ,
2020-08-26 11:45:27 -07:00
struct snd_dma_buffer * dmab ,
struct snd_pcm_hw_params * params )
{
2022-02-09 08:31:04 +02:00
struct hdac_stream * hstream = & hext_stream - > hstream ;
2020-08-26 11:45:27 -07:00
int sd_offset = SOF_STREAM_SD_OFFSET ( hstream ) ;
int ret ;
u32 mask = 0x1 < < hstream - > index ;
2022-02-09 08:31:04 +02:00
if ( ! hext_stream ) {
2020-08-26 11:45:27 -07:00
dev_err ( sdev - > dev , " error: no stream available \n " ) ;
return - ENODEV ;
}
2022-07-15 09:52:16 -05:00
if ( ! dmab ) {
dev_err ( sdev - > dev , " error: no dma buffer allocated! \n " ) ;
return - ENODEV ;
}
2020-08-26 11:45:27 -07:00
if ( hstream - > posbuf )
* hstream - > posbuf = 0 ;
/* reset BDL address */
snd_sof_dsp_write ( sdev , HDA_DSP_HDA_BAR ,
2022-10-24 11:53:07 -05:00
sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL ,
2020-08-26 11:45:27 -07:00
0x0 ) ;
snd_sof_dsp_write ( sdev , HDA_DSP_HDA_BAR ,
2022-10-24 11:53:07 -05:00
sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU ,
2020-08-26 11:45:27 -07:00
0x0 ) ;
hstream - > frags = 0 ;
ret = hda_dsp_stream_setup_bdl ( sdev , dmab , hstream ) ;
if ( ret < 0 ) {
dev_err ( sdev - > dev , " error: set up of BDL failed \n " ) ;
return ret ;
}
/* program BDL address */
snd_sof_dsp_write ( sdev , HDA_DSP_HDA_BAR ,
2022-10-24 11:53:07 -05:00
sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL ,
2020-08-26 11:45:27 -07:00
( u32 ) hstream - > bdl . addr ) ;
snd_sof_dsp_write ( sdev , HDA_DSP_HDA_BAR ,
2022-10-24 11:53:07 -05:00
sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU ,
2020-08-26 11:45:27 -07:00
upper_32_bits ( hstream - > bdl . addr ) ) ;
/* program cyclic buffer length */
snd_sof_dsp_write ( sdev , HDA_DSP_HDA_BAR ,
2022-10-24 11:53:07 -05:00
sd_offset + SOF_HDA_ADSP_REG_SD_CBL ,
2020-08-26 11:45:27 -07:00
hstream - > bufsize ) ;
/* program last valid index */
snd_sof_dsp_update_bits ( sdev , HDA_DSP_HDA_BAR ,
2022-10-24 11:53:07 -05:00
sd_offset + SOF_HDA_ADSP_REG_SD_LVI ,
2020-08-26 11:45:27 -07:00
0xffff , ( hstream - > frags - 1 ) ) ;
/* decouple host and link DMA, enable DSP features */
snd_sof_dsp_update_bits ( sdev , HDA_DSP_PP_BAR , SOF_HDA_REG_PP_PPCTL ,
mask , mask ) ;
/* Follow HW recommendation to set the guardband value to 95us during FW boot */
2022-10-31 15:53:40 -04:00
snd_sof_dsp_update8 ( sdev , HDA_DSP_HDA_BAR , HDA_VS_INTEL_LTRP ,
2022-10-24 11:53:09 -05:00
HDA_VS_INTEL_LTRP_GB_MASK , HDA_LTRP_GB_VALUE_US ) ;
2020-08-26 11:45:27 -07:00
/* start DMA */
snd_sof_dsp_update_bits ( sdev , HDA_DSP_HDA_BAR , sd_offset ,
SOF_HDA_SD_CTL_DMA_START , SOF_HDA_SD_CTL_DMA_START ) ;
return 0 ;
}
2019-04-12 11:08:54 -05:00
/*
* prepare for common hdac registers settings , for both code loader
* and normal stream .
*/
int hda_dsp_stream_hw_params ( struct snd_sof_dev * sdev ,
2022-02-09 08:31:04 +02:00
struct hdac_ext_stream * hext_stream ,
2019-04-12 11:08:54 -05:00
struct snd_dma_buffer * dmab ,
struct snd_pcm_hw_params * params )
{
2021-12-07 13:39:42 -06:00
const struct sof_intel_dsp_desc * chip = get_chip_info ( sdev - > pdata ) ;
2019-04-12 11:08:54 -05:00
struct hdac_bus * bus = sof_to_bus ( sdev ) ;
2023-04-04 12:21:04 +03:00
struct hdac_stream * hstream ;
int sd_offset , ret ;
2019-06-12 12:23:47 -05:00
u32 dma_start = SOF_HDA_SD_CTL_DMA_START ;
2021-11-25 12:15:12 +02:00
u32 mask ;
2019-06-12 12:23:47 -05:00
u32 run ;
2019-04-12 11:08:54 -05:00
2022-02-09 08:31:04 +02:00
if ( ! hext_stream ) {
2019-04-12 11:08:54 -05:00
dev_err ( sdev - > dev , " error: no stream available \n " ) ;
return - ENODEV ;
}
if ( ! dmab ) {
dev_err ( sdev - > dev , " error: no dma buffer allocated! \n " ) ;
return - ENODEV ;
}
2023-04-04 12:21:04 +03:00
hstream = & hext_stream - > hstream ;
sd_offset = SOF_STREAM_SD_OFFSET ( hstream ) ;
2022-07-15 09:52:16 -05:00
/* decouple host and link DMA */
mask = 0x1 < < hstream - > index ;
snd_sof_dsp_update_bits ( sdev , HDA_DSP_PP_BAR , SOF_HDA_REG_PP_PPCTL ,
mask , mask ) ;
2019-04-12 11:08:54 -05:00
/* clear stream status */
snd_sof_dsp_update_bits ( sdev , HDA_DSP_HDA_BAR , sd_offset ,
SOF_HDA_CL_DMA_SD_INT_MASK |
SOF_HDA_SD_CTL_DMA_START , 0 ) ;
2019-06-12 12:23:47 -05:00
ret = snd_sof_dsp_read_poll_timeout ( sdev , HDA_DSP_HDA_BAR ,
sd_offset , run ,
! ( run & dma_start ) ,
HDA_DSP_REG_POLL_INTERVAL_US ,
HDA_DSP_STREAM_RUN_TIMEOUT ) ;
2019-10-22 14:28:44 -05:00
if ( ret < 0 ) {
2021-09-28 11:17:44 +03:00
char * stream_name = hda_hstream_dbg_get_stream_info_str ( hstream ) ;
2019-10-22 14:28:44 -05:00
dev_err ( sdev - > dev ,
2021-09-28 11:17:44 +03:00
" %s: on %s: timeout on STREAM_SD_OFFSET read1 \n " ,
__func__ , stream_name ? stream_name : " unknown stream " ) ;
kfree ( stream_name ) ;
2019-06-12 12:23:47 -05:00
return ret ;
2019-10-22 14:28:44 -05:00
}
2019-06-12 12:23:47 -05:00
2019-04-12 11:08:54 -05:00
snd_sof_dsp_update_bits ( sdev , HDA_DSP_HDA_BAR ,
2022-10-24 11:53:07 -05:00
sd_offset + SOF_HDA_ADSP_REG_SD_STS ,
2019-04-12 11:08:54 -05:00
SOF_HDA_CL_DMA_SD_INT_MASK ,
SOF_HDA_CL_DMA_SD_INT_MASK ) ;
/* stream reset */
2021-11-25 12:15:12 +02:00
ret = hda_dsp_stream_reset ( sdev , hstream ) ;
if ( ret < 0 )
return ret ;
2019-04-12 11:08:54 -05:00
if ( hstream - > posbuf )
* hstream - > posbuf = 0 ;
/* reset BDL address */
snd_sof_dsp_write ( sdev , HDA_DSP_HDA_BAR ,
2022-10-24 11:53:07 -05:00
sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL ,
2019-04-12 11:08:54 -05:00
0x0 ) ;
snd_sof_dsp_write ( sdev , HDA_DSP_HDA_BAR ,
2022-10-24 11:53:07 -05:00
sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU ,
2019-04-12 11:08:54 -05:00
0x0 ) ;
/* clear stream status */
snd_sof_dsp_update_bits ( sdev , HDA_DSP_HDA_BAR , sd_offset ,
SOF_HDA_CL_DMA_SD_INT_MASK |
SOF_HDA_SD_CTL_DMA_START , 0 ) ;
2019-06-12 12:23:47 -05:00
ret = snd_sof_dsp_read_poll_timeout ( sdev , HDA_DSP_HDA_BAR ,
sd_offset , run ,
! ( run & dma_start ) ,
HDA_DSP_REG_POLL_INTERVAL_US ,
HDA_DSP_STREAM_RUN_TIMEOUT ) ;
2019-10-22 14:28:44 -05:00
if ( ret < 0 ) {
2021-09-28 11:17:44 +03:00
char * stream_name = hda_hstream_dbg_get_stream_info_str ( hstream ) ;
2019-10-22 14:28:44 -05:00
dev_err ( sdev - > dev ,
2021-09-28 11:17:44 +03:00
" %s: on %s: timeout on STREAM_SD_OFFSET read1 \n " ,
__func__ , stream_name ? stream_name : " unknown stream " ) ;
kfree ( stream_name ) ;
2019-06-12 12:23:47 -05:00
return ret ;
2019-10-22 14:28:44 -05:00
}
2019-06-12 12:23:47 -05:00
2019-04-12 11:08:54 -05:00
snd_sof_dsp_update_bits ( sdev , HDA_DSP_HDA_BAR ,
2022-10-24 11:53:07 -05:00
sd_offset + SOF_HDA_ADSP_REG_SD_STS ,
2019-04-12 11:08:54 -05:00
SOF_HDA_CL_DMA_SD_INT_MASK ,
SOF_HDA_CL_DMA_SD_INT_MASK ) ;
hstream - > frags = 0 ;
ret = hda_dsp_stream_setup_bdl ( sdev , dmab , hstream ) ;
if ( ret < 0 ) {
dev_err ( sdev - > dev , " error: set up of BDL failed \n " ) ;
return ret ;
}
/* program stream tag to set up stream descriptor for DMA */
snd_sof_dsp_update_bits ( sdev , HDA_DSP_HDA_BAR , sd_offset ,
SOF_HDA_CL_SD_CTL_STREAM_TAG_MASK ,
hstream - > stream_tag < <
SOF_HDA_CL_SD_CTL_STREAM_TAG_SHIFT ) ;
/* program cyclic buffer length */
snd_sof_dsp_write ( sdev , HDA_DSP_HDA_BAR ,
2022-10-24 11:53:07 -05:00
sd_offset + SOF_HDA_ADSP_REG_SD_CBL ,
2019-04-12 11:08:54 -05:00
hstream - > bufsize ) ;
/*
* Recommended hardware programming sequence for HDAudio DMA format
2021-12-07 13:39:42 -06:00
* on earlier platforms - this is not needed on newer platforms
2019-04-12 11:08:54 -05:00
*
* 1. Put DMA into coupled mode by clearing PPCTL . PROCEN bit
* for corresponding stream index before the time of writing
* format to SDxFMT register .
* 2. Write SDxFMT
* 3. Set PPCTL . PROCEN bit for corresponding stream index to
* enable decoupled mode
*/
2021-12-07 13:39:42 -06:00
if ( chip - > quirks & SOF_INTEL_PROCEN_FMT_QUIRK ) {
/* couple host and link DMA, disable DSP features */
snd_sof_dsp_update_bits ( sdev , HDA_DSP_PP_BAR , SOF_HDA_REG_PP_PPCTL ,
mask , 0 ) ;
}
2019-04-12 11:08:54 -05:00
/* program stream format */
snd_sof_dsp_update_bits ( sdev , HDA_DSP_HDA_BAR ,
sd_offset +
2022-10-24 11:53:07 -05:00
SOF_HDA_ADSP_REG_SD_FORMAT ,
2019-04-12 11:08:54 -05:00
0xffff , hstream - > format_val ) ;
2021-12-07 13:39:42 -06:00
if ( chip - > quirks & SOF_INTEL_PROCEN_FMT_QUIRK ) {
/* decouple host and link DMA, enable DSP features */
snd_sof_dsp_update_bits ( sdev , HDA_DSP_PP_BAR , SOF_HDA_REG_PP_PPCTL ,
mask , mask ) ;
}
2019-04-12 11:08:54 -05:00
/* program last valid index */
snd_sof_dsp_update_bits ( sdev , HDA_DSP_HDA_BAR ,
2022-10-24 11:53:07 -05:00
sd_offset + SOF_HDA_ADSP_REG_SD_LVI ,
2019-04-12 11:08:54 -05:00
0xffff , ( hstream - > frags - 1 ) ) ;
/* program BDL address */
snd_sof_dsp_write ( sdev , HDA_DSP_HDA_BAR ,
2022-10-24 11:53:07 -05:00
sd_offset + SOF_HDA_ADSP_REG_SD_BDLPL ,
2019-04-12 11:08:54 -05:00
( u32 ) hstream - > bdl . addr ) ;
snd_sof_dsp_write ( sdev , HDA_DSP_HDA_BAR ,
2022-10-24 11:53:07 -05:00
sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU ,
2019-04-12 11:08:54 -05:00
upper_32_bits ( hstream - > bdl . addr ) ) ;
2021-12-07 13:39:44 -06:00
/* enable position buffer, if needed */
if ( bus - > use_posbuf & & bus - > posbuf . addr & &
! ( snd_sof_dsp_read ( sdev , HDA_DSP_HDA_BAR , SOF_HDA_ADSP_DPLBASE )
& SOF_HDA_ADSP_DPLBASE_ENABLE ) ) {
2019-04-12 11:08:54 -05:00
snd_sof_dsp_write ( sdev , HDA_DSP_HDA_BAR , SOF_HDA_ADSP_DPUBASE ,
upper_32_bits ( bus - > posbuf . addr ) ) ;
snd_sof_dsp_write ( sdev , HDA_DSP_HDA_BAR , SOF_HDA_ADSP_DPLBASE ,
( u32 ) bus - > posbuf . addr |
SOF_HDA_ADSP_DPLBASE_ENABLE ) ;
}
/* set interrupt enable bits */
snd_sof_dsp_update_bits ( sdev , HDA_DSP_HDA_BAR , sd_offset ,
SOF_HDA_CL_DMA_SD_INT_MASK ,
SOF_HDA_CL_DMA_SD_INT_MASK ) ;
/* read FIFO size */
if ( hstream - > direction = = SNDRV_PCM_STREAM_PLAYBACK ) {
hstream - > fifo_size =
snd_sof_dsp_read ( sdev , HDA_DSP_HDA_BAR ,
sd_offset +
2022-10-24 11:53:07 -05:00
SOF_HDA_ADSP_REG_SD_FIFOSIZE ) ;
2019-04-12 11:08:54 -05:00
hstream - > fifo_size & = 0xffff ;
hstream - > fifo_size + = 1 ;
} else {
hstream - > fifo_size = 0 ;
}
return ret ;
}
2019-06-12 12:23:39 -05:00
int hda_dsp_stream_hw_free ( struct snd_sof_dev * sdev ,
struct snd_pcm_substream * substream )
{
2022-02-09 08:31:04 +02:00
struct hdac_stream * hstream = substream - > runtime - > private_data ;
struct hdac_ext_stream * hext_stream = container_of ( hstream ,
struct hdac_ext_stream ,
hstream ) ;
2019-06-12 12:23:39 -05:00
struct hdac_bus * bus = sof_to_bus ( sdev ) ;
2022-02-09 08:31:04 +02:00
u32 mask = 0x1 < < hstream - > index ;
2021-11-25 12:15:13 +02:00
int ret ;
2022-02-09 08:31:04 +02:00
ret = hda_dsp_stream_reset ( sdev , hstream ) ;
2021-11-25 12:15:13 +02:00
if ( ret < 0 )
return ret ;
2019-06-12 12:23:39 -05:00
2019-06-12 12:23:41 -05:00
spin_lock_irq ( & bus - > reg_lock ) ;
2019-06-12 12:23:39 -05:00
/* couple host and link DMA if link DMA channel is idle */
2022-02-09 08:31:04 +02:00
if ( ! hext_stream - > link_locked )
2019-06-12 12:23:39 -05:00
snd_sof_dsp_update_bits ( sdev , HDA_DSP_PP_BAR ,
SOF_HDA_REG_PP_PPCTL , mask , 0 ) ;
2019-06-12 12:23:41 -05:00
spin_unlock_irq ( & bus - > reg_lock ) ;
2019-06-12 12:23:39 -05:00
2022-02-09 08:31:04 +02:00
hda_dsp_stream_spib_config ( sdev , hext_stream , HDA_DSP_SPIB_DISABLE , 0 ) ;
2021-11-19 17:08:52 -06:00
2022-02-09 08:31:04 +02:00
hstream - > substream = NULL ;
2020-03-12 13:00:58 +01:00
2019-06-12 12:23:39 -05:00
return 0 ;
}
2019-12-04 15:28:59 -06:00
bool hda_dsp_check_stream_irq ( struct snd_sof_dev * sdev )
2019-04-12 11:08:54 -05:00
{
2019-12-04 15:28:59 -06:00
struct hdac_bus * bus = sof_to_bus ( sdev ) ;
bool ret = false ;
2019-04-12 11:08:54 -05:00
u32 status ;
2019-12-04 15:28:59 -06:00
/* The function can be called at irq thread, so use spin_lock_irq */
spin_lock_irq ( & bus - > reg_lock ) ;
2019-04-12 11:08:54 -05:00
2022-10-24 11:53:06 -05:00
status = snd_sof_dsp_read ( sdev , HDA_DSP_HDA_BAR , SOF_HDA_INTSTS ) ;
2022-09-19 14:21:06 +02:00
trace_sof_intel_hda_dsp_check_stream_irq ( sdev , status ) ;
2019-04-12 11:08:54 -05:00
2019-12-04 15:28:59 -06:00
/* if Register inaccessible, ignore it.*/
if ( status ! = 0xffffffff )
ret = true ;
2019-04-12 11:08:54 -05:00
2019-12-04 15:28:59 -06:00
spin_unlock_irq ( & bus - > reg_lock ) ;
2019-04-12 11:08:54 -05:00
2019-06-12 12:23:40 -05:00
return ret ;
2019-04-12 11:08:54 -05:00
}
2020-02-18 15:39:18 +01:00
static void
2022-06-16 15:19:53 -05:00
hda_dsp_compr_bytes_transferred ( struct hdac_stream * hstream , int direction )
2020-02-18 15:39:18 +01:00
{
2022-06-16 15:19:53 -05:00
u64 buffer_size = hstream - > bufsize ;
2020-02-18 15:39:18 +01:00
u64 prev_pos , pos , num_bytes ;
div64_u64_rem ( hstream - > curr_pos , buffer_size , & prev_pos ) ;
2022-06-16 15:19:53 -05:00
pos = hda_dsp_stream_get_position ( hstream , direction , false ) ;
2020-02-18 15:39:18 +01:00
if ( pos < prev_pos )
num_bytes = ( buffer_size - prev_pos ) + pos ;
else
num_bytes = pos - prev_pos ;
hstream - > curr_pos + = num_bytes ;
}
2019-06-12 12:23:40 -05:00
static bool hda_dsp_stream_check ( struct hdac_bus * bus , u32 status )
2019-04-12 11:08:54 -05:00
{
struct sof_intel_hda_dev * sof_hda = bus_to_sof_hda ( bus ) ;
2019-04-30 18:09:26 -05:00
struct hdac_stream * s ;
2019-06-12 12:23:40 -05:00
bool active = false ;
2019-04-12 11:08:54 -05:00
u32 sd_status ;
list_for_each_entry ( s , & bus - > stream_list , list ) {
2019-06-12 12:23:40 -05:00
if ( status & BIT ( s - > index ) & & s - > opened ) {
2022-10-24 11:53:10 -05:00
sd_status = readb ( s - > sd_addr + SOF_HDA_ADSP_REG_SD_STS ) ;
2019-04-12 11:08:54 -05:00
2022-09-19 14:21:06 +02:00
trace_sof_intel_hda_dsp_stream_status ( bus - > dev , s , sd_status ) ;
2019-04-12 11:08:54 -05:00
2022-10-24 11:53:10 -05:00
writeb ( sd_status , s - > sd_addr + SOF_HDA_ADSP_REG_SD_STS ) ;
2019-04-12 11:08:54 -05:00
2019-06-12 12:23:40 -05:00
active = true ;
2020-02-18 15:39:18 +01:00
if ( ( ! s - > substream & & ! s - > cstream ) | |
2019-04-12 11:08:54 -05:00
! s - > running | |
( sd_status & SOF_HDA_CL_DMA_SD_INT_COMPLETE ) = = 0 )
continue ;
/* Inform ALSA only in case not do that with IPC */
2020-02-18 15:39:18 +01:00
if ( s - > substream & & sof_hda - > no_ipc_position ) {
2019-04-30 18:09:26 -05:00
snd_sof_pcm_period_elapsed ( s - > substream ) ;
2020-02-18 15:39:18 +01:00
} else if ( s - > cstream ) {
2022-06-16 15:19:53 -05:00
hda_dsp_compr_bytes_transferred ( s , s - > cstream - > direction ) ;
2020-02-18 15:39:18 +01:00
snd_compr_fragment_elapsed ( s - > cstream ) ;
}
2019-06-12 12:23:40 -05:00
}
}
return active ;
}
2019-04-12 11:08:54 -05:00
2019-06-12 12:23:40 -05:00
irqreturn_t hda_dsp_stream_threaded_handler ( int irq , void * context )
{
2019-12-04 15:28:59 -06:00
struct snd_sof_dev * sdev = context ;
struct hdac_bus * bus = sof_to_bus ( sdev ) ;
2019-06-12 12:23:40 -05:00
bool active ;
u32 status ;
int i ;
/*
* Loop 10 times to handle missed interrupts caused by
* unsolicited responses from the codec
*/
for ( i = 0 , active = true ; i < 10 & & active ; i + + ) {
spin_lock_irq ( & bus - > reg_lock ) ;
2022-10-24 11:53:06 -05:00
status = snd_sof_dsp_read ( sdev , HDA_DSP_HDA_BAR , SOF_HDA_INTSTS ) ;
2019-06-12 12:23:40 -05:00
/* check streams */
active = hda_dsp_stream_check ( bus , status ) ;
/* check and clear RIRB interrupt */
if ( status & AZX_INT_CTRL_EN ) {
2022-10-27 15:35:31 -04:00
active | = hda_codec_check_rirb_status ( sdev ) ;
2019-04-12 11:08:54 -05:00
}
2019-06-12 12:23:40 -05:00
spin_unlock_irq ( & bus - > reg_lock ) ;
2019-04-12 11:08:54 -05:00
}
return IRQ_HANDLED ;
}
int hda_dsp_stream_init ( struct snd_sof_dev * sdev )
{
struct hdac_bus * bus = sof_to_bus ( sdev ) ;
2022-02-09 08:31:04 +02:00
struct hdac_ext_stream * hext_stream ;
2019-04-12 11:08:54 -05:00
struct hdac_stream * hstream ;
struct pci_dev * pci = to_pci_dev ( sdev - > dev ) ;
2019-04-30 18:09:29 -05:00
struct sof_intel_hda_dev * sof_hda = bus_to_sof_hda ( bus ) ;
2019-04-12 11:08:54 -05:00
int sd_offset ;
int i , num_playback , num_capture , num_total , ret ;
u32 gcap ;
gcap = snd_sof_dsp_read ( sdev , HDA_DSP_HDA_BAR , SOF_HDA_GCAP ) ;
dev_dbg ( sdev - > dev , " hda global caps = 0x%x \n " , gcap ) ;
/* get stream count from GCAP */
num_capture = ( gcap > > 8 ) & 0x0f ;
num_playback = ( gcap > > 12 ) & 0x0f ;
num_total = num_playback + num_capture ;
dev_dbg ( sdev - > dev , " detected %d playback and %d capture streams \n " ,
num_playback , num_capture ) ;
if ( num_playback > = SOF_HDA_PLAYBACK_STREAMS ) {
dev_err ( sdev - > dev , " error: too many playback streams %d \n " ,
num_playback ) ;
return - EINVAL ;
}
if ( num_capture > = SOF_HDA_CAPTURE_STREAMS ) {
dev_err ( sdev - > dev , " error: too many capture streams %d \n " ,
num_playback ) ;
return - EINVAL ;
}
/*
* mem alloc for the position buffer
* TODO : check position buffer update
*/
ret = snd_dma_alloc_pages ( SNDRV_DMA_TYPE_DEV , & pci - > dev ,
SOF_HDA_DPIB_ENTRY_SIZE * num_total ,
& bus - > posbuf ) ;
if ( ret < 0 ) {
dev_err ( sdev - > dev , " error: posbuffer dma alloc failed \n " ) ;
return - ENOMEM ;
}
2022-10-27 15:35:30 -04:00
/*
* mem alloc for the CORB / RIRB ringbuffers - this will be used only for
* HDAudio codecs
*/
2019-04-12 11:08:54 -05:00
ret = snd_dma_alloc_pages ( SNDRV_DMA_TYPE_DEV , & pci - > dev ,
PAGE_SIZE , & bus - > rb ) ;
if ( ret < 0 ) {
dev_err ( sdev - > dev , " error: RB alloc failed \n " ) ;
return - ENOMEM ;
}
/* create capture streams */
for ( i = 0 ; i < num_capture ; i + + ) {
struct sof_intel_hda_stream * hda_stream ;
hda_stream = devm_kzalloc ( sdev - > dev , sizeof ( * hda_stream ) ,
GFP_KERNEL ) ;
if ( ! hda_stream )
return - ENOMEM ;
2019-06-12 12:23:33 -05:00
hda_stream - > sdev = sdev ;
2022-02-09 08:31:04 +02:00
hext_stream = & hda_stream - > hext_stream ;
2019-04-12 11:08:54 -05:00
2022-02-09 08:31:04 +02:00
hext_stream - > pphc_addr = sdev - > bar [ HDA_DSP_PP_BAR ] +
2019-04-12 11:08:54 -05:00
SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i ;
2022-02-09 08:31:04 +02:00
hext_stream - > pplc_addr = sdev - > bar [ HDA_DSP_PP_BAR ] +
2019-04-12 11:08:54 -05:00
SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
SOF_HDA_PPLC_INTERVAL * i ;
2022-10-19 11:21:15 -05:00
hstream = & hext_stream - > hstream ;
2019-04-12 11:08:54 -05:00
/* do we support SPIB */
if ( sdev - > bar [ HDA_DSP_SPIB_BAR ] ) {
2022-10-19 11:21:15 -05:00
hstream - > spib_addr = sdev - > bar [ HDA_DSP_SPIB_BAR ] +
2019-04-12 11:08:54 -05:00
SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i +
SOF_HDA_SPIB_SPIB ;
2022-10-19 11:21:15 -05:00
hstream - > fifo_addr = sdev - > bar [ HDA_DSP_SPIB_BAR ] +
2019-04-12 11:08:54 -05:00
SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i +
SOF_HDA_SPIB_MAXFIFO ;
}
hstream - > bus = bus ;
hstream - > sd_int_sta_mask = 1 < < i ;
hstream - > index = i ;
sd_offset = SOF_STREAM_SD_OFFSET ( hstream ) ;
hstream - > sd_addr = sdev - > bar [ HDA_DSP_HDA_BAR ] + sd_offset ;
hstream - > stream_tag = i + 1 ;
hstream - > opened = false ;
hstream - > running = false ;
hstream - > direction = SNDRV_PCM_STREAM_CAPTURE ;
/* memory alloc for stream BDL */
ret = snd_dma_alloc_pages ( SNDRV_DMA_TYPE_DEV , & pci - > dev ,
HDA_DSP_BDL_SIZE , & hstream - > bdl ) ;
if ( ret < 0 ) {
dev_err ( sdev - > dev , " error: stream bdl dma alloc failed \n " ) ;
return - ENOMEM ;
}
hstream - > posbuf = ( __le32 * ) ( bus - > posbuf . area +
( hstream - > index ) * 8 ) ;
list_add_tail ( & hstream - > list , & bus - > stream_list ) ;
}
/* create playback streams */
for ( i = num_capture ; i < num_total ; i + + ) {
struct sof_intel_hda_stream * hda_stream ;
hda_stream = devm_kzalloc ( sdev - > dev , sizeof ( * hda_stream ) ,
GFP_KERNEL ) ;
if ( ! hda_stream )
return - ENOMEM ;
2019-06-12 12:23:33 -05:00
hda_stream - > sdev = sdev ;
2022-02-09 08:31:04 +02:00
hext_stream = & hda_stream - > hext_stream ;
2019-04-12 11:08:54 -05:00
/* we always have DSP support */
2022-02-09 08:31:04 +02:00
hext_stream - > pphc_addr = sdev - > bar [ HDA_DSP_PP_BAR ] +
2019-04-12 11:08:54 -05:00
SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i ;
2022-02-09 08:31:04 +02:00
hext_stream - > pplc_addr = sdev - > bar [ HDA_DSP_PP_BAR ] +
2019-04-12 11:08:54 -05:00
SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
SOF_HDA_PPLC_INTERVAL * i ;
2022-10-19 11:21:15 -05:00
hstream = & hext_stream - > hstream ;
2019-04-12 11:08:54 -05:00
/* do we support SPIB */
if ( sdev - > bar [ HDA_DSP_SPIB_BAR ] ) {
2022-10-19 11:21:15 -05:00
hstream - > spib_addr = sdev - > bar [ HDA_DSP_SPIB_BAR ] +
2019-04-12 11:08:54 -05:00
SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i +
SOF_HDA_SPIB_SPIB ;
2022-10-19 11:21:15 -05:00
hstream - > fifo_addr = sdev - > bar [ HDA_DSP_SPIB_BAR ] +
2019-04-12 11:08:54 -05:00
SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i +
SOF_HDA_SPIB_MAXFIFO ;
}
hstream - > bus = bus ;
hstream - > sd_int_sta_mask = 1 < < i ;
hstream - > index = i ;
sd_offset = SOF_STREAM_SD_OFFSET ( hstream ) ;
hstream - > sd_addr = sdev - > bar [ HDA_DSP_HDA_BAR ] + sd_offset ;
hstream - > stream_tag = i - num_capture + 1 ;
hstream - > opened = false ;
hstream - > running = false ;
hstream - > direction = SNDRV_PCM_STREAM_PLAYBACK ;
/* mem alloc for stream BDL */
ret = snd_dma_alloc_pages ( SNDRV_DMA_TYPE_DEV , & pci - > dev ,
HDA_DSP_BDL_SIZE , & hstream - > bdl ) ;
if ( ret < 0 ) {
dev_err ( sdev - > dev , " error: stream bdl dma alloc failed \n " ) ;
return - ENOMEM ;
}
hstream - > posbuf = ( __le32 * ) ( bus - > posbuf . area +
( hstream - > index ) * 8 ) ;
list_add_tail ( & hstream - > list , & bus - > stream_list ) ;
}
2019-04-30 18:09:29 -05:00
/* store total stream count (playback + capture) from GCAP */
sof_hda - > stream_max = num_total ;
2019-04-12 11:08:54 -05:00
return 0 ;
}
void hda_dsp_stream_free ( struct snd_sof_dev * sdev )
{
struct hdac_bus * bus = sof_to_bus ( sdev ) ;
struct hdac_stream * s , * _s ;
2022-02-09 08:31:04 +02:00
struct hdac_ext_stream * hext_stream ;
2019-04-12 11:08:54 -05:00
struct sof_intel_hda_stream * hda_stream ;
/* free position buffer */
if ( bus - > posbuf . area )
snd_dma_free_pages ( & bus - > posbuf ) ;
2022-10-27 15:35:30 -04:00
/* free CORB/RIRB buffer - only used for HDaudio codecs */
2019-04-12 11:08:54 -05:00
if ( bus - > rb . area )
snd_dma_free_pages ( & bus - > rb ) ;
list_for_each_entry_safe ( s , _s , & bus - > stream_list , list ) {
/* TODO: decouple */
/* free bdl buffer */
if ( s - > bdl . area )
snd_dma_free_pages ( & s - > bdl ) ;
list_del ( & s - > list ) ;
2022-02-09 08:31:04 +02:00
hext_stream = stream_to_hdac_ext_stream ( s ) ;
hda_stream = container_of ( hext_stream , struct sof_intel_hda_stream ,
hext_stream ) ;
2019-04-12 11:08:54 -05:00
devm_kfree ( sdev - > dev , hda_stream ) ;
}
}
2022-06-16 15:19:53 -05:00
snd_pcm_uframes_t hda_dsp_stream_get_position ( struct hdac_stream * hstream ,
int direction , bool can_sleep )
{
struct hdac_ext_stream * hext_stream = stream_to_hdac_ext_stream ( hstream ) ;
struct sof_intel_hda_stream * hda_stream = hstream_to_sof_hda_stream ( hext_stream ) ;
struct snd_sof_dev * sdev = hda_stream - > sdev ;
snd_pcm_uframes_t pos ;
switch ( sof_hda_position_quirk ) {
case SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY :
/*
* This legacy code , inherited from the Skylake driver ,
* mixes DPIB registers and DPIB DDR updates and
* does not seem to follow any known hardware recommendations .
* It ' s not clear e . g . why there is a different flow
* for capture and playback , the only information that matters is
* what traffic class is used , and on all SOF - enabled platforms
* only VC0 is supported so the work - around was likely not necessary
* and quite possibly wrong .
*/
/* DPIB/posbuf position mode:
* For Playback , Use DPIB register from HDA space which
* reflects the actual data transferred .
* For Capture , Use the position buffer for pointer , as DPIB
* is not accurate enough , its update may be completed
* earlier than the data written to DDR .
*/
if ( direction = = SNDRV_PCM_STREAM_PLAYBACK ) {
pos = snd_sof_dsp_read ( sdev , HDA_DSP_HDA_BAR ,
AZX_REG_VS_SDXDPIB_XBASE +
( AZX_REG_VS_SDXDPIB_XINTERVAL *
hstream - > index ) ) ;
} else {
/*
* For capture stream , we need more workaround to fix the
* position incorrect issue :
*
* 1. Wait at least 20u s before reading position buffer after
* the interrupt generated ( IOC ) , to make sure position update
* happens on frame boundary i . e . 20.833 uSec for 48 KHz .
* 2. Perform a dummy Read to DPIB register to flush DMA
* position value .
* 3. Read the DMA Position from posbuf . Now the readback
* value should be > = period boundary .
*/
if ( can_sleep )
usleep_range ( 20 , 21 ) ;
snd_sof_dsp_read ( sdev , HDA_DSP_HDA_BAR ,
AZX_REG_VS_SDXDPIB_XBASE +
( AZX_REG_VS_SDXDPIB_XINTERVAL *
hstream - > index ) ) ;
pos = snd_hdac_stream_get_pos_posbuf ( hstream ) ;
}
break ;
case SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS :
/*
* In case VC1 traffic is disabled this is the recommended option
*/
pos = snd_sof_dsp_read ( sdev , HDA_DSP_HDA_BAR ,
AZX_REG_VS_SDXDPIB_XBASE +
( AZX_REG_VS_SDXDPIB_XINTERVAL *
hstream - > index ) ) ;
break ;
case SOF_HDA_POSITION_QUIRK_USE_DPIB_DDR_UPDATE :
/*
* This is the recommended option when VC1 is enabled .
* While this isn ' t needed for SOF platforms it ' s added for
* consistency and debug .
*/
pos = snd_hdac_stream_get_pos_posbuf ( hstream ) ;
break ;
default :
dev_err_once ( sdev - > dev , " hda_position_quirk value %d not supported \n " ,
sof_hda_position_quirk ) ;
pos = 0 ;
break ;
}
if ( pos > = hstream - > bufsize )
pos = 0 ;
return pos ;
}