1 // SPDX-License-Identifier: GPL-2.0
2 //
3 // rk3228_codec.c -- rk3228 ALSA Soc Audio driver
4 //
5 // Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
6
7 #include <linux/module.h>
8 #include <linux/device.h>
9 #include <linux/delay.h>
10 #include <linux/of.h>
11 #include <linux/of_gpio.h>
12 #include <linux/clk.h>
13 #include <linux/platform_device.h>
14 #include <linux/pm_runtime.h>
15 #include <linux/regmap.h>
16 #include <sound/pcm_params.h>
17 #include <sound/dmaengine_pcm.h>
18 #include "rk3228_codec.h"
19
20 /*
21 * volume setting
22 * 0: -39dB
23 * 26: 0dB
24 * 31: 6dB
25 * Step: 1.5dB
26 */
27 #define OUT_VOLUME (0x18)
28 #define INITIAL_FREQ (11289600)
29
30 struct rk3228_codec_priv {
31 struct regmap *regmap;
32 struct clk *mclk;
33 struct clk *pclk;
34 struct clk *sclk;
35 struct gpio_desc *spk_en_gpio;
36 int spk_depop_time; /* msec */
37 };
38
39 static const struct reg_default rk3228_codec_reg_defaults[] = {
40 { CODEC_RESET, 0x03 },
41 { DAC_INIT_CTRL1, 0x00 },
42 { DAC_INIT_CTRL2, 0x50 },
43 { DAC_INIT_CTRL3, 0x0e },
44 { DAC_PRECHARGE_CTRL, 0x01 },
45 { DAC_PWR_CTRL, 0x00 },
46 { DAC_CLK_CTRL, 0x00 },
47 { HPMIX_CTRL, 0x00 },
48 { HPOUT_CTRL, 0x00 },
49 { HPOUTL_GAIN_CTRL, 0x00 },
50 { HPOUTR_GAIN_CTRL, 0x00 },
51 { HPOUT_POP_CTRL, 0x11 },
52 };
53
rk3228_codec_reset(struct snd_soc_component * component)54 static int rk3228_codec_reset(struct snd_soc_component *component)
55 {
56 struct rk3228_codec_priv *rk3228 = snd_soc_component_get_drvdata(component);
57
58 regmap_write(rk3228->regmap, CODEC_RESET, 0);
59 mdelay(10);
60 regmap_write(rk3228->regmap, CODEC_RESET, 0x03);
61
62 return 0;
63 }
64
rk3228_set_dai_fmt(struct snd_soc_dai * dai,unsigned int fmt)65 static int rk3228_set_dai_fmt(struct snd_soc_dai *dai,
66 unsigned int fmt)
67 {
68 struct snd_soc_component *component = dai->component;
69 struct rk3228_codec_priv *rk3228 = snd_soc_component_get_drvdata(component);
70 unsigned int val = 0;
71
72 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
73 case SND_SOC_DAIFMT_CBS_CFS:
74 val |= PIN_DIRECTION_IN | DAC_I2S_MODE_SLAVE;
75 break;
76 case SND_SOC_DAIFMT_CBM_CFM:
77 val |= PIN_DIRECTION_OUT | DAC_I2S_MODE_MASTER;
78 break;
79 default:
80 return -EINVAL;
81 }
82
83 regmap_update_bits(rk3228->regmap, DAC_INIT_CTRL1,
84 PIN_DIRECTION_MASK | DAC_I2S_MODE_MASK, val);
85
86 val = 0;
87 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
88 case SND_SOC_DAIFMT_DSP_A:
89 case SND_SOC_DAIFMT_DSP_B:
90 val |= DAC_MODE_PCM;
91 break;
92 case SND_SOC_DAIFMT_I2S:
93 val |= DAC_MODE_I2S;
94 break;
95 case SND_SOC_DAIFMT_RIGHT_J:
96 val |= DAC_MODE_RJM;
97 break;
98 case SND_SOC_DAIFMT_LEFT_J:
99 val |= DAC_MODE_LJM;
100 break;
101 default:
102 return -EINVAL;
103 }
104
105 regmap_update_bits(rk3228->regmap, DAC_INIT_CTRL2,
106 DAC_MODE_MASK, val);
107 return 0;
108 }
109
rk3228_analog_output(struct rk3228_codec_priv * rk3228,int mute)110 static void rk3228_analog_output(struct rk3228_codec_priv *rk3228, int mute)
111 {
112 if (rk3228->spk_en_gpio)
113 gpiod_set_value(rk3228->spk_en_gpio, mute);
114 }
115
rk3228_digital_mute(struct snd_soc_dai * dai,int mute,int stream)116 static int rk3228_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
117 {
118 struct snd_soc_component *component = dai->component;
119 struct rk3228_codec_priv *rk3228 = snd_soc_component_get_drvdata(component);
120 unsigned int val = 0;
121
122 if (mute)
123 val = HPOUTL_MUTE | HPOUTR_MUTE;
124 else
125 val = HPOUTL_UNMUTE | HPOUTR_UNMUTE;
126
127 regmap_update_bits(rk3228->regmap, HPOUT_CTRL,
128 HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK, val);
129 return 0;
130 }
131
rk3228_codec_power_on(struct snd_soc_component * component,int wait_ms)132 static int rk3228_codec_power_on(struct snd_soc_component *component, int wait_ms)
133 {
134 struct rk3228_codec_priv *rk3228 = snd_soc_component_get_drvdata(component);
135
136 regmap_update_bits(rk3228->regmap, DAC_PRECHARGE_CTRL,
137 DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_PRECHARGE);
138 mdelay(10);
139 regmap_update_bits(rk3228->regmap, DAC_PRECHARGE_CTRL,
140 DAC_CHARGE_CURRENT_ALL_MASK,
141 DAC_CHARGE_CURRENT_ALL_ON);
142
143 mdelay(wait_ms);
144
145 return 0;
146 }
147
rk3228_codec_power_off(struct snd_soc_component * component,int wait_ms)148 static int rk3228_codec_power_off(struct snd_soc_component *component, int wait_ms)
149 {
150 struct rk3228_codec_priv *rk3228 = snd_soc_component_get_drvdata(component);
151
152 regmap_update_bits(rk3228->regmap, DAC_PRECHARGE_CTRL,
153 DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_DISCHARGE);
154 mdelay(10);
155 regmap_update_bits(rk3228->regmap, DAC_PRECHARGE_CTRL,
156 DAC_CHARGE_CURRENT_ALL_MASK,
157 DAC_CHARGE_CURRENT_ALL_ON);
158
159 mdelay(wait_ms);
160
161 return 0;
162 }
163
164 static struct rk3228_reg_msk_val playback_open_list[] = {
165 { DAC_PWR_CTRL, DAC_PWR_MASK, DAC_PWR_ON },
166 { DAC_PWR_CTRL, DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK,
167 DACL_PATH_REFV_ON | DACR_PATH_REFV_ON },
168 { DAC_PWR_CTRL, HPOUTL_ZERO_CROSSING_ON | HPOUTR_ZERO_CROSSING_ON,
169 HPOUTL_ZERO_CROSSING_ON | HPOUTR_ZERO_CROSSING_ON },
170 { HPOUT_POP_CTRL, HPOUTR_POP_MASK | HPOUTL_POP_MASK,
171 HPOUTR_POP_WORK | HPOUTL_POP_WORK },
172 { HPMIX_CTRL, HPMIXL_MASK | HPMIXR_MASK, HPMIXL_EN | HPMIXR_EN },
173 { HPMIX_CTRL, HPMIXL_INIT_MASK | HPMIXR_INIT_MASK,
174 HPMIXL_INIT_EN | HPMIXR_INIT_EN },
175 { HPOUT_CTRL, HPOUTL_MASK | HPOUTR_MASK, HPOUTL_EN | HPOUTR_EN },
176 { HPOUT_CTRL, HPOUTL_INIT_MASK | HPOUTR_INIT_MASK,
177 HPOUTL_INIT_EN | HPOUTR_INIT_EN },
178 { DAC_CLK_CTRL, DACL_REFV_MASK | DACR_REFV_MASK,
179 DACL_REFV_ON | DACR_REFV_ON },
180 { DAC_CLK_CTRL, DACL_CLK_MASK | DACR_CLK_MASK,
181 DACL_CLK_ON | DACR_CLK_ON },
182 { DAC_CLK_CTRL, DACL_MASK | DACR_MASK, DACL_ON | DACR_ON },
183 { DAC_CLK_CTRL, DACL_INIT_MASK | DACR_INIT_MASK,
184 DACL_INIT_ON | DACR_INIT_ON },
185 { DAC_SELECT, DACL_SELECT_MASK | DACR_SELECT_MASK,
186 DACL_SELECT | DACR_SELECT },
187 { HPMIX_CTRL, HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK,
188 HPMIXL_INIT2_EN | HPMIXR_INIT2_EN },
189 { HPOUT_CTRL, HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK,
190 HPOUTL_UNMUTE | HPOUTR_UNMUTE },
191 };
192
193 #define PLAYBACK_OPEN_LIST_LEN ARRAY_SIZE(playback_open_list)
194
rk3228_codec_open_playback(struct snd_soc_component * component)195 static int rk3228_codec_open_playback(struct snd_soc_component *component)
196 {
197 struct rk3228_codec_priv *rk3228 = snd_soc_component_get_drvdata(component);
198 int i = 0;
199
200 regmap_update_bits(rk3228->regmap, DAC_PRECHARGE_CTRL,
201 DAC_CHARGE_CURRENT_ALL_MASK,
202 DAC_CHARGE_CURRENT_I);
203
204 for (i = 0; i < PLAYBACK_OPEN_LIST_LEN; i++) {
205 regmap_update_bits(rk3228->regmap,
206 playback_open_list[i].reg,
207 playback_open_list[i].msk,
208 playback_open_list[i].val);
209 mdelay(1);
210 }
211
212 msleep(rk3228->spk_depop_time);
213 rk3228_analog_output(rk3228, 1);
214
215 regmap_update_bits(rk3228->regmap, HPOUTL_GAIN_CTRL,
216 HPOUTL_GAIN_MASK, OUT_VOLUME);
217 regmap_update_bits(rk3228->regmap, HPOUTR_GAIN_CTRL,
218 HPOUTR_GAIN_MASK, OUT_VOLUME);
219 return 0;
220 }
221
222 static struct rk3228_reg_msk_val playback_close_list[] = {
223 { HPMIX_CTRL, HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK,
224 HPMIXL_INIT2_DIS | HPMIXR_INIT2_DIS },
225 { DAC_SELECT, DACL_SELECT_MASK | DACR_SELECT_MASK,
226 DACL_DESELECT | DACR_DESELECT },
227 { HPOUT_CTRL, HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK,
228 HPOUTL_MUTE | HPOUTR_MUTE },
229 { HPOUT_CTRL, HPOUTL_INIT_MASK | HPOUTR_INIT_MASK,
230 HPOUTL_INIT_DIS | HPOUTR_INIT_DIS },
231 { HPOUT_CTRL, HPOUTL_MASK | HPOUTR_MASK, HPOUTL_DIS | HPOUTR_DIS },
232 { HPMIX_CTRL, HPMIXL_MASK | HPMIXR_MASK, HPMIXL_DIS | HPMIXR_DIS },
233 { DAC_CLK_CTRL, DACL_MASK | DACR_MASK, DACL_OFF | DACR_OFF },
234 { DAC_CLK_CTRL, DACL_CLK_MASK | DACR_CLK_MASK,
235 DACL_CLK_OFF | DACR_CLK_OFF },
236 { DAC_CLK_CTRL, DACL_REFV_MASK | DACR_REFV_MASK,
237 DACL_REFV_OFF | DACR_REFV_OFF },
238 { HPOUT_POP_CTRL, HPOUTR_POP_MASK | HPOUTL_POP_MASK,
239 HPOUTR_POP_XCHARGE | HPOUTL_POP_XCHARGE },
240 { DAC_PWR_CTRL, DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK,
241 DACL_PATH_REFV_OFF | DACR_PATH_REFV_OFF },
242 { DAC_PWR_CTRL, DAC_PWR_MASK, DAC_PWR_OFF },
243 { HPMIX_CTRL, HPMIXL_INIT_MASK | HPMIXR_INIT_MASK,
244 HPMIXL_INIT_DIS | HPMIXR_INIT_DIS },
245 { DAC_CLK_CTRL, DACL_INIT_MASK | DACR_INIT_MASK,
246 DACL_INIT_OFF | DACR_INIT_OFF },
247 };
248
249 #define PLAYBACK_CLOSE_LIST_LEN ARRAY_SIZE(playback_close_list)
250
rk3228_codec_close_playback(struct snd_soc_component * component)251 static int rk3228_codec_close_playback(struct snd_soc_component *component)
252 {
253 struct rk3228_codec_priv *rk3228 = snd_soc_component_get_drvdata(component);
254 int i = 0;
255
256 rk3228_analog_output(rk3228, 0);
257
258 regmap_update_bits(rk3228->regmap, HPOUTL_GAIN_CTRL,
259 HPOUTL_GAIN_MASK, 0);
260 regmap_update_bits(rk3228->regmap, HPOUTR_GAIN_CTRL,
261 HPOUTR_GAIN_MASK, 0);
262
263 for (i = 0; i < PLAYBACK_CLOSE_LIST_LEN; i++) {
264 regmap_update_bits(rk3228->regmap,
265 playback_close_list[i].reg,
266 playback_close_list[i].msk,
267 playback_close_list[i].val);
268 mdelay(1);
269 }
270
271 regmap_update_bits(rk3228->regmap, DAC_PRECHARGE_CTRL,
272 DAC_CHARGE_CURRENT_ALL_MASK,
273 DAC_CHARGE_CURRENT_I);
274 return 0;
275 }
276
rk3228_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)277 static int rk3228_hw_params(struct snd_pcm_substream *substream,
278 struct snd_pcm_hw_params *params,
279 struct snd_soc_dai *dai)
280 {
281 struct snd_soc_component *component = dai->component;
282 struct rk3228_codec_priv *rk3228 = snd_soc_component_get_drvdata(component);
283 unsigned int val = 0;
284
285 switch (params_format(params)) {
286 case SNDRV_PCM_FORMAT_S16_LE:
287 val |= DAC_VDL_16BITS;
288 break;
289 case SNDRV_PCM_FORMAT_S20_3LE:
290 val |= DAC_VDL_20BITS;
291 break;
292 case SNDRV_PCM_FORMAT_S24_LE:
293 val |= DAC_VDL_24BITS;
294 break;
295 case SNDRV_PCM_FORMAT_S32_LE:
296 val |= DAC_VDL_32BITS;
297 break;
298 default:
299 return -EINVAL;
300 }
301
302 regmap_update_bits(rk3228->regmap, DAC_INIT_CTRL2, DAC_VDL_MASK, val);
303 val = DAC_WL_32BITS | DAC_RST_DIS;
304 regmap_update_bits(rk3228->regmap, DAC_INIT_CTRL3,
305 DAC_WL_MASK | DAC_RST_MASK, val);
306
307 return 0;
308 }
309
rk3228_pcm_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)310 static int rk3228_pcm_startup(struct snd_pcm_substream *substream,
311 struct snd_soc_dai *dai)
312 {
313 struct snd_soc_component *component = dai->component;
314
315 return rk3228_codec_open_playback(component);
316 }
317
rk3228_pcm_shutdown(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)318 static void rk3228_pcm_shutdown(struct snd_pcm_substream *substream,
319 struct snd_soc_dai *dai)
320 {
321 struct snd_soc_component *component = dai->component;
322
323 rk3228_codec_close_playback(component);
324 }
325
326 static struct snd_soc_dai_ops rk3228_dai_ops = {
327 .hw_params = rk3228_hw_params,
328 .set_fmt = rk3228_set_dai_fmt,
329 .mute_stream = rk3228_digital_mute,
330 .startup = rk3228_pcm_startup,
331 .shutdown = rk3228_pcm_shutdown,
332 .no_capture_mute = 1,
333 };
334
335 static struct snd_soc_dai_driver rk3228_dai[] = {
336 {
337 .name = "rk3228-hifi",
338 .id = RK3228_HIFI,
339 .playback = {
340 .stream_name = "HIFI Playback",
341 .channels_min = 1,
342 .channels_max = 2,
343 .rates = SNDRV_PCM_RATE_8000_96000,
344 .formats = (SNDRV_PCM_FMTBIT_S16_LE |
345 SNDRV_PCM_FMTBIT_S20_3LE |
346 SNDRV_PCM_FMTBIT_S24_LE |
347 SNDRV_PCM_FMTBIT_S32_LE),
348 },
349 .capture = {
350 .stream_name = "HIFI Capture",
351 .channels_min = 2,
352 .channels_max = 8,
353 .rates = SNDRV_PCM_RATE_8000_96000,
354 .formats = (SNDRV_PCM_FMTBIT_S16_LE |
355 SNDRV_PCM_FMTBIT_S20_3LE |
356 SNDRV_PCM_FMTBIT_S24_LE |
357 SNDRV_PCM_FMTBIT_S32_LE),
358 },
359 .ops = &rk3228_dai_ops,
360 },
361 };
362
rk3228_codec_probe(struct snd_soc_component * component)363 static int rk3228_codec_probe(struct snd_soc_component *component)
364 {
365 rk3228_codec_reset(component);
366 rk3228_codec_power_on(component, 0);
367
368 return 0;
369 }
370
rk3228_codec_remove(struct snd_soc_component * component)371 static void rk3228_codec_remove(struct snd_soc_component *component)
372 {
373 rk3228_codec_close_playback(component);
374 rk3228_codec_power_off(component, 0);
375 }
376
377 static struct snd_soc_component_driver soc_codec_dev_rk3228 = {
378 .probe = rk3228_codec_probe,
379 .remove = rk3228_codec_remove,
380 };
381
rk3228_codec_write_read_reg(struct device * dev,unsigned int reg)382 static bool rk3228_codec_write_read_reg(struct device *dev, unsigned int reg)
383 {
384 switch (reg) {
385 case CODEC_RESET:
386 case DAC_INIT_CTRL1:
387 case DAC_INIT_CTRL2:
388 case DAC_INIT_CTRL3:
389 case DAC_PRECHARGE_CTRL:
390 case DAC_PWR_CTRL:
391 case DAC_CLK_CTRL:
392 case HPMIX_CTRL:
393 case DAC_SELECT:
394 case HPOUT_CTRL:
395 case HPOUTL_GAIN_CTRL:
396 case HPOUTR_GAIN_CTRL:
397 case HPOUT_POP_CTRL:
398 return true;
399 default:
400 return false;
401 }
402 }
403
rk3228_codec_volatile_reg(struct device * dev,unsigned int reg)404 static bool rk3228_codec_volatile_reg(struct device *dev, unsigned int reg)
405 {
406 switch (reg) {
407 case CODEC_RESET:
408 return true;
409 default:
410 return false;
411 }
412 }
413
414 static const struct regmap_config rk3228_codec_regmap_config = {
415 .reg_bits = 32,
416 .reg_stride = 4,
417 .val_bits = 32,
418 .max_register = HPOUT_POP_CTRL,
419 .writeable_reg = rk3228_codec_write_read_reg,
420 .readable_reg = rk3228_codec_write_read_reg,
421 .volatile_reg = rk3228_codec_volatile_reg,
422 .reg_defaults = rk3228_codec_reg_defaults,
423 .num_reg_defaults = ARRAY_SIZE(rk3228_codec_reg_defaults),
424 .cache_type = REGCACHE_FLAT,
425 };
426
427 #ifdef CONFIG_OF
428 static const struct of_device_id rk3228codec_of_match[] = {
429 { .compatible = "rockchip,rk3228-codec", },
430 {},
431 };
432 MODULE_DEVICE_TABLE(of, rk3228codec_of_match);
433 #endif
434
rk3228_platform_probe(struct platform_device * pdev)435 static int rk3228_platform_probe(struct platform_device *pdev)
436 {
437 struct device_node *rk3228_np = pdev->dev.of_node;
438 struct rk3228_codec_priv *rk3228;
439 struct resource *res;
440 void __iomem *base;
441 int ret = 0;
442
443 rk3228 = devm_kzalloc(&pdev->dev, sizeof(*rk3228), GFP_KERNEL);
444 if (!rk3228)
445 return -ENOMEM;
446
447 rk3228->mclk = devm_clk_get(&pdev->dev, "mclk");
448 if (PTR_ERR(rk3228->mclk) == -EPROBE_DEFER)
449 return -EPROBE_DEFER;
450
451 rk3228->pclk = devm_clk_get(&pdev->dev, "pclk");
452 if (IS_ERR(rk3228->pclk))
453 return PTR_ERR(rk3228->pclk);
454
455 rk3228->sclk = devm_clk_get(&pdev->dev, "sclk");
456 if (IS_ERR(rk3228->sclk))
457 return PTR_ERR(rk3228->sclk);
458
459 rk3228->spk_en_gpio = devm_gpiod_get_optional(&pdev->dev,
460 "spk-en",
461 GPIOD_OUT_LOW);
462 if (IS_ERR(rk3228->spk_en_gpio))
463 return PTR_ERR(rk3228->spk_en_gpio);
464
465 ret = of_property_read_u32(rk3228_np, "spk-depop-time-ms",
466 &rk3228->spk_depop_time);
467 if (ret < 0) {
468 dev_info(&pdev->dev, "spk_depop_time use default value.\n");
469 rk3228->spk_depop_time = 100;
470 }
471
472 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
473 base = devm_ioremap_resource(&pdev->dev, res);
474 if (IS_ERR(base))
475 return PTR_ERR(base);
476
477 ret = clk_prepare_enable(rk3228->mclk);
478 if (ret)
479 return ret;
480
481 ret = clk_prepare_enable(rk3228->pclk);
482 if (ret < 0)
483 goto err_pclk;
484
485 ret = clk_prepare_enable(rk3228->sclk);
486 if (ret)
487 goto err_sclk;
488
489 clk_set_rate(rk3228->sclk, INITIAL_FREQ);
490
491 rk3228->regmap = devm_regmap_init_mmio(&pdev->dev, base,
492 &rk3228_codec_regmap_config);
493 if (IS_ERR(rk3228->regmap)) {
494 ret = PTR_ERR(rk3228->regmap);
495 goto err_clk;
496 }
497
498 platform_set_drvdata(pdev, rk3228);
499
500 ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_rk3228,
501 rk3228_dai, ARRAY_SIZE(rk3228_dai));
502 if (!ret)
503 return 0;
504
505 err_clk:
506 clk_disable_unprepare(rk3228->sclk);
507 err_sclk:
508 clk_disable_unprepare(rk3228->pclk);
509 err_pclk:
510 clk_disable_unprepare(rk3228->mclk);
511
512 return ret;
513 }
514
rk3228_platform_remove(struct platform_device * pdev)515 static int rk3228_platform_remove(struct platform_device *pdev)
516 {
517 struct rk3228_codec_priv *rk3228 = platform_get_drvdata(pdev);
518
519 if (!IS_ERR(rk3228->mclk))
520 clk_disable_unprepare(rk3228->mclk);
521
522 if (!IS_ERR(rk3228->pclk))
523 clk_disable_unprepare(rk3228->pclk);
524
525 if (!IS_ERR(rk3228->sclk))
526 clk_disable_unprepare(rk3228->sclk);
527
528 return 0;
529 }
530
531 static struct platform_driver rk3228_codec_driver = {
532 .driver = {
533 .name = "rk3228-codec",
534 .of_match_table = of_match_ptr(rk3228codec_of_match),
535 },
536 .probe = rk3228_platform_probe,
537 .remove = rk3228_platform_remove,
538 };
539 module_platform_driver(rk3228_codec_driver);
540
541 MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>");
542 MODULE_DESCRIPTION("ASoC rk3228 codec driver");
543 MODULE_LICENSE("GPL v2");
544