| OLD | NEW |
| 1 /* | 1 /* |
| 2 * wm8978.c -- WM8978 ALSA SoC Audio Codec driver | 2 * wm8978.c -- WM8978 ALSA SoC Audio Codec driver |
| 3 * | 3 * |
| 4 * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> | 4 * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> |
| 5 * Copyright (C) 2007 Carlos Munoz <carlos@kenati.com> | 5 * Copyright (C) 2007 Carlos Munoz <carlos@kenati.com> |
| 6 * Copyright 2006-2009 Wolfson Microelectronics PLC. | 6 * Copyright 2006-2009 Wolfson Microelectronics PLC. |
| 7 * Based on wm8974 and wm8990 by Liam Girdwood <lrg@slimlogic.co.uk> | 7 * Based on wm8974 and wm8990 by Liam Girdwood <lrg@slimlogic.co.uk> |
| 8 * | 8 * |
| 9 * This program is free software; you can redistribute it and/or modify | 9 * This program is free software; you can redistribute it and/or modify |
| 10 * it under the terms of the GNU General Public License version 2 as | 10 * it under the terms of the GNU General Public License version 2 as |
| 11 * published by the Free Software Foundation. | 11 * published by the Free Software Foundation. |
| 12 */ | 12 */ |
| 13 | 13 |
| 14 #include <linux/module.h> | 14 #include <linux/module.h> |
| 15 #include <linux/moduleparam.h> | 15 #include <linux/moduleparam.h> |
| 16 #include <linux/kernel.h> | 16 #include <linux/kernel.h> |
| 17 #include <linux/init.h> | 17 #include <linux/init.h> |
| 18 #include <linux/delay.h> | 18 #include <linux/delay.h> |
| 19 #include <linux/pm.h> | 19 #include <linux/pm.h> |
| 20 #include <linux/i2c.h> | 20 #include <linux/i2c.h> |
| 21 #include <linux/platform_device.h> | 21 #include <linux/platform_device.h> |
| 22 #include <linux/slab.h> | 22 #include <linux/slab.h> |
| 23 #include <sound/core.h> | 23 #include <sound/core.h> |
| 24 #include <sound/pcm.h> | 24 #include <sound/pcm.h> |
| 25 #include <sound/pcm_params.h> | 25 #include <sound/pcm_params.h> |
| 26 #include <sound/soc.h> | 26 #include <sound/soc.h> |
| 27 #include <sound/soc-dapm.h> | |
| 28 #include <sound/initval.h> | 27 #include <sound/initval.h> |
| 29 #include <sound/tlv.h> | 28 #include <sound/tlv.h> |
| 30 #include <asm/div64.h> | 29 #include <asm/div64.h> |
| 31 | 30 |
| 32 #include "wm8978.h" | 31 #include "wm8978.h" |
| 33 | 32 |
| 34 /* wm8978 register cache. Note that register 0 is not included in the cache. */ | 33 /* wm8978 register cache. Note that register 0 is not included in the cache. */ |
| 35 static const u16 wm8978_reg[WM8978_CACHEREGNUM] = { | 34 static const u16 wm8978_reg[WM8978_CACHEREGNUM] = { |
| 36 0x0000, 0x0000, 0x0000, 0x0000, /* 0x00...0x03 */ | 35 0x0000, 0x0000, 0x0000, 0x0000, /* 0x00...0x03 */ |
| 37 0x0050, 0x0000, 0x0140, 0x0000, /* 0x04...0x07 */ | 36 0x0050, 0x0000, 0x0140, 0x0000, /* 0x04...0x07 */ |
| (...skipping 15 matching lines...) Expand all Loading... |
| 53 /* codec private data */ | 52 /* codec private data */ |
| 54 struct wm8978_priv { | 53 struct wm8978_priv { |
| 55 enum snd_soc_control_type control_type; | 54 enum snd_soc_control_type control_type; |
| 56 void *control_data; | 55 void *control_data; |
| 57 unsigned int f_pllout; | 56 unsigned int f_pllout; |
| 58 unsigned int f_mclk; | 57 unsigned int f_mclk; |
| 59 unsigned int f_256fs; | 58 unsigned int f_256fs; |
| 60 unsigned int f_opclk; | 59 unsigned int f_opclk; |
| 61 int mclk_idx; | 60 int mclk_idx; |
| 62 enum wm8978_sysclk_src sysclk; | 61 enum wm8978_sysclk_src sysclk; |
| 63 u16 reg_cache[WM8978_CACHEREGNUM]; | |
| 64 }; | 62 }; |
| 65 | 63 |
| 66 static const char *wm8978_companding[] = {"Off", "NC", "u-law", "A-law"}; | 64 static const char *wm8978_companding[] = {"Off", "NC", "u-law", "A-law"}; |
| 67 static const char *wm8978_eqmode[] = {"Capture", "Playback"}; | 65 static const char *wm8978_eqmode[] = {"Capture", "Playback"}; |
| 68 static const char *wm8978_bw[] = {"Narrow", "Wide"}; | 66 static const char *wm8978_bw[] = {"Narrow", "Wide"}; |
| 69 static const char *wm8978_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz"}; | 67 static const char *wm8978_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz"}; |
| 70 static const char *wm8978_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz"}; | 68 static const char *wm8978_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz"}; |
| 71 static const char *wm8978_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz"}; | 69 static const char *wm8978_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz"}; |
| 72 static const char *wm8978_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz"}; | 70 static const char *wm8978_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz"}; |
| 73 static const char *wm8978_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz"}; | 71 static const char *wm8978_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz"}; |
| (...skipping 274 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 348 {"Right Input Mixer", "MicN Switch", "RMICN"}, | 346 {"Right Input Mixer", "MicN Switch", "RMICN"}, |
| 349 {"Right Input Mixer", "MicP Switch", "RMICP"}, | 347 {"Right Input Mixer", "MicP Switch", "RMICP"}, |
| 350 | 348 |
| 351 {"Left Input Mixer", "L2 Switch", "L2"}, | 349 {"Left Input Mixer", "L2 Switch", "L2"}, |
| 352 {"Left Input Mixer", "MicN Switch", "LMICN"}, | 350 {"Left Input Mixer", "MicN Switch", "LMICN"}, |
| 353 {"Left Input Mixer", "MicP Switch", "LMICP"}, | 351 {"Left Input Mixer", "MicP Switch", "LMICP"}, |
| 354 }; | 352 }; |
| 355 | 353 |
| 356 static int wm8978_add_widgets(struct snd_soc_codec *codec) | 354 static int wm8978_add_widgets(struct snd_soc_codec *codec) |
| 357 { | 355 { |
| 358 » snd_soc_dapm_new_controls(codec, wm8978_dapm_widgets, | 356 » struct snd_soc_dapm_context *dapm = &codec->dapm; |
| 357 |
| 358 » snd_soc_dapm_new_controls(dapm, wm8978_dapm_widgets, |
| 359 ARRAY_SIZE(wm8978_dapm_widgets)); | 359 ARRAY_SIZE(wm8978_dapm_widgets)); |
| 360 | |
| 361 /* set up the WM8978 audio map */ | 360 /* set up the WM8978 audio map */ |
| 362 » snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | 361 » snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); |
| 363 | 362 |
| 364 return 0; | 363 return 0; |
| 365 } | 364 } |
| 366 | 365 |
| 367 /* PLL divisors */ | 366 /* PLL divisors */ |
| 368 struct wm8978_pll_div { | 367 struct wm8978_pll_div { |
| 369 u32 k; | 368 u32 k; |
| 370 u8 n; | 369 u8 n; |
| 371 u8 div2; | 370 u8 div2; |
| 372 }; | 371 }; |
| (...skipping 457 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 830 switch (level) { | 829 switch (level) { |
| 831 case SND_SOC_BIAS_ON: | 830 case SND_SOC_BIAS_ON: |
| 832 case SND_SOC_BIAS_PREPARE: | 831 case SND_SOC_BIAS_PREPARE: |
| 833 power1 |= 1; /* VMID 75k */ | 832 power1 |= 1; /* VMID 75k */ |
| 834 snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, power1); | 833 snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, power1); |
| 835 break; | 834 break; |
| 836 case SND_SOC_BIAS_STANDBY: | 835 case SND_SOC_BIAS_STANDBY: |
| 837 /* bit 3: enable bias, bit 2: enable I/O tie off buffer */ | 836 /* bit 3: enable bias, bit 2: enable I/O tie off buffer */ |
| 838 power1 |= 0xc; | 837 power1 |= 0xc; |
| 839 | 838 |
| 840 » » if (codec->bias_level == SND_SOC_BIAS_OFF) { | 839 » » if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { |
| 841 /* Initial cap charge at VMID 5k */ | 840 /* Initial cap charge at VMID 5k */ |
| 842 snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, | 841 snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, |
| 843 power1 | 0x3); | 842 power1 | 0x3); |
| 844 mdelay(100); | 843 mdelay(100); |
| 845 } | 844 } |
| 846 | 845 |
| 847 power1 |= 0x2; /* VMID 500k */ | 846 power1 |= 0x2; /* VMID 500k */ |
| 848 snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, power1); | 847 snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, power1); |
| 849 break; | 848 break; |
| 850 case SND_SOC_BIAS_OFF: | 849 case SND_SOC_BIAS_OFF: |
| 851 /* Preserve PLL - OPCLK may be used by someone */ | 850 /* Preserve PLL - OPCLK may be used by someone */ |
| 852 snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, ~0x20, 0); | 851 snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, ~0x20, 0); |
| 853 snd_soc_write(codec, WM8978_POWER_MANAGEMENT_2, 0); | 852 snd_soc_write(codec, WM8978_POWER_MANAGEMENT_2, 0); |
| 854 snd_soc_write(codec, WM8978_POWER_MANAGEMENT_3, 0); | 853 snd_soc_write(codec, WM8978_POWER_MANAGEMENT_3, 0); |
| 855 break; | 854 break; |
| 856 } | 855 } |
| 857 | 856 |
| 858 dev_dbg(codec->dev, "%s: %d, %x\n", __func__, level, power1); | 857 dev_dbg(codec->dev, "%s: %d, %x\n", __func__, level, power1); |
| 859 | 858 |
| 860 » codec->bias_level = level; | 859 » codec->dapm.bias_level = level; |
| 861 return 0; | 860 return 0; |
| 862 } | 861 } |
| 863 | 862 |
| 864 #define WM8978_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ | 863 #define WM8978_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ |
| 865 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) | 864 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) |
| 866 | 865 |
| 867 static struct snd_soc_dai_ops wm8978_dai_ops = { | 866 static struct snd_soc_dai_ops wm8978_dai_ops = { |
| 868 .hw_params = wm8978_hw_params, | 867 .hw_params = wm8978_hw_params, |
| 869 .digital_mute = wm8978_mute, | 868 .digital_mute = wm8978_mute, |
| 870 .set_fmt = wm8978_set_dai_fmt, | 869 .set_fmt = wm8978_set_dai_fmt, |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 959 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); | 958 dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); |
| 960 return ret; | 959 return ret; |
| 961 } | 960 } |
| 962 | 961 |
| 963 /* | 962 /* |
| 964 * Set the update bit in all registers, that have one. This way all | 963 * Set the update bit in all registers, that have one. This way all |
| 965 * writes to those registers will also cause the update bit to be | 964 * writes to those registers will also cause the update bit to be |
| 966 * written. | 965 * written. |
| 967 */ | 966 */ |
| 968 for (i = 0; i < ARRAY_SIZE(update_reg); i++) | 967 for (i = 0; i < ARRAY_SIZE(update_reg); i++) |
| 969 » » ((u16 *)codec->reg_cache)[update_reg[i]] |= 0x100; | 968 » » snd_soc_update_bits(codec, update_reg[i], 0x100, 0x100); |
| 970 | 969 |
| 971 /* Reset the codec */ | 970 /* Reset the codec */ |
| 972 ret = snd_soc_write(codec, WM8978_RESET, 0); | 971 ret = snd_soc_write(codec, WM8978_RESET, 0); |
| 973 if (ret < 0) { | 972 if (ret < 0) { |
| 974 dev_err(codec->dev, "Failed to issue reset\n"); | 973 dev_err(codec->dev, "Failed to issue reset\n"); |
| 975 return ret; | 974 return ret; |
| 976 } | 975 } |
| 977 | 976 |
| 978 wm8978_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 977 wm8978_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
| 979 | 978 |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1065 { | 1064 { |
| 1066 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | 1065 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) |
| 1067 i2c_del_driver(&wm8978_i2c_driver); | 1066 i2c_del_driver(&wm8978_i2c_driver); |
| 1068 #endif | 1067 #endif |
| 1069 } | 1068 } |
| 1070 module_exit(wm8978_exit); | 1069 module_exit(wm8978_exit); |
| 1071 | 1070 |
| 1072 MODULE_DESCRIPTION("ASoC WM8978 codec driver"); | 1071 MODULE_DESCRIPTION("ASoC WM8978 codec driver"); |
| 1073 MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); | 1072 MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); |
| 1074 MODULE_LICENSE("GPL"); | 1073 MODULE_LICENSE("GPL"); |
| OLD | NEW |