2018-07-17 17:42:51 +02:00
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
//
// Copyright (c) 2018 BayLibre, SAS.
// Author: Jerome Brunet <jbrunet@baylibre.com>
# include <linux/clk.h>
# include <linux/of_irq.h>
# include <linux/of_platform.h>
# include <linux/module.h>
# include <linux/regmap.h>
# include <linux/reset.h>
# include <sound/pcm_params.h>
# include <sound/soc.h>
# include <sound/soc-dai.h>
# include "axg-fifo.h"
/*
* This file implements the platform operations common to the playback and
* capture frontend DAI . The logic behind this two types of fifo is very
* similar but some difference exist .
2019-04-04 13:17:29 +02:00
* These differences are handled in the respective DAI drivers
2018-07-17 17:42:51 +02:00
*/
static struct snd_pcm_hardware axg_fifo_hw = {
. info = ( SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_PAUSE ) ,
. formats = AXG_FIFO_FORMATS ,
. rate_min = 5512 ,
. rate_max = 192000 ,
. channels_min = 1 ,
. channels_max = AXG_FIFO_CH_MAX ,
2019-12-18 18:24:20 +01:00
. period_bytes_min = AXG_FIFO_BURST ,
2018-07-17 17:42:51 +02:00
. period_bytes_max = UINT_MAX ,
. periods_min = 2 ,
. periods_max = UINT_MAX ,
/* No real justification for this */
. buffer_bytes_max = 1 * 1024 * 1024 ,
} ;
static struct snd_soc_dai * axg_fifo_dai ( struct snd_pcm_substream * ss )
{
struct snd_soc_pcm_runtime * rtd = ss - > private_data ;
2020-03-23 14:19:32 +09:00
return asoc_rtd_to_cpu ( rtd , 0 ) ;
2018-07-17 17:42:51 +02:00
}
static struct axg_fifo * axg_fifo_data ( struct snd_pcm_substream * ss )
{
struct snd_soc_dai * dai = axg_fifo_dai ( ss ) ;
return snd_soc_dai_get_drvdata ( dai ) ;
}
static struct device * axg_fifo_dev ( struct snd_pcm_substream * ss )
{
struct snd_soc_dai * dai = axg_fifo_dai ( ss ) ;
return dai - > dev ;
}
static void __dma_enable ( struct axg_fifo * fifo , bool enable )
{
regmap_update_bits ( fifo - > map , FIFO_CTRL0 , CTRL0_DMA_EN ,
enable ? CTRL0_DMA_EN : 0 ) ;
}
2019-10-02 14:33:55 +09:00
int axg_fifo_pcm_trigger ( struct snd_soc_component * component ,
struct snd_pcm_substream * ss , int cmd )
2018-07-17 17:42:51 +02:00
{
struct axg_fifo * fifo = axg_fifo_data ( ss ) ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
__dma_enable ( fifo , true ) ;
break ;
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
case SNDRV_PCM_TRIGGER_STOP :
__dma_enable ( fifo , false ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
2019-10-02 14:33:55 +09:00
EXPORT_SYMBOL_GPL ( axg_fifo_pcm_trigger ) ;
2018-07-17 17:42:51 +02:00
2019-10-02 14:33:55 +09:00
snd_pcm_uframes_t axg_fifo_pcm_pointer ( struct snd_soc_component * component ,
struct snd_pcm_substream * ss )
2018-07-17 17:42:51 +02:00
{
struct axg_fifo * fifo = axg_fifo_data ( ss ) ;
struct snd_pcm_runtime * runtime = ss - > runtime ;
unsigned int addr ;
regmap_read ( fifo - > map , FIFO_STATUS2 , & addr ) ;
return bytes_to_frames ( runtime , addr - ( unsigned int ) runtime - > dma_addr ) ;
}
2019-10-02 14:33:55 +09:00
EXPORT_SYMBOL_GPL ( axg_fifo_pcm_pointer ) ;
2018-07-17 17:42:51 +02:00
2019-10-02 14:33:55 +09:00
int axg_fifo_pcm_hw_params ( struct snd_soc_component * component ,
struct snd_pcm_substream * ss ,
struct snd_pcm_hw_params * params )
2018-07-17 17:42:51 +02:00
{
struct snd_pcm_runtime * runtime = ss - > runtime ;
struct axg_fifo * fifo = axg_fifo_data ( ss ) ;
2019-12-18 18:24:17 +01:00
unsigned int burst_num , period , threshold ;
2018-07-17 17:42:51 +02:00
dma_addr_t end_ptr ;
2019-12-18 18:24:17 +01:00
period = params_period_bytes ( params ) ;
2018-07-17 17:42:51 +02:00
/* Setup dma memory pointers */
end_ptr = runtime - > dma_addr + runtime - > dma_bytes - AXG_FIFO_BURST ;
regmap_write ( fifo - > map , FIFO_START_ADDR , runtime - > dma_addr ) ;
regmap_write ( fifo - > map , FIFO_FINISH_ADDR , end_ptr ) ;
/* Setup interrupt periodicity */
2019-12-18 18:24:17 +01:00
burst_num = period / AXG_FIFO_BURST ;
2018-07-17 17:42:51 +02:00
regmap_write ( fifo - > map , FIFO_INT_ADDR , burst_num ) ;
2019-12-18 18:24:17 +01:00
/*
* Start the fifo request on the smallest of the following :
* - Half the fifo size
* - Half the period size
*/
2019-12-18 18:24:19 +01:00
threshold = min ( period / 2 , fifo - > depth / 2 ) ;
2019-12-18 18:24:17 +01:00
/*
* With the threshold in bytes , register value is :
* V = ( threshold / burst ) - 1
*/
threshold / = AXG_FIFO_BURST ;
regmap_field_write ( fifo - > field_threshold ,
threshold ? threshold - 1 : 0 ) ;
2018-07-17 17:42:51 +02:00
/* Enable block count irq */
regmap_update_bits ( fifo - > map , FIFO_CTRL0 ,
CTRL0_INT_EN ( FIFO_INT_COUNT_REPEAT ) ,
CTRL0_INT_EN ( FIFO_INT_COUNT_REPEAT ) ) ;
return 0 ;
}
2019-10-02 14:33:55 +09:00
EXPORT_SYMBOL_GPL ( axg_fifo_pcm_hw_params ) ;
2018-07-17 17:42:51 +02:00
2019-10-02 14:33:55 +09:00
int g12a_fifo_pcm_hw_params ( struct snd_soc_component * component ,
struct snd_pcm_substream * ss ,
struct snd_pcm_hw_params * params )
2019-04-04 13:17:29 +02:00
{
struct axg_fifo * fifo = axg_fifo_data ( ss ) ;
struct snd_pcm_runtime * runtime = ss - > runtime ;
int ret ;
2019-10-02 14:33:55 +09:00
ret = axg_fifo_pcm_hw_params ( component , ss , params ) ;
2019-04-04 13:17:29 +02:00
if ( ret )
return ret ;
/* Set the initial memory address of the DMA */
regmap_write ( fifo - > map , FIFO_INIT_ADDR , runtime - > dma_addr ) ;
return 0 ;
}
2019-10-02 14:33:55 +09:00
EXPORT_SYMBOL_GPL ( g12a_fifo_pcm_hw_params ) ;
2019-04-04 13:17:29 +02:00
2019-10-02 14:33:55 +09:00
int axg_fifo_pcm_hw_free ( struct snd_soc_component * component ,
struct snd_pcm_substream * ss )
2018-07-17 17:42:51 +02:00
{
struct axg_fifo * fifo = axg_fifo_data ( ss ) ;
/* Disable the block count irq */
regmap_update_bits ( fifo - > map , FIFO_CTRL0 ,
CTRL0_INT_EN ( FIFO_INT_COUNT_REPEAT ) , 0 ) ;
2019-12-10 15:25:56 +01:00
return 0 ;
2018-07-17 17:42:51 +02:00
}
2019-10-02 14:33:55 +09:00
EXPORT_SYMBOL_GPL ( axg_fifo_pcm_hw_free ) ;
2018-07-17 17:42:51 +02:00
static void axg_fifo_ack_irq ( struct axg_fifo * fifo , u8 mask )
{
regmap_update_bits ( fifo - > map , FIFO_CTRL1 ,
CTRL1_INT_CLR ( FIFO_INT_MASK ) ,
CTRL1_INT_CLR ( mask ) ) ;
/* Clear must also be cleared */
regmap_update_bits ( fifo - > map , FIFO_CTRL1 ,
CTRL1_INT_CLR ( FIFO_INT_MASK ) ,
0 ) ;
}
static irqreturn_t axg_fifo_pcm_irq_block ( int irq , void * dev_id )
{
struct snd_pcm_substream * ss = dev_id ;
struct axg_fifo * fifo = axg_fifo_data ( ss ) ;
unsigned int status ;
regmap_read ( fifo - > map , FIFO_STATUS1 , & status ) ;
status = STATUS1_INT_STS ( status ) & FIFO_INT_MASK ;
if ( status & FIFO_INT_COUNT_REPEAT )
snd_pcm_period_elapsed ( ss ) ;
else
dev_dbg ( axg_fifo_dev ( ss ) , " unexpected irq - STS 0x%02x \n " ,
status ) ;
/* Ack irqs */
axg_fifo_ack_irq ( fifo , status ) ;
2018-07-26 14:45:42 +02:00
return IRQ_RETVAL ( status ) ;
2018-07-17 17:42:51 +02:00
}
2019-10-02 14:33:55 +09:00
int axg_fifo_pcm_open ( struct snd_soc_component * component ,
struct snd_pcm_substream * ss )
2018-07-17 17:42:51 +02:00
{
struct axg_fifo * fifo = axg_fifo_data ( ss ) ;
struct device * dev = axg_fifo_dev ( ss ) ;
int ret ;
snd_soc_set_runtime_hwparams ( ss , & axg_fifo_hw ) ;
/*
* Make sure the buffer and period size are multiple of the FIFO
2019-12-18 18:24:20 +01:00
* burst
2018-07-17 17:42:51 +02:00
*/
ret = snd_pcm_hw_constraint_step ( ss - > runtime , 0 ,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES ,
2019-12-18 18:24:20 +01:00
AXG_FIFO_BURST ) ;
2018-07-17 17:42:51 +02:00
if ( ret )
return ret ;
ret = snd_pcm_hw_constraint_step ( ss - > runtime , 0 ,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES ,
2019-12-18 18:24:20 +01:00
AXG_FIFO_BURST ) ;
2018-07-17 17:42:51 +02:00
if ( ret )
return ret ;
ret = request_irq ( fifo - > irq , axg_fifo_pcm_irq_block , 0 ,
dev_name ( dev ) , ss ) ;
2018-08-27 16:15:29 +02:00
if ( ret )
return ret ;
2018-07-17 17:42:51 +02:00
/* Enable pclk to access registers and clock the fifo ip */
ret = clk_prepare_enable ( fifo - > pclk ) ;
if ( ret )
2020-06-06 17:31:03 +02:00
goto free_irq ;
2018-07-17 17:42:51 +02:00
/* Setup status2 so it reports the memory pointer */
regmap_update_bits ( fifo - > map , FIFO_CTRL1 ,
CTRL1_STATUS2_SEL_MASK ,
CTRL1_STATUS2_SEL ( STATUS2_SEL_DDR_READ ) ) ;
/* Make sure the dma is initially disabled */
__dma_enable ( fifo , false ) ;
/* Disable irqs until params are ready */
regmap_update_bits ( fifo - > map , FIFO_CTRL0 ,
CTRL0_INT_EN ( FIFO_INT_MASK ) , 0 ) ;
/* Clear any pending interrupt */
axg_fifo_ack_irq ( fifo , FIFO_INT_MASK ) ;
/* Take memory arbitror out of reset */
ret = reset_control_deassert ( fifo - > arb ) ;
if ( ret )
2020-06-06 17:31:03 +02:00
goto free_clk ;
return 0 ;
2018-07-17 17:42:51 +02:00
2020-06-06 17:31:03 +02:00
free_clk :
clk_disable_unprepare ( fifo - > pclk ) ;
free_irq :
free_irq ( fifo - > irq , ss ) ;
2018-07-17 17:42:51 +02:00
return ret ;
}
2019-10-02 14:33:55 +09:00
EXPORT_SYMBOL_GPL ( axg_fifo_pcm_open ) ;
2018-07-17 17:42:51 +02:00
2019-10-02 14:33:55 +09:00
int axg_fifo_pcm_close ( struct snd_soc_component * component ,
struct snd_pcm_substream * ss )
2018-07-17 17:42:51 +02:00
{
struct axg_fifo * fifo = axg_fifo_data ( ss ) ;
int ret ;
/* Put the memory arbitror back in reset */
ret = reset_control_assert ( fifo - > arb ) ;
/* Disable fifo ip and register access */
clk_disable_unprepare ( fifo - > pclk ) ;
/* remove IRQ */
free_irq ( fifo - > irq , ss ) ;
return ret ;
}
2019-10-02 14:33:55 +09:00
EXPORT_SYMBOL_GPL ( axg_fifo_pcm_close ) ;
2019-04-04 13:17:29 +02:00
2018-07-17 17:42:51 +02:00
int axg_fifo_pcm_new ( struct snd_soc_pcm_runtime * rtd , unsigned int type )
{
struct snd_card * card = rtd - > card - > snd_card ;
size_t size = axg_fifo_hw . buffer_bytes_max ;
2019-12-10 15:25:56 +01:00
snd_pcm_set_managed_buffer ( rtd - > pcm - > streams [ type ] . substream ,
SNDRV_DMA_TYPE_DEV , card - > dev ,
size , size ) ;
2019-02-04 16:39:39 +01:00
return 0 ;
2018-07-17 17:42:51 +02:00
}
EXPORT_SYMBOL_GPL ( axg_fifo_pcm_new ) ;
static const struct regmap_config axg_fifo_regmap_cfg = {
. reg_bits = 32 ,
. val_bits = 32 ,
. reg_stride = 4 ,
2019-09-05 14:01:18 +02:00
. max_register = FIFO_CTRL2 ,
2018-07-17 17:42:51 +02:00
} ;
int axg_fifo_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
const struct axg_fifo_match_data * data ;
struct axg_fifo * fifo ;
void __iomem * regs ;
2019-12-18 18:24:19 +01:00
int ret ;
2018-07-17 17:42:51 +02:00
data = of_device_get_match_data ( dev ) ;
if ( ! data ) {
dev_err ( dev , " failed to match device \n " ) ;
return - ENODEV ;
}
fifo = devm_kzalloc ( dev , sizeof ( * fifo ) , GFP_KERNEL ) ;
if ( ! fifo )
return - ENOMEM ;
platform_set_drvdata ( pdev , fifo ) ;
2019-07-27 23:07:34 +08:00
regs = devm_platform_ioremap_resource ( pdev , 0 ) ;
2018-07-17 17:42:51 +02:00
if ( IS_ERR ( regs ) )
return PTR_ERR ( regs ) ;
fifo - > map = devm_regmap_init_mmio ( dev , regs , & axg_fifo_regmap_cfg ) ;
if ( IS_ERR ( fifo - > map ) ) {
dev_err ( dev , " failed to init regmap: %ld \n " ,
PTR_ERR ( fifo - > map ) ) ;
return PTR_ERR ( fifo - > map ) ;
}
fifo - > pclk = devm_clk_get ( dev , NULL ) ;
if ( IS_ERR ( fifo - > pclk ) ) {
if ( PTR_ERR ( fifo - > pclk ) ! = - EPROBE_DEFER )
dev_err ( dev , " failed to get pclk: %ld \n " ,
PTR_ERR ( fifo - > pclk ) ) ;
return PTR_ERR ( fifo - > pclk ) ;
}
fifo - > arb = devm_reset_control_get_exclusive ( dev , NULL ) ;
if ( IS_ERR ( fifo - > arb ) ) {
if ( PTR_ERR ( fifo - > arb ) ! = - EPROBE_DEFER )
dev_err ( dev , " failed to get arb reset: %ld \n " ,
PTR_ERR ( fifo - > arb ) ) ;
return PTR_ERR ( fifo - > arb ) ;
}
fifo - > irq = of_irq_get ( dev - > of_node , 0 ) ;
if ( fifo - > irq < = 0 ) {
dev_err ( dev , " failed to get irq: %d \n " , fifo - > irq ) ;
return fifo - > irq ;
}
2019-12-18 18:24:17 +01:00
fifo - > field_threshold =
devm_regmap_field_alloc ( dev , fifo - > map , data - > field_threshold ) ;
if ( IS_ERR ( fifo - > field_threshold ) )
return PTR_ERR ( fifo - > field_threshold ) ;
2019-12-18 18:24:19 +01:00
ret = of_property_read_u32 ( dev - > of_node , " amlogic,fifo-depth " ,
& fifo - > depth ) ;
if ( ret ) {
/* Error out for anything but a missing property */
if ( ret ! = - EINVAL )
return ret ;
/*
* If the property is missing , it might be because of an old
* DT . In such case , assume the smallest known fifo depth
*/
fifo - > depth = 256 ;
dev_warn ( dev , " fifo depth not found, assume %u bytes \n " ,
fifo - > depth ) ;
}
2018-07-17 17:42:51 +02:00
return devm_snd_soc_register_component ( dev , data - > component_drv ,
data - > dai_drv , 1 ) ;
}
EXPORT_SYMBOL_GPL ( axg_fifo_probe ) ;
2019-04-04 13:17:29 +02:00
MODULE_DESCRIPTION ( " Amlogic AXG/G12A fifo driver " ) ;
2018-07-17 17:42:51 +02:00
MODULE_AUTHOR ( " Jerome Brunet <jbrunet@baylibre.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;