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 .controls
= atmel_classd_snd_controls
,
312 .num_controls
= ARRAY_SIZE(atmel_classd_snd_controls
),
313 .get_regmap
= atmel_classd_codec_get_remap
,
316 /* codec dai component */
317 static int atmel_classd_codec_dai_startup(struct snd_pcm_substream
*substream
,
318 struct snd_soc_dai
*codec_dai
)
320 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
321 struct atmel_classd
*dd
= snd_soc_card_get_drvdata(rtd
->card
);
324 ret
= clk_prepare_enable(dd
->aclk
);
328 return clk_prepare_enable(dd
->gclk
);
331 static int atmel_classd_codec_dai_digital_mute(struct snd_soc_dai
*codec_dai
,
334 struct snd_soc_codec
*codec
= codec_dai
->codec
;
337 mask
= CLASSD_MR_LMUTE_MASK
| CLASSD_MR_RMUTE_MASK
;
344 snd_soc_update_bits(codec
, CLASSD_MR
, mask
, val
);
349 #define CLASSD_ACLK_RATE_11M2896_MPY_8 (112896 * 100 * 8)
350 #define CLASSD_ACLK_RATE_12M288_MPY_8 (12228 * 1000 * 8)
356 unsigned long aclk_rate
;
357 } const sample_rates
[] = {
358 { 8000, CLASSD_INTPMR_FRAME_8K
,
359 CLASSD_INTPMR_DSP_CLK_FREQ_12M288
, CLASSD_ACLK_RATE_12M288_MPY_8
},
360 { 16000, CLASSD_INTPMR_FRAME_16K
,
361 CLASSD_INTPMR_DSP_CLK_FREQ_12M288
, CLASSD_ACLK_RATE_12M288_MPY_8
},
362 { 32000, CLASSD_INTPMR_FRAME_32K
,
363 CLASSD_INTPMR_DSP_CLK_FREQ_12M288
, CLASSD_ACLK_RATE_12M288_MPY_8
},
364 { 48000, CLASSD_INTPMR_FRAME_48K
,
365 CLASSD_INTPMR_DSP_CLK_FREQ_12M288
, CLASSD_ACLK_RATE_12M288_MPY_8
},
366 { 96000, CLASSD_INTPMR_FRAME_96K
,
367 CLASSD_INTPMR_DSP_CLK_FREQ_12M288
, CLASSD_ACLK_RATE_12M288_MPY_8
},
368 { 22050, CLASSD_INTPMR_FRAME_22K
,
369 CLASSD_INTPMR_DSP_CLK_FREQ_11M2896
, CLASSD_ACLK_RATE_11M2896_MPY_8
},
370 { 44100, CLASSD_INTPMR_FRAME_44K
,
371 CLASSD_INTPMR_DSP_CLK_FREQ_11M2896
, CLASSD_ACLK_RATE_11M2896_MPY_8
},
372 { 88200, CLASSD_INTPMR_FRAME_88K
,
373 CLASSD_INTPMR_DSP_CLK_FREQ_11M2896
, CLASSD_ACLK_RATE_11M2896_MPY_8
},
377 atmel_classd_codec_dai_hw_params(struct snd_pcm_substream
*substream
,
378 struct snd_pcm_hw_params
*params
,
379 struct snd_soc_dai
*codec_dai
)
381 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
382 struct atmel_classd
*dd
= snd_soc_card_get_drvdata(rtd
->card
);
383 struct snd_soc_codec
*codec
= codec_dai
->codec
;
385 int i
, best
, best_val
, cur_val
, ret
;
388 fs
= params_rate(params
);
391 best_val
= abs(fs
- sample_rates
[0].rate
);
392 for (i
= 1; i
< ARRAY_SIZE(sample_rates
); i
++) {
394 cur_val
= abs(fs
- sample_rates
[i
].rate
);
395 if (cur_val
< best_val
) {
402 "Selected SAMPLE_RATE of %dHz, ACLK_RATE of %ldHz\n",
403 sample_rates
[best
].rate
, sample_rates
[best
].aclk_rate
);
405 clk_disable_unprepare(dd
->gclk
);
406 clk_disable_unprepare(dd
->aclk
);
408 ret
= clk_set_rate(dd
->aclk
, sample_rates
[best
].aclk_rate
);
412 mask
= CLASSD_INTPMR_DSP_CLK_FREQ_MASK
| CLASSD_INTPMR_FRAME_MASK
;
413 val
= (sample_rates
[best
].dsp_clk
<< CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT
)
414 | (sample_rates
[best
].sample_rate
<< CLASSD_INTPMR_FRAME_SHIFT
);
416 snd_soc_update_bits(codec
, CLASSD_INTPMR
, mask
, val
);
418 ret
= clk_prepare_enable(dd
->aclk
);
422 return clk_prepare_enable(dd
->gclk
);
426 atmel_classd_codec_dai_shutdown(struct snd_pcm_substream
*substream
,
427 struct snd_soc_dai
*codec_dai
)
429 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
430 struct atmel_classd
*dd
= snd_soc_card_get_drvdata(rtd
->card
);
432 clk_disable_unprepare(dd
->gclk
);
433 clk_disable_unprepare(dd
->aclk
);
436 static int atmel_classd_codec_dai_prepare(struct snd_pcm_substream
*substream
,
437 struct snd_soc_dai
*codec_dai
)
439 struct snd_soc_codec
*codec
= codec_dai
->codec
;
441 snd_soc_update_bits(codec
, CLASSD_MR
,
442 CLASSD_MR_LEN_MASK
| CLASSD_MR_REN_MASK
,
443 (CLASSD_MR_LEN_DIS
<< CLASSD_MR_LEN_SHIFT
)
444 |(CLASSD_MR_REN_DIS
<< CLASSD_MR_REN_SHIFT
));
449 static int atmel_classd_codec_dai_trigger(struct snd_pcm_substream
*substream
,
450 int cmd
, struct snd_soc_dai
*codec_dai
)
452 struct snd_soc_codec
*codec
= codec_dai
->codec
;
455 mask
= CLASSD_MR_LEN_MASK
| CLASSD_MR_REN_MASK
;
458 case SNDRV_PCM_TRIGGER_START
:
459 case SNDRV_PCM_TRIGGER_RESUME
:
460 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
463 case SNDRV_PCM_TRIGGER_STOP
:
464 case SNDRV_PCM_TRIGGER_SUSPEND
:
465 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
466 val
= (CLASSD_MR_LEN_DIS
<< CLASSD_MR_LEN_SHIFT
)
467 | (CLASSD_MR_REN_DIS
<< CLASSD_MR_REN_SHIFT
);
473 snd_soc_update_bits(codec
, CLASSD_MR
, mask
, val
);
478 static const struct snd_soc_dai_ops atmel_classd_codec_dai_ops
= {
479 .digital_mute
= atmel_classd_codec_dai_digital_mute
,
480 .startup
= atmel_classd_codec_dai_startup
,
481 .shutdown
= atmel_classd_codec_dai_shutdown
,
482 .hw_params
= atmel_classd_codec_dai_hw_params
,
483 .prepare
= atmel_classd_codec_dai_prepare
,
484 .trigger
= atmel_classd_codec_dai_trigger
,
487 #define ATMEL_CLASSD_CODEC_DAI_NAME "atmel-classd-hifi"
489 static struct snd_soc_dai_driver atmel_classd_codec_dai
= {
490 .name
= ATMEL_CLASSD_CODEC_DAI_NAME
,
492 .stream_name
= "Playback",
495 .rates
= ATMEL_CLASSD_RATES
,
496 .formats
= SNDRV_PCM_FMTBIT_S16_LE
,
498 .ops
= &atmel_classd_codec_dai_ops
,
501 /* ASoC sound card */
502 static int atmel_classd_asoc_card_init(struct device
*dev
,
503 struct snd_soc_card
*card
)
505 struct snd_soc_dai_link
*dai_link
;
506 struct atmel_classd
*dd
= snd_soc_card_get_drvdata(card
);
508 dai_link
= devm_kzalloc(dev
, sizeof(*dai_link
), GFP_KERNEL
);
512 dai_link
->name
= "CLASSD";
513 dai_link
->stream_name
= "CLASSD PCM";
514 dai_link
->codec_dai_name
= ATMEL_CLASSD_CODEC_DAI_NAME
;
515 dai_link
->cpu_dai_name
= dev_name(dev
);
516 dai_link
->codec_name
= dev_name(dev
);
517 dai_link
->platform_name
= dev_name(dev
);
519 card
->dai_link
= dai_link
;
521 card
->name
= dd
->pdata
->card_name
;
527 /* regmap configuration */
528 static const struct reg_default atmel_classd_reg_defaults
[] = {
529 { CLASSD_INTPMR
, 0x00301212 },
532 #define ATMEL_CLASSD_REG_MAX 0xE4
533 static const struct regmap_config atmel_classd_regmap_config
= {
537 .max_register
= ATMEL_CLASSD_REG_MAX
,
539 .cache_type
= REGCACHE_FLAT
,
540 .reg_defaults
= atmel_classd_reg_defaults
,
541 .num_reg_defaults
= ARRAY_SIZE(atmel_classd_reg_defaults
),
544 static int atmel_classd_probe(struct platform_device
*pdev
)
546 struct device
*dev
= &pdev
->dev
;
547 struct atmel_classd
*dd
;
548 struct resource
*res
;
549 void __iomem
*io_base
;
550 const struct atmel_classd_pdata
*pdata
;
551 struct snd_soc_card
*card
;
554 pdata
= dev_get_platdata(dev
);
556 pdata
= atmel_classd_dt_init(dev
);
558 return PTR_ERR(pdata
);
561 dd
= devm_kzalloc(dev
, sizeof(*dd
), GFP_KERNEL
);
567 dd
->irq
= platform_get_irq(pdev
, 0);
570 dev_err(dev
, "failed to could not get irq: %d\n", ret
);
574 dd
->pclk
= devm_clk_get(dev
, "pclk");
575 if (IS_ERR(dd
->pclk
)) {
576 ret
= PTR_ERR(dd
->pclk
);
577 dev_err(dev
, "failed to get peripheral clock: %d\n", ret
);
581 dd
->gclk
= devm_clk_get(dev
, "gclk");
582 if (IS_ERR(dd
->gclk
)) {
583 ret
= PTR_ERR(dd
->gclk
);
584 dev_err(dev
, "failed to get GCK clock: %d\n", ret
);
588 dd
->aclk
= devm_clk_get(dev
, "aclk");
589 if (IS_ERR(dd
->aclk
)) {
590 ret
= PTR_ERR(dd
->aclk
);
591 dev_err(dev
, "failed to get audio clock: %d\n", ret
);
595 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
597 dev_err(dev
, "no memory resource\n");
601 io_base
= devm_ioremap_resource(dev
, res
);
602 if (IS_ERR(io_base
)) {
603 ret
= PTR_ERR(io_base
);
604 dev_err(dev
, "failed to remap register memory: %d\n", ret
);
608 dd
->phy_base
= res
->start
;
610 dd
->regmap
= devm_regmap_init_mmio(dev
, io_base
,
611 &atmel_classd_regmap_config
);
612 if (IS_ERR(dd
->regmap
)) {
613 ret
= PTR_ERR(dd
->regmap
);
614 dev_err(dev
, "failed to init register map: %d\n", ret
);
618 ret
= devm_snd_soc_register_component(dev
,
619 &atmel_classd_cpu_dai_component
,
620 &atmel_classd_cpu_dai
, 1);
622 dev_err(dev
, "could not register CPU DAI: %d\n", ret
);
626 ret
= devm_snd_dmaengine_pcm_register(dev
,
627 &atmel_classd_dmaengine_pcm_config
,
630 dev_err(dev
, "could not register platform: %d\n", ret
);
634 ret
= snd_soc_register_codec(dev
, &soc_codec_dev_classd
,
635 &atmel_classd_codec_dai
, 1);
637 dev_err(dev
, "could not register codec: %d\n", ret
);
641 /* register sound card */
642 card
= devm_kzalloc(dev
, sizeof(*card
), GFP_KERNEL
);
645 goto unregister_codec
;
648 snd_soc_card_set_drvdata(card
, dd
);
649 platform_set_drvdata(pdev
, card
);
651 ret
= atmel_classd_asoc_card_init(dev
, card
);
653 dev_err(dev
, "failed to init sound card\n");
654 goto unregister_codec
;
657 ret
= devm_snd_soc_register_card(dev
, card
);
659 dev_err(dev
, "failed to register sound card: %d\n", ret
);
660 goto unregister_codec
;
666 snd_soc_unregister_codec(dev
);
670 static int atmel_classd_remove(struct platform_device
*pdev
)
672 snd_soc_unregister_codec(&pdev
->dev
);
676 static struct platform_driver atmel_classd_driver
= {
678 .name
= "atmel-classd",
679 .of_match_table
= of_match_ptr(atmel_classd_of_match
),
680 .pm
= &snd_soc_pm_ops
,
682 .probe
= atmel_classd_probe
,
683 .remove
= atmel_classd_remove
,
685 module_platform_driver(atmel_classd_driver
);
687 MODULE_DESCRIPTION("Atmel ClassD driver under ALSA SoC architecture");
688 MODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>");
689 MODULE_LICENSE("GPL");