1 // SPDX-License-Identifier: GPL-2.0-only
3 * tegra20_ac97.c - Tegra20 AC97 platform driver
5 * Copyright (c) 2012 Lucas Stach <dev@lynxeye.de>
7 * Partly based on code copyright/by:
9 * Copyright (c) 2011,2012 Toradex Inc.
12 #include <linux/clk.h>
13 #include <linux/delay.h>
14 #include <linux/device.h>
15 #include <linux/gpio.h>
17 #include <linux/jiffies.h>
18 #include <linux/module.h>
20 #include <linux/of_gpio.h>
21 #include <linux/platform_device.h>
22 #include <linux/pm_runtime.h>
23 #include <linux/regmap.h>
24 #include <linux/slab.h>
25 #include <sound/core.h>
26 #include <sound/pcm.h>
27 #include <sound/pcm_params.h>
28 #include <sound/soc.h>
29 #include <sound/dmaengine_pcm.h>
31 #include "tegra20_ac97.h"
33 #define DRV_NAME "tegra20-ac97"
35 static struct tegra20_ac97
*workdata
;
37 static void tegra20_ac97_codec_reset(struct snd_ac97
*ac97
)
40 unsigned long timeout
;
42 /* reset line is not driven by DAC pad group, have to toggle GPIO */
43 gpio_set_value(workdata
->reset_gpio
, 0);
46 gpio_set_value(workdata
->reset_gpio
, 1);
49 timeout
= jiffies
+ msecs_to_jiffies(100);
52 regmap_read(workdata
->regmap
, TEGRA20_AC97_STATUS1
, &readback
);
53 if (readback
& TEGRA20_AC97_STATUS1_CODEC1_RDY
)
55 usleep_range(1000, 2000);
56 } while (!time_after(jiffies
, timeout
));
59 static void tegra20_ac97_codec_warm_reset(struct snd_ac97
*ac97
)
62 unsigned long timeout
;
65 * although sync line is driven by the DAC pad group warm reset using
66 * the controller cmd is not working, have to toggle sync line
69 gpio_request(workdata
->sync_gpio
, "codec-sync");
71 gpio_direction_output(workdata
->sync_gpio
, 1);
74 gpio_set_value(workdata
->sync_gpio
, 0);
76 gpio_free(workdata
->sync_gpio
);
78 timeout
= jiffies
+ msecs_to_jiffies(100);
81 regmap_read(workdata
->regmap
, TEGRA20_AC97_STATUS1
, &readback
);
82 if (readback
& TEGRA20_AC97_STATUS1_CODEC1_RDY
)
84 usleep_range(1000, 2000);
85 } while (!time_after(jiffies
, timeout
));
88 static unsigned short tegra20_ac97_codec_read(struct snd_ac97
*ac97_snd
,
92 unsigned long timeout
;
94 regmap_write(workdata
->regmap
, TEGRA20_AC97_CMD
,
95 (((reg
| 0x80) << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT
) &
96 TEGRA20_AC97_CMD_CMD_ADDR_MASK
) |
97 TEGRA20_AC97_CMD_BUSY
);
99 timeout
= jiffies
+ msecs_to_jiffies(100);
102 regmap_read(workdata
->regmap
, TEGRA20_AC97_STATUS1
, &readback
);
103 if (readback
& TEGRA20_AC97_STATUS1_STA_VALID1
)
105 usleep_range(1000, 2000);
106 } while (!time_after(jiffies
, timeout
));
108 return ((readback
& TEGRA20_AC97_STATUS1_STA_DATA1_MASK
) >>
109 TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT
);
112 static void tegra20_ac97_codec_write(struct snd_ac97
*ac97_snd
,
113 unsigned short reg
, unsigned short val
)
116 unsigned long timeout
;
118 regmap_write(workdata
->regmap
, TEGRA20_AC97_CMD
,
119 ((reg
<< TEGRA20_AC97_CMD_CMD_ADDR_SHIFT
) &
120 TEGRA20_AC97_CMD_CMD_ADDR_MASK
) |
121 ((val
<< TEGRA20_AC97_CMD_CMD_DATA_SHIFT
) &
122 TEGRA20_AC97_CMD_CMD_DATA_MASK
) |
123 TEGRA20_AC97_CMD_BUSY
);
125 timeout
= jiffies
+ msecs_to_jiffies(100);
128 regmap_read(workdata
->regmap
, TEGRA20_AC97_CMD
, &readback
);
129 if (!(readback
& TEGRA20_AC97_CMD_BUSY
))
131 usleep_range(1000, 2000);
132 } while (!time_after(jiffies
, timeout
));
135 static struct snd_ac97_bus_ops tegra20_ac97_ops
= {
136 .read
= tegra20_ac97_codec_read
,
137 .write
= tegra20_ac97_codec_write
,
138 .reset
= tegra20_ac97_codec_reset
,
139 .warm_reset
= tegra20_ac97_codec_warm_reset
,
142 static inline void tegra20_ac97_start_playback(struct tegra20_ac97
*ac97
)
144 regmap_update_bits(ac97
->regmap
, TEGRA20_AC97_FIFO1_SCR
,
145 TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN
,
146 TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN
);
148 regmap_update_bits(ac97
->regmap
, TEGRA20_AC97_CTRL
,
149 TEGRA20_AC97_CTRL_PCM_DAC_EN
|
150 TEGRA20_AC97_CTRL_STM_EN
,
151 TEGRA20_AC97_CTRL_PCM_DAC_EN
|
152 TEGRA20_AC97_CTRL_STM_EN
);
155 static inline void tegra20_ac97_stop_playback(struct tegra20_ac97
*ac97
)
157 regmap_update_bits(ac97
->regmap
, TEGRA20_AC97_FIFO1_SCR
,
158 TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN
, 0);
160 regmap_update_bits(ac97
->regmap
, TEGRA20_AC97_CTRL
,
161 TEGRA20_AC97_CTRL_PCM_DAC_EN
, 0);
164 static inline void tegra20_ac97_start_capture(struct tegra20_ac97
*ac97
)
166 regmap_update_bits(ac97
->regmap
, TEGRA20_AC97_FIFO1_SCR
,
167 TEGRA20_AC97_FIFO_SCR_REC_FULL_EN
,
168 TEGRA20_AC97_FIFO_SCR_REC_FULL_EN
);
171 static inline void tegra20_ac97_stop_capture(struct tegra20_ac97
*ac97
)
173 regmap_update_bits(ac97
->regmap
, TEGRA20_AC97_FIFO1_SCR
,
174 TEGRA20_AC97_FIFO_SCR_REC_FULL_EN
, 0);
177 static int tegra20_ac97_trigger(struct snd_pcm_substream
*substream
, int cmd
,
178 struct snd_soc_dai
*dai
)
180 struct tegra20_ac97
*ac97
= snd_soc_dai_get_drvdata(dai
);
183 case SNDRV_PCM_TRIGGER_START
:
184 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
185 case SNDRV_PCM_TRIGGER_RESUME
:
186 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
187 tegra20_ac97_start_playback(ac97
);
189 tegra20_ac97_start_capture(ac97
);
191 case SNDRV_PCM_TRIGGER_STOP
:
192 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
193 case SNDRV_PCM_TRIGGER_SUSPEND
:
194 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
195 tegra20_ac97_stop_playback(ac97
);
197 tegra20_ac97_stop_capture(ac97
);
206 static const struct snd_soc_dai_ops tegra20_ac97_dai_ops
= {
207 .trigger
= tegra20_ac97_trigger
,
210 static int tegra20_ac97_probe(struct snd_soc_dai
*dai
)
212 struct tegra20_ac97
*ac97
= snd_soc_dai_get_drvdata(dai
);
214 dai
->capture_dma_data
= &ac97
->capture_dma_data
;
215 dai
->playback_dma_data
= &ac97
->playback_dma_data
;
220 static struct snd_soc_dai_driver tegra20_ac97_dai
= {
221 .name
= "tegra-ac97-pcm",
222 .probe
= tegra20_ac97_probe
,
224 .stream_name
= "PCM Playback",
227 .rates
= SNDRV_PCM_RATE_8000_48000
,
228 .formats
= SNDRV_PCM_FMTBIT_S16_LE
,
231 .stream_name
= "PCM Capture",
234 .rates
= SNDRV_PCM_RATE_8000_48000
,
235 .formats
= SNDRV_PCM_FMTBIT_S16_LE
,
237 .ops
= &tegra20_ac97_dai_ops
,
240 static const struct snd_soc_component_driver tegra20_ac97_component
= {
244 static bool tegra20_ac97_wr_rd_reg(struct device
*dev
, unsigned int reg
)
247 case TEGRA20_AC97_CTRL
:
248 case TEGRA20_AC97_CMD
:
249 case TEGRA20_AC97_STATUS1
:
250 case TEGRA20_AC97_FIFO1_SCR
:
251 case TEGRA20_AC97_FIFO_TX1
:
252 case TEGRA20_AC97_FIFO_RX1
:
261 static bool tegra20_ac97_volatile_reg(struct device
*dev
, unsigned int reg
)
264 case TEGRA20_AC97_STATUS1
:
265 case TEGRA20_AC97_FIFO1_SCR
:
266 case TEGRA20_AC97_FIFO_TX1
:
267 case TEGRA20_AC97_FIFO_RX1
:
276 static bool tegra20_ac97_precious_reg(struct device
*dev
, unsigned int reg
)
279 case TEGRA20_AC97_FIFO_TX1
:
280 case TEGRA20_AC97_FIFO_RX1
:
289 static const struct regmap_config tegra20_ac97_regmap_config
= {
293 .max_register
= TEGRA20_AC97_FIFO_RX1
,
294 .writeable_reg
= tegra20_ac97_wr_rd_reg
,
295 .readable_reg
= tegra20_ac97_wr_rd_reg
,
296 .volatile_reg
= tegra20_ac97_volatile_reg
,
297 .precious_reg
= tegra20_ac97_precious_reg
,
298 .cache_type
= REGCACHE_FLAT
,
301 static int tegra20_ac97_platform_probe(struct platform_device
*pdev
)
303 struct tegra20_ac97
*ac97
;
304 struct resource
*mem
;
308 ac97
= devm_kzalloc(&pdev
->dev
, sizeof(struct tegra20_ac97
),
314 dev_set_drvdata(&pdev
->dev
, ac97
);
316 ac97
->clk_ac97
= devm_clk_get(&pdev
->dev
, NULL
);
317 if (IS_ERR(ac97
->clk_ac97
)) {
318 dev_err(&pdev
->dev
, "Can't retrieve ac97 clock\n");
319 ret
= PTR_ERR(ac97
->clk_ac97
);
323 mem
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
324 regs
= devm_ioremap_resource(&pdev
->dev
, mem
);
330 ac97
->regmap
= devm_regmap_init_mmio(&pdev
->dev
, regs
,
331 &tegra20_ac97_regmap_config
);
332 if (IS_ERR(ac97
->regmap
)) {
333 dev_err(&pdev
->dev
, "regmap init failed\n");
334 ret
= PTR_ERR(ac97
->regmap
);
338 ac97
->reset_gpio
= of_get_named_gpio(pdev
->dev
.of_node
,
339 "nvidia,codec-reset-gpio", 0);
340 if (gpio_is_valid(ac97
->reset_gpio
)) {
341 ret
= devm_gpio_request_one(&pdev
->dev
, ac97
->reset_gpio
,
342 GPIOF_OUT_INIT_HIGH
, "codec-reset");
344 dev_err(&pdev
->dev
, "could not get codec-reset GPIO\n");
348 dev_err(&pdev
->dev
, "no codec-reset GPIO supplied\n");
352 ac97
->sync_gpio
= of_get_named_gpio(pdev
->dev
.of_node
,
353 "nvidia,codec-sync-gpio", 0);
354 if (!gpio_is_valid(ac97
->sync_gpio
)) {
355 dev_err(&pdev
->dev
, "no codec-sync GPIO supplied\n");
359 ac97
->capture_dma_data
.addr
= mem
->start
+ TEGRA20_AC97_FIFO_RX1
;
360 ac97
->capture_dma_data
.addr_width
= DMA_SLAVE_BUSWIDTH_4_BYTES
;
361 ac97
->capture_dma_data
.maxburst
= 4;
363 ac97
->playback_dma_data
.addr
= mem
->start
+ TEGRA20_AC97_FIFO_TX1
;
364 ac97
->playback_dma_data
.addr_width
= DMA_SLAVE_BUSWIDTH_4_BYTES
;
365 ac97
->playback_dma_data
.maxburst
= 4;
367 ret
= clk_prepare_enable(ac97
->clk_ac97
);
369 dev_err(&pdev
->dev
, "clk_enable failed: %d\n", ret
);
373 ret
= snd_soc_set_ac97_ops(&tegra20_ac97_ops
);
375 dev_err(&pdev
->dev
, "Failed to set AC'97 ops: %d\n", ret
);
376 goto err_clk_disable_unprepare
;
379 ret
= snd_soc_register_component(&pdev
->dev
, &tegra20_ac97_component
,
380 &tegra20_ac97_dai
, 1);
382 dev_err(&pdev
->dev
, "Could not register DAI: %d\n", ret
);
384 goto err_clk_disable_unprepare
;
387 ret
= tegra_pcm_platform_register(&pdev
->dev
);
389 dev_err(&pdev
->dev
, "Could not register PCM: %d\n", ret
);
390 goto err_unregister_component
;
393 /* XXX: crufty ASoC AC97 API - only one AC97 codec allowed */
398 err_unregister_component
:
399 snd_soc_unregister_component(&pdev
->dev
);
400 err_clk_disable_unprepare
:
401 clk_disable_unprepare(ac97
->clk_ac97
);
404 snd_soc_set_ac97_ops(NULL
);
408 static int tegra20_ac97_platform_remove(struct platform_device
*pdev
)
410 struct tegra20_ac97
*ac97
= dev_get_drvdata(&pdev
->dev
);
412 tegra_pcm_platform_unregister(&pdev
->dev
);
413 snd_soc_unregister_component(&pdev
->dev
);
415 clk_disable_unprepare(ac97
->clk_ac97
);
417 snd_soc_set_ac97_ops(NULL
);
422 static const struct of_device_id tegra20_ac97_of_match
[] = {
423 { .compatible
= "nvidia,tegra20-ac97", },
427 static struct platform_driver tegra20_ac97_driver
= {
430 .of_match_table
= tegra20_ac97_of_match
,
432 .probe
= tegra20_ac97_platform_probe
,
433 .remove
= tegra20_ac97_platform_remove
,
435 module_platform_driver(tegra20_ac97_driver
);
437 MODULE_AUTHOR("Lucas Stach");
438 MODULE_DESCRIPTION("Tegra20 AC97 ASoC driver");
439 MODULE_LICENSE("GPL v2");
440 MODULE_ALIAS("platform:" DRV_NAME
);
441 MODULE_DEVICE_TABLE(of
, tegra20_ac97_of_match
);