xref: /openbmc/linux/sound/mips/snd-n64.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
11448f8acSLauri Kasanen // SPDX-License-Identifier: GPL-2.0
21448f8acSLauri Kasanen /*
31448f8acSLauri Kasanen  *   Sound driver for Nintendo 64.
41448f8acSLauri Kasanen  *
51448f8acSLauri Kasanen  *   Copyright 2021 Lauri Kasanen
61448f8acSLauri Kasanen  */
71448f8acSLauri Kasanen 
81448f8acSLauri Kasanen #include <linux/dma-mapping.h>
91448f8acSLauri Kasanen #include <linux/init.h>
101448f8acSLauri Kasanen #include <linux/interrupt.h>
111448f8acSLauri Kasanen #include <linux/io.h>
121448f8acSLauri Kasanen #include <linux/log2.h>
131448f8acSLauri Kasanen #include <linux/module.h>
141448f8acSLauri Kasanen #include <linux/platform_device.h>
151448f8acSLauri Kasanen #include <linux/spinlock.h>
161448f8acSLauri Kasanen 
171448f8acSLauri Kasanen #include <sound/control.h>
181448f8acSLauri Kasanen #include <sound/core.h>
191448f8acSLauri Kasanen #include <sound/initval.h>
201448f8acSLauri Kasanen #include <sound/pcm.h>
211448f8acSLauri Kasanen #include <sound/pcm_params.h>
221448f8acSLauri Kasanen 
231448f8acSLauri Kasanen MODULE_AUTHOR("Lauri Kasanen <cand@gmx.com>");
241448f8acSLauri Kasanen MODULE_DESCRIPTION("N64 Audio");
251448f8acSLauri Kasanen MODULE_LICENSE("GPL");
261448f8acSLauri Kasanen 
271448f8acSLauri Kasanen #define AI_NTSC_DACRATE 48681812
281448f8acSLauri Kasanen #define AI_STATUS_BUSY  (1 << 30)
291448f8acSLauri Kasanen #define AI_STATUS_FULL  (1 << 31)
301448f8acSLauri Kasanen 
311448f8acSLauri Kasanen #define AI_ADDR_REG 0
321448f8acSLauri Kasanen #define AI_LEN_REG 1
331448f8acSLauri Kasanen #define AI_CONTROL_REG 2
341448f8acSLauri Kasanen #define AI_STATUS_REG 3
351448f8acSLauri Kasanen #define AI_RATE_REG 4
361448f8acSLauri Kasanen #define AI_BITCLOCK_REG 5
371448f8acSLauri Kasanen 
381448f8acSLauri Kasanen #define MI_INTR_REG 2
391448f8acSLauri Kasanen #define MI_MASK_REG 3
401448f8acSLauri Kasanen 
411448f8acSLauri Kasanen #define MI_INTR_AI 0x04
421448f8acSLauri Kasanen 
431448f8acSLauri Kasanen #define MI_MASK_CLR_AI 0x0010
441448f8acSLauri Kasanen #define MI_MASK_SET_AI 0x0020
451448f8acSLauri Kasanen 
461448f8acSLauri Kasanen 
471448f8acSLauri Kasanen struct n64audio {
481448f8acSLauri Kasanen 	u32 __iomem *ai_reg_base;
491448f8acSLauri Kasanen 	u32 __iomem *mi_reg_base;
501448f8acSLauri Kasanen 
511448f8acSLauri Kasanen 	void *ring_base;
521448f8acSLauri Kasanen 	dma_addr_t ring_base_dma;
531448f8acSLauri Kasanen 
541448f8acSLauri Kasanen 	struct snd_card *card;
551448f8acSLauri Kasanen 
561448f8acSLauri Kasanen 	struct {
571448f8acSLauri Kasanen 		struct snd_pcm_substream *substream;
581448f8acSLauri Kasanen 		int pos, nextpos;
591448f8acSLauri Kasanen 		u32 writesize;
601448f8acSLauri Kasanen 		u32 bufsize;
611448f8acSLauri Kasanen 		spinlock_t lock;
621448f8acSLauri Kasanen 	} chan;
631448f8acSLauri Kasanen };
641448f8acSLauri Kasanen 
n64audio_write_reg(struct n64audio * priv,const u8 reg,const u32 value)651448f8acSLauri Kasanen static void n64audio_write_reg(struct n64audio *priv, const u8 reg, const u32 value)
661448f8acSLauri Kasanen {
671448f8acSLauri Kasanen 	writel(value, priv->ai_reg_base + reg);
681448f8acSLauri Kasanen }
691448f8acSLauri Kasanen 
n64mi_write_reg(struct n64audio * priv,const u8 reg,const u32 value)701448f8acSLauri Kasanen static void n64mi_write_reg(struct n64audio *priv, const u8 reg, const u32 value)
711448f8acSLauri Kasanen {
721448f8acSLauri Kasanen 	writel(value, priv->mi_reg_base + reg);
731448f8acSLauri Kasanen }
741448f8acSLauri Kasanen 
n64mi_read_reg(struct n64audio * priv,const u8 reg)751448f8acSLauri Kasanen static u32 n64mi_read_reg(struct n64audio *priv, const u8 reg)
761448f8acSLauri Kasanen {
771448f8acSLauri Kasanen 	return readl(priv->mi_reg_base + reg);
781448f8acSLauri Kasanen }
791448f8acSLauri Kasanen 
n64audio_push(struct n64audio * priv)801448f8acSLauri Kasanen static void n64audio_push(struct n64audio *priv)
811448f8acSLauri Kasanen {
821448f8acSLauri Kasanen 	struct snd_pcm_runtime *runtime = priv->chan.substream->runtime;
831448f8acSLauri Kasanen 	unsigned long flags;
841448f8acSLauri Kasanen 	u32 count;
851448f8acSLauri Kasanen 
861448f8acSLauri Kasanen 	spin_lock_irqsave(&priv->chan.lock, flags);
871448f8acSLauri Kasanen 
881448f8acSLauri Kasanen 	count = priv->chan.writesize;
891448f8acSLauri Kasanen 
901448f8acSLauri Kasanen 	memcpy(priv->ring_base + priv->chan.nextpos,
911448f8acSLauri Kasanen 	       runtime->dma_area + priv->chan.nextpos, count);
921448f8acSLauri Kasanen 
931448f8acSLauri Kasanen 	/*
941448f8acSLauri Kasanen 	 * The hw registers are double-buffered, and the IRQ fires essentially
951448f8acSLauri Kasanen 	 * one period behind. The core only allows one period's distance, so we
961448f8acSLauri Kasanen 	 * keep a private DMA buffer to afford two.
971448f8acSLauri Kasanen 	 */
981448f8acSLauri Kasanen 	n64audio_write_reg(priv, AI_ADDR_REG, priv->ring_base_dma + priv->chan.nextpos);
991448f8acSLauri Kasanen 	barrier();
1001448f8acSLauri Kasanen 	n64audio_write_reg(priv, AI_LEN_REG, count);
1011448f8acSLauri Kasanen 
1021448f8acSLauri Kasanen 	priv->chan.nextpos += count;
1031448f8acSLauri Kasanen 	priv->chan.nextpos %= priv->chan.bufsize;
1041448f8acSLauri Kasanen 
1051448f8acSLauri Kasanen 	runtime->delay = runtime->period_size;
1061448f8acSLauri Kasanen 
1071448f8acSLauri Kasanen 	spin_unlock_irqrestore(&priv->chan.lock, flags);
1081448f8acSLauri Kasanen }
1091448f8acSLauri Kasanen 
n64audio_isr(int irq,void * dev_id)1101448f8acSLauri Kasanen static irqreturn_t n64audio_isr(int irq, void *dev_id)
1111448f8acSLauri Kasanen {
1121448f8acSLauri Kasanen 	struct n64audio *priv = dev_id;
1131448f8acSLauri Kasanen 	const u32 intrs = n64mi_read_reg(priv, MI_INTR_REG);
1141448f8acSLauri Kasanen 	unsigned long flags;
1151448f8acSLauri Kasanen 
1161448f8acSLauri Kasanen 	// Check it's ours
1171448f8acSLauri Kasanen 	if (!(intrs & MI_INTR_AI))
1181448f8acSLauri Kasanen 		return IRQ_NONE;
1191448f8acSLauri Kasanen 
1201448f8acSLauri Kasanen 	n64audio_write_reg(priv, AI_STATUS_REG, 1);
1211448f8acSLauri Kasanen 
1221448f8acSLauri Kasanen 	if (priv->chan.substream && snd_pcm_running(priv->chan.substream)) {
1231448f8acSLauri Kasanen 		spin_lock_irqsave(&priv->chan.lock, flags);
1241448f8acSLauri Kasanen 
1251448f8acSLauri Kasanen 		priv->chan.pos = priv->chan.nextpos;
1261448f8acSLauri Kasanen 
1271448f8acSLauri Kasanen 		spin_unlock_irqrestore(&priv->chan.lock, flags);
1281448f8acSLauri Kasanen 
1291448f8acSLauri Kasanen 		snd_pcm_period_elapsed(priv->chan.substream);
1301448f8acSLauri Kasanen 		if (priv->chan.substream && snd_pcm_running(priv->chan.substream))
1311448f8acSLauri Kasanen 			n64audio_push(priv);
1321448f8acSLauri Kasanen 	}
1331448f8acSLauri Kasanen 
1341448f8acSLauri Kasanen 	return IRQ_HANDLED;
1351448f8acSLauri Kasanen }
1361448f8acSLauri Kasanen 
1371448f8acSLauri Kasanen static const struct snd_pcm_hardware n64audio_pcm_hw = {
1381448f8acSLauri Kasanen 	.info = (SNDRV_PCM_INFO_MMAP |
1391448f8acSLauri Kasanen 		 SNDRV_PCM_INFO_MMAP_VALID |
1401448f8acSLauri Kasanen 		 SNDRV_PCM_INFO_INTERLEAVED |
1411448f8acSLauri Kasanen 		 SNDRV_PCM_INFO_BLOCK_TRANSFER),
1421448f8acSLauri Kasanen 	.formats =          SNDRV_PCM_FMTBIT_S16_BE,
1431448f8acSLauri Kasanen 	.rates =            SNDRV_PCM_RATE_8000_48000,
1441448f8acSLauri Kasanen 	.rate_min =         8000,
1451448f8acSLauri Kasanen 	.rate_max =         48000,
1461448f8acSLauri Kasanen 	.channels_min =     2,
1471448f8acSLauri Kasanen 	.channels_max =     2,
1481448f8acSLauri Kasanen 	.buffer_bytes_max = 32768,
1491448f8acSLauri Kasanen 	.period_bytes_min = 1024,
1501448f8acSLauri Kasanen 	.period_bytes_max = 32768,
1511448f8acSLauri Kasanen 	.periods_min =      3,
1521448f8acSLauri Kasanen 	// 3 periods lets the double-buffering hw read one buffer behind safely
1531448f8acSLauri Kasanen 	.periods_max =      128,
1541448f8acSLauri Kasanen };
1551448f8acSLauri Kasanen 
hw_rule_period_size(struct snd_pcm_hw_params * params,struct snd_pcm_hw_rule * rule)1561448f8acSLauri Kasanen static int hw_rule_period_size(struct snd_pcm_hw_params *params,
1571448f8acSLauri Kasanen 			       struct snd_pcm_hw_rule *rule)
1581448f8acSLauri Kasanen {
1591448f8acSLauri Kasanen 	struct snd_interval *c = hw_param_interval(params,
1601448f8acSLauri Kasanen 						   SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
1611448f8acSLauri Kasanen 	int changed = 0;
1621448f8acSLauri Kasanen 
1631448f8acSLauri Kasanen 	/*
1641448f8acSLauri Kasanen 	 * The DMA unit has errata on (start + len) & 0x3fff == 0x2000.
1651448f8acSLauri Kasanen 	 * This constraint makes sure that the period size is not a power of two,
1661448f8acSLauri Kasanen 	 * which combined with dma_alloc_coherent aligning the buffer to the largest
1671448f8acSLauri Kasanen 	 * PoT <= size guarantees it won't be hit.
1681448f8acSLauri Kasanen 	 */
1691448f8acSLauri Kasanen 
1701448f8acSLauri Kasanen 	if (is_power_of_2(c->min)) {
1711448f8acSLauri Kasanen 		c->min += 2;
1721448f8acSLauri Kasanen 		changed = 1;
1731448f8acSLauri Kasanen 	}
1741448f8acSLauri Kasanen 	if (is_power_of_2(c->max)) {
1751448f8acSLauri Kasanen 		c->max -= 2;
1761448f8acSLauri Kasanen 		changed = 1;
1771448f8acSLauri Kasanen 	}
1781448f8acSLauri Kasanen 	if (snd_interval_checkempty(c)) {
1791448f8acSLauri Kasanen 		c->empty = 1;
1801448f8acSLauri Kasanen 		return -EINVAL;
1811448f8acSLauri Kasanen 	}
1821448f8acSLauri Kasanen 
1831448f8acSLauri Kasanen 	return changed;
1841448f8acSLauri Kasanen }
1851448f8acSLauri Kasanen 
n64audio_pcm_open(struct snd_pcm_substream * substream)1861448f8acSLauri Kasanen static int n64audio_pcm_open(struct snd_pcm_substream *substream)
1871448f8acSLauri Kasanen {
1881448f8acSLauri Kasanen 	struct snd_pcm_runtime *runtime = substream->runtime;
1891448f8acSLauri Kasanen 	int err;
1901448f8acSLauri Kasanen 
1911448f8acSLauri Kasanen 	runtime->hw = n64audio_pcm_hw;
1921448f8acSLauri Kasanen 	err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
1931448f8acSLauri Kasanen 	if (err < 0)
1941448f8acSLauri Kasanen 		return err;
1951448f8acSLauri Kasanen 
1961448f8acSLauri Kasanen 	err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
1971448f8acSLauri Kasanen 	if (err < 0)
1981448f8acSLauri Kasanen 		return err;
1991448f8acSLauri Kasanen 
2001448f8acSLauri Kasanen 	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
2011448f8acSLauri Kasanen 			    hw_rule_period_size, NULL, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
2021448f8acSLauri Kasanen 	if (err < 0)
2031448f8acSLauri Kasanen 		return err;
2041448f8acSLauri Kasanen 
2051448f8acSLauri Kasanen 	return 0;
2061448f8acSLauri Kasanen }
2071448f8acSLauri Kasanen 
n64audio_pcm_prepare(struct snd_pcm_substream * substream)2081448f8acSLauri Kasanen static int n64audio_pcm_prepare(struct snd_pcm_substream *substream)
2091448f8acSLauri Kasanen {
2101448f8acSLauri Kasanen 	struct snd_pcm_runtime *runtime = substream->runtime;
2111448f8acSLauri Kasanen 	struct n64audio *priv = substream->pcm->private_data;
2121448f8acSLauri Kasanen 	u32 rate;
2131448f8acSLauri Kasanen 
2141448f8acSLauri Kasanen 	rate = ((2 * AI_NTSC_DACRATE / runtime->rate) + 1) / 2 - 1;
2151448f8acSLauri Kasanen 
2161448f8acSLauri Kasanen 	n64audio_write_reg(priv, AI_RATE_REG, rate);
2171448f8acSLauri Kasanen 
2181448f8acSLauri Kasanen 	rate /= 66;
2191448f8acSLauri Kasanen 	if (rate > 16)
2201448f8acSLauri Kasanen 		rate = 16;
2211448f8acSLauri Kasanen 	n64audio_write_reg(priv, AI_BITCLOCK_REG, rate - 1);
2221448f8acSLauri Kasanen 
2231448f8acSLauri Kasanen 	spin_lock_irq(&priv->chan.lock);
2241448f8acSLauri Kasanen 
2251448f8acSLauri Kasanen 	/* Setup the pseudo-dma transfer pointers.  */
2261448f8acSLauri Kasanen 	priv->chan.pos = 0;
2271448f8acSLauri Kasanen 	priv->chan.nextpos = 0;
2281448f8acSLauri Kasanen 	priv->chan.substream = substream;
2291448f8acSLauri Kasanen 	priv->chan.writesize = snd_pcm_lib_period_bytes(substream);
2301448f8acSLauri Kasanen 	priv->chan.bufsize = snd_pcm_lib_buffer_bytes(substream);
2311448f8acSLauri Kasanen 
2321448f8acSLauri Kasanen 	spin_unlock_irq(&priv->chan.lock);
2331448f8acSLauri Kasanen 	return 0;
2341448f8acSLauri Kasanen }
2351448f8acSLauri Kasanen 
n64audio_pcm_trigger(struct snd_pcm_substream * substream,int cmd)2361448f8acSLauri Kasanen static int n64audio_pcm_trigger(struct snd_pcm_substream *substream,
2371448f8acSLauri Kasanen 				int cmd)
2381448f8acSLauri Kasanen {
2391448f8acSLauri Kasanen 	struct n64audio *priv = substream->pcm->private_data;
2401448f8acSLauri Kasanen 
2411448f8acSLauri Kasanen 	switch (cmd) {
2421448f8acSLauri Kasanen 	case SNDRV_PCM_TRIGGER_START:
2431448f8acSLauri Kasanen 		n64audio_push(substream->pcm->private_data);
2441448f8acSLauri Kasanen 		n64audio_write_reg(priv, AI_CONTROL_REG, 1);
2451448f8acSLauri Kasanen 		n64mi_write_reg(priv, MI_MASK_REG, MI_MASK_SET_AI);
2461448f8acSLauri Kasanen 		break;
2471448f8acSLauri Kasanen 	case SNDRV_PCM_TRIGGER_STOP:
2481448f8acSLauri Kasanen 		n64audio_write_reg(priv, AI_CONTROL_REG, 0);
2491448f8acSLauri Kasanen 		n64mi_write_reg(priv, MI_MASK_REG, MI_MASK_CLR_AI);
2501448f8acSLauri Kasanen 		break;
2511448f8acSLauri Kasanen 	default:
2521448f8acSLauri Kasanen 		return -EINVAL;
2531448f8acSLauri Kasanen 	}
2541448f8acSLauri Kasanen 	return 0;
2551448f8acSLauri Kasanen }
2561448f8acSLauri Kasanen 
n64audio_pcm_pointer(struct snd_pcm_substream * substream)2571448f8acSLauri Kasanen static snd_pcm_uframes_t n64audio_pcm_pointer(struct snd_pcm_substream *substream)
2581448f8acSLauri Kasanen {
2591448f8acSLauri Kasanen 	struct n64audio *priv = substream->pcm->private_data;
2601448f8acSLauri Kasanen 
2611448f8acSLauri Kasanen 	return bytes_to_frames(substream->runtime,
2621448f8acSLauri Kasanen 			       priv->chan.pos);
2631448f8acSLauri Kasanen }
2641448f8acSLauri Kasanen 
n64audio_pcm_close(struct snd_pcm_substream * substream)2651448f8acSLauri Kasanen static int n64audio_pcm_close(struct snd_pcm_substream *substream)
2661448f8acSLauri Kasanen {
2671448f8acSLauri Kasanen 	struct n64audio *priv = substream->pcm->private_data;
2681448f8acSLauri Kasanen 
2691448f8acSLauri Kasanen 	priv->chan.substream = NULL;
2701448f8acSLauri Kasanen 
2711448f8acSLauri Kasanen 	return 0;
2721448f8acSLauri Kasanen }
2731448f8acSLauri Kasanen 
2741448f8acSLauri Kasanen static const struct snd_pcm_ops n64audio_pcm_ops = {
2751448f8acSLauri Kasanen 	.open =		n64audio_pcm_open,
2761448f8acSLauri Kasanen 	.prepare =	n64audio_pcm_prepare,
2771448f8acSLauri Kasanen 	.trigger =	n64audio_pcm_trigger,
2781448f8acSLauri Kasanen 	.pointer =	n64audio_pcm_pointer,
2791448f8acSLauri Kasanen 	.close =	n64audio_pcm_close,
2801448f8acSLauri Kasanen };
2811448f8acSLauri Kasanen 
2821448f8acSLauri Kasanen /*
2831448f8acSLauri Kasanen  * The target device is embedded and RAM-constrained. We save RAM
2841448f8acSLauri Kasanen  * by initializing in __init code that gets dropped late in boot.
2851448f8acSLauri Kasanen  * For the same reason there is no module or unloading support.
2861448f8acSLauri Kasanen  */
n64audio_probe(struct platform_device * pdev)2871448f8acSLauri Kasanen static int __init n64audio_probe(struct platform_device *pdev)
2881448f8acSLauri Kasanen {
2891448f8acSLauri Kasanen 	struct snd_card *card;
2901448f8acSLauri Kasanen 	struct snd_pcm *pcm;
2911448f8acSLauri Kasanen 	struct n64audio *priv;
292*a544684bSMeng Tang 	int err, irq;
2931448f8acSLauri Kasanen 
2941448f8acSLauri Kasanen 	err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1,
2951448f8acSLauri Kasanen 			   SNDRV_DEFAULT_STR1,
2961448f8acSLauri Kasanen 			   THIS_MODULE, sizeof(*priv), &card);
2971448f8acSLauri Kasanen 	if (err < 0)
2981448f8acSLauri Kasanen 		return err;
2991448f8acSLauri Kasanen 
3001448f8acSLauri Kasanen 	priv = card->private_data;
3011448f8acSLauri Kasanen 
3021448f8acSLauri Kasanen 	spin_lock_init(&priv->chan.lock);
3031448f8acSLauri Kasanen 
3041448f8acSLauri Kasanen 	priv->card = card;
3051448f8acSLauri Kasanen 
3061448f8acSLauri Kasanen 	priv->ring_base = dma_alloc_coherent(card->dev, 32 * 1024, &priv->ring_base_dma,
3071448f8acSLauri Kasanen 					     GFP_DMA|GFP_KERNEL);
3081448f8acSLauri Kasanen 	if (!priv->ring_base) {
3091448f8acSLauri Kasanen 		err = -ENOMEM;
3101448f8acSLauri Kasanen 		goto fail_card;
3111448f8acSLauri Kasanen 	}
3121448f8acSLauri Kasanen 
3131448f8acSLauri Kasanen 	priv->mi_reg_base = devm_platform_ioremap_resource(pdev, 0);
314c88fb897SWei Yongjun 	if (IS_ERR(priv->mi_reg_base)) {
315c88fb897SWei Yongjun 		err = PTR_ERR(priv->mi_reg_base);
3161448f8acSLauri Kasanen 		goto fail_dma_alloc;
3171448f8acSLauri Kasanen 	}
3181448f8acSLauri Kasanen 
3191448f8acSLauri Kasanen 	priv->ai_reg_base = devm_platform_ioremap_resource(pdev, 1);
320c88fb897SWei Yongjun 	if (IS_ERR(priv->ai_reg_base)) {
321c88fb897SWei Yongjun 		err = PTR_ERR(priv->ai_reg_base);
3221448f8acSLauri Kasanen 		goto fail_dma_alloc;
3231448f8acSLauri Kasanen 	}
3241448f8acSLauri Kasanen 
3251448f8acSLauri Kasanen 	err = snd_pcm_new(card, "N64 Audio", 0, 1, 0, &pcm);
3261448f8acSLauri Kasanen 	if (err < 0)
3271448f8acSLauri Kasanen 		goto fail_dma_alloc;
3281448f8acSLauri Kasanen 
3291448f8acSLauri Kasanen 	pcm->private_data = priv;
3301448f8acSLauri Kasanen 	strcpy(pcm->name, "N64 Audio");
3311448f8acSLauri Kasanen 
3321448f8acSLauri Kasanen 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &n64audio_pcm_ops);
3331448f8acSLauri Kasanen 	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, card->dev, 0, 0);
3341448f8acSLauri Kasanen 
3351448f8acSLauri Kasanen 	strcpy(card->driver, "N64 Audio");
3361448f8acSLauri Kasanen 	strcpy(card->shortname, "N64 Audio");
3371448f8acSLauri Kasanen 	strcpy(card->longname, "N64 Audio");
3381448f8acSLauri Kasanen 
339*a544684bSMeng Tang 	irq = platform_get_irq(pdev, 0);
340*a544684bSMeng Tang 	if (irq < 0) {
341be471fe3SYang Yingliang 		err = -EINVAL;
342be471fe3SYang Yingliang 		goto fail_dma_alloc;
343be471fe3SYang Yingliang 	}
344*a544684bSMeng Tang 	if (devm_request_irq(&pdev->dev, irq, n64audio_isr,
3451448f8acSLauri Kasanen 				IRQF_SHARED, "N64 Audio", priv)) {
3461448f8acSLauri Kasanen 		err = -EBUSY;
3471448f8acSLauri Kasanen 		goto fail_dma_alloc;
3481448f8acSLauri Kasanen 	}
3491448f8acSLauri Kasanen 
3501448f8acSLauri Kasanen 	err = snd_card_register(card);
3511448f8acSLauri Kasanen 	if (err < 0)
3521448f8acSLauri Kasanen 		goto fail_dma_alloc;
3531448f8acSLauri Kasanen 
3541448f8acSLauri Kasanen 	return 0;
3551448f8acSLauri Kasanen 
3561448f8acSLauri Kasanen fail_dma_alloc:
3571448f8acSLauri Kasanen 	dma_free_coherent(card->dev, 32 * 1024, priv->ring_base, priv->ring_base_dma);
3581448f8acSLauri Kasanen 
3591448f8acSLauri Kasanen fail_card:
3601448f8acSLauri Kasanen 	snd_card_free(card);
3611448f8acSLauri Kasanen 	return err;
3621448f8acSLauri Kasanen }
3631448f8acSLauri Kasanen 
3641448f8acSLauri Kasanen static struct platform_driver n64audio_driver = {
3651448f8acSLauri Kasanen 	.driver = {
3661448f8acSLauri Kasanen 		.name = "n64audio",
3671448f8acSLauri Kasanen 	},
3681448f8acSLauri Kasanen };
3691448f8acSLauri Kasanen 
n64audio_init(void)3701448f8acSLauri Kasanen static int __init n64audio_init(void)
3711448f8acSLauri Kasanen {
3721448f8acSLauri Kasanen 	return platform_driver_probe(&n64audio_driver, n64audio_probe);
3731448f8acSLauri Kasanen }
3741448f8acSLauri Kasanen 
3751448f8acSLauri Kasanen module_init(n64audio_init);
376