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

ASoC: cs35l32: Add support for CS35L32 Boosted Amplifier

This patch adds support for the Cirrus Logic CS35L32 Boosted Amplifier
I2S output provides monitor data to the SOC/CODEC/DSP for speaker protection/enhancement algorithms

Signed-off-by: Brian Austin <brian.austin@cirrus.com>
Signed-off-by: Mark Brown <broonie@linaro.org>

authored by

Brian Austin and committed by
Mark Brown
eef5bb24 7d1311b9

+773
+26
include/dt-bindings/sound/cs35l32.h
··· 1 + #ifndef __DT_CS35L32_H 2 + #define __DT_CS35L32_H 3 + 4 + #define CS35L32_BOOST_MGR_AUTO 0 5 + #define CS35L32_BOOST_MGR_AUTO_AUDIO 1 6 + #define CS35L32_BOOST_MGR_BYPASS 2 7 + #define CS35L32_BOOST_MGR_FIXED 3 8 + 9 + #define CS35L32_DATA_CFG_LR_VP 0 10 + #define CS35L32_DATA_CFG_LR_STAT 1 11 + #define CS35L32_DATA_CFG_LR 2 12 + #define CS35L32_DATA_CFG_LR_VPSTAT 3 13 + 14 + #define CS35L32_BATT_THRESH_3_1V 0 15 + #define CS35L32_BATT_THRESH_3_2V 1 16 + #define CS35L32_BATT_THRESH_3_3V 2 17 + #define CS35L32_BATT_THRESH_3_4V 3 18 + 19 + #define CS35L32_BATT_RECOV_3_1V 0 20 + #define CS35L32_BATT_RECOV_3_2V 1 21 + #define CS35L32_BATT_RECOV_3_3V 2 22 + #define CS35L32_BATT_RECOV_3_4V 3 23 + #define CS35L32_BATT_RECOV_3_5V 4 24 + #define CS35L32_BATT_RECOV_3_6V 5 25 + 26 + #endif /* __DT_CS35L32_H */
+5
sound/soc/codecs/Kconfig
··· 43 43 select SND_SOC_ALC5623 if I2C 44 44 select SND_SOC_ALC5632 if I2C 45 45 select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC 46 + select SND_SOC_CS35L32 if I2C 46 47 select SND_SOC_CS42L51_I2C if I2C 47 48 select SND_SOC_CS42L52 if I2C && INPUT 48 49 select SND_SOC_CS42L56 if I2C && INPUT ··· 323 322 324 323 config SND_SOC_CQ0093VC 325 324 tristate 325 + 326 + config SND_SOC_CS35L32 327 + tristate "Cirrus Logic CS35L32 CODEC" 328 + depends on I2C 326 329 327 330 config SND_SOC_CS42L51 328 331 tristate
+2
sound/soc/codecs/Makefile
··· 32 32 snd-soc-ak5386-objs := ak5386.o 33 33 snd-soc-arizona-objs := arizona.o 34 34 snd-soc-cq93vc-objs := cq93vc.o 35 + snd-soc-cs35l32-objs := cs35l32.o 35 36 snd-soc-cs42l51-objs := cs42l51.o 36 37 snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o 37 38 snd-soc-cs42l52-objs := cs42l52.o ··· 204 203 obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o 205 204 obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o 206 205 obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o 206 + obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o 207 207 obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o 208 208 obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o 209 209 obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o
+647
sound/soc/codecs/cs35l32.c
··· 1 + /* 2 + * cs35l32.c -- CS35L32 ALSA SoC audio driver 3 + * 4 + * Copyright 2014 CirrusLogic, Inc. 5 + * 6 + * Author: Brian Austin <brian.austin@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/version.h> 17 + #include <linux/kernel.h> 18 + #include <linux/init.h> 19 + #include <linux/delay.h> 20 + #include <linux/i2c.h> 21 + #include <linux/gpio.h> 22 + #include <linux/regmap.h> 23 + #include <linux/slab.h> 24 + #include <linux/platform_device.h> 25 + #include <linux/regulator/consumer.h> 26 + #include <linux/gpio/consumer.h> 27 + #include <linux/of_device.h> 28 + #include <linux/slab.h> 29 + #include <sound/core.h> 30 + #include <sound/pcm.h> 31 + #include <sound/pcm_params.h> 32 + #include <sound/soc.h> 33 + #include <sound/soc-dapm.h> 34 + #include <sound/initval.h> 35 + #include <sound/tlv.h> 36 + #include <dt-bindings/sound/cs35l32.h> 37 + 38 + #include "cs35l32.h" 39 + 40 + #define CS35L32_NUM_SUPPLIES 2 41 + static const char *const cs35l32_supply_names[CS35L32_NUM_SUPPLIES] = { 42 + "VA", 43 + "VP", 44 + }; 45 + 46 + struct cs35l32_private { 47 + struct regmap *regmap; 48 + struct snd_soc_codec *codec; 49 + struct regulator_bulk_data supplies[CS35L32_NUM_SUPPLIES]; 50 + struct cs35l32_platform_data pdata; 51 + struct gpio_desc *reset_gpio; 52 + }; 53 + 54 + static const struct reg_default cs35l32_reg_defaults[] = { 55 + 56 + { 0x06, 0x04 }, /* Power Ctl 1 */ 57 + { 0x07, 0xE8 }, /* Power Ctl 2 */ 58 + { 0x08, 0x40 }, /* Clock Ctl */ 59 + { 0x09, 0x20 }, /* Low Battery Threshold */ 60 + { 0x0A, 0x00 }, /* Voltage Monitor [RO] */ 61 + { 0x0B, 0x40 }, /* Conv Peak Curr Protection CTL */ 62 + { 0x0C, 0x07 }, /* IMON Scaling */ 63 + { 0x0D, 0x03 }, /* Audio/LED Pwr Manager */ 64 + { 0x0F, 0x20 }, /* Serial Port Control */ 65 + { 0x10, 0x14 }, /* Class D Amp CTL */ 66 + { 0x11, 0x00 }, /* Protection Release CTL */ 67 + { 0x12, 0xFF }, /* Interrupt Mask 1 */ 68 + { 0x13, 0xFF }, /* Interrupt Mask 2 */ 69 + { 0x14, 0xFF }, /* Interrupt Mask 3 */ 70 + { 0x19, 0x00 }, /* LED Flash Mode Current */ 71 + { 0x1A, 0x00 }, /* LED Movie Mode Current */ 72 + { 0x1B, 0x20 }, /* LED Flash Timer */ 73 + { 0x1C, 0x00 }, /* LED Flash Inhibit Current */ 74 + }; 75 + 76 + static bool cs35l32_readable_register(struct device *dev, unsigned int reg) 77 + { 78 + switch (reg) { 79 + case CS35L32_DEVID_AB: 80 + case CS35L32_DEVID_CD: 81 + case CS35L32_DEVID_E: 82 + case CS35L32_FAB_ID: 83 + case CS35L32_REV_ID: 84 + case CS35L32_PWRCTL1: 85 + case CS35L32_PWRCTL2: 86 + case CS35L32_CLK_CTL: 87 + case CS35L32_BATT_THRESHOLD: 88 + case CS35L32_VMON: 89 + case CS35L32_BST_CPCP_CTL: 90 + case CS35L32_IMON_SCALING: 91 + case CS35L32_AUDIO_LED_MNGR: 92 + case CS35L32_ADSP_CTL: 93 + case CS35L32_CLASSD_CTL: 94 + case CS35L32_PROTECT_CTL: 95 + case CS35L32_INT_MASK_1: 96 + case CS35L32_INT_MASK_2: 97 + case CS35L32_INT_MASK_3: 98 + case CS35L32_INT_STATUS_1: 99 + case CS35L32_INT_STATUS_2: 100 + case CS35L32_INT_STATUS_3: 101 + case CS35L32_LED_STATUS: 102 + case CS35L32_FLASH_MODE: 103 + case CS35L32_MOVIE_MODE: 104 + case CS35L32_FLASH_TIMER: 105 + case CS35L32_FLASH_INHIBIT: 106 + return true; 107 + default: 108 + return false; 109 + } 110 + } 111 + 112 + static bool cs35l32_volatile_register(struct device *dev, unsigned int reg) 113 + { 114 + switch (reg) { 115 + case CS35L32_DEVID_AB: 116 + case CS35L32_DEVID_CD: 117 + case CS35L32_DEVID_E: 118 + case CS35L32_FAB_ID: 119 + case CS35L32_REV_ID: 120 + case CS35L32_INT_STATUS_1: 121 + case CS35L32_INT_STATUS_2: 122 + case CS35L32_INT_STATUS_3: 123 + case CS35L32_LED_STATUS: 124 + return 1; 125 + default: 126 + return 0; 127 + } 128 + } 129 + 130 + static bool cs35l32_precious_register(struct device *dev, unsigned int reg) 131 + { 132 + switch (reg) { 133 + case CS35L32_INT_STATUS_1: 134 + case CS35L32_INT_STATUS_2: 135 + case CS35L32_INT_STATUS_3: 136 + case CS35L32_LED_STATUS: 137 + return 1; 138 + default: 139 + return 0; 140 + } 141 + } 142 + 143 + static DECLARE_TLV_DB_SCALE(classd_ctl_tlv, 900, 300, 0); 144 + 145 + static const struct snd_kcontrol_new imon_ctl = 146 + SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 6, 1, 1); 147 + 148 + static const struct snd_kcontrol_new vmon_ctl = 149 + SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 7, 1, 1); 150 + 151 + static const struct snd_kcontrol_new vpmon_ctl = 152 + SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 5, 1, 1); 153 + 154 + static const struct snd_kcontrol_new cs35l32_snd_controls[] = { 155 + SOC_SINGLE_TLV("Speaker Volume", CS35L32_CLASSD_CTL, 156 + 3, 0x04, 1, classd_ctl_tlv), 157 + SOC_SINGLE("Zero Cross Switch", CS35L32_CLASSD_CTL, 2, 1, 0), 158 + SOC_SINGLE("Gain Manager Switch", CS35L32_AUDIO_LED_MNGR, 3, 1, 0), 159 + }; 160 + 161 + static const struct snd_soc_dapm_widget cs35l32_dapm_widgets[] = { 162 + 163 + SND_SOC_DAPM_SUPPLY("BOOST", CS35L32_PWRCTL1, 2, 1, NULL, 0), 164 + SND_SOC_DAPM_OUT_DRV("Speaker", CS35L32_PWRCTL1, 7, 1, NULL, 0), 165 + 166 + SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L32_PWRCTL2, 3, 1), 167 + 168 + SND_SOC_DAPM_INPUT("VP"), 169 + SND_SOC_DAPM_INPUT("ISENSE"), 170 + SND_SOC_DAPM_INPUT("VSENSE"), 171 + 172 + SND_SOC_DAPM_SWITCH("VMON ADC", CS35L32_PWRCTL2, 7, 1, &vmon_ctl), 173 + SND_SOC_DAPM_SWITCH("IMON ADC", CS35L32_PWRCTL2, 6, 1, &imon_ctl), 174 + SND_SOC_DAPM_SWITCH("VPMON ADC", CS35L32_PWRCTL2, 5, 1, &vpmon_ctl), 175 + }; 176 + 177 + static const struct snd_soc_dapm_route cs35l32_audio_map[] = { 178 + 179 + {"Speaker", NULL, "BOOST"}, 180 + 181 + {"VMON ADC", NULL, "VSENSE"}, 182 + {"IMON ADC", NULL, "ISENSE"}, 183 + {"VPMON ADC", NULL, "VP"}, 184 + 185 + {"SDOUT", "Switch", "VMON ADC"}, 186 + {"SDOUT", "Switch", "IMON ADC"}, 187 + {"SDOUT", "Switch", "VPMON ADC"}, 188 + 189 + {"Capture", NULL, "SDOUT"}, 190 + }; 191 + 192 + static int cs35l32_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) 193 + { 194 + struct snd_soc_codec *codec = codec_dai->codec; 195 + 196 + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 197 + case SND_SOC_DAIFMT_CBM_CFM: 198 + snd_soc_update_bits(codec, CS35L32_ADSP_CTL, 199 + CS35L32_ADSP_MASTER_MASK, 200 + CS35L32_ADSP_MASTER_MASK); 201 + break; 202 + case SND_SOC_DAIFMT_CBS_CFS: 203 + snd_soc_update_bits(codec, CS35L32_ADSP_CTL, 204 + CS35L32_ADSP_MASTER_MASK, 0); 205 + break; 206 + default: 207 + return -EINVAL; 208 + } 209 + 210 + return 0; 211 + } 212 + 213 + static int cs35l32_set_tristate(struct snd_soc_dai *dai, int tristate) 214 + { 215 + struct snd_soc_codec *codec = dai->codec; 216 + 217 + return snd_soc_update_bits(codec, CS35L32_PWRCTL2, 218 + CS35L32_SDOUT_3ST, tristate << 3); 219 + } 220 + 221 + static const struct snd_soc_dai_ops cs35l32_ops = { 222 + .set_fmt = cs35l32_set_dai_fmt, 223 + .set_tristate = cs35l32_set_tristate, 224 + }; 225 + 226 + static struct snd_soc_dai_driver cs35l32_dai[] = { 227 + { 228 + .name = "cs35l32-monitor", 229 + .id = 0, 230 + .capture = { 231 + .stream_name = "Capture", 232 + .channels_min = 2, 233 + .channels_max = 2, 234 + .rates = CS35L32_RATES, 235 + .formats = CS35L32_FORMATS, 236 + }, 237 + .ops = &cs35l32_ops, 238 + .symmetric_rates = 1, 239 + } 240 + }; 241 + 242 + static int cs35l32_codec_set_sysclk(struct snd_soc_codec *codec, 243 + int clk_id, int source, unsigned int freq, int dir) 244 + { 245 + 246 + switch (freq) { 247 + case 6000000: 248 + snd_soc_update_bits(codec, CS35L32_CLK_CTL, 249 + CS35L32_MCLK_DIV2_MASK, 0); 250 + snd_soc_update_bits(codec, CS35L32_CLK_CTL, 251 + CS35L32_MCLK_RATIO_MASK, 252 + CS35L32_MCLK_RATIO); 253 + break; 254 + case 12000000: 255 + snd_soc_update_bits(codec, CS35L32_CLK_CTL, 256 + CS35L32_MCLK_DIV2_MASK, 257 + CS35L32_MCLK_DIV2_MASK); 258 + snd_soc_update_bits(codec, CS35L32_CLK_CTL, 259 + CS35L32_MCLK_RATIO_MASK, 260 + CS35L32_MCLK_RATIO); 261 + break; 262 + case 6144000: 263 + snd_soc_update_bits(codec, CS35L32_CLK_CTL, 264 + CS35L32_MCLK_DIV2_MASK, 0); 265 + snd_soc_update_bits(codec, CS35L32_CLK_CTL, 266 + CS35L32_MCLK_RATIO_MASK, 0); 267 + break; 268 + case 12288000: 269 + snd_soc_update_bits(codec, CS35L32_CLK_CTL, 270 + CS35L32_MCLK_DIV2_MASK, 271 + CS35L32_MCLK_DIV2_MASK); 272 + snd_soc_update_bits(codec, CS35L32_CLK_CTL, 273 + CS35L32_MCLK_RATIO_MASK, 0); 274 + break; 275 + default: 276 + return -EINVAL; 277 + } 278 + 279 + return 0; 280 + } 281 + 282 + static struct snd_soc_codec_driver soc_codec_dev_cs35l32 = { 283 + .set_sysclk = cs35l32_codec_set_sysclk, 284 + 285 + .dapm_widgets = cs35l32_dapm_widgets, 286 + .num_dapm_widgets = ARRAY_SIZE(cs35l32_dapm_widgets), 287 + .dapm_routes = cs35l32_audio_map, 288 + .num_dapm_routes = ARRAY_SIZE(cs35l32_audio_map), 289 + 290 + .controls = cs35l32_snd_controls, 291 + .num_controls = ARRAY_SIZE(cs35l32_snd_controls), 292 + }; 293 + 294 + /* Current and threshold powerup sequence Pg37 in datasheet */ 295 + static const struct reg_default cs35l32_monitor_patch[] = { 296 + 297 + { 0x00, 0x99 }, 298 + { 0x48, 0x17 }, 299 + { 0x49, 0x56 }, 300 + { 0x43, 0x01 }, 301 + { 0x3B, 0x62 }, 302 + { 0x3C, 0x80 }, 303 + { 0x00, 0x00 }, 304 + }; 305 + 306 + static struct regmap_config cs35l32_regmap = { 307 + .reg_bits = 8, 308 + .val_bits = 8, 309 + 310 + .max_register = CS35L32_MAX_REGISTER, 311 + .reg_defaults = cs35l32_reg_defaults, 312 + .num_reg_defaults = ARRAY_SIZE(cs35l32_reg_defaults), 313 + .volatile_reg = cs35l32_volatile_register, 314 + .readable_reg = cs35l32_readable_register, 315 + .precious_reg = cs35l32_precious_register, 316 + .cache_type = REGCACHE_RBTREE, 317 + }; 318 + 319 + static int cs35l32_handle_of_data(struct i2c_client *i2c_client, 320 + struct cs35l32_platform_data *pdata) 321 + { 322 + struct device_node *np = i2c_client->dev.of_node; 323 + unsigned int val; 324 + 325 + if (of_property_read_u32(np, "cirrus,sdout-share", &val) >= 0) 326 + pdata->sdout_share = val; 327 + 328 + of_property_read_u32(np, "cirrus,boost-manager", &val); 329 + switch (val) { 330 + case CS35L32_BOOST_MGR_AUTO: 331 + case CS35L32_BOOST_MGR_AUTO_AUDIO: 332 + case CS35L32_BOOST_MGR_BYPASS: 333 + case CS35L32_BOOST_MGR_FIXED: 334 + pdata->boost_mng = val; 335 + break; 336 + default: 337 + dev_err(&i2c_client->dev, 338 + "Wrong cirrus,boost-manager DT value %d\n", val); 339 + pdata->boost_mng = CS35L32_BOOST_MGR_BYPASS; 340 + } 341 + 342 + of_property_read_u32(np, "cirrus,sdout-datacfg", &val); 343 + switch (val) { 344 + case CS35L32_DATA_CFG_LR_VP: 345 + case CS35L32_DATA_CFG_LR_STAT: 346 + case CS35L32_DATA_CFG_LR: 347 + case CS35L32_DATA_CFG_LR_VPSTAT: 348 + pdata->sdout_datacfg = val; 349 + break; 350 + default: 351 + dev_err(&i2c_client->dev, 352 + "Wrong cirrus,sdout-datacfg DT value %d\n", val); 353 + pdata->sdout_datacfg = CS35L32_DATA_CFG_LR; 354 + } 355 + 356 + of_property_read_u32(np, "cirrus,battery-threshold", &val); 357 + switch (val) { 358 + case CS35L32_BATT_THRESH_3_1V: 359 + case CS35L32_BATT_THRESH_3_2V: 360 + case CS35L32_BATT_THRESH_3_3V: 361 + case CS35L32_BATT_THRESH_3_4V: 362 + pdata->batt_thresh = val; 363 + break; 364 + default: 365 + dev_err(&i2c_client->dev, 366 + "Wrong cirrus,battery-threshold DT value %d\n", val); 367 + pdata->batt_thresh = CS35L32_BATT_THRESH_3_3V; 368 + } 369 + 370 + of_property_read_u32(np, "cirrus,battery-recovery", &val); 371 + switch (val) { 372 + case CS35L32_BATT_RECOV_3_1V: 373 + case CS35L32_BATT_RECOV_3_2V: 374 + case CS35L32_BATT_RECOV_3_3V: 375 + case CS35L32_BATT_RECOV_3_4V: 376 + case CS35L32_BATT_RECOV_3_5V: 377 + case CS35L32_BATT_RECOV_3_6V: 378 + pdata->batt_recov = val; 379 + break; 380 + default: 381 + dev_err(&i2c_client->dev, 382 + "Wrong cirrus,battery-recovery DT value %d\n", val); 383 + pdata->batt_recov = CS35L32_BATT_RECOV_3_4V; 384 + } 385 + 386 + return 0; 387 + } 388 + 389 + static int cs35l32_i2c_probe(struct i2c_client *i2c_client, 390 + const struct i2c_device_id *id) 391 + { 392 + struct cs35l32_private *cs35l32; 393 + struct cs35l32_platform_data *pdata = 394 + dev_get_platdata(&i2c_client->dev); 395 + int ret, i; 396 + unsigned int devid = 0; 397 + unsigned int reg; 398 + 399 + 400 + cs35l32 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs35l32_private), 401 + GFP_KERNEL); 402 + if (!cs35l32) { 403 + dev_err(&i2c_client->dev, "could not allocate codec\n"); 404 + return -ENOMEM; 405 + } 406 + 407 + i2c_set_clientdata(i2c_client, cs35l32); 408 + 409 + cs35l32->regmap = devm_regmap_init_i2c(i2c_client, &cs35l32_regmap); 410 + if (IS_ERR(cs35l32->regmap)) { 411 + ret = PTR_ERR(cs35l32->regmap); 412 + dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); 413 + return ret; 414 + } 415 + 416 + if (pdata) { 417 + cs35l32->pdata = *pdata; 418 + } else { 419 + pdata = devm_kzalloc(&i2c_client->dev, 420 + sizeof(struct cs35l32_platform_data), 421 + GFP_KERNEL); 422 + if (!pdata) { 423 + dev_err(&i2c_client->dev, "could not allocate pdata\n"); 424 + return -ENOMEM; 425 + } 426 + if (i2c_client->dev.of_node) { 427 + ret = cs35l32_handle_of_data(i2c_client, 428 + &cs35l32->pdata); 429 + if (ret != 0) 430 + return ret; 431 + } 432 + } 433 + 434 + for (i = 0; i < ARRAY_SIZE(cs35l32->supplies); i++) 435 + cs35l32->supplies[i].supply = cs35l32_supply_names[i]; 436 + 437 + ret = devm_regulator_bulk_get(&i2c_client->dev, 438 + ARRAY_SIZE(cs35l32->supplies), 439 + cs35l32->supplies); 440 + if (ret != 0) { 441 + dev_err(&i2c_client->dev, 442 + "Failed to request supplies: %d\n", ret); 443 + return ret; 444 + } 445 + 446 + ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies), 447 + cs35l32->supplies); 448 + if (ret != 0) { 449 + dev_err(&i2c_client->dev, 450 + "Failed to enable supplies: %d\n", ret); 451 + return ret; 452 + } 453 + 454 + /* Reset the Device */ 455 + cs35l32->reset_gpio = devm_gpiod_get(&i2c_client->dev, 456 + "reset-gpios"); 457 + if (IS_ERR(cs35l32->reset_gpio)) { 458 + ret = PTR_ERR(cs35l32->reset_gpio); 459 + if (ret != -ENOENT && ret != -ENOSYS) 460 + return ret; 461 + 462 + cs35l32->reset_gpio = NULL; 463 + } else { 464 + ret = gpiod_direction_output(cs35l32->reset_gpio, 0); 465 + if (ret) 466 + return ret; 467 + gpiod_set_value_cansleep(cs35l32->reset_gpio, 1); 468 + } 469 + 470 + /* initialize codec */ 471 + ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_AB, &reg); 472 + devid = (reg & 0xFF) << 12; 473 + 474 + ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_CD, &reg); 475 + devid |= (reg & 0xFF) << 4; 476 + 477 + ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_E, &reg); 478 + devid |= (reg & 0xF0) >> 4; 479 + 480 + if (devid != CS35L32_CHIP_ID) { 481 + ret = -ENODEV; 482 + dev_err(&i2c_client->dev, 483 + "CS35L32 Device ID (%X). Expected %X\n", 484 + devid, CS35L32_CHIP_ID); 485 + return ret; 486 + } 487 + 488 + ret = regmap_read(cs35l32->regmap, CS35L32_REV_ID, &reg); 489 + if (ret < 0) { 490 + dev_err(&i2c_client->dev, "Get Revision ID failed\n"); 491 + return ret; 492 + } 493 + 494 + ret = regmap_register_patch(cs35l32->regmap, cs35l32_monitor_patch, 495 + ARRAY_SIZE(cs35l32_monitor_patch)); 496 + if (ret < 0) { 497 + dev_err(&i2c_client->dev, "Failed to apply errata patch\n"); 498 + return ret; 499 + } 500 + 501 + dev_info(&i2c_client->dev, 502 + "Cirrus Logic CS35L32, Revision: %02X\n", reg & 0xFF); 503 + 504 + /* Setup VBOOST Management */ 505 + if (cs35l32->pdata.boost_mng) 506 + regmap_update_bits(cs35l32->regmap, CS35L32_AUDIO_LED_MNGR, 507 + CS35L32_BOOST_MASK, 508 + cs35l32->pdata.boost_mng); 509 + 510 + /* Setup ADSP Format Config */ 511 + if (cs35l32->pdata.sdout_share) 512 + regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL, 513 + CS35L32_ADSP_SHARE_MASK, 514 + cs35l32->pdata.sdout_share << 3); 515 + 516 + /* Setup ADSP Data Configuration */ 517 + if (cs35l32->pdata.sdout_datacfg) 518 + regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL, 519 + CS35L32_ADSP_DATACFG_MASK, 520 + cs35l32->pdata.sdout_datacfg << 4); 521 + 522 + /* Setup Low Battery Recovery */ 523 + if (cs35l32->pdata.batt_recov) 524 + regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD, 525 + CS35L32_BATT_REC_MASK, 526 + cs35l32->pdata.batt_recov << 1); 527 + 528 + /* Setup Low Battery Threshold */ 529 + if (cs35l32->pdata.batt_thresh) 530 + regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD, 531 + CS35L32_BATT_THRESH_MASK, 532 + cs35l32->pdata.batt_thresh << 4); 533 + 534 + /* Power down the AMP */ 535 + regmap_update_bits(cs35l32->regmap, CS35L32_PWRCTL1, CS35L32_PDN_AMP, 536 + CS35L32_PDN_AMP); 537 + 538 + /* Clear MCLK Error Bit since we don't have the clock yet */ 539 + ret = regmap_read(cs35l32->regmap, CS35L32_INT_STATUS_1, &reg); 540 + 541 + ret = snd_soc_register_codec(&i2c_client->dev, 542 + &soc_codec_dev_cs35l32, cs35l32_dai, 543 + ARRAY_SIZE(cs35l32_dai)); 544 + if (ret < 0) 545 + goto err_disable; 546 + 547 + return 0; 548 + 549 + err_disable: 550 + regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies), 551 + cs35l32->supplies); 552 + } 553 + 554 + static int cs35l32_i2c_remove(struct i2c_client *i2c_client) 555 + { 556 + struct cs35l32_private *cs35l32 = i2c_get_clientdata(i2c_client); 557 + 558 + snd_soc_unregister_codec(&i2c_client->dev); 559 + 560 + /* Hold down reset */ 561 + if (cs35l32->reset_gpio) 562 + gpiod_set_value_cansleep(cs35l32->reset_gpio, 0); 563 + 564 + regulator_bulk_free(ARRAY_SIZE(cs35l32->supplies), cs35l32->supplies); 565 + 566 + return 0; 567 + } 568 + 569 + #ifdef CONFIG_PM_RUNTIME 570 + static int cs35l32_runtime_suspend(struct device *dev) 571 + { 572 + struct cs35l32_private *cs35l32 = dev_get_drvdata(dev); 573 + 574 + regcache_cache_only(cs35l32->regmap, true); 575 + regcache_mark_dirty(cs35l32->regmap); 576 + 577 + /* Hold down reset */ 578 + if (cs35l32->reset_gpio) 579 + gpiod_set_value_cansleep(cs35l32->reset_gpio, 0); 580 + 581 + /* remove power */ 582 + regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies), 583 + cs35l32->supplies); 584 + 585 + return 0; 586 + } 587 + 588 + static int cs35l32_runtime_resume(struct device *dev) 589 + { 590 + struct cs35l32_private *cs35l32 = dev_get_drvdata(dev); 591 + int ret; 592 + 593 + /* Enable power */ 594 + ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies), 595 + cs35l32->supplies); 596 + if (ret != 0) { 597 + dev_err(dev, "Failed to enable supplies: %d\n", 598 + ret); 599 + return ret; 600 + } 601 + 602 + if (cs35l32->reset_gpio) 603 + gpiod_set_value_cansleep(cs35l32->reset_gpio, 1); 604 + 605 + regcache_cache_only(cs35l32->regmap, false); 606 + regcache_sync(cs35l32->regmap); 607 + 608 + return 0; 609 + } 610 + #endif 611 + 612 + static const struct dev_pm_ops cs35l32_runtime_pm = { 613 + SET_RUNTIME_PM_OPS(cs35l32_runtime_suspend, cs35l32_runtime_resume, 614 + NULL) 615 + }; 616 + 617 + static const struct of_device_id cs35l32_of_match[] = { 618 + { .compatible = "cirrus,cs35l32", }, 619 + {}, 620 + }; 621 + MODULE_DEVICE_TABLE(of, cs35l32_of_match); 622 + 623 + 624 + static const struct i2c_device_id cs35l32_id[] = { 625 + {"cs35l32", 0}, 626 + {} 627 + }; 628 + 629 + MODULE_DEVICE_TABLE(i2c, cs35l32_id); 630 + 631 + static struct i2c_driver cs35l32_i2c_driver = { 632 + .driver = { 633 + .name = "cs35l32", 634 + .owner = THIS_MODULE, 635 + .pm = &cs35l32_runtime_pm, 636 + .of_match_table = cs35l32_of_match, 637 + }, 638 + .id_table = cs35l32_id, 639 + .probe = cs35l32_i2c_probe, 640 + .remove = cs35l32_i2c_remove, 641 + }; 642 + 643 + module_i2c_driver(cs35l32_i2c_driver); 644 + 645 + MODULE_DESCRIPTION("ASoC CS35L32 driver"); 646 + MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>"); 647 + MODULE_LICENSE("GPL");
+93
sound/soc/codecs/cs35l32.h
··· 1 + /* 2 + * cs35l32.h -- CS35L32 ALSA SoC audio driver 3 + * 4 + * Copyright 2014 CirrusLogic, Inc. 5 + * 6 + * Author: Brian Austin <brian.austin@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 __CS35L32_H__ 15 + #define __CS35L32_H__ 16 + 17 + struct cs35l32_platform_data { 18 + /* Low Battery Threshold */ 19 + unsigned int batt_thresh; 20 + /* Low Battery Recovery */ 21 + unsigned int batt_recov; 22 + /* LED Current Management*/ 23 + unsigned int led_mng; 24 + /* Audio Gain w/ LED */ 25 + unsigned int audiogain_mng; 26 + /* Boost Management */ 27 + unsigned int boost_mng; 28 + /* Data CFG for DUAL device */ 29 + unsigned int sdout_datacfg; 30 + /* SDOUT Sharing */ 31 + unsigned int sdout_share; 32 + }; 33 + 34 + #define CS35L32_CHIP_ID 0x00035A32 35 + #define CS35L32_DEVID_AB 0x01 /* Device ID A & B [RO] */ 36 + #define CS35L32_DEVID_CD 0x02 /* Device ID C & D [RO] */ 37 + #define CS35L32_DEVID_E 0x03 /* Device ID E [RO] */ 38 + #define CS35L32_FAB_ID 0x04 /* Fab ID [RO] */ 39 + #define CS35L32_REV_ID 0x05 /* Revision ID [RO] */ 40 + #define CS35L32_PWRCTL1 0x06 /* Power Ctl 1 */ 41 + #define CS35L32_PWRCTL2 0x07 /* Power Ctl 2 */ 42 + #define CS35L32_CLK_CTL 0x08 /* Clock Ctl */ 43 + #define CS35L32_BATT_THRESHOLD 0x09 /* Low Battery Threshold */ 44 + #define CS35L32_VMON 0x0A /* Voltage Monitor [RO] */ 45 + #define CS35L32_BST_CPCP_CTL 0x0B /* Conv Peak Curr Protection CTL */ 46 + #define CS35L32_IMON_SCALING 0x0C /* IMON Scaling */ 47 + #define CS35L32_AUDIO_LED_MNGR 0x0D /* Audio/LED Pwr Manager */ 48 + #define CS35L32_ADSP_CTL 0x0F /* Serial Port Control */ 49 + #define CS35L32_CLASSD_CTL 0x10 /* Class D Amp CTL */ 50 + #define CS35L32_PROTECT_CTL 0x11 /* Protection Release CTL */ 51 + #define CS35L32_INT_MASK_1 0x12 /* Interrupt Mask 1 */ 52 + #define CS35L32_INT_MASK_2 0x13 /* Interrupt Mask 2 */ 53 + #define CS35L32_INT_MASK_3 0x14 /* Interrupt Mask 3 */ 54 + #define CS35L32_INT_STATUS_1 0x15 /* Interrupt Status 1 [RO] */ 55 + #define CS35L32_INT_STATUS_2 0x16 /* Interrupt Status 2 [RO] */ 56 + #define CS35L32_INT_STATUS_3 0x17 /* Interrupt Status 3 [RO] */ 57 + #define CS35L32_LED_STATUS 0x18 /* LED Lighting Status [RO] */ 58 + #define CS35L32_FLASH_MODE 0x19 /* LED Flash Mode Current */ 59 + #define CS35L32_MOVIE_MODE 0x1A /* LED Movie Mode Current */ 60 + #define CS35L32_FLASH_TIMER 0x1B /* LED Flash Timer */ 61 + #define CS35L32_FLASH_INHIBIT 0x1C /* LED Flash Inhibit Current */ 62 + #define CS35L32_MAX_REGISTER 0x1C 63 + 64 + #define CS35L32_MCLK_DIV2 0x01 65 + #define CS35L32_MCLK_RATIO 0x01 66 + #define CS35L32_MCLKDIS 0x80 67 + #define CS35L32_PDN_ALL 0x01 68 + #define CS35L32_PDN_AMP 0x80 69 + #define CS35L32_PDN_BOOST 0x04 70 + #define CS35L32_PDN_IMON 0x40 71 + #define CS35L32_PDN_VMON 0x80 72 + #define CS35L32_PDN_VPMON 0x20 73 + #define CS35L32_PDN_ADSP 0x08 74 + 75 + #define CS35L32_MCLK_DIV2_MASK 0x40 76 + #define CS35L32_MCLK_RATIO_MASK 0x01 77 + #define CS35L32_MCLK_MASK 0x41 78 + #define CS35L32_ADSP_MASTER_MASK 0x40 79 + #define CS35L32_BOOST_MASK 0x03 80 + #define CS35L32_GAIN_MGR_MASK 0x08 81 + #define CS35L32_ADSP_SHARE_MASK 0x08 82 + #define CS35L32_ADSP_DATACFG_MASK 0x30 83 + #define CS35L32_SDOUT_3ST 0x80 84 + #define CS35L32_BATT_REC_MASK 0x0E 85 + #define CS35L32_BATT_THRESH_MASK 0x30 86 + 87 + #define CS35L32_RATES (SNDRV_PCM_RATE_48000) 88 + #define CS35L32_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ 89 + SNDRV_PCM_FMTBIT_S24_LE | \ 90 + SNDRV_PCM_FMTBIT_S32_LE) 91 + 92 + 93 + #endif