2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-08-23 18:59:56 +04:00
/*
* wm0010 . c - - WM0010 DSP Driver
*
* Copyright 2012 Wolfson Microelectronics PLC .
*
* Authors : Mark Brown < broonie @ opensource . wolfsonmicro . com >
* Dimitris Papastamos < dp @ opensource . wolfsonmicro . com >
* Scott Ling < sl @ opensource . wolfsonmicro . com >
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
2013-06-03 18:57:55 +04:00
# include <linux/interrupt.h>
2012-08-23 18:59:56 +04:00
# include <linux/irqreturn.h>
# include <linux/init.h>
# include <linux/spi/spi.h>
# include <linux/firmware.h>
# include <linux/delay.h>
# include <linux/fs.h>
# include <linux/gpio.h>
# include <linux/regulator/consumer.h>
# include <linux/mutex.h>
# include <linux/workqueue.h>
# include <sound/soc.h>
# include <sound/wm0010.h>
# define DEVICE_ID_WM0010 10
2012-11-23 16:37:35 +04:00
/* We only support v1 of the .dfw INFO record */
# define INFO_VERSION 1
2012-08-23 18:59:56 +04:00
enum dfw_cmd {
DFW_CMD_FUSE = 0x01 ,
DFW_CMD_CODE_HDR ,
DFW_CMD_CODE_DATA ,
DFW_CMD_PLL ,
DFW_CMD_INFO = 0xff
} ;
struct dfw_binrec {
u8 command ;
u32 length : 24 ;
u32 address ;
2020-02-11 23:05:49 +03:00
uint8_t data [ ] ;
2012-08-23 18:59:56 +04:00
} __packed ;
2012-11-23 16:37:35 +04:00
struct dfw_inforec {
u8 info_version ;
u8 tool_major_version ;
u8 tool_minor_version ;
u8 dsp_target ;
} ;
2012-08-23 18:59:56 +04:00
struct dfw_pllrec {
u8 command ;
u32 length : 24 ;
u32 address ;
u32 clkctrl1 ;
u32 clkctrl2 ;
u32 clkctrl3 ;
u32 ldetctrl ;
u32 uart_div ;
u32 spi_div ;
} __packed ;
static struct pll_clock_map {
int max_sysclk ;
int max_pll_spi_speed ;
u32 pll_clkctrl1 ;
} pll_clock_map [ ] = { /* Dividers */
{ 22000000 , 26000000 , 0x00201f11 } , /* 2,32,2 */
{ 18000000 , 26000000 , 0x00203f21 } , /* 2,64,4 */
{ 14000000 , 26000000 , 0x00202620 } , /* 1,39,4 */
{ 10000000 , 22000000 , 0x00203120 } , /* 1,50,4 */
{ 6500000 , 22000000 , 0x00204520 } , /* 1,70,4 */
{ 5500000 , 22000000 , 0x00103f10 } , /* 1,64,2 */
} ;
enum wm0010_state {
WM0010_POWER_OFF ,
WM0010_OUT_OF_RESET ,
WM0010_BOOTROM ,
WM0010_STAGE2 ,
WM0010_FIRMWARE ,
} ;
struct wm0010_priv {
2018-01-29 05:58:43 +03:00
struct snd_soc_component * component ;
2012-08-23 18:59:56 +04:00
struct mutex lock ;
struct device * dev ;
struct wm0010_pdata pdata ;
int gpio_reset ;
int gpio_reset_value ;
struct regulator_bulk_data core_supplies [ 2 ] ;
struct regulator * dbvdd ;
int sysclk ;
enum wm0010_state state ;
bool boot_failed ;
bool ready ;
bool pll_running ;
int max_spi_freq ;
int board_max_spi_speed ;
u32 pll_clkctrl1 ;
spinlock_t irq_lock ;
int irq ;
struct completion boot_completion ;
} ;
struct wm0010_spi_msg {
struct spi_message m ;
struct spi_transfer t ;
u8 * tx_buf ;
u8 * rx_buf ;
size_t len ;
} ;
2012-08-25 20:17:42 +04:00
static const struct snd_soc_dapm_widget wm0010_dapm_widgets [ ] = {
SND_SOC_DAPM_SUPPLY ( " CLKIN " , SND_SOC_NOPM , 0 , 0 , NULL , 0 ) ,
} ;
2012-08-23 18:59:56 +04:00
static const struct snd_soc_dapm_route wm0010_dapm_routes [ ] = {
2012-08-23 20:50:52 +04:00
{ " SDI2 Capture " , NULL , " SDI1 Playback " } ,
{ " SDI1 Capture " , NULL , " SDI2 Playback " } ,
2012-08-25 20:17:42 +04:00
{ " SDI1 Capture " , NULL , " CLKIN " } ,
{ " SDI2 Capture " , NULL , " CLKIN " } ,
{ " SDI1 Playback " , NULL , " CLKIN " } ,
{ " SDI2 Playback " , NULL , " CLKIN " } ,
2012-08-23 18:59:56 +04:00
} ;
static const char * wm0010_state_to_str ( enum wm0010_state state )
{
2014-05-27 10:57:55 +04:00
static const char * const state_to_str [ ] = {
2012-08-23 18:59:56 +04:00
" Power off " ,
" Out of reset " ,
2012-08-26 00:01:15 +04:00
" Boot ROM " ,
2012-08-23 18:59:56 +04:00
" Stage2 " ,
" Firmware "
} ;
if ( state < 0 | | state > = ARRAY_SIZE ( state_to_str ) )
return " null " ;
return state_to_str [ state ] ;
}
/* Called with wm0010->lock held */
2018-01-29 05:58:43 +03:00
static void wm0010_halt ( struct snd_soc_component * component )
2012-08-23 18:59:56 +04:00
{
2018-01-29 05:58:43 +03:00
struct wm0010_priv * wm0010 = snd_soc_component_get_drvdata ( component ) ;
2012-08-23 18:59:56 +04:00
unsigned long flags ;
enum wm0010_state state ;
/* Fetch the wm0010 state */
spin_lock_irqsave ( & wm0010 - > irq_lock , flags ) ;
state = wm0010 - > state ;
spin_unlock_irqrestore ( & wm0010 - > irq_lock , flags ) ;
switch ( state ) {
case WM0010_POWER_OFF :
/* If there's nothing to do, bail out */
return ;
case WM0010_OUT_OF_RESET :
case WM0010_BOOTROM :
case WM0010_STAGE2 :
case WM0010_FIRMWARE :
/* Remember to put chip back into reset */
2012-09-25 19:35:26 +04:00
gpio_set_value_cansleep ( wm0010 - > gpio_reset ,
wm0010 - > gpio_reset_value ) ;
2012-08-23 18:59:56 +04:00
/* Disable the regulators */
regulator_disable ( wm0010 - > dbvdd ) ;
regulator_bulk_disable ( ARRAY_SIZE ( wm0010 - > core_supplies ) ,
wm0010 - > core_supplies ) ;
break ;
}
spin_lock_irqsave ( & wm0010 - > irq_lock , flags ) ;
wm0010 - > state = WM0010_POWER_OFF ;
spin_unlock_irqrestore ( & wm0010 - > irq_lock , flags ) ;
}
struct wm0010_boot_xfer {
struct list_head list ;
2018-01-29 05:58:43 +03:00
struct snd_soc_component * component ;
2012-08-23 18:59:56 +04:00
struct completion * done ;
struct spi_message m ;
struct spi_transfer t ;
} ;
/* Called with wm0010->lock held */
static void wm0010_mark_boot_failure ( struct wm0010_priv * wm0010 )
{
enum wm0010_state state ;
unsigned long flags ;
spin_lock_irqsave ( & wm0010 - > irq_lock , flags ) ;
state = wm0010 - > state ;
spin_unlock_irqrestore ( & wm0010 - > irq_lock , flags ) ;
dev_err ( wm0010 - > dev , " Failed to transition from `%s' state to `%s' state \n " ,
wm0010_state_to_str ( state ) , wm0010_state_to_str ( state + 1 ) ) ;
wm0010 - > boot_failed = true ;
}
static void wm0010_boot_xfer_complete ( void * data )
{
struct wm0010_boot_xfer * xfer = data ;
2018-01-29 05:58:43 +03:00
struct snd_soc_component * component = xfer - > component ;
struct wm0010_priv * wm0010 = snd_soc_component_get_drvdata ( component ) ;
2012-08-23 18:59:56 +04:00
u32 * out32 = xfer - > t . rx_buf ;
int i ;
if ( xfer - > m . status ! = 0 ) {
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " SPI transfer failed: %d \n " ,
2012-08-23 18:59:56 +04:00
xfer - > m . status ) ;
wm0010_mark_boot_failure ( wm0010 ) ;
if ( xfer - > done )
complete ( xfer - > done ) ;
return ;
}
for ( i = 0 ; i < xfer - > t . len / 4 ; i + + ) {
2018-01-29 05:58:43 +03:00
dev_dbg ( component - > dev , " %d: %04x \n " , i , out32 [ i ] ) ;
2012-08-23 18:59:56 +04:00
switch ( be32_to_cpu ( out32 [ i ] ) ) {
case 0xe0e0e0e0 :
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev ,
2012-08-23 18:59:56 +04:00
" %d: ROM error reported in stage 2 \n " , i ) ;
wm0010_mark_boot_failure ( wm0010 ) ;
break ;
case 0x55555555 :
2012-11-05 18:44:25 +04:00
if ( wm0010 - > state < WM0010_STAGE2 )
2012-08-23 18:59:56 +04:00
break ;
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev ,
2012-08-23 18:59:56 +04:00
" %d: ROM bootloader running in stage 2 \n " , i ) ;
wm0010_mark_boot_failure ( wm0010 ) ;
break ;
case 0x0fed0000 :
2018-01-29 05:58:43 +03:00
dev_dbg ( component - > dev , " Stage2 loader running \n " ) ;
2012-08-23 18:59:56 +04:00
break ;
case 0x0fed0007 :
2018-01-29 05:58:43 +03:00
dev_dbg ( component - > dev , " CODE_HDR packet received \n " ) ;
2012-08-23 18:59:56 +04:00
break ;
case 0x0fed0008 :
2018-01-29 05:58:43 +03:00
dev_dbg ( component - > dev , " CODE_DATA packet received \n " ) ;
2012-08-23 18:59:56 +04:00
break ;
case 0x0fed0009 :
2018-01-29 05:58:43 +03:00
dev_dbg ( component - > dev , " Download complete \n " ) ;
2012-08-23 18:59:56 +04:00
break ;
case 0x0fed000c :
2018-01-29 05:58:43 +03:00
dev_dbg ( component - > dev , " Application start \n " ) ;
2012-08-23 18:59:56 +04:00
break ;
case 0x0fed000e :
2018-01-29 05:58:43 +03:00
dev_dbg ( component - > dev , " PLL packet received \n " ) ;
2012-08-23 18:59:56 +04:00
wm0010 - > pll_running = true ;
break ;
case 0x0fed0025 :
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " Device reports image too long \n " ) ;
2012-08-23 18:59:56 +04:00
wm0010_mark_boot_failure ( wm0010 ) ;
break ;
case 0x0fed002c :
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " Device reports bad SPI packet \n " ) ;
2012-08-23 18:59:56 +04:00
wm0010_mark_boot_failure ( wm0010 ) ;
break ;
case 0x0fed0031 :
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " Device reports SPI read overflow \n " ) ;
2012-08-23 18:59:56 +04:00
wm0010_mark_boot_failure ( wm0010 ) ;
break ;
case 0x0fed0032 :
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " Device reports SPI underclock \n " ) ;
2012-08-23 18:59:56 +04:00
wm0010_mark_boot_failure ( wm0010 ) ;
break ;
case 0x0fed0033 :
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " Device reports bad header packet \n " ) ;
2012-08-23 18:59:56 +04:00
wm0010_mark_boot_failure ( wm0010 ) ;
break ;
case 0x0fed0034 :
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " Device reports invalid packet type \n " ) ;
2012-08-23 18:59:56 +04:00
wm0010_mark_boot_failure ( wm0010 ) ;
break ;
case 0x0fed0035 :
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " Device reports data before header error \n " ) ;
2012-08-23 18:59:56 +04:00
wm0010_mark_boot_failure ( wm0010 ) ;
break ;
case 0x0fed0038 :
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " Device reports invalid PLL packet \n " ) ;
2012-08-23 18:59:56 +04:00
break ;
case 0x0fed003a :
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " Device reports packet alignment error \n " ) ;
2012-08-23 18:59:56 +04:00
wm0010_mark_boot_failure ( wm0010 ) ;
break ;
default :
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " Unrecognised return 0x%x \n " ,
2012-08-23 18:59:56 +04:00
be32_to_cpu ( out32 [ i ] ) ) ;
wm0010_mark_boot_failure ( wm0010 ) ;
break ;
}
if ( wm0010 - > boot_failed )
break ;
}
if ( xfer - > done )
complete ( xfer - > done ) ;
}
static void byte_swap_64 ( u64 * data_in , u64 * data_out , u32 len )
{
int i ;
for ( i = 0 ; i < len / 8 ; i + + )
data_out [ i ] = cpu_to_be64 ( le64_to_cpu ( data_in [ i ] ) ) ;
}
2018-01-29 05:58:43 +03:00
static int wm0010_firmware_load ( const char * name , struct snd_soc_component * component )
2012-08-23 18:59:56 +04:00
{
2018-01-29 05:58:43 +03:00
struct spi_device * spi = to_spi_device ( component - > dev ) ;
struct wm0010_priv * wm0010 = snd_soc_component_get_drvdata ( component ) ;
2012-08-23 18:59:56 +04:00
struct list_head xfer_list ;
struct wm0010_boot_xfer * xfer ;
int ret ;
2020-08-13 20:54:42 +03:00
DECLARE_COMPLETION_ONSTACK ( done ) ;
2012-08-23 18:59:56 +04:00
const struct firmware * fw ;
const struct dfw_binrec * rec ;
2012-11-23 16:37:35 +04:00
const struct dfw_inforec * inforec ;
2012-11-07 20:53:17 +04:00
u64 * img ;
u8 * out , dsp ;
u32 len , offset ;
INIT_LIST_HEAD ( & xfer_list ) ;
2018-01-29 05:58:43 +03:00
ret = request_firmware ( & fw , name , component - > dev ) ;
2012-11-07 20:53:17 +04:00
if ( ret ! = 0 ) {
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " Failed to request application(%s): %d \n " ,
2013-03-29 13:45:35 +04:00
name , ret ) ;
2012-11-07 20:53:17 +04:00
return ret ;
}
rec = ( const struct dfw_binrec * ) fw - > data ;
2012-11-23 16:37:35 +04:00
inforec = ( const struct dfw_inforec * ) rec - > data ;
2012-11-07 20:53:17 +04:00
offset = 0 ;
2012-11-23 16:37:35 +04:00
dsp = inforec - > dsp_target ;
2012-11-07 20:53:17 +04:00
wm0010 - > boot_failed = false ;
2013-11-05 21:39:52 +04:00
if ( WARN_ON ( ! list_empty ( & xfer_list ) ) )
return - EINVAL ;
2012-11-07 20:53:17 +04:00
/* First record should be INFO */
if ( rec - > command ! = DFW_CMD_INFO ) {
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " First record not INFO \r \n " ) ;
2012-11-07 20:53:17 +04:00
ret = - EINVAL ;
goto abort ;
}
2012-11-23 16:37:35 +04:00
if ( inforec - > info_version ! = INFO_VERSION ) {
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev ,
2012-11-23 16:37:35 +04:00
" Unsupported version (%02d) of INFO record \r \n " ,
inforec - > info_version ) ;
ret = - EINVAL ;
goto abort ;
}
2018-01-29 05:58:43 +03:00
dev_dbg ( component - > dev , " Version v%02d INFO record found \r \n " ,
2012-11-23 16:37:35 +04:00
inforec - > info_version ) ;
2012-11-07 20:53:17 +04:00
/* Check it's a DSP file */
if ( dsp ! = DEVICE_ID_WM0010 ) {
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " Not a WM0010 firmware file. \r \n " ) ;
2012-11-07 20:53:17 +04:00
ret = - EINVAL ;
goto abort ;
}
/* Skip the info record as we don't need to send it */
offset + = ( ( rec - > length ) + 8 ) ;
rec = ( void * ) & rec - > data [ rec - > length ] ;
while ( offset < fw - > size ) {
2018-01-29 05:58:43 +03:00
dev_dbg ( component - > dev ,
2012-11-07 20:53:17 +04:00
" Packet: command %d, data length = 0x%x \r \n " ,
rec - > command , rec - > length ) ;
len = rec - > length + 8 ;
2013-07-31 16:28:52 +04:00
xfer = kzalloc ( sizeof ( * xfer ) , GFP_KERNEL ) ;
if ( ! xfer ) {
ret = - ENOMEM ;
goto abort ;
}
2018-01-29 05:58:43 +03:00
xfer - > component = component ;
2013-07-31 16:28:52 +04:00
list_add_tail ( & xfer - > list , & xfer_list ) ;
2013-08-01 12:53:45 +04:00
out = kzalloc ( len , GFP_KERNEL | GFP_DMA ) ;
2012-11-07 20:53:17 +04:00
if ( ! out ) {
ret = - ENOMEM ;
goto abort1 ;
}
2013-07-31 16:28:52 +04:00
xfer - > t . rx_buf = out ;
2012-11-07 20:53:17 +04:00
2013-08-01 12:53:45 +04:00
img = kzalloc ( len , GFP_KERNEL | GFP_DMA ) ;
2012-11-07 20:53:17 +04:00
if ( ! img ) {
ret = - ENOMEM ;
goto abort1 ;
}
2013-07-31 16:28:52 +04:00
xfer - > t . tx_buf = img ;
2012-11-07 20:53:17 +04:00
byte_swap_64 ( ( u64 * ) & rec - > command , img , len ) ;
spi_message_init ( & xfer - > m ) ;
xfer - > m . complete = wm0010_boot_xfer_complete ;
xfer - > m . context = xfer ;
xfer - > t . len = len ;
xfer - > t . bits_per_word = 8 ;
if ( ! wm0010 - > pll_running ) {
xfer - > t . speed_hz = wm0010 - > sysclk / 6 ;
} else {
xfer - > t . speed_hz = wm0010 - > max_spi_freq ;
if ( wm0010 - > board_max_spi_speed & &
( wm0010 - > board_max_spi_speed < wm0010 - > max_spi_freq ) )
xfer - > t . speed_hz = wm0010 - > board_max_spi_speed ;
}
/* Store max usable spi frequency for later use */
wm0010 - > max_spi_freq = xfer - > t . speed_hz ;
spi_message_add_tail ( & xfer - > t , & xfer - > m ) ;
offset + = ( ( rec - > length ) + 8 ) ;
rec = ( void * ) & rec - > data [ rec - > length ] ;
if ( offset > = fw - > size ) {
2018-01-29 05:58:43 +03:00
dev_dbg ( component - > dev , " All transfers scheduled \n " ) ;
2012-11-07 20:53:17 +04:00
xfer - > done = & done ;
}
ret = spi_async ( spi , & xfer - > m ) ;
if ( ret ! = 0 ) {
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " Write failed: %d \n " , ret ) ;
2012-11-07 20:53:17 +04:00
goto abort1 ;
}
if ( wm0010 - > boot_failed ) {
2018-01-29 05:58:43 +03:00
dev_dbg ( component - > dev , " Boot fail! \n " ) ;
2012-11-07 20:53:17 +04:00
ret = - EINVAL ;
goto abort1 ;
}
}
wait_for_completion ( & done ) ;
ret = 0 ;
abort1 :
while ( ! list_empty ( & xfer_list ) ) {
xfer = list_first_entry ( & xfer_list , struct wm0010_boot_xfer ,
list ) ;
kfree ( xfer - > t . rx_buf ) ;
kfree ( xfer - > t . tx_buf ) ;
list_del ( & xfer - > list ) ;
kfree ( xfer ) ;
}
abort :
release_firmware ( fw ) ;
return ret ;
}
2018-01-29 05:58:43 +03:00
static int wm0010_stage2_load ( struct snd_soc_component * component )
2012-11-07 20:53:18 +04:00
{
2018-01-29 05:58:43 +03:00
struct spi_device * spi = to_spi_device ( component - > dev ) ;
struct wm0010_priv * wm0010 = snd_soc_component_get_drvdata ( component ) ;
2012-11-07 20:53:18 +04:00
const struct firmware * fw ;
struct spi_message m ;
struct spi_transfer t ;
u32 * img ;
u8 * out ;
int i ;
int ret = 0 ;
2018-01-29 05:58:43 +03:00
ret = request_firmware ( & fw , " wm0010_stage2.bin " , component - > dev ) ;
2012-11-07 20:53:18 +04:00
if ( ret ! = 0 ) {
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " Failed to request stage2 loader: %d \n " ,
2012-11-07 20:53:18 +04:00
ret ) ;
return ret ;
}
2018-01-29 05:58:43 +03:00
dev_dbg ( component - > dev , " Downloading %zu byte stage 2 loader \n " , fw - > size ) ;
2012-11-07 20:53:18 +04:00
/* Copy to local buffer first as vmalloc causes problems for dma */
2019-07-03 19:32:24 +03:00
img = kmemdup ( & fw - > data [ 0 ] , fw - > size , GFP_KERNEL | GFP_DMA ) ;
2012-11-07 20:53:18 +04:00
if ( ! img ) {
ret = - ENOMEM ;
goto abort2 ;
}
2013-08-01 12:53:45 +04:00
out = kzalloc ( fw - > size , GFP_KERNEL | GFP_DMA ) ;
2012-11-07 20:53:18 +04:00
if ( ! out ) {
ret = - ENOMEM ;
goto abort1 ;
}
spi_message_init ( & m ) ;
memset ( & t , 0 , sizeof ( t ) ) ;
t . rx_buf = out ;
t . tx_buf = img ;
t . len = fw - > size ;
t . bits_per_word = 8 ;
t . speed_hz = wm0010 - > sysclk / 10 ;
spi_message_add_tail ( & t , & m ) ;
2018-01-29 05:58:43 +03:00
dev_dbg ( component - > dev , " Starting initial download at %dHz \n " ,
2012-11-07 20:53:18 +04:00
t . speed_hz ) ;
ret = spi_sync ( spi , & m ) ;
if ( ret ! = 0 ) {
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " Initial download failed: %d \n " , ret ) ;
2012-11-07 20:53:18 +04:00
goto abort ;
}
/* Look for errors from the boot ROM */
for ( i = 0 ; i < fw - > size ; i + + ) {
if ( out [ i ] ! = 0x55 ) {
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " Boot ROM error: %x in %d \n " ,
2012-11-07 20:53:18 +04:00
out [ i ] , i ) ;
wm0010_mark_boot_failure ( wm0010 ) ;
ret = - EBUSY ;
goto abort ;
}
}
abort :
kfree ( out ) ;
abort1 :
kfree ( img ) ;
abort2 :
release_firmware ( fw ) ;
return ret ;
}
2018-01-29 05:58:43 +03:00
static int wm0010_boot ( struct snd_soc_component * component )
2012-11-07 20:53:17 +04:00
{
2018-01-29 05:58:43 +03:00
struct spi_device * spi = to_spi_device ( component - > dev ) ;
struct wm0010_priv * wm0010 = snd_soc_component_get_drvdata ( component ) ;
2012-11-07 20:53:17 +04:00
unsigned long flags ;
int ret ;
2012-08-23 18:59:56 +04:00
struct spi_message m ;
struct spi_transfer t ;
struct dfw_pllrec pll_rec ;
2012-11-07 20:53:18 +04:00
u32 * p , len ;
2012-08-23 18:59:56 +04:00
u64 * img_swap ;
u8 * out ;
int i ;
spin_lock_irqsave ( & wm0010 - > irq_lock , flags ) ;
if ( wm0010 - > state ! = WM0010_POWER_OFF )
dev_warn ( wm0010 - > dev , " DSP already powered up! \n " ) ;
spin_unlock_irqrestore ( & wm0010 - > irq_lock , flags ) ;
if ( wm0010 - > sysclk > 26000000 ) {
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " Max DSP clock frequency is 26MHz \n " ) ;
2012-08-23 18:59:56 +04:00
ret = - ECANCELED ;
goto err ;
}
mutex_lock ( & wm0010 - > lock ) ;
wm0010 - > pll_running = false ;
2018-01-29 05:58:43 +03:00
dev_dbg ( component - > dev , " max_spi_freq: %d \n " , wm0010 - > max_spi_freq ) ;
2012-08-23 18:59:56 +04:00
ret = regulator_bulk_enable ( ARRAY_SIZE ( wm0010 - > core_supplies ) ,
wm0010 - > core_supplies ) ;
if ( ret ! = 0 ) {
dev_err ( & spi - > dev , " Failed to enable core supplies: %d \n " ,
ret ) ;
mutex_unlock ( & wm0010 - > lock ) ;
goto err ;
}
ret = regulator_enable ( wm0010 - > dbvdd ) ;
if ( ret ! = 0 ) {
dev_err ( & spi - > dev , " Failed to enable DBVDD: %d \n " , ret ) ;
goto err_core ;
}
/* Release reset */
2012-09-25 19:35:26 +04:00
gpio_set_value_cansleep ( wm0010 - > gpio_reset , ! wm0010 - > gpio_reset_value ) ;
2012-08-23 18:59:56 +04:00
spin_lock_irqsave ( & wm0010 - > irq_lock , flags ) ;
wm0010 - > state = WM0010_OUT_OF_RESET ;
spin_unlock_irqrestore ( & wm0010 - > irq_lock , flags ) ;
if ( ! wait_for_completion_timeout ( & wm0010 - > boot_completion ,
2012-11-21 14:47:44 +04:00
msecs_to_jiffies ( 20 ) ) )
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " Failed to get interrupt from DSP \n " ) ;
2012-08-23 18:59:56 +04:00
spin_lock_irqsave ( & wm0010 - > irq_lock , flags ) ;
wm0010 - > state = WM0010_BOOTROM ;
spin_unlock_irqrestore ( & wm0010 - > irq_lock , flags ) ;
2018-01-29 05:58:43 +03:00
ret = wm0010_stage2_load ( component ) ;
2012-11-07 20:53:18 +04:00
if ( ret )
2012-08-23 18:59:56 +04:00
goto abort ;
if ( ! wait_for_completion_timeout ( & wm0010 - > boot_completion ,
2012-11-21 14:47:44 +04:00
msecs_to_jiffies ( 20 ) ) )
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " Failed to get interrupt from DSP loader. \n " ) ;
2012-08-23 18:59:56 +04:00
spin_lock_irqsave ( & wm0010 - > irq_lock , flags ) ;
wm0010 - > state = WM0010_STAGE2 ;
spin_unlock_irqrestore ( & wm0010 - > irq_lock , flags ) ;
/* Only initialise PLL if max_spi_freq initialised */
if ( wm0010 - > max_spi_freq ) {
/* Initialise a PLL record */
memset ( & pll_rec , 0 , sizeof ( pll_rec ) ) ;
pll_rec . command = DFW_CMD_PLL ;
pll_rec . length = ( sizeof ( pll_rec ) - 8 ) ;
/* On wm0010 only the CLKCTRL1 value is used */
pll_rec . clkctrl1 = wm0010 - > pll_clkctrl1 ;
2013-05-07 15:38:52 +04:00
ret = - ENOMEM ;
2012-08-23 18:59:56 +04:00
len = pll_rec . length + 8 ;
2013-08-01 12:53:45 +04:00
out = kzalloc ( len , GFP_KERNEL | GFP_DMA ) ;
2017-11-22 19:17:48 +03:00
if ( ! out )
2012-08-23 18:59:56 +04:00
goto abort ;
2013-08-01 12:53:45 +04:00
img_swap = kzalloc ( len , GFP_KERNEL | GFP_DMA ) ;
2014-06-20 13:59:05 +04:00
if ( ! img_swap )
2015-09-18 13:32:21 +03:00
goto abort_out ;
2012-08-23 18:59:56 +04:00
/* We need to re-order for 0010 */
byte_swap_64 ( ( u64 * ) & pll_rec , img_swap , len ) ;
spi_message_init ( & m ) ;
memset ( & t , 0 , sizeof ( t ) ) ;
t . rx_buf = out ;
t . tx_buf = img_swap ;
t . len = len ;
t . bits_per_word = 8 ;
t . speed_hz = wm0010 - > sysclk / 6 ;
spi_message_add_tail ( & t , & m ) ;
ret = spi_sync ( spi , & m ) ;
2015-09-18 13:32:21 +03:00
if ( ret ) {
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " First PLL write failed: %d \n " , ret ) ;
2015-09-18 13:32:21 +03:00
goto abort_swap ;
2012-08-23 18:59:56 +04:00
}
/* Use a second send of the message to get the return status */
ret = spi_sync ( spi , & m ) ;
2015-09-18 13:32:21 +03:00
if ( ret ) {
2018-01-29 05:58:43 +03:00
dev_err ( component - > dev , " Second PLL write failed: %d \n " , ret ) ;
2015-09-18 13:32:21 +03:00
goto abort_swap ;
2012-08-23 18:59:56 +04:00
}
p = ( u32 * ) out ;
/* Look for PLL active code from the DSP */
for ( i = 0 ; i < len / 4 ; i + + ) {
if ( * p = = 0x0e00ed0f ) {
2018-01-29 05:58:43 +03:00
dev_dbg ( component - > dev , " PLL packet received \n " ) ;
2012-08-23 18:59:56 +04:00
wm0010 - > pll_running = true ;
break ;
}
p + + ;
}
kfree ( img_swap ) ;
kfree ( out ) ;
} else
2018-01-29 05:58:43 +03:00
dev_dbg ( component - > dev , " Not enabling DSP PLL. " ) ;
2012-08-23 18:59:56 +04:00
2018-01-29 05:58:43 +03:00
ret = wm0010_firmware_load ( " wm0010.dfw " , component ) ;
2012-08-23 18:59:56 +04:00
2012-11-07 20:53:17 +04:00
if ( ret ! = 0 )
2012-08-23 18:59:56 +04:00
goto abort ;
spin_lock_irqsave ( & wm0010 - > irq_lock , flags ) ;
wm0010 - > state = WM0010_FIRMWARE ;
spin_unlock_irqrestore ( & wm0010 - > irq_lock , flags ) ;
mutex_unlock ( & wm0010 - > lock ) ;
return 0 ;
2015-09-18 13:32:21 +03:00
abort_swap :
kfree ( img_swap ) ;
abort_out :
kfree ( out ) ;
2012-08-23 18:59:56 +04:00
abort :
/* Put the chip back into reset */
2018-01-29 05:58:43 +03:00
wm0010_halt ( component ) ;
2012-08-23 18:59:56 +04:00
mutex_unlock ( & wm0010 - > lock ) ;
return ret ;
2012-09-05 16:29:46 +04:00
2012-08-23 18:59:56 +04:00
err_core :
2012-09-05 16:29:46 +04:00
mutex_unlock ( & wm0010 - > lock ) ;
2012-08-23 18:59:56 +04:00
regulator_bulk_disable ( ARRAY_SIZE ( wm0010 - > core_supplies ) ,
wm0010 - > core_supplies ) ;
err :
return ret ;
}
2018-01-29 05:58:43 +03:00
static int wm0010_set_bias_level ( struct snd_soc_component * component ,
2012-08-23 18:59:56 +04:00
enum snd_soc_bias_level level )
{
2018-01-29 05:58:43 +03:00
struct wm0010_priv * wm0010 = snd_soc_component_get_drvdata ( component ) ;
2012-08-23 18:59:56 +04:00
switch ( level ) {
case SND_SOC_BIAS_ON :
2018-01-29 05:58:43 +03:00
if ( snd_soc_component_get_bias_level ( component ) = = SND_SOC_BIAS_PREPARE )
wm0010_boot ( component ) ;
2012-08-23 18:59:56 +04:00
break ;
case SND_SOC_BIAS_PREPARE :
break ;
case SND_SOC_BIAS_STANDBY :
2018-01-29 05:58:43 +03:00
if ( snd_soc_component_get_bias_level ( component ) = = SND_SOC_BIAS_PREPARE ) {
2012-08-23 18:59:56 +04:00
mutex_lock ( & wm0010 - > lock ) ;
2018-01-29 05:58:43 +03:00
wm0010_halt ( component ) ;
2012-08-23 18:59:56 +04:00
mutex_unlock ( & wm0010 - > lock ) ;
}
break ;
case SND_SOC_BIAS_OFF :
break ;
}
return 0 ;
}
2018-01-29 05:58:43 +03:00
static int wm0010_set_sysclk ( struct snd_soc_component * component , int source ,
2012-08-23 18:59:56 +04:00
int clk_id , unsigned int freq , int dir )
{
2018-01-29 05:58:43 +03:00
struct wm0010_priv * wm0010 = snd_soc_component_get_drvdata ( component ) ;
2012-08-23 18:59:56 +04:00
unsigned int i ;
wm0010 - > sysclk = freq ;
if ( freq < pll_clock_map [ ARRAY_SIZE ( pll_clock_map ) - 1 ] . max_sysclk ) {
wm0010 - > max_spi_freq = 0 ;
} else {
for ( i = 0 ; i < ARRAY_SIZE ( pll_clock_map ) ; i + + )
2013-10-30 11:35:03 +04:00
if ( freq > = pll_clock_map [ i ] . max_sysclk ) {
wm0010 - > max_spi_freq = pll_clock_map [ i ] . max_pll_spi_speed ;
wm0010 - > pll_clkctrl1 = pll_clock_map [ i ] . pll_clkctrl1 ;
2012-08-23 18:59:56 +04:00
break ;
2013-10-30 11:35:03 +04:00
}
2012-08-23 18:59:56 +04:00
}
return 0 ;
}
2018-01-29 05:58:43 +03:00
static int wm0010_probe ( struct snd_soc_component * component ) ;
static const struct snd_soc_component_driver soc_component_dev_wm0010 = {
. probe = wm0010_probe ,
. set_bias_level = wm0010_set_bias_level ,
. set_sysclk = wm0010_set_sysclk ,
. dapm_widgets = wm0010_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( wm0010_dapm_widgets ) ,
. dapm_routes = wm0010_dapm_routes ,
. num_dapm_routes = ARRAY_SIZE ( wm0010_dapm_routes ) ,
. use_pmdown_time = 1 ,
. endianness = 1 ,
2012-08-23 18:59:56 +04:00
} ;
2012-08-25 17:05:35 +04:00
# define WM0010_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
2012-08-23 18:59:56 +04:00
# define WM0010_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE )
static struct snd_soc_dai_driver wm0010_dai [ ] = {
{
. name = " wm0010-sdi1 " ,
. playback = {
. stream_name = " SDI1 Playback " ,
. channels_min = 1 ,
. channels_max = 2 ,
. rates = WM0010_RATES ,
. formats = WM0010_FORMATS ,
} ,
. capture = {
. stream_name = " SDI1 Capture " ,
. channels_min = 1 ,
. channels_max = 2 ,
. rates = WM0010_RATES ,
. formats = WM0010_FORMATS ,
} ,
} ,
{
. name = " wm0010-sdi2 " ,
. playback = {
. stream_name = " SDI2 Playback " ,
. channels_min = 1 ,
. channels_max = 2 ,
. rates = WM0010_RATES ,
. formats = WM0010_FORMATS ,
} ,
. capture = {
. stream_name = " SDI2 Capture " ,
. channels_min = 1 ,
. channels_max = 2 ,
. rates = WM0010_RATES ,
. formats = WM0010_FORMATS ,
} ,
} ,
} ;
static irqreturn_t wm0010_irq ( int irq , void * data )
{
struct wm0010_priv * wm0010 = data ;
switch ( wm0010 - > state ) {
case WM0010_OUT_OF_RESET :
case WM0010_BOOTROM :
case WM0010_STAGE2 :
spin_lock ( & wm0010 - > irq_lock ) ;
complete ( & wm0010 - > boot_completion ) ;
spin_unlock ( & wm0010 - > irq_lock ) ;
return IRQ_HANDLED ;
default :
return IRQ_NONE ;
}
return IRQ_NONE ;
}
2018-01-29 05:58:43 +03:00
static int wm0010_probe ( struct snd_soc_component * component )
2012-08-23 18:59:56 +04:00
{
2018-01-29 05:58:43 +03:00
struct wm0010_priv * wm0010 = snd_soc_component_get_drvdata ( component ) ;
2012-08-26 00:04:04 +04:00
2018-01-29 05:58:43 +03:00
wm0010 - > component = component ;
2012-08-26 00:04:04 +04:00
return 0 ;
}
2012-12-07 18:26:37 +04:00
static int wm0010_spi_probe ( struct spi_device * spi )
2012-08-26 00:04:04 +04:00
{
2012-08-23 18:59:56 +04:00
unsigned long gpio_flags ;
int ret ;
int trigger ;
int irq ;
2012-08-26 00:04:04 +04:00
struct wm0010_priv * wm0010 ;
2012-08-23 18:59:56 +04:00
2012-08-26 00:04:04 +04:00
wm0010 = devm_kzalloc ( & spi - > dev , sizeof ( * wm0010 ) ,
GFP_KERNEL ) ;
if ( ! wm0010 )
return - ENOMEM ;
mutex_init ( & wm0010 - > lock ) ;
spin_lock_init ( & wm0010 - > irq_lock ) ;
spi_set_drvdata ( spi , wm0010 ) ;
wm0010 - > dev = & spi - > dev ;
if ( dev_get_platdata ( & spi - > dev ) )
memcpy ( & wm0010 - > pdata , dev_get_platdata ( & spi - > dev ) ,
sizeof ( wm0010 - > pdata ) ) ;
2012-08-23 18:59:56 +04:00
init_completion ( & wm0010 - > boot_completion ) ;
wm0010 - > core_supplies [ 0 ] . supply = " AVDD " ;
wm0010 - > core_supplies [ 1 ] . supply = " DCVDD " ;
ret = devm_regulator_bulk_get ( wm0010 - > dev , ARRAY_SIZE ( wm0010 - > core_supplies ) ,
wm0010 - > core_supplies ) ;
if ( ret ! = 0 ) {
dev_err ( wm0010 - > dev , " Failed to obtain core supplies: %d \n " ,
ret ) ;
return ret ;
}
wm0010 - > dbvdd = devm_regulator_get ( wm0010 - > dev , " DBVDD " ) ;
if ( IS_ERR ( wm0010 - > dbvdd ) ) {
ret = PTR_ERR ( wm0010 - > dbvdd ) ;
dev_err ( wm0010 - > dev , " Failed to obtain DBVDD: %d \n " , ret ) ;
return ret ;
}
if ( wm0010 - > pdata . gpio_reset ) {
wm0010 - > gpio_reset = wm0010 - > pdata . gpio_reset ;
if ( wm0010 - > pdata . reset_active_high )
wm0010 - > gpio_reset_value = 1 ;
else
wm0010 - > gpio_reset_value = 0 ;
if ( wm0010 - > gpio_reset_value )
gpio_flags = GPIOF_OUT_INIT_HIGH ;
else
gpio_flags = GPIOF_OUT_INIT_LOW ;
ret = devm_gpio_request_one ( wm0010 - > dev , wm0010 - > gpio_reset ,
gpio_flags , " wm0010 reset " ) ;
if ( ret < 0 ) {
dev_err ( wm0010 - > dev ,
" Failed to request GPIO for DSP reset: %d \n " ,
ret ) ;
return ret ;
}
} else {
dev_err ( wm0010 - > dev , " No reset GPIO configured \n " ) ;
2012-08-26 00:04:04 +04:00
return - EINVAL ;
2012-08-23 18:59:56 +04:00
}
2012-09-25 22:04:25 +04:00
wm0010 - > state = WM0010_POWER_OFF ;
2012-08-23 18:59:56 +04:00
irq = spi - > irq ;
if ( wm0010 - > pdata . irq_flags )
trigger = wm0010 - > pdata . irq_flags ;
else
trigger = IRQF_TRIGGER_FALLING ;
trigger | = IRQF_ONESHOT ;
2015-07-02 16:26:44 +03:00
ret = request_threaded_irq ( irq , NULL , wm0010_irq , trigger ,
2012-08-23 18:59:56 +04:00
" wm0010 " , wm0010 ) ;
2012-08-26 00:04:04 +04:00
if ( ret ) {
2012-08-23 18:59:56 +04:00
dev_err ( wm0010 - > dev , " Failed to request IRQ %d: %d \n " ,
irq , ret ) ;
2012-08-26 00:04:04 +04:00
return ret ;
}
2012-08-23 18:59:56 +04:00
wm0010 - > irq = irq ;
2013-05-30 13:06:01 +04:00
ret = irq_set_irq_wake ( irq , 1 ) ;
if ( ret ) {
dev_err ( wm0010 - > dev , " Failed to set IRQ %d as wake source: %d \n " ,
irq , ret ) ;
return ret ;
}
2012-08-23 18:59:56 +04:00
if ( spi - > max_speed_hz )
wm0010 - > board_max_spi_speed = spi - > max_speed_hz ;
else
wm0010 - > board_max_spi_speed = 0 ;
2018-01-29 05:58:43 +03:00
ret = devm_snd_soc_register_component ( & spi - > dev ,
& soc_component_dev_wm0010 , wm0010_dai ,
2012-08-23 18:59:56 +04:00
ARRAY_SIZE ( wm0010_dai ) ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
2022-01-23 20:52:01 +03:00
static void wm0010_spi_remove ( struct spi_device * spi )
2012-08-23 18:59:56 +04:00
{
struct wm0010_priv * wm0010 = spi_get_drvdata ( spi ) ;
2012-09-25 19:36:56 +04:00
gpio_set_value_cansleep ( wm0010 - > gpio_reset ,
wm0010 - > gpio_reset_value ) ;
2012-08-23 18:59:56 +04:00
2013-06-03 18:57:55 +04:00
irq_set_irq_wake ( wm0010 - > irq , 0 ) ;
2012-08-23 18:59:56 +04:00
if ( wm0010 - > irq )
free_irq ( wm0010 - > irq , wm0010 ) ;
}
static struct spi_driver wm0010_spi_driver = {
. driver = {
. name = " wm0010 " ,
} ,
. probe = wm0010_spi_probe ,
2012-12-07 18:26:37 +04:00
. remove = wm0010_spi_remove ,
2012-08-23 18:59:56 +04:00
} ;
module_spi_driver ( wm0010_spi_driver ) ;
MODULE_DESCRIPTION ( " ASoC WM0010 driver " ) ;
MODULE_AUTHOR ( " Mark Brown <broonie@opensource.wolfsonmicro.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;