Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Au1000/Au1500/Au1100 AC97C controller driver for ASoC |
| 3 | * |
| 4 | * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com> |
| 5 | * |
| 6 | * based on the old ALSA driver originally written by |
| 7 | * Charles Eidsness <charles@cooper-street.com> |
| 8 | */ |
| 9 | |
| 10 | #include <linux/init.h> |
| 11 | #include <linux/module.h> |
| 12 | #include <linux/slab.h> |
| 13 | #include <linux/device.h> |
| 14 | #include <linux/delay.h> |
| 15 | #include <linux/mutex.h> |
| 16 | #include <linux/platform_device.h> |
| 17 | #include <linux/suspend.h> |
| 18 | #include <sound/core.h> |
| 19 | #include <sound/pcm.h> |
| 20 | #include <sound/initval.h> |
| 21 | #include <sound/soc.h> |
| 22 | #include <asm/mach-au1x00/au1000.h> |
| 23 | |
| 24 | #include "psc.h" |
| 25 | |
| 26 | /* register offsets and bits */ |
| 27 | #define AC97_CONFIG 0x00 |
| 28 | #define AC97_STATUS 0x04 |
| 29 | #define AC97_DATA 0x08 |
| 30 | #define AC97_CMDRESP 0x0c |
| 31 | #define AC97_ENABLE 0x10 |
| 32 | |
| 33 | #define CFG_RC(x) (((x) & 0x3ff) << 13) /* valid rx slots mask */ |
| 34 | #define CFG_XS(x) (((x) & 0x3ff) << 3) /* valid tx slots mask */ |
| 35 | #define CFG_SG (1 << 2) /* sync gate */ |
| 36 | #define CFG_SN (1 << 1) /* sync control */ |
| 37 | #define CFG_RS (1 << 0) /* acrst# control */ |
| 38 | #define STAT_XU (1 << 11) /* tx underflow */ |
| 39 | #define STAT_XO (1 << 10) /* tx overflow */ |
| 40 | #define STAT_RU (1 << 9) /* rx underflow */ |
| 41 | #define STAT_RO (1 << 8) /* rx overflow */ |
| 42 | #define STAT_RD (1 << 7) /* codec ready */ |
| 43 | #define STAT_CP (1 << 6) /* command pending */ |
| 44 | #define STAT_TE (1 << 4) /* tx fifo empty */ |
| 45 | #define STAT_TF (1 << 3) /* tx fifo full */ |
| 46 | #define STAT_RE (1 << 1) /* rx fifo empty */ |
| 47 | #define STAT_RF (1 << 0) /* rx fifo full */ |
| 48 | #define CMD_SET_DATA(x) (((x) & 0xffff) << 16) |
| 49 | #define CMD_GET_DATA(x) ((x) & 0xffff) |
| 50 | #define CMD_READ (1 << 7) |
| 51 | #define CMD_WRITE (0 << 7) |
| 52 | #define CMD_IDX(x) ((x) & 0x7f) |
| 53 | #define EN_D (1 << 1) /* DISable bit */ |
| 54 | #define EN_CE (1 << 0) /* clock enable bit */ |
| 55 | |
| 56 | /* how often to retry failed codec register reads/writes */ |
| 57 | #define AC97_RW_RETRIES 5 |
| 58 | |
| 59 | #define AC97_RATES \ |
| 60 | SNDRV_PCM_RATE_CONTINUOUS |
| 61 | |
| 62 | #define AC97_FMTS \ |
| 63 | (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE) |
| 64 | |
| 65 | /* instance data. There can be only one, MacLeod!!!!, fortunately there IS only |
| 66 | * once AC97C on early Alchemy chips. The newer ones aren't so lucky. |
| 67 | */ |
| 68 | static struct au1xpsc_audio_data *ac97c_workdata; |
| 69 | #define ac97_to_ctx(x) ac97c_workdata |
| 70 | |
| 71 | static inline unsigned long RD(struct au1xpsc_audio_data *ctx, int reg) |
| 72 | { |
| 73 | return __raw_readl(ctx->mmio + reg); |
| 74 | } |
| 75 | |
| 76 | static inline void WR(struct au1xpsc_audio_data *ctx, int reg, unsigned long v) |
| 77 | { |
| 78 | __raw_writel(v, ctx->mmio + reg); |
| 79 | wmb(); |
| 80 | } |
| 81 | |
| 82 | static unsigned short au1xac97c_ac97_read(struct snd_ac97 *ac97, |
| 83 | unsigned short r) |
| 84 | { |
| 85 | struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97); |
| 86 | unsigned int tmo, retry; |
| 87 | unsigned long data; |
| 88 | |
| 89 | data = ~0; |
| 90 | retry = AC97_RW_RETRIES; |
| 91 | do { |
| 92 | mutex_lock(&ctx->lock); |
| 93 | |
| 94 | tmo = 5; |
| 95 | while ((RD(ctx, AC97_STATUS) & STAT_CP) && tmo--) |
| 96 | udelay(21); /* wait an ac97 frame time */ |
| 97 | if (!tmo) { |
| 98 | pr_debug("ac97rd timeout #1\n"); |
| 99 | goto next; |
| 100 | } |
| 101 | |
| 102 | WR(ctx, AC97_CMDRESP, CMD_IDX(r) | CMD_READ); |
| 103 | |
| 104 | /* stupid errata: data is only valid for 21us, so |
| 105 | * poll, Forrest, poll... |
| 106 | */ |
| 107 | tmo = 0x10000; |
| 108 | while ((RD(ctx, AC97_STATUS) & STAT_CP) && tmo--) |
| 109 | asm volatile ("nop"); |
| 110 | data = RD(ctx, AC97_CMDRESP); |
| 111 | |
| 112 | if (!tmo) |
| 113 | pr_debug("ac97rd timeout #2\n"); |
| 114 | |
| 115 | next: |
| 116 | mutex_unlock(&ctx->lock); |
| 117 | } while (--retry && !tmo); |
| 118 | |
| 119 | pr_debug("AC97RD %04x %04lx %d\n", r, data, retry); |
| 120 | |
| 121 | return retry ? data & 0xffff : 0xffff; |
| 122 | } |
| 123 | |
| 124 | static void au1xac97c_ac97_write(struct snd_ac97 *ac97, unsigned short r, |
| 125 | unsigned short v) |
| 126 | { |
| 127 | struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97); |
| 128 | unsigned int tmo, retry; |
| 129 | |
| 130 | retry = AC97_RW_RETRIES; |
| 131 | do { |
| 132 | mutex_lock(&ctx->lock); |
| 133 | |
| 134 | for (tmo = 5; (RD(ctx, AC97_STATUS) & STAT_CP) && tmo; tmo--) |
| 135 | udelay(21); |
| 136 | if (!tmo) { |
| 137 | pr_debug("ac97wr timeout #1\n"); |
| 138 | goto next; |
| 139 | } |
| 140 | |
| 141 | WR(ctx, AC97_CMDRESP, CMD_WRITE | CMD_IDX(r) | CMD_SET_DATA(v)); |
| 142 | |
| 143 | for (tmo = 10; (RD(ctx, AC97_STATUS) & STAT_CP) && tmo; tmo--) |
| 144 | udelay(21); |
| 145 | if (!tmo) |
| 146 | pr_debug("ac97wr timeout #2\n"); |
| 147 | next: |
| 148 | mutex_unlock(&ctx->lock); |
| 149 | } while (--retry && !tmo); |
| 150 | |
| 151 | pr_debug("AC97WR %04x %04x %d\n", r, v, retry); |
| 152 | } |
| 153 | |
| 154 | static void au1xac97c_ac97_warm_reset(struct snd_ac97 *ac97) |
| 155 | { |
| 156 | struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97); |
| 157 | |
| 158 | WR(ctx, AC97_CONFIG, ctx->cfg | CFG_SG | CFG_SN); |
| 159 | msleep(20); |
| 160 | WR(ctx, AC97_CONFIG, ctx->cfg | CFG_SG); |
| 161 | WR(ctx, AC97_CONFIG, ctx->cfg); |
| 162 | } |
| 163 | |
| 164 | static void au1xac97c_ac97_cold_reset(struct snd_ac97 *ac97) |
| 165 | { |
| 166 | struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97); |
| 167 | int i; |
| 168 | |
| 169 | WR(ctx, AC97_CONFIG, ctx->cfg | CFG_RS); |
| 170 | msleep(500); |
| 171 | WR(ctx, AC97_CONFIG, ctx->cfg); |
| 172 | |
| 173 | /* wait for codec ready */ |
| 174 | i = 50; |
| 175 | while (((RD(ctx, AC97_STATUS) & STAT_RD) == 0) && --i) |
| 176 | msleep(20); |
| 177 | if (!i) |
| 178 | printk(KERN_ERR "ac97c: codec not ready after cold reset\n"); |
| 179 | } |
| 180 | |
| 181 | /* AC97 controller operations */ |
Mark Brown | b047e1c | 2013-06-26 12:45:59 +0100 | [diff] [blame] | 182 | static struct snd_ac97_bus_ops ac97c_bus_ops = { |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 183 | .read = au1xac97c_ac97_read, |
| 184 | .write = au1xac97c_ac97_write, |
| 185 | .reset = au1xac97c_ac97_cold_reset, |
| 186 | .warm_reset = au1xac97c_ac97_warm_reset, |
| 187 | }; |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 188 | |
| 189 | static int alchemy_ac97c_startup(struct snd_pcm_substream *substream, |
| 190 | struct snd_soc_dai *dai) |
| 191 | { |
| 192 | struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai); |
| 193 | snd_soc_dai_set_dma_data(dai, substream, &ctx->dmaids[0]); |
| 194 | return 0; |
| 195 | } |
| 196 | |
Lars-Peter Clausen | 85e7652 | 2011-11-23 11:40:40 +0100 | [diff] [blame] | 197 | static const struct snd_soc_dai_ops alchemy_ac97c_ops = { |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 198 | .startup = alchemy_ac97c_startup, |
| 199 | }; |
| 200 | |
| 201 | static int au1xac97c_dai_probe(struct snd_soc_dai *dai) |
| 202 | { |
| 203 | return ac97c_workdata ? 0 : -ENODEV; |
| 204 | } |
| 205 | |
| 206 | static struct snd_soc_dai_driver au1xac97c_dai_driver = { |
| 207 | .name = "alchemy-ac97c", |
Lars-Peter Clausen | bc26321 | 2014-11-10 22:41:52 +0100 | [diff] [blame] | 208 | .bus_control = true, |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 209 | .probe = au1xac97c_dai_probe, |
| 210 | .playback = { |
| 211 | .rates = AC97_RATES, |
| 212 | .formats = AC97_FMTS, |
| 213 | .channels_min = 2, |
| 214 | .channels_max = 2, |
| 215 | }, |
| 216 | .capture = { |
| 217 | .rates = AC97_RATES, |
| 218 | .formats = AC97_FMTS, |
| 219 | .channels_min = 2, |
| 220 | .channels_max = 2, |
| 221 | }, |
| 222 | .ops = &alchemy_ac97c_ops, |
| 223 | }; |
| 224 | |
Kuninori Morimoto | bbedf1b | 2013-03-21 03:29:34 -0700 | [diff] [blame] | 225 | static const struct snd_soc_component_driver au1xac97c_component = { |
| 226 | .name = "au1xac97c", |
| 227 | }; |
| 228 | |
Bill Pemberton | 5c658be | 2012-12-07 09:26:22 -0500 | [diff] [blame] | 229 | static int au1xac97c_drvprobe(struct platform_device *pdev) |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 230 | { |
| 231 | int ret; |
Julia Lawall | 226d0f2 | 2011-10-18 17:06:39 +0200 | [diff] [blame] | 232 | struct resource *iores, *dmares; |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 233 | struct au1xpsc_audio_data *ctx; |
| 234 | |
Julia Lawall | 6065abf | 2011-12-29 17:51:29 +0100 | [diff] [blame] | 235 | ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 236 | if (!ctx) |
| 237 | return -ENOMEM; |
| 238 | |
| 239 | mutex_init(&ctx->lock); |
| 240 | |
Julia Lawall | 226d0f2 | 2011-10-18 17:06:39 +0200 | [diff] [blame] | 241 | iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
Julia Lawall | 6065abf | 2011-12-29 17:51:29 +0100 | [diff] [blame] | 242 | if (!iores) |
| 243 | return -ENODEV; |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 244 | |
Julia Lawall | 6065abf | 2011-12-29 17:51:29 +0100 | [diff] [blame] | 245 | if (!devm_request_mem_region(&pdev->dev, iores->start, |
| 246 | resource_size(iores), |
| 247 | pdev->name)) |
| 248 | return -EBUSY; |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 249 | |
Julia Lawall | 6065abf | 2011-12-29 17:51:29 +0100 | [diff] [blame] | 250 | ctx->mmio = devm_ioremap_nocache(&pdev->dev, iores->start, |
| 251 | resource_size(iores)); |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 252 | if (!ctx->mmio) |
Julia Lawall | 6065abf | 2011-12-29 17:51:29 +0100 | [diff] [blame] | 253 | return -EBUSY; |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 254 | |
Julia Lawall | 226d0f2 | 2011-10-18 17:06:39 +0200 | [diff] [blame] | 255 | dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); |
| 256 | if (!dmares) |
Julia Lawall | 6065abf | 2011-12-29 17:51:29 +0100 | [diff] [blame] | 257 | return -EBUSY; |
Julia Lawall | 226d0f2 | 2011-10-18 17:06:39 +0200 | [diff] [blame] | 258 | ctx->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = dmares->start; |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 259 | |
Julia Lawall | 226d0f2 | 2011-10-18 17:06:39 +0200 | [diff] [blame] | 260 | dmares = platform_get_resource(pdev, IORESOURCE_DMA, 1); |
| 261 | if (!dmares) |
Julia Lawall | 6065abf | 2011-12-29 17:51:29 +0100 | [diff] [blame] | 262 | return -EBUSY; |
Julia Lawall | 226d0f2 | 2011-10-18 17:06:39 +0200 | [diff] [blame] | 263 | ctx->dmaids[SNDRV_PCM_STREAM_CAPTURE] = dmares->start; |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 264 | |
| 265 | /* switch it on */ |
| 266 | WR(ctx, AC97_ENABLE, EN_D | EN_CE); |
| 267 | WR(ctx, AC97_ENABLE, EN_CE); |
| 268 | |
| 269 | ctx->cfg = CFG_RC(3) | CFG_XS(3); |
| 270 | WR(ctx, AC97_CONFIG, ctx->cfg); |
| 271 | |
| 272 | platform_set_drvdata(pdev, ctx); |
| 273 | |
Mark Brown | b047e1c | 2013-06-26 12:45:59 +0100 | [diff] [blame] | 274 | ret = snd_soc_set_ac97_ops(&ac97c_bus_ops); |
| 275 | if (ret) |
| 276 | return ret; |
| 277 | |
Kuninori Morimoto | bbedf1b | 2013-03-21 03:29:34 -0700 | [diff] [blame] | 278 | ret = snd_soc_register_component(&pdev->dev, &au1xac97c_component, |
| 279 | &au1xac97c_dai_driver, 1); |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 280 | if (ret) |
Julia Lawall | 6065abf | 2011-12-29 17:51:29 +0100 | [diff] [blame] | 281 | return ret; |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 282 | |
| 283 | ac97c_workdata = ctx; |
| 284 | return 0; |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 285 | } |
| 286 | |
Bill Pemberton | 5c658be | 2012-12-07 09:26:22 -0500 | [diff] [blame] | 287 | static int au1xac97c_drvremove(struct platform_device *pdev) |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 288 | { |
| 289 | struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev); |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 290 | |
Kuninori Morimoto | bbedf1b | 2013-03-21 03:29:34 -0700 | [diff] [blame] | 291 | snd_soc_unregister_component(&pdev->dev); |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 292 | |
| 293 | WR(ctx, AC97_ENABLE, EN_D); /* clock off, disable */ |
| 294 | |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 295 | ac97c_workdata = NULL; /* MDEV */ |
| 296 | |
| 297 | return 0; |
| 298 | } |
| 299 | |
| 300 | #ifdef CONFIG_PM |
| 301 | static int au1xac97c_drvsuspend(struct device *dev) |
| 302 | { |
| 303 | struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev); |
| 304 | |
| 305 | WR(ctx, AC97_ENABLE, EN_D); /* clock off, disable */ |
| 306 | |
| 307 | return 0; |
| 308 | } |
| 309 | |
| 310 | static int au1xac97c_drvresume(struct device *dev) |
| 311 | { |
| 312 | struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev); |
| 313 | |
| 314 | WR(ctx, AC97_ENABLE, EN_D | EN_CE); |
| 315 | WR(ctx, AC97_ENABLE, EN_CE); |
| 316 | WR(ctx, AC97_CONFIG, ctx->cfg); |
| 317 | |
| 318 | return 0; |
| 319 | } |
| 320 | |
| 321 | static const struct dev_pm_ops au1xpscac97_pmops = { |
| 322 | .suspend = au1xac97c_drvsuspend, |
| 323 | .resume = au1xac97c_drvresume, |
| 324 | }; |
| 325 | |
| 326 | #define AU1XPSCAC97_PMOPS (&au1xpscac97_pmops) |
| 327 | |
| 328 | #else |
| 329 | |
| 330 | #define AU1XPSCAC97_PMOPS NULL |
| 331 | |
| 332 | #endif |
| 333 | |
| 334 | static struct platform_driver au1xac97c_driver = { |
| 335 | .driver = { |
| 336 | .name = "alchemy-ac97c", |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 337 | .pm = AU1XPSCAC97_PMOPS, |
| 338 | }, |
| 339 | .probe = au1xac97c_drvprobe, |
Bill Pemberton | 5c658be | 2012-12-07 09:26:22 -0500 | [diff] [blame] | 340 | .remove = au1xac97c_drvremove, |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 341 | }; |
| 342 | |
Ralf Baechle | d2ee88d | 2013-07-31 10:15:19 +0200 | [diff] [blame] | 343 | module_platform_driver(au1xac97c_driver); |
Manuel Lauss | b3c70c9 | 2011-07-25 13:44:45 +0200 | [diff] [blame] | 344 | |
| 345 | MODULE_LICENSE("GPL"); |
| 346 | MODULE_DESCRIPTION("Au1000/1500/1100 AC97C ASoC driver"); |
| 347 | MODULE_AUTHOR("Manuel Lauss"); |