1 /* Atmel ALSA SoC Audio Class D Amplifier (CLASSD) driver
3 * Copyright (C) 2015 Atmel
5 * Author: Songjun Wu <songjun.wu@atmel.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 or later
9 * as published by the Free Software Foundation.
13 #include <linux/clk.h>
14 #include <linux/module.h>
15 #include <linux/platform_device.h>
16 #include <linux/regmap.h>
17 #include <sound/core.h>
18 #include <sound/dmaengine_pcm.h>
19 #include <sound/pcm_params.h>
20 #include <sound/tlv.h>
21 #include "atmel-classd.h"
23 struct atmel_classd_pdata
{
24 bool non_overlap_enable
;
27 const char *card_name
;
32 struct regmap
*regmap
;
37 const struct atmel_classd_pdata
*pdata
;
41 static const struct of_device_id atmel_classd_of_match
[] = {
43 .compatible
= "atmel,sama5d2-classd",
48 MODULE_DEVICE_TABLE(of
, atmel_classd_of_match
);
50 static struct atmel_classd_pdata
*atmel_classd_dt_init(struct device
*dev
)
52 struct device_node
*np
= dev
->of_node
;
53 struct atmel_classd_pdata
*pdata
;
58 dev_err(dev
, "device node not found\n");
59 return ERR_PTR(-EINVAL
);
62 pdata
= devm_kzalloc(dev
, sizeof(*pdata
), GFP_KERNEL
);
64 return ERR_PTR(-ENOMEM
);
66 ret
= of_property_read_string(np
, "atmel,pwm-type", &pwm_type
);
67 if ((ret
== 0) && (strcmp(pwm_type
, "diff") == 0))
68 pdata
->pwm_type
= CLASSD_MR_PWMTYP_DIFF
;
70 pdata
->pwm_type
= CLASSD_MR_PWMTYP_SINGLE
;
72 ret
= of_property_read_u32(np
,
73 "atmel,non-overlap-time", &pdata
->non_overlap_time
);
75 pdata
->non_overlap_enable
= false;
77 pdata
->non_overlap_enable
= true;
79 ret
= of_property_read_string(np
, "atmel,model", &pdata
->card_name
);
81 pdata
->card_name
= "CLASSD";
86 static inline struct atmel_classd_pdata
*
87 atmel_classd_dt_init(struct device
*dev
)
89 return ERR_PTR(-EINVAL
);
93 #define ATMEL_CLASSD_RATES (SNDRV_PCM_RATE_8000 \
94 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 \
95 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 \
96 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 \
97 | SNDRV_PCM_RATE_96000)
99 static const struct snd_pcm_hardware atmel_classd_hw
= {
100 .info
= SNDRV_PCM_INFO_MMAP
101 | SNDRV_PCM_INFO_MMAP_VALID
102 | SNDRV_PCM_INFO_INTERLEAVED
103 | SNDRV_PCM_INFO_RESUME
104 | SNDRV_PCM_INFO_PAUSE
,
105 .formats
= (SNDRV_PCM_FMTBIT_S16_LE
),
106 .rates
= ATMEL_CLASSD_RATES
,
111 .buffer_bytes_max
= 64 * 1024,
112 .period_bytes_min
= 256,
113 .period_bytes_max
= 32 * 1024,
118 #define ATMEL_CLASSD_PREALLOC_BUF_SIZE (64 * 1024)
120 /* cpu dai component */
121 static int atmel_classd_cpu_dai_startup(struct snd_pcm_substream
*substream
,
122 struct snd_soc_dai
*cpu_dai
)
124 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
125 struct atmel_classd
*dd
= snd_soc_card_get_drvdata(rtd
->card
);
127 regmap_write(dd
->regmap
, CLASSD_THR
, 0x0);
129 return clk_prepare_enable(dd
->pclk
);
132 static void atmel_classd_cpu_dai_shutdown(struct snd_pcm_substream
*substream
,
133 struct snd_soc_dai
*cpu_dai
)
135 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
136 struct atmel_classd
*dd
= snd_soc_card_get_drvdata(rtd
->card
);
138 clk_disable_unprepare(dd
->pclk
);
141 static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops
= {
142 .startup
= atmel_classd_cpu_dai_startup
,
143 .shutdown
= atmel_classd_cpu_dai_shutdown
,
146 static struct snd_soc_dai_driver atmel_classd_cpu_dai
= {
150 .rates
= ATMEL_CLASSD_RATES
,
151 .formats
= SNDRV_PCM_FMTBIT_S16_LE
,},
152 .ops
= &atmel_classd_cpu_dai_ops
,
155 static const struct snd_soc_component_driver atmel_classd_cpu_dai_component
= {
156 .name
= "atmel-classd",
161 atmel_classd_platform_configure_dma(struct snd_pcm_substream
*substream
,
162 struct snd_pcm_hw_params
*params
,
163 struct dma_slave_config
*slave_config
)
165 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
166 struct atmel_classd
*dd
= snd_soc_card_get_drvdata(rtd
->card
);
168 if (params_physical_width(params
) != 16) {
169 dev_err(rtd
->platform
->dev
,
170 "only supports 16-bit audio data\n");
174 slave_config
->direction
= DMA_MEM_TO_DEV
;
175 slave_config
->dst_addr
= dd
->phy_base
+ CLASSD_THR
;
176 slave_config
->dst_addr_width
= DMA_SLAVE_BUSWIDTH_4_BYTES
;
177 slave_config
->dst_maxburst
= 1;
178 slave_config
->src_maxburst
= 1;
179 slave_config
->device_fc
= false;
184 static const struct snd_dmaengine_pcm_config
185 atmel_classd_dmaengine_pcm_config
= {
186 .prepare_slave_config
= atmel_classd_platform_configure_dma
,
187 .pcm_hardware
= &atmel_classd_hw
,
188 .prealloc_buffer_size
= ATMEL_CLASSD_PREALLOC_BUF_SIZE
,
192 static const char * const mono_mode_text
[] = {
193 "mix", "sat", "left", "right"
196 static SOC_ENUM_SINGLE_DECL(classd_mono_mode_enum
,
197 CLASSD_INTPMR
, CLASSD_INTPMR_MONO_MODE_SHIFT
,
200 static const char * const eqcfg_text
[] = {
201 "Treble-12dB", "Treble-6dB",
202 "Medium-8dB", "Medium-3dB",
203 "Bass-12dB", "Bass-6dB",
205 "Bass+6dB", "Bass+12dB",
206 "Medium+3dB", "Medium+8dB",
207 "Treble+6dB", "Treble+12dB",
210 static const unsigned int eqcfg_value
[] = {
211 CLASSD_INTPMR_EQCFG_T_CUT_12
, CLASSD_INTPMR_EQCFG_T_CUT_6
,
212 CLASSD_INTPMR_EQCFG_M_CUT_8
, CLASSD_INTPMR_EQCFG_M_CUT_3
,
213 CLASSD_INTPMR_EQCFG_B_CUT_12
, CLASSD_INTPMR_EQCFG_B_CUT_6
,
214 CLASSD_INTPMR_EQCFG_FLAT
,
215 CLASSD_INTPMR_EQCFG_B_BOOST_6
, CLASSD_INTPMR_EQCFG_B_BOOST_12
,
216 CLASSD_INTPMR_EQCFG_M_BOOST_3
, CLASSD_INTPMR_EQCFG_M_BOOST_8
,
217 CLASSD_INTPMR_EQCFG_T_BOOST_6
, CLASSD_INTPMR_EQCFG_T_BOOST_12
,
220 static SOC_VALUE_ENUM_SINGLE_DECL(classd_eqcfg_enum
,
221 CLASSD_INTPMR
, CLASSD_INTPMR_EQCFG_SHIFT
, 0xf,
222 eqcfg_text
, eqcfg_value
);
224 static const DECLARE_TLV_DB_SCALE(classd_digital_tlv
, -7800, 100, 1);
226 static const struct snd_kcontrol_new atmel_classd_snd_controls
[] = {
227 SOC_DOUBLE_TLV("Playback Volume", CLASSD_INTPMR
,
228 CLASSD_INTPMR_ATTL_SHIFT
, CLASSD_INTPMR_ATTR_SHIFT
,
229 78, 1, classd_digital_tlv
),
231 SOC_SINGLE("Deemphasis Switch", CLASSD_INTPMR
,
232 CLASSD_INTPMR_DEEMP_SHIFT
, 1, 0),
234 SOC_SINGLE("Mono Switch", CLASSD_INTPMR
, CLASSD_INTPMR_MONO_SHIFT
, 1, 0),
236 SOC_SINGLE("Swap Switch", CLASSD_INTPMR
, CLASSD_INTPMR_SWAP_SHIFT
, 1, 0),
238 SOC_ENUM("Mono Mode", classd_mono_mode_enum
),
240 SOC_ENUM("EQ", classd_eqcfg_enum
),
243 static const char * const pwm_type
[] = {
244 "Single ended", "Differential"
247 static int atmel_classd_codec_probe(struct snd_soc_codec
*codec
)
249 struct snd_soc_card
*card
= snd_soc_codec_get_drvdata(codec
);
250 struct atmel_classd
*dd
= snd_soc_card_get_drvdata(card
);
251 const struct atmel_classd_pdata
*pdata
= dd
->pdata
;
254 mask
= CLASSD_MR_PWMTYP_MASK
;
255 val
= pdata
->pwm_type
<< CLASSD_MR_PWMTYP_SHIFT
;
257 mask
|= CLASSD_MR_NON_OVERLAP_MASK
;
258 if (pdata
->non_overlap_enable
) {
259 val
|= (CLASSD_MR_NON_OVERLAP_EN
260 << CLASSD_MR_NON_OVERLAP_SHIFT
);
262 mask
|= CLASSD_MR_NOVR_VAL_MASK
;
263 switch (pdata
->non_overlap_time
) {
265 val
|= (CLASSD_MR_NOVR_VAL_5NS
266 << CLASSD_MR_NOVR_VAL_SHIFT
);
269 val
|= (CLASSD_MR_NOVR_VAL_10NS
270 << CLASSD_MR_NOVR_VAL_SHIFT
);
273 val
|= (CLASSD_MR_NOVR_VAL_15NS
274 << CLASSD_MR_NOVR_VAL_SHIFT
);
277 val
|= (CLASSD_MR_NOVR_VAL_20NS
278 << CLASSD_MR_NOVR_VAL_SHIFT
);
281 val
|= (CLASSD_MR_NOVR_VAL_10NS
282 << CLASSD_MR_NOVR_VAL_SHIFT
);
284 "non-overlapping value %d is invalid, the default value 10 is specified\n",
285 pdata
->non_overlap_time
);
290 snd_soc_update_bits(codec
, CLASSD_MR
, mask
, val
);
293 "PWM modulation type is %s, non-overlapping is %s\n",
294 pwm_type
[pdata
->pwm_type
],
295 pdata
->non_overlap_enable
?"enabled":"disabled");
300 static struct regmap
*atmel_classd_codec_get_remap(struct device
*dev
)
302 return dev_get_regmap(dev
, NULL
);
305 static struct snd_soc_codec_driver soc_codec_dev_classd
= {
306 .probe
= atmel_classd_codec_probe
,
307 .controls
= atmel_classd_snd_controls
,
308 .num_controls
= ARRAY_SIZE(atmel_classd_snd_controls
),
309 .get_regmap
= atmel_classd_codec_get_remap
,
312 /* codec dai component */
313 static int atmel_classd_codec_dai_startup(struct snd_pcm_substream
*substream
,
314 struct snd_soc_dai
*codec_dai
)
316 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
317 struct atmel_classd
*dd
= snd_soc_card_get_drvdata(rtd
->card
);
320 ret
= clk_prepare_enable(dd
->aclk
);
324 return clk_prepare_enable(dd
->gclk
);
327 static int atmel_classd_codec_dai_digital_mute(struct snd_soc_dai
*codec_dai
,
330 struct snd_soc_codec
*codec
= codec_dai
->codec
;
333 mask
= CLASSD_MR_LMUTE_MASK
| CLASSD_MR_RMUTE_MASK
;
340 snd_soc_update_bits(codec
, CLASSD_MR
, mask
, val
);
345 #define CLASSD_ACLK_RATE_11M2896_MPY_8 (112896 * 100 * 8)
346 #define CLASSD_ACLK_RATE_12M288_MPY_8 (12228 * 1000 * 8)
352 unsigned long aclk_rate
;
353 } const sample_rates
[] = {
354 { 8000, CLASSD_INTPMR_FRAME_8K
,
355 CLASSD_INTPMR_DSP_CLK_FREQ_12M288
, CLASSD_ACLK_RATE_12M288_MPY_8
},
356 { 16000, CLASSD_INTPMR_FRAME_16K
,
357 CLASSD_INTPMR_DSP_CLK_FREQ_12M288
, CLASSD_ACLK_RATE_12M288_MPY_8
},
358 { 32000, CLASSD_INTPMR_FRAME_32K
,
359 CLASSD_INTPMR_DSP_CLK_FREQ_12M288
, CLASSD_ACLK_RATE_12M288_MPY_8
},
360 { 48000, CLASSD_INTPMR_FRAME_48K
,
361 CLASSD_INTPMR_DSP_CLK_FREQ_12M288
, CLASSD_ACLK_RATE_12M288_MPY_8
},
362 { 96000, CLASSD_INTPMR_FRAME_96K
,
363 CLASSD_INTPMR_DSP_CLK_FREQ_12M288
, CLASSD_ACLK_RATE_12M288_MPY_8
},
364 { 22050, CLASSD_INTPMR_FRAME_22K
,
365 CLASSD_INTPMR_DSP_CLK_FREQ_11M2896
, CLASSD_ACLK_RATE_11M2896_MPY_8
},
366 { 44100, CLASSD_INTPMR_FRAME_44K
,
367 CLASSD_INTPMR_DSP_CLK_FREQ_11M2896
, CLASSD_ACLK_RATE_11M2896_MPY_8
},
368 { 88200, CLASSD_INTPMR_FRAME_88K
,
369 CLASSD_INTPMR_DSP_CLK_FREQ_11M2896
, CLASSD_ACLK_RATE_11M2896_MPY_8
},
373 atmel_classd_codec_dai_hw_params(struct snd_pcm_substream
*substream
,
374 struct snd_pcm_hw_params
*params
,
375 struct snd_soc_dai
*codec_dai
)
377 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
378 struct atmel_classd
*dd
= snd_soc_card_get_drvdata(rtd
->card
);
379 struct snd_soc_codec
*codec
= codec_dai
->codec
;
381 int i
, best
, best_val
, cur_val
, ret
;
384 fs
= params_rate(params
);
387 best_val
= abs(fs
- sample_rates
[0].rate
);
388 for (i
= 1; i
< ARRAY_SIZE(sample_rates
); i
++) {
390 cur_val
= abs(fs
- sample_rates
[i
].rate
);
391 if (cur_val
< best_val
) {
398 "Selected SAMPLE_RATE of %dHz, ACLK_RATE of %ldHz\n",
399 sample_rates
[best
].rate
, sample_rates
[best
].aclk_rate
);
401 clk_disable_unprepare(dd
->gclk
);
402 clk_disable_unprepare(dd
->aclk
);
404 ret
= clk_set_rate(dd
->aclk
, sample_rates
[best
].aclk_rate
);
408 mask
= CLASSD_INTPMR_DSP_CLK_FREQ_MASK
| CLASSD_INTPMR_FRAME_MASK
;
409 val
= (sample_rates
[best
].dsp_clk
<< CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT
)
410 | (sample_rates
[best
].sample_rate
<< CLASSD_INTPMR_FRAME_SHIFT
);
412 snd_soc_update_bits(codec
, CLASSD_INTPMR
, mask
, val
);
414 ret
= clk_prepare_enable(dd
->aclk
);
418 return clk_prepare_enable(dd
->gclk
);
422 atmel_classd_codec_dai_shutdown(struct snd_pcm_substream
*substream
,
423 struct snd_soc_dai
*codec_dai
)
425 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
426 struct atmel_classd
*dd
= snd_soc_card_get_drvdata(rtd
->card
);
428 clk_disable_unprepare(dd
->gclk
);
429 clk_disable_unprepare(dd
->aclk
);
432 static int atmel_classd_codec_dai_prepare(struct snd_pcm_substream
*substream
,
433 struct snd_soc_dai
*codec_dai
)
435 struct snd_soc_codec
*codec
= codec_dai
->codec
;
437 snd_soc_update_bits(codec
, CLASSD_MR
,
438 CLASSD_MR_LEN_MASK
| CLASSD_MR_REN_MASK
,
439 (CLASSD_MR_LEN_DIS
<< CLASSD_MR_LEN_SHIFT
)
440 |(CLASSD_MR_REN_DIS
<< CLASSD_MR_REN_SHIFT
));
445 static int atmel_classd_codec_dai_trigger(struct snd_pcm_substream
*substream
,
446 int cmd
, struct snd_soc_dai
*codec_dai
)
448 struct snd_soc_codec
*codec
= codec_dai
->codec
;
451 mask
= CLASSD_MR_LEN_MASK
| CLASSD_MR_REN_MASK
;
454 case SNDRV_PCM_TRIGGER_START
:
455 case SNDRV_PCM_TRIGGER_RESUME
:
456 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
459 case SNDRV_PCM_TRIGGER_STOP
:
460 case SNDRV_PCM_TRIGGER_SUSPEND
:
461 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
462 val
= (CLASSD_MR_LEN_DIS
<< CLASSD_MR_LEN_SHIFT
)
463 | (CLASSD_MR_REN_DIS
<< CLASSD_MR_REN_SHIFT
);
469 snd_soc_update_bits(codec
, CLASSD_MR
, mask
, val
);
474 static const struct snd_soc_dai_ops atmel_classd_codec_dai_ops
= {
475 .digital_mute
= atmel_classd_codec_dai_digital_mute
,
476 .startup
= atmel_classd_codec_dai_startup
,
477 .shutdown
= atmel_classd_codec_dai_shutdown
,
478 .hw_params
= atmel_classd_codec_dai_hw_params
,
479 .prepare
= atmel_classd_codec_dai_prepare
,
480 .trigger
= atmel_classd_codec_dai_trigger
,
483 #define ATMEL_CLASSD_CODEC_DAI_NAME "atmel-classd-hifi"
485 static struct snd_soc_dai_driver atmel_classd_codec_dai
= {
486 .name
= ATMEL_CLASSD_CODEC_DAI_NAME
,
488 .stream_name
= "Playback",
491 .rates
= ATMEL_CLASSD_RATES
,
492 .formats
= SNDRV_PCM_FMTBIT_S16_LE
,
494 .ops
= &atmel_classd_codec_dai_ops
,
497 /* ASoC sound card */
498 static int atmel_classd_asoc_card_init(struct device
*dev
,
499 struct snd_soc_card
*card
)
501 struct snd_soc_dai_link
*dai_link
;
502 struct atmel_classd
*dd
= snd_soc_card_get_drvdata(card
);
504 dai_link
= devm_kzalloc(dev
, sizeof(*dai_link
), GFP_KERNEL
);
508 dai_link
->name
= "CLASSD";
509 dai_link
->stream_name
= "CLASSD PCM";
510 dai_link
->codec_dai_name
= ATMEL_CLASSD_CODEC_DAI_NAME
;
511 dai_link
->cpu_dai_name
= dev_name(dev
);
512 dai_link
->codec_name
= dev_name(dev
);
513 dai_link
->platform_name
= dev_name(dev
);
515 card
->dai_link
= dai_link
;
517 card
->name
= dd
->pdata
->card_name
;
523 /* regmap configuration */
524 static const struct reg_default atmel_classd_reg_defaults
[] = {
525 { CLASSD_INTPMR
, 0x00301212 },
528 #define ATMEL_CLASSD_REG_MAX 0xE4
529 static const struct regmap_config atmel_classd_regmap_config
= {
533 .max_register
= ATMEL_CLASSD_REG_MAX
,
535 .cache_type
= REGCACHE_FLAT
,
536 .reg_defaults
= atmel_classd_reg_defaults
,
537 .num_reg_defaults
= ARRAY_SIZE(atmel_classd_reg_defaults
),
540 static int atmel_classd_probe(struct platform_device
*pdev
)
542 struct device
*dev
= &pdev
->dev
;
543 struct atmel_classd
*dd
;
544 struct resource
*res
;
545 void __iomem
*io_base
;
546 const struct atmel_classd_pdata
*pdata
;
547 struct snd_soc_card
*card
;
550 pdata
= dev_get_platdata(dev
);
552 pdata
= atmel_classd_dt_init(dev
);
554 return PTR_ERR(pdata
);
557 dd
= devm_kzalloc(dev
, sizeof(*dd
), GFP_KERNEL
);
563 dd
->irq
= platform_get_irq(pdev
, 0);
566 dev_err(dev
, "failed to could not get irq: %d\n", ret
);
570 dd
->pclk
= devm_clk_get(dev
, "pclk");
571 if (IS_ERR(dd
->pclk
)) {
572 ret
= PTR_ERR(dd
->pclk
);
573 dev_err(dev
, "failed to get peripheral clock: %d\n", ret
);
577 dd
->gclk
= devm_clk_get(dev
, "gclk");
578 if (IS_ERR(dd
->gclk
)) {
579 ret
= PTR_ERR(dd
->gclk
);
580 dev_err(dev
, "failed to get GCK clock: %d\n", ret
);
584 dd
->aclk
= devm_clk_get(dev
, "aclk");
585 if (IS_ERR(dd
->aclk
)) {
586 ret
= PTR_ERR(dd
->aclk
);
587 dev_err(dev
, "failed to get audio clock: %d\n", ret
);
591 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
593 dev_err(dev
, "no memory resource\n");
597 io_base
= devm_ioremap_resource(dev
, res
);
598 if (IS_ERR(io_base
)) {
599 ret
= PTR_ERR(io_base
);
600 dev_err(dev
, "failed to remap register memory: %d\n", ret
);
604 dd
->phy_base
= res
->start
;
606 dd
->regmap
= devm_regmap_init_mmio(dev
, io_base
,
607 &atmel_classd_regmap_config
);
608 if (IS_ERR(dd
->regmap
)) {
609 ret
= PTR_ERR(dd
->regmap
);
610 dev_err(dev
, "failed to init register map: %d\n", ret
);
614 ret
= devm_snd_soc_register_component(dev
,
615 &atmel_classd_cpu_dai_component
,
616 &atmel_classd_cpu_dai
, 1);
618 dev_err(dev
, "could not register CPU DAI: %d\n", ret
);
622 ret
= devm_snd_dmaengine_pcm_register(dev
,
623 &atmel_classd_dmaengine_pcm_config
,
626 dev_err(dev
, "could not register platform: %d\n", ret
);
630 ret
= snd_soc_register_codec(dev
, &soc_codec_dev_classd
,
631 &atmel_classd_codec_dai
, 1);
633 dev_err(dev
, "could not register codec: %d\n", ret
);
637 /* register sound card */
638 card
= devm_kzalloc(dev
, sizeof(*card
), GFP_KERNEL
);
642 snd_soc_card_set_drvdata(card
, dd
);
643 platform_set_drvdata(pdev
, card
);
645 ret
= atmel_classd_asoc_card_init(dev
, card
);
647 dev_err(dev
, "failed to init sound card\n");
651 ret
= devm_snd_soc_register_card(dev
, card
);
653 dev_err(dev
, "failed to register sound card: %d\n", ret
);
660 static int atmel_classd_remove(struct platform_device
*pdev
)
662 snd_soc_unregister_codec(&pdev
->dev
);
666 static struct platform_driver atmel_classd_driver
= {
668 .name
= "atmel-classd",
669 .of_match_table
= of_match_ptr(atmel_classd_of_match
),
670 .pm
= &snd_soc_pm_ops
,
672 .probe
= atmel_classd_probe
,
673 .remove
= atmel_classd_remove
,
675 module_platform_driver(atmel_classd_driver
);
677 MODULE_DESCRIPTION("Atmel ClassD driver under ALSA SoC architecture");
678 MODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>");
679 MODULE_LICENSE("GPL");