sti-sas.c (12705B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) STMicroelectronics SA 2015 4 * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com> 5 * for STMicroelectronics. 6 */ 7 8#include <linux/io.h> 9#include <linux/module.h> 10#include <linux/regmap.h> 11#include <linux/reset.h> 12#include <linux/mfd/syscon.h> 13 14#include <sound/soc.h> 15#include <sound/soc-dapm.h> 16 17/* DAC definitions */ 18 19/* stih407 DAC registers */ 20/* sysconf 5041: Audio-Gue-Control */ 21#define STIH407_AUDIO_GLUE_CTRL 0x000000A4 22/* sysconf 5042: Audio-DAC-Control */ 23#define STIH407_AUDIO_DAC_CTRL 0x000000A8 24 25/* DAC definitions */ 26#define STIH407_DAC_SOFTMUTE 0x0 27#define STIH407_DAC_STANDBY_ANA 0x1 28#define STIH407_DAC_STANDBY 0x2 29 30#define STIH407_DAC_SOFTMUTE_MASK BIT(STIH407_DAC_SOFTMUTE) 31#define STIH407_DAC_STANDBY_ANA_MASK BIT(STIH407_DAC_STANDBY_ANA) 32#define STIH407_DAC_STANDBY_MASK BIT(STIH407_DAC_STANDBY) 33 34/* SPDIF definitions */ 35#define SPDIF_BIPHASE_ENABLE 0x6 36#define SPDIF_BIPHASE_IDLE 0x7 37 38#define SPDIF_BIPHASE_ENABLE_MASK BIT(SPDIF_BIPHASE_ENABLE) 39#define SPDIF_BIPHASE_IDLE_MASK BIT(SPDIF_BIPHASE_IDLE) 40 41enum { 42 STI_SAS_DAI_SPDIF_OUT, 43 STI_SAS_DAI_ANALOG_OUT, 44}; 45 46static const struct reg_default stih407_sas_reg_defaults[] = { 47 { STIH407_AUDIO_DAC_CTRL, 0x000000000 }, 48 { STIH407_AUDIO_GLUE_CTRL, 0x00000040 }, 49}; 50 51struct sti_dac_audio { 52 struct regmap *regmap; 53 struct regmap *virt_regmap; 54 int mclk; 55}; 56 57struct sti_spdif_audio { 58 struct regmap *regmap; 59 int mclk; 60}; 61 62/* device data structure */ 63struct sti_sas_dev_data { 64 const struct regmap_config *regmap; 65 const struct snd_soc_dai_ops *dac_ops; /* DAC function callbacks */ 66 const struct snd_soc_dapm_widget *dapm_widgets; /* dapms declaration */ 67 const int num_dapm_widgets; /* dapms declaration */ 68 const struct snd_soc_dapm_route *dapm_routes; /* route declaration */ 69 const int num_dapm_routes; /* route declaration */ 70}; 71 72/* driver data structure */ 73struct sti_sas_data { 74 struct device *dev; 75 const struct sti_sas_dev_data *dev_data; 76 struct sti_dac_audio dac; 77 struct sti_spdif_audio spdif; 78}; 79 80/* Read a register from the sysconf reg bank */ 81static int sti_sas_read_reg(void *context, unsigned int reg, 82 unsigned int *value) 83{ 84 struct sti_sas_data *drvdata = context; 85 int status; 86 u32 val; 87 88 status = regmap_read(drvdata->dac.regmap, reg, &val); 89 *value = (unsigned int)val; 90 91 return status; 92} 93 94/* Read a register from the sysconf reg bank */ 95static int sti_sas_write_reg(void *context, unsigned int reg, 96 unsigned int value) 97{ 98 struct sti_sas_data *drvdata = context; 99 int status; 100 101 status = regmap_write(drvdata->dac.regmap, reg, value); 102 103 return status; 104} 105 106static int sti_sas_init_sas_registers(struct snd_soc_component *component, 107 struct sti_sas_data *data) 108{ 109 int ret; 110 /* 111 * DAC and SPDIF are activated by default 112 * put them in IDLE to save power 113 */ 114 115 /* Initialise bi-phase formatter to disabled */ 116 ret = snd_soc_component_update_bits(component, STIH407_AUDIO_GLUE_CTRL, 117 SPDIF_BIPHASE_ENABLE_MASK, 0); 118 119 if (!ret) 120 /* Initialise bi-phase formatter idle value to 0 */ 121 ret = snd_soc_component_update_bits(component, STIH407_AUDIO_GLUE_CTRL, 122 SPDIF_BIPHASE_IDLE_MASK, 0); 123 if (ret < 0) { 124 dev_err(component->dev, "Failed to update SPDIF registers\n"); 125 return ret; 126 } 127 128 /* Init DAC configuration */ 129 /* init configuration */ 130 ret = snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL, 131 STIH407_DAC_STANDBY_MASK, 132 STIH407_DAC_STANDBY_MASK); 133 134 if (!ret) 135 ret = snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL, 136 STIH407_DAC_STANDBY_ANA_MASK, 137 STIH407_DAC_STANDBY_ANA_MASK); 138 if (!ret) 139 ret = snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL, 140 STIH407_DAC_SOFTMUTE_MASK, 141 STIH407_DAC_SOFTMUTE_MASK); 142 143 if (ret < 0) { 144 dev_err(component->dev, "Failed to update DAC registers\n"); 145 return ret; 146 } 147 148 return ret; 149} 150 151/* 152 * DAC 153 */ 154static int sti_sas_dac_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 155{ 156 /* Sanity check only */ 157 if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) { 158 dev_err(dai->component->dev, 159 "%s: ERROR: Unsupported clocking 0x%x\n", 160 __func__, fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK); 161 return -EINVAL; 162 } 163 164 return 0; 165} 166 167static const struct snd_soc_dapm_widget stih407_sas_dapm_widgets[] = { 168 SND_SOC_DAPM_OUT_DRV("DAC standby ana", STIH407_AUDIO_DAC_CTRL, 169 STIH407_DAC_STANDBY_ANA, 1, NULL, 0), 170 SND_SOC_DAPM_DAC("DAC standby", "dac_p", STIH407_AUDIO_DAC_CTRL, 171 STIH407_DAC_STANDBY, 1), 172 SND_SOC_DAPM_OUTPUT("DAC Output"), 173}; 174 175static const struct snd_soc_dapm_route stih407_sas_route[] = { 176 {"DAC Output", NULL, "DAC standby ana"}, 177 {"DAC standby ana", NULL, "DAC standby"}, 178}; 179 180 181static int stih407_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream) 182{ 183 struct snd_soc_component *component = dai->component; 184 185 if (mute) { 186 return snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL, 187 STIH407_DAC_SOFTMUTE_MASK, 188 STIH407_DAC_SOFTMUTE_MASK); 189 } else { 190 return snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL, 191 STIH407_DAC_SOFTMUTE_MASK, 192 0); 193 } 194} 195 196/* 197 * SPDIF 198 */ 199static int sti_sas_spdif_set_fmt(struct snd_soc_dai *dai, 200 unsigned int fmt) 201{ 202 if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { 203 dev_err(dai->component->dev, 204 "%s: ERROR: Unsupporter master mask 0x%x\n", 205 __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK); 206 return -EINVAL; 207 } 208 209 return 0; 210} 211 212/* 213 * sti_sas_spdif_trigger: 214 * Trigger function is used to ensure that BiPhase Formater is disabled 215 * before CPU dai is stopped. 216 * This is mandatory to avoid that BPF is stalled 217 */ 218static int sti_sas_spdif_trigger(struct snd_pcm_substream *substream, int cmd, 219 struct snd_soc_dai *dai) 220{ 221 struct snd_soc_component *component = dai->component; 222 223 switch (cmd) { 224 case SNDRV_PCM_TRIGGER_START: 225 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 226 return snd_soc_component_update_bits(component, STIH407_AUDIO_GLUE_CTRL, 227 SPDIF_BIPHASE_ENABLE_MASK, 228 SPDIF_BIPHASE_ENABLE_MASK); 229 case SNDRV_PCM_TRIGGER_RESUME: 230 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 231 case SNDRV_PCM_TRIGGER_STOP: 232 case SNDRV_PCM_TRIGGER_SUSPEND: 233 return snd_soc_component_update_bits(component, STIH407_AUDIO_GLUE_CTRL, 234 SPDIF_BIPHASE_ENABLE_MASK, 235 0); 236 default: 237 return -EINVAL; 238 } 239} 240 241static bool sti_sas_volatile_register(struct device *dev, unsigned int reg) 242{ 243 if (reg == STIH407_AUDIO_GLUE_CTRL) 244 return true; 245 246 return false; 247} 248 249/* 250 * CODEC DAIS 251 */ 252 253/* 254 * sti_sas_set_sysclk: 255 * get MCLK input frequency to check that MCLK-FS ratio is coherent 256 */ 257static int sti_sas_set_sysclk(struct snd_soc_dai *dai, int clk_id, 258 unsigned int freq, int dir) 259{ 260 struct snd_soc_component *component = dai->component; 261 struct sti_sas_data *drvdata = dev_get_drvdata(component->dev); 262 263 if (dir == SND_SOC_CLOCK_OUT) 264 return 0; 265 266 if (clk_id != 0) 267 return -EINVAL; 268 269 switch (dai->id) { 270 case STI_SAS_DAI_SPDIF_OUT: 271 drvdata->spdif.mclk = freq; 272 break; 273 274 case STI_SAS_DAI_ANALOG_OUT: 275 drvdata->dac.mclk = freq; 276 break; 277 } 278 279 return 0; 280} 281 282static int sti_sas_prepare(struct snd_pcm_substream *substream, 283 struct snd_soc_dai *dai) 284{ 285 struct snd_soc_component *component = dai->component; 286 struct sti_sas_data *drvdata = dev_get_drvdata(component->dev); 287 struct snd_pcm_runtime *runtime = substream->runtime; 288 289 switch (dai->id) { 290 case STI_SAS_DAI_SPDIF_OUT: 291 if ((drvdata->spdif.mclk / runtime->rate) != 128) { 292 dev_err(component->dev, "unexpected mclk-fs ratio\n"); 293 return -EINVAL; 294 } 295 break; 296 case STI_SAS_DAI_ANALOG_OUT: 297 if ((drvdata->dac.mclk / runtime->rate) != 256) { 298 dev_err(component->dev, "unexpected mclk-fs ratio\n"); 299 return -EINVAL; 300 } 301 break; 302 } 303 304 return 0; 305} 306 307static const struct snd_soc_dai_ops stih407_dac_ops = { 308 .set_fmt = sti_sas_dac_set_fmt, 309 .mute_stream = stih407_sas_dac_mute, 310 .prepare = sti_sas_prepare, 311 .set_sysclk = sti_sas_set_sysclk, 312}; 313 314static const struct regmap_config stih407_sas_regmap = { 315 .reg_bits = 32, 316 .val_bits = 32, 317 .fast_io = true, 318 .max_register = STIH407_AUDIO_DAC_CTRL, 319 .reg_defaults = stih407_sas_reg_defaults, 320 .num_reg_defaults = ARRAY_SIZE(stih407_sas_reg_defaults), 321 .volatile_reg = sti_sas_volatile_register, 322 .cache_type = REGCACHE_RBTREE, 323 .reg_read = sti_sas_read_reg, 324 .reg_write = sti_sas_write_reg, 325}; 326 327static const struct sti_sas_dev_data stih407_data = { 328 .regmap = &stih407_sas_regmap, 329 .dac_ops = &stih407_dac_ops, 330 .dapm_widgets = stih407_sas_dapm_widgets, 331 .num_dapm_widgets = ARRAY_SIZE(stih407_sas_dapm_widgets), 332 .dapm_routes = stih407_sas_route, 333 .num_dapm_routes = ARRAY_SIZE(stih407_sas_route), 334}; 335 336static struct snd_soc_dai_driver sti_sas_dai[] = { 337 { 338 .name = "sas-dai-spdif-out", 339 .id = STI_SAS_DAI_SPDIF_OUT, 340 .playback = { 341 .stream_name = "spdif_p", 342 .channels_min = 2, 343 .channels_max = 2, 344 .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | 345 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | 346 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | 347 SNDRV_PCM_RATE_192000, 348 .formats = SNDRV_PCM_FMTBIT_S16_LE | 349 SNDRV_PCM_FMTBIT_S32_LE, 350 }, 351 .ops = (struct snd_soc_dai_ops[]) { 352 { 353 .set_fmt = sti_sas_spdif_set_fmt, 354 .trigger = sti_sas_spdif_trigger, 355 .set_sysclk = sti_sas_set_sysclk, 356 .prepare = sti_sas_prepare, 357 } 358 }, 359 }, 360 { 361 .name = "sas-dai-dac", 362 .id = STI_SAS_DAI_ANALOG_OUT, 363 .playback = { 364 .stream_name = "dac_p", 365 .channels_min = 2, 366 .channels_max = 2, 367 .rates = SNDRV_PCM_RATE_8000_48000, 368 .formats = SNDRV_PCM_FMTBIT_S16_LE | 369 SNDRV_PCM_FMTBIT_S32_LE, 370 }, 371 }, 372}; 373 374#ifdef CONFIG_PM_SLEEP 375static int sti_sas_resume(struct snd_soc_component *component) 376{ 377 struct sti_sas_data *drvdata = dev_get_drvdata(component->dev); 378 379 return sti_sas_init_sas_registers(component, drvdata); 380} 381#else 382#define sti_sas_resume NULL 383#endif 384 385static int sti_sas_component_probe(struct snd_soc_component *component) 386{ 387 struct sti_sas_data *drvdata = dev_get_drvdata(component->dev); 388 int ret; 389 390 ret = sti_sas_init_sas_registers(component, drvdata); 391 392 return ret; 393} 394 395static struct snd_soc_component_driver sti_sas_driver = { 396 .probe = sti_sas_component_probe, 397 .resume = sti_sas_resume, 398 .idle_bias_on = 1, 399 .use_pmdown_time = 1, 400 .endianness = 1, 401 .non_legacy_dai_naming = 1, 402}; 403 404static const struct of_device_id sti_sas_dev_match[] = { 405 { 406 .compatible = "st,stih407-sas-codec", 407 .data = &stih407_data, 408 }, 409 {}, 410}; 411MODULE_DEVICE_TABLE(of, sti_sas_dev_match); 412 413static int sti_sas_driver_probe(struct platform_device *pdev) 414{ 415 struct device_node *pnode = pdev->dev.of_node; 416 struct sti_sas_data *drvdata; 417 const struct of_device_id *of_id; 418 419 /* Allocate device structure */ 420 drvdata = devm_kzalloc(&pdev->dev, sizeof(struct sti_sas_data), 421 GFP_KERNEL); 422 if (!drvdata) 423 return -ENOMEM; 424 425 /* Populate data structure depending on compatibility */ 426 of_id = of_match_node(sti_sas_dev_match, pnode); 427 if (!of_id->data) { 428 dev_err(&pdev->dev, "data associated to device is missing\n"); 429 return -EINVAL; 430 } 431 432 drvdata->dev_data = (struct sti_sas_dev_data *)of_id->data; 433 434 /* Initialise device structure */ 435 drvdata->dev = &pdev->dev; 436 437 /* Request the DAC & SPDIF registers memory region */ 438 drvdata->dac.virt_regmap = devm_regmap_init(&pdev->dev, NULL, drvdata, 439 drvdata->dev_data->regmap); 440 if (IS_ERR(drvdata->dac.virt_regmap)) { 441 dev_err(&pdev->dev, "audio registers not enabled\n"); 442 return PTR_ERR(drvdata->dac.virt_regmap); 443 } 444 445 /* Request the syscon region */ 446 drvdata->dac.regmap = 447 syscon_regmap_lookup_by_phandle(pnode, "st,syscfg"); 448 if (IS_ERR(drvdata->dac.regmap)) { 449 dev_err(&pdev->dev, "syscon registers not available\n"); 450 return PTR_ERR(drvdata->dac.regmap); 451 } 452 drvdata->spdif.regmap = drvdata->dac.regmap; 453 454 sti_sas_dai[STI_SAS_DAI_ANALOG_OUT].ops = drvdata->dev_data->dac_ops; 455 456 /* Set dapms*/ 457 sti_sas_driver.dapm_widgets = drvdata->dev_data->dapm_widgets; 458 sti_sas_driver.num_dapm_widgets = drvdata->dev_data->num_dapm_widgets; 459 460 sti_sas_driver.dapm_routes = drvdata->dev_data->dapm_routes; 461 sti_sas_driver.num_dapm_routes = drvdata->dev_data->num_dapm_routes; 462 463 /* Store context */ 464 dev_set_drvdata(&pdev->dev, drvdata); 465 466 return devm_snd_soc_register_component(&pdev->dev, &sti_sas_driver, 467 sti_sas_dai, 468 ARRAY_SIZE(sti_sas_dai)); 469} 470 471static struct platform_driver sti_sas_platform_driver = { 472 .driver = { 473 .name = "sti-sas-codec", 474 .of_match_table = sti_sas_dev_match, 475 }, 476 .probe = sti_sas_driver_probe, 477}; 478 479module_platform_driver(sti_sas_platform_driver); 480 481MODULE_DESCRIPTION("audio codec for STMicroelectronics sti platforms"); 482MODULE_AUTHOR("Arnaud.pouliquen@st.com"); 483MODULE_LICENSE("GPL v2");