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

ASoC: Add support for the CS4265 CODEC

This patch adds support for the Cirrus Logic CS4265 Stereo I2C CODEC.

Signed-off-by: Paul Handrigan <paul.handrigan@cirrus.com>
Signed-off-by: Mark Brown <broonie@linaro.org>

authored by

Paul Handrigan and committed by
Mark Brown
fb6f8069 2b65df25

+754
+6
sound/soc/codecs/Kconfig
··· 47 47 select SND_SOC_CS42L52 if I2C && INPUT 48 48 select SND_SOC_CS42L56 if I2C && INPUT 49 49 select SND_SOC_CS42L73 if I2C 50 + select SND_SOC_CS4265 if I2C 50 51 select SND_SOC_CS4270 if I2C 51 52 select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI 52 53 select SND_SOC_CS42XX8_I2C if I2C ··· 338 337 config SND_SOC_CS42L73 339 338 tristate "Cirrus Logic CS42L73 CODEC" 340 339 depends on I2C 340 + 341 + config SND_SOC_CS4265 342 + tristate "Cirrus Logic CS4265 CODEC" 343 + depends on I2C 344 + select REGMAP_I2C 341 345 342 346 # Cirrus Logic CS4270 Codec 343 347 config SND_SOC_CS4270
+2
sound/soc/codecs/Makefile
··· 37 37 snd-soc-cs42l52-objs := cs42l52.o 38 38 snd-soc-cs42l56-objs := cs42l56.o 39 39 snd-soc-cs42l73-objs := cs42l73.o 40 + snd-soc-cs4265-objs := cs4265.o 40 41 snd-soc-cs4270-objs := cs4270.o 41 42 snd-soc-cs4271-objs := cs4271.o 42 43 snd-soc-cs42xx8-objs := cs42xx8.o ··· 203 202 obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o 204 203 obj-$(CONFIG_SND_SOC_CS42L56) += snd-soc-cs42l56.o 205 204 obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o 205 + obj-$(CONFIG_SND_SOC_CS4265) += snd-soc-cs4265.o 206 206 obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o 207 207 obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o 208 208 obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o
+682
sound/soc/codecs/cs4265.c
··· 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, &reg); 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");
+64
sound/soc/codecs/cs4265.h
··· 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