2015-07-21 23:53:55 +05:30
/*
* skl - nhlt . c - Intel SKL Platform NHLT parsing
*
* Copyright ( C ) 2015 Intel Corp
* Author : Sanjiv Kumar < sanjiv . kumar @ intel . com >
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; version 2 of the License .
*
* 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-26 21:30:15 -07:00
# include <linux/pci.h>
2015-07-21 23:53:55 +05:30
# include "skl.h"
2017-11-07 16:16:21 +05:30
# define NHLT_ACPI_HEADER_SIG "NHLT"
2015-07-21 23:53:55 +05:30
/* Unique identification for getting NHLT blobs */
2017-06-05 19:40:46 +03:00
static guid_t osc_guid =
GUID_INIT ( 0xA69F886E , 0x6CEB , 0x4594 ,
0xA4 , 0x1F , 0x7B , 0x5D , 0xCE , 0x24 , 0xC5 , 0x53 ) ;
2015-07-21 23:53:55 +05:30
2016-05-05 11:19:19 +05:30
struct nhlt_acpi_table * skl_nhlt_init ( struct device * dev )
2015-07-21 23:53:55 +05:30
{
acpi_handle handle ;
union acpi_object * obj ;
struct nhlt_resource_desc * nhlt_ptr = NULL ;
2016-05-05 11:19:19 +05:30
struct nhlt_acpi_table * nhlt_table = NULL ;
2015-07-21 23:53:55 +05:30
2017-03-24 23:10:30 +05:30
handle = ACPI_HANDLE ( dev ) ;
if ( ! handle ) {
dev_err ( dev , " Didn't find ACPI_HANDLE \n " ) ;
2015-07-21 23:53:55 +05:30
return NULL ;
}
2017-06-05 19:40:46 +03:00
obj = acpi_evaluate_dsm ( handle , & osc_guid , 1 , 1 , NULL ) ;
2015-07-21 23:53:55 +05:30
if ( obj & & obj - > type = = ACPI_TYPE_BUFFER ) {
nhlt_ptr = ( struct nhlt_resource_desc * ) obj - > buffer . pointer ;
2016-05-05 11:19:19 +05:30
nhlt_table = ( struct nhlt_acpi_table * )
memremap ( nhlt_ptr - > min_addr , nhlt_ptr - > length ,
2015-10-09 18:16:36 -04:00
MEMREMAP_WB ) ;
2016-05-05 11:19:19 +05:30
ACPI_FREE ( obj ) ;
2017-11-07 16:16:21 +05:30
if ( nhlt_table & & ( strncmp ( nhlt_table - > header . signature ,
NHLT_ACPI_HEADER_SIG ,
strlen ( NHLT_ACPI_HEADER_SIG ) ) ! = 0 ) ) {
memunmap ( nhlt_table ) ;
dev_err ( dev , " NHLT ACPI header signature incorrect \n " ) ;
return NULL ;
}
2016-05-05 11:19:19 +05:30
return nhlt_table ;
2015-07-21 23:53:55 +05:30
}
dev_err ( dev , " device specific method to extract NHLT blob failed \n " ) ;
return NULL ;
}
2016-05-05 11:19:19 +05:30
void skl_nhlt_free ( struct nhlt_acpi_table * nhlt )
2015-07-21 23:53:55 +05:30
{
2016-05-05 11:19:19 +05:30
memunmap ( ( void * ) nhlt ) ;
2015-07-21 23:53:55 +05:30
}
static struct nhlt_specific_cfg * skl_get_specific_cfg (
struct device * dev , struct nhlt_fmt * fmt ,
2015-10-27 09:22:58 +09:00
u8 no_ch , u32 rate , u16 bps , u8 linktype )
2015-07-21 23:53:55 +05:30
{
struct nhlt_specific_cfg * sp_config ;
struct wav_fmt * wfmt ;
struct nhlt_fmt_cfg * fmt_config = fmt - > fmt_config ;
int i ;
dev_dbg ( dev , " Format count =%d \n " , fmt - > fmt_count ) ;
for ( i = 0 ; i < fmt - > fmt_count ; i + + ) {
wfmt = & fmt_config - > fmt_ext . fmt ;
dev_dbg ( dev , " ch=%d fmt=%d s_rate=%d \n " , wfmt - > channels ,
wfmt - > bits_per_sample , wfmt - > samples_per_sec ) ;
2015-10-27 09:22:58 +09:00
if ( wfmt - > channels = = no_ch & & wfmt - > bits_per_sample = = bps ) {
/*
* if link type is dmic ignore rate check as the blob is
* generic for all rates
*/
2015-07-21 23:53:55 +05:30
sp_config = & fmt_config - > config ;
2015-10-27 09:22:58 +09:00
if ( linktype = = NHLT_LINK_DMIC )
return sp_config ;
2015-07-21 23:53:55 +05:30
2015-10-27 09:22:58 +09:00
if ( wfmt - > samples_per_sec = = rate )
return sp_config ;
2015-07-21 23:53:55 +05:30
}
fmt_config = ( struct nhlt_fmt_cfg * ) ( fmt_config - > config . caps +
fmt_config - > config . size ) ;
}
return NULL ;
}
static void dump_config ( struct device * dev , u32 instance_id , u8 linktype ,
u8 s_fmt , u8 num_channels , u32 s_rate , u8 dirn , u16 bps )
{
dev_dbg ( dev , " Input configuration \n " ) ;
dev_dbg ( dev , " ch=%d fmt=%d s_rate=%d \n " , num_channels , s_fmt , s_rate ) ;
dev_dbg ( dev , " vbus_id=%d link_type=%d \n " , instance_id , linktype ) ;
dev_dbg ( dev , " bits_per_sample=%d \n " , bps ) ;
}
static bool skl_check_ep_match ( struct device * dev , struct nhlt_endpoint * epnt ,
2017-02-09 16:44:01 +05:30
u32 instance_id , u8 link_type , u8 dirn , u8 dev_type )
2015-07-21 23:53:55 +05:30
{
2017-02-09 16:44:01 +05:30
dev_dbg ( dev , " vbus_id=%d link_type=%d dir=%d dev_type = %d \n " ,
epnt - > virtual_bus_id , epnt - > linktype ,
epnt - > direction , epnt - > device_type ) ;
2015-07-21 23:53:55 +05:30
if ( ( epnt - > virtual_bus_id = = instance_id ) & &
( epnt - > linktype = = link_type ) & &
2017-02-09 16:44:01 +05:30
( epnt - > direction = = dirn ) & &
( epnt - > device_type = = dev_type ) )
2015-07-21 23:53:55 +05:30
return true ;
else
return false ;
}
struct nhlt_specific_cfg
* skl_get_ep_blob ( struct skl * skl , u32 instance , u8 link_type ,
2017-02-09 16:44:01 +05:30
u8 s_fmt , u8 num_ch , u32 s_rate ,
u8 dirn , u8 dev_type )
2015-07-21 23:53:55 +05:30
{
struct nhlt_fmt * fmt ;
struct nhlt_endpoint * epnt ;
struct hdac_bus * bus = ebus_to_hbus ( & skl - > ebus ) ;
struct device * dev = bus - > dev ;
struct nhlt_specific_cfg * sp_config ;
2016-05-05 11:19:19 +05:30
struct nhlt_acpi_table * nhlt = skl - > nhlt ;
2015-10-27 09:22:50 +09:00
u16 bps = ( s_fmt = = 16 ) ? 16 : 32 ;
2015-07-21 23:53:55 +05:30
u8 j ;
dump_config ( dev , instance , link_type , s_fmt , num_ch , s_rate , dirn , bps ) ;
epnt = ( struct nhlt_endpoint * ) nhlt - > desc ;
dev_dbg ( dev , " endpoint count =%d \n " , nhlt - > endpoint_count ) ;
for ( j = 0 ; j < nhlt - > endpoint_count ; j + + ) {
2017-02-09 16:44:01 +05:30
if ( skl_check_ep_match ( dev , epnt , instance , link_type ,
dirn , dev_type ) ) {
2015-07-21 23:53:55 +05:30
fmt = ( struct nhlt_fmt * ) ( epnt - > config . caps +
epnt - > config . size ) ;
2015-10-27 09:22:58 +09:00
sp_config = skl_get_specific_cfg ( dev , fmt , num_ch ,
s_rate , bps , link_type ) ;
2015-07-21 23:53:55 +05:30
if ( sp_config )
return sp_config ;
}
epnt = ( struct nhlt_endpoint * ) ( ( u8 * ) epnt + epnt - > length ) ;
}
return NULL ;
}
2016-02-19 11:42:34 +05:30
2016-05-26 21:30:15 -07:00
int skl_get_dmic_geo ( struct skl * skl )
{
struct nhlt_acpi_table * nhlt = ( struct nhlt_acpi_table * ) skl - > nhlt ;
struct nhlt_endpoint * epnt ;
struct nhlt_dmic_array_config * cfg ;
struct device * dev = & skl - > pci - > dev ;
unsigned int dmic_geo = 0 ;
u8 j ;
epnt = ( struct nhlt_endpoint * ) nhlt - > desc ;
for ( j = 0 ; j < nhlt - > endpoint_count ; j + + ) {
if ( epnt - > linktype = = NHLT_LINK_DMIC ) {
cfg = ( struct nhlt_dmic_array_config * )
( epnt - > config . caps ) ;
switch ( cfg - > array_type ) {
case NHLT_MIC_ARRAY_2CH_SMALL :
case NHLT_MIC_ARRAY_2CH_BIG :
dmic_geo | = MIC_ARRAY_2CH ;
break ;
case NHLT_MIC_ARRAY_4CH_1ST_GEOM :
case NHLT_MIC_ARRAY_4CH_L_SHAPED :
case NHLT_MIC_ARRAY_4CH_2ND_GEOM :
dmic_geo | = MIC_ARRAY_4CH ;
break ;
default :
dev_warn ( dev , " undefined DMIC array_type 0x%0x \n " ,
cfg - > array_type ) ;
}
}
epnt = ( struct nhlt_endpoint * ) ( ( u8 * ) epnt + epnt - > length ) ;
}
return dmic_geo ;
}
2017-01-11 16:31:02 +05:30
static void skl_nhlt_trim_space ( char * trim )
2016-02-19 11:42:34 +05:30
{
2017-01-11 16:31:02 +05:30
char * s = trim ;
2016-02-19 11:42:34 +05:30
int cnt ;
int i ;
cnt = 0 ;
for ( i = 0 ; s [ i ] ; i + + ) {
if ( ! isspace ( s [ i ] ) )
s [ cnt + + ] = s [ i ] ;
}
s [ cnt ] = ' \0 ' ;
}
int skl_nhlt_update_topology_bin ( struct skl * skl )
{
struct nhlt_acpi_table * nhlt = ( struct nhlt_acpi_table * ) skl - > nhlt ;
struct hdac_bus * bus = ebus_to_hbus ( & skl - > ebus ) ;
struct device * dev = bus - > dev ;
dev_dbg ( dev , " oem_id %.6s, oem_table_id %8s oem_revision %d \n " ,
nhlt - > header . oem_id , nhlt - > header . oem_table_id ,
nhlt - > header . oem_revision ) ;
snprintf ( skl - > tplg_name , sizeof ( skl - > tplg_name ) , " %x-%.6s-%.8s-%d%s " ,
skl - > pci_id , nhlt - > header . oem_id , nhlt - > header . oem_table_id ,
nhlt - > header . oem_revision , " -tplg.bin " ) ;
2017-01-11 16:31:02 +05:30
skl_nhlt_trim_space ( skl - > tplg_name ) ;
2016-02-19 11:42:34 +05:30
return 0 ;
}
2017-01-11 16:31:02 +05:30
static ssize_t skl_nhlt_platform_id_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct pci_dev * pci = to_pci_dev ( dev ) ;
struct hdac_ext_bus * ebus = pci_get_drvdata ( pci ) ;
struct skl * skl = ebus_to_skl ( ebus ) ;
struct nhlt_acpi_table * nhlt = ( struct nhlt_acpi_table * ) skl - > nhlt ;
char platform_id [ 32 ] ;
sprintf ( platform_id , " %x-%.6s-%.8s-%d " , skl - > pci_id ,
nhlt - > header . oem_id , nhlt - > header . oem_table_id ,
nhlt - > header . oem_revision ) ;
skl_nhlt_trim_space ( platform_id ) ;
return sprintf ( buf , " %s \n " , platform_id ) ;
}
static DEVICE_ATTR ( platform_id , 0444 , skl_nhlt_platform_id_show , NULL ) ;
int skl_nhlt_create_sysfs ( struct skl * skl )
{
struct device * dev = & skl - > pci - > dev ;
if ( sysfs_create_file ( & dev - > kobj , & dev_attr_platform_id . attr ) )
dev_warn ( dev , " Error creating sysfs entry \n " ) ;
return 0 ;
}
void skl_nhlt_remove_sysfs ( struct skl * skl )
{
struct device * dev = & skl - > pci - > dev ;
sysfs_remove_file ( & dev - > kobj , & dev_attr_platform_id . attr ) ;
}