2019-04-05 14:50:02 +03:00
// SPDX-License-Identifier: GPL-2.0
//
// linux/sound/mpc5200-ac97.c -- AC97 support for the Freescale MPC52xx chip.
//
// Copyright (C) 2009 Jon Smirl, Digispeaker
// Author: Jon Smirl <jonsmirl@gmail.com>
2009-05-26 08:34:10 -04:00
# include <linux/module.h>
# include <linux/of_device.h>
# include <linux/of_platform.h>
2009-07-21 13:15:40 -07:00
# include <linux/delay.h>
2016-01-15 19:35:25 +08:00
# include <linux/time.h>
2009-05-26 08: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-06 20:49:19 -06:00
# include <asm/mpc52xx.h>
2009-05-26 08:34:10 -04:00
# include <asm/mpc52xx_psc.h>
# include "mpc5200_dma.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 01:06:19 -04:00
int status ;
2009-05-26 08:34:10 -04:00
unsigned int val ;
2009-07-02 11:57:25 -06:00
mutex_lock ( & psc_dma - > mutex ) ;
2009-05-26 08:34:10 -04:00
/* Wait for command send status zero = ready */
2009-05-27 01: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 08:34:10 -04:00
pr_err ( " timeout on ac97 bus (rdy) \n " ) ;
2009-07-02 11:57:25 -06:00
mutex_unlock ( & psc_dma - > mutex ) ;
2009-05-26 08:34:10 -04:00
return - ENODEV ;
}
2009-07-02 11:57:19 -06:00
/* Force clear the data valid bit */
in_be32 ( & psc_dma - > psc_regs - > ac97_data ) ;
2009-05-26 08: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 01: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 08: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 11:57:25 -06:00
mutex_unlock ( & psc_dma - > mutex ) ;
2009-05-26 08: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 11:57:25 -06:00
mutex_unlock ( & psc_dma - > mutex ) ;
2009-05-26 08:34:10 -04:00
return - ENODEV ;
}
val = ( val > > 8 ) & 0xffff ;
2009-07-02 11:57:25 -06:00
mutex_unlock ( & psc_dma - > mutex ) ;
2009-05-26 08: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 01:06:19 -04:00
int status ;
2009-05-26 08:34:10 -04:00
2009-07-02 11:57:25 -06:00
mutex_lock ( & psc_dma - > mutex ) ;
2009-05-26 08:34:10 -04:00
/* Wait for command status zero = ready */
2009-05-27 01: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 08:34:10 -04:00
pr_err ( " timeout on ac97 bus (write) \n " ) ;
2009-07-02 11:57:25 -06:00
goto out ;
2009-05-26 08:34:10 -04:00
}
/* Write data */
out_be32 ( & psc_dma - > psc_regs - > ac97_cmd ,
( ( reg & 0x7f ) < < 24 ) | ( val < < 8 ) ) ;
2009-07-02 11:57:25 -06:00
out :
mutex_unlock ( & psc_dma - > mutex ) ;
2009-05-26 08: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-06 20:49:19 -06:00
mutex_lock ( & psc_dma - > mutex ) ;
2009-05-26 08:34:10 -04:00
out_be32 ( & regs - > sicr , psc_dma - > sicr | MPC52xx_PSC_SICR_AWR ) ;
2009-05-27 01:06:19 -04:00
udelay ( 3 ) ;
2009-05-26 08:34:10 -04:00
out_be32 ( & regs - > sicr , psc_dma - > sicr ) ;
2010-08-06 20:49:19 -06:00
mutex_unlock ( & psc_dma - > mutex ) ;
2009-05-26 08: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-06 20:49:19 -06: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 19:35:25 +08:00
usleep_range ( 1000 , 2000 ) ;
2009-05-26 08:34:10 -04:00
psc_ac97_warm_reset ( ac97 ) ;
}
2013-06-26 12:45:59 +01:00
static struct snd_ac97_bus_ops psc_ac97_ops = {
2009-05-26 08: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 20:15:21 +00:00
struct psc_dma * psc_dma = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2009-11-07 01:34:43 -07:00
struct psc_dma_stream * s = to_psc_dma_stream ( substream , psc_dma ) ;
2009-05-26 08: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 01:34:43 -07: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 08: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 20:15:21 +00:00
struct psc_dma * psc_dma = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2009-05-26 08:34:10 -04:00
2009-11-07 01:34:43 -07:00
dev_dbg ( psc_dma - > dev , " %s(substream=%p) \n " , __func__ , substream ) ;
2009-05-26 08: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 20:15:21 +00:00
struct psc_dma * psc_dma = snd_soc_dai_get_drvdata ( dai ) ;
2009-11-07 01:34:43 -07:00
struct psc_dma_stream * s = to_psc_dma_stream ( substream , psc_dma ) ;
2009-05-26 08:34:10 -04:00
switch ( cmd ) {
2009-11-07 01:34:43 -07: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 08:34:10 -04:00
case SNDRV_PCM_TRIGGER_STOP :
2009-11-07 01:34:43 -07:00
dev_dbg ( psc_dma - > dev , " AC97 STOP: stream=%i \n " ,
substream - > pstr - > stream ) ;
2009-05-26 08:34:10 -04:00
2009-11-07 01:34:43 -07:00
/* Clear the slot enable bits */
psc_dma - > slots & = ~ ( s - > ac97_slot_bits ) ;
2009-05-26 08:34:10 -04:00
out_be32 ( & psc_dma - > psc_regs - > ac97_slots , psc_dma - > slots ) ;
break ;
}
return 0 ;
}
2010-03-17 20:15:21 +00:00
static int psc_ac97_probe ( struct snd_soc_dai * cpu_dai )
2009-05-26 08:34:10 -04:00
{
2010-03-17 20:15:21 +00:00
struct psc_dma * psc_dma = snd_soc_dai_get_drvdata ( cpu_dai ) ;
2009-05-26 08: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 11:40:40 +01:00
static const struct snd_soc_dai_ops psc_ac97_analog_ops = {
2009-05-26 08:34:10 -04:00
. hw_params = psc_ac97_hw_analog_params ,
. trigger = psc_ac97_trigger ,
} ;
2011-11-23 11:40:40 +01:00
static const struct snd_soc_dai_ops psc_ac97_digital_ops = {
2009-05-26 08:34:10 -04:00
. hw_params = psc_ac97_hw_digital_params ,
} ;
2010-03-17 20:15:21 +00:00
static struct snd_soc_dai_driver psc_ac97_dai [ ] = {
2009-05-26 08:34:10 -04:00
{
2012-09-13 17:43:12 -04:00
. name = " mpc5200-psc-ac97.0 " ,
2009-05-26 08:34:10 -04:00
. probe = psc_ac97_probe ,
. playback = {
2012-09-13 17:43:12 -04:00
. stream_name = " AC97 Playback " ,
2009-05-26 08: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-13 17:43:12 -04:00
. stream_name = " AC97 Capture " ,
2009-05-26 08: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-13 17:43:12 -04:00
. name = " mpc5200-psc-ac97.1 " ,
2009-05-26 08:34:10 -04:00
. playback = {
2012-09-13 17:43:12 -04:00
. stream_name = " AC97 SPDIF " ,
2009-05-26 08: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 03:31:41 -07:00
static const struct snd_soc_component_driver psc_ac97_component = {
. name = DRV_NAME ,
} ;
2009-05-26 08:34:10 -04:00
/* ---------------------------------------------------------------------
* OF platform bus binding code :
* - Probe / remove operations
* - OF device match table
*/
2012-12-07 09:26:16 -05:00
static int psc_ac97_of_probe ( struct platform_device * op )
2009-05-26 08:34:10 -04:00
{
2010-03-17 20:15:21 +00:00
int rc ;
2009-05-26 08:34:10 -04:00
struct mpc52xx_psc __iomem * regs ;
2012-09-13 17:43:11 -04:00
rc = mpc5200_audio_dma_create ( op ) ;
if ( rc ! = 0 )
return rc ;
2013-06-26 12:45:59 +01:00
rc = snd_soc_set_ac97_ops ( & psc_ac97_ops ) ;
if ( rc ! = 0 ) {
2013-09-17 14:28:33 -05:00
dev_err ( & op - > dev , " Failed to set AC'97 ops: %d \n " , rc ) ;
2013-06-26 12:45:59 +01:00
return rc ;
}
2013-03-21 03:31:41 -07:00
rc = snd_soc_register_component ( & op - > dev , & psc_ac97_component ,
psc_ac97_dai , ARRAY_SIZE ( psc_ac97_dai ) ) ;
2009-05-26 08: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 09:26:16 -05:00
static int psc_ac97_of_remove ( struct platform_device * op )
2009-05-26 08:34:10 -04:00
{
2012-09-13 17:43:11 -04:00
mpc5200_audio_dma_destroy ( op ) ;
2013-03-21 03:31:41 -07:00
snd_soc_unregister_component ( & op - > dev ) ;
2013-06-26 12:45:59 +01:00
snd_soc_set_ac97_ops ( NULL ) ;
2010-03-17 20:15:21 +00:00
return 0 ;
2009-05-26 08:34:10 -04:00
}
/* Match table for of_platform binding */
2015-03-18 17:48:57 +01:00
static const struct of_device_id psc_ac97_match [ ] = {
2009-05-26 08:34:10 -04:00
{ . compatible = " fsl,mpc5200-psc-ac97 " , } ,
{ . compatible = " fsl,mpc5200b-psc-ac97 " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , psc_ac97_match ) ;
2011-02-22 21:05:04 -07:00
static struct platform_driver psc_ac97_driver = {
2009-05-26 08:34:10 -04:00
. probe = psc_ac97_of_probe ,
2012-12-07 09:26:16 -05:00
. remove = psc_ac97_of_remove ,
2009-05-26 08:34:10 -04:00
. driver = {
. name = " mpc5200-psc-ac97 " ,
2010-04-13 16:13:02 -07:00
. of_match_table = psc_ac97_match ,
2009-05-26 08:34:10 -04:00
} ,
} ;
2011-11-25 10:10:55 +08:00
module_platform_driver ( psc_ac97_driver ) ;
2009-05-26 08:34:10 -04:00
MODULE_AUTHOR ( " Jon Smirl <jonsmirl@gmail.com> " ) ;
MODULE_DESCRIPTION ( " mpc5200 AC97 module " ) ;
MODULE_LICENSE ( " GPL " ) ;