448 lines · c
1// SPDX-License-Identifier: (GPL-2.0 OR MIT)2//3// Copyright (c) 2018 BayLibre, SAS.4// Author: Jerome Brunet <jbrunet@baylibre.com>5 6#include <linux/clk.h>7#include <linux/module.h>8#include <linux/of_platform.h>9#include <linux/regmap.h>10#include <sound/soc.h>11#include <sound/soc-dai.h>12#include <sound/pcm_params.h>13#include <sound/pcm_iec958.h>14 15/*16 * NOTE:17 * The meaning of bits SPDIFOUT_CTRL0_XXX_SEL is actually the opposite18 * of what the documentation says. Manual control on V, U and C bits is19 * applied when the related sel bits are cleared20 */21 22#define SPDIFOUT_STAT 0x0023#define SPDIFOUT_GAIN0 0x0424#define SPDIFOUT_GAIN1 0x0825#define SPDIFOUT_CTRL0 0x0c26#define SPDIFOUT_CTRL0_EN BIT(31)27#define SPDIFOUT_CTRL0_RST_OUT BIT(29)28#define SPDIFOUT_CTRL0_RST_IN BIT(28)29#define SPDIFOUT_CTRL0_USEL BIT(26)30#define SPDIFOUT_CTRL0_USET BIT(25)31#define SPDIFOUT_CTRL0_CHSTS_SEL BIT(24)32#define SPDIFOUT_CTRL0_DATA_SEL BIT(20)33#define SPDIFOUT_CTRL0_MSB_FIRST BIT(19)34#define SPDIFOUT_CTRL0_VSEL BIT(18)35#define SPDIFOUT_CTRL0_VSET BIT(17)36#define SPDIFOUT_CTRL0_MASK_MASK GENMASK(11, 4)37#define SPDIFOUT_CTRL0_MASK(x) ((x) << 4)38#define SPDIFOUT_CTRL1 0x1039#define SPDIFOUT_CTRL1_MSB_POS_MASK GENMASK(12, 8)40#define SPDIFOUT_CTRL1_MSB_POS(x) ((x) << 8)41#define SPDIFOUT_CTRL1_TYPE_MASK GENMASK(6, 4)42#define SPDIFOUT_CTRL1_TYPE(x) ((x) << 4)43#define SPDIFOUT_PREAMB 0x1444#define SPDIFOUT_SWAP 0x1845#define SPDIFOUT_CHSTS0 0x1c46#define SPDIFOUT_CHSTS1 0x2047#define SPDIFOUT_CHSTS2 0x2448#define SPDIFOUT_CHSTS3 0x2849#define SPDIFOUT_CHSTS4 0x2c50#define SPDIFOUT_CHSTS5 0x3051#define SPDIFOUT_CHSTS6 0x3452#define SPDIFOUT_CHSTS7 0x3853#define SPDIFOUT_CHSTS8 0x3c54#define SPDIFOUT_CHSTS9 0x4055#define SPDIFOUT_CHSTSA 0x4456#define SPDIFOUT_CHSTSB 0x4857#define SPDIFOUT_MUTE_VAL 0x4c58 59struct axg_spdifout {60 struct regmap *map;61 struct clk *mclk;62 struct clk *pclk;63};64 65static void axg_spdifout_enable(struct regmap *map)66{67 /* Apply both reset */68 regmap_update_bits(map, SPDIFOUT_CTRL0,69 SPDIFOUT_CTRL0_RST_OUT | SPDIFOUT_CTRL0_RST_IN,70 0);71 72 /* Clear out reset before in reset */73 regmap_update_bits(map, SPDIFOUT_CTRL0,74 SPDIFOUT_CTRL0_RST_OUT, SPDIFOUT_CTRL0_RST_OUT);75 regmap_update_bits(map, SPDIFOUT_CTRL0,76 SPDIFOUT_CTRL0_RST_IN, SPDIFOUT_CTRL0_RST_IN);77 78 /* Enable spdifout */79 regmap_update_bits(map, SPDIFOUT_CTRL0, SPDIFOUT_CTRL0_EN,80 SPDIFOUT_CTRL0_EN);81}82 83static void axg_spdifout_disable(struct regmap *map)84{85 regmap_update_bits(map, SPDIFOUT_CTRL0, SPDIFOUT_CTRL0_EN, 0);86}87 88static int axg_spdifout_trigger(struct snd_pcm_substream *substream, int cmd,89 struct snd_soc_dai *dai)90{91 struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai);92 93 switch (cmd) {94 case SNDRV_PCM_TRIGGER_START:95 case SNDRV_PCM_TRIGGER_RESUME:96 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:97 axg_spdifout_enable(priv->map);98 return 0;99 100 case SNDRV_PCM_TRIGGER_STOP:101 case SNDRV_PCM_TRIGGER_SUSPEND:102 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:103 axg_spdifout_disable(priv->map);104 return 0;105 106 default:107 return -EINVAL;108 }109}110 111static int axg_spdifout_mute(struct snd_soc_dai *dai, int mute, int direction)112{113 struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai);114 115 /* Use spdif valid bit to perform digital mute */116 regmap_update_bits(priv->map, SPDIFOUT_CTRL0, SPDIFOUT_CTRL0_VSET,117 mute ? SPDIFOUT_CTRL0_VSET : 0);118 119 return 0;120}121 122static int axg_spdifout_sample_fmt(struct snd_pcm_hw_params *params,123 struct snd_soc_dai *dai)124{125 struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai);126 unsigned int val;127 128 /* Set the samples spdifout will pull from the FIFO */129 switch (params_channels(params)) {130 case 1:131 val = SPDIFOUT_CTRL0_MASK(0x1);132 break;133 case 2:134 val = SPDIFOUT_CTRL0_MASK(0x3);135 break;136 default:137 dev_err(dai->dev, "too many channels for spdif dai: %u\n",138 params_channels(params));139 return -EINVAL;140 }141 142 regmap_update_bits(priv->map, SPDIFOUT_CTRL0,143 SPDIFOUT_CTRL0_MASK_MASK, val);144 145 /* FIFO data are arranged in chunks of 64bits */146 switch (params_physical_width(params)) {147 case 8:148 /* 8 samples of 8 bits */149 val = SPDIFOUT_CTRL1_TYPE(0);150 break;151 case 16:152 /* 4 samples of 16 bits - right justified */153 val = SPDIFOUT_CTRL1_TYPE(2);154 break;155 case 32:156 /* 2 samples of 32 bits - right justified */157 val = SPDIFOUT_CTRL1_TYPE(4);158 break;159 default:160 dev_err(dai->dev, "Unsupported physical width: %u\n",161 params_physical_width(params));162 return -EINVAL;163 }164 165 /* Position of the MSB in FIFO samples */166 val |= SPDIFOUT_CTRL1_MSB_POS(params_width(params) - 1);167 168 regmap_update_bits(priv->map, SPDIFOUT_CTRL1,169 SPDIFOUT_CTRL1_MSB_POS_MASK |170 SPDIFOUT_CTRL1_TYPE_MASK, val);171 172 regmap_update_bits(priv->map, SPDIFOUT_CTRL0,173 SPDIFOUT_CTRL0_MSB_FIRST | SPDIFOUT_CTRL0_DATA_SEL,174 0);175 176 return 0;177}178 179static int axg_spdifout_set_chsts(struct snd_pcm_hw_params *params,180 struct snd_soc_dai *dai)181{182 struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai);183 unsigned int offset;184 int ret;185 u8 cs[4];186 u32 val;187 188 ret = snd_pcm_create_iec958_consumer_hw_params(params, cs, 4);189 if (ret < 0) {190 dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",191 ret);192 return ret;193 }194 val = cs[0] | cs[1] << 8 | cs[2] << 16 | cs[3] << 24;195 196 /* Setup channel status A bits [31 - 0]*/197 regmap_write(priv->map, SPDIFOUT_CHSTS0, val);198 199 /* Clear channel status A bits [191 - 32] */200 for (offset = SPDIFOUT_CHSTS1; offset <= SPDIFOUT_CHSTS5;201 offset += regmap_get_reg_stride(priv->map))202 regmap_write(priv->map, offset, 0);203 204 /* Setup channel status B bits [31 - 0]*/205 regmap_write(priv->map, SPDIFOUT_CHSTS6, val);206 207 /* Clear channel status B bits [191 - 32] */208 for (offset = SPDIFOUT_CHSTS7; offset <= SPDIFOUT_CHSTSB;209 offset += regmap_get_reg_stride(priv->map))210 regmap_write(priv->map, offset, 0);211 212 return 0;213}214 215static int axg_spdifout_hw_params(struct snd_pcm_substream *substream,216 struct snd_pcm_hw_params *params,217 struct snd_soc_dai *dai)218{219 struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai);220 unsigned int rate = params_rate(params);221 int ret;222 223 /* 2 * 32bits per subframe * 2 channels = 128 */224 ret = clk_set_rate(priv->mclk, rate * 128);225 if (ret) {226 dev_err(dai->dev, "failed to set spdif clock\n");227 return ret;228 }229 230 ret = axg_spdifout_sample_fmt(params, dai);231 if (ret) {232 dev_err(dai->dev, "failed to setup sample format\n");233 return ret;234 }235 236 ret = axg_spdifout_set_chsts(params, dai);237 if (ret) {238 dev_err(dai->dev, "failed to setup channel status words\n");239 return ret;240 }241 242 return 0;243}244 245static int axg_spdifout_startup(struct snd_pcm_substream *substream,246 struct snd_soc_dai *dai)247{248 struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai);249 int ret;250 251 /* Clock the spdif output block */252 ret = clk_prepare_enable(priv->pclk);253 if (ret) {254 dev_err(dai->dev, "failed to enable pclk\n");255 return ret;256 }257 258 /* Make sure the block is initially stopped */259 axg_spdifout_disable(priv->map);260 261 /* Insert data from bit 27 lsb first */262 regmap_update_bits(priv->map, SPDIFOUT_CTRL0,263 SPDIFOUT_CTRL0_MSB_FIRST | SPDIFOUT_CTRL0_DATA_SEL,264 0);265 266 /* Manual control of V, C and U, U = 0 */267 regmap_update_bits(priv->map, SPDIFOUT_CTRL0,268 SPDIFOUT_CTRL0_CHSTS_SEL | SPDIFOUT_CTRL0_VSEL |269 SPDIFOUT_CTRL0_USEL | SPDIFOUT_CTRL0_USET,270 0);271 272 /* Static SWAP configuration ATM */273 regmap_write(priv->map, SPDIFOUT_SWAP, 0x10);274 275 return 0;276}277 278static void axg_spdifout_shutdown(struct snd_pcm_substream *substream,279 struct snd_soc_dai *dai)280{281 struct axg_spdifout *priv = snd_soc_dai_get_drvdata(dai);282 283 clk_disable_unprepare(priv->pclk);284}285 286static const struct snd_soc_dai_ops axg_spdifout_ops = {287 .trigger = axg_spdifout_trigger,288 .mute_stream = axg_spdifout_mute,289 .hw_params = axg_spdifout_hw_params,290 .startup = axg_spdifout_startup,291 .shutdown = axg_spdifout_shutdown,292 .no_capture_mute = 1,293};294 295static struct snd_soc_dai_driver axg_spdifout_dai_drv[] = {296 {297 .name = "SPDIF Output",298 .playback = {299 .stream_name = "Playback",300 .channels_min = 1,301 .channels_max = 2,302 .rates = (SNDRV_PCM_RATE_32000 |303 SNDRV_PCM_RATE_44100 |304 SNDRV_PCM_RATE_48000 |305 SNDRV_PCM_RATE_88200 |306 SNDRV_PCM_RATE_96000 |307 SNDRV_PCM_RATE_176400 |308 SNDRV_PCM_RATE_192000),309 .formats = (SNDRV_PCM_FMTBIT_S8 |310 SNDRV_PCM_FMTBIT_S16_LE |311 SNDRV_PCM_FMTBIT_S20_LE |312 SNDRV_PCM_FMTBIT_S24_LE),313 },314 .ops = &axg_spdifout_ops,315 },316};317 318static const char * const spdifout_sel_texts[] = {319 "IN 0", "IN 1", "IN 2",320};321 322static SOC_ENUM_SINGLE_DECL(axg_spdifout_sel_enum, SPDIFOUT_CTRL1, 24,323 spdifout_sel_texts);324 325static const struct snd_kcontrol_new axg_spdifout_in_mux =326 SOC_DAPM_ENUM("Input Source", axg_spdifout_sel_enum);327 328static const struct snd_soc_dapm_widget axg_spdifout_dapm_widgets[] = {329 SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0),330 SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0),331 SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0),332 SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_spdifout_in_mux),333};334 335static const struct snd_soc_dapm_route axg_spdifout_dapm_routes[] = {336 { "SRC SEL", "IN 0", "IN 0" },337 { "SRC SEL", "IN 1", "IN 1" },338 { "SRC SEL", "IN 2", "IN 2" },339 { "Playback", NULL, "SRC SEL" },340};341 342static const struct snd_kcontrol_new axg_spdifout_controls[] = {343 SOC_DOUBLE("Playback Volume", SPDIFOUT_GAIN0, 0, 8, 255, 0),344 SOC_DOUBLE("Playback Switch", SPDIFOUT_CTRL0, 22, 21, 1, 1),345 SOC_SINGLE("Playback Gain Enable Switch",346 SPDIFOUT_CTRL1, 26, 1, 0),347 SOC_SINGLE("Playback Channels Mix Switch",348 SPDIFOUT_CTRL0, 23, 1, 0),349};350 351static int axg_spdifout_set_bias_level(struct snd_soc_component *component,352 enum snd_soc_bias_level level)353{354 struct axg_spdifout *priv = snd_soc_component_get_drvdata(component);355 enum snd_soc_bias_level now =356 snd_soc_component_get_bias_level(component);357 int ret = 0;358 359 switch (level) {360 case SND_SOC_BIAS_PREPARE:361 if (now == SND_SOC_BIAS_STANDBY)362 ret = clk_prepare_enable(priv->mclk);363 break;364 365 case SND_SOC_BIAS_STANDBY:366 if (now == SND_SOC_BIAS_PREPARE)367 clk_disable_unprepare(priv->mclk);368 break;369 370 case SND_SOC_BIAS_OFF:371 case SND_SOC_BIAS_ON:372 break;373 }374 375 return ret;376}377 378static const struct snd_soc_component_driver axg_spdifout_component_drv = {379 .controls = axg_spdifout_controls,380 .num_controls = ARRAY_SIZE(axg_spdifout_controls),381 .dapm_widgets = axg_spdifout_dapm_widgets,382 .num_dapm_widgets = ARRAY_SIZE(axg_spdifout_dapm_widgets),383 .dapm_routes = axg_spdifout_dapm_routes,384 .num_dapm_routes = ARRAY_SIZE(axg_spdifout_dapm_routes),385 .set_bias_level = axg_spdifout_set_bias_level,386 .legacy_dai_naming = 1,387};388 389static const struct regmap_config axg_spdifout_regmap_cfg = {390 .reg_bits = 32,391 .val_bits = 32,392 .reg_stride = 4,393 .max_register = SPDIFOUT_MUTE_VAL,394};395 396static const struct of_device_id axg_spdifout_of_match[] = {397 { .compatible = "amlogic,axg-spdifout", },398 {}399};400MODULE_DEVICE_TABLE(of, axg_spdifout_of_match);401 402static int axg_spdifout_probe(struct platform_device *pdev)403{404 struct device *dev = &pdev->dev;405 struct axg_spdifout *priv;406 void __iomem *regs;407 408 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);409 if (!priv)410 return -ENOMEM;411 platform_set_drvdata(pdev, priv);412 413 regs = devm_platform_ioremap_resource(pdev, 0);414 if (IS_ERR(regs))415 return PTR_ERR(regs);416 417 priv->map = devm_regmap_init_mmio(dev, regs, &axg_spdifout_regmap_cfg);418 if (IS_ERR(priv->map)) {419 dev_err(dev, "failed to init regmap: %ld\n",420 PTR_ERR(priv->map));421 return PTR_ERR(priv->map);422 }423 424 priv->pclk = devm_clk_get(dev, "pclk");425 if (IS_ERR(priv->pclk))426 return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get pclk\n");427 428 priv->mclk = devm_clk_get(dev, "mclk");429 if (IS_ERR(priv->mclk))430 return dev_err_probe(dev, PTR_ERR(priv->mclk), "failed to get mclk\n");431 432 return devm_snd_soc_register_component(dev, &axg_spdifout_component_drv,433 axg_spdifout_dai_drv, ARRAY_SIZE(axg_spdifout_dai_drv));434}435 436static struct platform_driver axg_spdifout_pdrv = {437 .probe = axg_spdifout_probe,438 .driver = {439 .name = "axg-spdifout",440 .of_match_table = axg_spdifout_of_match,441 },442};443module_platform_driver(axg_spdifout_pdrv);444 445MODULE_DESCRIPTION("Amlogic AXG SPDIF Output driver");446MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");447MODULE_LICENSE("GPL v2");448