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 if (params_channels(params
) == 1)
175 slave_config
->dst_addr_width
= DMA_SLAVE_BUSWIDTH_2_BYTES
;
177 slave_config
->dst_addr_width
= DMA_SLAVE_BUSWIDTH_4_BYTES
;
179 slave_config
->direction
= DMA_MEM_TO_DEV
;
180 slave_config
->dst_addr
= dd
->phy_base
+ CLASSD_THR
;
181 slave_config
->dst_maxburst
= 1;
182 slave_config
->src_maxburst
= 1;
183 slave_config
->device_fc
= false;
188 static const struct snd_dmaengine_pcm_config
189 atmel_classd_dmaengine_pcm_config
= {
190 .prepare_slave_config
= atmel_classd_platform_configure_dma
,
191 .pcm_hardware
= &atmel_classd_hw
,
192 .prealloc_buffer_size
= ATMEL_CLASSD_PREALLOC_BUF_SIZE
,
196 static const char * const mono_mode_text
[] = {
197 "mix", "sat", "left", "right"
200 static SOC_ENUM_SINGLE_DECL(classd_mono_mode_enum
,
201 CLASSD_INTPMR
, CLASSD_INTPMR_MONO_MODE_SHIFT
,
204 static const char * const eqcfg_text
[] = {
205 "Treble-12dB", "Treble-6dB",
206 "Medium-8dB", "Medium-3dB",
207 "Bass-12dB", "Bass-6dB",
209 "Bass+6dB", "Bass+12dB",
210 "Medium+3dB", "Medium+8dB",
211 "Treble+6dB", "Treble+12dB",
214 static const unsigned int eqcfg_value
[] = {
215 CLASSD_INTPMR_EQCFG_T_CUT_12
, CLASSD_INTPMR_EQCFG_T_CUT_6
,
216 CLASSD_INTPMR_EQCFG_M_CUT_8
, CLASSD_INTPMR_EQCFG_M_CUT_3
,
217 CLASSD_INTPMR_EQCFG_B_CUT_12
, CLASSD_INTPMR_EQCFG_B_CUT_6
,
218 CLASSD_INTPMR_EQCFG_FLAT
,
219 CLASSD_INTPMR_EQCFG_B_BOOST_6
, CLASSD_INTPMR_EQCFG_B_BOOST_12
,
220 CLASSD_INTPMR_EQCFG_M_BOOST_3
, CLASSD_INTPMR_EQCFG_M_BOOST_8
,
221 CLASSD_INTPMR_EQCFG_T_BOOST_6
, CLASSD_INTPMR_EQCFG_T_BOOST_12
,
224 static SOC_VALUE_ENUM_SINGLE_DECL(classd_eqcfg_enum
,
225 CLASSD_INTPMR
, CLASSD_INTPMR_EQCFG_SHIFT
, 0xf,
226 eqcfg_text
, eqcfg_value
);
228 static const DECLARE_TLV_DB_SCALE(classd_digital_tlv
, -7800, 100, 1);
230 static const struct snd_kcontrol_new atmel_classd_snd_controls
[] = {
231 SOC_DOUBLE_TLV("Playback Volume", CLASSD_INTPMR
,
232 CLASSD_INTPMR_ATTL_SHIFT
, CLASSD_INTPMR_ATTR_SHIFT
,
233 78, 1, classd_digital_tlv
),
235 SOC_SINGLE("Deemphasis Switch", CLASSD_INTPMR
,
236 CLASSD_INTPMR_DEEMP_SHIFT
, 1, 0),
238 SOC_SINGLE("Mono Switch", CLASSD_INTPMR
, CLASSD_INTPMR_MONO_SHIFT
, 1, 0),
240 SOC_SINGLE("Swap Switch", CLASSD_INTPMR
, CLASSD_INTPMR_SWAP_SHIFT
, 1, 0),
242 SOC_ENUM("Mono Mode", classd_mono_mode_enum
),
244 SOC_ENUM("EQ", classd_eqcfg_enum
),
247 static const char * const pwm_type
[] = {
248 "Single ended", "Differential"
251 static int atmel_classd_codec_probe(struct snd_soc_codec
*codec
)
253 struct snd_soc_card
*card
= snd_soc_codec_get_drvdata(codec
);
254 struct atmel_classd
*dd
= snd_soc_card_get_drvdata(card
);
255 const struct atmel_classd_pdata
*pdata
= dd
->pdata
;
258 mask
= CLASSD_MR_PWMTYP_MASK
;
259 val
= pdata
->pwm_type
<< CLASSD_MR_PWMTYP_SHIFT
;
261 mask
|= CLASSD_MR_NON_OVERLAP_MASK
;
262 if (pdata
->non_overlap_enable
) {
263 val
|= (CLASSD_MR_NON_OVERLAP_EN
264 << CLASSD_MR_NON_OVERLAP_SHIFT
);
266 mask
|= CLASSD_MR_NOVR_VAL_MASK
;
267 switch (pdata
->non_overlap_time
) {
269 val
|= (CLASSD_MR_NOVR_VAL_5NS
270 << CLASSD_MR_NOVR_VAL_SHIFT
);
273 val
|= (CLASSD_MR_NOVR_VAL_10NS
274 << CLASSD_MR_NOVR_VAL_SHIFT
);
277 val
|= (CLASSD_MR_NOVR_VAL_15NS
278 << CLASSD_MR_NOVR_VAL_SHIFT
);
281 val
|= (CLASSD_MR_NOVR_VAL_20NS
282 << CLASSD_MR_NOVR_VAL_SHIFT
);
285 val
|= (CLASSD_MR_NOVR_VAL_10NS
286 << CLASSD_MR_NOVR_VAL_SHIFT
);
288 "non-overlapping value %d is invalid, the default value 10 is specified\n",
289 pdata
->non_overlap_time
);
294 snd_soc_update_bits(codec
, CLASSD_MR
, mask
, val
);
297 "PWM modulation type is %s, non-overlapping is %s\n",
298 pwm_type
[pdata
->pwm_type
],
299 pdata
->non_overlap_enable
?"enabled":"disabled");
304 static struct regmap
*atmel_classd_codec_get_remap(struct device
*dev
)
306 return dev_get_regmap(dev
, NULL
);
309 static struct snd_soc_codec_driver soc_codec_dev_classd
= {
310 .probe
= atmel_classd_codec_probe
,
311 .get_regmap
= atmel_classd_codec_get_remap
,
312 .component_driver
= {
313 .controls
= atmel_classd_snd_controls
,
314 .num_controls
= ARRAY_SIZE(atmel_classd_snd_controls
),
318 /* codec dai component */
319 static int atmel_classd_codec_dai_startup(struct snd_pcm_substream
*substream
,
320 struct snd_soc_dai
*codec_dai
)
322 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
323 struct atmel_classd
*dd
= snd_soc_card_get_drvdata(rtd
->card
);
326 ret
= clk_prepare_enable(dd
->aclk
);
330 return clk_prepare_enable(dd
->gclk
);
333 static int atmel_classd_codec_dai_digital_mute(struct snd_soc_dai
*codec_dai
,
336 struct snd_soc_codec
*codec
= codec_dai
->codec
;
339 mask
= CLASSD_MR_LMUTE_MASK
| CLASSD_MR_RMUTE_MASK
;
346 snd_soc_update_bits(codec
, CLASSD_MR
, mask
, val
);
351 #define CLASSD_ACLK_RATE_11M2896_MPY_8 (112896 * 100 * 8)
352 #define CLASSD_ACLK_RATE_12M288_MPY_8 (12288 * 1000 * 8)
358 unsigned long aclk_rate
;
359 } const sample_rates
[] = {
360 { 8000, CLASSD_INTPMR_FRAME_8K
,
361 CLASSD_INTPMR_DSP_CLK_FREQ_12M288
, CLASSD_ACLK_RATE_12M288_MPY_8
},
362 { 16000, CLASSD_INTPMR_FRAME_16K
,
363 CLASSD_INTPMR_DSP_CLK_FREQ_12M288
, CLASSD_ACLK_RATE_12M288_MPY_8
},
364 { 32000, CLASSD_INTPMR_FRAME_32K
,
365 CLASSD_INTPMR_DSP_CLK_FREQ_12M288
, CLASSD_ACLK_RATE_12M288_MPY_8
},
366 { 48000, CLASSD_INTPMR_FRAME_48K
,
367 CLASSD_INTPMR_DSP_CLK_FREQ_12M288
, CLASSD_ACLK_RATE_12M288_MPY_8
},
368 { 96000, CLASSD_INTPMR_FRAME_96K
,
369 CLASSD_INTPMR_DSP_CLK_FREQ_12M288
, CLASSD_ACLK_RATE_12M288_MPY_8
},
370 { 22050, CLASSD_INTPMR_FRAME_22K
,
371 CLASSD_INTPMR_DSP_CLK_FREQ_11M2896
, CLASSD_ACLK_RATE_11M2896_MPY_8
},
372 { 44100, CLASSD_INTPMR_FRAME_44K
,
373 CLASSD_INTPMR_DSP_CLK_FREQ_11M2896
, CLASSD_ACLK_RATE_11M2896_MPY_8
},
374 { 88200, CLASSD_INTPMR_FRAME_88K
,
375 CLASSD_INTPMR_DSP_CLK_FREQ_11M2896
, CLASSD_ACLK_RATE_11M2896_MPY_8
},
379 atmel_classd_codec_dai_hw_params(struct snd_pcm_substream
*substream
,
380 struct snd_pcm_hw_params
*params
,
381 struct snd_soc_dai
*codec_dai
)
383 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
384 struct atmel_classd
*dd
= snd_soc_card_get_drvdata(rtd
->card
);
385 struct snd_soc_codec
*codec
= codec_dai
->codec
;
387 int i
, best
, best_val
, cur_val
, ret
;
390 fs
= params_rate(params
);
393 best_val
= abs(fs
- sample_rates
[0].rate
);
394 for (i
= 1; i
< ARRAY_SIZE(sample_rates
); i
++) {
396 cur_val
= abs(fs
- sample_rates
[i
].rate
);
397 if (cur_val
< best_val
) {
404 "Selected SAMPLE_RATE of %dHz, ACLK_RATE of %ldHz\n",
405 sample_rates
[best
].rate
, sample_rates
[best
].aclk_rate
);
407 clk_disable_unprepare(dd
->gclk
);
408 clk_disable_unprepare(dd
->aclk
);
410 ret
= clk_set_rate(dd
->aclk
, sample_rates
[best
].aclk_rate
);
414 mask
= CLASSD_INTPMR_DSP_CLK_FREQ_MASK
| CLASSD_INTPMR_FRAME_MASK
;
415 val
= (sample_rates
[best
].dsp_clk
<< CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT
)
416 | (sample_rates
[best
].sample_rate
<< CLASSD_INTPMR_FRAME_SHIFT
);
418 snd_soc_update_bits(codec
, CLASSD_INTPMR
, mask
, val
);
420 ret
= clk_prepare_enable(dd
->aclk
);
424 return clk_prepare_enable(dd
->gclk
);
428 atmel_classd_codec_dai_shutdown(struct snd_pcm_substream
*substream
,
429 struct snd_soc_dai
*codec_dai
)
431 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
432 struct atmel_classd
*dd
= snd_soc_card_get_drvdata(rtd
->card
);
434 clk_disable_unprepare(dd
->gclk
);
435 clk_disable_unprepare(dd
->aclk
);
438 static int atmel_classd_codec_dai_prepare(struct snd_pcm_substream
*substream
,
439 struct snd_soc_dai
*codec_dai
)
441 struct snd_soc_codec
*codec
= codec_dai
->codec
;
443 snd_soc_update_bits(codec
, CLASSD_MR
,
444 CLASSD_MR_LEN_MASK
| CLASSD_MR_REN_MASK
,
445 (CLASSD_MR_LEN_DIS
<< CLASSD_MR_LEN_SHIFT
)
446 |(CLASSD_MR_REN_DIS
<< CLASSD_MR_REN_SHIFT
));
451 static int atmel_classd_codec_dai_trigger(struct snd_pcm_substream
*substream
,
452 int cmd
, struct snd_soc_dai
*codec_dai
)
454 struct snd_soc_codec
*codec
= codec_dai
->codec
;
457 mask
= CLASSD_MR_LEN_MASK
| CLASSD_MR_REN_MASK
;
460 case SNDRV_PCM_TRIGGER_START
:
461 case SNDRV_PCM_TRIGGER_RESUME
:
462 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
465 case SNDRV_PCM_TRIGGER_STOP
:
466 case SNDRV_PCM_TRIGGER_SUSPEND
:
467 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
468 val
= (CLASSD_MR_LEN_DIS
<< CLASSD_MR_LEN_SHIFT
)
469 | (CLASSD_MR_REN_DIS
<< CLASSD_MR_REN_SHIFT
);
475 snd_soc_update_bits(codec
, CLASSD_MR
, mask
, val
);
480 static const struct snd_soc_dai_ops atmel_classd_codec_dai_ops
= {
481 .digital_mute
= atmel_classd_codec_dai_digital_mute
,
482 .startup
= atmel_classd_codec_dai_startup
,
483 .shutdown
= atmel_classd_codec_dai_shutdown
,
484 .hw_params
= atmel_classd_codec_dai_hw_params
,
485 .prepare
= atmel_classd_codec_dai_prepare
,
486 .trigger
= atmel_classd_codec_dai_trigger
,
489 #define ATMEL_CLASSD_CODEC_DAI_NAME "atmel-classd-hifi"
491 static struct snd_soc_dai_driver atmel_classd_codec_dai
= {
492 .name
= ATMEL_CLASSD_CODEC_DAI_NAME
,
494 .stream_name
= "Playback",
497 .rates
= ATMEL_CLASSD_RATES
,
498 .formats
= SNDRV_PCM_FMTBIT_S16_LE
,
500 .ops
= &atmel_classd_codec_dai_ops
,
503 /* ASoC sound card */
504 static int atmel_classd_asoc_card_init(struct device
*dev
,
505 struct snd_soc_card
*card
)
507 struct snd_soc_dai_link
*dai_link
;
508 struct atmel_classd
*dd
= snd_soc_card_get_drvdata(card
);
510 dai_link
= devm_kzalloc(dev
, sizeof(*dai_link
), GFP_KERNEL
);
514 dai_link
->name
= "CLASSD";
515 dai_link
->stream_name
= "CLASSD PCM";
516 dai_link
->codec_dai_name
= ATMEL_CLASSD_CODEC_DAI_NAME
;
517 dai_link
->cpu_dai_name
= dev_name(dev
);
518 dai_link
->codec_name
= dev_name(dev
);
519 dai_link
->platform_name
= dev_name(dev
);
521 card
->dai_link
= dai_link
;
523 card
->name
= dd
->pdata
->card_name
;
529 /* regmap configuration */
530 static const struct reg_default atmel_classd_reg_defaults
[] = {
531 { CLASSD_INTPMR
, 0x00301212 },
534 #define ATMEL_CLASSD_REG_MAX 0xE4
535 static const struct regmap_config atmel_classd_regmap_config
= {
539 .max_register
= ATMEL_CLASSD_REG_MAX
,
541 .cache_type
= REGCACHE_FLAT
,
542 .reg_defaults
= atmel_classd_reg_defaults
,
543 .num_reg_defaults
= ARRAY_SIZE(atmel_classd_reg_defaults
),
546 static int atmel_classd_probe(struct platform_device
*pdev
)
548 struct device
*dev
= &pdev
->dev
;
549 struct atmel_classd
*dd
;
550 struct resource
*res
;
551 void __iomem
*io_base
;
552 const struct atmel_classd_pdata
*pdata
;
553 struct snd_soc_card
*card
;
556 pdata
= dev_get_platdata(dev
);
558 pdata
= atmel_classd_dt_init(dev
);
560 return PTR_ERR(pdata
);
563 dd
= devm_kzalloc(dev
, sizeof(*dd
), GFP_KERNEL
);
569 dd
->irq
= platform_get_irq(pdev
, 0);
572 dev_err(dev
, "failed to could not get irq: %d\n", ret
);
576 dd
->pclk
= devm_clk_get(dev
, "pclk");
577 if (IS_ERR(dd
->pclk
)) {
578 ret
= PTR_ERR(dd
->pclk
);
579 dev_err(dev
, "failed to get peripheral clock: %d\n", ret
);
583 dd
->gclk
= devm_clk_get(dev
, "gclk");
584 if (IS_ERR(dd
->gclk
)) {
585 ret
= PTR_ERR(dd
->gclk
);
586 dev_err(dev
, "failed to get GCK clock: %d\n", ret
);
590 dd
->aclk
= devm_clk_get(dev
, "aclk");
591 if (IS_ERR(dd
->aclk
)) {
592 ret
= PTR_ERR(dd
->aclk
);
593 dev_err(dev
, "failed to get audio clock: %d\n", ret
);
597 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
598 io_base
= devm_ioremap_resource(dev
, res
);
599 if (IS_ERR(io_base
)) {
600 ret
= PTR_ERR(io_base
);
601 dev_err(dev
, "failed to remap register memory: %d\n", ret
);
605 dd
->phy_base
= res
->start
;
607 dd
->regmap
= devm_regmap_init_mmio(dev
, io_base
,
608 &atmel_classd_regmap_config
);
609 if (IS_ERR(dd
->regmap
)) {
610 ret
= PTR_ERR(dd
->regmap
);
611 dev_err(dev
, "failed to init register map: %d\n", ret
);
615 ret
= devm_snd_soc_register_component(dev
,
616 &atmel_classd_cpu_dai_component
,
617 &atmel_classd_cpu_dai
, 1);
619 dev_err(dev
, "could not register CPU DAI: %d\n", ret
);
623 ret
= devm_snd_dmaengine_pcm_register(dev
,
624 &atmel_classd_dmaengine_pcm_config
,
627 dev_err(dev
, "could not register platform: %d\n", ret
);
631 ret
= snd_soc_register_codec(dev
, &soc_codec_dev_classd
,
632 &atmel_classd_codec_dai
, 1);
634 dev_err(dev
, "could not register codec: %d\n", ret
);
638 /* register sound card */
639 card
= devm_kzalloc(dev
, sizeof(*card
), GFP_KERNEL
);
642 goto unregister_codec
;
645 snd_soc_card_set_drvdata(card
, dd
);
646 platform_set_drvdata(pdev
, card
);
648 ret
= atmel_classd_asoc_card_init(dev
, card
);
650 dev_err(dev
, "failed to init sound card\n");
651 goto unregister_codec
;
654 ret
= devm_snd_soc_register_card(dev
, card
);
656 dev_err(dev
, "failed to register sound card: %d\n", ret
);
657 goto unregister_codec
;
663 snd_soc_unregister_codec(dev
);
667 static int atmel_classd_remove(struct platform_device
*pdev
)
669 snd_soc_unregister_codec(&pdev
->dev
);
673 static struct platform_driver atmel_classd_driver
= {
675 .name
= "atmel-classd",
676 .of_match_table
= of_match_ptr(atmel_classd_of_match
),
677 .pm
= &snd_soc_pm_ops
,
679 .probe
= atmel_classd_probe
,
680 .remove
= atmel_classd_remove
,
682 module_platform_driver(atmel_classd_driver
);
684 MODULE_DESCRIPTION("Atmel ClassD driver under ALSA SoC architecture");
685 MODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>");
686 MODULE_LICENSE("GPL");