2013-12-17 11:24:38 +08:00
/*
* Freescale ALSA SoC Digital Audio Interface ( SAI ) driver .
*
2015-05-11 18:24:41 +08:00
* Copyright 2012 - 2015 Freescale Semiconductor , Inc .
2013-12-17 11:24:38 +08:00
*
* This program is free software , you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation , either version 2 of the License , or ( at your
* option ) any later version .
*
*/
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/dmaengine.h>
# include <linux/module.h>
# include <linux/of_address.h>
2014-02-08 14:38:28 +08:00
# include <linux/regmap.h>
2013-12-17 11:24:38 +08:00
# include <linux/slab.h>
2016-01-15 19:35:24 +08:00
# include <linux/time.h>
2013-12-17 11:24:38 +08:00
# include <sound/core.h>
# include <sound/dmaengine_pcm.h>
# include <sound/pcm_params.h>
2016-05-04 19:33:59 -03:00
# include <linux/mfd/syscon.h>
# include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
2013-12-17 11:24:38 +08:00
# include "fsl_sai.h"
2014-04-01 19:34:09 +08:00
# include "imx-pcm.h"
2013-12-17 11:24:38 +08:00
2014-03-27 19:06:59 +08:00
# define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\
FSL_SAI_CSR_FEIE )
2015-10-22 10:43:23 +02:00
static const unsigned int fsl_sai_rates [ ] = {
2015-05-11 18:24:43 +08:00
8000 , 11025 , 12000 , 16000 , 22050 ,
24000 , 32000 , 44100 , 48000 , 64000 ,
88200 , 96000 , 176400 , 192000
} ;
2015-10-22 10:43:23 +02:00
static const struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = {
2015-05-11 18:24:43 +08:00
. count = ARRAY_SIZE ( fsl_sai_rates ) ,
. list = fsl_sai_rates ,
} ;
2014-03-27 19:06:59 +08:00
static irqreturn_t fsl_sai_isr ( int irq , void * devid )
{
struct fsl_sai * sai = ( struct fsl_sai * ) devid ;
struct device * dev = & sai - > pdev - > dev ;
2014-03-28 19:39:25 +08:00
u32 flags , xcsr , mask ;
bool irq_none = true ;
/*
* Both IRQ status bits and IRQ mask bits are in the xCSR but
* different shifts . And we here create a mask only for those
* IRQs that we activated .
*/
2014-03-27 19:06:59 +08:00
mask = ( FSL_SAI_FLAGS > > FSL_SAI_CSR_xIE_SHIFT ) < < FSL_SAI_CSR_xF_SHIFT ;
/* Tx IRQ */
regmap_read ( sai - > regmap , FSL_SAI_TCSR , & xcsr ) ;
2014-03-28 19:39:25 +08:00
flags = xcsr & mask ;
if ( flags )
irq_none = false ;
else
goto irq_rx ;
2014-03-27 19:06:59 +08:00
2014-03-28 19:39:25 +08:00
if ( flags & FSL_SAI_CSR_WSF )
2014-03-27 19:06:59 +08:00
dev_dbg ( dev , " isr: Start of Tx word detected \n " ) ;
2014-03-28 19:39:25 +08:00
if ( flags & FSL_SAI_CSR_SEF )
2014-03-27 19:06:59 +08:00
dev_warn ( dev , " isr: Tx Frame sync error detected \n " ) ;
2014-03-28 19:39:25 +08:00
if ( flags & FSL_SAI_CSR_FEF ) {
2014-03-27 19:06:59 +08:00
dev_warn ( dev , " isr: Transmit underrun detected \n " ) ;
/* FIFO reset for safety */
xcsr | = FSL_SAI_CSR_FR ;
}
2014-03-28 19:39:25 +08:00
if ( flags & FSL_SAI_CSR_FWF )
2014-03-27 19:06:59 +08:00
dev_dbg ( dev , " isr: Enabled transmit FIFO is empty \n " ) ;
2014-03-28 19:39:25 +08:00
if ( flags & FSL_SAI_CSR_FRF )
2014-03-27 19:06:59 +08:00
dev_dbg ( dev , " isr: Transmit FIFO watermark has been reached \n " ) ;
2014-03-28 19:39:25 +08:00
flags & = FSL_SAI_CSR_xF_W_MASK ;
xcsr & = ~ FSL_SAI_CSR_xF_MASK ;
if ( flags )
regmap_write ( sai - > regmap , FSL_SAI_TCSR , flags | xcsr ) ;
2014-03-27 19:06:59 +08:00
2014-03-28 19:39:25 +08:00
irq_rx :
2014-03-27 19:06:59 +08:00
/* Rx IRQ */
regmap_read ( sai - > regmap , FSL_SAI_RCSR , & xcsr ) ;
2014-03-28 19:39:25 +08:00
flags = xcsr & mask ;
2014-03-27 19:06:59 +08:00
2014-03-28 19:39:25 +08:00
if ( flags )
irq_none = false ;
else
goto out ;
if ( flags & FSL_SAI_CSR_WSF )
2014-03-27 19:06:59 +08:00
dev_dbg ( dev , " isr: Start of Rx word detected \n " ) ;
2014-03-28 19:39:25 +08:00
if ( flags & FSL_SAI_CSR_SEF )
2014-03-27 19:06:59 +08:00
dev_warn ( dev , " isr: Rx Frame sync error detected \n " ) ;
2014-03-28 19:39:25 +08:00
if ( flags & FSL_SAI_CSR_FEF ) {
2014-03-27 19:06:59 +08:00
dev_warn ( dev , " isr: Receive overflow detected \n " ) ;
/* FIFO reset for safety */
xcsr | = FSL_SAI_CSR_FR ;
}
2014-03-28 19:39:25 +08:00
if ( flags & FSL_SAI_CSR_FWF )
2014-03-27 19:06:59 +08:00
dev_dbg ( dev , " isr: Enabled receive FIFO is full \n " ) ;
2014-03-28 19:39:25 +08:00
if ( flags & FSL_SAI_CSR_FRF )
2014-03-27 19:06:59 +08:00
dev_dbg ( dev , " isr: Receive FIFO watermark has been reached \n " ) ;
2014-03-28 19:39:25 +08:00
flags & = FSL_SAI_CSR_xF_W_MASK ;
xcsr & = ~ FSL_SAI_CSR_xF_MASK ;
2014-03-27 19:06:59 +08:00
2014-03-28 19:39:25 +08:00
if ( flags )
2014-07-17 21:21:38 +08:00
regmap_write ( sai - > regmap , FSL_SAI_RCSR , flags | xcsr ) ;
2014-03-28 19:39:25 +08:00
out :
if ( irq_none )
return IRQ_NONE ;
else
return IRQ_HANDLED ;
2014-03-27 19:06:59 +08:00
}
2015-11-24 15:31:54 +08:00
static int fsl_sai_set_dai_tdm_slot ( struct snd_soc_dai * cpu_dai , u32 tx_mask ,
u32 rx_mask , int slots , int slot_width )
{
struct fsl_sai * sai = snd_soc_dai_get_drvdata ( cpu_dai ) ;
sai - > slots = slots ;
sai - > slot_width = slot_width ;
return 0 ;
}
2013-12-17 11:24:38 +08:00
static int fsl_sai_set_dai_sysclk_tr ( struct snd_soc_dai * cpu_dai ,
int clk_id , unsigned int freq , int fsl_dir )
{
struct fsl_sai * sai = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2014-04-11 18:30:09 +08:00
bool tx = fsl_dir = = FSL_FMT_TRANSMITTER ;
u32 val_cr2 = 0 ;
2014-01-08 16:13:05 +08:00
2013-12-17 11:24:38 +08:00
switch ( clk_id ) {
case FSL_SAI_CLK_BUS :
val_cr2 | = FSL_SAI_CR2_MSEL_BUS ;
break ;
case FSL_SAI_CLK_MAST1 :
val_cr2 | = FSL_SAI_CR2_MSEL_MCLK1 ;
break ;
case FSL_SAI_CLK_MAST2 :
val_cr2 | = FSL_SAI_CR2_MSEL_MCLK2 ;
break ;
case FSL_SAI_CLK_MAST3 :
val_cr2 | = FSL_SAI_CR2_MSEL_MCLK3 ;
break ;
default :
return - EINVAL ;
}
2014-01-08 16:13:05 +08:00
2014-04-11 18:30:09 +08:00
regmap_update_bits ( sai - > regmap , FSL_SAI_xCR2 ( tx ) ,
FSL_SAI_CR2_MSEL_MASK , val_cr2 ) ;
2013-12-17 11:24:38 +08:00
return 0 ;
}
static int fsl_sai_set_dai_sysclk ( struct snd_soc_dai * cpu_dai ,
int clk_id , unsigned int freq , int dir )
{
2013-12-20 16:41:05 +08:00
int ret ;
2013-12-17 11:24:38 +08:00
if ( dir = = SND_SOC_CLOCK_IN )
return 0 ;
ret = fsl_sai_set_dai_sysclk_tr ( cpu_dai , clk_id , freq ,
FSL_FMT_TRANSMITTER ) ;
if ( ret ) {
2013-12-20 16:41:04 +08:00
dev_err ( cpu_dai - > dev , " Cannot set tx sysclk: %d \n " , ret ) ;
2014-02-08 14:38:28 +08:00
return ret ;
2013-12-17 11:24:38 +08:00
}
ret = fsl_sai_set_dai_sysclk_tr ( cpu_dai , clk_id , freq ,
FSL_FMT_RECEIVER ) ;
2014-02-08 14:38:28 +08:00
if ( ret )
2013-12-20 16:41:04 +08:00
dev_err ( cpu_dai - > dev , " Cannot set rx sysclk: %d \n " , ret ) ;
2013-12-17 11:24:38 +08:00
2013-12-20 16:41:00 +08:00
return ret ;
2013-12-17 11:24:38 +08:00
}
static int fsl_sai_set_dai_fmt_tr ( struct snd_soc_dai * cpu_dai ,
unsigned int fmt , int fsl_dir )
{
struct fsl_sai * sai = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2014-04-11 18:30:09 +08:00
bool tx = fsl_dir = = FSL_FMT_TRANSMITTER ;
u32 val_cr2 = 0 , val_cr4 = 0 ;
2013-12-17 11:24:38 +08:00
2014-08-29 15:12:12 +08:00
if ( ! sai - > is_lsb_first )
2013-12-31 15:33:22 +08:00
val_cr4 | = FSL_SAI_CR4_MF ;
2013-12-17 11:24:38 +08:00
2014-02-25 17:54:51 +08:00
/* DAI mode */
2013-12-17 11:24:38 +08:00
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_I2S :
2014-02-27 08:45:01 +08:00
/*
* Frame low , 1 clk before data , one word length for frame sync ,
* frame sync starts one serial clock cycle earlier ,
* that is , together with the last bit of the previous
* data word .
*/
2014-04-04 15:09:47 +08:00
val_cr2 | = FSL_SAI_CR2_BCP ;
2014-02-25 17:54:51 +08:00
val_cr4 | = FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP ;
break ;
case SND_SOC_DAIFMT_LEFT_J :
2014-02-27 08:45:01 +08:00
/*
* Frame high , one word length for frame sync ,
* frame sync asserts with the first bit of the frame .
*/
2014-04-04 15:09:47 +08:00
val_cr2 | = FSL_SAI_CR2_BCP ;
2013-12-17 11:24:38 +08:00
break ;
2014-02-27 08:45:01 +08:00
case SND_SOC_DAIFMT_DSP_A :
/*
* Frame high , 1 clk before data , one bit for frame sync ,
* frame sync starts one serial clock cycle earlier ,
* that is , together with the last bit of the previous
* data word .
*/
2014-04-04 15:09:47 +08:00
val_cr2 | = FSL_SAI_CR2_BCP ;
2014-02-27 08:45:01 +08:00
val_cr4 | = FSL_SAI_CR4_FSE ;
sai - > is_dsp_mode = true ;
break ;
case SND_SOC_DAIFMT_DSP_B :
/*
* Frame high , one bit for frame sync ,
* frame sync asserts with the first bit of the frame .
*/
2014-04-04 15:09:47 +08:00
val_cr2 | = FSL_SAI_CR2_BCP ;
2014-02-27 08:45:01 +08:00
sai - > is_dsp_mode = true ;
break ;
2014-02-25 17:54:51 +08:00
case SND_SOC_DAIFMT_RIGHT_J :
/* To be done */
2013-12-17 11:24:38 +08:00
default :
return - EINVAL ;
}
2014-02-25 17:54:51 +08:00
/* DAI clock inversion */
2013-12-17 11:24:38 +08:00
switch ( fmt & SND_SOC_DAIFMT_INV_MASK ) {
case SND_SOC_DAIFMT_IB_IF :
2014-02-25 17:54:51 +08:00
/* Invert both clocks */
val_cr2 ^ = FSL_SAI_CR2_BCP ;
val_cr4 ^ = FSL_SAI_CR4_FSP ;
2013-12-17 11:24:38 +08:00
break ;
case SND_SOC_DAIFMT_IB_NF :
2014-02-25 17:54:51 +08:00
/* Invert bit clock */
val_cr2 ^ = FSL_SAI_CR2_BCP ;
2013-12-17 11:24:38 +08:00
break ;
case SND_SOC_DAIFMT_NB_IF :
2014-02-25 17:54:51 +08:00
/* Invert frame clock */
val_cr4 ^ = FSL_SAI_CR4_FSP ;
2013-12-17 11:24:38 +08:00
break ;
case SND_SOC_DAIFMT_NB_NF :
2014-02-25 17:54:51 +08:00
/* Nothing to do for both normal cases */
2013-12-17 11:24:38 +08:00
break ;
default :
return - EINVAL ;
}
2014-02-25 17:54:51 +08:00
/* DAI clock master masks */
2013-12-17 11:24:38 +08:00
switch ( fmt & SND_SOC_DAIFMT_MASTER_MASK ) {
case SND_SOC_DAIFMT_CBS_CFS :
val_cr2 | = FSL_SAI_CR2_BCD_MSTR ;
val_cr4 | = FSL_SAI_CR4_FSD_MSTR ;
break ;
case SND_SOC_DAIFMT_CBM_CFM :
2015-05-11 18:24:41 +08:00
sai - > is_slave_mode = true ;
2013-12-17 11:24:38 +08:00
break ;
2014-02-25 17:54:51 +08:00
case SND_SOC_DAIFMT_CBS_CFM :
val_cr2 | = FSL_SAI_CR2_BCD_MSTR ;
break ;
case SND_SOC_DAIFMT_CBM_CFS :
val_cr4 | = FSL_SAI_CR4_FSD_MSTR ;
2015-05-11 18:24:41 +08:00
sai - > is_slave_mode = true ;
2014-02-25 17:54:51 +08:00
break ;
2013-12-17 11:24:38 +08:00
default :
return - EINVAL ;
}
2014-04-11 18:30:09 +08:00
regmap_update_bits ( sai - > regmap , FSL_SAI_xCR2 ( tx ) ,
FSL_SAI_CR2_BCP | FSL_SAI_CR2_BCD_MSTR , val_cr2 ) ;
regmap_update_bits ( sai - > regmap , FSL_SAI_xCR4 ( tx ) ,
FSL_SAI_CR4_MF | FSL_SAI_CR4_FSE |
FSL_SAI_CR4_FSP | FSL_SAI_CR4_FSD_MSTR , val_cr4 ) ;
2013-12-17 11:24:38 +08:00
return 0 ;
}
static int fsl_sai_set_dai_fmt ( struct snd_soc_dai * cpu_dai , unsigned int fmt )
{
2013-12-20 16:41:05 +08:00
int ret ;
2013-12-17 11:24:38 +08:00
ret = fsl_sai_set_dai_fmt_tr ( cpu_dai , fmt , FSL_FMT_TRANSMITTER ) ;
if ( ret ) {
2013-12-20 16:41:04 +08:00
dev_err ( cpu_dai - > dev , " Cannot set tx format: %d \n " , ret ) ;
2014-02-08 14:38:28 +08:00
return ret ;
2013-12-17 11:24:38 +08:00
}
ret = fsl_sai_set_dai_fmt_tr ( cpu_dai , fmt , FSL_FMT_RECEIVER ) ;
2014-02-08 14:38:28 +08:00
if ( ret )
2013-12-20 16:41:04 +08:00
dev_err ( cpu_dai - > dev , " Cannot set rx format: %d \n " , ret ) ;
2013-12-17 11:24:38 +08:00
2013-12-20 16:41:00 +08:00
return ret ;
2013-12-17 11:24:38 +08:00
}
2015-05-11 18:24:41 +08:00
static int fsl_sai_set_bclk ( struct snd_soc_dai * dai , bool tx , u32 freq )
{
struct fsl_sai * sai = snd_soc_dai_get_drvdata ( dai ) ;
unsigned long clk_rate ;
u32 savediv = 0 , ratio , savesub = freq ;
u32 id ;
int ret = 0 ;
/* Don't apply to slave mode */
if ( sai - > is_slave_mode )
return 0 ;
for ( id = 0 ; id < FSL_SAI_MCLK_MAX ; id + + ) {
clk_rate = clk_get_rate ( sai - > mclk_clk [ id ] ) ;
if ( ! clk_rate )
continue ;
ratio = clk_rate / freq ;
ret = clk_rate - ratio * freq ;
/*
* Drop the source that can not be
* divided into the required rate .
*/
if ( ret ! = 0 & & clk_rate / ret < 1000 )
continue ;
dev_dbg ( dai - > dev ,
" ratio %d for freq %dHz based on clock %ldHz \n " ,
ratio , freq , clk_rate ) ;
if ( ratio % 2 = = 0 & & ratio > = 2 & & ratio < = 512 )
ratio / = 2 ;
else
continue ;
if ( ret < savesub ) {
savediv = ratio ;
sai - > mclk_id [ tx ] = id ;
savesub = ret ;
}
if ( ret = = 0 )
break ;
}
if ( savediv = = 0 ) {
dev_err ( dai - > dev , " failed to derive required %cx rate: %d \n " ,
tx ? ' T ' : ' R ' , freq ) ;
return - EINVAL ;
}
2015-11-09 19:02:29 +08:00
/*
* 1 ) For Asynchronous mode , we must set RCR2 register for capture , and
* set TCR2 register for playback .
* 2 ) For Tx sync with Rx clock , we must set RCR2 register for playback
* and capture .
* 3 ) For Rx sync with Tx clock , we must set TCR2 register for playback
* and capture .
* 4 ) For Tx and Rx are both Synchronous with another SAI , we just
* ignore it .
*/
if ( ( sai - > synchronous [ TX ] & & ! sai - > synchronous [ RX ] ) | |
( ! tx & & ! sai - > synchronous [ RX ] ) ) {
2015-05-11 18:24:41 +08:00
regmap_update_bits ( sai - > regmap , FSL_SAI_RCR2 ,
FSL_SAI_CR2_MSEL_MASK ,
FSL_SAI_CR2_MSEL ( sai - > mclk_id [ tx ] ) ) ;
regmap_update_bits ( sai - > regmap , FSL_SAI_RCR2 ,
FSL_SAI_CR2_DIV_MASK , savediv - 1 ) ;
2015-11-09 19:02:29 +08:00
} else if ( ( sai - > synchronous [ RX ] & & ! sai - > synchronous [ TX ] ) | |
( tx & & ! sai - > synchronous [ TX ] ) ) {
2015-05-11 18:24:41 +08:00
regmap_update_bits ( sai - > regmap , FSL_SAI_TCR2 ,
FSL_SAI_CR2_MSEL_MASK ,
FSL_SAI_CR2_MSEL ( sai - > mclk_id [ tx ] ) ) ;
regmap_update_bits ( sai - > regmap , FSL_SAI_TCR2 ,
FSL_SAI_CR2_DIV_MASK , savediv - 1 ) ;
}
dev_dbg ( dai - > dev , " best fit: clock id=%d, div=%d, deviation =%d \n " ,
sai - > mclk_id [ tx ] , savediv , savesub ) ;
return 0 ;
}
2013-12-17 11:24:38 +08:00
static int fsl_sai_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * cpu_dai )
{
2013-12-20 16:41:05 +08:00
struct fsl_sai * sai = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2014-04-11 18:30:09 +08:00
bool tx = substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ;
2013-12-17 11:24:38 +08:00
unsigned int channels = params_channels ( params ) ;
2015-11-24 15:32:09 +08:00
u32 word_width = params_width ( params ) ;
2014-04-11 18:30:09 +08:00
u32 val_cr4 = 0 , val_cr5 = 0 ;
2015-11-24 15:31:54 +08:00
u32 slots = ( channels = = 1 ) ? 2 : channels ;
u32 slot_width = word_width ;
2015-05-11 18:24:41 +08:00
int ret ;
2015-11-24 15:31:54 +08:00
if ( sai - > slots )
slots = sai - > slots ;
if ( sai - > slot_width )
slot_width = sai - > slot_width ;
2015-05-11 18:24:41 +08:00
if ( ! sai - > is_slave_mode ) {
ret = fsl_sai_set_bclk ( cpu_dai , tx ,
2015-11-24 15:31:54 +08:00
slots * slot_width * params_rate ( params ) ) ;
2015-05-11 18:24:41 +08:00
if ( ret )
return ret ;
/* Do not enable the clock if it is already enabled */
if ( ! ( sai - > mclk_streams & BIT ( substream - > stream ) ) ) {
ret = clk_prepare_enable ( sai - > mclk_clk [ sai - > mclk_id [ tx ] ] ) ;
if ( ret )
return ret ;
sai - > mclk_streams | = BIT ( substream - > stream ) ;
}
}
2013-12-17 11:24:38 +08:00
2014-02-27 08:45:01 +08:00
if ( ! sai - > is_dsp_mode )
2015-11-24 15:31:54 +08:00
val_cr4 | = FSL_SAI_CR4_SYWD ( slot_width ) ;
2014-02-27 08:45:01 +08:00
2015-11-24 15:31:54 +08:00
val_cr5 | = FSL_SAI_CR5_WNW ( slot_width ) ;
val_cr5 | = FSL_SAI_CR5_W0W ( slot_width ) ;
2013-12-17 11:24:38 +08:00
2014-08-29 15:12:12 +08:00
if ( sai - > is_lsb_first )
2013-12-17 11:24:38 +08:00
val_cr5 | = FSL_SAI_CR5_FBT ( 0 ) ;
2013-12-31 15:33:22 +08:00
else
val_cr5 | = FSL_SAI_CR5_FBT ( word_width - 1 ) ;
2013-12-17 11:24:38 +08:00
2015-11-24 15:31:54 +08:00
val_cr4 | = FSL_SAI_CR4_FRSZ ( slots ) ;
2013-12-17 11:24:38 +08:00
2015-11-09 19:03:13 +08:00
/*
* For SAI master mode , when Tx ( Rx ) sync with Rx ( Tx ) clock , Rx ( Tx ) will
* generate bclk and frame clock for Tx ( Rx ) , we should set RCR4 ( TCR4 ) ,
* RCR5 ( TCR5 ) and RMR ( TMR ) for playback ( capture ) , or there will be sync
* error .
*/
if ( ! sai - > is_slave_mode ) {
if ( ! sai - > synchronous [ TX ] & & sai - > synchronous [ RX ] & & ! tx ) {
regmap_update_bits ( sai - > regmap , FSL_SAI_TCR4 ,
FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK ,
val_cr4 ) ;
regmap_update_bits ( sai - > regmap , FSL_SAI_TCR5 ,
FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
FSL_SAI_CR5_FBT_MASK , val_cr5 ) ;
regmap_write ( sai - > regmap , FSL_SAI_TMR ,
~ 0UL - ( ( 1 < < channels ) - 1 ) ) ;
} else if ( ! sai - > synchronous [ RX ] & & sai - > synchronous [ TX ] & & tx ) {
regmap_update_bits ( sai - > regmap , FSL_SAI_RCR4 ,
FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK ,
val_cr4 ) ;
regmap_update_bits ( sai - > regmap , FSL_SAI_RCR5 ,
FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
FSL_SAI_CR5_FBT_MASK , val_cr5 ) ;
regmap_write ( sai - > regmap , FSL_SAI_RMR ,
~ 0UL - ( ( 1 < < channels ) - 1 ) ) ;
}
}
2013-12-17 11:24:38 +08:00
2014-04-11 18:30:09 +08:00
regmap_update_bits ( sai - > regmap , FSL_SAI_xCR4 ( tx ) ,
FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK ,
val_cr4 ) ;
regmap_update_bits ( sai - > regmap , FSL_SAI_xCR5 ( tx ) ,
FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
FSL_SAI_CR5_FBT_MASK , val_cr5 ) ;
regmap_write ( sai - > regmap , FSL_SAI_xMR ( tx ) , ~ 0UL - ( ( 1 < < channels ) - 1 ) ) ;
2013-12-17 11:24:38 +08:00
return 0 ;
}
2015-05-11 18:24:41 +08:00
static int fsl_sai_hw_free ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * cpu_dai )
{
struct fsl_sai * sai = snd_soc_dai_get_drvdata ( cpu_dai ) ;
bool tx = substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ;
if ( ! sai - > is_slave_mode & &
sai - > mclk_streams & BIT ( substream - > stream ) ) {
clk_disable_unprepare ( sai - > mclk_clk [ sai - > mclk_id [ tx ] ] ) ;
sai - > mclk_streams & = ~ BIT ( substream - > stream ) ;
}
return 0 ;
}
2013-12-17 11:24:38 +08:00
static int fsl_sai_trigger ( struct snd_pcm_substream * substream , int cmd ,
struct snd_soc_dai * cpu_dai )
{
struct fsl_sai * sai = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2014-04-01 11:17:06 +08:00
bool tx = substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ;
2014-07-23 19:23:39 +08:00
u32 xcsr , count = 100 ;
2013-12-31 15:33:21 +08:00
2014-02-27 08:45:01 +08:00
/*
2014-08-05 15:32:05 +08:00
* Asynchronous mode : Clear SYNC for both Tx and Rx .
* Rx sync with Tx clocks : Clear SYNC for Tx , set it for Rx .
* Tx sync with Rx clocks : Clear SYNC for Rx , set it for Tx .
2014-02-27 08:45:01 +08:00
*/
2015-10-19 17:42:23 -07:00
regmap_update_bits ( sai - > regmap , FSL_SAI_TCR2 , FSL_SAI_CR2_SYNC ,
sai - > synchronous [ TX ] ? FSL_SAI_CR2_SYNC : 0 ) ;
2014-02-08 14:38:28 +08:00
regmap_update_bits ( sai - > regmap , FSL_SAI_RCR2 , FSL_SAI_CR2_SYNC ,
2014-08-05 15:32:05 +08:00
sai - > synchronous [ RX ] ? FSL_SAI_CR2_SYNC : 0 ) ;
2013-12-17 11:24:38 +08:00
2014-02-27 08:45:01 +08:00
/*
* It is recommended that the transmitter is the last enabled
* and the first disabled .
*/
2013-12-17 11:24:38 +08:00
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
2014-07-23 19:23:40 +08:00
regmap_update_bits ( sai - > regmap , FSL_SAI_xCSR ( tx ) ,
FSL_SAI_CSR_FRDE , FSL_SAI_CSR_FRDE ) ;
2014-07-23 19:23:38 +08:00
regmap_update_bits ( sai - > regmap , FSL_SAI_RCSR ,
FSL_SAI_CSR_TERE , FSL_SAI_CSR_TERE ) ;
regmap_update_bits ( sai - > regmap , FSL_SAI_TCSR ,
FSL_SAI_CSR_TERE , FSL_SAI_CSR_TERE ) ;
2013-12-25 12:40:04 +08:00
2014-04-01 11:17:07 +08:00
regmap_update_bits ( sai - > regmap , FSL_SAI_xCSR ( tx ) ,
FSL_SAI_CSR_xIE_MASK , FSL_SAI_FLAGS ) ;
2013-12-17 11:24:38 +08:00
break ;
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
2014-04-01 11:17:06 +08:00
regmap_update_bits ( sai - > regmap , FSL_SAI_xCSR ( tx ) ,
FSL_SAI_CSR_FRDE , 0 ) ;
2014-04-01 11:17:07 +08:00
regmap_update_bits ( sai - > regmap , FSL_SAI_xCSR ( tx ) ,
FSL_SAI_CSR_xIE_MASK , 0 ) ;
2014-04-01 11:17:06 +08:00
2014-04-11 22:10:00 +08:00
/* Check if the opposite FRDE is also disabled */
2014-07-23 19:23:38 +08:00
regmap_read ( sai - > regmap , FSL_SAI_xCSR ( ! tx ) , & xcsr ) ;
if ( ! ( xcsr & FSL_SAI_CSR_FRDE ) ) {
2014-07-17 21:21:37 +08:00
/* Disable both directions and reset their FIFOs */
2014-04-01 11:17:06 +08:00
regmap_update_bits ( sai - > regmap , FSL_SAI_TCSR ,
2014-07-23 19:23:39 +08:00
FSL_SAI_CSR_TERE , 0 ) ;
2014-04-01 11:17:06 +08:00
regmap_update_bits ( sai - > regmap , FSL_SAI_RCSR ,
2014-07-23 19:23:39 +08:00
FSL_SAI_CSR_TERE , 0 ) ;
/* TERE will remain set till the end of current frame */
do {
udelay ( 10 ) ;
regmap_read ( sai - > regmap , FSL_SAI_xCSR ( tx ) , & xcsr ) ;
} while ( - - count & & xcsr & FSL_SAI_CSR_TERE ) ;
regmap_update_bits ( sai - > regmap , FSL_SAI_TCSR ,
FSL_SAI_CSR_FR , FSL_SAI_CSR_FR ) ;
regmap_update_bits ( sai - > regmap , FSL_SAI_RCSR ,
FSL_SAI_CSR_FR , FSL_SAI_CSR_FR ) ;
2015-12-18 16:53:41 +08:00
/*
* For sai master mode , after several open / close sai ,
* there will be no frame clock , and can ' t recover
* anymore . Add software reset to fix this issue .
* This is a hardware bug , and will be fix in the
* next sai version .
*/
if ( ! sai - > is_slave_mode ) {
/* Software Reset for both Tx and Rx */
regmap_write ( sai - > regmap ,
FSL_SAI_TCSR , FSL_SAI_CSR_SR ) ;
regmap_write ( sai - > regmap ,
FSL_SAI_RCSR , FSL_SAI_CSR_SR ) ;
/* Clear SR bit to finish the reset */
regmap_write ( sai - > regmap , FSL_SAI_TCSR , 0 ) ;
regmap_write ( sai - > regmap , FSL_SAI_RCSR , 0 ) ;
}
2013-12-17 11:24:38 +08:00
}
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static int fsl_sai_startup ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * cpu_dai )
{
struct fsl_sai * sai = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2014-04-11 18:30:09 +08:00
bool tx = substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ;
2014-04-10 23:26:15 +08:00
struct device * dev = & sai - > pdev - > dev ;
int ret ;
ret = clk_prepare_enable ( sai - > bus_clk ) ;
if ( ret ) {
dev_err ( dev , " failed to enable bus clock: %d \n " , ret ) ;
return ret ;
}
2013-12-17 11:24:38 +08:00
2014-04-11 18:30:09 +08:00
regmap_update_bits ( sai - > regmap , FSL_SAI_xCR3 ( tx ) , FSL_SAI_CR3_TRCE ,
2014-02-08 14:38:28 +08:00
FSL_SAI_CR3_TRCE ) ;
2015-05-11 18:24:43 +08:00
ret = snd_pcm_hw_constraint_list ( substream - > runtime , 0 ,
SNDRV_PCM_HW_PARAM_RATE , & fsl_sai_rate_constraints ) ;
return ret ;
2013-12-17 11:24:38 +08:00
}
static void fsl_sai_shutdown ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * cpu_dai )
{
struct fsl_sai * sai = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2014-04-11 18:30:09 +08:00
bool tx = substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ;
2013-12-17 11:24:38 +08:00
2014-04-11 18:30:09 +08:00
regmap_update_bits ( sai - > regmap , FSL_SAI_xCR3 ( tx ) , FSL_SAI_CR3_TRCE , 0 ) ;
2014-04-10 23:26:15 +08:00
clk_disable_unprepare ( sai - > bus_clk ) ;
2013-12-17 11:24:38 +08:00
}
static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
. set_sysclk = fsl_sai_set_dai_sysclk ,
. set_fmt = fsl_sai_set_dai_fmt ,
2015-11-24 15:31:54 +08:00
. set_tdm_slot = fsl_sai_set_dai_tdm_slot ,
2013-12-17 11:24:38 +08:00
. hw_params = fsl_sai_hw_params ,
2015-05-11 18:24:41 +08:00
. hw_free = fsl_sai_hw_free ,
2013-12-17 11:24:38 +08:00
. trigger = fsl_sai_trigger ,
. startup = fsl_sai_startup ,
. shutdown = fsl_sai_shutdown ,
} ;
static int fsl_sai_dai_probe ( struct snd_soc_dai * cpu_dai )
{
struct fsl_sai * sai = dev_get_drvdata ( cpu_dai - > dev ) ;
2013-12-25 11:20:14 +08:00
2014-08-05 17:20:21 +08:00
/* Software Reset for both Tx and Rx */
regmap_write ( sai - > regmap , FSL_SAI_TCSR , FSL_SAI_CSR_SR ) ;
regmap_write ( sai - > regmap , FSL_SAI_RCSR , FSL_SAI_CSR_SR ) ;
/* Clear SR bit to finish the reset */
regmap_write ( sai - > regmap , FSL_SAI_TCSR , 0 ) ;
regmap_write ( sai - > regmap , FSL_SAI_RCSR , 0 ) ;
2014-02-08 14:38:28 +08:00
regmap_update_bits ( sai - > regmap , FSL_SAI_TCR1 , FSL_SAI_CR1_RFW_MASK ,
FSL_SAI_MAXBURST_TX * 2 ) ;
regmap_update_bits ( sai - > regmap , FSL_SAI_RCR1 , FSL_SAI_CR1_RFW_MASK ,
FSL_SAI_MAXBURST_RX - 1 ) ;
2013-12-17 11:24:38 +08:00
2013-12-20 12:35:33 +08:00
snd_soc_dai_init_dma_data ( cpu_dai , & sai - > dma_params_tx ,
& sai - > dma_params_rx ) ;
2013-12-17 11:24:38 +08:00
snd_soc_dai_set_drvdata ( cpu_dai , sai ) ;
return 0 ;
}
static struct snd_soc_dai_driver fsl_sai_dai = {
. probe = fsl_sai_dai_probe ,
. playback = {
2014-07-30 11:10:27 +08:00
. stream_name = " CPU-Playback " ,
2013-12-17 11:24:38 +08:00
. channels_min = 1 ,
. channels_max = 2 ,
2015-05-11 18:24:43 +08:00
. rate_min = 8000 ,
. rate_max = 192000 ,
. rates = SNDRV_PCM_RATE_KNOT ,
2013-12-17 11:24:38 +08:00
. formats = FSL_SAI_FORMATS ,
} ,
. capture = {
2014-07-30 11:10:27 +08:00
. stream_name = " CPU-Capture " ,
2013-12-17 11:24:38 +08:00
. channels_min = 1 ,
. channels_max = 2 ,
2015-05-11 18:24:43 +08:00
. rate_min = 8000 ,
. rate_max = 192000 ,
. rates = SNDRV_PCM_RATE_KNOT ,
2013-12-17 11:24:38 +08:00
. formats = FSL_SAI_FORMATS ,
} ,
. ops = & fsl_sai_pcm_dai_ops ,
} ;
static const struct snd_soc_component_driver fsl_component = {
. name = " fsl-sai " ,
} ;
2015-10-26 15:19:03 +08:00
static struct reg_default fsl_sai_reg_defaults [ ] = {
{ FSL_SAI_TCR1 , 0 } ,
{ FSL_SAI_TCR2 , 0 } ,
{ FSL_SAI_TCR3 , 0 } ,
{ FSL_SAI_TCR4 , 0 } ,
{ FSL_SAI_TCR5 , 0 } ,
{ FSL_SAI_TDR , 0 } ,
{ FSL_SAI_TMR , 0 } ,
{ FSL_SAI_RCR1 , 0 } ,
{ FSL_SAI_RCR2 , 0 } ,
{ FSL_SAI_RCR3 , 0 } ,
{ FSL_SAI_RCR4 , 0 } ,
{ FSL_SAI_RCR5 , 0 } ,
{ FSL_SAI_RMR , 0 } ,
} ;
2014-02-08 14:38:28 +08:00
static bool fsl_sai_readable_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case FSL_SAI_TCSR :
case FSL_SAI_TCR1 :
case FSL_SAI_TCR2 :
case FSL_SAI_TCR3 :
case FSL_SAI_TCR4 :
case FSL_SAI_TCR5 :
case FSL_SAI_TFR :
case FSL_SAI_TMR :
case FSL_SAI_RCSR :
case FSL_SAI_RCR1 :
case FSL_SAI_RCR2 :
case FSL_SAI_RCR3 :
case FSL_SAI_RCR4 :
case FSL_SAI_RCR5 :
case FSL_SAI_RDR :
case FSL_SAI_RFR :
case FSL_SAI_RMR :
return true ;
default :
return false ;
}
}
static bool fsl_sai_volatile_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
2015-09-18 11:09:10 +08:00
case FSL_SAI_TCSR :
case FSL_SAI_RCSR :
2014-02-08 14:38:28 +08:00
case FSL_SAI_TFR :
case FSL_SAI_RFR :
case FSL_SAI_RDR :
return true ;
default :
return false ;
}
}
static bool fsl_sai_writeable_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case FSL_SAI_TCSR :
case FSL_SAI_TCR1 :
case FSL_SAI_TCR2 :
case FSL_SAI_TCR3 :
case FSL_SAI_TCR4 :
case FSL_SAI_TCR5 :
case FSL_SAI_TDR :
case FSL_SAI_TMR :
case FSL_SAI_RCSR :
case FSL_SAI_RCR1 :
case FSL_SAI_RCR2 :
case FSL_SAI_RCR3 :
case FSL_SAI_RCR4 :
case FSL_SAI_RCR5 :
case FSL_SAI_RMR :
return true ;
default :
return false ;
}
}
2014-08-25 11:31:02 +08:00
static const struct regmap_config fsl_sai_regmap_config = {
2014-02-08 14:38:28 +08:00
. reg_bits = 32 ,
. reg_stride = 4 ,
. val_bits = 32 ,
. max_register = FSL_SAI_RMR ,
2015-10-26 15:19:03 +08:00
. reg_defaults = fsl_sai_reg_defaults ,
. num_reg_defaults = ARRAY_SIZE ( fsl_sai_reg_defaults ) ,
2014-02-08 14:38:28 +08:00
. readable_reg = fsl_sai_readable_reg ,
. volatile_reg = fsl_sai_volatile_reg ,
. writeable_reg = fsl_sai_writeable_reg ,
2015-09-18 11:09:10 +08:00
. cache_type = REGCACHE_FLAT ,
2014-02-08 14:38:28 +08:00
} ;
2013-12-17 11:24:38 +08:00
static int fsl_sai_probe ( struct platform_device * pdev )
{
2013-12-20 16:41:05 +08:00
struct device_node * np = pdev - > dev . of_node ;
2013-12-17 11:24:38 +08:00
struct fsl_sai * sai ;
2016-05-04 19:33:59 -03:00
struct regmap * gpr ;
2013-12-17 11:24:38 +08:00
struct resource * res ;
2014-02-08 14:38:28 +08:00
void __iomem * base ;
2014-04-10 23:26:15 +08:00
char tmp [ 8 ] ;
int irq , ret , i ;
2016-05-04 19:33:59 -03:00
int index ;
2013-12-17 11:24:38 +08:00
sai = devm_kzalloc ( & pdev - > dev , sizeof ( * sai ) , GFP_KERNEL ) ;
if ( ! sai )
return - ENOMEM ;
2014-03-27 19:06:59 +08:00
sai - > pdev = pdev ;
2016-05-04 19:33:58 -03:00
if ( of_device_is_compatible ( pdev - > dev . of_node , " fsl,imx6sx-sai " ) | |
of_device_is_compatible ( pdev - > dev . of_node , " fsl,imx6ul-sai " ) )
2014-04-01 19:34:09 +08:00
sai - > sai_on_imx = true ;
2014-08-29 15:12:12 +08:00
sai - > is_lsb_first = of_property_read_bool ( np , " lsb-first " ) ;
2014-02-08 14:38:28 +08:00
2013-12-17 11:24:38 +08:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2014-02-08 14:38:28 +08:00
base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
sai - > regmap = devm_regmap_init_mmio_clk ( & pdev - > dev ,
2014-04-10 23:26:15 +08:00
" bus " , base , & fsl_sai_regmap_config ) ;
/* Compatible with old DTB cases */
if ( IS_ERR ( sai - > regmap ) )
sai - > regmap = devm_regmap_init_mmio_clk ( & pdev - > dev ,
" sai " , base , & fsl_sai_regmap_config ) ;
2014-02-08 14:38:28 +08:00
if ( IS_ERR ( sai - > regmap ) ) {
dev_err ( & pdev - > dev , " regmap init failed \n " ) ;
return PTR_ERR ( sai - > regmap ) ;
2013-12-17 11:24:38 +08:00
}
2014-04-10 23:26:15 +08:00
/* No error out for old DTB cases but only mark the clock NULL */
sai - > bus_clk = devm_clk_get ( & pdev - > dev , " bus " ) ;
if ( IS_ERR ( sai - > bus_clk ) ) {
dev_err ( & pdev - > dev , " failed to get bus clock: %ld \n " ,
PTR_ERR ( sai - > bus_clk ) ) ;
sai - > bus_clk = NULL ;
}
2015-05-11 18:24:41 +08:00
sai - > mclk_clk [ 0 ] = sai - > bus_clk ;
for ( i = 1 ; i < FSL_SAI_MCLK_MAX ; i + + ) {
sprintf ( tmp , " mclk%d " , i ) ;
2014-04-10 23:26:15 +08:00
sai - > mclk_clk [ i ] = devm_clk_get ( & pdev - > dev , tmp ) ;
if ( IS_ERR ( sai - > mclk_clk [ i ] ) ) {
dev_err ( & pdev - > dev , " failed to get mclk%d clock: %ld \n " ,
i + 1 , PTR_ERR ( sai - > mclk_clk [ i ] ) ) ;
sai - > mclk_clk [ i ] = NULL ;
}
}
2014-03-27 19:06:59 +08:00
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
2015-01-07 13:44:34 -02:00
dev_err ( & pdev - > dev , " no irq for node %s \n " , pdev - > name ) ;
2014-03-27 19:06:59 +08:00
return irq ;
}
ret = devm_request_irq ( & pdev - > dev , irq , fsl_sai_isr , 0 , np - > name , sai ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to claim irq %u \n " , irq ) ;
return ret ;
}
2014-08-05 15:32:05 +08:00
/* Sync Tx with Rx as default by following old DT binding */
sai - > synchronous [ RX ] = true ;
sai - > synchronous [ TX ] = false ;
fsl_sai_dai . symmetric_rates = 1 ;
fsl_sai_dai . symmetric_channels = 1 ;
fsl_sai_dai . symmetric_samplebits = 1 ;
2014-08-08 18:41:19 +08:00
if ( of_find_property ( np , " fsl,sai-synchronous-rx " , NULL ) & &
of_find_property ( np , " fsl,sai-asynchronous " , NULL ) ) {
/* error out if both synchronous and asynchronous are present */
dev_err ( & pdev - > dev , " invalid binding for synchronous mode \n " ) ;
return - EINVAL ;
}
2014-08-05 15:32:05 +08:00
if ( of_find_property ( np , " fsl,sai-synchronous-rx " , NULL ) ) {
/* Sync Rx with Tx */
sai - > synchronous [ RX ] = false ;
sai - > synchronous [ TX ] = true ;
} else if ( of_find_property ( np , " fsl,sai-asynchronous " , NULL ) ) {
/* Discard all settings for asynchronous mode */
sai - > synchronous [ RX ] = false ;
sai - > synchronous [ TX ] = false ;
fsl_sai_dai . symmetric_rates = 0 ;
fsl_sai_dai . symmetric_channels = 0 ;
fsl_sai_dai . symmetric_samplebits = 0 ;
}
2016-05-04 19:33:59 -03:00
if ( of_find_property ( np , " fsl,sai-mclk-direction-output " , NULL ) & &
of_device_is_compatible ( pdev - > dev . of_node , " fsl,imx6ul-sai " ) ) {
gpr = syscon_regmap_lookup_by_compatible ( " fsl,imx6ul-iomuxc-gpr " ) ;
if ( IS_ERR ( gpr ) ) {
dev_err ( & pdev - > dev , " cannot find iomuxc registers \n " ) ;
return PTR_ERR ( gpr ) ;
}
index = of_alias_get_id ( np , " sai " ) ;
if ( index < 0 )
return index ;
regmap_update_bits ( gpr , IOMUXC_GPR1 , MCLK_DIR ( index ) ,
MCLK_DIR ( index ) ) ;
}
2013-12-17 11:24:38 +08:00
sai - > dma_params_rx . addr = res - > start + FSL_SAI_RDR ;
sai - > dma_params_tx . addr = res - > start + FSL_SAI_TDR ;
sai - > dma_params_rx . maxburst = FSL_SAI_MAXBURST_RX ;
sai - > dma_params_tx . maxburst = FSL_SAI_MAXBURST_TX ;
platform_set_drvdata ( pdev , sai ) ;
ret = devm_snd_soc_register_component ( & pdev - > dev , & fsl_component ,
& fsl_sai_dai , 1 ) ;
if ( ret )
return ret ;
2014-04-01 19:34:09 +08:00
if ( sai - > sai_on_imx )
2015-06-23 18:23:53 +08:00
return imx_pcm_dma_init ( pdev , IMX_SAI_DMABUF_SIZE ) ;
2014-04-01 19:34:09 +08:00
else
2015-04-27 12:44:25 +02:00
return devm_snd_dmaengine_pcm_register ( & pdev - > dev , NULL , 0 ) ;
2013-12-17 11:24:38 +08:00
}
static const struct of_device_id fsl_sai_ids [ ] = {
{ . compatible = " fsl,vf610-sai " , } ,
2014-04-01 19:34:09 +08:00
{ . compatible = " fsl,imx6sx-sai " , } ,
2016-05-04 19:33:58 -03:00
{ . compatible = " fsl,imx6ul-sai " , } ,
2013-12-17 11:24:38 +08:00
{ /* sentinel */ }
} ;
2015-09-03 12:58:23 +02:00
MODULE_DEVICE_TABLE ( of , fsl_sai_ids ) ;
2013-12-17 11:24:38 +08:00
2015-10-22 15:56:40 -07:00
# ifdef CONFIG_PM_SLEEP
2015-09-18 11:09:10 +08:00
static int fsl_sai_suspend ( struct device * dev )
{
struct fsl_sai * sai = dev_get_drvdata ( dev ) ;
regcache_cache_only ( sai - > regmap , true ) ;
regcache_mark_dirty ( sai - > regmap ) ;
return 0 ;
}
static int fsl_sai_resume ( struct device * dev )
{
struct fsl_sai * sai = dev_get_drvdata ( dev ) ;
regcache_cache_only ( sai - > regmap , false ) ;
regmap_write ( sai - > regmap , FSL_SAI_TCSR , FSL_SAI_CSR_SR ) ;
regmap_write ( sai - > regmap , FSL_SAI_RCSR , FSL_SAI_CSR_SR ) ;
2016-01-15 19:35:24 +08:00
usleep_range ( 1000 , 2000 ) ;
2015-09-18 11:09:10 +08:00
regmap_write ( sai - > regmap , FSL_SAI_TCSR , 0 ) ;
regmap_write ( sai - > regmap , FSL_SAI_RCSR , 0 ) ;
return regcache_sync ( sai - > regmap ) ;
}
# endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops fsl_sai_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( fsl_sai_suspend , fsl_sai_resume )
} ;
2013-12-17 11:24:38 +08:00
static struct platform_driver fsl_sai_driver = {
. probe = fsl_sai_probe ,
. driver = {
. name = " fsl-sai " ,
2015-09-18 11:09:10 +08:00
. pm = & fsl_sai_pm_ops ,
2013-12-17 11:24:38 +08:00
. of_match_table = fsl_sai_ids ,
} ,
} ;
module_platform_driver ( fsl_sai_driver ) ;
MODULE_DESCRIPTION ( " Freescale Soc SAI Interface " ) ;
MODULE_AUTHOR ( " Xiubo Li, <Li.Xiubo@freescale.com> " ) ;
MODULE_ALIAS ( " platform:fsl-sai " ) ;
MODULE_LICENSE ( " GPL " ) ;