2019-05-29 16:57:50 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2011-11-28 15:45:40 +02:00
/*
* omap - dmic . c - - OMAP ASoC DMIC DAI driver
*
* Copyright ( C ) 2010 - 2011 Texas Instruments
*
* Author : David Lambert < dlambert @ ti . com >
* Misael Lopez Cruz < misael . lopez @ ti . com >
* Liam Girdwood < lrg @ ti . com >
* Peter Ujfalusi < peter . ujfalusi @ ti . com >
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/err.h>
# include <linux/clk.h>
# include <linux/io.h>
# include <linux/slab.h>
# include <linux/pm_runtime.h>
2011-11-18 16:39:26 +02:00
# include <linux/of_device.h>
2011-11-28 15:45:40 +02:00
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/initval.h>
# include <sound/soc.h>
2013-04-03 11:06:05 +02:00
# include <sound/dmaengine_pcm.h>
2011-11-28 15:45:40 +02:00
# include "omap-dmic.h"
2018-05-07 11:49:57 +03:00
# include "sdma-pcm.h"
2011-11-28 15:45:40 +02:00
struct omap_dmic {
struct device * dev ;
void __iomem * io_base ;
struct clk * fclk ;
2018-11-14 13:06:23 +02:00
struct pm_qos_request pm_qos_req ;
int latency ;
2011-11-28 15:45:40 +02:00
int fclk_freq ;
int out_freq ;
int clk_div ;
int sysclk ;
int threshold ;
u32 ch_enabled ;
bool active ;
struct mutex mutex ;
2013-04-03 11:06:05 +02:00
struct snd_dmaengine_dai_dma_data dma_data ;
2011-11-28 15:45:40 +02:00
} ;
static inline void omap_dmic_write ( struct omap_dmic * dmic , u16 reg , u32 val )
{
2013-11-16 02:01:19 +02:00
writel_relaxed ( val , dmic - > io_base + reg ) ;
2011-11-28 15:45:40 +02:00
}
static inline int omap_dmic_read ( struct omap_dmic * dmic , u16 reg )
{
2013-11-16 02:01:19 +02:00
return readl_relaxed ( dmic - > io_base + reg ) ;
2011-11-28 15:45:40 +02:00
}
static inline void omap_dmic_start ( struct omap_dmic * dmic )
{
u32 ctrl = omap_dmic_read ( dmic , OMAP_DMIC_CTRL_REG ) ;
/* Configure DMA controller */
omap_dmic_write ( dmic , OMAP_DMIC_DMAENABLE_SET_REG ,
OMAP_DMIC_DMA_ENABLE ) ;
omap_dmic_write ( dmic , OMAP_DMIC_CTRL_REG , ctrl | dmic - > ch_enabled ) ;
}
static inline void omap_dmic_stop ( struct omap_dmic * dmic )
{
u32 ctrl = omap_dmic_read ( dmic , OMAP_DMIC_CTRL_REG ) ;
omap_dmic_write ( dmic , OMAP_DMIC_CTRL_REG ,
ctrl & ~ OMAP_DMIC_UP_ENABLE_MASK ) ;
/* Disable DMA request generation */
omap_dmic_write ( dmic , OMAP_DMIC_DMAENABLE_CLR_REG ,
OMAP_DMIC_DMA_ENABLE ) ;
}
static inline int dmic_is_enabled ( struct omap_dmic * dmic )
{
return omap_dmic_read ( dmic , OMAP_DMIC_CTRL_REG ) &
OMAP_DMIC_UP_ENABLE_MASK ;
}
static int omap_dmic_dai_startup ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
struct omap_dmic * dmic = snd_soc_dai_get_drvdata ( dai ) ;
int ret = 0 ;
mutex_lock ( & dmic - > mutex ) ;
2020-05-15 09:47:48 +09:00
if ( ! snd_soc_dai_active ( dai ) )
2011-11-28 15:45:40 +02:00
dmic - > active = 1 ;
2012-01-18 12:18:23 +01:00
else
2011-11-28 15:45:40 +02:00
ret = - EBUSY ;
mutex_unlock ( & dmic - > mutex ) ;
return ret ;
}
static void omap_dmic_dai_shutdown ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
struct omap_dmic * dmic = snd_soc_dai_get_drvdata ( dai ) ;
mutex_lock ( & dmic - > mutex ) ;
2020-02-12 00:34:15 +01:00
cpu_latency_qos_remove_request ( & dmic - > pm_qos_req ) ;
2018-11-14 13:06:23 +02:00
2020-05-15 09:47:48 +09:00
if ( ! snd_soc_dai_active ( dai ) )
2011-11-28 15:45:40 +02:00
dmic - > active = 0 ;
mutex_unlock ( & dmic - > mutex ) ;
}
static int omap_dmic_select_divider ( struct omap_dmic * dmic , int sample_rate )
{
int divider = - EINVAL ;
/*
* 192 KHz rate is only supported with 19.2 MHz / 3.84 MHz clock
* configuration .
*/
if ( sample_rate = = 192000 ) {
if ( dmic - > fclk_freq = = 19200000 & & dmic - > out_freq = = 3840000 )
divider = 0x6 ; /* Divider: 5 (192KHz sampling rate) */
else
dev_err ( dmic - > dev ,
" invalid clock configuration for 192KHz \n " ) ;
return divider ;
}
switch ( dmic - > out_freq ) {
case 1536000 :
if ( dmic - > fclk_freq ! = 24576000 )
goto div_err ;
divider = 0x4 ; /* Divider: 16 */
break ;
case 2400000 :
switch ( dmic - > fclk_freq ) {
case 12000000 :
divider = 0x5 ; /* Divider: 5 */
break ;
case 19200000 :
divider = 0x0 ; /* Divider: 8 */
break ;
case 24000000 :
divider = 0x2 ; /* Divider: 10 */
break ;
default :
goto div_err ;
}
break ;
case 3072000 :
if ( dmic - > fclk_freq ! = 24576000 )
goto div_err ;
divider = 0x3 ; /* Divider: 8 */
break ;
case 3840000 :
if ( dmic - > fclk_freq ! = 19200000 )
goto div_err ;
divider = 0x1 ; /* Divider: 5 (96KHz sampling rate) */
break ;
default :
dev_err ( dmic - > dev , " invalid out frequency: %dHz \n " ,
dmic - > out_freq ) ;
break ;
}
return divider ;
div_err :
dev_err ( dmic - > dev , " invalid out frequency %dHz for %dHz input \n " ,
dmic - > out_freq , dmic - > fclk_freq ) ;
return - EINVAL ;
}
static int omap_dmic_dai_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
struct omap_dmic * dmic = snd_soc_dai_get_drvdata ( dai ) ;
2013-04-03 11:06:05 +02:00
struct snd_dmaengine_dai_dma_data * dma_data ;
2011-11-28 15:45:40 +02:00
int channels ;
dmic - > clk_div = omap_dmic_select_divider ( dmic , params_rate ( params ) ) ;
if ( dmic - > clk_div < 0 ) {
dev_err ( dmic - > dev , " no valid divider for %dHz from %dHz \n " ,
dmic - > out_freq , dmic - > fclk_freq ) ;
return - EINVAL ;
}
dmic - > ch_enabled = 0 ;
channels = params_channels ( params ) ;
switch ( channels ) {
case 6 :
dmic - > ch_enabled | = OMAP_DMIC_UP3_ENABLE ;
2020-08-23 17:36:59 -05:00
fallthrough ;
2011-11-28 15:45:40 +02:00
case 4 :
dmic - > ch_enabled | = OMAP_DMIC_UP2_ENABLE ;
2020-08-23 17:36:59 -05:00
fallthrough ;
2011-11-28 15:45:40 +02:00
case 2 :
dmic - > ch_enabled | = OMAP_DMIC_UP1_ENABLE ;
break ;
default :
dev_err ( dmic - > dev , " invalid number of legacy channels \n " ) ;
return - EINVAL ;
}
/* packet size is threshold * channels */
2012-09-14 15:05:57 +03:00
dma_data = snd_soc_dai_get_dma_data ( dai , substream ) ;
2013-04-03 11:06:05 +02:00
dma_data - > maxburst = dmic - > threshold * channels ;
2018-11-14 13:06:23 +02:00
dmic - > latency = ( OMAP_DMIC_THRES_MAX - dmic - > threshold ) * USEC_PER_SEC /
params_rate ( params ) ;
2011-11-28 15:45:40 +02:00
return 0 ;
}
static int omap_dmic_dai_prepare ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
struct omap_dmic * dmic = snd_soc_dai_get_drvdata ( dai ) ;
u32 ctrl ;
2020-02-12 00:34:15 +01:00
if ( cpu_latency_qos_request_active ( & dmic - > pm_qos_req ) )
cpu_latency_qos_update_request ( & dmic - > pm_qos_req ,
dmic - > latency ) ;
2018-11-14 13:06:23 +02:00
2011-11-28 15:45:40 +02:00
/* Configure uplink threshold */
omap_dmic_write ( dmic , OMAP_DMIC_FIFO_CTRL_REG , dmic - > threshold ) ;
ctrl = omap_dmic_read ( dmic , OMAP_DMIC_CTRL_REG ) ;
/* Set dmic out format */
ctrl & = ~ ( OMAP_DMIC_FORMAT | OMAP_DMIC_POLAR_MASK ) ;
ctrl | = ( OMAP_DMICOUTFORMAT_LJUST | OMAP_DMIC_POLAR1 |
OMAP_DMIC_POLAR2 | OMAP_DMIC_POLAR3 ) ;
/* Configure dmic clock divider */
ctrl & = ~ OMAP_DMIC_CLK_DIV_MASK ;
ctrl | = OMAP_DMIC_CLK_DIV ( dmic - > clk_div ) ;
omap_dmic_write ( dmic , OMAP_DMIC_CTRL_REG , ctrl ) ;
omap_dmic_write ( dmic , OMAP_DMIC_CTRL_REG ,
ctrl | OMAP_DMICOUTFORMAT_LJUST | OMAP_DMIC_POLAR1 |
OMAP_DMIC_POLAR2 | OMAP_DMIC_POLAR3 ) ;
return 0 ;
}
static int omap_dmic_dai_trigger ( struct snd_pcm_substream * substream ,
int cmd , struct snd_soc_dai * dai )
{
struct omap_dmic * dmic = snd_soc_dai_get_drvdata ( dai ) ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
omap_dmic_start ( dmic ) ;
break ;
case SNDRV_PCM_TRIGGER_STOP :
omap_dmic_stop ( dmic ) ;
break ;
default :
break ;
}
return 0 ;
}
static int omap_dmic_select_fclk ( struct omap_dmic * dmic , int clk_id ,
unsigned int freq )
{
2018-04-12 11:23:15 +03:00
struct clk * parent_clk , * mux ;
2011-11-28 15:45:40 +02:00
char * parent_clk_name ;
int ret = 0 ;
switch ( freq ) {
case 12000000 :
case 19200000 :
case 24000000 :
case 24576000 :
break ;
default :
dev_err ( dmic - > dev , " invalid input frequency: %dHz \n " , freq ) ;
dmic - > fclk_freq = 0 ;
return - EINVAL ;
}
if ( dmic - > sysclk = = clk_id ) {
dmic - > fclk_freq = freq ;
return 0 ;
}
/* re-parent not allowed if a stream is ongoing */
if ( dmic - > active & & dmic_is_enabled ( dmic ) ) {
dev_err ( dmic - > dev , " can't re-parent when DMIC active \n " ) ;
return - EBUSY ;
}
switch ( clk_id ) {
case OMAP_DMIC_SYSCLK_PAD_CLKS :
parent_clk_name = " pad_clks_ck " ;
break ;
case OMAP_DMIC_SYSCLK_SLIMBLUS_CLKS :
parent_clk_name = " slimbus_clk " ;
break ;
case OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS :
parent_clk_name = " dmic_sync_mux_ck " ;
break ;
default :
dev_err ( dmic - > dev , " fclk clk_id (%d) not supported \n " , clk_id ) ;
return - EINVAL ;
}
parent_clk = clk_get ( dmic - > dev , parent_clk_name ) ;
if ( IS_ERR ( parent_clk ) ) {
dev_err ( dmic - > dev , " can't get %s \n " , parent_clk_name ) ;
return - ENODEV ;
}
2018-04-12 11:23:15 +03:00
mux = clk_get_parent ( dmic - > fclk ) ;
if ( IS_ERR ( mux ) ) {
dev_err ( dmic - > dev , " can't get fck mux parent \n " ) ;
clk_put ( parent_clk ) ;
return - ENODEV ;
}
2011-11-28 15:45:40 +02:00
mutex_lock ( & dmic - > mutex ) ;
if ( dmic - > active ) {
/* disable clock while reparenting */
pm_runtime_put_sync ( dmic - > dev ) ;
2018-04-12 11:23:15 +03:00
ret = clk_set_parent ( mux , parent_clk ) ;
2011-11-28 15:45:40 +02:00
pm_runtime_get_sync ( dmic - > dev ) ;
} else {
2018-04-12 11:23:15 +03:00
ret = clk_set_parent ( mux , parent_clk ) ;
2011-11-28 15:45:40 +02:00
}
mutex_unlock ( & dmic - > mutex ) ;
if ( ret < 0 ) {
dev_err ( dmic - > dev , " re-parent failed \n " ) ;
goto err_busy ;
}
dmic - > sysclk = clk_id ;
dmic - > fclk_freq = freq ;
err_busy :
2018-04-12 11:23:15 +03:00
clk_put ( mux ) ;
2011-11-28 15:45:40 +02:00
clk_put ( parent_clk ) ;
return ret ;
}
static int omap_dmic_select_outclk ( struct omap_dmic * dmic , int clk_id ,
unsigned int freq )
{
int ret = 0 ;
if ( clk_id ! = OMAP_DMIC_ABE_DMIC_CLK ) {
dev_err ( dmic - > dev , " output clk_id (%d) not supported \n " ,
clk_id ) ;
return - EINVAL ;
}
switch ( freq ) {
case 1536000 :
case 2400000 :
case 3072000 :
case 3840000 :
dmic - > out_freq = freq ;
break ;
default :
dev_err ( dmic - > dev , " invalid out frequency: %dHz \n " , freq ) ;
dmic - > out_freq = 0 ;
ret = - EINVAL ;
}
return ret ;
}
static int omap_dmic_set_dai_sysclk ( struct snd_soc_dai * dai , int clk_id ,
unsigned int freq , int dir )
{
struct omap_dmic * dmic = snd_soc_dai_get_drvdata ( dai ) ;
if ( dir = = SND_SOC_CLOCK_IN )
return omap_dmic_select_fclk ( dmic , clk_id , freq ) ;
else if ( dir = = SND_SOC_CLOCK_OUT )
return omap_dmic_select_outclk ( dmic , clk_id , freq ) ;
dev_err ( dmic - > dev , " invalid clock direction (%d) \n " , dir ) ;
return - EINVAL ;
}
static int omap_dmic_probe ( struct snd_soc_dai * dai )
{
struct omap_dmic * dmic = snd_soc_dai_get_drvdata ( dai ) ;
pm_runtime_enable ( dmic - > dev ) ;
/* Disable lines while request is ongoing */
pm_runtime_get_sync ( dmic - > dev ) ;
omap_dmic_write ( dmic , OMAP_DMIC_CTRL_REG , 0x00 ) ;
pm_runtime_put_sync ( dmic - > dev ) ;
/* Configure DMIC threshold value */
dmic - > threshold = OMAP_DMIC_THRES_MAX - 3 ;
2014-04-16 15:46:17 +03:00
snd_soc_dai_init_dma_data ( dai , NULL , & dmic - > dma_data ) ;
2011-11-28 15:45:40 +02:00
return 0 ;
}
static int omap_dmic_remove ( struct snd_soc_dai * dai )
{
struct omap_dmic * dmic = snd_soc_dai_get_drvdata ( dai ) ;
pm_runtime_disable ( dmic - > dev ) ;
return 0 ;
}
2023-08-08 22:54:59 +00:00
static const struct snd_soc_dai_ops omap_dmic_dai_ops = {
. probe = omap_dmic_probe ,
. remove = omap_dmic_remove ,
. startup = omap_dmic_dai_startup ,
. shutdown = omap_dmic_dai_shutdown ,
. hw_params = omap_dmic_dai_hw_params ,
. prepare = omap_dmic_dai_prepare ,
. trigger = omap_dmic_dai_trigger ,
. set_sysclk = omap_dmic_set_dai_sysclk ,
} ;
2011-11-28 15:45:40 +02:00
static struct snd_soc_dai_driver omap_dmic_dai = {
. name = " omap-dmic " ,
. capture = {
. channels_min = 2 ,
. channels_max = 6 ,
. rates = SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 ,
. formats = SNDRV_PCM_FMTBIT_S32_LE ,
2012-01-18 12:18:23 +01:00
. sig_bits = 24 ,
2011-11-28 15:45:40 +02:00
} ,
. ops = & omap_dmic_dai_ops ,
} ;
2013-03-21 03:33:51 -07:00
static const struct snd_soc_component_driver omap_dmic_component = {
2022-06-23 13:51:45 +01:00
. name = " omap-dmic " ,
. legacy_dai_naming = 1 ,
2013-03-21 03:33:51 -07:00
} ;
2012-12-07 09:26:29 -05:00
static int asoc_dmic_probe ( struct platform_device * pdev )
2011-11-28 15:45:40 +02:00
{
struct omap_dmic * dmic ;
struct resource * res ;
int ret ;
dmic = devm_kzalloc ( & pdev - > dev , sizeof ( struct omap_dmic ) , GFP_KERNEL ) ;
if ( ! dmic )
return - ENOMEM ;
platform_set_drvdata ( pdev , dmic ) ;
dmic - > dev = & pdev - > dev ;
dmic - > sysclk = OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS ;
mutex_init ( & dmic - > mutex ) ;
2014-07-11 09:44:06 +05:30
dmic - > fclk = devm_clk_get ( dmic - > dev , " fck " ) ;
2011-11-28 15:45:40 +02:00
if ( IS_ERR ( dmic - > fclk ) ) {
2022-03-15 23:08:16 +00:00
dev_err ( dmic - > dev , " can't get fck \n " ) ;
2011-11-28 15:45:40 +02:00
return - ENODEV ;
}
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " dma " ) ;
if ( ! res ) {
dev_err ( dmic - > dev , " invalid dma memory resource \n " ) ;
2014-07-11 09:44:06 +05:30
return - ENODEV ;
2011-11-28 15:45:40 +02:00
}
2013-04-03 11:06:05 +02:00
dmic - > dma_data . addr = res - > start + OMAP_DMIC_DATA_REG ;
2011-11-28 15:45:40 +02:00
2013-07-11 14:35:45 +02:00
dmic - > dma_data . filter_data = " up_link " ;
2011-11-28 15:45:40 +02:00
2023-07-11 11:48:42 +08:00
dmic - > io_base = devm_platform_ioremap_resource_byname ( pdev , " mpu " ) ;
2014-07-11 09:44:06 +05:30
if ( IS_ERR ( dmic - > io_base ) )
return PTR_ERR ( dmic - > io_base ) ;
2011-11-28 15:45:40 +02:00
2014-07-08 22:23:17 +05:30
ret = devm_snd_soc_register_component ( & pdev - > dev ,
& omap_dmic_component ,
& omap_dmic_dai , 1 ) ;
2011-11-28 15:45:40 +02:00
if ( ret )
2014-07-11 09:44:06 +05:30
return ret ;
2011-11-28 15:45:40 +02:00
2018-05-07 11:49:57 +03:00
ret = sdma_pcm_platform_register ( & pdev - > dev , NULL , " up_link " ) ;
2014-04-16 15:46:18 +03:00
if ( ret )
2014-07-11 09:44:06 +05:30
return ret ;
2011-11-28 15:45:40 +02:00
return 0 ;
}
2011-11-18 16:39:26 +02:00
static const struct of_device_id omap_dmic_of_match [ ] = {
{ . compatible = " ti,omap4-dmic " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , omap_dmic_of_match ) ;
2011-11-28 15:45:40 +02:00
static struct platform_driver asoc_dmic_driver = {
. driver = {
. name = " omap-dmic " ,
2011-11-18 16:39:26 +02:00
. of_match_table = omap_dmic_of_match ,
2011-11-28 15:45:40 +02:00
} ,
. probe = asoc_dmic_probe ,
} ;
module_platform_driver ( asoc_dmic_driver ) ;
MODULE_ALIAS ( " platform:omap-dmic " ) ;
MODULE_AUTHOR ( " Peter Ujfalusi <peter.ujfalusi@ti.com> " ) ;
MODULE_DESCRIPTION ( " OMAP DMIC ASoC Interface " ) ;
MODULE_LICENSE ( " GPL " ) ;