| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * AD193X Audio Codec driver supporting AD1936/7/8/9 |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright 2010 Analog Devices Inc. |
|---|
| 5 | | - * |
|---|
| 6 | | - * Licensed under the GPL-2 or later. |
|---|
| 7 | 6 | */ |
|---|
| 8 | 7 | |
|---|
| 9 | 8 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 36 | 35 | ad193x_deemp); |
|---|
| 37 | 36 | |
|---|
| 38 | 37 | static const DECLARE_TLV_DB_MINMAX(adau193x_tlv, -9563, 0); |
|---|
| 38 | + |
|---|
| 39 | +static const unsigned int ad193x_sb[] = {32}; |
|---|
| 40 | + |
|---|
| 41 | +static struct snd_pcm_hw_constraint_list constr = { |
|---|
| 42 | + .list = ad193x_sb, |
|---|
| 43 | + .count = ARRAY_SIZE(ad193x_sb), |
|---|
| 44 | +}; |
|---|
| 39 | 45 | |
|---|
| 40 | 46 | static const struct snd_kcontrol_new ad193x_snd_controls[] = { |
|---|
| 41 | 47 | /* DAC volume control */ |
|---|
| .. | .. |
|---|
| 93 | 99 | SND_SOC_DAPM_INPUT("ADC2IN"), |
|---|
| 94 | 100 | }; |
|---|
| 95 | 101 | |
|---|
| 102 | +static int ad193x_check_pll(struct snd_soc_dapm_widget *source, |
|---|
| 103 | + struct snd_soc_dapm_widget *sink) |
|---|
| 104 | +{ |
|---|
| 105 | + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); |
|---|
| 106 | + struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); |
|---|
| 107 | + |
|---|
| 108 | + return !!ad193x->sysclk; |
|---|
| 109 | +} |
|---|
| 110 | + |
|---|
| 96 | 111 | static const struct snd_soc_dapm_route audio_paths[] = { |
|---|
| 97 | 112 | { "DAC", NULL, "SYSCLK" }, |
|---|
| 98 | 113 | { "DAC Output", NULL, "DAC" }, |
|---|
| .. | .. |
|---|
| 101 | 116 | { "DAC2OUT", NULL, "DAC Output" }, |
|---|
| 102 | 117 | { "DAC3OUT", NULL, "DAC Output" }, |
|---|
| 103 | 118 | { "DAC4OUT", NULL, "DAC Output" }, |
|---|
| 104 | | - { "SYSCLK", NULL, "PLL_PWR" }, |
|---|
| 119 | + { "SYSCLK", NULL, "PLL_PWR", &ad193x_check_pll }, |
|---|
| 105 | 120 | }; |
|---|
| 106 | 121 | |
|---|
| 107 | 122 | static const struct snd_soc_dapm_route ad193x_adc_audio_paths[] = { |
|---|
| .. | .. |
|---|
| 128 | 143 | * DAI ops entries |
|---|
| 129 | 144 | */ |
|---|
| 130 | 145 | |
|---|
| 131 | | -static int ad193x_mute(struct snd_soc_dai *dai, int mute) |
|---|
| 146 | +static int ad193x_mute(struct snd_soc_dai *dai, int mute, int direction) |
|---|
| 132 | 147 | { |
|---|
| 133 | 148 | struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(dai->component); |
|---|
| 134 | 149 | |
|---|
| .. | .. |
|---|
| 181 | 196 | { |
|---|
| 182 | 197 | struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(codec_dai->component); |
|---|
| 183 | 198 | unsigned int adc_serfmt = 0; |
|---|
| 199 | + unsigned int dac_serfmt = 0; |
|---|
| 184 | 200 | unsigned int adc_fmt = 0; |
|---|
| 185 | 201 | unsigned int dac_fmt = 0; |
|---|
| 186 | 202 | |
|---|
| 187 | 203 | /* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S |
|---|
| 188 | | - * with TDM) and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A) |
|---|
| 204 | + * with TDM), ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A) and DAC I2S mode |
|---|
| 205 | + * (SND_SOC_DAIFMT_I2S) |
|---|
| 189 | 206 | */ |
|---|
| 190 | 207 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
|---|
| 191 | 208 | case SND_SOC_DAIFMT_I2S: |
|---|
| 192 | 209 | adc_serfmt |= AD193X_ADC_SERFMT_TDM; |
|---|
| 210 | + dac_serfmt |= AD193X_DAC_SERFMT_STEREO; |
|---|
| 193 | 211 | break; |
|---|
| 194 | 212 | case SND_SOC_DAIFMT_DSP_A: |
|---|
| 195 | 213 | adc_serfmt |= AD193X_ADC_SERFMT_AUX; |
|---|
| 214 | + dac_serfmt |= AD193X_DAC_SERFMT_TDM; |
|---|
| 196 | 215 | break; |
|---|
| 197 | 216 | default: |
|---|
| 198 | 217 | if (ad193x_has_adc(ad193x)) |
|---|
| 199 | 218 | return -EINVAL; |
|---|
| 200 | | - break; |
|---|
| 201 | 219 | } |
|---|
| 202 | 220 | |
|---|
| 203 | 221 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
|---|
| .. | .. |
|---|
| 220 | 238 | default: |
|---|
| 221 | 239 | return -EINVAL; |
|---|
| 222 | 240 | } |
|---|
| 241 | + |
|---|
| 242 | + /* For DSP_*, LRCLK's polarity must be inverted */ |
|---|
| 243 | + if (fmt & SND_SOC_DAIFMT_DSP_A) |
|---|
| 244 | + dac_fmt ^= AD193X_DAC_LEFT_HIGH; |
|---|
| 223 | 245 | |
|---|
| 224 | 246 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
|---|
| 225 | 247 | case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */ |
|---|
| .. | .. |
|---|
| 248 | 270 | regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2, |
|---|
| 249 | 271 | AD193X_ADC_FMT_MASK, adc_fmt); |
|---|
| 250 | 272 | } |
|---|
| 273 | + regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL0, |
|---|
| 274 | + AD193X_DAC_SERFMT_MASK, dac_serfmt); |
|---|
| 251 | 275 | regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1, |
|---|
| 252 | 276 | AD193X_DAC_FMT_MASK, dac_fmt); |
|---|
| 253 | 277 | |
|---|
| .. | .. |
|---|
| 258 | 282 | int clk_id, unsigned int freq, int dir) |
|---|
| 259 | 283 | { |
|---|
| 260 | 284 | struct snd_soc_component *component = codec_dai->component; |
|---|
| 285 | + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); |
|---|
| 261 | 286 | struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); |
|---|
| 287 | + |
|---|
| 288 | + if (clk_id == AD193X_SYSCLK_MCLK) { |
|---|
| 289 | + /* MCLK must be 512 x fs */ |
|---|
| 290 | + if (dir == SND_SOC_CLOCK_OUT || freq != 24576000) |
|---|
| 291 | + return -EINVAL; |
|---|
| 292 | + |
|---|
| 293 | + regmap_update_bits(ad193x->regmap, AD193X_PLL_CLK_CTRL1, |
|---|
| 294 | + AD193X_PLL_SRC_MASK, |
|---|
| 295 | + AD193X_PLL_DAC_SRC_MCLK | |
|---|
| 296 | + AD193X_PLL_CLK_SRC_MCLK); |
|---|
| 297 | + |
|---|
| 298 | + snd_soc_dapm_sync(dapm); |
|---|
| 299 | + return 0; |
|---|
| 300 | + } |
|---|
| 262 | 301 | switch (freq) { |
|---|
| 263 | 302 | case 12288000: |
|---|
| 264 | 303 | case 18432000: |
|---|
| .. | .. |
|---|
| 321 | 360 | return 0; |
|---|
| 322 | 361 | } |
|---|
| 323 | 362 | |
|---|
| 363 | +static int ad193x_startup(struct snd_pcm_substream *substream, |
|---|
| 364 | + struct snd_soc_dai *dai) |
|---|
| 365 | +{ |
|---|
| 366 | + return snd_pcm_hw_constraint_list(substream->runtime, 0, |
|---|
| 367 | + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, |
|---|
| 368 | + &constr); |
|---|
| 369 | +} |
|---|
| 370 | + |
|---|
| 324 | 371 | static const struct snd_soc_dai_ops ad193x_dai_ops = { |
|---|
| 372 | + .startup = ad193x_startup, |
|---|
| 325 | 373 | .hw_params = ad193x_hw_params, |
|---|
| 326 | | - .digital_mute = ad193x_mute, |
|---|
| 374 | + .mute_stream = ad193x_mute, |
|---|
| 327 | 375 | .set_tdm_slot = ad193x_set_tdm_slot, |
|---|
| 328 | 376 | .set_sysclk = ad193x_set_dai_sysclk, |
|---|
| 329 | 377 | .set_fmt = ad193x_set_dai_fmt, |
|---|
| 378 | + .no_capture_mute = 1, |
|---|
| 330 | 379 | }; |
|---|
| 331 | 380 | |
|---|
| 332 | 381 | /* codec DAI instance */ |
|---|
| .. | .. |
|---|
| 351 | 400 | .ops = &ad193x_dai_ops, |
|---|
| 352 | 401 | }; |
|---|
| 353 | 402 | |
|---|
| 403 | +/* codec DAI instance for DAC only */ |
|---|
| 404 | +static struct snd_soc_dai_driver ad193x_no_adc_dai = { |
|---|
| 405 | + .name = "ad193x-hifi", |
|---|
| 406 | + .playback = { |
|---|
| 407 | + .stream_name = "Playback", |
|---|
| 408 | + .channels_min = 2, |
|---|
| 409 | + .channels_max = 8, |
|---|
| 410 | + .rates = SNDRV_PCM_RATE_48000, |
|---|
| 411 | + .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | |
|---|
| 412 | + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, |
|---|
| 413 | + }, |
|---|
| 414 | + .ops = &ad193x_dai_ops, |
|---|
| 415 | +}; |
|---|
| 416 | + |
|---|
| 417 | +/* codec register values to set after reset */ |
|---|
| 418 | +static void ad193x_reg_default_init(struct ad193x_priv *ad193x) |
|---|
| 419 | +{ |
|---|
| 420 | + static const struct reg_sequence reg_init[] = { |
|---|
| 421 | + { 0, 0x99 }, /* PLL_CLK_CTRL0: pll input: mclki/xi 12.288Mhz */ |
|---|
| 422 | + { 1, 0x04 }, /* PLL_CLK_CTRL1: no on-chip Vref */ |
|---|
| 423 | + { 2, 0x40 }, /* DAC_CTRL0: TDM mode */ |
|---|
| 424 | + { 3, 0x00 }, /* DAC_CTRL1: reset */ |
|---|
| 425 | + { 4, 0x1A }, /* DAC_CTRL2: 48kHz de-emphasis, unmute dac */ |
|---|
| 426 | + { 5, 0x00 }, /* DAC_CHNL_MUTE: unmute DAC channels */ |
|---|
| 427 | + { 6, 0x00 }, /* DAC_L1_VOL: no attenuation */ |
|---|
| 428 | + { 7, 0x00 }, /* DAC_R1_VOL: no attenuation */ |
|---|
| 429 | + { 8, 0x00 }, /* DAC_L2_VOL: no attenuation */ |
|---|
| 430 | + { 9, 0x00 }, /* DAC_R2_VOL: no attenuation */ |
|---|
| 431 | + { 10, 0x00 }, /* DAC_L3_VOL: no attenuation */ |
|---|
| 432 | + { 11, 0x00 }, /* DAC_R3_VOL: no attenuation */ |
|---|
| 433 | + { 12, 0x00 }, /* DAC_L4_VOL: no attenuation */ |
|---|
| 434 | + { 13, 0x00 }, /* DAC_R4_VOL: no attenuation */ |
|---|
| 435 | + }; |
|---|
| 436 | + static const struct reg_sequence reg_adc_init[] = { |
|---|
| 437 | + { 14, 0x03 }, /* ADC_CTRL0: high-pass filter enable */ |
|---|
| 438 | + { 15, 0x43 }, /* ADC_CTRL1: sata delay=1, adc aux mode */ |
|---|
| 439 | + { 16, 0x00 }, /* ADC_CTRL2: reset */ |
|---|
| 440 | + }; |
|---|
| 441 | + |
|---|
| 442 | + regmap_multi_reg_write(ad193x->regmap, reg_init, ARRAY_SIZE(reg_init)); |
|---|
| 443 | + |
|---|
| 444 | + if (ad193x_has_adc(ad193x)) { |
|---|
| 445 | + regmap_multi_reg_write(ad193x->regmap, reg_adc_init, |
|---|
| 446 | + ARRAY_SIZE(reg_adc_init)); |
|---|
| 447 | + } |
|---|
| 448 | +} |
|---|
| 449 | + |
|---|
| 354 | 450 | static int ad193x_component_probe(struct snd_soc_component *component) |
|---|
| 355 | 451 | { |
|---|
| 356 | 452 | struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); |
|---|
| .. | .. |
|---|
| 358 | 454 | int num, ret; |
|---|
| 359 | 455 | |
|---|
| 360 | 456 | /* default setting for ad193x */ |
|---|
| 361 | | - |
|---|
| 362 | | - /* unmute dac channels */ |
|---|
| 363 | | - regmap_write(ad193x->regmap, AD193X_DAC_CHNL_MUTE, 0x0); |
|---|
| 364 | | - /* de-emphasis: 48kHz, powedown dac */ |
|---|
| 365 | | - regmap_write(ad193x->regmap, AD193X_DAC_CTRL2, 0x1A); |
|---|
| 366 | | - /* dac in tdm mode */ |
|---|
| 367 | | - regmap_write(ad193x->regmap, AD193X_DAC_CTRL0, 0x40); |
|---|
| 368 | | - |
|---|
| 369 | | - /* adc only */ |
|---|
| 370 | | - if (ad193x_has_adc(ad193x)) { |
|---|
| 371 | | - /* high-pass filter enable */ |
|---|
| 372 | | - regmap_write(ad193x->regmap, AD193X_ADC_CTRL0, 0x3); |
|---|
| 373 | | - /* sata delay=1, adc aux mode */ |
|---|
| 374 | | - regmap_write(ad193x->regmap, AD193X_ADC_CTRL1, 0x43); |
|---|
| 375 | | - } |
|---|
| 376 | | - |
|---|
| 377 | | - /* pll input: mclki/xi */ |
|---|
| 378 | | - regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */ |
|---|
| 379 | | - regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL1, 0x04); |
|---|
| 457 | + ad193x_reg_default_init(ad193x); |
|---|
| 380 | 458 | |
|---|
| 381 | 459 | /* adc only */ |
|---|
| 382 | 460 | if (ad193x_has_adc(ad193x)) { |
|---|
| .. | .. |
|---|
| 444 | 522 | |
|---|
| 445 | 523 | dev_set_drvdata(dev, ad193x); |
|---|
| 446 | 524 | |
|---|
| 525 | + if (ad193x_has_adc(ad193x)) |
|---|
| 526 | + return devm_snd_soc_register_component(dev, &soc_component_dev_ad193x, |
|---|
| 527 | + &ad193x_dai, 1); |
|---|
| 447 | 528 | return devm_snd_soc_register_component(dev, &soc_component_dev_ad193x, |
|---|
| 448 | | - &ad193x_dai, 1); |
|---|
| 529 | + &ad193x_no_adc_dai, 1); |
|---|
| 449 | 530 | } |
|---|
| 450 | 531 | EXPORT_SYMBOL_GPL(ad193x_probe); |
|---|
| 451 | 532 | |
|---|