2019-02-14 12:37:47 +03:00
// SPDX-License-Identifier: GPL-2.0
//
// ALSA SoC Audio Layer - Samsung I2S Controller driver
//
// Copyright (c) 2010 Samsung Electronics Co. Ltd.
// Jaswinder Singh <jassisinghbrar@gmail.com>
2010-11-22 09:36:59 +03:00
2015-01-14 21:42:39 +03:00
# include <dt-bindings/sound/samsung-i2s.h>
2010-11-22 09:36:59 +03:00
# include <linux/delay.h>
# include <linux/slab.h>
# include <linux/clk.h>
2015-01-14 21:42:39 +03:00
# include <linux/clk-provider.h>
2010-11-22 09:36:59 +03:00
# include <linux/io.h>
2011-07-15 20:38:28 +04:00
# include <linux/module.h>
2013-01-18 15:47:01 +04:00
# include <linux/of.h>
2016-04-08 19:52:45 +03:00
# include <linux/of_device.h>
2013-01-18 15:47:01 +04:00
# include <linux/of_gpio.h>
2011-12-08 12:45:03 +04:00
# include <linux/pm_runtime.h>
2010-11-22 09:36:59 +03:00
# include <sound/soc.h>
2011-01-11 01:26:06 +03:00
# include <sound/pcm_params.h>
2010-11-22 09:36:59 +03:00
2012-08-24 17:22:12 +04:00
# include <linux/platform_data/asoc-s3c.h>
2010-11-22 09:36:59 +03:00
# include "dma.h"
2011-07-20 12:07:12 +04:00
# include "idma.h"
2010-11-22 09:36:59 +03:00
# include "i2s.h"
2011-06-20 11:36:18 +04:00
# include "i2s-regs.h"
2010-11-22 09:36:59 +03:00
# define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
2019-02-07 20:00:13 +03:00
# define SAMSUNG_I2S_ID_PRIMARY 1
# define SAMSUNG_I2S_ID_SECONDARY 2
2014-11-07 09:54:40 +03:00
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 15:47:01 +04:00
struct samsung_i2s_dai_data {
2013-08-12 13:49:51 +04:00
u32 quirks ;
2017-07-07 04:31:10 +03:00
unsigned int pcm_rates ;
2014-11-07 09:54:40 +03:00
const struct samsung_i2s_variant_regs * i2s_variant_regs ;
2023-01-16 13:38:20 +03:00
void ( * fixup_early ) ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai ) ;
void ( * fixup_late ) ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai ) ;
2013-01-18 15:47:01 +04:00
} ;
2010-11-22 09:36:59 +03:00
struct i2s_dai {
/* Platform device for this DAI */
struct platform_device * pdev ;
2019-02-14 12:37:35 +03:00
2019-02-14 12:37:46 +03:00
/* Frame clock */
2010-11-22 09:36:59 +03:00
unsigned frmclk ;
/*
2019-02-14 12:37:46 +03:00
* Specifically requested RCLK , BCLK by machine driver .
2010-11-22 09:36:59 +03:00
* 0 indicates CPU driver is free to choose any value .
*/
unsigned rfs , bfs ;
/* 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 ;
2019-02-14 12:37:46 +03:00
# define DAI_OPENED (1 << 0) /* DAI is opened */
# define DAI_MANAGER (1 << 1) /* DAI is the manager */
2010-11-22 09:36:59 +03:00
unsigned mode ;
2019-02-07 20:00:13 +03:00
2010-11-22 09:36:59 +03:00
/* Driver for this DAI */
2019-02-07 20:00:13 +03:00
struct snd_soc_dai_driver * drv ;
2010-11-22 09:36:59 +03:00
/* DMA parameters */
2016-08-04 12:30:26 +03: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-19 00:31:11 +03:00
dma_filter_fn filter ;
2015-01-14 21:42:35 +03:00
2019-02-12 21:03:23 +03:00
struct samsung_i2s_priv * priv ;
2010-11-22 09:36:59 +03:00
} ;
2019-02-07 20:00:13 +03:00
struct samsung_i2s_priv {
struct platform_device * pdev ;
2019-02-12 21:03:22 +03:00
struct platform_device * pdev_sec ;
2019-02-07 20:00:13 +03:00
2019-02-14 12:37:46 +03:00
/* Lock for cross interface checks */
2019-02-14 12:37:39 +03:00
spinlock_t pcm_lock ;
2019-02-07 20:00:13 +03:00
/* CPU DAIs and their corresponding drivers */
struct i2s_dai * dai ;
struct snd_soc_dai_driver * dai_drv ;
int num_dais ;
2019-02-12 21:03:23 +03:00
2019-02-12 21:03:25 +03:00
/* The I2S controller's core clock */
struct clk * clk ;
2019-02-12 21:03:26 +03:00
/* Clock for generating I2S signals */
struct clk * op_clk ;
/* Rate of RCLK source clock */
unsigned long rclk_srcrate ;
2019-02-12 21:03:27 +03:00
/* Cache of selected I2S registers for system suspend */
u32 suspend_i2smod ;
u32 suspend_i2scon ;
u32 suspend_i2spsr ;
2019-02-14 12:37:37 +03:00
const struct samsung_i2s_variant_regs * variant_regs ;
2023-01-16 13:38:20 +03:00
void ( * fixup_early ) ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai ) ;
void ( * fixup_late ) ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai ) ;
2019-02-14 12:37:38 +03:00
u32 quirks ;
2019-02-14 12:37:37 +03:00
2019-02-12 21:03:23 +03:00
/* The clock provider's data */
struct clk * clk_table [ 3 ] ;
struct clk_onecell_data clk_data ;
2019-03-19 15:11:02 +03:00
/* Spinlock protecting member fields below */
spinlock_t lock ;
/* Memory mapped SFR region */
void __iomem * addr ;
/* A flag indicating the I2S slave mode operation */
bool slave_mode ;
2019-02-07 20:00:13 +03:00
} ;
/* Returns true if this is the 'overlay' stereo DAI */
2010-11-22 09:36:59 +03:00
static inline bool is_secondary ( struct i2s_dai * i2s )
{
2019-02-07 20:00:13 +03:00
return i2s - > drv - > id = = SAMSUNG_I2S_ID_SECONDARY ;
2010-11-22 09:36:59 +03: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 ;
2019-02-14 12:37:35 +03:00
active = readl ( i2s - > priv - > addr + I2SCON ) ;
2010-11-22 09:36:59 +03:00
if ( is_secondary ( i2s ) )
active & = CON_TXSDMA_ACTIVE ;
else
active & = CON_TXDMA_ACTIVE ;
return active ? true : false ;
}
2015-01-14 21:42:33 +03: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 09:36:59 +03:00
/* If the other interface of the controller is transmitting data */
static inline bool other_tx_active ( struct i2s_dai * i2s )
{
2015-01-14 21:42:33 +03:00
struct i2s_dai * other = get_other_dai ( i2s ) ;
2010-11-22 09:36:59 +03: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 ;
2019-02-14 12:37:35 +03:00
active = readl ( i2s - > priv - > addr + I2SCON ) & CON_RXDMA_ACTIVE ;
2010-11-22 09:36:59 +03: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 21:42:33 +03:00
struct i2s_dai * other = get_other_dai ( i2s ) ;
2010-11-22 09:36:59 +03: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 )
{
2019-02-07 20:00:13 +03:00
struct samsung_i2s_priv * priv = snd_soc_dai_get_drvdata ( dai ) ;
return & priv - > dai [ dai - > id - 1 ] ;
2010-11-22 09:36:59 +03:00
}
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 )
{
2019-02-14 12:37:35 +03:00
struct samsung_i2s_priv * priv = i2s - > priv ;
2013-08-12 13:49:52 +04:00
u32 rfs ;
2019-02-14 12:37:35 +03:00
2019-02-14 12:37:37 +03:00
rfs = readl ( priv - > addr + I2SMOD ) > > priv - > variant_regs - > rfs_off ;
rfs & = priv - > variant_regs - > rfs_mask ;
2010-11-22 09:36:59 +03:00
switch ( rfs ) {
2014-11-07 09:54:40 +03:00
case 7 : return 192 ;
case 6 : return 96 ;
case 5 : return 128 ;
case 4 : return 64 ;
2010-11-22 09:36:59 +03: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 )
{
2019-02-14 12:37:35 +03:00
struct samsung_i2s_priv * priv = i2s - > priv ;
u32 mod = readl ( priv - > addr + I2SMOD ) ;
2019-02-14 12:37:37 +03:00
int rfs_shift = priv - > variant_regs - > rfs_off ;
2010-11-22 09:36:59 +03:00
2019-02-14 12:37:37 +03:00
mod & = ~ ( priv - > variant_regs - > rfs_mask < < rfs_shift ) ;
2010-11-22 09:36:59 +03:00
switch ( rfs ) {
2014-11-07 09:54:40 +03:00
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 09:36:59 +03:00
case 768 :
2013-07-26 17:36:48 +04:00
mod | = ( MOD_RCLK_768FS < < rfs_shift ) ;
2010-11-22 09:36:59 +03:00
break ;
case 512 :
2013-07-26 17:36:48 +04:00
mod | = ( MOD_RCLK_512FS < < rfs_shift ) ;
2010-11-22 09:36:59 +03:00
break ;
case 384 :
2013-07-26 17:36:48 +04:00
mod | = ( MOD_RCLK_384FS < < rfs_shift ) ;
2010-11-22 09:36:59 +03:00
break ;
default :
2013-07-26 17:36:48 +04:00
mod | = ( MOD_RCLK_256FS < < rfs_shift ) ;
2010-11-22 09:36:59 +03:00
break ;
}
2019-02-14 12:37:35 +03:00
writel ( mod , priv - > addr + I2SMOD ) ;
2010-11-22 09:36:59 +03:00
}
2019-02-14 12:37:46 +03:00
/* Read bit-clock of I2S (in multiples of LRCLK) */
2010-11-22 09:36:59 +03:00
static inline unsigned get_bfs ( struct i2s_dai * i2s )
{
2019-02-14 12:37:35 +03:00
struct samsung_i2s_priv * priv = i2s - > priv ;
2013-08-12 13:49:52 +04:00
u32 bfs ;
2019-02-14 12:37:35 +03:00
2019-02-14 12:37:37 +03:00
bfs = readl ( priv - > addr + I2SMOD ) > > priv - > variant_regs - > bfs_off ;
bfs & = priv - > variant_regs - > bfs_mask ;
2010-11-22 09:36:59 +03:00
switch ( bfs ) {
2013-08-12 13:49:52 +04:00
case 8 : return 256 ;
case 7 : return 192 ;
case 6 : return 128 ;
case 5 : return 96 ;
case 4 : return 64 ;
2010-11-22 09:36:59 +03:00
case 3 : return 24 ;
case 2 : return 16 ;
case 1 : return 48 ;
default : return 32 ;
}
}
2019-02-14 12:37:46 +03:00
/* Write bit-clock of I2S (in multiples of LRCLK) */
2010-11-22 09:36:59 +03:00
static inline void set_bfs ( struct i2s_dai * i2s , unsigned bfs )
{
2019-02-14 12:37:35 +03:00
struct samsung_i2s_priv * priv = i2s - > priv ;
u32 mod = readl ( priv - > addr + I2SMOD ) ;
2019-02-14 12:37:38 +03:00
int tdm = priv - > quirks & QUIRK_SUPPORTS_TDM ;
2019-02-14 12:37:37 +03:00
int bfs_shift = priv - > variant_regs - > bfs_off ;
2013-08-12 13:49:52 +04:00
/* 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 09:36:59 +03:00
2019-02-14 12:37:37 +03:00
mod & = ~ ( priv - > variant_regs - > bfs_mask < < bfs_shift ) ;
2014-11-07 09:54:40 +03:00
2010-11-22 09:36:59 +03:00
switch ( bfs ) {
case 48 :
2013-07-26 17:36:48 +04:00
mod | = ( MOD_BCLK_48FS < < bfs_shift ) ;
2010-11-22 09:36:59 +03:00
break ;
case 32 :
2013-07-26 17:36:48 +04:00
mod | = ( MOD_BCLK_32FS < < bfs_shift ) ;
2010-11-22 09:36:59 +03:00
break ;
case 24 :
2013-07-26 17:36:48 +04:00
mod | = ( MOD_BCLK_24FS < < bfs_shift ) ;
2010-11-22 09:36:59 +03:00
break ;
case 16 :
2013-07-26 17:36:48 +04:00
mod | = ( MOD_BCLK_16FS < < bfs_shift ) ;
2010-11-22 09:36:59 +03:00
break ;
2013-08-12 13:49:52 +04:00
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 09:36:59 +03:00
break ;
default :
dev_err ( & i2s - > pdev - > dev , " Wrong BCLK Divider! \n " ) ;
return ;
}
2019-02-14 12:37:35 +03:00
writel ( mod , priv - > addr + I2SMOD ) ;
2010-11-22 09:36:59 +03:00
}
2019-02-14 12:37:46 +03:00
/* Sample size */
2010-11-22 09:36:59 +03:00
static inline int get_blc ( struct i2s_dai * i2s )
{
2019-02-14 12:37:35 +03:00
int blc = readl ( i2s - > priv - > addr + I2SMOD ) ;
2010-11-22 09:36:59 +03:00
blc = ( blc > > 13 ) & 0x3 ;
switch ( blc ) {
case 2 : return 24 ;
case 1 : return 8 ;
default : return 16 ;
}
}
2019-02-14 12:37:46 +03:00
/* TX channel control */
2010-11-22 09:36:59 +03:00
static void i2s_txctrl ( struct i2s_dai * i2s , int on )
{
2019-02-14 12:37:35 +03:00
struct samsung_i2s_priv * priv = i2s - > priv ;
void __iomem * addr = priv - > addr ;
2019-02-14 12:37:37 +03:00
int txr_off = priv - > variant_regs - > txr_off ;
2010-11-22 09:36:59 +03:00
u32 con = readl ( addr + I2SCON ) ;
2014-11-07 09:54:40 +03:00
u32 mod = readl ( addr + I2SMOD ) & ~ ( 3 < < txr_off ) ;
2010-11-22 09:36:59 +03: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 09:54:40 +03:00
mod | = 2 < < txr_off ;
2010-11-22 09:36:59 +03:00
else
2014-11-07 09:54:40 +03:00
mod | = 0 < < txr_off ;
2010-11-22 09:36:59 +03: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 09:54:40 +03:00
mod | = 1 < < txr_off ;
2010-11-22 09:36:59 +03: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 )
{
2019-02-14 12:37:35 +03:00
struct samsung_i2s_priv * priv = i2s - > priv ;
void __iomem * addr = priv - > addr ;
2019-02-14 12:37:37 +03:00
int txr_off = priv - > variant_regs - > txr_off ;
2010-11-22 09:36:59 +03:00
u32 con = readl ( addr + I2SCON ) ;
2014-11-07 09:54:40 +03:00
u32 mod = readl ( addr + I2SMOD ) & ~ ( 3 < < txr_off ) ;
2010-11-22 09:36:59 +03:00
if ( on ) {
con | = CON_RXDMA_ACTIVE | CON_ACTIVE ;
con & = ~ ( CON_RXDMA_PAUSE | CON_RXCH_PAUSE ) ;
if ( any_tx_active ( i2s ) )
2014-11-07 09:54:40 +03:00
mod | = 2 < < txr_off ;
2010-11-22 09:36:59 +03:00
else
2014-11-07 09:54:40 +03:00
mod | = 1 < < txr_off ;
2010-11-22 09:36:59 +03:00
} else {
con | = CON_RXDMA_PAUSE | CON_RXCH_PAUSE ;
con & = ~ CON_RXDMA_ACTIVE ;
if ( any_tx_active ( i2s ) )
2014-11-07 09:54:40 +03:00
mod | = 0 < < txr_off ;
2010-11-22 09:36:59 +03: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 ) )
2019-02-14 12:37:35 +03:00
fic = i2s - > priv - > addr + I2SFICS ;
2010-11-22 09:36:59 +03:00
else
2019-02-14 12:37:35 +03:00
fic = i2s - > priv - > addr + I2SFIC ;
2010-11-22 09:36:59 +03:00
/* 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 ) ;
}
2019-02-12 21:03:26 +03:00
static int i2s_set_sysclk ( struct snd_soc_dai * dai , int clk_id , unsigned int rfs ,
int dir )
2010-11-22 09:36:59 +03:00
{
2019-02-12 21:03:26 +03:00
struct samsung_i2s_priv * priv = snd_soc_dai_get_drvdata ( dai ) ;
2010-11-22 09:36:59 +03:00
struct i2s_dai * i2s = to_info ( dai ) ;
2015-01-14 21:42:33 +03:00
struct i2s_dai * other = get_other_dai ( i2s ) ;
2019-02-14 12:37:37 +03:00
const struct samsung_i2s_variant_regs * i2s_regs = priv - > variant_regs ;
2014-11-07 09:54:40 +03:00
unsigned int cdcon_mask = 1 < < i2s_regs - > cdclkcon_off ;
unsigned int rsrc_mask = 1 < < i2s_regs - > rclksrc_off ;
2015-01-14 21:42:36 +03:00
u32 mod , mask , val = 0 ;
2016-02-18 18:47:13 +03:00
unsigned long flags ;
2016-12-29 14:34:04 +03:00
int ret = 0 ;
pm_runtime_get_sync ( dai - > dev ) ;
2015-01-14 21:42:36 +03:00
2019-02-14 12:37:36 +03:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
2019-02-14 12:37:35 +03:00
mod = readl ( priv - > addr + I2SMOD ) ;
2019-02-14 12:37:36 +03:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2010-11-22 09:36:59 +03:00
switch ( clk_id ) {
2014-05-19 21:30:38 +04:00
case SAMSUNG_I2S_OPCLK :
2015-01-14 21:42:36 +03:00
mask = MOD_OPCLK_MASK ;
2018-02-12 19:15:35 +03:00
val = ( dir < < MOD_OPCLK_SHIFT ) & MOD_OPCLK_MASK ;
2014-05-19 21:30:38 +04:00
break ;
2010-11-22 09:36:59 +03:00
case SAMSUNG_I2S_CDCLK :
2015-01-14 21:42:36 +03:00
mask = 1 < < i2s_regs - > cdclkcon_off ;
2010-11-22 09:36:59 +03:00
/* Shouldn't matter in GATING(CLOCK_IN) mode */
if ( dir = = SND_SOC_CLOCK_IN )
rfs = 0 ;
2014-09-09 19:51:49 +04:00
if ( ( rfs & & other & & other - > rfs & & ( other - > rfs ! = rfs ) ) | |
2010-11-22 09:36:59 +03:00
( any_active ( i2s ) & &
( ( ( dir = = SND_SOC_CLOCK_IN )
2014-11-07 09:54:40 +03:00
& & ! ( mod & cdcon_mask ) ) | |
2010-11-22 09:36:59 +03:00
( ( dir = = SND_SOC_CLOCK_OUT )
2014-11-07 09:54:40 +03:00
& & ( mod & cdcon_mask ) ) ) ) ) {
2010-11-22 09:36:59 +03:00
dev_err ( & i2s - > pdev - > dev ,
" %s:%d Other DAI busy \n " , __func__ , __LINE__ ) ;
2016-12-29 14:34:04 +03:00
ret = - EAGAIN ;
goto err ;
2010-11-22 09:36:59 +03:00
}
if ( dir = = SND_SOC_CLOCK_IN )
2015-01-14 21:42:36 +03:00
val = 1 < < i2s_regs - > cdclkcon_off ;
2010-11-22 09:36:59 +03: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 21:42:36 +03:00
mask = 1 < < i2s_regs - > rclksrc_off ;
2019-02-14 12:37:38 +03:00
if ( ( priv - > quirks & QUIRK_NO_MUXPSR )
2010-11-22 09:36:59 +03:00
| | ( clk_id = = SAMSUNG_I2S_RCLKSRC_0 ) )
clk_id = 0 ;
else
clk_id = 1 ;
if ( ! any_active ( i2s ) ) {
2019-02-12 21:03:26 +03:00
if ( priv - > op_clk & & ! IS_ERR ( priv - > op_clk ) ) {
2014-11-07 09:54:40 +03:00
if ( ( clk_id & & ! ( mod & rsrc_mask ) ) | |
( ! clk_id & & ( mod & rsrc_mask ) ) ) {
2019-02-12 21:03:26 +03:00
clk_disable_unprepare ( priv - > op_clk ) ;
clk_put ( priv - > op_clk ) ;
2010-11-22 09:36:59 +03:00
} else {
2019-02-12 21:03:26 +03:00
priv - > rclk_srcrate =
clk_get_rate ( priv - > op_clk ) ;
2016-12-29 14:34:04 +03:00
goto done ;
2010-11-22 09:36:59 +03:00
}
}
2012-11-28 14:47:48 +04:00
if ( clk_id )
2019-02-12 21:03:26 +03:00
priv - > op_clk = clk_get ( & i2s - > pdev - > dev ,
2012-11-28 14:47:48 +04:00
" i2s_opclk1 " ) ;
else
2019-02-12 21:03:26 +03:00
priv - > op_clk = clk_get ( & i2s - > pdev - > dev ,
2012-11-28 14:47:48 +04:00
" i2s_opclk0 " ) ;
2014-05-22 14:10:52 +04:00
2019-02-12 21:03:26 +03:00
if ( WARN_ON ( IS_ERR ( priv - > op_clk ) ) ) {
ret = PTR_ERR ( priv - > op_clk ) ;
priv - > op_clk = NULL ;
2016-12-29 14:34:04 +03:00
goto err ;
}
2014-05-22 14:10:52 +04:00
2019-02-12 21:03:26 +03:00
ret = clk_prepare_enable ( priv - > op_clk ) ;
2017-09-03 15:29:02 +03:00
if ( ret ) {
2019-02-12 21:03:26 +03:00
clk_put ( priv - > op_clk ) ;
priv - > op_clk = NULL ;
2017-07-25 13:14:31 +03:00
goto err ;
2017-09-03 15:29:02 +03:00
}
2019-02-12 21:03:26 +03:00
priv - > rclk_srcrate = clk_get_rate ( priv - > op_clk ) ;
2010-11-22 09:36:59 +03:00
2014-11-07 09:54:40 +03:00
} else if ( ( ! clk_id & & ( mod & rsrc_mask ) )
| | ( clk_id & & ! ( mod & rsrc_mask ) ) ) {
2010-11-22 09:36:59 +03:00
dev_err ( & i2s - > pdev - > dev ,
" %s:%d Other DAI busy \n " , __func__ , __LINE__ ) ;
2016-12-29 14:34:04 +03:00
ret = - EAGAIN ;
goto err ;
2010-11-22 09:36:59 +03:00
} else {
/* Call can't be on the active DAI */
2016-12-29 14:34:04 +03:00
goto done ;
2010-11-22 09:36:59 +03:00
}
2015-01-14 21:42:36 +03:00
if ( clk_id = = 1 )
val = 1 < < i2s_regs - > rclksrc_off ;
2014-11-20 13:03:17 +03:00
break ;
2010-11-22 09:36:59 +03:00
default :
dev_err ( & i2s - > pdev - > dev , " We don't serve that! \n " ) ;
2016-12-29 14:34:04 +03:00
ret = - EINVAL ;
goto err ;
2010-11-22 09:36:59 +03:00
}
2019-02-14 12:37:36 +03:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
2019-02-14 12:37:35 +03:00
mod = readl ( priv - > addr + I2SMOD ) ;
2015-01-14 21:42:36 +03:00
mod = ( mod & ~ mask ) | val ;
2019-02-14 12:37:35 +03:00
writel ( mod , priv - > addr + I2SMOD ) ;
2019-02-14 12:37:36 +03:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2016-12-29 14:34:04 +03:00
done :
pm_runtime_put ( dai - > dev ) ;
2010-11-22 09:36:59 +03:00
return 0 ;
2016-12-29 14:34:04 +03:00
err :
pm_runtime_put ( dai - > dev ) ;
return ret ;
2010-11-22 09:36:59 +03:00
}
2019-02-12 21:03:23 +03:00
static int i2s_set_fmt ( struct snd_soc_dai * dai , unsigned int fmt )
2010-11-22 09:36:59 +03:00
{
2019-02-12 21:03:23 +03:00
struct samsung_i2s_priv * priv = snd_soc_dai_get_drvdata ( dai ) ;
2010-11-22 09:36:59 +03:00
struct i2s_dai * i2s = to_info ( dai ) ;
2014-11-07 09:54:40 +03:00
int lrp_shift , sdf_shift , sdf_mask , lrp_rlow , mod_slave ;
2015-01-14 21:42:36 +03:00
u32 mod , tmp = 0 ;
2016-02-18 18:47:13 +03:00
unsigned long flags ;
2010-11-22 09:36:59 +03:00
2019-02-14 12:37:37 +03:00
lrp_shift = priv - > variant_regs - > lrp_off ;
sdf_shift = priv - > variant_regs - > sdf_off ;
mod_slave = 1 < < priv - > variant_regs - > mss_off ;
2013-08-12 13:49:52 +04:00
2013-07-26 17:36:48 +04:00
sdf_mask = MOD_SDF_MASK < < sdf_shift ;
lrp_rlow = MOD_LR_RLOW < < lrp_shift ;
2010-11-22 09:36:59 +03:00
/* Format is priority */
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_RIGHT_J :
2013-07-26 17:36:48 +04:00
tmp | = lrp_rlow ;
tmp | = ( MOD_SDF_MSB < < sdf_shift ) ;
2010-11-22 09:36:59 +03:00
break ;
case SND_SOC_DAIFMT_LEFT_J :
2013-07-26 17:36:48 +04:00
tmp | = lrp_rlow ;
tmp | = ( MOD_SDF_LSB < < sdf_shift ) ;
2010-11-22 09:36:59 +03:00
break ;
case SND_SOC_DAIFMT_I2S :
2013-07-26 17:36:48 +04:00
tmp | = ( MOD_SDF_IIS < < sdf_shift ) ;
2010-11-22 09:36:59 +03: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 17:36:48 +04:00
if ( tmp & lrp_rlow )
tmp & = ~ lrp_rlow ;
2010-11-22 09:36:59 +03:00
else
2013-07-26 17:36:48 +04:00
tmp | = lrp_rlow ;
2010-11-22 09:36:59 +03:00
break ;
default :
dev_err ( & i2s - > pdev - > dev , " Polarity not supported \n " ) ;
return - EINVAL ;
}
2022-05-19 18:42:41 +03:00
switch ( fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK ) {
case SND_SOC_DAIFMT_BC_FC :
2014-11-07 09:54:40 +03:00
tmp | = mod_slave ;
2010-11-22 09:36:59 +03:00
break ;
2022-05-19 18:42:41 +03:00
case SND_SOC_DAIFMT_BP_FP :
2018-02-05 18:43:56 +03: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 .
*/
2019-02-12 21:03:26 +03:00
if ( priv - > rclk_srcrate = = 0 & & priv - > clk_data . clks = = NULL )
2010-11-22 09:36:59 +03: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 14:34:04 +03:00
pm_runtime_get_sync ( dai - > dev ) ;
2019-02-14 12:37:36 +03:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
2019-02-14 12:37:35 +03:00
mod = readl ( priv - > addr + I2SMOD ) ;
2013-07-26 17:36:48 +04:00
/*
* Don ' t change the I2S mode if any controller is active on this
* channel .
*/
2010-11-22 09:36:59 +03:00
if ( any_active ( i2s ) & &
2014-11-07 09:54:40 +03:00
( ( mod & ( sdf_mask | lrp_rlow | mod_slave ) ) ! = tmp ) ) {
2019-02-14 12:37:36 +03:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2016-12-29 14:34:04 +03:00
pm_runtime_put ( dai - > dev ) ;
2010-11-22 09:36:59 +03:00
dev_err ( & i2s - > pdev - > dev ,
" %s:%d Other DAI busy \n " , __func__ , __LINE__ ) ;
return - EAGAIN ;
}
2014-11-07 09:54:40 +03:00
mod & = ~ ( sdf_mask | lrp_rlow | mod_slave ) ;
2010-11-22 09:36:59 +03:00
mod | = tmp ;
2019-02-14 12:37:35 +03:00
writel ( mod , priv - > addr + I2SMOD ) ;
2019-03-19 15:11:02 +03:00
priv - > slave_mode = ( mod & mod_slave ) ;
2019-02-14 12:37:36 +03:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2016-12-29 14:34:04 +03:00
pm_runtime_put ( dai - > dev ) ;
2010-11-22 09:36:59 +03:00
return 0 ;
}
static int i2s_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params , struct snd_soc_dai * dai )
{
2019-02-12 21:03:23 +03:00
struct samsung_i2s_priv * priv = snd_soc_dai_get_drvdata ( dai ) ;
2010-11-22 09:36:59 +03:00
struct i2s_dai * i2s = to_info ( dai ) ;
2015-01-14 21:42:36 +03:00
u32 mod , mask = 0 , val = 0 ;
2019-02-07 17:20:41 +03:00
struct clk * rclksrc ;
2016-02-18 18:47:13 +03:00
unsigned long flags ;
2010-11-22 09:36:59 +03:00
2016-12-29 14:34:04 +03:00
WARN_ON ( ! pm_runtime_active ( dai - > dev ) ) ;
2010-11-22 09:36:59 +03:00
if ( ! is_secondary ( i2s ) )
2015-01-14 21:42:36 +03:00
mask | = ( MOD_DC2_EN | MOD_DC1_EN ) ;
2010-11-22 09:36:59 +03:00
switch ( params_channels ( params ) ) {
case 6 :
2015-01-14 21:42:36 +03:00
val | = MOD_DC2_EN ;
2020-08-24 01:36:59 +03:00
fallthrough ;
2010-11-22 09:36:59 +03:00
case 4 :
2015-01-14 21:42:36 +03:00
val | = MOD_DC1_EN ;
2010-11-22 09:36:59 +03:00
break ;
case 2 :
2012-03-16 10:40:53 +04:00
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
2016-08-04 12:30:26 +03:00
i2s - > dma_playback . addr_width = 4 ;
2012-03-16 10:40:53 +04:00
else
2016-08-04 12:30:26 +03:00
i2s - > dma_capture . addr_width = 4 ;
2012-03-16 10:40:53 +04:00
break ;
case 1 :
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
2016-08-04 12:30:26 +03:00
i2s - > dma_playback . addr_width = 2 ;
2012-03-16 10:40:53 +04:00
else
2016-08-04 12:30:26 +03:00
i2s - > dma_capture . addr_width = 2 ;
2012-03-16 10:40:53 +04:00
2010-11-22 09:36:59 +03: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 21:42:36 +03:00
mask | = MOD_BLCS_MASK ;
2010-11-22 09:36:59 +03:00
else
2015-01-14 21:42:36 +03:00
mask | = MOD_BLCP_MASK ;
2010-11-22 09:36:59 +03:00
if ( is_manager ( i2s ) )
2015-01-14 21:42:36 +03:00
mask | = MOD_BLC_MASK ;
2010-11-22 09:36:59 +03:00
2014-05-23 16:05:39 +04:00
switch ( params_width ( params ) ) {
case 8 :
2010-11-22 09:36:59 +03:00
if ( is_secondary ( i2s ) )
2015-01-14 21:42:36 +03:00
val | = MOD_BLCS_8BIT ;
2010-11-22 09:36:59 +03:00
else
2015-01-14 21:42:36 +03:00
val | = MOD_BLCP_8BIT ;
2010-11-22 09:36:59 +03:00
if ( is_manager ( i2s ) )
2015-01-14 21:42:36 +03:00
val | = MOD_BLC_8BIT ;
2010-11-22 09:36:59 +03:00
break ;
2014-05-23 16:05:39 +04:00
case 16 :
2010-11-22 09:36:59 +03:00
if ( is_secondary ( i2s ) )
2015-01-14 21:42:36 +03:00
val | = MOD_BLCS_16BIT ;
2010-11-22 09:36:59 +03:00
else
2015-01-14 21:42:36 +03:00
val | = MOD_BLCP_16BIT ;
2010-11-22 09:36:59 +03:00
if ( is_manager ( i2s ) )
2015-01-14 21:42:36 +03:00
val | = MOD_BLC_16BIT ;
2010-11-22 09:36:59 +03:00
break ;
2014-05-23 16:05:39 +04:00
case 24 :
2010-11-22 09:36:59 +03:00
if ( is_secondary ( i2s ) )
2015-01-14 21:42:36 +03:00
val | = MOD_BLCS_24BIT ;
2010-11-22 09:36:59 +03:00
else
2015-01-14 21:42:36 +03:00
val | = MOD_BLCP_24BIT ;
2010-11-22 09:36:59 +03:00
if ( is_manager ( i2s ) )
2015-01-14 21:42:36 +03:00
val | = MOD_BLC_24BIT ;
2010-11-22 09:36:59 +03:00
break ;
default :
dev_err ( & i2s - > pdev - > dev , " Format(%d) not supported \n " ,
params_format ( params ) ) ;
return - EINVAL ;
}
2015-01-14 21:42:36 +03:00
2019-02-14 12:37:36 +03:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
2019-02-14 12:37:35 +03:00
mod = readl ( priv - > addr + I2SMOD ) ;
2015-01-14 21:42:36 +03:00
mod = ( mod & ~ mask ) | val ;
2019-02-14 12:37:35 +03:00
writel ( mod , priv - > addr + I2SMOD ) ;
2019-02-14 12:37:36 +03:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2010-11-22 09:36:59 +03:00
2016-08-04 12:30:26 +03:00
snd_soc_dai_init_dma_data ( dai , & i2s - > dma_playback , & i2s - > dma_capture ) ;
2013-12-05 18:14:52 +04:00
2010-11-22 09:36:59 +03:00
i2s - > frmclk = params_rate ( params ) ;
2019-02-12 21:03:23 +03:00
rclksrc = priv - > clk_table [ CLK_I2S_RCLK_SRC ] ;
2019-02-07 17:20:41 +03:00
if ( rclksrc & & ! IS_ERR ( rclksrc ) )
2019-02-12 21:03:26 +03:00
priv - > rclk_srcrate = clk_get_rate ( rclksrc ) ;
2019-02-07 17:20:41 +03:00
2010-11-22 09:36:59 +03:00
return 0 ;
}
2019-02-14 12:37:46 +03:00
/* We set constraints on the substream according to the version of I2S */
2010-11-22 09:36:59 +03:00
static int i2s_startup ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
2019-02-14 12:37:38 +03:00
struct samsung_i2s_priv * priv = snd_soc_dai_get_drvdata ( dai ) ;
2010-11-22 09:36:59 +03:00
struct i2s_dai * i2s = to_info ( dai ) ;
2015-01-14 21:42:33 +03:00
struct i2s_dai * other = get_other_dai ( i2s ) ;
2010-11-22 09:36:59 +03:00
unsigned long flags ;
2016-12-29 14:34:04 +03:00
pm_runtime_get_sync ( dai - > dev ) ;
2019-02-14 12:37:39 +03:00
spin_lock_irqsave ( & priv - > pcm_lock , flags ) ;
2010-11-22 09:36:59 +03:00
i2s - > mode | = DAI_OPENED ;
if ( is_manager ( other ) )
i2s - > mode & = ~ DAI_MANAGER ;
else
i2s - > mode | = DAI_MANAGER ;
2019-02-14 12:37:38 +03:00
if ( ! any_active ( i2s ) & & ( priv - > quirks & QUIRK_NEED_RSTCLR ) )
2019-02-14 12:37:35 +03:00
writel ( CON_RSTCLR , i2s - > priv - > addr + I2SCON ) ;
2013-01-24 16:35:31 +04:00
2019-02-14 12:37:39 +03:00
spin_unlock_irqrestore ( & priv - > pcm_lock , flags ) ;
2010-11-22 09:36:59 +03:00
return 0 ;
}
static void i2s_shutdown ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
2019-02-14 12:37:39 +03:00
struct samsung_i2s_priv * priv = snd_soc_dai_get_drvdata ( dai ) ;
2010-11-22 09:36:59 +03:00
struct i2s_dai * i2s = to_info ( dai ) ;
2015-01-14 21:42:33 +03:00
struct i2s_dai * other = get_other_dai ( i2s ) ;
2010-11-22 09:36:59 +03:00
unsigned long flags ;
2019-02-14 12:37:39 +03:00
spin_lock_irqsave ( & priv - > pcm_lock , flags ) ;
2010-11-22 09:36:59 +03:00
i2s - > mode & = ~ DAI_OPENED ;
i2s - > mode & = ~ DAI_MANAGER ;
2015-01-14 21:42:39 +03:00
if ( is_opened ( other ) )
2010-11-22 09:36:59 +03:00
other - > mode | = DAI_MANAGER ;
2015-01-14 21:42:39 +03:00
2010-11-22 09:36:59 +03:00
/* Reset any constraint on RFS and BFS */
i2s - > rfs = 0 ;
i2s - > bfs = 0 ;
2019-02-14 12:37:39 +03:00
spin_unlock_irqrestore ( & priv - > pcm_lock , flags ) ;
2016-12-29 14:34:04 +03:00
pm_runtime_put ( dai - > dev ) ;
2010-11-22 09:36:59 +03:00
}
static int config_setup ( struct i2s_dai * i2s )
{
2019-02-12 21:03:26 +03:00
struct samsung_i2s_priv * priv = i2s - > priv ;
2015-01-14 21:42:33 +03:00
struct i2s_dai * other = get_other_dai ( i2s ) ;
2010-11-22 09:36:59 +03: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 11:08:25 +04:00
/* Don't bother with PSR in Slave mode */
2019-03-19 15:11:02 +03:00
if ( priv - > slave_mode )
2013-07-11 11:08:25 +04:00
return 0 ;
2019-02-14 12:37:38 +03:00
if ( ! ( priv - > quirks & QUIRK_NO_MUXPSR ) ) {
2019-02-12 21:03:26 +03:00
psr = priv - > rclk_srcrate / i2s - > frmclk / rfs ;
2019-02-14 12:37:35 +03:00
writel ( ( ( psr - 1 ) < < 8 ) | PSR_PSREN , priv - > addr + I2SPSR ) ;
2010-11-22 09:36:59 +03:00
dev_dbg ( & i2s - > pdev - > dev ,
" RCLK_SRC=%luHz PSR=%u, RCLK=%dfs, BCLK=%dfs \n " ,
2019-02-12 21:03:26 +03:00
priv - > rclk_srcrate , psr , rfs , bfs ) ;
2010-11-22 09:36:59 +03:00
}
return 0 ;
}
static int i2s_trigger ( struct snd_pcm_substream * substream ,
int cmd , struct snd_soc_dai * dai )
{
2019-02-14 12:37:36 +03:00
struct samsung_i2s_priv * priv = snd_soc_dai_get_drvdata ( dai ) ;
2010-11-22 09:36:59 +03:00
int capture = ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE ) ;
2020-07-20 04:18:14 +03:00
struct snd_soc_pcm_runtime * rtd = asoc_substream_to_rtd ( substream ) ;
2020-03-23 08:20:20 +03:00
struct i2s_dai * i2s = to_info ( asoc_rtd_to_cpu ( rtd , 0 ) ) ;
2010-11-22 09:36:59 +03:00
unsigned long flags ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
2016-12-29 14:34:04 +03:00
pm_runtime_get_sync ( dai - > dev ) ;
2023-01-16 13:38:20 +03:00
if ( priv - > fixup_early )
priv - > fixup_early ( substream , dai ) ;
2019-02-14 12:37:36 +03:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
2010-11-22 09:36:59 +03:00
if ( config_setup ( i2s ) ) {
2019-02-14 12:37:36 +03:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2010-11-22 09:36:59 +03:00
return - EINVAL ;
}
2023-01-16 13:38:20 +03:00
if ( priv - > fixup_late )
priv - > fixup_late ( substream , dai ) ;
2010-11-22 09:36:59 +03:00
if ( capture )
i2s_rxctrl ( i2s , 1 ) ;
else
i2s_txctrl ( i2s , 1 ) ;
2019-02-14 12:37:36 +03:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2010-11-22 09:36:59 +03:00
break ;
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
2019-02-14 12:37:36 +03:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
2010-11-22 09:36:59 +03:00
2012-02-25 15:12:34 +04:00
if ( capture ) {
2010-11-22 09:36:59 +03:00
i2s_rxctrl ( i2s , 0 ) ;
2010-12-20 05:05:47 +03:00
i2s_fifo ( i2s , FIC_RXFLUSH ) ;
2012-02-25 15:12:34 +04:00
} else {
i2s_txctrl ( i2s , 0 ) ;
2010-12-20 05:05:47 +03:00
i2s_fifo ( i2s , FIC_TXFLUSH ) ;
2012-02-25 15:12:34 +04:00
}
2010-12-20 05:05:47 +03:00
2019-02-14 12:37:36 +03:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2016-12-29 14:34:04 +03:00
pm_runtime_put ( dai - > dev ) ;
2010-11-22 09:36:59 +03: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 21:42:33 +03:00
struct i2s_dai * other = get_other_dai ( i2s ) ;
2010-11-22 09:36:59 +03:00
switch ( div_id ) {
case SAMSUNG_I2S_DIV_BCLK :
2016-12-29 14:34:04 +03:00
pm_runtime_get_sync ( dai - > dev ) ;
2010-11-22 09:36:59 +03:00
if ( ( any_active ( i2s ) & & div & & ( get_bfs ( i2s ) ! = div ) )
| | ( other & & other - > bfs & & ( other - > bfs ! = div ) ) ) {
2016-12-29 14:34:04 +03:00
pm_runtime_put ( dai - > dev ) ;
2010-11-22 09:36:59 +03:00
dev_err ( & i2s - > pdev - > dev ,
" %s:%d Other DAI busy \n " , __func__ , __LINE__ ) ;
return - EAGAIN ;
}
i2s - > bfs = div ;
2016-12-29 14:34:04 +03:00
pm_runtime_put ( dai - > dev ) ;
2010-11-22 09:36:59 +03: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 )
{
2019-02-14 12:37:35 +03:00
struct samsung_i2s_priv * priv = snd_soc_dai_get_drvdata ( dai ) ;
2010-11-22 09:36:59 +03:00
struct i2s_dai * i2s = to_info ( dai ) ;
2019-02-14 12:37:35 +03:00
u32 reg = readl ( priv - > addr + I2SFIC ) ;
2010-11-22 09:36:59 +03:00
snd_pcm_sframes_t delay ;
2016-12-29 14:34:04 +03:00
WARN_ON ( ! pm_runtime_active ( dai - > dev ) ) ;
2010-11-22 09:36:59 +03:00
if ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE )
delay = FIC_RXCOUNT ( reg ) ;
else if ( is_secondary ( i2s ) )
2019-02-14 12:37:35 +03:00
delay = FICS_TXCOUNT ( readl ( priv - > addr + I2SFICS ) ) ;
2010-11-22 09:36:59 +03:00
else
2019-02-14 12:37:37 +03:00
delay = ( reg > > priv - > variant_regs - > ftx0cnt_off ) & 0x7f ;
2010-11-22 09:36:59 +03:00
return delay ;
}
# ifdef CONFIG_PM
2020-01-20 04:04:42 +03:00
static int i2s_suspend ( struct snd_soc_component * component )
2010-11-22 09:36:59 +03:00
{
2020-01-20 04:04:42 +03:00
return pm_runtime_force_suspend ( component - > dev ) ;
2010-11-22 09:36:59 +03:00
}
2020-01-20 04:04:42 +03:00
static int i2s_resume ( struct snd_soc_component * component )
2010-11-22 09:36:59 +03:00
{
2020-01-20 04:04:42 +03:00
return pm_runtime_force_resume ( component - > dev ) ;
2010-11-22 09:36:59 +03:00
}
# else
# define i2s_suspend NULL
# define i2s_resume NULL
# endif
static int samsung_i2s_dai_probe ( struct snd_soc_dai * dai )
{
2019-02-14 12:37:35 +03:00
struct samsung_i2s_priv * priv = snd_soc_dai_get_drvdata ( dai ) ;
2010-11-22 09:36:59 +03:00
struct i2s_dai * i2s = to_info ( dai ) ;
2015-01-14 21:42:33 +03:00
struct i2s_dai * other = get_other_dai ( i2s ) ;
2015-01-14 21:42:36 +03:00
unsigned long flags ;
2010-11-22 09:36:59 +03:00
2016-12-29 14:34:04 +03:00
pm_runtime_get_sync ( dai - > dev ) ;
2019-02-14 12:37:46 +03:00
if ( is_secondary ( i2s ) ) {
/* If this is probe on the secondary DAI */
2019-02-14 12:37:44 +03:00
snd_soc_dai_init_dma_data ( dai , & i2s - > dma_playback , NULL ) ;
2015-01-14 21:42:34 +03:00
} else {
2016-08-04 12:30:26 +03:00
snd_soc_dai_init_dma_data ( dai , & i2s - > dma_playback ,
2019-02-14 12:37:44 +03:00
& i2s - > dma_capture ) ;
2013-10-18 00:18:40 +04:00
2019-02-14 12:37:38 +03:00
if ( priv - > quirks & QUIRK_NEED_RSTCLR )
2019-02-14 12:37:35 +03:00
writel ( CON_RSTCLR , priv - > addr + I2SCON ) ;
2010-11-22 09:36:59 +03:00
2019-02-14 12:37:38 +03:00
if ( priv - > quirks & QUIRK_SUPPORTS_IDMA )
2019-02-14 12:37:35 +03:00
idma_reg_addr_init ( priv - > addr ,
2019-02-14 12:37:44 +03:00
other - > idma_playback . addr ) ;
2015-01-14 21:42:34 +03:00
}
2011-07-20 12:07:12 +04:00
2010-11-22 09:36:59 +03:00
/* Reset any constraint on RFS and BFS */
i2s - > rfs = 0 ;
i2s - > bfs = 0 ;
2015-01-14 21:42:36 +03:00
2019-02-14 12:37:36 +03:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
2010-11-22 09:36:59 +03:00
i2s_txctrl ( i2s , 0 ) ;
i2s_rxctrl ( i2s , 0 ) ;
i2s_fifo ( i2s , FIC_TXFLUSH ) ;
i2s_fifo ( other , FIC_TXFLUSH ) ;
i2s_fifo ( i2s , FIC_RXFLUSH ) ;
2019-02-14 12:37:36 +03:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2010-11-22 09:36:59 +03:00
/* Gate CDCLK by default */
if ( ! is_opened ( other ) )
i2s_set_sysclk ( dai , SAMSUNG_I2S_CDCLK ,
0 , SND_SOC_CLOCK_IN ) ;
2016-12-29 14:34:04 +03:00
pm_runtime_put ( dai - > dev ) ;
2010-11-22 09:36:59 +03:00
return 0 ;
}
static int samsung_i2s_dai_remove ( struct snd_soc_dai * dai )
{
2019-02-14 12:37:35 +03:00
struct samsung_i2s_priv * priv = snd_soc_dai_get_drvdata ( dai ) ;
2019-02-07 20:00:13 +03:00
struct i2s_dai * i2s = to_info ( dai ) ;
2016-10-21 16:18:48 +03:00
unsigned long flags ;
2010-11-22 09:36:59 +03:00
2016-12-29 14:34:04 +03:00
pm_runtime_get_sync ( dai - > dev ) ;
2015-01-14 21:42:32 +03:00
if ( ! is_secondary ( i2s ) ) {
2019-02-14 12:37:38 +03:00
if ( priv - > quirks & QUIRK_NEED_RSTCLR ) {
2019-02-14 12:37:36 +03:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
2019-02-14 12:37:35 +03:00
writel ( 0 , priv - > addr + I2SCON ) ;
2019-02-14 12:37:36 +03:00
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2015-01-14 21:42:36 +03:00
}
2010-11-22 09:36:59 +03:00
}
2016-12-29 14:34:04 +03:00
pm_runtime_put ( dai - > dev ) ;
2010-11-22 09:36:59 +03:00
return 0 ;
}
2011-11-23 14:40:40 +04:00
static const struct snd_soc_dai_ops samsung_i2s_dai_ops = {
2010-11-22 09:36:59 +03:00
. trigger = i2s_trigger ,
. hw_params = i2s_hw_params ,
2022-05-19 18:43:08 +03:00
. set_fmt = i2s_set_fmt ,
2010-11-22 09:36:59 +03:00
. set_clkdiv = i2s_set_clkdiv ,
. set_sysclk = i2s_set_sysclk ,
. startup = i2s_startup ,
. shutdown = i2s_shutdown ,
. delay = i2s_delay ,
} ;
2019-02-12 21:03:24 +03:00
static const struct snd_soc_dapm_widget samsung_i2s_widgets [ ] = {
/* Backend DAI */
SND_SOC_DAPM_AIF_OUT ( " Mixer DAI TX " , NULL , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_IN ( " Mixer DAI RX " , NULL , 0 , SND_SOC_NOPM , 0 , 0 ) ,
/* Playback Mixer */
SND_SOC_DAPM_MIXER ( " Playback Mixer " , SND_SOC_NOPM , 0 , 0 , NULL , 0 ) ,
} ;
static const struct snd_soc_dapm_route samsung_i2s_dapm_routes [ ] = {
2019-03-06 13:24:45 +03:00
{ " Playback Mixer " , NULL , " Primary Playback " } ,
{ " Playback Mixer " , NULL , " Secondary Playback " } ,
2019-02-12 21:03:24 +03:00
{ " Mixer DAI TX " , NULL , " Playback Mixer " } ,
2019-03-06 13:24:45 +03:00
{ " Primary Capture " , NULL , " Mixer DAI RX " } ,
2019-02-12 21:03:24 +03:00
} ;
2013-03-21 14:35:55 +04:00
static const struct snd_soc_component_driver samsung_i2s_component = {
2019-02-12 21:03:24 +03:00
. name = " samsung-i2s " ,
. dapm_widgets = samsung_i2s_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( samsung_i2s_widgets ) ,
. dapm_routes = samsung_i2s_dapm_routes ,
. num_dapm_routes = ARRAY_SIZE ( samsung_i2s_dapm_routes ) ,
2020-01-20 04:04:42 +03:00
. suspend = i2s_suspend ,
. resume = i2s_resume ,
2022-06-23 15:51:47 +03:00
. legacy_dai_naming = 1 ,
2013-03-21 14:35:55 +04:00
} ;
2019-02-14 12:37:45 +03:00
# define SAMSUNG_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE )
2010-11-22 09:36:59 +03:00
2019-02-07 20:00:13 +03:00
static int i2s_alloc_dais ( struct samsung_i2s_priv * priv ,
const struct samsung_i2s_dai_data * i2s_dai_data ,
int num_dais )
2010-11-22 09:36:59 +03:00
{
2019-02-07 20:00:13 +03:00
static const char * dai_names [ ] = { " samsung-i2s " , " samsung-i2s-sec " } ;
2019-03-06 13:24:45 +03:00
static const char * stream_names [ ] = { " Primary Playback " ,
" Secondary Playback " } ;
2019-02-07 20:00:13 +03:00
struct snd_soc_dai_driver * dai_drv ;
int i ;
priv - > dai = devm_kcalloc ( & priv - > pdev - > dev , num_dais ,
2021-02-20 02:09:13 +03:00
sizeof ( struct i2s_dai ) , GFP_KERNEL ) ;
2019-02-07 20:00:13 +03:00
if ( ! priv - > dai )
return - ENOMEM ;
priv - > dai_drv = devm_kcalloc ( & priv - > pdev - > dev , num_dais ,
sizeof ( * dai_drv ) , GFP_KERNEL ) ;
if ( ! priv - > dai_drv )
return - ENOMEM ;
for ( i = 0 ; i < num_dais ; i + + ) {
dai_drv = & priv - > dai_drv [ i ] ;
dai_drv - > probe = samsung_i2s_dai_probe ;
dai_drv - > remove = samsung_i2s_dai_remove ;
2021-01-15 07:53:43 +03:00
dai_drv - > symmetric_rate = 1 ;
2019-02-07 20:00:13 +03:00
dai_drv - > ops = & samsung_i2s_dai_ops ;
dai_drv - > playback . channels_min = 1 ;
dai_drv - > playback . channels_max = 2 ;
dai_drv - > playback . rates = i2s_dai_data - > pcm_rates ;
dai_drv - > playback . formats = SAMSUNG_I2S_FMTS ;
2019-02-12 21:03:24 +03:00
dai_drv - > playback . stream_name = stream_names [ i ] ;
2019-02-07 20:00:13 +03:00
dai_drv - > id = i + 1 ;
dai_drv - > name = dai_names [ i ] ;
priv - > dai [ i ] . drv = & priv - > dai_drv [ i ] ;
priv - > dai [ i ] . pdev = priv - > pdev ;
2013-04-02 15:23:02 +04:00
}
2019-02-07 20:00:13 +03:00
/* Initialize capture only for the primary DAI */
dai_drv = & priv - > dai_drv [ SAMSUNG_I2S_ID_PRIMARY - 1 ] ;
dai_drv - > capture . channels_min = 1 ;
dai_drv - > capture . channels_max = 2 ;
dai_drv - > capture . rates = i2s_dai_data - > pcm_rates ;
dai_drv - > capture . formats = SAMSUNG_I2S_FMTS ;
2019-03-06 13:24:45 +03:00
dai_drv - > capture . stream_name = " Primary Capture " ;
2019-02-07 20:00:13 +03:00
return 0 ;
2010-11-22 09:36:59 +03:00
}
2014-12-13 02:42:18 +03:00
# ifdef CONFIG_PM
2013-01-30 16:11:04 +04:00
static int i2s_runtime_suspend ( struct device * dev )
{
2019-02-12 21:03:25 +03:00
struct samsung_i2s_priv * priv = dev_get_drvdata ( dev ) ;
2013-01-30 16:11:04 +04:00
2019-02-14 12:37:35 +03:00
priv - > suspend_i2smod = readl ( priv - > addr + I2SMOD ) ;
priv - > suspend_i2scon = readl ( priv - > addr + I2SCON ) ;
priv - > suspend_i2spsr = readl ( priv - > addr + I2SPSR ) ;
2016-12-29 14:34:05 +03:00
2020-10-29 11:41:37 +03:00
clk_disable_unprepare ( priv - > op_clk ) ;
2019-02-12 21:03:25 +03:00
clk_disable_unprepare ( priv - > clk ) ;
2013-01-30 16:11:04 +04:00
return 0 ;
}
static int i2s_runtime_resume ( struct device * dev )
{
2019-02-12 21:03:25 +03:00
struct samsung_i2s_priv * priv = dev_get_drvdata ( dev ) ;
2017-07-25 13:14:31 +03:00
int ret ;
2013-01-30 16:11:04 +04:00
2019-02-12 21:03:25 +03:00
ret = clk_prepare_enable ( priv - > clk ) ;
2017-07-25 13:14:31 +03:00
if ( ret )
return ret ;
2019-02-12 21:03:26 +03:00
if ( priv - > op_clk ) {
ret = clk_prepare_enable ( priv - > op_clk ) ;
2017-07-25 13:14:31 +03:00
if ( ret ) {
2019-02-12 21:03:25 +03:00
clk_disable_unprepare ( priv - > clk ) ;
2017-07-25 13:14:31 +03:00
return ret ;
}
}
2013-01-30 16:11:04 +04:00
2019-02-14 12:37:35 +03:00
writel ( priv - > suspend_i2scon , priv - > addr + I2SCON ) ;
writel ( priv - > suspend_i2smod , priv - > addr + I2SMOD ) ;
writel ( priv - > suspend_i2spsr , priv - > addr + I2SPSR ) ;
2013-01-30 16:11:04 +04:00
return 0 ;
}
2014-12-13 02:42:18 +03:00
# endif /* CONFIG_PM */
2013-01-30 16:11:04 +04:00
2019-02-12 21:03:23 +03:00
static void i2s_unregister_clocks ( struct samsung_i2s_priv * priv )
2015-01-14 21:42:39 +03:00
{
int i ;
2019-02-12 21:03:23 +03:00
for ( i = 0 ; i < priv - > clk_data . clk_num ; i + + ) {
if ( ! IS_ERR ( priv - > clk_table [ i ] ) )
clk_unregister ( priv - > clk_table [ i ] ) ;
2015-01-14 21:42:39 +03:00
}
}
2019-02-12 21:03:23 +03:00
static void i2s_unregister_clock_provider ( struct samsung_i2s_priv * priv )
2015-01-14 21:42:39 +03:00
{
2019-02-12 21:03:23 +03:00
of_clk_del_provider ( priv - > pdev - > dev . of_node ) ;
i2s_unregister_clocks ( priv ) ;
2015-01-14 21:42:39 +03:00
}
2019-02-12 21:03:23 +03:00
static int i2s_register_clock_provider ( struct samsung_i2s_priv * priv )
2015-01-14 21:42:39 +03:00
{
2019-02-07 20:00:13 +03:00
2018-02-12 19:15:33 +03:00
const char * const i2s_clk_desc [ ] = { " cdclk " , " rclk_src " , " prescaler " } ;
2015-01-14 21:42:39 +03:00
const char * clk_name [ 2 ] = { " i2s_opclk0 " , " i2s_opclk1 " } ;
const char * p_names [ 2 ] = { NULL } ;
2019-02-12 21:03:23 +03:00
struct device * dev = & priv - > pdev - > dev ;
2019-02-14 12:37:37 +03:00
const struct samsung_i2s_variant_regs * reg_info = priv - > variant_regs ;
2018-02-12 19:15:33 +03:00
const char * i2s_clk_name [ ARRAY_SIZE ( i2s_clk_desc ) ] ;
2015-01-14 21:42:39 +03:00
struct clk * rclksrc ;
int ret , i ;
/* Register the clock provider only if it's expected in the DTB */
2023-03-10 17:47:31 +03:00
if ( ! of_property_present ( dev - > of_node , " #clock-cells " ) )
2015-01-14 21:42:39 +03:00
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 19:15:33 +03: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 ;
}
2019-02-14 12:37:38 +03:00
if ( ! ( priv - > quirks & QUIRK_NO_MUXPSR ) ) {
2015-01-14 21:42:39 +03:00
/* Activate the prescaler */
2019-02-14 12:37:35 +03:00
u32 val = readl ( priv - > addr + I2SPSR ) ;
writel ( val | PSR_PSREN , priv - > addr + I2SPSR ) ;
2015-01-14 21:42:39 +03:00
2019-02-12 21:03:23 +03:00
priv - > clk_table [ CLK_I2S_RCLK_SRC ] = clk_register_mux ( dev ,
2018-02-12 19:15:33 +03:00
i2s_clk_name [ CLK_I2S_RCLK_SRC ] , p_names ,
ARRAY_SIZE ( p_names ) ,
2015-01-14 21:42:39 +03:00
CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT ,
2019-02-14 12:37:35 +03:00
priv - > addr + I2SMOD , reg_info - > rclksrc_off ,
2019-02-14 12:37:36 +03:00
1 , 0 , & priv - > lock ) ;
2015-01-14 21:42:39 +03:00
2019-02-12 21:03:23 +03:00
priv - > clk_table [ CLK_I2S_RCLK_PSR ] = clk_register_divider ( dev ,
2018-02-12 19:15:33 +03:00
i2s_clk_name [ CLK_I2S_RCLK_PSR ] ,
i2s_clk_name [ CLK_I2S_RCLK_SRC ] ,
2015-01-14 21:42:39 +03:00
CLK_SET_RATE_PARENT ,
2019-02-14 12:37:36 +03:00
priv - > addr + I2SPSR , 8 , 6 , 0 , & priv - > lock ) ;
2015-01-14 21:42:39 +03:00
2018-02-12 19:15:33 +03:00
p_names [ 0 ] = i2s_clk_name [ CLK_I2S_RCLK_PSR ] ;
2019-02-12 21:03:23 +03:00
priv - > clk_data . clk_num = 2 ;
2015-01-14 21:42:39 +03:00
}
2019-02-12 21:03:23 +03:00
priv - > clk_table [ CLK_I2S_CDCLK ] = clk_register_gate ( dev ,
2018-02-12 19:15:33 +03:00
i2s_clk_name [ CLK_I2S_CDCLK ] , p_names [ 0 ] ,
CLK_SET_RATE_PARENT ,
2019-02-14 12:37:35 +03:00
priv - > addr + I2SMOD , reg_info - > cdclkcon_off ,
2019-02-14 12:37:36 +03:00
CLK_GATE_SET_TO_DISABLE , & priv - > lock ) ;
2015-01-14 21:42:39 +03:00
2019-02-12 21:03:23 +03:00
priv - > clk_data . clk_num + = 1 ;
priv - > clk_data . clks = priv - > clk_table ;
2015-01-14 21:42:39 +03:00
ret = of_clk_add_provider ( dev - > of_node , of_clk_src_onecell_get ,
2019-02-12 21:03:23 +03:00
& priv - > clk_data ) ;
2015-01-14 21:42:39 +03:00
if ( ret < 0 ) {
dev_err ( dev , " failed to add clock provider: %d \n " , ret ) ;
2019-02-12 21:03:23 +03:00
i2s_unregister_clocks ( priv ) ;
2015-01-14 21:42:39 +03:00
}
return ret ;
}
2019-02-12 21:03:22 +03:00
/* Create platform device for the secondary PCM */
static int i2s_create_secondary_device ( struct samsung_i2s_priv * priv )
{
2019-02-19 18:19:41 +03:00
struct platform_device * pdev_sec ;
const char * devname ;
2019-02-12 21:03:22 +03:00
int ret ;
2019-02-19 18:19:41 +03:00
devname = devm_kasprintf ( & priv - > pdev - > dev , GFP_KERNEL , " %s-sec " ,
dev_name ( & priv - > pdev - > dev ) ) ;
if ( ! devname )
2019-02-12 21:03:22 +03:00
return - ENOMEM ;
2019-02-19 18:19:41 +03:00
pdev_sec = platform_device_alloc ( devname , - 1 ) ;
if ( ! pdev_sec )
return - ENOMEM ;
pdev_sec - > driver_override = kstrdup ( " samsung-i2s " , GFP_KERNEL ) ;
2022-03-14 13:36:45 +03:00
if ( ! pdev_sec - > driver_override ) {
platform_device_put ( pdev_sec ) ;
return - ENOMEM ;
}
2019-02-19 18:19:41 +03:00
ret = platform_device_add ( pdev_sec ) ;
2019-02-12 21:03:22 +03:00
if ( ret < 0 ) {
2019-02-19 18:19:41 +03:00
platform_device_put ( pdev_sec ) ;
2019-02-12 21:03:22 +03:00
return ret ;
}
2019-02-19 18:19:41 +03:00
ret = device_attach ( & pdev_sec - > dev ) ;
if ( ret < = 0 ) {
platform_device_unregister ( priv - > pdev_sec ) ;
dev_info ( & pdev_sec - > dev , " device_attach() failed \n " ) ;
return ret ;
}
priv - > pdev_sec = pdev_sec ;
2019-02-12 21:03:22 +03:00
return 0 ;
}
static void i2s_delete_secondary_device ( struct samsung_i2s_priv * priv )
{
2019-02-19 18:19:40 +03:00
platform_device_unregister ( priv - > pdev_sec ) ;
priv - > pdev_sec = NULL ;
2019-02-12 21:03:22 +03:00
}
2019-02-19 18:19:40 +03:00
2012-12-07 18:26:15 +04:00
static int samsung_i2s_probe ( struct platform_device * pdev )
2010-11-22 09:36:59 +03:00
{
struct i2s_dai * pri_dai , * sec_dai = NULL ;
2013-01-18 15:47:01 +04:00
struct s3c_audio_pdata * i2s_pdata = pdev - > dev . platform_data ;
2019-02-14 18:58:40 +03:00
u32 regs_base , idma_addr = 0 ;
2013-01-18 15:47:01 +04:00
struct device_node * np = pdev - > dev . of_node ;
2013-08-12 13:49:51 +04:00
const struct samsung_i2s_dai_data * i2s_dai_data ;
2019-02-19 18:19:41 +03:00
const struct platform_device_id * id ;
2019-02-07 20:00:13 +03:00
struct samsung_i2s_priv * priv ;
2019-02-19 18:19:41 +03:00
struct resource * res ;
int num_dais , ret ;
2010-11-22 09:36:59 +03:00
2019-02-19 18:19:41 +03:00
if ( IS_ENABLED ( CONFIG_OF ) & & pdev - > dev . of_node ) {
2016-04-08 19:52:45 +03:00
i2s_dai_data = of_device_get_match_data ( & pdev - > dev ) ;
2019-02-19 18:19:41 +03:00
} else {
id = platform_get_device_id ( pdev ) ;
2013-01-18 15:47:00 +04:00
2019-02-19 18:19:41 +03:00
/* Nothing to do if it is the secondary device probe */
if ( ! id )
return 0 ;
i2s_dai_data = ( struct samsung_i2s_dai_data * ) id - > driver_data ;
}
2019-02-12 21:03:22 +03:00
2019-02-07 20:00:13 +03:00
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
2013-01-18 15:47:01 +04:00
return - ENOMEM ;
2010-11-22 09:36:59 +03:00
2019-02-14 18:58:40 +03:00
if ( np ) {
priv - > quirks = i2s_dai_data - > quirks ;
2023-01-16 13:38:20 +03:00
priv - > fixup_early = i2s_dai_data - > fixup_early ;
priv - > fixup_late = i2s_dai_data - > fixup_late ;
2019-02-14 18:58:40 +03:00
} else {
if ( ! i2s_pdata ) {
dev_err ( & pdev - > dev , " Missing platform data \n " ) ;
return - EINVAL ;
}
priv - > quirks = i2s_pdata - > type . quirks ;
}
num_dais = ( priv - > quirks & QUIRK_SEC_DAI ) ? 2 : 1 ;
2019-02-07 20:00:13 +03:00
priv - > pdev = pdev ;
2019-02-14 12:37:38 +03:00
priv - > variant_regs = i2s_dai_data - > i2s_variant_regs ;
2019-02-07 20:00:13 +03:00
ret = i2s_alloc_dais ( priv , i2s_dai_data , num_dais ) ;
if ( ret < 0 )
return ret ;
pri_dai = & priv - > dai [ SAMSUNG_I2S_ID_PRIMARY - 1 ] ;
2019-02-14 12:37:36 +03:00
spin_lock_init ( & priv - > lock ) ;
2019-02-14 12:37:39 +03:00
spin_lock_init ( & priv - > pcm_lock ) ;
2015-01-14 21:42:35 +03:00
2013-01-18 15:47:01 +04:00
if ( ! np ) {
2016-08-04 12:30:26 +03:00
pri_dai - > dma_playback . filter_data = i2s_pdata - > dma_playback ;
pri_dai - > dma_capture . filter_data = i2s_pdata - > dma_capture ;
2015-11-19 00:31:11 +03:00
pri_dai - > filter = i2s_pdata - > dma_filter ;
2015-11-18 17:25:23 +03:00
2016-12-10 12:51:11 +03:00
idma_addr = i2s_pdata - > type . idma_addr ;
2013-01-18 15:47:01 +04:00
} else {
if ( of_property_read_u32 ( np , " samsung,idma-addr " ,
& idma_addr ) ) {
2019-02-14 18:58:40 +03:00
if ( priv - > quirks & QUIRK_SUPPORTS_IDMA ) {
2014-11-07 09:54:39 +03:00
dev_info ( & pdev - > dev , " idma address is not " \
2013-01-18 15:47:01 +04:00
" specified " ) ;
}
}
}
2010-11-22 09:36:59 +03:00
2021-06-16 12:16:49 +03:00
priv - > addr = devm_platform_get_and_ioremap_resource ( pdev , 0 , & res ) ;
2019-02-14 12:37:35 +03:00
if ( IS_ERR ( priv - > addr ) )
return PTR_ERR ( priv - > addr ) ;
2010-11-22 09:36:59 +03:00
regs_base = res - > start ;
2019-02-12 21:03:25 +03:00
priv - > clk = devm_clk_get ( & pdev - > dev , " iis " ) ;
if ( IS_ERR ( priv - > clk ) ) {
2015-01-14 21:42:31 +03:00
dev_err ( & pdev - > dev , " Failed to get iis clock \n " ) ;
2019-02-12 21:03:25 +03:00
return PTR_ERR ( priv - > clk ) ;
2015-01-14 21:42:31 +03:00
}
2015-01-14 21:42:32 +03:00
2019-02-12 21:03:25 +03:00
ret = clk_prepare_enable ( priv - > clk ) ;
2015-01-14 21:42:32 +03:00
if ( ret ! = 0 ) {
dev_err ( & pdev - > dev , " failed to enable clock: %d \n " , ret ) ;
return ret ;
}
2016-08-04 12:30:26 +03:00
pri_dai - > dma_playback . addr = regs_base + I2STXD ;
pri_dai - > dma_capture . addr = regs_base + I2SRXD ;
2017-01-17 16:16:42 +03:00
pri_dai - > dma_playback . chan_name = " tx " ;
pri_dai - > dma_capture . chan_name = " rx " ;
2016-08-04 12:30:26 +03:00
pri_dai - > dma_playback . addr_width = 4 ;
pri_dai - > dma_capture . addr_width = 4 ;
2019-02-12 21:03:23 +03:00
pri_dai - > priv = priv ;
2010-11-22 09:36:59 +03:00
2019-02-14 18:58:40 +03:00
if ( priv - > quirks & QUIRK_PRI_6CHAN )
2019-02-07 20:00:13 +03:00
pri_dai - > drv - > playback . channels_max = 6 ;
2010-11-22 09:36:59 +03:00
2016-10-27 13:34:02 +03:00
ret = samsung_asoc_dma_platform_register ( & pdev - > dev , pri_dai - > filter ,
2019-02-14 12:37:41 +03:00
" tx " , " rx " , NULL ) ;
2016-10-27 13:34:02 +03:00
if ( ret < 0 )
goto err_disable_clk ;
2019-02-14 18:58:40 +03:00
if ( priv - > quirks & QUIRK_SEC_DAI ) {
2019-02-07 20:00:13 +03:00
sec_dai = & priv - > dai [ SAMSUNG_I2S_ID_SECONDARY - 1 ] ;
2014-12-08 20:45:54 +03:00
2016-08-04 12:30:26 +03:00
sec_dai - > dma_playback . addr = regs_base + I2STXDS ;
2017-01-17 16:16:42 +03:00
sec_dai - > dma_playback . chan_name = " tx-sec " ;
2013-01-18 15:47:01 +04:00
2015-11-19 00:31:11 +03:00
if ( ! np ) {
2016-08-04 12:30:26 +03:00
sec_dai - > dma_playback . filter_data = i2s_pdata - > dma_play_sec ;
2015-11-19 00:31:11 +03:00
sec_dai - > filter = i2s_pdata - > dma_filter ;
}
2013-01-18 15:47:01 +04:00
2016-08-04 12:30:26 +03:00
sec_dai - > dma_playback . addr_width = 4 ;
sec_dai - > idma_playback . addr = idma_addr ;
2010-11-22 09:36:59 +03:00
sec_dai - > pri_dai = pri_dai ;
2019-02-12 21:03:23 +03:00
sec_dai - > priv = priv ;
2010-11-22 09:36:59 +03:00
pri_dai - > sec_dai = sec_dai ;
2016-12-29 14:34:03 +03:00
2019-02-12 21:03:22 +03:00
ret = i2s_create_secondary_device ( priv ) ;
if ( ret < 0 )
goto err_disable_clk ;
ret = samsung_asoc_dma_platform_register ( & priv - > pdev_sec - > dev ,
sec_dai - > filter , " tx-sec " , NULL ,
& pdev - > dev ) ;
2016-12-29 14:34:03 +03:00
if ( ret < 0 )
2019-02-19 18:19:40 +03:00
goto err_del_sec ;
2016-12-29 14:34:03 +03:00
2010-11-22 09:36:59 +03:00
}
2013-07-02 16:10:28 +04: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 18:16:42 +03:00
ret = - EINVAL ;
2019-02-19 18:19:40 +03:00
goto err_del_sec ;
2010-11-22 09:36:59 +03:00
}
2019-02-07 20:00:13 +03:00
dev_set_drvdata ( & pdev - > dev , priv ) ;
ret = devm_snd_soc_register_component ( & pdev - > dev ,
& samsung_i2s_component ,
priv - > dai_drv , num_dais ) ;
if ( ret < 0 )
2019-02-19 18:19:40 +03:00
goto err_del_sec ;
2010-11-22 09:36:59 +03:00
2016-12-29 14:34:04 +03:00
pm_runtime_set_active ( & pdev - > dev ) ;
2011-12-08 12:45:03 +04:00
pm_runtime_enable ( & pdev - > dev ) ;
2019-02-12 21:03:23 +03:00
ret = i2s_register_clock_provider ( priv ) ;
2018-02-12 19:15:34 +03:00
if ( ret < 0 )
goto err_disable_pm ;
2019-02-12 21:03:26 +03:00
priv - > op_clk = clk_get_parent ( priv - > clk_table [ CLK_I2S_RCLK_SRC ] ) ;
2018-02-12 19:15:34 +03:00
return 0 ;
2012-12-07 12:29:21 +04:00
2018-02-12 19:15:34 +03:00
err_disable_pm :
2016-07-21 21:03:49 +03:00
pm_runtime_disable ( & pdev - > dev ) ;
2019-02-19 18:19:40 +03:00
err_del_sec :
i2s_delete_secondary_device ( priv ) ;
2016-08-23 18:16:42 +03:00
err_disable_clk :
2019-02-12 21:03:25 +03:00
clk_disable_unprepare ( priv - > clk ) ;
2016-07-21 21:03:49 +03:00
return ret ;
2010-11-22 09:36:59 +03:00
}
2023-03-15 18:06:57 +03:00
static void samsung_i2s_remove ( struct platform_device * pdev )
2010-11-22 09:36:59 +03:00
{
2019-02-07 20:00:13 +03:00
struct samsung_i2s_priv * priv = dev_get_drvdata ( & pdev - > dev ) ;
2010-11-22 09:36:59 +03:00
2019-02-12 21:03:22 +03:00
/* The secondary device has no driver data assigned */
if ( ! priv )
2023-03-15 18:06:57 +03:00
return ;
2019-02-12 21:03:22 +03:00
2016-12-29 14:34:04 +03:00
pm_runtime_get_sync ( & pdev - > dev ) ;
2016-12-29 14:34:03 +03:00
pm_runtime_disable ( & pdev - > dev ) ;
2015-01-14 21:42:32 +03:00
2019-02-12 21:03:23 +03:00
i2s_unregister_clock_provider ( priv ) ;
2019-02-19 18:19:40 +03:00
i2s_delete_secondary_device ( priv ) ;
2019-02-12 21:03:25 +03:00
clk_disable_unprepare ( priv - > clk ) ;
2019-02-19 18:19:40 +03:00
2016-12-29 14:34:04 +03:00
pm_runtime_put_noidle ( & pdev - > dev ) ;
2010-11-22 09:36:59 +03:00
}
2023-01-16 13:38:20 +03:00
static void fsd_i2s_fixup_early ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
struct snd_soc_pcm_runtime * rtd = asoc_substream_to_rtd ( substream ) ;
struct i2s_dai * i2s = to_info ( asoc_rtd_to_cpu ( rtd , 0 ) ) ;
struct i2s_dai * other = get_other_dai ( i2s ) ;
if ( ! is_opened ( other ) ) {
i2s_set_sysclk ( dai , SAMSUNG_I2S_CDCLK , 0 , SND_SOC_CLOCK_OUT ) ;
i2s_set_sysclk ( dai , SAMSUNG_I2S_OPCLK , 0 , MOD_OPCLK_PCLK ) ;
}
}
static void fsd_i2s_fixup_late ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
struct snd_soc_pcm_runtime * rtd = asoc_substream_to_rtd ( substream ) ;
struct samsung_i2s_priv * priv = snd_soc_dai_get_drvdata ( dai ) ;
struct i2s_dai * i2s = to_info ( asoc_rtd_to_cpu ( rtd , 0 ) ) ;
struct i2s_dai * other = get_other_dai ( i2s ) ;
if ( ! is_opened ( other ) )
writel ( PSR_PSVAL ( 2 ) | PSR_PSREN , priv - > addr + I2SPSR ) ;
}
2014-11-07 09:54:40 +03:00
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 13:49:51 +04:00
static const struct samsung_i2s_dai_data i2sv3_dai_type = {
. quirks = QUIRK_NO_MUXPSR ,
2017-07-07 04:31:10 +03:00
. pcm_rates = SNDRV_PCM_RATE_8000_96000 ,
2014-11-07 09:54:40 +03:00
. i2s_variant_regs = & i2sv3_regs ,
2013-08-12 13:49:51 +04:00
} ;
2020-11-25 19:44:48 +03:00
static const struct samsung_i2s_dai_data i2sv5_dai_type __maybe_unused = {
2014-11-07 09:54:39 +03:00
. quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
QUIRK_SUPPORTS_IDMA ,
2017-07-07 04:31:10 +03:00
. pcm_rates = SNDRV_PCM_RATE_8000_96000 ,
2014-11-07 09:54:40 +03:00
. i2s_variant_regs = & i2sv3_regs ,
2013-08-12 13:49:51 +04:00
} ;
2020-11-25 19:44:48 +03:00
static const struct samsung_i2s_dai_data i2sv6_dai_type __maybe_unused = {
2013-08-12 13:49:52 +04:00
. quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
2014-11-07 09:54:39 +03:00
QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA ,
2017-07-07 04:31:10 +03:00
. pcm_rates = SNDRV_PCM_RATE_8000_96000 ,
2014-11-07 09:54:40 +03:00
. i2s_variant_regs = & i2sv6_regs ,
} ;
2020-11-25 19:44:48 +03:00
static const struct samsung_i2s_dai_data i2sv7_dai_type __maybe_unused = {
2014-11-07 09:54:40 +03:00
. quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
QUIRK_SUPPORTS_TDM ,
2017-07-07 04:31:10 +03:00
. pcm_rates = SNDRV_PCM_RATE_8000_192000 ,
2014-11-07 09:54:40 +03:00
. i2s_variant_regs = & i2sv7_regs ,
} ;
2020-11-25 19:44:48 +03:00
static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 __maybe_unused = {
2014-11-07 09:54:40 +03:00
. quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR ,
2017-07-07 04:31:10 +03:00
. pcm_rates = SNDRV_PCM_RATE_8000_96000 ,
2014-11-07 09:54:40 +03:00
. i2s_variant_regs = & i2sv5_i2s1_regs ,
2013-08-12 13:49:52 +04:00
} ;
2023-01-16 13:38:20 +03:00
static const struct samsung_i2s_dai_data fsd_dai_type __maybe_unused = {
. quirks = QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_TDM ,
. pcm_rates = SNDRV_PCM_RATE_8000_192000 ,
. i2s_variant_regs = & i2sv7_regs ,
. fixup_early = fsd_i2s_fixup_early ,
. fixup_late = fsd_i2s_fixup_late ,
} ;
2015-05-01 19:00:14 +03:00
static const struct platform_device_id samsung_i2s_driver_ids [ ] = {
2013-01-18 15:47:00 +04:00
{
. name = " samsung-i2s " ,
2014-12-05 22:56:17 +03:00
. driver_data = ( kernel_ulong_t ) & i2sv3_dai_type ,
2013-01-18 15:47:00 +04:00
} ,
{ } ,
} ;
2013-04-11 04:05:03 +04:00
MODULE_DEVICE_TABLE ( platform , samsung_i2s_driver_ids ) ;
2013-01-18 15:47:00 +04:00
2013-01-18 15:47:01 +04:00
# ifdef CONFIG_OF
static const struct of_device_id exynos_i2s_match [ ] = {
2013-08-12 13:49:51 +04:00
{
. compatible = " samsung,s3c6410-i2s " ,
. data = & i2sv3_dai_type ,
} , {
. compatible = " samsung,s5pv210-i2s " ,
. data = & i2sv5_dai_type ,
2013-08-12 13:49:52 +04:00
} , {
. compatible = " samsung,exynos5420-i2s " ,
. data = & i2sv6_dai_type ,
2014-11-07 09:54:40 +03:00
} , {
. compatible = " samsung,exynos7-i2s " ,
. data = & i2sv7_dai_type ,
} , {
. compatible = " samsung,exynos7-i2s1 " ,
. data = & i2sv5_dai_type_i2s1 ,
2023-01-16 13:38:20 +03:00
} , {
. compatible = " tesla,fsd-i2s " ,
. data = & fsd_dai_type ,
2013-01-18 15:47:01 +04:00
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , exynos_i2s_match ) ;
# endif
2013-01-30 16:11:04 +04:00
static const struct dev_pm_ops samsung_i2s_pm = {
SET_RUNTIME_PM_OPS ( i2s_runtime_suspend ,
i2s_runtime_resume , NULL )
2016-12-29 14:34:05 +03:00
SET_SYSTEM_SLEEP_PM_OPS ( pm_runtime_force_suspend ,
pm_runtime_force_resume )
2013-01-30 16:11:04 +04:00
} ;
2010-11-22 09:36:59 +03:00
static struct platform_driver samsung_i2s_driver = {
. probe = samsung_i2s_probe ,
2023-03-15 18:06:57 +03:00
. remove_new = samsung_i2s_remove ,
2013-01-18 15:47:00 +04:00
. id_table = samsung_i2s_driver_ids ,
2010-11-22 09:36:59 +03:00
. driver = {
. name = " samsung-i2s " ,
2013-01-18 15:47:01 +04:00
. of_match_table = of_match_ptr ( exynos_i2s_match ) ,
2013-01-30 16:11:04 +04:00
. pm = & samsung_i2s_pm ,
2010-11-22 09:36:59 +03:00
} ,
} ;
2011-11-23 19:20:13 +04:00
module_platform_driver ( samsung_i2s_driver ) ;
2010-11-22 09:36:59 +03:00
/* Module information */
2012-02-25 14:54:36 +04:00
MODULE_AUTHOR ( " Jaswinder Singh, <jassisinghbrar@gmail.com> " ) ;
2010-11-22 09:36:59 +03:00
MODULE_DESCRIPTION ( " Samsung I2S Interface " ) ;
MODULE_ALIAS ( " platform:samsung-i2s " ) ;
MODULE_LICENSE ( " GPL " ) ;