root/sound/soc/spear/spdif_out.c

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

DEFINITIONS

This source file includes following definitions.
  1. spdif_out_configure
  2. spdif_out_startup
  3. spdif_out_shutdown
  4. spdif_out_clock
  5. spdif_out_hw_params
  6. spdif_out_trigger
  7. spdif_digital_mute
  8. spdif_mute_get
  9. spdif_mute_put
  10. spdif_soc_dai_probe
  11. spdif_out_probe
  12. spdif_out_suspend
  13. spdif_out_resume

   1 /*
   2  * ALSA SoC SPDIF Out Audio Layer for spear processors
   3  *
   4  * Copyright (C) 2012 ST Microelectronics
   5  * Vipin Kumar <vipin.kumar@st.com>
   6  *
   7  * This file is licensed under the terms of the GNU General Public
   8  * License version 2. This program is licensed "as is" without any
   9  * warranty of any kind, whether express or implied.
  10  */
  11 
  12 #include <linux/clk.h>
  13 #include <linux/delay.h>
  14 #include <linux/device.h>
  15 #include <linux/kernel.h>
  16 #include <linux/init.h>
  17 #include <linux/io.h>
  18 #include <linux/ioport.h>
  19 #include <linux/module.h>
  20 #include <linux/platform_device.h>
  21 #include <sound/dmaengine_pcm.h>
  22 #include <sound/soc.h>
  23 #include <sound/spear_dma.h>
  24 #include <sound/spear_spdif.h>
  25 #include "spdif_out_regs.h"
  26 #include "spear_pcm.h"
  27 
  28 struct spdif_out_params {
  29         u32 rate;
  30         u32 core_freq;
  31         u32 mute;
  32 };
  33 
  34 struct spdif_out_dev {
  35         struct clk *clk;
  36         struct spear_dma_data dma_params;
  37         struct spdif_out_params saved_params;
  38         u32 running;
  39         void __iomem *io_base;
  40         struct snd_dmaengine_dai_dma_data dma_params_tx;
  41         struct snd_dmaengine_pcm_config config;
  42 };
  43 
  44 static void spdif_out_configure(struct spdif_out_dev *host)
  45 {
  46         writel(SPDIF_OUT_RESET, host->io_base + SPDIF_OUT_SOFT_RST);
  47         mdelay(1);
  48         writel(readl(host->io_base + SPDIF_OUT_SOFT_RST) & ~SPDIF_OUT_RESET,
  49                         host->io_base + SPDIF_OUT_SOFT_RST);
  50 
  51         writel(SPDIF_OUT_FDMA_TRIG_16 | SPDIF_OUT_MEMFMT_16_16 |
  52                         SPDIF_OUT_VALID_HW | SPDIF_OUT_USER_HW |
  53                         SPDIF_OUT_CHNLSTA_HW | SPDIF_OUT_PARITY_HW,
  54                         host->io_base + SPDIF_OUT_CFG);
  55 
  56         writel(0x7F, host->io_base + SPDIF_OUT_INT_STA_CLR);
  57         writel(0x7F, host->io_base + SPDIF_OUT_INT_EN_CLR);
  58 }
  59 
  60 static int spdif_out_startup(struct snd_pcm_substream *substream,
  61                 struct snd_soc_dai *cpu_dai)
  62 {
  63         struct spdif_out_dev *host = snd_soc_dai_get_drvdata(cpu_dai);
  64         int ret;
  65 
  66         if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
  67                 return -EINVAL;
  68 
  69         ret = clk_enable(host->clk);
  70         if (ret)
  71                 return ret;
  72 
  73         host->running = true;
  74         spdif_out_configure(host);
  75 
  76         return 0;
  77 }
  78 
  79 static void spdif_out_shutdown(struct snd_pcm_substream *substream,
  80                 struct snd_soc_dai *dai)
  81 {
  82         struct spdif_out_dev *host = snd_soc_dai_get_drvdata(dai);
  83 
  84         if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
  85                 return;
  86 
  87         clk_disable(host->clk);
  88         host->running = false;
  89 }
  90 
  91 static void spdif_out_clock(struct spdif_out_dev *host, u32 core_freq,
  92                 u32 rate)
  93 {
  94         u32 divider, ctrl;
  95 
  96         clk_set_rate(host->clk, core_freq);
  97         divider = DIV_ROUND_CLOSEST(clk_get_rate(host->clk), (rate * 128));
  98 
  99         ctrl = readl(host->io_base + SPDIF_OUT_CTRL);
 100         ctrl &= ~SPDIF_DIVIDER_MASK;
 101         ctrl |= (divider << SPDIF_DIVIDER_SHIFT) & SPDIF_DIVIDER_MASK;
 102         writel(ctrl, host->io_base + SPDIF_OUT_CTRL);
 103 }
 104 
 105 static int spdif_out_hw_params(struct snd_pcm_substream *substream,
 106                 struct snd_pcm_hw_params *params,
 107                 struct snd_soc_dai *dai)
 108 {
 109         struct spdif_out_dev *host = snd_soc_dai_get_drvdata(dai);
 110         u32 rate, core_freq;
 111 
 112         if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
 113                 return -EINVAL;
 114 
 115         rate = params_rate(params);
 116 
 117         switch (rate) {
 118         case 8000:
 119         case 16000:
 120         case 32000:
 121         case 64000:
 122                 /*
 123                  * The clock is multiplied by 10 to bring it to feasible range
 124                  * of frequencies for sscg
 125                  */
 126                 core_freq = 64000 * 128 * 10;   /* 81.92 MHz */
 127                 break;
 128         case 5512:
 129         case 11025:
 130         case 22050:
 131         case 44100:
 132         case 88200:
 133         case 176400:
 134                 core_freq = 176400 * 128;       /* 22.5792 MHz */
 135                 break;
 136         case 48000:
 137         case 96000:
 138         case 192000:
 139         default:
 140                 core_freq = 192000 * 128;       /* 24.576 MHz */
 141                 break;
 142         }
 143 
 144         spdif_out_clock(host, core_freq, rate);
 145         host->saved_params.core_freq = core_freq;
 146         host->saved_params.rate = rate;
 147 
 148         return 0;
 149 }
 150 
 151 static int spdif_out_trigger(struct snd_pcm_substream *substream, int cmd,
 152                 struct snd_soc_dai *dai)
 153 {
 154         struct spdif_out_dev *host = snd_soc_dai_get_drvdata(dai);
 155         u32 ctrl;
 156         int ret = 0;
 157 
 158         if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
 159                 return -EINVAL;
 160 
 161         switch (cmd) {
 162         case SNDRV_PCM_TRIGGER_START:
 163         case SNDRV_PCM_TRIGGER_RESUME:
 164         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 165                         ctrl = readl(host->io_base + SPDIF_OUT_CTRL);
 166                         ctrl &= ~SPDIF_OPMODE_MASK;
 167                         if (!host->saved_params.mute)
 168                                 ctrl |= SPDIF_OPMODE_AUD_DATA |
 169                                         SPDIF_STATE_NORMAL;
 170                         else
 171                                 ctrl |= SPDIF_OPMODE_MUTE_PCM;
 172                         writel(ctrl, host->io_base + SPDIF_OUT_CTRL);
 173                 break;
 174 
 175         case SNDRV_PCM_TRIGGER_STOP:
 176         case SNDRV_PCM_TRIGGER_SUSPEND:
 177         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 178                 ctrl = readl(host->io_base + SPDIF_OUT_CTRL);
 179                 ctrl &= ~SPDIF_OPMODE_MASK;
 180                 ctrl |= SPDIF_OPMODE_OFF;
 181                 writel(ctrl, host->io_base + SPDIF_OUT_CTRL);
 182                 break;
 183 
 184         default:
 185                 ret = -EINVAL;
 186                 break;
 187         }
 188         return ret;
 189 }
 190 
 191 static int spdif_digital_mute(struct snd_soc_dai *dai, int mute)
 192 {
 193         struct spdif_out_dev *host = snd_soc_dai_get_drvdata(dai);
 194         u32 val;
 195 
 196         host->saved_params.mute = mute;
 197         val = readl(host->io_base + SPDIF_OUT_CTRL);
 198         val &= ~SPDIF_OPMODE_MASK;
 199 
 200         if (mute)
 201                 val |= SPDIF_OPMODE_MUTE_PCM;
 202         else {
 203                 if (host->running)
 204                         val |= SPDIF_OPMODE_AUD_DATA | SPDIF_STATE_NORMAL;
 205                 else
 206                         val |= SPDIF_OPMODE_OFF;
 207         }
 208 
 209         writel(val, host->io_base + SPDIF_OUT_CTRL);
 210         return 0;
 211 }
 212 
 213 static int spdif_mute_get(struct snd_kcontrol *kcontrol,
 214                 struct snd_ctl_elem_value *ucontrol)
 215 {
 216         struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
 217         struct spdif_out_dev *host = snd_soc_dai_get_drvdata(cpu_dai);
 218 
 219         ucontrol->value.integer.value[0] = host->saved_params.mute;
 220         return 0;
 221 }
 222 
 223 static int spdif_mute_put(struct snd_kcontrol *kcontrol,
 224                 struct snd_ctl_elem_value *ucontrol)
 225 {
 226         struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
 227         struct spdif_out_dev *host = snd_soc_dai_get_drvdata(cpu_dai);
 228 
 229         if (host->saved_params.mute == ucontrol->value.integer.value[0])
 230                 return 0;
 231 
 232         spdif_digital_mute(cpu_dai, ucontrol->value.integer.value[0]);
 233 
 234         return 1;
 235 }
 236 static const struct snd_kcontrol_new spdif_out_controls[] = {
 237         SOC_SINGLE_BOOL_EXT("IEC958 Playback Switch", 0,
 238                         spdif_mute_get, spdif_mute_put),
 239 };
 240 
 241 static int spdif_soc_dai_probe(struct snd_soc_dai *dai)
 242 {
 243         struct spdif_out_dev *host = snd_soc_dai_get_drvdata(dai);
 244 
 245         host->dma_params_tx.filter_data = &host->dma_params;
 246         dai->playback_dma_data = &host->dma_params_tx;
 247 
 248         return snd_soc_add_dai_controls(dai, spdif_out_controls,
 249                                 ARRAY_SIZE(spdif_out_controls));
 250 }
 251 
 252 static const struct snd_soc_dai_ops spdif_out_dai_ops = {
 253         .digital_mute   = spdif_digital_mute,
 254         .startup        = spdif_out_startup,
 255         .shutdown       = spdif_out_shutdown,
 256         .trigger        = spdif_out_trigger,
 257         .hw_params      = spdif_out_hw_params,
 258 };
 259 
 260 static struct snd_soc_dai_driver spdif_out_dai = {
 261         .playback = {
 262                 .channels_min = 2,
 263                 .channels_max = 2,
 264                 .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
 265                                  SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \
 266                                  SNDRV_PCM_RATE_192000),
 267                 .formats = SNDRV_PCM_FMTBIT_S16_LE,
 268         },
 269         .probe = spdif_soc_dai_probe,
 270         .ops = &spdif_out_dai_ops,
 271 };
 272 
 273 static const struct snd_soc_component_driver spdif_out_component = {
 274         .name           = "spdif-out",
 275 };
 276 
 277 static int spdif_out_probe(struct platform_device *pdev)
 278 {
 279         struct spdif_out_dev *host;
 280         struct spear_spdif_platform_data *pdata;
 281         struct resource *res;
 282         int ret;
 283 
 284         host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
 285         if (!host)
 286                 return -ENOMEM;
 287 
 288         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 289         host->io_base = devm_ioremap_resource(&pdev->dev, res);
 290         if (IS_ERR(host->io_base))
 291                 return PTR_ERR(host->io_base);
 292 
 293         host->clk = devm_clk_get(&pdev->dev, NULL);
 294         if (IS_ERR(host->clk))
 295                 return PTR_ERR(host->clk);
 296 
 297         pdata = dev_get_platdata(&pdev->dev);
 298 
 299         host->dma_params.data = pdata->dma_params;
 300         host->dma_params.addr = res->start + SPDIF_OUT_FIFO_DATA;
 301         host->dma_params.max_burst = 16;
 302         host->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 303 
 304         dev_set_drvdata(&pdev->dev, host);
 305 
 306         ret = devm_snd_soc_register_component(&pdev->dev, &spdif_out_component,
 307                                               &spdif_out_dai, 1);
 308         if (ret)
 309                 return ret;
 310 
 311         return devm_spear_pcm_platform_register(&pdev->dev, &host->config,
 312                                                 pdata->filter);
 313 }
 314 
 315 #ifdef CONFIG_PM
 316 static int spdif_out_suspend(struct device *dev)
 317 {
 318         struct platform_device *pdev = to_platform_device(dev);
 319         struct spdif_out_dev *host = dev_get_drvdata(&pdev->dev);
 320 
 321         if (host->running)
 322                 clk_disable(host->clk);
 323 
 324         return 0;
 325 }
 326 
 327 static int spdif_out_resume(struct device *dev)
 328 {
 329         struct platform_device *pdev = to_platform_device(dev);
 330         struct spdif_out_dev *host = dev_get_drvdata(&pdev->dev);
 331 
 332         if (host->running) {
 333                 clk_enable(host->clk);
 334                 spdif_out_configure(host);
 335                 spdif_out_clock(host, host->saved_params.core_freq,
 336                                 host->saved_params.rate);
 337         }
 338         return 0;
 339 }
 340 
 341 static SIMPLE_DEV_PM_OPS(spdif_out_dev_pm_ops, spdif_out_suspend, \
 342                 spdif_out_resume);
 343 
 344 #define SPDIF_OUT_DEV_PM_OPS (&spdif_out_dev_pm_ops)
 345 
 346 #else
 347 #define SPDIF_OUT_DEV_PM_OPS NULL
 348 
 349 #endif
 350 
 351 static struct platform_driver spdif_out_driver = {
 352         .probe          = spdif_out_probe,
 353         .driver         = {
 354                 .name   = "spdif-out",
 355                 .pm     = SPDIF_OUT_DEV_PM_OPS,
 356         },
 357 };
 358 
 359 module_platform_driver(spdif_out_driver);
 360 
 361 MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>");
 362 MODULE_DESCRIPTION("SPEAr SPDIF OUT SoC Interface");
 363 MODULE_LICENSE("GPL");
 364 MODULE_ALIAS("platform:spdif_out");

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