Version:  2.6.34 2.6.35 2.6.36 2.6.37 2.6.38 2.6.39 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14

Linux/sound/soc/tegra/tegra20_ac97.c

  1 /*
  2  * tegra20_ac97.c - Tegra20 AC97 platform driver
  3  *
  4  * Copyright (c) 2012 Lucas Stach <dev@lynxeye.de>
  5  *
  6  * Partly based on code copyright/by:
  7  *
  8  * Copyright (c) 2011,2012 Toradex Inc.
  9  *
 10  * This program is free software; you can redistribute it and/or
 11  * modify it under the terms of the GNU General Public License
 12  * version 2 as published by the Free Software Foundation.
 13  *
 14  * This program is distributed in the hope that it will be useful, but
 15  * WITHOUT ANY WARRANTY; without even the implied warranty of
 16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17  * General Public License for more details.
 18  *
 19  */
 20 
 21 #include <linux/clk.h>
 22 #include <linux/delay.h>
 23 #include <linux/device.h>
 24 #include <linux/gpio.h>
 25 #include <linux/io.h>
 26 #include <linux/jiffies.h>
 27 #include <linux/module.h>
 28 #include <linux/of.h>
 29 #include <linux/of_gpio.h>
 30 #include <linux/platform_device.h>
 31 #include <linux/pm_runtime.h>
 32 #include <linux/regmap.h>
 33 #include <linux/slab.h>
 34 #include <sound/core.h>
 35 #include <sound/pcm.h>
 36 #include <sound/pcm_params.h>
 37 #include <sound/soc.h>
 38 #include <sound/dmaengine_pcm.h>
 39 
 40 #include "tegra_asoc_utils.h"
 41 #include "tegra20_ac97.h"
 42 
 43 #define DRV_NAME "tegra20-ac97"
 44 
 45 static struct tegra20_ac97 *workdata;
 46 
 47 static void tegra20_ac97_codec_reset(struct snd_ac97 *ac97)
 48 {
 49         u32 readback;
 50         unsigned long timeout;
 51 
 52         /* reset line is not driven by DAC pad group, have to toggle GPIO */
 53         gpio_set_value(workdata->reset_gpio, 0);
 54         udelay(2);
 55 
 56         gpio_set_value(workdata->reset_gpio, 1);
 57         udelay(2);
 58 
 59         timeout = jiffies + msecs_to_jiffies(100);
 60 
 61         do {
 62                 regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback);
 63                 if (readback & TEGRA20_AC97_STATUS1_CODEC1_RDY)
 64                         break;
 65                 usleep_range(1000, 2000);
 66         } while (!time_after(jiffies, timeout));
 67 }
 68 
 69 static void tegra20_ac97_codec_warm_reset(struct snd_ac97 *ac97)
 70 {
 71         u32 readback;
 72         unsigned long timeout;
 73 
 74         /*
 75          * although sync line is driven by the DAC pad group warm reset using
 76          * the controller cmd is not working, have to toggle sync line
 77          * manually.
 78          */
 79         gpio_request(workdata->sync_gpio, "codec-sync");
 80 
 81         gpio_direction_output(workdata->sync_gpio, 1);
 82 
 83         udelay(2);
 84         gpio_set_value(workdata->sync_gpio, 0);
 85         udelay(2);
 86         gpio_free(workdata->sync_gpio);
 87 
 88         timeout = jiffies + msecs_to_jiffies(100);
 89 
 90         do {
 91                 regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback);
 92                 if (readback & TEGRA20_AC97_STATUS1_CODEC1_RDY)
 93                         break;
 94                 usleep_range(1000, 2000);
 95         } while (!time_after(jiffies, timeout));
 96 }
 97 
 98 static unsigned short tegra20_ac97_codec_read(struct snd_ac97 *ac97_snd,
 99                                               unsigned short reg)
100 {
101         u32 readback;
102         unsigned long timeout;
103 
104         regmap_write(workdata->regmap, TEGRA20_AC97_CMD,
105                      (((reg | 0x80) << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) &
106                       TEGRA20_AC97_CMD_CMD_ADDR_MASK) |
107                      TEGRA20_AC97_CMD_BUSY);
108 
109         timeout = jiffies + msecs_to_jiffies(100);
110 
111         do {
112                 regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback);
113                 if (readback & TEGRA20_AC97_STATUS1_STA_VALID1)
114                         break;
115                 usleep_range(1000, 2000);
116         } while (!time_after(jiffies, timeout));
117 
118         return ((readback & TEGRA20_AC97_STATUS1_STA_DATA1_MASK) >>
119                 TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT);
120 }
121 
122 static void tegra20_ac97_codec_write(struct snd_ac97 *ac97_snd,
123                                      unsigned short reg, unsigned short val)
124 {
125         u32 readback;
126         unsigned long timeout;
127 
128         regmap_write(workdata->regmap, TEGRA20_AC97_CMD,
129                      ((reg << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) &
130                       TEGRA20_AC97_CMD_CMD_ADDR_MASK) |
131                      ((val << TEGRA20_AC97_CMD_CMD_DATA_SHIFT) &
132                       TEGRA20_AC97_CMD_CMD_DATA_MASK) |
133                      TEGRA20_AC97_CMD_BUSY);
134 
135         timeout = jiffies + msecs_to_jiffies(100);
136 
137         do {
138                 regmap_read(workdata->regmap, TEGRA20_AC97_CMD, &readback);
139                 if (!(readback & TEGRA20_AC97_CMD_BUSY))
140                         break;
141                 usleep_range(1000, 2000);
142         } while (!time_after(jiffies, timeout));
143 }
144 
145 static struct snd_ac97_bus_ops tegra20_ac97_ops = {
146         .read           = tegra20_ac97_codec_read,
147         .write          = tegra20_ac97_codec_write,
148         .reset          = tegra20_ac97_codec_reset,
149         .warm_reset     = tegra20_ac97_codec_warm_reset,
150 };
151 
152 static inline void tegra20_ac97_start_playback(struct tegra20_ac97 *ac97)
153 {
154         regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR,
155                            TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN,
156                            TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN);
157 
158         regmap_update_bits(ac97->regmap, TEGRA20_AC97_CTRL,
159                            TEGRA20_AC97_CTRL_PCM_DAC_EN |
160                            TEGRA20_AC97_CTRL_STM_EN,
161                            TEGRA20_AC97_CTRL_PCM_DAC_EN |
162                            TEGRA20_AC97_CTRL_STM_EN);
163 }
164 
165 static inline void tegra20_ac97_stop_playback(struct tegra20_ac97 *ac97)
166 {
167         regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR,
168                            TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN, 0);
169 
170         regmap_update_bits(ac97->regmap, TEGRA20_AC97_CTRL,
171                            TEGRA20_AC97_CTRL_PCM_DAC_EN, 0);
172 }
173 
174 static inline void tegra20_ac97_start_capture(struct tegra20_ac97 *ac97)
175 {
176         regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR,
177                            TEGRA20_AC97_FIFO_SCR_REC_FULL_EN,
178                            TEGRA20_AC97_FIFO_SCR_REC_FULL_EN);
179 }
180 
181 static inline void tegra20_ac97_stop_capture(struct tegra20_ac97 *ac97)
182 {
183         regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR,
184                            TEGRA20_AC97_FIFO_SCR_REC_FULL_EN, 0);
185 }
186 
187 static int tegra20_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
188                                 struct snd_soc_dai *dai)
189 {
190         struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai);
191 
192         switch (cmd) {
193         case SNDRV_PCM_TRIGGER_START:
194         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
195         case SNDRV_PCM_TRIGGER_RESUME:
196                 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
197                         tegra20_ac97_start_playback(ac97);
198                 else
199                         tegra20_ac97_start_capture(ac97);
200                 break;
201         case SNDRV_PCM_TRIGGER_STOP:
202         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
203         case SNDRV_PCM_TRIGGER_SUSPEND:
204                 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
205                         tegra20_ac97_stop_playback(ac97);
206                 else
207                         tegra20_ac97_stop_capture(ac97);
208                 break;
209         default:
210                 return -EINVAL;
211         }
212 
213         return 0;
214 }
215 
216 static const struct snd_soc_dai_ops tegra20_ac97_dai_ops = {
217         .trigger        = tegra20_ac97_trigger,
218 };
219 
220 static int tegra20_ac97_probe(struct snd_soc_dai *dai)
221 {
222         struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai);
223 
224         dai->capture_dma_data = &ac97->capture_dma_data;
225         dai->playback_dma_data = &ac97->playback_dma_data;
226 
227         return 0;
228 }
229 
230 static struct snd_soc_dai_driver tegra20_ac97_dai = {
231         .name = "tegra-ac97-pcm",
232         .ac97_control = 1,
233         .probe = tegra20_ac97_probe,
234         .playback = {
235                 .stream_name = "PCM Playback",
236                 .channels_min = 2,
237                 .channels_max = 2,
238                 .rates = SNDRV_PCM_RATE_8000_48000,
239                 .formats = SNDRV_PCM_FMTBIT_S16_LE,
240         },
241         .capture = {
242                 .stream_name = "PCM Capture",
243                 .channels_min = 2,
244                 .channels_max = 2,
245                 .rates = SNDRV_PCM_RATE_8000_48000,
246                 .formats = SNDRV_PCM_FMTBIT_S16_LE,
247         },
248         .ops = &tegra20_ac97_dai_ops,
249 };
250 
251 static const struct snd_soc_component_driver tegra20_ac97_component = {
252         .name           = DRV_NAME,
253 };
254 
255 static bool tegra20_ac97_wr_rd_reg(struct device *dev, unsigned int reg)
256 {
257         switch (reg) {
258         case TEGRA20_AC97_CTRL:
259         case TEGRA20_AC97_CMD:
260         case TEGRA20_AC97_STATUS1:
261         case TEGRA20_AC97_FIFO1_SCR:
262         case TEGRA20_AC97_FIFO_TX1:
263         case TEGRA20_AC97_FIFO_RX1:
264                 return true;
265         default:
266                 break;
267         }
268 
269         return false;
270 }
271 
272 static bool tegra20_ac97_volatile_reg(struct device *dev, unsigned int reg)
273 {
274         switch (reg) {
275         case TEGRA20_AC97_STATUS1:
276         case TEGRA20_AC97_FIFO1_SCR:
277         case TEGRA20_AC97_FIFO_TX1:
278         case TEGRA20_AC97_FIFO_RX1:
279                 return true;
280         default:
281                 break;
282         }
283 
284         return false;
285 }
286 
287 static bool tegra20_ac97_precious_reg(struct device *dev, unsigned int reg)
288 {
289         switch (reg) {
290         case TEGRA20_AC97_FIFO_TX1:
291         case TEGRA20_AC97_FIFO_RX1:
292                 return true;
293         default:
294                 break;
295         }
296 
297         return false;
298 }
299 
300 static const struct regmap_config tegra20_ac97_regmap_config = {
301         .reg_bits = 32,
302         .reg_stride = 4,
303         .val_bits = 32,
304         .max_register = TEGRA20_AC97_FIFO_RX1,
305         .writeable_reg = tegra20_ac97_wr_rd_reg,
306         .readable_reg = tegra20_ac97_wr_rd_reg,
307         .volatile_reg = tegra20_ac97_volatile_reg,
308         .precious_reg = tegra20_ac97_precious_reg,
309         .cache_type = REGCACHE_RBTREE,
310 };
311 
312 static int tegra20_ac97_platform_probe(struct platform_device *pdev)
313 {
314         struct tegra20_ac97 *ac97;
315         struct resource *mem;
316         void __iomem *regs;
317         int ret = 0;
318 
319         ac97 = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_ac97),
320                             GFP_KERNEL);
321         if (!ac97) {
322                 dev_err(&pdev->dev, "Can't allocate tegra20_ac97\n");
323                 ret = -ENOMEM;
324                 goto err;
325         }
326         dev_set_drvdata(&pdev->dev, ac97);
327 
328         ac97->clk_ac97 = devm_clk_get(&pdev->dev, NULL);
329         if (IS_ERR(ac97->clk_ac97)) {
330                 dev_err(&pdev->dev, "Can't retrieve ac97 clock\n");
331                 ret = PTR_ERR(ac97->clk_ac97);
332                 goto err;
333         }
334 
335         mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
336         regs = devm_ioremap_resource(&pdev->dev, mem);
337         if (IS_ERR(regs)) {
338                 ret = PTR_ERR(regs);
339                 goto err_clk_put;
340         }
341 
342         ac97->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
343                                             &tegra20_ac97_regmap_config);
344         if (IS_ERR(ac97->regmap)) {
345                 dev_err(&pdev->dev, "regmap init failed\n");
346                 ret = PTR_ERR(ac97->regmap);
347                 goto err_clk_put;
348         }
349 
350         ac97->reset_gpio = of_get_named_gpio(pdev->dev.of_node,
351                                              "nvidia,codec-reset-gpio", 0);
352         if (gpio_is_valid(ac97->reset_gpio)) {
353                 ret = devm_gpio_request_one(&pdev->dev, ac97->reset_gpio,
354                                             GPIOF_OUT_INIT_HIGH, "codec-reset");
355                 if (ret) {
356                         dev_err(&pdev->dev, "could not get codec-reset GPIO\n");
357                         goto err_clk_put;
358                 }
359         } else {
360                 dev_err(&pdev->dev, "no codec-reset GPIO supplied\n");
361                 goto err_clk_put;
362         }
363 
364         ac97->sync_gpio = of_get_named_gpio(pdev->dev.of_node,
365                                             "nvidia,codec-sync-gpio", 0);
366         if (!gpio_is_valid(ac97->sync_gpio)) {
367                 dev_err(&pdev->dev, "no codec-sync GPIO supplied\n");
368                 goto err_clk_put;
369         }
370 
371         ac97->capture_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_RX1;
372         ac97->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
373         ac97->capture_dma_data.maxburst = 4;
374 
375         ac97->playback_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_TX1;
376         ac97->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
377         ac97->playback_dma_data.maxburst = 4;
378 
379         ret = tegra_asoc_utils_init(&ac97->util_data, &pdev->dev);
380         if (ret)
381                 goto err_clk_put;
382 
383         ret = tegra_asoc_utils_set_ac97_rate(&ac97->util_data);
384         if (ret)
385                 goto err_asoc_utils_fini;
386 
387         ret = clk_prepare_enable(ac97->clk_ac97);
388         if (ret) {
389                 dev_err(&pdev->dev, "clk_enable failed: %d\n", ret);
390                 goto err_asoc_utils_fini;
391         }
392 
393         ret = snd_soc_set_ac97_ops(&tegra20_ac97_ops);
394         if (ret) {
395                 dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret);
396                 goto err_clk_disable_unprepare;
397         }
398 
399         ret = snd_soc_register_component(&pdev->dev, &tegra20_ac97_component,
400                                          &tegra20_ac97_dai, 1);
401         if (ret) {
402                 dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
403                 ret = -ENOMEM;
404                 goto err_clk_disable_unprepare;
405         }
406 
407         ret = tegra_pcm_platform_register(&pdev->dev);
408         if (ret) {
409                 dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
410                 goto err_unregister_component;
411         }
412 
413         /* XXX: crufty ASoC AC97 API - only one AC97 codec allowed */
414         workdata = ac97;
415 
416         return 0;
417 
418 err_unregister_component:
419         snd_soc_unregister_component(&pdev->dev);
420 err_clk_disable_unprepare:
421         clk_disable_unprepare(ac97->clk_ac97);
422 err_asoc_utils_fini:
423         tegra_asoc_utils_fini(&ac97->util_data);
424 err_clk_put:
425 err:
426         snd_soc_set_ac97_ops(NULL);
427         return ret;
428 }
429 
430 static int tegra20_ac97_platform_remove(struct platform_device *pdev)
431 {
432         struct tegra20_ac97 *ac97 = dev_get_drvdata(&pdev->dev);
433 
434         tegra_pcm_platform_unregister(&pdev->dev);
435         snd_soc_unregister_component(&pdev->dev);
436 
437         tegra_asoc_utils_fini(&ac97->util_data);
438 
439         clk_disable_unprepare(ac97->clk_ac97);
440 
441         snd_soc_set_ac97_ops(NULL);
442 
443         return 0;
444 }
445 
446 static const struct of_device_id tegra20_ac97_of_match[] = {
447         { .compatible = "nvidia,tegra20-ac97", },
448         {},
449 };
450 
451 static struct platform_driver tegra20_ac97_driver = {
452         .driver = {
453                 .name = DRV_NAME,
454                 .owner = THIS_MODULE,
455                 .of_match_table = tegra20_ac97_of_match,
456         },
457         .probe = tegra20_ac97_platform_probe,
458         .remove = tegra20_ac97_platform_remove,
459 };
460 module_platform_driver(tegra20_ac97_driver);
461 
462 MODULE_AUTHOR("Lucas Stach");
463 MODULE_DESCRIPTION("Tegra20 AC97 ASoC driver");
464 MODULE_LICENSE("GPL v2");
465 MODULE_ALIAS("platform:" DRV_NAME);
466 MODULE_DEVICE_TABLE(of, tegra20_ac97_of_match);
467 

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us