2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2009-02-05 15:11:00 +03:00
/*
2009-04-02 10:21:12 +04:00
* Driver for Atmel AC97C
2009-02-05 15:11:00 +03:00
*
* Copyright ( C ) 2005 - 2009 Atmel Corporation
*/
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/bitmap.h>
2009-04-02 10:21:12 +04:00
# include <linux/device.h>
2010-03-01 14:19:18 +03:00
# include <linux/atmel_pdc.h>
2017-09-01 19:15:06 +03:00
# include <linux/gpio/consumer.h>
2009-02-05 15:11:00 +03:00
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/mutex.h>
2012-02-01 14:42:27 +04:00
# include <linux/types.h>
2009-02-05 15:11:00 +03:00
# include <linux/io.h>
2014-12-29 15:08:39 +03:00
# include <linux/of.h>
# include <linux/of_device.h>
2009-02-05 15:11:00 +03:00
# include <sound/core.h>
# include <sound/initval.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/ac97_codec.h>
# include <sound/memalloc.h>
# include "ac97c.h"
/* Serialize access to opened variable */
static DEFINE_MUTEX ( opened_mutex ) ;
struct atmel_ac97c {
struct clk * pclk ;
struct platform_device * pdev ;
struct snd_pcm_substream * playback_substream ;
struct snd_pcm_substream * capture_substream ;
struct snd_card * card ;
struct snd_pcm * pcm ;
struct snd_ac97 * ac97 ;
struct snd_ac97_bus * ac97_bus ;
u64 cur_format ;
unsigned int cur_rate ;
2010-03-01 14:19:18 +03:00
int playback_period , capture_period ;
2009-02-05 15:11:00 +03:00
/* Serialize access to opened variable */
spinlock_t lock ;
void __iomem * regs ;
2009-04-02 10:21:14 +04:00
int irq ;
2009-02-05 15:11:00 +03:00
int opened ;
2017-09-01 19:15:06 +03:00
struct gpio_desc * reset_pin ;
2009-02-05 15:11:00 +03:00
} ;
# define get_chip(card) ((struct atmel_ac97c *)(card)->private_data)
# define ac97c_writel(chip, reg, val) \
__raw_writel ( ( val ) , ( chip ) - > regs + AC97C_ # # reg )
# define ac97c_readl(chip, reg) \
__raw_readl ( ( chip ) - > regs + AC97C_ # # reg )
2017-08-17 12:15:50 +03:00
static const struct snd_pcm_hardware atmel_ac97c_hw = {
2009-02-05 15:11:00 +03:00
. info = ( SNDRV_PCM_INFO_MMAP
| SNDRV_PCM_INFO_MMAP_VALID
| SNDRV_PCM_INFO_INTERLEAVED
| SNDRV_PCM_INFO_BLOCK_TRANSFER
| SNDRV_PCM_INFO_JOINT_DUPLEX
| SNDRV_PCM_INFO_RESUME
| SNDRV_PCM_INFO_PAUSE ) ,
. formats = ( SNDRV_PCM_FMTBIT_S16_BE
| SNDRV_PCM_FMTBIT_S16_LE ) ,
. rates = ( SNDRV_PCM_RATE_CONTINUOUS ) ,
. rate_min = 4000 ,
. rate_max = 48000 ,
. channels_min = 1 ,
. channels_max = 2 ,
2009-04-02 10:21:13 +04:00
. buffer_bytes_max = 2 * 2 * 64 * 2048 ,
2009-02-05 15:11:00 +03:00
. period_bytes_min = 4096 ,
. period_bytes_max = 4096 ,
2009-04-02 10:21:13 +04:00
. periods_min = 6 ,
2009-02-05 15:11:00 +03:00
. periods_max = 64 ,
} ;
static int atmel_ac97c_playback_open ( struct snd_pcm_substream * substream )
{
struct atmel_ac97c * chip = snd_pcm_substream_chip ( substream ) ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
mutex_lock ( & opened_mutex ) ;
chip - > opened + + ;
runtime - > hw = atmel_ac97c_hw ;
if ( chip - > cur_rate ) {
runtime - > hw . rate_min = chip - > cur_rate ;
runtime - > hw . rate_max = chip - > cur_rate ;
}
if ( chip - > cur_format )
2013-04-23 03:00:41 +04:00
runtime - > hw . formats = pcm_format_to_bits ( chip - > cur_format ) ;
2009-02-05 15:11:00 +03:00
mutex_unlock ( & opened_mutex ) ;
chip - > playback_substream = substream ;
return 0 ;
}
static int atmel_ac97c_capture_open ( struct snd_pcm_substream * substream )
{
struct atmel_ac97c * chip = snd_pcm_substream_chip ( substream ) ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
mutex_lock ( & opened_mutex ) ;
chip - > opened + + ;
runtime - > hw = atmel_ac97c_hw ;
if ( chip - > cur_rate ) {
runtime - > hw . rate_min = chip - > cur_rate ;
runtime - > hw . rate_max = chip - > cur_rate ;
}
if ( chip - > cur_format )
2013-04-23 03:00:41 +04:00
runtime - > hw . formats = pcm_format_to_bits ( chip - > cur_format ) ;
2009-02-05 15:11:00 +03:00
mutex_unlock ( & opened_mutex ) ;
chip - > capture_substream = substream ;
return 0 ;
}
static int atmel_ac97c_playback_close ( struct snd_pcm_substream * substream )
{
struct atmel_ac97c * chip = snd_pcm_substream_chip ( substream ) ;
mutex_lock ( & opened_mutex ) ;
chip - > opened - - ;
if ( ! chip - > opened ) {
chip - > cur_rate = 0 ;
chip - > cur_format = 0 ;
}
mutex_unlock ( & opened_mutex ) ;
chip - > playback_substream = NULL ;
return 0 ;
}
static int atmel_ac97c_capture_close ( struct snd_pcm_substream * substream )
{
struct atmel_ac97c * chip = snd_pcm_substream_chip ( substream ) ;
mutex_lock ( & opened_mutex ) ;
chip - > opened - - ;
if ( ! chip - > opened ) {
chip - > cur_rate = 0 ;
chip - > cur_format = 0 ;
}
mutex_unlock ( & opened_mutex ) ;
chip - > capture_substream = NULL ;
return 0 ;
}
static int atmel_ac97c_playback_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * hw_params )
{
struct atmel_ac97c * chip = snd_pcm_substream_chip ( substream ) ;
2017-05-09 19:18:36 +03:00
2009-02-05 15:11:00 +03:00
/* Set restrictions to params. */
mutex_lock ( & opened_mutex ) ;
chip - > cur_rate = params_rate ( hw_params ) ;
chip - > cur_format = params_format ( hw_params ) ;
mutex_unlock ( & opened_mutex ) ;
2019-12-09 12:48:35 +03:00
return 0 ;
2009-02-05 15:11:00 +03:00
}
static int atmel_ac97c_capture_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * hw_params )
{
struct atmel_ac97c * chip = snd_pcm_substream_chip ( substream ) ;
/* Set restrictions to params. */
mutex_lock ( & opened_mutex ) ;
chip - > cur_rate = params_rate ( hw_params ) ;
chip - > cur_format = params_format ( hw_params ) ;
mutex_unlock ( & opened_mutex ) ;
2019-12-09 12:48:35 +03:00
return 0 ;
2009-02-05 15:11:00 +03:00
}
static int atmel_ac97c_playback_prepare ( struct snd_pcm_substream * substream )
{
struct atmel_ac97c * chip = snd_pcm_substream_chip ( substream ) ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
2010-03-01 14:19:18 +03:00
int block_size = frames_to_bytes ( runtime , runtime - > period_size ) ;
2009-04-02 10:21:12 +04:00
unsigned long word = ac97c_readl ( chip , OCA ) ;
2009-02-05 15:11:00 +03:00
int retval ;
2010-03-01 14:19:18 +03:00
chip - > playback_period = 0 ;
2009-04-02 10:21:12 +04:00
word & = ~ ( AC97C_CH_MASK ( PCM_LEFT ) | AC97C_CH_MASK ( PCM_RIGHT ) ) ;
2009-02-05 15:11:00 +03:00
/* assign channels to AC97C channel A */
switch ( runtime - > channels ) {
case 1 :
word | = AC97C_CH_ASSIGN ( PCM_LEFT , A ) ;
break ;
case 2 :
word | = AC97C_CH_ASSIGN ( PCM_LEFT , A )
| AC97C_CH_ASSIGN ( PCM_RIGHT , A ) ;
break ;
default :
/* TODO: support more than two channels */
return - EINVAL ;
}
ac97c_writel ( chip , OCA , word ) ;
/* configure sample format and size */
2010-02-25 20:59:40 +03:00
word = ac97c_readl ( chip , CAMR ) ;
if ( chip - > opened < = 1 )
word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16 ;
else
word | = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16 ;
2009-02-05 15:11:00 +03:00
switch ( runtime - > format ) {
case SNDRV_PCM_FORMAT_S16_LE :
break ;
2020-07-09 14:17:48 +03:00
case SNDRV_PCM_FORMAT_S16_BE :
2009-02-05 15:11:00 +03:00
word & = ~ ( AC97C_CMR_CEM_LITTLE ) ;
break ;
2009-04-02 10:21:12 +04:00
default :
word = ac97c_readl ( chip , OCA ) ;
word & = ~ ( AC97C_CH_MASK ( PCM_LEFT ) | AC97C_CH_MASK ( PCM_RIGHT ) ) ;
ac97c_writel ( chip , OCA , word ) ;
return - EINVAL ;
2009-02-05 15:11:00 +03:00
}
2009-04-02 10:21:14 +04:00
/* Enable underrun interrupt on channel A */
word | = AC97C_CSR_UNRUN ;
2009-02-05 15:11:00 +03:00
ac97c_writel ( chip , CAMR , word ) ;
2009-04-02 10:21:14 +04:00
/* Enable channel A event interrupt */
word = ac97c_readl ( chip , IMR ) ;
word | = AC97C_SR_CAEVT ;
ac97c_writel ( chip , IER , word ) ;
2009-02-05 15:11:00 +03:00
/* set variable rate if needed */
if ( runtime - > rate ! = 48000 ) {
word = ac97c_readl ( chip , MR ) ;
word | = AC97C_MR_VRA ;
ac97c_writel ( chip , MR , word ) ;
} else {
word = ac97c_readl ( chip , MR ) ;
word & = ~ ( AC97C_MR_VRA ) ;
ac97c_writel ( chip , MR , word ) ;
}
retval = snd_ac97_set_rate ( chip - > ac97 , AC97_PCM_FRONT_DAC_RATE ,
runtime - > rate ) ;
if ( retval )
dev_dbg ( & chip - > pdev - > dev , " could not set rate %d Hz \n " ,
runtime - > rate ) ;
2017-05-09 19:18:36 +03:00
/* Initialize and start the PDC */
writel ( runtime - > dma_addr , chip - > regs + ATMEL_PDC_TPR ) ;
writel ( block_size / 2 , chip - > regs + ATMEL_PDC_TCR ) ;
writel ( runtime - > dma_addr + block_size , chip - > regs + ATMEL_PDC_TNPR ) ;
writel ( block_size / 2 , chip - > regs + ATMEL_PDC_TNCR ) ;
2009-02-05 15:11:00 +03:00
return retval ;
}
static int atmel_ac97c_capture_prepare ( struct snd_pcm_substream * substream )
{
struct atmel_ac97c * chip = snd_pcm_substream_chip ( substream ) ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
2010-03-01 14:19:18 +03:00
int block_size = frames_to_bytes ( runtime , runtime - > period_size ) ;
2009-04-02 10:21:12 +04:00
unsigned long word = ac97c_readl ( chip , ICA ) ;
2009-02-05 15:11:00 +03:00
int retval ;
2010-03-01 14:19:18 +03:00
chip - > capture_period = 0 ;
2009-04-02 10:21:12 +04:00
word & = ~ ( AC97C_CH_MASK ( PCM_LEFT ) | AC97C_CH_MASK ( PCM_RIGHT ) ) ;
2009-02-05 15:11:00 +03:00
/* assign channels to AC97C channel A */
switch ( runtime - > channels ) {
case 1 :
word | = AC97C_CH_ASSIGN ( PCM_LEFT , A ) ;
break ;
case 2 :
word | = AC97C_CH_ASSIGN ( PCM_LEFT , A )
| AC97C_CH_ASSIGN ( PCM_RIGHT , A ) ;
break ;
default :
/* TODO: support more than two channels */
return - EINVAL ;
}
ac97c_writel ( chip , ICA , word ) ;
/* configure sample format and size */
2010-02-25 20:59:40 +03:00
word = ac97c_readl ( chip , CAMR ) ;
if ( chip - > opened < = 1 )
word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16 ;
else
word | = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16 ;
2009-02-05 15:11:00 +03:00
switch ( runtime - > format ) {
case SNDRV_PCM_FORMAT_S16_LE :
break ;
2020-07-09 14:17:48 +03:00
case SNDRV_PCM_FORMAT_S16_BE :
2009-02-05 15:11:00 +03:00
word & = ~ ( AC97C_CMR_CEM_LITTLE ) ;
break ;
2009-04-02 10:21:12 +04:00
default :
word = ac97c_readl ( chip , ICA ) ;
word & = ~ ( AC97C_CH_MASK ( PCM_LEFT ) | AC97C_CH_MASK ( PCM_RIGHT ) ) ;
ac97c_writel ( chip , ICA , word ) ;
return - EINVAL ;
2009-02-05 15:11:00 +03:00
}
2009-04-02 10:21:14 +04:00
/* Enable overrun interrupt on channel A */
word | = AC97C_CSR_OVRUN ;
2009-02-05 15:11:00 +03:00
ac97c_writel ( chip , CAMR , word ) ;
2009-04-02 10:21:14 +04:00
/* Enable channel A event interrupt */
word = ac97c_readl ( chip , IMR ) ;
word | = AC97C_SR_CAEVT ;
ac97c_writel ( chip , IER , word ) ;
2009-02-05 15:11:00 +03:00
/* set variable rate if needed */
if ( runtime - > rate ! = 48000 ) {
word = ac97c_readl ( chip , MR ) ;
word | = AC97C_MR_VRA ;
ac97c_writel ( chip , MR , word ) ;
} else {
word = ac97c_readl ( chip , MR ) ;
word & = ~ ( AC97C_MR_VRA ) ;
ac97c_writel ( chip , MR , word ) ;
}
retval = snd_ac97_set_rate ( chip - > ac97 , AC97_PCM_LR_ADC_RATE ,
runtime - > rate ) ;
if ( retval )
dev_dbg ( & chip - > pdev - > dev , " could not set rate %d Hz \n " ,
runtime - > rate ) ;
2017-05-09 19:18:36 +03:00
/* Initialize and start the PDC */
writel ( runtime - > dma_addr , chip - > regs + ATMEL_PDC_RPR ) ;
writel ( block_size / 2 , chip - > regs + ATMEL_PDC_RCR ) ;
writel ( runtime - > dma_addr + block_size , chip - > regs + ATMEL_PDC_RNPR ) ;
writel ( block_size / 2 , chip - > regs + ATMEL_PDC_RNCR ) ;
2009-02-05 15:11:00 +03:00
return retval ;
}
static int
atmel_ac97c_playback_trigger ( struct snd_pcm_substream * substream , int cmd )
{
struct atmel_ac97c * chip = snd_pcm_substream_chip ( substream ) ;
2010-03-01 14:19:18 +03:00
unsigned long camr , ptcr = 0 ;
2009-02-05 15:11:00 +03:00
camr = ac97c_readl ( chip , CAMR ) ;
switch ( cmd ) {
2020-07-08 23:32:36 +03:00
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
case SNDRV_PCM_TRIGGER_RESUME :
2009-02-05 15:11:00 +03:00
case SNDRV_PCM_TRIGGER_START :
2017-05-09 19:18:36 +03:00
ptcr = ATMEL_PDC_TXTEN ;
2010-02-25 20:59:40 +03:00
camr | = AC97C_CMR_CENA | AC97C_CSR_ENDTX ;
2009-02-05 15:11:00 +03:00
break ;
2020-07-08 23:32:36 +03:00
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
case SNDRV_PCM_TRIGGER_SUSPEND :
2009-02-05 15:11:00 +03:00
case SNDRV_PCM_TRIGGER_STOP :
2017-05-09 19:18:36 +03:00
ptcr | = ATMEL_PDC_TXTDIS ;
2009-02-05 15:11:00 +03:00
if ( chip - > opened < = 1 )
camr & = ~ AC97C_CMR_CENA ;
break ;
default :
2017-05-09 19:18:36 +03:00
return - EINVAL ;
2009-02-05 15:11:00 +03:00
}
ac97c_writel ( chip , CAMR , camr ) ;
2017-05-09 19:18:36 +03:00
writel ( ptcr , chip - > regs + ATMEL_PDC_PTCR ) ;
return 0 ;
2009-02-05 15:11:00 +03:00
}
static int
atmel_ac97c_capture_trigger ( struct snd_pcm_substream * substream , int cmd )
{
struct atmel_ac97c * chip = snd_pcm_substream_chip ( substream ) ;
2010-03-01 14:19:18 +03:00
unsigned long camr , ptcr = 0 ;
2009-02-05 15:11:00 +03:00
camr = ac97c_readl ( chip , CAMR ) ;
2010-03-01 14:19:18 +03:00
ptcr = readl ( chip - > regs + ATMEL_PDC_PTSR ) ;
2009-02-05 15:11:00 +03:00
switch ( cmd ) {
2020-07-08 23:32:36 +03:00
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
case SNDRV_PCM_TRIGGER_RESUME :
2009-02-05 15:11:00 +03:00
case SNDRV_PCM_TRIGGER_START :
2017-05-09 19:18:36 +03:00
ptcr = ATMEL_PDC_RXTEN ;
2010-02-25 20:59:40 +03:00
camr | = AC97C_CMR_CENA | AC97C_CSR_ENDRX ;
2009-02-05 15:11:00 +03:00
break ;
2020-07-08 23:32:36 +03:00
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
case SNDRV_PCM_TRIGGER_SUSPEND :
2009-02-05 15:11:00 +03:00
case SNDRV_PCM_TRIGGER_STOP :
2017-05-09 19:18:36 +03:00
ptcr | = ATMEL_PDC_RXTDIS ;
2009-02-05 15:11:00 +03:00
if ( chip - > opened < = 1 )
camr & = ~ AC97C_CMR_CENA ;
break ;
default :
2017-05-09 19:18:36 +03:00
return - EINVAL ;
2009-02-05 15:11:00 +03:00
}
ac97c_writel ( chip , CAMR , camr ) ;
2017-05-09 19:18:36 +03:00
writel ( ptcr , chip - > regs + ATMEL_PDC_PTCR ) ;
return 0 ;
2009-02-05 15:11:00 +03:00
}
static snd_pcm_uframes_t
atmel_ac97c_playback_pointer ( struct snd_pcm_substream * substream )
{
struct atmel_ac97c * chip = snd_pcm_substream_chip ( substream ) ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
snd_pcm_uframes_t frames ;
unsigned long bytes ;
2017-05-09 19:18:36 +03:00
bytes = readl ( chip - > regs + ATMEL_PDC_TPR ) ;
2009-02-05 15:11:00 +03:00
bytes - = runtime - > dma_addr ;
frames = bytes_to_frames ( runtime , bytes ) ;
if ( frames > = runtime - > buffer_size )
frames - = runtime - > buffer_size ;
return frames ;
}
static snd_pcm_uframes_t
atmel_ac97c_capture_pointer ( struct snd_pcm_substream * substream )
{
struct atmel_ac97c * chip = snd_pcm_substream_chip ( substream ) ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
snd_pcm_uframes_t frames ;
unsigned long bytes ;
2017-05-09 19:18:36 +03:00
bytes = readl ( chip - > regs + ATMEL_PDC_RPR ) ;
2009-02-05 15:11:00 +03:00
bytes - = runtime - > dma_addr ;
frames = bytes_to_frames ( runtime , bytes ) ;
if ( frames > = runtime - > buffer_size )
frames - = runtime - > buffer_size ;
return frames ;
}
2017-08-18 10:45:11 +03:00
static const struct snd_pcm_ops atmel_ac97_playback_ops = {
2009-02-05 15:11:00 +03:00
. open = atmel_ac97c_playback_open ,
. close = atmel_ac97c_playback_close ,
. hw_params = atmel_ac97c_playback_hw_params ,
. prepare = atmel_ac97c_playback_prepare ,
. trigger = atmel_ac97c_playback_trigger ,
. pointer = atmel_ac97c_playback_pointer ,
} ;
2017-08-18 10:45:11 +03:00
static const struct snd_pcm_ops atmel_ac97_capture_ops = {
2009-02-05 15:11:00 +03:00
. open = atmel_ac97c_capture_open ,
. close = atmel_ac97c_capture_close ,
. hw_params = atmel_ac97c_capture_hw_params ,
. prepare = atmel_ac97c_capture_prepare ,
. trigger = atmel_ac97c_capture_trigger ,
. pointer = atmel_ac97c_capture_pointer ,
} ;
2009-04-02 10:21:14 +04:00
static irqreturn_t atmel_ac97c_interrupt ( int irq , void * dev )
{
struct atmel_ac97c * chip = ( struct atmel_ac97c * ) dev ;
irqreturn_t retval = IRQ_NONE ;
u32 sr = ac97c_readl ( chip , SR ) ;
u32 casr = ac97c_readl ( chip , CASR ) ;
u32 cosr = ac97c_readl ( chip , COSR ) ;
2010-03-01 14:19:18 +03:00
u32 camr = ac97c_readl ( chip , CAMR ) ;
2009-04-02 10:21:14 +04:00
if ( sr & AC97C_SR_CAEVT ) {
2010-03-01 14:19:18 +03:00
struct snd_pcm_runtime * runtime ;
int offset , next_period , block_size ;
2010-06-08 10:57:13 +04:00
dev_dbg ( & chip - > pdev - > dev , " channel A event%s%s%s%s%s%s \n " ,
2020-09-03 00:21:29 +03:00
( casr & AC97C_CSR_OVRUN ) ? " OVRUN " : " " ,
( casr & AC97C_CSR_RXRDY ) ? " RXRDY " : " " ,
( casr & AC97C_CSR_UNRUN ) ? " UNRUN " : " " ,
( casr & AC97C_CSR_TXEMPTY ) ? " TXEMPTY " : " " ,
( casr & AC97C_CSR_TXRDY ) ? " TXRDY " : " " ,
! casr ? " NONE " : " " ) ;
2017-05-09 19:18:36 +03:00
if ( ( casr & camr ) & AC97C_CSR_ENDTX ) {
runtime = chip - > playback_substream - > runtime ;
block_size = frames_to_bytes ( runtime , runtime - > period_size ) ;
chip - > playback_period + + ;
if ( chip - > playback_period = = runtime - > periods )
chip - > playback_period = 0 ;
next_period = chip - > playback_period + 1 ;
if ( next_period = = runtime - > periods )
next_period = 0 ;
offset = block_size * next_period ;
writel ( runtime - > dma_addr + offset , chip - > regs + ATMEL_PDC_TNPR ) ;
writel ( block_size / 2 , chip - > regs + ATMEL_PDC_TNCR ) ;
snd_pcm_period_elapsed ( chip - > playback_substream ) ;
}
if ( ( casr & camr ) & AC97C_CSR_ENDRX ) {
runtime = chip - > capture_substream - > runtime ;
block_size = frames_to_bytes ( runtime , runtime - > period_size ) ;
chip - > capture_period + + ;
if ( chip - > capture_period = = runtime - > periods )
chip - > capture_period = 0 ;
next_period = chip - > capture_period + 1 ;
if ( next_period = = runtime - > periods )
next_period = 0 ;
offset = block_size * next_period ;
writel ( runtime - > dma_addr + offset , chip - > regs + ATMEL_PDC_RNPR ) ;
writel ( block_size / 2 , chip - > regs + ATMEL_PDC_RNCR ) ;
snd_pcm_period_elapsed ( chip - > capture_substream ) ;
2010-03-01 14:19:18 +03:00
}
2009-04-02 10:21:14 +04:00
retval = IRQ_HANDLED ;
}
if ( sr & AC97C_SR_COEVT ) {
dev_info ( & chip - > pdev - > dev , " codec channel event%s%s%s%s%s \n " ,
2020-09-03 00:21:29 +03:00
( cosr & AC97C_CSR_OVRUN ) ? " OVRUN " : " " ,
( cosr & AC97C_CSR_RXRDY ) ? " RXRDY " : " " ,
( cosr & AC97C_CSR_TXEMPTY ) ? " TXEMPTY " : " " ,
( cosr & AC97C_CSR_TXRDY ) ? " TXRDY " : " " ,
! cosr ? " NONE " : " " ) ;
2009-04-02 10:21:14 +04:00
retval = IRQ_HANDLED ;
}
if ( retval = = IRQ_NONE ) {
dev_err ( & chip - > pdev - > dev , " spurious interrupt sr 0x%08x "
" casr 0x%08x cosr 0x%08x \n " , sr , casr , cosr ) ;
}
return retval ;
}
2017-08-23 15:09:26 +03:00
static const struct ac97_pcm at91_ac97_pcm_defs [ ] = {
2010-03-01 14:19:18 +03:00
/* Playback */
{
. exclusive = 1 ,
. r = { {
. slots = ( ( 1 < < AC97_SLOT_PCM_LEFT )
| ( 1 < < AC97_SLOT_PCM_RIGHT ) ) ,
} } ,
} ,
/* PCM in */
{
. stream = 1 ,
. exclusive = 1 ,
. r = { {
. slots = ( ( 1 < < AC97_SLOT_PCM_LEFT )
| ( 1 < < AC97_SLOT_PCM_RIGHT ) ) ,
} }
} ,
/* Mic in */
{
. stream = 1 ,
. exclusive = 1 ,
. r = { {
. slots = ( 1 < < AC97_SLOT_MIC ) ,
} }
} ,
} ;
2012-12-06 21:35:13 +04:00
static int atmel_ac97c_pcm_new ( struct atmel_ac97c * chip )
2009-02-05 15:11:00 +03:00
{
struct snd_pcm * pcm ;
struct snd_pcm_hardware hw = atmel_ac97c_hw ;
2017-05-09 19:18:36 +03:00
int retval ;
2009-02-05 15:11:00 +03:00
2017-05-09 19:18:36 +03:00
retval = snd_ac97_pcm_assign ( chip - > ac97_bus ,
ARRAY_SIZE ( at91_ac97_pcm_defs ) ,
at91_ac97_pcm_defs ) ;
if ( retval )
return retval ;
2009-02-05 15:11:00 +03:00
2017-05-09 19:18:36 +03:00
retval = snd_pcm_new ( chip - > card , chip - > card - > shortname , 0 , 1 , 1 , & pcm ) ;
2009-02-05 15:11:00 +03:00
if ( retval )
return retval ;
2017-05-09 19:18:36 +03:00
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_CAPTURE , & atmel_ac97_capture_ops ) ;
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_PLAYBACK , & atmel_ac97_playback_ops ) ;
2009-02-05 15:11:00 +03:00
2019-12-09 12:48:35 +03:00
snd_pcm_set_managed_buffer_all ( pcm , SNDRV_DMA_TYPE_DEV ,
2009-02-05 15:11:00 +03:00
& chip - > pdev - > dev , hw . periods_min * hw . period_bytes_min ,
hw . buffer_bytes_max ) ;
pcm - > private_data = chip ;
pcm - > info_flags = 0 ;
strcpy ( pcm - > name , chip - > card - > shortname ) ;
chip - > pcm = pcm ;
return 0 ;
}
static int atmel_ac97c_mixer_new ( struct atmel_ac97c * chip )
{
struct snd_ac97_template template ;
memset ( & template , 0 , sizeof ( template ) ) ;
template . private_data = chip ;
return snd_ac97_mixer ( chip - > ac97_bus , & template , & chip - > ac97 ) ;
}
static void atmel_ac97c_write ( struct snd_ac97 * ac97 , unsigned short reg ,
unsigned short val )
{
struct atmel_ac97c * chip = get_chip ( ac97 ) ;
unsigned long word ;
int timeout = 40 ;
word = ( reg & 0x7f ) < < 16 | val ;
do {
if ( ac97c_readl ( chip , COSR ) & AC97C_CSR_TXRDY ) {
ac97c_writel ( chip , COTHR , word ) ;
return ;
}
udelay ( 1 ) ;
} while ( - - timeout ) ;
dev_dbg ( & chip - > pdev - > dev , " codec write timeout \n " ) ;
}
static unsigned short atmel_ac97c_read ( struct snd_ac97 * ac97 ,
unsigned short reg )
{
struct atmel_ac97c * chip = get_chip ( ac97 ) ;
unsigned long word ;
int timeout = 40 ;
int write = 10 ;
word = ( 0x80 | ( reg & 0x7f ) ) < < 16 ;
if ( ( ac97c_readl ( chip , COSR ) & AC97C_CSR_RXRDY ) ! = 0 )
ac97c_readl ( chip , CORHR ) ;
retry_write :
timeout = 40 ;
do {
if ( ( ac97c_readl ( chip , COSR ) & AC97C_CSR_TXRDY ) ! = 0 ) {
ac97c_writel ( chip , COTHR , word ) ;
goto read_reg ;
}
udelay ( 10 ) ;
} while ( - - timeout ) ;
if ( ! - - write )
goto timed_out ;
goto retry_write ;
read_reg :
do {
if ( ( ac97c_readl ( chip , COSR ) & AC97C_CSR_RXRDY ) ! = 0 ) {
unsigned short val = ac97c_readl ( chip , CORHR ) ;
return val ;
}
udelay ( 10 ) ;
} while ( - - timeout ) ;
if ( ! - - write )
goto timed_out ;
goto retry_write ;
timed_out :
dev_dbg ( & chip - > pdev - > dev , " codec read timeout \n " ) ;
return 0xffff ;
}
static void atmel_ac97c_reset ( struct atmel_ac97c * chip )
{
2009-04-02 10:21:15 +04:00
ac97c_writel ( chip , MR , 0 ) ;
ac97c_writel ( chip , MR , AC97C_MR_ENA ) ;
ac97c_writel ( chip , CAMR , 0 ) ;
ac97c_writel ( chip , COMR , 0 ) ;
2009-02-05 15:11:00 +03:00
2017-09-01 19:15:06 +03:00
if ( ! IS_ERR ( chip - > reset_pin ) ) {
gpiod_set_value ( chip - > reset_pin , 0 ) ;
2009-02-05 15:11:00 +03:00
/* AC97 v2.2 specifications says minimum 1 us. */
2009-04-02 10:21:15 +04:00
udelay ( 2 ) ;
2017-09-01 19:15:06 +03:00
gpiod_set_value ( chip - > reset_pin , 1 ) ;
2011-12-19 13:57:52 +04:00
} else {
ac97c_writel ( chip , MR , AC97C_MR_WRST | AC97C_MR_ENA ) ;
udelay ( 2 ) ;
ac97c_writel ( chip , MR , AC97C_MR_ENA ) ;
2009-02-05 15:11:00 +03:00
}
}
2014-12-29 15:08:39 +03:00
static const struct of_device_id atmel_ac97c_dt_ids [ ] = {
{ . compatible = " atmel,at91sam9263-ac97c " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , atmel_ac97c_dt_ids ) ;
2012-12-06 21:35:13 +04:00
static int atmel_ac97c_probe ( struct platform_device * pdev )
2009-02-05 15:11:00 +03:00
{
2017-09-01 19:15:06 +03:00
struct device * dev = & pdev - > dev ;
2009-02-05 15:11:00 +03:00
struct snd_card * card ;
struct atmel_ac97c * chip ;
struct resource * regs ;
struct clk * pclk ;
2020-01-03 11:16:41 +03:00
static const struct snd_ac97_bus_ops ops = {
2009-02-05 15:11:00 +03:00
. write = atmel_ac97c_write ,
. read = atmel_ac97c_read ,
} ;
int retval ;
2009-04-02 10:21:14 +04:00
int irq ;
2009-02-05 15:11:00 +03:00
regs = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! regs ) {
dev_dbg ( & pdev - > dev , " no memory resource \n " ) ;
return - ENXIO ;
}
2009-04-02 10:21:14 +04:00
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
2017-07-01 01:34:23 +03:00
dev_dbg ( & pdev - > dev , " could not get irq: %d \n " , irq ) ;
return irq ;
2009-04-02 10:21:14 +04:00
}
2017-05-09 19:18:36 +03:00
pclk = clk_get ( & pdev - > dev , " ac97_clk " ) ;
2009-02-05 15:11:00 +03:00
if ( IS_ERR ( pclk ) ) {
dev_dbg ( & pdev - > dev , " no peripheral clock \n " ) ;
return PTR_ERR ( pclk ) ;
}
2017-07-25 13:15:37 +03:00
retval = clk_prepare_enable ( pclk ) ;
if ( retval )
2017-08-31 07:40:42 +03:00
goto err_prepare_enable ;
2009-02-05 15:11:00 +03:00
2014-01-29 17:26:22 +04:00
retval = snd_card_new ( & pdev - > dev , SNDRV_DEFAULT_IDX1 ,
SNDRV_DEFAULT_STR1 , THIS_MODULE ,
sizeof ( struct atmel_ac97c ) , & card ) ;
2009-02-05 15:11:00 +03:00
if ( retval ) {
dev_dbg ( & pdev - > dev , " could not create sound card device \n " ) ;
goto err_snd_card_new ;
}
chip = get_chip ( card ) ;
2009-04-02 10:21:14 +04:00
retval = request_irq ( irq , atmel_ac97c_interrupt , 0 , " AC97C " , chip ) ;
if ( retval ) {
dev_dbg ( & pdev - > dev , " unable to request irq %d \n " , irq ) ;
goto err_request_irq ;
}
chip - > irq = irq ;
2009-02-05 15:11:00 +03:00
spin_lock_init ( & chip - > lock ) ;
strcpy ( card - > driver , " Atmel AC97C " ) ;
strcpy ( card - > shortname , " Atmel AC97C " ) ;
sprintf ( card - > longname , " Atmel AC97 controller " ) ;
chip - > card = card ;
chip - > pclk = pclk ;
chip - > pdev = pdev ;
2011-06-09 20:13:32 +04:00
chip - > regs = ioremap ( regs - > start , resource_size ( regs ) ) ;
2009-02-05 15:11:00 +03:00
if ( ! chip - > regs ) {
dev_dbg ( & pdev - > dev , " could not remap register memory \n " ) ;
2012-08-19 11:02:57 +04:00
retval = - ENOMEM ;
2009-02-05 15:11:00 +03:00
goto err_ioremap ;
}
2017-09-01 19:15:06 +03:00
chip - > reset_pin = devm_gpiod_get_index ( dev , " ac97 " , 2 , GPIOD_OUT_HIGH ) ;
if ( IS_ERR ( chip - > reset_pin ) )
dev_dbg ( dev , " reset pin not available \n " ) ;
2009-02-05 15:11:00 +03:00
2009-04-02 10:21:15 +04:00
atmel_ac97c_reset ( chip ) ;
2009-04-02 10:21:14 +04:00
/* Enable overrun interrupt from codec channel */
ac97c_writel ( chip , COMR , AC97C_CSR_OVRUN ) ;
ac97c_writel ( chip , IER , ac97c_readl ( chip , IMR ) | AC97C_SR_COEVT ) ;
2009-02-05 15:11:00 +03:00
retval = snd_ac97_bus ( card , 0 , & ops , chip , & chip - > ac97_bus ) ;
if ( retval ) {
dev_dbg ( & pdev - > dev , " could not register on ac97 bus \n " ) ;
goto err_ac97_bus ;
}
retval = atmel_ac97c_mixer_new ( chip ) ;
if ( retval ) {
dev_dbg ( & pdev - > dev , " could not register ac97 mixer \n " ) ;
goto err_ac97_bus ;
}
retval = atmel_ac97c_pcm_new ( chip ) ;
if ( retval ) {
dev_dbg ( & pdev - > dev , " could not register ac97 pcm device \n " ) ;
2017-05-09 19:18:36 +03:00
goto err_ac97_bus ;
2009-02-05 15:11:00 +03:00
}
retval = snd_card_register ( card ) ;
if ( retval ) {
dev_dbg ( & pdev - > dev , " could not register sound card \n " ) ;
2017-05-09 19:18:36 +03:00
goto err_ac97_bus ;
2009-02-05 15:11:00 +03:00
}
platform_set_drvdata ( pdev , card ) ;
2010-03-01 14:19:18 +03:00
dev_info ( & pdev - > dev , " Atmel AC97 controller at 0x%p, irq = %d \n " ,
chip - > regs , irq ) ;
2009-02-05 15:11:00 +03:00
return 0 ;
err_ac97_bus :
iounmap ( chip - > regs ) ;
err_ioremap :
2009-04-02 10:21:14 +04:00
free_irq ( irq , chip ) ;
err_request_irq :
2009-02-05 15:11:00 +03:00
snd_card_free ( card ) ;
err_snd_card_new :
2014-12-05 22:10:06 +03:00
clk_disable_unprepare ( pclk ) ;
2017-08-31 07:40:42 +03:00
err_prepare_enable :
2009-02-05 15:11:00 +03:00
clk_put ( pclk ) ;
return retval ;
}
2012-08-09 17:47:15 +04:00
# ifdef CONFIG_PM_SLEEP
2012-07-02 13:22:40 +04:00
static int atmel_ac97c_suspend ( struct device * pdev )
2009-02-05 15:11:00 +03:00
{
2012-07-02 13:22:40 +04:00
struct snd_card * card = dev_get_drvdata ( pdev ) ;
2009-02-05 15:11:00 +03:00
struct atmel_ac97c * chip = card - > private_data ;
2014-12-05 22:10:06 +03:00
clk_disable_unprepare ( chip - > pclk ) ;
2009-02-05 15:11:00 +03:00
return 0 ;
}
2012-07-02 13:22:40 +04:00
static int atmel_ac97c_resume ( struct device * pdev )
2009-02-05 15:11:00 +03:00
{
2012-07-02 13:22:40 +04:00
struct snd_card * card = dev_get_drvdata ( pdev ) ;
2009-02-05 15:11:00 +03:00
struct atmel_ac97c * chip = card - > private_data ;
2017-07-25 13:15:37 +03:00
int ret = clk_prepare_enable ( chip - > pclk ) ;
2009-02-05 15:11:00 +03:00
2017-07-25 13:15:37 +03:00
return ret ;
2009-02-05 15:11:00 +03:00
}
2012-07-02 13:22:40 +04:00
static SIMPLE_DEV_PM_OPS ( atmel_ac97c_pm , atmel_ac97c_suspend , atmel_ac97c_resume ) ;
# define ATMEL_AC97C_PM_OPS &atmel_ac97c_pm
2009-02-05 15:11:00 +03:00
# else
2012-07-02 13:22:40 +04:00
# define ATMEL_AC97C_PM_OPS NULL
2009-02-05 15:11:00 +03:00
# endif
2012-12-06 21:35:13 +04:00
static int atmel_ac97c_remove ( struct platform_device * pdev )
2009-02-05 15:11:00 +03:00
{
struct snd_card * card = platform_get_drvdata ( pdev ) ;
struct atmel_ac97c * chip = get_chip ( card ) ;
2009-04-02 10:21:16 +04:00
ac97c_writel ( chip , CAMR , 0 ) ;
ac97c_writel ( chip , COMR , 0 ) ;
ac97c_writel ( chip , MR , 0 ) ;
2014-12-05 22:10:06 +03:00
clk_disable_unprepare ( chip - > pclk ) ;
2009-02-05 15:11:00 +03:00
clk_put ( chip - > pclk ) ;
iounmap ( chip - > regs ) ;
2009-04-02 10:21:14 +04:00
free_irq ( chip - > irq , chip ) ;
2009-02-05 15:11:00 +03:00
snd_card_free ( card ) ;
return 0 ;
}
static struct platform_driver atmel_ac97c_driver = {
2014-04-15 21:38:56 +04:00
. probe = atmel_ac97c_probe ,
2012-12-06 21:35:13 +04:00
. remove = atmel_ac97c_remove ,
2009-02-05 15:11:00 +03:00
. driver = {
. name = " atmel_ac97c " ,
2012-07-02 13:22:40 +04:00
. pm = ATMEL_AC97C_PM_OPS ,
2017-09-01 19:15:06 +03:00
. of_match_table = atmel_ac97c_dt_ids ,
2009-02-05 15:11:00 +03:00
} ,
} ;
2014-04-15 21:38:56 +04:00
module_platform_driver ( atmel_ac97c_driver ) ;
2009-02-05 15:11:00 +03:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " Driver for Atmel AC97 controller " ) ;
2011-06-28 18:59:14 +04:00
MODULE_AUTHOR ( " Hans-Christian Egtvedt <egtvedt@samfundet.no> " ) ;