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>
2013-01-30 10:37:23 +04:00
# include <linux/list.h>
2012-10-11 06:54:02 +04:00
# 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>
2014-11-14 18:40:45 +03:00
# include <linux/vmalloc.h>
2013-05-08 17:15:35 +04:00
# include <linux/workqueue.h>
2012-10-11 06:54:02 +04:00
# 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>
2013-02-18 23:09:23 +04:00
# include "arizona.h"
2012-10-11 06:54:02 +04:00
# 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 */
2013-01-18 12:43:09 +04:00
/*
* ADSP1 Control 31
*/
# define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
# define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
# define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
2013-01-28 16:18:17 +04:00
# define ADSP2_CONTROL 0x0
# define ADSP2_CLOCKING 0x1
# define ADSP2_STATUS1 0x4
# define ADSP2_WDMA_CONFIG_1 0x30
# define ADSP2_WDMA_CONFIG_2 0x31
# define ADSP2_RDMA_CONFIG_1 0x34
2012-10-11 06:54:02 +04:00
2015-05-29 12:23:07 +03:00
# define ADSP2_SCRATCH0 0x40
# define ADSP2_SCRATCH1 0x41
# define ADSP2_SCRATCH2 0x42
# define ADSP2_SCRATCH3 0x43
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
2013-01-30 10:37:23 +04:00
struct wm_adsp_buf {
struct list_head list ;
void * buf ;
} ;
static struct wm_adsp_buf * wm_adsp_buf_alloc ( const void * src , size_t len ,
struct list_head * list )
{
struct wm_adsp_buf * buf = kzalloc ( sizeof ( * buf ) , GFP_KERNEL ) ;
if ( buf = = NULL )
return NULL ;
2014-11-14 18:40:45 +03:00
buf - > buf = vmalloc ( len ) ;
2013-01-30 10:37:23 +04:00
if ( ! buf - > buf ) {
2014-11-14 18:40:45 +03:00
vfree ( buf ) ;
2013-01-30 10:37:23 +04:00
return NULL ;
}
2014-11-14 18:40:45 +03:00
memcpy ( buf - > buf , src , len ) ;
2013-01-30 10:37:23 +04:00
if ( list )
list_add_tail ( & buf - > list , list ) ;
return buf ;
}
static void wm_adsp_buf_free ( struct list_head * list )
{
while ( ! list_empty ( list ) ) {
struct wm_adsp_buf * buf = list_first_entry ( list ,
struct wm_adsp_buf ,
list ) ;
list_del ( & buf - > list ) ;
2014-11-14 18:40:45 +03:00
vfree ( buf - > buf ) ;
2013-01-30 10:37:23 +04:00
kfree ( buf ) ;
}
}
2013-01-25 13:47:48 +04:00
# define WM_ADSP_NUM_FW 4
2013-01-12 02:58:28 +04:00
2013-03-08 11:25:58 +04:00
# define WM_ADSP_FW_MBC_VSS 0
# define WM_ADSP_FW_TX 1
# define WM_ADSP_FW_TX_SPK 2
# define WM_ADSP_FW_RX_ANC 3
2013-01-12 02:58:28 +04:00
static const char * wm_adsp_fw_text [ WM_ADSP_NUM_FW ] = {
2013-03-08 11:25:58 +04:00
[ WM_ADSP_FW_MBC_VSS ] = " MBC/VSS " ,
[ WM_ADSP_FW_TX ] = " Tx " ,
[ WM_ADSP_FW_TX_SPK ] = " Tx Speaker " ,
[ WM_ADSP_FW_RX_ANC ] = " Rx ANC " ,
2013-01-12 02:58:28 +04:00
} ;
static struct {
const char * file ;
} wm_adsp_fw [ WM_ADSP_NUM_FW ] = {
2013-03-08 11:25:58 +04:00
[ WM_ADSP_FW_MBC_VSS ] = { . file = " mbc-vss " } ,
[ WM_ADSP_FW_TX ] = { . file = " tx " } ,
[ WM_ADSP_FW_TX_SPK ] = { . file = " tx-spk " } ,
[ WM_ADSP_FW_RX_ANC ] = { . file = " rx-anc " } ,
2013-01-12 02:58:28 +04:00
} ;
2013-05-08 17:15:35 +04:00
struct wm_coeff_ctl_ops {
int ( * xget ) ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol ) ;
int ( * xput ) ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol ) ;
int ( * xinfo ) ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo ) ;
} ;
struct wm_coeff_ctl {
const char * name ;
2015-04-13 15:28:02 +03:00
const char * fw_name ;
2015-04-13 15:27:54 +03:00
struct wm_adsp_alg_region alg_region ;
2013-05-08 17:15:35 +04:00
struct wm_coeff_ctl_ops ops ;
2015-04-13 15:27:54 +03:00
struct wm_adsp * dsp ;
2013-05-08 17:15:35 +04:00
unsigned int enabled : 1 ;
struct list_head list ;
void * cache ;
2015-04-13 15:28:02 +03:00
unsigned int offset ;
2013-05-08 17:15:35 +04:00
size_t len ;
2013-05-28 15:01:50 +04:00
unsigned int set : 1 ;
2013-05-08 17:15:35 +04:00
struct snd_kcontrol * kcontrol ;
2015-04-20 15:52:45 +03:00
unsigned int flags ;
2013-05-08 17:15:35 +04:00
} ;
2013-01-12 02:58:28 +04:00
static int wm_adsp_fw_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2014-03-18 12:02:04 +04:00
struct snd_soc_codec * codec = snd_soc_kcontrol_codec ( kcontrol ) ;
2013-01-12 02:58:28 +04:00
struct soc_enum * e = ( struct soc_enum * ) kcontrol - > private_value ;
2015-04-13 15:27:54 +03:00
struct wm_adsp * dsp = snd_soc_codec_get_drvdata ( codec ) ;
2013-01-12 02:58:28 +04:00
2015-04-13 15:27:54 +03:00
ucontrol - > value . integer . value [ 0 ] = dsp [ e - > shift_l ] . fw ;
2013-01-12 02:58:28 +04:00
return 0 ;
}
static int wm_adsp_fw_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2014-03-18 12:02:04 +04:00
struct snd_soc_codec * codec = snd_soc_kcontrol_codec ( kcontrol ) ;
2013-01-12 02:58:28 +04:00
struct soc_enum * e = ( struct soc_enum * ) kcontrol - > private_value ;
2015-04-13 15:27:54 +03:00
struct wm_adsp * dsp = snd_soc_codec_get_drvdata ( codec ) ;
2013-01-12 02:58:28 +04:00
2015-04-13 15:27:54 +03:00
if ( ucontrol - > value . integer . value [ 0 ] = = dsp [ e - > shift_l ] . fw )
2013-01-12 02:58:28 +04:00
return 0 ;
if ( ucontrol - > value . integer . value [ 0 ] > = WM_ADSP_NUM_FW )
return - EINVAL ;
2015-04-13 15:27:54 +03:00
if ( dsp [ e - > shift_l ] . running )
2013-01-12 02:58:28 +04:00
return - EBUSY ;
2015-04-13 15:27:54 +03:00
dsp [ e - > shift_l ] . fw = ucontrol - > value . integer . value [ 0 ] ;
2013-01-12 02:58:28 +04:00
return 0 ;
}
static const struct soc_enum wm_adsp_fw_enum [ ] = {
SOC_ENUM_SINGLE ( 0 , 0 , ARRAY_SIZE ( wm_adsp_fw_text ) , wm_adsp_fw_text ) ,
SOC_ENUM_SINGLE ( 0 , 1 , ARRAY_SIZE ( wm_adsp_fw_text ) , wm_adsp_fw_text ) ,
SOC_ENUM_SINGLE ( 0 , 2 , ARRAY_SIZE ( wm_adsp_fw_text ) , wm_adsp_fw_text ) ,
SOC_ENUM_SINGLE ( 0 , 3 , ARRAY_SIZE ( wm_adsp_fw_text ) , wm_adsp_fw_text ) ,
} ;
2013-03-29 22:00:24 +04:00
const struct snd_kcontrol_new wm_adsp1_fw_controls [ ] = {
2013-01-12 02:58:28 +04:00
SOC_ENUM_EXT ( " DSP1 Firmware " , wm_adsp_fw_enum [ 0 ] ,
wm_adsp_fw_get , wm_adsp_fw_put ) ,
SOC_ENUM_EXT ( " DSP2 Firmware " , wm_adsp_fw_enum [ 1 ] ,
wm_adsp_fw_get , wm_adsp_fw_put ) ,
SOC_ENUM_EXT ( " DSP3 Firmware " , wm_adsp_fw_enum [ 2 ] ,
wm_adsp_fw_get , wm_adsp_fw_put ) ,
2013-03-29 22:00:24 +04:00
} ;
EXPORT_SYMBOL_GPL ( wm_adsp1_fw_controls ) ;
static const struct soc_enum wm_adsp2_rate_enum [ ] = {
2013-02-18 23:09:23 +04:00
SOC_VALUE_ENUM_SINGLE ( ARIZONA_DSP1_CONTROL_1 ,
ARIZONA_DSP1_RATE_SHIFT , 0xf ,
ARIZONA_RATE_ENUM_SIZE ,
arizona_rate_text , arizona_rate_val ) ,
SOC_VALUE_ENUM_SINGLE ( ARIZONA_DSP2_CONTROL_1 ,
ARIZONA_DSP1_RATE_SHIFT , 0xf ,
ARIZONA_RATE_ENUM_SIZE ,
arizona_rate_text , arizona_rate_val ) ,
SOC_VALUE_ENUM_SINGLE ( ARIZONA_DSP3_CONTROL_1 ,
ARIZONA_DSP1_RATE_SHIFT , 0xf ,
ARIZONA_RATE_ENUM_SIZE ,
arizona_rate_text , arizona_rate_val ) ,
2013-06-14 17:19:36 +04:00
SOC_VALUE_ENUM_SINGLE ( ARIZONA_DSP4_CONTROL_1 ,
2013-02-18 23:09:23 +04:00
ARIZONA_DSP1_RATE_SHIFT , 0xf ,
ARIZONA_RATE_ENUM_SIZE ,
arizona_rate_text , arizona_rate_val ) ,
} ;
2015-06-11 13:32:31 +03:00
static const struct snd_kcontrol_new wm_adsp2_fw_controls [ 4 ] [ 2 ] = {
{
SOC_ENUM_EXT ( " DSP1 Firmware " , wm_adsp_fw_enum [ 0 ] ,
wm_adsp_fw_get , wm_adsp_fw_put ) ,
SOC_ENUM ( " DSP1 Rate " , wm_adsp2_rate_enum [ 0 ] ) ,
} ,
{
SOC_ENUM_EXT ( " DSP2 Firmware " , wm_adsp_fw_enum [ 1 ] ,
wm_adsp_fw_get , wm_adsp_fw_put ) ,
SOC_ENUM ( " DSP2 Rate " , wm_adsp2_rate_enum [ 1 ] ) ,
} ,
{
SOC_ENUM_EXT ( " DSP3 Firmware " , wm_adsp_fw_enum [ 2 ] ,
wm_adsp_fw_get , wm_adsp_fw_put ) ,
SOC_ENUM ( " DSP3 Rate " , wm_adsp2_rate_enum [ 2 ] ) ,
} ,
{
SOC_ENUM_EXT ( " DSP4 Firmware " , wm_adsp_fw_enum [ 3 ] ,
wm_adsp_fw_get , wm_adsp_fw_put ) ,
SOC_ENUM ( " DSP4 Rate " , wm_adsp2_rate_enum [ 3 ] ) ,
} ,
2013-01-12 02:58:28 +04:00
} ;
2012-10-11 06:54:02 +04:00
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 ;
}
2015-04-13 15:27:54 +03:00
static unsigned int wm_adsp_region_to_reg ( struct wm_adsp_region const * mem ,
2013-01-08 20:02:06 +04:00
unsigned int offset )
{
2015-04-13 15:27:54 +03:00
if ( WARN_ON ( ! mem ) )
2013-11-05 21:40:00 +04:00
return offset ;
2015-04-13 15:27:54 +03:00
switch ( mem - > type ) {
2013-01-08 20:02:06 +04:00
case WMFW_ADSP1_PM :
2015-04-13 15:27:54 +03:00
return mem - > base + ( offset * 3 ) ;
2013-01-08 20:02:06 +04:00
case WMFW_ADSP1_DM :
2015-04-13 15:27:54 +03:00
return mem - > base + ( offset * 2 ) ;
2013-01-08 20:02:06 +04:00
case WMFW_ADSP2_XM :
2015-04-13 15:27:54 +03:00
return mem - > base + ( offset * 2 ) ;
2013-01-08 20:02:06 +04:00
case WMFW_ADSP2_YM :
2015-04-13 15:27:54 +03:00
return mem - > base + ( offset * 2 ) ;
2013-01-08 20:02:06 +04:00
case WMFW_ADSP1_ZM :
2015-04-13 15:27:54 +03:00
return mem - > base + ( offset * 2 ) ;
2013-01-08 20:02:06 +04:00
default :
2013-11-05 21:40:00 +04:00
WARN ( 1 , " Unknown memory region type " ) ;
2013-01-08 20:02:06 +04:00
return offset ;
}
}
2015-05-29 12:23:07 +03:00
static void wm_adsp2_show_fw_status ( struct wm_adsp * dsp )
{
u16 scratch [ 4 ] ;
int ret ;
ret = regmap_raw_read ( dsp - > regmap , dsp - > base + ADSP2_SCRATCH0 ,
scratch , sizeof ( scratch ) ) ;
if ( ret ) {
adsp_err ( dsp , " Failed to read SCRATCH regs: %d \n " , ret ) ;
return ;
}
adsp_dbg ( dsp , " FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x \n " ,
be16_to_cpu ( scratch [ 0 ] ) ,
be16_to_cpu ( scratch [ 1 ] ) ,
be16_to_cpu ( scratch [ 2 ] ) ,
be16_to_cpu ( scratch [ 3 ] ) ) ;
}
2013-05-08 17:15:35 +04:00
static int wm_coeff_info ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
{
struct wm_coeff_ctl * ctl = ( struct wm_coeff_ctl * ) kcontrol - > private_value ;
uinfo - > type = SNDRV_CTL_ELEM_TYPE_BYTES ;
uinfo - > count = ctl - > len ;
return 0 ;
}
2015-04-13 15:27:58 +03:00
static int wm_coeff_write_control ( struct wm_coeff_ctl * ctl ,
2013-05-08 17:15:35 +04:00
const void * buf , size_t len )
{
2015-04-13 15:27:54 +03:00
struct wm_adsp_alg_region * alg_region = & ctl - > alg_region ;
2013-05-08 17:15:35 +04:00
const struct wm_adsp_region * mem ;
2015-04-13 15:27:54 +03:00
struct wm_adsp * dsp = ctl - > dsp ;
2013-05-08 17:15:35 +04:00
void * scratch ;
int ret ;
unsigned int reg ;
2015-04-13 15:27:54 +03:00
mem = wm_adsp_find_region ( dsp , alg_region - > type ) ;
2013-05-08 17:15:35 +04:00
if ( ! mem ) {
2015-04-13 15:27:54 +03:00
adsp_err ( dsp , " No base for region %x \n " ,
alg_region - > type ) ;
2013-05-08 17:15:35 +04:00
return - EINVAL ;
}
2015-04-13 15:28:02 +03:00
reg = ctl - > alg_region . base + ctl - > offset ;
2013-05-08 17:15:35 +04:00
reg = wm_adsp_region_to_reg ( mem , reg ) ;
scratch = kmemdup ( buf , ctl - > len , GFP_KERNEL | GFP_DMA ) ;
if ( ! scratch )
return - ENOMEM ;
2015-04-13 15:27:54 +03:00
ret = regmap_raw_write ( dsp - > regmap , reg , scratch ,
2013-05-08 17:15:35 +04:00
ctl - > len ) ;
if ( ret ) {
2015-04-13 15:27:54 +03:00
adsp_err ( dsp , " Failed to write %zu bytes to %x: %d \n " ,
2013-11-01 19:56:52 +04:00
ctl - > len , reg , ret ) ;
2013-05-08 17:15:35 +04:00
kfree ( scratch ) ;
return ret ;
}
2015-04-13 15:27:54 +03:00
adsp_dbg ( dsp , " Wrote %zu bytes to %x \n " , ctl - > len , reg ) ;
2013-05-08 17:15:35 +04:00
kfree ( scratch ) ;
return 0 ;
}
static int wm_coeff_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct wm_coeff_ctl * ctl = ( struct wm_coeff_ctl * ) kcontrol - > private_value ;
char * p = ucontrol - > value . bytes . data ;
memcpy ( ctl - > cache , p , ctl - > len ) ;
2015-02-16 18:25:48 +03:00
ctl - > set = 1 ;
if ( ! ctl - > enabled )
2013-05-08 17:15:35 +04:00
return 0 ;
2015-04-13 15:27:58 +03:00
return wm_coeff_write_control ( ctl , p , ctl - > len ) ;
2013-05-08 17:15:35 +04:00
}
2015-04-13 15:27:58 +03:00
static int wm_coeff_read_control ( struct wm_coeff_ctl * ctl ,
2013-05-08 17:15:35 +04:00
void * buf , size_t len )
{
2015-04-13 15:27:54 +03:00
struct wm_adsp_alg_region * alg_region = & ctl - > alg_region ;
2013-05-08 17:15:35 +04:00
const struct wm_adsp_region * mem ;
2015-04-13 15:27:54 +03:00
struct wm_adsp * dsp = ctl - > dsp ;
2013-05-08 17:15:35 +04:00
void * scratch ;
int ret ;
unsigned int reg ;
2015-04-13 15:27:54 +03:00
mem = wm_adsp_find_region ( dsp , alg_region - > type ) ;
2013-05-08 17:15:35 +04:00
if ( ! mem ) {
2015-04-13 15:27:54 +03:00
adsp_err ( dsp , " No base for region %x \n " ,
alg_region - > type ) ;
2013-05-08 17:15:35 +04:00
return - EINVAL ;
}
2015-04-13 15:28:02 +03:00
reg = ctl - > alg_region . base + ctl - > offset ;
2013-05-08 17:15:35 +04:00
reg = wm_adsp_region_to_reg ( mem , reg ) ;
scratch = kmalloc ( ctl - > len , GFP_KERNEL | GFP_DMA ) ;
if ( ! scratch )
return - ENOMEM ;
2015-04-13 15:27:54 +03:00
ret = regmap_raw_read ( dsp - > regmap , reg , scratch , ctl - > len ) ;
2013-05-08 17:15:35 +04:00
if ( ret ) {
2015-04-13 15:27:54 +03:00
adsp_err ( dsp , " Failed to read %zu bytes from %x: %d \n " ,
2013-11-01 19:56:52 +04:00
ctl - > len , reg , ret ) ;
2013-05-08 17:15:35 +04:00
kfree ( scratch ) ;
return ret ;
}
2015-04-13 15:27:54 +03:00
adsp_dbg ( dsp , " Read %zu bytes from %x \n " , ctl - > len , reg ) ;
2013-05-08 17:15:35 +04:00
memcpy ( buf , scratch , ctl - > len ) ;
kfree ( scratch ) ;
return 0 ;
}
static int wm_coeff_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct wm_coeff_ctl * ctl = ( struct wm_coeff_ctl * ) kcontrol - > private_value ;
char * p = ucontrol - > value . bytes . data ;
2015-04-20 15:52:45 +03:00
if ( ctl - > flags & WMFW_CTL_FLAG_VOLATILE ) {
if ( ctl - > enabled )
return wm_coeff_read_control ( ctl , p , ctl - > len ) ;
else
return - EPERM ;
}
2013-05-08 17:15:35 +04:00
memcpy ( p , ctl - > cache , ctl - > len ) ;
2015-04-20 15:52:45 +03:00
2013-05-08 17:15:35 +04:00
return 0 ;
}
struct wmfw_ctl_work {
2015-04-13 15:27:54 +03:00
struct wm_adsp * dsp ;
2013-05-08 17:15:35 +04:00
struct wm_coeff_ctl * ctl ;
struct work_struct work ;
} ;
2015-04-13 15:27:54 +03:00
static int wmfw_add_ctl ( struct wm_adsp * dsp , struct wm_coeff_ctl * ctl )
2013-05-08 17:15:35 +04:00
{
struct snd_kcontrol_new * kcontrol ;
int ret ;
2013-08-01 14:11:28 +04:00
if ( ! ctl | | ! ctl - > name )
2013-05-08 17:15:35 +04:00
return - EINVAL ;
kcontrol = kzalloc ( sizeof ( * kcontrol ) , GFP_KERNEL ) ;
if ( ! kcontrol )
return - ENOMEM ;
kcontrol - > iface = SNDRV_CTL_ELEM_IFACE_MIXER ;
kcontrol - > name = ctl - > name ;
kcontrol - > info = wm_coeff_info ;
kcontrol - > get = wm_coeff_get ;
kcontrol - > put = wm_coeff_put ;
kcontrol - > private_value = ( unsigned long ) ctl ;
2015-04-20 15:52:45 +03:00
if ( ctl - > flags ) {
if ( ctl - > flags & WMFW_CTL_FLAG_WRITEABLE )
kcontrol - > access | = SNDRV_CTL_ELEM_ACCESS_WRITE ;
if ( ctl - > flags & WMFW_CTL_FLAG_READABLE )
kcontrol - > access | = SNDRV_CTL_ELEM_ACCESS_READ ;
if ( ctl - > flags & WMFW_CTL_FLAG_VOLATILE )
kcontrol - > access | = SNDRV_CTL_ELEM_ACCESS_VOLATILE ;
}
2015-04-13 15:27:54 +03:00
ret = snd_soc_add_card_controls ( dsp - > card ,
2013-07-29 16:51:59 +04:00
kcontrol , 1 ) ;
2013-05-08 17:15:35 +04:00
if ( ret < 0 )
goto err_kcontrol ;
kfree ( kcontrol ) ;
2015-04-13 15:27:54 +03:00
ctl - > kcontrol = snd_soc_card_get_kcontrol ( dsp - > card ,
2013-07-29 16:51:59 +04:00
ctl - > name ) ;
2013-05-08 17:15:35 +04:00
return 0 ;
err_kcontrol :
kfree ( kcontrol ) ;
return ret ;
}
2015-04-13 15:28:01 +03:00
static int wm_coeff_init_control_caches ( struct wm_adsp * dsp )
{
struct wm_coeff_ctl * ctl ;
int ret ;
list_for_each_entry ( ctl , & dsp - > ctl_list , list ) {
if ( ! ctl - > enabled | | ctl - > set )
continue ;
2015-04-20 15:52:45 +03:00
if ( ctl - > flags & WMFW_CTL_FLAG_VOLATILE )
continue ;
2015-04-13 15:28:01 +03:00
ret = wm_coeff_read_control ( ctl ,
ctl - > cache ,
ctl - > len ) ;
if ( ret < 0 )
return ret ;
}
return 0 ;
}
static int wm_coeff_sync_controls ( struct wm_adsp * dsp )
{
struct wm_coeff_ctl * ctl ;
int ret ;
list_for_each_entry ( ctl , & dsp - > ctl_list , list ) {
if ( ! ctl - > enabled )
continue ;
2015-04-20 15:52:45 +03:00
if ( ctl - > set & & ! ( ctl - > flags & WMFW_CTL_FLAG_VOLATILE ) ) {
2015-04-13 15:28:01 +03:00
ret = wm_coeff_write_control ( ctl ,
ctl - > cache ,
ctl - > len ) ;
if ( ret < 0 )
return ret ;
}
}
return 0 ;
}
static void wm_adsp_ctl_work ( struct work_struct * work )
{
struct wmfw_ctl_work * ctl_work = container_of ( work ,
struct wmfw_ctl_work ,
work ) ;
wmfw_add_ctl ( ctl_work - > dsp , ctl_work - > ctl ) ;
kfree ( ctl_work ) ;
}
static int wm_adsp_create_control ( struct wm_adsp * dsp ,
const struct wm_adsp_alg_region * alg_region ,
2015-04-13 15:28:02 +03:00
unsigned int offset , unsigned int len ,
2015-04-20 15:52:45 +03:00
const char * subname , unsigned int subname_len ,
unsigned int flags )
2015-04-13 15:28:01 +03:00
{
struct wm_coeff_ctl * ctl ;
struct wmfw_ctl_work * ctl_work ;
char name [ SNDRV_CTL_ELEM_ID_NAME_MAXLEN ] ;
char * region_name ;
int ret ;
2015-04-20 15:52:45 +03:00
if ( flags & WMFW_CTL_FLAG_SYS )
return 0 ;
2015-04-13 15:28:01 +03:00
switch ( alg_region - > type ) {
case WMFW_ADSP1_PM :
region_name = " PM " ;
break ;
case WMFW_ADSP1_DM :
region_name = " DM " ;
break ;
case WMFW_ADSP2_XM :
region_name = " XM " ;
break ;
case WMFW_ADSP2_YM :
region_name = " YM " ;
break ;
case WMFW_ADSP1_ZM :
region_name = " ZM " ;
break ;
default :
2015-04-13 15:28:02 +03:00
adsp_err ( dsp , " Unknown region type: %d \n " , alg_region - > type ) ;
2015-04-13 15:28:01 +03:00
return - EINVAL ;
}
2015-04-13 15:28:04 +03:00
switch ( dsp - > fw_ver ) {
case 0 :
case 1 :
snprintf ( name , SNDRV_CTL_ELEM_ID_NAME_MAXLEN , " DSP%d %s %x " ,
dsp - > num , region_name , alg_region - > alg ) ;
break ;
default :
ret = snprintf ( name , SNDRV_CTL_ELEM_ID_NAME_MAXLEN ,
" DSP%d%c %.12s %x " , dsp - > num , * region_name ,
wm_adsp_fw_text [ dsp - > fw ] , alg_region - > alg ) ;
/* Truncate the subname from the start if it is too long */
if ( subname ) {
int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2 ;
int skip = 0 ;
if ( subname_len > avail )
skip = subname_len - avail ;
snprintf ( name + ret ,
SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret , " %.*s " ,
subname_len - skip , subname + skip ) ;
}
break ;
}
2015-04-13 15:28:01 +03:00
list_for_each_entry ( ctl , & dsp - > ctl_list ,
list ) {
if ( ! strcmp ( ctl - > name , name ) ) {
if ( ! ctl - > enabled )
ctl - > enabled = 1 ;
return 0 ;
}
}
ctl = kzalloc ( sizeof ( * ctl ) , GFP_KERNEL ) ;
if ( ! ctl )
return - ENOMEM ;
2015-04-13 15:28:02 +03:00
ctl - > fw_name = wm_adsp_fw_text [ dsp - > fw ] ;
2015-04-13 15:28:01 +03:00
ctl - > alg_region = * alg_region ;
ctl - > name = kmemdup ( name , strlen ( name ) + 1 , GFP_KERNEL ) ;
if ( ! ctl - > name ) {
ret = - ENOMEM ;
goto err_ctl ;
}
ctl - > enabled = 1 ;
ctl - > set = 0 ;
ctl - > ops . xget = wm_coeff_get ;
ctl - > ops . xput = wm_coeff_put ;
ctl - > dsp = dsp ;
2015-04-20 15:52:45 +03:00
ctl - > flags = flags ;
2015-04-13 15:28:02 +03:00
ctl - > offset = offset ;
2015-04-13 15:28:01 +03:00
if ( len > 512 ) {
adsp_warn ( dsp , " Truncating control %s from %d \n " ,
ctl - > name , len ) ;
len = 512 ;
}
ctl - > len = len ;
ctl - > cache = kzalloc ( ctl - > len , GFP_KERNEL ) ;
if ( ! ctl - > cache ) {
ret = - ENOMEM ;
goto err_ctl_name ;
}
2015-04-13 15:28:02 +03:00
list_add ( & ctl - > list , & dsp - > ctl_list ) ;
2015-04-13 15:28:01 +03:00
ctl_work = kzalloc ( sizeof ( * ctl_work ) , GFP_KERNEL ) ;
if ( ! ctl_work ) {
ret = - ENOMEM ;
goto err_ctl_cache ;
}
ctl_work - > dsp = dsp ;
ctl_work - > ctl = ctl ;
INIT_WORK ( & ctl_work - > work , wm_adsp_ctl_work ) ;
schedule_work ( & ctl_work - > work ) ;
return 0 ;
err_ctl_cache :
kfree ( ctl - > cache ) ;
err_ctl_name :
kfree ( ctl - > name ) ;
err_ctl :
kfree ( ctl ) ;
return ret ;
}
2015-04-13 15:28:02 +03:00
struct wm_coeff_parsed_alg {
int id ;
const u8 * name ;
int name_len ;
int ncoeff ;
} ;
struct wm_coeff_parsed_coeff {
int offset ;
int mem_type ;
const u8 * name ;
int name_len ;
int ctl_type ;
int flags ;
int len ;
} ;
2015-04-13 15:28:04 +03:00
static int wm_coeff_parse_string ( int bytes , const u8 * * pos , const u8 * * str )
{
int length ;
switch ( bytes ) {
case 1 :
length = * * pos ;
break ;
case 2 :
2015-04-20 15:52:44 +03:00
length = le16_to_cpu ( * ( ( __le16 * ) * pos ) ) ;
2015-04-13 15:28:04 +03:00
break ;
default :
return 0 ;
}
if ( str )
* str = * pos + bytes ;
* pos + = ( ( length + bytes ) + 3 ) & ~ 0x03 ;
return length ;
}
static int wm_coeff_parse_int ( int bytes , const u8 * * pos )
{
int val = 0 ;
switch ( bytes ) {
case 2 :
2015-04-20 15:52:44 +03:00
val = le16_to_cpu ( * ( ( __le16 * ) * pos ) ) ;
2015-04-13 15:28:04 +03:00
break ;
case 4 :
2015-04-20 15:52:44 +03:00
val = le32_to_cpu ( * ( ( __le32 * ) * pos ) ) ;
2015-04-13 15:28:04 +03:00
break ;
default :
break ;
}
* pos + = bytes ;
return val ;
}
2015-04-13 15:28:02 +03:00
static inline void wm_coeff_parse_alg ( struct wm_adsp * dsp , const u8 * * data ,
struct wm_coeff_parsed_alg * blk )
{
const struct wmfw_adsp_alg_data * raw ;
2015-04-13 15:28:04 +03:00
switch ( dsp - > fw_ver ) {
case 0 :
case 1 :
raw = ( const struct wmfw_adsp_alg_data * ) * data ;
* data = raw - > data ;
2015-04-13 15:28:02 +03:00
2015-04-13 15:28:04 +03:00
blk - > id = le32_to_cpu ( raw - > id ) ;
blk - > name = raw - > name ;
blk - > name_len = strlen ( raw - > name ) ;
blk - > ncoeff = le32_to_cpu ( raw - > ncoeff ) ;
break ;
default :
blk - > id = wm_coeff_parse_int ( sizeof ( raw - > id ) , data ) ;
blk - > name_len = wm_coeff_parse_string ( sizeof ( u8 ) , data ,
& blk - > name ) ;
wm_coeff_parse_string ( sizeof ( u16 ) , data , NULL ) ;
blk - > ncoeff = wm_coeff_parse_int ( sizeof ( raw - > ncoeff ) , data ) ;
break ;
}
2015-04-13 15:28:02 +03:00
adsp_dbg ( dsp , " Algorithm ID: %#x \n " , blk - > id ) ;
adsp_dbg ( dsp , " Algorithm name: %.*s \n " , blk - > name_len , blk - > name ) ;
adsp_dbg ( dsp , " # of coefficient descriptors: %#x \n " , blk - > ncoeff ) ;
}
static inline void wm_coeff_parse_coeff ( struct wm_adsp * dsp , const u8 * * data ,
struct wm_coeff_parsed_coeff * blk )
{
const struct wmfw_adsp_coeff_data * raw ;
2015-04-13 15:28:04 +03:00
const u8 * tmp ;
int length ;
2015-04-13 15:28:02 +03:00
2015-04-13 15:28:04 +03:00
switch ( dsp - > fw_ver ) {
case 0 :
case 1 :
raw = ( const struct wmfw_adsp_coeff_data * ) * data ;
* data = * data + sizeof ( raw - > hdr ) + le32_to_cpu ( raw - > hdr . size ) ;
blk - > offset = le16_to_cpu ( raw - > hdr . offset ) ;
blk - > mem_type = le16_to_cpu ( raw - > hdr . type ) ;
blk - > name = raw - > name ;
blk - > name_len = strlen ( raw - > name ) ;
blk - > ctl_type = le16_to_cpu ( raw - > ctl_type ) ;
blk - > flags = le16_to_cpu ( raw - > flags ) ;
blk - > len = le32_to_cpu ( raw - > len ) ;
break ;
default :
tmp = * data ;
blk - > offset = wm_coeff_parse_int ( sizeof ( raw - > hdr . offset ) , & tmp ) ;
blk - > mem_type = wm_coeff_parse_int ( sizeof ( raw - > hdr . type ) , & tmp ) ;
length = wm_coeff_parse_int ( sizeof ( raw - > hdr . size ) , & tmp ) ;
blk - > name_len = wm_coeff_parse_string ( sizeof ( u8 ) , & tmp ,
& blk - > name ) ;
wm_coeff_parse_string ( sizeof ( u8 ) , & tmp , NULL ) ;
wm_coeff_parse_string ( sizeof ( u16 ) , & tmp , NULL ) ;
blk - > ctl_type = wm_coeff_parse_int ( sizeof ( raw - > ctl_type ) , & tmp ) ;
blk - > flags = wm_coeff_parse_int ( sizeof ( raw - > flags ) , & tmp ) ;
blk - > len = wm_coeff_parse_int ( sizeof ( raw - > len ) , & tmp ) ;
* data = * data + sizeof ( raw - > hdr ) + length ;
break ;
}
2015-04-13 15:28:02 +03:00
adsp_dbg ( dsp , " \t Coefficient type: %#x \n " , blk - > mem_type ) ;
adsp_dbg ( dsp , " \t Coefficient offset: %#x \n " , blk - > offset ) ;
adsp_dbg ( dsp , " \t Coefficient name: %.*s \n " , blk - > name_len , blk - > name ) ;
adsp_dbg ( dsp , " \t Coefficient flags: %#x \n " , blk - > flags ) ;
adsp_dbg ( dsp , " \t ALSA control type: %#x \n " , blk - > ctl_type ) ;
adsp_dbg ( dsp , " \t ALSA control len: %#x \n " , blk - > len ) ;
}
static int wm_adsp_parse_coeff ( struct wm_adsp * dsp ,
const struct wmfw_region * region )
{
struct wm_adsp_alg_region alg_region = { } ;
struct wm_coeff_parsed_alg alg_blk ;
struct wm_coeff_parsed_coeff coeff_blk ;
const u8 * data = region - > data ;
int i , ret ;
wm_coeff_parse_alg ( dsp , & data , & alg_blk ) ;
for ( i = 0 ; i < alg_blk . ncoeff ; i + + ) {
wm_coeff_parse_coeff ( dsp , & data , & coeff_blk ) ;
switch ( coeff_blk . ctl_type ) {
case SNDRV_CTL_ELEM_TYPE_BYTES :
break ;
default :
adsp_err ( dsp , " Unknown control type: %d \n " ,
coeff_blk . ctl_type ) ;
return - EINVAL ;
}
alg_region . type = coeff_blk . mem_type ;
alg_region . alg = alg_blk . id ;
ret = wm_adsp_create_control ( dsp , & alg_region ,
coeff_blk . offset ,
coeff_blk . len ,
coeff_blk . name ,
2015-04-20 15:52:45 +03:00
coeff_blk . name_len ,
coeff_blk . flags ) ;
2015-04-13 15:28:02 +03:00
if ( ret < 0 )
adsp_err ( dsp , " Failed to create control: %.*s, %d \n " ,
coeff_blk . name_len , coeff_blk . name , ret ) ;
}
return 0 ;
}
2012-10-11 06:54:02 +04:00
static int wm_adsp_load ( struct wm_adsp * dsp )
{
2013-01-30 10:37:23 +04:00
LIST_HEAD ( buf_list ) ;
2012-10-11 06:54:02 +04:00
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 ;
2013-01-30 10:37:23 +04:00
struct wm_adsp_buf * buf ;
2012-10-11 06:54:02 +04:00
unsigned int reg ;
int regions = 0 ;
int ret , offset , type , sizes ;
file = kzalloc ( PAGE_SIZE , GFP_KERNEL ) ;
if ( file = = NULL )
return - ENOMEM ;
2013-01-12 02:58:28 +04:00
snprintf ( file , PAGE_SIZE , " %s-dsp%d-%s.wmfw " , dsp - > part , dsp - > num ,
wm_adsp_fw [ dsp - > fw ] . file ) ;
2012-10-11 06:54:02 +04:00
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 ;
}
2015-04-13 15:28:02 +03:00
switch ( header - > ver ) {
case 0 :
2015-04-13 15:28:05 +03:00
adsp_warn ( dsp , " %s: Depreciated file format %d \n " ,
file , header - > ver ) ;
break ;
2015-04-13 15:28:02 +03:00
case 1 :
2015-04-13 15:28:04 +03:00
case 2 :
2015-04-13 15:28:02 +03:00
break ;
default :
2012-10-11 06:54:02 +04:00
adsp_err ( dsp , " %s: unknown file format %d \n " ,
file , header - > ver ) ;
goto out_fw ;
}
2015-04-13 15:28:02 +03:00
2013-11-01 19:56:57 +04:00
adsp_info ( dsp , " Firmware version: %d \n " , header - > ver ) ;
2015-04-13 15:28:02 +03:00
dsp - > fw_ver = header - > ver ;
2012-10-11 06:54:02 +04:00
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 :
2013-11-05 21:40:00 +04:00
WARN ( 1 , " Unknown DSP type " ) ;
2012-10-11 06:54:02 +04:00
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 ;
2015-04-13 15:28:02 +03:00
case WMFW_ALGORITHM_DATA :
region_name = " Algorithm " ;
ret = wm_adsp_parse_coeff ( dsp , region ) ;
if ( ret ! = 0 )
goto out_fw ;
break ;
2012-10-11 06:54:02 +04:00
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 :
region_name = " PM " ;
2013-01-08 20:02:06 +04:00
reg = wm_adsp_region_to_reg ( mem , offset ) ;
2012-10-11 06:54:02 +04:00
break ;
case WMFW_ADSP1_DM :
region_name = " DM " ;
2013-01-08 20:02:06 +04:00
reg = wm_adsp_region_to_reg ( mem , offset ) ;
2012-10-11 06:54:02 +04:00
break ;
case WMFW_ADSP2_XM :
region_name = " XM " ;
2013-01-08 20:02:06 +04:00
reg = wm_adsp_region_to_reg ( mem , offset ) ;
2012-10-11 06:54:02 +04:00
break ;
case WMFW_ADSP2_YM :
region_name = " YM " ;
2013-01-08 20:02:06 +04:00
reg = wm_adsp_region_to_reg ( mem , offset ) ;
2012-10-11 06:54:02 +04:00
break ;
case WMFW_ADSP1_ZM :
region_name = " ZM " ;
2013-01-08 20:02:06 +04:00
reg = wm_adsp_region_to_reg ( mem , offset ) ;
2012-10-11 06:54:02 +04:00
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 ) {
2014-11-14 18:40:45 +03:00
buf = wm_adsp_buf_alloc ( region - > data ,
le32_to_cpu ( region - > len ) ,
& buf_list ) ;
if ( ! buf ) {
adsp_err ( dsp , " Out of memory \n " ) ;
ret = - ENOMEM ;
goto out_fw ;
}
2014-03-05 18:28:16 +04:00
2014-11-14 18:40:45 +03:00
ret = regmap_raw_write_async ( regmap , reg , buf - > buf ,
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 ;
2012-10-11 06:54:02 +04:00
}
}
pos + = le32_to_cpu ( region - > len ) + sizeof ( * region ) ;
regions + + ;
}
2013-01-30 10:37:23 +04:00
ret = regmap_async_complete ( regmap ) ;
if ( ret ! = 0 ) {
adsp_err ( dsp , " Failed to complete async write: %d \n " , ret ) ;
goto out_fw ;
}
2012-10-11 06:54:02 +04:00
if ( pos > firmware - > size )
adsp_warn ( dsp , " %s.%d: %zu bytes at end of file \n " ,
file , regions , pos - firmware - > size ) ;
out_fw :
2013-01-30 10:37:23 +04:00
regmap_async_complete ( regmap ) ;
wm_adsp_buf_free ( & buf_list ) ;
2012-10-11 06:54:02 +04:00
release_firmware ( firmware ) ;
out :
kfree ( file ) ;
return ret ;
}
2015-04-13 15:28:02 +03:00
static void wm_adsp_ctl_fixup_base ( struct wm_adsp * dsp ,
const struct wm_adsp_alg_region * alg_region )
{
struct wm_coeff_ctl * ctl ;
list_for_each_entry ( ctl , & dsp - > ctl_list , list ) {
if ( ctl - > fw_name = = wm_adsp_fw_text [ dsp - > fw ] & &
alg_region - > alg = = ctl - > alg_region . alg & &
alg_region - > type = = ctl - > alg_region . type ) {
ctl - > alg_region . base = alg_region - > base ;
}
}
}
2015-04-13 15:27:54 +03:00
static void * wm_adsp_read_algs ( struct wm_adsp * dsp , size_t n_algs ,
2015-04-13 15:27:53 +03:00
unsigned int pos , unsigned int len )
2012-10-26 22:30:40 +04:00
{
2015-04-13 15:27:53 +03:00
void * alg ;
int ret ;
2012-10-26 22:30:40 +04:00
__be32 val ;
2015-04-13 15:27:54 +03:00
if ( n_algs = = 0 ) {
2015-04-13 15:27:53 +03:00
adsp_err ( dsp , " No algorithms \n " ) ;
return ERR_PTR ( - EINVAL ) ;
2012-10-26 22:30:40 +04:00
}
2015-04-13 15:27:54 +03:00
if ( n_algs > 1024 ) {
adsp_err ( dsp , " Algorithm count %zx excessive \n " , n_algs ) ;
2015-04-13 15:27:53 +03:00
return ERR_PTR ( - EINVAL ) ;
}
2012-10-26 22:30:40 +04:00
2015-04-13 15:27:53 +03:00
/* Read the terminator first to validate the length */
ret = regmap_raw_read ( dsp - > regmap , pos + len , & val , sizeof ( val ) ) ;
if ( ret ! = 0 ) {
adsp_err ( dsp , " Failed to read algorithm list end: %d \n " ,
ret ) ;
return ERR_PTR ( ret ) ;
}
2012-10-26 22:30:40 +04:00
2015-04-13 15:27:53 +03:00
if ( be32_to_cpu ( val ) ! = 0xbedead )
adsp_warn ( dsp , " Algorithm list end %x 0x%x != 0xbeadead \n " ,
pos + len , be32_to_cpu ( val ) ) ;
2012-12-19 18:00:30 +04:00
2015-04-13 15:27:53 +03:00
alg = kzalloc ( len * 2 , GFP_KERNEL | GFP_DMA ) ;
if ( ! alg )
return ERR_PTR ( - ENOMEM ) ;
2012-10-26 22:30:40 +04:00
2015-04-13 15:27:53 +03:00
ret = regmap_raw_read ( dsp - > regmap , pos , alg , len * 2 ) ;
if ( ret ! = 0 ) {
adsp_err ( dsp , " Failed to read algorithm list: %d \n " ,
ret ) ;
kfree ( alg ) ;
return ERR_PTR ( ret ) ;
}
2013-04-09 20:08:24 +04:00
2015-04-13 15:27:53 +03:00
return alg ;
}
2013-04-09 20:08:24 +04:00
2015-04-13 15:27:59 +03:00
static struct wm_adsp_alg_region * wm_adsp_create_region ( struct wm_adsp * dsp ,
int type , __be32 id ,
__be32 base )
{
struct wm_adsp_alg_region * alg_region ;
alg_region = kzalloc ( sizeof ( * alg_region ) , GFP_KERNEL ) ;
if ( ! alg_region )
return ERR_PTR ( - ENOMEM ) ;
alg_region - > type = type ;
alg_region - > alg = be32_to_cpu ( id ) ;
alg_region - > base = be32_to_cpu ( base ) ;
list_add_tail ( & alg_region - > list , & dsp - > alg_regions ) ;
2015-04-13 15:28:02 +03:00
if ( dsp - > fw_ver > 0 )
wm_adsp_ctl_fixup_base ( dsp , alg_region ) ;
2015-04-13 15:27:59 +03:00
return alg_region ;
}
2015-04-13 15:27:53 +03:00
static int wm_adsp1_setup_algs ( struct wm_adsp * dsp )
{
struct wmfw_adsp1_id_hdr adsp1_id ;
struct wmfw_adsp1_alg_hdr * adsp1_alg ;
2015-04-13 15:27:54 +03:00
struct wm_adsp_alg_region * alg_region ;
2015-04-13 15:27:53 +03:00
const struct wm_adsp_region * mem ;
unsigned int pos , len ;
2015-04-13 15:27:54 +03:00
size_t n_algs ;
2015-04-13 15:27:53 +03:00
int i , ret ;
2012-10-26 22:30:40 +04:00
2015-04-13 15:27:53 +03:00
mem = wm_adsp_find_region ( dsp , WMFW_ADSP1_DM ) ;
if ( WARN_ON ( ! mem ) )
return - EINVAL ;
ret = regmap_raw_read ( dsp - > regmap , mem - > base , & adsp1_id ,
sizeof ( adsp1_id ) ) ;
if ( ret ! = 0 ) {
adsp_err ( dsp , " Failed to read algorithm info: %d \n " ,
ret ) ;
return ret ;
}
2012-10-26 22:30:40 +04:00
2015-04-13 15:27:54 +03:00
n_algs = be32_to_cpu ( adsp1_id . n_algs ) ;
2015-04-13 15:27:53 +03:00
dsp - > fw_id = be32_to_cpu ( adsp1_id . fw . id ) ;
adsp_info ( dsp , " Firmware: %x v%d.%d.%d, %zu algorithms \n " ,
dsp - > fw_id ,
( be32_to_cpu ( adsp1_id . fw . ver ) & 0xff0000 ) > > 16 ,
( be32_to_cpu ( adsp1_id . fw . ver ) & 0xff00 ) > > 8 ,
be32_to_cpu ( adsp1_id . fw . ver ) & 0xff ,
2015-04-13 15:27:54 +03:00
n_algs ) ;
2015-04-13 15:27:53 +03:00
2015-04-13 15:27:59 +03:00
alg_region = wm_adsp_create_region ( dsp , WMFW_ADSP1_ZM ,
adsp1_id . fw . id , adsp1_id . zm ) ;
if ( IS_ERR ( alg_region ) )
return PTR_ERR ( alg_region ) ;
2012-12-19 18:00:30 +04:00
2015-04-13 15:27:59 +03:00
alg_region = wm_adsp_create_region ( dsp , WMFW_ADSP1_DM ,
adsp1_id . fw . id , adsp1_id . dm ) ;
if ( IS_ERR ( alg_region ) )
return PTR_ERR ( alg_region ) ;
2012-10-26 22:30:40 +04:00
2015-04-13 15:27:53 +03:00
pos = sizeof ( adsp1_id ) / 2 ;
2015-04-13 15:27:54 +03:00
len = ( sizeof ( * adsp1_alg ) * n_algs ) / 2 ;
2015-04-13 15:27:53 +03:00
2015-04-13 15:27:54 +03:00
adsp1_alg = wm_adsp_read_algs ( dsp , n_algs , mem - > base + pos , len ) ;
2015-04-13 15:27:53 +03:00
if ( IS_ERR ( adsp1_alg ) )
return PTR_ERR ( adsp1_alg ) ;
2015-04-13 15:27:54 +03:00
for ( i = 0 ; i < n_algs ; i + + ) {
2015-04-13 15:27:53 +03:00
adsp_info ( dsp , " %d: ID %x v%d.%d.%d DM@%x ZM@%x \n " ,
i , be32_to_cpu ( adsp1_alg [ i ] . alg . id ) ,
( be32_to_cpu ( adsp1_alg [ i ] . alg . ver ) & 0xff0000 ) > > 16 ,
( be32_to_cpu ( adsp1_alg [ i ] . alg . ver ) & 0xff00 ) > > 8 ,
be32_to_cpu ( adsp1_alg [ i ] . alg . ver ) & 0xff ,
be32_to_cpu ( adsp1_alg [ i ] . dm ) ,
be32_to_cpu ( adsp1_alg [ i ] . zm ) ) ;
2013-04-09 20:08:24 +04:00
2015-04-13 15:27:59 +03:00
alg_region = wm_adsp_create_region ( dsp , WMFW_ADSP1_DM ,
adsp1_alg [ i ] . alg . id ,
adsp1_alg [ i ] . dm ) ;
if ( IS_ERR ( alg_region ) ) {
ret = PTR_ERR ( alg_region ) ;
2015-04-13 15:27:53 +03:00
goto out ;
}
2015-04-13 15:28:02 +03:00
if ( dsp - > fw_ver = = 0 ) {
if ( i + 1 < n_algs ) {
len = be32_to_cpu ( adsp1_alg [ i + 1 ] . dm ) ;
len - = be32_to_cpu ( adsp1_alg [ i ] . dm ) ;
len * = 4 ;
wm_adsp_create_control ( dsp , alg_region , 0 ,
2015-04-20 15:52:45 +03:00
len , NULL , 0 , 0 ) ;
2015-04-13 15:28:02 +03:00
} else {
adsp_warn ( dsp , " Missing length info for region DM with ID %x \n " ,
be32_to_cpu ( adsp1_alg [ i ] . alg . id ) ) ;
}
2015-04-13 15:27:53 +03:00
}
2013-04-09 20:08:24 +04:00
2015-04-13 15:27:59 +03:00
alg_region = wm_adsp_create_region ( dsp , WMFW_ADSP1_ZM ,
adsp1_alg [ i ] . alg . id ,
adsp1_alg [ i ] . zm ) ;
if ( IS_ERR ( alg_region ) ) {
ret = PTR_ERR ( alg_region ) ;
2015-04-13 15:27:53 +03:00
goto out ;
}
2015-04-13 15:28:02 +03:00
if ( dsp - > fw_ver = = 0 ) {
if ( i + 1 < n_algs ) {
len = be32_to_cpu ( adsp1_alg [ i + 1 ] . zm ) ;
len - = be32_to_cpu ( adsp1_alg [ i ] . zm ) ;
len * = 4 ;
wm_adsp_create_control ( dsp , alg_region , 0 ,
2015-04-20 15:52:45 +03:00
len , NULL , 0 , 0 ) ;
2015-04-13 15:28:02 +03:00
} else {
adsp_warn ( dsp , " Missing length info for region ZM with ID %x \n " ,
be32_to_cpu ( adsp1_alg [ i ] . alg . id ) ) ;
}
2015-04-13 15:27:53 +03:00
}
2012-10-26 22:30:40 +04:00
}
2015-04-13 15:27:53 +03:00
out :
kfree ( adsp1_alg ) ;
return ret ;
}
2012-10-26 22:30:40 +04:00
2015-04-13 15:27:53 +03:00
static int wm_adsp2_setup_algs ( struct wm_adsp * dsp )
{
struct wmfw_adsp2_id_hdr adsp2_id ;
struct wmfw_adsp2_alg_hdr * adsp2_alg ;
2015-04-13 15:27:54 +03:00
struct wm_adsp_alg_region * alg_region ;
2015-04-13 15:27:53 +03:00
const struct wm_adsp_region * mem ;
unsigned int pos , len ;
2015-04-13 15:27:54 +03:00
size_t n_algs ;
2015-04-13 15:27:53 +03:00
int i , ret ;
mem = wm_adsp_find_region ( dsp , WMFW_ADSP2_XM ) ;
if ( WARN_ON ( ! mem ) )
2012-12-19 18:00:30 +04:00
return - EINVAL ;
2015-04-13 15:27:53 +03:00
ret = regmap_raw_read ( dsp - > regmap , mem - > base , & adsp2_id ,
sizeof ( adsp2_id ) ) ;
2012-10-26 22:30:40 +04:00
if ( ret ! = 0 ) {
2015-04-13 15:27:53 +03:00
adsp_err ( dsp , " Failed to read algorithm info: %d \n " ,
ret ) ;
2012-10-26 22:30:40 +04:00
return ret ;
}
2015-04-13 15:27:54 +03:00
n_algs = be32_to_cpu ( adsp2_id . n_algs ) ;
2015-04-13 15:27:53 +03:00
dsp - > fw_id = be32_to_cpu ( adsp2_id . fw . id ) ;
adsp_info ( dsp , " Firmware: %x v%d.%d.%d, %zu algorithms \n " ,
dsp - > fw_id ,
( be32_to_cpu ( adsp2_id . fw . ver ) & 0xff0000 ) > > 16 ,
( be32_to_cpu ( adsp2_id . fw . ver ) & 0xff00 ) > > 8 ,
be32_to_cpu ( adsp2_id . fw . ver ) & 0xff ,
2015-04-13 15:27:54 +03:00
n_algs ) ;
2015-04-13 15:27:53 +03:00
2015-04-13 15:27:59 +03:00
alg_region = wm_adsp_create_region ( dsp , WMFW_ADSP2_XM ,
adsp2_id . fw . id , adsp2_id . xm ) ;
if ( IS_ERR ( alg_region ) )
return PTR_ERR ( alg_region ) ;
2012-10-26 22:30:40 +04:00
2015-04-13 15:27:59 +03:00
alg_region = wm_adsp_create_region ( dsp , WMFW_ADSP2_YM ,
adsp2_id . fw . id , adsp2_id . ym ) ;
if ( IS_ERR ( alg_region ) )
return PTR_ERR ( alg_region ) ;
2012-10-26 22:30:40 +04:00
2015-04-13 15:27:59 +03:00
alg_region = wm_adsp_create_region ( dsp , WMFW_ADSP2_ZM ,
adsp2_id . fw . id , adsp2_id . zm ) ;
if ( IS_ERR ( alg_region ) )
return PTR_ERR ( alg_region ) ;
2012-10-26 22:30:40 +04:00
2015-04-13 15:27:53 +03:00
pos = sizeof ( adsp2_id ) / 2 ;
2015-04-13 15:27:54 +03:00
len = ( sizeof ( * adsp2_alg ) * n_algs ) / 2 ;
2012-10-26 22:30:40 +04:00
2015-04-13 15:27:54 +03:00
adsp2_alg = wm_adsp_read_algs ( dsp , n_algs , mem - > base + pos , len ) ;
2015-04-13 15:27:53 +03:00
if ( IS_ERR ( adsp2_alg ) )
return PTR_ERR ( adsp2_alg ) ;
2013-01-08 20:09:31 +04:00
2015-04-13 15:27:54 +03:00
for ( i = 0 ; i < n_algs ; i + + ) {
2015-04-13 15:27:53 +03:00
adsp_info ( dsp ,
" %d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x \n " ,
i , be32_to_cpu ( adsp2_alg [ i ] . alg . id ) ,
( be32_to_cpu ( adsp2_alg [ i ] . alg . ver ) & 0xff0000 ) > > 16 ,
( be32_to_cpu ( adsp2_alg [ i ] . alg . ver ) & 0xff00 ) > > 8 ,
be32_to_cpu ( adsp2_alg [ i ] . alg . ver ) & 0xff ,
be32_to_cpu ( adsp2_alg [ i ] . xm ) ,
be32_to_cpu ( adsp2_alg [ i ] . ym ) ,
be32_to_cpu ( adsp2_alg [ i ] . zm ) ) ;
2012-10-26 22:30:40 +04:00
2015-04-13 15:27:59 +03:00
alg_region = wm_adsp_create_region ( dsp , WMFW_ADSP2_XM ,
adsp2_alg [ i ] . alg . id ,
adsp2_alg [ i ] . xm ) ;
if ( IS_ERR ( alg_region ) ) {
ret = PTR_ERR ( alg_region ) ;
2015-04-13 15:27:53 +03:00
goto out ;
}
2015-04-13 15:28:02 +03:00
if ( dsp - > fw_ver = = 0 ) {
if ( i + 1 < n_algs ) {
len = be32_to_cpu ( adsp2_alg [ i + 1 ] . xm ) ;
len - = be32_to_cpu ( adsp2_alg [ i ] . xm ) ;
len * = 4 ;
wm_adsp_create_control ( dsp , alg_region , 0 ,
2015-04-20 15:52:45 +03:00
len , NULL , 0 , 0 ) ;
2015-04-13 15:28:02 +03:00
} else {
adsp_warn ( dsp , " Missing length info for region XM with ID %x \n " ,
be32_to_cpu ( adsp2_alg [ i ] . alg . id ) ) ;
}
2015-04-13 15:27:53 +03:00
}
2013-01-08 20:09:31 +04:00
2015-04-13 15:27:59 +03:00
alg_region = wm_adsp_create_region ( dsp , WMFW_ADSP2_YM ,
adsp2_alg [ i ] . alg . id ,
adsp2_alg [ i ] . ym ) ;
if ( IS_ERR ( alg_region ) ) {
ret = PTR_ERR ( alg_region ) ;
2015-04-13 15:27:53 +03:00
goto out ;
}
2015-04-13 15:28:02 +03:00
if ( dsp - > fw_ver = = 0 ) {
if ( i + 1 < n_algs ) {
len = be32_to_cpu ( adsp2_alg [ i + 1 ] . ym ) ;
len - = be32_to_cpu ( adsp2_alg [ i ] . ym ) ;
len * = 4 ;
wm_adsp_create_control ( dsp , alg_region , 0 ,
2015-04-20 15:52:45 +03:00
len , NULL , 0 , 0 ) ;
2015-04-13 15:28:02 +03:00
} else {
adsp_warn ( dsp , " Missing length info for region YM with ID %x \n " ,
be32_to_cpu ( adsp2_alg [ i ] . alg . id ) ) ;
}
2015-04-13 15:27:53 +03:00
}
2013-01-08 20:09:31 +04:00
2015-04-13 15:27:59 +03:00
alg_region = wm_adsp_create_region ( dsp , WMFW_ADSP2_ZM ,
adsp2_alg [ i ] . alg . id ,
adsp2_alg [ i ] . zm ) ;
if ( IS_ERR ( alg_region ) ) {
ret = PTR_ERR ( alg_region ) ;
2015-04-13 15:27:53 +03:00
goto out ;
}
2015-04-13 15:28:02 +03:00
if ( dsp - > fw_ver = = 0 ) {
if ( i + 1 < n_algs ) {
len = be32_to_cpu ( adsp2_alg [ i + 1 ] . zm ) ;
len - = be32_to_cpu ( adsp2_alg [ i ] . zm ) ;
len * = 4 ;
wm_adsp_create_control ( dsp , alg_region , 0 ,
2015-04-20 15:52:45 +03:00
len , NULL , 0 , 0 ) ;
2015-04-13 15:28:02 +03:00
} else {
adsp_warn ( dsp , " Missing length info for region ZM with ID %x \n " ,
be32_to_cpu ( adsp2_alg [ i ] . alg . id ) ) ;
}
2012-10-26 22:30:40 +04:00
}
}
out :
2015-04-13 15:27:53 +03:00
kfree ( adsp2_alg ) ;
2012-10-26 22:30:40 +04:00
return ret ;
}
2012-10-11 06:54:02 +04:00
static int wm_adsp_load_coeff ( struct wm_adsp * dsp )
{
2013-01-30 10:37:23 +04:00
LIST_HEAD ( buf_list ) ;
2012-10-11 06:54:02 +04:00
struct regmap * regmap = dsp - > regmap ;
struct wmfw_coeff_hdr * hdr ;
struct wmfw_coeff_item * blk ;
const struct firmware * firmware ;
2013-01-08 20:09:31 +04:00
const struct wm_adsp_region * mem ;
struct wm_adsp_alg_region * alg_region ;
2012-10-11 06:54:02 +04:00
const char * region_name ;
int ret , pos , blocks , type , offset , reg ;
char * file ;
2013-01-30 10:37:23 +04:00
struct wm_adsp_buf * buf ;
2012-10-11 06:54:02 +04:00
file = kzalloc ( PAGE_SIZE , GFP_KERNEL ) ;
if ( file = = NULL )
return - ENOMEM ;
2013-01-12 02:58:28 +04:00
snprintf ( file , PAGE_SIZE , " %s-dsp%d-%s.bin " , dsp - > part , dsp - > num ,
wm_adsp_fw [ dsp - > fw ] . file ) ;
2012-10-11 06:54:02 +04:00
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 ) ;
2013-01-21 13:02:31 +04:00
goto out_fw ;
2012-10-11 06:54:02 +04:00
}
2013-01-16 11:59:04 +04:00
switch ( be32_to_cpu ( hdr - > rev ) & 0xff ) {
case 1 :
break ;
default :
adsp_err ( dsp , " %s: Unsupported coefficient file format %d \n " ,
file , be32_to_cpu ( hdr - > rev ) & 0xff ) ;
ret = - EINVAL ;
goto out_fw ;
}
2012-10-11 06:54:02 +04:00
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 ] ) ;
2013-01-16 11:59:04 +04:00
type = le16_to_cpu ( blk - > type ) ;
offset = le16_to_cpu ( blk - > offset ) ;
2012-10-11 06:54:02 +04:00
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 ) {
2013-01-16 11:59:04 +04:00
case ( WMFW_NAME_TEXT < < 8 ) :
case ( WMFW_INFO_TEXT < < 8 ) :
2012-10-11 06:54:02 +04:00
break ;
2013-01-16 11:59:04 +04:00
case ( WMFW_ABSOLUTE < < 8 ) :
2013-03-05 18:39:54 +04:00
/*
* Old files may use this for global
* coefficients .
*/
if ( le32_to_cpu ( blk - > id ) = = dsp - > fw_id & &
offset = = 0 ) {
region_name = " global coefficients " ;
mem = wm_adsp_find_region ( dsp , type ) ;
if ( ! mem ) {
adsp_err ( dsp , " No ZM \n " ) ;
break ;
}
reg = wm_adsp_region_to_reg ( mem , 0 ) ;
} else {
region_name = " register " ;
reg = offset ;
}
2012-10-11 06:54:02 +04:00
break ;
2013-01-08 20:09:31 +04:00
case WMFW_ADSP1_DM :
case WMFW_ADSP1_ZM :
case WMFW_ADSP2_XM :
case WMFW_ADSP2_YM :
adsp_dbg ( dsp , " %s.%d: %d bytes in %x for %x \n " ,
file , blocks , le32_to_cpu ( blk - > len ) ,
type , le32_to_cpu ( blk - > id ) ) ;
mem = wm_adsp_find_region ( dsp , type ) ;
if ( ! mem ) {
adsp_err ( dsp , " No base for region %x \n " , type ) ;
break ;
}
reg = 0 ;
list_for_each_entry ( alg_region ,
& dsp - > alg_regions , list ) {
if ( le32_to_cpu ( blk - > id ) = = alg_region - > alg & &
type = = alg_region - > type ) {
2013-01-23 20:35:48 +04:00
reg = alg_region - > base ;
2013-01-08 20:09:31 +04:00
reg = wm_adsp_region_to_reg ( mem ,
reg ) ;
2013-01-23 20:35:48 +04:00
reg + = offset ;
2013-11-28 20:37:51 +04:00
break ;
2013-01-08 20:09:31 +04:00
}
}
if ( reg = = 0 )
adsp_err ( dsp , " No %x for algorithm %x \n " ,
type , le32_to_cpu ( blk - > id ) ) ;
break ;
2012-10-11 06:54:02 +04:00
default :
2013-01-20 14:02:19 +04:00
adsp_err ( dsp , " %s.%d: Unknown region type %x at %d \n " ,
file , blocks , type , pos ) ;
2012-10-11 06:54:02 +04:00
break ;
}
if ( reg ) {
2013-01-30 10:37:23 +04:00
buf = wm_adsp_buf_alloc ( blk - > data ,
le32_to_cpu ( blk - > len ) ,
& buf_list ) ;
2013-01-07 23:03:17 +04:00
if ( ! buf ) {
adsp_err ( dsp , " Out of memory \n " ) ;
2013-03-11 20:23:15 +04:00
ret = - ENOMEM ;
goto out_fw ;
2013-01-07 23:03:17 +04:00
}
2013-01-12 23:58:17 +04:00
adsp_dbg ( dsp , " %s.%d: Writing %d bytes at %x \n " ,
file , blocks , le32_to_cpu ( blk - > len ) ,
reg ) ;
2013-01-30 10:37:23 +04:00
ret = regmap_raw_write_async ( regmap , reg , buf - > buf ,
le32_to_cpu ( blk - > len ) ) ;
2012-10-11 06:54:02 +04:00
if ( ret ! = 0 ) {
adsp_err ( dsp ,
2013-11-01 19:56:52 +04:00
" %s.%d: Failed to write to %x in %s: %d \n " ,
file , blocks , reg , region_name , ret ) ;
2012-10-11 06:54:02 +04:00
}
}
2015-02-16 18:25:49 +03:00
pos + = ( le32_to_cpu ( blk - > len ) + sizeof ( * blk ) + 3 ) & ~ 0x03 ;
2012-10-11 06:54:02 +04:00
blocks + + ;
}
2013-01-30 10:37:23 +04:00
ret = regmap_async_complete ( regmap ) ;
if ( ret ! = 0 )
adsp_err ( dsp , " Failed to complete async write: %d \n " , ret ) ;
2012-10-11 06:54:02 +04:00
if ( pos > firmware - > size )
adsp_warn ( dsp , " %s.%d: %zu bytes at end of file \n " ,
file , blocks , pos - firmware - > size ) ;
out_fw :
2014-11-17 13:48:21 +03:00
regmap_async_complete ( regmap ) ;
2012-10-11 06:54:02 +04:00
release_firmware ( firmware ) ;
2013-01-30 10:37:23 +04:00
wm_adsp_buf_free ( & buf_list ) ;
2012-10-11 06:54:02 +04:00
out :
kfree ( file ) ;
2013-03-11 20:23:15 +04:00
return ret ;
2012-10-11 06:54:02 +04:00
}
2015-04-13 15:27:54 +03:00
int wm_adsp1_init ( struct wm_adsp * dsp )
2013-01-16 05:03:56 +04:00
{
2015-04-13 15:27:54 +03:00
INIT_LIST_HEAD ( & dsp - > alg_regions ) ;
2013-01-16 05:03:56 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( wm_adsp1_init ) ;
2012-10-11 06:54:02 +04:00
int wm_adsp1_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol ,
int event )
{
2015-01-13 12:27:34 +03:00
struct snd_soc_codec * codec = snd_soc_dapm_to_codec ( w - > dapm ) ;
2012-10-11 06:54:02 +04:00
struct wm_adsp * dsps = snd_soc_codec_get_drvdata ( codec ) ;
struct wm_adsp * dsp = & dsps [ w - > shift ] ;
2013-11-01 19:56:56 +04:00
struct wm_adsp_alg_region * alg_region ;
2013-05-08 17:15:35 +04:00
struct wm_coeff_ctl * ctl ;
2012-10-11 06:54:02 +04:00
int ret ;
2013-01-18 12:43:09 +04:00
int val ;
2012-10-11 06:54:02 +04:00
2014-07-18 00:01:07 +04:00
dsp - > card = codec - > component . card ;
2013-08-01 14:11:28 +04:00
2012-10-11 06:54:02 +04:00
switch ( event ) {
case SND_SOC_DAPM_POST_PMU :
regmap_update_bits ( dsp - > regmap , dsp - > base + ADSP1_CONTROL_30 ,
ADSP1_SYS_ENA , ADSP1_SYS_ENA ) ;
2013-01-18 12:43:09 +04:00
/*
* For simplicity set the DSP clock rate to be the
* SYSCLK rate rather than making it configurable .
*/
if ( dsp - > sysclk_reg ) {
ret = regmap_read ( dsp - > regmap , dsp - > sysclk_reg , & val ) ;
if ( ret ! = 0 ) {
adsp_err ( dsp , " Failed to read SYSCLK state: %d \n " ,
ret ) ;
return ret ;
}
val = ( val & dsp - > sysclk_mask )
> > dsp - > sysclk_shift ;
ret = regmap_update_bits ( dsp - > regmap ,
dsp - > base + ADSP1_CONTROL_31 ,
ADSP1_CLK_SEL_MASK , val ) ;
if ( ret ! = 0 ) {
adsp_err ( dsp , " Failed to set clock rate: %d \n " ,
ret ) ;
return ret ;
}
}
2012-10-11 06:54:02 +04:00
ret = wm_adsp_load ( dsp ) ;
if ( ret ! = 0 )
goto err ;
2015-04-13 15:27:53 +03:00
ret = wm_adsp1_setup_algs ( dsp ) ;
2012-10-26 22:30:40 +04:00
if ( ret ! = 0 )
goto err ;
2012-10-11 06:54:02 +04:00
ret = wm_adsp_load_coeff ( dsp ) ;
if ( ret ! = 0 )
goto err ;
2013-05-28 15:01:50 +04:00
/* Initialize caches for enabled and unset controls */
2013-07-29 16:51:59 +04:00
ret = wm_coeff_init_control_caches ( dsp ) ;
2013-05-08 17:15:35 +04:00
if ( ret ! = 0 )
goto err ;
2013-05-28 15:01:50 +04:00
/* Sync set controls */
2013-07-29 16:51:59 +04:00
ret = wm_coeff_sync_controls ( dsp ) ;
2013-05-08 17:15:35 +04:00
if ( ret ! = 0 )
goto err ;
2012-10-11 06:54:02 +04:00
/* 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 ) ;
2013-05-08 17:15:35 +04:00
2013-07-29 16:51:59 +04:00
list_for_each_entry ( ctl , & dsp - > ctl_list , list )
2013-05-08 17:15:35 +04:00
ctl - > enabled = 0 ;
2013-11-01 19:56:56 +04:00
while ( ! list_empty ( & dsp - > alg_regions ) ) {
alg_region = list_first_entry ( & dsp - > alg_regions ,
struct wm_adsp_alg_region ,
list ) ;
list_del ( & alg_region - > list ) ;
kfree ( alg_region ) ;
}
2012-10-11 06:54:02 +04:00
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 ;
2013-11-28 22:11:38 +04:00
ret = regmap_update_bits_async ( dsp - > regmap , dsp - > base + ADSP2_CONTROL ,
ADSP2_SYS_ENA , ADSP2_SYS_ENA ) ;
2012-10-11 06:54:02 +04:00
if ( ret ! = 0 )
return ret ;
/* Wait for the RAM to start, should be near instantaneous */
2013-12-18 13:25:49 +04:00
for ( count = 0 ; count < 10 ; + + count ) {
2012-10-11 06:54:02 +04:00
ret = regmap_read ( dsp - > regmap , dsp - > base + ADSP2_STATUS1 ,
& val ) ;
if ( ret ! = 0 )
return ret ;
2013-12-18 13:25:49 +04:00
if ( val & ADSP2_RAM_RDY )
break ;
msleep ( 1 ) ;
}
2012-10-11 06:54:02 +04:00
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 ) ;
return 0 ;
}
2014-01-09 13:06:54 +04:00
static void wm_adsp2_boot_work ( struct work_struct * work )
2012-10-11 06:54:02 +04:00
{
2014-01-08 21:42:18 +04:00
struct wm_adsp * dsp = container_of ( work ,
struct wm_adsp ,
boot_work ) ;
2012-10-11 06:54:02 +04:00
int ret ;
2014-01-08 21:42:18 +04:00
unsigned int val ;
2012-10-11 06:54:02 +04:00
2014-01-08 21:42:18 +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 ;
}
val = ( val & ARIZONA_SYSCLK_FREQ_MASK )
> > ARIZONA_SYSCLK_FREQ_SHIFT ;
2013-08-01 14:11:28 +04:00
2014-01-08 21:42:18 +04:00
ret = regmap_update_bits_async ( 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 ;
}
2012-12-02 16:50:46 +04:00
2014-01-08 21:42:18 +04:00
ret = wm_adsp2_ena ( dsp ) ;
if ( ret ! = 0 )
return ;
2012-10-11 06:54:02 +04:00
2014-01-08 21:42:18 +04:00
ret = wm_adsp_load ( dsp ) ;
if ( ret ! = 0 )
goto err ;
2012-10-11 06:54:02 +04:00
2015-04-13 15:27:53 +03:00
ret = wm_adsp2_setup_algs ( dsp ) ;
2014-01-08 21:42:18 +04:00
if ( ret ! = 0 )
goto err ;
2012-10-26 22:30:40 +04:00
2014-01-08 21:42:18 +04:00
ret = wm_adsp_load_coeff ( dsp ) ;
if ( ret ! = 0 )
goto err ;
2012-10-11 06:54:02 +04:00
2014-01-08 21:42:18 +04:00
/* Initialize caches for enabled and unset controls */
ret = wm_coeff_init_control_caches ( dsp ) ;
if ( ret ! = 0 )
goto err ;
2013-05-08 17:15:35 +04:00
2014-01-08 21:42:18 +04:00
/* Sync set controls */
ret = wm_coeff_sync_controls ( dsp ) ;
if ( ret ! = 0 )
goto err ;
dsp - > running = true ;
return ;
2013-05-08 17:15:35 +04:00
2014-01-08 21:42:18 +04:00
err :
regmap_update_bits ( dsp - > regmap , dsp - > base + ADSP2_CONTROL ,
ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START , 0 ) ;
}
2014-01-08 21:42:19 +04:00
int wm_adsp2_early_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
2015-01-13 12:27:34 +03:00
struct snd_soc_codec * codec = snd_soc_dapm_to_codec ( w - > dapm ) ;
2014-01-08 21:42:19 +04:00
struct wm_adsp * dsps = snd_soc_codec_get_drvdata ( codec ) ;
struct wm_adsp * dsp = & dsps [ w - > shift ] ;
2014-07-18 00:01:07 +04:00
dsp - > card = codec - > component . card ;
2014-01-08 21:42:19 +04:00
switch ( event ) {
case SND_SOC_DAPM_PRE_PMU :
queue_work ( system_unbound_wq , & dsp - > boot_work ) ;
break ;
default :
break ;
2014-04-17 16:42:54 +04:00
}
2014-01-08 21:42:19 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( wm_adsp2_early_event ) ;
2014-01-08 21:42:18 +04:00
int wm_adsp2_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
2015-01-13 12:27:34 +03:00
struct snd_soc_codec * codec = snd_soc_dapm_to_codec ( w - > dapm ) ;
2014-01-08 21:42:18 +04:00
struct wm_adsp * dsps = snd_soc_codec_get_drvdata ( codec ) ;
struct wm_adsp * dsp = & dsps [ w - > shift ] ;
struct wm_adsp_alg_region * alg_region ;
struct wm_coeff_ctl * ctl ;
int ret ;
switch ( event ) {
case SND_SOC_DAPM_POST_PMU :
flush_work ( & dsp - > boot_work ) ;
if ( ! dsp - > running )
return - EIO ;
2013-05-08 17:15:35 +04:00
2014-01-08 21:42:18 +04:00
ret = regmap_update_bits ( dsp - > regmap ,
dsp - > base + ADSP2_CONTROL ,
2014-11-18 19:25:27 +03: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 :
2015-05-29 12:23:07 +03:00
/* Log firmware state, it can be useful for analysis */
wm_adsp2_show_fw_status ( dsp ) ;
2013-01-12 02:58:28 +04:00
dsp - > running = false ;
2012-10-11 06:54:02 +04:00
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
2013-01-28 16:18:17 +04:00
/* Make sure DMAs are quiesced */
regmap_write ( dsp - > regmap , dsp - > base + ADSP2_WDMA_CONFIG_1 , 0 ) ;
regmap_write ( dsp - > regmap , dsp - > base + ADSP2_WDMA_CONFIG_2 , 0 ) ;
regmap_write ( dsp - > regmap , dsp - > base + ADSP2_RDMA_CONFIG_1 , 0 ) ;
2013-07-29 16:51:59 +04:00
list_for_each_entry ( ctl , & dsp - > ctl_list , list )
2013-05-08 17:15:35 +04:00
ctl - > enabled = 0 ;
2013-01-08 20:09:31 +04:00
while ( ! list_empty ( & dsp - > alg_regions ) ) {
alg_region = list_first_entry ( & dsp - > alg_regions ,
struct wm_adsp_alg_region ,
list ) ;
list_del ( & alg_region - > list ) ;
kfree ( alg_region ) ;
}
2014-01-22 14:09:11 +04:00
adsp_dbg ( dsp , " Shutdown complete \n " ) ;
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
2015-06-11 13:32:30 +03:00
int wm_adsp2_codec_probe ( struct wm_adsp * dsp , struct snd_soc_codec * codec )
{
2015-06-11 13:32:31 +03:00
return snd_soc_add_codec_controls ( codec ,
wm_adsp2_fw_controls [ dsp - > num - 1 ] ,
ARRAY_SIZE ( wm_adsp2_fw_controls [ 0 ] ) ) ;
2015-06-11 13:32:30 +03:00
}
EXPORT_SYMBOL_GPL ( wm_adsp2_codec_probe ) ;
int wm_adsp2_codec_remove ( struct wm_adsp * dsp , struct snd_soc_codec * codec )
{
return 0 ;
}
EXPORT_SYMBOL_GPL ( wm_adsp2_codec_remove ) ;
2015-06-02 13:53:34 +03:00
int wm_adsp2_init ( struct wm_adsp * dsp )
2012-11-28 21:20:32 +04:00
{
int ret ;
2012-12-02 16:37:00 +04:00
/*
* Disable the DSP memory by default when in reset for a small
* power saving .
*/
2015-04-13 15:27:54 +03:00
ret = regmap_update_bits ( dsp - > regmap , dsp - > base + ADSP2_CONTROL ,
2012-12-02 16:37:00 +04:00
ADSP2_MEM_ENA , 0 ) ;
if ( ret ! = 0 ) {
2015-04-13 15:27:54 +03:00
adsp_err ( dsp , " Failed to clear memory retention: %d \n " , ret ) ;
2012-12-02 16:37:00 +04:00
return ret ;
}
2015-04-13 15:27:54 +03:00
INIT_LIST_HEAD ( & dsp - > alg_regions ) ;
INIT_LIST_HEAD ( & dsp - > ctl_list ) ;
INIT_WORK ( & dsp - > boot_work , wm_adsp2_boot_work ) ;
2013-05-08 17:15:35 +04:00
2012-11-28 21:20:32 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( wm_adsp2_init ) ;
2014-07-04 09:47:41 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;