cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

atmel-classd.c (16847B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/* Atmel ALSA SoC Audio Class D Amplifier (CLASSD) driver
      3 *
      4 * Copyright (C) 2015 Atmel
      5 *
      6 * Author: Songjun Wu <songjun.wu@atmel.com>
      7 */
      8
      9#include <linux/of.h>
     10#include <linux/clk.h>
     11#include <linux/module.h>
     12#include <linux/platform_device.h>
     13#include <linux/regmap.h>
     14#include <sound/core.h>
     15#include <sound/dmaengine_pcm.h>
     16#include <sound/pcm_params.h>
     17#include <sound/tlv.h>
     18#include "atmel-classd.h"
     19
     20struct atmel_classd_pdata {
     21	bool non_overlap_enable;
     22	int non_overlap_time;
     23	int pwm_type;
     24	const char *card_name;
     25};
     26
     27struct atmel_classd {
     28	dma_addr_t phy_base;
     29	struct regmap *regmap;
     30	struct clk *pclk;
     31	struct clk *gclk;
     32	struct device *dev;
     33	int irq;
     34	const struct atmel_classd_pdata *pdata;
     35};
     36
     37#ifdef CONFIG_OF
     38static const struct of_device_id atmel_classd_of_match[] = {
     39	{
     40		.compatible = "atmel,sama5d2-classd",
     41	}, {
     42		/* sentinel */
     43	}
     44};
     45MODULE_DEVICE_TABLE(of, atmel_classd_of_match);
     46
     47static struct atmel_classd_pdata *atmel_classd_dt_init(struct device *dev)
     48{
     49	struct device_node *np = dev->of_node;
     50	struct atmel_classd_pdata *pdata;
     51	const char *pwm_type_s;
     52	int ret;
     53
     54	if (!np) {
     55		dev_err(dev, "device node not found\n");
     56		return ERR_PTR(-EINVAL);
     57	}
     58
     59	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
     60	if (!pdata)
     61		return ERR_PTR(-ENOMEM);
     62
     63	ret = of_property_read_string(np, "atmel,pwm-type", &pwm_type_s);
     64	if ((ret == 0) && (strcmp(pwm_type_s, "diff") == 0))
     65		pdata->pwm_type = CLASSD_MR_PWMTYP_DIFF;
     66	else
     67		pdata->pwm_type = CLASSD_MR_PWMTYP_SINGLE;
     68
     69	ret = of_property_read_u32(np,
     70			"atmel,non-overlap-time", &pdata->non_overlap_time);
     71	if (ret)
     72		pdata->non_overlap_enable = false;
     73	else
     74		pdata->non_overlap_enable = true;
     75
     76	ret = of_property_read_string(np, "atmel,model", &pdata->card_name);
     77	if (ret)
     78		pdata->card_name = "CLASSD";
     79
     80	return pdata;
     81}
     82#else
     83static inline struct atmel_classd_pdata *
     84atmel_classd_dt_init(struct device *dev)
     85{
     86	return ERR_PTR(-EINVAL);
     87}
     88#endif
     89
     90#define ATMEL_CLASSD_RATES (SNDRV_PCM_RATE_8000 \
     91			| SNDRV_PCM_RATE_16000	| SNDRV_PCM_RATE_22050 \
     92			| SNDRV_PCM_RATE_32000	| SNDRV_PCM_RATE_44100 \
     93			| SNDRV_PCM_RATE_48000	| SNDRV_PCM_RATE_88200 \
     94			| SNDRV_PCM_RATE_96000)
     95
     96static const struct snd_pcm_hardware atmel_classd_hw = {
     97	.info			= SNDRV_PCM_INFO_MMAP
     98				| SNDRV_PCM_INFO_MMAP_VALID
     99				| SNDRV_PCM_INFO_INTERLEAVED
    100				| SNDRV_PCM_INFO_RESUME
    101				| SNDRV_PCM_INFO_PAUSE,
    102	.formats		= (SNDRV_PCM_FMTBIT_S16_LE),
    103	.rates			= ATMEL_CLASSD_RATES,
    104	.rate_min		= 8000,
    105	.rate_max		= 96000,
    106	.channels_min		= 1,
    107	.channels_max		= 2,
    108	.buffer_bytes_max	= 64 * 1024,
    109	.period_bytes_min	= 256,
    110	.period_bytes_max	= 32 * 1024,
    111	.periods_min		= 2,
    112	.periods_max		= 256,
    113};
    114
    115#define ATMEL_CLASSD_PREALLOC_BUF_SIZE  (64 * 1024)
    116
    117/* cpu dai component */
    118static int atmel_classd_cpu_dai_startup(struct snd_pcm_substream *substream,
    119					struct snd_soc_dai *cpu_dai)
    120{
    121	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    122	struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
    123	int err;
    124
    125	regmap_write(dd->regmap, CLASSD_THR, 0x0);
    126
    127	err = clk_prepare_enable(dd->pclk);
    128	if (err)
    129		return err;
    130	err = clk_prepare_enable(dd->gclk);
    131	if (err) {
    132		clk_disable_unprepare(dd->pclk);
    133		return err;
    134	}
    135	return 0;
    136}
    137
    138/* platform */
    139static int
    140atmel_classd_platform_configure_dma(struct snd_pcm_substream *substream,
    141	struct snd_pcm_hw_params *params,
    142	struct dma_slave_config *slave_config)
    143{
    144	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    145	struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
    146
    147	if (params_physical_width(params) != 16) {
    148		dev_err(dd->dev,
    149			"only supports 16-bit audio data\n");
    150		return -EINVAL;
    151	}
    152
    153	if (params_channels(params) == 1)
    154		slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
    155	else
    156		slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
    157
    158	slave_config->direction		= DMA_MEM_TO_DEV;
    159	slave_config->dst_addr		= dd->phy_base + CLASSD_THR;
    160	slave_config->dst_maxburst	= 1;
    161	slave_config->src_maxburst	= 1;
    162	slave_config->device_fc		= false;
    163
    164	return 0;
    165}
    166
    167static const struct snd_dmaengine_pcm_config
    168atmel_classd_dmaengine_pcm_config = {
    169	.prepare_slave_config	= atmel_classd_platform_configure_dma,
    170	.pcm_hardware		= &atmel_classd_hw,
    171	.prealloc_buffer_size	= ATMEL_CLASSD_PREALLOC_BUF_SIZE,
    172};
    173
    174/* codec */
    175static const char * const mono_mode_text[] = {
    176	"mix", "sat", "left", "right"
    177};
    178
    179static SOC_ENUM_SINGLE_DECL(classd_mono_mode_enum,
    180			CLASSD_INTPMR, CLASSD_INTPMR_MONO_MODE_SHIFT,
    181			mono_mode_text);
    182
    183static const char * const eqcfg_text[] = {
    184	"Treble-12dB", "Treble-6dB",
    185	"Medium-8dB", "Medium-3dB",
    186	"Bass-12dB", "Bass-6dB",
    187	"0 dB",
    188	"Bass+6dB", "Bass+12dB",
    189	"Medium+3dB", "Medium+8dB",
    190	"Treble+6dB", "Treble+12dB",
    191};
    192
    193static const unsigned int eqcfg_value[] = {
    194	CLASSD_INTPMR_EQCFG_T_CUT_12, CLASSD_INTPMR_EQCFG_T_CUT_6,
    195	CLASSD_INTPMR_EQCFG_M_CUT_8, CLASSD_INTPMR_EQCFG_M_CUT_3,
    196	CLASSD_INTPMR_EQCFG_B_CUT_12, CLASSD_INTPMR_EQCFG_B_CUT_6,
    197	CLASSD_INTPMR_EQCFG_FLAT,
    198	CLASSD_INTPMR_EQCFG_B_BOOST_6, CLASSD_INTPMR_EQCFG_B_BOOST_12,
    199	CLASSD_INTPMR_EQCFG_M_BOOST_3, CLASSD_INTPMR_EQCFG_M_BOOST_8,
    200	CLASSD_INTPMR_EQCFG_T_BOOST_6, CLASSD_INTPMR_EQCFG_T_BOOST_12,
    201};
    202
    203static SOC_VALUE_ENUM_SINGLE_DECL(classd_eqcfg_enum,
    204		CLASSD_INTPMR, CLASSD_INTPMR_EQCFG_SHIFT, 0xf,
    205		eqcfg_text, eqcfg_value);
    206
    207static const DECLARE_TLV_DB_SCALE(classd_digital_tlv, -7800, 100, 1);
    208
    209static const struct snd_kcontrol_new atmel_classd_snd_controls[] = {
    210SOC_DOUBLE_TLV("Playback Volume", CLASSD_INTPMR,
    211		CLASSD_INTPMR_ATTL_SHIFT, CLASSD_INTPMR_ATTR_SHIFT,
    212		78, 1, classd_digital_tlv),
    213
    214SOC_SINGLE("Deemphasis Switch", CLASSD_INTPMR,
    215		CLASSD_INTPMR_DEEMP_SHIFT, 1, 0),
    216
    217SOC_SINGLE("Mono Switch", CLASSD_INTPMR, CLASSD_INTPMR_MONO_SHIFT, 1, 0),
    218
    219SOC_SINGLE("Swap Switch", CLASSD_INTPMR, CLASSD_INTPMR_SWAP_SHIFT, 1, 0),
    220
    221SOC_ENUM("Mono Mode", classd_mono_mode_enum),
    222
    223SOC_ENUM("EQ", classd_eqcfg_enum),
    224};
    225
    226static const char * const pwm_type[] = {
    227	"Single ended", "Differential"
    228};
    229
    230static int atmel_classd_component_probe(struct snd_soc_component *component)
    231{
    232	struct snd_soc_card *card = snd_soc_component_get_drvdata(component);
    233	struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
    234	const struct atmel_classd_pdata *pdata = dd->pdata;
    235	u32 mask, val;
    236
    237	mask = CLASSD_MR_PWMTYP_MASK;
    238	val = pdata->pwm_type << CLASSD_MR_PWMTYP_SHIFT;
    239
    240	mask |= CLASSD_MR_NON_OVERLAP_MASK;
    241	if (pdata->non_overlap_enable) {
    242		val |= (CLASSD_MR_NON_OVERLAP_EN
    243			<< CLASSD_MR_NON_OVERLAP_SHIFT);
    244
    245		mask |= CLASSD_MR_NOVR_VAL_MASK;
    246		switch (pdata->non_overlap_time) {
    247		case 5:
    248			val |= (CLASSD_MR_NOVR_VAL_5NS
    249				<< CLASSD_MR_NOVR_VAL_SHIFT);
    250			break;
    251		case 10:
    252			val |= (CLASSD_MR_NOVR_VAL_10NS
    253				<< CLASSD_MR_NOVR_VAL_SHIFT);
    254			break;
    255		case 15:
    256			val |= (CLASSD_MR_NOVR_VAL_15NS
    257				<< CLASSD_MR_NOVR_VAL_SHIFT);
    258			break;
    259		case 20:
    260			val |= (CLASSD_MR_NOVR_VAL_20NS
    261				<< CLASSD_MR_NOVR_VAL_SHIFT);
    262			break;
    263		default:
    264			val |= (CLASSD_MR_NOVR_VAL_10NS
    265				<< CLASSD_MR_NOVR_VAL_SHIFT);
    266			dev_warn(component->dev,
    267				"non-overlapping value %d is invalid, the default value 10 is specified\n",
    268				pdata->non_overlap_time);
    269			break;
    270		}
    271	}
    272
    273	snd_soc_component_update_bits(component, CLASSD_MR, mask, val);
    274
    275	dev_info(component->dev,
    276		"PWM modulation type is %s, non-overlapping is %s\n",
    277		pwm_type[pdata->pwm_type],
    278		pdata->non_overlap_enable?"enabled":"disabled");
    279
    280	return 0;
    281}
    282
    283static int atmel_classd_component_resume(struct snd_soc_component *component)
    284{
    285	struct snd_soc_card *card = snd_soc_component_get_drvdata(component);
    286	struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
    287
    288	return regcache_sync(dd->regmap);
    289}
    290
    291static int atmel_classd_cpu_dai_mute_stream(struct snd_soc_dai *cpu_dai,
    292					    int mute, int direction)
    293{
    294	struct snd_soc_component *component = cpu_dai->component;
    295	u32 mask, val;
    296
    297	mask = CLASSD_MR_LMUTE_MASK | CLASSD_MR_RMUTE_MASK;
    298
    299	if (mute)
    300		val = mask;
    301	else
    302		val = 0;
    303
    304	snd_soc_component_update_bits(component, CLASSD_MR, mask, val);
    305
    306	return 0;
    307}
    308
    309#define CLASSD_GCLK_RATE_11M2896_MPY_8 (112896 * 100 * 8)
    310#define CLASSD_GCLK_RATE_12M288_MPY_8  (12288 * 1000 * 8)
    311
    312static struct {
    313	int rate;
    314	int sample_rate;
    315	int dsp_clk;
    316	unsigned long gclk_rate;
    317} const sample_rates[] = {
    318	{ 8000,  CLASSD_INTPMR_FRAME_8K,
    319	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
    320	{ 16000, CLASSD_INTPMR_FRAME_16K,
    321	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
    322	{ 32000, CLASSD_INTPMR_FRAME_32K,
    323	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
    324	{ 48000, CLASSD_INTPMR_FRAME_48K,
    325	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
    326	{ 96000, CLASSD_INTPMR_FRAME_96K,
    327	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
    328	{ 22050, CLASSD_INTPMR_FRAME_22K,
    329	CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
    330	{ 44100, CLASSD_INTPMR_FRAME_44K,
    331	CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
    332	{ 88200, CLASSD_INTPMR_FRAME_88K,
    333	CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
    334};
    335
    336static int
    337atmel_classd_cpu_dai_hw_params(struct snd_pcm_substream *substream,
    338			       struct snd_pcm_hw_params *params,
    339			       struct snd_soc_dai *cpu_dai)
    340{
    341	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    342	struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
    343	struct snd_soc_component *component = cpu_dai->component;
    344	int fs;
    345	int i, best, best_val, cur_val, ret;
    346	u32 mask, val;
    347
    348	fs = params_rate(params);
    349
    350	best = 0;
    351	best_val = abs(fs - sample_rates[0].rate);
    352	for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
    353		/* Closest match */
    354		cur_val = abs(fs - sample_rates[i].rate);
    355		if (cur_val < best_val) {
    356			best = i;
    357			best_val = cur_val;
    358		}
    359	}
    360
    361	dev_dbg(component->dev,
    362		"Selected SAMPLE_RATE of %dHz, GCLK_RATE of %ldHz\n",
    363		sample_rates[best].rate, sample_rates[best].gclk_rate);
    364
    365	clk_disable_unprepare(dd->gclk);
    366
    367	ret = clk_set_rate(dd->gclk, sample_rates[best].gclk_rate);
    368	if (ret)
    369		return ret;
    370
    371	mask = CLASSD_INTPMR_DSP_CLK_FREQ_MASK | CLASSD_INTPMR_FRAME_MASK;
    372	val = (sample_rates[best].dsp_clk << CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT)
    373	| (sample_rates[best].sample_rate << CLASSD_INTPMR_FRAME_SHIFT);
    374
    375	snd_soc_component_update_bits(component, CLASSD_INTPMR, mask, val);
    376
    377	return clk_prepare_enable(dd->gclk);
    378}
    379
    380static void
    381atmel_classd_cpu_dai_shutdown(struct snd_pcm_substream *substream,
    382			      struct snd_soc_dai *cpu_dai)
    383{
    384	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    385	struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
    386
    387	clk_disable_unprepare(dd->gclk);
    388}
    389
    390static int atmel_classd_cpu_dai_prepare(struct snd_pcm_substream *substream,
    391					struct snd_soc_dai *cpu_dai)
    392{
    393	struct snd_soc_component *component = cpu_dai->component;
    394
    395	snd_soc_component_update_bits(component, CLASSD_MR,
    396				CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK,
    397				(CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT)
    398				|(CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT));
    399
    400	return 0;
    401}
    402
    403static int atmel_classd_cpu_dai_trigger(struct snd_pcm_substream *substream,
    404					int cmd, struct snd_soc_dai *cpu_dai)
    405{
    406	struct snd_soc_component *component = cpu_dai->component;
    407	u32 mask, val;
    408
    409	mask = CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK;
    410
    411	switch (cmd) {
    412	case SNDRV_PCM_TRIGGER_START:
    413	case SNDRV_PCM_TRIGGER_RESUME:
    414	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
    415		val = mask;
    416		break;
    417	case SNDRV_PCM_TRIGGER_STOP:
    418	case SNDRV_PCM_TRIGGER_SUSPEND:
    419	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    420		val = (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT)
    421			| (CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT);
    422		break;
    423	default:
    424		return -EINVAL;
    425	}
    426
    427	snd_soc_component_update_bits(component, CLASSD_MR, mask, val);
    428
    429	return 0;
    430}
    431
    432static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = {
    433	.startup        = atmel_classd_cpu_dai_startup,
    434	.shutdown       = atmel_classd_cpu_dai_shutdown,
    435	.mute_stream	= atmel_classd_cpu_dai_mute_stream,
    436	.hw_params	= atmel_classd_cpu_dai_hw_params,
    437	.prepare	= atmel_classd_cpu_dai_prepare,
    438	.trigger	= atmel_classd_cpu_dai_trigger,
    439	.no_capture_mute = 1,
    440};
    441
    442static struct snd_soc_dai_driver atmel_classd_cpu_dai = {
    443	.playback = {
    444		.stream_name	= "Playback",
    445		.channels_min	= 1,
    446		.channels_max	= 2,
    447		.rates		= ATMEL_CLASSD_RATES,
    448		.formats	= SNDRV_PCM_FMTBIT_S16_LE,
    449	},
    450	.ops = &atmel_classd_cpu_dai_ops,
    451};
    452
    453static const struct snd_soc_component_driver atmel_classd_cpu_dai_component = {
    454	.name			= "atmel-classd",
    455	.probe			= atmel_classd_component_probe,
    456	.resume			= atmel_classd_component_resume,
    457	.controls		= atmel_classd_snd_controls,
    458	.num_controls		= ARRAY_SIZE(atmel_classd_snd_controls),
    459	.idle_bias_on		= 1,
    460	.use_pmdown_time	= 1,
    461};
    462
    463/* ASoC sound card */
    464static int atmel_classd_asoc_card_init(struct device *dev,
    465					struct snd_soc_card *card)
    466{
    467	struct snd_soc_dai_link *dai_link;
    468	struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
    469	struct snd_soc_dai_link_component *comp;
    470
    471	dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL);
    472	if (!dai_link)
    473		return -ENOMEM;
    474
    475	comp = devm_kzalloc(dev, 3 * sizeof(*comp), GFP_KERNEL);
    476	if (!comp)
    477		return -ENOMEM;
    478
    479	dai_link->cpus		= &comp[0];
    480	dai_link->codecs	= &comp[1];
    481	dai_link->platforms	= &comp[2];
    482
    483	dai_link->num_cpus	= 1;
    484	dai_link->num_codecs	= 1;
    485	dai_link->num_platforms	= 1;
    486
    487	dai_link->name			= "CLASSD";
    488	dai_link->stream_name		= "CLASSD PCM";
    489	dai_link->codecs->dai_name	= "snd-soc-dummy-dai";
    490	dai_link->cpus->dai_name	= dev_name(dev);
    491	dai_link->codecs->name		= "snd-soc-dummy";
    492	dai_link->platforms->name	= dev_name(dev);
    493
    494	card->dai_link	= dai_link;
    495	card->num_links	= 1;
    496	card->name	= dd->pdata->card_name;
    497	card->dev	= dev;
    498
    499	return 0;
    500};
    501
    502/* regmap configuration */
    503static const struct reg_default atmel_classd_reg_defaults[] = {
    504	{ CLASSD_INTPMR,   0x00301212 },
    505};
    506
    507#define ATMEL_CLASSD_REG_MAX    0xE4
    508static const struct regmap_config atmel_classd_regmap_config = {
    509	.reg_bits	= 32,
    510	.reg_stride	= 4,
    511	.val_bits	= 32,
    512	.max_register	= ATMEL_CLASSD_REG_MAX,
    513
    514	.cache_type		= REGCACHE_FLAT,
    515	.reg_defaults		= atmel_classd_reg_defaults,
    516	.num_reg_defaults	= ARRAY_SIZE(atmel_classd_reg_defaults),
    517};
    518
    519static int atmel_classd_probe(struct platform_device *pdev)
    520{
    521	struct device *dev = &pdev->dev;
    522	struct atmel_classd *dd;
    523	struct resource *res;
    524	void __iomem *io_base;
    525	const struct atmel_classd_pdata *pdata;
    526	struct snd_soc_card *card;
    527	int ret;
    528
    529	pdata = dev_get_platdata(dev);
    530	if (!pdata) {
    531		pdata = atmel_classd_dt_init(dev);
    532		if (IS_ERR(pdata))
    533			return PTR_ERR(pdata);
    534	}
    535
    536	dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
    537	if (!dd)
    538		return -ENOMEM;
    539
    540	dd->pdata = pdata;
    541
    542	dd->irq = platform_get_irq(pdev, 0);
    543	if (dd->irq < 0)
    544		return dd->irq;
    545
    546	dd->pclk = devm_clk_get(dev, "pclk");
    547	if (IS_ERR(dd->pclk)) {
    548		ret = PTR_ERR(dd->pclk);
    549		dev_err(dev, "failed to get peripheral clock: %d\n", ret);
    550		return ret;
    551	}
    552
    553	dd->gclk = devm_clk_get(dev, "gclk");
    554	if (IS_ERR(dd->gclk)) {
    555		ret = PTR_ERR(dd->gclk);
    556		dev_err(dev, "failed to get GCK clock: %d\n", ret);
    557		return ret;
    558	}
    559
    560	io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
    561	if (IS_ERR(io_base))
    562		return PTR_ERR(io_base);
    563
    564	dd->phy_base = res->start;
    565	dd->dev = dev;
    566
    567	dd->regmap = devm_regmap_init_mmio(dev, io_base,
    568					&atmel_classd_regmap_config);
    569	if (IS_ERR(dd->regmap)) {
    570		ret = PTR_ERR(dd->regmap);
    571		dev_err(dev, "failed to init register map: %d\n", ret);
    572		return ret;
    573	}
    574
    575	ret = devm_snd_soc_register_component(dev,
    576					&atmel_classd_cpu_dai_component,
    577					&atmel_classd_cpu_dai, 1);
    578	if (ret) {
    579		dev_err(dev, "could not register CPU DAI: %d\n", ret);
    580		return ret;
    581	}
    582
    583	ret = devm_snd_dmaengine_pcm_register(dev,
    584					&atmel_classd_dmaengine_pcm_config,
    585					0);
    586	if (ret) {
    587		dev_err(dev, "could not register platform: %d\n", ret);
    588		return ret;
    589	}
    590
    591	/* register sound card */
    592	card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
    593	if (!card) {
    594		ret = -ENOMEM;
    595		goto unregister_codec;
    596	}
    597
    598	snd_soc_card_set_drvdata(card, dd);
    599
    600	ret = atmel_classd_asoc_card_init(dev, card);
    601	if (ret) {
    602		dev_err(dev, "failed to init sound card\n");
    603		goto unregister_codec;
    604	}
    605
    606	ret = devm_snd_soc_register_card(dev, card);
    607	if (ret) {
    608		dev_err(dev, "failed to register sound card: %d\n", ret);
    609		goto unregister_codec;
    610	}
    611
    612	return 0;
    613
    614unregister_codec:
    615	return ret;
    616}
    617
    618static int atmel_classd_remove(struct platform_device *pdev)
    619{
    620	return 0;
    621}
    622
    623static struct platform_driver atmel_classd_driver = {
    624	.driver	= {
    625		.name		= "atmel-classd",
    626		.of_match_table	= of_match_ptr(atmel_classd_of_match),
    627		.pm		= &snd_soc_pm_ops,
    628	},
    629	.probe	= atmel_classd_probe,
    630	.remove	= atmel_classd_remove,
    631};
    632module_platform_driver(atmel_classd_driver);
    633
    634MODULE_DESCRIPTION("Atmel ClassD driver under ALSA SoC architecture");
    635MODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>");
    636MODULE_LICENSE("GPL");