162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PMac DBDMA lowlevel functions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) by Takashi Iwai <tiwai@suse.de> 662306a36Sopenharmony_ci * code based on dmasound.c. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <asm/irq.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/pci.h> 1762306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1862306a36Sopenharmony_ci#include <linux/of_address.h> 1962306a36Sopenharmony_ci#include <linux/of_irq.h> 2062306a36Sopenharmony_ci#include <sound/core.h> 2162306a36Sopenharmony_ci#include "pmac.h" 2262306a36Sopenharmony_ci#include <sound/pcm_params.h> 2362306a36Sopenharmony_ci#include <asm/pmac_feature.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* fixed frequency table for awacs, screamer, burgundy, DACA (44100 max) */ 2762306a36Sopenharmony_cistatic const int awacs_freqs[8] = { 2862306a36Sopenharmony_ci 44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci/* fixed frequency table for tumbler */ 3162306a36Sopenharmony_cistatic const int tumbler_freqs[1] = { 3262306a36Sopenharmony_ci 44100 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * we will allocate a single 'emergency' dbdma cmd block to use if the 3862306a36Sopenharmony_ci * tx status comes up "DEAD". This happens on some PowerComputing Pmac 3962306a36Sopenharmony_ci * clones, either owing to a bug in dbdma or some interaction between 4062306a36Sopenharmony_ci * IDE and sound. However, this measure would deal with DEAD status if 4162306a36Sopenharmony_ci * it appeared elsewhere. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_cistatic struct pmac_dbdma emergency_dbdma; 4462306a36Sopenharmony_cistatic int emergency_in_use; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* 4862306a36Sopenharmony_ci * allocate DBDMA command arrays 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_cistatic int snd_pmac_dbdma_alloc(struct snd_pmac *chip, struct pmac_dbdma *rec, int size) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci unsigned int rsize = sizeof(struct dbdma_cmd) * (size + 1); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci rec->space = dma_alloc_coherent(&chip->pdev->dev, rsize, 5562306a36Sopenharmony_ci &rec->dma_base, GFP_KERNEL); 5662306a36Sopenharmony_ci if (rec->space == NULL) 5762306a36Sopenharmony_ci return -ENOMEM; 5862306a36Sopenharmony_ci rec->size = size; 5962306a36Sopenharmony_ci memset(rec->space, 0, rsize); 6062306a36Sopenharmony_ci rec->cmds = (void __iomem *)DBDMA_ALIGN(rec->space); 6162306a36Sopenharmony_ci rec->addr = rec->dma_base + (unsigned long)((char *)rec->cmds - (char *)rec->space); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return 0; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic void snd_pmac_dbdma_free(struct snd_pmac *chip, struct pmac_dbdma *rec) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci if (rec->space) { 6962306a36Sopenharmony_ci unsigned int rsize = sizeof(struct dbdma_cmd) * (rec->size + 1); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci dma_free_coherent(&chip->pdev->dev, rsize, rec->space, rec->dma_base); 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* 7762306a36Sopenharmony_ci * pcm stuff 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* 8162306a36Sopenharmony_ci * look up frequency table 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ciunsigned int snd_pmac_rate_index(struct snd_pmac *chip, struct pmac_stream *rec, unsigned int rate) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci int i, ok, found; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci ok = rec->cur_freqs; 8962306a36Sopenharmony_ci if (rate > chip->freq_table[0]) 9062306a36Sopenharmony_ci return 0; 9162306a36Sopenharmony_ci found = 0; 9262306a36Sopenharmony_ci for (i = 0; i < chip->num_freqs; i++, ok >>= 1) { 9362306a36Sopenharmony_ci if (! (ok & 1)) continue; 9462306a36Sopenharmony_ci found = i; 9562306a36Sopenharmony_ci if (rate >= chip->freq_table[i]) 9662306a36Sopenharmony_ci break; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci return found; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* 10262306a36Sopenharmony_ci * check whether another stream is active 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_cistatic inline int another_stream(int stream) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? 10762306a36Sopenharmony_ci SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/* 11162306a36Sopenharmony_ci * get a stream of the opposite direction 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_cistatic struct pmac_stream *snd_pmac_get_stream(struct snd_pmac *chip, int stream) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci switch (stream) { 11662306a36Sopenharmony_ci case SNDRV_PCM_STREAM_PLAYBACK: 11762306a36Sopenharmony_ci return &chip->playback; 11862306a36Sopenharmony_ci case SNDRV_PCM_STREAM_CAPTURE: 11962306a36Sopenharmony_ci return &chip->capture; 12062306a36Sopenharmony_ci default: 12162306a36Sopenharmony_ci snd_BUG(); 12262306a36Sopenharmony_ci return NULL; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* 12762306a36Sopenharmony_ci * wait while run status is on 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_cistatic inline void 13062306a36Sopenharmony_cisnd_pmac_wait_ack(struct pmac_stream *rec) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci int timeout = 50000; 13362306a36Sopenharmony_ci while ((in_le32(&rec->dma->status) & RUN) && timeout-- > 0) 13462306a36Sopenharmony_ci udelay(1); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* 13862306a36Sopenharmony_ci * set the format and rate to the chip. 13962306a36Sopenharmony_ci * call the lowlevel function if defined (e.g. for AWACS). 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_cistatic void snd_pmac_pcm_set_format(struct snd_pmac *chip) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci /* set up frequency and format */ 14462306a36Sopenharmony_ci out_le32(&chip->awacs->control, chip->control_mask | (chip->rate_index << 8)); 14562306a36Sopenharmony_ci out_le32(&chip->awacs->byteswap, chip->format == SNDRV_PCM_FORMAT_S16_LE ? 1 : 0); 14662306a36Sopenharmony_ci if (chip->set_format) 14762306a36Sopenharmony_ci chip->set_format(chip); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/* 15162306a36Sopenharmony_ci * stop the DMA transfer 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_cistatic inline void snd_pmac_dma_stop(struct pmac_stream *rec) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci out_le32(&rec->dma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); 15662306a36Sopenharmony_ci snd_pmac_wait_ack(rec); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* 16062306a36Sopenharmony_ci * set the command pointer address 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_cistatic inline void snd_pmac_dma_set_command(struct pmac_stream *rec, struct pmac_dbdma *cmd) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci out_le32(&rec->dma->cmdptr, cmd->addr); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci/* 16862306a36Sopenharmony_ci * start the DMA 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_cistatic inline void snd_pmac_dma_run(struct pmac_stream *rec, int status) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci out_le32(&rec->dma->control, status | (status << 16)); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/* 17762306a36Sopenharmony_ci * prepare playback/capture stream 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_cistatic int snd_pmac_pcm_prepare(struct snd_pmac *chip, struct pmac_stream *rec, struct snd_pcm_substream *subs) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci int i; 18262306a36Sopenharmony_ci volatile struct dbdma_cmd __iomem *cp; 18362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = subs->runtime; 18462306a36Sopenharmony_ci int rate_index; 18562306a36Sopenharmony_ci long offset; 18662306a36Sopenharmony_ci struct pmac_stream *astr; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci rec->dma_size = snd_pcm_lib_buffer_bytes(subs); 18962306a36Sopenharmony_ci rec->period_size = snd_pcm_lib_period_bytes(subs); 19062306a36Sopenharmony_ci rec->nperiods = rec->dma_size / rec->period_size; 19162306a36Sopenharmony_ci rec->cur_period = 0; 19262306a36Sopenharmony_ci rate_index = snd_pmac_rate_index(chip, rec, runtime->rate); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* set up constraints */ 19562306a36Sopenharmony_ci astr = snd_pmac_get_stream(chip, another_stream(rec->stream)); 19662306a36Sopenharmony_ci if (! astr) 19762306a36Sopenharmony_ci return -EINVAL; 19862306a36Sopenharmony_ci astr->cur_freqs = 1 << rate_index; 19962306a36Sopenharmony_ci astr->cur_formats = 1 << runtime->format; 20062306a36Sopenharmony_ci chip->rate_index = rate_index; 20162306a36Sopenharmony_ci chip->format = runtime->format; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* We really want to execute a DMA stop command, after the AWACS 20462306a36Sopenharmony_ci * is initialized. 20562306a36Sopenharmony_ci * For reasons I don't understand, it stops the hissing noise 20662306a36Sopenharmony_ci * common to many PowerBook G3 systems and random noise otherwise 20762306a36Sopenharmony_ci * captured on iBook2's about every third time. -ReneR 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 21062306a36Sopenharmony_ci snd_pmac_dma_stop(rec); 21162306a36Sopenharmony_ci chip->extra_dma.cmds->command = cpu_to_le16(DBDMA_STOP); 21262306a36Sopenharmony_ci snd_pmac_dma_set_command(rec, &chip->extra_dma); 21362306a36Sopenharmony_ci snd_pmac_dma_run(rec, RUN); 21462306a36Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 21562306a36Sopenharmony_ci mdelay(5); 21662306a36Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 21762306a36Sopenharmony_ci /* continuous DMA memory type doesn't provide the physical address, 21862306a36Sopenharmony_ci * so we need to resolve the address here... 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_ci offset = runtime->dma_addr; 22162306a36Sopenharmony_ci for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) { 22262306a36Sopenharmony_ci cp->phy_addr = cpu_to_le32(offset); 22362306a36Sopenharmony_ci cp->req_count = cpu_to_le16(rec->period_size); 22462306a36Sopenharmony_ci /*cp->res_count = cpu_to_le16(0);*/ 22562306a36Sopenharmony_ci cp->xfer_status = cpu_to_le16(0); 22662306a36Sopenharmony_ci offset += rec->period_size; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci /* make loop */ 22962306a36Sopenharmony_ci cp->command = cpu_to_le16(DBDMA_NOP | BR_ALWAYS); 23062306a36Sopenharmony_ci cp->cmd_dep = cpu_to_le32(rec->cmd.addr); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci snd_pmac_dma_stop(rec); 23362306a36Sopenharmony_ci snd_pmac_dma_set_command(rec, &rec->cmd); 23462306a36Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci/* 24162306a36Sopenharmony_ci * PCM trigger/stop 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_cistatic int snd_pmac_pcm_trigger(struct snd_pmac *chip, struct pmac_stream *rec, 24462306a36Sopenharmony_ci struct snd_pcm_substream *subs, int cmd) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci volatile struct dbdma_cmd __iomem *cp; 24762306a36Sopenharmony_ci int i, command; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci switch (cmd) { 25062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 25162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 25262306a36Sopenharmony_ci if (rec->running) 25362306a36Sopenharmony_ci return -EBUSY; 25462306a36Sopenharmony_ci command = (subs->stream == SNDRV_PCM_STREAM_PLAYBACK ? 25562306a36Sopenharmony_ci OUTPUT_MORE : INPUT_MORE) + INTR_ALWAYS; 25662306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 25762306a36Sopenharmony_ci snd_pmac_beep_stop(chip); 25862306a36Sopenharmony_ci snd_pmac_pcm_set_format(chip); 25962306a36Sopenharmony_ci for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) 26062306a36Sopenharmony_ci out_le16(&cp->command, command); 26162306a36Sopenharmony_ci snd_pmac_dma_set_command(rec, &rec->cmd); 26262306a36Sopenharmony_ci (void)in_le32(&rec->dma->status); 26362306a36Sopenharmony_ci snd_pmac_dma_run(rec, RUN|WAKE); 26462306a36Sopenharmony_ci rec->running = 1; 26562306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 26662306a36Sopenharmony_ci break; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 26962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 27062306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 27162306a36Sopenharmony_ci rec->running = 0; 27262306a36Sopenharmony_ci /*printk(KERN_DEBUG "stopped!!\n");*/ 27362306a36Sopenharmony_ci snd_pmac_dma_stop(rec); 27462306a36Sopenharmony_ci for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) 27562306a36Sopenharmony_ci out_le16(&cp->command, DBDMA_STOP); 27662306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci default: 28062306a36Sopenharmony_ci return -EINVAL; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci return 0; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci/* 28762306a36Sopenharmony_ci * return the current pointer 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_ciinline 29062306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_pmac_pcm_pointer(struct snd_pmac *chip, 29162306a36Sopenharmony_ci struct pmac_stream *rec, 29262306a36Sopenharmony_ci struct snd_pcm_substream *subs) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci int count = 0; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci#if 1 /* hmm.. how can we get the current dma pointer?? */ 29762306a36Sopenharmony_ci int stat; 29862306a36Sopenharmony_ci volatile struct dbdma_cmd __iomem *cp = &rec->cmd.cmds[rec->cur_period]; 29962306a36Sopenharmony_ci stat = le16_to_cpu(cp->xfer_status); 30062306a36Sopenharmony_ci if (stat & (ACTIVE|DEAD)) { 30162306a36Sopenharmony_ci count = in_le16(&cp->res_count); 30262306a36Sopenharmony_ci if (count) 30362306a36Sopenharmony_ci count = rec->period_size - count; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci#endif 30662306a36Sopenharmony_ci count += rec->cur_period * rec->period_size; 30762306a36Sopenharmony_ci /*printk(KERN_DEBUG "pointer=%d\n", count);*/ 30862306a36Sopenharmony_ci return bytes_to_frames(subs->runtime, count); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci/* 31262306a36Sopenharmony_ci * playback 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic int snd_pmac_playback_prepare(struct snd_pcm_substream *subs) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct snd_pmac *chip = snd_pcm_substream_chip(subs); 31862306a36Sopenharmony_ci return snd_pmac_pcm_prepare(chip, &chip->playback, subs); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic int snd_pmac_playback_trigger(struct snd_pcm_substream *subs, 32262306a36Sopenharmony_ci int cmd) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct snd_pmac *chip = snd_pcm_substream_chip(subs); 32562306a36Sopenharmony_ci return snd_pmac_pcm_trigger(chip, &chip->playback, subs, cmd); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_pmac_playback_pointer(struct snd_pcm_substream *subs) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct snd_pmac *chip = snd_pcm_substream_chip(subs); 33162306a36Sopenharmony_ci return snd_pmac_pcm_pointer(chip, &chip->playback, subs); 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci/* 33662306a36Sopenharmony_ci * capture 33762306a36Sopenharmony_ci */ 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic int snd_pmac_capture_prepare(struct snd_pcm_substream *subs) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct snd_pmac *chip = snd_pcm_substream_chip(subs); 34262306a36Sopenharmony_ci return snd_pmac_pcm_prepare(chip, &chip->capture, subs); 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic int snd_pmac_capture_trigger(struct snd_pcm_substream *subs, 34662306a36Sopenharmony_ci int cmd) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci struct snd_pmac *chip = snd_pcm_substream_chip(subs); 34962306a36Sopenharmony_ci return snd_pmac_pcm_trigger(chip, &chip->capture, subs, cmd); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_pmac_capture_pointer(struct snd_pcm_substream *subs) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct snd_pmac *chip = snd_pcm_substream_chip(subs); 35562306a36Sopenharmony_ci return snd_pmac_pcm_pointer(chip, &chip->capture, subs); 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci/* 36062306a36Sopenharmony_ci * Handle DEAD DMA transfers: 36162306a36Sopenharmony_ci * if the TX status comes up "DEAD" - reported on some Power Computing machines 36262306a36Sopenharmony_ci * we need to re-start the dbdma - but from a different physical start address 36362306a36Sopenharmony_ci * and with a different transfer length. It would get very messy to do this 36462306a36Sopenharmony_ci * with the normal dbdma_cmd blocks - we would have to re-write the buffer start 36562306a36Sopenharmony_ci * addresses each time. So, we will keep a single dbdma_cmd block which can be 36662306a36Sopenharmony_ci * fiddled with. 36762306a36Sopenharmony_ci * When DEAD status is first reported the content of the faulted dbdma block is 36862306a36Sopenharmony_ci * copied into the emergency buffer and we note that the buffer is in use. 36962306a36Sopenharmony_ci * we then bump the start physical address by the amount that was successfully 37062306a36Sopenharmony_ci * output before it died. 37162306a36Sopenharmony_ci * On any subsequent DEAD result we just do the bump-ups (we know that we are 37262306a36Sopenharmony_ci * already using the emergency dbdma_cmd). 37362306a36Sopenharmony_ci * CHECK: this just tries to "do it". It is possible that we should abandon 37462306a36Sopenharmony_ci * xfers when the number of residual bytes gets below a certain value - I can 37562306a36Sopenharmony_ci * see that this might cause a loop-forever if a too small transfer causes 37662306a36Sopenharmony_ci * DEAD status. However this is a TODO for now - we'll see what gets reported. 37762306a36Sopenharmony_ci * When we get a successful transfer result with the emergency buffer we just 37862306a36Sopenharmony_ci * pretend that it completed using the original dmdma_cmd and carry on. The 37962306a36Sopenharmony_ci * 'next_cmd' field will already point back to the original loop of blocks. 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_cistatic inline void snd_pmac_pcm_dead_xfer(struct pmac_stream *rec, 38262306a36Sopenharmony_ci volatile struct dbdma_cmd __iomem *cp) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci unsigned short req, res ; 38562306a36Sopenharmony_ci unsigned int phy ; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* printk(KERN_WARNING "snd-powermac: DMA died - patching it up!\n"); */ 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* to clear DEAD status we must first clear RUN 39062306a36Sopenharmony_ci set it to quiescent to be on the safe side */ 39162306a36Sopenharmony_ci (void)in_le32(&rec->dma->status); 39262306a36Sopenharmony_ci out_le32(&rec->dma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (!emergency_in_use) { /* new problem */ 39562306a36Sopenharmony_ci memcpy((void *)emergency_dbdma.cmds, (void *)cp, 39662306a36Sopenharmony_ci sizeof(struct dbdma_cmd)); 39762306a36Sopenharmony_ci emergency_in_use = 1; 39862306a36Sopenharmony_ci cp->xfer_status = cpu_to_le16(0); 39962306a36Sopenharmony_ci cp->req_count = cpu_to_le16(rec->period_size); 40062306a36Sopenharmony_ci cp = emergency_dbdma.cmds; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* now bump the values to reflect the amount 40462306a36Sopenharmony_ci we haven't yet shifted */ 40562306a36Sopenharmony_ci req = le16_to_cpu(cp->req_count); 40662306a36Sopenharmony_ci res = le16_to_cpu(cp->res_count); 40762306a36Sopenharmony_ci phy = le32_to_cpu(cp->phy_addr); 40862306a36Sopenharmony_ci phy += (req - res); 40962306a36Sopenharmony_ci cp->req_count = cpu_to_le16(res); 41062306a36Sopenharmony_ci cp->res_count = cpu_to_le16(0); 41162306a36Sopenharmony_ci cp->xfer_status = cpu_to_le16(0); 41262306a36Sopenharmony_ci cp->phy_addr = cpu_to_le32(phy); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci cp->cmd_dep = cpu_to_le32(rec->cmd.addr 41562306a36Sopenharmony_ci + sizeof(struct dbdma_cmd)*((rec->cur_period+1)%rec->nperiods)); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci cp->command = cpu_to_le16(OUTPUT_MORE | BR_ALWAYS | INTR_ALWAYS); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* point at our patched up command block */ 42062306a36Sopenharmony_ci out_le32(&rec->dma->cmdptr, emergency_dbdma.addr); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* we must re-start the controller */ 42362306a36Sopenharmony_ci (void)in_le32(&rec->dma->status); 42462306a36Sopenharmony_ci /* should complete clearing the DEAD status */ 42562306a36Sopenharmony_ci out_le32(&rec->dma->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci/* 42962306a36Sopenharmony_ci * update playback/capture pointer from interrupts 43062306a36Sopenharmony_ci */ 43162306a36Sopenharmony_cistatic void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci volatile struct dbdma_cmd __iomem *cp; 43462306a36Sopenharmony_ci int c; 43562306a36Sopenharmony_ci int stat; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 43862306a36Sopenharmony_ci if (rec->running) { 43962306a36Sopenharmony_ci for (c = 0; c < rec->nperiods; c++) { /* at most all fragments */ 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (emergency_in_use) /* already using DEAD xfer? */ 44262306a36Sopenharmony_ci cp = emergency_dbdma.cmds; 44362306a36Sopenharmony_ci else 44462306a36Sopenharmony_ci cp = &rec->cmd.cmds[rec->cur_period]; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci stat = le16_to_cpu(cp->xfer_status); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (stat & DEAD) { 44962306a36Sopenharmony_ci snd_pmac_pcm_dead_xfer(rec, cp); 45062306a36Sopenharmony_ci break; /* this block is still going */ 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (emergency_in_use) 45462306a36Sopenharmony_ci emergency_in_use = 0 ; /* done that */ 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (! (stat & ACTIVE)) 45762306a36Sopenharmony_ci break; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /*printk(KERN_DEBUG "update frag %d\n", rec->cur_period);*/ 46062306a36Sopenharmony_ci cp->xfer_status = cpu_to_le16(0); 46162306a36Sopenharmony_ci cp->req_count = cpu_to_le16(rec->period_size); 46262306a36Sopenharmony_ci /*cp->res_count = cpu_to_le16(0);*/ 46362306a36Sopenharmony_ci rec->cur_period++; 46462306a36Sopenharmony_ci if (rec->cur_period >= rec->nperiods) { 46562306a36Sopenharmony_ci rec->cur_period = 0; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 46962306a36Sopenharmony_ci snd_pcm_period_elapsed(rec->substream); 47062306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci/* 47862306a36Sopenharmony_ci * hw info 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_pmac_playback = 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_INTERLEAVED | 48462306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP | 48562306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 48662306a36Sopenharmony_ci SNDRV_PCM_INFO_RESUME), 48762306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_LE, 48862306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_44100, 48962306a36Sopenharmony_ci .rate_min = 7350, 49062306a36Sopenharmony_ci .rate_max = 44100, 49162306a36Sopenharmony_ci .channels_min = 2, 49262306a36Sopenharmony_ci .channels_max = 2, 49362306a36Sopenharmony_ci .buffer_bytes_max = 131072, 49462306a36Sopenharmony_ci .period_bytes_min = 256, 49562306a36Sopenharmony_ci .period_bytes_max = 16384, 49662306a36Sopenharmony_ci .periods_min = 3, 49762306a36Sopenharmony_ci .periods_max = PMAC_MAX_FRAGS, 49862306a36Sopenharmony_ci}; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_pmac_capture = 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_INTERLEAVED | 50362306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP | 50462306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 50562306a36Sopenharmony_ci SNDRV_PCM_INFO_RESUME), 50662306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_LE, 50762306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_44100, 50862306a36Sopenharmony_ci .rate_min = 7350, 50962306a36Sopenharmony_ci .rate_max = 44100, 51062306a36Sopenharmony_ci .channels_min = 2, 51162306a36Sopenharmony_ci .channels_max = 2, 51262306a36Sopenharmony_ci .buffer_bytes_max = 131072, 51362306a36Sopenharmony_ci .period_bytes_min = 256, 51462306a36Sopenharmony_ci .period_bytes_max = 16384, 51562306a36Sopenharmony_ci .periods_min = 3, 51662306a36Sopenharmony_ci .periods_max = PMAC_MAX_FRAGS, 51762306a36Sopenharmony_ci}; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci#if 0 // NYI 52162306a36Sopenharmony_cistatic int snd_pmac_hw_rule_rate(struct snd_pcm_hw_params *params, 52262306a36Sopenharmony_ci struct snd_pcm_hw_rule *rule) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci struct snd_pmac *chip = rule->private; 52562306a36Sopenharmony_ci struct pmac_stream *rec = snd_pmac_get_stream(chip, rule->deps[0]); 52662306a36Sopenharmony_ci int i, freq_table[8], num_freqs; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (! rec) 52962306a36Sopenharmony_ci return -EINVAL; 53062306a36Sopenharmony_ci num_freqs = 0; 53162306a36Sopenharmony_ci for (i = chip->num_freqs - 1; i >= 0; i--) { 53262306a36Sopenharmony_ci if (rec->cur_freqs & (1 << i)) 53362306a36Sopenharmony_ci freq_table[num_freqs++] = chip->freq_table[i]; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci return snd_interval_list(hw_param_interval(params, rule->var), 53762306a36Sopenharmony_ci num_freqs, freq_table, 0); 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic int snd_pmac_hw_rule_format(struct snd_pcm_hw_params *params, 54162306a36Sopenharmony_ci struct snd_pcm_hw_rule *rule) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci struct snd_pmac *chip = rule->private; 54462306a36Sopenharmony_ci struct pmac_stream *rec = snd_pmac_get_stream(chip, rule->deps[0]); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (! rec) 54762306a36Sopenharmony_ci return -EINVAL; 54862306a36Sopenharmony_ci return snd_mask_refine_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), 54962306a36Sopenharmony_ci rec->cur_formats); 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci#endif // NYI 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic int snd_pmac_pcm_open(struct snd_pmac *chip, struct pmac_stream *rec, 55462306a36Sopenharmony_ci struct snd_pcm_substream *subs) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = subs->runtime; 55762306a36Sopenharmony_ci int i; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* look up frequency table and fill bit mask */ 56062306a36Sopenharmony_ci runtime->hw.rates = 0; 56162306a36Sopenharmony_ci for (i = 0; i < chip->num_freqs; i++) 56262306a36Sopenharmony_ci if (chip->freqs_ok & (1 << i)) 56362306a36Sopenharmony_ci runtime->hw.rates |= 56462306a36Sopenharmony_ci snd_pcm_rate_to_rate_bit(chip->freq_table[i]); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* check for minimum and maximum rates */ 56762306a36Sopenharmony_ci for (i = 0; i < chip->num_freqs; i++) { 56862306a36Sopenharmony_ci if (chip->freqs_ok & (1 << i)) { 56962306a36Sopenharmony_ci runtime->hw.rate_max = chip->freq_table[i]; 57062306a36Sopenharmony_ci break; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci for (i = chip->num_freqs - 1; i >= 0; i--) { 57462306a36Sopenharmony_ci if (chip->freqs_ok & (1 << i)) { 57562306a36Sopenharmony_ci runtime->hw.rate_min = chip->freq_table[i]; 57662306a36Sopenharmony_ci break; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci runtime->hw.formats = chip->formats_ok; 58062306a36Sopenharmony_ci if (chip->can_capture) { 58162306a36Sopenharmony_ci if (! chip->can_duplex) 58262306a36Sopenharmony_ci runtime->hw.info |= SNDRV_PCM_INFO_HALF_DUPLEX; 58362306a36Sopenharmony_ci runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci runtime->private_data = rec; 58662306a36Sopenharmony_ci rec->substream = subs; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci#if 0 /* FIXME: still under development.. */ 58962306a36Sopenharmony_ci snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 59062306a36Sopenharmony_ci snd_pmac_hw_rule_rate, chip, rec->stream, -1); 59162306a36Sopenharmony_ci snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, 59262306a36Sopenharmony_ci snd_pmac_hw_rule_format, chip, rec->stream, -1); 59362306a36Sopenharmony_ci#endif 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci runtime->hw.periods_max = rec->cmd.size - 1; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* constraints to fix choppy sound */ 59862306a36Sopenharmony_ci snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic int snd_pmac_pcm_close(struct snd_pmac *chip, struct pmac_stream *rec, 60362306a36Sopenharmony_ci struct snd_pcm_substream *subs) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct pmac_stream *astr; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci snd_pmac_dma_stop(rec); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci astr = snd_pmac_get_stream(chip, another_stream(rec->stream)); 61062306a36Sopenharmony_ci if (! astr) 61162306a36Sopenharmony_ci return -EINVAL; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci /* reset constraints */ 61462306a36Sopenharmony_ci astr->cur_freqs = chip->freqs_ok; 61562306a36Sopenharmony_ci astr->cur_formats = chip->formats_ok; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci return 0; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic int snd_pmac_playback_open(struct snd_pcm_substream *subs) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci struct snd_pmac *chip = snd_pcm_substream_chip(subs); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci subs->runtime->hw = snd_pmac_playback; 62562306a36Sopenharmony_ci return snd_pmac_pcm_open(chip, &chip->playback, subs); 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic int snd_pmac_capture_open(struct snd_pcm_substream *subs) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci struct snd_pmac *chip = snd_pcm_substream_chip(subs); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci subs->runtime->hw = snd_pmac_capture; 63362306a36Sopenharmony_ci return snd_pmac_pcm_open(chip, &chip->capture, subs); 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic int snd_pmac_playback_close(struct snd_pcm_substream *subs) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct snd_pmac *chip = snd_pcm_substream_chip(subs); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci return snd_pmac_pcm_close(chip, &chip->playback, subs); 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic int snd_pmac_capture_close(struct snd_pcm_substream *subs) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci struct snd_pmac *chip = snd_pcm_substream_chip(subs); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci return snd_pmac_pcm_close(chip, &chip->capture, subs); 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci/* 65162306a36Sopenharmony_ci */ 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_pmac_playback_ops = { 65462306a36Sopenharmony_ci .open = snd_pmac_playback_open, 65562306a36Sopenharmony_ci .close = snd_pmac_playback_close, 65662306a36Sopenharmony_ci .prepare = snd_pmac_playback_prepare, 65762306a36Sopenharmony_ci .trigger = snd_pmac_playback_trigger, 65862306a36Sopenharmony_ci .pointer = snd_pmac_playback_pointer, 65962306a36Sopenharmony_ci}; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_pmac_capture_ops = { 66262306a36Sopenharmony_ci .open = snd_pmac_capture_open, 66362306a36Sopenharmony_ci .close = snd_pmac_capture_close, 66462306a36Sopenharmony_ci .prepare = snd_pmac_capture_prepare, 66562306a36Sopenharmony_ci .trigger = snd_pmac_capture_trigger, 66662306a36Sopenharmony_ci .pointer = snd_pmac_capture_pointer, 66762306a36Sopenharmony_ci}; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ciint snd_pmac_pcm_new(struct snd_pmac *chip) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci struct snd_pcm *pcm; 67262306a36Sopenharmony_ci int err; 67362306a36Sopenharmony_ci int num_captures = 1; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci if (! chip->can_capture) 67662306a36Sopenharmony_ci num_captures = 0; 67762306a36Sopenharmony_ci err = snd_pcm_new(chip->card, chip->card->driver, 0, 1, num_captures, &pcm); 67862306a36Sopenharmony_ci if (err < 0) 67962306a36Sopenharmony_ci return err; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_pmac_playback_ops); 68262306a36Sopenharmony_ci if (chip->can_capture) 68362306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_pmac_capture_ops); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci pcm->private_data = chip; 68662306a36Sopenharmony_ci pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; 68762306a36Sopenharmony_ci strcpy(pcm->name, chip->card->shortname); 68862306a36Sopenharmony_ci chip->pcm = pcm; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci chip->formats_ok = SNDRV_PCM_FMTBIT_S16_BE; 69162306a36Sopenharmony_ci if (chip->can_byte_swap) 69262306a36Sopenharmony_ci chip->formats_ok |= SNDRV_PCM_FMTBIT_S16_LE; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci chip->playback.cur_formats = chip->formats_ok; 69562306a36Sopenharmony_ci chip->capture.cur_formats = chip->formats_ok; 69662306a36Sopenharmony_ci chip->playback.cur_freqs = chip->freqs_ok; 69762306a36Sopenharmony_ci chip->capture.cur_freqs = chip->freqs_ok; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* preallocate 64k buffer */ 70062306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 70162306a36Sopenharmony_ci &chip->pdev->dev, 70262306a36Sopenharmony_ci 64 * 1024, 64 * 1024); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci return 0; 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cistatic void snd_pmac_dbdma_reset(struct snd_pmac *chip) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci out_le32(&chip->playback.dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16); 71162306a36Sopenharmony_ci snd_pmac_wait_ack(&chip->playback); 71262306a36Sopenharmony_ci out_le32(&chip->capture.dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16); 71362306a36Sopenharmony_ci snd_pmac_wait_ack(&chip->capture); 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci/* 71862306a36Sopenharmony_ci * handling beep 71962306a36Sopenharmony_ci */ 72062306a36Sopenharmony_civoid snd_pmac_beep_dma_start(struct snd_pmac *chip, int bytes, unsigned long addr, int speed) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci struct pmac_stream *rec = &chip->playback; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci snd_pmac_dma_stop(rec); 72562306a36Sopenharmony_ci chip->extra_dma.cmds->req_count = cpu_to_le16(bytes); 72662306a36Sopenharmony_ci chip->extra_dma.cmds->xfer_status = cpu_to_le16(0); 72762306a36Sopenharmony_ci chip->extra_dma.cmds->cmd_dep = cpu_to_le32(chip->extra_dma.addr); 72862306a36Sopenharmony_ci chip->extra_dma.cmds->phy_addr = cpu_to_le32(addr); 72962306a36Sopenharmony_ci chip->extra_dma.cmds->command = cpu_to_le16(OUTPUT_MORE | BR_ALWAYS); 73062306a36Sopenharmony_ci out_le32(&chip->awacs->control, 73162306a36Sopenharmony_ci (in_le32(&chip->awacs->control) & ~0x1f00) 73262306a36Sopenharmony_ci | (speed << 8)); 73362306a36Sopenharmony_ci out_le32(&chip->awacs->byteswap, 0); 73462306a36Sopenharmony_ci snd_pmac_dma_set_command(rec, &chip->extra_dma); 73562306a36Sopenharmony_ci snd_pmac_dma_run(rec, RUN); 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_civoid snd_pmac_beep_dma_stop(struct snd_pmac *chip) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci snd_pmac_dma_stop(&chip->playback); 74162306a36Sopenharmony_ci chip->extra_dma.cmds->command = cpu_to_le16(DBDMA_STOP); 74262306a36Sopenharmony_ci snd_pmac_pcm_set_format(chip); /* reset format */ 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci/* 74762306a36Sopenharmony_ci * interrupt handlers 74862306a36Sopenharmony_ci */ 74962306a36Sopenharmony_cistatic irqreturn_t 75062306a36Sopenharmony_cisnd_pmac_tx_intr(int irq, void *devid) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci struct snd_pmac *chip = devid; 75362306a36Sopenharmony_ci snd_pmac_pcm_update(chip, &chip->playback); 75462306a36Sopenharmony_ci return IRQ_HANDLED; 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic irqreturn_t 75962306a36Sopenharmony_cisnd_pmac_rx_intr(int irq, void *devid) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci struct snd_pmac *chip = devid; 76262306a36Sopenharmony_ci snd_pmac_pcm_update(chip, &chip->capture); 76362306a36Sopenharmony_ci return IRQ_HANDLED; 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_cistatic irqreturn_t 76862306a36Sopenharmony_cisnd_pmac_ctrl_intr(int irq, void *devid) 76962306a36Sopenharmony_ci{ 77062306a36Sopenharmony_ci struct snd_pmac *chip = devid; 77162306a36Sopenharmony_ci int ctrl = in_le32(&chip->awacs->control); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci /*printk(KERN_DEBUG "pmac: control interrupt.. 0x%x\n", ctrl);*/ 77462306a36Sopenharmony_ci if (ctrl & MASK_PORTCHG) { 77562306a36Sopenharmony_ci /* do something when headphone is plugged/unplugged? */ 77662306a36Sopenharmony_ci if (chip->update_automute) 77762306a36Sopenharmony_ci chip->update_automute(chip, 1); 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci if (ctrl & MASK_CNTLERR) { 78062306a36Sopenharmony_ci int err = (in_le32(&chip->awacs->codec_stat) & MASK_ERRCODE) >> 16; 78162306a36Sopenharmony_ci if (err && chip->model <= PMAC_SCREAMER) 78262306a36Sopenharmony_ci snd_printk(KERN_DEBUG "error %x\n", err); 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci /* Writing 1s to the CNTLERR and PORTCHG bits clears them... */ 78562306a36Sopenharmony_ci out_le32(&chip->awacs->control, ctrl); 78662306a36Sopenharmony_ci return IRQ_HANDLED; 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci/* 79162306a36Sopenharmony_ci * a wrapper to feature call for compatibility 79262306a36Sopenharmony_ci */ 79362306a36Sopenharmony_cistatic void snd_pmac_sound_feature(struct snd_pmac *chip, int enable) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci if (ppc_md.feature_call) 79662306a36Sopenharmony_ci ppc_md.feature_call(PMAC_FTR_SOUND_CHIP_ENABLE, chip->node, 0, enable); 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci/* 80062306a36Sopenharmony_ci * release resources 80162306a36Sopenharmony_ci */ 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic int snd_pmac_free(struct snd_pmac *chip) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci /* stop sounds */ 80662306a36Sopenharmony_ci if (chip->initialized) { 80762306a36Sopenharmony_ci snd_pmac_dbdma_reset(chip); 80862306a36Sopenharmony_ci /* disable interrupts from awacs interface */ 80962306a36Sopenharmony_ci out_le32(&chip->awacs->control, in_le32(&chip->awacs->control) & 0xfff); 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci if (chip->node) 81362306a36Sopenharmony_ci snd_pmac_sound_feature(chip, 0); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci /* clean up mixer if any */ 81662306a36Sopenharmony_ci if (chip->mixer_free) 81762306a36Sopenharmony_ci chip->mixer_free(chip); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci snd_pmac_detach_beep(chip); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci /* release resources */ 82262306a36Sopenharmony_ci if (chip->irq >= 0) 82362306a36Sopenharmony_ci free_irq(chip->irq, (void*)chip); 82462306a36Sopenharmony_ci if (chip->tx_irq >= 0) 82562306a36Sopenharmony_ci free_irq(chip->tx_irq, (void*)chip); 82662306a36Sopenharmony_ci if (chip->rx_irq >= 0) 82762306a36Sopenharmony_ci free_irq(chip->rx_irq, (void*)chip); 82862306a36Sopenharmony_ci snd_pmac_dbdma_free(chip, &chip->playback.cmd); 82962306a36Sopenharmony_ci snd_pmac_dbdma_free(chip, &chip->capture.cmd); 83062306a36Sopenharmony_ci snd_pmac_dbdma_free(chip, &chip->extra_dma); 83162306a36Sopenharmony_ci snd_pmac_dbdma_free(chip, &emergency_dbdma); 83262306a36Sopenharmony_ci iounmap(chip->macio_base); 83362306a36Sopenharmony_ci iounmap(chip->latch_base); 83462306a36Sopenharmony_ci iounmap(chip->awacs); 83562306a36Sopenharmony_ci iounmap(chip->playback.dma); 83662306a36Sopenharmony_ci iounmap(chip->capture.dma); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (chip->node) { 83962306a36Sopenharmony_ci int i; 84062306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 84162306a36Sopenharmony_ci if (chip->requested & (1 << i)) 84262306a36Sopenharmony_ci release_mem_region(chip->rsrc[i].start, 84362306a36Sopenharmony_ci resource_size(&chip->rsrc[i])); 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci pci_dev_put(chip->pdev); 84862306a36Sopenharmony_ci of_node_put(chip->node); 84962306a36Sopenharmony_ci kfree(chip); 85062306a36Sopenharmony_ci return 0; 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci/* 85562306a36Sopenharmony_ci * free the device 85662306a36Sopenharmony_ci */ 85762306a36Sopenharmony_cistatic int snd_pmac_dev_free(struct snd_device *device) 85862306a36Sopenharmony_ci{ 85962306a36Sopenharmony_ci struct snd_pmac *chip = device->device_data; 86062306a36Sopenharmony_ci return snd_pmac_free(chip); 86162306a36Sopenharmony_ci} 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci/* 86562306a36Sopenharmony_ci * check the machine support byteswap (little-endian) 86662306a36Sopenharmony_ci */ 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cistatic void detect_byte_swap(struct snd_pmac *chip) 86962306a36Sopenharmony_ci{ 87062306a36Sopenharmony_ci struct device_node *mio; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci /* if seems that Keylargo can't byte-swap */ 87362306a36Sopenharmony_ci for (mio = chip->node->parent; mio; mio = mio->parent) { 87462306a36Sopenharmony_ci if (of_node_name_eq(mio, "mac-io")) { 87562306a36Sopenharmony_ci if (of_device_is_compatible(mio, "Keylargo")) 87662306a36Sopenharmony_ci chip->can_byte_swap = 0; 87762306a36Sopenharmony_ci break; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci /* it seems the Pismo & iBook can't byte-swap in hardware. */ 88262306a36Sopenharmony_ci if (of_machine_is_compatible("PowerBook3,1") || 88362306a36Sopenharmony_ci of_machine_is_compatible("PowerBook2,1")) 88462306a36Sopenharmony_ci chip->can_byte_swap = 0 ; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci if (of_machine_is_compatible("PowerBook2,1")) 88762306a36Sopenharmony_ci chip->can_duplex = 0; 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci/* 89262306a36Sopenharmony_ci * detect a sound chip 89362306a36Sopenharmony_ci */ 89462306a36Sopenharmony_cistatic int snd_pmac_detect(struct snd_pmac *chip) 89562306a36Sopenharmony_ci{ 89662306a36Sopenharmony_ci struct device_node *sound; 89762306a36Sopenharmony_ci struct device_node *dn; 89862306a36Sopenharmony_ci const unsigned int *prop; 89962306a36Sopenharmony_ci unsigned int l; 90062306a36Sopenharmony_ci struct macio_chip* macio; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci if (!machine_is(powermac)) 90362306a36Sopenharmony_ci return -ENODEV; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci chip->subframe = 0; 90662306a36Sopenharmony_ci chip->revision = 0; 90762306a36Sopenharmony_ci chip->freqs_ok = 0xff; /* all ok */ 90862306a36Sopenharmony_ci chip->model = PMAC_AWACS; 90962306a36Sopenharmony_ci chip->can_byte_swap = 1; 91062306a36Sopenharmony_ci chip->can_duplex = 1; 91162306a36Sopenharmony_ci chip->can_capture = 1; 91262306a36Sopenharmony_ci chip->num_freqs = ARRAY_SIZE(awacs_freqs); 91362306a36Sopenharmony_ci chip->freq_table = awacs_freqs; 91462306a36Sopenharmony_ci chip->pdev = NULL; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci chip->control_mask = MASK_IEPC | MASK_IEE | 0x11; /* default */ 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci /* check machine type */ 91962306a36Sopenharmony_ci if (of_machine_is_compatible("AAPL,3400/2400") 92062306a36Sopenharmony_ci || of_machine_is_compatible("AAPL,3500")) 92162306a36Sopenharmony_ci chip->is_pbook_3400 = 1; 92262306a36Sopenharmony_ci else if (of_machine_is_compatible("PowerBook1,1") 92362306a36Sopenharmony_ci || of_machine_is_compatible("AAPL,PowerBook1998")) 92462306a36Sopenharmony_ci chip->is_pbook_G3 = 1; 92562306a36Sopenharmony_ci chip->node = of_find_node_by_name(NULL, "awacs"); 92662306a36Sopenharmony_ci sound = of_node_get(chip->node); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci /* 92962306a36Sopenharmony_ci * powermac G3 models have a node called "davbus" 93062306a36Sopenharmony_ci * with a child called "sound". 93162306a36Sopenharmony_ci */ 93262306a36Sopenharmony_ci if (!chip->node) 93362306a36Sopenharmony_ci chip->node = of_find_node_by_name(NULL, "davbus"); 93462306a36Sopenharmony_ci /* 93562306a36Sopenharmony_ci * if we didn't find a davbus device, try 'i2s-a' since 93662306a36Sopenharmony_ci * this seems to be what iBooks have 93762306a36Sopenharmony_ci */ 93862306a36Sopenharmony_ci if (! chip->node) { 93962306a36Sopenharmony_ci chip->node = of_find_node_by_name(NULL, "i2s-a"); 94062306a36Sopenharmony_ci if (chip->node && chip->node->parent && 94162306a36Sopenharmony_ci chip->node->parent->parent) { 94262306a36Sopenharmony_ci if (of_device_is_compatible(chip->node->parent->parent, 94362306a36Sopenharmony_ci "K2-Keylargo")) 94462306a36Sopenharmony_ci chip->is_k2 = 1; 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci if (! chip->node) 94862306a36Sopenharmony_ci return -ENODEV; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci if (!sound) { 95162306a36Sopenharmony_ci for_each_node_by_name(sound, "sound") 95262306a36Sopenharmony_ci if (sound->parent == chip->node) 95362306a36Sopenharmony_ci break; 95462306a36Sopenharmony_ci } 95562306a36Sopenharmony_ci if (! sound) { 95662306a36Sopenharmony_ci of_node_put(chip->node); 95762306a36Sopenharmony_ci chip->node = NULL; 95862306a36Sopenharmony_ci return -ENODEV; 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci prop = of_get_property(sound, "sub-frame", NULL); 96162306a36Sopenharmony_ci if (prop && *prop < 16) 96262306a36Sopenharmony_ci chip->subframe = *prop; 96362306a36Sopenharmony_ci prop = of_get_property(sound, "layout-id", NULL); 96462306a36Sopenharmony_ci if (prop) { 96562306a36Sopenharmony_ci /* partly deprecate snd-powermac, for those machines 96662306a36Sopenharmony_ci * that have a layout-id property for now */ 96762306a36Sopenharmony_ci printk(KERN_INFO "snd-powermac no longer handles any " 96862306a36Sopenharmony_ci "machines with a layout-id property " 96962306a36Sopenharmony_ci "in the device-tree, use snd-aoa.\n"); 97062306a36Sopenharmony_ci of_node_put(sound); 97162306a36Sopenharmony_ci of_node_put(chip->node); 97262306a36Sopenharmony_ci chip->node = NULL; 97362306a36Sopenharmony_ci return -ENODEV; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci /* This should be verified on older screamers */ 97662306a36Sopenharmony_ci if (of_device_is_compatible(sound, "screamer")) { 97762306a36Sopenharmony_ci chip->model = PMAC_SCREAMER; 97862306a36Sopenharmony_ci // chip->can_byte_swap = 0; /* FIXME: check this */ 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci if (of_device_is_compatible(sound, "burgundy")) { 98162306a36Sopenharmony_ci chip->model = PMAC_BURGUNDY; 98262306a36Sopenharmony_ci chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci if (of_device_is_compatible(sound, "daca")) { 98562306a36Sopenharmony_ci chip->model = PMAC_DACA; 98662306a36Sopenharmony_ci chip->can_capture = 0; /* no capture */ 98762306a36Sopenharmony_ci chip->can_duplex = 0; 98862306a36Sopenharmony_ci // chip->can_byte_swap = 0; /* FIXME: check this */ 98962306a36Sopenharmony_ci chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci if (of_device_is_compatible(sound, "tumbler")) { 99262306a36Sopenharmony_ci chip->model = PMAC_TUMBLER; 99362306a36Sopenharmony_ci chip->can_capture = of_machine_is_compatible("PowerMac4,2") 99462306a36Sopenharmony_ci || of_machine_is_compatible("PowerBook3,2") 99562306a36Sopenharmony_ci || of_machine_is_compatible("PowerBook3,3") 99662306a36Sopenharmony_ci || of_machine_is_compatible("PowerBook4,1") 99762306a36Sopenharmony_ci || of_machine_is_compatible("PowerBook4,2") 99862306a36Sopenharmony_ci || of_machine_is_compatible("PowerBook4,3"); 99962306a36Sopenharmony_ci chip->can_duplex = 0; 100062306a36Sopenharmony_ci // chip->can_byte_swap = 0; /* FIXME: check this */ 100162306a36Sopenharmony_ci chip->num_freqs = ARRAY_SIZE(tumbler_freqs); 100262306a36Sopenharmony_ci chip->freq_table = tumbler_freqs; 100362306a36Sopenharmony_ci chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ 100462306a36Sopenharmony_ci } 100562306a36Sopenharmony_ci if (of_device_is_compatible(sound, "snapper")) { 100662306a36Sopenharmony_ci chip->model = PMAC_SNAPPER; 100762306a36Sopenharmony_ci // chip->can_byte_swap = 0; /* FIXME: check this */ 100862306a36Sopenharmony_ci chip->num_freqs = ARRAY_SIZE(tumbler_freqs); 100962306a36Sopenharmony_ci chip->freq_table = tumbler_freqs; 101062306a36Sopenharmony_ci chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ 101162306a36Sopenharmony_ci } 101262306a36Sopenharmony_ci prop = of_get_property(sound, "device-id", NULL); 101362306a36Sopenharmony_ci if (prop) 101462306a36Sopenharmony_ci chip->device_id = *prop; 101562306a36Sopenharmony_ci dn = of_find_node_by_name(NULL, "perch"); 101662306a36Sopenharmony_ci chip->has_iic = (dn != NULL); 101762306a36Sopenharmony_ci of_node_put(dn); 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci /* We need the PCI device for DMA allocations, let's use a crude method 102062306a36Sopenharmony_ci * for now ... 102162306a36Sopenharmony_ci */ 102262306a36Sopenharmony_ci macio = macio_find(chip->node, macio_unknown); 102362306a36Sopenharmony_ci if (macio == NULL) 102462306a36Sopenharmony_ci printk(KERN_WARNING "snd-powermac: can't locate macio !\n"); 102562306a36Sopenharmony_ci else { 102662306a36Sopenharmony_ci struct pci_dev *pdev = NULL; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci for_each_pci_dev(pdev) { 102962306a36Sopenharmony_ci struct device_node *np = pci_device_to_OF_node(pdev); 103062306a36Sopenharmony_ci if (np && np == macio->of_node) { 103162306a36Sopenharmony_ci chip->pdev = pdev; 103262306a36Sopenharmony_ci break; 103362306a36Sopenharmony_ci } 103462306a36Sopenharmony_ci } 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci if (chip->pdev == NULL) 103762306a36Sopenharmony_ci printk(KERN_WARNING "snd-powermac: can't locate macio PCI" 103862306a36Sopenharmony_ci " device !\n"); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci detect_byte_swap(chip); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci /* look for a property saying what sample rates 104362306a36Sopenharmony_ci are available */ 104462306a36Sopenharmony_ci prop = of_get_property(sound, "sample-rates", &l); 104562306a36Sopenharmony_ci if (! prop) 104662306a36Sopenharmony_ci prop = of_get_property(sound, "output-frame-rates", &l); 104762306a36Sopenharmony_ci if (prop) { 104862306a36Sopenharmony_ci int i; 104962306a36Sopenharmony_ci chip->freqs_ok = 0; 105062306a36Sopenharmony_ci for (l /= sizeof(int); l > 0; --l) { 105162306a36Sopenharmony_ci unsigned int r = *prop++; 105262306a36Sopenharmony_ci /* Apple 'Fixed' format */ 105362306a36Sopenharmony_ci if (r >= 0x10000) 105462306a36Sopenharmony_ci r >>= 16; 105562306a36Sopenharmony_ci for (i = 0; i < chip->num_freqs; ++i) { 105662306a36Sopenharmony_ci if (r == chip->freq_table[i]) { 105762306a36Sopenharmony_ci chip->freqs_ok |= (1 << i); 105862306a36Sopenharmony_ci break; 105962306a36Sopenharmony_ci } 106062306a36Sopenharmony_ci } 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci } else { 106362306a36Sopenharmony_ci /* assume only 44.1khz */ 106462306a36Sopenharmony_ci chip->freqs_ok = 1; 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci of_node_put(sound); 106862306a36Sopenharmony_ci return 0; 106962306a36Sopenharmony_ci} 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci#ifdef PMAC_SUPPORT_AUTOMUTE 107262306a36Sopenharmony_ci/* 107362306a36Sopenharmony_ci * auto-mute 107462306a36Sopenharmony_ci */ 107562306a36Sopenharmony_cistatic int pmac_auto_mute_get(struct snd_kcontrol *kcontrol, 107662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 107762306a36Sopenharmony_ci{ 107862306a36Sopenharmony_ci struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); 107962306a36Sopenharmony_ci ucontrol->value.integer.value[0] = chip->auto_mute; 108062306a36Sopenharmony_ci return 0; 108162306a36Sopenharmony_ci} 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_cistatic int pmac_auto_mute_put(struct snd_kcontrol *kcontrol, 108462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 108562306a36Sopenharmony_ci{ 108662306a36Sopenharmony_ci struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); 108762306a36Sopenharmony_ci if (ucontrol->value.integer.value[0] != chip->auto_mute) { 108862306a36Sopenharmony_ci chip->auto_mute = !!ucontrol->value.integer.value[0]; 108962306a36Sopenharmony_ci if (chip->update_automute) 109062306a36Sopenharmony_ci chip->update_automute(chip, 1); 109162306a36Sopenharmony_ci return 1; 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci return 0; 109462306a36Sopenharmony_ci} 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_cistatic int pmac_hp_detect_get(struct snd_kcontrol *kcontrol, 109762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 109862306a36Sopenharmony_ci{ 109962306a36Sopenharmony_ci struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); 110062306a36Sopenharmony_ci if (chip->detect_headphone) 110162306a36Sopenharmony_ci ucontrol->value.integer.value[0] = chip->detect_headphone(chip); 110262306a36Sopenharmony_ci else 110362306a36Sopenharmony_ci ucontrol->value.integer.value[0] = 0; 110462306a36Sopenharmony_ci return 0; 110562306a36Sopenharmony_ci} 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_cistatic const struct snd_kcontrol_new auto_mute_controls[] = { 110862306a36Sopenharmony_ci { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 110962306a36Sopenharmony_ci .name = "Auto Mute Switch", 111062306a36Sopenharmony_ci .info = snd_pmac_boolean_mono_info, 111162306a36Sopenharmony_ci .get = pmac_auto_mute_get, 111262306a36Sopenharmony_ci .put = pmac_auto_mute_put, 111362306a36Sopenharmony_ci }, 111462306a36Sopenharmony_ci { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 111562306a36Sopenharmony_ci .name = "Headphone Detection", 111662306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 111762306a36Sopenharmony_ci .info = snd_pmac_boolean_mono_info, 111862306a36Sopenharmony_ci .get = pmac_hp_detect_get, 111962306a36Sopenharmony_ci }, 112062306a36Sopenharmony_ci}; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ciint snd_pmac_add_automute(struct snd_pmac *chip) 112362306a36Sopenharmony_ci{ 112462306a36Sopenharmony_ci int err; 112562306a36Sopenharmony_ci chip->auto_mute = 1; 112662306a36Sopenharmony_ci err = snd_ctl_add(chip->card, snd_ctl_new1(&auto_mute_controls[0], chip)); 112762306a36Sopenharmony_ci if (err < 0) { 112862306a36Sopenharmony_ci printk(KERN_ERR "snd-powermac: Failed to add automute control\n"); 112962306a36Sopenharmony_ci return err; 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci chip->hp_detect_ctl = snd_ctl_new1(&auto_mute_controls[1], chip); 113262306a36Sopenharmony_ci return snd_ctl_add(chip->card, chip->hp_detect_ctl); 113362306a36Sopenharmony_ci} 113462306a36Sopenharmony_ci#endif /* PMAC_SUPPORT_AUTOMUTE */ 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci/* 113762306a36Sopenharmony_ci * create and detect a pmac chip record 113862306a36Sopenharmony_ci */ 113962306a36Sopenharmony_ciint snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return) 114062306a36Sopenharmony_ci{ 114162306a36Sopenharmony_ci struct snd_pmac *chip; 114262306a36Sopenharmony_ci struct device_node *np; 114362306a36Sopenharmony_ci int i, err; 114462306a36Sopenharmony_ci unsigned int irq; 114562306a36Sopenharmony_ci unsigned long ctrl_addr, txdma_addr, rxdma_addr; 114662306a36Sopenharmony_ci static const struct snd_device_ops ops = { 114762306a36Sopenharmony_ci .dev_free = snd_pmac_dev_free, 114862306a36Sopenharmony_ci }; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci *chip_return = NULL; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci chip = kzalloc(sizeof(*chip), GFP_KERNEL); 115362306a36Sopenharmony_ci if (chip == NULL) 115462306a36Sopenharmony_ci return -ENOMEM; 115562306a36Sopenharmony_ci chip->card = card; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci spin_lock_init(&chip->reg_lock); 115862306a36Sopenharmony_ci chip->irq = chip->tx_irq = chip->rx_irq = -1; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci chip->playback.stream = SNDRV_PCM_STREAM_PLAYBACK; 116162306a36Sopenharmony_ci chip->capture.stream = SNDRV_PCM_STREAM_CAPTURE; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci err = snd_pmac_detect(chip); 116462306a36Sopenharmony_ci if (err < 0) 116562306a36Sopenharmony_ci goto __error; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci if (snd_pmac_dbdma_alloc(chip, &chip->playback.cmd, PMAC_MAX_FRAGS + 1) < 0 || 116862306a36Sopenharmony_ci snd_pmac_dbdma_alloc(chip, &chip->capture.cmd, PMAC_MAX_FRAGS + 1) < 0 || 116962306a36Sopenharmony_ci snd_pmac_dbdma_alloc(chip, &chip->extra_dma, 2) < 0 || 117062306a36Sopenharmony_ci snd_pmac_dbdma_alloc(chip, &emergency_dbdma, 2) < 0) { 117162306a36Sopenharmony_ci err = -ENOMEM; 117262306a36Sopenharmony_ci goto __error; 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci np = chip->node; 117662306a36Sopenharmony_ci chip->requested = 0; 117762306a36Sopenharmony_ci if (chip->is_k2) { 117862306a36Sopenharmony_ci static const char * const rnames[] = { 117962306a36Sopenharmony_ci "Sound Control", "Sound DMA" }; 118062306a36Sopenharmony_ci for (i = 0; i < 2; i ++) { 118162306a36Sopenharmony_ci if (of_address_to_resource(np->parent, i, 118262306a36Sopenharmony_ci &chip->rsrc[i])) { 118362306a36Sopenharmony_ci printk(KERN_ERR "snd: can't translate rsrc " 118462306a36Sopenharmony_ci " %d (%s)\n", i, rnames[i]); 118562306a36Sopenharmony_ci err = -ENODEV; 118662306a36Sopenharmony_ci goto __error; 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci if (request_mem_region(chip->rsrc[i].start, 118962306a36Sopenharmony_ci resource_size(&chip->rsrc[i]), 119062306a36Sopenharmony_ci rnames[i]) == NULL) { 119162306a36Sopenharmony_ci printk(KERN_ERR "snd: can't request rsrc " 119262306a36Sopenharmony_ci " %d (%s: %pR)\n", 119362306a36Sopenharmony_ci i, rnames[i], &chip->rsrc[i]); 119462306a36Sopenharmony_ci err = -ENODEV; 119562306a36Sopenharmony_ci goto __error; 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci chip->requested |= (1 << i); 119862306a36Sopenharmony_ci } 119962306a36Sopenharmony_ci ctrl_addr = chip->rsrc[0].start; 120062306a36Sopenharmony_ci txdma_addr = chip->rsrc[1].start; 120162306a36Sopenharmony_ci rxdma_addr = txdma_addr + 0x100; 120262306a36Sopenharmony_ci } else { 120362306a36Sopenharmony_ci static const char * const rnames[] = { 120462306a36Sopenharmony_ci "Sound Control", "Sound Tx DMA", "Sound Rx DMA" }; 120562306a36Sopenharmony_ci for (i = 0; i < 3; i ++) { 120662306a36Sopenharmony_ci if (of_address_to_resource(np, i, 120762306a36Sopenharmony_ci &chip->rsrc[i])) { 120862306a36Sopenharmony_ci printk(KERN_ERR "snd: can't translate rsrc " 120962306a36Sopenharmony_ci " %d (%s)\n", i, rnames[i]); 121062306a36Sopenharmony_ci err = -ENODEV; 121162306a36Sopenharmony_ci goto __error; 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci if (request_mem_region(chip->rsrc[i].start, 121462306a36Sopenharmony_ci resource_size(&chip->rsrc[i]), 121562306a36Sopenharmony_ci rnames[i]) == NULL) { 121662306a36Sopenharmony_ci printk(KERN_ERR "snd: can't request rsrc " 121762306a36Sopenharmony_ci " %d (%s: %pR)\n", 121862306a36Sopenharmony_ci i, rnames[i], &chip->rsrc[i]); 121962306a36Sopenharmony_ci err = -ENODEV; 122062306a36Sopenharmony_ci goto __error; 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci chip->requested |= (1 << i); 122362306a36Sopenharmony_ci } 122462306a36Sopenharmony_ci ctrl_addr = chip->rsrc[0].start; 122562306a36Sopenharmony_ci txdma_addr = chip->rsrc[1].start; 122662306a36Sopenharmony_ci rxdma_addr = chip->rsrc[2].start; 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci chip->awacs = ioremap(ctrl_addr, 0x1000); 123062306a36Sopenharmony_ci chip->playback.dma = ioremap(txdma_addr, 0x100); 123162306a36Sopenharmony_ci chip->capture.dma = ioremap(rxdma_addr, 0x100); 123262306a36Sopenharmony_ci if (chip->model <= PMAC_BURGUNDY) { 123362306a36Sopenharmony_ci irq = irq_of_parse_and_map(np, 0); 123462306a36Sopenharmony_ci if (request_irq(irq, snd_pmac_ctrl_intr, 0, 123562306a36Sopenharmony_ci "PMac", (void*)chip)) { 123662306a36Sopenharmony_ci snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", 123762306a36Sopenharmony_ci irq); 123862306a36Sopenharmony_ci err = -EBUSY; 123962306a36Sopenharmony_ci goto __error; 124062306a36Sopenharmony_ci } 124162306a36Sopenharmony_ci chip->irq = irq; 124262306a36Sopenharmony_ci } 124362306a36Sopenharmony_ci irq = irq_of_parse_and_map(np, 1); 124462306a36Sopenharmony_ci if (request_irq(irq, snd_pmac_tx_intr, 0, "PMac Output", (void*)chip)){ 124562306a36Sopenharmony_ci snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", irq); 124662306a36Sopenharmony_ci err = -EBUSY; 124762306a36Sopenharmony_ci goto __error; 124862306a36Sopenharmony_ci } 124962306a36Sopenharmony_ci chip->tx_irq = irq; 125062306a36Sopenharmony_ci irq = irq_of_parse_and_map(np, 2); 125162306a36Sopenharmony_ci if (request_irq(irq, snd_pmac_rx_intr, 0, "PMac Input", (void*)chip)) { 125262306a36Sopenharmony_ci snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", irq); 125362306a36Sopenharmony_ci err = -EBUSY; 125462306a36Sopenharmony_ci goto __error; 125562306a36Sopenharmony_ci } 125662306a36Sopenharmony_ci chip->rx_irq = irq; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci snd_pmac_sound_feature(chip, 1); 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci /* reset & enable interrupts */ 126162306a36Sopenharmony_ci if (chip->model <= PMAC_BURGUNDY) 126262306a36Sopenharmony_ci out_le32(&chip->awacs->control, chip->control_mask); 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci /* Powerbooks have odd ways of enabling inputs such as 126562306a36Sopenharmony_ci an expansion-bay CD or sound from an internal modem 126662306a36Sopenharmony_ci or a PC-card modem. */ 126762306a36Sopenharmony_ci if (chip->is_pbook_3400) { 126862306a36Sopenharmony_ci /* Enable CD and PC-card sound inputs. */ 126962306a36Sopenharmony_ci /* This is done by reading from address 127062306a36Sopenharmony_ci * f301a000, + 0x10 to enable the expansion-bay 127162306a36Sopenharmony_ci * CD sound input, + 0x80 to enable the PC-card 127262306a36Sopenharmony_ci * sound input. The 0x100 enables the SCSI bus 127362306a36Sopenharmony_ci * terminator power. 127462306a36Sopenharmony_ci */ 127562306a36Sopenharmony_ci chip->latch_base = ioremap (0xf301a000, 0x1000); 127662306a36Sopenharmony_ci in_8(chip->latch_base + 0x190); 127762306a36Sopenharmony_ci } else if (chip->is_pbook_G3) { 127862306a36Sopenharmony_ci struct device_node* mio; 127962306a36Sopenharmony_ci for (mio = chip->node->parent; mio; mio = mio->parent) { 128062306a36Sopenharmony_ci if (of_node_name_eq(mio, "mac-io")) { 128162306a36Sopenharmony_ci struct resource r; 128262306a36Sopenharmony_ci if (of_address_to_resource(mio, 0, &r) == 0) 128362306a36Sopenharmony_ci chip->macio_base = 128462306a36Sopenharmony_ci ioremap(r.start, 0x40); 128562306a36Sopenharmony_ci break; 128662306a36Sopenharmony_ci } 128762306a36Sopenharmony_ci } 128862306a36Sopenharmony_ci /* Enable CD sound input. */ 128962306a36Sopenharmony_ci /* The relevant bits for writing to this byte are 0x8f. 129062306a36Sopenharmony_ci * I haven't found out what the 0x80 bit does. 129162306a36Sopenharmony_ci * For the 0xf bits, writing 3 or 7 enables the CD 129262306a36Sopenharmony_ci * input, any other value disables it. Values 129362306a36Sopenharmony_ci * 1, 3, 5, 7 enable the microphone. Values 0, 2, 129462306a36Sopenharmony_ci * 4, 6, 8 - f enable the input from the modem. 129562306a36Sopenharmony_ci */ 129662306a36Sopenharmony_ci if (chip->macio_base) 129762306a36Sopenharmony_ci out_8(chip->macio_base + 0x37, 3); 129862306a36Sopenharmony_ci } 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci /* Reset dbdma channels */ 130162306a36Sopenharmony_ci snd_pmac_dbdma_reset(chip); 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); 130462306a36Sopenharmony_ci if (err < 0) 130562306a36Sopenharmony_ci goto __error; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci *chip_return = chip; 130862306a36Sopenharmony_ci return 0; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci __error: 131162306a36Sopenharmony_ci snd_pmac_free(chip); 131262306a36Sopenharmony_ci return err; 131362306a36Sopenharmony_ci} 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci/* 131762306a36Sopenharmony_ci * sleep notify for powerbook 131862306a36Sopenharmony_ci */ 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci#ifdef CONFIG_PM 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci/* 132362306a36Sopenharmony_ci * Save state when going to sleep, restore it afterwards. 132462306a36Sopenharmony_ci */ 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_civoid snd_pmac_suspend(struct snd_pmac *chip) 132762306a36Sopenharmony_ci{ 132862306a36Sopenharmony_ci unsigned long flags; 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot); 133162306a36Sopenharmony_ci if (chip->suspend) 133262306a36Sopenharmony_ci chip->suspend(chip); 133362306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 133462306a36Sopenharmony_ci snd_pmac_beep_stop(chip); 133562306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 133662306a36Sopenharmony_ci if (chip->irq >= 0) 133762306a36Sopenharmony_ci disable_irq(chip->irq); 133862306a36Sopenharmony_ci if (chip->tx_irq >= 0) 133962306a36Sopenharmony_ci disable_irq(chip->tx_irq); 134062306a36Sopenharmony_ci if (chip->rx_irq >= 0) 134162306a36Sopenharmony_ci disable_irq(chip->rx_irq); 134262306a36Sopenharmony_ci snd_pmac_sound_feature(chip, 0); 134362306a36Sopenharmony_ci} 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_civoid snd_pmac_resume(struct snd_pmac *chip) 134662306a36Sopenharmony_ci{ 134762306a36Sopenharmony_ci snd_pmac_sound_feature(chip, 1); 134862306a36Sopenharmony_ci if (chip->resume) 134962306a36Sopenharmony_ci chip->resume(chip); 135062306a36Sopenharmony_ci /* enable CD sound input */ 135162306a36Sopenharmony_ci if (chip->macio_base && chip->is_pbook_G3) 135262306a36Sopenharmony_ci out_8(chip->macio_base + 0x37, 3); 135362306a36Sopenharmony_ci else if (chip->is_pbook_3400) 135462306a36Sopenharmony_ci in_8(chip->latch_base + 0x190); 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci snd_pmac_pcm_set_format(chip); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci if (chip->irq >= 0) 135962306a36Sopenharmony_ci enable_irq(chip->irq); 136062306a36Sopenharmony_ci if (chip->tx_irq >= 0) 136162306a36Sopenharmony_ci enable_irq(chip->tx_irq); 136262306a36Sopenharmony_ci if (chip->rx_irq >= 0) 136362306a36Sopenharmony_ci enable_irq(chip->rx_irq); 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0); 136662306a36Sopenharmony_ci} 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci#endif /* CONFIG_PM */ 136962306a36Sopenharmony_ci 1370