2009-05-26 16:34:10 +04:00
/*
* linux / sound / mpc5200 - ac97 . c - - AC97 support for the Freescale MPC52xx chip .
*
* Copyright ( C ) 2009 Jon Smirl , Digispeaker
* Author : Jon Smirl < jonsmirl @ gmail . 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 .
*/
# include <linux/module.h>
# include <linux/of_device.h>
# include <linux/of_platform.h>
2009-07-22 00:15:40 +04:00
# include <linux/delay.h>
2016-01-15 14:35:25 +03:00
# include <linux/time.h>
2009-05-26 16:34:10 +04:00
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/soc.h>
# include <asm/time.h>
# include <asm/delay.h>
2010-08-07 06:49:19 +04:00
# include <asm/mpc52xx.h>
2009-05-26 16:34:10 +04:00
# include <asm/mpc52xx_psc.h>
# include "mpc5200_dma.h"
# include "mpc5200_psc_ac97.h"
# define DRV_NAME "mpc5200-psc-ac97"
/* ALSA only supports a single AC97 device so static is recommend here */
static struct psc_dma * psc_dma ;
static unsigned short psc_ac97_read ( struct snd_ac97 * ac97 , unsigned short reg )
{
2009-05-27 09:06:19 +04:00
int status ;
2009-05-26 16:34:10 +04:00
unsigned int val ;
2009-07-02 21:57:25 +04:00
mutex_lock ( & psc_dma - > mutex ) ;
2009-05-26 16:34:10 +04:00
/* Wait for command send status zero = ready */
2009-05-27 09:06:19 +04:00
status = spin_event_timeout ( ! ( in_be16 ( & psc_dma - > psc_regs - > sr_csr . status ) &
MPC52xx_PSC_SR_CMDSEND ) , 100 , 0 ) ;
if ( status = = 0 ) {
2009-05-26 16:34:10 +04:00
pr_err ( " timeout on ac97 bus (rdy) \n " ) ;
2009-07-02 21:57:25 +04:00
mutex_unlock ( & psc_dma - > mutex ) ;
2009-05-26 16:34:10 +04:00
return - ENODEV ;
}
2009-07-02 21:57:19 +04:00
/* Force clear the data valid bit */
in_be32 ( & psc_dma - > psc_regs - > ac97_data ) ;
2009-05-26 16:34:10 +04:00
/* Send the read */
out_be32 ( & psc_dma - > psc_regs - > ac97_cmd , ( 1 < < 31 ) | ( ( reg & 0x7f ) < < 24 ) ) ;
/* Wait for the answer */
2009-05-27 09:06:19 +04:00
status = spin_event_timeout ( ( in_be16 ( & psc_dma - > psc_regs - > sr_csr . status ) &
MPC52xx_PSC_SR_DATA_VAL ) , 100 , 0 ) ;
if ( status = = 0 ) {
2009-05-26 16:34:10 +04:00
pr_err ( " timeout on ac97 read (val) %x \n " ,
in_be16 ( & psc_dma - > psc_regs - > sr_csr . status ) ) ;
2009-07-02 21:57:25 +04:00
mutex_unlock ( & psc_dma - > mutex ) ;
2009-05-26 16:34:10 +04:00
return - ENODEV ;
}
/* Get the data */
val = in_be32 ( & psc_dma - > psc_regs - > ac97_data ) ;
if ( ( ( val > > 24 ) & 0x7f ) ! = reg ) {
pr_err ( " reg echo error on ac97 read \n " ) ;
2009-07-02 21:57:25 +04:00
mutex_unlock ( & psc_dma - > mutex ) ;
2009-05-26 16:34:10 +04:00
return - ENODEV ;
}
val = ( val > > 8 ) & 0xffff ;
2009-07-02 21:57:25 +04:00
mutex_unlock ( & psc_dma - > mutex ) ;
2009-05-26 16:34:10 +04:00
return ( unsigned short ) val ;
}
static void psc_ac97_write ( struct snd_ac97 * ac97 ,
unsigned short reg , unsigned short val )
{
2009-05-27 09:06:19 +04:00
int status ;
2009-05-26 16:34:10 +04:00
2009-07-02 21:57:25 +04:00
mutex_lock ( & psc_dma - > mutex ) ;
2009-05-26 16:34:10 +04:00
/* Wait for command status zero = ready */
2009-05-27 09:06:19 +04:00
status = spin_event_timeout ( ! ( in_be16 ( & psc_dma - > psc_regs - > sr_csr . status ) &
MPC52xx_PSC_SR_CMDSEND ) , 100 , 0 ) ;
if ( status = = 0 ) {
2009-05-26 16:34:10 +04:00
pr_err ( " timeout on ac97 bus (write) \n " ) ;
2009-07-02 21:57:25 +04:00
goto out ;
2009-05-26 16:34:10 +04:00
}
/* Write data */
out_be32 ( & psc_dma - > psc_regs - > ac97_cmd ,
( ( reg & 0x7f ) < < 24 ) | ( val < < 8 ) ) ;
2009-07-02 21:57:25 +04:00
out :
mutex_unlock ( & psc_dma - > mutex ) ;
2009-05-26 16:34:10 +04:00
}
static void psc_ac97_warm_reset ( struct snd_ac97 * ac97 )
{
struct mpc52xx_psc __iomem * regs = psc_dma - > psc_regs ;
2010-08-07 06:49:19 +04:00
mutex_lock ( & psc_dma - > mutex ) ;
2009-05-26 16:34:10 +04:00
out_be32 ( & regs - > sicr , psc_dma - > sicr | MPC52xx_PSC_SICR_AWR ) ;
2009-05-27 09:06:19 +04:00
udelay ( 3 ) ;
2009-05-26 16:34:10 +04:00
out_be32 ( & regs - > sicr , psc_dma - > sicr ) ;
2010-08-07 06:49:19 +04:00
mutex_unlock ( & psc_dma - > mutex ) ;
2009-05-26 16:34:10 +04:00
}
static void psc_ac97_cold_reset ( struct snd_ac97 * ac97 )
{
struct mpc52xx_psc __iomem * regs = psc_dma - > psc_regs ;
2010-08-07 06:49:19 +04:00
mutex_lock ( & psc_dma - > mutex ) ;
dev_dbg ( psc_dma - > dev , " cold reset \n " ) ;
mpc5200_psc_ac97_gpio_reset ( psc_dma - > id ) ;
/* Notify the PSC that a reset has occurred */
out_be32 ( & regs - > sicr , psc_dma - > sicr | MPC52xx_PSC_SICR_ACRB ) ;
/* Re-enable RX and TX */
out_8 ( & regs - > command , MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE ) ;
mutex_unlock ( & psc_dma - > mutex ) ;
2016-01-15 14:35:25 +03:00
usleep_range ( 1000 , 2000 ) ;
2009-05-26 16:34:10 +04:00
psc_ac97_warm_reset ( ac97 ) ;
}
2013-06-26 15:45:59 +04:00
static struct snd_ac97_bus_ops psc_ac97_ops = {
2009-05-26 16:34:10 +04:00
. read = psc_ac97_read ,
. write = psc_ac97_write ,
. reset = psc_ac97_cold_reset ,
. warm_reset = psc_ac97_warm_reset ,
} ;
static int psc_ac97_hw_analog_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * cpu_dai )
{
2010-03-17 23:15:21 +03:00
struct psc_dma * psc_dma = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2009-11-07 11:34:43 +03:00
struct psc_dma_stream * s = to_psc_dma_stream ( substream , psc_dma ) ;
2009-05-26 16:34:10 +04:00
dev_dbg ( psc_dma - > dev , " %s(substream=%p) p_size=%i p_bytes=%i "
" periods=%i buffer_size=%i buffer_bytes=%i channels=%i "
" rate=%i format=%i \n " ,
__func__ , substream , params_period_size ( params ) ,
params_period_bytes ( params ) , params_periods ( params ) ,
params_buffer_size ( params ) , params_buffer_bytes ( params ) ,
params_channels ( params ) , params_rate ( params ) ,
params_format ( params ) ) ;
2009-11-07 11:34:43 +03:00
/* Determine the set of enable bits to turn on */
s - > ac97_slot_bits = ( params_channels ( params ) = = 1 ) ? 0x100 : 0x300 ;
if ( substream - > pstr - > stream ! = SNDRV_PCM_STREAM_CAPTURE )
s - > ac97_slot_bits < < = 16 ;
2009-05-26 16:34:10 +04:00
return 0 ;
}
static int psc_ac97_hw_digital_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * cpu_dai )
{
2010-03-17 23:15:21 +03:00
struct psc_dma * psc_dma = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2009-05-26 16:34:10 +04:00
2009-11-07 11:34:43 +03:00
dev_dbg ( psc_dma - > dev , " %s(substream=%p) \n " , __func__ , substream ) ;
2009-05-26 16:34:10 +04:00
if ( params_channels ( params ) = = 1 )
out_be32 ( & psc_dma - > psc_regs - > ac97_slots , 0x01000000 ) ;
else
out_be32 ( & psc_dma - > psc_regs - > ac97_slots , 0x03000000 ) ;
return 0 ;
}
static int psc_ac97_trigger ( struct snd_pcm_substream * substream , int cmd ,
struct snd_soc_dai * dai )
{
2010-03-17 23:15:21 +03:00
struct psc_dma * psc_dma = snd_soc_dai_get_drvdata ( dai ) ;
2009-11-07 11:34:43 +03:00
struct psc_dma_stream * s = to_psc_dma_stream ( substream , psc_dma ) ;
2009-05-26 16:34:10 +04:00
switch ( cmd ) {
2009-11-07 11:34:43 +03:00
case SNDRV_PCM_TRIGGER_START :
dev_dbg ( psc_dma - > dev , " AC97 START: stream=%i \n " ,
substream - > pstr - > stream ) ;
/* Set the slot enable bits */
psc_dma - > slots | = s - > ac97_slot_bits ;
out_be32 ( & psc_dma - > psc_regs - > ac97_slots , psc_dma - > slots ) ;
break ;
2009-05-26 16:34:10 +04:00
case SNDRV_PCM_TRIGGER_STOP :
2009-11-07 11:34:43 +03:00
dev_dbg ( psc_dma - > dev , " AC97 STOP: stream=%i \n " ,
substream - > pstr - > stream ) ;
2009-05-26 16:34:10 +04:00
2009-11-07 11:34:43 +03:00
/* Clear the slot enable bits */
psc_dma - > slots & = ~ ( s - > ac97_slot_bits ) ;
2009-05-26 16:34:10 +04:00
out_be32 ( & psc_dma - > psc_regs - > ac97_slots , psc_dma - > slots ) ;
break ;
}
return 0 ;
}
2010-03-17 23:15:21 +03:00
static int psc_ac97_probe ( struct snd_soc_dai * cpu_dai )
2009-05-26 16:34:10 +04:00
{
2010-03-17 23:15:21 +03:00
struct psc_dma * psc_dma = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2009-05-26 16:34:10 +04:00
struct mpc52xx_psc __iomem * regs = psc_dma - > psc_regs ;
/* Go */
out_8 ( & regs - > command , MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE ) ;
return 0 ;
}
/* ---------------------------------------------------------------------
* ALSA SoC Bindings
*
* - Digital Audio Interface ( DAI ) template
* - create / destroy dai hooks
*/
/**
* psc_ac97_dai_template : template CPU Digital Audio Interface
*/
2011-11-23 14:40:40 +04:00
static const struct snd_soc_dai_ops psc_ac97_analog_ops = {
2009-05-26 16:34:10 +04:00
. hw_params = psc_ac97_hw_analog_params ,
. trigger = psc_ac97_trigger ,
} ;
2011-11-23 14:40:40 +04:00
static const struct snd_soc_dai_ops psc_ac97_digital_ops = {
2009-05-26 16:34:10 +04:00
. hw_params = psc_ac97_hw_digital_params ,
} ;
2010-03-17 23:15:21 +03:00
static struct snd_soc_dai_driver psc_ac97_dai [ ] = {
2009-05-26 16:34:10 +04:00
{
2012-09-14 01:43:12 +04:00
. name = " mpc5200-psc-ac97.0 " ,
2014-11-11 00:41:52 +03:00
. bus_control = true ,
2009-05-26 16:34:10 +04:00
. probe = psc_ac97_probe ,
. playback = {
2012-09-14 01:43:12 +04:00
. stream_name = " AC97 Playback " ,
2009-05-26 16:34:10 +04:00
. channels_min = 1 ,
. channels_max = 6 ,
. rates = SNDRV_PCM_RATE_8000_48000 ,
. formats = SNDRV_PCM_FMTBIT_S32_BE ,
} ,
. capture = {
2012-09-14 01:43:12 +04:00
. stream_name = " AC97 Capture " ,
2009-05-26 16:34:10 +04:00
. channels_min = 1 ,
. channels_max = 2 ,
. rates = SNDRV_PCM_RATE_8000_48000 ,
. formats = SNDRV_PCM_FMTBIT_S32_BE ,
} ,
. ops = & psc_ac97_analog_ops ,
} ,
{
2012-09-14 01:43:12 +04:00
. name = " mpc5200-psc-ac97.1 " ,
2014-11-11 00:41:52 +03:00
. bus_control = true ,
2009-05-26 16:34:10 +04:00
. playback = {
2012-09-14 01:43:12 +04:00
. stream_name = " AC97 SPDIF " ,
2009-05-26 16:34:10 +04:00
. channels_min = 1 ,
. channels_max = 2 ,
. rates = SNDRV_PCM_RATE_32000 | \
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 ,
. formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE ,
} ,
. ops = & psc_ac97_digital_ops ,
} } ;
2013-03-21 14:31:41 +04:00
static const struct snd_soc_component_driver psc_ac97_component = {
. name = DRV_NAME ,
} ;
2009-05-26 16:34:10 +04:00
/* ---------------------------------------------------------------------
* OF platform bus binding code :
* - Probe / remove operations
* - OF device match table
*/
2012-12-07 18:26:16 +04:00
static int psc_ac97_of_probe ( struct platform_device * op )
2009-05-26 16:34:10 +04:00
{
2010-03-17 23:15:21 +03:00
int rc ;
2009-05-26 16:34:10 +04:00
struct mpc52xx_psc __iomem * regs ;
2012-09-14 01:43:11 +04:00
rc = mpc5200_audio_dma_create ( op ) ;
if ( rc ! = 0 )
return rc ;
2013-06-26 15:45:59 +04:00
rc = snd_soc_set_ac97_ops ( & psc_ac97_ops ) ;
if ( rc ! = 0 ) {
2013-09-17 23:28:33 +04:00
dev_err ( & op - > dev , " Failed to set AC'97 ops: %d \n " , rc ) ;
2013-06-26 15:45:59 +04:00
return rc ;
}
2013-03-21 14:31:41 +04:00
rc = snd_soc_register_component ( & op - > dev , & psc_ac97_component ,
psc_ac97_dai , ARRAY_SIZE ( psc_ac97_dai ) ) ;
2009-05-26 16:34:10 +04:00
if ( rc ! = 0 ) {
dev_err ( & op - > dev , " Failed to register DAI \n " ) ;
return rc ;
}
psc_dma = dev_get_drvdata ( & op - > dev ) ;
regs = psc_dma - > psc_regs ;
psc_dma - > imr = 0 ;
out_be16 ( & psc_dma - > psc_regs - > isr_imr . imr , psc_dma - > imr ) ;
/* Configure the serial interface mode to AC97 */
psc_dma - > sicr = MPC52xx_PSC_SICR_SIM_AC97 | MPC52xx_PSC_SICR_ENAC97 ;
out_be32 ( & regs - > sicr , psc_dma - > sicr ) ;
/* No slots active */
out_be32 ( & regs - > ac97_slots , 0x00000000 ) ;
return 0 ;
}
2012-12-07 18:26:16 +04:00
static int psc_ac97_of_remove ( struct platform_device * op )
2009-05-26 16:34:10 +04:00
{
2012-09-14 01:43:11 +04:00
mpc5200_audio_dma_destroy ( op ) ;
2013-03-21 14:31:41 +04:00
snd_soc_unregister_component ( & op - > dev ) ;
2013-06-26 15:45:59 +04:00
snd_soc_set_ac97_ops ( NULL ) ;
2010-03-17 23:15:21 +03:00
return 0 ;
2009-05-26 16:34:10 +04:00
}
/* Match table for of_platform binding */
2015-03-18 19:48:57 +03:00
static const struct of_device_id psc_ac97_match [ ] = {
2009-05-26 16:34:10 +04:00
{ . compatible = " fsl,mpc5200-psc-ac97 " , } ,
{ . compatible = " fsl,mpc5200b-psc-ac97 " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , psc_ac97_match ) ;
2011-02-23 07:05:04 +03:00
static struct platform_driver psc_ac97_driver = {
2009-05-26 16:34:10 +04:00
. probe = psc_ac97_of_probe ,
2012-12-07 18:26:16 +04:00
. remove = psc_ac97_of_remove ,
2009-05-26 16:34:10 +04:00
. driver = {
. name = " mpc5200-psc-ac97 " ,
2010-04-14 03:13:02 +04:00
. of_match_table = psc_ac97_match ,
2009-05-26 16:34:10 +04:00
} ,
} ;
2011-11-25 06:10:55 +04:00
module_platform_driver ( psc_ac97_driver ) ;
2009-05-26 16:34:10 +04:00
MODULE_AUTHOR ( " Jon Smirl <jonsmirl@gmail.com> " ) ;
MODULE_DESCRIPTION ( " mpc5200 AC97 module " ) ;
MODULE_LICENSE ( " GPL " ) ;