162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* Atmel ALSA SoC Audio Class D Amplifier (CLASSD) driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2015 Atmel 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Author: Songjun Wu <songjun.wu@atmel.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/of.h> 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/regmap.h> 1462306a36Sopenharmony_ci#include <sound/core.h> 1562306a36Sopenharmony_ci#include <sound/dmaengine_pcm.h> 1662306a36Sopenharmony_ci#include <sound/pcm_params.h> 1762306a36Sopenharmony_ci#include <sound/tlv.h> 1862306a36Sopenharmony_ci#include "atmel-classd.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct atmel_classd_pdata { 2162306a36Sopenharmony_ci bool non_overlap_enable; 2262306a36Sopenharmony_ci int non_overlap_time; 2362306a36Sopenharmony_ci int pwm_type; 2462306a36Sopenharmony_ci const char *card_name; 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct atmel_classd { 2862306a36Sopenharmony_ci dma_addr_t phy_base; 2962306a36Sopenharmony_ci struct regmap *regmap; 3062306a36Sopenharmony_ci struct clk *pclk; 3162306a36Sopenharmony_ci struct clk *gclk; 3262306a36Sopenharmony_ci struct device *dev; 3362306a36Sopenharmony_ci int irq; 3462306a36Sopenharmony_ci const struct atmel_classd_pdata *pdata; 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#ifdef CONFIG_OF 3862306a36Sopenharmony_cistatic const struct of_device_id atmel_classd_of_match[] = { 3962306a36Sopenharmony_ci { 4062306a36Sopenharmony_ci .compatible = "atmel,sama5d2-classd", 4162306a36Sopenharmony_ci }, { 4262306a36Sopenharmony_ci /* sentinel */ 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, atmel_classd_of_match); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic struct atmel_classd_pdata *atmel_classd_dt_init(struct device *dev) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct device_node *np = dev->of_node; 5062306a36Sopenharmony_ci struct atmel_classd_pdata *pdata; 5162306a36Sopenharmony_ci const char *pwm_type_s; 5262306a36Sopenharmony_ci int ret; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (!np) { 5562306a36Sopenharmony_ci dev_err(dev, "device node not found\n"); 5662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 6062306a36Sopenharmony_ci if (!pdata) 6162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci ret = of_property_read_string(np, "atmel,pwm-type", &pwm_type_s); 6462306a36Sopenharmony_ci if ((ret == 0) && (strcmp(pwm_type_s, "diff") == 0)) 6562306a36Sopenharmony_ci pdata->pwm_type = CLASSD_MR_PWMTYP_DIFF; 6662306a36Sopenharmony_ci else 6762306a36Sopenharmony_ci pdata->pwm_type = CLASSD_MR_PWMTYP_SINGLE; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci ret = of_property_read_u32(np, 7062306a36Sopenharmony_ci "atmel,non-overlap-time", &pdata->non_overlap_time); 7162306a36Sopenharmony_ci if (ret) 7262306a36Sopenharmony_ci pdata->non_overlap_enable = false; 7362306a36Sopenharmony_ci else 7462306a36Sopenharmony_ci pdata->non_overlap_enable = true; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci ret = of_property_read_string(np, "atmel,model", &pdata->card_name); 7762306a36Sopenharmony_ci if (ret) 7862306a36Sopenharmony_ci pdata->card_name = "CLASSD"; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return pdata; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci#else 8362306a36Sopenharmony_cistatic inline struct atmel_classd_pdata * 8462306a36Sopenharmony_ciatmel_classd_dt_init(struct device *dev) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci#endif 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#define ATMEL_CLASSD_RATES (SNDRV_PCM_RATE_8000 \ 9162306a36Sopenharmony_ci | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 \ 9262306a36Sopenharmony_ci | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 \ 9362306a36Sopenharmony_ci | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 \ 9462306a36Sopenharmony_ci | SNDRV_PCM_RATE_96000) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic const struct snd_pcm_hardware atmel_classd_hw = { 9762306a36Sopenharmony_ci .info = SNDRV_PCM_INFO_MMAP 9862306a36Sopenharmony_ci | SNDRV_PCM_INFO_MMAP_VALID 9962306a36Sopenharmony_ci | SNDRV_PCM_INFO_INTERLEAVED 10062306a36Sopenharmony_ci | SNDRV_PCM_INFO_RESUME 10162306a36Sopenharmony_ci | SNDRV_PCM_INFO_PAUSE, 10262306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S16_LE), 10362306a36Sopenharmony_ci .rates = ATMEL_CLASSD_RATES, 10462306a36Sopenharmony_ci .rate_min = 8000, 10562306a36Sopenharmony_ci .rate_max = 96000, 10662306a36Sopenharmony_ci .channels_min = 1, 10762306a36Sopenharmony_ci .channels_max = 2, 10862306a36Sopenharmony_ci .buffer_bytes_max = 64 * 1024, 10962306a36Sopenharmony_ci .period_bytes_min = 256, 11062306a36Sopenharmony_ci .period_bytes_max = 32 * 1024, 11162306a36Sopenharmony_ci .periods_min = 2, 11262306a36Sopenharmony_ci .periods_max = 256, 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci#define ATMEL_CLASSD_PREALLOC_BUF_SIZE (64 * 1024) 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* cpu dai component */ 11862306a36Sopenharmony_cistatic int atmel_classd_cpu_dai_startup(struct snd_pcm_substream *substream, 11962306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 12262306a36Sopenharmony_ci struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); 12362306a36Sopenharmony_ci int err; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci regmap_write(dd->regmap, CLASSD_THR, 0x0); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci err = clk_prepare_enable(dd->pclk); 12862306a36Sopenharmony_ci if (err) 12962306a36Sopenharmony_ci return err; 13062306a36Sopenharmony_ci err = clk_prepare_enable(dd->gclk); 13162306a36Sopenharmony_ci if (err) { 13262306a36Sopenharmony_ci clk_disable_unprepare(dd->pclk); 13362306a36Sopenharmony_ci return err; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/* platform */ 13962306a36Sopenharmony_cistatic int 14062306a36Sopenharmony_ciatmel_classd_platform_configure_dma(struct snd_pcm_substream *substream, 14162306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 14262306a36Sopenharmony_ci struct dma_slave_config *slave_config) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 14562306a36Sopenharmony_ci struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (params_physical_width(params) != 16) { 14862306a36Sopenharmony_ci dev_err(dd->dev, 14962306a36Sopenharmony_ci "only supports 16-bit audio data\n"); 15062306a36Sopenharmony_ci return -EINVAL; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (params_channels(params) == 1) 15462306a36Sopenharmony_ci slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 15562306a36Sopenharmony_ci else 15662306a36Sopenharmony_ci slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci slave_config->direction = DMA_MEM_TO_DEV; 15962306a36Sopenharmony_ci slave_config->dst_addr = dd->phy_base + CLASSD_THR; 16062306a36Sopenharmony_ci slave_config->dst_maxburst = 1; 16162306a36Sopenharmony_ci slave_config->src_maxburst = 1; 16262306a36Sopenharmony_ci slave_config->device_fc = false; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic const struct snd_dmaengine_pcm_config 16862306a36Sopenharmony_ciatmel_classd_dmaengine_pcm_config = { 16962306a36Sopenharmony_ci .prepare_slave_config = atmel_classd_platform_configure_dma, 17062306a36Sopenharmony_ci .pcm_hardware = &atmel_classd_hw, 17162306a36Sopenharmony_ci .prealloc_buffer_size = ATMEL_CLASSD_PREALLOC_BUF_SIZE, 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* codec */ 17562306a36Sopenharmony_cistatic const char * const mono_mode_text[] = { 17662306a36Sopenharmony_ci "mix", "sat", "left", "right" 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(classd_mono_mode_enum, 18062306a36Sopenharmony_ci CLASSD_INTPMR, CLASSD_INTPMR_MONO_MODE_SHIFT, 18162306a36Sopenharmony_ci mono_mode_text); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic const char * const eqcfg_text[] = { 18462306a36Sopenharmony_ci "Treble-12dB", "Treble-6dB", 18562306a36Sopenharmony_ci "Medium-8dB", "Medium-3dB", 18662306a36Sopenharmony_ci "Bass-12dB", "Bass-6dB", 18762306a36Sopenharmony_ci "0 dB", 18862306a36Sopenharmony_ci "Bass+6dB", "Bass+12dB", 18962306a36Sopenharmony_ci "Medium+3dB", "Medium+8dB", 19062306a36Sopenharmony_ci "Treble+6dB", "Treble+12dB", 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic const unsigned int eqcfg_value[] = { 19462306a36Sopenharmony_ci CLASSD_INTPMR_EQCFG_T_CUT_12, CLASSD_INTPMR_EQCFG_T_CUT_6, 19562306a36Sopenharmony_ci CLASSD_INTPMR_EQCFG_M_CUT_8, CLASSD_INTPMR_EQCFG_M_CUT_3, 19662306a36Sopenharmony_ci CLASSD_INTPMR_EQCFG_B_CUT_12, CLASSD_INTPMR_EQCFG_B_CUT_6, 19762306a36Sopenharmony_ci CLASSD_INTPMR_EQCFG_FLAT, 19862306a36Sopenharmony_ci CLASSD_INTPMR_EQCFG_B_BOOST_6, CLASSD_INTPMR_EQCFG_B_BOOST_12, 19962306a36Sopenharmony_ci CLASSD_INTPMR_EQCFG_M_BOOST_3, CLASSD_INTPMR_EQCFG_M_BOOST_8, 20062306a36Sopenharmony_ci CLASSD_INTPMR_EQCFG_T_BOOST_6, CLASSD_INTPMR_EQCFG_T_BOOST_12, 20162306a36Sopenharmony_ci}; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic SOC_VALUE_ENUM_SINGLE_DECL(classd_eqcfg_enum, 20462306a36Sopenharmony_ci CLASSD_INTPMR, CLASSD_INTPMR_EQCFG_SHIFT, 0xf, 20562306a36Sopenharmony_ci eqcfg_text, eqcfg_value); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(classd_digital_tlv, -7800, 100, 1); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic const struct snd_kcontrol_new atmel_classd_snd_controls[] = { 21062306a36Sopenharmony_ciSOC_DOUBLE_TLV("Playback Volume", CLASSD_INTPMR, 21162306a36Sopenharmony_ci CLASSD_INTPMR_ATTL_SHIFT, CLASSD_INTPMR_ATTR_SHIFT, 21262306a36Sopenharmony_ci 78, 1, classd_digital_tlv), 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ciSOC_SINGLE("Deemphasis Switch", CLASSD_INTPMR, 21562306a36Sopenharmony_ci CLASSD_INTPMR_DEEMP_SHIFT, 1, 0), 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ciSOC_SINGLE("Mono Switch", CLASSD_INTPMR, CLASSD_INTPMR_MONO_SHIFT, 1, 0), 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ciSOC_SINGLE("Swap Switch", CLASSD_INTPMR, CLASSD_INTPMR_SWAP_SHIFT, 1, 0), 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ciSOC_ENUM("Mono Mode", classd_mono_mode_enum), 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ciSOC_ENUM("EQ", classd_eqcfg_enum), 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic const char * const pwm_type[] = { 22762306a36Sopenharmony_ci "Single ended", "Differential" 22862306a36Sopenharmony_ci}; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic int atmel_classd_component_probe(struct snd_soc_component *component) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci struct snd_soc_card *card = snd_soc_component_get_drvdata(component); 23362306a36Sopenharmony_ci struct atmel_classd *dd = snd_soc_card_get_drvdata(card); 23462306a36Sopenharmony_ci const struct atmel_classd_pdata *pdata = dd->pdata; 23562306a36Sopenharmony_ci u32 mask, val; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci mask = CLASSD_MR_PWMTYP_MASK; 23862306a36Sopenharmony_ci val = pdata->pwm_type << CLASSD_MR_PWMTYP_SHIFT; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci mask |= CLASSD_MR_NON_OVERLAP_MASK; 24162306a36Sopenharmony_ci if (pdata->non_overlap_enable) { 24262306a36Sopenharmony_ci val |= (CLASSD_MR_NON_OVERLAP_EN 24362306a36Sopenharmony_ci << CLASSD_MR_NON_OVERLAP_SHIFT); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci mask |= CLASSD_MR_NOVR_VAL_MASK; 24662306a36Sopenharmony_ci switch (pdata->non_overlap_time) { 24762306a36Sopenharmony_ci case 5: 24862306a36Sopenharmony_ci val |= (CLASSD_MR_NOVR_VAL_5NS 24962306a36Sopenharmony_ci << CLASSD_MR_NOVR_VAL_SHIFT); 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci case 10: 25262306a36Sopenharmony_ci val |= (CLASSD_MR_NOVR_VAL_10NS 25362306a36Sopenharmony_ci << CLASSD_MR_NOVR_VAL_SHIFT); 25462306a36Sopenharmony_ci break; 25562306a36Sopenharmony_ci case 15: 25662306a36Sopenharmony_ci val |= (CLASSD_MR_NOVR_VAL_15NS 25762306a36Sopenharmony_ci << CLASSD_MR_NOVR_VAL_SHIFT); 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci case 20: 26062306a36Sopenharmony_ci val |= (CLASSD_MR_NOVR_VAL_20NS 26162306a36Sopenharmony_ci << CLASSD_MR_NOVR_VAL_SHIFT); 26262306a36Sopenharmony_ci break; 26362306a36Sopenharmony_ci default: 26462306a36Sopenharmony_ci val |= (CLASSD_MR_NOVR_VAL_10NS 26562306a36Sopenharmony_ci << CLASSD_MR_NOVR_VAL_SHIFT); 26662306a36Sopenharmony_ci dev_warn(component->dev, 26762306a36Sopenharmony_ci "non-overlapping value %d is invalid, the default value 10 is specified\n", 26862306a36Sopenharmony_ci pdata->non_overlap_time); 26962306a36Sopenharmony_ci break; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci snd_soc_component_update_bits(component, CLASSD_MR, mask, val); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci dev_info(component->dev, 27662306a36Sopenharmony_ci "PWM modulation type is %s, non-overlapping is %s\n", 27762306a36Sopenharmony_ci pwm_type[pdata->pwm_type], 27862306a36Sopenharmony_ci pdata->non_overlap_enable?"enabled":"disabled"); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci return 0; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic int atmel_classd_component_resume(struct snd_soc_component *component) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct snd_soc_card *card = snd_soc_component_get_drvdata(component); 28662306a36Sopenharmony_ci struct atmel_classd *dd = snd_soc_card_get_drvdata(card); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return regcache_sync(dd->regmap); 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic int atmel_classd_cpu_dai_mute_stream(struct snd_soc_dai *cpu_dai, 29262306a36Sopenharmony_ci int mute, int direction) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct snd_soc_component *component = cpu_dai->component; 29562306a36Sopenharmony_ci u32 mask, val; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci mask = CLASSD_MR_LMUTE_MASK | CLASSD_MR_RMUTE_MASK; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (mute) 30062306a36Sopenharmony_ci val = mask; 30162306a36Sopenharmony_ci else 30262306a36Sopenharmony_ci val = 0; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci snd_soc_component_update_bits(component, CLASSD_MR, mask, val); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return 0; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci#define CLASSD_GCLK_RATE_11M2896_MPY_8 (112896 * 100 * 8) 31062306a36Sopenharmony_ci#define CLASSD_GCLK_RATE_12M288_MPY_8 (12288 * 1000 * 8) 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic struct { 31362306a36Sopenharmony_ci int rate; 31462306a36Sopenharmony_ci int sample_rate; 31562306a36Sopenharmony_ci int dsp_clk; 31662306a36Sopenharmony_ci unsigned long gclk_rate; 31762306a36Sopenharmony_ci} const sample_rates[] = { 31862306a36Sopenharmony_ci { 8000, CLASSD_INTPMR_FRAME_8K, 31962306a36Sopenharmony_ci CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 }, 32062306a36Sopenharmony_ci { 16000, CLASSD_INTPMR_FRAME_16K, 32162306a36Sopenharmony_ci CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 }, 32262306a36Sopenharmony_ci { 32000, CLASSD_INTPMR_FRAME_32K, 32362306a36Sopenharmony_ci CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 }, 32462306a36Sopenharmony_ci { 48000, CLASSD_INTPMR_FRAME_48K, 32562306a36Sopenharmony_ci CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 }, 32662306a36Sopenharmony_ci { 96000, CLASSD_INTPMR_FRAME_96K, 32762306a36Sopenharmony_ci CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 }, 32862306a36Sopenharmony_ci { 22050, CLASSD_INTPMR_FRAME_22K, 32962306a36Sopenharmony_ci CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 }, 33062306a36Sopenharmony_ci { 44100, CLASSD_INTPMR_FRAME_44K, 33162306a36Sopenharmony_ci CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 }, 33262306a36Sopenharmony_ci { 88200, CLASSD_INTPMR_FRAME_88K, 33362306a36Sopenharmony_ci CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 }, 33462306a36Sopenharmony_ci}; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic int 33762306a36Sopenharmony_ciatmel_classd_cpu_dai_hw_params(struct snd_pcm_substream *substream, 33862306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 33962306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 34262306a36Sopenharmony_ci struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); 34362306a36Sopenharmony_ci struct snd_soc_component *component = cpu_dai->component; 34462306a36Sopenharmony_ci int fs; 34562306a36Sopenharmony_ci int i, best, best_val, cur_val, ret; 34662306a36Sopenharmony_ci u32 mask, val; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci fs = params_rate(params); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci best = 0; 35162306a36Sopenharmony_ci best_val = abs(fs - sample_rates[0].rate); 35262306a36Sopenharmony_ci for (i = 1; i < ARRAY_SIZE(sample_rates); i++) { 35362306a36Sopenharmony_ci /* Closest match */ 35462306a36Sopenharmony_ci cur_val = abs(fs - sample_rates[i].rate); 35562306a36Sopenharmony_ci if (cur_val < best_val) { 35662306a36Sopenharmony_ci best = i; 35762306a36Sopenharmony_ci best_val = cur_val; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci dev_dbg(component->dev, 36262306a36Sopenharmony_ci "Selected SAMPLE_RATE of %dHz, GCLK_RATE of %ldHz\n", 36362306a36Sopenharmony_ci sample_rates[best].rate, sample_rates[best].gclk_rate); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci clk_disable_unprepare(dd->gclk); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci ret = clk_set_rate(dd->gclk, sample_rates[best].gclk_rate); 36862306a36Sopenharmony_ci if (ret) 36962306a36Sopenharmony_ci return ret; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci mask = CLASSD_INTPMR_DSP_CLK_FREQ_MASK | CLASSD_INTPMR_FRAME_MASK; 37262306a36Sopenharmony_ci val = (sample_rates[best].dsp_clk << CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT) 37362306a36Sopenharmony_ci | (sample_rates[best].sample_rate << CLASSD_INTPMR_FRAME_SHIFT); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci snd_soc_component_update_bits(component, CLASSD_INTPMR, mask, val); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return clk_prepare_enable(dd->gclk); 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic void 38162306a36Sopenharmony_ciatmel_classd_cpu_dai_shutdown(struct snd_pcm_substream *substream, 38262306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 38562306a36Sopenharmony_ci struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci clk_disable_unprepare(dd->gclk); 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic int atmel_classd_cpu_dai_prepare(struct snd_pcm_substream *substream, 39162306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci struct snd_soc_component *component = cpu_dai->component; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci snd_soc_component_update_bits(component, CLASSD_MR, 39662306a36Sopenharmony_ci CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK, 39762306a36Sopenharmony_ci (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT) 39862306a36Sopenharmony_ci |(CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT)); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic int atmel_classd_cpu_dai_trigger(struct snd_pcm_substream *substream, 40462306a36Sopenharmony_ci int cmd, struct snd_soc_dai *cpu_dai) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct snd_soc_component *component = cpu_dai->component; 40762306a36Sopenharmony_ci u32 mask, val; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci mask = CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci switch (cmd) { 41262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 41362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 41462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 41562306a36Sopenharmony_ci val = mask; 41662306a36Sopenharmony_ci break; 41762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 41862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 41962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 42062306a36Sopenharmony_ci val = (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT) 42162306a36Sopenharmony_ci | (CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT); 42262306a36Sopenharmony_ci break; 42362306a36Sopenharmony_ci default: 42462306a36Sopenharmony_ci return -EINVAL; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci snd_soc_component_update_bits(component, CLASSD_MR, mask, val); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = { 43362306a36Sopenharmony_ci .startup = atmel_classd_cpu_dai_startup, 43462306a36Sopenharmony_ci .shutdown = atmel_classd_cpu_dai_shutdown, 43562306a36Sopenharmony_ci .mute_stream = atmel_classd_cpu_dai_mute_stream, 43662306a36Sopenharmony_ci .hw_params = atmel_classd_cpu_dai_hw_params, 43762306a36Sopenharmony_ci .prepare = atmel_classd_cpu_dai_prepare, 43862306a36Sopenharmony_ci .trigger = atmel_classd_cpu_dai_trigger, 43962306a36Sopenharmony_ci .no_capture_mute = 1, 44062306a36Sopenharmony_ci}; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic struct snd_soc_dai_driver atmel_classd_cpu_dai = { 44362306a36Sopenharmony_ci .playback = { 44462306a36Sopenharmony_ci .stream_name = "Playback", 44562306a36Sopenharmony_ci .channels_min = 1, 44662306a36Sopenharmony_ci .channels_max = 2, 44762306a36Sopenharmony_ci .rates = ATMEL_CLASSD_RATES, 44862306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 44962306a36Sopenharmony_ci }, 45062306a36Sopenharmony_ci .ops = &atmel_classd_cpu_dai_ops, 45162306a36Sopenharmony_ci}; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic const struct snd_soc_component_driver atmel_classd_cpu_dai_component = { 45462306a36Sopenharmony_ci .name = "atmel-classd", 45562306a36Sopenharmony_ci .probe = atmel_classd_component_probe, 45662306a36Sopenharmony_ci .resume = atmel_classd_component_resume, 45762306a36Sopenharmony_ci .controls = atmel_classd_snd_controls, 45862306a36Sopenharmony_ci .num_controls = ARRAY_SIZE(atmel_classd_snd_controls), 45962306a36Sopenharmony_ci .idle_bias_on = 1, 46062306a36Sopenharmony_ci .use_pmdown_time = 1, 46162306a36Sopenharmony_ci .legacy_dai_naming = 1, 46262306a36Sopenharmony_ci}; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci/* ASoC sound card */ 46562306a36Sopenharmony_cistatic int atmel_classd_asoc_card_init(struct device *dev, 46662306a36Sopenharmony_ci struct snd_soc_card *card) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct snd_soc_dai_link *dai_link; 46962306a36Sopenharmony_ci struct atmel_classd *dd = snd_soc_card_get_drvdata(card); 47062306a36Sopenharmony_ci struct snd_soc_dai_link_component *comp; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL); 47362306a36Sopenharmony_ci if (!dai_link) 47462306a36Sopenharmony_ci return -ENOMEM; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL); 47762306a36Sopenharmony_ci if (!comp) 47862306a36Sopenharmony_ci return -ENOMEM; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci dai_link->cpus = comp; 48162306a36Sopenharmony_ci dai_link->codecs = &asoc_dummy_dlc; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci dai_link->num_cpus = 1; 48462306a36Sopenharmony_ci dai_link->num_codecs = 1; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci dai_link->name = "CLASSD"; 48762306a36Sopenharmony_ci dai_link->stream_name = "CLASSD PCM"; 48862306a36Sopenharmony_ci dai_link->cpus->dai_name = dev_name(dev); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci card->dai_link = dai_link; 49162306a36Sopenharmony_ci card->num_links = 1; 49262306a36Sopenharmony_ci card->name = dd->pdata->card_name; 49362306a36Sopenharmony_ci card->dev = dev; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci return 0; 49662306a36Sopenharmony_ci}; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci/* regmap configuration */ 49962306a36Sopenharmony_cistatic const struct reg_default atmel_classd_reg_defaults[] = { 50062306a36Sopenharmony_ci { CLASSD_INTPMR, 0x00301212 }, 50162306a36Sopenharmony_ci}; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci#define ATMEL_CLASSD_REG_MAX 0xE4 50462306a36Sopenharmony_cistatic const struct regmap_config atmel_classd_regmap_config = { 50562306a36Sopenharmony_ci .reg_bits = 32, 50662306a36Sopenharmony_ci .reg_stride = 4, 50762306a36Sopenharmony_ci .val_bits = 32, 50862306a36Sopenharmony_ci .max_register = ATMEL_CLASSD_REG_MAX, 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci .cache_type = REGCACHE_FLAT, 51162306a36Sopenharmony_ci .reg_defaults = atmel_classd_reg_defaults, 51262306a36Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(atmel_classd_reg_defaults), 51362306a36Sopenharmony_ci}; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic int atmel_classd_probe(struct platform_device *pdev) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 51862306a36Sopenharmony_ci struct atmel_classd *dd; 51962306a36Sopenharmony_ci struct resource *res; 52062306a36Sopenharmony_ci void __iomem *io_base; 52162306a36Sopenharmony_ci const struct atmel_classd_pdata *pdata; 52262306a36Sopenharmony_ci struct snd_soc_card *card; 52362306a36Sopenharmony_ci int ret; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci pdata = dev_get_platdata(dev); 52662306a36Sopenharmony_ci if (!pdata) { 52762306a36Sopenharmony_ci pdata = atmel_classd_dt_init(dev); 52862306a36Sopenharmony_ci if (IS_ERR(pdata)) 52962306a36Sopenharmony_ci return PTR_ERR(pdata); 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL); 53362306a36Sopenharmony_ci if (!dd) 53462306a36Sopenharmony_ci return -ENOMEM; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci dd->pdata = pdata; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci dd->irq = platform_get_irq(pdev, 0); 53962306a36Sopenharmony_ci if (dd->irq < 0) 54062306a36Sopenharmony_ci return dd->irq; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci dd->pclk = devm_clk_get(dev, "pclk"); 54362306a36Sopenharmony_ci if (IS_ERR(dd->pclk)) { 54462306a36Sopenharmony_ci ret = PTR_ERR(dd->pclk); 54562306a36Sopenharmony_ci dev_err(dev, "failed to get peripheral clock: %d\n", ret); 54662306a36Sopenharmony_ci return ret; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci dd->gclk = devm_clk_get(dev, "gclk"); 55062306a36Sopenharmony_ci if (IS_ERR(dd->gclk)) { 55162306a36Sopenharmony_ci ret = PTR_ERR(dd->gclk); 55262306a36Sopenharmony_ci dev_err(dev, "failed to get GCK clock: %d\n", ret); 55362306a36Sopenharmony_ci return ret; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 55762306a36Sopenharmony_ci if (IS_ERR(io_base)) 55862306a36Sopenharmony_ci return PTR_ERR(io_base); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci dd->phy_base = res->start; 56162306a36Sopenharmony_ci dd->dev = dev; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci dd->regmap = devm_regmap_init_mmio(dev, io_base, 56462306a36Sopenharmony_ci &atmel_classd_regmap_config); 56562306a36Sopenharmony_ci if (IS_ERR(dd->regmap)) { 56662306a36Sopenharmony_ci ret = PTR_ERR(dd->regmap); 56762306a36Sopenharmony_ci dev_err(dev, "failed to init register map: %d\n", ret); 56862306a36Sopenharmony_ci return ret; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci ret = devm_snd_soc_register_component(dev, 57262306a36Sopenharmony_ci &atmel_classd_cpu_dai_component, 57362306a36Sopenharmony_ci &atmel_classd_cpu_dai, 1); 57462306a36Sopenharmony_ci if (ret) { 57562306a36Sopenharmony_ci dev_err(dev, "could not register CPU DAI: %d\n", ret); 57662306a36Sopenharmony_ci return ret; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci ret = devm_snd_dmaengine_pcm_register(dev, 58062306a36Sopenharmony_ci &atmel_classd_dmaengine_pcm_config, 58162306a36Sopenharmony_ci 0); 58262306a36Sopenharmony_ci if (ret) { 58362306a36Sopenharmony_ci dev_err(dev, "could not register platform: %d\n", ret); 58462306a36Sopenharmony_ci return ret; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* register sound card */ 58862306a36Sopenharmony_ci card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); 58962306a36Sopenharmony_ci if (!card) { 59062306a36Sopenharmony_ci ret = -ENOMEM; 59162306a36Sopenharmony_ci goto unregister_codec; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci snd_soc_card_set_drvdata(card, dd); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci ret = atmel_classd_asoc_card_init(dev, card); 59762306a36Sopenharmony_ci if (ret) { 59862306a36Sopenharmony_ci dev_err(dev, "failed to init sound card\n"); 59962306a36Sopenharmony_ci goto unregister_codec; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci ret = devm_snd_soc_register_card(dev, card); 60362306a36Sopenharmony_ci if (ret) { 60462306a36Sopenharmony_ci dev_err(dev, "failed to register sound card: %d\n", ret); 60562306a36Sopenharmony_ci goto unregister_codec; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci return 0; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ciunregister_codec: 61162306a36Sopenharmony_ci return ret; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic struct platform_driver atmel_classd_driver = { 61562306a36Sopenharmony_ci .driver = { 61662306a36Sopenharmony_ci .name = "atmel-classd", 61762306a36Sopenharmony_ci .of_match_table = of_match_ptr(atmel_classd_of_match), 61862306a36Sopenharmony_ci .pm = &snd_soc_pm_ops, 61962306a36Sopenharmony_ci }, 62062306a36Sopenharmony_ci .probe = atmel_classd_probe, 62162306a36Sopenharmony_ci}; 62262306a36Sopenharmony_cimodule_platform_driver(atmel_classd_driver); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ciMODULE_DESCRIPTION("Atmel ClassD driver under ALSA SoC architecture"); 62562306a36Sopenharmony_ciMODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>"); 62662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 627