2 * tegra20_ac97.c - Tegra20 AC97 platform driver
4 * Copyright (c) 2012 Lucas Stach <dev@lynxeye.de>
6 * Partly based on code copyright/by:
8 * Copyright (c) 2011,2012 Toradex Inc.
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.
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.
21 #include <linux/clk.h>
22 #include <linux/delay.h>
23 #include <linux/device.h>
24 #include <linux/gpio.h>
26 #include <linux/jiffies.h>
27 #include <linux/module.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>
40 #include "tegra20_ac97.h"
42 #define DRV_NAME "tegra20-ac97"
44 static struct tegra20_ac97
*workdata
;
46 static void tegra20_ac97_codec_reset(struct snd_ac97
*ac97
)
49 unsigned long timeout
;
51 /* reset line is not driven by DAC pad group, have to toggle GPIO */
52 gpio_set_value(workdata
->reset_gpio
, 0);
55 gpio_set_value(workdata
->reset_gpio
, 1);
58 timeout
= jiffies
+ msecs_to_jiffies(100);
61 regmap_read(workdata
->regmap
, TEGRA20_AC97_STATUS1
, &readback
);
62 if (readback
& TEGRA20_AC97_STATUS1_CODEC1_RDY
)
64 usleep_range(1000, 2000);
65 } while (!time_after(jiffies
, timeout
));
68 static void tegra20_ac97_codec_warm_reset(struct snd_ac97
*ac97
)
71 unsigned long timeout
;
74 * although sync line is driven by the DAC pad group warm reset using
75 * the controller cmd is not working, have to toggle sync line
78 gpio_request(workdata
->sync_gpio
, "codec-sync");
80 gpio_direction_output(workdata
->sync_gpio
, 1);
83 gpio_set_value(workdata
->sync_gpio
, 0);
85 gpio_free(workdata
->sync_gpio
);
87 timeout
= jiffies
+ msecs_to_jiffies(100);
90 regmap_read(workdata
->regmap
, TEGRA20_AC97_STATUS1
, &readback
);
91 if (readback
& TEGRA20_AC97_STATUS1_CODEC1_RDY
)
93 usleep_range(1000, 2000);
94 } while (!time_after(jiffies
, timeout
));
97 static unsigned short tegra20_ac97_codec_read(struct snd_ac97
*ac97_snd
,
101 unsigned long timeout
;
103 regmap_write(workdata
->regmap
, TEGRA20_AC97_CMD
,
104 (((reg
| 0x80) << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT
) &
105 TEGRA20_AC97_CMD_CMD_ADDR_MASK
) |
106 TEGRA20_AC97_CMD_BUSY
);
108 timeout
= jiffies
+ msecs_to_jiffies(100);
111 regmap_read(workdata
->regmap
, TEGRA20_AC97_STATUS1
, &readback
);
112 if (readback
& TEGRA20_AC97_STATUS1_STA_VALID1
)
114 usleep_range(1000, 2000);
115 } while (!time_after(jiffies
, timeout
));
117 return ((readback
& TEGRA20_AC97_STATUS1_STA_DATA1_MASK
) >>
118 TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT
);
121 static void tegra20_ac97_codec_write(struct snd_ac97
*ac97_snd
,
122 unsigned short reg
, unsigned short val
)
125 unsigned long timeout
;
127 regmap_write(workdata
->regmap
, TEGRA20_AC97_CMD
,
128 ((reg
<< TEGRA20_AC97_CMD_CMD_ADDR_SHIFT
) &
129 TEGRA20_AC97_CMD_CMD_ADDR_MASK
) |
130 ((val
<< TEGRA20_AC97_CMD_CMD_DATA_SHIFT
) &
131 TEGRA20_AC97_CMD_CMD_DATA_MASK
) |
132 TEGRA20_AC97_CMD_BUSY
);
134 timeout
= jiffies
+ msecs_to_jiffies(100);
137 regmap_read(workdata
->regmap
, TEGRA20_AC97_CMD
, &readback
);
138 if (!(readback
& TEGRA20_AC97_CMD_BUSY
))
140 usleep_range(1000, 2000);
141 } while (!time_after(jiffies
, timeout
));
144 static struct snd_ac97_bus_ops tegra20_ac97_ops
= {
145 .read
= tegra20_ac97_codec_read
,
146 .write
= tegra20_ac97_codec_write
,
147 .reset
= tegra20_ac97_codec_reset
,
148 .warm_reset
= tegra20_ac97_codec_warm_reset
,
151 static inline void tegra20_ac97_start_playback(struct tegra20_ac97
*ac97
)
153 regmap_update_bits(ac97
->regmap
, TEGRA20_AC97_FIFO1_SCR
,
154 TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN
,
155 TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN
);
157 regmap_update_bits(ac97
->regmap
, TEGRA20_AC97_CTRL
,
158 TEGRA20_AC97_CTRL_PCM_DAC_EN
|
159 TEGRA20_AC97_CTRL_STM_EN
,
160 TEGRA20_AC97_CTRL_PCM_DAC_EN
|
161 TEGRA20_AC97_CTRL_STM_EN
);
164 static inline void tegra20_ac97_stop_playback(struct tegra20_ac97
*ac97
)
166 regmap_update_bits(ac97
->regmap
, TEGRA20_AC97_FIFO1_SCR
,
167 TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN
, 0);
169 regmap_update_bits(ac97
->regmap
, TEGRA20_AC97_CTRL
,
170 TEGRA20_AC97_CTRL_PCM_DAC_EN
, 0);
173 static inline void tegra20_ac97_start_capture(struct tegra20_ac97
*ac97
)
175 regmap_update_bits(ac97
->regmap
, TEGRA20_AC97_FIFO1_SCR
,
176 TEGRA20_AC97_FIFO_SCR_REC_FULL_EN
,
177 TEGRA20_AC97_FIFO_SCR_REC_FULL_EN
);
180 static inline void tegra20_ac97_stop_capture(struct tegra20_ac97
*ac97
)
182 regmap_update_bits(ac97
->regmap
, TEGRA20_AC97_FIFO1_SCR
,
183 TEGRA20_AC97_FIFO_SCR_REC_FULL_EN
, 0);
186 static int tegra20_ac97_trigger(struct snd_pcm_substream
*substream
, int cmd
,
187 struct snd_soc_dai
*dai
)
189 struct tegra20_ac97
*ac97
= snd_soc_dai_get_drvdata(dai
);
192 case SNDRV_PCM_TRIGGER_START
:
193 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
194 case SNDRV_PCM_TRIGGER_RESUME
:
195 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
196 tegra20_ac97_start_playback(ac97
);
198 tegra20_ac97_start_capture(ac97
);
200 case SNDRV_PCM_TRIGGER_STOP
:
201 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
202 case SNDRV_PCM_TRIGGER_SUSPEND
:
203 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
204 tegra20_ac97_stop_playback(ac97
);
206 tegra20_ac97_stop_capture(ac97
);
215 static const struct snd_soc_dai_ops tegra20_ac97_dai_ops
= {
216 .trigger
= tegra20_ac97_trigger
,
219 static int tegra20_ac97_probe(struct snd_soc_dai
*dai
)
221 struct tegra20_ac97
*ac97
= snd_soc_dai_get_drvdata(dai
);
223 dai
->capture_dma_data
= &ac97
->capture_dma_data
;
224 dai
->playback_dma_data
= &ac97
->playback_dma_data
;
229 static struct snd_soc_dai_driver tegra20_ac97_dai
= {
230 .name
= "tegra-ac97-pcm",
232 .probe
= tegra20_ac97_probe
,
234 .stream_name
= "PCM Playback",
237 .rates
= SNDRV_PCM_RATE_8000_48000
,
238 .formats
= SNDRV_PCM_FMTBIT_S16_LE
,
241 .stream_name
= "PCM Capture",
244 .rates
= SNDRV_PCM_RATE_8000_48000
,
245 .formats
= SNDRV_PCM_FMTBIT_S16_LE
,
247 .ops
= &tegra20_ac97_dai_ops
,
250 static const struct snd_soc_component_driver tegra20_ac97_component
= {
254 static bool tegra20_ac97_wr_rd_reg(struct device
*dev
, unsigned int reg
)
257 case TEGRA20_AC97_CTRL
:
258 case TEGRA20_AC97_CMD
:
259 case TEGRA20_AC97_STATUS1
:
260 case TEGRA20_AC97_FIFO1_SCR
:
261 case TEGRA20_AC97_FIFO_TX1
:
262 case TEGRA20_AC97_FIFO_RX1
:
271 static bool tegra20_ac97_volatile_reg(struct device
*dev
, unsigned int reg
)
274 case TEGRA20_AC97_STATUS1
:
275 case TEGRA20_AC97_FIFO1_SCR
:
276 case TEGRA20_AC97_FIFO_TX1
:
277 case TEGRA20_AC97_FIFO_RX1
:
286 static bool tegra20_ac97_precious_reg(struct device
*dev
, unsigned int reg
)
289 case TEGRA20_AC97_FIFO_TX1
:
290 case TEGRA20_AC97_FIFO_RX1
:
299 static const struct regmap_config tegra20_ac97_regmap_config
= {
303 .max_register
= TEGRA20_AC97_FIFO_RX1
,
304 .writeable_reg
= tegra20_ac97_wr_rd_reg
,
305 .readable_reg
= tegra20_ac97_wr_rd_reg
,
306 .volatile_reg
= tegra20_ac97_volatile_reg
,
307 .precious_reg
= tegra20_ac97_precious_reg
,
308 .cache_type
= REGCACHE_FLAT
,
311 static int tegra20_ac97_platform_probe(struct platform_device
*pdev
)
313 struct tegra20_ac97
*ac97
;
314 struct resource
*mem
;
318 ac97
= devm_kzalloc(&pdev
->dev
, sizeof(struct tegra20_ac97
),
321 dev_err(&pdev
->dev
, "Can't allocate tegra20_ac97\n");
325 dev_set_drvdata(&pdev
->dev
, ac97
);
327 ac97
->clk_ac97
= devm_clk_get(&pdev
->dev
, NULL
);
328 if (IS_ERR(ac97
->clk_ac97
)) {
329 dev_err(&pdev
->dev
, "Can't retrieve ac97 clock\n");
330 ret
= PTR_ERR(ac97
->clk_ac97
);
334 mem
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
335 regs
= devm_ioremap_resource(&pdev
->dev
, mem
);
341 ac97
->regmap
= devm_regmap_init_mmio(&pdev
->dev
, regs
,
342 &tegra20_ac97_regmap_config
);
343 if (IS_ERR(ac97
->regmap
)) {
344 dev_err(&pdev
->dev
, "regmap init failed\n");
345 ret
= PTR_ERR(ac97
->regmap
);
349 ac97
->reset_gpio
= of_get_named_gpio(pdev
->dev
.of_node
,
350 "nvidia,codec-reset-gpio", 0);
351 if (gpio_is_valid(ac97
->reset_gpio
)) {
352 ret
= devm_gpio_request_one(&pdev
->dev
, ac97
->reset_gpio
,
353 GPIOF_OUT_INIT_HIGH
, "codec-reset");
355 dev_err(&pdev
->dev
, "could not get codec-reset GPIO\n");
359 dev_err(&pdev
->dev
, "no codec-reset GPIO supplied\n");
363 ac97
->sync_gpio
= of_get_named_gpio(pdev
->dev
.of_node
,
364 "nvidia,codec-sync-gpio", 0);
365 if (!gpio_is_valid(ac97
->sync_gpio
)) {
366 dev_err(&pdev
->dev
, "no codec-sync GPIO supplied\n");
370 ac97
->capture_dma_data
.addr
= mem
->start
+ TEGRA20_AC97_FIFO_RX1
;
371 ac97
->capture_dma_data
.addr_width
= DMA_SLAVE_BUSWIDTH_4_BYTES
;
372 ac97
->capture_dma_data
.maxburst
= 4;
374 ac97
->playback_dma_data
.addr
= mem
->start
+ TEGRA20_AC97_FIFO_TX1
;
375 ac97
->playback_dma_data
.addr_width
= DMA_SLAVE_BUSWIDTH_4_BYTES
;
376 ac97
->playback_dma_data
.maxburst
= 4;
378 ret
= clk_prepare_enable(ac97
->clk_ac97
);
380 dev_err(&pdev
->dev
, "clk_enable failed: %d\n", ret
);
384 ret
= snd_soc_set_ac97_ops(&tegra20_ac97_ops
);
386 dev_err(&pdev
->dev
, "Failed to set AC'97 ops: %d\n", ret
);
387 goto err_clk_disable_unprepare
;
390 ret
= snd_soc_register_component(&pdev
->dev
, &tegra20_ac97_component
,
391 &tegra20_ac97_dai
, 1);
393 dev_err(&pdev
->dev
, "Could not register DAI: %d\n", ret
);
395 goto err_clk_disable_unprepare
;
398 ret
= tegra_pcm_platform_register(&pdev
->dev
);
400 dev_err(&pdev
->dev
, "Could not register PCM: %d\n", ret
);
401 goto err_unregister_component
;
404 /* XXX: crufty ASoC AC97 API - only one AC97 codec allowed */
409 err_unregister_component
:
410 snd_soc_unregister_component(&pdev
->dev
);
411 err_clk_disable_unprepare
:
412 clk_disable_unprepare(ac97
->clk_ac97
);
415 snd_soc_set_ac97_ops(NULL
);
419 static int tegra20_ac97_platform_remove(struct platform_device
*pdev
)
421 struct tegra20_ac97
*ac97
= dev_get_drvdata(&pdev
->dev
);
423 tegra_pcm_platform_unregister(&pdev
->dev
);
424 snd_soc_unregister_component(&pdev
->dev
);
426 clk_disable_unprepare(ac97
->clk_ac97
);
428 snd_soc_set_ac97_ops(NULL
);
433 static const struct of_device_id tegra20_ac97_of_match
[] = {
434 { .compatible
= "nvidia,tegra20-ac97", },
438 static struct platform_driver tegra20_ac97_driver
= {
441 .of_match_table
= tegra20_ac97_of_match
,
443 .probe
= tegra20_ac97_platform_probe
,
444 .remove
= tegra20_ac97_platform_remove
,
446 module_platform_driver(tegra20_ac97_driver
);
448 MODULE_AUTHOR("Lucas Stach");
449 MODULE_DESCRIPTION("Tegra20 AC97 ASoC driver");
450 MODULE_LICENSE("GPL v2");
451 MODULE_ALIAS("platform:" DRV_NAME
);
452 MODULE_DEVICE_TABLE(of
, tegra20_ac97_of_match
);