2016-05-30 17:42:55 +05:30
/*
* skl - sst - utils . c - SKL sst utils functions
*
* Copyright ( C ) 2016 Intel Corp
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as version 2 , as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*/
2016-05-30 17:42:59 +05:30
# include <linux/device.h>
# include <linux/slab.h>
# include <linux/uuid.h>
2016-05-30 17:42:55 +05:30
# include "skl-sst-dsp.h"
2016-05-30 17:42:59 +05:30
# include "../common/sst-dsp.h"
# include "../common/sst-dsp-priv.h"
# include "skl-sst-ipc.h"
# define UUID_STR_SIZE 37
# define DEFAULT_HASH_SHA256_LEN 32
2016-05-30 17:42:55 +05:30
/* FW Extended Manifest Header id = $AE1 */
# define SKL_EXT_MANIFEST_HEADER_MAGIC 0x31454124
2016-05-30 17:42:59 +05:30
struct UUID {
u8 id [ 16 ] ;
} ;
union seg_flags {
u32 ul ;
struct {
u32 contents : 1 ;
u32 alloc : 1 ;
u32 load : 1 ;
u32 read_only : 1 ;
u32 code : 1 ;
u32 data : 1 ;
u32 _rsvd0 : 2 ;
u32 type : 4 ;
u32 _rsvd1 : 4 ;
u32 length : 16 ;
} r ;
} __packed ;
struct segment_desc {
union seg_flags flags ;
u32 v_base_addr ;
u32 file_offset ;
} ;
struct module_type {
u32 load_type : 4 ;
u32 auto_start : 1 ;
u32 domain_ll : 1 ;
u32 domain_dp : 1 ;
u32 rsvd : 25 ;
} __packed ;
struct adsp_module_entry {
u32 struct_id ;
u8 name [ 8 ] ;
struct UUID uuid ;
struct module_type type ;
u8 hash1 [ DEFAULT_HASH_SHA256_LEN ] ;
u32 entry_point ;
u16 cfg_offset ;
u16 cfg_count ;
u32 affinity_mask ;
u16 instance_max_count ;
u16 instance_bss_size ;
struct segment_desc segments [ 3 ] ;
} __packed ;
struct adsp_fw_hdr {
u32 id ;
u32 len ;
u8 name [ 8 ] ;
u32 preload_page_count ;
u32 fw_image_flags ;
u32 feature_mask ;
u16 major ;
u16 minor ;
u16 hotfix ;
u16 build ;
u32 num_modules ;
u32 hw_buf_base ;
u32 hw_buf_length ;
u32 load_offset ;
} __packed ;
2016-05-30 17:42:55 +05:30
struct skl_ext_manifest_hdr {
u32 id ;
u32 len ;
u16 version_major ;
u16 version_minor ;
u32 entries ;
} ;
2016-09-22 14:00:39 +05:30
static int skl_get_pvtid_map ( struct uuid_module * module , int instance_id )
{
int pvt_id ;
for ( pvt_id = 0 ; pvt_id < module - > max_instance ; pvt_id + + ) {
if ( module - > instance_id [ pvt_id ] = = instance_id )
return pvt_id ;
}
return - EINVAL ;
}
int skl_get_pvt_instance_id_map ( struct skl_sst * ctx ,
int module_id , int instance_id )
{
struct uuid_module * module ;
list_for_each_entry ( module , & ctx - > uuid_list , list ) {
if ( module - > id = = module_id )
return skl_get_pvtid_map ( module , instance_id ) ;
}
return - EINVAL ;
}
EXPORT_SYMBOL_GPL ( skl_get_pvt_instance_id_map ) ;
2016-09-22 14:00:37 +05:30
static inline int skl_getid_32 ( struct uuid_module * module , u64 * val ,
2016-09-22 14:00:38 +05:30
int word1_mask , int word2_mask )
2016-09-22 14:00:37 +05:30
{
int index , max_inst , pvt_id ;
u32 mask_val ;
max_inst = module - > max_instance ;
mask_val = ( u32 ) ( * val > > word1_mask ) ;
if ( mask_val ! = 0xffffffff ) {
index = ffz ( mask_val ) ;
pvt_id = index + word1_mask + word2_mask ;
if ( pvt_id < = ( max_inst - 1 ) ) {
2016-10-13 11:55:48 +03:00
* val | = 1ULL < < ( index + word1_mask ) ;
2016-09-22 14:00:37 +05:30
return pvt_id ;
}
}
return - EINVAL ;
}
static inline int skl_pvtid_128 ( struct uuid_module * module )
{
int j , i , word1_mask , word2_mask = 0 , pvt_id ;
for ( j = 0 ; j < MAX_INSTANCE_BUFF ; j + + ) {
word1_mask = 0 ;
for ( i = 0 ; i < 2 ; i + + ) {
pvt_id = skl_getid_32 ( module , & module - > pvt_id [ j ] ,
word1_mask , word2_mask ) ;
if ( pvt_id > = 0 )
return pvt_id ;
word1_mask + = 32 ;
if ( ( word1_mask + word2_mask ) > = module - > max_instance )
return - EINVAL ;
}
word2_mask + = 64 ;
if ( word2_mask > = module - > max_instance )
return - EINVAL ;
}
return - EINVAL ;
}
/**
* skl_get_pvt_id : generate a private id for use as module id
*
* @ ctx : driver context
* @ mconfig : module configuration data
*
* This generates a 128 bit private unique id for a module TYPE so that
* module instance is unique
*/
2017-03-24 23:10:31 +05:30
int skl_get_pvt_id ( struct skl_sst * ctx , uuid_le * uuid_mod , int instance_id )
2016-09-22 14:00:37 +05:30
{
struct uuid_module * module ;
int pvt_id ;
list_for_each_entry ( module , & ctx - > uuid_list , list ) {
if ( uuid_le_cmp ( * uuid_mod , module - > uuid ) = = 0 ) {
pvt_id = skl_pvtid_128 ( module ) ;
2016-09-22 14:00:39 +05:30
if ( pvt_id > = 0 ) {
2017-03-24 23:10:31 +05:30
module - > instance_id [ pvt_id ] = instance_id ;
2016-09-22 14:00:37 +05:30
return pvt_id ;
2016-09-22 14:00:39 +05:30
}
2016-09-22 14:00:37 +05:30
}
}
return - EINVAL ;
}
EXPORT_SYMBOL_GPL ( skl_get_pvt_id ) ;
/**
* skl_put_pvt_id : free up the private id allocated
*
* @ ctx : driver context
* @ mconfig : module configuration data
*
* This frees a 128 bit private unique id previously generated
*/
2017-03-24 23:10:31 +05:30
int skl_put_pvt_id ( struct skl_sst * ctx , uuid_le * uuid_mod , int * pvt_id )
2016-09-22 14:00:37 +05:30
{
int i ;
struct uuid_module * module ;
list_for_each_entry ( module , & ctx - > uuid_list , list ) {
if ( uuid_le_cmp ( * uuid_mod , module - > uuid ) = = 0 ) {
2017-03-24 23:10:31 +05:30
if ( * pvt_id ! = 0 )
i = ( * pvt_id ) / 64 ;
2016-09-22 14:00:37 +05:30
else
i = 0 ;
2017-03-24 23:10:31 +05:30
module - > pvt_id [ i ] & = ~ ( 1 < < ( * pvt_id ) ) ;
* pvt_id = - 1 ;
2016-09-22 14:00:37 +05:30
return 0 ;
}
}
return - EINVAL ;
}
EXPORT_SYMBOL_GPL ( skl_put_pvt_id ) ;
2016-05-30 17:42:59 +05:30
/*
* Parse the firmware binary to get the UUID , module id
* and loadable flags
*/
2016-07-26 18:06:44 +05:30
int snd_skl_parse_uuids ( struct sst_dsp * ctx , const struct firmware * fw ,
unsigned int offset , int index )
2016-05-30 17:42:59 +05:30
{
struct adsp_fw_hdr * adsp_hdr ;
struct adsp_module_entry * mod_entry ;
2016-09-22 14:00:39 +05:30
int i , num_entry , size ;
2016-05-30 17:42:59 +05:30
uuid_le * uuid_bin ;
const char * buf ;
struct skl_sst * skl = ctx - > thread_context ;
struct uuid_module * module ;
struct firmware stripped_fw ;
unsigned int safe_file ;
/* Get the FW pointer to derive ADSP header */
2016-07-26 18:06:44 +05:30
stripped_fw . data = fw - > data ;
stripped_fw . size = fw - > size ;
2016-05-30 17:42:59 +05:30
skl_dsp_strip_extended_manifest ( & stripped_fw ) ;
buf = stripped_fw . data ;
/* check if we have enough space in file to move to header */
safe_file = sizeof ( * adsp_hdr ) + offset ;
if ( stripped_fw . size < = safe_file ) {
dev_err ( ctx - > dev , " Small fw file size, No space for hdr \n " ) ;
return - EINVAL ;
}
adsp_hdr = ( struct adsp_fw_hdr * ) ( buf + offset ) ;
/* check 1st module entry is in file */
safe_file + = adsp_hdr - > len + sizeof ( * mod_entry ) ;
if ( stripped_fw . size < = safe_file ) {
dev_err ( ctx - > dev , " Small fw file size, No module entry \n " ) ;
return - EINVAL ;
}
mod_entry = ( struct adsp_module_entry * )
( buf + offset + adsp_hdr - > len ) ;
num_entry = adsp_hdr - > num_modules ;
/* check all entries are in file */
safe_file + = num_entry * sizeof ( * mod_entry ) ;
if ( stripped_fw . size < = safe_file ) {
dev_err ( ctx - > dev , " Small fw file size, No modules \n " ) ;
return - EINVAL ;
}
/*
* Read the UUID ( GUID ) from FW Manifest .
*
* The 16 byte UUID format is : XXXXXXXX - XXXX - XXXX - XXXX - XXXXXXXXXX
* Populate the UUID table to store module_id and loadable flags
* for the module .
*/
for ( i = 0 ; i < num_entry ; i + + , mod_entry + + ) {
module = kzalloc ( sizeof ( * module ) , GFP_KERNEL ) ;
if ( ! module )
return - ENOMEM ;
uuid_bin = ( uuid_le * ) mod_entry - > uuid . id ;
memcpy ( & module - > uuid , uuid_bin , sizeof ( module - > uuid ) ) ;
2016-07-26 18:06:44 +05:30
module - > id = ( i | ( index < < 12 ) ) ;
2016-05-30 17:42:59 +05:30
module - > is_loadable = mod_entry - > type . load_type ;
2016-09-22 14:00:37 +05:30
module - > max_instance = mod_entry - > instance_max_count ;
2016-09-22 14:00:39 +05:30
size = sizeof ( int ) * mod_entry - > instance_max_count ;
module - > instance_id = devm_kzalloc ( ctx - > dev , size , GFP_KERNEL ) ;
2016-09-29 18:32:12 +01:00
if ( ! module - > instance_id ) {
kfree ( module ) ;
2016-09-22 14:00:39 +05:30
return - ENOMEM ;
2016-09-29 18:32:12 +01:00
}
2016-05-30 17:42:59 +05:30
list_add_tail ( & module - > list , & skl - > uuid_list ) ;
dev_dbg ( ctx - > dev ,
" Adding uuid :%pUL mod id: %d Loadable: %d \n " ,
& module - > uuid , module - > id , module - > is_loadable ) ;
}
return 0 ;
}
void skl_freeup_uuid_list ( struct skl_sst * ctx )
{
struct uuid_module * uuid , * _uuid ;
list_for_each_entry_safe ( uuid , _uuid , & ctx - > uuid_list , list ) {
list_del ( & uuid - > list ) ;
kfree ( uuid ) ;
}
}
2016-05-30 17:42:55 +05:30
/*
* some firmware binary contains some extended manifest . This needs
* to be stripped in that case before we load and use that image .
*
2016-05-30 17:42:59 +05:30
* Get the module id for the module by checking
* the table for the UUID for the module
2016-05-30 17:42:55 +05:30
*/
int skl_dsp_strip_extended_manifest ( struct firmware * fw )
{
struct skl_ext_manifest_hdr * hdr ;
/* check if fw file is greater than header we are looking */
if ( fw - > size < sizeof ( hdr ) ) {
pr_err ( " %s: Firmware file small, no hdr \n " , __func__ ) ;
return - EINVAL ;
}
hdr = ( struct skl_ext_manifest_hdr * ) fw - > data ;
if ( hdr - > id = = SKL_EXT_MANIFEST_HEADER_MAGIC ) {
fw - > size - = hdr - > len ;
fw - > data + = hdr - > len ;
}
return 0 ;
}
2017-04-25 12:18:19 +05:30
int skl_sst_ctx_init ( struct device * dev , int irq , const char * fw_name ,
struct skl_dsp_loader_ops dsp_ops , struct skl_sst * * dsp ,
struct sst_dsp_device * skl_dev )
{
struct skl_sst * skl ;
struct sst_dsp * sst ;
skl = devm_kzalloc ( dev , sizeof ( * skl ) , GFP_KERNEL ) ;
if ( skl = = NULL )
return - ENOMEM ;
skl - > dev = dev ;
skl_dev - > thread_context = skl ;
INIT_LIST_HEAD ( & skl - > uuid_list ) ;
skl - > dsp = skl_dsp_ctx_init ( dev , skl_dev , irq ) ;
if ( ! skl - > dsp ) {
dev_err ( skl - > dev , " %s: no device \n " , __func__ ) ;
return - ENODEV ;
}
sst = skl - > dsp ;
sst - > fw_name = fw_name ;
sst - > dsp_ops = dsp_ops ;
init_waitqueue_head ( & skl - > mod_load_wait ) ;
INIT_LIST_HEAD ( & sst - > module_list ) ;
skl - > is_first_boot = true ;
if ( dsp )
* dsp = skl ;
2017-08-02 21:51:16 +05:30
return 0 ;
2017-04-25 12:18:19 +05:30
}
2017-04-25 12:18:20 +05:30
int skl_prepare_lib_load ( struct skl_sst * skl , struct skl_lib_info * linfo ,
struct firmware * stripped_fw ,
unsigned int hdr_offset , int index )
{
int ret ;
struct sst_dsp * dsp = skl - > dsp ;
if ( linfo - > fw = = NULL ) {
ret = request_firmware ( & linfo - > fw , linfo - > name ,
skl - > dev ) ;
if ( ret < 0 ) {
dev_err ( skl - > dev , " Request lib %s failed:%d \n " ,
linfo - > name , ret ) ;
return ret ;
}
}
if ( skl - > is_first_boot ) {
ret = snd_skl_parse_uuids ( dsp , linfo - > fw , hdr_offset , index ) ;
if ( ret < 0 )
return ret ;
}
stripped_fw - > data = linfo - > fw - > data ;
stripped_fw - > size = linfo - > fw - > size ;
skl_dsp_strip_extended_manifest ( stripped_fw ) ;
return 0 ;
}
void skl_release_library ( struct skl_lib_info * linfo , int lib_count )
{
int i ;
/* library indices start from 1 to N. 0 represents base FW */
for ( i = 1 ; i < lib_count ; i + + ) {
if ( linfo [ i ] . fw ) {
release_firmware ( linfo [ i ] . fw ) ;
linfo [ i ] . fw = NULL ;
}
}
}