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