diff options
Diffstat (limited to 'sound')
| -rw-r--r-- | sound/soc/codecs/stac9766.c | 162 | ||||
| -rw-r--r-- | sound/soc/codecs/stac9766.h | 17 | ||||
| -rw-r--r-- | sound/soc/codecs/sti-sas.c | 179 | ||||
| -rw-r--r-- | sound/soc/fsl/efika-audio-fabric.c | 1 | ||||
| -rw-r--r-- | sound/soc/sti/sti_uniperif.c | 43 | ||||
| -rw-r--r-- | sound/soc/sti/uniperif.h | 2 | ||||
| -rw-r--r-- | sound/soc/sti/uniperif_player.c | 91 | ||||
| -rw-r--r-- | sound/soc/sti/uniperif_reader.c | 41 | ||||
| -rw-r--r-- | sound/soc/sunxi/Kconfig | 8 | ||||
| -rw-r--r-- | sound/soc/sunxi/Makefile | 1 | ||||
| -rw-r--r-- | sound/soc/sunxi/sun4i-codec.c | 867 | ||||
| -rw-r--r-- | sound/soc/sunxi/sun4i-i2s.c | 105 | ||||
| -rw-r--r-- | sound/soc/sunxi/sun8i-codec-analog.c | 665 | ||||
| -rw-r--r-- | sound/soc/tegra/tegra_alc5632.c | 2 | ||||
| -rw-r--r-- | sound/soc/tegra/tegra_max98090.c | 2 | ||||
| -rw-r--r-- | sound/soc/tegra/tegra_rt5640.c | 2 | ||||
| -rw-r--r-- | sound/soc/tegra/tegra_rt5677.c | 2 | ||||
| -rw-r--r-- | sound/soc/tegra/tegra_sgtl5000.c | 2 | ||||
| -rw-r--r-- | sound/soc/tegra/tegra_wm8753.c | 2 | ||||
| -rw-r--r-- | sound/soc/tegra/tegra_wm8903.c | 2 | ||||
| -rw-r--r-- | sound/soc/tegra/trimslice.c | 2 |
21 files changed, 1737 insertions, 461 deletions
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c index 27f30d352867..9de7fe8af255 100644 --- a/sound/soc/codecs/stac9766.c +++ b/sound/soc/codecs/stac9766.c | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
| 19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
| 20 | #include <linux/device.h> | 20 | #include <linux/device.h> |
| 21 | #include <linux/regmap.h> | ||
| 21 | #include <sound/core.h> | 22 | #include <sound/core.h> |
| 22 | #include <sound/pcm.h> | 23 | #include <sound/pcm.h> |
| 23 | #include <sound/ac97_codec.h> | 24 | #include <sound/ac97_codec.h> |
| @@ -26,31 +27,56 @@ | |||
| 26 | #include <sound/soc.h> | 27 | #include <sound/soc.h> |
| 27 | #include <sound/tlv.h> | 28 | #include <sound/tlv.h> |
| 28 | 29 | ||
| 29 | #include "stac9766.h" | ||
| 30 | |||
| 31 | #define STAC9766_VENDOR_ID 0x83847666 | 30 | #define STAC9766_VENDOR_ID 0x83847666 |
| 32 | #define STAC9766_VENDOR_ID_MASK 0xffffffff | 31 | #define STAC9766_VENDOR_ID_MASK 0xffffffff |
| 33 | 32 | ||
| 34 | /* | 33 | #define AC97_STAC_DA_CONTROL 0x6A |
| 35 | * STAC9766 register cache | 34 | #define AC97_STAC_ANALOG_SPECIAL 0x6E |
| 36 | */ | 35 | #define AC97_STAC_STEREO_MIC 0x78 |
| 37 | static const u16 stac9766_reg[] = { | 36 | |
| 38 | 0x6A90, 0x8000, 0x8000, 0x8000, /* 6 */ | 37 | static const struct reg_default stac9766_reg_defaults[] = { |
| 39 | 0x0000, 0x0000, 0x8008, 0x8008, /* e */ | 38 | { 0x02, 0x8000 }, |
| 40 | 0x8808, 0x8808, 0x8808, 0x8808, /* 16 */ | 39 | { 0x04, 0x8000 }, |
| 41 | 0x8808, 0x0000, 0x8000, 0x0000, /* 1e */ | 40 | { 0x06, 0x8000 }, |
| 42 | 0x0000, 0x0000, 0x0000, 0x000f, /* 26 */ | 41 | { 0x0a, 0x0000 }, |
| 43 | 0x0a05, 0x0400, 0xbb80, 0x0000, /* 2e */ | 42 | { 0x0c, 0x8008 }, |
| 44 | 0x0000, 0xbb80, 0x0000, 0x0000, /* 36 */ | 43 | { 0x0e, 0x8008 }, |
| 45 | 0x0000, 0x2000, 0x0000, 0x0100, /* 3e */ | 44 | { 0x10, 0x8808 }, |
| 46 | 0x0000, 0x0000, 0x0080, 0x0000, /* 46 */ | 45 | { 0x12, 0x8808 }, |
| 47 | 0x0000, 0x0000, 0x0003, 0xffff, /* 4e */ | 46 | { 0x14, 0x8808 }, |
| 48 | 0x0000, 0x0000, 0x0000, 0x0000, /* 56 */ | 47 | { 0x16, 0x8808 }, |
| 49 | 0x4000, 0x0000, 0x0000, 0x0000, /* 5e */ | 48 | { 0x18, 0x8808 }, |
| 50 | 0x1201, 0xFFFF, 0xFFFF, 0x0000, /* 66 */ | 49 | { 0x1a, 0x0000 }, |
| 51 | 0x0000, 0x0000, 0x0000, 0x0000, /* 6e */ | 50 | { 0x1c, 0x8000 }, |
| 52 | 0x0000, 0x0000, 0x0000, 0x0006, /* 76 */ | 51 | { 0x20, 0x0000 }, |
| 53 | 0x0000, 0x0000, 0x0000, 0x0000, /* 7e */ | 52 | { 0x22, 0x0000 }, |
| 53 | { 0x28, 0x0a05 }, | ||
| 54 | { 0x2c, 0xbb80 }, | ||
| 55 | { 0x32, 0xbb80 }, | ||
| 56 | { 0x3a, 0x2000 }, | ||
| 57 | { 0x3e, 0x0100 }, | ||
| 58 | { 0x4c, 0x0300 }, | ||
| 59 | { 0x4e, 0xffff }, | ||
| 60 | { 0x50, 0x0000 }, | ||
| 61 | { 0x52, 0x0000 }, | ||
| 62 | { 0x54, 0x0000 }, | ||
| 63 | { 0x6a, 0x0000 }, | ||
| 64 | { 0x6e, 0x1000 }, | ||
| 65 | { 0x72, 0x0000 }, | ||
| 66 | { 0x78, 0x0000 }, | ||
| 67 | }; | ||
| 68 | |||
| 69 | static const struct regmap_config stac9766_regmap_config = { | ||
| 70 | .reg_bits = 16, | ||
| 71 | .reg_stride = 2, | ||
| 72 | .val_bits = 16, | ||
| 73 | .max_register = 0x78, | ||
| 74 | .cache_type = REGCACHE_RBTREE, | ||
| 75 | |||
| 76 | .volatile_reg = regmap_ac97_default_volatile, | ||
| 77 | |||
| 78 | .reg_defaults = stac9766_reg_defaults, | ||
| 79 | .num_reg_defaults = ARRAY_SIZE(stac9766_reg_defaults), | ||
| 54 | }; | 80 | }; |
| 55 | 81 | ||
| 56 | static const char *stac9766_record_mux[] = {"Mic", "CD", "Video", "AUX", | 82 | static const char *stac9766_record_mux[] = {"Mic", "CD", "Video", "AUX", |
| @@ -139,71 +165,22 @@ static const struct snd_kcontrol_new stac9766_snd_ac97_controls[] = { | |||
| 139 | SOC_ENUM("Pop Bypass Mux", stac9766_popbypass_enum), | 165 | SOC_ENUM("Pop Bypass Mux", stac9766_popbypass_enum), |
| 140 | }; | 166 | }; |
| 141 | 167 | ||
| 142 | static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg, | ||
| 143 | unsigned int val) | ||
| 144 | { | ||
| 145 | struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); | ||
| 146 | u16 *cache = codec->reg_cache; | ||
| 147 | |||
| 148 | if (reg > AC97_STAC_PAGE0) { | ||
| 149 | stac9766_ac97_write(codec, AC97_INT_PAGING, 0); | ||
| 150 | soc_ac97_ops->write(ac97, reg, val); | ||
| 151 | stac9766_ac97_write(codec, AC97_INT_PAGING, 1); | ||
| 152 | return 0; | ||
| 153 | } | ||
| 154 | if (reg / 2 >= ARRAY_SIZE(stac9766_reg)) | ||
| 155 | return -EIO; | ||
| 156 | |||
| 157 | soc_ac97_ops->write(ac97, reg, val); | ||
| 158 | cache[reg / 2] = val; | ||
| 159 | return 0; | ||
| 160 | } | ||
| 161 | |||
| 162 | static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec, | ||
| 163 | unsigned int reg) | ||
| 164 | { | ||
| 165 | struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); | ||
| 166 | u16 val = 0, *cache = codec->reg_cache; | ||
| 167 | |||
| 168 | if (reg > AC97_STAC_PAGE0) { | ||
| 169 | stac9766_ac97_write(codec, AC97_INT_PAGING, 0); | ||
| 170 | val = soc_ac97_ops->read(ac97, reg - AC97_STAC_PAGE0); | ||
| 171 | stac9766_ac97_write(codec, AC97_INT_PAGING, 1); | ||
| 172 | return val; | ||
| 173 | } | ||
| 174 | if (reg / 2 >= ARRAY_SIZE(stac9766_reg)) | ||
| 175 | return -EIO; | ||
| 176 | |||
| 177 | if (reg == AC97_RESET || reg == AC97_GPIO_STATUS || | ||
| 178 | reg == AC97_INT_PAGING || reg == AC97_VENDOR_ID1 || | ||
| 179 | reg == AC97_VENDOR_ID2) { | ||
| 180 | |||
| 181 | val = soc_ac97_ops->read(ac97, reg); | ||
| 182 | return val; | ||
| 183 | } | ||
| 184 | return cache[reg / 2]; | ||
| 185 | } | ||
| 186 | |||
| 187 | static int ac97_analog_prepare(struct snd_pcm_substream *substream, | 168 | static int ac97_analog_prepare(struct snd_pcm_substream *substream, |
| 188 | struct snd_soc_dai *dai) | 169 | struct snd_soc_dai *dai) |
| 189 | { | 170 | { |
| 190 | struct snd_soc_codec *codec = dai->codec; | 171 | struct snd_soc_codec *codec = dai->codec; |
| 191 | struct snd_pcm_runtime *runtime = substream->runtime; | 172 | struct snd_pcm_runtime *runtime = substream->runtime; |
| 192 | unsigned short reg, vra; | 173 | unsigned short reg; |
| 193 | |||
| 194 | vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS); | ||
| 195 | 174 | ||
| 196 | vra |= 0x1; /* enable variable rate audio */ | 175 | /* enable variable rate audio, disable SPDIF output */ |
| 197 | vra &= ~0x4; /* disable SPDIF output */ | 176 | snd_soc_update_bits(codec, AC97_EXTENDED_STATUS, 0x5, 0x1); |
| 198 | |||
| 199 | stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra); | ||
| 200 | 177 | ||
| 201 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 178 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
| 202 | reg = AC97_PCM_FRONT_DAC_RATE; | 179 | reg = AC97_PCM_FRONT_DAC_RATE; |
| 203 | else | 180 | else |
| 204 | reg = AC97_PCM_LR_ADC_RATE; | 181 | reg = AC97_PCM_LR_ADC_RATE; |
| 205 | 182 | ||
| 206 | return stac9766_ac97_write(codec, reg, runtime->rate); | 183 | return snd_soc_write(codec, reg, runtime->rate); |
| 207 | } | 184 | } |
| 208 | 185 | ||
| 209 | static int ac97_digital_prepare(struct snd_pcm_substream *substream, | 186 | static int ac97_digital_prepare(struct snd_pcm_substream *substream, |
| @@ -211,18 +188,16 @@ static int ac97_digital_prepare(struct snd_pcm_substream *substream, | |||
| 211 | { | 188 | { |
| 212 | struct snd_soc_codec *codec = dai->codec; | 189 | struct snd_soc_codec *codec = dai->codec; |
| 213 | struct snd_pcm_runtime *runtime = substream->runtime; | 190 | struct snd_pcm_runtime *runtime = substream->runtime; |
| 214 | unsigned short reg, vra; | 191 | unsigned short reg; |
| 215 | |||
| 216 | stac9766_ac97_write(codec, AC97_SPDIF, 0x2002); | ||
| 217 | 192 | ||
| 218 | vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS); | 193 | snd_soc_write(codec, AC97_SPDIF, 0x2002); |
| 219 | vra |= 0x5; /* Enable VRA and SPDIF out */ | ||
| 220 | 194 | ||
| 221 | stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra); | 195 | /* Enable VRA and SPDIF out */ |
| 196 | snd_soc_update_bits(codec, AC97_EXTENDED_STATUS, 0x5, 0x5); | ||
| 222 | 197 | ||
| 223 | reg = AC97_PCM_FRONT_DAC_RATE; | 198 | reg = AC97_PCM_FRONT_DAC_RATE; |
| 224 | 199 | ||
| 225 | return stac9766_ac97_write(codec, reg, runtime->rate); | 200 | return snd_soc_write(codec, reg, runtime->rate); |
| 226 | } | 201 | } |
| 227 | 202 | ||
| 228 | static int stac9766_set_bias_level(struct snd_soc_codec *codec, | 203 | static int stac9766_set_bias_level(struct snd_soc_codec *codec, |
| @@ -232,11 +207,11 @@ static int stac9766_set_bias_level(struct snd_soc_codec *codec, | |||
| 232 | case SND_SOC_BIAS_ON: /* full On */ | 207 | case SND_SOC_BIAS_ON: /* full On */ |
| 233 | case SND_SOC_BIAS_PREPARE: /* partial On */ | 208 | case SND_SOC_BIAS_PREPARE: /* partial On */ |
| 234 | case SND_SOC_BIAS_STANDBY: /* Off, with power */ | 209 | case SND_SOC_BIAS_STANDBY: /* Off, with power */ |
| 235 | stac9766_ac97_write(codec, AC97_POWERDOWN, 0x0000); | 210 | snd_soc_write(codec, AC97_POWERDOWN, 0x0000); |
| 236 | break; | 211 | break; |
| 237 | case SND_SOC_BIAS_OFF: /* Off, without power */ | 212 | case SND_SOC_BIAS_OFF: /* Off, without power */ |
| 238 | /* disable everything including AC link */ | 213 | /* disable everything including AC link */ |
| 239 | stac9766_ac97_write(codec, AC97_POWERDOWN, 0xffff); | 214 | snd_soc_write(codec, AC97_POWERDOWN, 0xffff); |
| 240 | break; | 215 | break; |
| 241 | } | 216 | } |
| 242 | return 0; | 217 | return 0; |
| @@ -300,21 +275,34 @@ static struct snd_soc_dai_driver stac9766_dai[] = { | |||
| 300 | static int stac9766_codec_probe(struct snd_soc_codec *codec) | 275 | static int stac9766_codec_probe(struct snd_soc_codec *codec) |
| 301 | { | 276 | { |
| 302 | struct snd_ac97 *ac97; | 277 | struct snd_ac97 *ac97; |
| 278 | struct regmap *regmap; | ||
| 279 | int ret; | ||
| 303 | 280 | ||
| 304 | ac97 = snd_soc_new_ac97_codec(codec, STAC9766_VENDOR_ID, | 281 | ac97 = snd_soc_new_ac97_codec(codec, STAC9766_VENDOR_ID, |
| 305 | STAC9766_VENDOR_ID_MASK); | 282 | STAC9766_VENDOR_ID_MASK); |
| 306 | if (IS_ERR(ac97)) | 283 | if (IS_ERR(ac97)) |
| 307 | return PTR_ERR(ac97); | 284 | return PTR_ERR(ac97); |
| 308 | 285 | ||
| 286 | regmap = regmap_init_ac97(ac97, &stac9766_regmap_config); | ||
| 287 | if (IS_ERR(regmap)) { | ||
| 288 | ret = PTR_ERR(regmap); | ||
| 289 | goto err_free_ac97; | ||
| 290 | } | ||
| 291 | |||
| 292 | snd_soc_codec_init_regmap(codec, regmap); | ||
| 309 | snd_soc_codec_set_drvdata(codec, ac97); | 293 | snd_soc_codec_set_drvdata(codec, ac97); |
| 310 | 294 | ||
| 311 | return 0; | 295 | return 0; |
| 296 | err_free_ac97: | ||
| 297 | snd_soc_free_ac97_codec(ac97); | ||
| 298 | return ret; | ||
| 312 | } | 299 | } |
| 313 | 300 | ||
| 314 | static int stac9766_codec_remove(struct snd_soc_codec *codec) | 301 | static int stac9766_codec_remove(struct snd_soc_codec *codec) |
| 315 | { | 302 | { |
| 316 | struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); | 303 | struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); |
| 317 | 304 | ||
| 305 | snd_soc_codec_exit_regmap(codec); | ||
| 318 | snd_soc_free_ac97_codec(ac97); | 306 | snd_soc_free_ac97_codec(ac97); |
| 319 | return 0; | 307 | return 0; |
| 320 | } | 308 | } |
| @@ -324,17 +312,11 @@ static struct snd_soc_codec_driver soc_codec_dev_stac9766 = { | |||
| 324 | .controls = stac9766_snd_ac97_controls, | 312 | .controls = stac9766_snd_ac97_controls, |
| 325 | .num_controls = ARRAY_SIZE(stac9766_snd_ac97_controls), | 313 | .num_controls = ARRAY_SIZE(stac9766_snd_ac97_controls), |
| 326 | }, | 314 | }, |
| 327 | .write = stac9766_ac97_write, | ||
| 328 | .read = stac9766_ac97_read, | ||
| 329 | .set_bias_level = stac9766_set_bias_level, | 315 | .set_bias_level = stac9766_set_bias_level, |
| 330 | .suspend_bias_off = true, | 316 | .suspend_bias_off = true, |
| 331 | .probe = stac9766_codec_probe, | 317 | .probe = stac9766_codec_probe, |
| 332 | .remove = stac9766_codec_remove, | 318 | .remove = stac9766_codec_remove, |
| 333 | .resume = stac9766_codec_resume, | 319 | .resume = stac9766_codec_resume, |
| 334 | .reg_cache_size = ARRAY_SIZE(stac9766_reg), | ||
| 335 | .reg_word_size = sizeof(u16), | ||
| 336 | .reg_cache_step = 2, | ||
| 337 | .reg_cache_default = stac9766_reg, | ||
| 338 | }; | 320 | }; |
| 339 | 321 | ||
| 340 | static int stac9766_probe(struct platform_device *pdev) | 322 | static int stac9766_probe(struct platform_device *pdev) |
diff --git a/sound/soc/codecs/stac9766.h b/sound/soc/codecs/stac9766.h deleted file mode 100644 index c726f907e2c0..000000000000 --- a/sound/soc/codecs/stac9766.h +++ /dev/null | |||
| @@ -1,17 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * stac9766.h -- STAC9766 Soc Audio driver | ||
| 3 | */ | ||
| 4 | |||
| 5 | #ifndef _STAC9766_H | ||
| 6 | #define _STAC9766_H | ||
| 7 | |||
| 8 | #define AC97_STAC_PAGE0 0x1000 | ||
| 9 | #define AC97_STAC_DA_CONTROL (AC97_STAC_PAGE0 | 0x6A) | ||
| 10 | #define AC97_STAC_ANALOG_SPECIAL (AC97_STAC_PAGE0 | 0x6E) | ||
| 11 | #define AC97_STAC_STEREO_MIC 0x78 | ||
| 12 | |||
| 13 | /* STAC9766 DAI ID's */ | ||
| 14 | #define STAC9766_DAI_AC97_ANALOG 0 | ||
| 15 | #define STAC9766_DAI_AC97_DIGITAL 1 | ||
| 16 | |||
| 17 | #endif | ||
diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c index d6e00c77edcd..62c618765224 100644 --- a/sound/soc/codecs/sti-sas.c +++ b/sound/soc/codecs/sti-sas.c | |||
| @@ -14,28 +14,8 @@ | |||
| 14 | #include <sound/soc.h> | 14 | #include <sound/soc.h> |
| 15 | #include <sound/soc-dapm.h> | 15 | #include <sound/soc-dapm.h> |
| 16 | 16 | ||
| 17 | /* chipID supported */ | ||
| 18 | #define CHIPID_STIH416 0 | ||
| 19 | #define CHIPID_STIH407 1 | ||
| 20 | |||
| 21 | /* DAC definitions */ | 17 | /* DAC definitions */ |
| 22 | 18 | ||
| 23 | /* stih416 DAC registers */ | ||
| 24 | /* sysconf 2517: Audio-DAC-Control */ | ||
| 25 | #define STIH416_AUDIO_DAC_CTRL 0x00000814 | ||
| 26 | /* sysconf 2519: Audio-Gue-Control */ | ||
| 27 | #define STIH416_AUDIO_GLUE_CTRL 0x0000081C | ||
| 28 | |||
| 29 | #define STIH416_DAC_NOT_STANDBY 0x3 | ||
| 30 | #define STIH416_DAC_SOFTMUTE 0x4 | ||
| 31 | #define STIH416_DAC_ANA_NOT_PWR 0x5 | ||
| 32 | #define STIH416_DAC_NOT_PNDBG 0x6 | ||
| 33 | |||
| 34 | #define STIH416_DAC_NOT_STANDBY_MASK BIT(STIH416_DAC_NOT_STANDBY) | ||
| 35 | #define STIH416_DAC_SOFTMUTE_MASK BIT(STIH416_DAC_SOFTMUTE) | ||
| 36 | #define STIH416_DAC_ANA_NOT_PWR_MASK BIT(STIH416_DAC_ANA_NOT_PWR) | ||
| 37 | #define STIH416_DAC_NOT_PNDBG_MASK BIT(STIH416_DAC_NOT_PNDBG) | ||
| 38 | |||
| 39 | /* stih407 DAC registers */ | 19 | /* stih407 DAC registers */ |
| 40 | /* sysconf 5041: Audio-Gue-Control */ | 20 | /* sysconf 5041: Audio-Gue-Control */ |
| 41 | #define STIH407_AUDIO_GLUE_CTRL 0x000000A4 | 21 | #define STIH407_AUDIO_GLUE_CTRL 0x000000A4 |
| @@ -63,14 +43,9 @@ enum { | |||
| 63 | STI_SAS_DAI_ANALOG_OUT, | 43 | STI_SAS_DAI_ANALOG_OUT, |
| 64 | }; | 44 | }; |
| 65 | 45 | ||
| 66 | static const struct reg_default stih416_sas_reg_defaults[] = { | ||
| 67 | { STIH407_AUDIO_GLUE_CTRL, 0x00000040 }, | ||
| 68 | { STIH407_AUDIO_DAC_CTRL, 0x000000000 }, | ||
| 69 | }; | ||
| 70 | |||
| 71 | static const struct reg_default stih407_sas_reg_defaults[] = { | 46 | static const struct reg_default stih407_sas_reg_defaults[] = { |
| 72 | { STIH416_AUDIO_DAC_CTRL, 0x000000000 }, | 47 | { STIH407_AUDIO_DAC_CTRL, 0x000000000 }, |
| 73 | { STIH416_AUDIO_GLUE_CTRL, 0x00000040 }, | 48 | { STIH407_AUDIO_GLUE_CTRL, 0x00000040 }, |
| 74 | }; | 49 | }; |
| 75 | 50 | ||
| 76 | struct sti_dac_audio { | 51 | struct sti_dac_audio { |
| @@ -89,7 +64,6 @@ struct sti_spdif_audio { | |||
| 89 | 64 | ||
| 90 | /* device data structure */ | 65 | /* device data structure */ |
| 91 | struct sti_sas_dev_data { | 66 | struct sti_sas_dev_data { |
| 92 | const int chipid; /* IC version */ | ||
| 93 | const struct regmap_config *regmap; | 67 | const struct regmap_config *regmap; |
| 94 | const struct snd_soc_dai_ops *dac_ops; /* DAC function callbacks */ | 68 | const struct snd_soc_dai_ops *dac_ops; /* DAC function callbacks */ |
| 95 | const struct snd_soc_dapm_widget *dapm_widgets; /* dapms declaration */ | 69 | const struct snd_soc_dapm_widget *dapm_widgets; /* dapms declaration */ |
| @@ -150,51 +124,27 @@ static int sti_sas_init_sas_registers(struct snd_soc_codec *codec, | |||
| 150 | ret = snd_soc_update_bits(codec, STIH407_AUDIO_GLUE_CTRL, | 124 | ret = snd_soc_update_bits(codec, STIH407_AUDIO_GLUE_CTRL, |
| 151 | SPDIF_BIPHASE_IDLE_MASK, 0); | 125 | SPDIF_BIPHASE_IDLE_MASK, 0); |
| 152 | if (ret < 0) { | 126 | if (ret < 0) { |
| 153 | dev_err(codec->dev, "Failed to update SPDIF registers"); | 127 | dev_err(codec->dev, "Failed to update SPDIF registers\n"); |
| 154 | return ret; | 128 | return ret; |
| 155 | } | 129 | } |
| 156 | 130 | ||
| 157 | /* Init DAC configuration */ | 131 | /* Init DAC configuration */ |
| 158 | switch (data->dev_data->chipid) { | 132 | /* init configuration */ |
| 159 | case CHIPID_STIH407: | 133 | ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, |
| 160 | /* init configuration */ | 134 | STIH407_DAC_STANDBY_MASK, |
| 161 | ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, | 135 | STIH407_DAC_STANDBY_MASK); |
| 162 | STIH407_DAC_STANDBY_MASK, | 136 | |
| 163 | STIH407_DAC_STANDBY_MASK); | 137 | if (!ret) |
| 164 | 138 | ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, | |
| 165 | if (!ret) | 139 | STIH407_DAC_STANDBY_ANA_MASK, |
| 166 | ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, | 140 | STIH407_DAC_STANDBY_ANA_MASK); |
| 167 | STIH407_DAC_STANDBY_ANA_MASK, | 141 | if (!ret) |
| 168 | STIH407_DAC_STANDBY_ANA_MASK); | 142 | ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, |
| 169 | if (!ret) | 143 | STIH407_DAC_SOFTMUTE_MASK, |
| 170 | ret = snd_soc_update_bits(codec, STIH407_AUDIO_DAC_CTRL, | 144 | STIH407_DAC_SOFTMUTE_MASK); |
| 171 | STIH407_DAC_SOFTMUTE_MASK, | ||
| 172 | STIH407_DAC_SOFTMUTE_MASK); | ||
| 173 | break; | ||
| 174 | case CHIPID_STIH416: | ||
| 175 | ret = snd_soc_update_bits(codec, STIH416_AUDIO_DAC_CTRL, | ||
| 176 | STIH416_DAC_NOT_STANDBY_MASK, 0); | ||
| 177 | if (!ret) | ||
| 178 | ret = snd_soc_update_bits(codec, | ||
| 179 | STIH416_AUDIO_DAC_CTRL, | ||
| 180 | STIH416_DAC_ANA_NOT_PWR, 0); | ||
| 181 | if (!ret) | ||
| 182 | ret = snd_soc_update_bits(codec, | ||
| 183 | STIH416_AUDIO_DAC_CTRL, | ||
| 184 | STIH416_DAC_NOT_PNDBG_MASK, | ||
| 185 | 0); | ||
| 186 | if (!ret) | ||
| 187 | ret = snd_soc_update_bits(codec, | ||
| 188 | STIH416_AUDIO_DAC_CTRL, | ||
| 189 | STIH416_DAC_SOFTMUTE_MASK, | ||
| 190 | STIH416_DAC_SOFTMUTE_MASK); | ||
| 191 | break; | ||
| 192 | default: | ||
| 193 | return -EINVAL; | ||
| 194 | } | ||
| 195 | 145 | ||
| 196 | if (ret < 0) { | 146 | if (ret < 0) { |
| 197 | dev_err(codec->dev, "Failed to update DAC registers"); | 147 | dev_err(codec->dev, "Failed to update DAC registers\n"); |
| 198 | return ret; | 148 | return ret; |
| 199 | } | 149 | } |
| 200 | 150 | ||
| @@ -217,37 +167,6 @@ static int sti_sas_dac_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) | |||
| 217 | return 0; | 167 | return 0; |
| 218 | } | 168 | } |
| 219 | 169 | ||
| 220 | static int stih416_dac_probe(struct snd_soc_dai *dai) | ||
| 221 | { | ||
| 222 | struct snd_soc_codec *codec = dai->codec; | ||
| 223 | struct sti_sas_data *drvdata = dev_get_drvdata(codec->dev); | ||
| 224 | struct sti_dac_audio *dac = &drvdata->dac; | ||
| 225 | |||
| 226 | /* Get reset control */ | ||
| 227 | dac->rst = devm_reset_control_get(codec->dev, "dac_rst"); | ||
| 228 | if (IS_ERR(dac->rst)) { | ||
| 229 | dev_err(dai->codec->dev, | ||
| 230 | "%s: ERROR: DAC reset control not defined !\n", | ||
| 231 | __func__); | ||
| 232 | dac->rst = NULL; | ||
| 233 | return -EFAULT; | ||
| 234 | } | ||
| 235 | /* Put the DAC into reset */ | ||
| 236 | reset_control_assert(dac->rst); | ||
| 237 | |||
| 238 | return 0; | ||
| 239 | } | ||
| 240 | |||
| 241 | static const struct snd_soc_dapm_widget stih416_sas_dapm_widgets[] = { | ||
| 242 | SND_SOC_DAPM_PGA("DAC bandgap", STIH416_AUDIO_DAC_CTRL, | ||
| 243 | STIH416_DAC_NOT_PNDBG_MASK, 0, NULL, 0), | ||
| 244 | SND_SOC_DAPM_OUT_DRV("DAC standby ana", STIH416_AUDIO_DAC_CTRL, | ||
| 245 | STIH416_DAC_ANA_NOT_PWR, 0, NULL, 0), | ||
| 246 | SND_SOC_DAPM_DAC("DAC standby", "dac_p", STIH416_AUDIO_DAC_CTRL, | ||
| 247 | STIH416_DAC_NOT_STANDBY, 0), | ||
| 248 | SND_SOC_DAPM_OUTPUT("DAC Output"), | ||
| 249 | }; | ||
| 250 | |||
| 251 | static const struct snd_soc_dapm_widget stih407_sas_dapm_widgets[] = { | 170 | static const struct snd_soc_dapm_widget stih407_sas_dapm_widgets[] = { |
| 252 | SND_SOC_DAPM_OUT_DRV("DAC standby ana", STIH407_AUDIO_DAC_CTRL, | 171 | SND_SOC_DAPM_OUT_DRV("DAC standby ana", STIH407_AUDIO_DAC_CTRL, |
| 253 | STIH407_DAC_STANDBY_ANA, 1, NULL, 0), | 172 | STIH407_DAC_STANDBY_ANA, 1, NULL, 0), |
| @@ -256,30 +175,11 @@ static const struct snd_soc_dapm_widget stih407_sas_dapm_widgets[] = { | |||
| 256 | SND_SOC_DAPM_OUTPUT("DAC Output"), | 175 | SND_SOC_DAPM_OUTPUT("DAC Output"), |
| 257 | }; | 176 | }; |
| 258 | 177 | ||
| 259 | static const struct snd_soc_dapm_route stih416_sas_route[] = { | ||
| 260 | {"DAC Output", NULL, "DAC bandgap"}, | ||
| 261 | {"DAC Output", NULL, "DAC standby ana"}, | ||
| 262 | {"DAC standby ana", NULL, "DAC standby"}, | ||
| 263 | }; | ||
| 264 | |||
| 265 | static const struct snd_soc_dapm_route stih407_sas_route[] = { | 178 | static const struct snd_soc_dapm_route stih407_sas_route[] = { |
| 266 | {"DAC Output", NULL, "DAC standby ana"}, | 179 | {"DAC Output", NULL, "DAC standby ana"}, |
| 267 | {"DAC standby ana", NULL, "DAC standby"}, | 180 | {"DAC standby ana", NULL, "DAC standby"}, |
| 268 | }; | 181 | }; |
| 269 | 182 | ||
| 270 | static int stih416_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream) | ||
| 271 | { | ||
| 272 | struct snd_soc_codec *codec = dai->codec; | ||
| 273 | |||
| 274 | if (mute) { | ||
| 275 | return snd_soc_update_bits(codec, STIH416_AUDIO_DAC_CTRL, | ||
| 276 | STIH416_DAC_SOFTMUTE_MASK, | ||
| 277 | STIH416_DAC_SOFTMUTE_MASK); | ||
| 278 | } else { | ||
| 279 | return snd_soc_update_bits(codec, STIH416_AUDIO_DAC_CTRL, | ||
| 280 | STIH416_DAC_SOFTMUTE_MASK, 0); | ||
| 281 | } | ||
| 282 | } | ||
| 283 | 183 | ||
| 284 | static int stih407_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream) | 184 | static int stih407_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream) |
| 285 | { | 185 | { |
| @@ -392,13 +292,13 @@ static int sti_sas_prepare(struct snd_pcm_substream *substream, | |||
| 392 | switch (dai->id) { | 292 | switch (dai->id) { |
| 393 | case STI_SAS_DAI_SPDIF_OUT: | 293 | case STI_SAS_DAI_SPDIF_OUT: |
| 394 | if ((drvdata->spdif.mclk / runtime->rate) != 128) { | 294 | if ((drvdata->spdif.mclk / runtime->rate) != 128) { |
| 395 | dev_err(codec->dev, "unexpected mclk-fs ratio"); | 295 | dev_err(codec->dev, "unexpected mclk-fs ratio\n"); |
| 396 | return -EINVAL; | 296 | return -EINVAL; |
| 397 | } | 297 | } |
| 398 | break; | 298 | break; |
| 399 | case STI_SAS_DAI_ANALOG_OUT: | 299 | case STI_SAS_DAI_ANALOG_OUT: |
| 400 | if ((drvdata->dac.mclk / runtime->rate) != 256) { | 300 | if ((drvdata->dac.mclk / runtime->rate) != 256) { |
| 401 | dev_err(codec->dev, "unexpected mclk-fs ratio"); | 301 | dev_err(codec->dev, "unexpected mclk-fs ratio\n"); |
| 402 | return -EINVAL; | 302 | return -EINVAL; |
| 403 | } | 303 | } |
| 404 | break; | 304 | break; |
| @@ -407,13 +307,6 @@ static int sti_sas_prepare(struct snd_pcm_substream *substream, | |||
| 407 | return 0; | 307 | return 0; |
| 408 | } | 308 | } |
| 409 | 309 | ||
| 410 | static const struct snd_soc_dai_ops stih416_dac_ops = { | ||
| 411 | .set_fmt = sti_sas_dac_set_fmt, | ||
| 412 | .mute_stream = stih416_sas_dac_mute, | ||
| 413 | .prepare = sti_sas_prepare, | ||
| 414 | .set_sysclk = sti_sas_set_sysclk, | ||
| 415 | }; | ||
| 416 | |||
| 417 | static const struct snd_soc_dai_ops stih407_dac_ops = { | 310 | static const struct snd_soc_dai_ops stih407_dac_ops = { |
| 418 | .set_fmt = sti_sas_dac_set_fmt, | 311 | .set_fmt = sti_sas_dac_set_fmt, |
| 419 | .mute_stream = stih407_sas_dac_mute, | 312 | .mute_stream = stih407_sas_dac_mute, |
| @@ -434,31 +327,7 @@ static const struct regmap_config stih407_sas_regmap = { | |||
| 434 | .reg_write = sti_sas_write_reg, | 327 | .reg_write = sti_sas_write_reg, |
| 435 | }; | 328 | }; |
| 436 | 329 | ||
| 437 | static const struct regmap_config stih416_sas_regmap = { | ||
| 438 | .reg_bits = 32, | ||
| 439 | .val_bits = 32, | ||
| 440 | |||
| 441 | .max_register = STIH416_AUDIO_DAC_CTRL, | ||
| 442 | .reg_defaults = stih416_sas_reg_defaults, | ||
| 443 | .num_reg_defaults = ARRAY_SIZE(stih416_sas_reg_defaults), | ||
| 444 | .volatile_reg = sti_sas_volatile_register, | ||
| 445 | .cache_type = REGCACHE_RBTREE, | ||
| 446 | .reg_read = sti_sas_read_reg, | ||
| 447 | .reg_write = sti_sas_write_reg, | ||
| 448 | }; | ||
| 449 | |||
| 450 | static const struct sti_sas_dev_data stih416_data = { | ||
| 451 | .chipid = CHIPID_STIH416, | ||
| 452 | .regmap = &stih416_sas_regmap, | ||
| 453 | .dac_ops = &stih416_dac_ops, | ||
| 454 | .dapm_widgets = stih416_sas_dapm_widgets, | ||
| 455 | .num_dapm_widgets = ARRAY_SIZE(stih416_sas_dapm_widgets), | ||
| 456 | .dapm_routes = stih416_sas_route, | ||
| 457 | .num_dapm_routes = ARRAY_SIZE(stih416_sas_route), | ||
| 458 | }; | ||
| 459 | |||
| 460 | static const struct sti_sas_dev_data stih407_data = { | 330 | static const struct sti_sas_dev_data stih407_data = { |
| 461 | .chipid = CHIPID_STIH407, | ||
| 462 | .regmap = &stih407_sas_regmap, | 331 | .regmap = &stih407_sas_regmap, |
| 463 | .dac_ops = &stih407_dac_ops, | 332 | .dac_ops = &stih407_dac_ops, |
| 464 | .dapm_widgets = stih407_sas_dapm_widgets, | 333 | .dapm_widgets = stih407_sas_dapm_widgets, |
| @@ -533,10 +402,6 @@ static struct snd_soc_codec_driver sti_sas_driver = { | |||
| 533 | 402 | ||
| 534 | static const struct of_device_id sti_sas_dev_match[] = { | 403 | static const struct of_device_id sti_sas_dev_match[] = { |
| 535 | { | 404 | { |
| 536 | .compatible = "st,stih416-sas-codec", | ||
| 537 | .data = &stih416_data, | ||
| 538 | }, | ||
| 539 | { | ||
| 540 | .compatible = "st,stih407-sas-codec", | 405 | .compatible = "st,stih407-sas-codec", |
| 541 | .data = &stih407_data, | 406 | .data = &stih407_data, |
| 542 | }, | 407 | }, |
| @@ -558,7 +423,7 @@ static int sti_sas_driver_probe(struct platform_device *pdev) | |||
| 558 | /* Populate data structure depending on compatibility */ | 423 | /* Populate data structure depending on compatibility */ |
| 559 | of_id = of_match_node(sti_sas_dev_match, pnode); | 424 | of_id = of_match_node(sti_sas_dev_match, pnode); |
| 560 | if (!of_id->data) { | 425 | if (!of_id->data) { |
| 561 | dev_err(&pdev->dev, "data associated to device is missing"); | 426 | dev_err(&pdev->dev, "data associated to device is missing\n"); |
| 562 | return -EINVAL; | 427 | return -EINVAL; |
| 563 | } | 428 | } |
| 564 | 429 | ||
| @@ -584,10 +449,6 @@ static int sti_sas_driver_probe(struct platform_device *pdev) | |||
| 584 | } | 449 | } |
| 585 | drvdata->spdif.regmap = drvdata->dac.regmap; | 450 | drvdata->spdif.regmap = drvdata->dac.regmap; |
| 586 | 451 | ||
| 587 | /* Set DAC dai probe */ | ||
| 588 | if (drvdata->dev_data->chipid == CHIPID_STIH416) | ||
| 589 | sti_sas_dai[STI_SAS_DAI_ANALOG_OUT].probe = stih416_dac_probe; | ||
| 590 | |||
| 591 | sti_sas_dai[STI_SAS_DAI_ANALOG_OUT].ops = drvdata->dev_data->dac_ops; | 452 | sti_sas_dai[STI_SAS_DAI_ANALOG_OUT].ops = drvdata->dev_data->dac_ops; |
| 592 | 453 | ||
| 593 | /* Set dapms*/ | 454 | /* Set dapms*/ |
diff --git a/sound/soc/fsl/efika-audio-fabric.c b/sound/soc/fsl/efika-audio-fabric.c index b2acd3293ea8..f200d1cfc4bd 100644 --- a/sound/soc/fsl/efika-audio-fabric.c +++ b/sound/soc/fsl/efika-audio-fabric.c | |||
| @@ -27,7 +27,6 @@ | |||
| 27 | 27 | ||
| 28 | #include "mpc5200_dma.h" | 28 | #include "mpc5200_dma.h" |
| 29 | #include "mpc5200_psc_ac97.h" | 29 | #include "mpc5200_psc_ac97.h" |
| 30 | #include "../codecs/stac9766.h" | ||
| 31 | 30 | ||
| 32 | #define DRV_NAME "efika-audio-fabric" | 31 | #define DRV_NAME "efika-audio-fabric" |
| 33 | 32 | ||
diff --git a/sound/soc/sti/sti_uniperif.c b/sound/soc/sti/sti_uniperif.c index 549fac349fa0..98eb205a0b62 100644 --- a/sound/soc/sti/sti_uniperif.c +++ b/sound/soc/sti/sti_uniperif.c | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | #include <linux/module.h> | 8 | #include <linux/module.h> |
| 9 | #include <linux/pinctrl/consumer.h> | 9 | #include <linux/pinctrl/consumer.h> |
| 10 | #include <linux/delay.h> | ||
| 10 | 11 | ||
| 11 | #include "uniperif.h" | 12 | #include "uniperif.h" |
| 12 | 13 | ||
| @@ -97,6 +98,28 @@ static const struct of_device_id snd_soc_sti_match[] = { | |||
| 97 | {}, | 98 | {}, |
| 98 | }; | 99 | }; |
| 99 | 100 | ||
| 101 | int sti_uniperiph_reset(struct uniperif *uni) | ||
| 102 | { | ||
| 103 | int count = 10; | ||
| 104 | |||
| 105 | /* Reset uniperipheral uni */ | ||
| 106 | SET_UNIPERIF_SOFT_RST_SOFT_RST(uni); | ||
| 107 | |||
| 108 | if (uni->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) { | ||
| 109 | while (GET_UNIPERIF_SOFT_RST_SOFT_RST(uni) && count) { | ||
| 110 | udelay(5); | ||
| 111 | count--; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | if (!count) { | ||
| 116 | dev_err(uni->dev, "Failed to reset uniperif\n"); | ||
| 117 | return -EIO; | ||
| 118 | } | ||
| 119 | |||
| 120 | return 0; | ||
| 121 | } | ||
| 122 | |||
| 100 | int sti_uniperiph_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, | 123 | int sti_uniperiph_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, |
| 101 | unsigned int rx_mask, int slots, | 124 | unsigned int rx_mask, int slots, |
| 102 | int slot_width) | 125 | int slot_width) |
| @@ -293,7 +316,7 @@ static int sti_uniperiph_dai_suspend(struct snd_soc_dai *dai) | |||
| 293 | 316 | ||
| 294 | /* The uniperipheral should be in stopped state */ | 317 | /* The uniperipheral should be in stopped state */ |
| 295 | if (uni->state != UNIPERIF_STATE_STOPPED) { | 318 | if (uni->state != UNIPERIF_STATE_STOPPED) { |
| 296 | dev_err(uni->dev, "%s: invalid uni state( %d)", | 319 | dev_err(uni->dev, "%s: invalid uni state( %d)\n", |
| 297 | __func__, (int)uni->state); | 320 | __func__, (int)uni->state); |
| 298 | return -EBUSY; | 321 | return -EBUSY; |
| 299 | } | 322 | } |
| @@ -301,7 +324,7 @@ static int sti_uniperiph_dai_suspend(struct snd_soc_dai *dai) | |||
| 301 | /* Pinctrl: switch pinstate to sleep */ | 324 | /* Pinctrl: switch pinstate to sleep */ |
| 302 | ret = pinctrl_pm_select_sleep_state(uni->dev); | 325 | ret = pinctrl_pm_select_sleep_state(uni->dev); |
| 303 | if (ret) | 326 | if (ret) |
| 304 | dev_err(uni->dev, "%s: failed to select pinctrl state", | 327 | dev_err(uni->dev, "%s: failed to select pinctrl state\n", |
| 305 | __func__); | 328 | __func__); |
| 306 | 329 | ||
| 307 | return ret; | 330 | return ret; |
| @@ -322,7 +345,7 @@ static int sti_uniperiph_dai_resume(struct snd_soc_dai *dai) | |||
| 322 | /* pinctrl: switch pinstate to default */ | 345 | /* pinctrl: switch pinstate to default */ |
| 323 | ret = pinctrl_pm_select_default_state(uni->dev); | 346 | ret = pinctrl_pm_select_default_state(uni->dev); |
| 324 | if (ret) | 347 | if (ret) |
| 325 | dev_err(uni->dev, "%s: failed to select pinctrl state", | 348 | dev_err(uni->dev, "%s: failed to select pinctrl state\n", |
| 326 | __func__); | 349 | __func__); |
| 327 | 350 | ||
| 328 | return ret; | 351 | return ret; |
| @@ -366,11 +389,12 @@ static int sti_uniperiph_cpu_dai_of(struct device_node *node, | |||
| 366 | const struct of_device_id *of_id; | 389 | const struct of_device_id *of_id; |
| 367 | const struct sti_uniperiph_dev_data *dev_data; | 390 | const struct sti_uniperiph_dev_data *dev_data; |
| 368 | const char *mode; | 391 | const char *mode; |
| 392 | int ret; | ||
| 369 | 393 | ||
| 370 | /* Populate data structure depending on compatibility */ | 394 | /* Populate data structure depending on compatibility */ |
| 371 | of_id = of_match_node(snd_soc_sti_match, node); | 395 | of_id = of_match_node(snd_soc_sti_match, node); |
| 372 | if (!of_id->data) { | 396 | if (!of_id->data) { |
| 373 | dev_err(dev, "data associated to device is missing"); | 397 | dev_err(dev, "data associated to device is missing\n"); |
| 374 | return -EINVAL; | 398 | return -EINVAL; |
| 375 | } | 399 | } |
| 376 | dev_data = (struct sti_uniperiph_dev_data *)of_id->data; | 400 | dev_data = (struct sti_uniperiph_dev_data *)of_id->data; |
| @@ -389,7 +413,7 @@ static int sti_uniperiph_cpu_dai_of(struct device_node *node, | |||
| 389 | uni->mem_region = platform_get_resource(priv->pdev, IORESOURCE_MEM, 0); | 413 | uni->mem_region = platform_get_resource(priv->pdev, IORESOURCE_MEM, 0); |
| 390 | 414 | ||
| 391 | if (!uni->mem_region) { | 415 | if (!uni->mem_region) { |
| 392 | dev_err(dev, "Failed to get memory resource"); | 416 | dev_err(dev, "Failed to get memory resource\n"); |
| 393 | return -ENODEV; | 417 | return -ENODEV; |
| 394 | } | 418 | } |
| 395 | 419 | ||
| @@ -403,7 +427,7 @@ static int sti_uniperiph_cpu_dai_of(struct device_node *node, | |||
| 403 | 427 | ||
| 404 | uni->irq = platform_get_irq(priv->pdev, 0); | 428 | uni->irq = platform_get_irq(priv->pdev, 0); |
| 405 | if (uni->irq < 0) { | 429 | if (uni->irq < 0) { |
| 406 | dev_err(dev, "Failed to get IRQ resource"); | 430 | dev_err(dev, "Failed to get IRQ resource\n"); |
| 407 | return -ENXIO; | 431 | return -ENXIO; |
| 408 | } | 432 | } |
| 409 | 433 | ||
| @@ -421,12 +445,15 @@ static int sti_uniperiph_cpu_dai_of(struct device_node *node, | |||
| 421 | dai_data->stream = dev_data->stream; | 445 | dai_data->stream = dev_data->stream; |
| 422 | 446 | ||
| 423 | if (priv->dai_data.stream == SNDRV_PCM_STREAM_PLAYBACK) { | 447 | if (priv->dai_data.stream == SNDRV_PCM_STREAM_PLAYBACK) { |
| 424 | uni_player_init(priv->pdev, uni); | 448 | ret = uni_player_init(priv->pdev, uni); |
| 425 | stream = &dai->playback; | 449 | stream = &dai->playback; |
| 426 | } else { | 450 | } else { |
| 427 | uni_reader_init(priv->pdev, uni); | 451 | ret = uni_reader_init(priv->pdev, uni); |
| 428 | stream = &dai->capture; | 452 | stream = &dai->capture; |
| 429 | } | 453 | } |
| 454 | if (ret < 0) | ||
| 455 | return ret; | ||
| 456 | |||
| 430 | dai->ops = uni->dai_ops; | 457 | dai->ops = uni->dai_ops; |
| 431 | 458 | ||
| 432 | stream->stream_name = dai->name; | 459 | stream->stream_name = dai->name; |
diff --git a/sound/soc/sti/uniperif.h b/sound/soc/sti/uniperif.h index 1993c655fb79..d487dd2ef016 100644 --- a/sound/soc/sti/uniperif.h +++ b/sound/soc/sti/uniperif.h | |||
| @@ -1397,6 +1397,8 @@ static inline int sti_uniperiph_get_unip_tdm_frame_size(struct uniperif *uni) | |||
| 1397 | return (uni->tdm_slot.slots * uni->tdm_slot.slot_width / 8); | 1397 | return (uni->tdm_slot.slots * uni->tdm_slot.slot_width / 8); |
| 1398 | } | 1398 | } |
| 1399 | 1399 | ||
| 1400 | int sti_uniperiph_reset(struct uniperif *uni); | ||
| 1401 | |||
| 1400 | int sti_uniperiph_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, | 1402 | int sti_uniperiph_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, |
| 1401 | unsigned int rx_mask, int slots, | 1403 | unsigned int rx_mask, int slots, |
| 1402 | int slot_width); | 1404 | int slot_width); |
diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c index ad54d4cf58ad..60ae31a303ab 100644 --- a/sound/soc/sti/uniperif_player.c +++ b/sound/soc/sti/uniperif_player.c | |||
| @@ -6,8 +6,6 @@ | |||
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | #include <linux/clk.h> | 8 | #include <linux/clk.h> |
| 9 | #include <linux/delay.h> | ||
| 10 | #include <linux/io.h> | ||
| 11 | #include <linux/mfd/syscon.h> | 9 | #include <linux/mfd/syscon.h> |
| 12 | 10 | ||
| 13 | #include <sound/asoundef.h> | 11 | #include <sound/asoundef.h> |
| @@ -55,25 +53,6 @@ static const struct snd_pcm_hardware uni_player_pcm_hw = { | |||
| 55 | .buffer_bytes_max = 256 * PAGE_SIZE | 53 | .buffer_bytes_max = 256 * PAGE_SIZE |
| 56 | }; | 54 | }; |
| 57 | 55 | ||
| 58 | static inline int reset_player(struct uniperif *player) | ||
| 59 | { | ||
| 60 | int count = 10; | ||
| 61 | |||
| 62 | if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) { | ||
| 63 | while (GET_UNIPERIF_SOFT_RST_SOFT_RST(player) && count) { | ||
| 64 | udelay(5); | ||
| 65 | count--; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | if (!count) { | ||
| 70 | dev_err(player->dev, "Failed to reset uniperif"); | ||
| 71 | return -EIO; | ||
| 72 | } | ||
| 73 | |||
| 74 | return 0; | ||
| 75 | } | ||
| 76 | |||
| 77 | /* | 56 | /* |
| 78 | * uni_player_irq_handler | 57 | * uni_player_irq_handler |
| 79 | * In case of error audio stream is stopped; stop action is protected via PCM | 58 | * In case of error audio stream is stopped; stop action is protected via PCM |
| @@ -97,7 +76,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) | |||
| 97 | 76 | ||
| 98 | /* Check for fifo error (underrun) */ | 77 | /* Check for fifo error (underrun) */ |
| 99 | if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(player))) { | 78 | if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(player))) { |
| 100 | dev_err(player->dev, "FIFO underflow error detected"); | 79 | dev_err(player->dev, "FIFO underflow error detected\n"); |
| 101 | 80 | ||
| 102 | /* Interrupt is just for information when underflow recovery */ | 81 | /* Interrupt is just for information when underflow recovery */ |
| 103 | if (player->underflow_enabled) { | 82 | if (player->underflow_enabled) { |
| @@ -119,7 +98,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) | |||
| 119 | 98 | ||
| 120 | /* Check for dma error (overrun) */ | 99 | /* Check for dma error (overrun) */ |
| 121 | if (unlikely(status & UNIPERIF_ITS_DMA_ERROR_MASK(player))) { | 100 | if (unlikely(status & UNIPERIF_ITS_DMA_ERROR_MASK(player))) { |
| 122 | dev_err(player->dev, "DMA error detected"); | 101 | dev_err(player->dev, "DMA error detected\n"); |
| 123 | 102 | ||
| 124 | /* Disable interrupt so doesn't continually fire */ | 103 | /* Disable interrupt so doesn't continually fire */ |
| 125 | SET_UNIPERIF_ITM_BCLR_DMA_ERROR(player); | 104 | SET_UNIPERIF_ITM_BCLR_DMA_ERROR(player); |
| @@ -135,11 +114,14 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) | |||
| 135 | /* Check for underflow recovery done */ | 114 | /* Check for underflow recovery done */ |
| 136 | if (unlikely(status & UNIPERIF_ITM_UNDERFLOW_REC_DONE_MASK(player))) { | 115 | if (unlikely(status & UNIPERIF_ITM_UNDERFLOW_REC_DONE_MASK(player))) { |
| 137 | if (!player->underflow_enabled) { | 116 | if (!player->underflow_enabled) { |
| 138 | dev_err(player->dev, "unexpected Underflow recovering"); | 117 | dev_err(player->dev, |
| 118 | "unexpected Underflow recovering\n"); | ||
| 139 | return -EPERM; | 119 | return -EPERM; |
| 140 | } | 120 | } |
| 141 | /* Read the underflow recovery duration */ | 121 | /* Read the underflow recovery duration */ |
| 142 | tmp = GET_UNIPERIF_STATUS_1_UNDERFLOW_DURATION(player); | 122 | tmp = GET_UNIPERIF_STATUS_1_UNDERFLOW_DURATION(player); |
| 123 | dev_dbg(player->dev, "Underflow recovered (%d LR clocks max)\n", | ||
| 124 | tmp); | ||
| 143 | 125 | ||
| 144 | /* Clear the underflow recovery duration */ | 126 | /* Clear the underflow recovery duration */ |
| 145 | SET_UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION(player); | 127 | SET_UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION(player); |
| @@ -153,7 +135,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) | |||
| 153 | /* Check if underflow recovery failed */ | 135 | /* Check if underflow recovery failed */ |
| 154 | if (unlikely(status & | 136 | if (unlikely(status & |
| 155 | UNIPERIF_ITM_UNDERFLOW_REC_FAILED_MASK(player))) { | 137 | UNIPERIF_ITM_UNDERFLOW_REC_FAILED_MASK(player))) { |
| 156 | dev_err(player->dev, "Underflow recovery failed"); | 138 | dev_err(player->dev, "Underflow recovery failed\n"); |
| 157 | 139 | ||
| 158 | /* Stop the player */ | 140 | /* Stop the player */ |
| 159 | snd_pcm_stream_lock(player->substream); | 141 | snd_pcm_stream_lock(player->substream); |
| @@ -336,7 +318,7 @@ static int uni_player_prepare_iec958(struct uniperif *player, | |||
| 336 | 318 | ||
| 337 | /* Oversampling must be multiple of 128 as iec958 frame is 32-bits */ | 319 | /* Oversampling must be multiple of 128 as iec958 frame is 32-bits */ |
| 338 | if ((clk_div % 128) || (clk_div <= 0)) { | 320 | if ((clk_div % 128) || (clk_div <= 0)) { |
| 339 | dev_err(player->dev, "%s: invalid clk_div %d", | 321 | dev_err(player->dev, "%s: invalid clk_div %d\n", |
| 340 | __func__, clk_div); | 322 | __func__, clk_div); |
| 341 | return -EINVAL; | 323 | return -EINVAL; |
| 342 | } | 324 | } |
| @@ -359,7 +341,7 @@ static int uni_player_prepare_iec958(struct uniperif *player, | |||
| 359 | SET_UNIPERIF_I2S_FMT_DATA_SIZE_24(player); | 341 | SET_UNIPERIF_I2S_FMT_DATA_SIZE_24(player); |
| 360 | break; | 342 | break; |
| 361 | default: | 343 | default: |
| 362 | dev_err(player->dev, "format not supported"); | 344 | dev_err(player->dev, "format not supported\n"); |
| 363 | return -EINVAL; | 345 | return -EINVAL; |
| 364 | } | 346 | } |
| 365 | 347 | ||
| @@ -448,12 +430,12 @@ static int uni_player_prepare_pcm(struct uniperif *player, | |||
| 448 | * for 16 bits must be a multiple of 64 | 430 | * for 16 bits must be a multiple of 64 |
| 449 | */ | 431 | */ |
| 450 | if ((slot_width == 32) && (clk_div % 128)) { | 432 | if ((slot_width == 32) && (clk_div % 128)) { |
| 451 | dev_err(player->dev, "%s: invalid clk_div", __func__); | 433 | dev_err(player->dev, "%s: invalid clk_div\n", __func__); |
| 452 | return -EINVAL; | 434 | return -EINVAL; |
| 453 | } | 435 | } |
| 454 | 436 | ||
| 455 | if ((slot_width == 16) && (clk_div % 64)) { | 437 | if ((slot_width == 16) && (clk_div % 64)) { |
| 456 | dev_err(player->dev, "%s: invalid clk_div", __func__); | 438 | dev_err(player->dev, "%s: invalid clk_div\n", __func__); |
| 457 | return -EINVAL; | 439 | return -EINVAL; |
| 458 | } | 440 | } |
| 459 | 441 | ||
| @@ -471,7 +453,7 @@ static int uni_player_prepare_pcm(struct uniperif *player, | |||
| 471 | SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(player); | 453 | SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(player); |
| 472 | break; | 454 | break; |
| 473 | default: | 455 | default: |
| 474 | dev_err(player->dev, "subframe format not supported"); | 456 | dev_err(player->dev, "subframe format not supported\n"); |
| 475 | return -EINVAL; | 457 | return -EINVAL; |
| 476 | } | 458 | } |
| 477 | 459 | ||
| @@ -491,7 +473,7 @@ static int uni_player_prepare_pcm(struct uniperif *player, | |||
| 491 | break; | 473 | break; |
| 492 | 474 | ||
| 493 | default: | 475 | default: |
| 494 | dev_err(player->dev, "format not supported"); | 476 | dev_err(player->dev, "format not supported\n"); |
| 495 | return -EINVAL; | 477 | return -EINVAL; |
| 496 | } | 478 | } |
| 497 | 479 | ||
| @@ -504,7 +486,7 @@ static int uni_player_prepare_pcm(struct uniperif *player, | |||
| 504 | /* Number of channelsmust be even*/ | 486 | /* Number of channelsmust be even*/ |
| 505 | if ((runtime->channels % 2) || (runtime->channels < 2) || | 487 | if ((runtime->channels % 2) || (runtime->channels < 2) || |
| 506 | (runtime->channels > 10)) { | 488 | (runtime->channels > 10)) { |
| 507 | dev_err(player->dev, "%s: invalid nb of channels", __func__); | 489 | dev_err(player->dev, "%s: invalid nb of channels\n", __func__); |
| 508 | return -EINVAL; | 490 | return -EINVAL; |
| 509 | } | 491 | } |
| 510 | 492 | ||
| @@ -762,7 +744,7 @@ static int uni_player_prepare(struct snd_pcm_substream *substream, | |||
| 762 | 744 | ||
| 763 | /* The player should be stopped */ | 745 | /* The player should be stopped */ |
| 764 | if (player->state != UNIPERIF_STATE_STOPPED) { | 746 | if (player->state != UNIPERIF_STATE_STOPPED) { |
| 765 | dev_err(player->dev, "%s: invalid player state %d", __func__, | 747 | dev_err(player->dev, "%s: invalid player state %d\n", __func__, |
| 766 | player->state); | 748 | player->state); |
| 767 | return -EINVAL; | 749 | return -EINVAL; |
| 768 | } | 750 | } |
| @@ -791,7 +773,8 @@ static int uni_player_prepare(struct snd_pcm_substream *substream, | |||
| 791 | /* Trigger limit must be an even number */ | 773 | /* Trigger limit must be an even number */ |
| 792 | if ((!trigger_limit % 2) || (trigger_limit != 1 && transfer_size % 2) || | 774 | if ((!trigger_limit % 2) || (trigger_limit != 1 && transfer_size % 2) || |
| 793 | (trigger_limit > UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(player))) { | 775 | (trigger_limit > UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(player))) { |
| 794 | dev_err(player->dev, "invalid trigger limit %d", trigger_limit); | 776 | dev_err(player->dev, "invalid trigger limit %d\n", |
| 777 | trigger_limit); | ||
| 795 | return -EINVAL; | 778 | return -EINVAL; |
| 796 | } | 779 | } |
| 797 | 780 | ||
| @@ -812,7 +795,7 @@ static int uni_player_prepare(struct snd_pcm_substream *substream, | |||
| 812 | ret = uni_player_prepare_tdm(player, runtime); | 795 | ret = uni_player_prepare_tdm(player, runtime); |
| 813 | break; | 796 | break; |
| 814 | default: | 797 | default: |
| 815 | dev_err(player->dev, "invalid player type"); | 798 | dev_err(player->dev, "invalid player type\n"); |
| 816 | return -EINVAL; | 799 | return -EINVAL; |
| 817 | } | 800 | } |
| 818 | 801 | ||
| @@ -852,16 +835,14 @@ static int uni_player_prepare(struct snd_pcm_substream *substream, | |||
| 852 | SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(player); | 835 | SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(player); |
| 853 | break; | 836 | break; |
| 854 | default: | 837 | default: |
| 855 | dev_err(player->dev, "format not supported"); | 838 | dev_err(player->dev, "format not supported\n"); |
| 856 | return -EINVAL; | 839 | return -EINVAL; |
| 857 | } | 840 | } |
| 858 | 841 | ||
| 859 | SET_UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ(player, 0); | 842 | SET_UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ(player, 0); |
| 860 | 843 | ||
| 861 | /* Reset uniperipheral player */ | ||
| 862 | SET_UNIPERIF_SOFT_RST_SOFT_RST(player); | ||
| 863 | 844 | ||
| 864 | return reset_player(player); | 845 | return sti_uniperiph_reset(player); |
| 865 | } | 846 | } |
| 866 | 847 | ||
| 867 | static int uni_player_start(struct uniperif *player) | 848 | static int uni_player_start(struct uniperif *player) |
| @@ -870,13 +851,13 @@ static int uni_player_start(struct uniperif *player) | |||
| 870 | 851 | ||
| 871 | /* The player should be stopped */ | 852 | /* The player should be stopped */ |
| 872 | if (player->state != UNIPERIF_STATE_STOPPED) { | 853 | if (player->state != UNIPERIF_STATE_STOPPED) { |
| 873 | dev_err(player->dev, "%s: invalid player state", __func__); | 854 | dev_err(player->dev, "%s: invalid player state\n", __func__); |
| 874 | return -EINVAL; | 855 | return -EINVAL; |
| 875 | } | 856 | } |
| 876 | 857 | ||
| 877 | ret = clk_prepare_enable(player->clk); | 858 | ret = clk_prepare_enable(player->clk); |
| 878 | if (ret) { | 859 | if (ret) { |
| 879 | dev_err(player->dev, "%s: Failed to enable clock", __func__); | 860 | dev_err(player->dev, "%s: Failed to enable clock\n", __func__); |
| 880 | return ret; | 861 | return ret; |
| 881 | } | 862 | } |
| 882 | 863 | ||
| @@ -893,10 +874,7 @@ static int uni_player_start(struct uniperif *player) | |||
| 893 | SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED(player); | 874 | SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED(player); |
| 894 | } | 875 | } |
| 895 | 876 | ||
| 896 | /* Reset uniperipheral player */ | 877 | ret = sti_uniperiph_reset(player); |
| 897 | SET_UNIPERIF_SOFT_RST_SOFT_RST(player); | ||
| 898 | |||
| 899 | ret = reset_player(player); | ||
| 900 | if (ret < 0) { | 878 | if (ret < 0) { |
| 901 | clk_disable_unprepare(player->clk); | 879 | clk_disable_unprepare(player->clk); |
| 902 | return ret; | 880 | return ret; |
| @@ -938,17 +916,14 @@ static int uni_player_stop(struct uniperif *player) | |||
| 938 | 916 | ||
| 939 | /* The player should not be in stopped state */ | 917 | /* The player should not be in stopped state */ |
| 940 | if (player->state == UNIPERIF_STATE_STOPPED) { | 918 | if (player->state == UNIPERIF_STATE_STOPPED) { |
| 941 | dev_err(player->dev, "%s: invalid player state", __func__); | 919 | dev_err(player->dev, "%s: invalid player state\n", __func__); |
| 942 | return -EINVAL; | 920 | return -EINVAL; |
| 943 | } | 921 | } |
| 944 | 922 | ||
| 945 | /* Turn the player off */ | 923 | /* Turn the player off */ |
| 946 | SET_UNIPERIF_CTRL_OPERATION_OFF(player); | 924 | SET_UNIPERIF_CTRL_OPERATION_OFF(player); |
| 947 | 925 | ||
| 948 | /* Soft reset the player */ | 926 | ret = sti_uniperiph_reset(player); |
| 949 | SET_UNIPERIF_SOFT_RST_SOFT_RST(player); | ||
| 950 | |||
| 951 | ret = reset_player(player); | ||
| 952 | if (ret < 0) | 927 | if (ret < 0) |
| 953 | return ret; | 928 | return ret; |
| 954 | 929 | ||
| @@ -973,7 +948,7 @@ int uni_player_resume(struct uniperif *player) | |||
| 973 | ret = regmap_field_write(player->clk_sel, 1); | 948 | ret = regmap_field_write(player->clk_sel, 1); |
| 974 | if (ret) { | 949 | if (ret) { |
| 975 | dev_err(player->dev, | 950 | dev_err(player->dev, |
| 976 | "%s: Failed to select freq synth clock", | 951 | "%s: Failed to select freq synth clock\n", |
| 977 | __func__); | 952 | __func__); |
| 978 | return ret; | 953 | return ret; |
| 979 | } | 954 | } |
| @@ -1070,7 +1045,7 @@ int uni_player_init(struct platform_device *pdev, | |||
| 1070 | ret = uni_player_parse_dt_audio_glue(pdev, player); | 1045 | ret = uni_player_parse_dt_audio_glue(pdev, player); |
| 1071 | 1046 | ||
| 1072 | if (ret < 0) { | 1047 | if (ret < 0) { |
| 1073 | dev_err(player->dev, "Failed to parse DeviceTree"); | 1048 | dev_err(player->dev, "Failed to parse DeviceTree\n"); |
| 1074 | return ret; | 1049 | return ret; |
| 1075 | } | 1050 | } |
| 1076 | 1051 | ||
| @@ -1085,15 +1060,17 @@ int uni_player_init(struct platform_device *pdev, | |||
| 1085 | 1060 | ||
| 1086 | /* Get uniperif resource */ | 1061 | /* Get uniperif resource */ |
| 1087 | player->clk = of_clk_get(pdev->dev.of_node, 0); | 1062 | player->clk = of_clk_get(pdev->dev.of_node, 0); |
| 1088 | if (IS_ERR(player->clk)) | 1063 | if (IS_ERR(player->clk)) { |
| 1064 | dev_err(player->dev, "Failed to get clock\n"); | ||
| 1089 | ret = PTR_ERR(player->clk); | 1065 | ret = PTR_ERR(player->clk); |
| 1066 | } | ||
| 1090 | 1067 | ||
| 1091 | /* Select the frequency synthesizer clock */ | 1068 | /* Select the frequency synthesizer clock */ |
| 1092 | if (player->clk_sel) { | 1069 | if (player->clk_sel) { |
| 1093 | ret = regmap_field_write(player->clk_sel, 1); | 1070 | ret = regmap_field_write(player->clk_sel, 1); |
| 1094 | if (ret) { | 1071 | if (ret) { |
| 1095 | dev_err(player->dev, | 1072 | dev_err(player->dev, |
| 1096 | "%s: Failed to select freq synth clock", | 1073 | "%s: Failed to select freq synth clock\n", |
| 1097 | __func__); | 1074 | __func__); |
| 1098 | return ret; | 1075 | return ret; |
| 1099 | } | 1076 | } |
| @@ -1105,7 +1082,7 @@ int uni_player_init(struct platform_device *pdev, | |||
| 1105 | ret = regmap_field_write(player->valid_sel, player->id); | 1082 | ret = regmap_field_write(player->valid_sel, player->id); |
| 1106 | if (ret) { | 1083 | if (ret) { |
| 1107 | dev_err(player->dev, | 1084 | dev_err(player->dev, |
| 1108 | "%s: unable to connect to tdm bus", __func__); | 1085 | "%s: unable to connect to tdm bus\n", __func__); |
| 1109 | return ret; | 1086 | return ret; |
| 1110 | } | 1087 | } |
| 1111 | } | 1088 | } |
| @@ -1113,8 +1090,10 @@ int uni_player_init(struct platform_device *pdev, | |||
| 1113 | ret = devm_request_irq(&pdev->dev, player->irq, | 1090 | ret = devm_request_irq(&pdev->dev, player->irq, |
| 1114 | uni_player_irq_handler, IRQF_SHARED, | 1091 | uni_player_irq_handler, IRQF_SHARED, |
| 1115 | dev_name(&pdev->dev), player); | 1092 | dev_name(&pdev->dev), player); |
| 1116 | if (ret < 0) | 1093 | if (ret < 0) { |
| 1094 | dev_err(player->dev, "unable to request IRQ %d\n", player->irq); | ||
| 1117 | return ret; | 1095 | return ret; |
| 1096 | } | ||
| 1118 | 1097 | ||
| 1119 | mutex_init(&player->ctrl_lock); | 1098 | mutex_init(&player->ctrl_lock); |
| 1120 | 1099 | ||
diff --git a/sound/soc/sti/uniperif_reader.c b/sound/soc/sti/uniperif_reader.c index 0e1c3ee56675..5992c6ab3833 100644 --- a/sound/soc/sti/uniperif_reader.c +++ b/sound/soc/sti/uniperif_reader.c | |||
| @@ -5,10 +5,6 @@ | |||
| 5 | * License terms: GNU General Public License (GPL), version 2 | 5 | * License terms: GNU General Public License (GPL), version 2 |
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | #include <linux/clk.h> | ||
| 9 | #include <linux/delay.h> | ||
| 10 | #include <linux/io.h> | ||
| 11 | |||
| 12 | #include <sound/soc.h> | 8 | #include <sound/soc.h> |
| 13 | 9 | ||
| 14 | #include "uniperif.h" | 10 | #include "uniperif.h" |
| @@ -52,7 +48,7 @@ static irqreturn_t uni_reader_irq_handler(int irq, void *dev_id) | |||
| 52 | 48 | ||
| 53 | if (reader->state == UNIPERIF_STATE_STOPPED) { | 49 | if (reader->state == UNIPERIF_STATE_STOPPED) { |
| 54 | /* Unexpected IRQ: do nothing */ | 50 | /* Unexpected IRQ: do nothing */ |
| 55 | dev_warn(reader->dev, "unexpected IRQ "); | 51 | dev_warn(reader->dev, "unexpected IRQ\n"); |
| 56 | return IRQ_HANDLED; | 52 | return IRQ_HANDLED; |
| 57 | } | 53 | } |
| 58 | 54 | ||
| @@ -62,7 +58,7 @@ static irqreturn_t uni_reader_irq_handler(int irq, void *dev_id) | |||
| 62 | 58 | ||
| 63 | /* Check for fifo overflow error */ | 59 | /* Check for fifo overflow error */ |
| 64 | if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(reader))) { | 60 | if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(reader))) { |
| 65 | dev_err(reader->dev, "FIFO error detected"); | 61 | dev_err(reader->dev, "FIFO error detected\n"); |
| 66 | 62 | ||
| 67 | snd_pcm_stream_lock(reader->substream); | 63 | snd_pcm_stream_lock(reader->substream); |
| 68 | snd_pcm_stop(reader->substream, SNDRV_PCM_STATE_XRUN); | 64 | snd_pcm_stop(reader->substream, SNDRV_PCM_STATE_XRUN); |
| @@ -105,7 +101,7 @@ static int uni_reader_prepare_pcm(struct snd_pcm_runtime *runtime, | |||
| 105 | SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(reader); | 101 | SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(reader); |
| 106 | break; | 102 | break; |
| 107 | default: | 103 | default: |
| 108 | dev_err(reader->dev, "subframe format not supported"); | 104 | dev_err(reader->dev, "subframe format not supported\n"); |
| 109 | return -EINVAL; | 105 | return -EINVAL; |
| 110 | } | 106 | } |
| 111 | 107 | ||
| @@ -125,14 +121,14 @@ static int uni_reader_prepare_pcm(struct snd_pcm_runtime *runtime, | |||
| 125 | break; | 121 | break; |
| 126 | 122 | ||
| 127 | default: | 123 | default: |
| 128 | dev_err(reader->dev, "format not supported"); | 124 | dev_err(reader->dev, "format not supported\n"); |
| 129 | return -EINVAL; | 125 | return -EINVAL; |
| 130 | } | 126 | } |
| 131 | 127 | ||
| 132 | /* Number of channels must be even */ | 128 | /* Number of channels must be even */ |
| 133 | if ((runtime->channels % 2) || (runtime->channels < 2) || | 129 | if ((runtime->channels % 2) || (runtime->channels < 2) || |
| 134 | (runtime->channels > 10)) { | 130 | (runtime->channels > 10)) { |
| 135 | dev_err(reader->dev, "%s: invalid nb of channels", __func__); | 131 | dev_err(reader->dev, "%s: invalid nb of channels\n", __func__); |
| 136 | return -EINVAL; | 132 | return -EINVAL; |
| 137 | } | 133 | } |
| 138 | 134 | ||
| @@ -186,11 +182,10 @@ static int uni_reader_prepare(struct snd_pcm_substream *substream, | |||
| 186 | struct uniperif *reader = priv->dai_data.uni; | 182 | struct uniperif *reader = priv->dai_data.uni; |
| 187 | struct snd_pcm_runtime *runtime = substream->runtime; | 183 | struct snd_pcm_runtime *runtime = substream->runtime; |
| 188 | int transfer_size, trigger_limit, ret; | 184 | int transfer_size, trigger_limit, ret; |
| 189 | int count = 10; | ||
| 190 | 185 | ||
| 191 | /* The reader should be stopped */ | 186 | /* The reader should be stopped */ |
| 192 | if (reader->state != UNIPERIF_STATE_STOPPED) { | 187 | if (reader->state != UNIPERIF_STATE_STOPPED) { |
| 193 | dev_err(reader->dev, "%s: invalid reader state %d", __func__, | 188 | dev_err(reader->dev, "%s: invalid reader state %d\n", __func__, |
| 194 | reader->state); | 189 | reader->state); |
| 195 | return -EINVAL; | 190 | return -EINVAL; |
| 196 | } | 191 | } |
| @@ -219,7 +214,8 @@ static int uni_reader_prepare(struct snd_pcm_substream *substream, | |||
| 219 | if ((!trigger_limit % 2) || | 214 | if ((!trigger_limit % 2) || |
| 220 | (trigger_limit != 1 && transfer_size % 2) || | 215 | (trigger_limit != 1 && transfer_size % 2) || |
| 221 | (trigger_limit > UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(reader))) { | 216 | (trigger_limit > UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(reader))) { |
| 222 | dev_err(reader->dev, "invalid trigger limit %d", trigger_limit); | 217 | dev_err(reader->dev, "invalid trigger limit %d\n", |
| 218 | trigger_limit); | ||
| 223 | return -EINVAL; | 219 | return -EINVAL; |
| 224 | } | 220 | } |
| 225 | 221 | ||
| @@ -246,7 +242,7 @@ static int uni_reader_prepare(struct snd_pcm_substream *substream, | |||
| 246 | SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(reader); | 242 | SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(reader); |
| 247 | break; | 243 | break; |
| 248 | default: | 244 | default: |
| 249 | dev_err(reader->dev, "format not supported"); | 245 | dev_err(reader->dev, "format not supported\n"); |
| 250 | return -EINVAL; | 246 | return -EINVAL; |
| 251 | } | 247 | } |
| 252 | 248 | ||
| @@ -287,25 +283,14 @@ static int uni_reader_prepare(struct snd_pcm_substream *substream, | |||
| 287 | } | 283 | } |
| 288 | 284 | ||
| 289 | /* Reset uniperipheral reader */ | 285 | /* Reset uniperipheral reader */ |
| 290 | SET_UNIPERIF_SOFT_RST_SOFT_RST(reader); | 286 | return sti_uniperiph_reset(reader); |
| 291 | |||
| 292 | while (GET_UNIPERIF_SOFT_RST_SOFT_RST(reader)) { | ||
| 293 | udelay(5); | ||
| 294 | count--; | ||
| 295 | } | ||
| 296 | if (!count) { | ||
| 297 | dev_err(reader->dev, "Failed to reset uniperif"); | ||
| 298 | return -EIO; | ||
| 299 | } | ||
| 300 | |||
| 301 | return 0; | ||
| 302 | } | 287 | } |
| 303 | 288 | ||
| 304 | static int uni_reader_start(struct uniperif *reader) | 289 | static int uni_reader_start(struct uniperif *reader) |
| 305 | { | 290 | { |
| 306 | /* The reader should be stopped */ | 291 | /* The reader should be stopped */ |
| 307 | if (reader->state != UNIPERIF_STATE_STOPPED) { | 292 | if (reader->state != UNIPERIF_STATE_STOPPED) { |
| 308 | dev_err(reader->dev, "%s: invalid reader state", __func__); | 293 | dev_err(reader->dev, "%s: invalid reader state\n", __func__); |
| 309 | return -EINVAL; | 294 | return -EINVAL; |
| 310 | } | 295 | } |
| 311 | 296 | ||
| @@ -325,7 +310,7 @@ static int uni_reader_stop(struct uniperif *reader) | |||
| 325 | { | 310 | { |
| 326 | /* The reader should not be in stopped state */ | 311 | /* The reader should not be in stopped state */ |
| 327 | if (reader->state == UNIPERIF_STATE_STOPPED) { | 312 | if (reader->state == UNIPERIF_STATE_STOPPED) { |
| 328 | dev_err(reader->dev, "%s: invalid reader state", __func__); | 313 | dev_err(reader->dev, "%s: invalid reader state\n", __func__); |
| 329 | return -EINVAL; | 314 | return -EINVAL; |
| 330 | } | 315 | } |
| 331 | 316 | ||
| @@ -423,7 +408,7 @@ int uni_reader_init(struct platform_device *pdev, | |||
| 423 | uni_reader_irq_handler, IRQF_SHARED, | 408 | uni_reader_irq_handler, IRQF_SHARED, |
| 424 | dev_name(&pdev->dev), reader); | 409 | dev_name(&pdev->dev), reader); |
| 425 | if (ret < 0) { | 410 | if (ret < 0) { |
| 426 | dev_err(&pdev->dev, "Failed to request IRQ"); | 411 | dev_err(&pdev->dev, "Failed to request IRQ\n"); |
| 427 | return -EBUSY; | 412 | return -EBUSY; |
| 428 | } | 413 | } |
| 429 | 414 | ||
diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig index dd2368297fd3..6c344e16aca4 100644 --- a/sound/soc/sunxi/Kconfig +++ b/sound/soc/sunxi/Kconfig | |||
| @@ -9,6 +9,14 @@ config SND_SUN4I_CODEC | |||
| 9 | Select Y or M to add support for the Codec embedded in the Allwinner | 9 | Select Y or M to add support for the Codec embedded in the Allwinner |
| 10 | A10 and affiliated SoCs. | 10 | A10 and affiliated SoCs. |
| 11 | 11 | ||
| 12 | config SND_SUN8I_CODEC_ANALOG | ||
| 13 | tristate "Allwinner sun8i Codec Analog Controls Support" | ||
| 14 | depends on MACH_SUN8I || COMPILE_TEST | ||
| 15 | select REGMAP | ||
| 16 | help | ||
| 17 | Say Y or M if you want to add support for the analog controls for | ||
| 18 | the codec embedded in newer Allwinner SoCs. | ||
| 19 | |||
| 12 | config SND_SUN4I_I2S | 20 | config SND_SUN4I_I2S |
| 13 | tristate "Allwinner A10 I2S Support" | 21 | tristate "Allwinner A10 I2S Support" |
| 14 | select SND_SOC_GENERIC_DMAENGINE_PCM | 22 | select SND_SOC_GENERIC_DMAENGINE_PCM |
diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile index 604c7b842837..241c0df9ca0c 100644 --- a/sound/soc/sunxi/Makefile +++ b/sound/soc/sunxi/Makefile | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o | 1 | obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o |
| 2 | obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o | 2 | obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o |
| 3 | obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o | 3 | obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o |
| 4 | obj-$(CONFIG_SND_SUN8I_CODEC_ANALOG) += sun8i-codec-analog.o | ||
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 56ed9472e89f..848af01692a0 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | * Copyright 2014 Jon Smirl <jonsmirl@gmail.com> | 3 | * Copyright 2014 Jon Smirl <jonsmirl@gmail.com> |
| 4 | * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com> | 4 | * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com> |
| 5 | * Copyright 2015 Adam Sampson <ats@offog.org> | 5 | * Copyright 2015 Adam Sampson <ats@offog.org> |
| 6 | * Copyright 2016 Chen-Yu Tsai <wens@csie.org> | ||
| 6 | * | 7 | * |
| 7 | * Based on the Allwinner SDK driver, released under the GPL. | 8 | * Based on the Allwinner SDK driver, released under the GPL. |
| 8 | * | 9 | * |
| @@ -24,10 +25,12 @@ | |||
| 24 | #include <linux/delay.h> | 25 | #include <linux/delay.h> |
| 25 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
| 26 | #include <linux/of.h> | 27 | #include <linux/of.h> |
| 27 | #include <linux/of_platform.h> | ||
| 28 | #include <linux/of_address.h> | 28 | #include <linux/of_address.h> |
| 29 | #include <linux/of_device.h> | ||
| 30 | #include <linux/of_platform.h> | ||
| 29 | #include <linux/clk.h> | 31 | #include <linux/clk.h> |
| 30 | #include <linux/regmap.h> | 32 | #include <linux/regmap.h> |
| 33 | #include <linux/reset.h> | ||
| 31 | #include <linux/gpio/consumer.h> | 34 | #include <linux/gpio/consumer.h> |
| 32 | 35 | ||
| 33 | #include <sound/core.h> | 36 | #include <sound/core.h> |
| @@ -38,7 +41,7 @@ | |||
| 38 | #include <sound/initval.h> | 41 | #include <sound/initval.h> |
| 39 | #include <sound/dmaengine_pcm.h> | 42 | #include <sound/dmaengine_pcm.h> |
| 40 | 43 | ||
| 41 | /* Codec DAC register offsets and bit fields */ | 44 | /* Codec DAC digital controls and FIFO registers */ |
| 42 | #define SUN4I_CODEC_DAC_DPC (0x00) | 45 | #define SUN4I_CODEC_DAC_DPC (0x00) |
| 43 | #define SUN4I_CODEC_DAC_DPC_EN_DA (31) | 46 | #define SUN4I_CODEC_DAC_DPC_EN_DA (31) |
| 44 | #define SUN4I_CODEC_DAC_DPC_DVOL (12) | 47 | #define SUN4I_CODEC_DAC_DPC_DVOL (12) |
| @@ -55,6 +58,8 @@ | |||
| 55 | #define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH (0) | 58 | #define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH (0) |
| 56 | #define SUN4I_CODEC_DAC_FIFOS (0x08) | 59 | #define SUN4I_CODEC_DAC_FIFOS (0x08) |
| 57 | #define SUN4I_CODEC_DAC_TXDATA (0x0c) | 60 | #define SUN4I_CODEC_DAC_TXDATA (0x0c) |
| 61 | |||
| 62 | /* Codec DAC side analog signal controls */ | ||
| 58 | #define SUN4I_CODEC_DAC_ACTL (0x10) | 63 | #define SUN4I_CODEC_DAC_ACTL (0x10) |
| 59 | #define SUN4I_CODEC_DAC_ACTL_DACAENR (31) | 64 | #define SUN4I_CODEC_DAC_ACTL_DACAENR (31) |
| 60 | #define SUN4I_CODEC_DAC_ACTL_DACAENL (30) | 65 | #define SUN4I_CODEC_DAC_ACTL_DACAENL (30) |
| @@ -69,7 +74,7 @@ | |||
| 69 | #define SUN4I_CODEC_DAC_TUNE (0x14) | 74 | #define SUN4I_CODEC_DAC_TUNE (0x14) |
| 70 | #define SUN4I_CODEC_DAC_DEBUG (0x18) | 75 | #define SUN4I_CODEC_DAC_DEBUG (0x18) |
| 71 | 76 | ||
| 72 | /* Codec ADC register offsets and bit fields */ | 77 | /* Codec ADC digital controls and FIFO registers */ |
| 73 | #define SUN4I_CODEC_ADC_FIFOC (0x1c) | 78 | #define SUN4I_CODEC_ADC_FIFOC (0x1c) |
| 74 | #define SUN4I_CODEC_ADC_FIFOC_ADC_FS (29) | 79 | #define SUN4I_CODEC_ADC_FIFOC_ADC_FS (29) |
| 75 | #define SUN4I_CODEC_ADC_FIFOC_EN_AD (28) | 80 | #define SUN4I_CODEC_ADC_FIFOC_EN_AD (28) |
| @@ -81,6 +86,8 @@ | |||
| 81 | #define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH (0) | 86 | #define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH (0) |
| 82 | #define SUN4I_CODEC_ADC_FIFOS (0x20) | 87 | #define SUN4I_CODEC_ADC_FIFOS (0x20) |
| 83 | #define SUN4I_CODEC_ADC_RXDATA (0x24) | 88 | #define SUN4I_CODEC_ADC_RXDATA (0x24) |
| 89 | |||
| 90 | /* Codec ADC side analog signal controls */ | ||
| 84 | #define SUN4I_CODEC_ADC_ACTL (0x28) | 91 | #define SUN4I_CODEC_ADC_ACTL (0x28) |
| 85 | #define SUN4I_CODEC_ADC_ACTL_ADC_R_EN (31) | 92 | #define SUN4I_CODEC_ADC_ACTL_ADC_R_EN (31) |
| 86 | #define SUN4I_CODEC_ADC_ACTL_ADC_L_EN (30) | 93 | #define SUN4I_CODEC_ADC_ACTL_ADC_L_EN (30) |
| @@ -93,19 +100,141 @@ | |||
| 93 | #define SUN4I_CODEC_ADC_ACTL_DDE (3) | 100 | #define SUN4I_CODEC_ADC_ACTL_DDE (3) |
| 94 | #define SUN4I_CODEC_ADC_DEBUG (0x2c) | 101 | #define SUN4I_CODEC_ADC_DEBUG (0x2c) |
| 95 | 102 | ||
| 96 | /* Other various ADC registers */ | 103 | /* FIFO counters */ |
| 97 | #define SUN4I_CODEC_DAC_TXCNT (0x30) | 104 | #define SUN4I_CODEC_DAC_TXCNT (0x30) |
| 98 | #define SUN4I_CODEC_ADC_RXCNT (0x34) | 105 | #define SUN4I_CODEC_ADC_RXCNT (0x34) |
| 106 | |||
| 107 | /* Calibration register (sun7i only) */ | ||
| 99 | #define SUN7I_CODEC_AC_DAC_CAL (0x38) | 108 | #define SUN7I_CODEC_AC_DAC_CAL (0x38) |
| 109 | |||
| 110 | /* Microphone controls (sun7i only) */ | ||
| 100 | #define SUN7I_CODEC_AC_MIC_PHONE_CAL (0x3c) | 111 | #define SUN7I_CODEC_AC_MIC_PHONE_CAL (0x3c) |
| 101 | 112 | ||
| 113 | /* | ||
| 114 | * sun6i specific registers | ||
| 115 | * | ||
| 116 | * sun6i shares the same digital control and FIFO registers as sun4i, | ||
| 117 | * but only the DAC digital controls are at the same offset. The others | ||
| 118 | * have been moved around to accommodate extra analog controls. | ||
| 119 | */ | ||
| 120 | |||
| 121 | /* Codec DAC digital controls and FIFO registers */ | ||
| 122 | #define SUN6I_CODEC_ADC_FIFOC (0x10) | ||
| 123 | #define SUN6I_CODEC_ADC_FIFOC_EN_AD (28) | ||
| 124 | #define SUN6I_CODEC_ADC_FIFOS (0x14) | ||
| 125 | #define SUN6I_CODEC_ADC_RXDATA (0x18) | ||
| 126 | |||
| 127 | /* Output mixer and gain controls */ | ||
| 128 | #define SUN6I_CODEC_OM_DACA_CTRL (0x20) | ||
| 129 | #define SUN6I_CODEC_OM_DACA_CTRL_DACAREN (31) | ||
| 130 | #define SUN6I_CODEC_OM_DACA_CTRL_DACALEN (30) | ||
| 131 | #define SUN6I_CODEC_OM_DACA_CTRL_RMIXEN (29) | ||
| 132 | #define SUN6I_CODEC_OM_DACA_CTRL_LMIXEN (28) | ||
| 133 | #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1 (23) | ||
| 134 | #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2 (22) | ||
| 135 | #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONE (21) | ||
| 136 | #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONEP (20) | ||
| 137 | #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR (19) | ||
| 138 | #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR (18) | ||
| 139 | #define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL (17) | ||
| 140 | #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1 (16) | ||
| 141 | #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2 (15) | ||
| 142 | #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONE (14) | ||
| 143 | #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONEN (13) | ||
| 144 | #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL (12) | ||
| 145 | #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL (11) | ||
| 146 | #define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR (10) | ||
| 147 | #define SUN6I_CODEC_OM_DACA_CTRL_RHPIS (9) | ||
| 148 | #define SUN6I_CODEC_OM_DACA_CTRL_LHPIS (8) | ||
| 149 | #define SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE (7) | ||
| 150 | #define SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE (6) | ||
| 151 | #define SUN6I_CODEC_OM_DACA_CTRL_HPVOL (0) | ||
| 152 | #define SUN6I_CODEC_OM_PA_CTRL (0x24) | ||
| 153 | #define SUN6I_CODEC_OM_PA_CTRL_HPPAEN (31) | ||
| 154 | #define SUN6I_CODEC_OM_PA_CTRL_HPCOM_CTL (29) | ||
| 155 | #define SUN6I_CODEC_OM_PA_CTRL_COMPTEN (28) | ||
| 156 | #define SUN6I_CODEC_OM_PA_CTRL_MIC1G (15) | ||
| 157 | #define SUN6I_CODEC_OM_PA_CTRL_MIC2G (12) | ||
| 158 | #define SUN6I_CODEC_OM_PA_CTRL_LINEING (9) | ||
| 159 | #define SUN6I_CODEC_OM_PA_CTRL_PHONEG (6) | ||
| 160 | #define SUN6I_CODEC_OM_PA_CTRL_PHONEPG (3) | ||
| 161 | #define SUN6I_CODEC_OM_PA_CTRL_PHONENG (0) | ||
| 162 | |||
| 163 | /* Microphone, line out and phone out controls */ | ||
| 164 | #define SUN6I_CODEC_MIC_CTRL (0x28) | ||
| 165 | #define SUN6I_CODEC_MIC_CTRL_HBIASEN (31) | ||
| 166 | #define SUN6I_CODEC_MIC_CTRL_MBIASEN (30) | ||
| 167 | #define SUN6I_CODEC_MIC_CTRL_MIC1AMPEN (28) | ||
| 168 | #define SUN6I_CODEC_MIC_CTRL_MIC1BOOST (25) | ||
| 169 | #define SUN6I_CODEC_MIC_CTRL_MIC2AMPEN (24) | ||
| 170 | #define SUN6I_CODEC_MIC_CTRL_MIC2BOOST (21) | ||
| 171 | #define SUN6I_CODEC_MIC_CTRL_MIC2SLT (20) | ||
| 172 | #define SUN6I_CODEC_MIC_CTRL_LINEOUTLEN (19) | ||
| 173 | #define SUN6I_CODEC_MIC_CTRL_LINEOUTREN (18) | ||
| 174 | #define SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC (17) | ||
| 175 | #define SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC (16) | ||
| 176 | #define SUN6I_CODEC_MIC_CTRL_LINEOUTVC (11) | ||
| 177 | #define SUN6I_CODEC_MIC_CTRL_PHONEPREG (8) | ||
| 178 | |||
| 179 | /* ADC mixer controls */ | ||
| 180 | #define SUN6I_CODEC_ADC_ACTL (0x2c) | ||
| 181 | #define SUN6I_CODEC_ADC_ACTL_ADCREN (31) | ||
| 182 | #define SUN6I_CODEC_ADC_ACTL_ADCLEN (30) | ||
| 183 | #define SUN6I_CODEC_ADC_ACTL_ADCRG (27) | ||
| 184 | #define SUN6I_CODEC_ADC_ACTL_ADCLG (24) | ||
| 185 | #define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1 (13) | ||
| 186 | #define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2 (12) | ||
| 187 | #define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONE (11) | ||
| 188 | #define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONEP (10) | ||
| 189 | #define SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR (9) | ||
| 190 | #define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR (8) | ||
| 191 | #define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL (7) | ||
| 192 | #define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1 (6) | ||
| 193 | #define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2 (5) | ||
| 194 | #define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONE (4) | ||
| 195 | #define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONEN (3) | ||
| 196 | #define SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL (2) | ||
| 197 | #define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL (1) | ||
| 198 | #define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR (0) | ||
| 199 | |||
| 200 | /* Analog performance tuning controls */ | ||
| 201 | #define SUN6I_CODEC_ADDA_TUNE (0x30) | ||
| 202 | |||
| 203 | /* Calibration controls */ | ||
| 204 | #define SUN6I_CODEC_CALIBRATION (0x34) | ||
| 205 | |||
| 206 | /* FIFO counters */ | ||
| 207 | #define SUN6I_CODEC_DAC_TXCNT (0x40) | ||
| 208 | #define SUN6I_CODEC_ADC_RXCNT (0x44) | ||
| 209 | |||
| 210 | /* headset jack detection and button support registers */ | ||
| 211 | #define SUN6I_CODEC_HMIC_CTL (0x50) | ||
| 212 | #define SUN6I_CODEC_HMIC_DATA (0x54) | ||
| 213 | |||
| 214 | /* TODO sun6i DAP (Digital Audio Processing) bits */ | ||
| 215 | |||
| 216 | /* FIFO counters moved on A23 */ | ||
| 217 | #define SUN8I_A23_CODEC_DAC_TXCNT (0x1c) | ||
| 218 | #define SUN8I_A23_CODEC_ADC_RXCNT (0x20) | ||
| 219 | |||
| 220 | /* TX FIFO moved on H3 */ | ||
| 221 | #define SUN8I_H3_CODEC_DAC_TXDATA (0x20) | ||
| 222 | #define SUN8I_H3_CODEC_DAC_DBG (0x48) | ||
| 223 | #define SUN8I_H3_CODEC_ADC_DBG (0x4c) | ||
| 224 | |||
| 225 | /* TODO H3 DAP (Digital Audio Processing) bits */ | ||
| 226 | |||
| 102 | struct sun4i_codec { | 227 | struct sun4i_codec { |
| 103 | struct device *dev; | 228 | struct device *dev; |
| 104 | struct regmap *regmap; | 229 | struct regmap *regmap; |
| 105 | struct clk *clk_apb; | 230 | struct clk *clk_apb; |
| 106 | struct clk *clk_module; | 231 | struct clk *clk_module; |
| 232 | struct reset_control *rst; | ||
| 107 | struct gpio_desc *gpio_pa; | 233 | struct gpio_desc *gpio_pa; |
| 108 | 234 | ||
| 235 | /* ADC_FIFOC register is at different offset on different SoCs */ | ||
| 236 | struct regmap_field *reg_adc_fifoc; | ||
| 237 | |||
| 109 | struct snd_dmaengine_dai_dma_data capture_dma_data; | 238 | struct snd_dmaengine_dai_dma_data capture_dma_data; |
| 110 | struct snd_dmaengine_dai_dma_data playback_dma_data; | 239 | struct snd_dmaengine_dai_dma_data playback_dma_data; |
| 111 | }; | 240 | }; |
| @@ -134,16 +263,16 @@ static void sun4i_codec_stop_playback(struct sun4i_codec *scodec) | |||
| 134 | static void sun4i_codec_start_capture(struct sun4i_codec *scodec) | 263 | static void sun4i_codec_start_capture(struct sun4i_codec *scodec) |
| 135 | { | 264 | { |
| 136 | /* Enable ADC DRQ */ | 265 | /* Enable ADC DRQ */ |
| 137 | regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, | 266 | regmap_field_update_bits(scodec->reg_adc_fifoc, |
| 138 | BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), | 267 | BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), |
| 139 | BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN)); | 268 | BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN)); |
| 140 | } | 269 | } |
| 141 | 270 | ||
| 142 | static void sun4i_codec_stop_capture(struct sun4i_codec *scodec) | 271 | static void sun4i_codec_stop_capture(struct sun4i_codec *scodec) |
| 143 | { | 272 | { |
| 144 | /* Disable ADC DRQ */ | 273 | /* Disable ADC DRQ */ |
| 145 | regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, | 274 | regmap_field_update_bits(scodec->reg_adc_fifoc, |
| 146 | BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), 0); | 275 | BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), 0); |
| 147 | } | 276 | } |
| 148 | 277 | ||
| 149 | static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd, | 278 | static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd, |
| @@ -186,24 +315,29 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream, | |||
| 186 | 315 | ||
| 187 | 316 | ||
| 188 | /* Flush RX FIFO */ | 317 | /* Flush RX FIFO */ |
| 189 | regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, | 318 | regmap_field_update_bits(scodec->reg_adc_fifoc, |
| 190 | BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH), | 319 | BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH), |
| 191 | BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH)); | 320 | BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH)); |
| 192 | 321 | ||
| 193 | 322 | ||
| 194 | /* Set RX FIFO trigger level */ | 323 | /* Set RX FIFO trigger level */ |
| 195 | regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, | 324 | regmap_field_update_bits(scodec->reg_adc_fifoc, |
| 196 | 0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL, | 325 | 0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL, |
| 197 | 0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL); | 326 | 0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL); |
| 198 | 327 | ||
| 199 | /* | 328 | /* |
| 200 | * FIXME: Undocumented in the datasheet, but | 329 | * FIXME: Undocumented in the datasheet, but |
| 201 | * Allwinner's code mentions that it is related | 330 | * Allwinner's code mentions that it is related |
| 202 | * related to microphone gain | 331 | * related to microphone gain |
| 203 | */ | 332 | */ |
| 204 | regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL, | 333 | if (of_device_is_compatible(scodec->dev->of_node, |
| 205 | 0x3 << 25, | 334 | "allwinner,sun4i-a10-codec") || |
| 206 | 0x1 << 25); | 335 | of_device_is_compatible(scodec->dev->of_node, |
| 336 | "allwinner,sun7i-a20-codec")) { | ||
| 337 | regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL, | ||
| 338 | 0x3 << 25, | ||
| 339 | 0x1 << 25); | ||
| 340 | } | ||
| 207 | 341 | ||
| 208 | if (of_device_is_compatible(scodec->dev->of_node, | 342 | if (of_device_is_compatible(scodec->dev->of_node, |
| 209 | "allwinner,sun7i-a20-codec")) | 343 | "allwinner,sun7i-a20-codec")) |
| @@ -213,9 +347,9 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream, | |||
| 213 | 0x1 << 8); | 347 | 0x1 << 8); |
| 214 | 348 | ||
| 215 | /* Fill most significant bits with valid data MSB */ | 349 | /* Fill most significant bits with valid data MSB */ |
| 216 | regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, | 350 | regmap_field_update_bits(scodec->reg_adc_fifoc, |
| 217 | BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE), | 351 | BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE), |
| 218 | BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE)); | 352 | BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE)); |
| 219 | 353 | ||
| 220 | return 0; | 354 | return 0; |
| 221 | } | 355 | } |
| @@ -342,18 +476,19 @@ static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec, | |||
| 342 | unsigned int hwrate) | 476 | unsigned int hwrate) |
| 343 | { | 477 | { |
| 344 | /* Set ADC sample rate */ | 478 | /* Set ADC sample rate */ |
| 345 | regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, | 479 | regmap_field_update_bits(scodec->reg_adc_fifoc, |
| 346 | 7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS, | 480 | 7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS, |
| 347 | hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS); | 481 | hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS); |
| 348 | 482 | ||
| 349 | /* Set the number of channels we want to use */ | 483 | /* Set the number of channels we want to use */ |
| 350 | if (params_channels(params) == 1) | 484 | if (params_channels(params) == 1) |
| 351 | regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, | 485 | regmap_field_update_bits(scodec->reg_adc_fifoc, |
| 352 | BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), | 486 | BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), |
| 353 | BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN)); | 487 | BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN)); |
| 354 | else | 488 | else |
| 355 | regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, | 489 | regmap_field_update_bits(scodec->reg_adc_fifoc, |
| 356 | BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), 0); | 490 | BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), |
| 491 | 0); | ||
| 357 | 492 | ||
| 358 | return 0; | 493 | return 0; |
| 359 | } | 494 | } |
| @@ -502,7 +637,7 @@ static struct snd_soc_dai_driver sun4i_codec_dai = { | |||
| 502 | }, | 637 | }, |
| 503 | }; | 638 | }; |
| 504 | 639 | ||
| 505 | /*** Codec ***/ | 640 | /*** sun4i Codec ***/ |
| 506 | static const struct snd_kcontrol_new sun4i_codec_pa_mute = | 641 | static const struct snd_kcontrol_new sun4i_codec_pa_mute = |
| 507 | SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL, | 642 | SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL, |
| 508 | SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0); | 643 | SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0); |
| @@ -638,6 +773,337 @@ static struct snd_soc_codec_driver sun4i_codec_codec = { | |||
| 638 | }, | 773 | }, |
| 639 | }; | 774 | }; |
| 640 | 775 | ||
| 776 | /*** sun6i Codec ***/ | ||
| 777 | |||
| 778 | /* mixer controls */ | ||
| 779 | static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = { | ||
| 780 | SOC_DAPM_DOUBLE("DAC Playback Switch", | ||
| 781 | SUN6I_CODEC_OM_DACA_CTRL, | ||
| 782 | SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL, | ||
| 783 | SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR, 1, 0), | ||
| 784 | SOC_DAPM_DOUBLE("DAC Reversed Playback Switch", | ||
| 785 | SUN6I_CODEC_OM_DACA_CTRL, | ||
| 786 | SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR, | ||
| 787 | SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL, 1, 0), | ||
| 788 | SOC_DAPM_DOUBLE("Line In Playback Switch", | ||
| 789 | SUN6I_CODEC_OM_DACA_CTRL, | ||
| 790 | SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL, | ||
| 791 | SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR, 1, 0), | ||
| 792 | SOC_DAPM_DOUBLE("Mic1 Playback Switch", | ||
| 793 | SUN6I_CODEC_OM_DACA_CTRL, | ||
| 794 | SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1, | ||
| 795 | SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1, 1, 0), | ||
| 796 | SOC_DAPM_DOUBLE("Mic2 Playback Switch", | ||
| 797 | SUN6I_CODEC_OM_DACA_CTRL, | ||
| 798 | SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2, | ||
| 799 | SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2, 1, 0), | ||
| 800 | }; | ||
| 801 | |||
| 802 | /* ADC mixer controls */ | ||
| 803 | static const struct snd_kcontrol_new sun6i_codec_adc_mixer_controls[] = { | ||
| 804 | SOC_DAPM_DOUBLE("Mixer Capture Switch", | ||
| 805 | SUN6I_CODEC_ADC_ACTL, | ||
| 806 | SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL, | ||
| 807 | SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR, 1, 0), | ||
| 808 | SOC_DAPM_DOUBLE("Mixer Reversed Capture Switch", | ||
| 809 | SUN6I_CODEC_ADC_ACTL, | ||
| 810 | SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR, | ||
| 811 | SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL, 1, 0), | ||
| 812 | SOC_DAPM_DOUBLE("Line In Capture Switch", | ||
| 813 | SUN6I_CODEC_ADC_ACTL, | ||
| 814 | SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL, | ||
| 815 | SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR, 1, 0), | ||
| 816 | SOC_DAPM_DOUBLE("Mic1 Capture Switch", | ||
| 817 | SUN6I_CODEC_ADC_ACTL, | ||
| 818 | SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1, | ||
| 819 | SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1, 1, 0), | ||
| 820 | SOC_DAPM_DOUBLE("Mic2 Capture Switch", | ||
| 821 | SUN6I_CODEC_ADC_ACTL, | ||
| 822 | SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2, | ||
| 823 | SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2, 1, 0), | ||
| 824 | }; | ||
| 825 | |||
| 826 | /* headphone controls */ | ||
| 827 | static const char * const sun6i_codec_hp_src_enum_text[] = { | ||
| 828 | "DAC", "Mixer", | ||
| 829 | }; | ||
| 830 | |||
| 831 | static SOC_ENUM_DOUBLE_DECL(sun6i_codec_hp_src_enum, | ||
| 832 | SUN6I_CODEC_OM_DACA_CTRL, | ||
| 833 | SUN6I_CODEC_OM_DACA_CTRL_LHPIS, | ||
| 834 | SUN6I_CODEC_OM_DACA_CTRL_RHPIS, | ||
| 835 | sun6i_codec_hp_src_enum_text); | ||
| 836 | |||
| 837 | static const struct snd_kcontrol_new sun6i_codec_hp_src[] = { | ||
| 838 | SOC_DAPM_ENUM("Headphone Source Playback Route", | ||
| 839 | sun6i_codec_hp_src_enum), | ||
| 840 | }; | ||
| 841 | |||
| 842 | /* microphone controls */ | ||
| 843 | static const char * const sun6i_codec_mic2_src_enum_text[] = { | ||
| 844 | "Mic2", "Mic3", | ||
| 845 | }; | ||
| 846 | |||
| 847 | static SOC_ENUM_SINGLE_DECL(sun6i_codec_mic2_src_enum, | ||
| 848 | SUN6I_CODEC_MIC_CTRL, | ||
| 849 | SUN6I_CODEC_MIC_CTRL_MIC2SLT, | ||
| 850 | sun6i_codec_mic2_src_enum_text); | ||
| 851 | |||
| 852 | static const struct snd_kcontrol_new sun6i_codec_mic2_src[] = { | ||
| 853 | SOC_DAPM_ENUM("Mic2 Amplifier Source Route", | ||
| 854 | sun6i_codec_mic2_src_enum), | ||
| 855 | }; | ||
| 856 | |||
| 857 | /* line out controls */ | ||
| 858 | static const char * const sun6i_codec_lineout_src_enum_text[] = { | ||
| 859 | "Stereo", "Mono Differential", | ||
| 860 | }; | ||
| 861 | |||
| 862 | static SOC_ENUM_DOUBLE_DECL(sun6i_codec_lineout_src_enum, | ||
| 863 | SUN6I_CODEC_MIC_CTRL, | ||
| 864 | SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC, | ||
| 865 | SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC, | ||
| 866 | sun6i_codec_lineout_src_enum_text); | ||
| 867 | |||
| 868 | static const struct snd_kcontrol_new sun6i_codec_lineout_src[] = { | ||
| 869 | SOC_DAPM_ENUM("Line Out Source Playback Route", | ||
| 870 | sun6i_codec_lineout_src_enum), | ||
| 871 | }; | ||
| 872 | |||
| 873 | /* volume / mute controls */ | ||
| 874 | static const DECLARE_TLV_DB_SCALE(sun6i_codec_dvol_scale, -7308, 116, 0); | ||
| 875 | static const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1); | ||
| 876 | static const DECLARE_TLV_DB_SCALE(sun6i_codec_out_mixer_pregain_scale, | ||
| 877 | -450, 150, 0); | ||
| 878 | static const DECLARE_TLV_DB_RANGE(sun6i_codec_lineout_vol_scale, | ||
| 879 | 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), | ||
| 880 | 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0), | ||
| 881 | ); | ||
| 882 | static const DECLARE_TLV_DB_RANGE(sun6i_codec_mic_gain_scale, | ||
| 883 | 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), | ||
| 884 | 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0), | ||
| 885 | ); | ||
| 886 | |||
| 887 | static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = { | ||
| 888 | SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC, | ||
| 889 | SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1, | ||
| 890 | sun6i_codec_dvol_scale), | ||
| 891 | SOC_SINGLE_TLV("Headphone Playback Volume", | ||
| 892 | SUN6I_CODEC_OM_DACA_CTRL, | ||
| 893 | SUN6I_CODEC_OM_DACA_CTRL_HPVOL, 0x3f, 0, | ||
| 894 | sun6i_codec_hp_vol_scale), | ||
| 895 | SOC_SINGLE_TLV("Line Out Playback Volume", | ||
| 896 | SUN6I_CODEC_MIC_CTRL, | ||
| 897 | SUN6I_CODEC_MIC_CTRL_LINEOUTVC, 0x1f, 0, | ||
| 898 | sun6i_codec_lineout_vol_scale), | ||
| 899 | SOC_DOUBLE("Headphone Playback Switch", | ||
| 900 | SUN6I_CODEC_OM_DACA_CTRL, | ||
| 901 | SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE, | ||
| 902 | SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0), | ||
| 903 | SOC_DOUBLE("Line Out Playback Switch", | ||
| 904 | SUN6I_CODEC_MIC_CTRL, | ||
| 905 | SUN6I_CODEC_MIC_CTRL_LINEOUTLEN, | ||
| 906 | SUN6I_CODEC_MIC_CTRL_LINEOUTREN, 1, 0), | ||
| 907 | /* Mixer pre-gains */ | ||
| 908 | SOC_SINGLE_TLV("Line In Playback Volume", | ||
| 909 | SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_LINEING, | ||
| 910 | 0x7, 0, sun6i_codec_out_mixer_pregain_scale), | ||
| 911 | SOC_SINGLE_TLV("Mic1 Playback Volume", | ||
| 912 | SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_MIC1G, | ||
| 913 | 0x7, 0, sun6i_codec_out_mixer_pregain_scale), | ||
| 914 | SOC_SINGLE_TLV("Mic2 Playback Volume", | ||
| 915 | SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_MIC2G, | ||
| 916 | 0x7, 0, sun6i_codec_out_mixer_pregain_scale), | ||
| 917 | |||
| 918 | /* Microphone Amp boost gains */ | ||
| 919 | SOC_SINGLE_TLV("Mic1 Boost Volume", SUN6I_CODEC_MIC_CTRL, | ||
| 920 | SUN6I_CODEC_MIC_CTRL_MIC1BOOST, 0x7, 0, | ||
| 921 | sun6i_codec_mic_gain_scale), | ||
| 922 | SOC_SINGLE_TLV("Mic2 Boost Volume", SUN6I_CODEC_MIC_CTRL, | ||
| 923 | SUN6I_CODEC_MIC_CTRL_MIC2BOOST, 0x7, 0, | ||
| 924 | sun6i_codec_mic_gain_scale), | ||
| 925 | SOC_DOUBLE_TLV("ADC Capture Volume", | ||
| 926 | SUN6I_CODEC_ADC_ACTL, SUN6I_CODEC_ADC_ACTL_ADCLG, | ||
| 927 | SUN6I_CODEC_ADC_ACTL_ADCRG, 0x7, 0, | ||
| 928 | sun6i_codec_out_mixer_pregain_scale), | ||
| 929 | }; | ||
| 930 | |||
| 931 | static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = { | ||
| 932 | /* Microphone inputs */ | ||
| 933 | SND_SOC_DAPM_INPUT("MIC1"), | ||
| 934 | SND_SOC_DAPM_INPUT("MIC2"), | ||
| 935 | SND_SOC_DAPM_INPUT("MIC3"), | ||
| 936 | |||
| 937 | /* Microphone Bias */ | ||
| 938 | SND_SOC_DAPM_SUPPLY("HBIAS", SUN6I_CODEC_MIC_CTRL, | ||
| 939 | SUN6I_CODEC_MIC_CTRL_HBIASEN, 0, NULL, 0), | ||
| 940 | SND_SOC_DAPM_SUPPLY("MBIAS", SUN6I_CODEC_MIC_CTRL, | ||
| 941 | SUN6I_CODEC_MIC_CTRL_MBIASEN, 0, NULL, 0), | ||
| 942 | |||
| 943 | /* Mic input path */ | ||
| 944 | SND_SOC_DAPM_MUX("Mic2 Amplifier Source Route", | ||
| 945 | SND_SOC_NOPM, 0, 0, sun6i_codec_mic2_src), | ||
| 946 | SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN6I_CODEC_MIC_CTRL, | ||
| 947 | SUN6I_CODEC_MIC_CTRL_MIC1AMPEN, 0, NULL, 0), | ||
| 948 | SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN6I_CODEC_MIC_CTRL, | ||
| 949 | SUN6I_CODEC_MIC_CTRL_MIC2AMPEN, 0, NULL, 0), | ||
| 950 | |||
| 951 | /* Line In */ | ||
| 952 | SND_SOC_DAPM_INPUT("LINEIN"), | ||
| 953 | |||
| 954 | /* Digital parts of the ADCs */ | ||
| 955 | SND_SOC_DAPM_SUPPLY("ADC Enable", SUN6I_CODEC_ADC_FIFOC, | ||
| 956 | SUN6I_CODEC_ADC_FIFOC_EN_AD, 0, | ||
| 957 | NULL, 0), | ||
| 958 | |||
| 959 | /* Analog parts of the ADCs */ | ||
| 960 | SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL, | ||
| 961 | SUN6I_CODEC_ADC_ACTL_ADCLEN, 0), | ||
| 962 | SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL, | ||
| 963 | SUN6I_CODEC_ADC_ACTL_ADCREN, 0), | ||
| 964 | |||
| 965 | /* ADC Mixers */ | ||
| 966 | SOC_MIXER_ARRAY("Left ADC Mixer", SND_SOC_NOPM, 0, 0, | ||
| 967 | sun6i_codec_adc_mixer_controls), | ||
| 968 | SOC_MIXER_ARRAY("Right ADC Mixer", SND_SOC_NOPM, 0, 0, | ||
| 969 | sun6i_codec_adc_mixer_controls), | ||
| 970 | |||
| 971 | /* Digital parts of the DACs */ | ||
| 972 | SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC, | ||
| 973 | SUN4I_CODEC_DAC_DPC_EN_DA, 0, | ||
| 974 | NULL, 0), | ||
| 975 | |||
| 976 | /* Analog parts of the DACs */ | ||
| 977 | SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", | ||
| 978 | SUN6I_CODEC_OM_DACA_CTRL, | ||
| 979 | SUN6I_CODEC_OM_DACA_CTRL_DACALEN, 0), | ||
| 980 | SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", | ||
| 981 | SUN6I_CODEC_OM_DACA_CTRL, | ||
| 982 | SUN6I_CODEC_OM_DACA_CTRL_DACAREN, 0), | ||
| 983 | |||
| 984 | /* Mixers */ | ||
| 985 | SOC_MIXER_ARRAY("Left Mixer", SUN6I_CODEC_OM_DACA_CTRL, | ||
| 986 | SUN6I_CODEC_OM_DACA_CTRL_LMIXEN, 0, | ||
| 987 | sun6i_codec_mixer_controls), | ||
| 988 | SOC_MIXER_ARRAY("Right Mixer", SUN6I_CODEC_OM_DACA_CTRL, | ||
| 989 | SUN6I_CODEC_OM_DACA_CTRL_RMIXEN, 0, | ||
| 990 | sun6i_codec_mixer_controls), | ||
| 991 | |||
| 992 | /* Headphone output path */ | ||
| 993 | SND_SOC_DAPM_MUX("Headphone Source Playback Route", | ||
| 994 | SND_SOC_NOPM, 0, 0, sun6i_codec_hp_src), | ||
| 995 | SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN6I_CODEC_OM_PA_CTRL, | ||
| 996 | SUN6I_CODEC_OM_PA_CTRL_HPPAEN, 0, NULL, 0), | ||
| 997 | SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN6I_CODEC_OM_PA_CTRL, | ||
| 998 | SUN6I_CODEC_OM_PA_CTRL_COMPTEN, 0, NULL, 0), | ||
| 999 | SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN6I_CODEC_OM_PA_CTRL, | ||
| 1000 | SUN6I_CODEC_OM_PA_CTRL_HPCOM_CTL, 0x3, 0x3, 0), | ||
| 1001 | SND_SOC_DAPM_OUTPUT("HP"), | ||
| 1002 | |||
| 1003 | /* Line Out path */ | ||
| 1004 | SND_SOC_DAPM_MUX("Line Out Source Playback Route", | ||
| 1005 | SND_SOC_NOPM, 0, 0, sun6i_codec_lineout_src), | ||
| 1006 | SND_SOC_DAPM_OUTPUT("LINEOUT"), | ||
| 1007 | }; | ||
| 1008 | |||
| 1009 | static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = { | ||
| 1010 | /* DAC Routes */ | ||
| 1011 | { "Left DAC", NULL, "DAC Enable" }, | ||
| 1012 | { "Right DAC", NULL, "DAC Enable" }, | ||
| 1013 | |||
| 1014 | /* Microphone Routes */ | ||
| 1015 | { "Mic1 Amplifier", NULL, "MIC1"}, | ||
| 1016 | { "Mic2 Amplifier Source Route", "Mic2", "MIC2" }, | ||
| 1017 | { "Mic2 Amplifier Source Route", "Mic3", "MIC3" }, | ||
| 1018 | { "Mic2 Amplifier", NULL, "Mic2 Amplifier Source Route"}, | ||
| 1019 | |||
| 1020 | /* Left Mixer Routes */ | ||
| 1021 | { "Left Mixer", "DAC Playback Switch", "Left DAC" }, | ||
| 1022 | { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" }, | ||
| 1023 | { "Left Mixer", "Line In Playback Switch", "LINEIN" }, | ||
| 1024 | { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, | ||
| 1025 | { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, | ||
| 1026 | |||
| 1027 | /* Right Mixer Routes */ | ||
| 1028 | { "Right Mixer", "DAC Playback Switch", "Right DAC" }, | ||
| 1029 | { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" }, | ||
| 1030 | { "Right Mixer", "Line In Playback Switch", "LINEIN" }, | ||
| 1031 | { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, | ||
| 1032 | { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, | ||
| 1033 | |||
| 1034 | /* Left ADC Mixer Routes */ | ||
| 1035 | { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" }, | ||
| 1036 | { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" }, | ||
| 1037 | { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" }, | ||
| 1038 | { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, | ||
| 1039 | { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, | ||
| 1040 | |||
| 1041 | /* Right ADC Mixer Routes */ | ||
| 1042 | { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" }, | ||
| 1043 | { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" }, | ||
| 1044 | { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" }, | ||
| 1045 | { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, | ||
| 1046 | { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, | ||
| 1047 | |||
| 1048 | /* Headphone Routes */ | ||
| 1049 | { "Headphone Source Playback Route", "DAC", "Left DAC" }, | ||
| 1050 | { "Headphone Source Playback Route", "DAC", "Right DAC" }, | ||
| 1051 | { "Headphone Source Playback Route", "Mixer", "Left Mixer" }, | ||
| 1052 | { "Headphone Source Playback Route", "Mixer", "Right Mixer" }, | ||
| 1053 | { "Headphone Amp", NULL, "Headphone Source Playback Route" }, | ||
| 1054 | { "HP", NULL, "Headphone Amp" }, | ||
| 1055 | { "HPCOM", NULL, "HPCOM Protection" }, | ||
| 1056 | |||
| 1057 | /* Line Out Routes */ | ||
| 1058 | { "Line Out Source Playback Route", "Stereo", "Left Mixer" }, | ||
| 1059 | { "Line Out Source Playback Route", "Stereo", "Right Mixer" }, | ||
| 1060 | { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" }, | ||
| 1061 | { "LINEOUT", NULL, "Line Out Source Playback Route" }, | ||
| 1062 | |||
| 1063 | /* ADC Routes */ | ||
| 1064 | { "Left ADC", NULL, "ADC Enable" }, | ||
| 1065 | { "Right ADC", NULL, "ADC Enable" }, | ||
| 1066 | { "Left ADC", NULL, "Left ADC Mixer" }, | ||
| 1067 | { "Right ADC", NULL, "Right ADC Mixer" }, | ||
| 1068 | }; | ||
| 1069 | |||
| 1070 | static struct snd_soc_codec_driver sun6i_codec_codec = { | ||
| 1071 | .component_driver = { | ||
| 1072 | .controls = sun6i_codec_codec_widgets, | ||
| 1073 | .num_controls = ARRAY_SIZE(sun6i_codec_codec_widgets), | ||
| 1074 | .dapm_widgets = sun6i_codec_codec_dapm_widgets, | ||
| 1075 | .num_dapm_widgets = ARRAY_SIZE(sun6i_codec_codec_dapm_widgets), | ||
| 1076 | .dapm_routes = sun6i_codec_codec_dapm_routes, | ||
| 1077 | .num_dapm_routes = ARRAY_SIZE(sun6i_codec_codec_dapm_routes), | ||
| 1078 | }, | ||
| 1079 | }; | ||
| 1080 | |||
| 1081 | /* sun8i A23 codec */ | ||
| 1082 | static const struct snd_kcontrol_new sun8i_a23_codec_codec_controls[] = { | ||
| 1083 | SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC, | ||
| 1084 | SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1, | ||
| 1085 | sun6i_codec_dvol_scale), | ||
| 1086 | }; | ||
| 1087 | |||
| 1088 | static const struct snd_soc_dapm_widget sun8i_a23_codec_codec_widgets[] = { | ||
| 1089 | /* Digital parts of the ADCs */ | ||
| 1090 | SND_SOC_DAPM_SUPPLY("ADC Enable", SUN6I_CODEC_ADC_FIFOC, | ||
| 1091 | SUN6I_CODEC_ADC_FIFOC_EN_AD, 0, NULL, 0), | ||
| 1092 | /* Digital parts of the DACs */ | ||
| 1093 | SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC, | ||
| 1094 | SUN4I_CODEC_DAC_DPC_EN_DA, 0, NULL, 0), | ||
| 1095 | |||
| 1096 | }; | ||
| 1097 | |||
| 1098 | static struct snd_soc_codec_driver sun8i_a23_codec_codec = { | ||
| 1099 | .component_driver = { | ||
| 1100 | .controls = sun8i_a23_codec_codec_controls, | ||
| 1101 | .num_controls = ARRAY_SIZE(sun8i_a23_codec_codec_controls), | ||
| 1102 | .dapm_widgets = sun8i_a23_codec_codec_widgets, | ||
| 1103 | .num_dapm_widgets = ARRAY_SIZE(sun8i_a23_codec_codec_widgets), | ||
| 1104 | }, | ||
| 1105 | }; | ||
| 1106 | |||
| 641 | static const struct snd_soc_component_driver sun4i_codec_component = { | 1107 | static const struct snd_soc_component_driver sun4i_codec_component = { |
| 642 | .name = "sun4i-codec", | 1108 | .name = "sun4i-codec", |
| 643 | }; | 1109 | }; |
| @@ -678,45 +1144,6 @@ static struct snd_soc_dai_driver dummy_cpu_dai = { | |||
| 678 | }, | 1144 | }, |
| 679 | }; | 1145 | }; |
| 680 | 1146 | ||
| 681 | static const struct regmap_config sun4i_codec_regmap_config = { | ||
| 682 | .reg_bits = 32, | ||
| 683 | .reg_stride = 4, | ||
| 684 | .val_bits = 32, | ||
| 685 | .max_register = SUN4I_CODEC_ADC_RXCNT, | ||
| 686 | }; | ||
| 687 | |||
| 688 | static const struct regmap_config sun7i_codec_regmap_config = { | ||
| 689 | .reg_bits = 32, | ||
| 690 | .reg_stride = 4, | ||
| 691 | .val_bits = 32, | ||
| 692 | .max_register = SUN7I_CODEC_AC_MIC_PHONE_CAL, | ||
| 693 | }; | ||
| 694 | |||
| 695 | struct sun4i_codec_quirks { | ||
| 696 | const struct regmap_config *regmap_config; | ||
| 697 | }; | ||
| 698 | |||
| 699 | static const struct sun4i_codec_quirks sun4i_codec_quirks = { | ||
| 700 | .regmap_config = &sun4i_codec_regmap_config, | ||
| 701 | }; | ||
| 702 | |||
| 703 | static const struct sun4i_codec_quirks sun7i_codec_quirks = { | ||
| 704 | .regmap_config = &sun7i_codec_regmap_config, | ||
| 705 | }; | ||
| 706 | |||
| 707 | static const struct of_device_id sun4i_codec_of_match[] = { | ||
| 708 | { | ||
| 709 | .compatible = "allwinner,sun4i-a10-codec", | ||
| 710 | .data = &sun4i_codec_quirks, | ||
| 711 | }, | ||
| 712 | { | ||
| 713 | .compatible = "allwinner,sun7i-a20-codec", | ||
| 714 | .data = &sun7i_codec_quirks, | ||
| 715 | }, | ||
| 716 | {} | ||
| 717 | }; | ||
| 718 | MODULE_DEVICE_TABLE(of, sun4i_codec_of_match); | ||
| 719 | |||
| 720 | static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev, | 1147 | static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev, |
| 721 | int *num_links) | 1148 | int *num_links) |
| 722 | { | 1149 | { |
| @@ -781,6 +1208,259 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev) | |||
| 781 | return card; | 1208 | return card; |
| 782 | }; | 1209 | }; |
| 783 | 1210 | ||
| 1211 | static const struct snd_soc_dapm_widget sun6i_codec_card_dapm_widgets[] = { | ||
| 1212 | SND_SOC_DAPM_HP("Headphone", NULL), | ||
| 1213 | SND_SOC_DAPM_LINE("Line In", NULL), | ||
| 1214 | SND_SOC_DAPM_LINE("Line Out", NULL), | ||
| 1215 | SND_SOC_DAPM_MIC("Headset Mic", NULL), | ||
| 1216 | SND_SOC_DAPM_MIC("Mic", NULL), | ||
| 1217 | SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event), | ||
| 1218 | }; | ||
| 1219 | |||
| 1220 | static struct snd_soc_card *sun6i_codec_create_card(struct device *dev) | ||
| 1221 | { | ||
| 1222 | struct snd_soc_card *card; | ||
| 1223 | int ret; | ||
| 1224 | |||
| 1225 | card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); | ||
| 1226 | if (!card) | ||
| 1227 | return ERR_PTR(-ENOMEM); | ||
| 1228 | |||
| 1229 | card->dai_link = sun4i_codec_create_link(dev, &card->num_links); | ||
| 1230 | if (!card->dai_link) | ||
| 1231 | return ERR_PTR(-ENOMEM); | ||
| 1232 | |||
| 1233 | card->dev = dev; | ||
| 1234 | card->name = "A31 Audio Codec"; | ||
| 1235 | card->dapm_widgets = sun6i_codec_card_dapm_widgets; | ||
| 1236 | card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets); | ||
| 1237 | card->fully_routed = true; | ||
| 1238 | |||
| 1239 | ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing"); | ||
| 1240 | if (ret) | ||
| 1241 | dev_warn(dev, "failed to parse audio-routing: %d\n", ret); | ||
| 1242 | |||
| 1243 | return card; | ||
| 1244 | }; | ||
| 1245 | |||
| 1246 | /* Connect digital side enables to analog side widgets */ | ||
| 1247 | static const struct snd_soc_dapm_route sun8i_codec_card_routes[] = { | ||
| 1248 | /* ADC Routes */ | ||
| 1249 | { "Left ADC", NULL, "ADC Enable" }, | ||
| 1250 | { "Right ADC", NULL, "ADC Enable" }, | ||
| 1251 | { "Codec Capture", NULL, "Left ADC" }, | ||
| 1252 | { "Codec Capture", NULL, "Right ADC" }, | ||
| 1253 | |||
| 1254 | /* DAC Routes */ | ||
| 1255 | { "Left DAC", NULL, "DAC Enable" }, | ||
| 1256 | { "Right DAC", NULL, "DAC Enable" }, | ||
| 1257 | { "Left DAC", NULL, "Codec Playback" }, | ||
| 1258 | { "Right DAC", NULL, "Codec Playback" }, | ||
| 1259 | }; | ||
| 1260 | |||
| 1261 | static struct snd_soc_aux_dev aux_dev = { | ||
| 1262 | .name = "Codec Analog Controls", | ||
| 1263 | }; | ||
| 1264 | |||
| 1265 | static struct snd_soc_card *sun8i_a23_codec_create_card(struct device *dev) | ||
| 1266 | { | ||
| 1267 | struct snd_soc_card *card; | ||
| 1268 | int ret; | ||
| 1269 | |||
| 1270 | card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); | ||
| 1271 | if (!card) | ||
| 1272 | return ERR_PTR(-ENOMEM); | ||
| 1273 | |||
| 1274 | aux_dev.codec_of_node = of_parse_phandle(dev->of_node, | ||
| 1275 | "allwinner,codec-analog-controls", | ||
| 1276 | 0); | ||
| 1277 | if (!aux_dev.codec_of_node) { | ||
| 1278 | dev_err(dev, "Can't find analog controls for codec.\n"); | ||
| 1279 | return ERR_PTR(-EINVAL); | ||
| 1280 | }; | ||
| 1281 | |||
| 1282 | card->dai_link = sun4i_codec_create_link(dev, &card->num_links); | ||
| 1283 | if (!card->dai_link) | ||
| 1284 | return ERR_PTR(-ENOMEM); | ||
| 1285 | |||
| 1286 | card->dev = dev; | ||
| 1287 | card->name = "A23 Audio Codec"; | ||
| 1288 | card->dapm_widgets = sun6i_codec_card_dapm_widgets; | ||
| 1289 | card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets); | ||
| 1290 | card->dapm_routes = sun8i_codec_card_routes; | ||
| 1291 | card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes); | ||
| 1292 | card->aux_dev = &aux_dev; | ||
| 1293 | card->num_aux_devs = 1; | ||
| 1294 | card->fully_routed = true; | ||
| 1295 | |||
| 1296 | ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing"); | ||
| 1297 | if (ret) | ||
| 1298 | dev_warn(dev, "failed to parse audio-routing: %d\n", ret); | ||
| 1299 | |||
| 1300 | return card; | ||
| 1301 | }; | ||
| 1302 | |||
| 1303 | static struct snd_soc_card *sun8i_h3_codec_create_card(struct device *dev) | ||
| 1304 | { | ||
| 1305 | struct snd_soc_card *card; | ||
| 1306 | int ret; | ||
| 1307 | |||
| 1308 | card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); | ||
| 1309 | if (!card) | ||
| 1310 | return ERR_PTR(-ENOMEM); | ||
| 1311 | |||
| 1312 | aux_dev.codec_of_node = of_parse_phandle(dev->of_node, | ||
| 1313 | "allwinner,codec-analog-controls", | ||
| 1314 | 0); | ||
| 1315 | if (!aux_dev.codec_of_node) { | ||
| 1316 | dev_err(dev, "Can't find analog controls for codec.\n"); | ||
| 1317 | return ERR_PTR(-EINVAL); | ||
| 1318 | }; | ||
| 1319 | |||
| 1320 | card->dai_link = sun4i_codec_create_link(dev, &card->num_links); | ||
| 1321 | if (!card->dai_link) | ||
| 1322 | return ERR_PTR(-ENOMEM); | ||
| 1323 | |||
| 1324 | card->dev = dev; | ||
| 1325 | card->name = "H3 Audio Codec"; | ||
| 1326 | card->dapm_widgets = sun6i_codec_card_dapm_widgets; | ||
| 1327 | card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets); | ||
| 1328 | card->dapm_routes = sun8i_codec_card_routes; | ||
| 1329 | card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes); | ||
| 1330 | card->aux_dev = &aux_dev; | ||
| 1331 | card->num_aux_devs = 1; | ||
| 1332 | card->fully_routed = true; | ||
| 1333 | |||
| 1334 | ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing"); | ||
| 1335 | if (ret) | ||
| 1336 | dev_warn(dev, "failed to parse audio-routing: %d\n", ret); | ||
| 1337 | |||
| 1338 | return card; | ||
| 1339 | }; | ||
| 1340 | |||
| 1341 | static const struct regmap_config sun4i_codec_regmap_config = { | ||
| 1342 | .reg_bits = 32, | ||
| 1343 | .reg_stride = 4, | ||
| 1344 | .val_bits = 32, | ||
| 1345 | .max_register = SUN4I_CODEC_ADC_RXCNT, | ||
| 1346 | }; | ||
| 1347 | |||
| 1348 | static const struct regmap_config sun6i_codec_regmap_config = { | ||
| 1349 | .reg_bits = 32, | ||
| 1350 | .reg_stride = 4, | ||
| 1351 | .val_bits = 32, | ||
| 1352 | .max_register = SUN6I_CODEC_HMIC_DATA, | ||
| 1353 | }; | ||
| 1354 | |||
| 1355 | static const struct regmap_config sun7i_codec_regmap_config = { | ||
| 1356 | .reg_bits = 32, | ||
| 1357 | .reg_stride = 4, | ||
| 1358 | .val_bits = 32, | ||
| 1359 | .max_register = SUN7I_CODEC_AC_MIC_PHONE_CAL, | ||
| 1360 | }; | ||
| 1361 | |||
| 1362 | static const struct regmap_config sun8i_a23_codec_regmap_config = { | ||
| 1363 | .reg_bits = 32, | ||
| 1364 | .reg_stride = 4, | ||
| 1365 | .val_bits = 32, | ||
| 1366 | .max_register = SUN8I_A23_CODEC_ADC_RXCNT, | ||
| 1367 | }; | ||
| 1368 | |||
| 1369 | static const struct regmap_config sun8i_h3_codec_regmap_config = { | ||
| 1370 | .reg_bits = 32, | ||
| 1371 | .reg_stride = 4, | ||
| 1372 | .val_bits = 32, | ||
| 1373 | .max_register = SUN8I_H3_CODEC_ADC_DBG, | ||
| 1374 | }; | ||
| 1375 | |||
| 1376 | struct sun4i_codec_quirks { | ||
| 1377 | const struct regmap_config *regmap_config; | ||
| 1378 | const struct snd_soc_codec_driver *codec; | ||
| 1379 | struct snd_soc_card * (*create_card)(struct device *dev); | ||
| 1380 | struct reg_field reg_adc_fifoc; /* used for regmap_field */ | ||
| 1381 | unsigned int reg_dac_txdata; /* TX FIFO offset for DMA config */ | ||
| 1382 | unsigned int reg_adc_rxdata; /* RX FIFO offset for DMA config */ | ||
| 1383 | bool has_reset; | ||
| 1384 | }; | ||
| 1385 | |||
| 1386 | static const struct sun4i_codec_quirks sun4i_codec_quirks = { | ||
| 1387 | .regmap_config = &sun4i_codec_regmap_config, | ||
| 1388 | .codec = &sun4i_codec_codec, | ||
| 1389 | .create_card = sun4i_codec_create_card, | ||
| 1390 | .reg_adc_fifoc = REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31), | ||
| 1391 | .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, | ||
| 1392 | .reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA, | ||
| 1393 | }; | ||
| 1394 | |||
| 1395 | static const struct sun4i_codec_quirks sun6i_a31_codec_quirks = { | ||
| 1396 | .regmap_config = &sun6i_codec_regmap_config, | ||
| 1397 | .codec = &sun6i_codec_codec, | ||
| 1398 | .create_card = sun6i_codec_create_card, | ||
| 1399 | .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31), | ||
| 1400 | .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, | ||
| 1401 | .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA, | ||
| 1402 | .has_reset = true, | ||
| 1403 | }; | ||
| 1404 | |||
| 1405 | static const struct sun4i_codec_quirks sun7i_codec_quirks = { | ||
| 1406 | .regmap_config = &sun7i_codec_regmap_config, | ||
| 1407 | .codec = &sun4i_codec_codec, | ||
| 1408 | .create_card = sun4i_codec_create_card, | ||
| 1409 | .reg_adc_fifoc = REG_FIELD(SUN4I_CODEC_ADC_FIFOC, 0, 31), | ||
| 1410 | .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, | ||
| 1411 | .reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA, | ||
| 1412 | }; | ||
| 1413 | |||
| 1414 | static const struct sun4i_codec_quirks sun8i_a23_codec_quirks = { | ||
| 1415 | .regmap_config = &sun8i_a23_codec_regmap_config, | ||
| 1416 | .codec = &sun8i_a23_codec_codec, | ||
| 1417 | .create_card = sun8i_a23_codec_create_card, | ||
| 1418 | .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31), | ||
| 1419 | .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, | ||
| 1420 | .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA, | ||
| 1421 | .has_reset = true, | ||
| 1422 | }; | ||
| 1423 | |||
| 1424 | static const struct sun4i_codec_quirks sun8i_h3_codec_quirks = { | ||
| 1425 | .regmap_config = &sun8i_h3_codec_regmap_config, | ||
| 1426 | /* | ||
| 1427 | * TODO Share the codec structure with A23 for now. | ||
| 1428 | * This should be split out when adding digital audio | ||
| 1429 | * processing support for the H3. | ||
| 1430 | */ | ||
| 1431 | .codec = &sun8i_a23_codec_codec, | ||
| 1432 | .create_card = sun8i_h3_codec_create_card, | ||
| 1433 | .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31), | ||
| 1434 | .reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA, | ||
| 1435 | .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA, | ||
| 1436 | .has_reset = true, | ||
| 1437 | }; | ||
| 1438 | |||
| 1439 | static const struct of_device_id sun4i_codec_of_match[] = { | ||
| 1440 | { | ||
| 1441 | .compatible = "allwinner,sun4i-a10-codec", | ||
| 1442 | .data = &sun4i_codec_quirks, | ||
| 1443 | }, | ||
| 1444 | { | ||
| 1445 | .compatible = "allwinner,sun6i-a31-codec", | ||
| 1446 | .data = &sun6i_a31_codec_quirks, | ||
| 1447 | }, | ||
| 1448 | { | ||
| 1449 | .compatible = "allwinner,sun7i-a20-codec", | ||
| 1450 | .data = &sun7i_codec_quirks, | ||
| 1451 | }, | ||
| 1452 | { | ||
| 1453 | .compatible = "allwinner,sun8i-a23-codec", | ||
| 1454 | .data = &sun8i_a23_codec_quirks, | ||
| 1455 | }, | ||
| 1456 | { | ||
| 1457 | .compatible = "allwinner,sun8i-h3-codec", | ||
| 1458 | .data = &sun8i_h3_codec_quirks, | ||
| 1459 | }, | ||
| 1460 | {} | ||
| 1461 | }; | ||
| 1462 | MODULE_DEVICE_TABLE(of, sun4i_codec_of_match); | ||
| 1463 | |||
| 784 | static int sun4i_codec_probe(struct platform_device *pdev) | 1464 | static int sun4i_codec_probe(struct platform_device *pdev) |
| 785 | { | 1465 | { |
| 786 | struct snd_soc_card *card; | 1466 | struct snd_soc_card *card; |
| @@ -829,6 +1509,14 @@ static int sun4i_codec_probe(struct platform_device *pdev) | |||
| 829 | return PTR_ERR(scodec->clk_module); | 1509 | return PTR_ERR(scodec->clk_module); |
| 830 | } | 1510 | } |
| 831 | 1511 | ||
| 1512 | if (quirks->has_reset) { | ||
| 1513 | scodec->rst = devm_reset_control_get(&pdev->dev, NULL); | ||
| 1514 | if (IS_ERR(scodec->rst)) { | ||
| 1515 | dev_err(&pdev->dev, "Failed to get reset control\n"); | ||
| 1516 | return PTR_ERR(scodec->rst); | ||
| 1517 | } | ||
| 1518 | } | ||
| 1519 | |||
| 832 | scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa", | 1520 | scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa", |
| 833 | GPIOD_OUT_LOW); | 1521 | GPIOD_OUT_LOW); |
| 834 | if (IS_ERR(scodec->gpio_pa)) { | 1522 | if (IS_ERR(scodec->gpio_pa)) { |
| @@ -838,27 +1526,48 @@ static int sun4i_codec_probe(struct platform_device *pdev) | |||
| 838 | return ret; | 1526 | return ret; |
| 839 | } | 1527 | } |
| 840 | 1528 | ||
| 1529 | /* reg_field setup */ | ||
| 1530 | scodec->reg_adc_fifoc = devm_regmap_field_alloc(&pdev->dev, | ||
| 1531 | scodec->regmap, | ||
| 1532 | quirks->reg_adc_fifoc); | ||
| 1533 | if (IS_ERR(scodec->reg_adc_fifoc)) { | ||
| 1534 | ret = PTR_ERR(scodec->reg_adc_fifoc); | ||
| 1535 | dev_err(&pdev->dev, "Failed to create regmap fields: %d\n", | ||
| 1536 | ret); | ||
| 1537 | return ret; | ||
| 1538 | } | ||
| 1539 | |||
| 841 | /* Enable the bus clock */ | 1540 | /* Enable the bus clock */ |
| 842 | if (clk_prepare_enable(scodec->clk_apb)) { | 1541 | if (clk_prepare_enable(scodec->clk_apb)) { |
| 843 | dev_err(&pdev->dev, "Failed to enable the APB clock\n"); | 1542 | dev_err(&pdev->dev, "Failed to enable the APB clock\n"); |
| 844 | return -EINVAL; | 1543 | return -EINVAL; |
| 845 | } | 1544 | } |
| 846 | 1545 | ||
| 1546 | /* Deassert the reset control */ | ||
| 1547 | if (scodec->rst) { | ||
| 1548 | ret = reset_control_deassert(scodec->rst); | ||
| 1549 | if (ret) { | ||
| 1550 | dev_err(&pdev->dev, | ||
| 1551 | "Failed to deassert the reset control\n"); | ||
| 1552 | goto err_clk_disable; | ||
| 1553 | } | ||
| 1554 | } | ||
| 1555 | |||
| 847 | /* DMA configuration for TX FIFO */ | 1556 | /* DMA configuration for TX FIFO */ |
| 848 | scodec->playback_dma_data.addr = res->start + SUN4I_CODEC_DAC_TXDATA; | 1557 | scodec->playback_dma_data.addr = res->start + quirks->reg_dac_txdata; |
| 849 | scodec->playback_dma_data.maxburst = 4; | 1558 | scodec->playback_dma_data.maxburst = 8; |
| 850 | scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; | 1559 | scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; |
| 851 | 1560 | ||
| 852 | /* DMA configuration for RX FIFO */ | 1561 | /* DMA configuration for RX FIFO */ |
| 853 | scodec->capture_dma_data.addr = res->start + SUN4I_CODEC_ADC_RXDATA; | 1562 | scodec->capture_dma_data.addr = res->start + quirks->reg_adc_rxdata; |
| 854 | scodec->capture_dma_data.maxburst = 4; | 1563 | scodec->capture_dma_data.maxburst = 8; |
| 855 | scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; | 1564 | scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; |
| 856 | 1565 | ||
| 857 | ret = snd_soc_register_codec(&pdev->dev, &sun4i_codec_codec, | 1566 | ret = snd_soc_register_codec(&pdev->dev, quirks->codec, |
| 858 | &sun4i_codec_dai, 1); | 1567 | &sun4i_codec_dai, 1); |
| 859 | if (ret) { | 1568 | if (ret) { |
| 860 | dev_err(&pdev->dev, "Failed to register our codec\n"); | 1569 | dev_err(&pdev->dev, "Failed to register our codec\n"); |
| 861 | goto err_clk_disable; | 1570 | goto err_assert_reset; |
| 862 | } | 1571 | } |
| 863 | 1572 | ||
| 864 | ret = devm_snd_soc_register_component(&pdev->dev, | 1573 | ret = devm_snd_soc_register_component(&pdev->dev, |
| @@ -875,7 +1584,7 @@ static int sun4i_codec_probe(struct platform_device *pdev) | |||
| 875 | goto err_unregister_codec; | 1584 | goto err_unregister_codec; |
| 876 | } | 1585 | } |
| 877 | 1586 | ||
| 878 | card = sun4i_codec_create_card(&pdev->dev); | 1587 | card = quirks->create_card(&pdev->dev); |
| 879 | if (IS_ERR(card)) { | 1588 | if (IS_ERR(card)) { |
| 880 | ret = PTR_ERR(card); | 1589 | ret = PTR_ERR(card); |
| 881 | dev_err(&pdev->dev, "Failed to create our card\n"); | 1590 | dev_err(&pdev->dev, "Failed to create our card\n"); |
| @@ -895,6 +1604,9 @@ static int sun4i_codec_probe(struct platform_device *pdev) | |||
| 895 | 1604 | ||
| 896 | err_unregister_codec: | 1605 | err_unregister_codec: |
| 897 | snd_soc_unregister_codec(&pdev->dev); | 1606 | snd_soc_unregister_codec(&pdev->dev); |
| 1607 | err_assert_reset: | ||
| 1608 | if (scodec->rst) | ||
| 1609 | reset_control_assert(scodec->rst); | ||
| 898 | err_clk_disable: | 1610 | err_clk_disable: |
| 899 | clk_disable_unprepare(scodec->clk_apb); | 1611 | clk_disable_unprepare(scodec->clk_apb); |
| 900 | return ret; | 1612 | return ret; |
| @@ -907,6 +1619,8 @@ static int sun4i_codec_remove(struct platform_device *pdev) | |||
| 907 | 1619 | ||
| 908 | snd_soc_unregister_card(card); | 1620 | snd_soc_unregister_card(card); |
| 909 | snd_soc_unregister_codec(&pdev->dev); | 1621 | snd_soc_unregister_codec(&pdev->dev); |
| 1622 | if (scodec->rst) | ||
| 1623 | reset_control_assert(scodec->rst); | ||
| 910 | clk_disable_unprepare(scodec->clk_apb); | 1624 | clk_disable_unprepare(scodec->clk_apb); |
| 911 | 1625 | ||
| 912 | return 0; | 1626 | return 0; |
| @@ -926,4 +1640,5 @@ MODULE_DESCRIPTION("Allwinner A10 codec driver"); | |||
| 926 | MODULE_AUTHOR("Emilio López <emilio@elopez.com.ar>"); | 1640 | MODULE_AUTHOR("Emilio López <emilio@elopez.com.ar>"); |
| 927 | MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>"); | 1641 | MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>"); |
| 928 | MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); | 1642 | MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); |
| 1643 | MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>"); | ||
| 929 | MODULE_LICENSE("GPL"); | 1644 | MODULE_LICENSE("GPL"); |
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 687a8f83dbe5..f24d19526603 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c | |||
| @@ -93,6 +93,9 @@ struct sun4i_i2s { | |||
| 93 | struct clk *mod_clk; | 93 | struct clk *mod_clk; |
| 94 | struct regmap *regmap; | 94 | struct regmap *regmap; |
| 95 | 95 | ||
| 96 | unsigned int mclk_freq; | ||
| 97 | |||
| 98 | struct snd_dmaengine_dai_dma_data capture_dma_data; | ||
| 96 | struct snd_dmaengine_dai_dma_data playback_dma_data; | 99 | struct snd_dmaengine_dai_dma_data playback_dma_data; |
| 97 | }; | 100 | }; |
| 98 | 101 | ||
| @@ -157,14 +160,24 @@ static int sun4i_i2s_get_mclk_div(struct sun4i_i2s *i2s, | |||
| 157 | } | 160 | } |
| 158 | 161 | ||
| 159 | static int sun4i_i2s_oversample_rates[] = { 128, 192, 256, 384, 512, 768 }; | 162 | static int sun4i_i2s_oversample_rates[] = { 128, 192, 256, 384, 512, 768 }; |
| 163 | static bool sun4i_i2s_oversample_is_valid(unsigned int oversample) | ||
| 164 | { | ||
| 165 | int i; | ||
| 166 | |||
| 167 | for (i = 0; i < ARRAY_SIZE(sun4i_i2s_oversample_rates); i++) | ||
| 168 | if (sun4i_i2s_oversample_rates[i] == oversample) | ||
| 169 | return true; | ||
| 170 | |||
| 171 | return false; | ||
| 172 | } | ||
| 160 | 173 | ||
| 161 | static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s, | 174 | static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s, |
| 162 | unsigned int rate, | 175 | unsigned int rate, |
| 163 | unsigned int word_size) | 176 | unsigned int word_size) |
| 164 | { | 177 | { |
| 165 | unsigned int clk_rate; | 178 | unsigned int oversample_rate, clk_rate; |
| 166 | int bclk_div, mclk_div; | 179 | int bclk_div, mclk_div; |
| 167 | int ret, i; | 180 | int ret; |
| 168 | 181 | ||
| 169 | switch (rate) { | 182 | switch (rate) { |
| 170 | case 176400: | 183 | case 176400: |
| @@ -196,21 +209,18 @@ static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s, | |||
| 196 | if (ret) | 209 | if (ret) |
| 197 | return ret; | 210 | return ret; |
| 198 | 211 | ||
| 199 | /* Always favor the highest oversampling rate */ | 212 | oversample_rate = i2s->mclk_freq / rate; |
| 200 | for (i = (ARRAY_SIZE(sun4i_i2s_oversample_rates) - 1); i >= 0; i--) { | 213 | if (!sun4i_i2s_oversample_is_valid(oversample_rate)) |
| 201 | unsigned int oversample_rate = sun4i_i2s_oversample_rates[i]; | 214 | return -EINVAL; |
| 202 | |||
| 203 | bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate, | ||
| 204 | word_size); | ||
| 205 | mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate, | ||
| 206 | clk_rate, | ||
| 207 | rate); | ||
| 208 | 215 | ||
| 209 | if ((bclk_div >= 0) && (mclk_div >= 0)) | 216 | bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate, |
| 210 | break; | 217 | word_size); |
| 211 | } | 218 | if (bclk_div < 0) |
| 219 | return -EINVAL; | ||
| 212 | 220 | ||
| 213 | if ((bclk_div < 0) || (mclk_div < 0)) | 221 | mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate, |
| 222 | clk_rate, rate); | ||
| 223 | if (mclk_div < 0) | ||
| 214 | return -EINVAL; | 224 | return -EINVAL; |
| 215 | 225 | ||
| 216 | regmap_write(i2s->regmap, SUN4I_I2S_CLK_DIV_REG, | 226 | regmap_write(i2s->regmap, SUN4I_I2S_CLK_DIV_REG, |
| @@ -341,6 +351,27 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) | |||
| 341 | return 0; | 351 | return 0; |
| 342 | } | 352 | } |
| 343 | 353 | ||
| 354 | static void sun4i_i2s_start_capture(struct sun4i_i2s *i2s) | ||
| 355 | { | ||
| 356 | /* Flush RX FIFO */ | ||
| 357 | regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG, | ||
| 358 | SUN4I_I2S_FIFO_CTRL_FLUSH_RX, | ||
| 359 | SUN4I_I2S_FIFO_CTRL_FLUSH_RX); | ||
| 360 | |||
| 361 | /* Clear RX counter */ | ||
| 362 | regmap_write(i2s->regmap, SUN4I_I2S_RX_CNT_REG, 0); | ||
| 363 | |||
| 364 | /* Enable RX Block */ | ||
| 365 | regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, | ||
| 366 | SUN4I_I2S_CTRL_RX_EN, | ||
| 367 | SUN4I_I2S_CTRL_RX_EN); | ||
| 368 | |||
| 369 | /* Enable RX DRQ */ | ||
| 370 | regmap_update_bits(i2s->regmap, SUN4I_I2S_DMA_INT_CTRL_REG, | ||
| 371 | SUN4I_I2S_DMA_INT_CTRL_RX_DRQ_EN, | ||
| 372 | SUN4I_I2S_DMA_INT_CTRL_RX_DRQ_EN); | ||
| 373 | } | ||
| 374 | |||
| 344 | static void sun4i_i2s_start_playback(struct sun4i_i2s *i2s) | 375 | static void sun4i_i2s_start_playback(struct sun4i_i2s *i2s) |
| 345 | { | 376 | { |
| 346 | /* Flush TX FIFO */ | 377 | /* Flush TX FIFO */ |
| @@ -362,6 +393,18 @@ static void sun4i_i2s_start_playback(struct sun4i_i2s *i2s) | |||
| 362 | SUN4I_I2S_DMA_INT_CTRL_TX_DRQ_EN); | 393 | SUN4I_I2S_DMA_INT_CTRL_TX_DRQ_EN); |
| 363 | } | 394 | } |
| 364 | 395 | ||
| 396 | static void sun4i_i2s_stop_capture(struct sun4i_i2s *i2s) | ||
| 397 | { | ||
| 398 | /* Disable RX Block */ | ||
| 399 | regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, | ||
| 400 | SUN4I_I2S_CTRL_RX_EN, | ||
| 401 | 0); | ||
| 402 | |||
| 403 | /* Disable RX DRQ */ | ||
| 404 | regmap_update_bits(i2s->regmap, SUN4I_I2S_DMA_INT_CTRL_REG, | ||
| 405 | SUN4I_I2S_DMA_INT_CTRL_RX_DRQ_EN, | ||
| 406 | 0); | ||
| 407 | } | ||
| 365 | 408 | ||
| 366 | static void sun4i_i2s_stop_playback(struct sun4i_i2s *i2s) | 409 | static void sun4i_i2s_stop_playback(struct sun4i_i2s *i2s) |
| 367 | { | 410 | { |
| @@ -388,7 +431,7 @@ static int sun4i_i2s_trigger(struct snd_pcm_substream *substream, int cmd, | |||
| 388 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 431 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
| 389 | sun4i_i2s_start_playback(i2s); | 432 | sun4i_i2s_start_playback(i2s); |
| 390 | else | 433 | else |
| 391 | return -EINVAL; | 434 | sun4i_i2s_start_capture(i2s); |
| 392 | break; | 435 | break; |
| 393 | 436 | ||
| 394 | case SNDRV_PCM_TRIGGER_STOP: | 437 | case SNDRV_PCM_TRIGGER_STOP: |
| @@ -397,7 +440,7 @@ static int sun4i_i2s_trigger(struct snd_pcm_substream *substream, int cmd, | |||
| 397 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 440 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
| 398 | sun4i_i2s_stop_playback(i2s); | 441 | sun4i_i2s_stop_playback(i2s); |
| 399 | else | 442 | else |
| 400 | return -EINVAL; | 443 | sun4i_i2s_stop_capture(i2s); |
| 401 | break; | 444 | break; |
| 402 | 445 | ||
| 403 | default: | 446 | default: |
| @@ -447,9 +490,23 @@ static void sun4i_i2s_shutdown(struct snd_pcm_substream *substream, | |||
| 447 | regmap_write(i2s->regmap, SUN4I_I2S_CTRL_REG, 0); | 490 | regmap_write(i2s->regmap, SUN4I_I2S_CTRL_REG, 0); |
| 448 | } | 491 | } |
| 449 | 492 | ||
| 493 | static int sun4i_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, | ||
| 494 | unsigned int freq, int dir) | ||
| 495 | { | ||
| 496 | struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); | ||
| 497 | |||
| 498 | if (clk_id != 0) | ||
| 499 | return -EINVAL; | ||
| 500 | |||
| 501 | i2s->mclk_freq = freq; | ||
| 502 | |||
| 503 | return 0; | ||
| 504 | } | ||
| 505 | |||
| 450 | static const struct snd_soc_dai_ops sun4i_i2s_dai_ops = { | 506 | static const struct snd_soc_dai_ops sun4i_i2s_dai_ops = { |
| 451 | .hw_params = sun4i_i2s_hw_params, | 507 | .hw_params = sun4i_i2s_hw_params, |
| 452 | .set_fmt = sun4i_i2s_set_fmt, | 508 | .set_fmt = sun4i_i2s_set_fmt, |
| 509 | .set_sysclk = sun4i_i2s_set_sysclk, | ||
| 453 | .shutdown = sun4i_i2s_shutdown, | 510 | .shutdown = sun4i_i2s_shutdown, |
| 454 | .startup = sun4i_i2s_startup, | 511 | .startup = sun4i_i2s_startup, |
| 455 | .trigger = sun4i_i2s_trigger, | 512 | .trigger = sun4i_i2s_trigger, |
| @@ -459,7 +516,9 @@ static int sun4i_i2s_dai_probe(struct snd_soc_dai *dai) | |||
| 459 | { | 516 | { |
| 460 | struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); | 517 | struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
| 461 | 518 | ||
| 462 | snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, NULL); | 519 | snd_soc_dai_init_dma_data(dai, |
| 520 | &i2s->playback_dma_data, | ||
| 521 | &i2s->capture_dma_data); | ||
| 463 | 522 | ||
| 464 | snd_soc_dai_set_drvdata(dai, i2s); | 523 | snd_soc_dai_set_drvdata(dai, i2s); |
| 465 | 524 | ||
| @@ -468,6 +527,13 @@ static int sun4i_i2s_dai_probe(struct snd_soc_dai *dai) | |||
| 468 | 527 | ||
| 469 | static struct snd_soc_dai_driver sun4i_i2s_dai = { | 528 | static struct snd_soc_dai_driver sun4i_i2s_dai = { |
| 470 | .probe = sun4i_i2s_dai_probe, | 529 | .probe = sun4i_i2s_dai_probe, |
| 530 | .capture = { | ||
| 531 | .stream_name = "Capture", | ||
| 532 | .channels_min = 2, | ||
| 533 | .channels_max = 2, | ||
| 534 | .rates = SNDRV_PCM_RATE_8000_192000, | ||
| 535 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
| 536 | }, | ||
| 471 | .playback = { | 537 | .playback = { |
| 472 | .stream_name = "Playback", | 538 | .stream_name = "Playback", |
| 473 | .channels_min = 2, | 539 | .channels_min = 2, |
| @@ -630,6 +696,9 @@ static int sun4i_i2s_probe(struct platform_device *pdev) | |||
| 630 | i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG; | 696 | i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG; |
| 631 | i2s->playback_dma_data.maxburst = 4; | 697 | i2s->playback_dma_data.maxburst = 4; |
| 632 | 698 | ||
| 699 | i2s->capture_dma_data.addr = res->start + SUN4I_I2S_FIFO_RX_REG; | ||
| 700 | i2s->capture_dma_data.maxburst = 4; | ||
| 701 | |||
| 633 | pm_runtime_enable(&pdev->dev); | 702 | pm_runtime_enable(&pdev->dev); |
| 634 | if (!pm_runtime_enabled(&pdev->dev)) { | 703 | if (!pm_runtime_enabled(&pdev->dev)) { |
| 635 | ret = sun4i_i2s_runtime_resume(&pdev->dev); | 704 | ret = sun4i_i2s_runtime_resume(&pdev->dev); |
diff --git a/sound/soc/sunxi/sun8i-codec-analog.c b/sound/soc/sunxi/sun8i-codec-analog.c new file mode 100644 index 000000000000..af02290ebe49 --- /dev/null +++ b/sound/soc/sunxi/sun8i-codec-analog.c | |||
| @@ -0,0 +1,665 @@ | |||
| 1 | /* | ||
| 2 | * This driver supports the analog controls for the internal codec | ||
| 3 | * found in Allwinner's A31s, A23, A33 and H3 SoCs. | ||
| 4 | * | ||
| 5 | * Copyright 2016 Chen-Yu Tsai <wens@csie.org> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License as published by | ||
| 9 | * the Free Software Foundation; either version 2 of the License, or | ||
| 10 | * (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <linux/io.h> | ||
| 19 | #include <linux/kernel.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/of.h> | ||
| 22 | #include <linux/of_device.h> | ||
| 23 | #include <linux/platform_device.h> | ||
| 24 | #include <linux/regmap.h> | ||
| 25 | |||
| 26 | #include <sound/soc.h> | ||
| 27 | #include <sound/soc-dapm.h> | ||
| 28 | #include <sound/tlv.h> | ||
| 29 | |||
| 30 | /* Codec analog control register offsets and bit fields */ | ||
| 31 | #define SUN8I_ADDA_HP_VOLC 0x00 | ||
| 32 | #define SUN8I_ADDA_HP_VOLC_PA_CLK_GATE 7 | ||
| 33 | #define SUN8I_ADDA_HP_VOLC_HP_VOL 0 | ||
| 34 | #define SUN8I_ADDA_LOMIXSC 0x01 | ||
| 35 | #define SUN8I_ADDA_LOMIXSC_MIC1 6 | ||
| 36 | #define SUN8I_ADDA_LOMIXSC_MIC2 5 | ||
| 37 | #define SUN8I_ADDA_LOMIXSC_PHONE 4 | ||
| 38 | #define SUN8I_ADDA_LOMIXSC_PHONEN 3 | ||
| 39 | #define SUN8I_ADDA_LOMIXSC_LINEINL 2 | ||
| 40 | #define SUN8I_ADDA_LOMIXSC_DACL 1 | ||
| 41 | #define SUN8I_ADDA_LOMIXSC_DACR 0 | ||
| 42 | #define SUN8I_ADDA_ROMIXSC 0x02 | ||
| 43 | #define SUN8I_ADDA_ROMIXSC_MIC1 6 | ||
| 44 | #define SUN8I_ADDA_ROMIXSC_MIC2 5 | ||
| 45 | #define SUN8I_ADDA_ROMIXSC_PHONE 4 | ||
| 46 | #define SUN8I_ADDA_ROMIXSC_PHONEP 3 | ||
| 47 | #define SUN8I_ADDA_ROMIXSC_LINEINR 2 | ||
| 48 | #define SUN8I_ADDA_ROMIXSC_DACR 1 | ||
| 49 | #define SUN8I_ADDA_ROMIXSC_DACL 0 | ||
| 50 | #define SUN8I_ADDA_DAC_PA_SRC 0x03 | ||
| 51 | #define SUN8I_ADDA_DAC_PA_SRC_DACAREN 7 | ||
| 52 | #define SUN8I_ADDA_DAC_PA_SRC_DACALEN 6 | ||
| 53 | #define SUN8I_ADDA_DAC_PA_SRC_RMIXEN 5 | ||
| 54 | #define SUN8I_ADDA_DAC_PA_SRC_LMIXEN 4 | ||
| 55 | #define SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE 3 | ||
| 56 | #define SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE 2 | ||
| 57 | #define SUN8I_ADDA_DAC_PA_SRC_RHPIS 1 | ||
| 58 | #define SUN8I_ADDA_DAC_PA_SRC_LHPIS 0 | ||
| 59 | #define SUN8I_ADDA_PHONEIN_GCTRL 0x04 | ||
| 60 | #define SUN8I_ADDA_PHONEIN_GCTRL_PHONEPG 4 | ||
| 61 | #define SUN8I_ADDA_PHONEIN_GCTRL_PHONENG 0 | ||
| 62 | #define SUN8I_ADDA_LINEIN_GCTRL 0x05 | ||
| 63 | #define SUN8I_ADDA_LINEIN_GCTRL_LINEING 4 | ||
| 64 | #define SUN8I_ADDA_LINEIN_GCTRL_PHONEG 0 | ||
| 65 | #define SUN8I_ADDA_MICIN_GCTRL 0x06 | ||
| 66 | #define SUN8I_ADDA_MICIN_GCTRL_MIC1G 4 | ||
| 67 | #define SUN8I_ADDA_MICIN_GCTRL_MIC2G 0 | ||
| 68 | #define SUN8I_ADDA_PAEN_HP_CTRL 0x07 | ||
| 69 | #define SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN 7 | ||
| 70 | #define SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN 7 /* H3 specific */ | ||
| 71 | #define SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC 5 | ||
| 72 | #define SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN 4 | ||
| 73 | #define SUN8I_ADDA_PAEN_HP_CTRL_PA_ANTI_POP_CTRL 2 | ||
| 74 | #define SUN8I_ADDA_PAEN_HP_CTRL_LTRNMUTE 1 | ||
| 75 | #define SUN8I_ADDA_PAEN_HP_CTRL_RTLNMUTE 0 | ||
| 76 | #define SUN8I_ADDA_PHONEOUT_CTRL 0x08 | ||
| 77 | #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUTG 5 | ||
| 78 | #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUTEN 4 | ||
| 79 | #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_MIC1 3 | ||
| 80 | #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_MIC2 2 | ||
| 81 | #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_RMIX 1 | ||
| 82 | #define SUN8I_ADDA_PHONEOUT_CTRL_PHONEOUT_LMIX 0 | ||
| 83 | #define SUN8I_ADDA_PHONE_GAIN_CTRL 0x09 | ||
| 84 | #define SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL 3 | ||
| 85 | #define SUN8I_ADDA_PHONE_GAIN_CTRL_PHONEPREG 0 | ||
| 86 | #define SUN8I_ADDA_MIC2G_CTRL 0x0a | ||
| 87 | #define SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN 7 | ||
| 88 | #define SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST 4 | ||
| 89 | #define SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN 3 | ||
| 90 | #define SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN 2 | ||
| 91 | #define SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC 1 | ||
| 92 | #define SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC 0 | ||
| 93 | #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL 0x0b | ||
| 94 | #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN 7 | ||
| 95 | #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN 6 | ||
| 96 | #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIAS_MODE 5 | ||
| 97 | #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN 3 | ||
| 98 | #define SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST 0 | ||
| 99 | #define SUN8I_ADDA_LADCMIXSC 0x0c | ||
| 100 | #define SUN8I_ADDA_LADCMIXSC_MIC1 6 | ||
| 101 | #define SUN8I_ADDA_LADCMIXSC_MIC2 5 | ||
| 102 | #define SUN8I_ADDA_LADCMIXSC_PHONE 4 | ||
| 103 | #define SUN8I_ADDA_LADCMIXSC_PHONEN 3 | ||
| 104 | #define SUN8I_ADDA_LADCMIXSC_LINEINL 2 | ||
| 105 | #define SUN8I_ADDA_LADCMIXSC_OMIXRL 1 | ||
| 106 | #define SUN8I_ADDA_LADCMIXSC_OMIXRR 0 | ||
| 107 | #define SUN8I_ADDA_RADCMIXSC 0x0d | ||
| 108 | #define SUN8I_ADDA_RADCMIXSC_MIC1 6 | ||
| 109 | #define SUN8I_ADDA_RADCMIXSC_MIC2 5 | ||
| 110 | #define SUN8I_ADDA_RADCMIXSC_PHONE 4 | ||
| 111 | #define SUN8I_ADDA_RADCMIXSC_PHONEP 3 | ||
| 112 | #define SUN8I_ADDA_RADCMIXSC_LINEINR 2 | ||
| 113 | #define SUN8I_ADDA_RADCMIXSC_OMIXR 1 | ||
| 114 | #define SUN8I_ADDA_RADCMIXSC_OMIXL 0 | ||
| 115 | #define SUN8I_ADDA_RES 0x0e | ||
| 116 | #define SUN8I_ADDA_RES_MMICBIAS_SEL 4 | ||
| 117 | #define SUN8I_ADDA_RES_PA_ANTI_POP_CTRL 0 | ||
| 118 | #define SUN8I_ADDA_ADC_AP_EN 0x0f | ||
| 119 | #define SUN8I_ADDA_ADC_AP_EN_ADCREN 7 | ||
| 120 | #define SUN8I_ADDA_ADC_AP_EN_ADCLEN 6 | ||
| 121 | #define SUN8I_ADDA_ADC_AP_EN_ADCG 0 | ||
| 122 | |||
| 123 | /* Analog control register access bits */ | ||
| 124 | #define ADDA_PR 0x0 /* PRCM base + 0x1c0 */ | ||
| 125 | #define ADDA_PR_RESET BIT(28) | ||
| 126 | #define ADDA_PR_WRITE BIT(24) | ||
| 127 | #define ADDA_PR_ADDR_SHIFT 16 | ||
| 128 | #define ADDA_PR_ADDR_MASK GENMASK(4, 0) | ||
| 129 | #define ADDA_PR_DATA_IN_SHIFT 8 | ||
| 130 | #define ADDA_PR_DATA_IN_MASK GENMASK(7, 0) | ||
| 131 | #define ADDA_PR_DATA_OUT_SHIFT 0 | ||
| 132 | #define ADDA_PR_DATA_OUT_MASK GENMASK(7, 0) | ||
| 133 | |||
| 134 | /* regmap access bits */ | ||
| 135 | static int adda_reg_read(void *context, unsigned int reg, unsigned int *val) | ||
| 136 | { | ||
| 137 | void __iomem *base = (void __iomem *)context; | ||
| 138 | u32 tmp; | ||
| 139 | |||
| 140 | /* De-assert reset */ | ||
| 141 | writel(readl(base) | ADDA_PR_RESET, base); | ||
| 142 | |||
| 143 | /* Clear write bit */ | ||
| 144 | writel(readl(base) & ~ADDA_PR_WRITE, base); | ||
| 145 | |||
| 146 | /* Set register address */ | ||
| 147 | tmp = readl(base); | ||
| 148 | tmp &= ~(ADDA_PR_ADDR_MASK << ADDA_PR_ADDR_SHIFT); | ||
| 149 | tmp |= (reg & ADDA_PR_ADDR_MASK) << ADDA_PR_ADDR_SHIFT; | ||
| 150 | writel(tmp, base); | ||
| 151 | |||
| 152 | /* Read back value */ | ||
| 153 | *val = readl(base) & ADDA_PR_DATA_OUT_MASK; | ||
| 154 | |||
| 155 | return 0; | ||
| 156 | } | ||
| 157 | |||
| 158 | static int adda_reg_write(void *context, unsigned int reg, unsigned int val) | ||
| 159 | { | ||
| 160 | void __iomem *base = (void __iomem *)context; | ||
| 161 | u32 tmp; | ||
| 162 | |||
| 163 | /* De-assert reset */ | ||
| 164 | writel(readl(base) | ADDA_PR_RESET, base); | ||
| 165 | |||
| 166 | /* Set register address */ | ||
| 167 | tmp = readl(base); | ||
| 168 | tmp &= ~(ADDA_PR_ADDR_MASK << ADDA_PR_ADDR_SHIFT); | ||
| 169 | tmp |= (reg & ADDA_PR_ADDR_MASK) << ADDA_PR_ADDR_SHIFT; | ||
| 170 | writel(tmp, base); | ||
| 171 | |||
| 172 | /* Set data to write */ | ||
| 173 | tmp = readl(base); | ||
| 174 | tmp &= ~(ADDA_PR_DATA_IN_MASK << ADDA_PR_DATA_IN_SHIFT); | ||
| 175 | tmp |= (val & ADDA_PR_DATA_IN_MASK) << ADDA_PR_DATA_IN_SHIFT; | ||
| 176 | writel(tmp, base); | ||
| 177 | |||
| 178 | /* Set write bit to signal a write */ | ||
| 179 | writel(readl(base) | ADDA_PR_WRITE, base); | ||
| 180 | |||
| 181 | /* Clear write bit */ | ||
| 182 | writel(readl(base) & ~ADDA_PR_WRITE, base); | ||
| 183 | |||
| 184 | return 0; | ||
| 185 | } | ||
| 186 | |||
| 187 | static const struct regmap_config adda_pr_regmap_cfg = { | ||
| 188 | .name = "adda-pr", | ||
| 189 | .reg_bits = 5, | ||
| 190 | .reg_stride = 1, | ||
| 191 | .val_bits = 8, | ||
| 192 | .reg_read = adda_reg_read, | ||
| 193 | .reg_write = adda_reg_write, | ||
| 194 | .fast_io = true, | ||
| 195 | .max_register = 24, | ||
| 196 | }; | ||
| 197 | |||
| 198 | /* mixer controls */ | ||
| 199 | static const struct snd_kcontrol_new sun8i_codec_mixer_controls[] = { | ||
| 200 | SOC_DAPM_DOUBLE_R("DAC Playback Switch", | ||
| 201 | SUN8I_ADDA_LOMIXSC, | ||
| 202 | SUN8I_ADDA_ROMIXSC, | ||
| 203 | SUN8I_ADDA_LOMIXSC_DACL, 1, 0), | ||
| 204 | SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch", | ||
| 205 | SUN8I_ADDA_LOMIXSC, | ||
| 206 | SUN8I_ADDA_ROMIXSC, | ||
| 207 | SUN8I_ADDA_LOMIXSC_DACR, 1, 0), | ||
| 208 | SOC_DAPM_DOUBLE_R("Line In Playback Switch", | ||
| 209 | SUN8I_ADDA_LOMIXSC, | ||
| 210 | SUN8I_ADDA_ROMIXSC, | ||
| 211 | SUN8I_ADDA_LOMIXSC_LINEINL, 1, 0), | ||
| 212 | SOC_DAPM_DOUBLE_R("Mic1 Playback Switch", | ||
| 213 | SUN8I_ADDA_LOMIXSC, | ||
| 214 | SUN8I_ADDA_ROMIXSC, | ||
| 215 | SUN8I_ADDA_LOMIXSC_MIC1, 1, 0), | ||
| 216 | SOC_DAPM_DOUBLE_R("Mic2 Playback Switch", | ||
| 217 | SUN8I_ADDA_LOMIXSC, | ||
| 218 | SUN8I_ADDA_ROMIXSC, | ||
| 219 | SUN8I_ADDA_LOMIXSC_MIC2, 1, 0), | ||
| 220 | }; | ||
| 221 | |||
| 222 | /* ADC mixer controls */ | ||
| 223 | static const struct snd_kcontrol_new sun8i_codec_adc_mixer_controls[] = { | ||
| 224 | SOC_DAPM_DOUBLE_R("Mixer Capture Switch", | ||
| 225 | SUN8I_ADDA_LADCMIXSC, | ||
| 226 | SUN8I_ADDA_RADCMIXSC, | ||
| 227 | SUN8I_ADDA_LADCMIXSC_OMIXRL, 1, 0), | ||
| 228 | SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch", | ||
| 229 | SUN8I_ADDA_LADCMIXSC, | ||
| 230 | SUN8I_ADDA_RADCMIXSC, | ||
| 231 | SUN8I_ADDA_LADCMIXSC_OMIXRR, 1, 0), | ||
| 232 | SOC_DAPM_DOUBLE_R("Line In Capture Switch", | ||
| 233 | SUN8I_ADDA_LADCMIXSC, | ||
| 234 | SUN8I_ADDA_RADCMIXSC, | ||
| 235 | SUN8I_ADDA_LADCMIXSC_LINEINL, 1, 0), | ||
| 236 | SOC_DAPM_DOUBLE_R("Mic1 Capture Switch", | ||
| 237 | SUN8I_ADDA_LADCMIXSC, | ||
| 238 | SUN8I_ADDA_RADCMIXSC, | ||
| 239 | SUN8I_ADDA_LADCMIXSC_MIC1, 1, 0), | ||
| 240 | SOC_DAPM_DOUBLE_R("Mic2 Capture Switch", | ||
| 241 | SUN8I_ADDA_LADCMIXSC, | ||
| 242 | SUN8I_ADDA_RADCMIXSC, | ||
| 243 | SUN8I_ADDA_LADCMIXSC_MIC2, 1, 0), | ||
| 244 | }; | ||
| 245 | |||
| 246 | /* volume / mute controls */ | ||
| 247 | static const DECLARE_TLV_DB_SCALE(sun8i_codec_out_mixer_pregain_scale, | ||
| 248 | -450, 150, 0); | ||
| 249 | static const DECLARE_TLV_DB_RANGE(sun8i_codec_mic_gain_scale, | ||
| 250 | 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), | ||
| 251 | 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0), | ||
| 252 | ); | ||
| 253 | |||
| 254 | static const struct snd_kcontrol_new sun8i_codec_common_controls[] = { | ||
| 255 | /* Mixer pre-gains */ | ||
| 256 | SOC_SINGLE_TLV("Line In Playback Volume", SUN8I_ADDA_LINEIN_GCTRL, | ||
| 257 | SUN8I_ADDA_LINEIN_GCTRL_LINEING, | ||
| 258 | 0x7, 0, sun8i_codec_out_mixer_pregain_scale), | ||
| 259 | SOC_SINGLE_TLV("Mic1 Playback Volume", SUN8I_ADDA_MICIN_GCTRL, | ||
| 260 | SUN8I_ADDA_MICIN_GCTRL_MIC1G, | ||
| 261 | 0x7, 0, sun8i_codec_out_mixer_pregain_scale), | ||
| 262 | SOC_SINGLE_TLV("Mic2 Playback Volume", | ||
| 263 | SUN8I_ADDA_MICIN_GCTRL, SUN8I_ADDA_MICIN_GCTRL_MIC2G, | ||
| 264 | 0x7, 0, sun8i_codec_out_mixer_pregain_scale), | ||
| 265 | |||
| 266 | /* Microphone Amp boost gains */ | ||
| 267 | SOC_SINGLE_TLV("Mic1 Boost Volume", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, | ||
| 268 | SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST, 0x7, 0, | ||
| 269 | sun8i_codec_mic_gain_scale), | ||
| 270 | SOC_SINGLE_TLV("Mic2 Boost Volume", SUN8I_ADDA_MIC2G_CTRL, | ||
| 271 | SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST, 0x7, 0, | ||
| 272 | sun8i_codec_mic_gain_scale), | ||
| 273 | |||
| 274 | /* ADC */ | ||
| 275 | SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN8I_ADDA_ADC_AP_EN, | ||
| 276 | SUN8I_ADDA_ADC_AP_EN_ADCG, 0x7, 0, | ||
| 277 | sun8i_codec_out_mixer_pregain_scale), | ||
| 278 | }; | ||
| 279 | |||
| 280 | static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = { | ||
| 281 | /* ADC */ | ||
| 282 | SND_SOC_DAPM_ADC("Left ADC", NULL, SUN8I_ADDA_ADC_AP_EN, | ||
| 283 | SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0), | ||
| 284 | SND_SOC_DAPM_ADC("Right ADC", NULL, SUN8I_ADDA_ADC_AP_EN, | ||
| 285 | SUN8I_ADDA_ADC_AP_EN_ADCREN, 0), | ||
| 286 | |||
| 287 | /* DAC */ | ||
| 288 | SND_SOC_DAPM_DAC("Left DAC", NULL, SUN8I_ADDA_DAC_PA_SRC, | ||
| 289 | SUN8I_ADDA_DAC_PA_SRC_DACALEN, 0), | ||
| 290 | SND_SOC_DAPM_DAC("Right DAC", NULL, SUN8I_ADDA_DAC_PA_SRC, | ||
| 291 | SUN8I_ADDA_DAC_PA_SRC_DACAREN, 0), | ||
| 292 | /* | ||
| 293 | * Due to this component and the codec belonging to separate DAPM | ||
| 294 | * contexts, we need to manually link the above widgets to their | ||
| 295 | * stream widgets at the card level. | ||
| 296 | */ | ||
| 297 | |||
| 298 | /* Line In */ | ||
| 299 | SND_SOC_DAPM_INPUT("LINEIN"), | ||
| 300 | |||
| 301 | /* Microphone inputs */ | ||
| 302 | SND_SOC_DAPM_INPUT("MIC1"), | ||
| 303 | SND_SOC_DAPM_INPUT("MIC2"), | ||
| 304 | |||
| 305 | /* Microphone Bias */ | ||
| 306 | SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, | ||
| 307 | SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN, | ||
| 308 | 0, NULL, 0), | ||
| 309 | |||
| 310 | /* Mic input path */ | ||
| 311 | SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, | ||
| 312 | SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN, 0, NULL, 0), | ||
| 313 | SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN8I_ADDA_MIC2G_CTRL, | ||
| 314 | SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN, 0, NULL, 0), | ||
| 315 | |||
| 316 | /* Mixers */ | ||
| 317 | SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC, | ||
| 318 | SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0, | ||
| 319 | sun8i_codec_mixer_controls, | ||
| 320 | ARRAY_SIZE(sun8i_codec_mixer_controls)), | ||
| 321 | SND_SOC_DAPM_MIXER("Right Mixer", SUN8I_ADDA_DAC_PA_SRC, | ||
| 322 | SUN8I_ADDA_DAC_PA_SRC_RMIXEN, 0, | ||
| 323 | sun8i_codec_mixer_controls, | ||
| 324 | ARRAY_SIZE(sun8i_codec_mixer_controls)), | ||
| 325 | SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN8I_ADDA_ADC_AP_EN, | ||
| 326 | SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0, | ||
| 327 | sun8i_codec_adc_mixer_controls, | ||
| 328 | ARRAY_SIZE(sun8i_codec_adc_mixer_controls)), | ||
| 329 | SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN8I_ADDA_ADC_AP_EN, | ||
| 330 | SUN8I_ADDA_ADC_AP_EN_ADCREN, 0, | ||
| 331 | sun8i_codec_adc_mixer_controls, | ||
| 332 | ARRAY_SIZE(sun8i_codec_adc_mixer_controls)), | ||
| 333 | }; | ||
| 334 | |||
| 335 | static const struct snd_soc_dapm_route sun8i_codec_common_routes[] = { | ||
| 336 | /* Microphone Routes */ | ||
| 337 | { "Mic1 Amplifier", NULL, "MIC1"}, | ||
| 338 | { "Mic2 Amplifier", NULL, "MIC2"}, | ||
| 339 | |||
| 340 | /* Left Mixer Routes */ | ||
| 341 | { "Left Mixer", "DAC Playback Switch", "Left DAC" }, | ||
| 342 | { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" }, | ||
| 343 | { "Left Mixer", "Line In Playback Switch", "LINEIN" }, | ||
| 344 | { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, | ||
| 345 | { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, | ||
| 346 | |||
| 347 | /* Right Mixer Routes */ | ||
| 348 | { "Right Mixer", "DAC Playback Switch", "Right DAC" }, | ||
| 349 | { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" }, | ||
| 350 | { "Right Mixer", "Line In Playback Switch", "LINEIN" }, | ||
| 351 | { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, | ||
| 352 | { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, | ||
| 353 | |||
| 354 | /* Left ADC Mixer Routes */ | ||
| 355 | { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" }, | ||
| 356 | { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" }, | ||
| 357 | { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" }, | ||
| 358 | { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, | ||
| 359 | { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, | ||
| 360 | |||
| 361 | /* Right ADC Mixer Routes */ | ||
| 362 | { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" }, | ||
| 363 | { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" }, | ||
| 364 | { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" }, | ||
| 365 | { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, | ||
| 366 | { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, | ||
| 367 | |||
| 368 | /* ADC Routes */ | ||
| 369 | { "Left ADC", NULL, "Left ADC Mixer" }, | ||
| 370 | { "Right ADC", NULL, "Right ADC Mixer" }, | ||
| 371 | }; | ||
| 372 | |||
| 373 | /* headphone specific controls, widgets, and routes */ | ||
| 374 | static const DECLARE_TLV_DB_SCALE(sun8i_codec_hp_vol_scale, -6300, 100, 1); | ||
| 375 | static const struct snd_kcontrol_new sun8i_codec_headphone_controls[] = { | ||
| 376 | SOC_SINGLE_TLV("Headphone Playback Volume", | ||
| 377 | SUN8I_ADDA_HP_VOLC, | ||
| 378 | SUN8I_ADDA_HP_VOLC_HP_VOL, 0x3f, 0, | ||
| 379 | sun8i_codec_hp_vol_scale), | ||
| 380 | SOC_DOUBLE("Headphone Playback Switch", | ||
| 381 | SUN8I_ADDA_DAC_PA_SRC, | ||
| 382 | SUN8I_ADDA_DAC_PA_SRC_LHPPAMUTE, | ||
| 383 | SUN8I_ADDA_DAC_PA_SRC_RHPPAMUTE, 1, 0), | ||
| 384 | }; | ||
| 385 | |||
| 386 | static const char * const sun8i_codec_hp_src_enum_text[] = { | ||
| 387 | "DAC", "Mixer", | ||
| 388 | }; | ||
| 389 | |||
| 390 | static SOC_ENUM_DOUBLE_DECL(sun8i_codec_hp_src_enum, | ||
| 391 | SUN8I_ADDA_DAC_PA_SRC, | ||
| 392 | SUN8I_ADDA_DAC_PA_SRC_LHPIS, | ||
| 393 | SUN8I_ADDA_DAC_PA_SRC_RHPIS, | ||
| 394 | sun8i_codec_hp_src_enum_text); | ||
| 395 | |||
| 396 | static const struct snd_kcontrol_new sun8i_codec_hp_src[] = { | ||
| 397 | SOC_DAPM_ENUM("Headphone Source Playback Route", | ||
| 398 | sun8i_codec_hp_src_enum), | ||
| 399 | }; | ||
| 400 | |||
| 401 | static const struct snd_soc_dapm_widget sun8i_codec_headphone_widgets[] = { | ||
| 402 | SND_SOC_DAPM_MUX("Headphone Source Playback Route", | ||
| 403 | SND_SOC_NOPM, 0, 0, sun8i_codec_hp_src), | ||
| 404 | SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN8I_ADDA_PAEN_HP_CTRL, | ||
| 405 | SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN, 0, NULL, 0), | ||
| 406 | SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN8I_ADDA_PAEN_HP_CTRL, | ||
| 407 | SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN, 0, NULL, 0), | ||
| 408 | SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN8I_ADDA_PAEN_HP_CTRL, | ||
| 409 | SUN8I_ADDA_PAEN_HP_CTRL_HPCOM_FC, 0x3, 0x3, 0), | ||
| 410 | SND_SOC_DAPM_OUTPUT("HP"), | ||
| 411 | }; | ||
| 412 | |||
| 413 | static const struct snd_soc_dapm_route sun8i_codec_headphone_routes[] = { | ||
| 414 | { "Headphone Source Playback Route", "DAC", "Left DAC" }, | ||
| 415 | { "Headphone Source Playback Route", "DAC", "Right DAC" }, | ||
| 416 | { "Headphone Source Playback Route", "Mixer", "Left Mixer" }, | ||
| 417 | { "Headphone Source Playback Route", "Mixer", "Right Mixer" }, | ||
| 418 | { "Headphone Amp", NULL, "Headphone Source Playback Route" }, | ||
| 419 | { "HPCOM", NULL, "HPCOM Protection" }, | ||
| 420 | { "HP", NULL, "Headphone Amp" }, | ||
| 421 | }; | ||
| 422 | |||
| 423 | static int sun8i_codec_add_headphone(struct snd_soc_component *cmpnt) | ||
| 424 | { | ||
| 425 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); | ||
| 426 | struct device *dev = cmpnt->dev; | ||
| 427 | int ret; | ||
| 428 | |||
| 429 | ret = snd_soc_add_component_controls(cmpnt, | ||
| 430 | sun8i_codec_headphone_controls, | ||
| 431 | ARRAY_SIZE(sun8i_codec_headphone_controls)); | ||
| 432 | if (ret) { | ||
| 433 | dev_err(dev, "Failed to add Headphone controls: %d\n", ret); | ||
| 434 | return ret; | ||
| 435 | } | ||
| 436 | |||
| 437 | ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_headphone_widgets, | ||
| 438 | ARRAY_SIZE(sun8i_codec_headphone_widgets)); | ||
| 439 | if (ret) { | ||
| 440 | dev_err(dev, "Failed to add Headphone DAPM widgets: %d\n", ret); | ||
| 441 | return ret; | ||
| 442 | } | ||
| 443 | |||
| 444 | ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_headphone_routes, | ||
| 445 | ARRAY_SIZE(sun8i_codec_headphone_routes)); | ||
| 446 | if (ret) { | ||
| 447 | dev_err(dev, "Failed to add Headphone DAPM routes: %d\n", ret); | ||
| 448 | return ret; | ||
| 449 | } | ||
| 450 | |||
| 451 | return 0; | ||
| 452 | } | ||
| 453 | |||
| 454 | /* hmic specific widget */ | ||
| 455 | static const struct snd_soc_dapm_widget sun8i_codec_hmic_widgets[] = { | ||
| 456 | SND_SOC_DAPM_SUPPLY("HBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, | ||
| 457 | SUN8I_ADDA_MIC1G_MICBIAS_CTRL_HMICBIASEN, | ||
| 458 | 0, NULL, 0), | ||
| 459 | }; | ||
| 460 | |||
| 461 | static int sun8i_codec_add_hmic(struct snd_soc_component *cmpnt) | ||
| 462 | { | ||
| 463 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); | ||
| 464 | struct device *dev = cmpnt->dev; | ||
| 465 | int ret; | ||
| 466 | |||
| 467 | ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_hmic_widgets, | ||
| 468 | ARRAY_SIZE(sun8i_codec_hmic_widgets)); | ||
| 469 | if (ret) | ||
| 470 | dev_err(dev, "Failed to add Mic3 DAPM widgets: %d\n", ret); | ||
| 471 | |||
| 472 | return ret; | ||
| 473 | } | ||
| 474 | |||
| 475 | /* line out specific controls, widgets and routes */ | ||
| 476 | static const DECLARE_TLV_DB_RANGE(sun8i_codec_lineout_vol_scale, | ||
| 477 | 0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), | ||
| 478 | 2, 31, TLV_DB_SCALE_ITEM(-4350, 150, 0), | ||
| 479 | ); | ||
| 480 | static const struct snd_kcontrol_new sun8i_codec_lineout_controls[] = { | ||
| 481 | SOC_SINGLE_TLV("Line Out Playback Volume", | ||
| 482 | SUN8I_ADDA_PHONE_GAIN_CTRL, | ||
| 483 | SUN8I_ADDA_PHONE_GAIN_CTRL_LINEOUT_VOL, 0x1f, 0, | ||
| 484 | sun8i_codec_lineout_vol_scale), | ||
| 485 | SOC_DOUBLE("Line Out Playback Switch", | ||
| 486 | SUN8I_ADDA_MIC2G_CTRL, | ||
| 487 | SUN8I_ADDA_MIC2G_CTRL_LINEOUTLEN, | ||
| 488 | SUN8I_ADDA_MIC2G_CTRL_LINEOUTREN, 1, 0), | ||
| 489 | }; | ||
| 490 | |||
| 491 | static const char * const sun8i_codec_lineout_src_enum_text[] = { | ||
| 492 | "Stereo", "Mono Differential", | ||
| 493 | }; | ||
| 494 | |||
| 495 | static SOC_ENUM_DOUBLE_DECL(sun8i_codec_lineout_src_enum, | ||
| 496 | SUN8I_ADDA_MIC2G_CTRL, | ||
| 497 | SUN8I_ADDA_MIC2G_CTRL_LINEOUTLSRC, | ||
| 498 | SUN8I_ADDA_MIC2G_CTRL_LINEOUTRSRC, | ||
| 499 | sun8i_codec_lineout_src_enum_text); | ||
| 500 | |||
| 501 | static const struct snd_kcontrol_new sun8i_codec_lineout_src[] = { | ||
| 502 | SOC_DAPM_ENUM("Line Out Source Playback Route", | ||
| 503 | sun8i_codec_lineout_src_enum), | ||
| 504 | }; | ||
| 505 | |||
| 506 | static const struct snd_soc_dapm_widget sun8i_codec_lineout_widgets[] = { | ||
| 507 | SND_SOC_DAPM_MUX("Line Out Source Playback Route", | ||
| 508 | SND_SOC_NOPM, 0, 0, sun8i_codec_lineout_src), | ||
| 509 | /* It is unclear if this is a buffer or gate, model it as a supply */ | ||
| 510 | SND_SOC_DAPM_SUPPLY("Line Out Enable", SUN8I_ADDA_PAEN_HP_CTRL, | ||
| 511 | SUN8I_ADDA_PAEN_HP_CTRL_LINEOUTEN, 0, NULL, 0), | ||
| 512 | SND_SOC_DAPM_OUTPUT("LINEOUT"), | ||
| 513 | }; | ||
| 514 | |||
| 515 | static const struct snd_soc_dapm_route sun8i_codec_lineout_routes[] = { | ||
| 516 | { "Line Out Source Playback Route", "Stereo", "Left Mixer" }, | ||
| 517 | { "Line Out Source Playback Route", "Stereo", "Right Mixer" }, | ||
| 518 | { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" }, | ||
| 519 | { "Line Out Source Playback Route", "Mono Differential", "Right Mixer" }, | ||
| 520 | { "LINEOUT", NULL, "Line Out Source Playback Route" }, | ||
| 521 | { "LINEOUT", NULL, "Line Out Enable", }, | ||
| 522 | }; | ||
| 523 | |||
| 524 | static int sun8i_codec_add_lineout(struct snd_soc_component *cmpnt) | ||
| 525 | { | ||
| 526 | struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); | ||
| 527 | struct device *dev = cmpnt->dev; | ||
| 528 | int ret; | ||
| 529 | |||
| 530 | ret = snd_soc_add_component_controls(cmpnt, | ||
| 531 | sun8i_codec_lineout_controls, | ||
| 532 | ARRAY_SIZE(sun8i_codec_lineout_controls)); | ||
| 533 | if (ret) { | ||
| 534 | dev_err(dev, "Failed to add Line Out controls: %d\n", ret); | ||
| 535 | return ret; | ||
| 536 | } | ||
| 537 | |||
| 538 | ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_lineout_widgets, | ||
| 539 | ARRAY_SIZE(sun8i_codec_lineout_widgets)); | ||
| 540 | if (ret) { | ||
| 541 | dev_err(dev, "Failed to add Line Out DAPM widgets: %d\n", ret); | ||
| 542 | return ret; | ||
| 543 | } | ||
| 544 | |||
| 545 | ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_lineout_routes, | ||
| 546 | ARRAY_SIZE(sun8i_codec_lineout_routes)); | ||
| 547 | if (ret) { | ||
| 548 | dev_err(dev, "Failed to add Line Out DAPM routes: %d\n", ret); | ||
| 549 | return ret; | ||
| 550 | } | ||
| 551 | |||
| 552 | return 0; | ||
| 553 | } | ||
| 554 | |||
| 555 | struct sun8i_codec_analog_quirks { | ||
| 556 | bool has_headphone; | ||
| 557 | bool has_hmic; | ||
| 558 | bool has_lineout; | ||
| 559 | }; | ||
| 560 | |||
| 561 | static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = { | ||
| 562 | .has_headphone = true, | ||
| 563 | .has_hmic = true, | ||
| 564 | }; | ||
| 565 | |||
| 566 | static const struct sun8i_codec_analog_quirks sun8i_h3_quirks = { | ||
| 567 | .has_lineout = true, | ||
| 568 | }; | ||
| 569 | |||
| 570 | static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt) | ||
| 571 | { | ||
| 572 | struct device *dev = cmpnt->dev; | ||
| 573 | const struct sun8i_codec_analog_quirks *quirks; | ||
| 574 | int ret; | ||
| 575 | |||
| 576 | /* | ||
| 577 | * This would never return NULL unless someone directly registers a | ||
| 578 | * platform device matching this driver's name, without specifying a | ||
| 579 | * device tree node. | ||
| 580 | */ | ||
| 581 | quirks = of_device_get_match_data(dev); | ||
| 582 | |||
| 583 | /* Add controls, widgets, and routes for individual features */ | ||
| 584 | |||
| 585 | if (quirks->has_headphone) { | ||
| 586 | ret = sun8i_codec_add_headphone(cmpnt); | ||
| 587 | if (ret) | ||
| 588 | return ret; | ||
| 589 | } | ||
| 590 | |||
| 591 | if (quirks->has_hmic) { | ||
| 592 | ret = sun8i_codec_add_hmic(cmpnt); | ||
| 593 | if (ret) | ||
| 594 | return ret; | ||
| 595 | } | ||
| 596 | |||
| 597 | if (quirks->has_lineout) { | ||
| 598 | ret = sun8i_codec_add_lineout(cmpnt); | ||
| 599 | if (ret) | ||
| 600 | return ret; | ||
| 601 | } | ||
| 602 | |||
| 603 | return 0; | ||
| 604 | } | ||
| 605 | |||
| 606 | static const struct snd_soc_component_driver sun8i_codec_analog_cmpnt_drv = { | ||
| 607 | .controls = sun8i_codec_common_controls, | ||
| 608 | .num_controls = ARRAY_SIZE(sun8i_codec_common_controls), | ||
| 609 | .dapm_widgets = sun8i_codec_common_widgets, | ||
| 610 | .num_dapm_widgets = ARRAY_SIZE(sun8i_codec_common_widgets), | ||
| 611 | .dapm_routes = sun8i_codec_common_routes, | ||
| 612 | .num_dapm_routes = ARRAY_SIZE(sun8i_codec_common_routes), | ||
| 613 | .probe = sun8i_codec_analog_cmpnt_probe, | ||
| 614 | }; | ||
| 615 | |||
| 616 | static const struct of_device_id sun8i_codec_analog_of_match[] = { | ||
| 617 | { | ||
| 618 | .compatible = "allwinner,sun8i-a23-codec-analog", | ||
| 619 | .data = &sun8i_a23_quirks, | ||
| 620 | }, | ||
| 621 | { | ||
| 622 | .compatible = "allwinner,sun8i-h3-codec-analog", | ||
| 623 | .data = &sun8i_h3_quirks, | ||
| 624 | }, | ||
| 625 | {} | ||
| 626 | }; | ||
| 627 | MODULE_DEVICE_TABLE(of, sun8i_codec_analog_of_match); | ||
| 628 | |||
| 629 | static int sun8i_codec_analog_probe(struct platform_device *pdev) | ||
| 630 | { | ||
| 631 | struct resource *res; | ||
| 632 | struct regmap *regmap; | ||
| 633 | void __iomem *base; | ||
| 634 | |||
| 635 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 636 | base = devm_ioremap_resource(&pdev->dev, res); | ||
| 637 | if (IS_ERR(base)) { | ||
| 638 | dev_err(&pdev->dev, "Failed to map the registers\n"); | ||
| 639 | return PTR_ERR(base); | ||
| 640 | } | ||
| 641 | |||
| 642 | regmap = devm_regmap_init(&pdev->dev, NULL, base, &adda_pr_regmap_cfg); | ||
| 643 | if (IS_ERR(regmap)) { | ||
| 644 | dev_err(&pdev->dev, "Failed to create regmap\n"); | ||
| 645 | return PTR_ERR(regmap); | ||
| 646 | } | ||
| 647 | |||
| 648 | return devm_snd_soc_register_component(&pdev->dev, | ||
| 649 | &sun8i_codec_analog_cmpnt_drv, | ||
| 650 | NULL, 0); | ||
| 651 | } | ||
| 652 | |||
| 653 | static struct platform_driver sun8i_codec_analog_driver = { | ||
| 654 | .driver = { | ||
| 655 | .name = "sun8i-codec-analog", | ||
| 656 | .of_match_table = sun8i_codec_analog_of_match, | ||
| 657 | }, | ||
| 658 | .probe = sun8i_codec_analog_probe, | ||
| 659 | }; | ||
| 660 | module_platform_driver(sun8i_codec_analog_driver); | ||
| 661 | |||
| 662 | MODULE_DESCRIPTION("Allwinner internal codec analog controls driver"); | ||
| 663 | MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>"); | ||
| 664 | MODULE_LICENSE("GPL"); | ||
| 665 | MODULE_ALIAS("platform:sun8i-codec-analog"); | ||
diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c index deb597f7c302..eead6e7f205b 100644 --- a/sound/soc/tegra/tegra_alc5632.c +++ b/sound/soc/tegra/tegra_alc5632.c | |||
| @@ -65,7 +65,7 @@ static int tegra_alc5632_asoc_hw_params(struct snd_pcm_substream *substream, | |||
| 65 | return 0; | 65 | return 0; |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | static struct snd_soc_ops tegra_alc5632_asoc_ops = { | 68 | static const struct snd_soc_ops tegra_alc5632_asoc_ops = { |
| 69 | .hw_params = tegra_alc5632_asoc_hw_params, | 69 | .hw_params = tegra_alc5632_asoc_hw_params, |
| 70 | }; | 70 | }; |
| 71 | 71 | ||
diff --git a/sound/soc/tegra/tegra_max98090.c b/sound/soc/tegra/tegra_max98090.c index 902da36581d1..a403db6d563e 100644 --- a/sound/soc/tegra/tegra_max98090.c +++ b/sound/soc/tegra/tegra_max98090.c | |||
| @@ -93,7 +93,7 @@ static int tegra_max98090_asoc_hw_params(struct snd_pcm_substream *substream, | |||
| 93 | return 0; | 93 | return 0; |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | static struct snd_soc_ops tegra_max98090_ops = { | 96 | static const struct snd_soc_ops tegra_max98090_ops = { |
| 97 | .hw_params = tegra_max98090_asoc_hw_params, | 97 | .hw_params = tegra_max98090_asoc_hw_params, |
| 98 | }; | 98 | }; |
| 99 | 99 | ||
diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c index e5ef4e9c4ac5..25b9fc03ba62 100644 --- a/sound/soc/tegra/tegra_rt5640.c +++ b/sound/soc/tegra/tegra_rt5640.c | |||
| @@ -76,7 +76,7 @@ static int tegra_rt5640_asoc_hw_params(struct snd_pcm_substream *substream, | |||
| 76 | return 0; | 76 | return 0; |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | static struct snd_soc_ops tegra_rt5640_ops = { | 79 | static const struct snd_soc_ops tegra_rt5640_ops = { |
| 80 | .hw_params = tegra_rt5640_asoc_hw_params, | 80 | .hw_params = tegra_rt5640_asoc_hw_params, |
| 81 | }; | 81 | }; |
| 82 | 82 | ||
diff --git a/sound/soc/tegra/tegra_rt5677.c b/sound/soc/tegra/tegra_rt5677.c index 1470873ecde6..ebf58d0e0f10 100644 --- a/sound/soc/tegra/tegra_rt5677.c +++ b/sound/soc/tegra/tegra_rt5677.c | |||
| @@ -93,7 +93,7 @@ static int tegra_rt5677_event_hp(struct snd_soc_dapm_widget *w, | |||
| 93 | return 0; | 93 | return 0; |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | static struct snd_soc_ops tegra_rt5677_ops = { | 96 | static const struct snd_soc_ops tegra_rt5677_ops = { |
| 97 | .hw_params = tegra_rt5677_asoc_hw_params, | 97 | .hw_params = tegra_rt5677_asoc_hw_params, |
| 98 | }; | 98 | }; |
| 99 | 99 | ||
diff --git a/sound/soc/tegra/tegra_sgtl5000.c b/sound/soc/tegra/tegra_sgtl5000.c index 1e76869dd488..4bbab098f50b 100644 --- a/sound/soc/tegra/tegra_sgtl5000.c +++ b/sound/soc/tegra/tegra_sgtl5000.c | |||
| @@ -82,7 +82,7 @@ static int tegra_sgtl5000_hw_params(struct snd_pcm_substream *substream, | |||
| 82 | return 0; | 82 | return 0; |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | static struct snd_soc_ops tegra_sgtl5000_ops = { | 85 | static const struct snd_soc_ops tegra_sgtl5000_ops = { |
| 86 | .hw_params = tegra_sgtl5000_hw_params, | 86 | .hw_params = tegra_sgtl5000_hw_params, |
| 87 | }; | 87 | }; |
| 88 | 88 | ||
diff --git a/sound/soc/tegra/tegra_wm8753.c b/sound/soc/tegra/tegra_wm8753.c index f0cd01dbfc38..bdedd1028569 100644 --- a/sound/soc/tegra/tegra_wm8753.c +++ b/sound/soc/tegra/tegra_wm8753.c | |||
| @@ -89,7 +89,7 @@ static int tegra_wm8753_hw_params(struct snd_pcm_substream *substream, | |||
| 89 | return 0; | 89 | return 0; |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | static struct snd_soc_ops tegra_wm8753_ops = { | 92 | static const struct snd_soc_ops tegra_wm8753_ops = { |
| 93 | .hw_params = tegra_wm8753_hw_params, | 93 | .hw_params = tegra_wm8753_hw_params, |
| 94 | }; | 94 | }; |
| 95 | 95 | ||
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index e485278e027a..2013e9c4bba0 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c | |||
| @@ -96,7 +96,7 @@ static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream, | |||
| 96 | return 0; | 96 | return 0; |
| 97 | } | 97 | } |
| 98 | 98 | ||
| 99 | static struct snd_soc_ops tegra_wm8903_ops = { | 99 | static const struct snd_soc_ops tegra_wm8903_ops = { |
| 100 | .hw_params = tegra_wm8903_hw_params, | 100 | .hw_params = tegra_wm8903_hw_params, |
| 101 | }; | 101 | }; |
| 102 | 102 | ||
diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c index 2cea203c4f5f..870f84ab5005 100644 --- a/sound/soc/tegra/trimslice.c +++ b/sound/soc/tegra/trimslice.c | |||
| @@ -74,7 +74,7 @@ static int trimslice_asoc_hw_params(struct snd_pcm_substream *substream, | |||
| 74 | return 0; | 74 | return 0; |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | static struct snd_soc_ops trimslice_asoc_ops = { | 77 | static const struct snd_soc_ops trimslice_asoc_ops = { |
| 78 | .hw_params = trimslice_asoc_hw_params, | 78 | .hw_params = trimslice_asoc_hw_params, |
| 79 | }; | 79 | }; |
| 80 | 80 | ||
