1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * tegra20_das.c - Tegra20 DAS driver 4 * 5 * Author: Stephen Warren <swarren@nvidia.com> 6 * Copyright (C) 2010 - NVIDIA, Inc. 7 */ 8 9 #include <linux/device.h> 10 #include <linux/io.h> 11 #include <linux/module.h> 12 #include <linux/platform_device.h> 13 #include <linux/regmap.h> 14 #include <linux/slab.h> 15 #include <sound/soc.h> 16 #include "tegra20_das.h" 17 18 #define DRV_NAME "tegra20-das" 19 20 static struct tegra20_das *das; 21 22 static inline void tegra20_das_write(u32 reg, u32 val) 23 { 24 regmap_write(das->regmap, reg, val); 25 } 26 27 static inline u32 tegra20_das_read(u32 reg) 28 { 29 u32 val; 30 31 regmap_read(das->regmap, reg, &val); 32 return val; 33 } 34 35 int tegra20_das_connect_dap_to_dac(int dap, int dac) 36 { 37 u32 addr; 38 u32 reg; 39 40 if (!das) 41 return -ENODEV; 42 43 addr = TEGRA20_DAS_DAP_CTRL_SEL + 44 (dap * TEGRA20_DAS_DAP_CTRL_SEL_STRIDE); 45 reg = dac << TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P; 46 47 tegra20_das_write(addr, reg); 48 49 return 0; 50 } 51 EXPORT_SYMBOL_GPL(tegra20_das_connect_dap_to_dac); 52 53 int tegra20_das_connect_dap_to_dap(int dap, int otherdap, int master, 54 int sdata1rx, int sdata2rx) 55 { 56 u32 addr; 57 u32 reg; 58 59 if (!das) 60 return -ENODEV; 61 62 addr = TEGRA20_DAS_DAP_CTRL_SEL + 63 (dap * TEGRA20_DAS_DAP_CTRL_SEL_STRIDE); 64 reg = (otherdap << TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P) | 65 (!!sdata2rx << TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P) | 66 (!!sdata1rx << TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P) | 67 (!!master << TEGRA20_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P); 68 69 tegra20_das_write(addr, reg); 70 71 return 0; 72 } 73 EXPORT_SYMBOL_GPL(tegra20_das_connect_dap_to_dap); 74 75 int tegra20_das_connect_dac_to_dap(int dac, int dap) 76 { 77 u32 addr; 78 u32 reg; 79 80 if (!das) 81 return -ENODEV; 82 83 addr = TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL + 84 (dac * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE); 85 reg = dap << TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P | 86 dap << TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P | 87 dap << TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P; 88 89 tegra20_das_write(addr, reg); 90 91 return 0; 92 } 93 EXPORT_SYMBOL_GPL(tegra20_das_connect_dac_to_dap); 94 95 #define LAST_REG(name) \ 96 (TEGRA20_DAS_##name + \ 97 (TEGRA20_DAS_##name##_STRIDE * (TEGRA20_DAS_##name##_COUNT - 1))) 98 99 static bool tegra20_das_wr_rd_reg(struct device *dev, unsigned int reg) 100 { 101 if (reg <= LAST_REG(DAP_CTRL_SEL)) 102 return true; 103 if ((reg >= TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL) && 104 (reg <= LAST_REG(DAC_INPUT_DATA_CLK_SEL))) 105 return true; 106 107 return false; 108 } 109 110 static const struct regmap_config tegra20_das_regmap_config = { 111 .reg_bits = 32, 112 .reg_stride = 4, 113 .val_bits = 32, 114 .max_register = LAST_REG(DAC_INPUT_DATA_CLK_SEL), 115 .writeable_reg = tegra20_das_wr_rd_reg, 116 .readable_reg = tegra20_das_wr_rd_reg, 117 .cache_type = REGCACHE_FLAT, 118 }; 119 120 static int tegra20_das_probe(struct platform_device *pdev) 121 { 122 void __iomem *regs; 123 int ret = 0; 124 125 if (das) 126 return -ENODEV; 127 128 das = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_das), GFP_KERNEL); 129 if (!das) { 130 ret = -ENOMEM; 131 goto err; 132 } 133 das->dev = &pdev->dev; 134 135 regs = devm_platform_ioremap_resource(pdev, 0); 136 if (IS_ERR(regs)) { 137 ret = PTR_ERR(regs); 138 goto err; 139 } 140 141 das->regmap = devm_regmap_init_mmio(&pdev->dev, regs, 142 &tegra20_das_regmap_config); 143 if (IS_ERR(das->regmap)) { 144 dev_err(&pdev->dev, "regmap init failed\n"); 145 ret = PTR_ERR(das->regmap); 146 goto err; 147 } 148 149 ret = tegra20_das_connect_dap_to_dac(TEGRA20_DAS_DAP_ID_1, 150 TEGRA20_DAS_DAP_SEL_DAC1); 151 if (ret) { 152 dev_err(&pdev->dev, "Can't set up DAS DAP connection\n"); 153 goto err; 154 } 155 ret = tegra20_das_connect_dac_to_dap(TEGRA20_DAS_DAC_ID_1, 156 TEGRA20_DAS_DAC_SEL_DAP1); 157 if (ret) { 158 dev_err(&pdev->dev, "Can't set up DAS DAC connection\n"); 159 goto err; 160 } 161 162 ret = tegra20_das_connect_dap_to_dac(TEGRA20_DAS_DAP_ID_3, 163 TEGRA20_DAS_DAP_SEL_DAC3); 164 if (ret) { 165 dev_err(&pdev->dev, "Can't set up DAS DAP connection\n"); 166 goto err; 167 } 168 ret = tegra20_das_connect_dac_to_dap(TEGRA20_DAS_DAC_ID_3, 169 TEGRA20_DAS_DAC_SEL_DAP3); 170 if (ret) { 171 dev_err(&pdev->dev, "Can't set up DAS DAC connection\n"); 172 goto err; 173 } 174 175 platform_set_drvdata(pdev, das); 176 177 return 0; 178 179 err: 180 das = NULL; 181 return ret; 182 } 183 184 static int tegra20_das_remove(struct platform_device *pdev) 185 { 186 if (!das) 187 return -ENODEV; 188 189 das = NULL; 190 191 return 0; 192 } 193 194 static const struct of_device_id tegra20_das_of_match[] = { 195 { .compatible = "nvidia,tegra20-das", }, 196 {}, 197 }; 198 199 static struct platform_driver tegra20_das_driver = { 200 .probe = tegra20_das_probe, 201 .remove = tegra20_das_remove, 202 .driver = { 203 .name = DRV_NAME, 204 .of_match_table = tegra20_das_of_match, 205 }, 206 }; 207 module_platform_driver(tegra20_das_driver); 208 209 MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 210 MODULE_DESCRIPTION("Tegra20 DAS driver"); 211 MODULE_LICENSE("GPL"); 212 MODULE_ALIAS("platform:" DRV_NAME); 213 MODULE_DEVICE_TABLE(of, tegra20_das_of_match); 214