2010-11-22 15:37:25 +09:00
/* sound/soc/samsung/i2s.c
2010-11-22 15:36:59 +09:00
*
* ALSA SoC Audio Layer - Samsung I2S Controller driver
*
* Copyright ( c ) 2010 Samsung Electronics Co . Ltd .
2012-02-25 16:24:36 +05:30
* Jaswinder Singh < jassisinghbrar @ gmail . com >
2010-11-22 15:36:59 +09:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
2015-01-14 19:42:39 +01:00
# include <dt-bindings/sound/samsung-i2s.h>
2010-11-22 15:36:59 +09:00
# include <linux/delay.h>
# include <linux/slab.h>
# include <linux/clk.h>
2015-01-14 19:42:39 +01:00
# include <linux/clk-provider.h>
2010-11-22 15:36:59 +09:00
# include <linux/io.h>
2011-07-15 12:38:28 -04:00
# include <linux/module.h>
2013-01-18 17:17:01 +05:30
# include <linux/of.h>
2016-04-08 18:52:45 +02:00
# include <linux/of_device.h>
2013-01-18 17:17:01 +05:30
# include <linux/of_gpio.h>
2011-12-08 16:45:03 +08:00
# include <linux/pm_runtime.h>
2010-11-22 15:36:59 +09:00
# include <sound/soc.h>
2011-01-11 07:26:06 +09:00
# include <sound/pcm_params.h>
2010-11-22 15:36:59 +09:00
2012-08-24 15:22:12 +02:00
# include <linux/platform_data/asoc-s3c.h>
2010-11-22 15:36:59 +09:00
# include "dma.h"
2011-07-20 17:07:12 +09:00
# include "idma.h"
2010-11-22 15:36:59 +09:00
# include "i2s.h"
2011-06-20 16:36:18 +09:00
# include "i2s-regs.h"
2010-11-22 15:36:59 +09:00
# define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
2014-11-07 12:24:40 +05:30
struct samsung_i2s_variant_regs {
unsigned int bfs_off ;
unsigned int rfs_off ;
unsigned int sdf_off ;
unsigned int txr_off ;
unsigned int rclksrc_off ;
unsigned int mss_off ;
unsigned int cdclkcon_off ;
unsigned int lrp_off ;
unsigned int bfs_mask ;
unsigned int rfs_mask ;
unsigned int ftx0cnt_off ;
} ;
2013-01-18 17:17:01 +05:30
struct samsung_i2s_dai_data {
2013-08-12 15:19:51 +05:30
u32 quirks ;
2017-07-07 10:31:10 +09:00
unsigned int pcm_rates ;
2014-11-07 12:24:40 +05:30
const struct samsung_i2s_variant_regs * i2s_variant_regs ;
2013-01-18 17:17:01 +05:30
} ;
2010-11-22 15:36:59 +09:00
struct i2s_dai {
/* Platform device for this DAI */
struct platform_device * pdev ;
2015-01-14 19:42:30 +01:00
/* Memory mapped SFR region */
2010-11-22 15:36:59 +09:00
void __iomem * addr ;
/* Rate of RCLK source clock */
unsigned long rclk_srcrate ;
/* Frame Clock */
unsigned frmclk ;
/*
* Specifically requested RCLK , BCLK by MACHINE Driver .
* 0 indicates CPU driver is free to choose any value .
*/
unsigned rfs , bfs ;
/* I2S Controller's core clock */
struct clk * clk ;
/* Clock for generating I2S signals */
struct clk * op_clk ;
/* Pointer to the Primary_Fifo if this is Sec_Fifo, NULL otherwise */
struct i2s_dai * pri_dai ;
/* Pointer to the Secondary_Fifo if it has one, NULL otherwise */
struct i2s_dai * sec_dai ;
# define DAI_OPENED (1 << 0) /* Dai is opened */
# define DAI_MANAGER (1 << 1) /* Dai is the manager */
unsigned mode ;
/* Driver for this DAI */
struct snd_soc_dai_driver i2s_dai_drv ;
/* DMA parameters */
2016-08-04 11:30:26 +02:00
struct snd_dmaengine_dai_dma_data dma_playback ;
struct snd_dmaengine_dai_dma_data dma_capture ;
struct snd_dmaengine_dai_dma_data idma_playback ;
2015-11-18 22:31:11 +01:00
dma_filter_fn filter ;
2010-11-22 15:36:59 +09:00
u32 quirks ;
u32 suspend_i2smod ;
u32 suspend_i2scon ;
u32 suspend_i2spsr ;
2014-11-07 12:24:40 +05:30
const struct samsung_i2s_variant_regs * variant_regs ;
2015-01-14 19:42:35 +01:00
/* Spinlock protecting access to the device's registers */
spinlock_t spinlock ;
spinlock_t * lock ;
2015-01-14 19:42:39 +01:00
/* Below fields are only valid if this is the primary FIFO */
struct clk * clk_table [ 3 ] ;
struct clk_onecell_data clk_data ;
2010-11-22 15:36:59 +09:00
} ;
/* Lock for cross i/f checks */
static DEFINE_SPINLOCK ( lock ) ;
/* If this is the 'overlay' stereo DAI */
static inline bool is_secondary ( struct i2s_dai * i2s )
{
return i2s - > pri_dai ? true : false ;
}
/* If operating in SoC-Slave mode */
static inline bool is_slave ( struct i2s_dai * i2s )
{
2014-11-07 12:24:40 +05:30
u32 mod = readl ( i2s - > addr + I2SMOD ) ;
return ( mod & ( 1 < < i2s - > variant_regs - > mss_off ) ) ? true : false ;
2010-11-22 15:36:59 +09:00
}
/* If this interface of the controller is transmitting data */
static inline bool tx_active ( struct i2s_dai * i2s )
{
u32 active ;
if ( ! i2s )
return false ;
2011-06-10 10:36:54 +09:00
active = readl ( i2s - > addr + I2SCON ) ;
2010-11-22 15:36:59 +09:00
if ( is_secondary ( i2s ) )
active & = CON_TXSDMA_ACTIVE ;
else
active & = CON_TXDMA_ACTIVE ;
return active ? true : false ;
}
2015-01-14 19:42:33 +01:00
/* Return pointer to the other DAI */
static inline struct i2s_dai * get_other_dai ( struct i2s_dai * i2s )
{
return i2s - > pri_dai ? : i2s - > sec_dai ;
}
2010-11-22 15:36:59 +09:00
/* If the other interface of the controller is transmitting data */
static inline bool other_tx_active ( struct i2s_dai * i2s )
{
2015-01-14 19:42:33 +01:00
struct i2s_dai * other = get_other_dai ( i2s ) ;
2010-11-22 15:36:59 +09:00
return tx_active ( other ) ;
}
/* If any interface of the controller is transmitting data */
static inline bool any_tx_active ( struct i2s_dai * i2s )
{
return tx_active ( i2s ) | | other_tx_active ( i2s ) ;
}
/* If this interface of the controller is receiving data */
static inline bool rx_active ( struct i2s_dai * i2s )
{
u32 active ;
if ( ! i2s )
return false ;
2011-06-10 10:36:54 +09:00
active = readl ( i2s - > addr + I2SCON ) & CON_RXDMA_ACTIVE ;
2010-11-22 15:36:59 +09:00
return active ? true : false ;
}
/* If the other interface of the controller is receiving data */
static inline bool other_rx_active ( struct i2s_dai * i2s )
{
2015-01-14 19:42:33 +01:00
struct i2s_dai * other = get_other_dai ( i2s ) ;
2010-11-22 15:36:59 +09:00
return rx_active ( other ) ;
}
/* If any interface of the controller is receiving data */
static inline bool any_rx_active ( struct i2s_dai * i2s )
{
return rx_active ( i2s ) | | other_rx_active ( i2s ) ;
}
/* If the other DAI is transmitting or receiving data */
static inline bool other_active ( struct i2s_dai * i2s )
{
return other_rx_active ( i2s ) | | other_tx_active ( i2s ) ;
}
/* If this DAI is transmitting or receiving data */
static inline bool this_active ( struct i2s_dai * i2s )
{
return tx_active ( i2s ) | | rx_active ( i2s ) ;
}
/* If the controller is active anyway */
static inline bool any_active ( struct i2s_dai * i2s )
{
return this_active ( i2s ) | | other_active ( i2s ) ;
}
static inline struct i2s_dai * to_info ( struct snd_soc_dai * dai )
{
return snd_soc_dai_get_drvdata ( dai ) ;
}
static inline bool is_opened ( struct i2s_dai * i2s )
{
if ( i2s & & ( i2s - > mode & DAI_OPENED ) )
return true ;
else
return false ;
}
static inline bool is_manager ( struct i2s_dai * i2s )
{
if ( is_opened ( i2s ) & & ( i2s - > mode & DAI_MANAGER ) )
return true ;
else
return false ;
}
/* Read RCLK of I2S (in multiples of LRCLK) */
static inline unsigned get_rfs ( struct i2s_dai * i2s )
{
2013-08-12 15:19:52 +05:30
u32 rfs ;
2014-11-07 12:24:40 +05:30
rfs = readl ( i2s - > addr + I2SMOD ) > > i2s - > variant_regs - > rfs_off ;
rfs & = i2s - > variant_regs - > rfs_mask ;
2010-11-22 15:36:59 +09:00
switch ( rfs ) {
2014-11-07 12:24:40 +05:30
case 7 : return 192 ;
case 6 : return 96 ;
case 5 : return 128 ;
case 4 : return 64 ;
2010-11-22 15:36:59 +09:00
case 3 : return 768 ;
case 2 : return 384 ;
case 1 : return 512 ;
default : return 256 ;
}
}
/* Write RCLK of I2S (in multiples of LRCLK) */
static inline void set_rfs ( struct i2s_dai * i2s , unsigned rfs )
{
u32 mod = readl ( i2s - > addr + I2SMOD ) ;
2014-11-07 12:24:40 +05:30
int rfs_shift = i2s - > variant_regs - > rfs_off ;
2010-11-22 15:36:59 +09:00
2014-11-07 12:24:40 +05:30
mod & = ~ ( i2s - > variant_regs - > rfs_mask < < rfs_shift ) ;
2010-11-22 15:36:59 +09:00
switch ( rfs ) {
2014-11-07 12:24:40 +05:30
case 192 :
mod | = ( EXYNOS7_MOD_RCLK_192FS < < rfs_shift ) ;
break ;
case 96 :
mod | = ( EXYNOS7_MOD_RCLK_96FS < < rfs_shift ) ;
break ;
case 128 :
mod | = ( EXYNOS7_MOD_RCLK_128FS < < rfs_shift ) ;
break ;
case 64 :
mod | = ( EXYNOS7_MOD_RCLK_64FS < < rfs_shift ) ;
break ;
2010-11-22 15:36:59 +09:00
case 768 :
2013-07-26 19:06:48 +05:30
mod | = ( MOD_RCLK_768FS < < rfs_shift ) ;
2010-11-22 15:36:59 +09:00
break ;
case 512 :
2013-07-26 19:06:48 +05:30
mod | = ( MOD_RCLK_512FS < < rfs_shift ) ;
2010-11-22 15:36:59 +09:00
break ;
case 384 :
2013-07-26 19:06:48 +05:30
mod | = ( MOD_RCLK_384FS < < rfs_shift ) ;
2010-11-22 15:36:59 +09:00
break ;
default :
2013-07-26 19:06:48 +05:30
mod | = ( MOD_RCLK_256FS < < rfs_shift ) ;
2010-11-22 15:36:59 +09:00
break ;
}
writel ( mod , i2s - > addr + I2SMOD ) ;
}
/* Read Bit-Clock of I2S (in multiples of LRCLK) */
static inline unsigned get_bfs ( struct i2s_dai * i2s )
{
2013-08-12 15:19:52 +05:30
u32 bfs ;
2014-11-07 12:24:40 +05:30
bfs = readl ( i2s - > addr + I2SMOD ) > > i2s - > variant_regs - > bfs_off ;
bfs & = i2s - > variant_regs - > bfs_mask ;
2010-11-22 15:36:59 +09:00
switch ( bfs ) {
2013-08-12 15:19:52 +05:30
case 8 : return 256 ;
case 7 : return 192 ;
case 6 : return 128 ;
case 5 : return 96 ;
case 4 : return 64 ;
2010-11-22 15:36:59 +09:00
case 3 : return 24 ;
case 2 : return 16 ;
case 1 : return 48 ;
default : return 32 ;
}
}
/* Write Bit-Clock of I2S (in multiples of LRCLK) */
static inline void set_bfs ( struct i2s_dai * i2s , unsigned bfs )
{
u32 mod = readl ( i2s - > addr + I2SMOD ) ;
2013-08-12 15:19:52 +05:30
int tdm = i2s - > quirks & QUIRK_SUPPORTS_TDM ;
2014-11-07 12:24:40 +05:30
int bfs_shift = i2s - > variant_regs - > bfs_off ;
2013-08-12 15:19:52 +05:30
/* Non-TDM I2S controllers do not support BCLK > 48 * FS */
if ( ! tdm & & bfs > 48 ) {
dev_err ( & i2s - > pdev - > dev , " Unsupported BCLK divider \n " ) ;
return ;
}
2010-11-22 15:36:59 +09:00
2014-11-07 12:24:40 +05:30
mod & = ~ ( i2s - > variant_regs - > bfs_mask < < bfs_shift ) ;
2010-11-22 15:36:59 +09:00
switch ( bfs ) {
case 48 :
2013-07-26 19:06:48 +05:30
mod | = ( MOD_BCLK_48FS < < bfs_shift ) ;
2010-11-22 15:36:59 +09:00
break ;
case 32 :
2013-07-26 19:06:48 +05:30
mod | = ( MOD_BCLK_32FS < < bfs_shift ) ;
2010-11-22 15:36:59 +09:00
break ;
case 24 :
2013-07-26 19:06:48 +05:30
mod | = ( MOD_BCLK_24FS < < bfs_shift ) ;
2010-11-22 15:36:59 +09:00
break ;
case 16 :
2013-07-26 19:06:48 +05:30
mod | = ( MOD_BCLK_16FS < < bfs_shift ) ;
2010-11-22 15:36:59 +09:00
break ;
2013-08-12 15:19:52 +05:30
case 64 :
mod | = ( EXYNOS5420_MOD_BCLK_64FS < < bfs_shift ) ;
break ;
case 96 :
mod | = ( EXYNOS5420_MOD_BCLK_96FS < < bfs_shift ) ;
break ;
case 128 :
mod | = ( EXYNOS5420_MOD_BCLK_128FS < < bfs_shift ) ;
break ;
case 192 :
mod | = ( EXYNOS5420_MOD_BCLK_192FS < < bfs_shift ) ;
break ;
case 256 :
mod | = ( EXYNOS5420_MOD_BCLK_256FS < < bfs_shift ) ;
2010-11-22 15:36:59 +09:00
break ;
default :
dev_err ( & i2s - > pdev - > dev , " Wrong BCLK Divider! \n " ) ;
return ;
}
writel ( mod , i2s - > addr + I2SMOD ) ;
}
/* Sample-Size */
static inline int get_blc ( struct i2s_dai * i2s )
{
int blc = readl ( i2s - > addr + I2SMOD ) ;
blc = ( blc > > 13 ) & 0x3 ;
switch ( blc ) {
case 2 : return 24 ;
case 1 : return 8 ;
default : return 16 ;
}
}
/* TX Channel Control */
static void i2s_txctrl ( struct i2s_dai * i2s , int on )
{
void __iomem * addr = i2s - > addr ;
2014-11-07 12:24:40 +05:30
int txr_off = i2s - > variant_regs - > txr_off ;
2010-11-22 15:36:59 +09:00
u32 con = readl ( addr + I2SCON ) ;
2014-11-07 12:24:40 +05:30
u32 mod = readl ( addr + I2SMOD ) & ~ ( 3 < < txr_off ) ;
2010-11-22 15:36:59 +09:00
if ( on ) {
con | = CON_ACTIVE ;
con & = ~ CON_TXCH_PAUSE ;
if ( is_secondary ( i2s ) ) {
con | = CON_TXSDMA_ACTIVE ;
con & = ~ CON_TXSDMA_PAUSE ;
} else {
con | = CON_TXDMA_ACTIVE ;
con & = ~ CON_TXDMA_PAUSE ;
}
if ( any_rx_active ( i2s ) )
2014-11-07 12:24:40 +05:30
mod | = 2 < < txr_off ;
2010-11-22 15:36:59 +09:00
else
2014-11-07 12:24:40 +05:30
mod | = 0 < < txr_off ;
2010-11-22 15:36:59 +09:00
} else {
if ( is_secondary ( i2s ) ) {
con | = CON_TXSDMA_PAUSE ;
con & = ~ CON_TXSDMA_ACTIVE ;
} else {
con | = CON_TXDMA_PAUSE ;
con & = ~ CON_TXDMA_ACTIVE ;
}
if ( other_tx_active ( i2s ) ) {
writel ( con , addr + I2SCON ) ;
return ;
}
con | = CON_TXCH_PAUSE ;
if ( any_rx_active ( i2s ) )
2014-11-07 12:24:40 +05:30
mod | = 1 < < txr_off ;
2010-11-22 15:36:59 +09:00
else
con & = ~ CON_ACTIVE ;
}
writel ( mod , addr + I2SMOD ) ;
writel ( con , addr + I2SCON ) ;
}
/* RX Channel Control */
static void i2s_rxctrl ( struct i2s_dai * i2s , int on )
{
void __iomem * addr = i2s - > addr ;
2014-11-07 12:24:40 +05:30
int txr_off = i2s - > variant_regs - > txr_off ;
2010-11-22 15:36:59 +09:00
u32 con = readl ( addr + I2SCON ) ;
2014-11-07 12:24:40 +05:30
u32 mod = readl ( addr + I2SMOD ) & ~ ( 3 < < txr_off ) ;
2010-11-22 15:36:59 +09:00
if ( on ) {
con | = CON_RXDMA_ACTIVE | CON_ACTIVE ;
con & = ~ ( CON_RXDMA_PAUSE | CON_RXCH_PAUSE ) ;
if ( any_tx_active ( i2s ) )
2014-11-07 12:24:40 +05:30
mod | = 2 < < txr_off ;
2010-11-22 15:36:59 +09:00
else
2014-11-07 12:24:40 +05:30
mod | = 1 < < txr_off ;
2010-11-22 15:36:59 +09:00
} else {
con | = CON_RXDMA_PAUSE | CON_RXCH_PAUSE ;
con & = ~ CON_RXDMA_ACTIVE ;
if ( any_tx_active ( i2s ) )
2014-11-07 12:24:40 +05:30
mod | = 0 < < txr_off ;
2010-11-22 15:36:59 +09:00
else
con & = ~ CON_ACTIVE ;
}
writel ( mod , addr + I2SMOD ) ;
writel ( con , addr + I2SCON ) ;
}
/* Flush FIFO of an interface */
static inline void i2s_fifo ( struct i2s_dai * i2s , u32 flush )
{
void __iomem * fic ;
u32 val ;
if ( ! i2s )
return ;
if ( is_secondary ( i2s ) )
fic = i2s - > addr + I2SFICS ;
else
fic = i2s - > addr + I2SFIC ;
/* Flush the FIFO */
writel ( readl ( fic ) | flush , fic ) ;
/* Be patient */
val = msecs_to_loops ( 1 ) / 1000 ; /* 1 usec */
while ( - - val )
cpu_relax ( ) ;
writel ( readl ( fic ) & ~ flush , fic ) ;
}
static int i2s_set_sysclk ( struct snd_soc_dai * dai ,
int clk_id , unsigned int rfs , int dir )
{
struct i2s_dai * i2s = to_info ( dai ) ;
2015-01-14 19:42:33 +01:00
struct i2s_dai * other = get_other_dai ( i2s ) ;
2014-11-07 12:24:40 +05:30
const struct samsung_i2s_variant_regs * i2s_regs = i2s - > variant_regs ;
unsigned int cdcon_mask = 1 < < i2s_regs - > cdclkcon_off ;
unsigned int rsrc_mask = 1 < < i2s_regs - > rclksrc_off ;
2015-01-14 19:42:36 +01:00
u32 mod , mask , val = 0 ;
2016-02-18 15:47:13 +00:00
unsigned long flags ;
2016-12-29 12:34:04 +01:00
int ret = 0 ;
pm_runtime_get_sync ( dai - > dev ) ;
2015-01-14 19:42:36 +01:00
2016-02-18 15:47:13 +00:00
spin_lock_irqsave ( i2s - > lock , flags ) ;
2015-01-14 19:42:36 +01:00
mod = readl ( i2s - > addr + I2SMOD ) ;
2016-02-18 15:47:13 +00:00
spin_unlock_irqrestore ( i2s - > lock , flags ) ;
2010-11-22 15:36:59 +09:00
switch ( clk_id ) {
2014-05-19 19:30:38 +02:00
case SAMSUNG_I2S_OPCLK :
2015-01-14 19:42:36 +01:00
mask = MOD_OPCLK_MASK ;
val = dir ;
2014-05-19 19:30:38 +02:00
break ;
2010-11-22 15:36:59 +09:00
case SAMSUNG_I2S_CDCLK :
2015-01-14 19:42:36 +01:00
mask = 1 < < i2s_regs - > cdclkcon_off ;
2010-11-22 15:36:59 +09:00
/* Shouldn't matter in GATING(CLOCK_IN) mode */
if ( dir = = SND_SOC_CLOCK_IN )
rfs = 0 ;
2014-09-09 16:51:49 +01:00
if ( ( rfs & & other & & other - > rfs & & ( other - > rfs ! = rfs ) ) | |
2010-11-22 15:36:59 +09:00
( any_active ( i2s ) & &
( ( ( dir = = SND_SOC_CLOCK_IN )
2014-11-07 12:24:40 +05:30
& & ! ( mod & cdcon_mask ) ) | |
2010-11-22 15:36:59 +09:00
( ( dir = = SND_SOC_CLOCK_OUT )
2014-11-07 12:24:40 +05:30
& & ( mod & cdcon_mask ) ) ) ) ) {
2010-11-22 15:36:59 +09:00
dev_err ( & i2s - > pdev - > dev ,
" %s:%d Other DAI busy \n " , __func__ , __LINE__ ) ;
2016-12-29 12:34:04 +01:00
ret = - EAGAIN ;
goto err ;
2010-11-22 15:36:59 +09:00
}
if ( dir = = SND_SOC_CLOCK_IN )
2015-01-14 19:42:36 +01:00
val = 1 < < i2s_regs - > cdclkcon_off ;
2010-11-22 15:36:59 +09:00
i2s - > rfs = rfs ;
break ;
case SAMSUNG_I2S_RCLKSRC_0 : /* clock corrsponding to IISMOD[10] := 0 */
case SAMSUNG_I2S_RCLKSRC_1 : /* clock corrsponding to IISMOD[10] := 1 */
2015-01-14 19:42:36 +01:00
mask = 1 < < i2s_regs - > rclksrc_off ;
2010-11-22 15:36:59 +09:00
if ( ( i2s - > quirks & QUIRK_NO_MUXPSR )
| | ( clk_id = = SAMSUNG_I2S_RCLKSRC_0 ) )
clk_id = 0 ;
else
clk_id = 1 ;
if ( ! any_active ( i2s ) ) {
2014-05-22 12:10:52 +02:00
if ( i2s - > op_clk & & ! IS_ERR ( i2s - > op_clk ) ) {
2014-11-07 12:24:40 +05:30
if ( ( clk_id & & ! ( mod & rsrc_mask ) ) | |
( ! clk_id & & ( mod & rsrc_mask ) ) ) {
2012-10-03 08:46:58 +09:00
clk_disable_unprepare ( i2s - > op_clk ) ;
2010-11-22 15:36:59 +09:00
clk_put ( i2s - > op_clk ) ;
} else {
2010-12-20 11:05:46 +09:00
i2s - > rclk_srcrate =
clk_get_rate ( i2s - > op_clk ) ;
2016-12-29 12:34:04 +01:00
goto done ;
2010-11-22 15:36:59 +09:00
}
}
2012-11-28 16:17:48 +05:30
if ( clk_id )
i2s - > op_clk = clk_get ( & i2s - > pdev - > dev ,
" i2s_opclk1 " ) ;
else
i2s - > op_clk = clk_get ( & i2s - > pdev - > dev ,
" i2s_opclk0 " ) ;
2014-05-22 12:10:52 +02:00
2016-12-29 12:34:04 +01:00
if ( WARN_ON ( IS_ERR ( i2s - > op_clk ) ) ) {
ret = PTR_ERR ( i2s - > op_clk ) ;
2016-12-29 12:34:06 +01:00
i2s - > op_clk = NULL ;
2016-12-29 12:34:04 +01:00
goto err ;
}
2014-05-22 12:10:52 +02:00
2017-07-25 15:44:31 +05:30
ret = clk_prepare_enable ( i2s - > op_clk ) ;
2017-09-03 14:29:02 +02:00
if ( ret ) {
clk_put ( i2s - > op_clk ) ;
i2s - > op_clk = NULL ;
2017-07-25 15:44:31 +05:30
goto err ;
2017-09-03 14:29:02 +02:00
}
2010-11-22 15:36:59 +09:00
i2s - > rclk_srcrate = clk_get_rate ( i2s - > op_clk ) ;
/* Over-ride the other's */
if ( other ) {
other - > op_clk = i2s - > op_clk ;
other - > rclk_srcrate = i2s - > rclk_srcrate ;
}
2014-11-07 12:24:40 +05:30
} else if ( ( ! clk_id & & ( mod & rsrc_mask ) )
| | ( clk_id & & ! ( mod & rsrc_mask ) ) ) {
2010-11-22 15:36:59 +09:00
dev_err ( & i2s - > pdev - > dev ,
" %s:%d Other DAI busy \n " , __func__ , __LINE__ ) ;
2016-12-29 12:34:04 +01:00
ret = - EAGAIN ;
goto err ;
2010-11-22 15:36:59 +09:00
} else {
/* Call can't be on the active DAI */
i2s - > op_clk = other - > op_clk ;
i2s - > rclk_srcrate = other - > rclk_srcrate ;
2016-12-29 12:34:04 +01:00
goto done ;
2010-11-22 15:36:59 +09:00
}
2015-01-14 19:42:36 +01:00
if ( clk_id = = 1 )
val = 1 < < i2s_regs - > rclksrc_off ;
2014-11-20 15:33:17 +05:30
break ;
2010-11-22 15:36:59 +09:00
default :
dev_err ( & i2s - > pdev - > dev , " We don't serve that! \n " ) ;
2016-12-29 12:34:04 +01:00
ret = - EINVAL ;
goto err ;
2010-11-22 15:36:59 +09:00
}
2016-02-18 15:47:13 +00:00
spin_lock_irqsave ( i2s - > lock , flags ) ;
2015-01-14 19:42:36 +01:00
mod = readl ( i2s - > addr + I2SMOD ) ;
mod = ( mod & ~ mask ) | val ;
2010-11-22 15:36:59 +09:00
writel ( mod , i2s - > addr + I2SMOD ) ;
2016-02-18 15:47:13 +00:00
spin_unlock_irqrestore ( i2s - > lock , flags ) ;
2016-12-29 12:34:04 +01:00
done :
pm_runtime_put ( dai - > dev ) ;
2010-11-22 15:36:59 +09:00
return 0 ;
2016-12-29 12:34:04 +01:00
err :
pm_runtime_put ( dai - > dev ) ;
return ret ;
2010-11-22 15:36:59 +09:00
}
static int i2s_set_fmt ( struct snd_soc_dai * dai ,
unsigned int fmt )
{
struct i2s_dai * i2s = to_info ( dai ) ;
2014-11-07 12:24:40 +05:30
int lrp_shift , sdf_shift , sdf_mask , lrp_rlow , mod_slave ;
2015-01-14 19:42:36 +01:00
u32 mod , tmp = 0 ;
2016-02-18 15:47:13 +00:00
unsigned long flags ;
2010-11-22 15:36:59 +09:00
2014-11-07 12:24:40 +05:30
lrp_shift = i2s - > variant_regs - > lrp_off ;
sdf_shift = i2s - > variant_regs - > sdf_off ;
mod_slave = 1 < < i2s - > variant_regs - > mss_off ;
2013-08-12 15:19:52 +05:30
2013-07-26 19:06:48 +05:30
sdf_mask = MOD_SDF_MASK < < sdf_shift ;
lrp_rlow = MOD_LR_RLOW < < lrp_shift ;
2010-11-22 15:36:59 +09:00
/* Format is priority */
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_RIGHT_J :
2013-07-26 19:06:48 +05:30
tmp | = lrp_rlow ;
tmp | = ( MOD_SDF_MSB < < sdf_shift ) ;
2010-11-22 15:36:59 +09:00
break ;
case SND_SOC_DAIFMT_LEFT_J :
2013-07-26 19:06:48 +05:30
tmp | = lrp_rlow ;
tmp | = ( MOD_SDF_LSB < < sdf_shift ) ;
2010-11-22 15:36:59 +09:00
break ;
case SND_SOC_DAIFMT_I2S :
2013-07-26 19:06:48 +05:30
tmp | = ( MOD_SDF_IIS < < sdf_shift ) ;
2010-11-22 15:36:59 +09:00
break ;
default :
dev_err ( & i2s - > pdev - > dev , " Format not supported \n " ) ;
return - EINVAL ;
}
/*
* INV flag is relative to the FORMAT flag - if set it simply
* flips the polarity specified by the Standard
*/
switch ( fmt & SND_SOC_DAIFMT_INV_MASK ) {
case SND_SOC_DAIFMT_NB_NF :
break ;
case SND_SOC_DAIFMT_NB_IF :
2013-07-26 19:06:48 +05:30
if ( tmp & lrp_rlow )
tmp & = ~ lrp_rlow ;
2010-11-22 15:36:59 +09:00
else
2013-07-26 19:06:48 +05:30
tmp | = lrp_rlow ;
2010-11-22 15:36:59 +09:00
break ;
default :
dev_err ( & i2s - > pdev - > dev , " Polarity not supported \n " ) ;
return - EINVAL ;
}
switch ( fmt & SND_SOC_DAIFMT_MASTER_MASK ) {
case SND_SOC_DAIFMT_CBM_CFM :
2014-11-07 12:24:40 +05:30
tmp | = mod_slave ;
2010-11-22 15:36:59 +09:00
break ;
case SND_SOC_DAIFMT_CBS_CFS :
2018-02-05 16:43:56 +01:00
/*
* Set default source clock in Master mode , only when the
* CLK_I2S_RCLK_SRC clock is not exposed so we ensure any
* clock configuration assigned in DT is not overwritten .
*/
if ( i2s - > rclk_srcrate = = 0 & & i2s - > clk_data . clks = = NULL )
2010-11-22 15:36:59 +09:00
i2s_set_sysclk ( dai , SAMSUNG_I2S_RCLKSRC_0 ,
0 , SND_SOC_CLOCK_IN ) ;
break ;
default :
dev_err ( & i2s - > pdev - > dev , " master/slave format not supported \n " ) ;
return - EINVAL ;
}
2016-12-29 12:34:04 +01:00
pm_runtime_get_sync ( dai - > dev ) ;
2016-02-18 15:47:13 +00:00
spin_lock_irqsave ( i2s - > lock , flags ) ;
2015-01-14 19:42:36 +01:00
mod = readl ( i2s - > addr + I2SMOD ) ;
2013-07-26 19:06:48 +05:30
/*
* Don ' t change the I2S mode if any controller is active on this
* channel .
*/
2010-11-22 15:36:59 +09:00
if ( any_active ( i2s ) & &
2014-11-07 12:24:40 +05:30
( ( mod & ( sdf_mask | lrp_rlow | mod_slave ) ) ! = tmp ) ) {
2016-02-18 15:47:13 +00:00
spin_unlock_irqrestore ( i2s - > lock , flags ) ;
2016-12-29 12:34:04 +01:00
pm_runtime_put ( dai - > dev ) ;
2010-11-22 15:36:59 +09:00
dev_err ( & i2s - > pdev - > dev ,
" %s:%d Other DAI busy \n " , __func__ , __LINE__ ) ;
return - EAGAIN ;
}
2014-11-07 12:24:40 +05:30
mod & = ~ ( sdf_mask | lrp_rlow | mod_slave ) ;
2010-11-22 15:36:59 +09:00
mod | = tmp ;
writel ( mod , i2s - > addr + I2SMOD ) ;
2016-02-18 15:47:13 +00:00
spin_unlock_irqrestore ( i2s - > lock , flags ) ;
2016-12-29 12:34:04 +01:00
pm_runtime_put ( dai - > dev ) ;
2010-11-22 15:36:59 +09:00
return 0 ;
}
static int i2s_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params , struct snd_soc_dai * dai )
{
struct i2s_dai * i2s = to_info ( dai ) ;
2015-01-14 19:42:36 +01:00
u32 mod , mask = 0 , val = 0 ;
2016-02-18 15:47:13 +00:00
unsigned long flags ;
2010-11-22 15:36:59 +09:00
2016-12-29 12:34:04 +01:00
WARN_ON ( ! pm_runtime_active ( dai - > dev ) ) ;
2010-11-22 15:36:59 +09:00
if ( ! is_secondary ( i2s ) )
2015-01-14 19:42:36 +01:00
mask | = ( MOD_DC2_EN | MOD_DC1_EN ) ;
2010-11-22 15:36:59 +09:00
switch ( params_channels ( params ) ) {
case 6 :
2015-01-14 19:42:36 +01:00
val | = MOD_DC2_EN ;
2010-11-22 15:36:59 +09:00
case 4 :
2015-01-14 19:42:36 +01:00
val | = MOD_DC1_EN ;
2010-11-22 15:36:59 +09:00
break ;
case 2 :
2012-03-16 15:40:53 +09:00
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
2016-08-04 11:30:26 +02:00
i2s - > dma_playback . addr_width = 4 ;
2012-03-16 15:40:53 +09:00
else
2016-08-04 11:30:26 +02:00
i2s - > dma_capture . addr_width = 4 ;
2012-03-16 15:40:53 +09:00
break ;
case 1 :
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
2016-08-04 11:30:26 +02:00
i2s - > dma_playback . addr_width = 2 ;
2012-03-16 15:40:53 +09:00
else
2016-08-04 11:30:26 +02:00
i2s - > dma_capture . addr_width = 2 ;
2012-03-16 15:40:53 +09:00
2010-11-22 15:36:59 +09:00
break ;
default :
dev_err ( & i2s - > pdev - > dev , " %d channels not supported \n " ,
params_channels ( params ) ) ;
return - EINVAL ;
}
if ( is_secondary ( i2s ) )
2015-01-14 19:42:36 +01:00
mask | = MOD_BLCS_MASK ;
2010-11-22 15:36:59 +09:00
else
2015-01-14 19:42:36 +01:00
mask | = MOD_BLCP_MASK ;
2010-11-22 15:36:59 +09:00
if ( is_manager ( i2s ) )
2015-01-14 19:42:36 +01:00
mask | = MOD_BLC_MASK ;
2010-11-22 15:36:59 +09:00
2014-05-23 17:35:39 +05:30
switch ( params_width ( params ) ) {
case 8 :
2010-11-22 15:36:59 +09:00
if ( is_secondary ( i2s ) )
2015-01-14 19:42:36 +01:00
val | = MOD_BLCS_8BIT ;
2010-11-22 15:36:59 +09:00
else
2015-01-14 19:42:36 +01:00
val | = MOD_BLCP_8BIT ;
2010-11-22 15:36:59 +09:00
if ( is_manager ( i2s ) )
2015-01-14 19:42:36 +01:00
val | = MOD_BLC_8BIT ;
2010-11-22 15:36:59 +09:00
break ;
2014-05-23 17:35:39 +05:30
case 16 :
2010-11-22 15:36:59 +09:00
if ( is_secondary ( i2s ) )
2015-01-14 19:42:36 +01:00
val | = MOD_BLCS_16BIT ;
2010-11-22 15:36:59 +09:00
else
2015-01-14 19:42:36 +01:00
val | = MOD_BLCP_16BIT ;
2010-11-22 15:36:59 +09:00
if ( is_manager ( i2s ) )
2015-01-14 19:42:36 +01:00
val | = MOD_BLC_16BIT ;
2010-11-22 15:36:59 +09:00
break ;
2014-05-23 17:35:39 +05:30
case 24 :
2010-11-22 15:36:59 +09:00
if ( is_secondary ( i2s ) )
2015-01-14 19:42:36 +01:00
val | = MOD_BLCS_24BIT ;
2010-11-22 15:36:59 +09:00
else
2015-01-14 19:42:36 +01:00
val | = MOD_BLCP_24BIT ;
2010-11-22 15:36:59 +09:00
if ( is_manager ( i2s ) )
2015-01-14 19:42:36 +01:00
val | = MOD_BLC_24BIT ;
2010-11-22 15:36:59 +09:00
break ;
default :
dev_err ( & i2s - > pdev - > dev , " Format(%d) not supported \n " ,
params_format ( params ) ) ;
return - EINVAL ;
}
2015-01-14 19:42:36 +01:00
2016-02-18 15:47:13 +00:00
spin_lock_irqsave ( i2s - > lock , flags ) ;
2015-01-14 19:42:36 +01:00
mod = readl ( i2s - > addr + I2SMOD ) ;
mod = ( mod & ~ mask ) | val ;
2010-11-22 15:36:59 +09:00
writel ( mod , i2s - > addr + I2SMOD ) ;
2016-02-18 15:47:13 +00:00
spin_unlock_irqrestore ( i2s - > lock , flags ) ;
2010-11-22 15:36:59 +09:00
2016-08-04 11:30:26 +02:00
snd_soc_dai_init_dma_data ( dai , & i2s - > dma_playback , & i2s - > dma_capture ) ;
2013-12-05 14:14:52 +00:00
2010-11-22 15:36:59 +09:00
i2s - > frmclk = params_rate ( params ) ;
return 0 ;
}
/* We set constraints on the substream acc to the version of I2S */
static int i2s_startup ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
struct i2s_dai * i2s = to_info ( dai ) ;
2015-01-14 19:42:33 +01:00
struct i2s_dai * other = get_other_dai ( i2s ) ;
2010-11-22 15:36:59 +09:00
unsigned long flags ;
2016-12-29 12:34:04 +01:00
pm_runtime_get_sync ( dai - > dev ) ;
2010-11-22 15:36:59 +09:00
spin_lock_irqsave ( & lock , flags ) ;
i2s - > mode | = DAI_OPENED ;
if ( is_manager ( other ) )
i2s - > mode & = ~ DAI_MANAGER ;
else
i2s - > mode | = DAI_MANAGER ;
2013-01-24 18:05:31 +05:30
if ( ! any_active ( i2s ) & & ( i2s - > quirks & QUIRK_NEED_RSTCLR ) )
writel ( CON_RSTCLR , i2s - > addr + I2SCON ) ;
2010-11-22 15:36:59 +09:00
spin_unlock_irqrestore ( & lock , flags ) ;
return 0 ;
}
static void i2s_shutdown ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
struct i2s_dai * i2s = to_info ( dai ) ;
2015-01-14 19:42:33 +01:00
struct i2s_dai * other = get_other_dai ( i2s ) ;
2010-11-22 15:36:59 +09:00
unsigned long flags ;
spin_lock_irqsave ( & lock , flags ) ;
i2s - > mode & = ~ DAI_OPENED ;
i2s - > mode & = ~ DAI_MANAGER ;
2015-01-14 19:42:39 +01:00
if ( is_opened ( other ) )
2010-11-22 15:36:59 +09:00
other - > mode | = DAI_MANAGER ;
2015-01-14 19:42:39 +01:00
2010-11-22 15:36:59 +09:00
/* Reset any constraint on RFS and BFS */
i2s - > rfs = 0 ;
i2s - > bfs = 0 ;
spin_unlock_irqrestore ( & lock , flags ) ;
2016-12-29 12:34:04 +01:00
pm_runtime_put ( dai - > dev ) ;
2010-11-22 15:36:59 +09:00
}
static int config_setup ( struct i2s_dai * i2s )
{
2015-01-14 19:42:33 +01:00
struct i2s_dai * other = get_other_dai ( i2s ) ;
2010-11-22 15:36:59 +09:00
unsigned rfs , bfs , blc ;
u32 psr ;
blc = get_blc ( i2s ) ;
bfs = i2s - > bfs ;
if ( ! bfs & & other )
bfs = other - > bfs ;
/* Select least possible multiple(2) if no constraint set */
if ( ! bfs )
bfs = blc * 2 ;
rfs = i2s - > rfs ;
if ( ! rfs & & other )
rfs = other - > rfs ;
if ( ( rfs = = 256 | | rfs = = 512 ) & & ( blc = = 24 ) ) {
dev_err ( & i2s - > pdev - > dev ,
" %d-RFS not supported for 24-blc \n " , rfs ) ;
return - EINVAL ;
}
if ( ! rfs ) {
if ( bfs = = 16 | | bfs = = 32 )
rfs = 256 ;
else
rfs = 384 ;
}
/* If already setup and running */
if ( any_active ( i2s ) & & ( get_rfs ( i2s ) ! = rfs | | get_bfs ( i2s ) ! = bfs ) ) {
dev_err ( & i2s - > pdev - > dev ,
" %s:%d Other DAI busy \n " , __func__ , __LINE__ ) ;
return - EAGAIN ;
}
set_bfs ( i2s , bfs ) ;
set_rfs ( i2s , rfs ) ;
2013-07-11 12:38:25 +05:30
/* Don't bother with PSR in Slave mode */
if ( is_slave ( i2s ) )
return 0 ;
2010-11-22 15:36:59 +09:00
if ( ! ( i2s - > quirks & QUIRK_NO_MUXPSR ) ) {
2018-02-05 16:43:56 +01:00
struct clk * rclksrc = i2s - > clk_table [ CLK_I2S_RCLK_SRC ] ;
if ( i2s - > rclk_srcrate = = 0 & & rclksrc & & ! IS_ERR ( rclksrc ) )
i2s - > rclk_srcrate = clk_get_rate ( rclksrc ) ;
2010-11-22 15:36:59 +09:00
psr = i2s - > rclk_srcrate / i2s - > frmclk / rfs ;
writel ( ( ( psr - 1 ) < < 8 ) | PSR_PSREN , i2s - > addr + I2SPSR ) ;
dev_dbg ( & i2s - > pdev - > dev ,
" RCLK_SRC=%luHz PSR=%u, RCLK=%dfs, BCLK=%dfs \n " ,
i2s - > rclk_srcrate , psr , rfs , bfs ) ;
}
return 0 ;
}
static int i2s_trigger ( struct snd_pcm_substream * substream ,
int cmd , struct snd_soc_dai * dai )
{
int capture = ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE ) ;
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
struct i2s_dai * i2s = to_info ( rtd - > cpu_dai ) ;
unsigned long flags ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
2016-12-29 12:34:04 +01:00
pm_runtime_get_sync ( dai - > dev ) ;
2015-01-14 19:42:35 +01:00
spin_lock_irqsave ( i2s - > lock , flags ) ;
2010-11-22 15:36:59 +09:00
if ( config_setup ( i2s ) ) {
2015-01-14 19:42:35 +01:00
spin_unlock_irqrestore ( i2s - > lock , flags ) ;
2010-11-22 15:36:59 +09:00
return - EINVAL ;
}
if ( capture )
i2s_rxctrl ( i2s , 1 ) ;
else
i2s_txctrl ( i2s , 1 ) ;
2015-01-14 19:42:35 +01:00
spin_unlock_irqrestore ( i2s - > lock , flags ) ;
2010-11-22 15:36:59 +09:00
break ;
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
2015-01-14 19:42:35 +01:00
spin_lock_irqsave ( i2s - > lock , flags ) ;
2010-11-22 15:36:59 +09:00
2012-02-25 16:42:34 +05:30
if ( capture ) {
2010-11-22 15:36:59 +09:00
i2s_rxctrl ( i2s , 0 ) ;
2010-12-20 11:05:47 +09:00
i2s_fifo ( i2s , FIC_RXFLUSH ) ;
2012-02-25 16:42:34 +05:30
} else {
i2s_txctrl ( i2s , 0 ) ;
2010-12-20 11:05:47 +09:00
i2s_fifo ( i2s , FIC_TXFLUSH ) ;
2012-02-25 16:42:34 +05:30
}
2010-12-20 11:05:47 +09:00
2015-01-14 19:42:35 +01:00
spin_unlock_irqrestore ( i2s - > lock , flags ) ;
2016-12-29 12:34:04 +01:00
pm_runtime_put ( dai - > dev ) ;
2010-11-22 15:36:59 +09:00
break ;
}
return 0 ;
}
static int i2s_set_clkdiv ( struct snd_soc_dai * dai ,
int div_id , int div )
{
struct i2s_dai * i2s = to_info ( dai ) ;
2015-01-14 19:42:33 +01:00
struct i2s_dai * other = get_other_dai ( i2s ) ;
2010-11-22 15:36:59 +09:00
switch ( div_id ) {
case SAMSUNG_I2S_DIV_BCLK :
2016-12-29 12:34:04 +01:00
pm_runtime_get_sync ( dai - > dev ) ;
2010-11-22 15:36:59 +09:00
if ( ( any_active ( i2s ) & & div & & ( get_bfs ( i2s ) ! = div ) )
| | ( other & & other - > bfs & & ( other - > bfs ! = div ) ) ) {
2016-12-29 12:34:04 +01:00
pm_runtime_put ( dai - > dev ) ;
2010-11-22 15:36:59 +09:00
dev_err ( & i2s - > pdev - > dev ,
" %s:%d Other DAI busy \n " , __func__ , __LINE__ ) ;
return - EAGAIN ;
}
i2s - > bfs = div ;
2016-12-29 12:34:04 +01:00
pm_runtime_put ( dai - > dev ) ;
2010-11-22 15:36:59 +09:00
break ;
default :
dev_err ( & i2s - > pdev - > dev ,
" Invalid clock divider(%d) \n " , div_id ) ;
return - EINVAL ;
}
return 0 ;
}
static snd_pcm_sframes_t
i2s_delay ( struct snd_pcm_substream * substream , struct snd_soc_dai * dai )
{
struct i2s_dai * i2s = to_info ( dai ) ;
u32 reg = readl ( i2s - > addr + I2SFIC ) ;
snd_pcm_sframes_t delay ;
2014-11-07 12:24:40 +05:30
const struct samsung_i2s_variant_regs * i2s_regs = i2s - > variant_regs ;
2010-11-22 15:36:59 +09:00
2016-12-29 12:34:04 +01:00
WARN_ON ( ! pm_runtime_active ( dai - > dev ) ) ;
2010-11-22 15:36:59 +09:00
if ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE )
delay = FIC_RXCOUNT ( reg ) ;
else if ( is_secondary ( i2s ) )
delay = FICS_TXCOUNT ( readl ( i2s - > addr + I2SFICS ) ) ;
else
2014-11-07 12:24:40 +05:30
delay = ( reg > > i2s_regs - > ftx0cnt_off ) & 0x7f ;
2010-11-22 15:36:59 +09:00
return delay ;
}
# ifdef CONFIG_PM
static int i2s_suspend ( struct snd_soc_dai * dai )
{
2016-12-29 12:34:05 +01:00
return pm_runtime_force_suspend ( dai - > dev ) ;
2010-11-22 15:36:59 +09:00
}
static int i2s_resume ( struct snd_soc_dai * dai )
{
2016-12-29 12:34:05 +01:00
return pm_runtime_force_resume ( dai - > dev ) ;
2010-11-22 15:36:59 +09:00
}
# else
# define i2s_suspend NULL
# define i2s_resume NULL
# endif
static int samsung_i2s_dai_probe ( struct snd_soc_dai * dai )
{
struct i2s_dai * i2s = to_info ( dai ) ;
2015-01-14 19:42:33 +01:00
struct i2s_dai * other = get_other_dai ( i2s ) ;
2015-01-14 19:42:36 +01:00
unsigned long flags ;
2010-11-22 15:36:59 +09:00
2016-12-29 12:34:04 +01:00
pm_runtime_get_sync ( dai - > dev ) ;
2015-01-14 19:42:31 +01:00
if ( is_secondary ( i2s ) ) { /* If this is probe on the secondary DAI */
2016-08-04 11:30:26 +02:00
snd_soc_dai_init_dma_data ( dai , & other - > sec_dai - > dma_playback ,
2013-10-19 15:23:15 +01:00
NULL ) ;
2015-01-14 19:42:34 +01:00
} else {
2016-08-04 11:30:26 +02:00
snd_soc_dai_init_dma_data ( dai , & i2s - > dma_playback ,
2015-01-14 19:42:34 +01:00
& i2s - > dma_capture ) ;
2013-10-17 21:18:40 +01:00
2015-01-14 19:42:34 +01:00
if ( i2s - > quirks & QUIRK_NEED_RSTCLR )
writel ( CON_RSTCLR , i2s - > addr + I2SCON ) ;
2010-11-22 15:36:59 +09:00
2015-01-14 19:42:34 +01:00
if ( i2s - > quirks & QUIRK_SUPPORTS_IDMA )
idma_reg_addr_init ( i2s - > addr ,
2016-08-04 11:30:26 +02:00
i2s - > sec_dai - > idma_playback . addr ) ;
2015-01-14 19:42:34 +01:00
}
2011-07-20 17:07:12 +09:00
2010-11-22 15:36:59 +09:00
/* Reset any constraint on RFS and BFS */
i2s - > rfs = 0 ;
i2s - > bfs = 0 ;
2014-04-23 13:34:24 +05:30
i2s - > rclk_srcrate = 0 ;
2015-01-14 19:42:36 +01:00
spin_lock_irqsave ( i2s - > lock , flags ) ;
2010-11-22 15:36:59 +09:00
i2s_txctrl ( i2s , 0 ) ;
i2s_rxctrl ( i2s , 0 ) ;
i2s_fifo ( i2s , FIC_TXFLUSH ) ;
i2s_fifo ( other , FIC_TXFLUSH ) ;
i2s_fifo ( i2s , FIC_RXFLUSH ) ;
2015-01-14 19:42:36 +01:00
spin_unlock_irqrestore ( i2s - > lock , flags ) ;
2010-11-22 15:36:59 +09:00
/* Gate CDCLK by default */
if ( ! is_opened ( other ) )
i2s_set_sysclk ( dai , SAMSUNG_I2S_CDCLK ,
0 , SND_SOC_CLOCK_IN ) ;
2016-12-29 12:34:04 +01:00
pm_runtime_put ( dai - > dev ) ;
2010-11-22 15:36:59 +09:00
return 0 ;
}
static int samsung_i2s_dai_remove ( struct snd_soc_dai * dai )
{
struct i2s_dai * i2s = snd_soc_dai_get_drvdata ( dai ) ;
2016-10-21 14:18:48 +01:00
unsigned long flags ;
2010-11-22 15:36:59 +09:00
2016-12-29 12:34:04 +01:00
pm_runtime_get_sync ( dai - > dev ) ;
2015-01-14 19:42:32 +01:00
if ( ! is_secondary ( i2s ) ) {
2015-01-14 19:42:36 +01:00
if ( i2s - > quirks & QUIRK_NEED_RSTCLR ) {
2016-10-21 14:18:48 +01:00
spin_lock_irqsave ( i2s - > lock , flags ) ;
2010-11-22 15:36:59 +09:00
writel ( 0 , i2s - > addr + I2SCON ) ;
2016-10-21 14:18:48 +01:00
spin_unlock_irqrestore ( i2s - > lock , flags ) ;
2015-01-14 19:42:36 +01:00
}
2010-11-22 15:36:59 +09:00
}
2016-12-29 12:34:04 +01:00
pm_runtime_put ( dai - > dev ) ;
2010-11-22 15:36:59 +09:00
return 0 ;
}
2011-11-23 11:40:40 +01:00
static const struct snd_soc_dai_ops samsung_i2s_dai_ops = {
2010-11-22 15:36:59 +09:00
. trigger = i2s_trigger ,
. hw_params = i2s_hw_params ,
. set_fmt = i2s_set_fmt ,
. set_clkdiv = i2s_set_clkdiv ,
. set_sysclk = i2s_set_sysclk ,
. startup = i2s_startup ,
. shutdown = i2s_shutdown ,
. delay = i2s_delay ,
} ;
2013-03-21 03:35:55 -07:00
static const struct snd_soc_component_driver samsung_i2s_component = {
. name = " samsung-i2s " ,
} ;
2010-11-22 15:36:59 +09:00
# define SAMSUNG_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE )
2017-07-07 10:31:10 +09:00
static struct i2s_dai * i2s_alloc_dai ( struct platform_device * pdev ,
const struct samsung_i2s_dai_data * i2s_dai_data ,
bool sec )
2010-11-22 15:36:59 +09:00
{
struct i2s_dai * i2s ;
2011-12-03 20:30:37 +00:00
i2s = devm_kzalloc ( & pdev - > dev , sizeof ( struct i2s_dai ) , GFP_KERNEL ) ;
2010-11-22 15:36:59 +09:00
if ( i2s = = NULL )
return NULL ;
i2s - > pdev = pdev ;
i2s - > pri_dai = NULL ;
i2s - > sec_dai = NULL ;
2017-09-06 10:04:13 +09:00
i2s - > i2s_dai_drv . id = 1 ;
2010-11-22 15:36:59 +09:00
i2s - > i2s_dai_drv . symmetric_rates = 1 ;
i2s - > i2s_dai_drv . probe = samsung_i2s_dai_probe ;
i2s - > i2s_dai_drv . remove = samsung_i2s_dai_remove ;
i2s - > i2s_dai_drv . ops = & samsung_i2s_dai_ops ;
i2s - > i2s_dai_drv . suspend = i2s_suspend ;
i2s - > i2s_dai_drv . resume = i2s_resume ;
2013-09-11 15:27:29 +01:00
i2s - > i2s_dai_drv . playback . channels_min = 1 ;
2010-11-22 15:36:59 +09:00
i2s - > i2s_dai_drv . playback . channels_max = 2 ;
2017-07-07 10:31:10 +09:00
i2s - > i2s_dai_drv . playback . rates = i2s_dai_data - > pcm_rates ;
2010-11-22 15:36:59 +09:00
i2s - > i2s_dai_drv . playback . formats = SAMSUNG_I2S_FMTS ;
if ( ! sec ) {
2017-09-06 10:04:13 +09:00
i2s - > i2s_dai_drv . name = SAMSUNG_I2S_DAI ;
2012-03-16 15:40:53 +09:00
i2s - > i2s_dai_drv . capture . channels_min = 1 ;
2010-11-22 15:36:59 +09:00
i2s - > i2s_dai_drv . capture . channels_max = 2 ;
2017-07-07 10:31:10 +09:00
i2s - > i2s_dai_drv . capture . rates = i2s_dai_data - > pcm_rates ;
2010-11-22 15:36:59 +09:00
i2s - > i2s_dai_drv . capture . formats = SAMSUNG_I2S_FMTS ;
2017-09-06 10:04:13 +09:00
} else {
i2s - > i2s_dai_drv . name = SAMSUNG_I2S_DAI_SEC ;
2013-04-02 16:53:02 +05:30
}
2010-11-22 15:36:59 +09:00
return i2s ;
}
2014-12-13 00:42:18 +01:00
# ifdef CONFIG_PM
2013-01-30 17:41:04 +05:30
static int i2s_runtime_suspend ( struct device * dev )
{
struct i2s_dai * i2s = dev_get_drvdata ( dev ) ;
2016-12-29 12:34:05 +01:00
i2s - > suspend_i2smod = readl ( i2s - > addr + I2SMOD ) ;
i2s - > suspend_i2scon = readl ( i2s - > addr + I2SCON ) ;
i2s - > suspend_i2spsr = readl ( i2s - > addr + I2SPSR ) ;
2016-12-29 12:34:06 +01:00
if ( i2s - > op_clk )
clk_disable_unprepare ( i2s - > op_clk ) ;
2013-01-30 17:41:04 +05:30
clk_disable_unprepare ( i2s - > clk ) ;
return 0 ;
}
static int i2s_runtime_resume ( struct device * dev )
{
struct i2s_dai * i2s = dev_get_drvdata ( dev ) ;
2017-07-25 15:44:31 +05:30
int ret ;
2013-01-30 17:41:04 +05:30
2017-07-25 15:44:31 +05:30
ret = clk_prepare_enable ( i2s - > clk ) ;
if ( ret )
return ret ;
if ( i2s - > op_clk ) {
ret = clk_prepare_enable ( i2s - > op_clk ) ;
if ( ret ) {
clk_disable_unprepare ( i2s - > clk ) ;
return ret ;
}
}
2013-01-30 17:41:04 +05:30
2016-12-29 12:34:05 +01:00
writel ( i2s - > suspend_i2scon , i2s - > addr + I2SCON ) ;
writel ( i2s - > suspend_i2smod , i2s - > addr + I2SMOD ) ;
writel ( i2s - > suspend_i2spsr , i2s - > addr + I2SPSR ) ;
2013-01-30 17:41:04 +05:30
return 0 ;
}
2014-12-13 00:42:18 +01:00
# endif /* CONFIG_PM */
2013-01-30 17:41:04 +05:30
2015-01-14 19:42:39 +01:00
static void i2s_unregister_clocks ( struct i2s_dai * i2s )
{
int i ;
for ( i = 0 ; i < i2s - > clk_data . clk_num ; i + + ) {
if ( ! IS_ERR ( i2s - > clk_table [ i ] ) )
clk_unregister ( i2s - > clk_table [ i ] ) ;
}
}
static void i2s_unregister_clock_provider ( struct platform_device * pdev )
{
struct i2s_dai * i2s = dev_get_drvdata ( & pdev - > dev ) ;
of_clk_del_provider ( pdev - > dev . of_node ) ;
i2s_unregister_clocks ( i2s ) ;
}
static int i2s_register_clock_provider ( struct platform_device * pdev )
{
2018-02-12 17:15:33 +01:00
const char * const i2s_clk_desc [ ] = { " cdclk " , " rclk_src " , " prescaler " } ;
2015-01-14 19:42:39 +01:00
const char * clk_name [ 2 ] = { " i2s_opclk0 " , " i2s_opclk1 " } ;
const char * p_names [ 2 ] = { NULL } ;
2018-02-12 17:15:33 +01:00
struct device * dev = & pdev - > dev ;
struct i2s_dai * i2s = dev_get_drvdata ( dev ) ;
2015-01-14 19:42:39 +01:00
const struct samsung_i2s_variant_regs * reg_info = i2s - > variant_regs ;
2018-02-12 17:15:33 +01:00
const char * i2s_clk_name [ ARRAY_SIZE ( i2s_clk_desc ) ] ;
2015-01-14 19:42:39 +01:00
struct clk * rclksrc ;
int ret , i ;
/* Register the clock provider only if it's expected in the DTB */
if ( ! of_find_property ( dev - > of_node , " #clock-cells " , NULL ) )
return 0 ;
/* Get the RCLKSRC mux clock parent clock names */
for ( i = 0 ; i < ARRAY_SIZE ( p_names ) ; i + + ) {
rclksrc = clk_get ( dev , clk_name [ i ] ) ;
if ( IS_ERR ( rclksrc ) )
continue ;
p_names [ i ] = __clk_get_name ( rclksrc ) ;
clk_put ( rclksrc ) ;
}
2018-02-12 17:15:33 +01:00
for ( i = 0 ; i < ARRAY_SIZE ( i2s_clk_desc ) ; i + + ) {
i2s_clk_name [ i ] = devm_kasprintf ( dev , GFP_KERNEL , " %s_%s " ,
dev_name ( dev ) , i2s_clk_desc [ i ] ) ;
if ( ! i2s_clk_name [ i ] )
return - ENOMEM ;
}
2015-01-14 19:42:39 +01:00
if ( ! ( i2s - > quirks & QUIRK_NO_MUXPSR ) ) {
/* Activate the prescaler */
u32 val = readl ( i2s - > addr + I2SPSR ) ;
writel ( val | PSR_PSREN , i2s - > addr + I2SPSR ) ;
2016-12-29 12:34:07 +01:00
i2s - > clk_table [ CLK_I2S_RCLK_SRC ] = clk_register_mux ( dev ,
2018-02-12 17:15:33 +01:00
i2s_clk_name [ CLK_I2S_RCLK_SRC ] , p_names ,
ARRAY_SIZE ( p_names ) ,
2015-01-14 19:42:39 +01:00
CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT ,
i2s - > addr + I2SMOD , reg_info - > rclksrc_off ,
1 , 0 , i2s - > lock ) ;
2016-12-29 12:34:07 +01:00
i2s - > clk_table [ CLK_I2S_RCLK_PSR ] = clk_register_divider ( dev ,
2018-02-12 17:15:33 +01:00
i2s_clk_name [ CLK_I2S_RCLK_PSR ] ,
i2s_clk_name [ CLK_I2S_RCLK_SRC ] ,
2015-01-14 19:42:39 +01:00
CLK_SET_RATE_PARENT ,
i2s - > addr + I2SPSR , 8 , 6 , 0 , i2s - > lock ) ;
2018-02-12 17:15:33 +01:00
p_names [ 0 ] = i2s_clk_name [ CLK_I2S_RCLK_PSR ] ;
2015-01-14 19:42:39 +01:00
i2s - > clk_data . clk_num = 2 ;
}
2018-02-12 17:15:33 +01:00
i2s - > clk_table [ CLK_I2S_CDCLK ] = clk_register_gate ( dev ,
i2s_clk_name [ CLK_I2S_CDCLK ] , p_names [ 0 ] ,
CLK_SET_RATE_PARENT ,
2015-01-14 19:42:39 +01:00
i2s - > addr + I2SMOD , reg_info - > cdclkcon_off ,
CLK_GATE_SET_TO_DISABLE , i2s - > lock ) ;
i2s - > clk_data . clk_num + = 1 ;
i2s - > clk_data . clks = i2s - > clk_table ;
ret = of_clk_add_provider ( dev - > of_node , of_clk_src_onecell_get ,
& i2s - > clk_data ) ;
if ( ret < 0 ) {
dev_err ( dev , " failed to add clock provider: %d \n " , ret ) ;
i2s_unregister_clocks ( i2s ) ;
}
return ret ;
}
2012-12-07 09:26:15 -05:00
static int samsung_i2s_probe ( struct platform_device * pdev )
2010-11-22 15:36:59 +09:00
{
struct i2s_dai * pri_dai , * sec_dai = NULL ;
2013-01-18 17:17:01 +05:30
struct s3c_audio_pdata * i2s_pdata = pdev - > dev . platform_data ;
2010-11-22 15:36:59 +09:00
struct resource * res ;
2013-01-18 17:17:01 +05:30
u32 regs_base , quirks = 0 , idma_addr = 0 ;
struct device_node * np = pdev - > dev . of_node ;
2013-08-12 15:19:51 +05:30
const struct samsung_i2s_dai_data * i2s_dai_data ;
2015-01-14 19:42:32 +01:00
int ret ;
2010-11-22 15:36:59 +09:00
2016-04-08 18:52:45 +02:00
if ( IS_ENABLED ( CONFIG_OF ) & & pdev - > dev . of_node )
i2s_dai_data = of_device_get_match_data ( & pdev - > dev ) ;
else
i2s_dai_data = ( struct samsung_i2s_dai_data * )
platform_get_device_id ( pdev ) - > driver_data ;
2013-01-18 17:17:00 +05:30
2017-07-07 10:31:10 +09:00
pri_dai = i2s_alloc_dai ( pdev , i2s_dai_data , false ) ;
2013-01-18 17:17:01 +05:30
if ( ! pri_dai ) {
dev_err ( & pdev - > dev , " Unable to alloc I2S_pri \n " ) ;
return - ENOMEM ;
2010-11-22 15:36:59 +09:00
}
2015-01-14 19:42:35 +01:00
spin_lock_init ( & pri_dai - > spinlock ) ;
pri_dai - > lock = & pri_dai - > spinlock ;
2013-01-18 17:17:01 +05:30
if ( ! np ) {
if ( i2s_pdata = = NULL ) {
dev_err ( & pdev - > dev , " Can't work without s3c_audio_pdata \n " ) ;
return - EINVAL ;
}
2016-08-04 11:30:26 +02:00
pri_dai - > dma_playback . filter_data = i2s_pdata - > dma_playback ;
pri_dai - > dma_capture . filter_data = i2s_pdata - > dma_capture ;
2015-11-18 22:31:11 +01:00
pri_dai - > filter = i2s_pdata - > dma_filter ;
2015-11-18 15:25:23 +01:00
2016-12-10 11:51:11 +02:00
quirks = i2s_pdata - > type . quirks ;
idma_addr = i2s_pdata - > type . idma_addr ;
2013-01-18 17:17:01 +05:30
} else {
2013-08-12 15:19:51 +05:30
quirks = i2s_dai_data - > quirks ;
2013-01-18 17:17:01 +05:30
if ( of_property_read_u32 ( np , " samsung,idma-addr " ,
& idma_addr ) ) {
2014-11-07 12:24:39 +05:30
if ( quirks & QUIRK_SUPPORTS_IDMA ) {
dev_info ( & pdev - > dev , " idma address is not " \
2013-01-18 17:17:01 +05:30
" specified " ) ;
}
}
}
2017-10-18 09:25:34 +02:00
quirks & = ~ ( QUIRK_SEC_DAI | QUIRK_SUPPORTS_IDMA ) ;
2010-11-22 15:36:59 +09:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2015-01-14 19:42:30 +01:00
pri_dai - > addr = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( pri_dai - > addr ) )
return PTR_ERR ( pri_dai - > addr ) ;
2010-11-22 15:36:59 +09:00
regs_base = res - > start ;
2015-01-14 19:42:31 +01:00
pri_dai - > clk = devm_clk_get ( & pdev - > dev , " iis " ) ;
if ( IS_ERR ( pri_dai - > clk ) ) {
dev_err ( & pdev - > dev , " Failed to get iis clock \n " ) ;
return PTR_ERR ( pri_dai - > clk ) ;
}
2015-01-14 19:42:32 +01:00
ret = clk_prepare_enable ( pri_dai - > clk ) ;
if ( ret ! = 0 ) {
dev_err ( & pdev - > dev , " failed to enable clock: %d \n " , ret ) ;
return ret ;
}
2016-08-04 11:30:26 +02:00
pri_dai - > dma_playback . addr = regs_base + I2STXD ;
pri_dai - > dma_capture . addr = regs_base + I2SRXD ;
2017-01-17 14:16:42 +01:00
pri_dai - > dma_playback . chan_name = " tx " ;
pri_dai - > dma_capture . chan_name = " rx " ;
2016-08-04 11:30:26 +02:00
pri_dai - > dma_playback . addr_width = 4 ;
pri_dai - > dma_capture . addr_width = 4 ;
2010-11-22 15:36:59 +09:00
pri_dai - > quirks = quirks ;
2014-11-07 12:24:40 +05:30
pri_dai - > variant_regs = i2s_dai_data - > i2s_variant_regs ;
2010-11-22 15:36:59 +09:00
if ( quirks & QUIRK_PRI_6CHAN )
pri_dai - > i2s_dai_drv . playback . channels_max = 6 ;
2016-10-27 12:34:02 +02:00
ret = samsung_asoc_dma_platform_register ( & pdev - > dev , pri_dai - > filter ,
NULL , NULL ) ;
if ( ret < 0 )
goto err_disable_clk ;
2016-12-29 12:34:03 +01:00
ret = devm_snd_soc_register_component ( & pdev - > dev ,
& samsung_i2s_component ,
& pri_dai - > i2s_dai_drv , 1 ) ;
if ( ret < 0 )
goto err_disable_clk ;
2010-11-22 15:36:59 +09:00
if ( quirks & QUIRK_SEC_DAI ) {
2017-07-07 10:31:10 +09:00
sec_dai = i2s_alloc_dai ( pdev , i2s_dai_data , true ) ;
2010-11-22 15:36:59 +09:00
if ( ! sec_dai ) {
dev_err ( & pdev - > dev , " Unable to alloc I2S_sec \n " ) ;
2016-08-23 15:16:42 +00:00
ret = - ENOMEM ;
goto err_disable_clk ;
2010-11-22 15:36:59 +09:00
}
2014-12-08 18:45:54 +01:00
2015-01-14 19:42:35 +01:00
sec_dai - > lock = & pri_dai - > spinlock ;
2014-12-08 18:45:54 +01:00
sec_dai - > variant_regs = pri_dai - > variant_regs ;
2016-08-04 11:30:26 +02:00
sec_dai - > dma_playback . addr = regs_base + I2STXDS ;
2017-01-17 14:16:42 +01:00
sec_dai - > dma_playback . chan_name = " tx-sec " ;
2013-01-18 17:17:01 +05:30
2015-11-18 22:31:11 +01:00
if ( ! np ) {
2016-08-04 11:30:26 +02:00
sec_dai - > dma_playback . filter_data = i2s_pdata - > dma_play_sec ;
2015-11-18 22:31:11 +01:00
sec_dai - > filter = i2s_pdata - > dma_filter ;
}
2013-01-18 17:17:01 +05:30
2016-08-04 11:30:26 +02:00
sec_dai - > dma_playback . addr_width = 4 ;
2015-01-14 19:42:30 +01:00
sec_dai - > addr = pri_dai - > addr ;
2015-01-14 19:42:31 +01:00
sec_dai - > clk = pri_dai - > clk ;
2010-11-22 15:36:59 +09:00
sec_dai - > quirks = quirks ;
2016-08-04 11:30:26 +02:00
sec_dai - > idma_playback . addr = idma_addr ;
2010-11-22 15:36:59 +09:00
sec_dai - > pri_dai = pri_dai ;
pri_dai - > sec_dai = sec_dai ;
2016-12-29 12:34:03 +01:00
ret = samsung_asoc_dma_platform_register ( & pdev - > dev ,
sec_dai - > filter , " tx-sec " , NULL ) ;
if ( ret < 0 )
goto err_disable_clk ;
ret = devm_snd_soc_register_component ( & pdev - > dev ,
& samsung_i2s_component ,
& sec_dai - > i2s_dai_drv , 1 ) ;
if ( ret < 0 )
goto err_disable_clk ;
2010-11-22 15:36:59 +09:00
}
2013-07-02 13:10:28 +01:00
if ( i2s_pdata & & i2s_pdata - > cfg_gpio & & i2s_pdata - > cfg_gpio ( pdev ) ) {
dev_err ( & pdev - > dev , " Unable to configure gpio \n " ) ;
2016-08-23 15:16:42 +00:00
ret = - EINVAL ;
goto err_disable_clk ;
2010-11-22 15:36:59 +09:00
}
2016-12-29 12:34:03 +01:00
dev_set_drvdata ( & pdev - > dev , pri_dai ) ;
2010-11-22 15:36:59 +09:00
2016-12-29 12:34:04 +01:00
pm_runtime_set_active ( & pdev - > dev ) ;
2011-12-08 16:45:03 +08:00
pm_runtime_enable ( & pdev - > dev ) ;
2016-07-21 20:03:49 +02:00
ret = i2s_register_clock_provider ( pdev ) ;
if ( ! ret )
return 0 ;
2012-12-07 13:59:21 +05:30
2016-07-21 20:03:49 +02:00
pm_runtime_disable ( & pdev - > dev ) ;
2016-08-23 15:16:42 +00:00
err_disable_clk :
clk_disable_unprepare ( pri_dai - > clk ) ;
2016-07-21 20:03:49 +02:00
return ret ;
2010-11-22 15:36:59 +09:00
}
2012-12-07 09:26:15 -05:00
static int samsung_i2s_remove ( struct platform_device * pdev )
2010-11-22 15:36:59 +09:00
{
2017-08-15 15:19:54 +03:00
struct i2s_dai * pri_dai ;
2010-11-22 15:36:59 +09:00
2016-12-29 12:34:03 +01:00
pri_dai = dev_get_drvdata ( & pdev - > dev ) ;
2010-11-22 15:36:59 +09:00
2016-12-29 12:34:04 +01:00
pm_runtime_get_sync ( & pdev - > dev ) ;
2016-12-29 12:34:03 +01:00
pm_runtime_disable ( & pdev - > dev ) ;
2015-01-14 19:42:32 +01:00
2016-12-29 12:34:03 +01:00
i2s_unregister_clock_provider ( pdev ) ;
clk_disable_unprepare ( pri_dai - > clk ) ;
2016-12-29 12:34:04 +01:00
pm_runtime_put_noidle ( & pdev - > dev ) ;
2010-11-22 15:36:59 +09:00
return 0 ;
}
2014-11-07 12:24:40 +05:30
static const struct samsung_i2s_variant_regs i2sv3_regs = {
. bfs_off = 1 ,
. rfs_off = 3 ,
. sdf_off = 5 ,
. txr_off = 8 ,
. rclksrc_off = 10 ,
. mss_off = 11 ,
. cdclkcon_off = 12 ,
. lrp_off = 7 ,
. bfs_mask = 0x3 ,
. rfs_mask = 0x3 ,
. ftx0cnt_off = 8 ,
} ;
static const struct samsung_i2s_variant_regs i2sv6_regs = {
. bfs_off = 0 ,
. rfs_off = 4 ,
. sdf_off = 6 ,
. txr_off = 8 ,
. rclksrc_off = 10 ,
. mss_off = 11 ,
. cdclkcon_off = 12 ,
. lrp_off = 15 ,
. bfs_mask = 0xf ,
. rfs_mask = 0x3 ,
. ftx0cnt_off = 8 ,
} ;
static const struct samsung_i2s_variant_regs i2sv7_regs = {
. bfs_off = 0 ,
. rfs_off = 4 ,
. sdf_off = 7 ,
. txr_off = 9 ,
. rclksrc_off = 11 ,
. mss_off = 12 ,
. cdclkcon_off = 22 ,
. lrp_off = 15 ,
. bfs_mask = 0xf ,
. rfs_mask = 0x7 ,
. ftx0cnt_off = 0 ,
} ;
static const struct samsung_i2s_variant_regs i2sv5_i2s1_regs = {
. bfs_off = 0 ,
. rfs_off = 3 ,
. sdf_off = 6 ,
. txr_off = 8 ,
. rclksrc_off = 10 ,
. mss_off = 11 ,
. cdclkcon_off = 12 ,
. lrp_off = 15 ,
. bfs_mask = 0x7 ,
. rfs_mask = 0x7 ,
. ftx0cnt_off = 8 ,
} ;
2013-08-12 15:19:51 +05:30
static const struct samsung_i2s_dai_data i2sv3_dai_type = {
. quirks = QUIRK_NO_MUXPSR ,
2017-07-07 10:31:10 +09:00
. pcm_rates = SNDRV_PCM_RATE_8000_96000 ,
2014-11-07 12:24:40 +05:30
. i2s_variant_regs = & i2sv3_regs ,
2013-08-12 15:19:51 +05:30
} ;
static const struct samsung_i2s_dai_data i2sv5_dai_type = {
2014-11-07 12:24:39 +05:30
. quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
QUIRK_SUPPORTS_IDMA ,
2017-07-07 10:31:10 +09:00
. pcm_rates = SNDRV_PCM_RATE_8000_96000 ,
2014-11-07 12:24:40 +05:30
. i2s_variant_regs = & i2sv3_regs ,
2013-08-12 15:19:51 +05:30
} ;
2013-08-12 15:19:52 +05:30
static const struct samsung_i2s_dai_data i2sv6_dai_type = {
. quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
2014-11-07 12:24:39 +05:30
QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA ,
2017-07-07 10:31:10 +09:00
. pcm_rates = SNDRV_PCM_RATE_8000_96000 ,
2014-11-07 12:24:40 +05:30
. i2s_variant_regs = & i2sv6_regs ,
} ;
static const struct samsung_i2s_dai_data i2sv7_dai_type = {
. quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
QUIRK_SUPPORTS_TDM ,
2017-07-07 10:31:10 +09:00
. pcm_rates = SNDRV_PCM_RATE_8000_192000 ,
2014-11-07 12:24:40 +05:30
. i2s_variant_regs = & i2sv7_regs ,
} ;
static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = {
. quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR ,
2017-07-07 10:31:10 +09:00
. pcm_rates = SNDRV_PCM_RATE_8000_96000 ,
2014-11-07 12:24:40 +05:30
. i2s_variant_regs = & i2sv5_i2s1_regs ,
2013-08-12 15:19:52 +05:30
} ;
2015-05-02 01:00:14 +09:00
static const struct platform_device_id samsung_i2s_driver_ids [ ] = {
2013-01-18 17:17:00 +05:30
{
. name = " samsung-i2s " ,
2014-12-05 19:56:17 +00:00
. driver_data = ( kernel_ulong_t ) & i2sv3_dai_type ,
2013-01-18 17:17:00 +05:30
} ,
{ } ,
} ;
2013-04-11 02:05:03 +02:00
MODULE_DEVICE_TABLE ( platform , samsung_i2s_driver_ids ) ;
2013-01-18 17:17:00 +05:30
2013-01-18 17:17:01 +05:30
# ifdef CONFIG_OF
static const struct of_device_id exynos_i2s_match [ ] = {
2013-08-12 15:19:51 +05:30
{
. compatible = " samsung,s3c6410-i2s " ,
. data = & i2sv3_dai_type ,
} , {
. compatible = " samsung,s5pv210-i2s " ,
. data = & i2sv5_dai_type ,
2013-08-12 15:19:52 +05:30
} , {
. compatible = " samsung,exynos5420-i2s " ,
. data = & i2sv6_dai_type ,
2014-11-07 12:24:40 +05:30
} , {
. compatible = " samsung,exynos7-i2s " ,
. data = & i2sv7_dai_type ,
} , {
. compatible = " samsung,exynos7-i2s1 " ,
. data = & i2sv5_dai_type_i2s1 ,
2013-01-18 17:17:01 +05:30
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , exynos_i2s_match ) ;
# endif
2013-01-30 17:41:04 +05:30
static const struct dev_pm_ops samsung_i2s_pm = {
SET_RUNTIME_PM_OPS ( i2s_runtime_suspend ,
i2s_runtime_resume , NULL )
2016-12-29 12:34:05 +01:00
SET_SYSTEM_SLEEP_PM_OPS ( pm_runtime_force_suspend ,
pm_runtime_force_resume )
2013-01-30 17:41:04 +05:30
} ;
2010-11-22 15:36:59 +09:00
static struct platform_driver samsung_i2s_driver = {
. probe = samsung_i2s_probe ,
2012-12-07 09:26:15 -05:00
. remove = samsung_i2s_remove ,
2013-01-18 17:17:00 +05:30
. id_table = samsung_i2s_driver_ids ,
2010-11-22 15:36:59 +09:00
. driver = {
. name = " samsung-i2s " ,
2013-01-18 17:17:01 +05:30
. of_match_table = of_match_ptr ( exynos_i2s_match ) ,
2013-01-30 17:41:04 +05:30
. pm = & samsung_i2s_pm ,
2010-11-22 15:36:59 +09:00
} ,
} ;
2011-11-23 15:20:13 +00:00
module_platform_driver ( samsung_i2s_driver ) ;
2010-11-22 15:36:59 +09:00
/* Module information */
2012-02-25 16:24:36 +05:30
MODULE_AUTHOR ( " Jaswinder Singh, <jassisinghbrar@gmail.com> " ) ;
2010-11-22 15:36:59 +09:00
MODULE_DESCRIPTION ( " Samsung I2S Interface " ) ;
MODULE_ALIAS ( " platform:samsung-i2s " ) ;
MODULE_LICENSE ( " GPL " ) ;