2006-10-12 14:29:03 +02: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 14:33:19 -05:00
# include <linux/io.h>
2006-10-12 14:29:03 +02:00
# include <linux/module.h>
# include <linux/platform_device.h>
2013-08-12 10:42:39 +02:00
# include <linux/dmaengine.h>
2015-09-30 22:51:52 +02:00
# include <linux/dma/pxa-dma.h>
2006-10-12 14:29:03 +02:00
# include <sound/core.h>
# include <sound/ac97_codec.h>
# include <sound/soc.h>
2008-09-10 05:01:17 +04:00
# include <sound/pxa2xx-lib.h>
2013-08-12 10:42:39 +02:00
# include <sound/dmaengine_pcm.h>
2006-10-12 14:29:03 +02:00
2008-08-05 16:14:15 +01:00
# include <mach/hardware.h>
2008-11-28 14:19:33 +08:00
# include <mach/regs-ac97.h>
2009-07-30 02:55:01 +02:00
# include <mach/audio.h>
2006-10-12 14:29:03 +02:00
2007-02-02 17:21:16 +01:00
# include "pxa2xx-ac97.h"
2006-10-12 14:29:03 +02:00
static void pxa2xx_ac97_warm_reset ( struct snd_ac97 * ac97 )
{
2008-09-10 05:01:17 +04:00
pxa2xx_ac97_try_warm_reset ( ac97 ) ;
2006-10-12 14:29:03 +02:00
2008-09-10 05:01:17 +04:00
pxa2xx_ac97_finish_reset ( ac97 ) ;
2006-10-12 14:29:03 +02:00
}
static void pxa2xx_ac97_cold_reset ( struct snd_ac97 * ac97 )
{
2008-09-10 05:01:17 +04:00
pxa2xx_ac97_try_cold_reset ( ac97 ) ;
2008-04-22 17:08:52 +02:00
2008-09-10 05:01:17 +04:00
pxa2xx_ac97_finish_reset ( ac97 ) ;
2006-10-12 14:29:03 +02:00
}
2013-06-26 12:45:59 +01:00
static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
2006-10-12 14:29:03 +02:00
. read = pxa2xx_ac97_read ,
. write = pxa2xx_ac97_write ,
. warm_reset = pxa2xx_ac97_warm_reset ,
. reset = pxa2xx_ac97_cold_reset ,
} ;
2015-09-30 22:51:52 +02:00
static struct pxad_param pxa2xx_ac97_pcm_stereo_in_req = {
. prio = PXAD_PRIO_LOWEST ,
. drcmr = 11 ,
} ;
2013-08-12 10:42:39 +02:00
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = {
. addr = __PREG ( PCDR ) ,
. addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES ,
. maxburst = 32 ,
. filter_data = & pxa2xx_ac97_pcm_stereo_in_req ,
2006-10-12 14:29:03 +02:00
} ;
2015-09-30 22:51:52 +02:00
static struct pxad_param pxa2xx_ac97_pcm_stereo_out_req = {
. prio = PXAD_PRIO_LOWEST ,
. drcmr = 12 ,
} ;
2013-08-12 10:42:39 +02:00
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = {
. addr = __PREG ( PCDR ) ,
. addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES ,
. maxburst = 32 ,
. filter_data = & pxa2xx_ac97_pcm_stereo_out_req ,
2006-10-12 14:29:03 +02:00
} ;
2015-09-30 22:51:52 +02:00
static struct pxad_param pxa2xx_ac97_pcm_aux_mono_out_req = {
. prio = PXAD_PRIO_LOWEST ,
. drcmr = 10 ,
} ;
2013-08-12 10:42:39 +02:00
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = {
. addr = __PREG ( MODR ) ,
. addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES ,
. maxburst = 16 ,
. filter_data = & pxa2xx_ac97_pcm_aux_mono_out_req ,
2006-10-12 14:29:03 +02:00
} ;
2015-09-30 22:51:52 +02:00
static struct pxad_param pxa2xx_ac97_pcm_aux_mono_in_req = {
. prio = PXAD_PRIO_LOWEST ,
. drcmr = 9 ,
} ;
2013-08-12 10:42:39 +02:00
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = {
. addr = __PREG ( MODR ) ,
. addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES ,
. maxburst = 16 ,
. filter_data = & pxa2xx_ac97_pcm_aux_mono_in_req ,
2006-10-12 14:29:03 +02:00
} ;
2015-09-30 22:51:52 +02:00
static struct pxad_param pxa2xx_ac97_pcm_aux_mic_mono_req = {
. prio = PXAD_PRIO_LOWEST ,
. drcmr = 8 ,
} ;
2013-08-12 10:42:39 +02:00
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = {
. addr = __PREG ( MCDR ) ,
. addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES ,
. maxburst = 16 ,
. filter_data = & pxa2xx_ac97_pcm_aux_mic_mono_req ,
2006-10-12 14:29:03 +02:00
} ;
2015-09-30 22:51:52 +02:00
static int pxa2xx_ac97_hifi_startup ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * cpu_dai )
2006-10-12 14:29:03 +02:00
{
2013-08-12 10:42:39 +02:00
struct snd_dmaengine_dai_dma_data * dma_data ;
2006-10-12 14:29:03 +02:00
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
2010-03-22 10:11:15 +01:00
dma_data = & pxa2xx_ac97_pcm_stereo_out ;
2006-10-12 14:29:03 +02:00
else
2010-03-22 10:11:15 +01:00
dma_data = & pxa2xx_ac97_pcm_stereo_in ;
snd_soc_dai_set_dma_data ( cpu_dai , substream , dma_data ) ;
2006-10-12 14:29:03 +02:00
return 0 ;
}
2015-09-30 22:51:52 +02:00
static int pxa2xx_ac97_aux_startup ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * cpu_dai )
2006-10-12 14:29:03 +02:00
{
2013-08-12 10:42:39 +02:00
struct snd_dmaengine_dai_dma_data * dma_data ;
2006-10-12 14:29:03 +02:00
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
2010-03-22 10:11:15 +01:00
dma_data = & pxa2xx_ac97_pcm_aux_mono_out ;
2006-10-12 14:29:03 +02:00
else
2010-03-22 10:11:15 +01:00
dma_data = & pxa2xx_ac97_pcm_aux_mono_in ;
snd_soc_dai_set_dma_data ( cpu_dai , substream , dma_data ) ;
2006-10-12 14:29:03 +02:00
return 0 ;
}
2015-09-30 22:51:52 +02:00
static int pxa2xx_ac97_mic_startup ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * cpu_dai )
2006-10-12 14:29:03 +02:00
{
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK )
return - ENODEV ;
else
2010-03-22 10:11:15 +01:00
snd_soc_dai_set_dma_data ( cpu_dai , substream ,
& pxa2xx_ac97_pcm_mic_mono_in ) ;
2006-10-12 14:29:03 +02:00
return 0 ;
}
2007-02-02 17:21:16 +01:00
# define PXA2XX_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 )
2011-11-23 11:40:40 +01:00
static const struct snd_soc_dai_ops pxa_ac97_hifi_dai_ops = {
2015-09-30 22:51:52 +02:00
. startup = pxa2xx_ac97_hifi_startup ,
2009-03-03 09:41:00 +08:00
} ;
2011-11-23 11:40:40 +01:00
static const struct snd_soc_dai_ops pxa_ac97_aux_dai_ops = {
2015-09-30 22:51:52 +02:00
. startup = pxa2xx_ac97_aux_startup ,
2009-03-16 14:13:12 +00:00
} ;
2011-11-23 11:40:40 +01:00
static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = {
2015-09-30 22:51:52 +02:00
. startup = pxa2xx_ac97_mic_startup ,
2009-03-16 14:13:12 +00:00
} ;
2006-10-12 14:29:03 +02:00
/*
* There is only 1 physical AC97 interface for pxa2xx , but it
* has extra fifo ' s that can be used for aux DACs and ADCs .
*/
2012-02-20 08:04:50 +08:00
static struct snd_soc_dai_driver pxa_ac97_dai_driver [ ] = {
2006-10-12 14:29:03 +02:00
{
. name = " pxa2xx-ac97 " ,
2014-11-10 22:41:52 +01:00
. bus_control = true ,
2006-10-12 14:29:03 +02:00
. playback = {
. stream_name = " AC97 Playback " ,
. channels_min = 2 ,
2007-02-02 17:21:16 +01:00
. channels_max = 2 ,
. rates = PXA2XX_AC97_RATES ,
. formats = SNDRV_PCM_FMTBIT_S16_LE , } ,
2006-10-12 14:29:03 +02:00
. capture = {
. stream_name = " AC97 Capture " ,
. channels_min = 2 ,
2007-02-02 17:21:16 +01:00
. channels_max = 2 ,
. rates = PXA2XX_AC97_RATES ,
. formats = SNDRV_PCM_FMTBIT_S16_LE , } ,
2009-03-16 14:13:12 +00:00
. ops = & pxa_ac97_hifi_dai_ops ,
2006-10-12 14:29:03 +02:00
} ,
{
. name = " pxa2xx-ac97-aux " ,
2014-11-10 22:41:52 +01:00
. bus_control = true ,
2006-10-12 14:29:03 +02:00
. playback = {
. stream_name = " AC97 Aux Playback " ,
. channels_min = 1 ,
2007-02-02 17:21:16 +01:00
. channels_max = 1 ,
. rates = PXA2XX_AC97_RATES ,
. formats = SNDRV_PCM_FMTBIT_S16_LE , } ,
2006-10-12 14:29:03 +02:00
. capture = {
. stream_name = " AC97 Aux Capture " ,
. channels_min = 1 ,
2007-02-02 17:21:16 +01:00
. channels_max = 1 ,
. rates = PXA2XX_AC97_RATES ,
. formats = SNDRV_PCM_FMTBIT_S16_LE , } ,
2009-03-16 14:13:12 +00:00
. ops = & pxa_ac97_aux_dai_ops ,
2006-10-12 14:29:03 +02:00
} ,
{
. name = " pxa2xx-ac97-mic " ,
2014-11-10 22:41:52 +01:00
. bus_control = true ,
2006-10-12 14:29:03 +02:00
. capture = {
. stream_name = " AC97 Mic Capture " ,
. channels_min = 1 ,
2007-02-02 17:21:16 +01:00
. channels_max = 1 ,
. rates = PXA2XX_AC97_RATES ,
. formats = SNDRV_PCM_FMTBIT_S16_LE , } ,
2009-03-16 14:13:12 +00:00
. ops = & pxa_ac97_mic_dai_ops ,
2007-02-02 17:21:16 +01:00
} ,
2006-10-12 14:29:03 +02:00
} ;
2013-03-21 03:34:23 -07:00
static const struct snd_soc_component_driver pxa_ac97_component = {
. name = " pxa-ac97 " ,
} ;
2012-12-07 09:26:17 -05:00
static int pxa2xx_ac97_dev_probe ( struct platform_device * pdev )
2008-12-03 19:26:35 +00:00
{
2013-06-28 11:17:48 -07:00
int ret ;
2010-03-17 20:15:21 +00:00
if ( pdev - > id ! = - 1 ) {
2009-07-30 02:55:01 +02:00
dev_err ( & pdev - > dev , " PXA2xx has only one AC97 port. \n " ) ;
return - ENXIO ;
}
2013-10-17 14:01:36 +04:00
ret = pxa2xx_ac97_hw_probe ( pdev ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " PXA2xx AC97 hw probe error (%d) \n " , ret ) ;
return ret ;
}
2013-06-26 12:45:59 +01:00
ret = snd_soc_set_ac97_ops ( & pxa2xx_ac97_ops ) ;
if ( ret ! = 0 )
return ret ;
2009-03-09 18:18:33 +00:00
/* Punt most of the init to the SoC probe; we may need the machine
* driver to do interesting things with the clocking to get us up
* and running .
*/
2013-03-21 03:34:23 -07:00
return snd_soc_register_component ( & pdev - > dev , & pxa_ac97_component ,
pxa_ac97_dai_driver , ARRAY_SIZE ( pxa_ac97_dai_driver ) ) ;
2008-12-03 19:26:35 +00:00
}
2009-03-09 18:18:33 +00:00
2012-12-07 09:26:17 -05:00
static int pxa2xx_ac97_dev_remove ( struct platform_device * pdev )
2009-03-09 18:18:33 +00:00
{
2013-03-21 03:34:23 -07:00
snd_soc_unregister_component ( & pdev - > dev ) ;
2013-06-26 12:45:59 +01:00
snd_soc_set_ac97_ops ( NULL ) ;
2013-10-17 14:01:36 +04:00
pxa2xx_ac97_hw_remove ( pdev ) ;
2009-03-09 18:18:33 +00:00
return 0 ;
}
2013-10-17 14:01:36 +04:00
# ifdef CONFIG_PM_SLEEP
static int pxa2xx_ac97_dev_suspend ( struct device * dev )
{
return pxa2xx_ac97_hw_suspend ( ) ;
}
static int pxa2xx_ac97_dev_resume ( struct device * dev )
{
return pxa2xx_ac97_hw_resume ( ) ;
}
static SIMPLE_DEV_PM_OPS ( pxa2xx_ac97_pm_ops ,
pxa2xx_ac97_dev_suspend , pxa2xx_ac97_dev_resume ) ;
# endif
2009-03-09 18:18:33 +00:00
static struct platform_driver pxa2xx_ac97_driver = {
. probe = pxa2xx_ac97_dev_probe ,
2012-12-07 09:26:17 -05:00
. remove = pxa2xx_ac97_dev_remove ,
2009-03-09 18:18:33 +00:00
. driver = {
. name = " pxa2xx-ac97 " ,
2013-10-17 14:01:36 +04:00
# ifdef CONFIG_PM_SLEEP
. pm = & pxa2xx_ac97_pm_ops ,
# endif
2009-03-09 18:18:33 +00:00
} ,
} ;
2011-11-25 10:13:37 +08:00
module_platform_driver ( pxa2xx_ac97_driver ) ;
2008-12-03 19:26:35 +00:00
2006-10-12 14:29:03 +02:00
MODULE_AUTHOR ( " Nicolas Pitre " ) ;
MODULE_DESCRIPTION ( " AC97 driver for the Intel PXA2xx chip " ) ;
MODULE_LICENSE ( " GPL " ) ;
2016-05-06 17:27:34 +02:00
MODULE_ALIAS ( " platform:pxa2xx-ac97 " ) ;