2020-02-13 16:51:54 +01:00
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2020 BayLibre, SAS.
// Author: Jerome Brunet <jbrunet@baylibre.com>
# include <linux/bitfield.h>
# include <linux/clk.h>
# include <linux/module.h>
# include <linux/of_platform.h>
# include <linux/regmap.h>
# include <linux/reset.h>
# include <sound/soc.h>
# include <sound/soc-dai.h>
# include <dt-bindings/sound/meson-aiu.h>
# include "aiu.h"
# include "aiu-fifo.h"
# define AIU_I2S_MISC_958_SRC_SHIFT 3
static const char * const aiu_spdif_encode_sel_texts [ ] = {
" SPDIF " , " I2S " ,
} ;
static SOC_ENUM_SINGLE_DECL ( aiu_spdif_encode_sel_enum , AIU_I2S_MISC ,
AIU_I2S_MISC_958_SRC_SHIFT ,
aiu_spdif_encode_sel_texts ) ;
static const struct snd_kcontrol_new aiu_spdif_encode_mux =
SOC_DAPM_ENUM ( " SPDIF Buffer Src " , aiu_spdif_encode_sel_enum ) ;
static const struct snd_soc_dapm_widget aiu_cpu_dapm_widgets [ ] = {
SND_SOC_DAPM_MUX ( " SPDIF SRC SEL " , SND_SOC_NOPM , 0 , 0 ,
& aiu_spdif_encode_mux ) ,
} ;
static const struct snd_soc_dapm_route aiu_cpu_dapm_routes [ ] = {
{ " I2S Encoder Playback " , NULL , " I2S FIFO Playback " } ,
{ " SPDIF SRC SEL " , " SPDIF " , " SPDIF FIFO Playback " } ,
{ " SPDIF SRC SEL " , " I2S " , " I2S FIFO Playback " } ,
{ " SPDIF Encoder Playback " , NULL , " SPDIF SRC SEL " } ,
} ;
int aiu_of_xlate_dai_name ( struct snd_soc_component * component ,
2021-02-21 16:30:24 +01:00
const struct of_phandle_args * args ,
2020-02-13 16:51:54 +01:00
const char * * dai_name ,
unsigned int component_id )
{
struct snd_soc_dai * dai ;
int id ;
if ( args - > args_count ! = 2 )
return - EINVAL ;
if ( args - > args [ 0 ] ! = component_id )
return - EINVAL ;
id = args - > args [ 1 ] ;
if ( id < 0 | | id > = component - > num_dai )
return - EINVAL ;
for_each_component_dais ( component , dai ) {
if ( id = = 0 )
break ;
id - - ;
}
* dai_name = dai - > driver - > name ;
return 0 ;
}
static int aiu_cpu_of_xlate_dai_name ( struct snd_soc_component * component ,
2021-02-21 16:30:24 +01:00
const struct of_phandle_args * args ,
2020-02-13 16:51:54 +01:00
const char * * dai_name )
{
return aiu_of_xlate_dai_name ( component , args , dai_name , AIU_CPU ) ;
}
static int aiu_cpu_component_probe ( struct snd_soc_component * component )
{
struct aiu * aiu = snd_soc_component_get_drvdata ( component ) ;
/* Required for the SPDIF Source control operation */
return clk_prepare_enable ( aiu - > i2s . clks [ PCLK ] . clk ) ;
}
static void aiu_cpu_component_remove ( struct snd_soc_component * component )
{
struct aiu * aiu = snd_soc_component_get_drvdata ( component ) ;
clk_disable_unprepare ( aiu - > i2s . clks [ PCLK ] . clk ) ;
}
static const struct snd_soc_component_driver aiu_cpu_component = {
. name = " AIU CPU " ,
. dapm_widgets = aiu_cpu_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( aiu_cpu_dapm_widgets ) ,
. dapm_routes = aiu_cpu_dapm_routes ,
. num_dapm_routes = ARRAY_SIZE ( aiu_cpu_dapm_routes ) ,
. of_xlate_dai_name = aiu_cpu_of_xlate_dai_name ,
. pointer = aiu_fifo_pointer ,
. probe = aiu_cpu_component_probe ,
. remove = aiu_cpu_component_remove ,
2022-03-09 21:23:06 +01:00
# ifdef CONFIG_DEBUG_FS
. debugfs_prefix = " cpu " ,
# endif
2020-02-13 16:51:54 +01:00
} ;
static struct snd_soc_dai_driver aiu_cpu_dai_drv [ ] = {
[ CPU_I2S_FIFO ] = {
. name = " I2S FIFO " ,
. playback = {
. stream_name = " I2S FIFO Playback " ,
. channels_min = 2 ,
. channels_max = 8 ,
. rates = SNDRV_PCM_RATE_CONTINUOUS ,
. rate_min = 5512 ,
. rate_max = 192000 ,
. formats = AIU_FORMATS ,
} ,
. ops = & aiu_fifo_i2s_dai_ops ,
. pcm_new = aiu_fifo_pcm_new ,
. probe = aiu_fifo_i2s_dai_probe ,
. remove = aiu_fifo_dai_remove ,
} ,
[ CPU_SPDIF_FIFO ] = {
. name = " SPDIF FIFO " ,
. playback = {
. stream_name = " SPDIF FIFO Playback " ,
. channels_min = 2 ,
. channels_max = 2 ,
. rates = SNDRV_PCM_RATE_CONTINUOUS ,
. rate_min = 5512 ,
. rate_max = 192000 ,
. formats = AIU_FORMATS ,
} ,
. ops = & aiu_fifo_spdif_dai_ops ,
. pcm_new = aiu_fifo_pcm_new ,
. probe = aiu_fifo_spdif_dai_probe ,
. remove = aiu_fifo_dai_remove ,
} ,
[ CPU_I2S_ENCODER ] = {
. name = " I2S Encoder " ,
. playback = {
. stream_name = " I2S Encoder Playback " ,
. channels_min = 2 ,
. channels_max = 8 ,
. rates = SNDRV_PCM_RATE_8000_192000 ,
. formats = AIU_FORMATS ,
} ,
. ops = & aiu_encoder_i2s_dai_ops ,
} ,
[ CPU_SPDIF_ENCODER ] = {
. name = " SPDIF Encoder " ,
. playback = {
. stream_name = " SPDIF Encoder Playback " ,
. channels_min = 2 ,
. channels_max = 2 ,
. rates = ( SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_176400 |
SNDRV_PCM_RATE_192000 ) ,
. formats = AIU_FORMATS ,
} ,
. ops = & aiu_encoder_spdif_dai_ops ,
}
} ;
static const struct regmap_config aiu_regmap_cfg = {
. reg_bits = 32 ,
. val_bits = 32 ,
. reg_stride = 4 ,
. max_register = 0x2ac ,
} ;
static int aiu_clk_bulk_get ( struct device * dev ,
const char * const * ids ,
unsigned int num ,
struct aiu_interface * interface )
{
struct clk_bulk_data * clks ;
int i , ret ;
2020-02-14 14:13:47 +01:00
clks = devm_kcalloc ( dev , num , sizeof ( * clks ) , GFP_KERNEL ) ;
2020-02-13 16:51:54 +01:00
if ( ! clks )
return - ENOMEM ;
for ( i = 0 ; i < num ; i + + )
clks [ i ] . id = ids [ i ] ;
ret = devm_clk_bulk_get ( dev , num , clks ) ;
if ( ret < 0 )
return ret ;
interface - > clks = clks ;
interface - > clk_num = num ;
return 0 ;
}
static const char * const aiu_i2s_ids [ ] = {
[ PCLK ] = " i2s_pclk " ,
[ AOCLK ] = " i2s_aoclk " ,
[ MCLK ] = " i2s_mclk " ,
[ MIXER ] = " i2s_mixer " ,
} ;
static const char * const aiu_spdif_ids [ ] = {
[ PCLK ] = " spdif_pclk " ,
[ AOCLK ] = " spdif_aoclk " ,
[ MCLK ] = " spdif_mclk_sel "
} ;
static int aiu_clk_get ( struct device * dev )
{
struct aiu * aiu = dev_get_drvdata ( dev ) ;
int ret ;
aiu - > pclk = devm_clk_get ( dev , " pclk " ) ;
2021-12-14 11:08:37 +09:00
if ( IS_ERR ( aiu - > pclk ) )
return dev_err_probe ( dev , PTR_ERR ( aiu - > pclk ) , " Can't get the aiu pclk \n " ) ;
2020-02-13 16:51:54 +01:00
aiu - > spdif_mclk = devm_clk_get ( dev , " spdif_mclk " ) ;
2021-12-14 11:08:37 +09:00
if ( IS_ERR ( aiu - > spdif_mclk ) )
return dev_err_probe ( dev , PTR_ERR ( aiu - > spdif_mclk ) ,
" Can't get the aiu spdif master clock \n " ) ;
2020-02-13 16:51:54 +01:00
ret = aiu_clk_bulk_get ( dev , aiu_i2s_ids , ARRAY_SIZE ( aiu_i2s_ids ) ,
& aiu - > i2s ) ;
2021-12-14 11:08:37 +09:00
if ( ret )
return dev_err_probe ( dev , ret , " Can't get the i2s clocks \n " ) ;
2020-02-13 16:51:54 +01:00
ret = aiu_clk_bulk_get ( dev , aiu_spdif_ids , ARRAY_SIZE ( aiu_spdif_ids ) ,
& aiu - > spdif ) ;
2021-12-14 11:08:37 +09:00
if ( ret )
return dev_err_probe ( dev , ret , " Can't get the spdif clocks \n " ) ;
2020-02-13 16:51:54 +01:00
ret = clk_prepare_enable ( aiu - > pclk ) ;
if ( ret ) {
dev_err ( dev , " peripheral clock enable failed \n " ) ;
return ret ;
}
ret = devm_add_action_or_reset ( dev ,
( void ( * ) ( void * ) ) clk_disable_unprepare ,
aiu - > pclk ) ;
if ( ret )
dev_err ( dev , " failed to add reset action on pclk " ) ;
return ret ;
}
static int aiu_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
void __iomem * regs ;
struct regmap * map ;
struct aiu * aiu ;
int ret ;
aiu = devm_kzalloc ( dev , sizeof ( * aiu ) , GFP_KERNEL ) ;
if ( ! aiu )
return - ENOMEM ;
2020-02-20 21:57:10 +01:00
aiu - > platform = device_get_match_data ( dev ) ;
if ( ! aiu - > platform )
return - ENODEV ;
2020-02-13 16:51:54 +01:00
platform_set_drvdata ( pdev , aiu ) ;
ret = device_reset ( dev ) ;
2021-12-14 11:08:37 +09:00
if ( ret )
return dev_err_probe ( dev , ret , " Failed to reset device \n " ) ;
2020-02-13 16:51:54 +01:00
regs = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( regs ) )
return PTR_ERR ( regs ) ;
map = devm_regmap_init_mmio ( dev , regs , & aiu_regmap_cfg ) ;
if ( IS_ERR ( map ) ) {
dev_err ( dev , " failed to init regmap: %ld \n " ,
PTR_ERR ( map ) ) ;
return PTR_ERR ( map ) ;
}
aiu - > i2s . irq = platform_get_irq_byname ( pdev , " i2s " ) ;
2020-02-14 14:13:48 +01:00
if ( aiu - > i2s . irq < 0 )
2020-02-13 16:51:54 +01:00
return aiu - > i2s . irq ;
aiu - > spdif . irq = platform_get_irq_byname ( pdev , " spdif " ) ;
2020-02-14 14:13:48 +01:00
if ( aiu - > spdif . irq < 0 )
2020-02-13 16:51:54 +01:00
return aiu - > spdif . irq ;
ret = aiu_clk_get ( dev ) ;
if ( ret )
return ret ;
/* Register the cpu component of the aiu */
ret = snd_soc_register_component ( dev , & aiu_cpu_component ,
aiu_cpu_dai_drv ,
ARRAY_SIZE ( aiu_cpu_dai_drv ) ) ;
2020-02-13 16:51:55 +01:00
if ( ret ) {
2020-02-13 16:51:54 +01:00
dev_err ( dev , " Failed to register cpu component \n " ) ;
2020-02-13 16:51:55 +01:00
return ret ;
}
2020-02-13 16:51:54 +01:00
2020-02-13 16:51:55 +01:00
/* Register the hdmi codec control component */
ret = aiu_hdmi_ctrl_register_component ( dev ) ;
if ( ret ) {
dev_err ( dev , " Failed to register hdmi control component \n " ) ;
goto err ;
}
2020-02-13 16:51:56 +01:00
/* Register the internal dac control component on gxl */
2020-02-20 21:57:10 +01:00
if ( aiu - > platform - > has_acodec ) {
2020-02-13 16:51:56 +01:00
ret = aiu_acodec_ctrl_register_component ( dev ) ;
if ( ret ) {
dev_err ( dev ,
" Failed to register acodec control component \n " ) ;
goto err ;
}
}
2020-02-13 16:51:55 +01:00
return 0 ;
err :
snd_soc_unregister_component ( dev ) ;
2020-02-13 16:51:54 +01:00
return ret ;
}
static int aiu_remove ( struct platform_device * pdev )
{
snd_soc_unregister_component ( & pdev - > dev ) ;
return 0 ;
}
2020-02-20 21:57:10 +01:00
static const struct aiu_platform_data aiu_gxbb_pdata = {
. has_acodec = false ,
2020-02-20 21:57:11 +01:00
. has_clk_ctrl_more_i2s_div = true ,
2020-02-20 21:57:10 +01:00
} ;
static const struct aiu_platform_data aiu_gxl_pdata = {
. has_acodec = true ,
2020-02-20 21:57:11 +01:00
. has_clk_ctrl_more_i2s_div = true ,
} ;
static const struct aiu_platform_data aiu_meson8_pdata = {
. has_acodec = false ,
. has_clk_ctrl_more_i2s_div = false ,
2020-02-20 21:57:10 +01:00
} ;
2020-02-13 16:51:54 +01:00
static const struct of_device_id aiu_of_match [ ] = {
2020-02-20 21:57:10 +01:00
{ . compatible = " amlogic,aiu-gxbb " , . data = & aiu_gxbb_pdata } ,
{ . compatible = " amlogic,aiu-gxl " , . data = & aiu_gxl_pdata } ,
2020-02-20 21:57:11 +01:00
{ . compatible = " amlogic,aiu-meson8 " , . data = & aiu_meson8_pdata } ,
{ . compatible = " amlogic,aiu-meson8b " , . data = & aiu_meson8_pdata } ,
2020-02-13 16:51:54 +01:00
{ }
} ;
MODULE_DEVICE_TABLE ( of , aiu_of_match ) ;
static struct platform_driver aiu_pdrv = {
. probe = aiu_probe ,
. remove = aiu_remove ,
. driver = {
. name = " meson-aiu " ,
. of_match_table = aiu_of_match ,
} ,
} ;
module_platform_driver ( aiu_pdrv ) ;
MODULE_DESCRIPTION ( " Meson AIU Driver " ) ;
MODULE_AUTHOR ( " Jerome Brunet <jbrunet@baylibre.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;