162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/sound/arm/aaci.c - ARM PrimeCell AACI PL041 driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Documentation: ARM DDI 0173B 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/ioport.h> 1362306a36Sopenharmony_ci#include <linux/device.h> 1462306a36Sopenharmony_ci#include <linux/spinlock.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/err.h> 1762306a36Sopenharmony_ci#include <linux/amba/bus.h> 1862306a36Sopenharmony_ci#include <linux/io.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <sound/core.h> 2162306a36Sopenharmony_ci#include <sound/initval.h> 2262306a36Sopenharmony_ci#include <sound/ac97_codec.h> 2362306a36Sopenharmony_ci#include <sound/pcm.h> 2462306a36Sopenharmony_ci#include <sound/pcm_params.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "aaci.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define DRIVER_NAME "aaci-pl041" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define FRAME_PERIOD_US 21 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* 3362306a36Sopenharmony_ci * PM support is not complete. Turn it off. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ci#undef CONFIG_PM 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic void aaci_ac97_select_codec(struct aaci *aaci, struct snd_ac97 *ac97) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci u32 v, maincr = aaci->maincr | MAINCR_SCRA(ac97->num); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci /* 4262306a36Sopenharmony_ci * Ensure that the slot 1/2 RX registers are empty. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci v = readl(aaci->base + AACI_SLFR); 4562306a36Sopenharmony_ci if (v & SLFR_2RXV) 4662306a36Sopenharmony_ci readl(aaci->base + AACI_SL2RX); 4762306a36Sopenharmony_ci if (v & SLFR_1RXV) 4862306a36Sopenharmony_ci readl(aaci->base + AACI_SL1RX); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (maincr != readl(aaci->base + AACI_MAINCR)) { 5162306a36Sopenharmony_ci writel(maincr, aaci->base + AACI_MAINCR); 5262306a36Sopenharmony_ci readl(aaci->base + AACI_MAINCR); 5362306a36Sopenharmony_ci udelay(1); 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* 5862306a36Sopenharmony_ci * P29: 5962306a36Sopenharmony_ci * The recommended use of programming the external codec through slot 1 6062306a36Sopenharmony_ci * and slot 2 data is to use the channels during setup routines and the 6162306a36Sopenharmony_ci * slot register at any other time. The data written into slot 1, slot 2 6262306a36Sopenharmony_ci * and slot 12 registers is transmitted only when their corresponding 6362306a36Sopenharmony_ci * SI1TxEn, SI2TxEn and SI12TxEn bits are set in the AACI_MAINCR 6462306a36Sopenharmony_ci * register. 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_cistatic void aaci_ac97_write(struct snd_ac97 *ac97, unsigned short reg, 6762306a36Sopenharmony_ci unsigned short val) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct aaci *aaci = ac97->private_data; 7062306a36Sopenharmony_ci int timeout; 7162306a36Sopenharmony_ci u32 v; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (ac97->num >= 4) 7462306a36Sopenharmony_ci return; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci mutex_lock(&aaci->ac97_sem); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci aaci_ac97_select_codec(aaci, ac97); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* 8162306a36Sopenharmony_ci * P54: You must ensure that AACI_SL2TX is always written 8262306a36Sopenharmony_ci * to, if required, before data is written to AACI_SL1TX. 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_ci writel(val << 4, aaci->base + AACI_SL2TX); 8562306a36Sopenharmony_ci writel(reg << 12, aaci->base + AACI_SL1TX); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* Initially, wait one frame period */ 8862306a36Sopenharmony_ci udelay(FRAME_PERIOD_US); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* And then wait an additional eight frame periods for it to be sent */ 9162306a36Sopenharmony_ci timeout = FRAME_PERIOD_US * 8; 9262306a36Sopenharmony_ci do { 9362306a36Sopenharmony_ci udelay(1); 9462306a36Sopenharmony_ci v = readl(aaci->base + AACI_SLFR); 9562306a36Sopenharmony_ci } while ((v & (SLFR_1TXB|SLFR_2TXB)) && --timeout); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (v & (SLFR_1TXB|SLFR_2TXB)) 9862306a36Sopenharmony_ci dev_err(&aaci->dev->dev, 9962306a36Sopenharmony_ci "timeout waiting for write to complete\n"); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci mutex_unlock(&aaci->ac97_sem); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* 10562306a36Sopenharmony_ci * Read an AC'97 register. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_cistatic unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct aaci *aaci = ac97->private_data; 11062306a36Sopenharmony_ci int timeout, retries = 10; 11162306a36Sopenharmony_ci u32 v; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (ac97->num >= 4) 11462306a36Sopenharmony_ci return ~0; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci mutex_lock(&aaci->ac97_sem); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci aaci_ac97_select_codec(aaci, ac97); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* 12162306a36Sopenharmony_ci * Write the register address to slot 1. 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_ci writel((reg << 12) | (1 << 19), aaci->base + AACI_SL1TX); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* Initially, wait one frame period */ 12662306a36Sopenharmony_ci udelay(FRAME_PERIOD_US); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* And then wait an additional eight frame periods for it to be sent */ 12962306a36Sopenharmony_ci timeout = FRAME_PERIOD_US * 8; 13062306a36Sopenharmony_ci do { 13162306a36Sopenharmony_ci udelay(1); 13262306a36Sopenharmony_ci v = readl(aaci->base + AACI_SLFR); 13362306a36Sopenharmony_ci } while ((v & SLFR_1TXB) && --timeout); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (v & SLFR_1TXB) { 13662306a36Sopenharmony_ci dev_err(&aaci->dev->dev, "timeout on slot 1 TX busy\n"); 13762306a36Sopenharmony_ci v = ~0; 13862306a36Sopenharmony_ci goto out; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* Now wait for the response frame */ 14262306a36Sopenharmony_ci udelay(FRAME_PERIOD_US); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* And then wait an additional eight frame periods for data */ 14562306a36Sopenharmony_ci timeout = FRAME_PERIOD_US * 8; 14662306a36Sopenharmony_ci do { 14762306a36Sopenharmony_ci udelay(1); 14862306a36Sopenharmony_ci cond_resched(); 14962306a36Sopenharmony_ci v = readl(aaci->base + AACI_SLFR) & (SLFR_1RXV|SLFR_2RXV); 15062306a36Sopenharmony_ci } while ((v != (SLFR_1RXV|SLFR_2RXV)) && --timeout); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (v != (SLFR_1RXV|SLFR_2RXV)) { 15362306a36Sopenharmony_ci dev_err(&aaci->dev->dev, "timeout on RX valid\n"); 15462306a36Sopenharmony_ci v = ~0; 15562306a36Sopenharmony_ci goto out; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci do { 15962306a36Sopenharmony_ci v = readl(aaci->base + AACI_SL1RX) >> 12; 16062306a36Sopenharmony_ci if (v == reg) { 16162306a36Sopenharmony_ci v = readl(aaci->base + AACI_SL2RX) >> 4; 16262306a36Sopenharmony_ci break; 16362306a36Sopenharmony_ci } else if (--retries) { 16462306a36Sopenharmony_ci dev_warn(&aaci->dev->dev, 16562306a36Sopenharmony_ci "ac97 read back fail. retry\n"); 16662306a36Sopenharmony_ci continue; 16762306a36Sopenharmony_ci } else { 16862306a36Sopenharmony_ci dev_warn(&aaci->dev->dev, 16962306a36Sopenharmony_ci "wrong ac97 register read back (%x != %x)\n", 17062306a36Sopenharmony_ci v, reg); 17162306a36Sopenharmony_ci v = ~0; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci } while (retries); 17462306a36Sopenharmony_ci out: 17562306a36Sopenharmony_ci mutex_unlock(&aaci->ac97_sem); 17662306a36Sopenharmony_ci return v; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic inline void 18062306a36Sopenharmony_ciaaci_chan_wait_ready(struct aaci_runtime *aacirun, unsigned long mask) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci u32 val; 18362306a36Sopenharmony_ci int timeout = 5000; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci do { 18662306a36Sopenharmony_ci udelay(1); 18762306a36Sopenharmony_ci val = readl(aacirun->base + AACI_SR); 18862306a36Sopenharmony_ci } while (val & mask && timeout--); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/* 19462306a36Sopenharmony_ci * Interrupt support. 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_cistatic void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci if (mask & ISR_ORINTR) { 19962306a36Sopenharmony_ci dev_warn(&aaci->dev->dev, "RX overrun on chan %d\n", channel); 20062306a36Sopenharmony_ci writel(ICLR_RXOEC1 << channel, aaci->base + AACI_INTCLR); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (mask & ISR_RXTOINTR) { 20462306a36Sopenharmony_ci dev_warn(&aaci->dev->dev, "RX timeout on chan %d\n", channel); 20562306a36Sopenharmony_ci writel(ICLR_RXTOFEC1 << channel, aaci->base + AACI_INTCLR); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (mask & ISR_RXINTR) { 20962306a36Sopenharmony_ci struct aaci_runtime *aacirun = &aaci->capture; 21062306a36Sopenharmony_ci bool period_elapsed = false; 21162306a36Sopenharmony_ci void *ptr; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (!aacirun->substream || !aacirun->start) { 21462306a36Sopenharmony_ci dev_warn(&aaci->dev->dev, "RX interrupt???\n"); 21562306a36Sopenharmony_ci writel(0, aacirun->base + AACI_IE); 21662306a36Sopenharmony_ci return; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci spin_lock(&aacirun->lock); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci ptr = aacirun->ptr; 22262306a36Sopenharmony_ci do { 22362306a36Sopenharmony_ci unsigned int len = aacirun->fifo_bytes; 22462306a36Sopenharmony_ci u32 val; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (aacirun->bytes <= 0) { 22762306a36Sopenharmony_ci aacirun->bytes += aacirun->period; 22862306a36Sopenharmony_ci period_elapsed = true; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci if (!(aacirun->cr & CR_EN)) 23162306a36Sopenharmony_ci break; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci val = readl(aacirun->base + AACI_SR); 23462306a36Sopenharmony_ci if (!(val & SR_RXHF)) 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci if (!(val & SR_RXFF)) 23762306a36Sopenharmony_ci len >>= 1; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci aacirun->bytes -= len; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* reading 16 bytes at a time */ 24262306a36Sopenharmony_ci for( ; len > 0; len -= 16) { 24362306a36Sopenharmony_ci asm( 24462306a36Sopenharmony_ci "ldmia %1, {r0, r1, r2, r3}\n\t" 24562306a36Sopenharmony_ci "stmia %0!, {r0, r1, r2, r3}" 24662306a36Sopenharmony_ci : "+r" (ptr) 24762306a36Sopenharmony_ci : "r" (aacirun->fifo) 24862306a36Sopenharmony_ci : "r0", "r1", "r2", "r3", "cc"); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (ptr >= aacirun->end) 25162306a36Sopenharmony_ci ptr = aacirun->start; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci } while(1); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci aacirun->ptr = ptr; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci spin_unlock(&aacirun->lock); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (period_elapsed) 26062306a36Sopenharmony_ci snd_pcm_period_elapsed(aacirun->substream); 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (mask & ISR_URINTR) { 26462306a36Sopenharmony_ci dev_dbg(&aaci->dev->dev, "TX underrun on chan %d\n", channel); 26562306a36Sopenharmony_ci writel(ICLR_TXUEC1 << channel, aaci->base + AACI_INTCLR); 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (mask & ISR_TXINTR) { 26962306a36Sopenharmony_ci struct aaci_runtime *aacirun = &aaci->playback; 27062306a36Sopenharmony_ci bool period_elapsed = false; 27162306a36Sopenharmony_ci void *ptr; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (!aacirun->substream || !aacirun->start) { 27462306a36Sopenharmony_ci dev_warn(&aaci->dev->dev, "TX interrupt???\n"); 27562306a36Sopenharmony_ci writel(0, aacirun->base + AACI_IE); 27662306a36Sopenharmony_ci return; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci spin_lock(&aacirun->lock); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci ptr = aacirun->ptr; 28262306a36Sopenharmony_ci do { 28362306a36Sopenharmony_ci unsigned int len = aacirun->fifo_bytes; 28462306a36Sopenharmony_ci u32 val; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (aacirun->bytes <= 0) { 28762306a36Sopenharmony_ci aacirun->bytes += aacirun->period; 28862306a36Sopenharmony_ci period_elapsed = true; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci if (!(aacirun->cr & CR_EN)) 29162306a36Sopenharmony_ci break; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci val = readl(aacirun->base + AACI_SR); 29462306a36Sopenharmony_ci if (!(val & SR_TXHE)) 29562306a36Sopenharmony_ci break; 29662306a36Sopenharmony_ci if (!(val & SR_TXFE)) 29762306a36Sopenharmony_ci len >>= 1; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci aacirun->bytes -= len; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* writing 16 bytes at a time */ 30262306a36Sopenharmony_ci for ( ; len > 0; len -= 16) { 30362306a36Sopenharmony_ci asm( 30462306a36Sopenharmony_ci "ldmia %0!, {r0, r1, r2, r3}\n\t" 30562306a36Sopenharmony_ci "stmia %1, {r0, r1, r2, r3}" 30662306a36Sopenharmony_ci : "+r" (ptr) 30762306a36Sopenharmony_ci : "r" (aacirun->fifo) 30862306a36Sopenharmony_ci : "r0", "r1", "r2", "r3", "cc"); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (ptr >= aacirun->end) 31162306a36Sopenharmony_ci ptr = aacirun->start; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci } while (1); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci aacirun->ptr = ptr; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci spin_unlock(&aacirun->lock); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (period_elapsed) 32062306a36Sopenharmony_ci snd_pcm_period_elapsed(aacirun->substream); 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic irqreturn_t aaci_irq(int irq, void *devid) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct aaci *aaci = devid; 32762306a36Sopenharmony_ci u32 mask; 32862306a36Sopenharmony_ci int i; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci mask = readl(aaci->base + AACI_ALLINTS); 33162306a36Sopenharmony_ci if (mask) { 33262306a36Sopenharmony_ci u32 m = mask; 33362306a36Sopenharmony_ci for (i = 0; i < 4; i++, m >>= 7) { 33462306a36Sopenharmony_ci if (m & 0x7f) { 33562306a36Sopenharmony_ci aaci_fifo_irq(aaci, i, m); 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci return mask ? IRQ_HANDLED : IRQ_NONE; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci/* 34662306a36Sopenharmony_ci * ALSA support. 34762306a36Sopenharmony_ci */ 34862306a36Sopenharmony_cistatic const struct snd_pcm_hardware aaci_hw_info = { 34962306a36Sopenharmony_ci .info = SNDRV_PCM_INFO_MMAP | 35062306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 35162306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 35262306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 35362306a36Sopenharmony_ci SNDRV_PCM_INFO_RESUME, 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* 35662306a36Sopenharmony_ci * ALSA doesn't support 18-bit or 20-bit packed into 32-bit 35762306a36Sopenharmony_ci * words. It also doesn't support 12-bit at all. 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* rates are setup from the AC'97 codec */ 36262306a36Sopenharmony_ci .channels_min = 2, 36362306a36Sopenharmony_ci .channels_max = 2, 36462306a36Sopenharmony_ci .buffer_bytes_max = 64 * 1024, 36562306a36Sopenharmony_ci .period_bytes_min = 256, 36662306a36Sopenharmony_ci .period_bytes_max = PAGE_SIZE, 36762306a36Sopenharmony_ci .periods_min = 4, 36862306a36Sopenharmony_ci .periods_max = PAGE_SIZE / 16, 36962306a36Sopenharmony_ci}; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci/* 37262306a36Sopenharmony_ci * We can support two and four channel audio. Unfortunately 37362306a36Sopenharmony_ci * six channel audio requires a non-standard channel ordering: 37462306a36Sopenharmony_ci * 2 -> FL(3), FR(4) 37562306a36Sopenharmony_ci * 4 -> FL(3), FR(4), SL(7), SR(8) 37662306a36Sopenharmony_ci * 6 -> FL(3), FR(4), SL(7), SR(8), C(6), LFE(9) (required) 37762306a36Sopenharmony_ci * FL(3), FR(4), C(6), SL(7), SR(8), LFE(9) (actual) 37862306a36Sopenharmony_ci * This requires an ALSA configuration file to correct. 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_cistatic int aaci_rule_channels(struct snd_pcm_hw_params *p, 38162306a36Sopenharmony_ci struct snd_pcm_hw_rule *rule) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci static const unsigned int channel_list[] = { 2, 4, 6 }; 38462306a36Sopenharmony_ci struct aaci *aaci = rule->private; 38562306a36Sopenharmony_ci unsigned int mask = 1 << 0, slots; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* pcms[0] is the our 5.1 PCM instance. */ 38862306a36Sopenharmony_ci slots = aaci->ac97_bus->pcms[0].r[0].slots; 38962306a36Sopenharmony_ci if (slots & (1 << AC97_SLOT_PCM_SLEFT)) { 39062306a36Sopenharmony_ci mask |= 1 << 1; 39162306a36Sopenharmony_ci if (slots & (1 << AC97_SLOT_LFE)) 39262306a36Sopenharmony_ci mask |= 1 << 2; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return snd_interval_list(hw_param_interval(p, rule->var), 39662306a36Sopenharmony_ci ARRAY_SIZE(channel_list), channel_list, mask); 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic int aaci_pcm_open(struct snd_pcm_substream *substream) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 40262306a36Sopenharmony_ci struct aaci *aaci = substream->private_data; 40362306a36Sopenharmony_ci struct aaci_runtime *aacirun; 40462306a36Sopenharmony_ci int ret = 0; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 40762306a36Sopenharmony_ci aacirun = &aaci->playback; 40862306a36Sopenharmony_ci } else { 40962306a36Sopenharmony_ci aacirun = &aaci->capture; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci aacirun->substream = substream; 41362306a36Sopenharmony_ci runtime->private_data = aacirun; 41462306a36Sopenharmony_ci runtime->hw = aaci_hw_info; 41562306a36Sopenharmony_ci runtime->hw.rates = aacirun->pcm->rates; 41662306a36Sopenharmony_ci snd_pcm_limit_hw_rates(runtime); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 41962306a36Sopenharmony_ci runtime->hw.channels_max = 6; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* Add rule describing channel dependency. */ 42262306a36Sopenharmony_ci ret = snd_pcm_hw_rule_add(substream->runtime, 0, 42362306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, 42462306a36Sopenharmony_ci aaci_rule_channels, aaci, 42562306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, -1); 42662306a36Sopenharmony_ci if (ret) 42762306a36Sopenharmony_ci return ret; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (aacirun->pcm->r[1].slots) 43062306a36Sopenharmony_ci snd_ac97_pcm_double_rate_rules(runtime); 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* 43462306a36Sopenharmony_ci * ALSA wants the byte-size of the FIFOs. As we only support 43562306a36Sopenharmony_ci * 16-bit samples, this is twice the FIFO depth irrespective 43662306a36Sopenharmony_ci * of whether it's in compact mode or not. 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_ci runtime->hw.fifo_size = aaci->fifo_depth * 2; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci mutex_lock(&aaci->irq_lock); 44162306a36Sopenharmony_ci if (!aaci->users++) { 44262306a36Sopenharmony_ci ret = request_irq(aaci->dev->irq[0], aaci_irq, 44362306a36Sopenharmony_ci IRQF_SHARED, DRIVER_NAME, aaci); 44462306a36Sopenharmony_ci if (ret != 0) 44562306a36Sopenharmony_ci aaci->users--; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci mutex_unlock(&aaci->irq_lock); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return ret; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci/* 45462306a36Sopenharmony_ci * Common ALSA stuff 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_cistatic int aaci_pcm_close(struct snd_pcm_substream *substream) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct aaci *aaci = substream->private_data; 45962306a36Sopenharmony_ci struct aaci_runtime *aacirun = substream->runtime->private_data; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci WARN_ON(aacirun->cr & CR_EN); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci aacirun->substream = NULL; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci mutex_lock(&aaci->irq_lock); 46662306a36Sopenharmony_ci if (!--aaci->users) 46762306a36Sopenharmony_ci free_irq(aaci->dev->irq[0], aaci); 46862306a36Sopenharmony_ci mutex_unlock(&aaci->irq_lock); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci return 0; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic int aaci_pcm_hw_free(struct snd_pcm_substream *substream) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci struct aaci_runtime *aacirun = substream->runtime->private_data; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci /* 47862306a36Sopenharmony_ci * This must not be called with the device enabled. 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_ci WARN_ON(aacirun->cr & CR_EN); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (aacirun->pcm_open) 48362306a36Sopenharmony_ci snd_ac97_pcm_close(aacirun->pcm); 48462306a36Sopenharmony_ci aacirun->pcm_open = 0; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci return 0; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci/* Channel to slot mask */ 49062306a36Sopenharmony_cistatic const u32 channels_to_slotmask[] = { 49162306a36Sopenharmony_ci [2] = CR_SL3 | CR_SL4, 49262306a36Sopenharmony_ci [4] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8, 49362306a36Sopenharmony_ci [6] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8 | CR_SL6 | CR_SL9, 49462306a36Sopenharmony_ci}; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic int aaci_pcm_hw_params(struct snd_pcm_substream *substream, 49762306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct aaci_runtime *aacirun = substream->runtime->private_data; 50062306a36Sopenharmony_ci struct aaci *aaci = substream->private_data; 50162306a36Sopenharmony_ci unsigned int channels = params_channels(params); 50262306a36Sopenharmony_ci unsigned int rate = params_rate(params); 50362306a36Sopenharmony_ci int dbl = rate > 48000; 50462306a36Sopenharmony_ci int err; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci aaci_pcm_hw_free(substream); 50762306a36Sopenharmony_ci if (aacirun->pcm_open) { 50862306a36Sopenharmony_ci snd_ac97_pcm_close(aacirun->pcm); 50962306a36Sopenharmony_ci aacirun->pcm_open = 0; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* channels is already limited to 2, 4, or 6 by aaci_rule_channels */ 51362306a36Sopenharmony_ci if (dbl && channels != 2) 51462306a36Sopenharmony_ci return -EINVAL; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci err = snd_ac97_pcm_open(aacirun->pcm, rate, channels, 51762306a36Sopenharmony_ci aacirun->pcm->r[dbl].slots); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci aacirun->pcm_open = err == 0; 52062306a36Sopenharmony_ci aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16; 52162306a36Sopenharmony_ci aacirun->cr |= channels_to_slotmask[channels + dbl * 2]; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* 52462306a36Sopenharmony_ci * fifo_bytes is the number of bytes we transfer to/from 52562306a36Sopenharmony_ci * the FIFO, including padding. So that's x4. As we're 52662306a36Sopenharmony_ci * in compact mode, the FIFO is half the size. 52762306a36Sopenharmony_ci */ 52862306a36Sopenharmony_ci aacirun->fifo_bytes = aaci->fifo_depth * 4 / 2; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci return err; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic int aaci_pcm_prepare(struct snd_pcm_substream *substream) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 53662306a36Sopenharmony_ci struct aaci_runtime *aacirun = runtime->private_data; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci aacirun->period = snd_pcm_lib_period_bytes(substream); 53962306a36Sopenharmony_ci aacirun->start = runtime->dma_area; 54062306a36Sopenharmony_ci aacirun->end = aacirun->start + snd_pcm_lib_buffer_bytes(substream); 54162306a36Sopenharmony_ci aacirun->ptr = aacirun->start; 54262306a36Sopenharmony_ci aacirun->bytes = aacirun->period; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci return 0; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic snd_pcm_uframes_t aaci_pcm_pointer(struct snd_pcm_substream *substream) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 55062306a36Sopenharmony_ci struct aaci_runtime *aacirun = runtime->private_data; 55162306a36Sopenharmony_ci ssize_t bytes = aacirun->ptr - aacirun->start; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci return bytes_to_frames(runtime, bytes); 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci/* 55862306a36Sopenharmony_ci * Playback specific ALSA stuff 55962306a36Sopenharmony_ci */ 56062306a36Sopenharmony_cistatic void aaci_pcm_playback_stop(struct aaci_runtime *aacirun) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci u32 ie; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci ie = readl(aacirun->base + AACI_IE); 56562306a36Sopenharmony_ci ie &= ~(IE_URIE|IE_TXIE); 56662306a36Sopenharmony_ci writel(ie, aacirun->base + AACI_IE); 56762306a36Sopenharmony_ci aacirun->cr &= ~CR_EN; 56862306a36Sopenharmony_ci aaci_chan_wait_ready(aacirun, SR_TXB); 56962306a36Sopenharmony_ci writel(aacirun->cr, aacirun->base + AACI_TXCR); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic void aaci_pcm_playback_start(struct aaci_runtime *aacirun) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci u32 ie; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci aaci_chan_wait_ready(aacirun, SR_TXB); 57762306a36Sopenharmony_ci aacirun->cr |= CR_EN; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci ie = readl(aacirun->base + AACI_IE); 58062306a36Sopenharmony_ci ie |= IE_URIE | IE_TXIE; 58162306a36Sopenharmony_ci writel(ie, aacirun->base + AACI_IE); 58262306a36Sopenharmony_ci writel(aacirun->cr, aacirun->base + AACI_TXCR); 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic int aaci_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci struct aaci_runtime *aacirun = substream->runtime->private_data; 58862306a36Sopenharmony_ci unsigned long flags; 58962306a36Sopenharmony_ci int ret = 0; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci spin_lock_irqsave(&aacirun->lock, flags); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci switch (cmd) { 59462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 59562306a36Sopenharmony_ci aaci_pcm_playback_start(aacirun); 59662306a36Sopenharmony_ci break; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 59962306a36Sopenharmony_ci aaci_pcm_playback_start(aacirun); 60062306a36Sopenharmony_ci break; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 60362306a36Sopenharmony_ci aaci_pcm_playback_stop(aacirun); 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 60762306a36Sopenharmony_ci aaci_pcm_playback_stop(aacirun); 60862306a36Sopenharmony_ci break; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 61162306a36Sopenharmony_ci break; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 61462306a36Sopenharmony_ci break; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci default: 61762306a36Sopenharmony_ci ret = -EINVAL; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci spin_unlock_irqrestore(&aacirun->lock, flags); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci return ret; 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic const struct snd_pcm_ops aaci_playback_ops = { 62662306a36Sopenharmony_ci .open = aaci_pcm_open, 62762306a36Sopenharmony_ci .close = aaci_pcm_close, 62862306a36Sopenharmony_ci .hw_params = aaci_pcm_hw_params, 62962306a36Sopenharmony_ci .hw_free = aaci_pcm_hw_free, 63062306a36Sopenharmony_ci .prepare = aaci_pcm_prepare, 63162306a36Sopenharmony_ci .trigger = aaci_pcm_playback_trigger, 63262306a36Sopenharmony_ci .pointer = aaci_pcm_pointer, 63362306a36Sopenharmony_ci}; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic void aaci_pcm_capture_stop(struct aaci_runtime *aacirun) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci u32 ie; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci aaci_chan_wait_ready(aacirun, SR_RXB); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci ie = readl(aacirun->base + AACI_IE); 64262306a36Sopenharmony_ci ie &= ~(IE_ORIE | IE_RXIE); 64362306a36Sopenharmony_ci writel(ie, aacirun->base+AACI_IE); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci aacirun->cr &= ~CR_EN; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci writel(aacirun->cr, aacirun->base + AACI_RXCR); 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic void aaci_pcm_capture_start(struct aaci_runtime *aacirun) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci u32 ie; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci aaci_chan_wait_ready(aacirun, SR_RXB); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci#ifdef DEBUG 65762306a36Sopenharmony_ci /* RX Timeout value: bits 28:17 in RXCR */ 65862306a36Sopenharmony_ci aacirun->cr |= 0xf << 17; 65962306a36Sopenharmony_ci#endif 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci aacirun->cr |= CR_EN; 66262306a36Sopenharmony_ci writel(aacirun->cr, aacirun->base + AACI_RXCR); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci ie = readl(aacirun->base + AACI_IE); 66562306a36Sopenharmony_ci ie |= IE_ORIE |IE_RXIE; // overrun and rx interrupt -- half full 66662306a36Sopenharmony_ci writel(ie, aacirun->base + AACI_IE); 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic int aaci_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci struct aaci_runtime *aacirun = substream->runtime->private_data; 67262306a36Sopenharmony_ci unsigned long flags; 67362306a36Sopenharmony_ci int ret = 0; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci spin_lock_irqsave(&aacirun->lock, flags); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci switch (cmd) { 67862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 67962306a36Sopenharmony_ci aaci_pcm_capture_start(aacirun); 68062306a36Sopenharmony_ci break; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 68362306a36Sopenharmony_ci aaci_pcm_capture_start(aacirun); 68462306a36Sopenharmony_ci break; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 68762306a36Sopenharmony_ci aaci_pcm_capture_stop(aacirun); 68862306a36Sopenharmony_ci break; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 69162306a36Sopenharmony_ci aaci_pcm_capture_stop(aacirun); 69262306a36Sopenharmony_ci break; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 69562306a36Sopenharmony_ci break; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 69862306a36Sopenharmony_ci break; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci default: 70162306a36Sopenharmony_ci ret = -EINVAL; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci spin_unlock_irqrestore(&aacirun->lock, flags); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci return ret; 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic int aaci_pcm_capture_prepare(struct snd_pcm_substream *substream) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 71262306a36Sopenharmony_ci struct aaci *aaci = substream->private_data; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci aaci_pcm_prepare(substream); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci /* allow changing of sample rate */ 71762306a36Sopenharmony_ci aaci_ac97_write(aaci->ac97, AC97_EXTENDED_STATUS, 0x0001); /* VRA */ 71862306a36Sopenharmony_ci aaci_ac97_write(aaci->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); 71962306a36Sopenharmony_ci aaci_ac97_write(aaci->ac97, AC97_PCM_MIC_ADC_RATE, runtime->rate); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci /* Record select: Mic: 0, Aux: 3, Line: 4 */ 72262306a36Sopenharmony_ci aaci_ac97_write(aaci->ac97, AC97_REC_SEL, 0x0404); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci return 0; 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic const struct snd_pcm_ops aaci_capture_ops = { 72862306a36Sopenharmony_ci .open = aaci_pcm_open, 72962306a36Sopenharmony_ci .close = aaci_pcm_close, 73062306a36Sopenharmony_ci .hw_params = aaci_pcm_hw_params, 73162306a36Sopenharmony_ci .hw_free = aaci_pcm_hw_free, 73262306a36Sopenharmony_ci .prepare = aaci_pcm_capture_prepare, 73362306a36Sopenharmony_ci .trigger = aaci_pcm_capture_trigger, 73462306a36Sopenharmony_ci .pointer = aaci_pcm_pointer, 73562306a36Sopenharmony_ci}; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci/* 73862306a36Sopenharmony_ci * Power Management. 73962306a36Sopenharmony_ci */ 74062306a36Sopenharmony_ci#ifdef CONFIG_PM 74162306a36Sopenharmony_cistatic int aaci_do_suspend(struct snd_card *card) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci struct aaci *aaci = card->private_data; 74462306a36Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D3cold); 74562306a36Sopenharmony_ci return 0; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic int aaci_do_resume(struct snd_card *card) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D0); 75162306a36Sopenharmony_ci return 0; 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic int aaci_suspend(struct device *dev) 75562306a36Sopenharmony_ci{ 75662306a36Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 75762306a36Sopenharmony_ci return card ? aaci_do_suspend(card) : 0; 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_cistatic int aaci_resume(struct device *dev) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 76362306a36Sopenharmony_ci return card ? aaci_do_resume(card) : 0; 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(aaci_dev_pm_ops, aaci_suspend, aaci_resume); 76762306a36Sopenharmony_ci#define AACI_DEV_PM_OPS (&aaci_dev_pm_ops) 76862306a36Sopenharmony_ci#else 76962306a36Sopenharmony_ci#define AACI_DEV_PM_OPS NULL 77062306a36Sopenharmony_ci#endif 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic const struct ac97_pcm ac97_defs[] = { 77462306a36Sopenharmony_ci [0] = { /* Front PCM */ 77562306a36Sopenharmony_ci .exclusive = 1, 77662306a36Sopenharmony_ci .r = { 77762306a36Sopenharmony_ci [0] = { 77862306a36Sopenharmony_ci .slots = (1 << AC97_SLOT_PCM_LEFT) | 77962306a36Sopenharmony_ci (1 << AC97_SLOT_PCM_RIGHT) | 78062306a36Sopenharmony_ci (1 << AC97_SLOT_PCM_CENTER) | 78162306a36Sopenharmony_ci (1 << AC97_SLOT_PCM_SLEFT) | 78262306a36Sopenharmony_ci (1 << AC97_SLOT_PCM_SRIGHT) | 78362306a36Sopenharmony_ci (1 << AC97_SLOT_LFE), 78462306a36Sopenharmony_ci }, 78562306a36Sopenharmony_ci [1] = { 78662306a36Sopenharmony_ci .slots = (1 << AC97_SLOT_PCM_LEFT) | 78762306a36Sopenharmony_ci (1 << AC97_SLOT_PCM_RIGHT) | 78862306a36Sopenharmony_ci (1 << AC97_SLOT_PCM_LEFT_0) | 78962306a36Sopenharmony_ci (1 << AC97_SLOT_PCM_RIGHT_0), 79062306a36Sopenharmony_ci }, 79162306a36Sopenharmony_ci }, 79262306a36Sopenharmony_ci }, 79362306a36Sopenharmony_ci [1] = { /* PCM in */ 79462306a36Sopenharmony_ci .stream = 1, 79562306a36Sopenharmony_ci .exclusive = 1, 79662306a36Sopenharmony_ci .r = { 79762306a36Sopenharmony_ci [0] = { 79862306a36Sopenharmony_ci .slots = (1 << AC97_SLOT_PCM_LEFT) | 79962306a36Sopenharmony_ci (1 << AC97_SLOT_PCM_RIGHT), 80062306a36Sopenharmony_ci }, 80162306a36Sopenharmony_ci }, 80262306a36Sopenharmony_ci }, 80362306a36Sopenharmony_ci [2] = { /* Mic in */ 80462306a36Sopenharmony_ci .stream = 1, 80562306a36Sopenharmony_ci .exclusive = 1, 80662306a36Sopenharmony_ci .r = { 80762306a36Sopenharmony_ci [0] = { 80862306a36Sopenharmony_ci .slots = (1 << AC97_SLOT_MIC), 80962306a36Sopenharmony_ci }, 81062306a36Sopenharmony_ci }, 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci}; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic const struct snd_ac97_bus_ops aaci_bus_ops = { 81562306a36Sopenharmony_ci .write = aaci_ac97_write, 81662306a36Sopenharmony_ci .read = aaci_ac97_read, 81762306a36Sopenharmony_ci}; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_cistatic int aaci_probe_ac97(struct aaci *aaci) 82062306a36Sopenharmony_ci{ 82162306a36Sopenharmony_ci struct snd_ac97_template ac97_template; 82262306a36Sopenharmony_ci struct snd_ac97_bus *ac97_bus; 82362306a36Sopenharmony_ci struct snd_ac97 *ac97; 82462306a36Sopenharmony_ci int ret; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci /* 82762306a36Sopenharmony_ci * Assert AACIRESET for 2us 82862306a36Sopenharmony_ci */ 82962306a36Sopenharmony_ci writel(0, aaci->base + AACI_RESET); 83062306a36Sopenharmony_ci udelay(2); 83162306a36Sopenharmony_ci writel(RESET_NRST, aaci->base + AACI_RESET); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci /* 83462306a36Sopenharmony_ci * Give the AC'97 codec more than enough time 83562306a36Sopenharmony_ci * to wake up. (42us = ~2 frames at 48kHz.) 83662306a36Sopenharmony_ci */ 83762306a36Sopenharmony_ci udelay(FRAME_PERIOD_US * 2); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci ret = snd_ac97_bus(aaci->card, 0, &aaci_bus_ops, aaci, &ac97_bus); 84062306a36Sopenharmony_ci if (ret) 84162306a36Sopenharmony_ci goto out; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci ac97_bus->clock = 48000; 84462306a36Sopenharmony_ci aaci->ac97_bus = ac97_bus; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci memset(&ac97_template, 0, sizeof(struct snd_ac97_template)); 84762306a36Sopenharmony_ci ac97_template.private_data = aaci; 84862306a36Sopenharmony_ci ac97_template.num = 0; 84962306a36Sopenharmony_ci ac97_template.scaps = AC97_SCAP_SKIP_MODEM; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97); 85262306a36Sopenharmony_ci if (ret) 85362306a36Sopenharmony_ci goto out; 85462306a36Sopenharmony_ci aaci->ac97 = ac97; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci /* 85762306a36Sopenharmony_ci * Disable AC97 PC Beep input on audio codecs. 85862306a36Sopenharmony_ci */ 85962306a36Sopenharmony_ci if (ac97_is_audio(ac97)) 86062306a36Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_PC_BEEP, 0x801e); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci ret = snd_ac97_pcm_assign(ac97_bus, ARRAY_SIZE(ac97_defs), ac97_defs); 86362306a36Sopenharmony_ci if (ret) 86462306a36Sopenharmony_ci goto out; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci aaci->playback.pcm = &ac97_bus->pcms[0]; 86762306a36Sopenharmony_ci aaci->capture.pcm = &ac97_bus->pcms[1]; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci out: 87062306a36Sopenharmony_ci return ret; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic void aaci_free_card(struct snd_card *card) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci struct aaci *aaci = card->private_data; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci iounmap(aaci->base); 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cistatic struct aaci *aaci_init_card(struct amba_device *dev) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci struct aaci *aaci; 88362306a36Sopenharmony_ci struct snd_card *card; 88462306a36Sopenharmony_ci int err; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci err = snd_card_new(&dev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, 88762306a36Sopenharmony_ci THIS_MODULE, sizeof(struct aaci), &card); 88862306a36Sopenharmony_ci if (err < 0) 88962306a36Sopenharmony_ci return NULL; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci card->private_free = aaci_free_card; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci strscpy(card->driver, DRIVER_NAME, sizeof(card->driver)); 89462306a36Sopenharmony_ci strscpy(card->shortname, "ARM AC'97 Interface", sizeof(card->shortname)); 89562306a36Sopenharmony_ci snprintf(card->longname, sizeof(card->longname), 89662306a36Sopenharmony_ci "%s PL%03x rev%u at 0x%08llx, irq %d", 89762306a36Sopenharmony_ci card->shortname, amba_part(dev), amba_rev(dev), 89862306a36Sopenharmony_ci (unsigned long long)dev->res.start, dev->irq[0]); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci aaci = card->private_data; 90162306a36Sopenharmony_ci mutex_init(&aaci->ac97_sem); 90262306a36Sopenharmony_ci mutex_init(&aaci->irq_lock); 90362306a36Sopenharmony_ci aaci->card = card; 90462306a36Sopenharmony_ci aaci->dev = dev; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci /* Set MAINCR to allow slot 1 and 2 data IO */ 90762306a36Sopenharmony_ci aaci->maincr = MAINCR_IE | MAINCR_SL1RXEN | MAINCR_SL1TXEN | 90862306a36Sopenharmony_ci MAINCR_SL2RXEN | MAINCR_SL2TXEN; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci return aaci; 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_cistatic int aaci_init_pcm(struct aaci *aaci) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci struct snd_pcm *pcm; 91662306a36Sopenharmony_ci int ret; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci ret = snd_pcm_new(aaci->card, "AACI AC'97", 0, 1, 1, &pcm); 91962306a36Sopenharmony_ci if (ret == 0) { 92062306a36Sopenharmony_ci aaci->pcm = pcm; 92162306a36Sopenharmony_ci pcm->private_data = aaci; 92262306a36Sopenharmony_ci pcm->info_flags = 0; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci strscpy(pcm->name, DRIVER_NAME, sizeof(pcm->name)); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaci_playback_ops); 92762306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &aaci_capture_ops); 92862306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 92962306a36Sopenharmony_ci aaci->card->dev, 93062306a36Sopenharmony_ci 0, 64 * 1024); 93162306a36Sopenharmony_ci } 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci return ret; 93462306a36Sopenharmony_ci} 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_cistatic unsigned int aaci_size_fifo(struct aaci *aaci) 93762306a36Sopenharmony_ci{ 93862306a36Sopenharmony_ci struct aaci_runtime *aacirun = &aaci->playback; 93962306a36Sopenharmony_ci int i; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci /* 94262306a36Sopenharmony_ci * Enable the channel, but don't assign it to any slots, so 94362306a36Sopenharmony_ci * it won't empty onto the AC'97 link. 94462306a36Sopenharmony_ci */ 94562306a36Sopenharmony_ci writel(CR_FEN | CR_SZ16 | CR_EN, aacirun->base + AACI_TXCR); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci for (i = 0; !(readl(aacirun->base + AACI_SR) & SR_TXFF) && i < 4096; i++) 94862306a36Sopenharmony_ci writel(0, aacirun->fifo); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci writel(0, aacirun->base + AACI_TXCR); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci /* 95362306a36Sopenharmony_ci * Re-initialise the AACI after the FIFO depth test, to 95462306a36Sopenharmony_ci * ensure that the FIFOs are empty. Unfortunately, merely 95562306a36Sopenharmony_ci * disabling the channel doesn't clear the FIFO. 95662306a36Sopenharmony_ci */ 95762306a36Sopenharmony_ci writel(aaci->maincr & ~MAINCR_IE, aaci->base + AACI_MAINCR); 95862306a36Sopenharmony_ci readl(aaci->base + AACI_MAINCR); 95962306a36Sopenharmony_ci udelay(1); 96062306a36Sopenharmony_ci writel(aaci->maincr, aaci->base + AACI_MAINCR); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci /* 96362306a36Sopenharmony_ci * If we hit 4096 entries, we failed. Go back to the specified 96462306a36Sopenharmony_ci * fifo depth. 96562306a36Sopenharmony_ci */ 96662306a36Sopenharmony_ci if (i == 4096) 96762306a36Sopenharmony_ci i = 8; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci return i; 97062306a36Sopenharmony_ci} 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cistatic int aaci_probe(struct amba_device *dev, 97362306a36Sopenharmony_ci const struct amba_id *id) 97462306a36Sopenharmony_ci{ 97562306a36Sopenharmony_ci struct aaci *aaci; 97662306a36Sopenharmony_ci int ret, i; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci ret = amba_request_regions(dev, NULL); 97962306a36Sopenharmony_ci if (ret) 98062306a36Sopenharmony_ci return ret; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci aaci = aaci_init_card(dev); 98362306a36Sopenharmony_ci if (!aaci) { 98462306a36Sopenharmony_ci ret = -ENOMEM; 98562306a36Sopenharmony_ci goto out; 98662306a36Sopenharmony_ci } 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci aaci->base = ioremap(dev->res.start, resource_size(&dev->res)); 98962306a36Sopenharmony_ci if (!aaci->base) { 99062306a36Sopenharmony_ci ret = -ENOMEM; 99162306a36Sopenharmony_ci goto out; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci /* 99562306a36Sopenharmony_ci * Playback uses AACI channel 0 99662306a36Sopenharmony_ci */ 99762306a36Sopenharmony_ci spin_lock_init(&aaci->playback.lock); 99862306a36Sopenharmony_ci aaci->playback.base = aaci->base + AACI_CSCH1; 99962306a36Sopenharmony_ci aaci->playback.fifo = aaci->base + AACI_DR1; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci /* 100262306a36Sopenharmony_ci * Capture uses AACI channel 0 100362306a36Sopenharmony_ci */ 100462306a36Sopenharmony_ci spin_lock_init(&aaci->capture.lock); 100562306a36Sopenharmony_ci aaci->capture.base = aaci->base + AACI_CSCH1; 100662306a36Sopenharmony_ci aaci->capture.fifo = aaci->base + AACI_DR1; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 100962306a36Sopenharmony_ci void __iomem *base = aaci->base + i * 0x14; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci writel(0, base + AACI_IE); 101262306a36Sopenharmony_ci writel(0, base + AACI_TXCR); 101362306a36Sopenharmony_ci writel(0, base + AACI_RXCR); 101462306a36Sopenharmony_ci } 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci writel(0x1fff, aaci->base + AACI_INTCLR); 101762306a36Sopenharmony_ci writel(aaci->maincr, aaci->base + AACI_MAINCR); 101862306a36Sopenharmony_ci /* 101962306a36Sopenharmony_ci * Fix: ac97 read back fail errors by reading 102062306a36Sopenharmony_ci * from any arbitrary aaci register. 102162306a36Sopenharmony_ci */ 102262306a36Sopenharmony_ci readl(aaci->base + AACI_CSCH1); 102362306a36Sopenharmony_ci ret = aaci_probe_ac97(aaci); 102462306a36Sopenharmony_ci if (ret) 102562306a36Sopenharmony_ci goto out; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci /* 102862306a36Sopenharmony_ci * Size the FIFOs (must be multiple of 16). 102962306a36Sopenharmony_ci * This is the number of entries in the FIFO. 103062306a36Sopenharmony_ci */ 103162306a36Sopenharmony_ci aaci->fifo_depth = aaci_size_fifo(aaci); 103262306a36Sopenharmony_ci if (aaci->fifo_depth & 15) { 103362306a36Sopenharmony_ci printk(KERN_WARNING "AACI: FIFO depth %d not supported\n", 103462306a36Sopenharmony_ci aaci->fifo_depth); 103562306a36Sopenharmony_ci ret = -ENODEV; 103662306a36Sopenharmony_ci goto out; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci ret = aaci_init_pcm(aaci); 104062306a36Sopenharmony_ci if (ret) 104162306a36Sopenharmony_ci goto out; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci ret = snd_card_register(aaci->card); 104462306a36Sopenharmony_ci if (ret == 0) { 104562306a36Sopenharmony_ci dev_info(&dev->dev, "%s\n", aaci->card->longname); 104662306a36Sopenharmony_ci dev_info(&dev->dev, "FIFO %u entries\n", aaci->fifo_depth); 104762306a36Sopenharmony_ci amba_set_drvdata(dev, aaci->card); 104862306a36Sopenharmony_ci return ret; 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci out: 105262306a36Sopenharmony_ci if (aaci) 105362306a36Sopenharmony_ci snd_card_free(aaci->card); 105462306a36Sopenharmony_ci amba_release_regions(dev); 105562306a36Sopenharmony_ci return ret; 105662306a36Sopenharmony_ci} 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_cistatic void aaci_remove(struct amba_device *dev) 105962306a36Sopenharmony_ci{ 106062306a36Sopenharmony_ci struct snd_card *card = amba_get_drvdata(dev); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci if (card) { 106362306a36Sopenharmony_ci struct aaci *aaci = card->private_data; 106462306a36Sopenharmony_ci writel(0, aaci->base + AACI_MAINCR); 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci snd_card_free(card); 106762306a36Sopenharmony_ci amba_release_regions(dev); 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci} 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_cistatic struct amba_id aaci_ids[] = { 107262306a36Sopenharmony_ci { 107362306a36Sopenharmony_ci .id = 0x00041041, 107462306a36Sopenharmony_ci .mask = 0x000fffff, 107562306a36Sopenharmony_ci }, 107662306a36Sopenharmony_ci { 0, 0 }, 107762306a36Sopenharmony_ci}; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(amba, aaci_ids); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_cistatic struct amba_driver aaci_driver = { 108262306a36Sopenharmony_ci .drv = { 108362306a36Sopenharmony_ci .name = DRIVER_NAME, 108462306a36Sopenharmony_ci .pm = AACI_DEV_PM_OPS, 108562306a36Sopenharmony_ci }, 108662306a36Sopenharmony_ci .probe = aaci_probe, 108762306a36Sopenharmony_ci .remove = aaci_remove, 108862306a36Sopenharmony_ci .id_table = aaci_ids, 108962306a36Sopenharmony_ci}; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_cimodule_amba_driver(aaci_driver); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 109462306a36Sopenharmony_ciMODULE_DESCRIPTION("ARM PrimeCell PL041 Advanced Audio CODEC Interface driver"); 1095