2012-10-11 06:54:02 +04:00
/*
* wm_adsp . c - - Wolfson ADSP support
*
* Copyright 2012 Wolfson Microelectronics plc
*
* Author : Mark Brown < broonie @ opensource . wolfsonmicro . 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/moduleparam.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/firmware.h>
# include <linux/pm.h>
# include <linux/pm_runtime.h>
# include <linux/regmap.h>
2012-11-28 21:20:32 +04:00
# include <linux/regulator/consumer.h>
2012-10-11 06:54:02 +04:00
# include <linux/slab.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/soc.h>
# include <sound/jack.h>
# include <sound/initval.h>
# include <sound/tlv.h>
# include <linux/mfd/arizona/registers.h>
# include "wm_adsp.h"
# define adsp_crit(_dsp, fmt, ...) \
dev_crit ( _dsp - > dev , " DSP%d: " fmt , _dsp - > num , # # __VA_ARGS__ )
# define adsp_err(_dsp, fmt, ...) \
dev_err ( _dsp - > dev , " DSP%d: " fmt , _dsp - > num , # # __VA_ARGS__ )
# define adsp_warn(_dsp, fmt, ...) \
dev_warn ( _dsp - > dev , " DSP%d: " fmt , _dsp - > num , # # __VA_ARGS__ )
# define adsp_info(_dsp, fmt, ...) \
dev_info ( _dsp - > dev , " DSP%d: " fmt , _dsp - > num , # # __VA_ARGS__ )
# define adsp_dbg(_dsp, fmt, ...) \
dev_dbg ( _dsp - > dev , " DSP%d: " fmt , _dsp - > num , # # __VA_ARGS__ )
# define ADSP1_CONTROL_1 0x00
# define ADSP1_CONTROL_2 0x02
# define ADSP1_CONTROL_3 0x03
# define ADSP1_CONTROL_4 0x04
# define ADSP1_CONTROL_5 0x06
# define ADSP1_CONTROL_6 0x07
# define ADSP1_CONTROL_7 0x08
# define ADSP1_CONTROL_8 0x09
# define ADSP1_CONTROL_9 0x0A
# define ADSP1_CONTROL_10 0x0B
# define ADSP1_CONTROL_11 0x0C
# define ADSP1_CONTROL_12 0x0D
# define ADSP1_CONTROL_13 0x0F
# define ADSP1_CONTROL_14 0x10
# define ADSP1_CONTROL_15 0x11
# define ADSP1_CONTROL_16 0x12
# define ADSP1_CONTROL_17 0x13
# define ADSP1_CONTROL_18 0x14
# define ADSP1_CONTROL_19 0x16
# define ADSP1_CONTROL_20 0x17
# define ADSP1_CONTROL_21 0x18
# define ADSP1_CONTROL_22 0x1A
# define ADSP1_CONTROL_23 0x1B
# define ADSP1_CONTROL_24 0x1C
# define ADSP1_CONTROL_25 0x1E
# define ADSP1_CONTROL_26 0x20
# define ADSP1_CONTROL_27 0x21
# define ADSP1_CONTROL_28 0x22
# define ADSP1_CONTROL_29 0x23
# define ADSP1_CONTROL_30 0x24
# define ADSP1_CONTROL_31 0x26
/*
* ADSP1 Control 19
*/
# define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
# define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
# define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
/*
* ADSP1 Control 30
*/
# define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */
# define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */
# define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */
# define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */
# define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
# define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
# define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
# define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
# define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
# define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
# define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
# define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
# define ADSP1_START 0x0001 /* DSP1_START */
# define ADSP1_START_MASK 0x0001 /* DSP1_START */
# define ADSP1_START_SHIFT 0 /* DSP1_START */
# define ADSP1_START_WIDTH 1 /* DSP1_START */
2012-11-28 21:20:32 +04:00
# define ADSP2_CONTROL 0
# define ADSP2_CLOCKING 1
# define ADSP2_STATUS1 4
2012-10-11 06:54:02 +04:00
/*
* ADSP2 Control
*/
# define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */
# define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */
# define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */
# define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */
# define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
# define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
# define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
# define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
# define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
# define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
# define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
# define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
# define ADSP2_START 0x0001 /* DSP1_START */
# define ADSP2_START_MASK 0x0001 /* DSP1_START */
# define ADSP2_START_SHIFT 0 /* DSP1_START */
# define ADSP2_START_WIDTH 1 /* DSP1_START */
2012-11-28 21:20:32 +04:00
/*
* ADSP2 clocking
*/
# define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
# define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
# define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
2012-10-11 06:54:02 +04:00
/*
* ADSP2 Status 1
*/
# define ADSP2_RAM_RDY 0x0001
# define ADSP2_RAM_RDY_MASK 0x0001
# define ADSP2_RAM_RDY_SHIFT 0
# define ADSP2_RAM_RDY_WIDTH 1
static struct wm_adsp_region const * wm_adsp_find_region ( struct wm_adsp * dsp ,
int type )
{
int i ;
for ( i = 0 ; i < dsp - > num_mems ; i + + )
if ( dsp - > mem [ i ] . type = = type )
return & dsp - > mem [ i ] ;
return NULL ;
}
static int wm_adsp_load ( struct wm_adsp * dsp )
{
const struct firmware * firmware ;
struct regmap * regmap = dsp - > regmap ;
unsigned int pos = 0 ;
const struct wmfw_header * header ;
const struct wmfw_adsp1_sizes * adsp1_sizes ;
const struct wmfw_adsp2_sizes * adsp2_sizes ;
const struct wmfw_footer * footer ;
const struct wmfw_region * region ;
const struct wm_adsp_region * mem ;
const char * region_name ;
char * file , * text ;
unsigned int reg ;
int regions = 0 ;
int ret , offset , type , sizes ;
file = kzalloc ( PAGE_SIZE , GFP_KERNEL ) ;
if ( file = = NULL )
return - ENOMEM ;
snprintf ( file , PAGE_SIZE , " %s-dsp%d.wmfw " , dsp - > part , dsp - > num ) ;
file [ PAGE_SIZE - 1 ] = ' \0 ' ;
ret = request_firmware ( & firmware , file , dsp - > dev ) ;
if ( ret ! = 0 ) {
adsp_err ( dsp , " Failed to request '%s' \n " , file ) ;
goto out ;
}
ret = - EINVAL ;
pos = sizeof ( * header ) + sizeof ( * adsp1_sizes ) + sizeof ( * footer ) ;
if ( pos > = firmware - > size ) {
adsp_err ( dsp , " %s: file too short, %zu bytes \n " ,
file , firmware - > size ) ;
goto out_fw ;
}
header = ( void * ) & firmware - > data [ 0 ] ;
if ( memcmp ( & header - > magic [ 0 ] , " WMFW " , 4 ) ! = 0 ) {
adsp_err ( dsp , " %s: invalid magic \n " , file ) ;
goto out_fw ;
}
if ( header - > ver ! = 0 ) {
adsp_err ( dsp , " %s: unknown file format %d \n " ,
file , header - > ver ) ;
goto out_fw ;
}
if ( header - > core ! = dsp - > type ) {
adsp_err ( dsp , " %s: invalid core %d != %d \n " ,
file , header - > core , dsp - > type ) ;
goto out_fw ;
}
switch ( dsp - > type ) {
case WMFW_ADSP1 :
pos = sizeof ( * header ) + sizeof ( * adsp1_sizes ) + sizeof ( * footer ) ;
adsp1_sizes = ( void * ) & ( header [ 1 ] ) ;
footer = ( void * ) & ( adsp1_sizes [ 1 ] ) ;
sizes = sizeof ( * adsp1_sizes ) ;
adsp_dbg ( dsp , " %s: %d DM, %d PM, %d ZM \n " ,
file , le32_to_cpu ( adsp1_sizes - > dm ) ,
le32_to_cpu ( adsp1_sizes - > pm ) ,
le32_to_cpu ( adsp1_sizes - > zm ) ) ;
break ;
case WMFW_ADSP2 :
pos = sizeof ( * header ) + sizeof ( * adsp2_sizes ) + sizeof ( * footer ) ;
adsp2_sizes = ( void * ) & ( header [ 1 ] ) ;
footer = ( void * ) & ( adsp2_sizes [ 1 ] ) ;
sizes = sizeof ( * adsp2_sizes ) ;
adsp_dbg ( dsp , " %s: %d XM, %d YM %d PM, %d ZM \n " ,
file , le32_to_cpu ( adsp2_sizes - > xm ) ,
le32_to_cpu ( adsp2_sizes - > ym ) ,
le32_to_cpu ( adsp2_sizes - > pm ) ,
le32_to_cpu ( adsp2_sizes - > zm ) ) ;
break ;
default :
BUG_ON ( NULL = = " Unknown DSP type " ) ;
goto out_fw ;
}
if ( le32_to_cpu ( header - > len ) ! = sizeof ( * header ) +
sizes + sizeof ( * footer ) ) {
adsp_err ( dsp , " %s: unexpected header length %d \n " ,
file , le32_to_cpu ( header - > len ) ) ;
goto out_fw ;
}
adsp_dbg ( dsp , " %s: timestamp %llu \n " , file ,
le64_to_cpu ( footer - > timestamp ) ) ;
while ( pos < firmware - > size & &
pos - firmware - > size > sizeof ( * region ) ) {
region = ( void * ) & ( firmware - > data [ pos ] ) ;
region_name = " Unknown " ;
reg = 0 ;
text = NULL ;
offset = le32_to_cpu ( region - > offset ) & 0xffffff ;
type = be32_to_cpu ( region - > type ) & 0xff ;
mem = wm_adsp_find_region ( dsp , type ) ;
switch ( type ) {
case WMFW_NAME_TEXT :
region_name = " Firmware name " ;
text = kzalloc ( le32_to_cpu ( region - > len ) + 1 ,
GFP_KERNEL ) ;
break ;
case WMFW_INFO_TEXT :
region_name = " Information " ;
text = kzalloc ( le32_to_cpu ( region - > len ) + 1 ,
GFP_KERNEL ) ;
break ;
case WMFW_ABSOLUTE :
region_name = " Absolute " ;
reg = offset ;
break ;
case WMFW_ADSP1_PM :
BUG_ON ( ! mem ) ;
region_name = " PM " ;
reg = mem - > base + ( offset * 3 ) ;
break ;
case WMFW_ADSP1_DM :
BUG_ON ( ! mem ) ;
region_name = " DM " ;
reg = mem - > base + ( offset * 2 ) ;
break ;
case WMFW_ADSP2_XM :
BUG_ON ( ! mem ) ;
region_name = " XM " ;
reg = mem - > base + ( offset * 2 ) ;
break ;
case WMFW_ADSP2_YM :
BUG_ON ( ! mem ) ;
region_name = " YM " ;
reg = mem - > base + ( offset * 2 ) ;
break ;
case WMFW_ADSP1_ZM :
BUG_ON ( ! mem ) ;
region_name = " ZM " ;
reg = mem - > base + ( offset * 2 ) ;
break ;
default :
adsp_warn ( dsp ,
" %s.%d: Unknown region type %x at %d(%x) \n " ,
file , regions , type , pos , pos ) ;
break ;
}
adsp_dbg ( dsp , " %s.%d: %d bytes at %d in %s \n " , file ,
regions , le32_to_cpu ( region - > len ) , offset ,
region_name ) ;
if ( text ) {
memcpy ( text , region - > data , le32_to_cpu ( region - > len ) ) ;
adsp_info ( dsp , " %s: %s \n " , file , text ) ;
kfree ( text ) ;
}
if ( reg ) {
ret = regmap_raw_write ( regmap , reg , region - > data ,
le32_to_cpu ( region - > len ) ) ;
if ( ret ! = 0 ) {
adsp_err ( dsp ,
" %s.%d: Failed to write %d bytes at %d in %s: %d \n " ,
file , regions ,
le32_to_cpu ( region - > len ) , offset ,
region_name , ret ) ;
goto out_fw ;
}
}
pos + = le32_to_cpu ( region - > len ) + sizeof ( * region ) ;
regions + + ;
}
if ( pos > firmware - > size )
adsp_warn ( dsp , " %s.%d: %zu bytes at end of file \n " ,
file , regions , pos - firmware - > size ) ;
out_fw :
release_firmware ( firmware ) ;
out :
kfree ( file ) ;
return ret ;
}
static int wm_adsp_load_coeff ( struct wm_adsp * dsp )
{
struct regmap * regmap = dsp - > regmap ;
struct wmfw_coeff_hdr * hdr ;
struct wmfw_coeff_item * blk ;
const struct firmware * firmware ;
const char * region_name ;
int ret , pos , blocks , type , offset , reg ;
char * file ;
file = kzalloc ( PAGE_SIZE , GFP_KERNEL ) ;
if ( file = = NULL )
return - ENOMEM ;
snprintf ( file , PAGE_SIZE , " %s-dsp%d.bin " , dsp - > part , dsp - > num ) ;
file [ PAGE_SIZE - 1 ] = ' \0 ' ;
ret = request_firmware ( & firmware , file , dsp - > dev ) ;
if ( ret ! = 0 ) {
adsp_warn ( dsp , " Failed to request '%s' \n " , file ) ;
ret = 0 ;
goto out ;
}
ret = - EINVAL ;
if ( sizeof ( * hdr ) > = firmware - > size ) {
adsp_err ( dsp , " %s: file too short, %zu bytes \n " ,
file , firmware - > size ) ;
goto out_fw ;
}
hdr = ( void * ) & firmware - > data [ 0 ] ;
if ( memcmp ( hdr - > magic , " WMDR " , 4 ) ! = 0 ) {
adsp_err ( dsp , " %s: invalid magic \n " , file ) ;
return - EINVAL ;
}
adsp_dbg ( dsp , " %s: v%d.%d.%d \n " , file ,
( le32_to_cpu ( hdr - > ver ) > > 16 ) & 0xff ,
( le32_to_cpu ( hdr - > ver ) > > 8 ) & 0xff ,
le32_to_cpu ( hdr - > ver ) & 0xff ) ;
pos = le32_to_cpu ( hdr - > len ) ;
blocks = 0 ;
while ( pos < firmware - > size & &
pos - firmware - > size > sizeof ( * blk ) ) {
blk = ( void * ) ( & firmware - > data [ pos ] ) ;
type = be32_to_cpu ( blk - > type ) & 0xff ;
offset = le32_to_cpu ( blk - > offset ) & 0xffffff ;
adsp_dbg ( dsp , " %s.%d: %x v%d.%d.%d \n " ,
file , blocks , le32_to_cpu ( blk - > id ) ,
( le32_to_cpu ( blk - > ver ) > > 16 ) & 0xff ,
( le32_to_cpu ( blk - > ver ) > > 8 ) & 0xff ,
le32_to_cpu ( blk - > ver ) & 0xff ) ;
adsp_dbg ( dsp , " %s.%d: %d bytes at 0x%x in %x \n " ,
file , blocks , le32_to_cpu ( blk - > len ) , offset , type ) ;
reg = 0 ;
region_name = " Unknown " ;
switch ( type ) {
case WMFW_NAME_TEXT :
case WMFW_INFO_TEXT :
break ;
case WMFW_ABSOLUTE :
region_name = " register " ;
reg = offset ;
break ;
default :
adsp_err ( dsp , " Unknown region type %x \n " , type ) ;
break ;
}
if ( reg ) {
ret = regmap_raw_write ( regmap , reg , blk - > data ,
le32_to_cpu ( blk - > len ) ) ;
if ( ret ! = 0 ) {
adsp_err ( dsp ,
" %s.%d: Failed to write to %x in %s \n " ,
file , blocks , reg , region_name ) ;
}
}
pos + = le32_to_cpu ( blk - > len ) + sizeof ( * blk ) ;
blocks + + ;
}
if ( pos > firmware - > size )
adsp_warn ( dsp , " %s.%d: %zu bytes at end of file \n " ,
file , blocks , pos - firmware - > size ) ;
out_fw :
release_firmware ( firmware ) ;
out :
kfree ( file ) ;
return 0 ;
}
int wm_adsp1_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol ,
int event )
{
struct snd_soc_codec * codec = w - > codec ;
struct wm_adsp * dsps = snd_soc_codec_get_drvdata ( codec ) ;
struct wm_adsp * dsp = & dsps [ w - > shift ] ;
int ret ;
switch ( event ) {
case SND_SOC_DAPM_POST_PMU :
regmap_update_bits ( dsp - > regmap , dsp - > base + ADSP1_CONTROL_30 ,
ADSP1_SYS_ENA , ADSP1_SYS_ENA ) ;
ret = wm_adsp_load ( dsp ) ;
if ( ret ! = 0 )
goto err ;
ret = wm_adsp_load_coeff ( dsp ) ;
if ( ret ! = 0 )
goto err ;
/* Start the core running */
regmap_update_bits ( dsp - > regmap , dsp - > base + ADSP1_CONTROL_30 ,
ADSP1_CORE_ENA | ADSP1_START ,
ADSP1_CORE_ENA | ADSP1_START ) ;
break ;
case SND_SOC_DAPM_PRE_PMD :
/* Halt the core */
regmap_update_bits ( dsp - > regmap , dsp - > base + ADSP1_CONTROL_30 ,
ADSP1_CORE_ENA | ADSP1_START , 0 ) ;
regmap_update_bits ( dsp - > regmap , dsp - > base + ADSP1_CONTROL_19 ,
ADSP1_WDMA_BUFFER_LENGTH_MASK , 0 ) ;
regmap_update_bits ( dsp - > regmap , dsp - > base + ADSP1_CONTROL_30 ,
ADSP1_SYS_ENA , 0 ) ;
break ;
default :
break ;
}
return 0 ;
err :
regmap_update_bits ( dsp - > regmap , dsp - > base + ADSP1_CONTROL_30 ,
ADSP1_SYS_ENA , 0 ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( wm_adsp1_event ) ;
static int wm_adsp2_ena ( struct wm_adsp * dsp )
{
unsigned int val ;
int ret , count ;
ret = regmap_update_bits ( dsp - > regmap , dsp - > base + ADSP2_CONTROL ,
ADSP2_SYS_ENA , ADSP2_SYS_ENA ) ;
if ( ret ! = 0 )
return ret ;
/* Wait for the RAM to start, should be near instantaneous */
count = 0 ;
do {
ret = regmap_read ( dsp - > regmap , dsp - > base + ADSP2_STATUS1 ,
& val ) ;
if ( ret ! = 0 )
return ret ;
} while ( ! ( val & ADSP2_RAM_RDY ) & & + + count < 10 ) ;
if ( ! ( val & ADSP2_RAM_RDY ) ) {
adsp_err ( dsp , " Failed to start DSP RAM \n " ) ;
return - EBUSY ;
}
adsp_dbg ( dsp , " RAM ready after %d polls \n " , count ) ;
adsp_info ( dsp , " RAM ready after %d polls \n " , count ) ;
return 0 ;
}
int wm_adsp2_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
struct snd_soc_codec * codec = w - > codec ;
struct wm_adsp * dsps = snd_soc_codec_get_drvdata ( codec ) ;
struct wm_adsp * dsp = & dsps [ w - > shift ] ;
2012-11-28 21:20:32 +04:00
unsigned int val ;
2012-10-11 06:54:02 +04:00
int ret ;
switch ( event ) {
case SND_SOC_DAPM_POST_PMU :
2012-12-02 16:50:46 +04:00
/*
* For simplicity set the DSP clock rate to be the
* SYSCLK rate rather than making it configurable .
*/
ret = regmap_read ( dsp - > regmap , ARIZONA_SYSTEM_CLOCK_1 , & val ) ;
if ( ret ! = 0 ) {
adsp_err ( dsp , " Failed to read SYSCLK state: %d \n " ,
ret ) ;
return ret ;
}
val = ( val & ARIZONA_SYSCLK_FREQ_MASK )
> > ARIZONA_SYSCLK_FREQ_SHIFT ;
ret = regmap_update_bits ( dsp - > regmap ,
dsp - > base + ADSP2_CLOCKING ,
ADSP2_CLK_SEL_MASK , val ) ;
if ( ret ! = 0 ) {
adsp_err ( dsp , " Failed to set clock rate: %d \n " ,
ret ) ;
return ret ;
}
2012-11-28 21:20:32 +04:00
if ( dsp - > dvfs ) {
ret = regmap_read ( dsp - > regmap ,
dsp - > base + ADSP2_CLOCKING , & val ) ;
if ( ret ! = 0 ) {
dev_err ( dsp - > dev ,
" Failed to read clocking: %d \n " , ret ) ;
return ret ;
}
2012-11-29 19:16:10 +04:00
if ( ( val & ADSP2_CLK_SEL_MASK ) > = 3 ) {
2012-11-28 21:20:32 +04:00
ret = regulator_enable ( dsp - > dvfs ) ;
if ( ret ! = 0 ) {
dev_err ( dsp - > dev ,
" Failed to enable supply: %d \n " ,
ret ) ;
return ret ;
}
ret = regulator_set_voltage ( dsp - > dvfs ,
1800000 ,
1800000 ) ;
if ( ret ! = 0 ) {
dev_err ( dsp - > dev ,
" Failed to raise supply: %d \n " ,
ret ) ;
return ret ;
}
}
}
2012-10-11 06:54:02 +04:00
ret = wm_adsp2_ena ( dsp ) ;
if ( ret ! = 0 )
return ret ;
ret = wm_adsp_load ( dsp ) ;
if ( ret ! = 0 )
goto err ;
ret = wm_adsp_load_coeff ( dsp ) ;
if ( ret ! = 0 )
goto err ;
ret = regmap_update_bits ( dsp - > regmap ,
dsp - > base + ADSP2_CONTROL ,
2012-11-28 23:53:59 +04:00
ADSP2_CORE_ENA | ADSP2_START ,
ADSP2_CORE_ENA | ADSP2_START ) ;
2012-10-11 06:54:02 +04:00
if ( ret ! = 0 )
goto err ;
break ;
case SND_SOC_DAPM_PRE_PMD :
regmap_update_bits ( dsp - > regmap , dsp - > base + ADSP2_CONTROL ,
2012-11-28 23:53:59 +04:00
ADSP2_SYS_ENA | ADSP2_CORE_ENA |
ADSP2_START , 0 ) ;
2012-11-28 21:20:32 +04:00
if ( dsp - > dvfs ) {
ret = regulator_set_voltage ( dsp - > dvfs , 1200000 ,
1800000 ) ;
if ( ret ! = 0 )
dev_warn ( dsp - > dev ,
" Failed to lower supply: %d \n " ,
ret ) ;
ret = regulator_disable ( dsp - > dvfs ) ;
if ( ret ! = 0 )
dev_err ( dsp - > dev ,
" Failed to enable supply: %d \n " ,
ret ) ;
}
2012-10-11 06:54:02 +04:00
break ;
default :
break ;
}
return 0 ;
err :
regmap_update_bits ( dsp - > regmap , dsp - > base + ADSP2_CONTROL ,
2012-11-28 23:53:59 +04:00
ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START , 0 ) ;
2012-10-11 06:54:02 +04:00
return ret ;
}
EXPORT_SYMBOL_GPL ( wm_adsp2_event ) ;
2012-11-28 21:20:32 +04:00
int wm_adsp2_init ( struct wm_adsp * adsp , bool dvfs )
{
int ret ;
2012-12-02 16:37:00 +04:00
/*
* Disable the DSP memory by default when in reset for a small
* power saving .
*/
ret = regmap_update_bits ( adsp - > regmap , adsp - > base + ADSP2_CONTROL ,
ADSP2_MEM_ENA , 0 ) ;
if ( ret ! = 0 ) {
adsp_err ( adsp , " Failed to clear memory retention: %d \n " , ret ) ;
return ret ;
}
2012-11-28 21:20:32 +04:00
if ( dvfs ) {
adsp - > dvfs = devm_regulator_get ( adsp - > dev , " DCVDD " ) ;
if ( IS_ERR ( adsp - > dvfs ) ) {
ret = PTR_ERR ( adsp - > dvfs ) ;
dev_err ( adsp - > dev , " Failed to get DCVDD: %d \n " , ret ) ;
return ret ;
}
ret = regulator_enable ( adsp - > dvfs ) ;
if ( ret ! = 0 ) {
dev_err ( adsp - > dev , " Failed to enable DCVDD: %d \n " ,
ret ) ;
return ret ;
}
ret = regulator_set_voltage ( adsp - > dvfs , 1200000 , 1800000 ) ;
if ( ret ! = 0 ) {
dev_err ( adsp - > dev , " Failed to initialise DVFS: %d \n " ,
ret ) ;
return ret ;
}
ret = regulator_disable ( adsp - > dvfs ) ;
if ( ret ! = 0 ) {
dev_err ( adsp - > dev , " Failed to disable DCVDD: %d \n " ,
ret ) ;
return ret ;
}
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( wm_adsp2_init ) ;