1 // SPDX-License-Identifier: GPL-2.0+159 2 /* 3 * Take from dc tegra_ahub.c 4 * 5 * Copyright 2018 Google LLC 6 */ 7 8 #define LOG_CATEGORY UCLASS_MISC 9 10 #include <common.h> 11 #include <dm.h> 12 #include <i2s.h> 13 #include <log.h> 14 #include <misc.h> 15 #include <asm/io.h> 16 #include <asm/arch-tegra/tegra_ahub.h> 17 #include <asm/arch-tegra/tegra_i2s.h> 18 #include "tegra_i2s_priv.h" 19 20 struct tegra_ahub_priv { 21 struct apbif_regs *apbif_regs; 22 struct xbar_regs *xbar_regs; 23 u32 full_mask; 24 int capacity_words; /* FIFO capacity in words */ 25 26 /* 27 * This is unset intially, but is set by tegra_ahub_ioctl() called 28 * from the misc_ioctl() in tegra_sound_probe() 29 */ 30 struct udevice *i2s; 31 struct udevice *dma; 32 }; 33 34 static int tegra_ahub_xbar_enable_i2s(struct xbar_regs *regs, int i2s_id) 35 { 36 /* 37 * Enables I2S as the receiver of APBIF by writing APBIF_TX0 (0x01) to 38 * the rx0 register 39 */ 40 switch (i2s_id) { 41 case 0: 42 writel(1, ®s->i2s0_rx0); 43 break; 44 case 1: 45 writel(1, ®s->i2s1_rx0); 46 break; 47 case 2: 48 writel(1, ®s->i2s2_rx0); 49 break; 50 case 3: 51 writel(1, ®s->i2s3_rx0); 52 break; 53 case 4: 54 writel(1, ®s->i2s4_rx0); 55 break; 56 default: 57 log_err("Invalid I2S component id: %d\n", i2s_id); 58 return -EINVAL; 59 } 60 return 0; 61 } 62 63 static int tegra_ahub_apbif_is_full(struct udevice *dev) 64 { 65 struct tegra_ahub_priv *priv = dev_get_priv(dev); 66 67 return readl(&priv->apbif_regs->apbdma_live_stat) & priv->full_mask; 68 } 69 70 /** 71 * tegra_ahub_wait_for_space() - Wait for space in the FIFO 72 * 73 * @return 0 if OK, -ETIMEDOUT if no space was available in time 74 */ 75 static int tegra_ahub_wait_for_space(struct udevice *dev) 76 { 77 int i = 100000; 78 ulong start; 79 80 /* Busy-wait initially, since this should take almost no time */ 81 while (i--) { 82 if (!tegra_ahub_apbif_is_full(dev)) 83 return 0; 84 } 85 86 /* Failed, so do a slower loop for 100ms */ 87 start = get_timer(0); 88 while (tegra_ahub_apbif_is_full(dev)) { 89 if (get_timer(start) > 100) 90 return -ETIMEDOUT; 91 } 92 93 return 0; 94 } 95 96 static int tegra_ahub_apbif_send(struct udevice *dev, int offset, 97 const void *buf, int len) 98 { 99 struct tegra_ahub_priv *priv = dev_get_priv(dev); 100 const u32 *data = (const u32 *)buf; 101 ssize_t written = 0; 102 103 if (len % sizeof(*data)) { 104 log_err("Data size (%zd) must be aligned to %zd.\n", len, 105 sizeof(*data)); 106 return -EFAULT; 107 } 108 while (written < len) { 109 int ret = tegra_ahub_wait_for_space(dev); 110 111 if (ret) 112 return ret; 113 114 writel(*data++, &priv->apbif_regs->channel0_txfifo); 115 written += sizeof(*data); 116 } 117 118 return written; 119 } 120 121 static void tegra_ahub_apbif_set_cif(struct udevice *dev, u32 value) 122 { 123 struct tegra_ahub_priv *priv = dev_get_priv(dev); 124 125 writel(value, &priv->apbif_regs->channel0_cif_tx0_ctrl); 126 } 127 128 static void tegra_ahub_apbif_enable_channel0(struct udevice *dev, 129 int fifo_threshold) 130 { 131 struct tegra_ahub_priv *priv = dev_get_priv(dev); 132 133 u32 ctrl = TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_EN | 134 TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_16 | 135 TEGRA_AHUB_CHANNEL_CTRL_TX_EN; 136 137 fifo_threshold--; /* fifo_threshold starts from 1 */ 138 ctrl |= (fifo_threshold << TEGRA_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT); 139 writel(ctrl, &priv->apbif_regs->channel0_ctrl); 140 } 141 142 static u32 tegra_ahub_get_cif(bool is_receive, uint channels, 143 uint bits_per_sample, uint fifo_threshold) 144 { 145 uint audio_bits = (bits_per_sample >> 2) - 1; 146 u32 val; 147 148 channels--; /* Channels in CIF starts from 1 */ 149 fifo_threshold--; /* FIFO threshold starts from 1 */ 150 /* Assume input and output are always using same channel / bits */ 151 val = channels << TEGRA_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT | 152 channels << TEGRA_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT | 153 audio_bits << TEGRA_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT | 154 audio_bits << TEGRA_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT | 155 fifo_threshold << TEGRA_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT | 156 (is_receive ? TEGRA_AUDIOCIF_DIRECTION_RX << 157 TEGRA_AUDIOCIF_CTRL_DIRECTION_SHIFT : 0); 158 159 return val; 160 } 161 162 static int tegra_ahub_enable(struct udevice *dev) 163 { 164 struct tegra_ahub_priv *priv = dev_get_priv(dev); 165 struct i2s_uc_priv *uc_priv = dev_get_uclass_priv(priv->i2s); 166 u32 cif_ctrl = 0; 167 int ret; 168 169 /* We use APBIF channel0 as a sender */ 170 priv->full_mask = TEGRA_AHUB_APBDMA_LIVE_STATUS_CH0_TX_CIF_FIFO_FULL; 171 priv->capacity_words = 8; 172 173 /* 174 * FIFO is inactive until (fifo_threshold) of words are sent. For 175 * better performance, we want to set it to half of capacity. 176 */ 177 u32 fifo_threshold = priv->capacity_words / 2; 178 179 /* 180 * Setup audio client interface (ACIF): APBIF (channel0) as sender and 181 * I2S as receiver 182 */ 183 cif_ctrl = tegra_ahub_get_cif(true, uc_priv->channels, 184 uc_priv->bitspersample, fifo_threshold); 185 tegra_i2s_set_cif_tx_ctrl(priv->i2s, cif_ctrl); 186 187 cif_ctrl = tegra_ahub_get_cif(false, uc_priv->channels, 188 uc_priv->bitspersample, fifo_threshold); 189 tegra_ahub_apbif_set_cif(dev, cif_ctrl); 190 tegra_ahub_apbif_enable_channel0(dev, fifo_threshold); 191 192 ret = tegra_ahub_xbar_enable_i2s(priv->xbar_regs, uc_priv->id); 193 if (ret) 194 return ret; 195 log_debug("ahub: channels=%d, bitspersample=%d, cif_ctrl=%x, fifo_threshold=%d, id=%d\n", 196 uc_priv->channels, uc_priv->bitspersample, cif_ctrl, 197 fifo_threshold, uc_priv->id); 198 199 return 0; 200 } 201 202 static int tegra_ahub_ioctl(struct udevice *dev, unsigned long request, 203 void *buf) 204 { 205 struct tegra_ahub_priv *priv = dev_get_priv(dev); 206 207 if (request != AHUB_MISCOP_SET_I2S) 208 return -ENOSYS; 209 210 priv->i2s = *(struct udevice **)buf; 211 log_debug("i2s set to '%s'\n", priv->i2s->name); 212 213 return tegra_ahub_enable(dev); 214 } 215 216 static int tegra_ahub_probe(struct udevice *dev) 217 { 218 struct tegra_ahub_priv *priv = dev_get_priv(dev); 219 ulong addr; 220 221 addr = dev_read_addr_index(dev, 0); 222 if (addr == FDT_ADDR_T_NONE) { 223 log_debug("Invalid apbif address\n"); 224 return -EINVAL; 225 } 226 priv->apbif_regs = (struct apbif_regs *)addr; 227 228 addr = dev_read_addr_index(dev, 1); 229 if (addr == FDT_ADDR_T_NONE) { 230 log_debug("Invalid xbar address\n"); 231 return -EINVAL; 232 } 233 priv->xbar_regs = (struct xbar_regs *)addr; 234 log_debug("ahub apbif_regs=%p, xbar_regs=%p\n", priv->apbif_regs, 235 priv->xbar_regs); 236 237 return 0; 238 } 239 240 static struct misc_ops tegra_ahub_ops = { 241 .write = tegra_ahub_apbif_send, 242 .ioctl = tegra_ahub_ioctl, 243 }; 244 245 static const struct udevice_id tegra_ahub_ids[] = { 246 { .compatible = "nvidia,tegra124-ahub" }, 247 { } 248 }; 249 250 U_BOOT_DRIVER(tegra_ahub) = { 251 .name = "tegra_ahub", 252 .id = UCLASS_MISC, 253 .of_match = tegra_ahub_ids, 254 .ops = &tegra_ahub_ops, 255 .probe = tegra_ahub_probe, 256 .priv_auto = sizeof(struct tegra_ahub_priv), 257 }; 258