2018-07-24 09:48:31 -03:00
// SPDX-License-Identifier: GPL-2.0
//
// Freescale ASRC ALSA SoC Digital Audio Interface (DAI) driver
//
// Copyright (C) 2014 Freescale Semiconductor, Inc.
//
// Author: Nicolin Chen <nicoleotsuka@gmail.com>
2014-07-29 18:08:53 +08:00
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/dma-mapping.h>
# include <linux/module.h>
# include <linux/of_platform.h>
# include <linux/platform_data/dma-imx.h>
# include <linux/pm_runtime.h>
# include <sound/dmaengine_pcm.h>
# include <sound/pcm_params.h>
# include "fsl_asrc.h"
# define IDEAL_RATIO_DECIMAL_DEPTH 26
# define pair_err(fmt, ...) \
2020-04-16 20:25:31 +08:00
dev_err ( & asrc - > pdev - > dev , " Pair %c: " fmt , ' A ' + index , # # __VA_ARGS__ )
2014-07-29 18:08:53 +08:00
# define pair_dbg(fmt, ...) \
2020-04-16 20:25:31 +08:00
dev_dbg ( & asrc - > pdev - > dev , " Pair %c: " fmt , ' A ' + index , # # __VA_ARGS__ )
2014-07-29 18:08:53 +08:00
/* Corresponding to process_option */
2019-05-15 06:42:26 +00:00
static unsigned int supported_asrc_rate [ ] = {
5512 , 8000 , 11025 , 12000 , 16000 , 22050 , 24000 , 32000 , 44100 , 48000 ,
64000 , 88200 , 96000 , 128000 , 176400 , 192000 ,
2014-07-29 18:08:53 +08:00
} ;
2019-05-15 06:42:26 +00:00
static struct snd_pcm_hw_constraint_list fsl_asrc_rate_constraints = {
. count = ARRAY_SIZE ( supported_asrc_rate ) ,
. list = supported_asrc_rate ,
2014-07-29 18:08:53 +08:00
} ;
2020-07-02 14:21:40 -05:00
/*
2014-07-29 18:08:53 +08:00
* The following tables map the relationship between asrc_inclk / asrc_outclk in
* fsl_asrc . h and the registers of ASRCSR
*/
2019-12-04 20:00:19 +08:00
static unsigned char input_clk_map_imx35 [ ASRC_CLK_MAP_LEN ] = {
2014-07-29 18:08:53 +08:00
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 0xa , 0xb , 0xc , 0xd , 0xe , 0xf ,
2019-12-04 20:00:19 +08:00
3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 ,
3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 ,
2014-07-29 18:08:53 +08:00
} ;
2019-12-04 20:00:19 +08:00
static unsigned char output_clk_map_imx35 [ ASRC_CLK_MAP_LEN ] = {
2014-07-29 18:08:53 +08:00
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 0xa , 0xb , 0xc , 0xd , 0xe , 0xf ,
2019-12-04 20:00:19 +08:00
3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 ,
3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 ,
2014-07-29 18:08:53 +08:00
} ;
/* i.MX53 uses the same map for input and output */
2019-12-04 20:00:19 +08:00
static unsigned char input_clk_map_imx53 [ ASRC_CLK_MAP_LEN ] = {
2014-07-29 18:08:53 +08:00
/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */
0x0 , 0x1 , 0x2 , 0x7 , 0x4 , 0x5 , 0x6 , 0x3 , 0x8 , 0x9 , 0xa , 0xb , 0xc , 0xf , 0xe , 0xd ,
2019-12-04 20:00:19 +08:00
0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 ,
0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 ,
2014-07-29 18:08:53 +08:00
} ;
2019-12-04 20:00:19 +08:00
static unsigned char output_clk_map_imx53 [ ASRC_CLK_MAP_LEN ] = {
2014-07-29 18:08:53 +08:00
/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */
0x8 , 0x9 , 0xa , 0x7 , 0xc , 0x5 , 0x6 , 0xb , 0x0 , 0x1 , 0x2 , 0x3 , 0x4 , 0xf , 0xe , 0xd ,
2019-12-04 20:00:19 +08:00
0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 ,
0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 , 0x7 ,
2014-07-29 18:08:53 +08:00
} ;
2020-07-02 14:21:40 -05:00
/*
2019-12-04 20:00:19 +08:00
* i . MX8QM / i . MX8QXP uses the same map for input and output .
* clk_map_imx8qm [ 0 ] is for i . MX8QM asrc0
* clk_map_imx8qm [ 1 ] is for i . MX8QM asrc1
* clk_map_imx8qxp [ 0 ] is for i . MX8QXP asrc0
* clk_map_imx8qxp [ 1 ] is for i . MX8QXP asrc1
*/
static unsigned char clk_map_imx8qm [ 2 ] [ ASRC_CLK_MAP_LEN ] = {
{
0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0x0 ,
0x0 , 0x1 , 0x2 , 0x3 , 0x4 , 0x5 , 0x6 , 0x7 , 0x8 , 0x9 , 0xa , 0xb , 0xc , 0xd , 0xe , 0xf ,
0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf ,
} ,
{
0xf , 0xf , 0xf , 0xf , 0xf , 0x7 , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0x0 ,
0x0 , 0x1 , 0x2 , 0x3 , 0xb , 0xc , 0xf , 0xf , 0xd , 0xe , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf ,
0x4 , 0x5 , 0x6 , 0xf , 0x8 , 0x9 , 0xa , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf ,
} ,
} ;
static unsigned char clk_map_imx8qxp [ 2 ] [ ASRC_CLK_MAP_LEN ] = {
{
0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0x0 ,
0x0 , 0x1 , 0x2 , 0x3 , 0x4 , 0x5 , 0x6 , 0xf , 0x7 , 0x8 , 0x9 , 0xa , 0xb , 0xc , 0xf , 0xf ,
0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf ,
} ,
{
0xf , 0xf , 0xf , 0xf , 0xf , 0x7 , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0x0 ,
0x0 , 0x1 , 0x2 , 0x3 , 0x7 , 0x8 , 0xf , 0xf , 0x9 , 0xa , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf ,
0xf , 0xf , 0x6 , 0xf , 0xf , 0xf , 0xa , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf , 0xf ,
} ,
} ;
2014-07-29 18:08:53 +08:00
2019-05-15 06:42:22 +00:00
/**
2020-07-02 14:21:40 -05:00
* fsl_asrc_sel_proc - Select the pre - processing and post - processing options
* @ inrate : input sample rate
* @ outrate : output sample rate
* @ pre_proc : return value for pre - processing option
* @ post_proc : return value for post - processing option
*
2019-05-15 06:42:22 +00:00
* Make sure to exclude following unsupported cases before
* calling this function :
* 1 ) inrate > 8.125 * outrate
* 2 ) inrate > 16.125 * outrate
*
*/
static void fsl_asrc_sel_proc ( int inrate , int outrate ,
int * pre_proc , int * post_proc )
{
bool post_proc_cond2 ;
bool post_proc_cond0 ;
/* select pre_proc between [0, 2] */
if ( inrate * 8 > 33 * outrate )
* pre_proc = 2 ;
else if ( inrate * 8 > 15 * outrate ) {
if ( inrate > 152000 )
* pre_proc = 2 ;
else
* pre_proc = 1 ;
} else if ( inrate < 76000 )
* pre_proc = 0 ;
else if ( inrate > 152000 )
* pre_proc = 2 ;
else
* pre_proc = 1 ;
/* Condition for selection of post-processing */
post_proc_cond2 = ( inrate * 15 > outrate * 16 & & outrate < 56000 ) | |
( inrate > 56000 & & outrate < 56000 ) ;
post_proc_cond0 = inrate * 23 < outrate * 8 ;
if ( post_proc_cond2 )
* post_proc = 2 ;
else if ( post_proc_cond0 )
* post_proc = 0 ;
else
* post_proc = 1 ;
}
2014-07-29 18:08:53 +08:00
/**
2020-07-02 14:21:40 -05:00
* fsl_asrc_request_pair - Request ASRC pair
* @ channels : number of channels
* @ pair : pointer to pair
2014-07-29 18:08:53 +08:00
*
* It assigns pair by the order of A - > C - > B because allocation of pair B ,
* within range [ ANCA , ANCA + ANCB - 1 ] , depends on the channels of pair A
* while pair A and pair C are comparatively independent .
*/
2020-05-07 10:29:59 +08:00
static int fsl_asrc_request_pair ( int channels , struct fsl_asrc_pair * pair )
2014-07-29 18:08:53 +08:00
{
enum asrc_pair_index index = ASRC_INVALID_PAIR ;
2020-04-16 20:25:31 +08:00
struct fsl_asrc * asrc = pair - > asrc ;
struct device * dev = & asrc - > pdev - > dev ;
2014-07-29 18:08:53 +08:00
unsigned long lock_flags ;
int i , ret = 0 ;
2020-04-16 20:25:31 +08:00
spin_lock_irqsave ( & asrc - > lock , lock_flags ) ;
2014-07-29 18:08:53 +08:00
for ( i = ASRC_PAIR_A ; i < ASRC_PAIR_MAX_NUM ; i + + ) {
2020-04-16 20:25:31 +08:00
if ( asrc - > pair [ i ] ! = NULL )
2014-07-29 18:08:53 +08:00
continue ;
index = i ;
if ( i ! = ASRC_PAIR_B )
break ;
}
if ( index = = ASRC_INVALID_PAIR ) {
dev_err ( dev , " all pairs are busy now \n " ) ;
ret = - EBUSY ;
2020-04-16 20:25:31 +08:00
} else if ( asrc - > channel_avail < channels ) {
2014-07-29 18:08:53 +08:00
dev_err ( dev , " can't afford required channels: %d \n " , channels ) ;
ret = - EINVAL ;
} else {
2020-04-16 20:25:31 +08:00
asrc - > channel_avail - = channels ;
asrc - > pair [ index ] = pair ;
2014-07-29 18:08:53 +08:00
pair - > channels = channels ;
pair - > index = index ;
}
2020-04-16 20:25:31 +08:00
spin_unlock_irqrestore ( & asrc - > lock , lock_flags ) ;
2014-07-29 18:08:53 +08:00
return ret ;
}
/**
2020-07-02 14:21:40 -05:00
* fsl_asrc_release_pair - Release ASRC pair
* @ pair : pair to release
2014-07-29 18:08:53 +08:00
*
2020-04-16 20:25:31 +08:00
* It clears the resource from asrc and releases the occupied channels .
2014-07-29 18:08:53 +08:00
*/
2020-05-07 10:29:59 +08:00
static void fsl_asrc_release_pair ( struct fsl_asrc_pair * pair )
2014-07-29 18:08:53 +08:00
{
2020-04-16 20:25:31 +08:00
struct fsl_asrc * asrc = pair - > asrc ;
2014-07-29 18:08:53 +08:00
enum asrc_pair_index index = pair - > index ;
unsigned long lock_flags ;
/* Make sure the pair is disabled */
2020-04-16 20:25:31 +08:00
regmap_update_bits ( asrc - > regmap , REG_ASRCTR ,
2014-07-29 18:08:53 +08:00
ASRCTR_ASRCEi_MASK ( index ) , 0 ) ;
2020-04-16 20:25:31 +08:00
spin_lock_irqsave ( & asrc - > lock , lock_flags ) ;
2014-07-29 18:08:53 +08:00
2020-04-16 20:25:31 +08:00
asrc - > channel_avail + = pair - > channels ;
asrc - > pair [ index ] = NULL ;
2014-07-29 18:08:53 +08:00
pair - > error = 0 ;
2020-04-16 20:25:31 +08:00
spin_unlock_irqrestore ( & asrc - > lock , lock_flags ) ;
2014-07-29 18:08:53 +08:00
}
/**
2020-07-02 14:21:40 -05:00
* fsl_asrc_set_watermarks - configure input and output thresholds
* @ pair : pointer to pair
* @ in : input threshold
* @ out : output threshold
2014-07-29 18:08:53 +08:00
*/
static void fsl_asrc_set_watermarks ( struct fsl_asrc_pair * pair , u32 in , u32 out )
{
2020-04-16 20:25:31 +08:00
struct fsl_asrc * asrc = pair - > asrc ;
2014-07-29 18:08:53 +08:00
enum asrc_pair_index index = pair - > index ;
2020-04-16 20:25:31 +08:00
regmap_update_bits ( asrc - > regmap , REG_ASRMCR ( index ) ,
2014-07-29 18:08:53 +08:00
ASRMCRi_EXTTHRSHi_MASK |
ASRMCRi_INFIFO_THRESHOLD_MASK |
ASRMCRi_OUTFIFO_THRESHOLD_MASK ,
ASRMCRi_EXTTHRSHi |
ASRMCRi_INFIFO_THRESHOLD ( in ) |
ASRMCRi_OUTFIFO_THRESHOLD ( out ) ) ;
}
/**
2020-07-02 14:21:40 -05:00
* fsl_asrc_cal_asrck_divisor - Calculate the total divisor between asrck clock rate and sample rate
* @ pair : pointer to pair
* @ div : divider
2014-07-29 18:08:53 +08:00
*
* It follows the formula clk_rate = samplerate * ( 2 ^ prescaler ) * divider
*/
static u32 fsl_asrc_cal_asrck_divisor ( struct fsl_asrc_pair * pair , u32 div )
{
u32 ps ;
/* Calculate the divisors: prescaler [2^0, 2^7], divder [1, 8] */
for ( ps = 0 ; div > 8 ; ps + + )
div > > = 1 ;
return ( ( div - 1 ) < < ASRCDRi_AxCPi_WIDTH ) | ps ;
}
/**
2020-07-02 14:21:40 -05:00
* fsl_asrc_set_ideal_ratio - Calculate and set the ratio for Ideal Ratio mode only
* @ pair : pointer to pair
* @ inrate : input rate
* @ outrate : output rate
2014-07-29 18:08:53 +08:00
*
* The ratio is a 32 - bit fixed point value with 26 fractional bits .
*/
static int fsl_asrc_set_ideal_ratio ( struct fsl_asrc_pair * pair ,
int inrate , int outrate )
{
2020-04-16 20:25:31 +08:00
struct fsl_asrc * asrc = pair - > asrc ;
2014-07-29 18:08:53 +08:00
enum asrc_pair_index index = pair - > index ;
unsigned long ratio ;
int i ;
if ( ! outrate ) {
pair_err ( " output rate should not be zero \n " ) ;
return - EINVAL ;
}
/* Calculate the intergal part of the ratio */
ratio = ( inrate / outrate ) < < IDEAL_RATIO_DECIMAL_DEPTH ;
/* ... and then the 26 depth decimal part */
inrate % = outrate ;
for ( i = 1 ; i < = IDEAL_RATIO_DECIMAL_DEPTH ; i + + ) {
inrate < < = 1 ;
if ( inrate < outrate )
continue ;
ratio | = 1 < < ( IDEAL_RATIO_DECIMAL_DEPTH - i ) ;
inrate - = outrate ;
if ( ! inrate )
break ;
}
2020-04-16 20:25:31 +08:00
regmap_write ( asrc - > regmap , REG_ASRIDRL ( index ) , ratio ) ;
regmap_write ( asrc - > regmap , REG_ASRIDRH ( index ) , ratio > > 24 ) ;
2014-07-29 18:08:53 +08:00
return 0 ;
}
/**
2020-07-02 14:21:40 -05:00
* fsl_asrc_config_pair - Configure the assigned ASRC pair
* @ pair : pointer to pair
* @ use_ideal_rate : boolean configuration
2014-07-29 18:08:53 +08:00
*
* It configures those ASRC registers according to a configuration instance
* of struct asrc_config which includes in / output sample rate , width , channel
* and clock settings .
2019-10-28 17:10:29 +08:00
*
* Note :
* The ideal ratio configuration can work with a flexible clock rate setting .
* Using IDEAL_RATIO_RATE gives a faster converting speed but overloads ASRC .
* For a regular audio playback , the clock rate should not be slower than an
* clock rate aligning with the output sample rate ; For a use case requiring
* faster conversion , set use_ideal_rate to have the faster speed .
2014-07-29 18:08:53 +08:00
*/
2019-10-28 17:10:29 +08:00
static int fsl_asrc_config_pair ( struct fsl_asrc_pair * pair , bool use_ideal_rate )
2014-07-29 18:08:53 +08:00
{
2020-04-16 20:25:35 +08:00
struct fsl_asrc_pair_priv * pair_priv = pair - > private ;
struct asrc_config * config = pair_priv - > config ;
2020-04-16 20:25:31 +08:00
struct fsl_asrc * asrc = pair - > asrc ;
2020-04-16 20:25:35 +08:00
struct fsl_asrc_priv * asrc_priv = asrc - > private ;
2014-07-29 18:08:53 +08:00
enum asrc_pair_index index = pair - > index ;
2019-09-27 09:46:09 +08:00
enum asrc_word_width input_word_width ;
enum asrc_word_width output_word_width ;
2014-08-04 12:19:49 +08:00
u32 inrate , outrate , indiv , outdiv ;
2019-10-28 17:10:29 +08:00
u32 clk_index [ 2 ] , div [ 2 ] , rem [ 2 ] ;
u64 clk_rate ;
2014-07-29 18:08:53 +08:00
int in , out , channels ;
2019-05-15 06:42:22 +00:00
int pre_proc , post_proc ;
2014-07-29 18:08:53 +08:00
struct clk * clk ;
2014-08-04 12:19:49 +08:00
bool ideal ;
2014-07-29 18:08:53 +08:00
if ( ! config ) {
pair_err ( " invalid pair config \n " ) ;
return - EINVAL ;
}
/* Validate channels */
if ( config - > channel_num < 1 | | config - > channel_num > 10 ) {
pair_err ( " does not support %d channels \n " , config - > channel_num ) ;
return - EINVAL ;
}
2019-09-27 09:46:09 +08:00
switch ( snd_pcm_format_width ( config - > input_format ) ) {
case 8 :
input_word_width = ASRC_WIDTH_8_BIT ;
break ;
case 16 :
input_word_width = ASRC_WIDTH_16_BIT ;
break ;
case 24 :
input_word_width = ASRC_WIDTH_24_BIT ;
break ;
default :
pair_err ( " does not support this input format, %d \n " ,
config - > input_format ) ;
return - EINVAL ;
}
switch ( snd_pcm_format_width ( config - > output_format ) ) {
case 16 :
output_word_width = ASRC_WIDTH_16_BIT ;
break ;
case 24 :
output_word_width = ASRC_WIDTH_24_BIT ;
break ;
default :
pair_err ( " does not support this output format, %d \n " ,
config - > output_format ) ;
2014-07-29 18:08:53 +08:00
return - EINVAL ;
}
2014-08-04 12:19:49 +08:00
inrate = config - > input_sample_rate ;
outrate = config - > output_sample_rate ;
ideal = config - > inclk = = INCLK_NONE ;
2014-07-29 18:08:53 +08:00
/* Validate input and output sample rates */
2019-05-15 06:42:26 +00:00
for ( in = 0 ; in < ARRAY_SIZE ( supported_asrc_rate ) ; in + + )
if ( inrate = = supported_asrc_rate [ in ] )
2014-07-29 18:08:53 +08:00
break ;
2019-05-15 06:42:26 +00:00
if ( in = = ARRAY_SIZE ( supported_asrc_rate ) ) {
2014-07-29 18:08:53 +08:00
pair_err ( " unsupported input sample rate: %dHz \n " , inrate ) ;
return - EINVAL ;
}
for ( out = 0 ; out < ARRAY_SIZE ( supported_asrc_rate ) ; out + + )
if ( outrate = = supported_asrc_rate [ out ] )
break ;
if ( out = = ARRAY_SIZE ( supported_asrc_rate ) ) {
pair_err ( " unsupported output sample rate: %dHz \n " , outrate ) ;
return - EINVAL ;
}
2019-05-15 06:42:26 +00:00
if ( ( outrate > = 5512 & & outrate < = 30000 ) & &
2019-05-15 06:42:18 +00:00
( outrate > 24 * inrate | | inrate > 8 * outrate ) ) {
2015-12-18 17:00:09 +08:00
pair_err ( " exceed supported ratio range [1/24, 8] for \
inrate / outrate : % d / % d \ n " , inrate, outrate);
return - EINVAL ;
}
2014-07-29 18:08:53 +08:00
/* Validate input and output clock sources */
2020-04-16 20:25:35 +08:00
clk_index [ IN ] = asrc_priv - > clk_map [ IN ] [ config - > inclk ] ;
clk_index [ OUT ] = asrc_priv - > clk_map [ OUT ] [ config - > outclk ] ;
2014-07-29 18:08:53 +08:00
/* We only have output clock for ideal ratio mode */
2020-04-16 20:25:35 +08:00
clk = asrc_priv - > asrck_clk [ clk_index [ ideal ? OUT : IN ] ] ;
2014-07-29 18:08:53 +08:00
2019-10-28 17:10:29 +08:00
clk_rate = clk_get_rate ( clk ) ;
rem [ IN ] = do_div ( clk_rate , inrate ) ;
div [ IN ] = ( u32 ) clk_rate ;
/*
* The divider range is [ 1 , 1024 ] , defined by the hardware . For non -
* ideal ratio configuration , clock rate has to be strictly aligned
* with the sample rate . For ideal ratio configuration , clock rates
* only result in different converting speeds . So remainder does not
* matter , as long as we keep the divider within its valid range .
*/
if ( div [ IN ] = = 0 | | ( ! ideal & & ( div [ IN ] > 1024 | | rem [ IN ] ! = 0 ) ) ) {
2014-07-29 18:08:53 +08:00
pair_err ( " failed to support input sample rate %dHz by asrck_%x \n " ,
inrate , clk_index [ ideal ? OUT : IN ] ) ;
return - EINVAL ;
}
2019-10-28 17:10:29 +08:00
div [ IN ] = min_t ( u32 , 1024 , div [ IN ] ) ;
2014-07-29 18:08:53 +08:00
2020-04-16 20:25:35 +08:00
clk = asrc_priv - > asrck_clk [ clk_index [ OUT ] ] ;
2019-10-28 17:10:29 +08:00
clk_rate = clk_get_rate ( clk ) ;
if ( ideal & & use_ideal_rate )
rem [ OUT ] = do_div ( clk_rate , IDEAL_RATIO_RATE ) ;
2014-07-29 18:08:53 +08:00
else
2019-10-28 17:10:29 +08:00
rem [ OUT ] = do_div ( clk_rate , outrate ) ;
div [ OUT ] = clk_rate ;
2014-07-29 18:08:53 +08:00
2019-10-28 17:10:29 +08:00
/* Output divider has the same limitation as the input one */
if ( div [ OUT ] = = 0 | | ( ! ideal & & ( div [ OUT ] > 1024 | | rem [ OUT ] ! = 0 ) ) ) {
2014-07-29 18:08:53 +08:00
pair_err ( " failed to support output sample rate %dHz by asrck_%x \n " ,
outrate , clk_index [ OUT ] ) ;
return - EINVAL ;
}
2019-10-28 17:10:29 +08:00
div [ OUT ] = min_t ( u32 , 1024 , div [ OUT ] ) ;
2014-07-29 18:08:53 +08:00
/* Set the channel number */
channels = config - > channel_num ;
2020-04-16 20:25:35 +08:00
if ( asrc_priv - > soc - > channel_bits < 4 )
2014-07-29 18:08:53 +08:00
channels / = 2 ;
/* Update channels for current pair */
2020-04-16 20:25:31 +08:00
regmap_update_bits ( asrc - > regmap , REG_ASRCNCR ,
2020-04-16 20:25:35 +08:00
ASRCNCR_ANCi_MASK ( index , asrc_priv - > soc - > channel_bits ) ,
ASRCNCR_ANCi ( index , channels , asrc_priv - > soc - > channel_bits ) ) ;
2014-07-29 18:08:53 +08:00
/* Default setting: Automatic selection for processing mode */
2020-04-16 20:25:31 +08:00
regmap_update_bits ( asrc - > regmap , REG_ASRCTR ,
2014-07-29 18:08:53 +08:00
ASRCTR_ATSi_MASK ( index ) , ASRCTR_ATS ( index ) ) ;
2020-04-16 20:25:31 +08:00
regmap_update_bits ( asrc - > regmap , REG_ASRCTR ,
2014-07-29 18:08:53 +08:00
ASRCTR_USRi_MASK ( index ) , 0 ) ;
/* Set the input and output clock sources */
2020-04-16 20:25:31 +08:00
regmap_update_bits ( asrc - > regmap , REG_ASRCSR ,
2014-07-29 18:08:53 +08:00
ASRCSR_AICSi_MASK ( index ) | ASRCSR_AOCSi_MASK ( index ) ,
ASRCSR_AICS ( index , clk_index [ IN ] ) |
ASRCSR_AOCS ( index , clk_index [ OUT ] ) ) ;
/* Calculate the input clock divisors */
indiv = fsl_asrc_cal_asrck_divisor ( pair , div [ IN ] ) ;
outdiv = fsl_asrc_cal_asrck_divisor ( pair , div [ OUT ] ) ;
/* Suppose indiv and outdiv includes prescaler, so add its MASK too */
2020-04-16 20:25:31 +08:00
regmap_update_bits ( asrc - > regmap , REG_ASRCDR ( index ) ,
2014-07-29 18:08:53 +08:00
ASRCDRi_AOCPi_MASK ( index ) | ASRCDRi_AICPi_MASK ( index ) |
ASRCDRi_AOCDi_MASK ( index ) | ASRCDRi_AICDi_MASK ( index ) ,
ASRCDRi_AOCP ( index , outdiv ) | ASRCDRi_AICP ( index , indiv ) ) ;
/* Implement word_width configurations */
2020-04-16 20:25:31 +08:00
regmap_update_bits ( asrc - > regmap , REG_ASRMCR1 ( index ) ,
2014-07-29 18:08:53 +08:00
ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK ,
2019-09-27 09:46:09 +08:00
ASRMCR1i_OW16 ( output_word_width ) |
ASRMCR1i_IWD ( input_word_width ) ) ;
2014-07-29 18:08:53 +08:00
/* Enable BUFFER STALL */
2020-04-16 20:25:31 +08:00
regmap_update_bits ( asrc - > regmap , REG_ASRMCR ( index ) ,
2014-07-29 18:08:53 +08:00
ASRMCRi_BUFSTALLi_MASK , ASRMCRi_BUFSTALLi ) ;
/* Set default thresholds for input and output FIFO */
fsl_asrc_set_watermarks ( pair , ASRC_INPUTFIFO_THRESHOLD ,
ASRC_INPUTFIFO_THRESHOLD ) ;
2017-02-27 14:29:56 -08:00
/* Configure the following only for Ideal Ratio mode */
2014-07-29 18:08:53 +08:00
if ( ! ideal )
return 0 ;
/* Clear ASTSx bit to use Ideal Ratio mode */
2020-04-16 20:25:31 +08:00
regmap_update_bits ( asrc - > regmap , REG_ASRCTR ,
2014-07-29 18:08:53 +08:00
ASRCTR_ATSi_MASK ( index ) , 0 ) ;
/* Enable Ideal Ratio mode */
2020-04-16 20:25:31 +08:00
regmap_update_bits ( asrc - > regmap , REG_ASRCTR ,
2014-07-29 18:08:53 +08:00
ASRCTR_IDRi_MASK ( index ) | ASRCTR_USRi_MASK ( index ) ,
ASRCTR_IDR ( index ) | ASRCTR_USR ( index ) ) ;
2019-05-15 06:42:22 +00:00
fsl_asrc_sel_proc ( inrate , outrate , & pre_proc , & post_proc ) ;
2014-07-29 18:08:53 +08:00
/* Apply configurations for pre- and post-processing */
2020-04-16 20:25:31 +08:00
regmap_update_bits ( asrc - > regmap , REG_ASRCFG ,
2014-07-29 18:08:53 +08:00
ASRCFG_PREMODi_MASK ( index ) | ASRCFG_POSTMODi_MASK ( index ) ,
2019-05-15 06:42:22 +00:00
ASRCFG_PREMOD ( index , pre_proc ) |
ASRCFG_POSTMOD ( index , post_proc ) ) ;
2014-07-29 18:08:53 +08:00
return fsl_asrc_set_ideal_ratio ( pair , inrate , outrate ) ;
}
/**
2020-07-02 14:21:40 -05:00
* fsl_asrc_start_pair - Start the assigned ASRC pair
* @ pair : pointer to pair
2014-07-29 18:08:53 +08:00
*
* It enables the assigned pair and makes it stopped at the stall level .
*/
static void fsl_asrc_start_pair ( struct fsl_asrc_pair * pair )
{
2020-04-16 20:25:31 +08:00
struct fsl_asrc * asrc = pair - > asrc ;
2014-07-29 18:08:53 +08:00
enum asrc_pair_index index = pair - > index ;
int reg , retry = 10 , i ;
/* Enable the current pair */
2020-04-16 20:25:31 +08:00
regmap_update_bits ( asrc - > regmap , REG_ASRCTR ,
2014-07-29 18:08:53 +08:00
ASRCTR_ASRCEi_MASK ( index ) , ASRCTR_ASRCE ( index ) ) ;
/* Wait for status of initialization */
do {
udelay ( 5 ) ;
2020-04-16 20:25:31 +08:00
regmap_read ( asrc - > regmap , REG_ASRCFG , & reg ) ;
2014-07-29 18:08:53 +08:00
reg & = ASRCFG_INIRQi_MASK ( index ) ;
} while ( ! reg & & - - retry ) ;
/* Make the input fifo to ASRC STALL level */
2020-04-16 20:25:31 +08:00
regmap_read ( asrc - > regmap , REG_ASRCNCR , & reg ) ;
2014-07-29 18:08:53 +08:00
for ( i = 0 ; i < pair - > channels * 4 ; i + + )
2020-04-16 20:25:31 +08:00
regmap_write ( asrc - > regmap , REG_ASRDI ( index ) , 0 ) ;
2014-07-29 18:08:53 +08:00
/* Enable overload interrupt */
2020-04-16 20:25:31 +08:00
regmap_write ( asrc - > regmap , REG_ASRIER , ASRIER_AOLIE ) ;
2014-07-29 18:08:53 +08:00
}
/**
2020-07-02 14:21:40 -05:00
* fsl_asrc_stop_pair - Stop the assigned ASRC pair
* @ pair : pointer to pair
2014-07-29 18:08:53 +08:00
*/
static void fsl_asrc_stop_pair ( struct fsl_asrc_pair * pair )
{
2020-04-16 20:25:31 +08:00
struct fsl_asrc * asrc = pair - > asrc ;
2014-07-29 18:08:53 +08:00
enum asrc_pair_index index = pair - > index ;
/* Stop the current pair */
2020-04-16 20:25:31 +08:00
regmap_update_bits ( asrc - > regmap , REG_ASRCTR ,
2014-07-29 18:08:53 +08:00
ASRCTR_ASRCEi_MASK ( index ) , 0 ) ;
}
/**
2020-07-02 14:21:40 -05:00
* fsl_asrc_get_dma_channel - Get DMA channel according to the pair and direction .
* @ pair : pointer to pair
* @ dir : DMA direction
2014-07-29 18:08:53 +08:00
*/
2020-05-25 17:03:32 +08:00
static struct dma_chan * fsl_asrc_get_dma_channel ( struct fsl_asrc_pair * pair ,
bool dir )
2014-07-29 18:08:53 +08:00
{
2020-04-16 20:25:31 +08:00
struct fsl_asrc * asrc = pair - > asrc ;
2014-07-29 18:08:53 +08:00
enum asrc_pair_index index = pair - > index ;
char name [ 4 ] ;
sprintf ( name , " %cx%c " , dir = = IN ? ' r ' : ' t ' , index + ' a ' ) ;
2020-04-16 20:25:31 +08:00
return dma_request_slave_channel ( & asrc - > pdev - > dev , name ) ;
2014-07-29 18:08:53 +08:00
}
2019-03-02 05:52:19 +00:00
static int fsl_asrc_dai_startup ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
2020-04-16 20:25:31 +08:00
struct fsl_asrc * asrc = snd_soc_dai_get_drvdata ( dai ) ;
2020-04-16 20:25:35 +08:00
struct fsl_asrc_priv * asrc_priv = asrc - > private ;
2019-03-02 05:52:19 +00:00
/* Odd channel number is not valid for older ASRC (channel_bits==3) */
2020-04-16 20:25:35 +08:00
if ( asrc_priv - > soc - > channel_bits = = 3 )
2019-03-02 05:52:19 +00:00
snd_pcm_hw_constraint_step ( substream - > runtime , 0 ,
SNDRV_PCM_HW_PARAM_CHANNELS , 2 ) ;
2019-05-15 06:42:26 +00:00
return snd_pcm_hw_constraint_list ( substream - > runtime , 0 ,
SNDRV_PCM_HW_PARAM_RATE , & fsl_asrc_rate_constraints ) ;
2019-03-02 05:52:19 +00:00
}
2020-06-30 21:56:07 +08:00
/* Select proper clock source for internal ratio mode */
static void fsl_asrc_select_clk ( struct fsl_asrc_priv * asrc_priv ,
struct fsl_asrc_pair * pair ,
int in_rate ,
int out_rate )
{
struct fsl_asrc_pair_priv * pair_priv = pair - > private ;
struct asrc_config * config = pair_priv - > config ;
int rate [ 2 ] , select_clk [ 2 ] ; /* Array size 2 means IN and OUT */
int clk_rate , clk_index ;
2021-02-19 17:29:29 -06:00
int i , j ;
2020-06-30 21:56:07 +08:00
rate [ IN ] = in_rate ;
rate [ OUT ] = out_rate ;
/* Select proper clock source for internal ratio mode */
for ( j = 0 ; j < 2 ; j + + ) {
for ( i = 0 ; i < ASRC_CLK_MAP_LEN ; i + + ) {
clk_index = asrc_priv - > clk_map [ j ] [ i ] ;
clk_rate = clk_get_rate ( asrc_priv - > asrck_clk [ clk_index ] ) ;
/* Only match a perfect clock source with no remainder */
if ( clk_rate ! = 0 & & ( clk_rate / rate [ j ] ) < = 1024 & &
( clk_rate % rate [ j ] ) = = 0 )
break ;
}
select_clk [ j ] = i ;
}
/* Switch to ideal ratio mode if there is no proper clock source */
if ( select_clk [ IN ] = = ASRC_CLK_MAP_LEN | | select_clk [ OUT ] = = ASRC_CLK_MAP_LEN ) {
select_clk [ IN ] = INCLK_NONE ;
select_clk [ OUT ] = OUTCLK_ASRCK1_CLK ;
}
config - > inclk = select_clk [ IN ] ;
config - > outclk = select_clk [ OUT ] ;
}
2014-07-29 18:08:53 +08:00
static int fsl_asrc_dai_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
2020-04-16 20:25:31 +08:00
struct fsl_asrc * asrc = snd_soc_dai_get_drvdata ( dai ) ;
2020-06-30 21:56:07 +08:00
struct fsl_asrc_priv * asrc_priv = asrc - > private ;
2014-07-29 18:08:53 +08:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct fsl_asrc_pair * pair = runtime - > private_data ;
2020-04-16 20:25:35 +08:00
struct fsl_asrc_pair_priv * pair_priv = pair - > private ;
2014-07-29 18:08:53 +08:00
unsigned int channels = params_channels ( params ) ;
unsigned int rate = params_rate ( params ) ;
struct asrc_config config ;
2019-09-27 09:46:09 +08:00
int ret ;
2014-07-29 18:08:53 +08:00
ret = fsl_asrc_request_pair ( channels , pair ) ;
if ( ret ) {
dev_err ( dai - > dev , " fail to request asrc pair \n " ) ;
return ret ;
}
2020-04-16 20:25:35 +08:00
pair_priv - > config = & config ;
2014-07-29 18:08:53 +08:00
config . pair = pair - > index ;
config . channel_num = channels ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
2019-09-27 09:46:09 +08:00
config . input_format = params_format ( params ) ;
2020-04-16 20:25:34 +08:00
config . output_format = asrc - > asrc_format ;
2014-07-29 18:08:53 +08:00
config . input_sample_rate = rate ;
2020-04-16 20:25:31 +08:00
config . output_sample_rate = asrc - > asrc_rate ;
2014-07-29 18:08:53 +08:00
} else {
2020-04-16 20:25:34 +08:00
config . input_format = asrc - > asrc_format ;
2019-09-27 09:46:09 +08:00
config . output_format = params_format ( params ) ;
2020-04-16 20:25:31 +08:00
config . input_sample_rate = asrc - > asrc_rate ;
2014-07-29 18:08:53 +08:00
config . output_sample_rate = rate ;
}
2020-06-30 21:56:07 +08:00
fsl_asrc_select_clk ( asrc_priv , pair ,
config . input_sample_rate ,
config . output_sample_rate ) ;
2019-10-28 17:10:29 +08:00
ret = fsl_asrc_config_pair ( pair , false ) ;
2014-07-29 18:08:53 +08:00
if ( ret ) {
dev_err ( dai - > dev , " fail to config asrc pair \n " ) ;
return ret ;
}
return 0 ;
}
static int fsl_asrc_dai_hw_free ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct fsl_asrc_pair * pair = runtime - > private_data ;
if ( pair )
fsl_asrc_release_pair ( pair ) ;
return 0 ;
}
static int fsl_asrc_dai_trigger ( struct snd_pcm_substream * substream , int cmd ,
struct snd_soc_dai * dai )
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct fsl_asrc_pair * pair = runtime - > private_data ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
fsl_asrc_start_pair ( pair ) ;
break ;
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
fsl_asrc_stop_pair ( pair ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
2017-07-13 02:23:51 -05:00
static const struct snd_soc_dai_ops fsl_asrc_dai_ops = {
2019-03-02 05:52:19 +00:00
. startup = fsl_asrc_dai_startup ,
2014-07-29 18:08:53 +08:00
. hw_params = fsl_asrc_dai_hw_params ,
. hw_free = fsl_asrc_dai_hw_free ,
. trigger = fsl_asrc_dai_trigger ,
} ;
static int fsl_asrc_dai_probe ( struct snd_soc_dai * dai )
{
2020-04-16 20:25:31 +08:00
struct fsl_asrc * asrc = snd_soc_dai_get_drvdata ( dai ) ;
2014-07-29 18:08:53 +08:00
2020-04-16 20:25:31 +08:00
snd_soc_dai_init_dma_data ( dai , & asrc - > dma_params_tx ,
& asrc - > dma_params_rx ) ;
2014-07-29 18:08:53 +08:00
return 0 ;
}
# define FSL_ASRC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S16_LE | \
2019-09-27 09:46:10 +08:00
SNDRV_PCM_FMTBIT_S24_3LE )
2014-07-29 18:08:53 +08:00
static struct snd_soc_dai_driver fsl_asrc_dai = {
. probe = fsl_asrc_dai_probe ,
. playback = {
. stream_name = " ASRC-Playback " ,
. channels_min = 1 ,
. channels_max = 10 ,
2019-05-15 06:42:26 +00:00
. rate_min = 5512 ,
. rate_max = 192000 ,
. rates = SNDRV_PCM_RATE_KNOT ,
2019-09-27 09:46:10 +08:00
. formats = FSL_ASRC_FORMATS |
SNDRV_PCM_FMTBIT_S8 ,
2014-07-29 18:08:53 +08:00
} ,
. capture = {
. stream_name = " ASRC-Capture " ,
. channels_min = 1 ,
. channels_max = 10 ,
2019-05-15 06:42:26 +00:00
. rate_min = 5512 ,
. rate_max = 192000 ,
. rates = SNDRV_PCM_RATE_KNOT ,
2014-07-29 18:08:53 +08:00
. formats = FSL_ASRC_FORMATS ,
} ,
. ops = & fsl_asrc_dai_ops ,
} ;
static bool fsl_asrc_readable_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case REG_ASRCTR :
case REG_ASRIER :
case REG_ASRCNCR :
case REG_ASRCFG :
case REG_ASRCSR :
case REG_ASRCDR1 :
case REG_ASRCDR2 :
case REG_ASRSTR :
case REG_ASRPM1 :
case REG_ASRPM2 :
case REG_ASRPM3 :
case REG_ASRPM4 :
case REG_ASRPM5 :
case REG_ASRTFR1 :
case REG_ASRCCR :
case REG_ASRDOA :
case REG_ASRDOB :
case REG_ASRDOC :
case REG_ASRIDRHA :
case REG_ASRIDRLA :
case REG_ASRIDRHB :
case REG_ASRIDRLB :
case REG_ASRIDRHC :
case REG_ASRIDRLC :
case REG_ASR76K :
case REG_ASR56K :
case REG_ASRMCRA :
case REG_ASRFSTA :
case REG_ASRMCRB :
case REG_ASRFSTB :
case REG_ASRMCRC :
case REG_ASRFSTC :
case REG_ASRMCR1A :
case REG_ASRMCR1B :
case REG_ASRMCR1C :
return true ;
default :
return false ;
}
}
static bool fsl_asrc_volatile_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case REG_ASRSTR :
case REG_ASRDIA :
case REG_ASRDIB :
case REG_ASRDIC :
case REG_ASRDOA :
case REG_ASRDOB :
case REG_ASRDOC :
case REG_ASRFSTA :
case REG_ASRFSTB :
case REG_ASRFSTC :
case REG_ASRCFG :
return true ;
default :
return false ;
}
}
static bool fsl_asrc_writeable_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case REG_ASRCTR :
case REG_ASRIER :
case REG_ASRCNCR :
case REG_ASRCFG :
case REG_ASRCSR :
case REG_ASRCDR1 :
case REG_ASRCDR2 :
case REG_ASRSTR :
case REG_ASRPM1 :
case REG_ASRPM2 :
case REG_ASRPM3 :
case REG_ASRPM4 :
case REG_ASRPM5 :
case REG_ASRTFR1 :
case REG_ASRCCR :
case REG_ASRDIA :
case REG_ASRDIB :
case REG_ASRDIC :
case REG_ASRIDRHA :
case REG_ASRIDRLA :
case REG_ASRIDRHB :
case REG_ASRIDRLB :
case REG_ASRIDRHC :
case REG_ASRIDRLC :
case REG_ASR76K :
case REG_ASR56K :
case REG_ASRMCRA :
case REG_ASRMCRB :
case REG_ASRMCRC :
case REG_ASRMCR1A :
case REG_ASRMCR1B :
case REG_ASRMCR1C :
return true ;
default :
return false ;
}
}
2014-10-24 19:03:57 -07:00
static struct reg_default fsl_asrc_reg [ ] = {
{ REG_ASRCTR , 0x0000 } , { REG_ASRIER , 0x0000 } ,
{ REG_ASRCNCR , 0x0000 } , { REG_ASRCFG , 0x0000 } ,
{ REG_ASRCSR , 0x0000 } , { REG_ASRCDR1 , 0x0000 } ,
{ REG_ASRCDR2 , 0x0000 } , { REG_ASRSTR , 0x0000 } ,
{ REG_ASRRA , 0x0000 } , { REG_ASRRB , 0x0000 } ,
{ REG_ASRRC , 0x0000 } , { REG_ASRPM1 , 0x0000 } ,
{ REG_ASRPM2 , 0x0000 } , { REG_ASRPM3 , 0x0000 } ,
{ REG_ASRPM4 , 0x0000 } , { REG_ASRPM5 , 0x0000 } ,
{ REG_ASRTFR1 , 0x0000 } , { REG_ASRCCR , 0x0000 } ,
{ REG_ASRDIA , 0x0000 } , { REG_ASRDOA , 0x0000 } ,
{ REG_ASRDIB , 0x0000 } , { REG_ASRDOB , 0x0000 } ,
{ REG_ASRDIC , 0x0000 } , { REG_ASRDOC , 0x0000 } ,
{ REG_ASRIDRHA , 0x0000 } , { REG_ASRIDRLA , 0x0000 } ,
{ REG_ASRIDRHB , 0x0000 } , { REG_ASRIDRLB , 0x0000 } ,
{ REG_ASRIDRHC , 0x0000 } , { REG_ASRIDRLC , 0x0000 } ,
{ REG_ASR76K , 0x0A47 } , { REG_ASR56K , 0x0DF3 } ,
{ REG_ASRMCRA , 0x0000 } , { REG_ASRFSTA , 0x0000 } ,
{ REG_ASRMCRB , 0x0000 } , { REG_ASRFSTB , 0x0000 } ,
{ REG_ASRMCRC , 0x0000 } , { REG_ASRFSTC , 0x0000 } ,
{ REG_ASRMCR1A , 0x0000 } , { REG_ASRMCR1B , 0x0000 } ,
{ REG_ASRMCR1C , 0x0000 } ,
} ;
2014-08-25 11:30:59 +08:00
static const struct regmap_config fsl_asrc_regmap_config = {
2014-07-29 18:08:53 +08:00
. reg_bits = 32 ,
. reg_stride = 4 ,
. val_bits = 32 ,
. max_register = REG_ASRMCR1C ,
2014-10-24 19:03:57 -07:00
. reg_defaults = fsl_asrc_reg ,
. num_reg_defaults = ARRAY_SIZE ( fsl_asrc_reg ) ,
2014-07-29 18:08:53 +08:00
. readable_reg = fsl_asrc_readable_reg ,
. volatile_reg = fsl_asrc_volatile_reg ,
. writeable_reg = fsl_asrc_writeable_reg ,
2016-09-19 21:30:25 +02:00
. cache_type = REGCACHE_FLAT ,
2014-07-29 18:08:53 +08:00
} ;
/**
2020-07-02 14:21:40 -05:00
* fsl_asrc_init - Initialize ASRC registers with a default configuration
* @ asrc : ASRC context
2014-07-29 18:08:53 +08:00
*/
2020-04-16 20:25:31 +08:00
static int fsl_asrc_init ( struct fsl_asrc * asrc )
2014-07-29 18:08:53 +08:00
{
2020-05-12 18:22:59 +08:00
unsigned long ipg_rate ;
2014-07-29 18:08:53 +08:00
/* Halt ASRC internal FP when input FIFO needs data for pair A, B, C */
2020-04-16 20:25:31 +08:00
regmap_write ( asrc - > regmap , REG_ASRCTR , ASRCTR_ASRCEN ) ;
2014-07-29 18:08:53 +08:00
/* Disable interrupt by default */
2020-04-16 20:25:31 +08:00
regmap_write ( asrc - > regmap , REG_ASRIER , 0x0 ) ;
2014-07-29 18:08:53 +08:00
/* Apply recommended settings for parameters from Reference Manual */
2020-04-16 20:25:31 +08:00
regmap_write ( asrc - > regmap , REG_ASRPM1 , 0x7fffff ) ;
regmap_write ( asrc - > regmap , REG_ASRPM2 , 0x255555 ) ;
regmap_write ( asrc - > regmap , REG_ASRPM3 , 0xff7280 ) ;
regmap_write ( asrc - > regmap , REG_ASRPM4 , 0xff7280 ) ;
regmap_write ( asrc - > regmap , REG_ASRPM5 , 0xff7280 ) ;
2014-07-29 18:08:53 +08:00
/* Base address for task queue FIFO. Set to 0x7C */
2020-04-16 20:25:31 +08:00
regmap_update_bits ( asrc - > regmap , REG_ASRTFR1 ,
2014-07-29 18:08:53 +08:00
ASRTFR1_TF_BASE_MASK , ASRTFR1_TF_BASE ( 0xfc ) ) ;
2020-05-12 18:22:59 +08:00
/*
* Set the period of the 76 KHz and 56 KHz sampling clocks based on
* the ASRC processing clock .
* On iMX6 , ipg_clk = 133 MHz , REG_ASR76K = 0x06D6 , REG_ASR56K = 0x0947
*/
ipg_rate = clk_get_rate ( asrc - > ipg_clk ) ;
regmap_write ( asrc - > regmap , REG_ASR76K , ipg_rate / 76000 ) ;
return regmap_write ( asrc - > regmap , REG_ASR56K , ipg_rate / 56000 ) ;
2014-07-29 18:08:53 +08:00
}
/**
2020-07-02 14:21:40 -05:00
* fsl_asrc_isr - Interrupt handler for ASRC
* @ irq : irq number
* @ dev_id : ASRC context
2014-07-29 18:08:53 +08:00
*/
static irqreturn_t fsl_asrc_isr ( int irq , void * dev_id )
{
2020-04-16 20:25:31 +08:00
struct fsl_asrc * asrc = ( struct fsl_asrc * ) dev_id ;
struct device * dev = & asrc - > pdev - > dev ;
2014-07-29 18:08:53 +08:00
enum asrc_pair_index index ;
u32 status ;
2020-04-16 20:25:31 +08:00
regmap_read ( asrc - > regmap , REG_ASRSTR , & status ) ;
2014-07-29 18:08:53 +08:00
/* Clean overload error */
2020-04-16 20:25:31 +08:00
regmap_write ( asrc - > regmap , REG_ASRSTR , ASRSTR_AOLE ) ;
2014-07-29 18:08:53 +08:00
/*
* We here use dev_dbg ( ) for all exceptions because ASRC itself does
* not care if FIFO overflowed or underrun while a warning in the
* interrupt would result a ridged conversion .
*/
for ( index = ASRC_PAIR_A ; index < ASRC_PAIR_MAX_NUM ; index + + ) {
2020-04-16 20:25:31 +08:00
if ( ! asrc - > pair [ index ] )
2014-07-29 18:08:53 +08:00
continue ;
if ( status & ASRSTR_ATQOL ) {
2020-04-16 20:25:31 +08:00
asrc - > pair [ index ] - > error | = ASRC_TASK_Q_OVERLOAD ;
2014-07-29 18:08:53 +08:00
dev_dbg ( dev , " ASRC Task Queue FIFO overload \n " ) ;
}
if ( status & ASRSTR_AOOL ( index ) ) {
2020-04-16 20:25:31 +08:00
asrc - > pair [ index ] - > error | = ASRC_OUTPUT_TASK_OVERLOAD ;
2014-07-29 18:08:53 +08:00
pair_dbg ( " Output Task Overload \n " ) ;
}
if ( status & ASRSTR_AIOL ( index ) ) {
2020-04-16 20:25:31 +08:00
asrc - > pair [ index ] - > error | = ASRC_INPUT_TASK_OVERLOAD ;
2014-07-29 18:08:53 +08:00
pair_dbg ( " Input Task Overload \n " ) ;
}
if ( status & ASRSTR_AODO ( index ) ) {
2020-04-16 20:25:31 +08:00
asrc - > pair [ index ] - > error | = ASRC_OUTPUT_BUFFER_OVERFLOW ;
2014-07-29 18:08:53 +08:00
pair_dbg ( " Output Data Buffer has overflowed \n " ) ;
}
if ( status & ASRSTR_AIDU ( index ) ) {
2020-04-16 20:25:31 +08:00
asrc - > pair [ index ] - > error | = ASRC_INPUT_BUFFER_UNDERRUN ;
2014-07-29 18:08:53 +08:00
pair_dbg ( " Input Data Buffer has underflowed \n " ) ;
}
}
return IRQ_HANDLED ;
}
2020-04-16 20:25:35 +08:00
static int fsl_asrc_get_fifo_addr ( u8 dir , enum asrc_pair_index index )
{
return REG_ASRDx ( dir , index ) ;
}
2021-03-24 17:58:45 +08:00
static int fsl_asrc_runtime_resume ( struct device * dev ) ;
static int fsl_asrc_runtime_suspend ( struct device * dev ) ;
2014-07-29 18:08:53 +08:00
static int fsl_asrc_probe ( struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
2020-04-16 20:25:35 +08:00
struct fsl_asrc_priv * asrc_priv ;
2020-04-16 20:25:31 +08:00
struct fsl_asrc * asrc ;
2014-07-29 18:08:53 +08:00
struct resource * res ;
void __iomem * regs ;
int irq , ret , i ;
2019-12-04 20:00:19 +08:00
u32 map_idx ;
2014-07-29 18:08:53 +08:00
char tmp [ 16 ] ;
2020-04-16 20:25:34 +08:00
u32 width ;
2014-07-29 18:08:53 +08:00
2020-04-16 20:25:31 +08:00
asrc = devm_kzalloc ( & pdev - > dev , sizeof ( * asrc ) , GFP_KERNEL ) ;
if ( ! asrc )
2014-07-29 18:08:53 +08:00
return - ENOMEM ;
2020-04-16 20:25:35 +08:00
asrc_priv = devm_kzalloc ( & pdev - > dev , sizeof ( * asrc_priv ) , GFP_KERNEL ) ;
if ( ! asrc_priv )
return - ENOMEM ;
2020-04-16 20:25:31 +08:00
asrc - > pdev = pdev ;
2020-04-16 20:25:35 +08:00
asrc - > private = asrc_priv ;
2014-07-29 18:08:53 +08:00
/* Get the addresses and IRQ */
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
regs = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( regs ) )
return PTR_ERR ( regs ) ;
2020-04-16 20:25:31 +08:00
asrc - > paddr = res - > start ;
2014-07-29 18:08:53 +08:00
2021-03-24 17:58:45 +08:00
asrc - > regmap = devm_regmap_init_mmio ( & pdev - > dev , regs , & fsl_asrc_regmap_config ) ;
2020-04-16 20:25:31 +08:00
if ( IS_ERR ( asrc - > regmap ) ) {
2014-07-29 18:08:53 +08:00
dev_err ( & pdev - > dev , " failed to init regmap \n " ) ;
2020-04-16 20:25:31 +08:00
return PTR_ERR ( asrc - > regmap ) ;
2014-07-29 18:08:53 +08:00
}
irq = platform_get_irq ( pdev , 0 ) ;
2019-07-30 11:15:49 -07:00
if ( irq < 0 )
2014-07-29 18:08:53 +08:00
return irq ;
ret = devm_request_irq ( & pdev - > dev , irq , fsl_asrc_isr , 0 ,
2020-04-16 20:25:31 +08:00
dev_name ( & pdev - > dev ) , asrc ) ;
2014-07-29 18:08:53 +08:00
if ( ret ) {
dev_err ( & pdev - > dev , " failed to claim irq %u: %d \n " , irq , ret ) ;
return ret ;
}
2020-04-16 20:25:31 +08:00
asrc - > mem_clk = devm_clk_get ( & pdev - > dev , " mem " ) ;
if ( IS_ERR ( asrc - > mem_clk ) ) {
2014-07-29 18:08:53 +08:00
dev_err ( & pdev - > dev , " failed to get mem clock \n " ) ;
2020-04-16 20:25:31 +08:00
return PTR_ERR ( asrc - > mem_clk ) ;
2014-07-29 18:08:53 +08:00
}
2020-04-16 20:25:31 +08:00
asrc - > ipg_clk = devm_clk_get ( & pdev - > dev , " ipg " ) ;
if ( IS_ERR ( asrc - > ipg_clk ) ) {
2014-07-29 18:08:53 +08:00
dev_err ( & pdev - > dev , " failed to get ipg clock \n " ) ;
2020-04-16 20:25:31 +08:00
return PTR_ERR ( asrc - > ipg_clk ) ;
2014-07-29 18:08:53 +08:00
}
2020-04-16 20:25:31 +08:00
asrc - > spba_clk = devm_clk_get ( & pdev - > dev , " spba " ) ;
if ( IS_ERR ( asrc - > spba_clk ) )
2015-11-24 17:19:34 +08:00
dev_warn ( & pdev - > dev , " failed to get spba clock \n " ) ;
2014-07-29 18:08:53 +08:00
for ( i = 0 ; i < ASRC_CLK_MAX_NUM ; i + + ) {
sprintf ( tmp , " asrck_%x " , i ) ;
2020-04-16 20:25:35 +08:00
asrc_priv - > asrck_clk [ i ] = devm_clk_get ( & pdev - > dev , tmp ) ;
if ( IS_ERR ( asrc_priv - > asrck_clk [ i ] ) ) {
2014-07-29 18:08:53 +08:00
dev_err ( & pdev - > dev , " failed to get %s clock \n " , tmp ) ;
2020-04-16 20:25:35 +08:00
return PTR_ERR ( asrc_priv - > asrck_clk [ i ] ) ;
2014-07-29 18:08:53 +08:00
}
}
2020-04-16 20:25:35 +08:00
asrc_priv - > soc = of_device_get_match_data ( & pdev - > dev ) ;
asrc - > use_edma = asrc_priv - > soc - > use_edma ;
asrc - > get_dma_channel = fsl_asrc_get_dma_channel ;
asrc - > request_pair = fsl_asrc_request_pair ;
asrc - > release_pair = fsl_asrc_release_pair ;
asrc - > get_fifo_addr = fsl_asrc_get_fifo_addr ;
asrc - > pair_priv_size = sizeof ( struct fsl_asrc_pair_priv ) ;
2016-08-19 10:31:00 -03:00
if ( of_device_is_compatible ( np , " fsl,imx35-asrc " ) ) {
2020-04-16 20:25:35 +08:00
asrc_priv - > clk_map [ IN ] = input_clk_map_imx35 ;
asrc_priv - > clk_map [ OUT ] = output_clk_map_imx35 ;
2019-12-04 20:00:19 +08:00
} else if ( of_device_is_compatible ( np , " fsl,imx53-asrc " ) ) {
2020-04-16 20:25:35 +08:00
asrc_priv - > clk_map [ IN ] = input_clk_map_imx53 ;
asrc_priv - > clk_map [ OUT ] = output_clk_map_imx53 ;
2019-12-04 20:00:19 +08:00
} else if ( of_device_is_compatible ( np , " fsl,imx8qm-asrc " ) | |
of_device_is_compatible ( np , " fsl,imx8qxp-asrc " ) ) {
ret = of_property_read_u32 ( np , " fsl,asrc-clk-map " , & map_idx ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to get clk map index \n " ) ;
return ret ;
}
if ( map_idx > 1 ) {
dev_err ( & pdev - > dev , " unsupported clk map index \n " ) ;
return - EINVAL ;
}
if ( of_device_is_compatible ( np , " fsl,imx8qm-asrc " ) ) {
2020-04-16 20:25:35 +08:00
asrc_priv - > clk_map [ IN ] = clk_map_imx8qm [ map_idx ] ;
asrc_priv - > clk_map [ OUT ] = clk_map_imx8qm [ map_idx ] ;
2019-12-04 20:00:19 +08:00
} else {
2020-04-16 20:25:35 +08:00
asrc_priv - > clk_map [ IN ] = clk_map_imx8qxp [ map_idx ] ;
asrc_priv - > clk_map [ OUT ] = clk_map_imx8qxp [ map_idx ] ;
2019-12-04 20:00:19 +08:00
}
2014-07-29 18:08:53 +08:00
}
2020-04-16 20:25:31 +08:00
asrc - > channel_avail = 10 ;
2014-07-29 18:08:53 +08:00
ret = of_property_read_u32 ( np , " fsl,asrc-rate " ,
2020-04-16 20:25:31 +08:00
& asrc - > asrc_rate ) ;
2014-07-29 18:08:53 +08:00
if ( ret ) {
dev_err ( & pdev - > dev , " failed to get output rate \n " ) ;
2016-08-19 10:30:59 -03:00
return ret ;
2014-07-29 18:08:53 +08:00
}
2020-04-16 20:25:34 +08:00
ret = of_property_read_u32 ( np , " fsl,asrc-format " , & asrc - > asrc_format ) ;
2014-07-29 18:08:53 +08:00
if ( ret ) {
2020-04-16 20:25:34 +08:00
ret = of_property_read_u32 ( np , " fsl,asrc-width " , & width ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to decide output format \n " ) ;
return ret ;
}
switch ( width ) {
case 16 :
asrc - > asrc_format = SNDRV_PCM_FORMAT_S16_LE ;
break ;
case 24 :
asrc - > asrc_format = SNDRV_PCM_FORMAT_S24_LE ;
break ;
default :
dev_warn ( & pdev - > dev ,
" unsupported width, use default S24_LE \n " ) ;
asrc - > asrc_format = SNDRV_PCM_FORMAT_S24_LE ;
break ;
}
2014-07-29 18:08:53 +08:00
}
2020-04-16 20:25:34 +08:00
if ( ! ( FSL_ASRC_FORMATS & ( 1ULL < < asrc - > asrc_format ) ) ) {
dev_warn ( & pdev - > dev , " unsupported width, use default S24_LE \n " ) ;
asrc - > asrc_format = SNDRV_PCM_FORMAT_S24_LE ;
2014-07-29 18:08:53 +08:00
}
2020-04-16 20:25:31 +08:00
platform_set_drvdata ( pdev , asrc ) ;
spin_lock_init ( & asrc - > lock ) ;
2021-03-24 17:58:45 +08:00
pm_runtime_enable ( & pdev - > dev ) ;
if ( ! pm_runtime_enabled ( & pdev - > dev ) ) {
ret = fsl_asrc_runtime_resume ( & pdev - > dev ) ;
if ( ret )
goto err_pm_disable ;
}
ret = pm_runtime_get_sync ( & pdev - > dev ) ;
if ( ret < 0 ) {
pm_runtime_put_noidle ( & pdev - > dev ) ;
goto err_pm_get_sync ;
}
ret = fsl_asrc_init ( asrc ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to init asrc %d \n " , ret ) ;
goto err_pm_get_sync ;
}
ret = pm_runtime_put_sync ( & pdev - > dev ) ;
if ( ret < 0 )
goto err_pm_get_sync ;
2014-07-29 18:08:53 +08:00
ret = devm_snd_soc_register_component ( & pdev - > dev , & fsl_asrc_component ,
& fsl_asrc_dai , 1 ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to register ASoC DAI \n " ) ;
2021-03-24 17:58:45 +08:00
goto err_pm_get_sync ;
2014-07-29 18:08:53 +08:00
}
return 0 ;
2021-03-24 17:58:45 +08:00
err_pm_get_sync :
if ( ! pm_runtime_status_suspended ( & pdev - > dev ) )
fsl_asrc_runtime_suspend ( & pdev - > dev ) ;
err_pm_disable :
pm_runtime_disable ( & pdev - > dev ) ;
return ret ;
}
static int fsl_asrc_remove ( struct platform_device * pdev )
{
pm_runtime_disable ( & pdev - > dev ) ;
if ( ! pm_runtime_status_suspended ( & pdev - > dev ) )
fsl_asrc_runtime_suspend ( & pdev - > dev ) ;
return 0 ;
2014-07-29 18:08:53 +08:00
}
static int fsl_asrc_runtime_resume ( struct device * dev )
{
2020-04-16 20:25:31 +08:00
struct fsl_asrc * asrc = dev_get_drvdata ( dev ) ;
2020-04-16 20:25:35 +08:00
struct fsl_asrc_priv * asrc_priv = asrc - > private ;
2015-06-20 18:00:13 -03:00
int i , ret ;
2020-05-22 17:57:24 +08:00
u32 asrctr ;
2014-07-29 18:08:53 +08:00
2020-04-16 20:25:31 +08:00
ret = clk_prepare_enable ( asrc - > mem_clk ) ;
2015-06-20 18:00:13 -03:00
if ( ret )
return ret ;
2020-04-16 20:25:31 +08:00
ret = clk_prepare_enable ( asrc - > ipg_clk ) ;
2015-06-20 18:00:13 -03:00
if ( ret )
goto disable_mem_clk ;
2020-04-16 20:25:31 +08:00
if ( ! IS_ERR ( asrc - > spba_clk ) ) {
ret = clk_prepare_enable ( asrc - > spba_clk ) ;
2015-11-24 17:19:34 +08:00
if ( ret )
goto disable_ipg_clk ;
}
2015-06-20 18:00:13 -03:00
for ( i = 0 ; i < ASRC_CLK_MAX_NUM ; i + + ) {
2020-04-16 20:25:35 +08:00
ret = clk_prepare_enable ( asrc_priv - > asrck_clk [ i ] ) ;
2015-06-20 18:00:13 -03:00
if ( ret )
goto disable_asrck_clk ;
}
2014-07-29 18:08:53 +08:00
2020-05-22 17:57:24 +08:00
/* Stop all pairs provisionally */
regmap_read ( asrc - > regmap , REG_ASRCTR , & asrctr ) ;
regmap_update_bits ( asrc - > regmap , REG_ASRCTR ,
ASRCTR_ASRCEi_ALL_MASK , 0 ) ;
/* Restore all registers */
regcache_cache_only ( asrc - > regmap , false ) ;
regcache_mark_dirty ( asrc - > regmap ) ;
regcache_sync ( asrc - > regmap ) ;
regmap_update_bits ( asrc - > regmap , REG_ASRCFG ,
ASRCFG_NDPRi_ALL_MASK | ASRCFG_POSTMODi_ALL_MASK |
ASRCFG_PREMODi_ALL_MASK , asrc_priv - > regcache_cfg ) ;
/* Restart enabled pairs */
regmap_update_bits ( asrc - > regmap , REG_ASRCTR ,
ASRCTR_ASRCEi_ALL_MASK , asrctr ) ;
2014-07-29 18:08:53 +08:00
return 0 ;
2015-06-20 18:00:13 -03:00
disable_asrck_clk :
for ( i - - ; i > = 0 ; i - - )
2020-04-16 20:25:35 +08:00
clk_disable_unprepare ( asrc_priv - > asrck_clk [ i ] ) ;
2020-04-16 20:25:31 +08:00
if ( ! IS_ERR ( asrc - > spba_clk ) )
clk_disable_unprepare ( asrc - > spba_clk ) ;
2015-11-24 17:19:34 +08:00
disable_ipg_clk :
2020-04-16 20:25:31 +08:00
clk_disable_unprepare ( asrc - > ipg_clk ) ;
2015-06-20 18:00:13 -03:00
disable_mem_clk :
2020-04-16 20:25:31 +08:00
clk_disable_unprepare ( asrc - > mem_clk ) ;
2015-06-20 18:00:13 -03:00
return ret ;
2014-07-29 18:08:53 +08:00
}
static int fsl_asrc_runtime_suspend ( struct device * dev )
{
2020-04-16 20:25:31 +08:00
struct fsl_asrc * asrc = dev_get_drvdata ( dev ) ;
2020-04-16 20:25:35 +08:00
struct fsl_asrc_priv * asrc_priv = asrc - > private ;
2014-07-29 18:08:53 +08:00
int i ;
2020-05-22 17:57:24 +08:00
regmap_read ( asrc - > regmap , REG_ASRCFG ,
& asrc_priv - > regcache_cfg ) ;
regcache_cache_only ( asrc - > regmap , true ) ;
2014-07-29 18:08:53 +08:00
for ( i = 0 ; i < ASRC_CLK_MAX_NUM ; i + + )
2020-04-16 20:25:35 +08:00
clk_disable_unprepare ( asrc_priv - > asrck_clk [ i ] ) ;
2020-04-16 20:25:31 +08:00
if ( ! IS_ERR ( asrc - > spba_clk ) )
clk_disable_unprepare ( asrc - > spba_clk ) ;
clk_disable_unprepare ( asrc - > ipg_clk ) ;
clk_disable_unprepare ( asrc - > mem_clk ) ;
2014-07-29 18:08:53 +08:00
return 0 ;
}
static const struct dev_pm_ops fsl_asrc_pm = {
SET_RUNTIME_PM_OPS ( fsl_asrc_runtime_suspend , fsl_asrc_runtime_resume , NULL )
2020-05-22 17:57:24 +08:00
SET_SYSTEM_SLEEP_PM_OPS ( pm_runtime_force_suspend ,
pm_runtime_force_resume )
2014-07-29 18:08:53 +08:00
} ;
2019-12-04 20:00:19 +08:00
static const struct fsl_asrc_soc_data fsl_asrc_imx35_data = {
. use_edma = false ,
. channel_bits = 3 ,
} ;
static const struct fsl_asrc_soc_data fsl_asrc_imx53_data = {
. use_edma = false ,
. channel_bits = 4 ,
} ;
static const struct fsl_asrc_soc_data fsl_asrc_imx8qm_data = {
. use_edma = true ,
. channel_bits = 4 ,
} ;
static const struct fsl_asrc_soc_data fsl_asrc_imx8qxp_data = {
. use_edma = true ,
. channel_bits = 4 ,
} ;
2014-07-29 18:08:53 +08:00
static const struct of_device_id fsl_asrc_ids [ ] = {
2019-12-04 20:00:19 +08:00
{ . compatible = " fsl,imx35-asrc " , . data = & fsl_asrc_imx35_data } ,
{ . compatible = " fsl,imx53-asrc " , . data = & fsl_asrc_imx53_data } ,
{ . compatible = " fsl,imx8qm-asrc " , . data = & fsl_asrc_imx8qm_data } ,
{ . compatible = " fsl,imx8qxp-asrc " , . data = & fsl_asrc_imx8qxp_data } ,
2014-07-29 18:08:53 +08:00
{ }
} ;
MODULE_DEVICE_TABLE ( of , fsl_asrc_ids ) ;
static struct platform_driver fsl_asrc_driver = {
. probe = fsl_asrc_probe ,
2021-03-24 17:58:45 +08:00
. remove = fsl_asrc_remove ,
2014-07-29 18:08:53 +08:00
. driver = {
. name = " fsl-asrc " ,
. of_match_table = fsl_asrc_ids ,
. pm = & fsl_asrc_pm ,
} ,
} ;
module_platform_driver ( fsl_asrc_driver ) ;
MODULE_DESCRIPTION ( " Freescale ASRC ASoC driver " ) ;
MODULE_AUTHOR ( " Nicolin Chen <nicoleotsuka@gmail.com> " ) ;
MODULE_ALIAS ( " platform:fsl-asrc " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;