2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
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 .
*/
# 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
2018-08-25 10:46:18 +02:00
# include <sound/ac97/controller.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
2019-09-02 00:02:08 +02:00
# include <linux/platform_data/asoc-pxa.h>
2006-10-12 14:29:03 +02:00
2019-09-18 10:20:19 +02:00
# define PCDR 0x0040 /* PCM FIFO Data Register */
# define MODR 0x0140 /* Modem FIFO Data Register */
# define MCDR 0x0060 /* Mic-in FIFO Data Register */
2018-08-25 10:46:18 +02:00
static void pxa2xx_ac97_warm_reset ( struct ac97_controller * adrv )
2006-10-12 14:29:03 +02:00
{
2017-09-02 21:54:06 +02:00
pxa2xx_ac97_try_warm_reset ( ) ;
2006-10-12 14:29:03 +02:00
2017-09-02 21:54:06 +02:00
pxa2xx_ac97_finish_reset ( ) ;
2006-10-12 14:29:03 +02:00
}
2018-08-25 10:46:18 +02:00
static void pxa2xx_ac97_cold_reset ( struct ac97_controller * adrv )
2006-10-12 14:29:03 +02:00
{
2017-09-02 21:54:06 +02:00
pxa2xx_ac97_try_cold_reset ( ) ;
2008-04-22 17:08:52 +02:00
2017-09-02 21:54:06 +02:00
pxa2xx_ac97_finish_reset ( ) ;
}
2018-08-25 10:46:18 +02:00
static int pxa2xx_ac97_read_actrl ( struct ac97_controller * adrv , int slot ,
unsigned short reg )
2017-09-02 21:54:06 +02:00
{
2018-08-25 10:46:18 +02:00
return pxa2xx_ac97_read ( slot , reg ) ;
2017-09-02 21:54:06 +02:00
}
2018-08-25 10:46:18 +02:00
static int pxa2xx_ac97_write_actrl ( struct ac97_controller * adrv , int slot ,
unsigned short reg , unsigned short val )
2017-09-02 21:54:06 +02:00
{
2018-08-25 10:46:18 +02:00
return pxa2xx_ac97_write ( slot , reg , val ) ;
2006-10-12 14:29:03 +02:00
}
2018-08-25 10:46:18 +02:00
static struct ac97_controller_ops pxa2xx_ac97_ops = {
. read = pxa2xx_ac97_read_actrl ,
. write = pxa2xx_ac97_write_actrl ,
2006-10-12 14:29:03 +02:00
. warm_reset = pxa2xx_ac97_warm_reset ,
. reset = pxa2xx_ac97_cold_reset ,
} ;
2013-08-12 10:42:39 +02:00
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = {
. addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES ,
2018-06-28 22:08:37 +02:00
. chan_name = " pcm_pcm_stereo_in " ,
2013-08-12 10:42:39 +02:00
. maxburst = 32 ,
2015-09-30 22:51:52 +02:00
} ;
2013-08-12 10:42:39 +02:00
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = {
. addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES ,
2018-06-28 22:08:37 +02:00
. chan_name = " pcm_pcm_stereo_out " ,
2013-08-12 10:42:39 +02:00
. maxburst = 32 ,
2006-10-12 14:29:03 +02:00
} ;
2013-08-12 10:42:39 +02:00
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = {
. addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES ,
2018-06-28 22:08:37 +02:00
. chan_name = " pcm_aux_mono_out " ,
2013-08-12 10:42:39 +02:00
. maxburst = 16 ,
2006-10-12 14:29:03 +02:00
} ;
2013-08-12 10:42:39 +02:00
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = {
. addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES ,
2018-06-28 22:08:37 +02:00
. chan_name = " pcm_aux_mono_in " ,
2013-08-12 10:42:39 +02:00
. maxburst = 16 ,
2006-10-12 14:29:03 +02:00
} ;
2013-08-12 10:42:39 +02:00
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = {
. addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES ,
2018-06-28 22:08:37 +02:00
. chan_name = " pcm_aux_mic_mono " ,
2013-08-12 10:42:39 +02:00
. maxburst = 16 ,
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 ;
2017-02-25 23:26:56 +02: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 " ,
. 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 " ,
. 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 " ,
. 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 " ,
2019-10-02 14:33:50 +09:00
. pcm_construct = pxa2xx_soc_pcm_new ,
. open = pxa2xx_soc_pcm_open ,
. close = pxa2xx_soc_pcm_close ,
. hw_params = pxa2xx_soc_pcm_hw_params ,
. prepare = pxa2xx_soc_pcm_prepare ,
. trigger = pxa2xx_soc_pcm_trigger ,
. pointer = pxa2xx_soc_pcm_pointer ,
2013-03-21 03:34:23 -07:00
} ;
2018-06-17 12:50:01 +02:00
# ifdef CONFIG_OF
static const struct of_device_id pxa2xx_ac97_dt_ids [ ] = {
{ . compatible = " marvell,pxa250-ac97 " , } ,
{ . compatible = " marvell,pxa270-ac97 " , } ,
{ . compatible = " marvell,pxa300-ac97 " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , pxa2xx_ac97_dt_ids ) ;
# endif
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 ;
2018-08-25 10:46:18 +02:00
struct ac97_controller * ctrl ;
pxa2xx_audio_ops_t * pdata = pdev - > dev . platform_data ;
2019-09-18 10:20:19 +02:00
struct resource * regs ;
2018-08-25 10:46:18 +02:00
void * * codecs_pdata ;
2013-06-28 11:17:48 -07:00
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 ;
}
2019-09-18 10:20:19 +02:00
regs = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! regs )
return - ENXIO ;
pxa2xx_ac97_pcm_stereo_in . addr = regs - > start + PCDR ;
pxa2xx_ac97_pcm_stereo_out . addr = regs - > start + PCDR ;
pxa2xx_ac97_pcm_aux_mono_out . addr = regs - > start + MODR ;
pxa2xx_ac97_pcm_aux_mono_in . addr = regs - > start + MODR ;
pxa2xx_ac97_pcm_mic_mono_in . addr = regs - > start + MCDR ;
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 ;
}
2018-08-25 10:46:18 +02:00
codecs_pdata = pdata ? pdata - > codec_pdata : NULL ;
ctrl = snd_ac97_controller_register ( & pxa2xx_ac97_ops , & pdev - > dev ,
AC97_SLOTS_AVAILABLE_ALL ,
codecs_pdata ) ;
if ( IS_ERR ( ctrl ) )
return PTR_ERR ( ctrl ) ;
2013-06-26 12:45:59 +01:00
2018-08-25 10:46:18 +02:00
platform_set_drvdata ( pdev , ctrl ) ;
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 .
*/
2019-06-28 13:10:31 +09:00
return devm_snd_soc_register_component ( & pdev - > dev , & pxa_ac97_component ,
2013-03-21 03:34:23 -07:00
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
{
2018-08-25 10:46:18 +02:00
struct ac97_controller * ctrl = platform_get_drvdata ( pdev ) ;
snd_ac97_controller_unregister ( ctrl ) ;
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
2018-06-17 12:50:01 +02:00
. of_match_table = of_match_ptr ( pxa2xx_ac97_dt_ids ) ,
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 " ) ;