2005-06-30 20:54:04 +04:00
/*
* linux / sound / pxa2xx - ac97 . c - - AC97 support for the Intel PXA2xx chip .
*
* Author : Nicolas Pitre
* Created : Dec 02 , 2004
* Copyright : MontaVista Software Inc .
*
* 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 .
*/
# include <linux/init.h>
2012-03-20 23:33:19 +04:00
# include <linux/io.h>
2005-06-30 20:54:04 +04:00
# include <linux/module.h>
2005-10-29 22:07:23 +04:00
# include <linux/platform_device.h>
2013-08-12 12:42:39 +04:00
# include <linux/dmaengine.h>
2018-06-27 22:33:53 +03:00
# include <linux/dma-mapping.h>
2005-06-30 20:54:04 +04:00
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/ac97_codec.h>
# include <sound/initval.h>
2008-09-10 05:01:17 +04:00
# include <sound/pxa2xx-lib.h>
2013-08-12 12:42:39 +04:00
# include <sound/dmaengine_pcm.h>
2005-06-30 20:54:04 +04:00
2008-11-28 09:19:33 +03:00
# include <mach/regs-ac97.h>
2008-08-05 19:14:15 +04:00
# include <mach/audio.h>
2005-06-30 20:54:04 +04:00
2017-09-02 22:54:06 +03:00
static void pxa2xx_ac97_legacy_reset ( struct snd_ac97 * ac97 )
2005-06-30 20:54:04 +04:00
{
2017-09-02 22:54:06 +03:00
if ( ! pxa2xx_ac97_try_cold_reset ( ) )
pxa2xx_ac97_try_warm_reset ( ) ;
2005-06-30 20:54:04 +04:00
2017-09-02 22:54:06 +03:00
pxa2xx_ac97_finish_reset ( ) ;
}
static unsigned short pxa2xx_ac97_legacy_read ( struct snd_ac97 * ac97 ,
unsigned short reg )
{
int ret ;
ret = pxa2xx_ac97_read ( ac97 - > num , reg ) ;
if ( ret < 0 )
return 0 ;
else
return ( unsigned short ) ( ret & 0xffff ) ;
}
static void pxa2xx_ac97_legacy_write ( struct snd_ac97 * ac97 ,
unsigned short reg , unsigned short val )
{
int __always_unused ret ;
ret = pxa2xx_ac97_write ( ac97 - > num , reg , val ) ;
2005-06-30 20:54:04 +04:00
}
2005-11-17 17:10:38 +03:00
static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
2017-09-02 22:54:06 +03:00
. read = pxa2xx_ac97_legacy_read ,
. write = pxa2xx_ac97_legacy_write ,
. reset = pxa2xx_ac97_legacy_reset ,
2005-06-30 20:54:04 +04:00
} ;
2005-11-17 17:10:38 +03:00
static struct snd_pcm * pxa2xx_ac97_pcm ;
static struct snd_ac97 * pxa2xx_ac97_ac97 ;
2005-06-30 20:54:04 +04:00
2018-06-27 22:33:53 +03:00
static int pxa2xx_ac97_pcm_open ( struct snd_pcm_substream * substream )
2005-06-30 20:54:04 +04:00
{
2005-11-17 17:10:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-06-30 20:54:04 +04:00
pxa2xx_audio_ops_t * platform_ops ;
2018-06-27 22:33:53 +03:00
int ret , i ;
ret = __pxa2xx_pcm_open ( substream ) ;
if ( ret )
return ret ;
2005-06-30 20:54:04 +04:00
runtime - > hw . channels_min = 2 ;
runtime - > hw . channels_max = 2 ;
2018-06-27 22:33:53 +03:00
i = ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) ?
AC97_RATES_FRONT_DAC : AC97_RATES_ADC ;
runtime - > hw . rates = pxa2xx_ac97_ac97 - > rates [ i ] ;
2005-06-30 20:54:04 +04:00
snd_pcm_limit_hw_rates ( runtime ) ;
2018-06-27 22:33:53 +03:00
platform_ops = substream - > pcm - > card - > dev - > platform_data ;
if ( platform_ops & & platform_ops - > startup ) {
ret = platform_ops - > startup ( substream , platform_ops - > priv ) ;
if ( ret < 0 )
__pxa2xx_pcm_close ( substream ) ;
}
return ret ;
2005-06-30 20:54:04 +04:00
}
2018-06-27 22:33:53 +03:00
static int pxa2xx_ac97_pcm_close ( struct snd_pcm_substream * substream )
2005-06-30 20:54:04 +04:00
{
pxa2xx_audio_ops_t * platform_ops ;
2018-06-27 22:33:53 +03:00
platform_ops = substream - > pcm - > card - > dev - > platform_data ;
2005-06-30 20:54:04 +04:00
if ( platform_ops & & platform_ops - > shutdown )
platform_ops - > shutdown ( substream , platform_ops - > priv ) ;
2018-06-27 22:33:53 +03:00
return 0 ;
2005-06-30 20:54:04 +04:00
}
2005-11-17 17:10:38 +03:00
static int pxa2xx_ac97_pcm_prepare ( struct snd_pcm_substream * substream )
2005-06-30 20:54:04 +04:00
{
2005-11-17 17:10:38 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-06-30 20:54:04 +04:00
int reg = ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) ?
AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE ;
2018-06-27 22:33:53 +03:00
int ret ;
ret = __pxa2xx_pcm_prepare ( substream ) ;
if ( ret < 0 )
return ret ;
2005-06-30 20:54:04 +04:00
return snd_ac97_set_rate ( pxa2xx_ac97_ac97 , reg , runtime - > rate ) ;
}
2012-08-09 17:47:15 +04:00
# ifdef CONFIG_PM_SLEEP
2005-06-30 20:54:04 +04:00
2012-07-02 13:22:40 +04:00
static int pxa2xx_ac97_do_suspend ( struct snd_card * card )
2005-06-30 20:54:04 +04:00
{
2005-11-17 19:19:25 +03:00
pxa2xx_audio_ops_t * platform_ops = card - > dev - > platform_data ;
snd_power_change_state ( card , SNDRV_CTL_POWER_D3cold ) ;
snd_pcm_suspend_all ( pxa2xx_ac97_pcm ) ;
snd_ac97_suspend ( pxa2xx_ac97_ac97 ) ;
if ( platform_ops & & platform_ops - > suspend )
platform_ops - > suspend ( platform_ops - > priv ) ;
2005-06-30 20:54:04 +04:00
2008-09-10 05:01:17 +04:00
return pxa2xx_ac97_hw_suspend ( ) ;
2005-06-30 20:54:04 +04:00
}
2005-11-17 17:10:38 +03:00
static int pxa2xx_ac97_do_resume ( struct snd_card * card )
2005-06-30 20:54:04 +04:00
{
2005-11-17 19:19:25 +03:00
pxa2xx_audio_ops_t * platform_ops = card - > dev - > platform_data ;
2008-09-10 05:01:17 +04:00
int rc ;
rc = pxa2xx_ac97_hw_resume ( ) ;
if ( rc )
return rc ;
2005-11-17 19:19:25 +03:00
if ( platform_ops & & platform_ops - > resume )
platform_ops - > resume ( platform_ops - > priv ) ;
snd_ac97_resume ( pxa2xx_ac97_ac97 ) ;
snd_power_change_state ( card , SNDRV_CTL_POWER_D0 ) ;
2005-06-30 20:54:04 +04:00
return 0 ;
}
2009-07-29 12:59:23 +04:00
static int pxa2xx_ac97_suspend ( struct device * dev )
2005-06-30 20:54:04 +04:00
{
2009-07-29 12:59:23 +04:00
struct snd_card * card = dev_get_drvdata ( dev ) ;
2005-06-30 20:54:04 +04:00
int ret = 0 ;
2005-10-28 20:52:56 +04:00
if ( card )
2012-07-02 13:22:40 +04:00
ret = pxa2xx_ac97_do_suspend ( card ) ;
2005-06-30 20:54:04 +04:00
return ret ;
}
2009-07-29 12:59:23 +04:00
static int pxa2xx_ac97_resume ( struct device * dev )
2005-06-30 20:54:04 +04:00
{
2009-07-29 12:59:23 +04:00
struct snd_card * card = dev_get_drvdata ( dev ) ;
2005-06-30 20:54:04 +04:00
int ret = 0 ;
2005-10-28 20:52:56 +04:00
if ( card )
2005-08-08 18:29:43 +04:00
ret = pxa2xx_ac97_do_resume ( card ) ;
2005-06-30 20:54:04 +04:00
return ret ;
}
2012-07-02 13:22:40 +04:00
static SIMPLE_DEV_PM_OPS ( pxa2xx_ac97_pm_ops , pxa2xx_ac97_suspend , pxa2xx_ac97_resume ) ;
2005-06-30 20:54:04 +04:00
# endif
2018-06-27 22:33:53 +03:00
static const struct snd_pcm_ops pxa2xx_pcm_ops = {
. open = pxa2xx_ac97_pcm_open ,
. close = pxa2xx_ac97_pcm_close ,
. ioctl = snd_pcm_lib_ioctl ,
. hw_params = __pxa2xx_pcm_hw_params ,
. hw_free = __pxa2xx_pcm_hw_free ,
. prepare = pxa2xx_ac97_pcm_prepare ,
. trigger = pxa2xx_pcm_trigger ,
. pointer = pxa2xx_pcm_pointer ,
. mmap = pxa2xx_pcm_mmap ,
} ;
static int pxa2xx_ac97_pcm_new ( struct snd_card * card )
{
struct snd_pcm * pcm ;
int stream , ret ;
ret = snd_pcm_new ( card , " PXA2xx-PCM " , 0 , 1 , 1 , & pcm ) ;
if ( ret )
goto out ;
pcm - > private_free = pxa2xx_pcm_free_dma_buffers ;
ret = dma_coerce_mask_and_coherent ( card - > dev , DMA_BIT_MASK ( 32 ) ) ;
if ( ret )
goto out ;
stream = SNDRV_PCM_STREAM_PLAYBACK ;
snd_pcm_set_ops ( pcm , stream , & pxa2xx_pcm_ops ) ;
ret = pxa2xx_pcm_preallocate_dma_buffer ( pcm , stream ) ;
if ( ret )
goto out ;
stream = SNDRV_PCM_STREAM_CAPTURE ;
snd_pcm_set_ops ( pcm , stream , & pxa2xx_pcm_ops ) ;
ret = pxa2xx_pcm_preallocate_dma_buffer ( pcm , stream ) ;
if ( ret )
goto out ;
pxa2xx_ac97_pcm = pcm ;
ret = 0 ;
out :
return ret ;
}
2012-12-06 21:35:12 +04:00
static int pxa2xx_ac97_probe ( struct platform_device * dev )
2005-06-30 20:54:04 +04:00
{
2005-11-17 17:10:38 +03:00
struct snd_card * card ;
struct snd_ac97_bus * ac97_bus ;
struct snd_ac97_template ac97_template ;
2005-06-30 20:54:04 +04:00
int ret ;
2009-07-30 04:55:01 +04:00
pxa2xx_audio_ops_t * pdata = dev - > dev . platform_data ;
if ( dev - > id > = 0 ) {
dev_err ( & dev - > dev , " PXA2xx has only one AC97 port. \n " ) ;
ret = - ENXIO ;
goto err_dev ;
}
2005-06-30 20:54:04 +04:00
2014-01-29 17:25:18 +04:00
ret = snd_card_new ( & dev - > dev , SNDRV_DEFAULT_IDX1 , SNDRV_DEFAULT_STR1 ,
THIS_MODULE , 0 , & card ) ;
2008-12-28 18:45:02 +03:00
if ( ret < 0 )
2005-06-30 20:54:04 +04:00
goto err ;
2013-10-29 18:26:12 +04:00
strlcpy ( card - > driver , dev - > dev . driver - > name , sizeof ( card - > driver ) ) ;
2005-06-30 20:54:04 +04:00
2018-06-27 22:33:53 +03:00
ret = pxa2xx_ac97_pcm_new ( card ) ;
2005-06-30 20:54:04 +04:00
if ( ret )
goto err ;
2008-09-10 05:01:17 +04:00
ret = pxa2xx_ac97_hw_probe ( dev ) ;
if ( ret )
2008-03-04 13:14:25 +03:00
goto err ;
2005-06-30 20:54:04 +04:00
ret = snd_ac97_bus ( card , 0 , & pxa2xx_ac97_ops , NULL , & ac97_bus ) ;
if ( ret )
2008-09-10 05:01:17 +04:00
goto err_remove ;
2005-06-30 20:54:04 +04:00
memset ( & ac97_template , 0 , sizeof ( ac97_template ) ) ;
ret = snd_ac97_mixer ( ac97_bus , & ac97_template , & pxa2xx_ac97_ac97 ) ;
if ( ret )
2008-09-10 05:01:17 +04:00
goto err_remove ;
2005-06-30 20:54:04 +04:00
snprintf ( card - > shortname , sizeof ( card - > shortname ) ,
" %s " , snd_ac97_get_short_name ( pxa2xx_ac97_ac97 ) ) ;
snprintf ( card - > longname , sizeof ( card - > longname ) ,
2005-11-10 01:32:44 +03:00
" %s (%s) " , dev - > dev . driver - > name , card - > mixername ) ;
2005-06-30 20:54:04 +04:00
2009-09-04 23:16:36 +04:00
if ( pdata & & pdata - > codec_pdata [ 0 ] )
2009-08-21 22:02:52 +04:00
snd_ac97_dev_add_pdata ( ac97_bus - > codec [ 0 ] , pdata - > codec_pdata [ 0 ] ) ;
2005-06-30 20:54:04 +04:00
ret = snd_card_register ( card ) ;
if ( ret = = 0 ) {
2005-11-10 01:32:44 +03:00
platform_set_drvdata ( dev , card ) ;
2005-06-30 20:54:04 +04:00
return 0 ;
}
2008-09-10 05:01:17 +04:00
err_remove :
pxa2xx_ac97_hw_remove ( dev ) ;
err :
2005-06-30 20:54:04 +04:00
if ( card )
snd_card_free ( card ) ;
2009-07-30 04:55:01 +04:00
err_dev :
2005-06-30 20:54:04 +04:00
return ret ;
}
2012-12-06 21:35:12 +04:00
static int pxa2xx_ac97_remove ( struct platform_device * dev )
2005-06-30 20:54:04 +04:00
{
2005-11-17 17:10:38 +03:00
struct snd_card * card = platform_get_drvdata ( dev ) ;
2005-06-30 20:54:04 +04:00
if ( card ) {
snd_card_free ( card ) ;
2008-09-10 05:01:17 +04:00
pxa2xx_ac97_hw_remove ( dev ) ;
2005-06-30 20:54:04 +04:00
}
return 0 ;
}
2005-11-10 01:32:44 +03:00
static struct platform_driver pxa2xx_ac97_driver = {
2005-06-30 20:54:04 +04:00
. probe = pxa2xx_ac97_probe ,
2012-12-06 21:35:12 +04:00
. remove = pxa2xx_ac97_remove ,
2005-11-10 01:32:44 +03:00
. driver = {
. name = " pxa2xx-ac97 " ,
2012-08-09 17:47:15 +04:00
# ifdef CONFIG_PM_SLEEP
2009-07-29 12:59:23 +04:00
. pm = & pxa2xx_ac97_pm_ops ,
# endif
2005-11-10 01:32:44 +03:00
} ,
2005-06-30 20:54:04 +04:00
} ;
2011-11-27 12:36:04 +04:00
module_platform_driver ( pxa2xx_ac97_driver ) ;
2005-06-30 20:54:04 +04:00
MODULE_AUTHOR ( " Nicolas Pitre " ) ;
MODULE_DESCRIPTION ( " AC97 driver for the Intel PXA2xx chip " ) ;
MODULE_LICENSE ( " GPL " ) ;
2008-04-14 15:33:36 +04:00
MODULE_ALIAS ( " platform:pxa2xx-ac97 " ) ;