2019-05-28 19:57:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2015-07-16 12:36:04 +03:00
/*
* Copyright ( C ) STMicroelectronics SA 2015
* Authors : Arnaud Pouliquen < arnaud . pouliquen @ st . com >
* for STMicroelectronics .
*/
# include <linux/module.h>
2015-07-20 20:36:15 +03:00
# include <linux/pinctrl/consumer.h>
2016-10-24 17:42:53 +03:00
# include <linux/delay.h>
2015-07-16 12:36:04 +03:00
# include "uniperif.h"
2016-04-07 12:25:33 +03:00
/*
* User frame size shall be 2 , 4 , 6 or 8 32 - bits words length
* ( i . e . 8 , 16 , 24 or 32 bytes )
* This constraint comes from allowed values for
* UNIPERIF_I2S_FMT_NUM_CH register
*/
# define UNIPERIF_MAX_FRAME_SZ 0x20
# define UNIPERIF_ALLOWED_FRAME_SZ (0x08 | 0x10 | 0x18 | UNIPERIF_MAX_FRAME_SZ)
2016-09-13 10:58:23 +03:00
struct sti_uniperiph_dev_data {
unsigned int id ; /* Nb available player instances */
unsigned int version ; /* player IP version */
unsigned int stream ;
const char * dai_names ;
enum uniperif_type type ;
} ;
static const struct sti_uniperiph_dev_data sti_uniplayer_hdmi = {
. id = 0 ,
. version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ,
. stream = SNDRV_PCM_STREAM_PLAYBACK ,
. dai_names = " Uni Player #0 (HDMI) " ,
. type = SND_ST_UNIPERIF_TYPE_HDMI
} ;
static const struct sti_uniperiph_dev_data sti_uniplayer_pcm_out = {
. id = 1 ,
. version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ,
. stream = SNDRV_PCM_STREAM_PLAYBACK ,
. dai_names = " Uni Player #1 (PCM OUT) " ,
. type = SND_ST_UNIPERIF_TYPE_PCM | SND_ST_UNIPERIF_TYPE_TDM ,
} ;
static const struct sti_uniperiph_dev_data sti_uniplayer_dac = {
. id = 2 ,
. version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ,
. stream = SNDRV_PCM_STREAM_PLAYBACK ,
. dai_names = " Uni Player #2 (DAC) " ,
. type = SND_ST_UNIPERIF_TYPE_PCM ,
} ;
static const struct sti_uniperiph_dev_data sti_uniplayer_spdif = {
. id = 3 ,
. version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ,
. stream = SNDRV_PCM_STREAM_PLAYBACK ,
. dai_names = " Uni Player #3 (SPDIF) " ,
. type = SND_ST_UNIPERIF_TYPE_SPDIF
} ;
static const struct sti_uniperiph_dev_data sti_unireader_pcm_in = {
. id = 0 ,
. version = SND_ST_UNIPERIF_VERSION_UNI_RDR_1_0 ,
. stream = SNDRV_PCM_STREAM_CAPTURE ,
. dai_names = " Uni Reader #0 (PCM IN) " ,
. type = SND_ST_UNIPERIF_TYPE_PCM | SND_ST_UNIPERIF_TYPE_TDM ,
} ;
static const struct sti_uniperiph_dev_data sti_unireader_hdmi_in = {
. id = 1 ,
. version = SND_ST_UNIPERIF_VERSION_UNI_RDR_1_0 ,
. stream = SNDRV_PCM_STREAM_CAPTURE ,
. dai_names = " Uni Reader #1 (HDMI IN) " ,
. type = SND_ST_UNIPERIF_TYPE_PCM ,
} ;
static const struct of_device_id snd_soc_sti_match [ ] = {
{ . compatible = " st,stih407-uni-player-hdmi " ,
. data = & sti_uniplayer_hdmi
} ,
{ . compatible = " st,stih407-uni-player-pcm-out " ,
. data = & sti_uniplayer_pcm_out
} ,
{ . compatible = " st,stih407-uni-player-dac " ,
. data = & sti_uniplayer_dac
} ,
{ . compatible = " st,stih407-uni-player-spdif " ,
. data = & sti_uniplayer_spdif
} ,
{ . compatible = " st,stih407-uni-reader-pcm_in " ,
. data = & sti_unireader_pcm_in
} ,
{ . compatible = " st,stih407-uni-reader-hdmi " ,
. data = & sti_unireader_hdmi_in
} ,
{ } ,
} ;
2021-04-09 04:59:53 +03:00
MODULE_DEVICE_TABLE ( of , snd_soc_sti_match ) ;
2016-09-13 10:58:23 +03:00
2016-10-24 17:42:53 +03:00
int sti_uniperiph_reset ( struct uniperif * uni )
{
int count = 10 ;
/* Reset uniperipheral uni */
SET_UNIPERIF_SOFT_RST_SOFT_RST ( uni ) ;
if ( uni - > ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ) {
while ( GET_UNIPERIF_SOFT_RST_SOFT_RST ( uni ) & & count ) {
udelay ( 5 ) ;
count - - ;
}
}
if ( ! count ) {
dev_err ( uni - > dev , " Failed to reset uniperif \n " ) ;
return - EIO ;
}
return 0 ;
}
2016-04-07 12:25:33 +03:00
int sti_uniperiph_set_tdm_slot ( struct snd_soc_dai * dai , unsigned int tx_mask ,
unsigned int rx_mask , int slots ,
int slot_width )
{
struct sti_uniperiph_data * priv = snd_soc_dai_get_drvdata ( dai ) ;
struct uniperif * uni = priv - > dai_data . uni ;
int i , frame_size , avail_slots ;
if ( ! UNIPERIF_TYPE_IS_TDM ( uni ) ) {
dev_err ( uni - > dev , " cpu dai not in tdm mode \n " ) ;
return - EINVAL ;
}
/* store info in unip context */
uni - > tdm_slot . slots = slots ;
uni - > tdm_slot . slot_width = slot_width ;
/* unip is unidirectionnal */
uni - > tdm_slot . mask = ( tx_mask ! = 0 ) ? tx_mask : rx_mask ;
/* number of available timeslots */
for ( i = 0 , avail_slots = 0 ; i < uni - > tdm_slot . slots ; i + + ) {
if ( ( uni - > tdm_slot . mask > > i ) & 0x01 )
avail_slots + + ;
}
uni - > tdm_slot . avail_slots = avail_slots ;
/* frame size in bytes */
frame_size = uni - > tdm_slot . avail_slots * uni - > tdm_slot . slot_width / 8 ;
/* check frame size is allowed */
if ( ( frame_size > UNIPERIF_MAX_FRAME_SZ ) | |
( frame_size & ~ ( int ) UNIPERIF_ALLOWED_FRAME_SZ ) ) {
dev_err ( uni - > dev , " frame size not allowed: %d bytes \n " ,
frame_size ) ;
return - EINVAL ;
}
2016-04-07 12:25:34 +03:00
return 0 ;
}
int sti_uniperiph_fix_tdm_chan ( struct snd_pcm_hw_params * params ,
struct snd_pcm_hw_rule * rule )
{
struct uniperif * uni = rule - > private ;
struct snd_interval t ;
t . min = uni - > tdm_slot . avail_slots ;
t . max = uni - > tdm_slot . avail_slots ;
t . openmin = 0 ;
t . openmax = 0 ;
t . integer = 0 ;
return snd_interval_refine ( hw_param_interval ( params , rule - > var ) , & t ) ;
}
int sti_uniperiph_fix_tdm_format ( struct snd_pcm_hw_params * params ,
struct snd_pcm_hw_rule * rule )
{
struct uniperif * uni = rule - > private ;
struct snd_mask * maskp = hw_param_mask ( params , rule - > var ) ;
u64 format ;
switch ( uni - > tdm_slot . slot_width ) {
case 16 :
format = SNDRV_PCM_FMTBIT_S16_LE ;
break ;
case 32 :
format = SNDRV_PCM_FMTBIT_S32_LE ;
break ;
default :
dev_err ( uni - > dev , " format not supported: %d bits \n " ,
uni - > tdm_slot . slot_width ) ;
return - EINVAL ;
}
maskp - > bits [ 0 ] & = ( u_int32_t ) format ;
maskp - > bits [ 1 ] & = ( u_int32_t ) ( format > > 32 ) ;
/* clear remaining indexes */
memset ( maskp - > bits + 2 , 0 , ( SNDRV_MASK_MAX - 64 ) / 8 ) ;
if ( ! maskp - > bits [ 0 ] & & ! maskp - > bits [ 1 ] )
return - EINVAL ;
2016-04-07 12:25:33 +03:00
return 0 ;
}
int sti_uniperiph_get_tdm_word_pos ( struct uniperif * uni ,
unsigned int * word_pos )
{
int slot_width = uni - > tdm_slot . slot_width / 8 ;
int slots_num = uni - > tdm_slot . slots ;
unsigned int slots_mask = uni - > tdm_slot . mask ;
int i , j , k ;
unsigned int word16_pos [ 4 ] ;
/* word16_pos:
* word16_pos [ 0 ] = WORDX_LSB
* word16_pos [ 1 ] = WORDX_MSB ,
* word16_pos [ 2 ] = WORDX + 1 _LSB
* word16_pos [ 3 ] = WORDX + 1 _MSB
*/
/* set unip word position */
for ( i = 0 , j = 0 , k = 0 ; ( i < slots_num ) & & ( k < WORD_MAX ) ; i + + ) {
if ( ( slots_mask > > i ) & 0x01 ) {
word16_pos [ j ] = i * slot_width ;
if ( slot_width = = 4 ) {
word16_pos [ j + 1 ] = word16_pos [ j ] + 2 ;
j + + ;
}
j + + ;
if ( j > 3 ) {
word_pos [ k ] = word16_pos [ 1 ] |
( word16_pos [ 0 ] < < 8 ) |
( word16_pos [ 3 ] < < 16 ) |
( word16_pos [ 2 ] < < 24 ) ;
j = 0 ;
k + + ;
}
}
}
return 0 ;
}
2015-07-16 12:36:04 +03:00
/*
* sti_uniperiph_dai_create_ctrl
* This function is used to create Ctrl associated to DAI but also pcm device .
* Request is done by front end to associate ctrl with pcm device id
*/
2015-07-17 02:44:09 +03:00
static int sti_uniperiph_dai_create_ctrl ( struct snd_soc_dai * dai )
2015-07-16 12:36:04 +03:00
{
struct sti_uniperiph_data * priv = snd_soc_dai_get_drvdata ( dai ) ;
struct uniperif * uni = priv - > dai_data . uni ;
struct snd_kcontrol_new * ctrl ;
int i ;
if ( ! uni - > num_ctrls )
return 0 ;
for ( i = 0 ; i < uni - > num_ctrls ; i + + ) {
/*
* Several Control can have same name . Controls are indexed on
* Uniperipheral instance ID
*/
ctrl = & uni - > snd_ctrls [ i ] ;
2016-09-13 10:58:23 +03:00
ctrl - > index = uni - > id ;
ctrl - > device = uni - > id ;
2015-07-16 12:36:04 +03:00
}
return snd_soc_add_dai_controls ( dai , uni - > snd_ctrls , uni - > num_ctrls ) ;
}
/*
* DAI
*/
int sti_uniperiph_dai_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
2016-04-07 12:25:35 +03:00
struct sti_uniperiph_data * priv = snd_soc_dai_get_drvdata ( dai ) ;
struct uniperif * uni = priv - > dai_data . uni ;
2015-07-16 12:36:04 +03:00
struct snd_dmaengine_dai_dma_data * dma_data ;
int transfer_size ;
2016-09-13 10:58:23 +03:00
if ( uni - > type = = SND_ST_UNIPERIF_TYPE_TDM )
2016-04-07 12:25:35 +03:00
/* transfer size = user frame size (in 32-bits FIFO cell) */
transfer_size = snd_soc_params_to_frame_size ( params ) / 32 ;
else
transfer_size = params_channels ( params ) * UNIPERIF_FIFO_FRAMES ;
2015-07-16 12:36:04 +03:00
dma_data = snd_soc_dai_get_dma_data ( dai , substream ) ;
dma_data - > maxburst = transfer_size ;
return 0 ;
}
int sti_uniperiph_dai_set_fmt ( struct snd_soc_dai * dai , unsigned int fmt )
{
struct sti_uniperiph_data * priv = snd_soc_dai_get_drvdata ( dai ) ;
priv - > dai_data . uni - > daifmt = fmt ;
return 0 ;
}
2020-01-20 04:04:18 +03:00
static int sti_uniperiph_suspend ( struct snd_soc_component * component )
2015-07-16 12:36:04 +03:00
{
2020-01-20 04:04:18 +03:00
struct sti_uniperiph_data * priv = snd_soc_component_get_drvdata ( component ) ;
2015-07-16 12:36:04 +03:00
struct uniperif * uni = priv - > dai_data . uni ;
int ret ;
/* The uniperipheral should be in stopped state */
if ( uni - > state ! = UNIPERIF_STATE_STOPPED ) {
2016-10-24 17:42:51 +03:00
dev_err ( uni - > dev , " %s: invalid uni state( %d) \n " ,
2015-07-16 12:36:04 +03:00
__func__ , ( int ) uni - > state ) ;
return - EBUSY ;
}
/* Pinctrl: switch pinstate to sleep */
ret = pinctrl_pm_select_sleep_state ( uni - > dev ) ;
if ( ret )
2016-10-24 17:42:51 +03:00
dev_err ( uni - > dev , " %s: failed to select pinctrl state \n " ,
2015-07-16 12:36:04 +03:00
__func__ ) ;
return ret ;
}
2020-01-20 04:04:18 +03:00
static int sti_uniperiph_resume ( struct snd_soc_component * component )
2015-07-16 12:36:04 +03:00
{
2020-01-20 04:04:18 +03:00
struct sti_uniperiph_data * priv = snd_soc_component_get_drvdata ( component ) ;
2015-07-16 12:36:04 +03:00
struct uniperif * uni = priv - > dai_data . uni ;
int ret ;
2016-09-13 10:58:23 +03:00
if ( priv - > dai_data . stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
2015-07-16 12:36:04 +03:00
ret = uni_player_resume ( uni ) ;
if ( ret )
return ret ;
}
/* pinctrl: switch pinstate to default */
ret = pinctrl_pm_select_default_state ( uni - > dev ) ;
if ( ret )
2016-10-24 17:42:51 +03:00
dev_err ( uni - > dev , " %s: failed to select pinctrl state \n " ,
2015-07-16 12:36:04 +03:00
__func__ ) ;
return ret ;
}
static int sti_uniperiph_dai_probe ( struct snd_soc_dai * dai )
{
struct sti_uniperiph_data * priv = snd_soc_dai_get_drvdata ( dai ) ;
struct sti_uniperiph_dai * dai_data = & priv - > dai_data ;
/* DMA settings*/
2016-09-13 10:58:23 +03:00
if ( priv - > dai_data . stream = = SNDRV_PCM_STREAM_PLAYBACK )
2015-07-16 12:36:04 +03:00
snd_soc_dai_init_dma_data ( dai , & dai_data - > dma_data , NULL ) ;
else
snd_soc_dai_init_dma_data ( dai , NULL , & dai_data - > dma_data ) ;
dai_data - > dma_data . addr = dai_data - > uni - > fifo_phys_address ;
dai_data - > dma_data . addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES ;
return sti_uniperiph_dai_create_ctrl ( dai ) ;
}
2023-08-09 01:56:11 +03:00
static const struct snd_soc_dai_ops sti_uniperiph_dai_ops = {
2015-07-16 12:36:04 +03:00
. probe = sti_uniperiph_dai_probe ,
} ;
2023-08-09 01:56:11 +03:00
static const struct snd_soc_dai_driver sti_uniperiph_dai_template = {
. ops = & sti_uniperiph_dai_ops ,
} ;
2015-07-16 12:36:04 +03:00
static const struct snd_soc_component_driver sti_uniperiph_dai_component = {
. name = " sti_cpu_dai " ,
2020-01-20 04:04:18 +03:00
. suspend = sti_uniperiph_suspend ,
2022-06-23 15:51:31 +03:00
. resume = sti_uniperiph_resume ,
. legacy_dai_naming = 1 ,
2015-07-16 12:36:04 +03:00
} ;
static int sti_uniperiph_cpu_dai_of ( struct device_node * node ,
struct sti_uniperiph_data * priv )
{
struct device * dev = & priv - > pdev - > dev ;
struct sti_uniperiph_dai * dai_data = & priv - > dai_data ;
struct snd_soc_dai_driver * dai = priv - > dai ;
struct snd_soc_pcm_stream * stream ;
struct uniperif * uni ;
2016-09-13 10:58:23 +03:00
const struct of_device_id * of_id ;
const struct sti_uniperiph_dev_data * dev_data ;
const char * mode ;
2016-10-24 17:42:51 +03:00
int ret ;
2016-09-13 10:58:23 +03:00
/* Populate data structure depending on compatibility */
of_id = of_match_node ( snd_soc_sti_match , node ) ;
if ( ! of_id - > data ) {
2016-10-24 17:42:51 +03:00
dev_err ( dev , " data associated to device is missing \n " ) ;
2016-09-13 10:58:23 +03:00
return - EINVAL ;
}
dev_data = ( struct sti_uniperiph_dev_data * ) of_id - > data ;
2015-07-16 12:36:04 +03:00
uni = devm_kzalloc ( dev , sizeof ( * uni ) , GFP_KERNEL ) ;
if ( ! uni )
return - ENOMEM ;
2016-09-13 10:58:23 +03:00
uni - > id = dev_data - > id ;
uni - > ver = dev_data - > version ;
2015-07-16 12:36:04 +03:00
* dai = sti_uniperiph_dai_template ;
2016-09-13 10:58:23 +03:00
dai - > name = dev_data - > dai_names ;
2015-07-16 12:36:04 +03:00
2021-06-17 06:39:03 +03:00
/* Get resources and base address */
uni - > base = devm_platform_get_and_ioremap_resource ( priv - > pdev , 0 , & uni - > mem_region ) ;
2015-07-16 12:36:04 +03:00
if ( IS_ERR ( uni - > base ) )
return PTR_ERR ( uni - > base ) ;
uni - > fifo_phys_address = uni - > mem_region - > start +
UNIPERIF_FIFO_DATA_OFFSET ( uni ) ;
uni - > irq = platform_get_irq ( priv - > pdev , 0 ) ;
2019-07-30 21:15:49 +03:00
if ( uni - > irq < 0 )
2015-07-16 12:36:04 +03:00
return - ENXIO ;
2016-09-13 10:58:23 +03:00
uni - > type = dev_data - > type ;
/* check if player should be configured for tdm */
if ( dev_data - > type & SND_ST_UNIPERIF_TYPE_TDM ) {
if ( ! of_property_read_string ( node , " st,tdm-mode " , & mode ) )
uni - > type = SND_ST_UNIPERIF_TYPE_TDM ;
else
uni - > type = SND_ST_UNIPERIF_TYPE_PCM ;
}
2015-07-16 12:36:04 +03:00
dai_data - > uni = uni ;
2016-09-13 10:58:23 +03:00
dai_data - > stream = dev_data - > stream ;
2015-07-16 12:36:04 +03:00
2016-09-13 10:58:23 +03:00
if ( priv - > dai_data . stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
2016-10-24 17:42:51 +03:00
ret = uni_player_init ( priv - > pdev , uni ) ;
2015-07-16 12:36:04 +03:00
stream = & dai - > playback ;
} else {
2016-10-24 17:42:51 +03:00
ret = uni_reader_init ( priv - > pdev , uni ) ;
2015-07-16 12:36:04 +03:00
stream = & dai - > capture ;
}
2016-10-24 17:42:51 +03:00
if ( ret < 0 )
return ret ;
2015-07-16 12:36:04 +03:00
dai - > ops = uni - > dai_ops ;
stream - > stream_name = dai - > name ;
stream - > channels_min = uni - > hw - > channels_min ;
stream - > channels_max = uni - > hw - > channels_max ;
stream - > rates = uni - > hw - > rates ;
stream - > formats = uni - > hw - > formats ;
return 0 ;
}
static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = {
. prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config ,
} ;
static int sti_uniperiph_probe ( struct platform_device * pdev )
{
struct sti_uniperiph_data * priv ;
struct device_node * node = pdev - > dev . of_node ;
int ret ;
/* Allocate the private data and the CPU_DAI array */
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > dai = devm_kzalloc ( & pdev - > dev , sizeof ( * priv - > dai ) , GFP_KERNEL ) ;
if ( ! priv - > dai )
return - ENOMEM ;
priv - > pdev = pdev ;
ret = sti_uniperiph_cpu_dai_of ( node , priv ) ;
2021-03-27 00:59:18 +03:00
if ( ret < 0 )
return ret ;
2015-07-16 12:36:04 +03:00
dev_set_drvdata ( & pdev - > dev , priv ) ;
2015-08-29 18:06:32 +03:00
ret = devm_snd_soc_register_component ( & pdev - > dev ,
& sti_uniperiph_dai_component ,
priv - > dai , 1 ) ;
2015-07-16 12:36:04 +03:00
if ( ret < 0 )
return ret ;
return devm_snd_dmaengine_pcm_register ( & pdev - > dev ,
& dmaengine_pcm_config , 0 ) ;
}
static struct platform_driver sti_uniperiph_driver = {
. driver = {
. name = " sti-uniperiph-dai " ,
. of_match_table = snd_soc_sti_match ,
} ,
. probe = sti_uniperiph_probe ,
} ;
module_platform_driver ( sti_uniperiph_driver ) ;
MODULE_DESCRIPTION ( " uniperipheral DAI driver " ) ;
MODULE_AUTHOR ( " Arnaud Pouliquen <arnaud.pouliquen@st.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;