~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/adi/axi-spdif.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * Copyright (C) 2012-2013, Analog Devices Inc.
  4  * Author: Lars-Peter Clausen <lars@metafoo.de>
  5  */
  6 
  7 #include <linux/init.h>
  8 #include <linux/kernel.h>
  9 #include <linux/module.h>
 10 #include <linux/platform_device.h>
 11 #include <linux/slab.h>
 12 #include <linux/of.h>
 13 #include <linux/clk.h>
 14 #include <linux/regmap.h>
 15 
 16 #include <sound/core.h>
 17 #include <sound/pcm.h>
 18 #include <sound/pcm_params.h>
 19 #include <sound/soc.h>
 20 #include <sound/initval.h>
 21 #include <sound/dmaengine_pcm.h>
 22 
 23 #define AXI_SPDIF_REG_CTRL      0x0
 24 #define AXI_SPDIF_REG_STAT      0x4
 25 #define AXI_SPDIF_REG_TX_FIFO   0xc
 26 
 27 #define AXI_SPDIF_CTRL_TXDATA BIT(1)
 28 #define AXI_SPDIF_CTRL_TXEN BIT(0)
 29 #define AXI_SPDIF_CTRL_CLKDIV_OFFSET 8
 30 #define AXI_SPDIF_CTRL_CLKDIV_MASK (0xff << 8)
 31 
 32 #define AXI_SPDIF_FREQ_44100    (0x0 << 6)
 33 #define AXI_SPDIF_FREQ_48000    (0x1 << 6)
 34 #define AXI_SPDIF_FREQ_32000    (0x2 << 6)
 35 #define AXI_SPDIF_FREQ_NA       (0x3 << 6)
 36 
 37 struct axi_spdif {
 38         struct regmap *regmap;
 39         struct clk *clk;
 40         struct clk *clk_ref;
 41 
 42         struct snd_dmaengine_dai_dma_data dma_data;
 43 
 44         struct snd_ratnum ratnum;
 45         struct snd_pcm_hw_constraint_ratnums rate_constraints;
 46 };
 47 
 48 static int axi_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
 49         struct snd_soc_dai *dai)
 50 {
 51         struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
 52         unsigned int val;
 53 
 54         switch (cmd) {
 55         case SNDRV_PCM_TRIGGER_START:
 56         case SNDRV_PCM_TRIGGER_RESUME:
 57         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 58                 val = AXI_SPDIF_CTRL_TXDATA;
 59                 break;
 60         case SNDRV_PCM_TRIGGER_STOP:
 61         case SNDRV_PCM_TRIGGER_SUSPEND:
 62         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 63                 val = 0;
 64                 break;
 65         default:
 66                 return -EINVAL;
 67         }
 68 
 69         regmap_update_bits(spdif->regmap, AXI_SPDIF_REG_CTRL,
 70                 AXI_SPDIF_CTRL_TXDATA, val);
 71 
 72         return 0;
 73 }
 74 
 75 static int axi_spdif_hw_params(struct snd_pcm_substream *substream,
 76         struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 77 {
 78         struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
 79         unsigned int rate = params_rate(params);
 80         unsigned int clkdiv, stat;
 81 
 82         switch (params_rate(params)) {
 83         case 32000:
 84                 stat = AXI_SPDIF_FREQ_32000;
 85                 break;
 86         case 44100:
 87                 stat = AXI_SPDIF_FREQ_44100;
 88                 break;
 89         case 48000:
 90                 stat = AXI_SPDIF_FREQ_48000;
 91                 break;
 92         default:
 93                 stat = AXI_SPDIF_FREQ_NA;
 94                 break;
 95         }
 96 
 97         clkdiv = DIV_ROUND_CLOSEST(clk_get_rate(spdif->clk_ref),
 98                         rate * 64 * 2) - 1;
 99         clkdiv <<= AXI_SPDIF_CTRL_CLKDIV_OFFSET;
100 
101         regmap_write(spdif->regmap, AXI_SPDIF_REG_STAT, stat);
102         regmap_update_bits(spdif->regmap, AXI_SPDIF_REG_CTRL,
103                 AXI_SPDIF_CTRL_CLKDIV_MASK, clkdiv);
104 
105         return 0;
106 }
107 
108 static int axi_spdif_dai_probe(struct snd_soc_dai *dai)
109 {
110         struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
111 
112         snd_soc_dai_init_dma_data(dai, &spdif->dma_data, NULL);
113 
114         return 0;
115 }
116 
117 static int axi_spdif_startup(struct snd_pcm_substream *substream,
118         struct snd_soc_dai *dai)
119 {
120         struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
121         int ret;
122 
123         ret = snd_pcm_hw_constraint_ratnums(substream->runtime, 0,
124                            SNDRV_PCM_HW_PARAM_RATE,
125                            &spdif->rate_constraints);
126         if (ret)
127                 return ret;
128 
129         ret = clk_prepare_enable(spdif->clk_ref);
130         if (ret)
131                 return ret;
132 
133         regmap_update_bits(spdif->regmap, AXI_SPDIF_REG_CTRL,
134                 AXI_SPDIF_CTRL_TXEN, AXI_SPDIF_CTRL_TXEN);
135 
136         return 0;
137 }
138 
139 static void axi_spdif_shutdown(struct snd_pcm_substream *substream,
140         struct snd_soc_dai *dai)
141 {
142         struct axi_spdif *spdif = snd_soc_dai_get_drvdata(dai);
143 
144         regmap_update_bits(spdif->regmap, AXI_SPDIF_REG_CTRL,
145                 AXI_SPDIF_CTRL_TXEN, 0);
146 
147         clk_disable_unprepare(spdif->clk_ref);
148 }
149 
150 static const struct snd_soc_dai_ops axi_spdif_dai_ops = {
151         .probe = axi_spdif_dai_probe,
152         .startup = axi_spdif_startup,
153         .shutdown = axi_spdif_shutdown,
154         .trigger = axi_spdif_trigger,
155         .hw_params = axi_spdif_hw_params,
156 };
157 
158 static struct snd_soc_dai_driver axi_spdif_dai = {
159         .playback = {
160                 .channels_min = 2,
161                 .channels_max = 2,
162                 .rates = SNDRV_PCM_RATE_KNOT,
163                 .formats = SNDRV_PCM_FMTBIT_S16_LE,
164         },
165         .ops = &axi_spdif_dai_ops,
166 };
167 
168 static const struct snd_soc_component_driver axi_spdif_component = {
169         .name = "axi-spdif",
170         .legacy_dai_naming = 1,
171 };
172 
173 static const struct regmap_config axi_spdif_regmap_config = {
174         .reg_bits = 32,
175         .reg_stride = 4,
176         .val_bits = 32,
177         .max_register = AXI_SPDIF_REG_STAT,
178 };
179 
180 static int axi_spdif_probe(struct platform_device *pdev)
181 {
182         struct axi_spdif *spdif;
183         struct resource *res;
184         void __iomem *base;
185         int ret;
186 
187         spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
188         if (!spdif)
189                 return -ENOMEM;
190 
191         platform_set_drvdata(pdev, spdif);
192 
193         base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
194         if (IS_ERR(base))
195                 return PTR_ERR(base);
196 
197         spdif->regmap = devm_regmap_init_mmio(&pdev->dev, base,
198                                             &axi_spdif_regmap_config);
199         if (IS_ERR(spdif->regmap))
200                 return PTR_ERR(spdif->regmap);
201 
202         spdif->clk = devm_clk_get(&pdev->dev, "axi");
203         if (IS_ERR(spdif->clk))
204                 return PTR_ERR(spdif->clk);
205 
206         spdif->clk_ref = devm_clk_get(&pdev->dev, "ref");
207         if (IS_ERR(spdif->clk_ref))
208                 return PTR_ERR(spdif->clk_ref);
209 
210         ret = clk_prepare_enable(spdif->clk);
211         if (ret)
212                 return ret;
213 
214         spdif->dma_data.addr = res->start + AXI_SPDIF_REG_TX_FIFO;
215         spdif->dma_data.addr_width = 4;
216         spdif->dma_data.maxburst = 1;
217 
218         spdif->ratnum.num = clk_get_rate(spdif->clk_ref) / 128;
219         spdif->ratnum.den_step = 1;
220         spdif->ratnum.den_min = 1;
221         spdif->ratnum.den_max = 64;
222 
223         spdif->rate_constraints.rats = &spdif->ratnum;
224         spdif->rate_constraints.nrats = 1;
225 
226         ret = devm_snd_soc_register_component(&pdev->dev, &axi_spdif_component,
227                                          &axi_spdif_dai, 1);
228         if (ret)
229                 goto err_clk_disable;
230 
231         ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
232         if (ret)
233                 goto err_clk_disable;
234 
235         return 0;
236 
237 err_clk_disable:
238         clk_disable_unprepare(spdif->clk);
239         return ret;
240 }
241 
242 static void axi_spdif_dev_remove(struct platform_device *pdev)
243 {
244         struct axi_spdif *spdif = platform_get_drvdata(pdev);
245 
246         clk_disable_unprepare(spdif->clk);
247 }
248 
249 static const struct of_device_id axi_spdif_of_match[] = {
250         { .compatible = "adi,axi-spdif-tx-1.00.a", },
251         {},
252 };
253 MODULE_DEVICE_TABLE(of, axi_spdif_of_match);
254 
255 static struct platform_driver axi_spdif_driver = {
256         .driver = {
257                 .name = "axi-spdif",
258                 .of_match_table = axi_spdif_of_match,
259         },
260         .probe = axi_spdif_probe,
261         .remove_new = axi_spdif_dev_remove,
262 };
263 module_platform_driver(axi_spdif_driver);
264 
265 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
266 MODULE_DESCRIPTION("AXI SPDIF driver");
267 MODULE_LICENSE("GPL");
268 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php