0001
0002
0003
0004
0005
0006
0007 #include <linux/init.h>
0008 #include <linux/kernel.h>
0009 #include <linux/module.h>
0010 #include <linux/platform_device.h>
0011 #include <linux/slab.h>
0012 #include <linux/of.h>
0013 #include <linux/clk.h>
0014 #include <linux/regmap.h>
0015
0016 #include <sound/core.h>
0017 #include <sound/pcm.h>
0018 #include <sound/pcm_params.h>
0019 #include <sound/soc.h>
0020 #include <sound/initval.h>
0021 #include <sound/dmaengine_pcm.h>
0022
0023 #define AXI_SPDIF_REG_CTRL 0x0
0024 #define AXI_SPDIF_REG_STAT 0x4
0025 #define AXI_SPDIF_REG_TX_FIFO 0xc
0026
0027 #define AXI_SPDIF_CTRL_TXDATA BIT(1)
0028 #define AXI_SPDIF_CTRL_TXEN BIT(0)
0029 #define AXI_SPDIF_CTRL_CLKDIV_OFFSET 8
0030 #define AXI_SPDIF_CTRL_CLKDIV_MASK (0xff << 8)
0031
0032 #define AXI_SPDIF_FREQ_44100 (0x0 << 6)
0033 #define AXI_SPDIF_FREQ_48000 (0x1 << 6)
0034 #define AXI_SPDIF_FREQ_32000 (0x2 << 6)
0035 #define AXI_SPDIF_FREQ_NA (0x3 << 6)
0036
0037 struct axi_spdif {
0038 struct regmap *regmap;
0039 struct clk *clk;
0040 struct clk *clk_ref;
0041
0042 struct snd_dmaengine_dai_dma_data dma_data;
0043
0044 struct snd_ratnum ratnum;
0045 struct snd_pcm_hw_constraint_ratnums rate_constraints;
0046 };
0047
0048 static int axi_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
0049 struct snd_soc_dai *dai)
0050 {
0051 struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
0052 unsigned int val;
0053
0054 switch (cmd) {
0055 case SNDRV_PCM_TRIGGER_START:
0056 case SNDRV_PCM_TRIGGER_RESUME:
0057 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
0058 val = AXI_SPDIF_CTRL_TXDATA;
0059 break;
0060 case SNDRV_PCM_TRIGGER_STOP:
0061 case SNDRV_PCM_TRIGGER_SUSPEND:
0062 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
0063 val = 0;
0064 break;
0065 default:
0066 return -EINVAL;
0067 }
0068
0069 regmap_update_bits(spdif->regmap, AXI_SPDIF_REG_CTRL,
0070 AXI_SPDIF_CTRL_TXDATA, val);
0071
0072 return 0;
0073 }
0074
0075 static int axi_spdif_hw_params(struct snd_pcm_substream *substream,
0076 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
0077 {
0078 struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
0079 unsigned int rate = params_rate(params);
0080 unsigned int clkdiv, stat;
0081
0082 switch (params_rate(params)) {
0083 case 32000:
0084 stat = AXI_SPDIF_FREQ_32000;
0085 break;
0086 case 44100:
0087 stat = AXI_SPDIF_FREQ_44100;
0088 break;
0089 case 48000:
0090 stat = AXI_SPDIF_FREQ_48000;
0091 break;
0092 default:
0093 stat = AXI_SPDIF_FREQ_NA;
0094 break;
0095 }
0096
0097 clkdiv = DIV_ROUND_CLOSEST(clk_get_rate(spdif->clk_ref),
0098 rate * 64 * 2) - 1;
0099 clkdiv <<= AXI_SPDIF_CTRL_CLKDIV_OFFSET;
0100
0101 regmap_write(spdif->regmap, AXI_SPDIF_REG_STAT, stat);
0102 regmap_update_bits(spdif->regmap, AXI_SPDIF_REG_CTRL,
0103 AXI_SPDIF_CTRL_CLKDIV_MASK, clkdiv);
0104
0105 return 0;
0106 }
0107
0108 static int axi_spdif_dai_probe(struct snd_soc_dai *dai)
0109 {
0110 struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
0111
0112 snd_soc_dai_init_dma_data(dai, &spdif->dma_data, NULL);
0113
0114 return 0;
0115 }
0116
0117 static int axi_spdif_startup(struct snd_pcm_substream *substream,
0118 struct snd_soc_dai *dai)
0119 {
0120 struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
0121 int ret;
0122
0123 ret = snd_pcm_hw_constraint_ratnums(substream->runtime, 0,
0124 SNDRV_PCM_HW_PARAM_RATE,
0125 &spdif->rate_constraints);
0126 if (ret)
0127 return ret;
0128
0129 ret = clk_prepare_enable(spdif->clk_ref);
0130 if (ret)
0131 return ret;
0132
0133 regmap_update_bits(spdif->regmap, AXI_SPDIF_REG_CTRL,
0134 AXI_SPDIF_CTRL_TXEN, AXI_SPDIF_CTRL_TXEN);
0135
0136 return 0;
0137 }
0138
0139 static void axi_spdif_shutdown(struct snd_pcm_substream *substream,
0140 struct snd_soc_dai *dai)
0141 {
0142 struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
0143
0144 regmap_update_bits(spdif->regmap, AXI_SPDIF_REG_CTRL,
0145 AXI_SPDIF_CTRL_TXEN, 0);
0146
0147 clk_disable_unprepare(spdif->clk_ref);
0148 }
0149
0150 static const struct snd_soc_dai_ops axi_spdif_dai_ops = {
0151 .startup = axi_spdif_startup,
0152 .shutdown = axi_spdif_shutdown,
0153 .trigger = axi_spdif_trigger,
0154 .hw_params = axi_spdif_hw_params,
0155 };
0156
0157 static struct snd_soc_dai_driver axi_spdif_dai = {
0158 .probe = axi_spdif_dai_probe,
0159 .playback = {
0160 .channels_min = 2,
0161 .channels_max = 2,
0162 .rates = SNDRV_PCM_RATE_KNOT,
0163 .formats = SNDRV_PCM_FMTBIT_S16_LE,
0164 },
0165 .ops = &axi_spdif_dai_ops,
0166 };
0167
0168 static const struct snd_soc_component_driver axi_spdif_component = {
0169 .name = "axi-spdif",
0170 .legacy_dai_naming = 1,
0171 };
0172
0173 static const struct regmap_config axi_spdif_regmap_config = {
0174 .reg_bits = 32,
0175 .reg_stride = 4,
0176 .val_bits = 32,
0177 .max_register = AXI_SPDIF_REG_STAT,
0178 };
0179
0180 static int axi_spdif_probe(struct platform_device *pdev)
0181 {
0182 struct axi_spdif *spdif;
0183 struct resource *res;
0184 void __iomem *base;
0185 int ret;
0186
0187 spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
0188 if (!spdif)
0189 return -ENOMEM;
0190
0191 platform_set_drvdata(pdev, spdif);
0192
0193 base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
0194 if (IS_ERR(base))
0195 return PTR_ERR(base);
0196
0197 spdif->regmap = devm_regmap_init_mmio(&pdev->dev, base,
0198 &axi_spdif_regmap_config);
0199 if (IS_ERR(spdif->regmap))
0200 return PTR_ERR(spdif->regmap);
0201
0202 spdif->clk = devm_clk_get(&pdev->dev, "axi");
0203 if (IS_ERR(spdif->clk))
0204 return PTR_ERR(spdif->clk);
0205
0206 spdif->clk_ref = devm_clk_get(&pdev->dev, "ref");
0207 if (IS_ERR(spdif->clk_ref))
0208 return PTR_ERR(spdif->clk_ref);
0209
0210 ret = clk_prepare_enable(spdif->clk);
0211 if (ret)
0212 return ret;
0213
0214 spdif->dma_data.addr = res->start + AXI_SPDIF_REG_TX_FIFO;
0215 spdif->dma_data.addr_width = 4;
0216 spdif->dma_data.maxburst = 1;
0217
0218 spdif->ratnum.num = clk_get_rate(spdif->clk_ref) / 128;
0219 spdif->ratnum.den_step = 1;
0220 spdif->ratnum.den_min = 1;
0221 spdif->ratnum.den_max = 64;
0222
0223 spdif->rate_constraints.rats = &spdif->ratnum;
0224 spdif->rate_constraints.nrats = 1;
0225
0226 ret = devm_snd_soc_register_component(&pdev->dev, &axi_spdif_component,
0227 &axi_spdif_dai, 1);
0228 if (ret)
0229 goto err_clk_disable;
0230
0231 ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
0232 if (ret)
0233 goto err_clk_disable;
0234
0235 return 0;
0236
0237 err_clk_disable:
0238 clk_disable_unprepare(spdif->clk);
0239 return ret;
0240 }
0241
0242 static int axi_spdif_dev_remove(struct platform_device *pdev)
0243 {
0244 struct axi_spdif *spdif = platform_get_drvdata(pdev);
0245
0246 clk_disable_unprepare(spdif->clk);
0247
0248 return 0;
0249 }
0250
0251 static const struct of_device_id axi_spdif_of_match[] = {
0252 { .compatible = "adi,axi-spdif-tx-1.00.a", },
0253 {},
0254 };
0255 MODULE_DEVICE_TABLE(of, axi_spdif_of_match);
0256
0257 static struct platform_driver axi_spdif_driver = {
0258 .driver = {
0259 .name = "axi-spdif",
0260 .of_match_table = axi_spdif_of_match,
0261 },
0262 .probe = axi_spdif_probe,
0263 .remove = axi_spdif_dev_remove,
0264 };
0265 module_platform_driver(axi_spdif_driver);
0266
0267 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
0268 MODULE_DESCRIPTION("AXI SPDIF driver");
0269 MODULE_LICENSE("GPL");