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

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

Version: ~ [ linux-5.10-rc5 ] ~ [ linux-5.9.10 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.79 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.159 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.208 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.245 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.245 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.85 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ 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.5 ] ~ [ 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         .startup = axi_spdif_startup,
152         .shutdown = axi_spdif_shutdown,
153         .trigger = axi_spdif_trigger,
154         .hw_params = axi_spdif_hw_params,
155 };
156 
157 static struct snd_soc_dai_driver axi_spdif_dai = {
158         .probe = axi_spdif_dai_probe,
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 };
171 
172 static const struct regmap_config axi_spdif_regmap_config = {
173         .reg_bits = 32,
174         .reg_stride = 4,
175         .val_bits = 32,
176         .max_register = AXI_SPDIF_REG_STAT,
177 };
178 
179 static int axi_spdif_probe(struct platform_device *pdev)
180 {
181         struct axi_spdif *spdif;
182         struct resource *res;
183         void __iomem *base;
184         int ret;
185 
186         spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
187         if (!spdif)
188                 return -ENOMEM;
189 
190         platform_set_drvdata(pdev, spdif);
191 
192         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
193         base = devm_ioremap_resource(&pdev->dev, 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 int 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         return 0;
249 }
250 
251 static const struct of_device_id axi_spdif_of_match[] = {
252         { .compatible = "adi,axi-spdif-tx-1.00.a", },
253         {},
254 };
255 MODULE_DEVICE_TABLE(of, axi_spdif_of_match);
256 
257 static struct platform_driver axi_spdif_driver = {
258         .driver = {
259                 .name = "axi-spdif",
260                 .of_match_table = axi_spdif_of_match,
261         },
262         .probe = axi_spdif_probe,
263         .remove = axi_spdif_dev_remove,
264 };
265 module_platform_driver(axi_spdif_driver);
266 
267 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
268 MODULE_DESCRIPTION("AXI SPDIF driver");
269 MODULE_LICENSE("GPL");
270 

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

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | 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.

osdn.jp