root/sound/soc/adi/axi-spdif.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. axi_spdif_trigger
  2. axi_spdif_hw_params
  3. axi_spdif_dai_probe
  4. axi_spdif_startup
  5. axi_spdif_shutdown
  6. axi_spdif_probe
  7. axi_spdif_dev_remove

   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");

/* [<][>][^][v][top][bottom][index][help] */