diff options
| -rw-r--r-- | sound/soc/codecs/Kconfig | 6 | ||||
| -rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
| -rw-r--r-- | sound/soc/codecs/cs4265.c | 682 | ||||
| -rw-r--r-- | sound/soc/codecs/cs4265.h | 64 |
4 files changed, 754 insertions, 0 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index cbfa1e18f651..3960a57124fa 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
| @@ -47,6 +47,7 @@ config SND_SOC_ALL_CODECS | |||
| 47 | select SND_SOC_CS42L52 if I2C && INPUT | 47 | select SND_SOC_CS42L52 if I2C && INPUT |
| 48 | select SND_SOC_CS42L56 if I2C && INPUT | 48 | select SND_SOC_CS42L56 if I2C && INPUT |
| 49 | select SND_SOC_CS42L73 if I2C | 49 | select SND_SOC_CS42L73 if I2C |
| 50 | select SND_SOC_CS4265 if I2C | ||
| 50 | select SND_SOC_CS4270 if I2C | 51 | select SND_SOC_CS4270 if I2C |
| 51 | select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI | 52 | select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI |
| 52 | select SND_SOC_CS42XX8_I2C if I2C | 53 | select SND_SOC_CS42XX8_I2C if I2C |
| @@ -338,6 +339,11 @@ config SND_SOC_CS42L73 | |||
| 338 | tristate "Cirrus Logic CS42L73 CODEC" | 339 | tristate "Cirrus Logic CS42L73 CODEC" |
| 339 | depends on I2C | 340 | depends on I2C |
| 340 | 341 | ||
| 342 | config SND_SOC_CS4265 | ||
| 343 | tristate "Cirrus Logic CS4265 CODEC" | ||
| 344 | depends on I2C | ||
| 345 | select REGMAP_I2C | ||
| 346 | |||
| 341 | # Cirrus Logic CS4270 Codec | 347 | # Cirrus Logic CS4270 Codec |
| 342 | config SND_SOC_CS4270 | 348 | config SND_SOC_CS4270 |
| 343 | tristate "Cirrus Logic CS4270 CODEC" | 349 | tristate "Cirrus Logic CS4270 CODEC" |
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index be3377b8d73f..2e62bad23ddc 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
| @@ -37,6 +37,7 @@ snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o | |||
| 37 | snd-soc-cs42l52-objs := cs42l52.o | 37 | snd-soc-cs42l52-objs := cs42l52.o |
| 38 | snd-soc-cs42l56-objs := cs42l56.o | 38 | snd-soc-cs42l56-objs := cs42l56.o |
| 39 | snd-soc-cs42l73-objs := cs42l73.o | 39 | snd-soc-cs42l73-objs := cs42l73.o |
| 40 | snd-soc-cs4265-objs := cs4265.o | ||
| 40 | snd-soc-cs4270-objs := cs4270.o | 41 | snd-soc-cs4270-objs := cs4270.o |
| 41 | snd-soc-cs4271-objs := cs4271.o | 42 | snd-soc-cs4271-objs := cs4271.o |
| 42 | snd-soc-cs42xx8-objs := cs42xx8.o | 43 | snd-soc-cs42xx8-objs := cs42xx8.o |
| @@ -202,6 +203,7 @@ obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o | |||
| 202 | obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o | 203 | obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o |
| 203 | obj-$(CONFIG_SND_SOC_CS42L56) += snd-soc-cs42l56.o | 204 | obj-$(CONFIG_SND_SOC_CS42L56) += snd-soc-cs42l56.o |
| 204 | obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o | 205 | obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o |
| 206 | obj-$(CONFIG_SND_SOC_CS4265) += snd-soc-cs4265.o | ||
| 205 | obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o | 207 | obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o |
| 206 | obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o | 208 | obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o |
| 207 | obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o | 209 | obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o |
diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c new file mode 100644 index 000000000000..c9c04d2987c2 --- /dev/null +++ b/sound/soc/codecs/cs4265.c | |||
| @@ -0,0 +1,682 @@ | |||
| 1 | /* | ||
| 2 | * cs4265.c -- CS4265 ALSA SoC audio driver | ||
| 3 | * | ||
| 4 | * Copyright 2014 Cirrus Logic, Inc. | ||
| 5 | * | ||
| 6 | * Author: Paul Handrigan <paul.handrigan@cirrus.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/moduleparam.h> | ||
| 16 | #include <linux/kernel.h> | ||
| 17 | #include <linux/gpio/consumer.h> | ||
| 18 | #include <linux/init.h> | ||
| 19 | #include <linux/delay.h> | ||
| 20 | #include <linux/i2c.h> | ||
| 21 | #include <linux/input.h> | ||
| 22 | #include <linux/regmap.h> | ||
| 23 | #include <linux/slab.h> | ||
| 24 | #include <linux/platform_device.h> | ||
| 25 | #include <sound/core.h> | ||
| 26 | #include <sound/pcm.h> | ||
| 27 | #include <sound/pcm_params.h> | ||
| 28 | #include <sound/soc.h> | ||
| 29 | #include <sound/soc-dapm.h> | ||
| 30 | #include <sound/initval.h> | ||
| 31 | #include <sound/tlv.h> | ||
| 32 | #include "cs4265.h" | ||
| 33 | |||
| 34 | struct cs4265_private { | ||
| 35 | struct device *dev; | ||
| 36 | struct regmap *regmap; | ||
| 37 | struct gpio_desc *reset_gpio; | ||
| 38 | u8 format; | ||
| 39 | u32 sysclk; | ||
| 40 | }; | ||
| 41 | |||
| 42 | static const struct reg_default cs4265_reg_defaults[] = { | ||
| 43 | { CS4265_PWRCTL, 0x0F }, | ||
| 44 | { CS4265_DAC_CTL, 0x08 }, | ||
| 45 | { CS4265_ADC_CTL, 0x00 }, | ||
| 46 | { CS4265_MCLK_FREQ, 0x00 }, | ||
| 47 | { CS4265_SIG_SEL, 0x40 }, | ||
| 48 | { CS4265_CHB_PGA_CTL, 0x00 }, | ||
| 49 | { CS4265_CHA_PGA_CTL, 0x00 }, | ||
| 50 | { CS4265_ADC_CTL2, 0x19 }, | ||
| 51 | { CS4265_DAC_CHA_VOL, 0x00 }, | ||
| 52 | { CS4265_DAC_CHB_VOL, 0x00 }, | ||
| 53 | { CS4265_DAC_CTL2, 0xC0 }, | ||
| 54 | { CS4265_SPDIF_CTL1, 0x00 }, | ||
| 55 | { CS4265_SPDIF_CTL2, 0x00 }, | ||
| 56 | { CS4265_INT_MASK, 0x00 }, | ||
| 57 | { CS4265_STATUS_MODE_MSB, 0x00 }, | ||
| 58 | { CS4265_STATUS_MODE_LSB, 0x00 }, | ||
| 59 | }; | ||
| 60 | |||
| 61 | static bool cs4265_readable_register(struct device *dev, unsigned int reg) | ||
| 62 | { | ||
| 63 | switch (reg) { | ||
| 64 | case CS4265_PWRCTL: | ||
| 65 | case CS4265_DAC_CTL: | ||
| 66 | case CS4265_ADC_CTL: | ||
| 67 | case CS4265_MCLK_FREQ: | ||
| 68 | case CS4265_SIG_SEL: | ||
| 69 | case CS4265_CHB_PGA_CTL: | ||
| 70 | case CS4265_CHA_PGA_CTL: | ||
| 71 | case CS4265_ADC_CTL2: | ||
| 72 | case CS4265_DAC_CHA_VOL: | ||
| 73 | case CS4265_DAC_CHB_VOL: | ||
| 74 | case CS4265_DAC_CTL2: | ||
| 75 | case CS4265_SPDIF_CTL1: | ||
| 76 | case CS4265_SPDIF_CTL2: | ||
| 77 | case CS4265_INT_MASK: | ||
| 78 | case CS4265_STATUS_MODE_MSB: | ||
| 79 | case CS4265_STATUS_MODE_LSB: | ||
| 80 | return true; | ||
| 81 | default: | ||
| 82 | return false; | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | static bool cs4265_volatile_register(struct device *dev, unsigned int reg) | ||
| 87 | { | ||
| 88 | switch (reg) { | ||
| 89 | case CS4265_INT_STATUS: | ||
| 90 | return 1; | ||
| 91 | default: | ||
| 92 | return 0; | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | static DECLARE_TLV_DB_SCALE(pga_tlv, -1200, 50, 0); | ||
| 97 | |||
| 98 | static DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 0); | ||
| 99 | |||
| 100 | static const char * const digital_input_mux_text[] = { | ||
| 101 | "SDIN1", "SDIN2" | ||
| 102 | }; | ||
| 103 | |||
| 104 | static SOC_ENUM_SINGLE_DECL(digital_input_mux_enum, CS4265_SIG_SEL, 7, | ||
| 105 | digital_input_mux_text); | ||
| 106 | |||
| 107 | static const struct snd_kcontrol_new digital_input_mux = | ||
| 108 | SOC_DAPM_ENUM("Digital Input Mux", digital_input_mux_enum); | ||
| 109 | |||
| 110 | static const char * const mic_linein_text[] = { | ||
| 111 | "MIC", "LINEIN" | ||
| 112 | }; | ||
| 113 | |||
| 114 | static SOC_ENUM_SINGLE_DECL(mic_linein_enum, CS4265_ADC_CTL2, 0, | ||
| 115 | mic_linein_text); | ||
| 116 | |||
| 117 | static const char * const cam_mode_text[] = { | ||
| 118 | "One Byte", "Two Byte" | ||
| 119 | }; | ||
| 120 | |||
| 121 | static SOC_ENUM_SINGLE_DECL(cam_mode_enum, CS4265_SPDIF_CTL1, 5, | ||
| 122 | cam_mode_text); | ||
| 123 | |||
| 124 | static const char * const cam_mono_stereo_text[] = { | ||
| 125 | "Stereo", "Mono" | ||
| 126 | }; | ||
| 127 | |||
| 128 | static SOC_ENUM_SINGLE_DECL(spdif_mono_stereo_enum, CS4265_SPDIF_CTL2, 2, | ||
| 129 | cam_mono_stereo_text); | ||
| 130 | |||
| 131 | static const char * const mono_select_text[] = { | ||
| 132 | "Channel A", "Channel B" | ||
| 133 | }; | ||
| 134 | |||
| 135 | static SOC_ENUM_SINGLE_DECL(spdif_mono_select_enum, CS4265_SPDIF_CTL2, 0, | ||
| 136 | mono_select_text); | ||
| 137 | |||
| 138 | static const struct snd_kcontrol_new mic_linein_mux = | ||
| 139 | SOC_DAPM_ENUM("ADC Input Capture Mux", mic_linein_enum); | ||
| 140 | |||
| 141 | static const struct snd_kcontrol_new loopback_ctl = | ||
| 142 | SOC_DAPM_SINGLE("Switch", CS4265_SIG_SEL, 1, 1, 0); | ||
| 143 | |||
| 144 | static const struct snd_kcontrol_new spdif_switch = | ||
| 145 | SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 0, 0); | ||
| 146 | |||
| 147 | static const struct snd_kcontrol_new dac_switch = | ||
| 148 | SOC_DAPM_SINGLE("Switch", CS4265_PWRCTL, 1, 1, 0); | ||
| 149 | |||
| 150 | static const struct snd_kcontrol_new cs4265_snd_controls[] = { | ||
| 151 | |||
| 152 | SOC_DOUBLE_R_SX_TLV("PGA Volume", CS4265_CHA_PGA_CTL, | ||
| 153 | CS4265_CHB_PGA_CTL, 0, 0x28, 0x30, pga_tlv), | ||
| 154 | SOC_DOUBLE_R_TLV("DAC Volume", CS4265_DAC_CHA_VOL, | ||
| 155 | CS4265_DAC_CHB_VOL, 0, 0xFF, 1, dac_tlv), | ||
| 156 | SOC_SINGLE("De-emp 44.1kHz Switch", CS4265_DAC_CTL, 1, | ||
| 157 | 1, 0), | ||
| 158 | SOC_SINGLE("DAC INV Switch", CS4265_DAC_CTL2, 5, | ||
| 159 | 1, 0), | ||
| 160 | SOC_SINGLE("DAC Zero Cross Switch", CS4265_DAC_CTL2, 6, | ||
| 161 | 1, 0), | ||
| 162 | SOC_SINGLE("DAC Soft Ramp Switch", CS4265_DAC_CTL2, 7, | ||
| 163 | 1, 0), | ||
| 164 | SOC_SINGLE("ADC HPF Switch", CS4265_ADC_CTL, 1, | ||
| 165 | 1, 0), | ||
| 166 | SOC_SINGLE("ADC Zero Cross Switch", CS4265_ADC_CTL2, 3, | ||
| 167 | 1, 1), | ||
| 168 | SOC_SINGLE("ADC Soft Ramp Switch", CS4265_ADC_CTL2, 7, | ||
| 169 | 1, 0), | ||
| 170 | SOC_SINGLE("E to F Buffer Disable Switch", CS4265_SPDIF_CTL1, | ||
| 171 | 6, 1, 0), | ||
| 172 | SOC_ENUM("C Data Access", cam_mode_enum), | ||
| 173 | SOC_SINGLE("Validity Bit Control Switch", CS4265_SPDIF_CTL2, | ||
| 174 | 3, 1, 0), | ||
| 175 | SOC_ENUM("SPDIF Mono/Stereo", spdif_mono_stereo_enum), | ||
| 176 | SOC_SINGLE("MMTLR Data Switch", 0, | ||
| 177 | 1, 1, 0), | ||
| 178 | SOC_ENUM("Mono Channel Select", spdif_mono_select_enum), | ||
| 179 | SND_SOC_BYTES("C Data Buffer", CS4265_C_DATA_BUFF, 24), | ||
| 180 | }; | ||
| 181 | |||
| 182 | static const struct snd_soc_dapm_widget cs4265_dapm_widgets[] = { | ||
| 183 | |||
| 184 | SND_SOC_DAPM_INPUT("LINEINL"), | ||
| 185 | SND_SOC_DAPM_INPUT("LINEINR"), | ||
| 186 | SND_SOC_DAPM_INPUT("MICL"), | ||
| 187 | SND_SOC_DAPM_INPUT("MICR"), | ||
| 188 | |||
| 189 | SND_SOC_DAPM_AIF_OUT("DOUT", NULL, 0, | ||
| 190 | SND_SOC_NOPM, 0, 0), | ||
| 191 | SND_SOC_DAPM_AIF_OUT("SPDIFOUT", NULL, 0, | ||
| 192 | SND_SOC_NOPM, 0, 0), | ||
| 193 | |||
| 194 | SND_SOC_DAPM_MUX("ADC Mux", SND_SOC_NOPM, 0, 0, &mic_linein_mux), | ||
| 195 | |||
| 196 | SND_SOC_DAPM_ADC("ADC", NULL, CS4265_PWRCTL, 2, 1), | ||
| 197 | SND_SOC_DAPM_PGA("Pre-amp MIC", CS4265_PWRCTL, 3, | ||
| 198 | 1, NULL, 0), | ||
| 199 | |||
| 200 | SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, | ||
| 201 | 0, 0, &digital_input_mux), | ||
| 202 | |||
| 203 | SND_SOC_DAPM_MIXER("SDIN1 Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), | ||
| 204 | SND_SOC_DAPM_MIXER("SDIN2 Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), | ||
| 205 | SND_SOC_DAPM_MIXER("SPDIF Transmitter", SND_SOC_NOPM, 0, 0, NULL, 0), | ||
| 206 | |||
| 207 | SND_SOC_DAPM_SWITCH("Loopback", SND_SOC_NOPM, 0, 0, | ||
| 208 | &loopback_ctl), | ||
| 209 | SND_SOC_DAPM_SWITCH("SPDIF", SND_SOC_NOPM, 0, 0, | ||
| 210 | &spdif_switch), | ||
| 211 | SND_SOC_DAPM_SWITCH("DAC", CS4265_PWRCTL, 1, 1, | ||
| 212 | &dac_switch), | ||
| 213 | |||
| 214 | SND_SOC_DAPM_AIF_IN("DIN1", NULL, 0, | ||
| 215 | SND_SOC_NOPM, 0, 0), | ||
| 216 | SND_SOC_DAPM_AIF_IN("DIN2", NULL, 0, | ||
| 217 | SND_SOC_NOPM, 0, 0), | ||
| 218 | SND_SOC_DAPM_AIF_IN("TXIN", NULL, 0, | ||
| 219 | CS4265_SPDIF_CTL2, 5, 1), | ||
| 220 | |||
| 221 | SND_SOC_DAPM_OUTPUT("LINEOUTL"), | ||
| 222 | SND_SOC_DAPM_OUTPUT("LINEOUTR"), | ||
| 223 | |||
| 224 | }; | ||
| 225 | |||
| 226 | static const struct snd_soc_dapm_route cs4265_audio_map[] = { | ||
| 227 | |||
| 228 | {"DIN1", NULL, "DAI1 Playback"}, | ||
| 229 | {"DIN2", NULL, "DAI2 Playback"}, | ||
| 230 | {"SDIN1 Input Mixer", NULL, "DIN1"}, | ||
| 231 | {"SDIN2 Input Mixer", NULL, "DIN2"}, | ||
| 232 | {"Input Mux", "SDIN1", "SDIN1 Input Mixer"}, | ||
| 233 | {"Input Mux", "SDIN2", "SDIN2 Input Mixer"}, | ||
| 234 | {"DAC", "Switch", "Input Mux"}, | ||
| 235 | {"SPDIF", "Switch", "Input Mux"}, | ||
| 236 | {"LINEOUTL", NULL, "DAC"}, | ||
| 237 | {"LINEOUTR", NULL, "DAC"}, | ||
| 238 | {"SPDIFOUT", NULL, "SPDIF"}, | ||
| 239 | |||
| 240 | {"ADC Mux", "LINEIN", "LINEINL"}, | ||
| 241 | {"ADC Mux", "LINEIN", "LINEINR"}, | ||
| 242 | {"ADC Mux", "MIC", "MICL"}, | ||
| 243 | {"ADC Mux", "MIC", "MICR"}, | ||
| 244 | {"ADC", NULL, "ADC Mux"}, | ||
| 245 | {"DOUT", NULL, "ADC"}, | ||
| 246 | {"DAI1 Capture", NULL, "DOUT"}, | ||
| 247 | {"DAI2 Capture", NULL, "DOUT"}, | ||
| 248 | |||
| 249 | /* Loopback */ | ||
| 250 | {"Loopback", "Switch", "ADC"}, | ||
| 251 | {"DAC", NULL, "Loopback"}, | ||
| 252 | }; | ||
| 253 | |||
| 254 | struct cs4265_clk_para { | ||
| 255 | u32 mclk; | ||
| 256 | u32 rate; | ||
| 257 | u8 fm_mode; /* values 1, 2, or 4 */ | ||
| 258 | u8 mclkdiv; | ||
| 259 | }; | ||
| 260 | |||
| 261 | static const struct cs4265_clk_para clk_map_table[] = { | ||
| 262 | /*32k*/ | ||
| 263 | {8192000, 32000, 0, 0}, | ||
| 264 | {12288000, 32000, 0, 1}, | ||
| 265 | {16384000, 32000, 0, 2}, | ||
| 266 | {24576000, 32000, 0, 3}, | ||
| 267 | {32768000, 32000, 0, 4}, | ||
| 268 | |||
| 269 | /*44.1k*/ | ||
| 270 | {11289600, 44100, 0, 0}, | ||
| 271 | {16934400, 44100, 0, 1}, | ||
| 272 | {22579200, 44100, 0, 2}, | ||
| 273 | {33868000, 44100, 0, 3}, | ||
| 274 | {45158400, 44100, 0, 4}, | ||
| 275 | |||
| 276 | /*48k*/ | ||
| 277 | {12288000, 48000, 0, 0}, | ||
| 278 | {18432000, 48000, 0, 1}, | ||
| 279 | {24576000, 48000, 0, 2}, | ||
| 280 | {36864000, 48000, 0, 3}, | ||
| 281 | {49152000, 48000, 0, 4}, | ||
| 282 | |||
| 283 | /*64k*/ | ||
| 284 | {8192000, 64000, 1, 0}, | ||
| 285 | {1228800, 64000, 1, 1}, | ||
| 286 | {1693440, 64000, 1, 2}, | ||
| 287 | {2457600, 64000, 1, 3}, | ||
| 288 | {3276800, 64000, 1, 4}, | ||
| 289 | |||
| 290 | /* 88.2k */ | ||
| 291 | {11289600, 88200, 1, 0}, | ||
| 292 | {16934400, 88200, 1, 1}, | ||
| 293 | {22579200, 88200, 1, 2}, | ||
| 294 | {33868000, 88200, 1, 3}, | ||
| 295 | {45158400, 88200, 1, 4}, | ||
| 296 | |||
| 297 | /* 96k */ | ||
| 298 | {12288000, 96000, 1, 0}, | ||
| 299 | {18432000, 96000, 1, 1}, | ||
| 300 | {24576000, 96000, 1, 2}, | ||
| 301 | {36864000, 96000, 1, 3}, | ||
| 302 | {49152000, 96000, 1, 4}, | ||
| 303 | |||
| 304 | /* 128k */ | ||
| 305 | {8192000, 128000, 2, 0}, | ||
| 306 | {12288000, 128000, 2, 1}, | ||
| 307 | {16934400, 128000, 2, 2}, | ||
| 308 | {24576000, 128000, 2, 3}, | ||
| 309 | {32768000, 128000, 2, 4}, | ||
| 310 | |||
| 311 | /* 176.4k */ | ||
| 312 | {11289600, 176400, 2, 0}, | ||
| 313 | {16934400, 176400, 2, 1}, | ||
| 314 | {22579200, 176400, 2, 2}, | ||
| 315 | {33868000, 176400, 2, 3}, | ||
| 316 | {49152000, 176400, 2, 4}, | ||
| 317 | |||
| 318 | /* 192k */ | ||
| 319 | {12288000, 192000, 2, 0}, | ||
| 320 | {18432000, 192000, 2, 1}, | ||
| 321 | {24576000, 192000, 2, 2}, | ||
| 322 | {36864000, 192000, 2, 3}, | ||
| 323 | {49152000, 192000, 2, 4}, | ||
| 324 | }; | ||
| 325 | |||
| 326 | static int cs4265_get_clk_index(int mclk, int rate) | ||
| 327 | { | ||
| 328 | int i; | ||
| 329 | |||
| 330 | for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) { | ||
| 331 | if (clk_map_table[i].rate == rate && | ||
| 332 | clk_map_table[i].mclk == mclk) | ||
| 333 | return i; | ||
| 334 | } | ||
| 335 | return -EINVAL; | ||
| 336 | } | ||
| 337 | |||
| 338 | static int cs4265_set_sysclk(struct snd_soc_dai *codec_dai, int clk_id, | ||
| 339 | unsigned int freq, int dir) | ||
| 340 | { | ||
| 341 | struct snd_soc_codec *codec = codec_dai->codec; | ||
| 342 | struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec); | ||
| 343 | int i; | ||
| 344 | |||
| 345 | if (clk_id != 0) { | ||
| 346 | dev_err(codec->dev, "Invalid clk_id %d\n", clk_id); | ||
| 347 | return -EINVAL; | ||
| 348 | } | ||
| 349 | for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) { | ||
| 350 | if (clk_map_table[i].mclk == freq) { | ||
| 351 | cs4265->sysclk = freq; | ||
| 352 | return 0; | ||
| 353 | } | ||
| 354 | } | ||
| 355 | cs4265->sysclk = 0; | ||
| 356 | dev_err(codec->dev, "Invalid freq parameter %d\n", freq); | ||
| 357 | return -EINVAL; | ||
| 358 | } | ||
| 359 | |||
| 360 | static int cs4265_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) | ||
| 361 | { | ||
| 362 | struct snd_soc_codec *codec = codec_dai->codec; | ||
| 363 | struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec); | ||
| 364 | u8 iface = 0; | ||
| 365 | |||
| 366 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
| 367 | case SND_SOC_DAIFMT_CBM_CFM: | ||
| 368 | snd_soc_update_bits(codec, CS4265_ADC_CTL, | ||
| 369 | CS4265_ADC_MASTER, | ||
| 370 | CS4265_ADC_MASTER); | ||
| 371 | break; | ||
| 372 | case SND_SOC_DAIFMT_CBS_CFS: | ||
| 373 | snd_soc_update_bits(codec, CS4265_ADC_CTL, | ||
| 374 | CS4265_ADC_MASTER, | ||
| 375 | 0); | ||
| 376 | break; | ||
| 377 | default: | ||
| 378 | return -EINVAL; | ||
| 379 | } | ||
| 380 | |||
| 381 | /* interface format */ | ||
| 382 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
| 383 | case SND_SOC_DAIFMT_I2S: | ||
| 384 | iface |= SND_SOC_DAIFMT_I2S; | ||
| 385 | break; | ||
| 386 | case SND_SOC_DAIFMT_RIGHT_J: | ||
| 387 | iface |= SND_SOC_DAIFMT_RIGHT_J; | ||
| 388 | break; | ||
| 389 | case SND_SOC_DAIFMT_LEFT_J: | ||
| 390 | iface |= SND_SOC_DAIFMT_LEFT_J; | ||
| 391 | break; | ||
| 392 | default: | ||
| 393 | return -EINVAL; | ||
| 394 | } | ||
| 395 | |||
| 396 | cs4265->format = iface; | ||
| 397 | return 0; | ||
| 398 | } | ||
| 399 | |||
| 400 | static int cs4265_digital_mute(struct snd_soc_dai *dai, int mute) | ||
| 401 | { | ||
| 402 | struct snd_soc_codec *codec = dai->codec; | ||
| 403 | |||
| 404 | if (mute) { | ||
| 405 | snd_soc_update_bits(codec, CS4265_DAC_CTL, | ||
| 406 | CS4265_DAC_CTL_MUTE, | ||
| 407 | CS4265_DAC_CTL_MUTE); | ||
| 408 | snd_soc_update_bits(codec, CS4265_SPDIF_CTL2, | ||
| 409 | CS4265_SPDIF_CTL2_MUTE, | ||
| 410 | CS4265_SPDIF_CTL2_MUTE); | ||
| 411 | } else { | ||
| 412 | snd_soc_update_bits(codec, CS4265_DAC_CTL, | ||
| 413 | CS4265_DAC_CTL_MUTE, | ||
| 414 | 0); | ||
| 415 | snd_soc_update_bits(codec, CS4265_SPDIF_CTL2, | ||
| 416 | CS4265_SPDIF_CTL2_MUTE, | ||
| 417 | 0); | ||
| 418 | } | ||
| 419 | return 0; | ||
| 420 | } | ||
| 421 | |||
| 422 | static int cs4265_pcm_hw_params(struct snd_pcm_substream *substream, | ||
| 423 | struct snd_pcm_hw_params *params, | ||
| 424 | struct snd_soc_dai *dai) | ||
| 425 | { | ||
| 426 | struct snd_soc_codec *codec = dai->codec; | ||
| 427 | struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec); | ||
| 428 | int index; | ||
| 429 | |||
| 430 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && | ||
| 431 | ((cs4265->format & SND_SOC_DAIFMT_FORMAT_MASK) | ||
| 432 | == SND_SOC_DAIFMT_RIGHT_J)) | ||
| 433 | return -EINVAL; | ||
| 434 | |||
| 435 | index = cs4265_get_clk_index(cs4265->sysclk, params_rate(params)); | ||
| 436 | if (index >= 0) { | ||
| 437 | snd_soc_update_bits(codec, CS4265_ADC_CTL, | ||
| 438 | CS4265_ADC_FM, clk_map_table[index].fm_mode); | ||
| 439 | snd_soc_update_bits(codec, CS4265_MCLK_FREQ, | ||
| 440 | CS4265_MCLK_FREQ_MASK, | ||
| 441 | clk_map_table[index].mclkdiv); | ||
| 442 | |||
| 443 | } else { | ||
| 444 | dev_err(codec->dev, "can't get correct mclk\n"); | ||
| 445 | return -EINVAL; | ||
| 446 | } | ||
| 447 | |||
| 448 | switch (cs4265->format & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
| 449 | case SND_SOC_DAIFMT_I2S: | ||
| 450 | snd_soc_update_bits(codec, CS4265_DAC_CTL, | ||
| 451 | CS4265_DAC_CTL_DIF, (1 << 4)); | ||
| 452 | snd_soc_update_bits(codec, CS4265_ADC_CTL, | ||
| 453 | CS4265_ADC_DIF, (1 << 4)); | ||
| 454 | snd_soc_update_bits(codec, CS4265_SPDIF_CTL2, | ||
| 455 | CS4265_SPDIF_CTL2_DIF, (1 << 6)); | ||
| 456 | break; | ||
| 457 | case SND_SOC_DAIFMT_RIGHT_J: | ||
| 458 | if (params_format(params) & SNDRV_PCM_FORMAT_S16_LE) { | ||
| 459 | snd_soc_update_bits(codec, CS4265_DAC_CTL, | ||
| 460 | CS4265_DAC_CTL_DIF, (1 << 5)); | ||
| 461 | snd_soc_update_bits(codec, CS4265_ADC_CTL, | ||
| 462 | CS4265_SPDIF_CTL2_DIF, (1 << 7)); | ||
| 463 | } else { | ||
| 464 | snd_soc_update_bits(codec, CS4265_DAC_CTL, | ||
| 465 | CS4265_DAC_CTL_DIF, (3 << 5)); | ||
| 466 | snd_soc_update_bits(codec, CS4265_ADC_CTL, | ||
| 467 | CS4265_SPDIF_CTL2_DIF, (1 << 7)); | ||
| 468 | } | ||
| 469 | break; | ||
| 470 | case SND_SOC_DAIFMT_LEFT_J: | ||
| 471 | snd_soc_update_bits(codec, CS4265_DAC_CTL, | ||
| 472 | CS4265_DAC_CTL_DIF, 0); | ||
| 473 | snd_soc_update_bits(codec, CS4265_ADC_CTL, | ||
| 474 | CS4265_ADC_DIF, 0); | ||
| 475 | snd_soc_update_bits(codec, CS4265_ADC_CTL, | ||
| 476 | CS4265_SPDIF_CTL2_DIF, (1 << 6)); | ||
| 477 | |||
| 478 | break; | ||
| 479 | default: | ||
| 480 | return -EINVAL; | ||
| 481 | } | ||
| 482 | return 0; | ||
| 483 | } | ||
| 484 | |||
| 485 | static int cs4265_set_bias_level(struct snd_soc_codec *codec, | ||
| 486 | enum snd_soc_bias_level level) | ||
| 487 | { | ||
| 488 | switch (level) { | ||
| 489 | case SND_SOC_BIAS_ON: | ||
| 490 | break; | ||
| 491 | case SND_SOC_BIAS_PREPARE: | ||
| 492 | snd_soc_update_bits(codec, CS4265_PWRCTL, | ||
| 493 | CS4265_PWRCTL_PDN, 0); | ||
| 494 | break; | ||
| 495 | case SND_SOC_BIAS_STANDBY: | ||
| 496 | snd_soc_update_bits(codec, CS4265_PWRCTL, | ||
| 497 | CS4265_PWRCTL_PDN, | ||
| 498 | CS4265_PWRCTL_PDN); | ||
| 499 | break; | ||
| 500 | case SND_SOC_BIAS_OFF: | ||
| 501 | snd_soc_update_bits(codec, CS4265_PWRCTL, | ||
| 502 | CS4265_PWRCTL_PDN, | ||
| 503 | CS4265_PWRCTL_PDN); | ||
| 504 | break; | ||
| 505 | } | ||
| 506 | codec->dapm.bias_level = level; | ||
| 507 | return 0; | ||
| 508 | } | ||
| 509 | |||
| 510 | #define CS4265_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ | ||
| 511 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \ | ||
| 512 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \ | ||
| 513 | SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) | ||
| 514 | |||
| 515 | #define CS4265_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \ | ||
| 516 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE) | ||
| 517 | |||
| 518 | static const struct snd_soc_dai_ops cs4265_ops = { | ||
| 519 | .hw_params = cs4265_pcm_hw_params, | ||
| 520 | .digital_mute = cs4265_digital_mute, | ||
| 521 | .set_fmt = cs4265_set_fmt, | ||
| 522 | .set_sysclk = cs4265_set_sysclk, | ||
| 523 | }; | ||
| 524 | |||
| 525 | static struct snd_soc_dai_driver cs4265_dai[] = { | ||
| 526 | { | ||
| 527 | .name = "cs4265-dai1", | ||
| 528 | .playback = { | ||
| 529 | .stream_name = "DAI1 Playback", | ||
| 530 | .channels_min = 1, | ||
| 531 | .channels_max = 2, | ||
| 532 | .rates = CS4265_RATES, | ||
| 533 | .formats = CS4265_FORMATS, | ||
| 534 | }, | ||
| 535 | .capture = { | ||
| 536 | .stream_name = "DAI1 Capture", | ||
| 537 | .channels_min = 1, | ||
| 538 | .channels_max = 2, | ||
| 539 | .rates = CS4265_RATES, | ||
| 540 | .formats = CS4265_FORMATS, | ||
| 541 | }, | ||
| 542 | .ops = &cs4265_ops, | ||
| 543 | }, | ||
| 544 | { | ||
| 545 | .name = "cs4265-dai2", | ||
| 546 | .playback = { | ||
| 547 | .stream_name = "DAI2 Playback", | ||
| 548 | .channels_min = 1, | ||
| 549 | .channels_max = 2, | ||
| 550 | .rates = CS4265_RATES, | ||
| 551 | .formats = CS4265_FORMATS, | ||
| 552 | }, | ||
| 553 | .capture = { | ||
| 554 | .stream_name = "DAI2 Capture", | ||
| 555 | .channels_min = 1, | ||
| 556 | .channels_max = 2, | ||
| 557 | .rates = CS4265_RATES, | ||
| 558 | .formats = CS4265_FORMATS, | ||
| 559 | }, | ||
| 560 | .ops = &cs4265_ops, | ||
| 561 | }, | ||
| 562 | }; | ||
| 563 | |||
| 564 | static const struct snd_soc_codec_driver soc_codec_cs4265 = { | ||
| 565 | .set_bias_level = cs4265_set_bias_level, | ||
| 566 | |||
| 567 | .dapm_widgets = cs4265_dapm_widgets, | ||
| 568 | .num_dapm_widgets = ARRAY_SIZE(cs4265_dapm_widgets), | ||
| 569 | .dapm_routes = cs4265_audio_map, | ||
| 570 | .num_dapm_routes = ARRAY_SIZE(cs4265_audio_map), | ||
| 571 | |||
| 572 | .controls = cs4265_snd_controls, | ||
| 573 | .num_controls = ARRAY_SIZE(cs4265_snd_controls), | ||
| 574 | }; | ||
| 575 | |||
| 576 | static const struct regmap_config cs4265_regmap = { | ||
| 577 | .reg_bits = 8, | ||
| 578 | .val_bits = 8, | ||
| 579 | |||
| 580 | .max_register = CS4265_MAX_REGISTER, | ||
| 581 | .reg_defaults = cs4265_reg_defaults, | ||
| 582 | .num_reg_defaults = ARRAY_SIZE(cs4265_reg_defaults), | ||
| 583 | .readable_reg = cs4265_readable_register, | ||
| 584 | .volatile_reg = cs4265_volatile_register, | ||
| 585 | .cache_type = REGCACHE_RBTREE, | ||
| 586 | }; | ||
| 587 | |||
| 588 | static int cs4265_i2c_probe(struct i2c_client *i2c_client, | ||
| 589 | const struct i2c_device_id *id) | ||
| 590 | { | ||
| 591 | struct cs4265_private *cs4265; | ||
| 592 | int ret = 0; | ||
| 593 | unsigned int devid = 0; | ||
| 594 | unsigned int reg; | ||
| 595 | |||
| 596 | cs4265 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs4265_private), | ||
| 597 | GFP_KERNEL); | ||
| 598 | if (cs4265 == NULL) | ||
| 599 | return -ENOMEM; | ||
| 600 | cs4265->dev = &i2c_client->dev; | ||
| 601 | |||
| 602 | cs4265->regmap = devm_regmap_init_i2c(i2c_client, &cs4265_regmap); | ||
| 603 | if (IS_ERR(cs4265->regmap)) { | ||
| 604 | ret = PTR_ERR(cs4265->regmap); | ||
| 605 | dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); | ||
| 606 | return ret; | ||
| 607 | } | ||
| 608 | |||
| 609 | cs4265->reset_gpio = devm_gpiod_get(&i2c_client->dev, | ||
| 610 | "reset-gpios"); | ||
| 611 | if (IS_ERR(cs4265->reset_gpio)) { | ||
| 612 | ret = PTR_ERR(cs4265->reset_gpio); | ||
| 613 | if (ret != -ENOENT && ret != -ENOSYS) | ||
| 614 | return ret; | ||
| 615 | |||
| 616 | cs4265->reset_gpio = NULL; | ||
| 617 | } else { | ||
| 618 | ret = gpiod_direction_output(cs4265->reset_gpio, 0); | ||
| 619 | if (ret) | ||
| 620 | return ret; | ||
| 621 | mdelay(1); | ||
| 622 | gpiod_set_value_cansleep(cs4265->reset_gpio, 1); | ||
| 623 | |||
| 624 | } | ||
| 625 | |||
| 626 | i2c_set_clientdata(i2c_client, cs4265); | ||
| 627 | |||
| 628 | ret = regmap_read(cs4265->regmap, CS4265_CHIP_ID, ®); | ||
| 629 | devid = reg & CS4265_CHIP_ID_MASK; | ||
| 630 | if (devid != CS4265_CHIP_ID_VAL) { | ||
| 631 | ret = -ENODEV; | ||
| 632 | dev_err(&i2c_client->dev, | ||
| 633 | "CS4265 Device ID (%X). Expected %X\n", | ||
| 634 | devid, CS4265_CHIP_ID); | ||
| 635 | return ret; | ||
| 636 | } | ||
| 637 | dev_info(&i2c_client->dev, | ||
| 638 | "CS4265 Version %x\n", | ||
| 639 | reg & CS4265_REV_ID_MASK); | ||
| 640 | |||
| 641 | regmap_write(cs4265->regmap, CS4265_PWRCTL, 0x0F); | ||
| 642 | |||
| 643 | ret = snd_soc_register_codec(&i2c_client->dev, | ||
| 644 | &soc_codec_cs4265, cs4265_dai, | ||
| 645 | ARRAY_SIZE(cs4265_dai)); | ||
| 646 | return ret; | ||
| 647 | } | ||
| 648 | |||
| 649 | static int cs4265_i2c_remove(struct i2c_client *client) | ||
| 650 | { | ||
| 651 | snd_soc_unregister_codec(&client->dev); | ||
| 652 | return 0; | ||
| 653 | } | ||
| 654 | |||
| 655 | static const struct of_device_id cs4265_of_match[] = { | ||
| 656 | { .compatible = "cirrus,cs4265", }, | ||
| 657 | { } | ||
| 658 | }; | ||
| 659 | MODULE_DEVICE_TABLE(of, cs4265_of_match); | ||
| 660 | |||
| 661 | static const struct i2c_device_id cs4265_id[] = { | ||
| 662 | { "cs4265", 0 }, | ||
| 663 | { } | ||
| 664 | }; | ||
| 665 | MODULE_DEVICE_TABLE(i2c, cs4265_id); | ||
| 666 | |||
| 667 | static struct i2c_driver cs4265_i2c_driver = { | ||
| 668 | .driver = { | ||
| 669 | .name = "cs4265", | ||
| 670 | .owner = THIS_MODULE, | ||
| 671 | .of_match_table = cs4265_of_match, | ||
| 672 | }, | ||
| 673 | .id_table = cs4265_id, | ||
| 674 | .probe = cs4265_i2c_probe, | ||
| 675 | .remove = cs4265_i2c_remove, | ||
| 676 | }; | ||
| 677 | |||
| 678 | module_i2c_driver(cs4265_i2c_driver); | ||
| 679 | |||
| 680 | MODULE_DESCRIPTION("ASoC CS4265 driver"); | ||
| 681 | MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <paul.handrigan@cirrus.com>"); | ||
| 682 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/cs4265.h b/sound/soc/codecs/cs4265.h new file mode 100644 index 000000000000..0a80a8dcec67 --- /dev/null +++ b/sound/soc/codecs/cs4265.h | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | /* | ||
| 2 | * cs4265.h -- CS4265 ALSA SoC audio driver | ||
| 3 | * | ||
| 4 | * Copyright 2014 Cirrus Logic, Inc. | ||
| 5 | * | ||
| 6 | * Author: Paul Handrigan <paul.handrigan@cirrus.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | */ | ||
| 13 | |||
| 14 | #ifndef __CS4265_H__ | ||
| 15 | #define __CS4265_H__ | ||
| 16 | |||
| 17 | #define CS4265_CHIP_ID 0x1 | ||
| 18 | #define CS4265_CHIP_ID_VAL 0xD0 | ||
| 19 | #define CS4265_CHIP_ID_MASK 0xF0 | ||
| 20 | #define CS4265_REV_ID_MASK 0x0F | ||
| 21 | |||
| 22 | #define CS4265_PWRCTL 0x02 | ||
| 23 | #define CS4265_PWRCTL_PDN 1 | ||
| 24 | |||
| 25 | #define CS4265_DAC_CTL 0x3 | ||
| 26 | #define CS4265_DAC_CTL_MUTE (1 << 2) | ||
| 27 | #define CS4265_DAC_CTL_DIF (3 << 4) | ||
| 28 | |||
| 29 | #define CS4265_ADC_CTL 0x4 | ||
| 30 | #define CS4265_ADC_MASTER 1 | ||
| 31 | #define CS4265_ADC_DIF (1 << 4) | ||
| 32 | #define CS4265_ADC_FM (3 << 6) | ||
| 33 | |||
| 34 | #define CS4265_MCLK_FREQ 0x5 | ||
| 35 | #define CS4265_MCLK_FREQ_MASK (7 << 4) | ||
| 36 | |||
| 37 | #define CS4265_SIG_SEL 0x6 | ||
| 38 | #define CS4265_SIG_SEL_LOOP (1 << 1) | ||
| 39 | |||
| 40 | #define CS4265_CHB_PGA_CTL 0x7 | ||
| 41 | #define CS4265_CHA_PGA_CTL 0x8 | ||
| 42 | |||
| 43 | #define CS4265_ADC_CTL2 0x9 | ||
| 44 | |||
| 45 | #define CS4265_DAC_CHA_VOL 0xA | ||
| 46 | #define CS4265_DAC_CHB_VOL 0xB | ||
| 47 | |||
| 48 | #define CS4265_DAC_CTL2 0xC | ||
| 49 | |||
| 50 | #define CS4265_INT_STATUS 0xD | ||
| 51 | #define CS4265_INT_MASK 0xE | ||
| 52 | #define CS4265_STATUS_MODE_MSB 0xF | ||
| 53 | #define CS4265_STATUS_MODE_LSB 0x10 | ||
| 54 | |||
| 55 | #define CS4265_SPDIF_CTL1 0x11 | ||
| 56 | |||
| 57 | #define CS4265_SPDIF_CTL2 0x12 | ||
| 58 | #define CS4265_SPDIF_CTL2_MUTE (1 << 4) | ||
| 59 | #define CS4265_SPDIF_CTL2_DIF (3 << 6) | ||
| 60 | |||
| 61 | #define CS4265_C_DATA_BUFF 0x13 | ||
| 62 | #define CS4265_MAX_REGISTER 0x2A | ||
| 63 | |||
| 64 | #endif | ||
