cs35l45.c (20528B)
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2// 3// cs35l45.c - CS35L45 ALSA SoC audio driver 4// 5// Copyright 2019-2022 Cirrus Logic, Inc. 6// 7// Author: James Schulman <james.schulman@cirrus.com> 8 9#include <linux/gpio/consumer.h> 10#include <linux/module.h> 11#include <linux/pm_runtime.h> 12#include <linux/property.h> 13#include <linux/regulator/consumer.h> 14#include <sound/core.h> 15#include <sound/pcm.h> 16#include <sound/pcm_params.h> 17#include <sound/soc.h> 18#include <sound/tlv.h> 19 20#include "cs35l45.h" 21 22static int cs35l45_global_en_ev(struct snd_soc_dapm_widget *w, 23 struct snd_kcontrol *kcontrol, int event) 24{ 25 struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 26 struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component); 27 28 dev_dbg(cs35l45->dev, "%s event : %x\n", __func__, event); 29 30 switch (event) { 31 case SND_SOC_DAPM_POST_PMU: 32 regmap_write(cs35l45->regmap, CS35L45_GLOBAL_ENABLES, 33 CS35L45_GLOBAL_EN_MASK); 34 35 usleep_range(CS35L45_POST_GLOBAL_EN_US, CS35L45_POST_GLOBAL_EN_US + 100); 36 break; 37 case SND_SOC_DAPM_PRE_PMD: 38 usleep_range(CS35L45_PRE_GLOBAL_DIS_US, CS35L45_PRE_GLOBAL_DIS_US + 100); 39 40 regmap_write(cs35l45->regmap, CS35L45_GLOBAL_ENABLES, 0); 41 break; 42 default: 43 break; 44 } 45 46 return 0; 47} 48 49static const char * const cs35l45_asp_tx_txt[] = { 50 "Zero", "ASP_RX1", "ASP_RX2", 51 "VMON", "IMON", "ERR_VOL", 52 "VDD_BATTMON", "VDD_BSTMON", 53 "Interpolator", "IL_TARGET", 54}; 55 56static const unsigned int cs35l45_asp_tx_val[] = { 57 CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2, 58 CS35L45_PCM_SRC_VMON, CS35L45_PCM_SRC_IMON, CS35L45_PCM_SRC_ERR_VOL, 59 CS35L45_PCM_SRC_VDD_BATTMON, CS35L45_PCM_SRC_VDD_BSTMON, 60 CS35L45_PCM_SRC_INTERPOLATOR, CS35L45_PCM_SRC_IL_TARGET, 61}; 62 63static const struct soc_enum cs35l45_asp_tx_enums[] = { 64 SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX1_INPUT, 0, CS35L45_PCM_SRC_MASK, 65 ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt, 66 cs35l45_asp_tx_val), 67 SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX2_INPUT, 0, CS35L45_PCM_SRC_MASK, 68 ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt, 69 cs35l45_asp_tx_val), 70 SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX3_INPUT, 0, CS35L45_PCM_SRC_MASK, 71 ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt, 72 cs35l45_asp_tx_val), 73 SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX4_INPUT, 0, CS35L45_PCM_SRC_MASK, 74 ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt, 75 cs35l45_asp_tx_val), 76 SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX5_INPUT, 0, CS35L45_PCM_SRC_MASK, 77 ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt, 78 cs35l45_asp_tx_val), 79}; 80 81static const char * const cs35l45_dac_txt[] = { 82 "Zero", "ASP_RX1", "ASP_RX2" 83}; 84 85static const unsigned int cs35l45_dac_val[] = { 86 CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2 87}; 88 89static const struct soc_enum cs35l45_dacpcm_enums[] = { 90 SOC_VALUE_ENUM_SINGLE(CS35L45_DACPCM1_INPUT, 0, CS35L45_PCM_SRC_MASK, 91 ARRAY_SIZE(cs35l45_dac_txt), cs35l45_dac_txt, 92 cs35l45_dac_val), 93}; 94 95static const struct snd_kcontrol_new cs35l45_asp_muxes[] = { 96 SOC_DAPM_ENUM("ASP_TX1 Source", cs35l45_asp_tx_enums[0]), 97 SOC_DAPM_ENUM("ASP_TX2 Source", cs35l45_asp_tx_enums[1]), 98 SOC_DAPM_ENUM("ASP_TX3 Source", cs35l45_asp_tx_enums[2]), 99 SOC_DAPM_ENUM("ASP_TX4 Source", cs35l45_asp_tx_enums[3]), 100 SOC_DAPM_ENUM("ASP_TX5 Source", cs35l45_asp_tx_enums[4]), 101}; 102 103static const struct snd_kcontrol_new cs35l45_dac_muxes[] = { 104 SOC_DAPM_ENUM("DACPCM1 Source", cs35l45_dacpcm_enums[0]), 105}; 106 107static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = { 108 SND_SOC_DAPM_SUPPLY("GLOBAL_EN", SND_SOC_NOPM, 0, 0, 109 cs35l45_global_en_ev, 110 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), 111 SND_SOC_DAPM_SUPPLY("ASP_EN", CS35L45_BLOCK_ENABLES2, CS35L45_ASP_EN_SHIFT, 0, NULL, 0), 112 113 SND_SOC_DAPM_SIGGEN("VMON_SRC"), 114 SND_SOC_DAPM_SIGGEN("IMON_SRC"), 115 SND_SOC_DAPM_SIGGEN("VDD_BATTMON_SRC"), 116 SND_SOC_DAPM_SIGGEN("VDD_BSTMON_SRC"), 117 SND_SOC_DAPM_SIGGEN("ERR_VOL"), 118 SND_SOC_DAPM_SIGGEN("AMP_INTP"), 119 SND_SOC_DAPM_SIGGEN("IL_TARGET"), 120 SND_SOC_DAPM_ADC("VMON", NULL, CS35L45_BLOCK_ENABLES, CS35L45_VMON_EN_SHIFT, 0), 121 SND_SOC_DAPM_ADC("IMON", NULL, CS35L45_BLOCK_ENABLES, CS35L45_IMON_EN_SHIFT, 0), 122 SND_SOC_DAPM_ADC("VDD_BATTMON", NULL, CS35L45_BLOCK_ENABLES, 123 CS35L45_VDD_BATTMON_EN_SHIFT, 0), 124 SND_SOC_DAPM_ADC("VDD_BSTMON", NULL, CS35L45_BLOCK_ENABLES, 125 CS35L45_VDD_BSTMON_EN_SHIFT, 0), 126 127 SND_SOC_DAPM_AIF_IN("ASP_RX1", NULL, 0, CS35L45_ASP_ENABLES1, CS35L45_ASP_RX1_EN_SHIFT, 0), 128 SND_SOC_DAPM_AIF_IN("ASP_RX2", NULL, 1, CS35L45_ASP_ENABLES1, CS35L45_ASP_RX2_EN_SHIFT, 0), 129 130 SND_SOC_DAPM_AIF_OUT("ASP_TX1", NULL, 0, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX1_EN_SHIFT, 0), 131 SND_SOC_DAPM_AIF_OUT("ASP_TX2", NULL, 1, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX2_EN_SHIFT, 0), 132 SND_SOC_DAPM_AIF_OUT("ASP_TX3", NULL, 2, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX3_EN_SHIFT, 0), 133 SND_SOC_DAPM_AIF_OUT("ASP_TX4", NULL, 3, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX4_EN_SHIFT, 0), 134 SND_SOC_DAPM_AIF_OUT("ASP_TX5", NULL, 3, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX5_EN_SHIFT, 0), 135 136 SND_SOC_DAPM_MUX("ASP_TX1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[0]), 137 SND_SOC_DAPM_MUX("ASP_TX2 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[1]), 138 SND_SOC_DAPM_MUX("ASP_TX3 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[2]), 139 SND_SOC_DAPM_MUX("ASP_TX4 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[3]), 140 SND_SOC_DAPM_MUX("ASP_TX5 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[4]), 141 142 SND_SOC_DAPM_MUX("DACPCM1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dac_muxes[0]), 143 144 SND_SOC_DAPM_OUT_DRV("AMP", SND_SOC_NOPM, 0, 0, NULL, 0), 145 146 SND_SOC_DAPM_OUTPUT("SPK"), 147}; 148 149#define CS35L45_ASP_MUX_ROUTE(name) \ 150 { name" Source", "ASP_RX1", "ASP_RX1" }, \ 151 { name" Source", "ASP_RX2", "ASP_RX2" }, \ 152 { name" Source", "VMON", "VMON" }, \ 153 { name" Source", "IMON", "IMON" }, \ 154 { name" Source", "ERR_VOL", "ERR_VOL" }, \ 155 { name" Source", "VDD_BATTMON", "VDD_BATTMON" }, \ 156 { name" Source", "VDD_BSTMON", "VDD_BSTMON" }, \ 157 { name" Source", "Interpolator", "AMP_INTP" }, \ 158 { name" Source", "IL_TARGET", "IL_TARGET" } 159 160#define CS35L45_DAC_MUX_ROUTE(name) \ 161 { name" Source", "ASP_RX1", "ASP_RX1" }, \ 162 { name" Source", "ASP_RX2", "ASP_RX2" } 163 164static const struct snd_soc_dapm_route cs35l45_dapm_routes[] = { 165 /* Feedback */ 166 { "VMON", NULL, "VMON_SRC" }, 167 { "IMON", NULL, "IMON_SRC" }, 168 { "VDD_BATTMON", NULL, "VDD_BATTMON_SRC" }, 169 { "VDD_BSTMON", NULL, "VDD_BSTMON_SRC" }, 170 171 { "Capture", NULL, "ASP_TX1"}, 172 { "Capture", NULL, "ASP_TX2"}, 173 { "Capture", NULL, "ASP_TX3"}, 174 { "Capture", NULL, "ASP_TX4"}, 175 { "Capture", NULL, "ASP_TX5"}, 176 { "ASP_TX1", NULL, "ASP_TX1 Source"}, 177 { "ASP_TX2", NULL, "ASP_TX2 Source"}, 178 { "ASP_TX3", NULL, "ASP_TX3 Source"}, 179 { "ASP_TX4", NULL, "ASP_TX4 Source"}, 180 { "ASP_TX5", NULL, "ASP_TX5 Source"}, 181 182 { "ASP_TX1", NULL, "ASP_EN" }, 183 { "ASP_TX2", NULL, "ASP_EN" }, 184 { "ASP_TX3", NULL, "ASP_EN" }, 185 { "ASP_TX4", NULL, "ASP_EN" }, 186 { "ASP_TX1", NULL, "GLOBAL_EN" }, 187 { "ASP_TX2", NULL, "GLOBAL_EN" }, 188 { "ASP_TX3", NULL, "GLOBAL_EN" }, 189 { "ASP_TX4", NULL, "GLOBAL_EN" }, 190 { "ASP_TX5", NULL, "GLOBAL_EN" }, 191 192 CS35L45_ASP_MUX_ROUTE("ASP_TX1"), 193 CS35L45_ASP_MUX_ROUTE("ASP_TX2"), 194 CS35L45_ASP_MUX_ROUTE("ASP_TX3"), 195 CS35L45_ASP_MUX_ROUTE("ASP_TX4"), 196 CS35L45_ASP_MUX_ROUTE("ASP_TX5"), 197 198 /* Playback */ 199 { "ASP_RX1", NULL, "Playback" }, 200 { "ASP_RX2", NULL, "Playback" }, 201 { "ASP_RX1", NULL, "ASP_EN" }, 202 { "ASP_RX2", NULL, "ASP_EN" }, 203 204 { "AMP", NULL, "DACPCM1 Source"}, 205 { "AMP", NULL, "GLOBAL_EN"}, 206 207 CS35L45_DAC_MUX_ROUTE("DACPCM1"), 208 209 { "SPK", NULL, "AMP"}, 210}; 211 212static const DECLARE_TLV_DB_SCALE(cs35l45_dig_pcm_vol_tlv, -10225, 25, true); 213 214static const struct snd_kcontrol_new cs35l45_controls[] = { 215 /* Ignore bit 0: it is beyond the resolution of TLV_DB_SCALE */ 216 SOC_SINGLE_S_TLV("Digital PCM Volume", 217 CS35L45_AMP_PCM_CONTROL, 218 CS35L45_AMP_VOL_PCM_SHIFT + 1, 219 -409, 48, 220 (CS35L45_AMP_VOL_PCM_WIDTH - 1) - 1, 221 0, cs35l45_dig_pcm_vol_tlv), 222}; 223 224static int cs35l45_set_pll(struct cs35l45_private *cs35l45, unsigned int freq) 225{ 226 unsigned int val; 227 int freq_id; 228 229 freq_id = cs35l45_get_clk_freq_id(freq); 230 if (freq_id < 0) { 231 dev_err(cs35l45->dev, "Invalid freq: %u\n", freq); 232 return -EINVAL; 233 } 234 235 regmap_read(cs35l45->regmap, CS35L45_REFCLK_INPUT, &val); 236 val = (val & CS35L45_PLL_REFCLK_FREQ_MASK) >> CS35L45_PLL_REFCLK_FREQ_SHIFT; 237 if (val == freq_id) 238 return 0; 239 240 regmap_set_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_OPEN_LOOP_MASK); 241 regmap_update_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, 242 CS35L45_PLL_REFCLK_FREQ_MASK, 243 freq_id << CS35L45_PLL_REFCLK_FREQ_SHIFT); 244 regmap_clear_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_REFCLK_EN_MASK); 245 regmap_clear_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_OPEN_LOOP_MASK); 246 regmap_set_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_REFCLK_EN_MASK); 247 248 return 0; 249} 250 251static int cs35l45_asp_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) 252{ 253 struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(codec_dai->component); 254 unsigned int asp_fmt, fsync_inv, bclk_inv; 255 256 switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 257 case SND_SOC_DAIFMT_CBC_CFC: 258 break; 259 default: 260 dev_err(cs35l45->dev, "Invalid DAI clocking\n"); 261 return -EINVAL; 262 } 263 264 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 265 case SND_SOC_DAIFMT_DSP_A: 266 asp_fmt = CS35l45_ASP_FMT_DSP_A; 267 break; 268 case SND_SOC_DAIFMT_I2S: 269 asp_fmt = CS35L45_ASP_FMT_I2S; 270 break; 271 default: 272 dev_err(cs35l45->dev, "Invalid DAI format\n"); 273 return -EINVAL; 274 } 275 276 switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 277 case SND_SOC_DAIFMT_NB_IF: 278 fsync_inv = 1; 279 bclk_inv = 0; 280 break; 281 case SND_SOC_DAIFMT_IB_NF: 282 fsync_inv = 0; 283 bclk_inv = 1; 284 break; 285 case SND_SOC_DAIFMT_IB_IF: 286 fsync_inv = 1; 287 bclk_inv = 1; 288 break; 289 case SND_SOC_DAIFMT_NB_NF: 290 fsync_inv = 0; 291 bclk_inv = 0; 292 break; 293 default: 294 dev_warn(cs35l45->dev, "Invalid DAI clock polarity\n"); 295 return -EINVAL; 296 } 297 298 regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2, 299 CS35L45_ASP_FMT_MASK | 300 CS35L45_ASP_FSYNC_INV_MASK | 301 CS35L45_ASP_BCLK_INV_MASK, 302 (asp_fmt << CS35L45_ASP_FMT_SHIFT) | 303 (fsync_inv << CS35L45_ASP_FSYNC_INV_SHIFT) | 304 (bclk_inv << CS35L45_ASP_BCLK_INV_SHIFT)); 305 306 return 0; 307} 308 309static int cs35l45_asp_hw_params(struct snd_pcm_substream *substream, 310 struct snd_pcm_hw_params *params, 311 struct snd_soc_dai *dai) 312{ 313 struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component); 314 unsigned int asp_width, asp_wl, global_fs, slot_multiple, asp_fmt; 315 int bclk; 316 317 switch (params_rate(params)) { 318 case 44100: 319 global_fs = CS35L45_44P100_KHZ; 320 break; 321 case 48000: 322 global_fs = CS35L45_48P0_KHZ; 323 break; 324 case 88200: 325 global_fs = CS35L45_88P200_KHZ; 326 break; 327 case 96000: 328 global_fs = CS35L45_96P0_KHZ; 329 break; 330 default: 331 dev_warn(cs35l45->dev, "Unsupported sample rate (%d)\n", 332 params_rate(params)); 333 return -EINVAL; 334 } 335 336 regmap_update_bits(cs35l45->regmap, CS35L45_GLOBAL_SAMPLE_RATE, 337 CS35L45_GLOBAL_FS_MASK, 338 global_fs << CS35L45_GLOBAL_FS_SHIFT); 339 340 asp_wl = params_width(params); 341 342 if (cs35l45->slot_width) 343 asp_width = cs35l45->slot_width; 344 else 345 asp_width = params_width(params); 346 347 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 348 regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2, 349 CS35L45_ASP_WIDTH_RX_MASK, 350 asp_width << CS35L45_ASP_WIDTH_RX_SHIFT); 351 352 regmap_update_bits(cs35l45->regmap, CS35L45_ASP_DATA_CONTROL5, 353 CS35L45_ASP_WL_MASK, 354 asp_wl << CS35L45_ASP_WL_SHIFT); 355 } else { 356 regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2, 357 CS35L45_ASP_WIDTH_TX_MASK, 358 asp_width << CS35L45_ASP_WIDTH_TX_SHIFT); 359 360 regmap_update_bits(cs35l45->regmap, CS35L45_ASP_DATA_CONTROL1, 361 CS35L45_ASP_WL_MASK, 362 asp_wl << CS35L45_ASP_WL_SHIFT); 363 } 364 365 if (cs35l45->sysclk_set) 366 return 0; 367 368 /* I2S always has an even number of channels */ 369 regmap_read(cs35l45->regmap, CS35L45_ASP_CONTROL2, &asp_fmt); 370 asp_fmt = (asp_fmt & CS35L45_ASP_FMT_MASK) >> CS35L45_ASP_FMT_SHIFT; 371 if (asp_fmt == CS35L45_ASP_FMT_I2S) 372 slot_multiple = 2; 373 else 374 slot_multiple = 1; 375 376 bclk = snd_soc_tdm_params_to_bclk(params, asp_width, 377 cs35l45->slot_count, slot_multiple); 378 379 return cs35l45_set_pll(cs35l45, bclk); 380} 381 382static int cs35l45_asp_set_tdm_slot(struct snd_soc_dai *dai, 383 unsigned int tx_mask, unsigned int rx_mask, 384 int slots, int slot_width) 385{ 386 struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component); 387 388 if (slot_width && ((slot_width < 16) || (slot_width > 128))) 389 return -EINVAL; 390 391 cs35l45->slot_width = slot_width; 392 cs35l45->slot_count = slots; 393 394 return 0; 395} 396 397static int cs35l45_asp_set_sysclk(struct snd_soc_dai *dai, 398 int clk_id, unsigned int freq, int dir) 399{ 400 struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component); 401 int ret; 402 403 if (clk_id != 0) { 404 dev_err(cs35l45->dev, "Invalid clk_id %d\n", clk_id); 405 return -EINVAL; 406 } 407 408 cs35l45->sysclk_set = false; 409 if (freq == 0) 410 return 0; 411 412 ret = cs35l45_set_pll(cs35l45, freq); 413 if (ret < 0) 414 return -EINVAL; 415 416 cs35l45->sysclk_set = true; 417 418 return 0; 419} 420 421static int cs35l45_mute_stream(struct snd_soc_dai *dai, int mute, int stream) 422{ 423 struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component); 424 unsigned int global_fs, val, hpf_tune; 425 426 if (mute) 427 return 0; 428 429 regmap_read(cs35l45->regmap, CS35L45_GLOBAL_SAMPLE_RATE, &global_fs); 430 global_fs = (global_fs & CS35L45_GLOBAL_FS_MASK) >> CS35L45_GLOBAL_FS_SHIFT; 431 switch (global_fs) { 432 case CS35L45_44P100_KHZ: 433 hpf_tune = CS35L45_HPF_44P1; 434 break; 435 case CS35L45_88P200_KHZ: 436 hpf_tune = CS35L45_HPF_88P2; 437 break; 438 default: 439 hpf_tune = CS35l45_HPF_DEFAULT; 440 break; 441 } 442 443 regmap_read(cs35l45->regmap, CS35L45_AMP_PCM_HPF_TST, &val); 444 if (val != hpf_tune) { 445 struct reg_sequence hpf_override_seq[] = { 446 { 0x00000040, 0x00000055 }, 447 { 0x00000040, 0x000000AA }, 448 { 0x00000044, 0x00000055 }, 449 { 0x00000044, 0x000000AA }, 450 { CS35L45_AMP_PCM_HPF_TST, hpf_tune }, 451 { 0x00000040, 0x00000000 }, 452 { 0x00000044, 0x00000000 }, 453 }; 454 regmap_multi_reg_write(cs35l45->regmap, hpf_override_seq, 455 ARRAY_SIZE(hpf_override_seq)); 456 } 457 458 return 0; 459} 460 461static const struct snd_soc_dai_ops cs35l45_asp_dai_ops = { 462 .set_fmt = cs35l45_asp_set_fmt, 463 .hw_params = cs35l45_asp_hw_params, 464 .set_tdm_slot = cs35l45_asp_set_tdm_slot, 465 .set_sysclk = cs35l45_asp_set_sysclk, 466 .mute_stream = cs35l45_mute_stream, 467}; 468 469static struct snd_soc_dai_driver cs35l45_dai[] = { 470 { 471 .name = "cs35l45", 472 .playback = { 473 .stream_name = "Playback", 474 .channels_min = 1, 475 .channels_max = 2, 476 .rates = CS35L45_RATES, 477 .formats = CS35L45_FORMATS, 478 }, 479 .capture = { 480 .stream_name = "Capture", 481 .channels_min = 1, 482 .channels_max = 5, 483 .rates = CS35L45_RATES, 484 .formats = CS35L45_FORMATS, 485 }, 486 .symmetric_rate = true, 487 .symmetric_sample_bits = true, 488 .ops = &cs35l45_asp_dai_ops, 489 }, 490}; 491 492static const struct snd_soc_component_driver cs35l45_component = { 493 .dapm_widgets = cs35l45_dapm_widgets, 494 .num_dapm_widgets = ARRAY_SIZE(cs35l45_dapm_widgets), 495 496 .dapm_routes = cs35l45_dapm_routes, 497 .num_dapm_routes = ARRAY_SIZE(cs35l45_dapm_routes), 498 499 .controls = cs35l45_controls, 500 .num_controls = ARRAY_SIZE(cs35l45_controls), 501 502 .name = "cs35l45", 503}; 504 505static int __maybe_unused cs35l45_runtime_suspend(struct device *dev) 506{ 507 struct cs35l45_private *cs35l45 = dev_get_drvdata(dev); 508 509 regcache_cache_only(cs35l45->regmap, true); 510 511 dev_dbg(cs35l45->dev, "Runtime suspended\n"); 512 513 return 0; 514} 515 516static int __maybe_unused cs35l45_runtime_resume(struct device *dev) 517{ 518 struct cs35l45_private *cs35l45 = dev_get_drvdata(dev); 519 int ret; 520 521 dev_dbg(cs35l45->dev, "Runtime resume\n"); 522 523 regcache_cache_only(cs35l45->regmap, false); 524 ret = regcache_sync(cs35l45->regmap); 525 if (ret != 0) 526 dev_warn(cs35l45->dev, "regcache_sync failed: %d\n", ret); 527 528 /* Clear global error status */ 529 regmap_clear_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK); 530 regmap_set_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK); 531 regmap_clear_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK); 532 return ret; 533} 534 535static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45) 536{ 537 unsigned int val; 538 539 if (device_property_read_u32(cs35l45->dev, 540 "cirrus,asp-sdout-hiz-ctrl", &val) == 0) { 541 regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL3, 542 CS35L45_ASP_DOUT_HIZ_CTRL_MASK, 543 val << CS35L45_ASP_DOUT_HIZ_CTRL_SHIFT); 544 } 545 546 return 0; 547} 548 549static int cs35l45_initialize(struct cs35l45_private *cs35l45) 550{ 551 struct device *dev = cs35l45->dev; 552 unsigned int dev_id[5]; 553 unsigned int sts; 554 int ret; 555 556 ret = regmap_read_poll_timeout(cs35l45->regmap, CS35L45_IRQ1_EINT_4, sts, 557 (sts & CS35L45_OTP_BOOT_DONE_STS_MASK), 558 1000, 5000); 559 if (ret < 0) { 560 dev_err(cs35l45->dev, "Timeout waiting for OTP boot\n"); 561 return ret; 562 } 563 564 ret = regmap_bulk_read(cs35l45->regmap, CS35L45_DEVID, dev_id, ARRAY_SIZE(dev_id)); 565 if (ret) { 566 dev_err(cs35l45->dev, "Get Device ID failed: %d\n", ret); 567 return ret; 568 } 569 570 switch (dev_id[0]) { 571 case 0x35A450: 572 break; 573 default: 574 dev_err(cs35l45->dev, "Bad DEVID 0x%x\n", dev_id[0]); 575 return -ENODEV; 576 } 577 578 dev_info(cs35l45->dev, "Cirrus Logic CS35L45: REVID %02X OTPID %02X\n", 579 dev_id[1], dev_id[4]); 580 581 regmap_write(cs35l45->regmap, CS35L45_IRQ1_EINT_4, 582 CS35L45_OTP_BOOT_DONE_STS_MASK | CS35L45_OTP_BUSY_MASK); 583 584 ret = cs35l45_apply_patch(cs35l45); 585 if (ret < 0) { 586 dev_err(dev, "Failed to apply init patch %d\n", ret); 587 return ret; 588 } 589 590 ret = cs35l45_apply_property_config(cs35l45); 591 if (ret < 0) 592 return ret; 593 594 pm_runtime_set_autosuspend_delay(cs35l45->dev, 3000); 595 pm_runtime_use_autosuspend(cs35l45->dev); 596 pm_runtime_set_active(cs35l45->dev); 597 pm_runtime_enable(cs35l45->dev); 598 599 return 0; 600} 601 602int cs35l45_probe(struct cs35l45_private *cs35l45) 603{ 604 struct device *dev = cs35l45->dev; 605 int ret; 606 607 cs35l45->vdd_batt = devm_regulator_get(dev, "vdd-batt"); 608 if (IS_ERR(cs35l45->vdd_batt)) 609 return dev_err_probe(dev, PTR_ERR(cs35l45->vdd_batt), 610 "Failed to request vdd-batt\n"); 611 612 cs35l45->vdd_a = devm_regulator_get(dev, "vdd-a"); 613 if (IS_ERR(cs35l45->vdd_a)) 614 return dev_err_probe(dev, PTR_ERR(cs35l45->vdd_a), 615 "Failed to request vdd-a\n"); 616 617 /* VDD_BATT must always be enabled before other supplies */ 618 ret = regulator_enable(cs35l45->vdd_batt); 619 if (ret < 0) 620 return dev_err_probe(dev, ret, "Failed to enable vdd-batt\n"); 621 622 ret = regulator_enable(cs35l45->vdd_a); 623 if (ret < 0) 624 return dev_err_probe(dev, ret, "Failed to enable vdd-a\n"); 625 626 /* If reset is shared only one instance can claim it */ 627 cs35l45->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 628 if (IS_ERR(cs35l45->reset_gpio)) { 629 ret = PTR_ERR(cs35l45->reset_gpio); 630 cs35l45->reset_gpio = NULL; 631 if (ret == -EBUSY) { 632 dev_dbg(dev, "Reset line busy, assuming shared reset\n"); 633 } else { 634 dev_err_probe(dev, ret, "Failed to get reset GPIO\n"); 635 goto err; 636 } 637 } 638 639 if (cs35l45->reset_gpio) { 640 usleep_range(CS35L45_RESET_HOLD_US, CS35L45_RESET_HOLD_US + 100); 641 gpiod_set_value_cansleep(cs35l45->reset_gpio, 1); 642 } 643 644 usleep_range(CS35L45_RESET_US, CS35L45_RESET_US + 100); 645 646 ret = cs35l45_initialize(cs35l45); 647 if (ret < 0) 648 goto err_reset; 649 650 ret = devm_snd_soc_register_component(dev, &cs35l45_component, 651 cs35l45_dai, 652 ARRAY_SIZE(cs35l45_dai)); 653 if (ret < 0) 654 goto err_reset; 655 656 return 0; 657 658err_reset: 659 gpiod_set_value_cansleep(cs35l45->reset_gpio, 0); 660err: 661 regulator_disable(cs35l45->vdd_a); 662 regulator_disable(cs35l45->vdd_batt); 663 664 return ret; 665} 666EXPORT_SYMBOL_NS_GPL(cs35l45_probe, SND_SOC_CS35L45); 667 668int cs35l45_remove(struct cs35l45_private *cs35l45) 669{ 670 pm_runtime_disable(cs35l45->dev); 671 672 gpiod_set_value_cansleep(cs35l45->reset_gpio, 0); 673 regulator_disable(cs35l45->vdd_a); 674 /* VDD_BATT must be the last to power-off */ 675 regulator_disable(cs35l45->vdd_batt); 676 677 return 0; 678} 679EXPORT_SYMBOL_NS_GPL(cs35l45_remove, SND_SOC_CS35L45); 680 681const struct dev_pm_ops cs35l45_pm_ops = { 682 SET_RUNTIME_PM_OPS(cs35l45_runtime_suspend, cs35l45_runtime_resume, NULL) 683}; 684EXPORT_SYMBOL_NS_GPL(cs35l45_pm_ops, SND_SOC_CS35L45); 685 686MODULE_DESCRIPTION("ASoC CS35L45 driver"); 687MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); 688MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); 689MODULE_LICENSE("Dual BSD/GPL"); 690MODULE_IMPORT_NS(SND_SOC_CS35L45_TABLES);