376 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Sound driver for Nintendo 64.4 *5 * Copyright 2021 Lauri Kasanen6 */7 8#include <linux/dma-mapping.h>9#include <linux/init.h>10#include <linux/interrupt.h>11#include <linux/io.h>12#include <linux/log2.h>13#include <linux/module.h>14#include <linux/platform_device.h>15#include <linux/spinlock.h>16 17#include <sound/control.h>18#include <sound/core.h>19#include <sound/initval.h>20#include <sound/pcm.h>21#include <sound/pcm_params.h>22 23MODULE_AUTHOR("Lauri Kasanen <cand@gmx.com>");24MODULE_DESCRIPTION("N64 Audio");25MODULE_LICENSE("GPL");26 27#define AI_NTSC_DACRATE 4868181228#define AI_STATUS_BUSY (1 << 30)29#define AI_STATUS_FULL (1 << 31)30 31#define AI_ADDR_REG 032#define AI_LEN_REG 133#define AI_CONTROL_REG 234#define AI_STATUS_REG 335#define AI_RATE_REG 436#define AI_BITCLOCK_REG 537 38#define MI_INTR_REG 239#define MI_MASK_REG 340 41#define MI_INTR_AI 0x0442 43#define MI_MASK_CLR_AI 0x001044#define MI_MASK_SET_AI 0x002045 46 47struct n64audio {48 u32 __iomem *ai_reg_base;49 u32 __iomem *mi_reg_base;50 51 void *ring_base;52 dma_addr_t ring_base_dma;53 54 struct snd_card *card;55 56 struct {57 struct snd_pcm_substream *substream;58 int pos, nextpos;59 u32 writesize;60 u32 bufsize;61 spinlock_t lock;62 } chan;63};64 65static void n64audio_write_reg(struct n64audio *priv, const u8 reg, const u32 value)66{67 writel(value, priv->ai_reg_base + reg);68}69 70static void n64mi_write_reg(struct n64audio *priv, const u8 reg, const u32 value)71{72 writel(value, priv->mi_reg_base + reg);73}74 75static u32 n64mi_read_reg(struct n64audio *priv, const u8 reg)76{77 return readl(priv->mi_reg_base + reg);78}79 80static void n64audio_push(struct n64audio *priv)81{82 struct snd_pcm_runtime *runtime = priv->chan.substream->runtime;83 unsigned long flags;84 u32 count;85 86 spin_lock_irqsave(&priv->chan.lock, flags);87 88 count = priv->chan.writesize;89 90 memcpy(priv->ring_base + priv->chan.nextpos,91 runtime->dma_area + priv->chan.nextpos, count);92 93 /*94 * The hw registers are double-buffered, and the IRQ fires essentially95 * one period behind. The core only allows one period's distance, so we96 * keep a private DMA buffer to afford two.97 */98 n64audio_write_reg(priv, AI_ADDR_REG, priv->ring_base_dma + priv->chan.nextpos);99 barrier();100 n64audio_write_reg(priv, AI_LEN_REG, count);101 102 priv->chan.nextpos += count;103 priv->chan.nextpos %= priv->chan.bufsize;104 105 runtime->delay = runtime->period_size;106 107 spin_unlock_irqrestore(&priv->chan.lock, flags);108}109 110static irqreturn_t n64audio_isr(int irq, void *dev_id)111{112 struct n64audio *priv = dev_id;113 const u32 intrs = n64mi_read_reg(priv, MI_INTR_REG);114 unsigned long flags;115 116 // Check it's ours117 if (!(intrs & MI_INTR_AI))118 return IRQ_NONE;119 120 n64audio_write_reg(priv, AI_STATUS_REG, 1);121 122 if (priv->chan.substream && snd_pcm_running(priv->chan.substream)) {123 spin_lock_irqsave(&priv->chan.lock, flags);124 125 priv->chan.pos = priv->chan.nextpos;126 127 spin_unlock_irqrestore(&priv->chan.lock, flags);128 129 snd_pcm_period_elapsed(priv->chan.substream);130 if (priv->chan.substream && snd_pcm_running(priv->chan.substream))131 n64audio_push(priv);132 }133 134 return IRQ_HANDLED;135}136 137static const struct snd_pcm_hardware n64audio_pcm_hw = {138 .info = (SNDRV_PCM_INFO_MMAP |139 SNDRV_PCM_INFO_MMAP_VALID |140 SNDRV_PCM_INFO_INTERLEAVED |141 SNDRV_PCM_INFO_BLOCK_TRANSFER),142 .formats = SNDRV_PCM_FMTBIT_S16_BE,143 .rates = SNDRV_PCM_RATE_8000_48000,144 .rate_min = 8000,145 .rate_max = 48000,146 .channels_min = 2,147 .channels_max = 2,148 .buffer_bytes_max = 32768,149 .period_bytes_min = 1024,150 .period_bytes_max = 32768,151 .periods_min = 3,152 // 3 periods lets the double-buffering hw read one buffer behind safely153 .periods_max = 128,154};155 156static int hw_rule_period_size(struct snd_pcm_hw_params *params,157 struct snd_pcm_hw_rule *rule)158{159 struct snd_interval *c = hw_param_interval(params,160 SNDRV_PCM_HW_PARAM_PERIOD_SIZE);161 int changed = 0;162 163 /*164 * The DMA unit has errata on (start + len) & 0x3fff == 0x2000.165 * This constraint makes sure that the period size is not a power of two,166 * which combined with dma_alloc_coherent aligning the buffer to the largest167 * PoT <= size guarantees it won't be hit.168 */169 170 if (is_power_of_2(c->min)) {171 c->min += 2;172 changed = 1;173 }174 if (is_power_of_2(c->max)) {175 c->max -= 2;176 changed = 1;177 }178 if (snd_interval_checkempty(c)) {179 c->empty = 1;180 return -EINVAL;181 }182 183 return changed;184}185 186static int n64audio_pcm_open(struct snd_pcm_substream *substream)187{188 struct snd_pcm_runtime *runtime = substream->runtime;189 int err;190 191 runtime->hw = n64audio_pcm_hw;192 err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);193 if (err < 0)194 return err;195 196 err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);197 if (err < 0)198 return err;199 200 err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,201 hw_rule_period_size, NULL, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);202 if (err < 0)203 return err;204 205 return 0;206}207 208static int n64audio_pcm_prepare(struct snd_pcm_substream *substream)209{210 struct snd_pcm_runtime *runtime = substream->runtime;211 struct n64audio *priv = substream->pcm->private_data;212 u32 rate;213 214 rate = ((2 * AI_NTSC_DACRATE / runtime->rate) + 1) / 2 - 1;215 216 n64audio_write_reg(priv, AI_RATE_REG, rate);217 218 rate /= 66;219 if (rate > 16)220 rate = 16;221 n64audio_write_reg(priv, AI_BITCLOCK_REG, rate - 1);222 223 spin_lock_irq(&priv->chan.lock);224 225 /* Setup the pseudo-dma transfer pointers. */226 priv->chan.pos = 0;227 priv->chan.nextpos = 0;228 priv->chan.substream = substream;229 priv->chan.writesize = snd_pcm_lib_period_bytes(substream);230 priv->chan.bufsize = snd_pcm_lib_buffer_bytes(substream);231 232 spin_unlock_irq(&priv->chan.lock);233 return 0;234}235 236static int n64audio_pcm_trigger(struct snd_pcm_substream *substream,237 int cmd)238{239 struct n64audio *priv = substream->pcm->private_data;240 241 switch (cmd) {242 case SNDRV_PCM_TRIGGER_START:243 n64audio_push(substream->pcm->private_data);244 n64audio_write_reg(priv, AI_CONTROL_REG, 1);245 n64mi_write_reg(priv, MI_MASK_REG, MI_MASK_SET_AI);246 break;247 case SNDRV_PCM_TRIGGER_STOP:248 n64audio_write_reg(priv, AI_CONTROL_REG, 0);249 n64mi_write_reg(priv, MI_MASK_REG, MI_MASK_CLR_AI);250 break;251 default:252 return -EINVAL;253 }254 return 0;255}256 257static snd_pcm_uframes_t n64audio_pcm_pointer(struct snd_pcm_substream *substream)258{259 struct n64audio *priv = substream->pcm->private_data;260 261 return bytes_to_frames(substream->runtime,262 priv->chan.pos);263}264 265static int n64audio_pcm_close(struct snd_pcm_substream *substream)266{267 struct n64audio *priv = substream->pcm->private_data;268 269 priv->chan.substream = NULL;270 271 return 0;272}273 274static const struct snd_pcm_ops n64audio_pcm_ops = {275 .open = n64audio_pcm_open,276 .prepare = n64audio_pcm_prepare,277 .trigger = n64audio_pcm_trigger,278 .pointer = n64audio_pcm_pointer,279 .close = n64audio_pcm_close,280};281 282/*283 * The target device is embedded and RAM-constrained. We save RAM284 * by initializing in __init code that gets dropped late in boot.285 * For the same reason there is no module or unloading support.286 */287static int __init n64audio_probe(struct platform_device *pdev)288{289 struct snd_card *card;290 struct snd_pcm *pcm;291 struct n64audio *priv;292 int err, irq;293 294 err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1,295 SNDRV_DEFAULT_STR1,296 THIS_MODULE, sizeof(*priv), &card);297 if (err < 0)298 return err;299 300 priv = card->private_data;301 302 spin_lock_init(&priv->chan.lock);303 304 priv->card = card;305 306 priv->ring_base = dma_alloc_coherent(card->dev, 32 * 1024, &priv->ring_base_dma,307 GFP_DMA|GFP_KERNEL);308 if (!priv->ring_base) {309 err = -ENOMEM;310 goto fail_card;311 }312 313 priv->mi_reg_base = devm_platform_ioremap_resource(pdev, 0);314 if (IS_ERR(priv->mi_reg_base)) {315 err = PTR_ERR(priv->mi_reg_base);316 goto fail_dma_alloc;317 }318 319 priv->ai_reg_base = devm_platform_ioremap_resource(pdev, 1);320 if (IS_ERR(priv->ai_reg_base)) {321 err = PTR_ERR(priv->ai_reg_base);322 goto fail_dma_alloc;323 }324 325 err = snd_pcm_new(card, "N64 Audio", 0, 1, 0, &pcm);326 if (err < 0)327 goto fail_dma_alloc;328 329 pcm->private_data = priv;330 strcpy(pcm->name, "N64 Audio");331 332 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &n64audio_pcm_ops);333 snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, card->dev, 0, 0);334 335 strcpy(card->driver, "N64 Audio");336 strcpy(card->shortname, "N64 Audio");337 strcpy(card->longname, "N64 Audio");338 339 irq = platform_get_irq(pdev, 0);340 if (irq < 0) {341 err = -EINVAL;342 goto fail_dma_alloc;343 }344 if (devm_request_irq(&pdev->dev, irq, n64audio_isr,345 IRQF_SHARED, "N64 Audio", priv)) {346 err = -EBUSY;347 goto fail_dma_alloc;348 }349 350 err = snd_card_register(card);351 if (err < 0)352 goto fail_dma_alloc;353 354 return 0;355 356fail_dma_alloc:357 dma_free_coherent(card->dev, 32 * 1024, priv->ring_base, priv->ring_base_dma);358 359fail_card:360 snd_card_free(card);361 return err;362}363 364static struct platform_driver n64audio_driver = {365 .driver = {366 .name = "n64audio",367 },368};369 370static int __init n64audio_init(void)371{372 return platform_driver_probe(&n64audio_driver, n64audio_probe);373}374 375module_init(n64audio_init);376