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

ASoC: sirf: Add SiRF internal audio codec driver

SiRF internal audio codec is integrated in SiRF atlas6 and prima2 SoC.
Features include:
1. Stereo DAC and ADC with 16-bit resolution amd 48KHz sample rate
2. Support headphone and/or speaker output
3. Integrate headphone and speaker output amp
4. Support LINE and MIC input
5. Support single ended and differential input mode

Signed-off-by: Rongjun Ying <rongjun.ying@csr.com>
--v5:
1. Drop all inlines.
2. Reordering the Kconfig and Makefile
3. Remove the sirf_audio_codec_reg_bits struct, use the new controls instead it.
4. Add some SND_SOC_DAPM_OUT_DRV instead of HP and SPK enable driver
5. Add audio codec clock supply instead of adc event callback
6. Fixed playback and capture can't concurrent work bug.

--
.../devicetree/bindings/sound/sirf-audio-codec.txt | 17 +
sound/soc/codecs/Kconfig | 5 +
sound/soc/codecs/Makefile | 1 +
sound/soc/codecs/sirf-audio-codec.c | 533 ++++++++++++++++++++
sound/soc/codecs/sirf-audio-codec.h | 75 +++
5 files changed, 631 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/sound/sirf-audio-codec.txt
create mode 100644 sound/soc/codecs/sirf-audio-codec.c
create mode 100644 sound/soc/codecs/sirf-audio-codec.h
Signed-off-by: Mark Brown <broonie@linaro.org>

authored by

Rongjun Ying and committed by
Mark Brown
f516e368 38dbfb59

+631
+17
Documentation/devicetree/bindings/sound/sirf-audio-codec.txt
··· 1 + SiRF internal audio CODEC 2 + 3 + Required properties: 4 + 5 + - compatible : "sirf,atlas6-audio-codec" or "sirf,prima2-audio-codec" 6 + 7 + - reg : the register address of the device. 8 + 9 + - clocks: the clock of SiRF internal audio codec 10 + 11 + Example: 12 + 13 + audiocodec: audiocodec@b0040000 { 14 + compatible = "sirf,atlas6-audio-codec"; 15 + reg = <0xb0040000 0x10000>; 16 + clocks = <&clks 27>; 17 + };
+5
sound/soc/codecs/Kconfig
··· 63 63 select SND_SOC_RT5640 if I2C 64 64 select SND_SOC_SGTL5000 if I2C 65 65 select SND_SOC_SI476X if MFD_SI476X_CORE 66 + select SND_SOC_SIRF_AUDIO_CODEC 66 67 select SND_SOC_SN95031 if INTEL_SCU_IPC 67 68 select SND_SOC_SPDIF 68 69 select SND_SOC_SSM2518 if I2C ··· 330 329 config SND_SOC_SIGMADSP 331 330 tristate 332 331 select CRC32 332 + 333 + config SND_SOC_SIRF_AUDIO_CODEC 334 + tristate "SiRF SoC internal audio codec" 335 + select REGMAP_MMIO 333 336 334 337 config SND_SOC_SN95031 335 338 tristate
+1
sound/soc/codecs/Makefile
··· 53 53 snd-soc-alc5632-objs := alc5632.o 54 54 snd-soc-sigmadsp-objs := sigmadsp.o 55 55 snd-soc-si476x-objs := si476x.o 56 + snd-soc-sirf-audio-codec-objs := sirf-audio-codec.o 56 57 snd-soc-sn95031-objs := sn95031.o 57 58 snd-soc-spdif-tx-objs := spdif_transmitter.o 58 59 snd-soc-spdif-rx-objs := spdif_receiver.o
+533
sound/soc/codecs/sirf-audio-codec.c
··· 1 + /* 2 + * SiRF audio codec driver 3 + * 4 + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. 5 + * 6 + * Licensed under GPLv2 or later. 7 + */ 8 + 9 + #include <linux/module.h> 10 + #include <linux/platform_device.h> 11 + #include <linux/pm_runtime.h> 12 + #include <linux/of.h> 13 + #include <linux/of_device.h> 14 + #include <linux/clk.h> 15 + #include <linux/delay.h> 16 + #include <linux/io.h> 17 + #include <linux/regmap.h> 18 + #include <sound/core.h> 19 + #include <sound/pcm.h> 20 + #include <sound/pcm_params.h> 21 + #include <sound/initval.h> 22 + #include <sound/tlv.h> 23 + #include <sound/soc.h> 24 + #include <sound/dmaengine_pcm.h> 25 + 26 + #include "sirf-audio-codec.h" 27 + 28 + struct sirf_audio_codec { 29 + struct clk *clk; 30 + struct regmap *regmap; 31 + u32 reg_ctrl0, reg_ctrl1; 32 + }; 33 + 34 + static const char * const input_mode_mux[] = {"Single-ended", 35 + "Differential"}; 36 + 37 + static const struct soc_enum input_mode_mux_enum = 38 + SOC_ENUM_SINGLE(AUDIO_IC_CODEC_CTRL1, 4, 2, input_mode_mux); 39 + 40 + static const struct snd_kcontrol_new sirf_audio_codec_input_mode_control = 41 + SOC_DAPM_ENUM("Route", input_mode_mux_enum); 42 + 43 + static const DECLARE_TLV_DB_SCALE(playback_vol_tlv, -12400, 100, 0); 44 + static const DECLARE_TLV_DB_SCALE(capture_vol_tlv_prima2, 500, 100, 0); 45 + static const DECLARE_TLV_DB_RANGE(capture_vol_tlv_atlas6, 46 + 0, 7, TLV_DB_SCALE_ITEM(-100, 100, 0), 47 + 0x22, 0x3F, TLV_DB_SCALE_ITEM(700, 100, 0), 48 + ); 49 + 50 + static struct snd_kcontrol_new volume_controls_atlas6[] = { 51 + SOC_DOUBLE_TLV("Playback Volume", AUDIO_IC_CODEC_CTRL0, 21, 14, 52 + 0x7F, 0, playback_vol_tlv), 53 + SOC_DOUBLE_TLV("Capture Volume", AUDIO_IC_CODEC_CTRL1, 16, 10, 54 + 0x3F, 0, capture_vol_tlv_atlas6), 55 + }; 56 + 57 + static struct snd_kcontrol_new volume_controls_prima2[] = { 58 + SOC_DOUBLE_TLV("Speaker Volume", AUDIO_IC_CODEC_CTRL0, 21, 14, 59 + 0x7F, 0, playback_vol_tlv), 60 + SOC_DOUBLE_TLV("Capture Volume", AUDIO_IC_CODEC_CTRL1, 15, 10, 61 + 0x1F, 0, capture_vol_tlv_prima2), 62 + }; 63 + 64 + static struct snd_kcontrol_new left_input_path_controls[] = { 65 + SOC_DAPM_SINGLE("Line Left Switch", AUDIO_IC_CODEC_CTRL1, 6, 1, 0), 66 + SOC_DAPM_SINGLE("Mic Left Switch", AUDIO_IC_CODEC_CTRL1, 3, 1, 0), 67 + }; 68 + 69 + static struct snd_kcontrol_new right_input_path_controls[] = { 70 + SOC_DAPM_SINGLE("Line Right Switch", AUDIO_IC_CODEC_CTRL1, 5, 1, 0), 71 + SOC_DAPM_SINGLE("Mic Right Switch", AUDIO_IC_CODEC_CTRL1, 2, 1, 0), 72 + }; 73 + 74 + static struct snd_kcontrol_new left_dac_to_hp_left_amp_switch_control = 75 + SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 9, 1, 0); 76 + 77 + static struct snd_kcontrol_new left_dac_to_hp_right_amp_switch_control = 78 + SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 8, 1, 0); 79 + 80 + static struct snd_kcontrol_new right_dac_to_hp_left_amp_switch_control = 81 + SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 7, 1, 0); 82 + 83 + static struct snd_kcontrol_new right_dac_to_hp_right_amp_switch_control = 84 + SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 6, 1, 0); 85 + 86 + static struct snd_kcontrol_new left_dac_to_speaker_lineout_switch_control = 87 + SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 11, 1, 0); 88 + 89 + static struct snd_kcontrol_new right_dac_to_speaker_lineout_switch_control = 90 + SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 10, 1, 0); 91 + 92 + /* After enable adc, Delay 200ms to avoid pop noise */ 93 + static int adc_enable_delay_event(struct snd_soc_dapm_widget *w, 94 + struct snd_kcontrol *kcontrol, int event) 95 + { 96 + switch (event) { 97 + case SND_SOC_DAPM_POST_PMU: 98 + msleep(200); 99 + break; 100 + default: 101 + break; 102 + } 103 + 104 + return 0; 105 + } 106 + 107 + static void enable_and_reset_codec(struct regmap *regmap, 108 + u32 codec_enable_bits, u32 codec_reset_bits) 109 + { 110 + regmap_update_bits(regmap, AUDIO_IC_CODEC_CTRL1, 111 + codec_enable_bits | codec_reset_bits, 112 + codec_enable_bits | ~codec_reset_bits); 113 + msleep(20); 114 + regmap_update_bits(regmap, AUDIO_IC_CODEC_CTRL1, 115 + codec_reset_bits, codec_reset_bits); 116 + } 117 + 118 + static int atlas6_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w, 119 + struct snd_kcontrol *kcontrol, int event) 120 + { 121 + #define ATLAS6_CODEC_ENABLE_BITS (1 << 29) 122 + #define ATLAS6_CODEC_RESET_BITS (1 << 28) 123 + struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(w->codec->dev); 124 + switch (event) { 125 + case SND_SOC_DAPM_PRE_PMU: 126 + enable_and_reset_codec(sirf_audio_codec->regmap, 127 + ATLAS6_CODEC_ENABLE_BITS, ATLAS6_CODEC_RESET_BITS); 128 + break; 129 + case SND_SOC_DAPM_POST_PMD: 130 + regmap_update_bits(sirf_audio_codec->regmap, 131 + AUDIO_IC_CODEC_CTRL1, ATLAS6_CODEC_ENABLE_BITS, 132 + ~ATLAS6_CODEC_ENABLE_BITS); 133 + break; 134 + default: 135 + break; 136 + } 137 + 138 + return 0; 139 + } 140 + 141 + static int prima2_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w, 142 + struct snd_kcontrol *kcontrol, int event) 143 + { 144 + #define PRIMA2_CODEC_ENABLE_BITS (1 << 27) 145 + #define PRIMA2_CODEC_RESET_BITS (1 << 26) 146 + struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(w->codec->dev); 147 + switch (event) { 148 + case SND_SOC_DAPM_POST_PMU: 149 + enable_and_reset_codec(sirf_audio_codec->regmap, 150 + PRIMA2_CODEC_ENABLE_BITS, PRIMA2_CODEC_RESET_BITS); 151 + break; 152 + case SND_SOC_DAPM_POST_PMD: 153 + regmap_update_bits(sirf_audio_codec->regmap, 154 + AUDIO_IC_CODEC_CTRL1, PRIMA2_CODEC_ENABLE_BITS, 155 + ~PRIMA2_CODEC_ENABLE_BITS); 156 + break; 157 + default: 158 + break; 159 + } 160 + 161 + return 0; 162 + } 163 + 164 + static const struct snd_soc_dapm_widget atlas6_output_driver_dapm_widgets[] = { 165 + SND_SOC_DAPM_OUT_DRV("HP Left Driver", AUDIO_IC_CODEC_CTRL1, 166 + 25, 0, NULL, 0), 167 + SND_SOC_DAPM_OUT_DRV("HP Right Driver", AUDIO_IC_CODEC_CTRL1, 168 + 26, 0, NULL, 0), 169 + SND_SOC_DAPM_OUT_DRV("Speaker Driver", AUDIO_IC_CODEC_CTRL1, 170 + 27, 0, NULL, 0), 171 + }; 172 + 173 + static const struct snd_soc_dapm_widget prima2_output_driver_dapm_widgets[] = { 174 + SND_SOC_DAPM_OUT_DRV("HP Left Driver", AUDIO_IC_CODEC_CTRL1, 175 + 23, 0, NULL, 0), 176 + SND_SOC_DAPM_OUT_DRV("HP Right Driver", AUDIO_IC_CODEC_CTRL1, 177 + 24, 0, NULL, 0), 178 + SND_SOC_DAPM_OUT_DRV("Speaker Driver", AUDIO_IC_CODEC_CTRL1, 179 + 25, 0, NULL, 0), 180 + }; 181 + 182 + static const struct snd_soc_dapm_widget atlas6_codec_clock_dapm_widget = 183 + SND_SOC_DAPM_SUPPLY("codecclk", SND_SOC_NOPM, 0, 0, 184 + atlas6_codec_enable_and_reset_event, 185 + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD); 186 + 187 + static const struct snd_soc_dapm_widget prima2_codec_clock_dapm_widget = 188 + SND_SOC_DAPM_SUPPLY("codecclk", SND_SOC_NOPM, 0, 0, 189 + prima2_codec_enable_and_reset_event, 190 + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD); 191 + 192 + static const struct snd_soc_dapm_widget sirf_audio_codec_dapm_widgets[] = { 193 + SND_SOC_DAPM_DAC("DAC left", NULL, AUDIO_IC_CODEC_CTRL0, 1, 0), 194 + SND_SOC_DAPM_DAC("DAC right", NULL, AUDIO_IC_CODEC_CTRL0, 0, 0), 195 + SND_SOC_DAPM_SWITCH("Left dac to hp left amp", SND_SOC_NOPM, 0, 0, 196 + &left_dac_to_hp_left_amp_switch_control), 197 + SND_SOC_DAPM_SWITCH("Left dac to hp right amp", SND_SOC_NOPM, 0, 0, 198 + &left_dac_to_hp_right_amp_switch_control), 199 + SND_SOC_DAPM_SWITCH("Right dac to hp left amp", SND_SOC_NOPM, 0, 0, 200 + &right_dac_to_hp_left_amp_switch_control), 201 + SND_SOC_DAPM_SWITCH("Right dac to hp right amp", SND_SOC_NOPM, 0, 0, 202 + &right_dac_to_hp_right_amp_switch_control), 203 + SND_SOC_DAPM_OUT_DRV("HP amp left driver", AUDIO_IC_CODEC_CTRL0, 3, 0, 204 + NULL, 0), 205 + SND_SOC_DAPM_OUT_DRV("HP amp right driver", AUDIO_IC_CODEC_CTRL0, 3, 0, 206 + NULL, 0), 207 + 208 + SND_SOC_DAPM_SWITCH("Left dac to speaker lineout", SND_SOC_NOPM, 0, 0, 209 + &left_dac_to_speaker_lineout_switch_control), 210 + SND_SOC_DAPM_SWITCH("Right dac to speaker lineout", SND_SOC_NOPM, 0, 0, 211 + &right_dac_to_speaker_lineout_switch_control), 212 + SND_SOC_DAPM_OUT_DRV("Speaker amp driver", AUDIO_IC_CODEC_CTRL0, 4, 0, 213 + NULL, 0), 214 + 215 + SND_SOC_DAPM_OUTPUT("HPOUTL"), 216 + SND_SOC_DAPM_OUTPUT("HPOUTR"), 217 + SND_SOC_DAPM_OUTPUT("SPKOUT"), 218 + 219 + SND_SOC_DAPM_ADC_E("ADC left", NULL, AUDIO_IC_CODEC_CTRL1, 8, 0, 220 + adc_enable_delay_event, SND_SOC_DAPM_POST_PMU), 221 + SND_SOC_DAPM_ADC_E("ADC right", NULL, AUDIO_IC_CODEC_CTRL1, 7, 0, 222 + adc_enable_delay_event, SND_SOC_DAPM_POST_PMU), 223 + SND_SOC_DAPM_MIXER("Left PGA mixer", AUDIO_IC_CODEC_CTRL1, 1, 0, 224 + &left_input_path_controls[0], 225 + ARRAY_SIZE(left_input_path_controls)), 226 + SND_SOC_DAPM_MIXER("Right PGA mixer", AUDIO_IC_CODEC_CTRL1, 0, 0, 227 + &right_input_path_controls[0], 228 + ARRAY_SIZE(right_input_path_controls)), 229 + 230 + SND_SOC_DAPM_MUX("Mic input mode mux", SND_SOC_NOPM, 0, 0, 231 + &sirf_audio_codec_input_mode_control), 232 + SND_SOC_DAPM_MICBIAS("Mic Bias", AUDIO_IC_CODEC_PWR, 3, 0), 233 + SND_SOC_DAPM_INPUT("MICIN1"), 234 + SND_SOC_DAPM_INPUT("MICIN2"), 235 + SND_SOC_DAPM_INPUT("LINEIN1"), 236 + SND_SOC_DAPM_INPUT("LINEIN2"), 237 + 238 + SND_SOC_DAPM_SUPPLY("HSL Phase Opposite", AUDIO_IC_CODEC_CTRL0, 239 + 30, 0, NULL, 0), 240 + }; 241 + 242 + static const struct snd_soc_dapm_route sirf_audio_codec_map[] = { 243 + {"SPKOUT", NULL, "Speaker Driver"}, 244 + {"Speaker Driver", NULL, "Speaker amp driver"}, 245 + {"Speaker amp driver", NULL, "Left dac to speaker lineout"}, 246 + {"Speaker amp driver", NULL, "Right dac to speaker lineout"}, 247 + {"Left dac to speaker lineout", "Switch", "DAC left"}, 248 + {"Right dac to speaker lineout", "Switch", "DAC right"}, 249 + {"HPOUTL", NULL, "HP Left Driver"}, 250 + {"HPOUTR", NULL, "HP Right Driver"}, 251 + {"HP Left Driver", NULL, "HP amp left driver"}, 252 + {"HP Right Driver", NULL, "HP amp right driver"}, 253 + {"HP amp left driver", NULL, "Right dac to hp left amp"}, 254 + {"HP amp right driver", NULL , "Right dac to hp right amp"}, 255 + {"HP amp left driver", NULL, "Left dac to hp left amp"}, 256 + {"HP amp right driver", NULL , "Right dac to hp right amp"}, 257 + {"Right dac to hp left amp", "Switch", "DAC left"}, 258 + {"Right dac to hp right amp", "Switch", "DAC right"}, 259 + {"Left dac to hp left amp", "Switch", "DAC left"}, 260 + {"Left dac to hp right amp", "Switch", "DAC right"}, 261 + {"DAC left", NULL, "codecclk"}, 262 + {"DAC right", NULL, "codecclk"}, 263 + {"DAC left", NULL, "Playback"}, 264 + {"DAC right", NULL, "Playback"}, 265 + {"DAC left", NULL, "HSL Phase Opposite"}, 266 + {"DAC right", NULL, "HSL Phase Opposite"}, 267 + 268 + {"Capture", NULL, "ADC left"}, 269 + {"Capture", NULL, "ADC right"}, 270 + {"ADC left", NULL, "codecclk"}, 271 + {"ADC right", NULL, "codecclk"}, 272 + {"ADC left", NULL, "Left PGA mixer"}, 273 + {"ADC right", NULL, "Right PGA mixer"}, 274 + {"Left PGA mixer", "Line Left Switch", "LINEIN2"}, 275 + {"Right PGA mixer", "Line Right Switch", "LINEIN1"}, 276 + {"Left PGA mixer", "Mic Left Switch", "MICIN2"}, 277 + {"Right PGA mixer", "Mic Right Switch", "Mic input mode mux"}, 278 + {"Mic input mode mux", "Single-ended", "MICIN1"}, 279 + {"Mic input mode mux", "Differential", "MICIN1"}, 280 + }; 281 + 282 + static int sirf_audio_codec_trigger(struct snd_pcm_substream *substream, 283 + int cmd, 284 + struct snd_soc_dai *dai) 285 + { 286 + int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 287 + struct snd_soc_codec *codec = dai->codec; 288 + u32 val = 0; 289 + 290 + /* 291 + * This is a workaround, When stop playback, 292 + * need disable HP amp, avoid the current noise. 293 + */ 294 + switch (cmd) { 295 + case SNDRV_PCM_TRIGGER_STOP: 296 + case SNDRV_PCM_TRIGGER_SUSPEND: 297 + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 298 + break; 299 + case SNDRV_PCM_TRIGGER_START: 300 + case SNDRV_PCM_TRIGGER_RESUME: 301 + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 302 + if (playback) 303 + val = IC_HSLEN | IC_HSREN; 304 + break; 305 + default: 306 + return -EINVAL; 307 + } 308 + 309 + if (playback) 310 + snd_soc_update_bits(codec, AUDIO_IC_CODEC_CTRL0, 311 + IC_HSLEN | IC_HSREN, val); 312 + return 0; 313 + } 314 + 315 + struct snd_soc_dai_ops sirf_audio_codec_dai_ops = { 316 + .trigger = sirf_audio_codec_trigger, 317 + }; 318 + 319 + struct snd_soc_dai_driver sirf_audio_codec_dai = { 320 + .name = "sirf-audio-codec", 321 + .playback = { 322 + .stream_name = "Playback", 323 + .channels_min = 2, 324 + .channels_max = 2, 325 + .rates = SNDRV_PCM_RATE_48000, 326 + .formats = SNDRV_PCM_FMTBIT_S16_LE, 327 + }, 328 + .capture = { 329 + .stream_name = "Capture", 330 + .channels_min = 1, 331 + .channels_max = 2, 332 + .rates = SNDRV_PCM_RATE_48000, 333 + .formats = SNDRV_PCM_FMTBIT_S16_LE, 334 + }, 335 + .ops = &sirf_audio_codec_dai_ops, 336 + }; 337 + 338 + static int sirf_audio_codec_probe(struct snd_soc_codec *codec) 339 + { 340 + int ret; 341 + struct snd_soc_dapm_context *dapm = &codec->dapm; 342 + struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec); 343 + 344 + pm_runtime_enable(codec->dev); 345 + codec->control_data = sirf_audio_codec->regmap; 346 + 347 + ret = snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP); 348 + if (ret != 0) { 349 + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); 350 + return ret; 351 + } 352 + 353 + if (of_device_is_compatible(codec->dev->of_node, "sirf,prima2-audio-codec")) { 354 + snd_soc_dapm_new_controls(dapm, 355 + prima2_output_driver_dapm_widgets, 356 + ARRAY_SIZE(prima2_output_driver_dapm_widgets)); 357 + snd_soc_dapm_new_controls(dapm, 358 + &prima2_codec_clock_dapm_widget, 1); 359 + return snd_soc_add_codec_controls(codec, 360 + volume_controls_prima2, 361 + ARRAY_SIZE(volume_controls_prima2)); 362 + } 363 + if (of_device_is_compatible(codec->dev->of_node, "sirf,atlas6-audio-codec")) { 364 + snd_soc_dapm_new_controls(dapm, 365 + atlas6_output_driver_dapm_widgets, 366 + ARRAY_SIZE(atlas6_output_driver_dapm_widgets)); 367 + snd_soc_dapm_new_controls(dapm, 368 + &atlas6_codec_clock_dapm_widget, 1); 369 + return snd_soc_add_codec_controls(codec, 370 + volume_controls_atlas6, 371 + ARRAY_SIZE(volume_controls_atlas6)); 372 + } 373 + 374 + return -EINVAL; 375 + } 376 + 377 + static int sirf_audio_codec_remove(struct snd_soc_codec *codec) 378 + { 379 + pm_runtime_disable(codec->dev); 380 + return 0; 381 + } 382 + 383 + static struct snd_soc_codec_driver soc_codec_device_sirf_audio_codec = { 384 + .probe = sirf_audio_codec_probe, 385 + .remove = sirf_audio_codec_remove, 386 + .dapm_widgets = sirf_audio_codec_dapm_widgets, 387 + .num_dapm_widgets = ARRAY_SIZE(sirf_audio_codec_dapm_widgets), 388 + .dapm_routes = sirf_audio_codec_map, 389 + .num_dapm_routes = ARRAY_SIZE(sirf_audio_codec_map), 390 + .idle_bias_off = true, 391 + }; 392 + 393 + static const struct of_device_id sirf_audio_codec_of_match[] = { 394 + { .compatible = "sirf,prima2-audio-codec" }, 395 + { .compatible = "sirf,atlas6-audio-codec" }, 396 + {} 397 + }; 398 + MODULE_DEVICE_TABLE(of, sirf_audio_codec_of_match); 399 + 400 + static const struct regmap_config sirf_audio_codec_regmap_config = { 401 + .reg_bits = 32, 402 + .reg_stride = 4, 403 + .val_bits = 32, 404 + .max_register = AUDIO_IC_CODEC_CTRL3, 405 + .cache_type = REGCACHE_NONE, 406 + }; 407 + 408 + static int sirf_audio_codec_driver_probe(struct platform_device *pdev) 409 + { 410 + int ret; 411 + struct sirf_audio_codec *sirf_audio_codec; 412 + void __iomem *base; 413 + struct resource *mem_res; 414 + const struct of_device_id *match; 415 + 416 + match = of_match_node(sirf_audio_codec_of_match, pdev->dev.of_node); 417 + 418 + sirf_audio_codec = devm_kzalloc(&pdev->dev, 419 + sizeof(struct sirf_audio_codec), GFP_KERNEL); 420 + if (!sirf_audio_codec) 421 + return -ENOMEM; 422 + 423 + platform_set_drvdata(pdev, sirf_audio_codec); 424 + 425 + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 426 + base = devm_ioremap_resource(&pdev->dev, mem_res); 427 + if (base == NULL) 428 + return -ENOMEM; 429 + 430 + sirf_audio_codec->regmap = devm_regmap_init_mmio(&pdev->dev, base, 431 + &sirf_audio_codec_regmap_config); 432 + if (IS_ERR(sirf_audio_codec->regmap)) 433 + return PTR_ERR(sirf_audio_codec->regmap); 434 + 435 + sirf_audio_codec->clk = devm_clk_get(&pdev->dev, NULL); 436 + if (IS_ERR(sirf_audio_codec->clk)) { 437 + dev_err(&pdev->dev, "Get clock failed.\n"); 438 + return PTR_ERR(sirf_audio_codec->clk); 439 + } 440 + 441 + ret = clk_prepare_enable(sirf_audio_codec->clk); 442 + if (ret) { 443 + dev_err(&pdev->dev, "Enable clock failed.\n"); 444 + return ret; 445 + } 446 + 447 + ret = snd_soc_register_codec(&(pdev->dev), 448 + &soc_codec_device_sirf_audio_codec, 449 + &sirf_audio_codec_dai, 1); 450 + if (ret) { 451 + dev_err(&pdev->dev, "Register Audio Codec dai failed.\n"); 452 + goto err_clk_put; 453 + } 454 + 455 + /* 456 + * Always open charge pump, if not, when the charge pump closed the 457 + * adc will not stable 458 + */ 459 + regmap_update_bits(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL0, 460 + IC_CPFREQ, IC_CPFREQ); 461 + 462 + if (of_device_is_compatible(pdev->dev.of_node, "sirf,atlas6-audio-codec")) 463 + regmap_update_bits(sirf_audio_codec->regmap, 464 + AUDIO_IC_CODEC_CTRL0, IC_CPEN, IC_CPEN); 465 + return 0; 466 + 467 + err_clk_put: 468 + clk_disable_unprepare(sirf_audio_codec->clk); 469 + return ret; 470 + } 471 + 472 + static int sirf_audio_codec_driver_remove(struct platform_device *pdev) 473 + { 474 + struct sirf_audio_codec *sirf_audio_codec = platform_get_drvdata(pdev); 475 + 476 + clk_disable_unprepare(sirf_audio_codec->clk); 477 + snd_soc_unregister_codec(&(pdev->dev)); 478 + 479 + return 0; 480 + } 481 + 482 + #ifdef CONFIG_PM_SLEEP 483 + static int sirf_audio_codec_suspend(struct device *dev) 484 + { 485 + struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(dev); 486 + 487 + regmap_read(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL0, 488 + &sirf_audio_codec->reg_ctrl0); 489 + regmap_read(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL1, 490 + &sirf_audio_codec->reg_ctrl1); 491 + clk_disable_unprepare(sirf_audio_codec->clk); 492 + 493 + return 0; 494 + } 495 + 496 + static int sirf_audio_codec_resume(struct device *dev) 497 + { 498 + struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(dev); 499 + int ret; 500 + 501 + ret = clk_prepare_enable(sirf_audio_codec->clk); 502 + if (ret) 503 + return ret; 504 + 505 + regmap_write(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL0, 506 + sirf_audio_codec->reg_ctrl0); 507 + regmap_write(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL1, 508 + sirf_audio_codec->reg_ctrl1); 509 + 510 + return 0; 511 + } 512 + #endif 513 + 514 + static const struct dev_pm_ops sirf_audio_codec_pm_ops = { 515 + SET_SYSTEM_SLEEP_PM_OPS(sirf_audio_codec_suspend, sirf_audio_codec_resume) 516 + }; 517 + 518 + static struct platform_driver sirf_audio_codec_driver = { 519 + .driver = { 520 + .name = "sirf-audio-codec", 521 + .owner = THIS_MODULE, 522 + .of_match_table = sirf_audio_codec_of_match, 523 + .pm = &sirf_audio_codec_pm_ops, 524 + }, 525 + .probe = sirf_audio_codec_driver_probe, 526 + .remove = sirf_audio_codec_driver_remove, 527 + }; 528 + 529 + module_platform_driver(sirf_audio_codec_driver); 530 + 531 + MODULE_DESCRIPTION("SiRF audio codec driver"); 532 + MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>"); 533 + MODULE_LICENSE("GPL v2");
+75
sound/soc/codecs/sirf-audio-codec.h
··· 1 + /* 2 + * SiRF inner codec controllers define 3 + * 4 + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. 5 + * 6 + * Licensed under GPLv2 or later. 7 + */ 8 + 9 + #ifndef _SIRF_AUDIO_CODEC_H 10 + #define _SIRF_AUDIO_CODEC_H 11 + 12 + 13 + #define AUDIO_IC_CODEC_PWR (0x00E0) 14 + #define AUDIO_IC_CODEC_CTRL0 (0x00E4) 15 + #define AUDIO_IC_CODEC_CTRL1 (0x00E8) 16 + #define AUDIO_IC_CODEC_CTRL2 (0x00EC) 17 + #define AUDIO_IC_CODEC_CTRL3 (0x00F0) 18 + 19 + #define MICBIASEN (1 << 3) 20 + 21 + #define IC_RDACEN (1 << 0) 22 + #define IC_LDACEN (1 << 1) 23 + #define IC_HSREN (1 << 2) 24 + #define IC_HSLEN (1 << 3) 25 + #define IC_SPEN (1 << 4) 26 + #define IC_CPEN (1 << 5) 27 + 28 + #define IC_HPRSELR (1 << 6) 29 + #define IC_HPLSELR (1 << 7) 30 + #define IC_HPRSELL (1 << 8) 31 + #define IC_HPLSELL (1 << 9) 32 + #define IC_SPSELR (1 << 10) 33 + #define IC_SPSELL (1 << 11) 34 + 35 + #define IC_MONOR (1 << 12) 36 + #define IC_MONOL (1 << 13) 37 + 38 + #define IC_RXOSRSEL (1 << 28) 39 + #define IC_CPFREQ (1 << 29) 40 + #define IC_HSINVEN (1 << 30) 41 + 42 + #define IC_MICINREN (1 << 0) 43 + #define IC_MICINLEN (1 << 1) 44 + #define IC_MICIN1SEL (1 << 2) 45 + #define IC_MICIN2SEL (1 << 3) 46 + #define IC_MICDIFSEL (1 << 4) 47 + #define IC_LINEIN1SEL (1 << 5) 48 + #define IC_LINEIN2SEL (1 << 6) 49 + #define IC_RADCEN (1 << 7) 50 + #define IC_LADCEN (1 << 8) 51 + #define IC_ALM (1 << 9) 52 + 53 + #define IC_DIGMICEN (1 << 22) 54 + #define IC_DIGMICFREQ (1 << 23) 55 + #define IC_ADC14B_12 (1 << 24) 56 + #define IC_FIRDAC_HSL_EN (1 << 25) 57 + #define IC_FIRDAC_HSR_EN (1 << 26) 58 + #define IC_FIRDAC_LOUT_EN (1 << 27) 59 + #define IC_POR (1 << 28) 60 + #define IC_CODEC_CLK_EN (1 << 29) 61 + #define IC_HP_3DB_BOOST (1 << 30) 62 + 63 + #define IC_ADC_LEFT_GAIN_SHIFT 16 64 + #define IC_ADC_RIGHT_GAIN_SHIFT 10 65 + #define IC_ADC_GAIN_MASK 0x3F 66 + #define IC_MIC_MAX_GAIN 0x39 67 + 68 + #define IC_RXPGAR_MASK 0x3F 69 + #define IC_RXPGAR_SHIFT 14 70 + #define IC_RXPGAL_MASK 0x3F 71 + #define IC_RXPGAL_SHIFT 21 72 + #define IC_RXPGAR 0x7B 73 + #define IC_RXPGAL 0x7B 74 + 75 + #endif /*__SIRF_AUDIO_CODEC_H*/