162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Au1000/Au1500/Au1100 AC97C controller driver for ASoC 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * based on the old ALSA driver originally written by 862306a36Sopenharmony_ci * Charles Eidsness <charles@cooper-street.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/device.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/mutex.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/suspend.h> 1962306a36Sopenharmony_ci#include <sound/core.h> 2062306a36Sopenharmony_ci#include <sound/pcm.h> 2162306a36Sopenharmony_ci#include <sound/initval.h> 2262306a36Sopenharmony_ci#include <sound/soc.h> 2362306a36Sopenharmony_ci#include <asm/mach-au1x00/au1000.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "psc.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* register offsets and bits */ 2862306a36Sopenharmony_ci#define AC97_CONFIG 0x00 2962306a36Sopenharmony_ci#define AC97_STATUS 0x04 3062306a36Sopenharmony_ci#define AC97_DATA 0x08 3162306a36Sopenharmony_ci#define AC97_CMDRESP 0x0c 3262306a36Sopenharmony_ci#define AC97_ENABLE 0x10 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define CFG_RC(x) (((x) & 0x3ff) << 13) /* valid rx slots mask */ 3562306a36Sopenharmony_ci#define CFG_XS(x) (((x) & 0x3ff) << 3) /* valid tx slots mask */ 3662306a36Sopenharmony_ci#define CFG_SG (1 << 2) /* sync gate */ 3762306a36Sopenharmony_ci#define CFG_SN (1 << 1) /* sync control */ 3862306a36Sopenharmony_ci#define CFG_RS (1 << 0) /* acrst# control */ 3962306a36Sopenharmony_ci#define STAT_XU (1 << 11) /* tx underflow */ 4062306a36Sopenharmony_ci#define STAT_XO (1 << 10) /* tx overflow */ 4162306a36Sopenharmony_ci#define STAT_RU (1 << 9) /* rx underflow */ 4262306a36Sopenharmony_ci#define STAT_RO (1 << 8) /* rx overflow */ 4362306a36Sopenharmony_ci#define STAT_RD (1 << 7) /* codec ready */ 4462306a36Sopenharmony_ci#define STAT_CP (1 << 6) /* command pending */ 4562306a36Sopenharmony_ci#define STAT_TE (1 << 4) /* tx fifo empty */ 4662306a36Sopenharmony_ci#define STAT_TF (1 << 3) /* tx fifo full */ 4762306a36Sopenharmony_ci#define STAT_RE (1 << 1) /* rx fifo empty */ 4862306a36Sopenharmony_ci#define STAT_RF (1 << 0) /* rx fifo full */ 4962306a36Sopenharmony_ci#define CMD_SET_DATA(x) (((x) & 0xffff) << 16) 5062306a36Sopenharmony_ci#define CMD_GET_DATA(x) ((x) & 0xffff) 5162306a36Sopenharmony_ci#define CMD_READ (1 << 7) 5262306a36Sopenharmony_ci#define CMD_WRITE (0 << 7) 5362306a36Sopenharmony_ci#define CMD_IDX(x) ((x) & 0x7f) 5462306a36Sopenharmony_ci#define EN_D (1 << 1) /* DISable bit */ 5562306a36Sopenharmony_ci#define EN_CE (1 << 0) /* clock enable bit */ 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* how often to retry failed codec register reads/writes */ 5862306a36Sopenharmony_ci#define AC97_RW_RETRIES 5 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define AC97_RATES \ 6162306a36Sopenharmony_ci SNDRV_PCM_RATE_CONTINUOUS 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define AC97_FMTS \ 6462306a36Sopenharmony_ci (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* instance data. There can be only one, MacLeod!!!!, fortunately there IS only 6762306a36Sopenharmony_ci * once AC97C on early Alchemy chips. The newer ones aren't so lucky. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_cistatic struct au1xpsc_audio_data *ac97c_workdata; 7062306a36Sopenharmony_ci#define ac97_to_ctx(x) ac97c_workdata 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic inline unsigned long RD(struct au1xpsc_audio_data *ctx, int reg) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci return __raw_readl(ctx->mmio + reg); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic inline void WR(struct au1xpsc_audio_data *ctx, int reg, unsigned long v) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci __raw_writel(v, ctx->mmio + reg); 8062306a36Sopenharmony_ci wmb(); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic unsigned short au1xac97c_ac97_read(struct snd_ac97 *ac97, 8462306a36Sopenharmony_ci unsigned short r) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97); 8762306a36Sopenharmony_ci unsigned int tmo, retry; 8862306a36Sopenharmony_ci unsigned long data; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci data = ~0; 9162306a36Sopenharmony_ci retry = AC97_RW_RETRIES; 9262306a36Sopenharmony_ci do { 9362306a36Sopenharmony_ci mutex_lock(&ctx->lock); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci tmo = 6; 9662306a36Sopenharmony_ci while ((RD(ctx, AC97_STATUS) & STAT_CP) && --tmo) 9762306a36Sopenharmony_ci udelay(21); /* wait an ac97 frame time */ 9862306a36Sopenharmony_ci if (!tmo) { 9962306a36Sopenharmony_ci pr_debug("ac97rd timeout #1\n"); 10062306a36Sopenharmony_ci goto next; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci WR(ctx, AC97_CMDRESP, CMD_IDX(r) | CMD_READ); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* stupid errata: data is only valid for 21us, so 10662306a36Sopenharmony_ci * poll, Forrest, poll... 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci tmo = 0x10000; 10962306a36Sopenharmony_ci while ((RD(ctx, AC97_STATUS) & STAT_CP) && --tmo) 11062306a36Sopenharmony_ci asm volatile ("nop"); 11162306a36Sopenharmony_ci data = RD(ctx, AC97_CMDRESP); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (!tmo) 11462306a36Sopenharmony_ci pr_debug("ac97rd timeout #2\n"); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cinext: 11762306a36Sopenharmony_ci mutex_unlock(&ctx->lock); 11862306a36Sopenharmony_ci } while (--retry && !tmo); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci pr_debug("AC97RD %04x %04lx %d\n", r, data, retry); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return retry ? data & 0xffff : 0xffff; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic void au1xac97c_ac97_write(struct snd_ac97 *ac97, unsigned short r, 12662306a36Sopenharmony_ci unsigned short v) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97); 12962306a36Sopenharmony_ci unsigned int tmo, retry; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci retry = AC97_RW_RETRIES; 13262306a36Sopenharmony_ci do { 13362306a36Sopenharmony_ci mutex_lock(&ctx->lock); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci for (tmo = 5; (RD(ctx, AC97_STATUS) & STAT_CP) && tmo; tmo--) 13662306a36Sopenharmony_ci udelay(21); 13762306a36Sopenharmony_ci if (!tmo) { 13862306a36Sopenharmony_ci pr_debug("ac97wr timeout #1\n"); 13962306a36Sopenharmony_ci goto next; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci WR(ctx, AC97_CMDRESP, CMD_WRITE | CMD_IDX(r) | CMD_SET_DATA(v)); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci for (tmo = 10; (RD(ctx, AC97_STATUS) & STAT_CP) && tmo; tmo--) 14562306a36Sopenharmony_ci udelay(21); 14662306a36Sopenharmony_ci if (!tmo) 14762306a36Sopenharmony_ci pr_debug("ac97wr timeout #2\n"); 14862306a36Sopenharmony_cinext: 14962306a36Sopenharmony_ci mutex_unlock(&ctx->lock); 15062306a36Sopenharmony_ci } while (--retry && !tmo); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci pr_debug("AC97WR %04x %04x %d\n", r, v, retry); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void au1xac97c_ac97_warm_reset(struct snd_ac97 *ac97) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci WR(ctx, AC97_CONFIG, ctx->cfg | CFG_SG | CFG_SN); 16062306a36Sopenharmony_ci msleep(20); 16162306a36Sopenharmony_ci WR(ctx, AC97_CONFIG, ctx->cfg | CFG_SG); 16262306a36Sopenharmony_ci WR(ctx, AC97_CONFIG, ctx->cfg); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic void au1xac97c_ac97_cold_reset(struct snd_ac97 *ac97) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97); 16862306a36Sopenharmony_ci int i; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci WR(ctx, AC97_CONFIG, ctx->cfg | CFG_RS); 17162306a36Sopenharmony_ci msleep(500); 17262306a36Sopenharmony_ci WR(ctx, AC97_CONFIG, ctx->cfg); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* wait for codec ready */ 17562306a36Sopenharmony_ci i = 50; 17662306a36Sopenharmony_ci while (((RD(ctx, AC97_STATUS) & STAT_RD) == 0) && --i) 17762306a36Sopenharmony_ci msleep(20); 17862306a36Sopenharmony_ci if (!i) 17962306a36Sopenharmony_ci printk(KERN_ERR "ac97c: codec not ready after cold reset\n"); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/* AC97 controller operations */ 18362306a36Sopenharmony_cistatic struct snd_ac97_bus_ops ac97c_bus_ops = { 18462306a36Sopenharmony_ci .read = au1xac97c_ac97_read, 18562306a36Sopenharmony_ci .write = au1xac97c_ac97_write, 18662306a36Sopenharmony_ci .reset = au1xac97c_ac97_cold_reset, 18762306a36Sopenharmony_ci .warm_reset = au1xac97c_ac97_warm_reset, 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int alchemy_ac97c_startup(struct snd_pcm_substream *substream, 19162306a36Sopenharmony_ci struct snd_soc_dai *dai) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai); 19462306a36Sopenharmony_ci snd_soc_dai_set_dma_data(dai, substream, &ctx->dmaids[0]); 19562306a36Sopenharmony_ci return 0; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int au1xac97c_dai_probe(struct snd_soc_dai *dai) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci return ac97c_workdata ? 0 : -ENODEV; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic const struct snd_soc_dai_ops alchemy_ac97c_ops = { 20462306a36Sopenharmony_ci .probe = au1xac97c_dai_probe, 20562306a36Sopenharmony_ci .startup = alchemy_ac97c_startup, 20662306a36Sopenharmony_ci}; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic struct snd_soc_dai_driver au1xac97c_dai_driver = { 20962306a36Sopenharmony_ci .name = "alchemy-ac97c", 21062306a36Sopenharmony_ci .playback = { 21162306a36Sopenharmony_ci .rates = AC97_RATES, 21262306a36Sopenharmony_ci .formats = AC97_FMTS, 21362306a36Sopenharmony_ci .channels_min = 2, 21462306a36Sopenharmony_ci .channels_max = 2, 21562306a36Sopenharmony_ci }, 21662306a36Sopenharmony_ci .capture = { 21762306a36Sopenharmony_ci .rates = AC97_RATES, 21862306a36Sopenharmony_ci .formats = AC97_FMTS, 21962306a36Sopenharmony_ci .channels_min = 2, 22062306a36Sopenharmony_ci .channels_max = 2, 22162306a36Sopenharmony_ci }, 22262306a36Sopenharmony_ci .ops = &alchemy_ac97c_ops, 22362306a36Sopenharmony_ci}; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic const struct snd_soc_component_driver au1xac97c_component = { 22662306a36Sopenharmony_ci .name = "au1xac97c", 22762306a36Sopenharmony_ci .legacy_dai_naming = 1, 22862306a36Sopenharmony_ci}; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic int au1xac97c_drvprobe(struct platform_device *pdev) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci int ret; 23362306a36Sopenharmony_ci struct resource *iores, *dmares; 23462306a36Sopenharmony_ci struct au1xpsc_audio_data *ctx; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); 23762306a36Sopenharmony_ci if (!ctx) 23862306a36Sopenharmony_ci return -ENOMEM; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci mutex_init(&ctx->lock); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); 24362306a36Sopenharmony_ci if (!iores) 24462306a36Sopenharmony_ci return -ENODEV; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (!devm_request_mem_region(&pdev->dev, iores->start, 24762306a36Sopenharmony_ci resource_size(iores), 24862306a36Sopenharmony_ci pdev->name)) 24962306a36Sopenharmony_ci return -EBUSY; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci ctx->mmio = devm_ioremap(&pdev->dev, iores->start, 25262306a36Sopenharmony_ci resource_size(iores)); 25362306a36Sopenharmony_ci if (!ctx->mmio) 25462306a36Sopenharmony_ci return -EBUSY; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); 25762306a36Sopenharmony_ci if (!dmares) 25862306a36Sopenharmony_ci return -EBUSY; 25962306a36Sopenharmony_ci ctx->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = dmares->start; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci dmares = platform_get_resource(pdev, IORESOURCE_DMA, 1); 26262306a36Sopenharmony_ci if (!dmares) 26362306a36Sopenharmony_ci return -EBUSY; 26462306a36Sopenharmony_ci ctx->dmaids[SNDRV_PCM_STREAM_CAPTURE] = dmares->start; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* switch it on */ 26762306a36Sopenharmony_ci WR(ctx, AC97_ENABLE, EN_D | EN_CE); 26862306a36Sopenharmony_ci WR(ctx, AC97_ENABLE, EN_CE); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci ctx->cfg = CFG_RC(3) | CFG_XS(3); 27162306a36Sopenharmony_ci WR(ctx, AC97_CONFIG, ctx->cfg); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci platform_set_drvdata(pdev, ctx); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci ret = snd_soc_set_ac97_ops(&ac97c_bus_ops); 27662306a36Sopenharmony_ci if (ret) 27762306a36Sopenharmony_ci return ret; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci ret = snd_soc_register_component(&pdev->dev, &au1xac97c_component, 28062306a36Sopenharmony_ci &au1xac97c_dai_driver, 1); 28162306a36Sopenharmony_ci if (ret) 28262306a36Sopenharmony_ci return ret; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci ac97c_workdata = ctx; 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic void au1xac97c_drvremove(struct platform_device *pdev) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci snd_soc_unregister_component(&pdev->dev); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci WR(ctx, AC97_ENABLE, EN_D); /* clock off, disable */ 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci ac97c_workdata = NULL; /* MDEV */ 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci#ifdef CONFIG_PM 30062306a36Sopenharmony_cistatic int au1xac97c_drvsuspend(struct device *dev) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci WR(ctx, AC97_ENABLE, EN_D); /* clock off, disable */ 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return 0; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic int au1xac97c_drvresume(struct device *dev) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci WR(ctx, AC97_ENABLE, EN_D | EN_CE); 31462306a36Sopenharmony_ci WR(ctx, AC97_ENABLE, EN_CE); 31562306a36Sopenharmony_ci WR(ctx, AC97_CONFIG, ctx->cfg); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic const struct dev_pm_ops au1xpscac97_pmops = { 32162306a36Sopenharmony_ci .suspend = au1xac97c_drvsuspend, 32262306a36Sopenharmony_ci .resume = au1xac97c_drvresume, 32362306a36Sopenharmony_ci}; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci#define AU1XPSCAC97_PMOPS (&au1xpscac97_pmops) 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci#else 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci#define AU1XPSCAC97_PMOPS NULL 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci#endif 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic struct platform_driver au1xac97c_driver = { 33462306a36Sopenharmony_ci .driver = { 33562306a36Sopenharmony_ci .name = "alchemy-ac97c", 33662306a36Sopenharmony_ci .pm = AU1XPSCAC97_PMOPS, 33762306a36Sopenharmony_ci }, 33862306a36Sopenharmony_ci .probe = au1xac97c_drvprobe, 33962306a36Sopenharmony_ci .remove_new = au1xac97c_drvremove, 34062306a36Sopenharmony_ci}; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cimodule_platform_driver(au1xac97c_driver); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 34562306a36Sopenharmony_ciMODULE_DESCRIPTION("Au1000/1500/1100 AC97C ASoC driver"); 34662306a36Sopenharmony_ciMODULE_AUTHOR("Manuel Lauss"); 347