Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.10 224 lines 5.4 kB view raw
1/* 2 * Lowland audio support 3 * 4 * Copyright 2011 Wolfson Microelectronics 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the 8 * Free Software Foundation; either version 2 of the License, or (at your 9 * option) any later version. 10 */ 11 12#include <sound/soc.h> 13#include <sound/soc-dapm.h> 14#include <sound/jack.h> 15#include <linux/gpio.h> 16#include <linux/module.h> 17 18#include "../codecs/wm5100.h" 19#include "../codecs/wm9081.h" 20 21#define MCLK1_RATE (44100 * 512) 22#define CLKOUT_RATE (44100 * 256) 23 24static struct snd_soc_jack lowland_headset; 25 26/* Headset jack detection DAPM pins */ 27static struct snd_soc_jack_pin lowland_headset_pins[] = { 28 { 29 .pin = "Headphone", 30 .mask = SND_JACK_HEADPHONE | SND_JACK_LINEOUT, 31 }, 32 { 33 .pin = "Headset Mic", 34 .mask = SND_JACK_MICROPHONE, 35 }, 36}; 37 38static int lowland_wm5100_init(struct snd_soc_pcm_runtime *rtd) 39{ 40 struct snd_soc_codec *codec = rtd->codec; 41 int ret; 42 43 ret = snd_soc_codec_set_sysclk(codec, WM5100_CLK_SYSCLK, 44 WM5100_CLKSRC_MCLK1, MCLK1_RATE, 45 SND_SOC_CLOCK_IN); 46 if (ret < 0) { 47 pr_err("Failed to set SYSCLK clock source: %d\n", ret); 48 return ret; 49 } 50 51 /* Clock OPCLK, used by the other audio components. */ 52 ret = snd_soc_codec_set_sysclk(codec, WM5100_CLK_OPCLK, 0, 53 CLKOUT_RATE, 0); 54 if (ret < 0) { 55 pr_err("Failed to set OPCLK rate: %d\n", ret); 56 return ret; 57 } 58 59 ret = snd_soc_jack_new(codec, "Headset", 60 SND_JACK_LINEOUT | SND_JACK_HEADSET | 61 SND_JACK_BTN_0, 62 &lowland_headset); 63 if (ret) 64 return ret; 65 66 ret = snd_soc_jack_add_pins(&lowland_headset, 67 ARRAY_SIZE(lowland_headset_pins), 68 lowland_headset_pins); 69 if (ret) 70 return ret; 71 72 wm5100_detect(codec, &lowland_headset); 73 74 return 0; 75} 76 77static int lowland_wm9081_init(struct snd_soc_pcm_runtime *rtd) 78{ 79 struct snd_soc_codec *codec = rtd->codec; 80 81 snd_soc_dapm_nc_pin(&codec->dapm, "LINEOUT"); 82 83 /* At any time the WM9081 is active it will have this clock */ 84 return snd_soc_codec_set_sysclk(codec, WM9081_SYSCLK_MCLK, 0, 85 CLKOUT_RATE, 0); 86} 87 88static const struct snd_soc_pcm_stream sub_params = { 89 .formats = SNDRV_PCM_FMTBIT_S32_LE, 90 .rate_min = 44100, 91 .rate_max = 44100, 92 .channels_min = 2, 93 .channels_max = 2, 94}; 95 96static struct snd_soc_dai_link lowland_dai[] = { 97 { 98 .name = "CPU", 99 .stream_name = "CPU", 100 .cpu_dai_name = "samsung-i2s.0", 101 .codec_dai_name = "wm5100-aif1", 102 .platform_name = "samsung-i2s.0", 103 .codec_name = "wm5100.1-001a", 104 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 105 SND_SOC_DAIFMT_CBM_CFM, 106 .init = lowland_wm5100_init, 107 }, 108 { 109 .name = "Baseband", 110 .stream_name = "Baseband", 111 .cpu_dai_name = "wm5100-aif2", 112 .codec_dai_name = "wm1250-ev1", 113 .codec_name = "wm1250-ev1.1-0027", 114 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 115 SND_SOC_DAIFMT_CBM_CFM, 116 .ignore_suspend = 1, 117 }, 118 { 119 .name = "Sub Speaker", 120 .stream_name = "Sub Speaker", 121 .cpu_dai_name = "wm5100-aif3", 122 .codec_dai_name = "wm9081-hifi", 123 .codec_name = "wm9081.1-006c", 124 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 125 SND_SOC_DAIFMT_CBM_CFM, 126 .ignore_suspend = 1, 127 .params = &sub_params, 128 .init = lowland_wm9081_init, 129 }, 130}; 131 132static struct snd_soc_codec_conf lowland_codec_conf[] = { 133 { 134 .dev_name = "wm9081.1-006c", 135 .name_prefix = "Sub", 136 }, 137}; 138 139static const struct snd_kcontrol_new controls[] = { 140 SOC_DAPM_PIN_SWITCH("Main Speaker"), 141 SOC_DAPM_PIN_SWITCH("Main DMIC"), 142 SOC_DAPM_PIN_SWITCH("Main AMIC"), 143 SOC_DAPM_PIN_SWITCH("WM1250 Input"), 144 SOC_DAPM_PIN_SWITCH("WM1250 Output"), 145 SOC_DAPM_PIN_SWITCH("Headphone"), 146}; 147 148static struct snd_soc_dapm_widget widgets[] = { 149 SND_SOC_DAPM_HP("Headphone", NULL), 150 SND_SOC_DAPM_MIC("Headset Mic", NULL), 151 152 SND_SOC_DAPM_SPK("Main Speaker", NULL), 153 154 SND_SOC_DAPM_MIC("Main AMIC", NULL), 155 SND_SOC_DAPM_MIC("Main DMIC", NULL), 156}; 157 158static struct snd_soc_dapm_route audio_paths[] = { 159 { "Sub IN1", NULL, "HPOUT2L" }, 160 { "Sub IN2", NULL, "HPOUT2R" }, 161 162 { "Main Speaker", NULL, "Sub SPKN" }, 163 { "Main Speaker", NULL, "Sub SPKP" }, 164 { "Main Speaker", NULL, "SPKDAT1" }, 165}; 166 167static struct snd_soc_card lowland = { 168 .name = "Lowland", 169 .owner = THIS_MODULE, 170 .dai_link = lowland_dai, 171 .num_links = ARRAY_SIZE(lowland_dai), 172 .codec_conf = lowland_codec_conf, 173 .num_configs = ARRAY_SIZE(lowland_codec_conf), 174 175 .controls = controls, 176 .num_controls = ARRAY_SIZE(controls), 177 .dapm_widgets = widgets, 178 .num_dapm_widgets = ARRAY_SIZE(widgets), 179 .dapm_routes = audio_paths, 180 .num_dapm_routes = ARRAY_SIZE(audio_paths), 181}; 182 183static int lowland_probe(struct platform_device *pdev) 184{ 185 struct snd_soc_card *card = &lowland; 186 int ret; 187 188 card->dev = &pdev->dev; 189 190 ret = snd_soc_register_card(card); 191 if (ret) { 192 dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", 193 ret); 194 return ret; 195 } 196 197 return 0; 198} 199 200static int lowland_remove(struct platform_device *pdev) 201{ 202 struct snd_soc_card *card = platform_get_drvdata(pdev); 203 204 snd_soc_unregister_card(card); 205 206 return 0; 207} 208 209static struct platform_driver lowland_driver = { 210 .driver = { 211 .name = "lowland", 212 .owner = THIS_MODULE, 213 .pm = &snd_soc_pm_ops, 214 }, 215 .probe = lowland_probe, 216 .remove = lowland_remove, 217}; 218 219module_platform_driver(lowland_driver); 220 221MODULE_DESCRIPTION("Lowland audio support"); 222MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 223MODULE_LICENSE("GPL"); 224MODULE_ALIAS("platform:lowland");