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

TOMOYO Linux Cross Reference
Linux/sound/soc/zte/zx-tdm.c

Version: ~ [ linux-6.1-rc5 ] ~ [ linux-6.0.8 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.78 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.154 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.224 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.265 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.299 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.333 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.302 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ 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 /*
  2  * ZTE's TDM driver
  3  *
  4  * Copyright (C) 2017 ZTE Ltd
  5  *
  6  * Author: Baoyou Xie <baoyou.xie@linaro.org>
  7  *
  8  * License terms: GNU General Public License (GPL) version 2
  9  */
 10 
 11 #include <linux/clk.h>
 12 #include <linux/io.h>
 13 #include <linux/mfd/syscon.h>
 14 #include <linux/module.h>
 15 #include <sound/dmaengine_pcm.h>
 16 #include <sound/pcm_params.h>
 17 #include <sound/soc.h>
 18 #include <sound/soc-dai.h>
 19 
 20 #define REG_TIMING_CTRL         0x04
 21 #define REG_TX_FIFO_CTRL        0x0C
 22 #define REG_RX_FIFO_CTRL        0x10
 23 #define REG_INT_EN              0x1C
 24 #define REG_INT_STATUS          0x20
 25 #define REG_DATABUF             0x24
 26 #define REG_TS_MASK0            0x44
 27 #define REG_PROCESS_CTRL        0x54
 28 
 29 #define FIFO_CTRL_TX_RST        BIT(0)
 30 #define FIFO_CTRL_RX_RST        BIT(0)
 31 #define DEAGULT_FIFO_THRES      GENMASK(4, 2)
 32 
 33 #define FIFO_CTRL_TX_DMA_EN     BIT(1)
 34 #define FIFO_CTRL_RX_DMA_EN     BIT(1)
 35 
 36 #define TX_FIFO_RST_MASK        BIT(0)
 37 #define RX_FIFO_RST_MASK        BIT(0)
 38 
 39 #define FIFOCTRL_TX_FIFO_RST    BIT(0)
 40 #define FIFOCTRL_RX_FIFO_RST    BIT(0)
 41 
 42 #define TXTH_MASK               GENMASK(5, 2)
 43 #define RXTH_MASK               GENMASK(5, 2)
 44 
 45 #define FIFOCTRL_THRESHOLD(x)   ((x) << 2)
 46 
 47 #define TIMING_MS_MASK          BIT(1)
 48 /*
 49  * 00: 8 clk cycles every timeslot
 50  * 01: 16 clk cycles every timeslot
 51  * 10: 32 clk cycles every timeslot
 52  */
 53 #define TIMING_SYNC_WIDTH_MASK  GENMASK(6, 5)
 54 #define TIMING_WIDTH_SHIFT      5
 55 #define TIMING_DEFAULT_WIDTH    0
 56 #define TIMING_TS_WIDTH(x)      ((x) << TIMING_WIDTH_SHIFT)
 57 #define TIMING_WIDTH_FACTOR     8
 58 
 59 #define TIMING_MASTER_MODE      BIT(21)
 60 #define TIMING_LSB_FIRST        BIT(20)
 61 #define TIMING_TS_NUM(x)        (((x) - 1) << 7)
 62 #define TIMING_CLK_SEL_MASK     GENMASK(2, 0)
 63 #define TIMING_CLK_SEL_DEF      BIT(2)
 64 
 65 #define PROCESS_TX_EN           BIT(0)
 66 #define PROCESS_RX_EN           BIT(1)
 67 #define PROCESS_TDM_EN          BIT(2)
 68 #define PROCESS_DISABLE_ALL     0
 69 
 70 #define INT_DISABLE_ALL         0
 71 #define INT_STATUS_MASK         GENMASK(6, 0)
 72 
 73 struct zx_tdm_info {
 74         struct snd_dmaengine_dai_dma_data       dma_playback;
 75         struct snd_dmaengine_dai_dma_data       dma_capture;
 76         resource_size_t                         phy_addr;
 77         void __iomem                            *regbase;
 78         struct clk                              *dai_wclk;
 79         struct clk                              *dai_pclk;
 80         int                                     master;
 81         struct device                           *dev;
 82 };
 83 
 84 static inline u32 zx_tdm_readl(struct zx_tdm_info *tdm, u16 reg)
 85 {
 86         return readl_relaxed(tdm->regbase + reg);
 87 }
 88 
 89 static inline void zx_tdm_writel(struct zx_tdm_info *tdm, u16 reg, u32 val)
 90 {
 91         writel_relaxed(val, tdm->regbase + reg);
 92 }
 93 
 94 static void zx_tdm_tx_en(struct zx_tdm_info *tdm, bool on)
 95 {
 96         unsigned long val;
 97 
 98         val = zx_tdm_readl(tdm, REG_PROCESS_CTRL);
 99         if (on)
100                 val |= PROCESS_TX_EN | PROCESS_TDM_EN;
101         else
102                 val &= ~(PROCESS_TX_EN | PROCESS_TDM_EN);
103         zx_tdm_writel(tdm, REG_PROCESS_CTRL, val);
104 }
105 
106 static void zx_tdm_rx_en(struct zx_tdm_info *tdm, bool on)
107 {
108         unsigned long val;
109 
110         val = zx_tdm_readl(tdm, REG_PROCESS_CTRL);
111         if (on)
112                 val |= PROCESS_RX_EN | PROCESS_TDM_EN;
113         else
114                 val &= ~(PROCESS_RX_EN | PROCESS_TDM_EN);
115         zx_tdm_writel(tdm, REG_PROCESS_CTRL, val);
116 }
117 
118 static void zx_tdm_tx_dma_en(struct zx_tdm_info *tdm, bool on)
119 {
120         unsigned long val;
121 
122         val = zx_tdm_readl(tdm, REG_TX_FIFO_CTRL);
123         val |= FIFO_CTRL_TX_RST | DEAGULT_FIFO_THRES;
124         if (on)
125                 val |= FIFO_CTRL_TX_DMA_EN;
126         else
127                 val &= ~FIFO_CTRL_TX_DMA_EN;
128         zx_tdm_writel(tdm, REG_TX_FIFO_CTRL, val);
129 }
130 
131 static void zx_tdm_rx_dma_en(struct zx_tdm_info *tdm, bool on)
132 {
133         unsigned long val;
134 
135         val = zx_tdm_readl(tdm, REG_RX_FIFO_CTRL);
136         val |= FIFO_CTRL_RX_RST | DEAGULT_FIFO_THRES;
137         if (on)
138                 val |= FIFO_CTRL_RX_DMA_EN;
139         else
140                 val &= ~FIFO_CTRL_RX_DMA_EN;
141         zx_tdm_writel(tdm, REG_RX_FIFO_CTRL, val);
142 }
143 
144 #define ZX_TDM_RATES    (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000)
145 
146 #define ZX_TDM_FMTBIT \
147         (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_MU_LAW | \
148         SNDRV_PCM_FORMAT_A_LAW)
149 
150 static int zx_tdm_dai_probe(struct snd_soc_dai *dai)
151 {
152         struct zx_tdm_info *zx_tdm = dev_get_drvdata(dai->dev);
153 
154         snd_soc_dai_set_drvdata(dai, zx_tdm);
155         zx_tdm->dma_playback.addr = zx_tdm->phy_addr + REG_DATABUF;
156         zx_tdm->dma_playback.maxburst = 16;
157         zx_tdm->dma_capture.addr = zx_tdm->phy_addr + REG_DATABUF;
158         zx_tdm->dma_capture.maxburst = 16;
159         snd_soc_dai_init_dma_data(dai, &zx_tdm->dma_playback,
160                                   &zx_tdm->dma_capture);
161         return 0;
162 }
163 
164 static int zx_tdm_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
165 {
166         struct zx_tdm_info *tdm = snd_soc_dai_get_drvdata(cpu_dai);
167         unsigned long val;
168 
169         val = zx_tdm_readl(tdm, REG_TIMING_CTRL);
170         val &= ~(TIMING_SYNC_WIDTH_MASK | TIMING_MS_MASK);
171         val |= TIMING_DEFAULT_WIDTH << TIMING_WIDTH_SHIFT;
172 
173         switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
174         case SND_SOC_DAIFMT_CBM_CFM:
175                 tdm->master = 1;
176                 val |= TIMING_MASTER_MODE;
177                 break;
178         case SND_SOC_DAIFMT_CBS_CFS:
179                 tdm->master = 0;
180                 val &= ~TIMING_MASTER_MODE;
181                 break;
182         default:
183                 dev_err(cpu_dai->dev, "Unknown master/slave format\n");
184                 return -EINVAL;
185         }
186 
187 
188         zx_tdm_writel(tdm, REG_TIMING_CTRL, val);
189 
190         return 0;
191 }
192 
193 static int zx_tdm_hw_params(struct snd_pcm_substream *substream,
194                             struct snd_pcm_hw_params *params,
195                             struct snd_soc_dai *socdai)
196 {
197         struct zx_tdm_info *tdm = snd_soc_dai_get_drvdata(socdai);
198         struct snd_dmaengine_dai_dma_data *dma_data;
199         unsigned int ts_width = TIMING_DEFAULT_WIDTH;
200         unsigned int ch_num = 32;
201         unsigned int mask = 0;
202         unsigned int ret = 0;
203         unsigned long val;
204 
205         dma_data = snd_soc_dai_get_dma_data(socdai, substream);
206         dma_data->addr_width = ch_num >> 3;
207 
208         switch (params_format(params)) {
209         case SNDRV_PCM_FORMAT_MU_LAW:
210         case SNDRV_PCM_FORMAT_A_LAW:
211         case SNDRV_PCM_FORMAT_S16_LE:
212                 ts_width = 1;
213                 break;
214         default:
215                 ts_width = 0;
216                 dev_err(socdai->dev, "Unknown data format\n");
217                 return -EINVAL;
218         }
219 
220         val = zx_tdm_readl(tdm, REG_TIMING_CTRL);
221         val |= TIMING_TS_WIDTH(ts_width) | TIMING_TS_NUM(1);
222         zx_tdm_writel(tdm, REG_TIMING_CTRL, val);
223         zx_tdm_writel(tdm, REG_TS_MASK0, mask);
224 
225         if (tdm->master)
226                 ret = clk_set_rate(tdm->dai_wclk,
227                         params_rate(params) * TIMING_WIDTH_FACTOR * ch_num);
228 
229         return ret;
230 }
231 
232 static int zx_tdm_trigger(struct snd_pcm_substream *substream, int cmd,
233                           struct snd_soc_dai *dai)
234 {
235         int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
236         struct zx_tdm_info *zx_tdm = dev_get_drvdata(dai->dev);
237         unsigned int val;
238         int ret = 0;
239 
240         switch (cmd) {
241         case SNDRV_PCM_TRIGGER_START:
242                 if (capture) {
243                         val = zx_tdm_readl(zx_tdm, REG_RX_FIFO_CTRL);
244                         val |= FIFOCTRL_RX_FIFO_RST;
245                         zx_tdm_writel(zx_tdm, REG_RX_FIFO_CTRL, val);
246 
247                         zx_tdm_rx_dma_en(zx_tdm, true);
248                 } else {
249                         val = zx_tdm_readl(zx_tdm, REG_TX_FIFO_CTRL);
250                         val |= FIFOCTRL_TX_FIFO_RST;
251                         zx_tdm_writel(zx_tdm, REG_TX_FIFO_CTRL, val);
252 
253                         zx_tdm_tx_dma_en(zx_tdm, true);
254                 }
255                 break;
256         case SNDRV_PCM_TRIGGER_RESUME:
257         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
258                 if (capture)
259                         zx_tdm_rx_en(zx_tdm, true);
260                 else
261                         zx_tdm_tx_en(zx_tdm, true);
262                 break;
263         case SNDRV_PCM_TRIGGER_STOP:
264                 if (capture)
265                         zx_tdm_rx_dma_en(zx_tdm, false);
266                 else
267                         zx_tdm_tx_dma_en(zx_tdm, false);
268                 break;
269         case SNDRV_PCM_TRIGGER_SUSPEND:
270         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
271                 if (capture)
272                         zx_tdm_rx_en(zx_tdm, false);
273                 else
274                         zx_tdm_tx_en(zx_tdm, false);
275                 break;
276         default:
277                 ret = -EINVAL;
278                 break;
279         }
280 
281         return ret;
282 }
283 
284 static int zx_tdm_startup(struct snd_pcm_substream *substream,
285                           struct snd_soc_dai *dai)
286 {
287         struct zx_tdm_info *zx_tdm = dev_get_drvdata(dai->dev);
288         int ret;
289 
290         ret = clk_prepare_enable(zx_tdm->dai_wclk);
291         if (ret)
292                 return ret;
293 
294         ret = clk_prepare_enable(zx_tdm->dai_pclk);
295         if (ret) {
296                 clk_disable_unprepare(zx_tdm->dai_wclk);
297                 return ret;
298         }
299 
300         return 0;
301 }
302 
303 static void zx_tdm_shutdown(struct snd_pcm_substream *substream,
304                             struct snd_soc_dai *dai)
305 {
306         struct zx_tdm_info *zx_tdm = dev_get_drvdata(dai->dev);
307 
308         clk_disable_unprepare(zx_tdm->dai_pclk);
309         clk_disable_unprepare(zx_tdm->dai_wclk);
310 }
311 
312 static struct snd_soc_dai_ops zx_tdm_dai_ops = {
313         .trigger        = zx_tdm_trigger,
314         .hw_params      = zx_tdm_hw_params,
315         .set_fmt        = zx_tdm_set_fmt,
316         .startup        = zx_tdm_startup,
317         .shutdown       = zx_tdm_shutdown,
318 };
319 
320 static const struct snd_soc_component_driver zx_tdm_component = {
321         .name                   = "zx-tdm",
322 };
323 
324 static void zx_tdm_init_state(struct zx_tdm_info *tdm)
325 {
326         unsigned int val;
327 
328         zx_tdm_writel(tdm, REG_PROCESS_CTRL, PROCESS_DISABLE_ALL);
329 
330         val = zx_tdm_readl(tdm, REG_TIMING_CTRL);
331         val |= TIMING_LSB_FIRST;
332         val &= ~TIMING_CLK_SEL_MASK;
333         val |= TIMING_CLK_SEL_DEF;
334         zx_tdm_writel(tdm, REG_TIMING_CTRL, val);
335 
336         zx_tdm_writel(tdm, REG_INT_EN, INT_DISABLE_ALL);
337         /*
338          * write INT_STATUS register to clear it.
339          */
340         zx_tdm_writel(tdm, REG_INT_STATUS, INT_STATUS_MASK);
341         zx_tdm_writel(tdm, REG_RX_FIFO_CTRL, FIFOCTRL_RX_FIFO_RST);
342         zx_tdm_writel(tdm, REG_TX_FIFO_CTRL, FIFOCTRL_TX_FIFO_RST);
343 
344         val = zx_tdm_readl(tdm, REG_RX_FIFO_CTRL);
345         val &= ~(RXTH_MASK | RX_FIFO_RST_MASK);
346         val |= FIFOCTRL_THRESHOLD(8);
347         zx_tdm_writel(tdm, REG_RX_FIFO_CTRL, val);
348 
349         val = zx_tdm_readl(tdm, REG_TX_FIFO_CTRL);
350         val &= ~(TXTH_MASK | TX_FIFO_RST_MASK);
351         val |= FIFOCTRL_THRESHOLD(8);
352         zx_tdm_writel(tdm, REG_TX_FIFO_CTRL, val);
353 }
354 
355 static struct snd_soc_dai_driver zx_tdm_dai = {
356         .name   = "zx-tdm-dai",
357         .id     = 0,
358         .probe  = zx_tdm_dai_probe,
359         .playback   = {
360                 .channels_min   = 1,
361                 .channels_max   = 4,
362                 .rates          = ZX_TDM_RATES,
363                 .formats        = ZX_TDM_FMTBIT,
364         },
365         .capture = {
366                 .channels_min   = 1,
367                 .channels_max   = 4,
368                 .rates          = ZX_TDM_RATES,
369                 .formats        = ZX_TDM_FMTBIT,
370         },
371         .ops    = &zx_tdm_dai_ops,
372 };
373 
374 static int zx_tdm_probe(struct platform_device *pdev)
375 {
376         struct device *dev = &pdev->dev;
377         struct of_phandle_args out_args;
378         unsigned int dma_reg_offset;
379         struct zx_tdm_info *zx_tdm;
380         unsigned int dma_mask;
381         struct resource *res;
382         struct regmap *regmap_sysctrl;
383         int ret;
384 
385         zx_tdm = devm_kzalloc(&pdev->dev, sizeof(*zx_tdm), GFP_KERNEL);
386         if (!zx_tdm)
387                 return -ENOMEM;
388 
389         zx_tdm->dev = dev;
390 
391         zx_tdm->dai_wclk = devm_clk_get(&pdev->dev, "wclk");
392         if (IS_ERR(zx_tdm->dai_wclk)) {
393                 dev_err(&pdev->dev, "Fail to get wclk\n");
394                 return PTR_ERR(zx_tdm->dai_wclk);
395         }
396 
397         zx_tdm->dai_pclk = devm_clk_get(&pdev->dev, "pclk");
398         if (IS_ERR(zx_tdm->dai_pclk)) {
399                 dev_err(&pdev->dev, "Fail to get pclk\n");
400                 return PTR_ERR(zx_tdm->dai_pclk);
401         }
402 
403         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
404         zx_tdm->phy_addr = res->start;
405         zx_tdm->regbase = devm_ioremap_resource(&pdev->dev, res);
406         if (IS_ERR(zx_tdm->regbase))
407                 return PTR_ERR(zx_tdm->regbase);
408 
409         ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
410                                 "zte,tdm-dma-sysctrl", 2, 0, &out_args);
411         if (ret) {
412                 dev_err(&pdev->dev, "Fail to get zte,tdm-dma-sysctrl\n");
413                 return ret;
414         }
415 
416         dma_reg_offset = out_args.args[0];
417         dma_mask = out_args.args[1];
418         regmap_sysctrl = syscon_node_to_regmap(out_args.np);
419         if (IS_ERR(regmap_sysctrl)) {
420                 of_node_put(out_args.np);
421                 return PTR_ERR(regmap_sysctrl);
422         }
423 
424         regmap_update_bits(regmap_sysctrl, dma_reg_offset, dma_mask, dma_mask);
425         of_node_put(out_args.np);
426 
427         zx_tdm_init_state(zx_tdm);
428         platform_set_drvdata(pdev, zx_tdm);
429 
430         ret = devm_snd_soc_register_component(&pdev->dev, &zx_tdm_component,
431                                                 &zx_tdm_dai, 1);
432         if (ret) {
433                 dev_err(&pdev->dev, "Register DAI failed: %d\n", ret);
434                 return ret;
435         }
436 
437         ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
438         if (ret)
439                 dev_err(&pdev->dev, "Register platform PCM failed: %d\n", ret);
440 
441         return ret;
442 }
443 
444 static const struct of_device_id zx_tdm_dt_ids[] = {
445         { .compatible = "zte,zx296718-tdm", },
446         {}
447 };
448 MODULE_DEVICE_TABLE(of, zx_tdm_dt_ids);
449 
450 static struct platform_driver tdm_driver = {
451         .probe = zx_tdm_probe,
452         .driver = {
453                 .name = "zx-tdm",
454                 .of_match_table = zx_tdm_dt_ids,
455         },
456 };
457 module_platform_driver(tdm_driver);
458 
459 MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>");
460 MODULE_DESCRIPTION("ZTE TDM DAI driver");
461 MODULE_LICENSE("GPL v2");
462 

~ [ 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