2017-12-14 08:49:43 +03:00
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
// Copyright(c) 2015-17 Intel Corporation.
/*
* Soundwire Intel Master Driver
*/
# include <linux/acpi.h>
2019-08-21 21:58:20 +03:00
# include <linux/debugfs.h>
2017-12-14 08:49:43 +03:00
# include <linux/delay.h>
2019-04-13 18:12:52 +03:00
# include <linux/module.h>
2017-12-14 08:49:43 +03:00
# include <linux/interrupt.h>
2019-09-16 21:57:38 +03:00
# include <linux/io.h>
2017-12-14 08:49:43 +03:00
# include <linux/platform_device.h>
2018-04-26 16:08:58 +03:00
# include <sound/pcm_params.h>
# include <sound/soc.h>
2017-12-14 08:49:43 +03:00
# include <linux/soundwire/sdw_registers.h>
# include <linux/soundwire/sdw.h>
# include <linux/soundwire/sdw_intel.h>
# include "cadence_master.h"
2019-08-21 21:58:20 +03:00
# include "bus.h"
2017-12-14 08:49:43 +03:00
# include "intel.h"
/* Intel SHIM Registers Definition */
# define SDW_SHIM_LCAP 0x0
# define SDW_SHIM_LCTL 0x4
# define SDW_SHIM_IPPTR 0x8
# define SDW_SHIM_SYNC 0xC
2019-05-01 18:57:39 +03:00
# define SDW_SHIM_CTLSCAP(x) (0x010 + 0x60 * (x))
# define SDW_SHIM_CTLS0CM(x) (0x012 + 0x60 * (x))
# define SDW_SHIM_CTLS1CM(x) (0x014 + 0x60 * (x))
# define SDW_SHIM_CTLS2CM(x) (0x016 + 0x60 * (x))
# define SDW_SHIM_CTLS3CM(x) (0x018 + 0x60 * (x))
# define SDW_SHIM_PCMSCAP(x) (0x020 + 0x60 * (x))
# define SDW_SHIM_PCMSYCHM(x, y) (0x022 + (0x60 * (x)) + (0x2 * (y)))
# define SDW_SHIM_PCMSYCHC(x, y) (0x042 + (0x60 * (x)) + (0x2 * (y)))
# define SDW_SHIM_PDMSCAP(x) (0x062 + 0x60 * (x))
# define SDW_SHIM_IOCTL(x) (0x06C + 0x60 * (x))
# define SDW_SHIM_CTMCTL(x) (0x06E + 0x60 * (x))
2017-12-14 08:49:43 +03:00
# define SDW_SHIM_WAKEEN 0x190
# define SDW_SHIM_WAKESTS 0x192
# define SDW_SHIM_LCTL_SPA BIT(0)
# define SDW_SHIM_LCTL_CPA BIT(8)
2020-07-16 18:09:40 +03:00
# define SDW_SHIM_SYNC_SYNCPRD_VAL_24 (24000 / SDW_CADENCE_GSYNC_KHZ - 1)
# define SDW_SHIM_SYNC_SYNCPRD_VAL_38_4 (38400 / SDW_CADENCE_GSYNC_KHZ - 1)
2017-12-14 08:49:43 +03:00
# define SDW_SHIM_SYNC_SYNCPRD GENMASK(14, 0)
# define SDW_SHIM_SYNC_SYNCCPU BIT(15)
# define SDW_SHIM_SYNC_CMDSYNC_MASK GENMASK(19, 16)
# define SDW_SHIM_SYNC_CMDSYNC BIT(16)
# define SDW_SHIM_SYNC_SYNCGO BIT(24)
# define SDW_SHIM_PCMSCAP_ISS GENMASK(3, 0)
# define SDW_SHIM_PCMSCAP_OSS GENMASK(7, 4)
# define SDW_SHIM_PCMSCAP_BSS GENMASK(12, 8)
# define SDW_SHIM_PCMSYCM_LCHN GENMASK(3, 0)
# define SDW_SHIM_PCMSYCM_HCHN GENMASK(7, 4)
# define SDW_SHIM_PCMSYCM_STREAM GENMASK(13, 8)
# define SDW_SHIM_PCMSYCM_DIR BIT(15)
# define SDW_SHIM_PDMSCAP_ISS GENMASK(3, 0)
# define SDW_SHIM_PDMSCAP_OSS GENMASK(7, 4)
# define SDW_SHIM_PDMSCAP_BSS GENMASK(12, 8)
# define SDW_SHIM_PDMSCAP_CPSS GENMASK(15, 13)
# define SDW_SHIM_IOCTL_MIF BIT(0)
# define SDW_SHIM_IOCTL_CO BIT(1)
# define SDW_SHIM_IOCTL_COE BIT(2)
# define SDW_SHIM_IOCTL_DO BIT(3)
# define SDW_SHIM_IOCTL_DOE BIT(4)
# define SDW_SHIM_IOCTL_BKE BIT(5)
# define SDW_SHIM_IOCTL_WPDD BIT(6)
# define SDW_SHIM_IOCTL_CIBD BIT(8)
# define SDW_SHIM_IOCTL_DIBD BIT(9)
# define SDW_SHIM_CTMCTL_DACTQE BIT(0)
# define SDW_SHIM_CTMCTL_DODS BIT(1)
# define SDW_SHIM_CTMCTL_DOAIS GENMASK(4, 3)
# define SDW_SHIM_WAKEEN_ENABLE BIT(0)
# define SDW_SHIM_WAKESTS_STATUS BIT(0)
/* Intel ALH Register definitions */
2019-05-01 18:57:39 +03:00
# define SDW_ALH_STRMZCFG(x) (0x000 + (0x4 * (x)))
2019-08-21 21:58:20 +03:00
# define SDW_ALH_NUM_STREAMS 64
2017-12-14 08:49:43 +03:00
# define SDW_ALH_STRMZCFG_DMAT_VAL 0x3
# define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0)
# define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16)
2018-04-26 16:09:05 +03:00
enum intel_pdi_type {
INTEL_PDI_IN = 0 ,
INTEL_PDI_OUT = 1 ,
INTEL_PDI_BD = 2 ,
} ;
2017-12-14 08:49:43 +03:00
# define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns)
/*
* Read , write helpers for HW registers
*/
static inline int intel_readl ( void __iomem * base , int offset )
{
return readl ( base + offset ) ;
}
static inline void intel_writel ( void __iomem * base , int offset , int value )
{
writel ( value , base + offset ) ;
}
static inline u16 intel_readw ( void __iomem * base , int offset )
{
return readw ( base + offset ) ;
}
static inline void intel_writew ( void __iomem * base , int offset , u16 value )
{
writew ( value , base + offset ) ;
}
2020-07-16 18:09:39 +03:00
static int intel_wait_bit ( void __iomem * base , int offset , u32 mask , u32 target )
2017-12-14 08:49:43 +03:00
{
int timeout = 10 ;
u32 reg_read ;
do {
reg_read = readl ( base + offset ) ;
2020-07-16 18:09:39 +03:00
if ( ( reg_read & mask ) = = target )
2017-12-14 08:49:43 +03:00
return 0 ;
timeout - - ;
2020-07-16 18:09:39 +03:00
usleep_range ( 50 , 100 ) ;
2017-12-14 08:49:43 +03:00
} while ( timeout ! = 0 ) ;
return - EAGAIN ;
}
2020-07-16 18:09:39 +03:00
static int intel_clear_bit ( void __iomem * base , int offset , u32 value , u32 mask )
2017-12-14 08:49:43 +03:00
{
writel ( value , base + offset ) ;
2020-07-16 18:09:39 +03:00
return intel_wait_bit ( base , offset , mask , 0 ) ;
}
2017-12-14 08:49:43 +03:00
2020-07-16 18:09:39 +03:00
static int intel_set_bit ( void __iomem * base , int offset , u32 value , u32 mask )
{
writel ( value , base + offset ) ;
return intel_wait_bit ( base , offset , mask , mask ) ;
2017-12-14 08:49:43 +03:00
}
2019-08-21 21:58:20 +03:00
/*
* debugfs
*/
# ifdef CONFIG_DEBUG_FS
# define RD_BUF (2 * PAGE_SIZE)
static ssize_t intel_sprintf ( void __iomem * mem , bool l ,
char * buf , size_t pos , unsigned int reg )
{
int value ;
if ( l )
value = intel_readl ( mem , reg ) ;
else
value = intel_readw ( mem , reg ) ;
return scnprintf ( buf + pos , RD_BUF - pos , " %4x \t %4x \n " , reg , value ) ;
}
static int intel_reg_show ( struct seq_file * s_file , void * data )
{
struct sdw_intel * sdw = s_file - > private ;
2020-02-15 04:47:36 +03:00
void __iomem * s = sdw - > link_res - > shim ;
void __iomem * a = sdw - > link_res - > alh ;
2019-08-21 21:58:20 +03:00
char * buf ;
ssize_t ret ;
int i , j ;
unsigned int links , reg ;
buf = kzalloc ( RD_BUF , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
links = intel_readl ( s , SDW_SHIM_LCAP ) & GENMASK ( 2 , 0 ) ;
ret = scnprintf ( buf , RD_BUF , " Register Value \n " ) ;
ret + = scnprintf ( buf + ret , RD_BUF - ret , " \n Shim \n " ) ;
for ( i = 0 ; i < links ; i + + ) {
reg = SDW_SHIM_LCAP + i * 4 ;
ret + = intel_sprintf ( s , true , buf , ret , reg ) ;
}
for ( i = 0 ; i < links ; i + + ) {
ret + = scnprintf ( buf + ret , RD_BUF - ret , " \n Link%d \n " , i ) ;
ret + = intel_sprintf ( s , false , buf , ret , SDW_SHIM_CTLSCAP ( i ) ) ;
ret + = intel_sprintf ( s , false , buf , ret , SDW_SHIM_CTLS0CM ( i ) ) ;
ret + = intel_sprintf ( s , false , buf , ret , SDW_SHIM_CTLS1CM ( i ) ) ;
ret + = intel_sprintf ( s , false , buf , ret , SDW_SHIM_CTLS2CM ( i ) ) ;
ret + = intel_sprintf ( s , false , buf , ret , SDW_SHIM_CTLS3CM ( i ) ) ;
ret + = intel_sprintf ( s , false , buf , ret , SDW_SHIM_PCMSCAP ( i ) ) ;
ret + = scnprintf ( buf + ret , RD_BUF - ret , " \n PCMSyCH registers \n " ) ;
/*
* the value 10 is the number of PDIs . We will need a
* cleanup to remove hard - coded Intel configurations
* from cadence_master . c
*/
for ( j = 0 ; j < 10 ; j + + ) {
ret + = intel_sprintf ( s , false , buf , ret ,
SDW_SHIM_PCMSYCHM ( i , j ) ) ;
ret + = intel_sprintf ( s , false , buf , ret ,
SDW_SHIM_PCMSYCHC ( i , j ) ) ;
}
ret + = scnprintf ( buf + ret , RD_BUF - ret , " \n PDMSCAP, IOCTL, CTMCTL \n " ) ;
ret + = intel_sprintf ( s , false , buf , ret , SDW_SHIM_PDMSCAP ( i ) ) ;
ret + = intel_sprintf ( s , false , buf , ret , SDW_SHIM_IOCTL ( i ) ) ;
ret + = intel_sprintf ( s , false , buf , ret , SDW_SHIM_CTMCTL ( i ) ) ;
}
ret + = scnprintf ( buf + ret , RD_BUF - ret , " \n Wake registers \n " ) ;
ret + = intel_sprintf ( s , false , buf , ret , SDW_SHIM_WAKEEN ) ;
ret + = intel_sprintf ( s , false , buf , ret , SDW_SHIM_WAKESTS ) ;
ret + = scnprintf ( buf + ret , RD_BUF - ret , " \n ALH STRMzCFG \n " ) ;
for ( i = 0 ; i < SDW_ALH_NUM_STREAMS ; i + + )
ret + = intel_sprintf ( a , true , buf , ret , SDW_ALH_STRMZCFG ( i ) ) ;
seq_printf ( s_file , " %s " , buf ) ;
kfree ( buf ) ;
return 0 ;
}
DEFINE_SHOW_ATTRIBUTE ( intel_reg ) ;
static void intel_debugfs_init ( struct sdw_intel * sdw )
{
struct dentry * root = sdw - > cdns . bus . debugfs ;
if ( ! root )
return ;
sdw - > debugfs = debugfs_create_dir ( " intel-sdw " , root ) ;
debugfs_create_file ( " intel-registers " , 0400 , sdw - > debugfs , sdw ,
& intel_reg_fops ) ;
sdw_cdns_debugfs_init ( & sdw - > cdns , sdw - > debugfs ) ;
}
static void intel_debugfs_exit ( struct sdw_intel * sdw )
{
debugfs_remove_recursive ( sdw - > debugfs ) ;
}
# else
static void intel_debugfs_init ( struct sdw_intel * sdw ) { }
static void intel_debugfs_exit ( struct sdw_intel * sdw ) { }
# endif /* CONFIG_DEBUG_FS */
2017-12-14 08:49:43 +03:00
/*
* shim ops
*/
static int intel_link_power_up ( struct sdw_intel * sdw )
{
unsigned int link_id = sdw - > instance ;
2020-02-15 04:47:36 +03:00
void __iomem * shim = sdw - > link_res - > shim ;
2020-07-16 18:09:40 +03:00
u32 * shim_mask = sdw - > link_res - > shim_mask ;
struct sdw_bus * bus = & sdw - > cdns . bus ;
struct sdw_master_prop * prop = & bus - > prop ;
2017-12-14 08:49:43 +03:00
int spa_mask , cpa_mask ;
2020-07-16 18:09:40 +03:00
int link_control ;
int ret = 0 ;
u32 syncprd ;
u32 sync_reg ;
mutex_lock ( sdw - > link_res - > shim_lock ) ;
/*
* The hardware relies on an internal counter , typically 4 kHz ,
* to generate the SoundWire SSP - which defines a ' safe '
* synchronization point between commands and audio transport
* and allows for multi link synchronization . The SYNCPRD value
* is only dependent on the oscillator clock provided to
* the IP , so adjust based on _DSD properties reported in DSDT
* tables . The values reported are based on either 24 MHz
* ( CNL / CML ) or 38.4 MHz ( ICL / TGL + ) .
*/
if ( prop - > mclk_freq % 6000000 )
syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4 ;
else
syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24 ;
if ( ! * shim_mask ) {
/* we first need to program the SyncPRD/CPU registers */
dev_dbg ( sdw - > cdns . dev ,
" %s: first link up, programming SYNCPRD \n " , __func__ ) ;
/* set SyncPRD period */
sync_reg = intel_readl ( shim , SDW_SHIM_SYNC ) ;
sync_reg | = ( syncprd < <
SDW_REG_SHIFT ( SDW_SHIM_SYNC_SYNCPRD ) ) ;
/* Set SyncCPU bit */
sync_reg | = SDW_SHIM_SYNC_SYNCCPU ;
intel_writel ( shim , SDW_SHIM_SYNC , sync_reg ) ;
}
2017-12-14 08:49:43 +03:00
/* Link power up sequence */
link_control = intel_readl ( shim , SDW_SHIM_LCTL ) ;
spa_mask = ( SDW_SHIM_LCTL_SPA < < link_id ) ;
cpa_mask = ( SDW_SHIM_LCTL_CPA < < link_id ) ;
link_control | = spa_mask ;
ret = intel_set_bit ( shim , SDW_SHIM_LCTL , link_control , cpa_mask ) ;
2020-07-16 18:09:40 +03:00
if ( ret < 0 ) {
dev_err ( sdw - > cdns . dev , " Failed to power up link: %d \n " , ret ) ;
goto out ;
}
if ( ! * shim_mask ) {
/* SyncCPU will change once link is active */
ret = intel_wait_bit ( shim , SDW_SHIM_SYNC ,
SDW_SHIM_SYNC_SYNCCPU , 0 ) ;
if ( ret < 0 ) {
dev_err ( sdw - > cdns . dev ,
" Failed to set SHIM_SYNC: %d \n " , ret ) ;
goto out ;
}
}
* shim_mask | = BIT ( link_id ) ;
2017-12-14 08:49:43 +03:00
sdw - > cdns . link_up = true ;
2020-07-16 18:09:40 +03:00
out :
mutex_unlock ( sdw - > link_res - > shim_lock ) ;
return ret ;
2017-12-14 08:49:43 +03:00
}
2020-07-16 18:09:40 +03:00
/* this needs to be called with shim_lock */
static void intel_shim_glue_to_master_ip ( struct sdw_intel * sdw )
2017-12-14 08:49:43 +03:00
{
2020-02-15 04:47:36 +03:00
void __iomem * shim = sdw - > link_res - > shim ;
2017-12-14 08:49:43 +03:00
unsigned int link_id = sdw - > instance ;
2020-07-16 18:09:40 +03:00
u16 ioctl ;
2017-12-14 08:49:43 +03:00
2020-07-16 18:09:40 +03:00
/* Switch to MIP from Glue logic */
ioctl = intel_readw ( shim , SDW_SHIM_IOCTL ( link_id ) ) ;
ioctl & = ~ ( SDW_SHIM_IOCTL_DOE ) ;
2017-12-14 08:49:43 +03:00
intel_writew ( shim , SDW_SHIM_IOCTL ( link_id ) , ioctl ) ;
2020-07-16 18:09:40 +03:00
usleep_range ( 10 , 15 ) ;
2017-12-14 08:49:43 +03:00
2020-07-16 18:09:40 +03:00
ioctl & = ~ ( SDW_SHIM_IOCTL_DO ) ;
2017-12-14 08:49:43 +03:00
intel_writew ( shim , SDW_SHIM_IOCTL ( link_id ) , ioctl ) ;
2020-07-16 18:09:40 +03:00
usleep_range ( 10 , 15 ) ;
2017-12-14 08:49:43 +03:00
2020-07-16 18:09:40 +03:00
ioctl | = ( SDW_SHIM_IOCTL_MIF ) ;
2017-12-14 08:49:43 +03:00
intel_writew ( shim , SDW_SHIM_IOCTL ( link_id ) , ioctl ) ;
2020-07-16 18:09:40 +03:00
usleep_range ( 10 , 15 ) ;
2017-12-14 08:49:43 +03:00
2020-07-16 18:09:40 +03:00
ioctl & = ~ ( SDW_SHIM_IOCTL_BKE ) ;
ioctl & = ~ ( SDW_SHIM_IOCTL_COE ) ;
2017-12-14 08:49:43 +03:00
intel_writew ( shim , SDW_SHIM_IOCTL ( link_id ) , ioctl ) ;
2020-07-16 18:09:40 +03:00
usleep_range ( 10 , 15 ) ;
2017-12-14 08:49:43 +03:00
2020-07-16 18:09:40 +03:00
/* at this point Master IP has full control of the I/Os */
}
2017-12-14 08:49:43 +03:00
2020-07-16 18:09:40 +03:00
/* this needs to be called with shim_lock */
static void intel_shim_master_ip_to_glue ( struct sdw_intel * sdw )
{
unsigned int link_id = sdw - > instance ;
void __iomem * shim = sdw - > link_res - > shim ;
u16 ioctl ;
/* Glue logic */
ioctl = intel_readw ( shim , SDW_SHIM_IOCTL ( link_id ) ) ;
ioctl | = SDW_SHIM_IOCTL_BKE ;
ioctl | = SDW_SHIM_IOCTL_COE ;
2017-12-14 08:49:43 +03:00
intel_writew ( shim , SDW_SHIM_IOCTL ( link_id ) , ioctl ) ;
2020-07-16 18:09:40 +03:00
usleep_range ( 10 , 15 ) ;
2017-12-14 08:49:43 +03:00
2020-07-16 18:09:40 +03:00
ioctl & = ~ ( SDW_SHIM_IOCTL_MIF ) ;
2017-12-14 08:49:43 +03:00
intel_writew ( shim , SDW_SHIM_IOCTL ( link_id ) , ioctl ) ;
2020-07-16 18:09:40 +03:00
usleep_range ( 10 , 15 ) ;
2017-12-14 08:49:43 +03:00
2020-07-16 18:09:40 +03:00
/* at this point Integration Glue has full control of the I/Os */
}
static int intel_shim_init ( struct sdw_intel * sdw , bool clock_stop )
{
void __iomem * shim = sdw - > link_res - > shim ;
unsigned int link_id = sdw - > instance ;
int ret = 0 ;
u16 ioctl = 0 , act = 0 ;
mutex_lock ( sdw - > link_res - > shim_lock ) ;
/* Initialize Shim */
ioctl | = SDW_SHIM_IOCTL_BKE ;
2017-12-14 08:49:43 +03:00
intel_writew ( shim , SDW_SHIM_IOCTL ( link_id ) , ioctl ) ;
2020-07-16 18:09:40 +03:00
usleep_range ( 10 , 15 ) ;
2017-12-14 08:49:43 +03:00
2020-07-16 18:09:40 +03:00
ioctl | = SDW_SHIM_IOCTL_WPDD ;
intel_writew ( shim , SDW_SHIM_IOCTL ( link_id ) , ioctl ) ;
usleep_range ( 10 , 15 ) ;
2017-12-14 08:49:43 +03:00
2020-07-16 18:09:40 +03:00
ioctl | = SDW_SHIM_IOCTL_DO ;
intel_writew ( shim , SDW_SHIM_IOCTL ( link_id ) , ioctl ) ;
usleep_range ( 10 , 15 ) ;
ioctl | = SDW_SHIM_IOCTL_DOE ;
2017-12-14 08:49:43 +03:00
intel_writew ( shim , SDW_SHIM_IOCTL ( link_id ) , ioctl ) ;
2020-07-16 18:09:40 +03:00
usleep_range ( 10 , 15 ) ;
intel_shim_glue_to_master_ip ( sdw ) ;
2017-12-14 08:49:43 +03:00
act | = 0x1 < < SDW_REG_SHIFT ( SDW_SHIM_CTMCTL_DOAIS ) ;
act | = SDW_SHIM_CTMCTL_DACTQE ;
act | = SDW_SHIM_CTMCTL_DODS ;
intel_writew ( shim , SDW_SHIM_CTMCTL ( link_id ) , act ) ;
2020-07-16 18:09:40 +03:00
usleep_range ( 10 , 15 ) ;
2017-12-14 08:49:43 +03:00
2020-07-16 18:09:40 +03:00
mutex_unlock ( sdw - > link_res - > shim_lock ) ;
return ret ;
}
static void __maybe_unused intel_shim_wake ( struct sdw_intel * sdw , bool wake_enable )
{
void __iomem * shim = sdw - > link_res - > shim ;
unsigned int link_id = sdw - > instance ;
u16 wake_en , wake_sts ;
mutex_lock ( sdw - > link_res - > shim_lock ) ;
wake_en = intel_readw ( shim , SDW_SHIM_WAKEEN ) ;
if ( wake_enable ) {
/* Enable the wakeup */
wake_en | = ( SDW_SHIM_WAKEEN_ENABLE < < link_id ) ;
intel_writew ( shim , SDW_SHIM_WAKEEN , wake_en ) ;
} else {
/* Disable the wake up interrupt */
wake_en & = ~ ( SDW_SHIM_WAKEEN_ENABLE < < link_id ) ;
intel_writew ( shim , SDW_SHIM_WAKEEN , wake_en ) ;
/* Clear wake status */
wake_sts = intel_readw ( shim , SDW_SHIM_WAKESTS ) ;
wake_sts | = ( SDW_SHIM_WAKEEN_ENABLE < < link_id ) ;
intel_writew ( shim , SDW_SHIM_WAKESTS_STATUS , wake_sts ) ;
}
mutex_unlock ( sdw - > link_res - > shim_lock ) ;
}
static int __maybe_unused intel_link_power_down ( struct sdw_intel * sdw )
{
int link_control , spa_mask , cpa_mask ;
unsigned int link_id = sdw - > instance ;
void __iomem * shim = sdw - > link_res - > shim ;
u32 * shim_mask = sdw - > link_res - > shim_mask ;
int ret = 0 ;
mutex_lock ( sdw - > link_res - > shim_lock ) ;
intel_shim_master_ip_to_glue ( sdw ) ;
/* Link power down sequence */
link_control = intel_readl ( shim , SDW_SHIM_LCTL ) ;
spa_mask = ~ ( SDW_SHIM_LCTL_SPA < < link_id ) ;
cpa_mask = ( SDW_SHIM_LCTL_CPA < < link_id ) ;
link_control & = spa_mask ;
ret = intel_clear_bit ( shim , SDW_SHIM_LCTL , link_control , cpa_mask ) ;
if ( ! ( * shim_mask & BIT ( link_id ) ) )
dev_err ( sdw - > cdns . dev ,
" %s: Unbalanced power-up/down calls \n " , __func__ ) ;
* shim_mask & = ~ BIT ( link_id ) ;
mutex_unlock ( sdw - > link_res - > shim_lock ) ;
2017-12-14 08:49:43 +03:00
if ( ret < 0 )
2020-07-16 18:09:40 +03:00
return ret ;
2017-12-14 08:49:43 +03:00
2020-07-16 18:09:40 +03:00
sdw - > cdns . link_up = false ;
return 0 ;
2017-12-14 08:49:43 +03:00
}
2020-07-16 18:09:41 +03:00
static void intel_shim_sync_arm ( struct sdw_intel * sdw )
{
void __iomem * shim = sdw - > link_res - > shim ;
u32 sync_reg ;
mutex_lock ( sdw - > link_res - > shim_lock ) ;
/* update SYNC register */
sync_reg = intel_readl ( shim , SDW_SHIM_SYNC ) ;
sync_reg | = ( SDW_SHIM_SYNC_CMDSYNC < < sdw - > instance ) ;
intel_writel ( shim , SDW_SHIM_SYNC , sync_reg ) ;
mutex_unlock ( sdw - > link_res - > shim_lock ) ;
}
2018-04-26 16:08:58 +03:00
/*
* PDI routines
*/
static void intel_pdi_init ( struct sdw_intel * sdw ,
2019-05-01 18:57:38 +03:00
struct sdw_cdns_stream_config * config )
2018-04-26 16:08:58 +03:00
{
2020-02-15 04:47:36 +03:00
void __iomem * shim = sdw - > link_res - > shim ;
2018-04-26 16:08:58 +03:00
unsigned int link_id = sdw - > instance ;
int pcm_cap , pdm_cap ;
/* PCM Stream Capability */
pcm_cap = intel_readw ( shim , SDW_SHIM_PCMSCAP ( link_id ) ) ;
config - > pcm_bd = ( pcm_cap & SDW_SHIM_PCMSCAP_BSS ) > >
SDW_REG_SHIFT ( SDW_SHIM_PCMSCAP_BSS ) ;
config - > pcm_in = ( pcm_cap & SDW_SHIM_PCMSCAP_ISS ) > >
SDW_REG_SHIFT ( SDW_SHIM_PCMSCAP_ISS ) ;
config - > pcm_out = ( pcm_cap & SDW_SHIM_PCMSCAP_OSS ) > >
SDW_REG_SHIFT ( SDW_SHIM_PCMSCAP_OSS ) ;
2019-05-22 22:47:29 +03:00
dev_dbg ( sdw - > cdns . dev , " PCM cap bd:%d in:%d out:%d \n " ,
config - > pcm_bd , config - > pcm_in , config - > pcm_out ) ;
2018-04-26 16:08:58 +03:00
/* PDM Stream Capability */
pdm_cap = intel_readw ( shim , SDW_SHIM_PDMSCAP ( link_id ) ) ;
config - > pdm_bd = ( pdm_cap & SDW_SHIM_PDMSCAP_BSS ) > >
SDW_REG_SHIFT ( SDW_SHIM_PDMSCAP_BSS ) ;
config - > pdm_in = ( pdm_cap & SDW_SHIM_PDMSCAP_ISS ) > >
SDW_REG_SHIFT ( SDW_SHIM_PDMSCAP_ISS ) ;
config - > pdm_out = ( pdm_cap & SDW_SHIM_PDMSCAP_OSS ) > >
SDW_REG_SHIFT ( SDW_SHIM_PDMSCAP_OSS ) ;
2019-05-22 22:47:29 +03:00
dev_dbg ( sdw - > cdns . dev , " PDM cap bd:%d in:%d out:%d \n " ,
config - > pdm_bd , config - > pdm_in , config - > pdm_out ) ;
2018-04-26 16:08:58 +03:00
}
static int
intel_pdi_get_ch_cap ( struct sdw_intel * sdw , unsigned int pdi_num , bool pcm )
{
2020-02-15 04:47:36 +03:00
void __iomem * shim = sdw - > link_res - > shim ;
2018-04-26 16:08:58 +03:00
unsigned int link_id = sdw - > instance ;
int count ;
if ( pcm ) {
count = intel_readw ( shim , SDW_SHIM_PCMSYCHC ( link_id , pdi_num ) ) ;
2019-08-06 03:55:07 +03:00
/*
* WORKAROUND : on all existing Intel controllers , pdi
* number 2 reports channel count as 1 even though it
* supports 8 channels . Performing hardcoding for pdi
* number 2.
*/
if ( pdi_num = = 2 )
count = 7 ;
2018-04-26 16:08:58 +03:00
} else {
count = intel_readw ( shim , SDW_SHIM_PDMSCAP ( link_id ) ) ;
count = ( ( count & SDW_SHIM_PDMSCAP_CPSS ) > >
SDW_REG_SHIFT ( SDW_SHIM_PDMSCAP_CPSS ) ) ;
}
/* zero based values for channel count in register */
count + + ;
return count ;
}
static int intel_pdi_get_ch_update ( struct sdw_intel * sdw ,
2019-05-01 18:57:38 +03:00
struct sdw_cdns_pdi * pdi ,
unsigned int num_pdi ,
unsigned int * num_ch , bool pcm )
2018-04-26 16:08:58 +03:00
{
int i , ch_count = 0 ;
for ( i = 0 ; i < num_pdi ; i + + ) {
pdi - > ch_count = intel_pdi_get_ch_cap ( sdw , pdi - > num , pcm ) ;
ch_count + = pdi - > ch_count ;
pdi + + ;
}
* num_ch = ch_count ;
return 0 ;
}
static int intel_pdi_stream_ch_update ( struct sdw_intel * sdw ,
2019-05-01 18:57:38 +03:00
struct sdw_cdns_streams * stream , bool pcm )
2018-04-26 16:08:58 +03:00
{
intel_pdi_get_ch_update ( sdw , stream - > bd , stream - > num_bd ,
2019-05-01 18:57:38 +03:00
& stream - > num_ch_bd , pcm ) ;
2018-04-26 16:08:58 +03:00
intel_pdi_get_ch_update ( sdw , stream - > in , stream - > num_in ,
2019-05-01 18:57:38 +03:00
& stream - > num_ch_in , pcm ) ;
2018-04-26 16:08:58 +03:00
intel_pdi_get_ch_update ( sdw , stream - > out , stream - > num_out ,
2019-05-01 18:57:38 +03:00
& stream - > num_ch_out , pcm ) ;
2018-04-26 16:08:58 +03:00
return 0 ;
}
static int intel_pdi_ch_update ( struct sdw_intel * sdw )
{
/* First update PCM streams followed by PDM streams */
intel_pdi_stream_ch_update ( sdw , & sdw - > cdns . pcm , true ) ;
intel_pdi_stream_ch_update ( sdw , & sdw - > cdns . pdm , false ) ;
return 0 ;
}
static void
intel_pdi_shim_configure ( struct sdw_intel * sdw , struct sdw_cdns_pdi * pdi )
{
2020-02-15 04:47:36 +03:00
void __iomem * shim = sdw - > link_res - > shim ;
2018-04-26 16:08:58 +03:00
unsigned int link_id = sdw - > instance ;
int pdi_conf = 0 ;
2019-10-23 02:29:48 +03:00
/* the Bulk and PCM streams are not contiguous */
pdi - > intel_alh_id = ( link_id * 16 ) + pdi - > num + 3 ;
if ( pdi - > num > = 2 )
pdi - > intel_alh_id + = 2 ;
2018-04-26 16:08:58 +03:00
/*
* Program stream parameters to stream SHIM register
* This is applicable for PCM stream only .
*/
if ( pdi - > type ! = SDW_STREAM_PCM )
return ;
if ( pdi - > dir = = SDW_DATA_DIR_RX )
pdi_conf | = SDW_SHIM_PCMSYCM_DIR ;
else
pdi_conf & = ~ ( SDW_SHIM_PCMSYCM_DIR ) ;
pdi_conf | = ( pdi - > intel_alh_id < <
SDW_REG_SHIFT ( SDW_SHIM_PCMSYCM_STREAM ) ) ;
pdi_conf | = ( pdi - > l_ch_num < < SDW_REG_SHIFT ( SDW_SHIM_PCMSYCM_LCHN ) ) ;
pdi_conf | = ( pdi - > h_ch_num < < SDW_REG_SHIFT ( SDW_SHIM_PCMSYCM_HCHN ) ) ;
intel_writew ( shim , SDW_SHIM_PCMSYCHM ( link_id , pdi - > num ) , pdi_conf ) ;
}
static void
intel_pdi_alh_configure ( struct sdw_intel * sdw , struct sdw_cdns_pdi * pdi )
{
2020-02-15 04:47:36 +03:00
void __iomem * alh = sdw - > link_res - > alh ;
2018-04-26 16:08:58 +03:00
unsigned int link_id = sdw - > instance ;
unsigned int conf ;
2019-10-23 02:29:48 +03:00
/* the Bulk and PCM streams are not contiguous */
pdi - > intel_alh_id = ( link_id * 16 ) + pdi - > num + 3 ;
if ( pdi - > num > = 2 )
pdi - > intel_alh_id + = 2 ;
2018-04-26 16:08:58 +03:00
/* Program Stream config ALH register */
conf = intel_readl ( alh , SDW_ALH_STRMZCFG ( pdi - > intel_alh_id ) ) ;
conf | = ( SDW_ALH_STRMZCFG_DMAT_VAL < <
SDW_REG_SHIFT ( SDW_ALH_STRMZCFG_DMAT ) ) ;
conf | = ( ( pdi - > ch_count - 1 ) < <
SDW_REG_SHIFT ( SDW_ALH_STRMZCFG_CHN ) ) ;
intel_writel ( alh , SDW_ALH_STRMZCFG ( pdi - > intel_alh_id ) , conf ) ;
}
2019-12-12 04:45:02 +03:00
static int intel_params_stream ( struct sdw_intel * sdw ,
2019-05-01 18:57:38 +03:00
struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai ,
2019-12-12 04:45:02 +03:00
struct snd_pcm_hw_params * hw_params ,
int link_id , int alh_stream_id )
2018-04-26 16:09:05 +03:00
{
2020-02-15 04:47:36 +03:00
struct sdw_intel_link_res * res = sdw - > link_res ;
2019-12-12 04:45:02 +03:00
struct sdw_intel_stream_params_data params_data ;
2019-08-06 03:55:06 +03:00
2019-12-12 04:45:02 +03:00
params_data . substream = substream ;
params_data . dai = dai ;
params_data . hw_params = hw_params ;
params_data . link_id = link_id ;
params_data . alh_stream_id = alh_stream_id ;
2018-04-26 16:09:05 +03:00
2019-12-12 04:45:02 +03:00
if ( res - > ops & & res - > ops - > params_stream & & res - > dev )
return res - > ops - > params_stream ( res - > dev ,
& params_data ) ;
2018-04-26 16:09:05 +03:00
return - EIO ;
}
2020-02-15 04:47:40 +03:00
static int intel_free_stream ( struct sdw_intel * sdw ,
struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai ,
int link_id )
{
struct sdw_intel_link_res * res = sdw - > link_res ;
struct sdw_intel_stream_free_data free_data ;
free_data . substream = substream ;
free_data . dai = dai ;
free_data . link_id = link_id ;
if ( res - > ops & & res - > ops - > free_stream & & res - > dev )
return res - > ops - > free_stream ( res - > dev ,
& free_data ) ;
return 0 ;
}
2018-07-27 12:14:17 +03:00
/*
* bank switch routines
*/
static int intel_pre_bank_switch ( struct sdw_bus * bus )
{
struct sdw_cdns * cdns = bus_to_cdns ( bus ) ;
struct sdw_intel * sdw = cdns_to_intel ( cdns ) ;
/* Write to register only for multi-link */
if ( ! bus - > multi_link )
return 0 ;
2020-07-16 18:09:41 +03:00
intel_shim_sync_arm ( sdw ) ;
2020-07-16 18:09:40 +03:00
2018-07-27 12:14:17 +03:00
return 0 ;
}
static int intel_post_bank_switch ( struct sdw_bus * bus )
{
struct sdw_cdns * cdns = bus_to_cdns ( bus ) ;
struct sdw_intel * sdw = cdns_to_intel ( cdns ) ;
2020-02-15 04:47:36 +03:00
void __iomem * shim = sdw - > link_res - > shim ;
2018-07-27 12:14:17 +03:00
int sync_reg , ret ;
/* Write to register only for multi-link */
if ( ! bus - > multi_link )
return 0 ;
2020-07-16 18:09:40 +03:00
mutex_lock ( sdw - > link_res - > shim_lock ) ;
2018-07-27 12:14:17 +03:00
/* Read SYNC register */
sync_reg = intel_readl ( shim , SDW_SHIM_SYNC ) ;
/*
* post_bank_switch ( ) ops is called from the bus in loop for
* all the Masters in the steam with the expectation that
* we trigger the bankswitch for the only first Master in the list
* and do nothing for the other Masters
*
* So , set the SYNCGO bit only if CMDSYNC bit is set for any Master .
*/
2020-07-16 18:09:40 +03:00
if ( ! ( sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK ) ) {
ret = 0 ;
goto unlock ;
}
2018-07-27 12:14:17 +03:00
/*
* Set SyncGO bit to synchronously trigger a bank switch for
* all the masters . A write to SYNCGO bit clears CMDSYNC bit for all
* the Masters .
*/
sync_reg | = SDW_SHIM_SYNC_SYNCGO ;
ret = intel_clear_bit ( shim , SDW_SHIM_SYNC , sync_reg ,
2019-05-01 18:57:38 +03:00
SDW_SHIM_SYNC_SYNCGO ) ;
2020-07-16 18:09:40 +03:00
unlock :
mutex_unlock ( sdw - > link_res - > shim_lock ) ;
2018-07-27 12:14:17 +03:00
if ( ret < 0 )
2019-05-01 18:57:45 +03:00
dev_err ( sdw - > cdns . dev , " Post bank switch failed: %d \n " , ret ) ;
2018-07-27 12:14:17 +03:00
return ret ;
}
2018-04-26 16:09:05 +03:00
/*
* DAI routines
*/
2020-02-15 04:47:39 +03:00
static int intel_startup ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
/*
* TODO : add pm_runtime support here , the startup callback
* will make sure the IP is ' active '
*/
2020-06-30 21:43:54 +03:00
return 0 ;
2020-02-15 04:47:39 +03:00
}
2018-04-26 16:09:05 +03:00
static int intel_hw_params ( struct snd_pcm_substream * substream ,
2019-05-01 18:57:38 +03:00
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
2018-04-26 16:09:05 +03:00
{
struct sdw_cdns * cdns = snd_soc_dai_get_drvdata ( dai ) ;
struct sdw_intel * sdw = cdns_to_intel ( cdns ) ;
struct sdw_cdns_dma_data * dma ;
2019-09-16 22:23:46 +03:00
struct sdw_cdns_pdi * pdi ;
2018-04-26 16:09:05 +03:00
struct sdw_stream_config sconfig ;
struct sdw_port_config * pconfig ;
2019-09-16 22:23:46 +03:00
int ch , dir ;
int ret ;
2018-04-26 16:09:05 +03:00
bool pcm = true ;
dma = snd_soc_dai_get_dma_data ( dai , substream ) ;
if ( ! dma )
return - EIO ;
ch = params_channels ( params ) ;
if ( substream - > stream = = SNDRV_PCM_STREAM_CAPTURE )
dir = SDW_DATA_DIR_RX ;
else
dir = SDW_DATA_DIR_TX ;
2019-09-16 22:23:46 +03:00
if ( dma - > stream_type = = SDW_STREAM_PDM )
2018-04-26 16:09:05 +03:00
pcm = false ;
2019-09-16 22:23:46 +03:00
if ( pcm )
2019-09-16 22:23:48 +03:00
pdi = sdw_cdns_alloc_pdi ( cdns , & cdns - > pcm , ch , dir , dai - > id ) ;
2019-09-16 22:23:46 +03:00
else
2019-09-16 22:23:48 +03:00
pdi = sdw_cdns_alloc_pdi ( cdns , & cdns - > pdm , ch , dir , dai - > id ) ;
2019-09-16 22:23:46 +03:00
if ( ! pdi ) {
ret = - EINVAL ;
goto error ;
2018-04-26 16:09:05 +03:00
}
2019-09-16 22:23:46 +03:00
/* do run-time configurations for SHIM, ALH and PDI/PORT */
intel_pdi_shim_configure ( sdw , pdi ) ;
intel_pdi_alh_configure ( sdw , pdi ) ;
sdw_cdns_config_stream ( cdns , ch , dir , pdi ) ;
2018-04-26 16:09:05 +03:00
/* Inform DSP about PDI stream number */
2019-12-12 04:45:02 +03:00
ret = intel_params_stream ( sdw , substream , dai , params ,
sdw - > instance ,
2019-09-16 22:23:46 +03:00
pdi - > intel_alh_id ) ;
if ( ret )
goto error ;
2018-04-26 16:09:05 +03:00
sconfig . direction = dir ;
sconfig . ch_count = ch ;
sconfig . frame_rate = params_rate ( params ) ;
sconfig . type = dma - > stream_type ;
if ( dma - > stream_type = = SDW_STREAM_PDM ) {
sconfig . frame_rate * = 50 ;
sconfig . bps = 1 ;
} else {
sconfig . bps = snd_pcm_format_width ( params_format ( params ) ) ;
}
/* Port configuration */
2019-09-16 22:23:46 +03:00
pconfig = kcalloc ( 1 , sizeof ( * pconfig ) , GFP_KERNEL ) ;
2018-04-26 16:09:05 +03:00
if ( ! pconfig ) {
ret = - ENOMEM ;
2019-09-16 22:23:46 +03:00
goto error ;
2018-04-26 16:09:05 +03:00
}
2019-09-16 22:23:46 +03:00
pconfig - > num = pdi - > num ;
pconfig - > ch_mask = ( 1 < < ch ) - 1 ;
2018-04-26 16:09:05 +03:00
ret = sdw_stream_add_master ( & cdns - > bus , & sconfig ,
2019-09-16 22:23:46 +03:00
pconfig , 1 , dma - > stream ) ;
if ( ret )
2019-05-01 18:57:45 +03:00
dev_err ( cdns - > dev , " add master to stream failed:%d \n " , ret ) ;
2018-04-26 16:09:05 +03:00
kfree ( pconfig ) ;
2019-09-16 22:23:46 +03:00
error :
2018-04-26 16:09:05 +03:00
return ret ;
}
2020-02-15 04:47:37 +03:00
static int intel_prepare ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
struct sdw_cdns_dma_data * dma ;
dma = snd_soc_dai_get_dma_data ( dai , substream ) ;
if ( ! dma ) {
dev_err ( dai - > dev , " failed to get dma data in %s " ,
__func__ ) ;
return - EIO ;
}
return sdw_prepare_stream ( dma - > stream ) ;
}
2020-02-15 04:47:38 +03:00
static int intel_trigger ( struct snd_pcm_substream * substream , int cmd ,
struct snd_soc_dai * dai )
{
struct sdw_cdns_dma_data * dma ;
int ret ;
dma = snd_soc_dai_get_dma_data ( dai , substream ) ;
if ( ! dma ) {
dev_err ( dai - > dev , " failed to get dma data in %s " , __func__ ) ;
return - EIO ;
}
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
case SNDRV_PCM_TRIGGER_RESUME :
ret = sdw_enable_stream ( dma - > stream ) ;
break ;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
case SNDRV_PCM_TRIGGER_SUSPEND :
case SNDRV_PCM_TRIGGER_STOP :
ret = sdw_disable_stream ( dma - > stream ) ;
break ;
default :
ret = - EINVAL ;
break ;
}
if ( ret )
dev_err ( dai - > dev ,
" %s trigger %d failed: %d " ,
__func__ , cmd , ret ) ;
return ret ;
}
2018-04-26 16:09:05 +03:00
static int
intel_hw_free ( struct snd_pcm_substream * substream , struct snd_soc_dai * dai )
{
struct sdw_cdns * cdns = snd_soc_dai_get_drvdata ( dai ) ;
2020-02-15 04:47:40 +03:00
struct sdw_intel * sdw = cdns_to_intel ( cdns ) ;
2018-04-26 16:09:05 +03:00
struct sdw_cdns_dma_data * dma ;
int ret ;
dma = snd_soc_dai_get_dma_data ( dai , substream ) ;
if ( ! dma )
return - EIO ;
2020-02-15 04:47:40 +03:00
ret = sdw_deprepare_stream ( dma - > stream ) ;
if ( ret ) {
dev_err ( dai - > dev , " sdw_deprepare_stream: failed %d " , ret ) ;
return ret ;
}
2018-04-26 16:09:05 +03:00
ret = sdw_stream_remove_master ( & cdns - > bus , dma - > stream ) ;
2020-02-15 04:47:40 +03:00
if ( ret < 0 ) {
2019-05-01 18:57:45 +03:00
dev_err ( dai - > dev , " remove master from stream %s failed: %d \n " ,
2019-05-01 18:57:38 +03:00
dma - > stream - > name , ret ) ;
2020-02-15 04:47:40 +03:00
return ret ;
}
2018-04-26 16:09:05 +03:00
2020-02-15 04:47:40 +03:00
ret = intel_free_stream ( sdw , substream , dai , sdw - > instance ) ;
if ( ret < 0 ) {
dev_err ( dai - > dev , " intel_free_stream: failed %d " , ret ) ;
return ret ;
}
return 0 ;
2018-04-26 16:09:05 +03:00
}
2019-08-06 03:55:22 +03:00
static void intel_shutdown ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
}
2018-04-26 16:09:05 +03:00
static int intel_pcm_set_sdw_stream ( struct snd_soc_dai * dai ,
2019-05-01 18:57:38 +03:00
void * stream , int direction )
2018-04-26 16:09:05 +03:00
{
return cdns_set_sdw_stream ( dai , stream , true , direction ) ;
}
static int intel_pdm_set_sdw_stream ( struct snd_soc_dai * dai ,
2019-05-01 18:57:38 +03:00
void * stream , int direction )
2018-04-26 16:09:05 +03:00
{
return cdns_set_sdw_stream ( dai , stream , false , direction ) ;
}
2020-06-30 21:43:52 +03:00
static void * intel_get_sdw_stream ( struct snd_soc_dai * dai ,
int direction )
{
struct sdw_cdns_dma_data * dma ;
if ( direction = = SNDRV_PCM_STREAM_PLAYBACK )
dma = dai - > playback_dma_data ;
else
dma = dai - > capture_dma_data ;
if ( ! dma )
return NULL ;
return dma - > stream ;
}
2018-10-27 16:34:42 +03:00
static const struct snd_soc_dai_ops intel_pcm_dai_ops = {
2020-02-15 04:47:39 +03:00
. startup = intel_startup ,
2018-04-26 16:09:05 +03:00
. hw_params = intel_hw_params ,
2020-02-15 04:47:37 +03:00
. prepare = intel_prepare ,
2020-02-15 04:47:38 +03:00
. trigger = intel_trigger ,
2018-04-26 16:09:05 +03:00
. hw_free = intel_hw_free ,
2019-08-06 03:55:22 +03:00
. shutdown = intel_shutdown ,
2018-04-26 16:09:05 +03:00
. set_sdw_stream = intel_pcm_set_sdw_stream ,
2020-06-30 21:43:52 +03:00
. get_sdw_stream = intel_get_sdw_stream ,
2018-04-26 16:09:05 +03:00
} ;
2018-10-27 16:34:42 +03:00
static const struct snd_soc_dai_ops intel_pdm_dai_ops = {
2020-02-15 04:47:39 +03:00
. startup = intel_startup ,
2018-04-26 16:09:05 +03:00
. hw_params = intel_hw_params ,
2020-02-15 04:47:37 +03:00
. prepare = intel_prepare ,
2020-02-15 04:47:38 +03:00
. trigger = intel_trigger ,
2018-04-26 16:09:05 +03:00
. hw_free = intel_hw_free ,
2019-08-06 03:55:22 +03:00
. shutdown = intel_shutdown ,
2018-04-26 16:09:05 +03:00
. set_sdw_stream = intel_pdm_set_sdw_stream ,
2020-06-30 21:43:52 +03:00
. get_sdw_stream = intel_get_sdw_stream ,
2018-04-26 16:09:05 +03:00
} ;
static const struct snd_soc_component_driver dai_component = {
. name = " soundwire " ,
} ;
static int intel_create_dai ( struct sdw_cdns * cdns ,
2019-05-01 18:57:38 +03:00
struct snd_soc_dai_driver * dais ,
enum intel_pdi_type type ,
u32 num , u32 off , u32 max_ch , bool pcm )
2018-04-26 16:09:05 +03:00
{
int i ;
if ( num = = 0 )
return 0 ;
/* TODO: Read supported rates/formats from hardware */
for ( i = off ; i < ( off + num ) ; i + + ) {
dais [ i ] . name = kasprintf ( GFP_KERNEL , " SDW%d Pin%d " ,
2019-05-01 18:57:38 +03:00
cdns - > instance , i ) ;
2018-04-26 16:09:05 +03:00
if ( ! dais [ i ] . name )
return - ENOMEM ;
if ( type = = INTEL_PDI_BD | | type = = INTEL_PDI_OUT ) {
dais [ i ] . playback . channels_min = 1 ;
dais [ i ] . playback . channels_max = max_ch ;
dais [ i ] . playback . rates = SNDRV_PCM_RATE_48000 ;
dais [ i ] . playback . formats = SNDRV_PCM_FMTBIT_S16_LE ;
}
if ( type = = INTEL_PDI_BD | | type = = INTEL_PDI_IN ) {
2019-06-06 14:23:04 +03:00
dais [ i ] . capture . channels_min = 1 ;
dais [ i ] . capture . channels_max = max_ch ;
2018-04-26 16:09:05 +03:00
dais [ i ] . capture . rates = SNDRV_PCM_RATE_48000 ;
dais [ i ] . capture . formats = SNDRV_PCM_FMTBIT_S16_LE ;
}
if ( pcm )
dais [ i ] . ops = & intel_pcm_dai_ops ;
else
dais [ i ] . ops = & intel_pdm_dai_ops ;
}
return 0 ;
}
static int intel_register_dai ( struct sdw_intel * sdw )
{
struct sdw_cdns * cdns = & sdw - > cdns ;
struct sdw_cdns_streams * stream ;
struct snd_soc_dai_driver * dais ;
int num_dai , ret , off = 0 ;
/* DAIs are created based on total number of PDIs supported */
num_dai = cdns - > pcm . num_pdi + cdns - > pdm . num_pdi ;
dais = devm_kcalloc ( cdns - > dev , num_dai , sizeof ( * dais ) , GFP_KERNEL ) ;
if ( ! dais )
return - ENOMEM ;
/* Create PCM DAIs */
stream = & cdns - > pcm ;
2019-09-16 22:23:43 +03:00
ret = intel_create_dai ( cdns , dais , INTEL_PDI_IN , cdns - > pcm . num_in ,
2019-05-02 13:59:25 +03:00
off , stream - > num_ch_in , true ) ;
2018-04-26 16:09:05 +03:00
if ( ret )
return ret ;
off + = cdns - > pcm . num_in ;
2019-05-02 13:59:25 +03:00
ret = intel_create_dai ( cdns , dais , INTEL_PDI_OUT , cdns - > pcm . num_out ,
off , stream - > num_ch_out , true ) ;
2018-04-26 16:09:05 +03:00
if ( ret )
return ret ;
off + = cdns - > pcm . num_out ;
2019-05-02 13:59:25 +03:00
ret = intel_create_dai ( cdns , dais , INTEL_PDI_BD , cdns - > pcm . num_bd ,
off , stream - > num_ch_bd , true ) ;
2018-04-26 16:09:05 +03:00
if ( ret )
return ret ;
/* Create PDM DAIs */
stream = & cdns - > pdm ;
off + = cdns - > pcm . num_bd ;
2019-05-02 13:59:25 +03:00
ret = intel_create_dai ( cdns , dais , INTEL_PDI_IN , cdns - > pdm . num_in ,
off , stream - > num_ch_in , false ) ;
2018-04-26 16:09:05 +03:00
if ( ret )
return ret ;
off + = cdns - > pdm . num_in ;
2019-05-02 13:59:25 +03:00
ret = intel_create_dai ( cdns , dais , INTEL_PDI_OUT , cdns - > pdm . num_out ,
off , stream - > num_ch_out , false ) ;
2018-04-26 16:09:05 +03:00
if ( ret )
return ret ;
2019-09-16 22:23:43 +03:00
off + = cdns - > pdm . num_out ;
2019-05-02 13:59:25 +03:00
ret = intel_create_dai ( cdns , dais , INTEL_PDI_BD , cdns - > pdm . num_bd ,
off , stream - > num_ch_bd , false ) ;
2018-04-26 16:09:05 +03:00
if ( ret )
return ret ;
return snd_soc_register_component ( cdns - > dev , & dai_component ,
2019-05-01 18:57:38 +03:00
dais , num_dai ) ;
2018-04-26 16:09:05 +03:00
}
2019-08-06 03:55:16 +03:00
static int sdw_master_read_intel_prop ( struct sdw_bus * bus )
{
struct sdw_master_prop * prop = & bus - > prop ;
struct fwnode_handle * link ;
char name [ 32 ] ;
2019-08-21 21:58:21 +03:00
u32 quirk_mask ;
2019-08-06 03:55:16 +03:00
/* Find master handle */
snprintf ( name , sizeof ( name ) ,
" mipi-sdw-link-%d-subproperties " , bus - > link_id ) ;
link = device_get_named_child_node ( bus - > dev , name ) ;
if ( ! link ) {
dev_err ( bus - > dev , " Master node %s not found \n " , name ) ;
return - EIO ;
}
fwnode_property_read_u32 ( link ,
" intel-sdw-ip-clock " ,
& prop - > mclk_freq ) ;
2019-08-21 21:58:21 +03:00
2020-01-14 02:11:29 +03:00
/* the values reported by BIOS are the 2x clock, not the bus clock */
prop - > mclk_freq / = 2 ;
2019-08-21 21:58:21 +03:00
fwnode_property_read_u32 ( link ,
" intel-quirk-mask " ,
& quirk_mask ) ;
if ( quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE )
prop - > hw_disabled = true ;
2019-08-06 03:55:16 +03:00
return 0 ;
}
2017-12-14 08:49:43 +03:00
static int intel_prop_read ( struct sdw_bus * bus )
{
/* Initialize with default handler to read all DisCo properties */
sdw_master_read_prop ( bus ) ;
2019-08-06 03:55:16 +03:00
/* read Intel-specific properties */
sdw_master_read_intel_prop ( bus ) ;
2017-12-14 08:49:43 +03:00
return 0 ;
}
2018-04-26 16:08:43 +03:00
static struct sdw_master_ops sdw_intel_ops = {
. read_prop = sdw_master_read_prop ,
. xfer_msg = cdns_xfer_msg ,
. xfer_msg_defer = cdns_xfer_msg_defer ,
. reset_page_addr = cdns_reset_page_addr ,
2018-04-26 16:08:48 +03:00
. set_bus_conf = cdns_bus_conf ,
2018-07-27 12:14:17 +03:00
. pre_bank_switch = intel_pre_bank_switch ,
. post_bank_switch = intel_post_bank_switch ,
2018-04-26 16:08:43 +03:00
} ;
2019-10-23 02:54:46 +03:00
static int intel_init ( struct sdw_intel * sdw )
{
2020-07-16 18:09:40 +03:00
bool clock_stop ;
2019-10-23 02:54:46 +03:00
/* Initialize shim and controller */
intel_link_power_up ( sdw ) ;
2020-07-16 18:09:40 +03:00
clock_stop = sdw_cdns_is_clock_stop ( & sdw - > cdns ) ;
intel_shim_init ( sdw , clock_stop ) ;
if ( clock_stop )
return 0 ;
2019-10-23 02:54:46 +03:00
2020-03-17 19:33:14 +03:00
return sdw_cdns_init ( & sdw - > cdns ) ;
2019-10-23 02:54:46 +03:00
}
2017-12-14 08:49:43 +03:00
/*
* probe and init
*/
2020-05-31 21:20:57 +03:00
static int intel_master_probe ( struct platform_device * pdev )
2017-12-14 08:49:43 +03:00
{
2020-05-31 21:20:57 +03:00
struct device * dev = & pdev - > dev ;
2017-12-14 08:49:43 +03:00
struct sdw_intel * sdw ;
2020-05-31 21:20:58 +03:00
struct sdw_cdns * cdns ;
2020-05-31 21:20:57 +03:00
struct sdw_bus * bus ;
2017-12-14 08:49:43 +03:00
int ret ;
2020-05-31 21:20:57 +03:00
sdw = devm_kzalloc ( dev , sizeof ( * sdw ) , GFP_KERNEL ) ;
2017-12-14 08:49:43 +03:00
if ( ! sdw )
return - ENOMEM ;
2020-05-31 21:20:58 +03:00
cdns = & sdw - > cdns ;
bus = & cdns - > bus ;
2020-05-31 21:20:57 +03:00
2017-12-14 08:49:43 +03:00
sdw - > instance = pdev - > id ;
2020-05-31 21:20:57 +03:00
sdw - > link_res = dev_get_platdata ( dev ) ;
2020-05-31 21:20:58 +03:00
cdns - > dev = dev ;
cdns - > registers = sdw - > link_res - > registers ;
cdns - > instance = sdw - > instance ;
cdns - > msg_count = 0 ;
2020-05-31 21:20:57 +03:00
bus - > link_id = pdev - > id ;
2017-12-14 08:49:43 +03:00
2020-05-31 21:20:58 +03:00
sdw_cdns_probe ( cdns ) ;
2017-12-14 08:49:43 +03:00
/* Set property read ops */
2018-04-26 16:08:43 +03:00
sdw_intel_ops . read_prop = intel_prop_read ;
2020-05-31 21:20:57 +03:00
bus - > ops = & sdw_intel_ops ;
2017-12-14 08:49:43 +03:00
2020-05-31 21:20:57 +03:00
/* set driver data, accessed by snd_soc_dai_get_drvdata() */
2020-05-31 21:20:58 +03:00
dev_set_drvdata ( dev , cdns ) ;
2017-12-14 08:49:43 +03:00
2020-05-31 21:20:57 +03:00
ret = sdw_bus_master_add ( bus , dev , dev - > fwnode ) ;
2017-12-14 08:49:43 +03:00
if ( ret ) {
2020-05-31 21:20:57 +03:00
dev_err ( dev , " sdw_bus_master_add fail: %d \n " , ret ) ;
2019-10-23 02:54:47 +03:00
return ret ;
2017-12-14 08:49:43 +03:00
}
2020-05-31 21:21:02 +03:00
if ( bus - > prop . hw_disabled )
2020-05-31 21:20:57 +03:00
dev_info ( dev ,
" SoundWire master %d is disabled, will be ignored \n " ,
bus - > link_id ) ;
2020-05-31 21:21:02 +03:00
/* Acquire IRQ */
ret = request_threaded_irq ( sdw - > link_res - > irq ,
sdw_cdns_irq , sdw_cdns_thread ,
IRQF_SHARED , KBUILD_MODNAME , cdns ) ;
if ( ret < 0 ) {
dev_err ( dev , " unable to grab IRQ %d, disabling device \n " ,
sdw - > link_res - > irq ) ;
goto err_init ;
}
return 0 ;
err_init :
sdw_bus_master_delete ( bus ) ;
return ret ;
}
int intel_master_startup ( struct platform_device * pdev )
{
struct sdw_cdns_stream_config config ;
struct device * dev = & pdev - > dev ;
struct sdw_cdns * cdns = dev_get_drvdata ( dev ) ;
struct sdw_intel * sdw = cdns_to_intel ( cdns ) ;
struct sdw_bus * bus = & cdns - > bus ;
int ret ;
if ( bus - > prop . hw_disabled ) {
dev_info ( dev ,
" SoundWire master %d is disabled, ignoring \n " ,
sdw - > instance ) ;
2019-08-21 21:58:21 +03:00
return 0 ;
}
2019-10-23 02:54:46 +03:00
/* Initialize shim, controller and Cadence IP */
ret = intel_init ( sdw ) ;
2017-12-14 08:49:43 +03:00
if ( ret )
goto err_init ;
2018-04-26 16:08:58 +03:00
/* Read the PDI config and initialize cadence PDI */
intel_pdi_init ( sdw , & config ) ;
2020-05-31 21:20:58 +03:00
ret = sdw_cdns_pdi_init ( cdns , config ) ;
2017-12-14 08:49:43 +03:00
if ( ret )
goto err_init ;
2018-04-26 16:08:58 +03:00
intel_pdi_ch_update ( sdw ) ;
2020-05-31 21:20:58 +03:00
ret = sdw_cdns_enable_interrupt ( cdns , true ) ;
2019-10-23 02:54:44 +03:00
if ( ret < 0 ) {
2020-05-31 21:20:57 +03:00
dev_err ( dev , " cannot enable interrupts \n " ) ;
2019-10-23 02:54:44 +03:00
goto err_init ;
}
2020-05-31 21:20:58 +03:00
ret = sdw_cdns_exit_reset ( cdns ) ;
2019-10-23 02:54:44 +03:00
if ( ret < 0 ) {
2020-05-31 21:20:57 +03:00
dev_err ( dev , " unable to exit bus reset sequence \n " ) ;
2019-10-23 02:54:47 +03:00
goto err_interrupt ;
2019-10-23 02:54:44 +03:00
}
2018-04-26 16:09:05 +03:00
/* Register DAIs */
ret = intel_register_dai ( sdw ) ;
if ( ret ) {
2020-05-31 21:20:57 +03:00
dev_err ( dev , " DAI registration failed: %d \n " , ret ) ;
snd_soc_unregister_component ( dev ) ;
2019-10-23 02:54:47 +03:00
goto err_interrupt ;
2018-04-26 16:09:05 +03:00
}
2019-08-21 21:58:20 +03:00
intel_debugfs_init ( sdw ) ;
2017-12-14 08:49:43 +03:00
return 0 ;
2019-10-23 02:54:47 +03:00
err_interrupt :
2020-05-31 21:20:58 +03:00
sdw_cdns_enable_interrupt ( cdns , false ) ;
2017-12-14 08:49:43 +03:00
err_init :
return ret ;
}
2020-05-31 21:20:57 +03:00
static int intel_master_remove ( struct platform_device * pdev )
2017-12-14 08:49:43 +03:00
{
2020-05-31 21:20:57 +03:00
struct device * dev = & pdev - > dev ;
2020-05-31 21:20:58 +03:00
struct sdw_cdns * cdns = dev_get_drvdata ( dev ) ;
struct sdw_intel * sdw = cdns_to_intel ( cdns ) ;
struct sdw_bus * bus = & cdns - > bus ;
2020-05-31 21:20:57 +03:00
if ( ! bus - > prop . hw_disabled ) {
2019-08-21 21:58:21 +03:00
intel_debugfs_exit ( sdw ) ;
2020-05-31 21:20:58 +03:00
sdw_cdns_enable_interrupt ( cdns , false ) ;
2020-02-15 04:47:36 +03:00
free_irq ( sdw - > link_res - > irq , sdw ) ;
2020-05-31 21:20:57 +03:00
snd_soc_unregister_component ( dev ) ;
2019-08-21 21:58:21 +03:00
}
2020-05-31 21:20:57 +03:00
sdw_bus_master_delete ( bus ) ;
2017-12-14 08:49:43 +03:00
return 0 ;
}
static struct platform_driver sdw_intel_drv = {
2020-05-31 21:20:57 +03:00
. probe = intel_master_probe ,
. remove = intel_master_remove ,
2017-12-14 08:49:43 +03:00
. driver = {
2020-05-31 21:21:02 +03:00
. name = " intel-sdw " ,
2017-12-14 08:49:43 +03:00
} ,
} ;
module_platform_driver ( sdw_intel_drv ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
2020-05-31 21:21:02 +03:00
MODULE_ALIAS ( " platform:intel-sdw " ) ;
2017-12-14 08:49:43 +03:00
MODULE_DESCRIPTION ( " Intel Soundwire Master Driver " ) ;