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