2011-01-07 22:36:14 -07:00
/*
2012-04-05 15:54:53 -06:00
* tegra20_i2s . c - Tegra20 I2S driver
2011-01-07 22:36:14 -07:00
*
* Author : Stephen Warren < swarren @ nvidia . com >
2012-03-20 14:55:49 -06:00
* Copyright ( C ) 2010 , 2012 - NVIDIA , Inc .
2011-01-07 22:36:14 -07:00
*
* Based on code copyright / by :
*
* Copyright ( c ) 2009 - 2010 , NVIDIA Corporation .
* Scott Peterson < speterson @ nvidia . com >
*
* Copyright ( C ) 2010 Google , Inc .
* Iliyan Malchev < malchev @ google . com >
*
* 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 .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA
* 02110 - 1301 USA
*
*/
# include <linux/clk.h>
# include <linux/device.h>
2012-04-06 11:12:25 -06:00
# include <linux/io.h>
# include <linux/module.h>
# include <linux/of.h>
2011-01-07 22:36:14 -07:00
# include <linux/platform_device.h>
2012-04-09 09:52:22 -06:00
# include <linux/pm_runtime.h>
2012-04-13 12:14:06 -06:00
# include <linux/regmap.h>
2011-01-07 22:36:14 -07:00
# include <linux/slab.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/soc.h>
2012-04-05 15:54:53 -06:00
# include "tegra20_i2s.h"
2011-01-07 22:36:14 -07:00
2012-04-06 10:30:52 -06:00
# define DRV_NAME "tegra20-i2s"
2011-01-07 22:36:14 -07:00
2012-04-06 10:30:52 -06:00
static inline void tegra20_i2s_write ( struct tegra20_i2s * i2s , u32 reg , u32 val )
2011-01-07 22:36:14 -07:00
{
2012-04-13 12:14:06 -06:00
regmap_write ( i2s - > regmap , reg , val ) ;
2011-01-07 22:36:14 -07:00
}
2012-04-06 10:30:52 -06:00
static inline u32 tegra20_i2s_read ( struct tegra20_i2s * i2s , u32 reg )
2011-01-07 22:36:14 -07:00
{
2012-04-13 12:14:06 -06:00
u32 val ;
regmap_read ( i2s - > regmap , reg , & val ) ;
return val ;
2011-01-07 22:36:14 -07:00
}
2012-04-09 09:52:22 -06:00
static int tegra20_i2s_runtime_suspend ( struct device * dev )
{
struct tegra20_i2s * i2s = dev_get_drvdata ( dev ) ;
clk_disable ( i2s - > clk_i2s ) ;
return 0 ;
}
static int tegra20_i2s_runtime_resume ( struct device * dev )
{
struct tegra20_i2s * i2s = dev_get_drvdata ( dev ) ;
int ret ;
ret = clk_enable ( i2s - > clk_i2s ) ;
if ( ret ) {
dev_err ( dev , " clk_enable failed: %d \n " , ret ) ;
return ret ;
}
return 0 ;
}
2012-04-06 10:30:52 -06:00
static int tegra20_i2s_set_fmt ( struct snd_soc_dai * dai ,
2011-01-07 22:36:14 -07:00
unsigned int fmt )
{
2012-04-06 10:30:52 -06:00
struct tegra20_i2s * i2s = snd_soc_dai_get_drvdata ( dai ) ;
2011-01-07 22:36:14 -07:00
switch ( fmt & SND_SOC_DAIFMT_INV_MASK ) {
case SND_SOC_DAIFMT_NB_NF :
break ;
default :
return - EINVAL ;
}
2012-04-06 10:30:52 -06:00
i2s - > reg_ctrl & = ~ TEGRA20_I2S_CTRL_MASTER_ENABLE ;
2011-01-07 22:36:14 -07:00
switch ( fmt & SND_SOC_DAIFMT_MASTER_MASK ) {
case SND_SOC_DAIFMT_CBS_CFS :
2012-04-06 10:30:52 -06:00
i2s - > reg_ctrl | = TEGRA20_I2S_CTRL_MASTER_ENABLE ;
2011-01-07 22:36:14 -07:00
break ;
case SND_SOC_DAIFMT_CBM_CFM :
break ;
default :
return - EINVAL ;
}
2012-04-06 10:30:52 -06:00
i2s - > reg_ctrl & = ~ ( TEGRA20_I2S_CTRL_BIT_FORMAT_MASK |
TEGRA20_I2S_CTRL_LRCK_MASK ) ;
2011-01-07 22:36:14 -07:00
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_DSP_A :
2012-04-06 10:30:52 -06:00
i2s - > reg_ctrl | = TEGRA20_I2S_CTRL_BIT_FORMAT_DSP ;
i2s - > reg_ctrl | = TEGRA20_I2S_CTRL_LRCK_L_LOW ;
2011-01-07 22:36:14 -07:00
break ;
case SND_SOC_DAIFMT_DSP_B :
2012-04-06 10:30:52 -06:00
i2s - > reg_ctrl | = TEGRA20_I2S_CTRL_BIT_FORMAT_DSP ;
i2s - > reg_ctrl | = TEGRA20_I2S_CTRL_LRCK_R_LOW ;
2011-01-07 22:36:14 -07:00
break ;
case SND_SOC_DAIFMT_I2S :
2012-04-06 10:30:52 -06:00
i2s - > reg_ctrl | = TEGRA20_I2S_CTRL_BIT_FORMAT_I2S ;
i2s - > reg_ctrl | = TEGRA20_I2S_CTRL_LRCK_L_LOW ;
2011-01-07 22:36:14 -07:00
break ;
case SND_SOC_DAIFMT_RIGHT_J :
2012-04-06 10:30:52 -06:00
i2s - > reg_ctrl | = TEGRA20_I2S_CTRL_BIT_FORMAT_RJM ;
i2s - > reg_ctrl | = TEGRA20_I2S_CTRL_LRCK_L_LOW ;
2011-01-07 22:36:14 -07:00
break ;
case SND_SOC_DAIFMT_LEFT_J :
2012-04-06 10:30:52 -06:00
i2s - > reg_ctrl | = TEGRA20_I2S_CTRL_BIT_FORMAT_LJM ;
i2s - > reg_ctrl | = TEGRA20_I2S_CTRL_LRCK_L_LOW ;
2011-01-07 22:36:14 -07:00
break ;
default :
return - EINVAL ;
}
return 0 ;
}
2012-04-06 10:30:52 -06:00
static int tegra20_i2s_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
2011-01-07 22:36:14 -07:00
{
2012-03-30 17:07:21 -06:00
struct device * dev = substream - > pcm - > card - > dev ;
2012-04-06 10:30:52 -06:00
struct tegra20_i2s * i2s = snd_soc_dai_get_drvdata ( dai ) ;
2011-01-07 22:36:14 -07:00
u32 reg ;
int ret , sample_size , srate , i2sclock , bitcnt ;
2012-04-06 10:30:52 -06:00
i2s - > reg_ctrl & = ~ TEGRA20_I2S_CTRL_BIT_SIZE_MASK ;
2011-01-07 22:36:14 -07:00
switch ( params_format ( params ) ) {
case SNDRV_PCM_FORMAT_S16_LE :
2012-04-06 10:30:52 -06:00
i2s - > reg_ctrl | = TEGRA20_I2S_CTRL_BIT_SIZE_16 ;
2011-01-07 22:36:14 -07:00
sample_size = 16 ;
break ;
case SNDRV_PCM_FORMAT_S24_LE :
2012-04-06 10:30:52 -06:00
i2s - > reg_ctrl | = TEGRA20_I2S_CTRL_BIT_SIZE_24 ;
2011-01-07 22:36:14 -07:00
sample_size = 24 ;
break ;
case SNDRV_PCM_FORMAT_S32_LE :
2012-04-06 10:30:52 -06:00
i2s - > reg_ctrl | = TEGRA20_I2S_CTRL_BIT_SIZE_32 ;
2011-01-07 22:36:14 -07:00
sample_size = 32 ;
break ;
default :
return - EINVAL ;
}
srate = params_rate ( params ) ;
/* Final "* 2" required by Tegra hardware */
i2sclock = srate * params_channels ( params ) * sample_size * 2 ;
ret = clk_set_rate ( i2s - > clk_i2s , i2sclock ) ;
if ( ret ) {
dev_err ( dev , " Can't set I2S clock rate: %d \n " , ret ) ;
return ret ;
}
bitcnt = ( i2sclock / ( 2 * srate ) ) - 1 ;
2012-04-06 10:30:52 -06:00
if ( bitcnt < 0 | | bitcnt > TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US )
2011-01-07 22:36:14 -07:00
return - EINVAL ;
2012-04-06 10:30:52 -06:00
reg = bitcnt < < TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT ;
2011-01-07 22:36:14 -07:00
if ( i2sclock % ( 2 * srate ) )
2012-04-06 10:30:52 -06:00
reg | = TEGRA20_I2S_TIMING_NON_SYM_ENABLE ;
2011-01-07 22:36:14 -07:00
2012-04-06 10:30:52 -06:00
tegra20_i2s_write ( i2s , TEGRA20_I2S_TIMING , reg ) ;
2011-01-07 22:36:14 -07:00
2012-04-06 10:30:52 -06:00
tegra20_i2s_write ( i2s , TEGRA20_I2S_FIFO_SCR ,
TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS |
TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS ) ;
2011-01-07 22:36:14 -07:00
return 0 ;
}
2012-04-06 10:30:52 -06:00
static void tegra20_i2s_start_playback ( struct tegra20_i2s * i2s )
2011-01-07 22:36:14 -07:00
{
2012-04-06 10:30:52 -06:00
i2s - > reg_ctrl | = TEGRA20_I2S_CTRL_FIFO1_ENABLE ;
tegra20_i2s_write ( i2s , TEGRA20_I2S_CTRL , i2s - > reg_ctrl ) ;
2011-01-07 22:36:14 -07:00
}
2012-04-06 10:30:52 -06:00
static void tegra20_i2s_stop_playback ( struct tegra20_i2s * i2s )
2011-01-07 22:36:14 -07:00
{
2012-04-06 10:30:52 -06:00
i2s - > reg_ctrl & = ~ TEGRA20_I2S_CTRL_FIFO1_ENABLE ;
tegra20_i2s_write ( i2s , TEGRA20_I2S_CTRL , i2s - > reg_ctrl ) ;
2011-01-07 22:36:14 -07:00
}
2012-04-06 10:30:52 -06:00
static void tegra20_i2s_start_capture ( struct tegra20_i2s * i2s )
2011-01-07 22:36:14 -07:00
{
2012-04-06 10:30:52 -06:00
i2s - > reg_ctrl | = TEGRA20_I2S_CTRL_FIFO2_ENABLE ;
tegra20_i2s_write ( i2s , TEGRA20_I2S_CTRL , i2s - > reg_ctrl ) ;
2011-01-07 22:36:14 -07:00
}
2012-04-06 10:30:52 -06:00
static void tegra20_i2s_stop_capture ( struct tegra20_i2s * i2s )
2011-01-07 22:36:14 -07:00
{
2012-04-06 10:30:52 -06:00
i2s - > reg_ctrl & = ~ TEGRA20_I2S_CTRL_FIFO2_ENABLE ;
tegra20_i2s_write ( i2s , TEGRA20_I2S_CTRL , i2s - > reg_ctrl ) ;
2011-01-07 22:36:14 -07:00
}
2012-04-06 10:30:52 -06:00
static int tegra20_i2s_trigger ( struct snd_pcm_substream * substream , int cmd ,
struct snd_soc_dai * dai )
2011-01-07 22:36:14 -07:00
{
2012-04-06 10:30:52 -06:00
struct tegra20_i2s * i2s = snd_soc_dai_get_drvdata ( dai ) ;
2011-01-07 22:36:14 -07:00
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
case SNDRV_PCM_TRIGGER_RESUME :
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
2012-04-06 10:30:52 -06:00
tegra20_i2s_start_playback ( i2s ) ;
2011-01-07 22:36:14 -07:00
else
2012-04-06 10:30:52 -06:00
tegra20_i2s_start_capture ( i2s ) ;
2011-01-07 22:36:14 -07:00
break ;
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
case SNDRV_PCM_TRIGGER_SUSPEND :
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
2012-04-06 10:30:52 -06:00
tegra20_i2s_stop_playback ( i2s ) ;
2011-01-07 22:36:14 -07:00
else
2012-04-06 10:30:52 -06:00
tegra20_i2s_stop_capture ( i2s ) ;
2011-01-07 22:36:14 -07:00
break ;
default :
return - EINVAL ;
}
return 0 ;
}
2012-04-06 10:30:52 -06:00
static int tegra20_i2s_probe ( struct snd_soc_dai * dai )
2011-01-07 22:36:14 -07:00
{
2012-04-06 10:30:52 -06:00
struct tegra20_i2s * i2s = snd_soc_dai_get_drvdata ( dai ) ;
2011-01-07 22:36:14 -07:00
dai - > capture_dma_data = & i2s - > capture_dma_data ;
dai - > playback_dma_data = & i2s - > playback_dma_data ;
return 0 ;
}
2012-04-06 10:30:52 -06:00
static const struct snd_soc_dai_ops tegra20_i2s_dai_ops = {
. set_fmt = tegra20_i2s_set_fmt ,
. hw_params = tegra20_i2s_hw_params ,
. trigger = tegra20_i2s_trigger ,
2011-01-07 22:36:14 -07:00
} ;
2012-04-06 10:30:52 -06:00
static const struct snd_soc_dai_driver tegra20_i2s_dai_template = {
. probe = tegra20_i2s_probe ,
2011-11-23 13:33:25 -07:00
. playback = {
. channels_min = 2 ,
. channels_max = 2 ,
. rates = SNDRV_PCM_RATE_8000_96000 ,
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
2011-01-07 22:36:14 -07:00
} ,
2011-11-23 13:33:25 -07:00
. capture = {
. channels_min = 2 ,
. channels_max = 2 ,
. rates = SNDRV_PCM_RATE_8000_96000 ,
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
2011-01-07 22:36:14 -07:00
} ,
2012-04-06 10:30:52 -06:00
. ops = & tegra20_i2s_dai_ops ,
2011-11-23 13:33:25 -07:00
. symmetric_rates = 1 ,
2011-01-07 22:36:14 -07:00
} ;
2012-04-13 12:14:06 -06:00
static bool tegra20_i2s_wr_rd_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case TEGRA20_I2S_CTRL :
case TEGRA20_I2S_STATUS :
case TEGRA20_I2S_TIMING :
case TEGRA20_I2S_FIFO_SCR :
case TEGRA20_I2S_PCM_CTRL :
case TEGRA20_I2S_NW_CTRL :
case TEGRA20_I2S_TDM_CTRL :
case TEGRA20_I2S_TDM_TX_RX_CTRL :
case TEGRA20_I2S_FIFO1 :
case TEGRA20_I2S_FIFO2 :
return true ;
default :
return false ;
} ;
}
static bool tegra20_i2s_volatile_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case TEGRA20_I2S_STATUS :
case TEGRA20_I2S_FIFO_SCR :
case TEGRA20_I2S_FIFO1 :
case TEGRA20_I2S_FIFO2 :
return true ;
default :
return false ;
} ;
}
static bool tegra20_i2s_precious_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case TEGRA20_I2S_FIFO1 :
case TEGRA20_I2S_FIFO2 :
return true ;
default :
return false ;
} ;
}
static const struct regmap_config tegra20_i2s_regmap_config = {
. reg_bits = 32 ,
. reg_stride = 4 ,
. val_bits = 32 ,
. max_register = TEGRA20_I2S_FIFO2 ,
. writeable_reg = tegra20_i2s_wr_rd_reg ,
. readable_reg = tegra20_i2s_wr_rd_reg ,
. volatile_reg = tegra20_i2s_volatile_reg ,
. precious_reg = tegra20_i2s_precious_reg ,
. cache_type = REGCACHE_RBTREE ,
} ;
2012-04-06 10:30:52 -06:00
static __devinit int tegra20_i2s_platform_probe ( struct platform_device * pdev )
2011-01-07 22:36:14 -07:00
{
2012-04-06 10:30:52 -06:00
struct tegra20_i2s * i2s ;
2011-01-07 22:36:14 -07:00
struct resource * mem , * memregion , * dmareq ;
2011-11-29 18:36:48 -07:00
u32 of_dma [ 2 ] ;
u32 dma_ch ;
2012-04-13 12:14:06 -06:00
void __iomem * regs ;
2011-01-07 22:36:14 -07:00
int ret ;
2012-04-06 10:30:52 -06:00
i2s = devm_kzalloc ( & pdev - > dev , sizeof ( struct tegra20_i2s ) , GFP_KERNEL ) ;
2011-01-07 22:36:14 -07:00
if ( ! i2s ) {
2012-04-06 10:30:52 -06:00
dev_err ( & pdev - > dev , " Can't allocate tegra20_i2s \n " ) ;
2011-01-07 22:36:14 -07:00
ret = - ENOMEM ;
2011-11-22 18:21:16 -07:00
goto err ;
2011-01-07 22:36:14 -07:00
}
dev_set_drvdata ( & pdev - > dev , i2s ) ;
2012-04-06 10:30:52 -06:00
i2s - > dai = tegra20_i2s_dai_template ;
2011-11-23 13:33:25 -07:00
i2s - > dai . name = dev_name ( & pdev - > dev ) ;
2011-07-01 13:56:14 -06:00
i2s - > clk_i2s = clk_get ( & pdev - > dev , NULL ) ;
2011-01-11 12:48:53 -07:00
if ( IS_ERR ( i2s - > clk_i2s ) ) {
2011-01-28 14:26:41 -07:00
dev_err ( & pdev - > dev , " Can't retrieve i2s clock \n " ) ;
2011-01-07 22:36:14 -07:00
ret = PTR_ERR ( i2s - > clk_i2s ) ;
2011-11-22 18:21:16 -07:00
goto err ;
2011-01-07 22:36:14 -07:00
}
mem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! mem ) {
dev_err ( & pdev - > dev , " No memory resource \n " ) ;
ret = - ENODEV ;
goto err_clk_put ;
}
dmareq = platform_get_resource ( pdev , IORESOURCE_DMA , 0 ) ;
if ( ! dmareq ) {
2011-11-29 18:36:48 -07:00
if ( of_property_read_u32_array ( pdev - > dev . of_node ,
" nvidia,dma-request-selector " ,
of_dma , 2 ) < 0 ) {
dev_err ( & pdev - > dev , " No DMA resource \n " ) ;
ret = - ENODEV ;
goto err_clk_put ;
}
dma_ch = of_dma [ 1 ] ;
} else {
dma_ch = dmareq - > start ;
2011-01-07 22:36:14 -07:00
}
2011-11-22 18:21:16 -07:00
memregion = devm_request_mem_region ( & pdev - > dev , mem - > start ,
resource_size ( mem ) , DRV_NAME ) ;
2011-01-07 22:36:14 -07:00
if ( ! memregion ) {
dev_err ( & pdev - > dev , " Memory region already claimed \n " ) ;
ret = - EBUSY ;
goto err_clk_put ;
}
2012-04-13 12:14:06 -06:00
regs = devm_ioremap ( & pdev - > dev , mem - > start , resource_size ( mem ) ) ;
if ( ! regs ) {
2011-01-07 22:36:14 -07:00
dev_err ( & pdev - > dev , " ioremap failed \n " ) ;
ret = - ENOMEM ;
2011-11-22 18:21:16 -07:00
goto err_clk_put ;
2011-01-07 22:36:14 -07:00
}
2012-04-13 12:14:06 -06:00
i2s - > regmap = devm_regmap_init_mmio ( & pdev - > dev , regs ,
& tegra20_i2s_regmap_config ) ;
if ( IS_ERR ( i2s - > regmap ) ) {
dev_err ( & pdev - > dev , " regmap init failed \n " ) ;
ret = PTR_ERR ( i2s - > regmap ) ;
goto err_clk_put ;
}
2012-04-06 10:30:52 -06:00
i2s - > capture_dma_data . addr = mem - > start + TEGRA20_I2S_FIFO2 ;
2011-01-07 22:36:14 -07:00
i2s - > capture_dma_data . wrap = 4 ;
i2s - > capture_dma_data . width = 32 ;
2011-11-29 18:36:48 -07:00
i2s - > capture_dma_data . req_sel = dma_ch ;
2011-01-07 22:36:14 -07:00
2012-04-06 10:30:52 -06:00
i2s - > playback_dma_data . addr = mem - > start + TEGRA20_I2S_FIFO1 ;
2011-01-07 22:36:14 -07:00
i2s - > playback_dma_data . wrap = 4 ;
i2s - > playback_dma_data . width = 32 ;
2011-11-29 18:36:48 -07:00
i2s - > playback_dma_data . req_sel = dma_ch ;
2011-01-07 22:36:14 -07:00
2012-04-06 10:30:52 -06:00
i2s - > reg_ctrl = TEGRA20_I2S_CTRL_FIFO_FORMAT_PACKED ;
2011-01-07 22:36:14 -07:00
2012-04-09 09:52:22 -06:00
pm_runtime_enable ( & pdev - > dev ) ;
if ( ! pm_runtime_enabled ( & pdev - > dev ) ) {
ret = tegra20_i2s_runtime_resume ( & pdev - > dev ) ;
if ( ret )
goto err_pm_disable ;
}
2011-11-23 13:33:25 -07:00
ret = snd_soc_register_dai ( & pdev - > dev , & i2s - > dai ) ;
2011-01-07 22:36:14 -07:00
if ( ret ) {
dev_err ( & pdev - > dev , " Could not register DAI: %d \n " , ret ) ;
ret = - ENOMEM ;
2012-04-09 09:52:22 -06:00
goto err_suspend ;
2011-01-07 22:36:14 -07:00
}
2012-03-20 14:55:49 -06:00
ret = tegra_pcm_platform_register ( & pdev - > dev ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Could not register PCM: %d \n " , ret ) ;
goto err_unregister_dai ;
}
2011-01-07 22:36:14 -07:00
return 0 ;
2012-03-20 14:55:49 -06:00
err_unregister_dai :
snd_soc_unregister_dai ( & pdev - > dev ) ;
2012-04-09 09:52:22 -06:00
err_suspend :
if ( ! pm_runtime_status_suspended ( & pdev - > dev ) )
tegra20_i2s_runtime_suspend ( & pdev - > dev ) ;
err_pm_disable :
pm_runtime_disable ( & pdev - > dev ) ;
2011-01-07 22:36:14 -07:00
err_clk_put :
clk_put ( i2s - > clk_i2s ) ;
2011-11-22 18:21:16 -07:00
err :
2011-01-07 22:36:14 -07:00
return ret ;
}
2012-04-06 10:30:52 -06:00
static int __devexit tegra20_i2s_platform_remove ( struct platform_device * pdev )
2011-01-07 22:36:14 -07:00
{
2012-04-06 10:30:52 -06:00
struct tegra20_i2s * i2s = dev_get_drvdata ( & pdev - > dev ) ;
2011-01-07 22:36:14 -07:00
2012-04-09 09:52:22 -06:00
pm_runtime_disable ( & pdev - > dev ) ;
if ( ! pm_runtime_status_suspended ( & pdev - > dev ) )
tegra20_i2s_runtime_suspend ( & pdev - > dev ) ;
2012-03-20 14:55:49 -06:00
tegra_pcm_platform_unregister ( & pdev - > dev ) ;
2011-01-07 22:36:14 -07:00
snd_soc_unregister_dai ( & pdev - > dev ) ;
clk_put ( i2s - > clk_i2s ) ;
return 0 ;
}
2012-04-06 10:30:52 -06:00
static const struct of_device_id tegra20_i2s_of_match [ ] __devinitconst = {
2011-11-29 18:36:48 -07:00
{ . compatible = " nvidia,tegra20-i2s " , } ,
{ } ,
} ;
2012-04-09 09:52:22 -06:00
static const struct dev_pm_ops tegra20_i2s_pm_ops __devinitconst = {
SET_RUNTIME_PM_OPS ( tegra20_i2s_runtime_suspend ,
tegra20_i2s_runtime_resume , NULL )
} ;
2012-04-06 10:30:52 -06:00
static struct platform_driver tegra20_i2s_driver = {
2011-01-07 22:36:14 -07:00
. driver = {
. name = DRV_NAME ,
. owner = THIS_MODULE ,
2012-04-06 10:30:52 -06:00
. of_match_table = tegra20_i2s_of_match ,
2012-04-09 09:52:22 -06:00
. pm = & tegra20_i2s_pm_ops ,
2011-01-07 22:36:14 -07:00
} ,
2012-04-06 10:30:52 -06:00
. probe = tegra20_i2s_platform_probe ,
. remove = __devexit_p ( tegra20_i2s_platform_remove ) ,
2011-01-07 22:36:14 -07:00
} ;
2012-04-06 10:30:52 -06:00
module_platform_driver ( tegra20_i2s_driver ) ;
2011-01-07 22:36:14 -07:00
MODULE_AUTHOR ( " Stephen Warren <swarren@nvidia.com> " ) ;
2012-04-06 10:30:52 -06:00
MODULE_DESCRIPTION ( " Tegra20 I2S ASoC driver " ) ;
2011-01-07 22:36:14 -07:00
MODULE_LICENSE ( " GPL " ) ;
2011-02-10 15:37:19 -07:00
MODULE_ALIAS ( " platform: " DRV_NAME ) ;
2012-04-06 10:30:52 -06:00
MODULE_DEVICE_TABLE ( of , tegra20_i2s_of_match ) ;