18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * tegra20_ac97.c - Tegra20 AC97 platform driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2012 Lucas Stach <dev@lynxeye.de> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Partly based on code copyright/by: 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright (c) 2011,2012 Toradex Inc. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/clk.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/device.h> 158c2ecf20Sopenharmony_ci#include <linux/gpio.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/of.h> 208c2ecf20Sopenharmony_ci#include <linux/of_gpio.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 238c2ecf20Sopenharmony_ci#include <linux/regmap.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <sound/core.h> 268c2ecf20Sopenharmony_ci#include <sound/pcm.h> 278c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 288c2ecf20Sopenharmony_ci#include <sound/soc.h> 298c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include "tegra20_ac97.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define DRV_NAME "tegra20-ac97" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic struct tegra20_ac97 *workdata; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic void tegra20_ac97_codec_reset(struct snd_ac97 *ac97) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci u32 readback; 408c2ecf20Sopenharmony_ci unsigned long timeout; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* reset line is not driven by DAC pad group, have to toggle GPIO */ 438c2ecf20Sopenharmony_ci gpio_set_value(workdata->reset_gpio, 0); 448c2ecf20Sopenharmony_ci udelay(2); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci gpio_set_value(workdata->reset_gpio, 1); 478c2ecf20Sopenharmony_ci udelay(2); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(100); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci do { 528c2ecf20Sopenharmony_ci regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback); 538c2ecf20Sopenharmony_ci if (readback & TEGRA20_AC97_STATUS1_CODEC1_RDY) 548c2ecf20Sopenharmony_ci break; 558c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 568c2ecf20Sopenharmony_ci } while (!time_after(jiffies, timeout)); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic void tegra20_ac97_codec_warm_reset(struct snd_ac97 *ac97) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci u32 readback; 628c2ecf20Sopenharmony_ci unsigned long timeout; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci /* 658c2ecf20Sopenharmony_ci * although sync line is driven by the DAC pad group warm reset using 668c2ecf20Sopenharmony_ci * the controller cmd is not working, have to toggle sync line 678c2ecf20Sopenharmony_ci * manually. 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ci gpio_request(workdata->sync_gpio, "codec-sync"); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci gpio_direction_output(workdata->sync_gpio, 1); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci udelay(2); 748c2ecf20Sopenharmony_ci gpio_set_value(workdata->sync_gpio, 0); 758c2ecf20Sopenharmony_ci udelay(2); 768c2ecf20Sopenharmony_ci gpio_free(workdata->sync_gpio); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(100); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci do { 818c2ecf20Sopenharmony_ci regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback); 828c2ecf20Sopenharmony_ci if (readback & TEGRA20_AC97_STATUS1_CODEC1_RDY) 838c2ecf20Sopenharmony_ci break; 848c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 858c2ecf20Sopenharmony_ci } while (!time_after(jiffies, timeout)); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic unsigned short tegra20_ac97_codec_read(struct snd_ac97 *ac97_snd, 898c2ecf20Sopenharmony_ci unsigned short reg) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci u32 readback; 928c2ecf20Sopenharmony_ci unsigned long timeout; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci regmap_write(workdata->regmap, TEGRA20_AC97_CMD, 958c2ecf20Sopenharmony_ci (((reg | 0x80) << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) & 968c2ecf20Sopenharmony_ci TEGRA20_AC97_CMD_CMD_ADDR_MASK) | 978c2ecf20Sopenharmony_ci TEGRA20_AC97_CMD_BUSY); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(100); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci do { 1028c2ecf20Sopenharmony_ci regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback); 1038c2ecf20Sopenharmony_ci if (readback & TEGRA20_AC97_STATUS1_STA_VALID1) 1048c2ecf20Sopenharmony_ci break; 1058c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 1068c2ecf20Sopenharmony_ci } while (!time_after(jiffies, timeout)); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return ((readback & TEGRA20_AC97_STATUS1_STA_DATA1_MASK) >> 1098c2ecf20Sopenharmony_ci TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void tegra20_ac97_codec_write(struct snd_ac97 *ac97_snd, 1138c2ecf20Sopenharmony_ci unsigned short reg, unsigned short val) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci u32 readback; 1168c2ecf20Sopenharmony_ci unsigned long timeout; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci regmap_write(workdata->regmap, TEGRA20_AC97_CMD, 1198c2ecf20Sopenharmony_ci ((reg << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) & 1208c2ecf20Sopenharmony_ci TEGRA20_AC97_CMD_CMD_ADDR_MASK) | 1218c2ecf20Sopenharmony_ci ((val << TEGRA20_AC97_CMD_CMD_DATA_SHIFT) & 1228c2ecf20Sopenharmony_ci TEGRA20_AC97_CMD_CMD_DATA_MASK) | 1238c2ecf20Sopenharmony_ci TEGRA20_AC97_CMD_BUSY); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(100); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci do { 1288c2ecf20Sopenharmony_ci regmap_read(workdata->regmap, TEGRA20_AC97_CMD, &readback); 1298c2ecf20Sopenharmony_ci if (!(readback & TEGRA20_AC97_CMD_BUSY)) 1308c2ecf20Sopenharmony_ci break; 1318c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 1328c2ecf20Sopenharmony_ci } while (!time_after(jiffies, timeout)); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic struct snd_ac97_bus_ops tegra20_ac97_ops = { 1368c2ecf20Sopenharmony_ci .read = tegra20_ac97_codec_read, 1378c2ecf20Sopenharmony_ci .write = tegra20_ac97_codec_write, 1388c2ecf20Sopenharmony_ci .reset = tegra20_ac97_codec_reset, 1398c2ecf20Sopenharmony_ci .warm_reset = tegra20_ac97_codec_warm_reset, 1408c2ecf20Sopenharmony_ci}; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic inline void tegra20_ac97_start_playback(struct tegra20_ac97 *ac97) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR, 1458c2ecf20Sopenharmony_ci TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN, 1468c2ecf20Sopenharmony_ci TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci regmap_update_bits(ac97->regmap, TEGRA20_AC97_CTRL, 1498c2ecf20Sopenharmony_ci TEGRA20_AC97_CTRL_PCM_DAC_EN | 1508c2ecf20Sopenharmony_ci TEGRA20_AC97_CTRL_STM_EN, 1518c2ecf20Sopenharmony_ci TEGRA20_AC97_CTRL_PCM_DAC_EN | 1528c2ecf20Sopenharmony_ci TEGRA20_AC97_CTRL_STM_EN); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic inline void tegra20_ac97_stop_playback(struct tegra20_ac97 *ac97) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR, 1588c2ecf20Sopenharmony_ci TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN, 0); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci regmap_update_bits(ac97->regmap, TEGRA20_AC97_CTRL, 1618c2ecf20Sopenharmony_ci TEGRA20_AC97_CTRL_PCM_DAC_EN, 0); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic inline void tegra20_ac97_start_capture(struct tegra20_ac97 *ac97) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR, 1678c2ecf20Sopenharmony_ci TEGRA20_AC97_FIFO_SCR_REC_FULL_EN, 1688c2ecf20Sopenharmony_ci TEGRA20_AC97_FIFO_SCR_REC_FULL_EN); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic inline void tegra20_ac97_stop_capture(struct tegra20_ac97 *ac97) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR, 1748c2ecf20Sopenharmony_ci TEGRA20_AC97_FIFO_SCR_REC_FULL_EN, 0); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic int tegra20_ac97_trigger(struct snd_pcm_substream *substream, int cmd, 1788c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci switch (cmd) { 1838c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 1848c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 1858c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 1868c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 1878c2ecf20Sopenharmony_ci tegra20_ac97_start_playback(ac97); 1888c2ecf20Sopenharmony_ci else 1898c2ecf20Sopenharmony_ci tegra20_ac97_start_capture(ac97); 1908c2ecf20Sopenharmony_ci break; 1918c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 1928c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 1938c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 1948c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 1958c2ecf20Sopenharmony_ci tegra20_ac97_stop_playback(ac97); 1968c2ecf20Sopenharmony_ci else 1978c2ecf20Sopenharmony_ci tegra20_ac97_stop_capture(ac97); 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci default: 2008c2ecf20Sopenharmony_ci return -EINVAL; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return 0; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops tegra20_ac97_dai_ops = { 2078c2ecf20Sopenharmony_ci .trigger = tegra20_ac97_trigger, 2088c2ecf20Sopenharmony_ci}; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int tegra20_ac97_probe(struct snd_soc_dai *dai) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci dai->capture_dma_data = &ac97->capture_dma_data; 2158c2ecf20Sopenharmony_ci dai->playback_dma_data = &ac97->playback_dma_data; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver tegra20_ac97_dai = { 2218c2ecf20Sopenharmony_ci .name = "tegra-ac97-pcm", 2228c2ecf20Sopenharmony_ci .probe = tegra20_ac97_probe, 2238c2ecf20Sopenharmony_ci .playback = { 2248c2ecf20Sopenharmony_ci .stream_name = "PCM Playback", 2258c2ecf20Sopenharmony_ci .channels_min = 2, 2268c2ecf20Sopenharmony_ci .channels_max = 2, 2278c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 2288c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 2298c2ecf20Sopenharmony_ci }, 2308c2ecf20Sopenharmony_ci .capture = { 2318c2ecf20Sopenharmony_ci .stream_name = "PCM Capture", 2328c2ecf20Sopenharmony_ci .channels_min = 2, 2338c2ecf20Sopenharmony_ci .channels_max = 2, 2348c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 2358c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 2368c2ecf20Sopenharmony_ci }, 2378c2ecf20Sopenharmony_ci .ops = &tegra20_ac97_dai_ops, 2388c2ecf20Sopenharmony_ci}; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver tegra20_ac97_component = { 2418c2ecf20Sopenharmony_ci .name = DRV_NAME, 2428c2ecf20Sopenharmony_ci}; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic bool tegra20_ac97_wr_rd_reg(struct device *dev, unsigned int reg) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci switch (reg) { 2478c2ecf20Sopenharmony_ci case TEGRA20_AC97_CTRL: 2488c2ecf20Sopenharmony_ci case TEGRA20_AC97_CMD: 2498c2ecf20Sopenharmony_ci case TEGRA20_AC97_STATUS1: 2508c2ecf20Sopenharmony_ci case TEGRA20_AC97_FIFO1_SCR: 2518c2ecf20Sopenharmony_ci case TEGRA20_AC97_FIFO_TX1: 2528c2ecf20Sopenharmony_ci case TEGRA20_AC97_FIFO_RX1: 2538c2ecf20Sopenharmony_ci return true; 2548c2ecf20Sopenharmony_ci default: 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return false; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic bool tegra20_ac97_volatile_reg(struct device *dev, unsigned int reg) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci switch (reg) { 2648c2ecf20Sopenharmony_ci case TEGRA20_AC97_STATUS1: 2658c2ecf20Sopenharmony_ci case TEGRA20_AC97_FIFO1_SCR: 2668c2ecf20Sopenharmony_ci case TEGRA20_AC97_FIFO_TX1: 2678c2ecf20Sopenharmony_ci case TEGRA20_AC97_FIFO_RX1: 2688c2ecf20Sopenharmony_ci return true; 2698c2ecf20Sopenharmony_ci default: 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return false; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic bool tegra20_ac97_precious_reg(struct device *dev, unsigned int reg) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci switch (reg) { 2798c2ecf20Sopenharmony_ci case TEGRA20_AC97_FIFO_TX1: 2808c2ecf20Sopenharmony_ci case TEGRA20_AC97_FIFO_RX1: 2818c2ecf20Sopenharmony_ci return true; 2828c2ecf20Sopenharmony_ci default: 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci return false; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic const struct regmap_config tegra20_ac97_regmap_config = { 2908c2ecf20Sopenharmony_ci .reg_bits = 32, 2918c2ecf20Sopenharmony_ci .reg_stride = 4, 2928c2ecf20Sopenharmony_ci .val_bits = 32, 2938c2ecf20Sopenharmony_ci .max_register = TEGRA20_AC97_FIFO_RX1, 2948c2ecf20Sopenharmony_ci .writeable_reg = tegra20_ac97_wr_rd_reg, 2958c2ecf20Sopenharmony_ci .readable_reg = tegra20_ac97_wr_rd_reg, 2968c2ecf20Sopenharmony_ci .volatile_reg = tegra20_ac97_volatile_reg, 2978c2ecf20Sopenharmony_ci .precious_reg = tegra20_ac97_precious_reg, 2988c2ecf20Sopenharmony_ci .cache_type = REGCACHE_FLAT, 2998c2ecf20Sopenharmony_ci}; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic int tegra20_ac97_platform_probe(struct platform_device *pdev) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci struct tegra20_ac97 *ac97; 3048c2ecf20Sopenharmony_ci struct resource *mem; 3058c2ecf20Sopenharmony_ci void __iomem *regs; 3068c2ecf20Sopenharmony_ci int ret = 0; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci ac97 = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_ac97), 3098c2ecf20Sopenharmony_ci GFP_KERNEL); 3108c2ecf20Sopenharmony_ci if (!ac97) { 3118c2ecf20Sopenharmony_ci ret = -ENOMEM; 3128c2ecf20Sopenharmony_ci goto err; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci dev_set_drvdata(&pdev->dev, ac97); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci ac97->clk_ac97 = devm_clk_get(&pdev->dev, NULL); 3178c2ecf20Sopenharmony_ci if (IS_ERR(ac97->clk_ac97)) { 3188c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can't retrieve ac97 clock\n"); 3198c2ecf20Sopenharmony_ci ret = PTR_ERR(ac97->clk_ac97); 3208c2ecf20Sopenharmony_ci goto err; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3248c2ecf20Sopenharmony_ci regs = devm_ioremap_resource(&pdev->dev, mem); 3258c2ecf20Sopenharmony_ci if (IS_ERR(regs)) { 3268c2ecf20Sopenharmony_ci ret = PTR_ERR(regs); 3278c2ecf20Sopenharmony_ci goto err_clk_put; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci ac97->regmap = devm_regmap_init_mmio(&pdev->dev, regs, 3318c2ecf20Sopenharmony_ci &tegra20_ac97_regmap_config); 3328c2ecf20Sopenharmony_ci if (IS_ERR(ac97->regmap)) { 3338c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "regmap init failed\n"); 3348c2ecf20Sopenharmony_ci ret = PTR_ERR(ac97->regmap); 3358c2ecf20Sopenharmony_ci goto err_clk_put; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci ac97->reset_gpio = of_get_named_gpio(pdev->dev.of_node, 3398c2ecf20Sopenharmony_ci "nvidia,codec-reset-gpio", 0); 3408c2ecf20Sopenharmony_ci if (gpio_is_valid(ac97->reset_gpio)) { 3418c2ecf20Sopenharmony_ci ret = devm_gpio_request_one(&pdev->dev, ac97->reset_gpio, 3428c2ecf20Sopenharmony_ci GPIOF_OUT_INIT_HIGH, "codec-reset"); 3438c2ecf20Sopenharmony_ci if (ret) { 3448c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not get codec-reset GPIO\n"); 3458c2ecf20Sopenharmony_ci goto err_clk_put; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci } else { 3488c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no codec-reset GPIO supplied\n"); 3498c2ecf20Sopenharmony_ci goto err_clk_put; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci ac97->sync_gpio = of_get_named_gpio(pdev->dev.of_node, 3538c2ecf20Sopenharmony_ci "nvidia,codec-sync-gpio", 0); 3548c2ecf20Sopenharmony_ci if (!gpio_is_valid(ac97->sync_gpio)) { 3558c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no codec-sync GPIO supplied\n"); 3568c2ecf20Sopenharmony_ci goto err_clk_put; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci ac97->capture_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_RX1; 3608c2ecf20Sopenharmony_ci ac97->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 3618c2ecf20Sopenharmony_ci ac97->capture_dma_data.maxburst = 4; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci ac97->playback_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_TX1; 3648c2ecf20Sopenharmony_ci ac97->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 3658c2ecf20Sopenharmony_ci ac97->playback_dma_data.maxburst = 4; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci ret = clk_prepare_enable(ac97->clk_ac97); 3688c2ecf20Sopenharmony_ci if (ret) { 3698c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "clk_enable failed: %d\n", ret); 3708c2ecf20Sopenharmony_ci goto err_clk_put; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci ret = snd_soc_set_ac97_ops(&tegra20_ac97_ops); 3748c2ecf20Sopenharmony_ci if (ret) { 3758c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret); 3768c2ecf20Sopenharmony_ci goto err_clk_disable_unprepare; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci ret = snd_soc_register_component(&pdev->dev, &tegra20_ac97_component, 3808c2ecf20Sopenharmony_ci &tegra20_ac97_dai, 1); 3818c2ecf20Sopenharmony_ci if (ret) { 3828c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); 3838c2ecf20Sopenharmony_ci ret = -ENOMEM; 3848c2ecf20Sopenharmony_ci goto err_clk_disable_unprepare; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci ret = tegra_pcm_platform_register(&pdev->dev); 3888c2ecf20Sopenharmony_ci if (ret) { 3898c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); 3908c2ecf20Sopenharmony_ci goto err_unregister_component; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* XXX: crufty ASoC AC97 API - only one AC97 codec allowed */ 3948c2ecf20Sopenharmony_ci workdata = ac97; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return 0; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cierr_unregister_component: 3998c2ecf20Sopenharmony_ci snd_soc_unregister_component(&pdev->dev); 4008c2ecf20Sopenharmony_cierr_clk_disable_unprepare: 4018c2ecf20Sopenharmony_ci clk_disable_unprepare(ac97->clk_ac97); 4028c2ecf20Sopenharmony_cierr_clk_put: 4038c2ecf20Sopenharmony_cierr: 4048c2ecf20Sopenharmony_ci snd_soc_set_ac97_ops(NULL); 4058c2ecf20Sopenharmony_ci return ret; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic int tegra20_ac97_platform_remove(struct platform_device *pdev) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct tegra20_ac97 *ac97 = dev_get_drvdata(&pdev->dev); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci tegra_pcm_platform_unregister(&pdev->dev); 4138c2ecf20Sopenharmony_ci snd_soc_unregister_component(&pdev->dev); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci clk_disable_unprepare(ac97->clk_ac97); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci snd_soc_set_ac97_ops(NULL); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci return 0; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic const struct of_device_id tegra20_ac97_of_match[] = { 4238c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra20-ac97", }, 4248c2ecf20Sopenharmony_ci {}, 4258c2ecf20Sopenharmony_ci}; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic struct platform_driver tegra20_ac97_driver = { 4288c2ecf20Sopenharmony_ci .driver = { 4298c2ecf20Sopenharmony_ci .name = DRV_NAME, 4308c2ecf20Sopenharmony_ci .of_match_table = tegra20_ac97_of_match, 4318c2ecf20Sopenharmony_ci }, 4328c2ecf20Sopenharmony_ci .probe = tegra20_ac97_platform_probe, 4338c2ecf20Sopenharmony_ci .remove = tegra20_ac97_platform_remove, 4348c2ecf20Sopenharmony_ci}; 4358c2ecf20Sopenharmony_cimodule_platform_driver(tegra20_ac97_driver); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lucas Stach"); 4388c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Tegra20 AC97 ASoC driver"); 4398c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 4408c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRV_NAME); 4418c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra20_ac97_of_match); 442