1b545dd92SMark Brown /* 2b545dd92SMark Brown * Bells audio support 3b545dd92SMark Brown * 4b545dd92SMark Brown * Copyright 2012 Wolfson Microelectronics 5b545dd92SMark Brown * 6b545dd92SMark Brown * This program is free software; you can redistribute it and/or modify it 7b545dd92SMark Brown * under the terms of the GNU General Public License as published by the 8b545dd92SMark Brown * Free Software Foundation; either version 2 of the License, or (at your 9b545dd92SMark Brown * option) any later version. 10b545dd92SMark Brown */ 11b545dd92SMark Brown 12b545dd92SMark Brown #include <sound/soc.h> 13b545dd92SMark Brown #include <sound/soc-dapm.h> 14b545dd92SMark Brown #include <sound/jack.h> 15b545dd92SMark Brown #include <linux/gpio.h> 16b545dd92SMark Brown #include <linux/module.h> 17b545dd92SMark Brown 18b545dd92SMark Brown #include "../codecs/wm5102.h" 19b545dd92SMark Brown #include "../codecs/wm9081.h" 20b545dd92SMark Brown 21b545dd92SMark Brown /* BCLK2 is fixed at this currently */ 22b545dd92SMark Brown #define BCLK2_RATE (64 * 8000) 23b545dd92SMark Brown 24b545dd92SMark Brown /* 25b545dd92SMark Brown * Expect a 24.576MHz crystal if one is fitted (the driver will function 26b545dd92SMark Brown * if this is not fitted). 27b545dd92SMark Brown */ 28b545dd92SMark Brown #define MCLK_RATE 24576000 29b545dd92SMark Brown 30344c5edeSMark Brown #define SYS_AUDIO_RATE 44100 31344c5edeSMark Brown #define SYS_MCLK_RATE (SYS_AUDIO_RATE * 256) 32344c5edeSMark Brown 33344c5edeSMark Brown #define DAI_AP_DSP 0 34344c5edeSMark Brown #define DAI_DSP_CODEC 1 35344c5edeSMark Brown #define DAI_CODEC_CP 2 36344c5edeSMark Brown #define DAI_CODEC_SUB 3 37b545dd92SMark Brown 38*b1387078SMark Brown struct bells_drvdata { 39*b1387078SMark Brown int sysclk_rate; 40*b1387078SMark Brown int asyncclk_rate; 41*b1387078SMark Brown }; 42*b1387078SMark Brown 43*b1387078SMark Brown static struct bells_drvdata wm5102_drvdata = { 44*b1387078SMark Brown .sysclk_rate = 45158400, 45*b1387078SMark Brown .asyncclk_rate = 49152000, 46*b1387078SMark Brown }; 47*b1387078SMark Brown 48*b1387078SMark Brown static struct bells_drvdata wm5110_drvdata = { 49*b1387078SMark Brown .sysclk_rate = 135475200, 50*b1387078SMark Brown .asyncclk_rate = 147456000, 51*b1387078SMark Brown }; 52*b1387078SMark Brown 53b545dd92SMark Brown static int bells_set_bias_level(struct snd_soc_card *card, 54b545dd92SMark Brown struct snd_soc_dapm_context *dapm, 55b545dd92SMark Brown enum snd_soc_bias_level level) 56b545dd92SMark Brown { 57344c5edeSMark Brown struct snd_soc_dai *codec_dai = card->rtd[DAI_DSP_CODEC].codec_dai; 58b545dd92SMark Brown struct snd_soc_codec *codec = codec_dai->codec; 59*b1387078SMark Brown struct bells_drvdata *bells = card->drvdata; 60b545dd92SMark Brown int ret; 61b545dd92SMark Brown 62b545dd92SMark Brown if (dapm->dev != codec_dai->dev) 63b545dd92SMark Brown return 0; 64b545dd92SMark Brown 65b545dd92SMark Brown switch (level) { 66b545dd92SMark Brown case SND_SOC_BIAS_PREPARE: 67*b1387078SMark Brown if (dapm->bias_level != SND_SOC_BIAS_STANDBY) 68*b1387078SMark Brown break; 69*b1387078SMark Brown 70b545dd92SMark Brown ret = snd_soc_codec_set_pll(codec, WM5102_FLL1, 71b545dd92SMark Brown ARIZONA_FLL_SRC_MCLK1, 72b545dd92SMark Brown MCLK_RATE, 73*b1387078SMark Brown bells->sysclk_rate); 74b545dd92SMark Brown if (ret < 0) 75b545dd92SMark Brown pr_err("Failed to start FLL: %d\n", ret); 76b545dd92SMark Brown 77*b1387078SMark Brown if (bells->asyncclk_rate) { 78b545dd92SMark Brown ret = snd_soc_codec_set_pll(codec, WM5102_FLL2, 79b545dd92SMark Brown ARIZONA_FLL_SRC_AIF2BCLK, 80b545dd92SMark Brown BCLK2_RATE, 81*b1387078SMark Brown bells->asyncclk_rate); 82b545dd92SMark Brown if (ret < 0) 83b545dd92SMark Brown pr_err("Failed to start FLL: %d\n", ret); 84b545dd92SMark Brown } 85b545dd92SMark Brown break; 86b545dd92SMark Brown 87b545dd92SMark Brown default: 88b545dd92SMark Brown break; 89b545dd92SMark Brown } 90b545dd92SMark Brown 91b545dd92SMark Brown return 0; 92b545dd92SMark Brown } 93b545dd92SMark Brown 94b545dd92SMark Brown static int bells_set_bias_level_post(struct snd_soc_card *card, 95b545dd92SMark Brown struct snd_soc_dapm_context *dapm, 96b545dd92SMark Brown enum snd_soc_bias_level level) 97b545dd92SMark Brown { 98344c5edeSMark Brown struct snd_soc_dai *codec_dai = card->rtd[DAI_DSP_CODEC].codec_dai; 99b545dd92SMark Brown struct snd_soc_codec *codec = codec_dai->codec; 100*b1387078SMark Brown struct bells_drvdata *bells = card->drvdata; 101b545dd92SMark Brown int ret; 102b545dd92SMark Brown 103b545dd92SMark Brown if (dapm->dev != codec_dai->dev) 104b545dd92SMark Brown return 0; 105b545dd92SMark Brown 106b545dd92SMark Brown switch (level) { 107b545dd92SMark Brown case SND_SOC_BIAS_STANDBY: 108b545dd92SMark Brown ret = snd_soc_codec_set_pll(codec, WM5102_FLL1, 0, 0, 0); 109b545dd92SMark Brown if (ret < 0) { 110b545dd92SMark Brown pr_err("Failed to stop FLL: %d\n", ret); 111b545dd92SMark Brown return ret; 112b545dd92SMark Brown } 113b545dd92SMark Brown 114*b1387078SMark Brown if (bells->asyncclk_rate) { 115*b1387078SMark Brown ret = snd_soc_codec_set_pll(codec, WM5102_FLL2, 116*b1387078SMark Brown 0, 0, 0); 117b545dd92SMark Brown if (ret < 0) { 118b545dd92SMark Brown pr_err("Failed to stop FLL: %d\n", ret); 119b545dd92SMark Brown return ret; 120b545dd92SMark Brown } 121*b1387078SMark Brown } 122b545dd92SMark Brown break; 123b545dd92SMark Brown 124b545dd92SMark Brown default: 125b545dd92SMark Brown break; 126b545dd92SMark Brown } 127b545dd92SMark Brown 128b545dd92SMark Brown dapm->bias_level = level; 129b545dd92SMark Brown 130b545dd92SMark Brown return 0; 131b545dd92SMark Brown } 132b545dd92SMark Brown 133b545dd92SMark Brown static int bells_late_probe(struct snd_soc_card *card) 134b545dd92SMark Brown { 135*b1387078SMark Brown struct bells_drvdata *bells = card->drvdata; 136344c5edeSMark Brown struct snd_soc_codec *wm0010 = card->rtd[DAI_AP_DSP].codec; 137344c5edeSMark Brown struct snd_soc_codec *codec = card->rtd[DAI_DSP_CODEC].codec; 138344c5edeSMark Brown struct snd_soc_dai *aif1_dai = card->rtd[DAI_DSP_CODEC].codec_dai; 139*b1387078SMark Brown struct snd_soc_dai *aif2_dai; 140*b1387078SMark Brown struct snd_soc_dai *aif3_dai; 141*b1387078SMark Brown struct snd_soc_dai *wm9081_dai; 142b545dd92SMark Brown int ret; 143b545dd92SMark Brown 144*b1387078SMark Brown ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK, 145*b1387078SMark Brown ARIZONA_CLK_SRC_FLL1, 146*b1387078SMark Brown bells->sysclk_rate, 147*b1387078SMark Brown SND_SOC_CLOCK_IN); 148*b1387078SMark Brown if (ret != 0) { 149*b1387078SMark Brown dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret); 150*b1387078SMark Brown return ret; 151*b1387078SMark Brown } 152*b1387078SMark Brown 153344c5edeSMark Brown ret = snd_soc_codec_set_sysclk(wm0010, 0, 0, SYS_MCLK_RATE, 0); 154344c5edeSMark Brown if (ret != 0) { 155344c5edeSMark Brown dev_err(wm0010->dev, "Failed to set WM0010 clock: %d\n", ret); 156344c5edeSMark Brown return ret; 157344c5edeSMark Brown } 158344c5edeSMark Brown 159b545dd92SMark Brown ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0); 160*b1387078SMark Brown if (ret != 0) 161b545dd92SMark Brown dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret); 162*b1387078SMark Brown 163*b1387078SMark Brown ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_OPCLK, 0, 164*b1387078SMark Brown SYS_MCLK_RATE, SND_SOC_CLOCK_OUT); 165*b1387078SMark Brown if (ret != 0) 166*b1387078SMark Brown dev_err(codec->dev, "Failed to set OPCLK: %d\n", ret); 167*b1387078SMark Brown 168*b1387078SMark Brown if (card->num_rtd == DAI_CODEC_CP) 169*b1387078SMark Brown return 0; 170*b1387078SMark Brown 171*b1387078SMark Brown ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_ASYNCCLK, 172*b1387078SMark Brown ARIZONA_CLK_SRC_FLL2, 173*b1387078SMark Brown bells->asyncclk_rate, 174*b1387078SMark Brown SND_SOC_CLOCK_IN); 175*b1387078SMark Brown if (ret != 0) { 176*b1387078SMark Brown dev_err(codec->dev, "Failed to set ASYNCCLK: %d\n", ret); 177b545dd92SMark Brown return ret; 178b545dd92SMark Brown } 179b545dd92SMark Brown 180*b1387078SMark Brown aif2_dai = card->rtd[DAI_CODEC_CP].cpu_dai; 181*b1387078SMark Brown 182b545dd92SMark Brown ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0); 183b545dd92SMark Brown if (ret != 0) { 184b545dd92SMark Brown dev_err(aif2_dai->dev, "Failed to set AIF2 clock: %d\n", ret); 185b545dd92SMark Brown return ret; 186b545dd92SMark Brown } 187b545dd92SMark Brown 188*b1387078SMark Brown if (card->num_rtd == DAI_CODEC_SUB) 189*b1387078SMark Brown return 0; 190*b1387078SMark Brown 191*b1387078SMark Brown aif3_dai = card->rtd[DAI_CODEC_SUB].cpu_dai; 192*b1387078SMark Brown wm9081_dai = card->rtd[DAI_CODEC_SUB].codec_dai; 193*b1387078SMark Brown 194b545dd92SMark Brown ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0); 195b545dd92SMark Brown if (ret != 0) { 196b545dd92SMark Brown dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret); 197b545dd92SMark Brown return ret; 198b545dd92SMark Brown } 199b545dd92SMark Brown 200b545dd92SMark Brown ret = snd_soc_codec_set_sysclk(wm9081_dai->codec, WM9081_SYSCLK_MCLK, 201344c5edeSMark Brown 0, SYS_MCLK_RATE, 0); 202b545dd92SMark Brown if (ret != 0) { 203b545dd92SMark Brown dev_err(wm9081_dai->dev, "Failed to set MCLK: %d\n", ret); 204b545dd92SMark Brown return ret; 205b545dd92SMark Brown } 206b545dd92SMark Brown 207b545dd92SMark Brown return 0; 208b545dd92SMark Brown } 209b545dd92SMark Brown 210b545dd92SMark Brown static const struct snd_soc_pcm_stream baseband_params = { 211b545dd92SMark Brown .formats = SNDRV_PCM_FMTBIT_S32_LE, 212b545dd92SMark Brown .rate_min = 8000, 213b545dd92SMark Brown .rate_max = 8000, 214b545dd92SMark Brown .channels_min = 2, 215b545dd92SMark Brown .channels_max = 2, 216b545dd92SMark Brown }; 217b545dd92SMark Brown 218b545dd92SMark Brown static const struct snd_soc_pcm_stream sub_params = { 219b545dd92SMark Brown .formats = SNDRV_PCM_FMTBIT_S32_LE, 220344c5edeSMark Brown .rate_min = SYS_AUDIO_RATE, 221344c5edeSMark Brown .rate_max = SYS_AUDIO_RATE, 222b545dd92SMark Brown .channels_min = 2, 223b545dd92SMark Brown .channels_max = 2, 224b545dd92SMark Brown }; 225b545dd92SMark Brown 226b545dd92SMark Brown static struct snd_soc_dai_link bells_dai_wm5102[] = { 227b545dd92SMark Brown { 228344c5edeSMark Brown .name = "CPU-DSP", 229344c5edeSMark Brown .stream_name = "CPU-DSP", 230b545dd92SMark Brown .cpu_dai_name = "samsung-i2s.0", 231344c5edeSMark Brown .codec_dai_name = "wm0010-sdi1", 232b545dd92SMark Brown .platform_name = "samsung-audio", 233344c5edeSMark Brown .codec_name = "spi0.0", 234344c5edeSMark Brown .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 235344c5edeSMark Brown | SND_SOC_DAIFMT_CBM_CFM, 236344c5edeSMark Brown }, 237344c5edeSMark Brown { 238344c5edeSMark Brown .name = "DSP-CODEC", 239344c5edeSMark Brown .stream_name = "DSP-CODEC", 240344c5edeSMark Brown .cpu_dai_name = "wm0010-sdi2", 241344c5edeSMark Brown .codec_dai_name = "wm5102-aif1", 242b545dd92SMark Brown .codec_name = "wm5102-codec", 243b545dd92SMark Brown .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 244b545dd92SMark Brown | SND_SOC_DAIFMT_CBM_CFM, 245344c5edeSMark Brown .params = &sub_params, 246344c5edeSMark Brown .ignore_suspend = 1, 247b545dd92SMark Brown }, 248b545dd92SMark Brown { 249b545dd92SMark Brown .name = "Baseband", 250b545dd92SMark Brown .stream_name = "Baseband", 251b545dd92SMark Brown .cpu_dai_name = "wm5102-aif2", 252b545dd92SMark Brown .codec_dai_name = "wm1250-ev1", 253b545dd92SMark Brown .codec_name = "wm1250-ev1.1-0027", 254b545dd92SMark Brown .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 255b545dd92SMark Brown | SND_SOC_DAIFMT_CBM_CFM, 256b545dd92SMark Brown .ignore_suspend = 1, 257b545dd92SMark Brown .params = &baseband_params, 258b545dd92SMark Brown }, 259b545dd92SMark Brown { 260b545dd92SMark Brown .name = "Sub", 261b545dd92SMark Brown .stream_name = "Sub", 262b545dd92SMark Brown .cpu_dai_name = "wm5102-aif3", 263b545dd92SMark Brown .codec_dai_name = "wm9081-hifi", 264b545dd92SMark Brown .codec_name = "wm9081.1-006c", 265b545dd92SMark Brown .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 266b545dd92SMark Brown | SND_SOC_DAIFMT_CBS_CFS, 267b545dd92SMark Brown .ignore_suspend = 1, 268b545dd92SMark Brown .params = &sub_params, 269b545dd92SMark Brown }, 270b545dd92SMark Brown }; 271b545dd92SMark Brown 272b545dd92SMark Brown static struct snd_soc_dai_link bells_dai_wm5110[] = { 273b545dd92SMark Brown { 274344c5edeSMark Brown .name = "CPU-DSP", 275344c5edeSMark Brown .stream_name = "CPU-DSP", 276b545dd92SMark Brown .cpu_dai_name = "samsung-i2s.0", 277344c5edeSMark Brown .codec_dai_name = "wm0010-sdi1", 278b545dd92SMark Brown .platform_name = "samsung-audio", 279344c5edeSMark Brown .codec_name = "spi0.0", 280344c5edeSMark Brown .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 281344c5edeSMark Brown | SND_SOC_DAIFMT_CBM_CFM, 282344c5edeSMark Brown }, 283344c5edeSMark Brown { 284344c5edeSMark Brown .name = "DSP-CODEC", 285344c5edeSMark Brown .stream_name = "DSP-CODEC", 286344c5edeSMark Brown .cpu_dai_name = "wm0010-sdi2", 287344c5edeSMark Brown .codec_dai_name = "wm5110-aif1", 288b545dd92SMark Brown .codec_name = "wm5110-codec", 289b545dd92SMark Brown .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 290b545dd92SMark Brown | SND_SOC_DAIFMT_CBM_CFM, 291344c5edeSMark Brown .params = &sub_params, 292344c5edeSMark Brown .ignore_suspend = 1, 293b545dd92SMark Brown }, 294b545dd92SMark Brown { 295b545dd92SMark Brown .name = "Baseband", 296b545dd92SMark Brown .stream_name = "Baseband", 297b545dd92SMark Brown .cpu_dai_name = "wm5110-aif2", 298b545dd92SMark Brown .codec_dai_name = "wm1250-ev1", 299b545dd92SMark Brown .codec_name = "wm1250-ev1.1-0027", 300b545dd92SMark Brown .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 301b545dd92SMark Brown | SND_SOC_DAIFMT_CBM_CFM, 302b545dd92SMark Brown .ignore_suspend = 1, 303b545dd92SMark Brown .params = &baseband_params, 304b545dd92SMark Brown }, 305b545dd92SMark Brown { 306b545dd92SMark Brown .name = "Sub", 307b545dd92SMark Brown .stream_name = "Sub", 308ffaa839bSMark Brown .cpu_dai_name = "wm5110-aif3", 309b545dd92SMark Brown .codec_dai_name = "wm9081-hifi", 310b545dd92SMark Brown .codec_name = "wm9081.1-006c", 311b545dd92SMark Brown .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 312b545dd92SMark Brown | SND_SOC_DAIFMT_CBS_CFS, 313b545dd92SMark Brown .ignore_suspend = 1, 314b545dd92SMark Brown .params = &sub_params, 315b545dd92SMark Brown }, 316b545dd92SMark Brown }; 317b545dd92SMark Brown 318b545dd92SMark Brown static struct snd_soc_codec_conf bells_codec_conf[] = { 319b545dd92SMark Brown { 320b545dd92SMark Brown .dev_name = "wm9081.1-006c", 321b545dd92SMark Brown .name_prefix = "Sub", 322b545dd92SMark Brown }, 323b545dd92SMark Brown }; 324b545dd92SMark Brown 325b545dd92SMark Brown static struct snd_soc_dapm_route bells_routes[] = { 326b545dd92SMark Brown { "Sub CLK_SYS", NULL, "OPCLK" }, 327b545dd92SMark Brown }; 328b545dd92SMark Brown 329b545dd92SMark Brown static struct snd_soc_card bells_cards[] = { 330b545dd92SMark Brown { 331b545dd92SMark Brown .name = "Bells WM5102", 332b545dd92SMark Brown .owner = THIS_MODULE, 333b545dd92SMark Brown .dai_link = bells_dai_wm5102, 334b545dd92SMark Brown .num_links = ARRAY_SIZE(bells_dai_wm5102), 335b545dd92SMark Brown .codec_conf = bells_codec_conf, 336b545dd92SMark Brown .num_configs = ARRAY_SIZE(bells_codec_conf), 337b545dd92SMark Brown 338b545dd92SMark Brown .late_probe = bells_late_probe, 339b545dd92SMark Brown 340b545dd92SMark Brown .dapm_routes = bells_routes, 341b545dd92SMark Brown .num_dapm_routes = ARRAY_SIZE(bells_routes), 342b545dd92SMark Brown 343b545dd92SMark Brown .set_bias_level = bells_set_bias_level, 344b545dd92SMark Brown .set_bias_level_post = bells_set_bias_level_post, 345*b1387078SMark Brown 346*b1387078SMark Brown .drvdata = &wm5102_drvdata, 347b545dd92SMark Brown }, 348b545dd92SMark Brown { 349b545dd92SMark Brown .name = "Bells WM5110", 350b545dd92SMark Brown .owner = THIS_MODULE, 351b545dd92SMark Brown .dai_link = bells_dai_wm5110, 352b545dd92SMark Brown .num_links = ARRAY_SIZE(bells_dai_wm5110), 353b545dd92SMark Brown .codec_conf = bells_codec_conf, 354b545dd92SMark Brown .num_configs = ARRAY_SIZE(bells_codec_conf), 355b545dd92SMark Brown 356b545dd92SMark Brown .late_probe = bells_late_probe, 357b545dd92SMark Brown 358b545dd92SMark Brown .dapm_routes = bells_routes, 359b545dd92SMark Brown .num_dapm_routes = ARRAY_SIZE(bells_routes), 360b545dd92SMark Brown 361b545dd92SMark Brown .set_bias_level = bells_set_bias_level, 362b545dd92SMark Brown .set_bias_level_post = bells_set_bias_level_post, 363*b1387078SMark Brown 364*b1387078SMark Brown .drvdata = &wm5110_drvdata, 365b545dd92SMark Brown }, 366b545dd92SMark Brown }; 367b545dd92SMark Brown 368b545dd92SMark Brown 369b545dd92SMark Brown static __devinit int bells_probe(struct platform_device *pdev) 370b545dd92SMark Brown { 371b545dd92SMark Brown int ret; 372b545dd92SMark Brown 373b545dd92SMark Brown bells_cards[pdev->id].dev = &pdev->dev; 374b545dd92SMark Brown 375b545dd92SMark Brown ret = snd_soc_register_card(&bells_cards[pdev->id]); 376b545dd92SMark Brown if (ret) { 377b545dd92SMark Brown dev_err(&pdev->dev, 378b545dd92SMark Brown "snd_soc_register_card(%s) failed: %d\n", 379b545dd92SMark Brown bells_cards[pdev->id].name, ret); 380b545dd92SMark Brown return ret; 381b545dd92SMark Brown } 382b545dd92SMark Brown 383b545dd92SMark Brown return 0; 384b545dd92SMark Brown } 385b545dd92SMark Brown 386b545dd92SMark Brown static int __devexit bells_remove(struct platform_device *pdev) 387b545dd92SMark Brown { 388b545dd92SMark Brown snd_soc_unregister_card(&bells_cards[pdev->id]); 389b545dd92SMark Brown 390b545dd92SMark Brown return 0; 391b545dd92SMark Brown } 392b545dd92SMark Brown 393b545dd92SMark Brown static struct platform_driver bells_driver = { 394b545dd92SMark Brown .driver = { 395b545dd92SMark Brown .name = "bells", 396b545dd92SMark Brown .owner = THIS_MODULE, 397b545dd92SMark Brown .pm = &snd_soc_pm_ops, 398b545dd92SMark Brown }, 399b545dd92SMark Brown .probe = bells_probe, 400b545dd92SMark Brown .remove = __devexit_p(bells_remove), 401b545dd92SMark Brown }; 402b545dd92SMark Brown 403b545dd92SMark Brown module_platform_driver(bells_driver); 404b545dd92SMark Brown 405b545dd92SMark Brown MODULE_DESCRIPTION("Bells audio support"); 406b545dd92SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 407b545dd92SMark Brown MODULE_LICENSE("GPL"); 408b545dd92SMark Brown MODULE_ALIAS("platform:bells"); 409