18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci* 48c2ecf20Sopenharmony_ci* Copyright Adrian McMenamin 2005, 2006, 2007 58c2ecf20Sopenharmony_ci* <adrian@mcmen.demon.co.uk> 68c2ecf20Sopenharmony_ci* Requires firmware (BSD licenced) available from: 78c2ecf20Sopenharmony_ci* http://linuxdc.cvs.sourceforge.net/linuxdc/linux-sh-dc/sound/oss/aica/firmware/ 88c2ecf20Sopenharmony_ci* or the maintainer 98c2ecf20Sopenharmony_ci*/ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/time.h> 158c2ecf20Sopenharmony_ci#include <linux/wait.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/firmware.h> 198c2ecf20Sopenharmony_ci#include <linux/timer.h> 208c2ecf20Sopenharmony_ci#include <linux/delay.h> 218c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 228c2ecf20Sopenharmony_ci#include <linux/io.h> 238c2ecf20Sopenharmony_ci#include <sound/core.h> 248c2ecf20Sopenharmony_ci#include <sound/control.h> 258c2ecf20Sopenharmony_ci#include <sound/pcm.h> 268c2ecf20Sopenharmony_ci#include <sound/initval.h> 278c2ecf20Sopenharmony_ci#include <sound/info.h> 288c2ecf20Sopenharmony_ci#include <asm/dma.h> 298c2ecf20Sopenharmony_ci#include <mach/sysasic.h> 308c2ecf20Sopenharmony_ci#include "aica.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>"); 338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Dreamcast AICA sound (pcm) driver"); 348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 358c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Yamaha/SEGA, AICA}}"); 368c2ecf20Sopenharmony_ciMODULE_FIRMWARE("aica_firmware.bin"); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* module parameters */ 398c2ecf20Sopenharmony_ci#define CARD_NAME "AICA" 408c2ecf20Sopenharmony_cistatic int index = -1; 418c2ecf20Sopenharmony_cistatic char *id; 428c2ecf20Sopenharmony_cistatic bool enable = 1; 438c2ecf20Sopenharmony_cimodule_param(index, int, 0444); 448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); 458c2ecf20Sopenharmony_cimodule_param(id, charp, 0444); 468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); 478c2ecf20Sopenharmony_cimodule_param(enable, bool, 0644); 488c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* Simple platform device */ 518c2ecf20Sopenharmony_cistatic struct platform_device *pd; 528c2ecf20Sopenharmony_cistatic struct resource aica_memory_space[2] = { 538c2ecf20Sopenharmony_ci { 548c2ecf20Sopenharmony_ci .name = "AICA ARM CONTROL", 558c2ecf20Sopenharmony_ci .start = ARM_RESET_REGISTER, 568c2ecf20Sopenharmony_ci .flags = IORESOURCE_MEM, 578c2ecf20Sopenharmony_ci .end = ARM_RESET_REGISTER + 3, 588c2ecf20Sopenharmony_ci }, 598c2ecf20Sopenharmony_ci { 608c2ecf20Sopenharmony_ci .name = "AICA Sound RAM", 618c2ecf20Sopenharmony_ci .start = SPU_MEMORY_BASE, 628c2ecf20Sopenharmony_ci .flags = IORESOURCE_MEM, 638c2ecf20Sopenharmony_ci .end = SPU_MEMORY_BASE + 0x200000 - 1, 648c2ecf20Sopenharmony_ci }, 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* SPU specific functions */ 688c2ecf20Sopenharmony_ci/* spu_write_wait - wait for G2-SH FIFO to clear */ 698c2ecf20Sopenharmony_cistatic void spu_write_wait(void) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci int time_count; 728c2ecf20Sopenharmony_ci time_count = 0; 738c2ecf20Sopenharmony_ci while (1) { 748c2ecf20Sopenharmony_ci if (!(readl(G2_FIFO) & 0x11)) 758c2ecf20Sopenharmony_ci break; 768c2ecf20Sopenharmony_ci /* To ensure hardware failure doesn't wedge kernel */ 778c2ecf20Sopenharmony_ci time_count++; 788c2ecf20Sopenharmony_ci if (time_count > 0x10000) { 798c2ecf20Sopenharmony_ci snd_printk 808c2ecf20Sopenharmony_ci ("WARNING: G2 FIFO appears to be blocked.\n"); 818c2ecf20Sopenharmony_ci break; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* spu_memset - write to memory in SPU address space */ 878c2ecf20Sopenharmony_cistatic void spu_memset(u32 toi, u32 what, int length) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci int i; 908c2ecf20Sopenharmony_ci unsigned long flags; 918c2ecf20Sopenharmony_ci if (snd_BUG_ON(length % 4)) 928c2ecf20Sopenharmony_ci return; 938c2ecf20Sopenharmony_ci for (i = 0; i < length; i++) { 948c2ecf20Sopenharmony_ci if (!(i % 8)) 958c2ecf20Sopenharmony_ci spu_write_wait(); 968c2ecf20Sopenharmony_ci local_irq_save(flags); 978c2ecf20Sopenharmony_ci writel(what, toi + SPU_MEMORY_BASE); 988c2ecf20Sopenharmony_ci local_irq_restore(flags); 998c2ecf20Sopenharmony_ci toi++; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* spu_memload - write to SPU address space */ 1048c2ecf20Sopenharmony_cistatic void spu_memload(u32 toi, const void *from, int length) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci unsigned long flags; 1078c2ecf20Sopenharmony_ci const u32 *froml = from; 1088c2ecf20Sopenharmony_ci u32 __iomem *to = (u32 __iomem *) (SPU_MEMORY_BASE + toi); 1098c2ecf20Sopenharmony_ci int i; 1108c2ecf20Sopenharmony_ci u32 val; 1118c2ecf20Sopenharmony_ci length = DIV_ROUND_UP(length, 4); 1128c2ecf20Sopenharmony_ci spu_write_wait(); 1138c2ecf20Sopenharmony_ci for (i = 0; i < length; i++) { 1148c2ecf20Sopenharmony_ci if (!(i % 8)) 1158c2ecf20Sopenharmony_ci spu_write_wait(); 1168c2ecf20Sopenharmony_ci val = *froml; 1178c2ecf20Sopenharmony_ci local_irq_save(flags); 1188c2ecf20Sopenharmony_ci writel(val, to); 1198c2ecf20Sopenharmony_ci local_irq_restore(flags); 1208c2ecf20Sopenharmony_ci froml++; 1218c2ecf20Sopenharmony_ci to++; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* spu_disable - set spu registers to stop sound output */ 1268c2ecf20Sopenharmony_cistatic void spu_disable(void) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci int i; 1298c2ecf20Sopenharmony_ci unsigned long flags; 1308c2ecf20Sopenharmony_ci u32 regval; 1318c2ecf20Sopenharmony_ci spu_write_wait(); 1328c2ecf20Sopenharmony_ci regval = readl(ARM_RESET_REGISTER); 1338c2ecf20Sopenharmony_ci regval |= 1; 1348c2ecf20Sopenharmony_ci spu_write_wait(); 1358c2ecf20Sopenharmony_ci local_irq_save(flags); 1368c2ecf20Sopenharmony_ci writel(regval, ARM_RESET_REGISTER); 1378c2ecf20Sopenharmony_ci local_irq_restore(flags); 1388c2ecf20Sopenharmony_ci for (i = 0; i < 64; i++) { 1398c2ecf20Sopenharmony_ci spu_write_wait(); 1408c2ecf20Sopenharmony_ci regval = readl(SPU_REGISTER_BASE + (i * 0x80)); 1418c2ecf20Sopenharmony_ci regval = (regval & ~0x4000) | 0x8000; 1428c2ecf20Sopenharmony_ci spu_write_wait(); 1438c2ecf20Sopenharmony_ci local_irq_save(flags); 1448c2ecf20Sopenharmony_ci writel(regval, SPU_REGISTER_BASE + (i * 0x80)); 1458c2ecf20Sopenharmony_ci local_irq_restore(flags); 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/* spu_enable - set spu registers to enable sound output */ 1508c2ecf20Sopenharmony_cistatic void spu_enable(void) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci unsigned long flags; 1538c2ecf20Sopenharmony_ci u32 regval = readl(ARM_RESET_REGISTER); 1548c2ecf20Sopenharmony_ci regval &= ~1; 1558c2ecf20Sopenharmony_ci spu_write_wait(); 1568c2ecf20Sopenharmony_ci local_irq_save(flags); 1578c2ecf20Sopenharmony_ci writel(regval, ARM_RESET_REGISTER); 1588c2ecf20Sopenharmony_ci local_irq_restore(flags); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci/* 1628c2ecf20Sopenharmony_ci * Halt the sound processor, clear the memory, 1638c2ecf20Sopenharmony_ci * load some default ARM7 code, and then restart ARM7 1648c2ecf20Sopenharmony_ci*/ 1658c2ecf20Sopenharmony_cistatic void spu_reset(void) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci unsigned long flags; 1688c2ecf20Sopenharmony_ci spu_disable(); 1698c2ecf20Sopenharmony_ci spu_memset(0, 0, 0x200000 / 4); 1708c2ecf20Sopenharmony_ci /* Put ARM7 in endless loop */ 1718c2ecf20Sopenharmony_ci local_irq_save(flags); 1728c2ecf20Sopenharmony_ci __raw_writel(0xea000002, SPU_MEMORY_BASE); 1738c2ecf20Sopenharmony_ci local_irq_restore(flags); 1748c2ecf20Sopenharmony_ci spu_enable(); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci/* aica_chn_start - write to spu to start playback */ 1788c2ecf20Sopenharmony_cistatic void aica_chn_start(void) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci unsigned long flags; 1818c2ecf20Sopenharmony_ci spu_write_wait(); 1828c2ecf20Sopenharmony_ci local_irq_save(flags); 1838c2ecf20Sopenharmony_ci writel(AICA_CMD_KICK | AICA_CMD_START, (u32 *) AICA_CONTROL_POINT); 1848c2ecf20Sopenharmony_ci local_irq_restore(flags); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci/* aica_chn_halt - write to spu to halt playback */ 1888c2ecf20Sopenharmony_cistatic void aica_chn_halt(void) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci unsigned long flags; 1918c2ecf20Sopenharmony_ci spu_write_wait(); 1928c2ecf20Sopenharmony_ci local_irq_save(flags); 1938c2ecf20Sopenharmony_ci writel(AICA_CMD_KICK | AICA_CMD_STOP, (u32 *) AICA_CONTROL_POINT); 1948c2ecf20Sopenharmony_ci local_irq_restore(flags); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/* ALSA code below */ 1988c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_pcm_aica_playback_hw = { 1998c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_NONINTERLEAVED), 2008c2ecf20Sopenharmony_ci .formats = 2018c2ecf20Sopenharmony_ci (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | 2028c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_IMA_ADPCM), 2038c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 2048c2ecf20Sopenharmony_ci .rate_min = 8000, 2058c2ecf20Sopenharmony_ci .rate_max = 48000, 2068c2ecf20Sopenharmony_ci .channels_min = 1, 2078c2ecf20Sopenharmony_ci .channels_max = 2, 2088c2ecf20Sopenharmony_ci .buffer_bytes_max = AICA_BUFFER_SIZE, 2098c2ecf20Sopenharmony_ci .period_bytes_min = AICA_PERIOD_SIZE, 2108c2ecf20Sopenharmony_ci .period_bytes_max = AICA_PERIOD_SIZE, 2118c2ecf20Sopenharmony_ci .periods_min = AICA_PERIOD_NUMBER, 2128c2ecf20Sopenharmony_ci .periods_max = AICA_PERIOD_NUMBER, 2138c2ecf20Sopenharmony_ci}; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int aica_dma_transfer(int channels, int buffer_size, 2168c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci int q, err, period_offset; 2198c2ecf20Sopenharmony_ci struct snd_card_aica *dreamcastcard; 2208c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 2218c2ecf20Sopenharmony_ci unsigned long flags; 2228c2ecf20Sopenharmony_ci err = 0; 2238c2ecf20Sopenharmony_ci dreamcastcard = substream->pcm->private_data; 2248c2ecf20Sopenharmony_ci period_offset = dreamcastcard->clicks; 2258c2ecf20Sopenharmony_ci period_offset %= (AICA_PERIOD_NUMBER / channels); 2268c2ecf20Sopenharmony_ci runtime = substream->runtime; 2278c2ecf20Sopenharmony_ci for (q = 0; q < channels; q++) { 2288c2ecf20Sopenharmony_ci local_irq_save(flags); 2298c2ecf20Sopenharmony_ci err = dma_xfer(AICA_DMA_CHANNEL, 2308c2ecf20Sopenharmony_ci (unsigned long) (runtime->dma_area + 2318c2ecf20Sopenharmony_ci (AICA_BUFFER_SIZE * q) / 2328c2ecf20Sopenharmony_ci channels + 2338c2ecf20Sopenharmony_ci AICA_PERIOD_SIZE * 2348c2ecf20Sopenharmony_ci period_offset), 2358c2ecf20Sopenharmony_ci AICA_CHANNEL0_OFFSET + q * CHANNEL_OFFSET + 2368c2ecf20Sopenharmony_ci AICA_PERIOD_SIZE * period_offset, 2378c2ecf20Sopenharmony_ci buffer_size / channels, AICA_DMA_MODE); 2388c2ecf20Sopenharmony_ci if (unlikely(err < 0)) { 2398c2ecf20Sopenharmony_ci local_irq_restore(flags); 2408c2ecf20Sopenharmony_ci break; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci dma_wait_for_completion(AICA_DMA_CHANNEL); 2438c2ecf20Sopenharmony_ci local_irq_restore(flags); 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci return err; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic void startup_aica(struct snd_card_aica *dreamcastcard) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci spu_memload(AICA_CHANNEL0_CONTROL_OFFSET, 2518c2ecf20Sopenharmony_ci dreamcastcard->channel, sizeof(struct aica_channel)); 2528c2ecf20Sopenharmony_ci aica_chn_start(); 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic void run_spu_dma(struct work_struct *work) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci int buffer_size; 2588c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 2598c2ecf20Sopenharmony_ci struct snd_card_aica *dreamcastcard; 2608c2ecf20Sopenharmony_ci dreamcastcard = 2618c2ecf20Sopenharmony_ci container_of(work, struct snd_card_aica, spu_dma_work); 2628c2ecf20Sopenharmony_ci runtime = dreamcastcard->substream->runtime; 2638c2ecf20Sopenharmony_ci if (unlikely(dreamcastcard->dma_check == 0)) { 2648c2ecf20Sopenharmony_ci buffer_size = 2658c2ecf20Sopenharmony_ci frames_to_bytes(runtime, runtime->buffer_size); 2668c2ecf20Sopenharmony_ci if (runtime->channels > 1) 2678c2ecf20Sopenharmony_ci dreamcastcard->channel->flags |= 0x01; 2688c2ecf20Sopenharmony_ci aica_dma_transfer(runtime->channels, buffer_size, 2698c2ecf20Sopenharmony_ci dreamcastcard->substream); 2708c2ecf20Sopenharmony_ci startup_aica(dreamcastcard); 2718c2ecf20Sopenharmony_ci dreamcastcard->clicks = 2728c2ecf20Sopenharmony_ci buffer_size / (AICA_PERIOD_SIZE * runtime->channels); 2738c2ecf20Sopenharmony_ci return; 2748c2ecf20Sopenharmony_ci } else { 2758c2ecf20Sopenharmony_ci aica_dma_transfer(runtime->channels, 2768c2ecf20Sopenharmony_ci AICA_PERIOD_SIZE * runtime->channels, 2778c2ecf20Sopenharmony_ci dreamcastcard->substream); 2788c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(dreamcastcard->substream); 2798c2ecf20Sopenharmony_ci dreamcastcard->clicks++; 2808c2ecf20Sopenharmony_ci if (unlikely(dreamcastcard->clicks >= AICA_PERIOD_NUMBER)) 2818c2ecf20Sopenharmony_ci dreamcastcard->clicks %= AICA_PERIOD_NUMBER; 2828c2ecf20Sopenharmony_ci mod_timer(&dreamcastcard->timer, jiffies + 1); 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic void aica_period_elapsed(struct timer_list *t) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct snd_card_aica *dreamcastcard = from_timer(dreamcastcard, 2898c2ecf20Sopenharmony_ci t, timer); 2908c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream = dreamcastcard->substream; 2918c2ecf20Sopenharmony_ci /*timer function - so cannot sleep */ 2928c2ecf20Sopenharmony_ci int play_period; 2938c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 2948c2ecf20Sopenharmony_ci runtime = substream->runtime; 2958c2ecf20Sopenharmony_ci dreamcastcard = substream->pcm->private_data; 2968c2ecf20Sopenharmony_ci /* Have we played out an additional period? */ 2978c2ecf20Sopenharmony_ci play_period = 2988c2ecf20Sopenharmony_ci frames_to_bytes(runtime, 2998c2ecf20Sopenharmony_ci readl 3008c2ecf20Sopenharmony_ci (AICA_CONTROL_CHANNEL_SAMPLE_NUMBER)) / 3018c2ecf20Sopenharmony_ci AICA_PERIOD_SIZE; 3028c2ecf20Sopenharmony_ci if (play_period == dreamcastcard->current_period) { 3038c2ecf20Sopenharmony_ci /* reschedule the timer */ 3048c2ecf20Sopenharmony_ci mod_timer(&(dreamcastcard->timer), jiffies + 1); 3058c2ecf20Sopenharmony_ci return; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci if (runtime->channels > 1) 3088c2ecf20Sopenharmony_ci dreamcastcard->current_period = play_period; 3098c2ecf20Sopenharmony_ci if (unlikely(dreamcastcard->dma_check == 0)) 3108c2ecf20Sopenharmony_ci dreamcastcard->dma_check = 1; 3118c2ecf20Sopenharmony_ci schedule_work(&(dreamcastcard->spu_dma_work)); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic void spu_begin_dma(struct snd_pcm_substream *substream) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct snd_card_aica *dreamcastcard; 3178c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 3188c2ecf20Sopenharmony_ci runtime = substream->runtime; 3198c2ecf20Sopenharmony_ci dreamcastcard = substream->pcm->private_data; 3208c2ecf20Sopenharmony_ci /*get the queue to do the work */ 3218c2ecf20Sopenharmony_ci schedule_work(&(dreamcastcard->spu_dma_work)); 3228c2ecf20Sopenharmony_ci mod_timer(&dreamcastcard->timer, jiffies + 4); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic int snd_aicapcm_pcm_open(struct snd_pcm_substream 3268c2ecf20Sopenharmony_ci *substream) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 3298c2ecf20Sopenharmony_ci struct aica_channel *channel; 3308c2ecf20Sopenharmony_ci struct snd_card_aica *dreamcastcard; 3318c2ecf20Sopenharmony_ci if (!enable) 3328c2ecf20Sopenharmony_ci return -ENOENT; 3338c2ecf20Sopenharmony_ci dreamcastcard = substream->pcm->private_data; 3348c2ecf20Sopenharmony_ci channel = kmalloc(sizeof(struct aica_channel), GFP_KERNEL); 3358c2ecf20Sopenharmony_ci if (!channel) 3368c2ecf20Sopenharmony_ci return -ENOMEM; 3378c2ecf20Sopenharmony_ci /* set defaults for channel */ 3388c2ecf20Sopenharmony_ci channel->sfmt = SM_8BIT; 3398c2ecf20Sopenharmony_ci channel->cmd = AICA_CMD_START; 3408c2ecf20Sopenharmony_ci channel->vol = dreamcastcard->master_volume; 3418c2ecf20Sopenharmony_ci channel->pan = 0x80; 3428c2ecf20Sopenharmony_ci channel->pos = 0; 3438c2ecf20Sopenharmony_ci channel->flags = 0; /* default to mono */ 3448c2ecf20Sopenharmony_ci dreamcastcard->channel = channel; 3458c2ecf20Sopenharmony_ci runtime = substream->runtime; 3468c2ecf20Sopenharmony_ci runtime->hw = snd_pcm_aica_playback_hw; 3478c2ecf20Sopenharmony_ci spu_enable(); 3488c2ecf20Sopenharmony_ci dreamcastcard->clicks = 0; 3498c2ecf20Sopenharmony_ci dreamcastcard->current_period = 0; 3508c2ecf20Sopenharmony_ci dreamcastcard->dma_check = 0; 3518c2ecf20Sopenharmony_ci return 0; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic int snd_aicapcm_pcm_close(struct snd_pcm_substream 3558c2ecf20Sopenharmony_ci *substream) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct snd_card_aica *dreamcastcard = substream->pcm->private_data; 3588c2ecf20Sopenharmony_ci flush_work(&(dreamcastcard->spu_dma_work)); 3598c2ecf20Sopenharmony_ci del_timer(&dreamcastcard->timer); 3608c2ecf20Sopenharmony_ci dreamcastcard->substream = NULL; 3618c2ecf20Sopenharmony_ci kfree(dreamcastcard->channel); 3628c2ecf20Sopenharmony_ci spu_disable(); 3638c2ecf20Sopenharmony_ci return 0; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic int snd_aicapcm_pcm_prepare(struct snd_pcm_substream 3678c2ecf20Sopenharmony_ci *substream) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci struct snd_card_aica *dreamcastcard = substream->pcm->private_data; 3708c2ecf20Sopenharmony_ci if ((substream->runtime)->format == SNDRV_PCM_FORMAT_S16_LE) 3718c2ecf20Sopenharmony_ci dreamcastcard->channel->sfmt = SM_16BIT; 3728c2ecf20Sopenharmony_ci dreamcastcard->channel->freq = substream->runtime->rate; 3738c2ecf20Sopenharmony_ci dreamcastcard->substream = substream; 3748c2ecf20Sopenharmony_ci return 0; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic int snd_aicapcm_pcm_trigger(struct snd_pcm_substream 3788c2ecf20Sopenharmony_ci *substream, int cmd) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci switch (cmd) { 3818c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 3828c2ecf20Sopenharmony_ci spu_begin_dma(substream); 3838c2ecf20Sopenharmony_ci break; 3848c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 3858c2ecf20Sopenharmony_ci aica_chn_halt(); 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci default: 3888c2ecf20Sopenharmony_ci return -EINVAL; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci return 0; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic unsigned long snd_aicapcm_pcm_pointer(struct snd_pcm_substream 3948c2ecf20Sopenharmony_ci *substream) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci return readl(AICA_CONTROL_CHANNEL_SAMPLE_NUMBER); 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_aicapcm_playback_ops = { 4008c2ecf20Sopenharmony_ci .open = snd_aicapcm_pcm_open, 4018c2ecf20Sopenharmony_ci .close = snd_aicapcm_pcm_close, 4028c2ecf20Sopenharmony_ci .prepare = snd_aicapcm_pcm_prepare, 4038c2ecf20Sopenharmony_ci .trigger = snd_aicapcm_pcm_trigger, 4048c2ecf20Sopenharmony_ci .pointer = snd_aicapcm_pcm_pointer, 4058c2ecf20Sopenharmony_ci}; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci/* TO DO: set up to handle more than one pcm instance */ 4088c2ecf20Sopenharmony_cistatic int __init snd_aicapcmchip(struct snd_card_aica 4098c2ecf20Sopenharmony_ci *dreamcastcard, int pcm_index) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 4128c2ecf20Sopenharmony_ci int err; 4138c2ecf20Sopenharmony_ci /* AICA has no capture ability */ 4148c2ecf20Sopenharmony_ci err = 4158c2ecf20Sopenharmony_ci snd_pcm_new(dreamcastcard->card, "AICA PCM", pcm_index, 1, 0, 4168c2ecf20Sopenharmony_ci &pcm); 4178c2ecf20Sopenharmony_ci if (unlikely(err < 0)) 4188c2ecf20Sopenharmony_ci return err; 4198c2ecf20Sopenharmony_ci pcm->private_data = dreamcastcard; 4208c2ecf20Sopenharmony_ci strcpy(pcm->name, "AICA PCM"); 4218c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, 4228c2ecf20Sopenharmony_ci &snd_aicapcm_playback_ops); 4238c2ecf20Sopenharmony_ci /* Allocate the DMA buffers */ 4248c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, 4258c2ecf20Sopenharmony_ci SNDRV_DMA_TYPE_CONTINUOUS, 4268c2ecf20Sopenharmony_ci NULL, 4278c2ecf20Sopenharmony_ci AICA_BUFFER_SIZE, 4288c2ecf20Sopenharmony_ci AICA_BUFFER_SIZE); 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci/* Mixer controls */ 4338c2ecf20Sopenharmony_ci#define aica_pcmswitch_info snd_ctl_boolean_mono_info 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic int aica_pcmswitch_get(struct snd_kcontrol *kcontrol, 4368c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = 1; /* TO DO: Fix me */ 4398c2ecf20Sopenharmony_ci return 0; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int aica_pcmswitch_put(struct snd_kcontrol *kcontrol, 4438c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci if (ucontrol->value.integer.value[0] == 1) 4468c2ecf20Sopenharmony_ci return 0; /* TO DO: Fix me */ 4478c2ecf20Sopenharmony_ci else 4488c2ecf20Sopenharmony_ci aica_chn_halt(); 4498c2ecf20Sopenharmony_ci return 0; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic int aica_pcmvolume_info(struct snd_kcontrol *kcontrol, 4538c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 4568c2ecf20Sopenharmony_ci uinfo->count = 1; 4578c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 4588c2ecf20Sopenharmony_ci uinfo->value.integer.max = 0xFF; 4598c2ecf20Sopenharmony_ci return 0; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic int aica_pcmvolume_get(struct snd_kcontrol *kcontrol, 4638c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci struct snd_card_aica *dreamcastcard; 4668c2ecf20Sopenharmony_ci dreamcastcard = kcontrol->private_data; 4678c2ecf20Sopenharmony_ci if (unlikely(!dreamcastcard->channel)) 4688c2ecf20Sopenharmony_ci return -ETXTBSY; /* we've not yet been set up */ 4698c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = dreamcastcard->channel->vol; 4708c2ecf20Sopenharmony_ci return 0; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic int aica_pcmvolume_put(struct snd_kcontrol *kcontrol, 4748c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct snd_card_aica *dreamcastcard; 4778c2ecf20Sopenharmony_ci unsigned int vol; 4788c2ecf20Sopenharmony_ci dreamcastcard = kcontrol->private_data; 4798c2ecf20Sopenharmony_ci if (unlikely(!dreamcastcard->channel)) 4808c2ecf20Sopenharmony_ci return -ETXTBSY; 4818c2ecf20Sopenharmony_ci vol = ucontrol->value.integer.value[0]; 4828c2ecf20Sopenharmony_ci if (vol > 0xff) 4838c2ecf20Sopenharmony_ci return -EINVAL; 4848c2ecf20Sopenharmony_ci if (unlikely(dreamcastcard->channel->vol == vol)) 4858c2ecf20Sopenharmony_ci return 0; 4868c2ecf20Sopenharmony_ci dreamcastcard->channel->vol = ucontrol->value.integer.value[0]; 4878c2ecf20Sopenharmony_ci dreamcastcard->master_volume = ucontrol->value.integer.value[0]; 4888c2ecf20Sopenharmony_ci spu_memload(AICA_CHANNEL0_CONTROL_OFFSET, 4898c2ecf20Sopenharmony_ci dreamcastcard->channel, sizeof(struct aica_channel)); 4908c2ecf20Sopenharmony_ci return 1; 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_aica_pcmswitch_control = { 4948c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4958c2ecf20Sopenharmony_ci .name = "PCM Playback Switch", 4968c2ecf20Sopenharmony_ci .index = 0, 4978c2ecf20Sopenharmony_ci .info = aica_pcmswitch_info, 4988c2ecf20Sopenharmony_ci .get = aica_pcmswitch_get, 4998c2ecf20Sopenharmony_ci .put = aica_pcmswitch_put 5008c2ecf20Sopenharmony_ci}; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_aica_pcmvolume_control = { 5038c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5048c2ecf20Sopenharmony_ci .name = "PCM Playback Volume", 5058c2ecf20Sopenharmony_ci .index = 0, 5068c2ecf20Sopenharmony_ci .info = aica_pcmvolume_info, 5078c2ecf20Sopenharmony_ci .get = aica_pcmvolume_get, 5088c2ecf20Sopenharmony_ci .put = aica_pcmvolume_put 5098c2ecf20Sopenharmony_ci}; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic int load_aica_firmware(void) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci int err; 5148c2ecf20Sopenharmony_ci const struct firmware *fw_entry; 5158c2ecf20Sopenharmony_ci spu_reset(); 5168c2ecf20Sopenharmony_ci err = request_firmware(&fw_entry, "aica_firmware.bin", &pd->dev); 5178c2ecf20Sopenharmony_ci if (unlikely(err)) 5188c2ecf20Sopenharmony_ci return err; 5198c2ecf20Sopenharmony_ci /* write firmware into memory */ 5208c2ecf20Sopenharmony_ci spu_disable(); 5218c2ecf20Sopenharmony_ci spu_memload(0, fw_entry->data, fw_entry->size); 5228c2ecf20Sopenharmony_ci spu_enable(); 5238c2ecf20Sopenharmony_ci release_firmware(fw_entry); 5248c2ecf20Sopenharmony_ci return err; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic int add_aicamixer_controls(struct snd_card_aica *dreamcastcard) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci int err; 5308c2ecf20Sopenharmony_ci err = snd_ctl_add 5318c2ecf20Sopenharmony_ci (dreamcastcard->card, 5328c2ecf20Sopenharmony_ci snd_ctl_new1(&snd_aica_pcmvolume_control, dreamcastcard)); 5338c2ecf20Sopenharmony_ci if (unlikely(err < 0)) 5348c2ecf20Sopenharmony_ci return err; 5358c2ecf20Sopenharmony_ci err = snd_ctl_add 5368c2ecf20Sopenharmony_ci (dreamcastcard->card, 5378c2ecf20Sopenharmony_ci snd_ctl_new1(&snd_aica_pcmswitch_control, dreamcastcard)); 5388c2ecf20Sopenharmony_ci if (unlikely(err < 0)) 5398c2ecf20Sopenharmony_ci return err; 5408c2ecf20Sopenharmony_ci return 0; 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic int snd_aica_remove(struct platform_device *devptr) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci struct snd_card_aica *dreamcastcard; 5468c2ecf20Sopenharmony_ci dreamcastcard = platform_get_drvdata(devptr); 5478c2ecf20Sopenharmony_ci if (unlikely(!dreamcastcard)) 5488c2ecf20Sopenharmony_ci return -ENODEV; 5498c2ecf20Sopenharmony_ci snd_card_free(dreamcastcard->card); 5508c2ecf20Sopenharmony_ci kfree(dreamcastcard); 5518c2ecf20Sopenharmony_ci return 0; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic int snd_aica_probe(struct platform_device *devptr) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci int err; 5578c2ecf20Sopenharmony_ci struct snd_card_aica *dreamcastcard; 5588c2ecf20Sopenharmony_ci dreamcastcard = kzalloc(sizeof(struct snd_card_aica), GFP_KERNEL); 5598c2ecf20Sopenharmony_ci if (unlikely(!dreamcastcard)) 5608c2ecf20Sopenharmony_ci return -ENOMEM; 5618c2ecf20Sopenharmony_ci err = snd_card_new(&devptr->dev, index, SND_AICA_DRIVER, 5628c2ecf20Sopenharmony_ci THIS_MODULE, 0, &dreamcastcard->card); 5638c2ecf20Sopenharmony_ci if (unlikely(err < 0)) { 5648c2ecf20Sopenharmony_ci kfree(dreamcastcard); 5658c2ecf20Sopenharmony_ci return err; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci strcpy(dreamcastcard->card->driver, "snd_aica"); 5688c2ecf20Sopenharmony_ci strcpy(dreamcastcard->card->shortname, SND_AICA_DRIVER); 5698c2ecf20Sopenharmony_ci strcpy(dreamcastcard->card->longname, 5708c2ecf20Sopenharmony_ci "Yamaha AICA Super Intelligent Sound Processor for SEGA Dreamcast"); 5718c2ecf20Sopenharmony_ci /* Prepare to use the queue */ 5728c2ecf20Sopenharmony_ci INIT_WORK(&(dreamcastcard->spu_dma_work), run_spu_dma); 5738c2ecf20Sopenharmony_ci timer_setup(&dreamcastcard->timer, aica_period_elapsed, 0); 5748c2ecf20Sopenharmony_ci /* Load the PCM 'chip' */ 5758c2ecf20Sopenharmony_ci err = snd_aicapcmchip(dreamcastcard, 0); 5768c2ecf20Sopenharmony_ci if (unlikely(err < 0)) 5778c2ecf20Sopenharmony_ci goto freedreamcast; 5788c2ecf20Sopenharmony_ci /* Add basic controls */ 5798c2ecf20Sopenharmony_ci err = add_aicamixer_controls(dreamcastcard); 5808c2ecf20Sopenharmony_ci if (unlikely(err < 0)) 5818c2ecf20Sopenharmony_ci goto freedreamcast; 5828c2ecf20Sopenharmony_ci /* Register the card with ALSA subsystem */ 5838c2ecf20Sopenharmony_ci err = snd_card_register(dreamcastcard->card); 5848c2ecf20Sopenharmony_ci if (unlikely(err < 0)) 5858c2ecf20Sopenharmony_ci goto freedreamcast; 5868c2ecf20Sopenharmony_ci platform_set_drvdata(devptr, dreamcastcard); 5878c2ecf20Sopenharmony_ci snd_printk 5888c2ecf20Sopenharmony_ci ("ALSA Driver for Yamaha AICA Super Intelligent Sound Processor\n"); 5898c2ecf20Sopenharmony_ci return 0; 5908c2ecf20Sopenharmony_ci freedreamcast: 5918c2ecf20Sopenharmony_ci snd_card_free(dreamcastcard->card); 5928c2ecf20Sopenharmony_ci kfree(dreamcastcard); 5938c2ecf20Sopenharmony_ci return err; 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic struct platform_driver snd_aica_driver = { 5978c2ecf20Sopenharmony_ci .probe = snd_aica_probe, 5988c2ecf20Sopenharmony_ci .remove = snd_aica_remove, 5998c2ecf20Sopenharmony_ci .driver = { 6008c2ecf20Sopenharmony_ci .name = SND_AICA_DRIVER, 6018c2ecf20Sopenharmony_ci }, 6028c2ecf20Sopenharmony_ci}; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_cistatic int __init aica_init(void) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci int err; 6078c2ecf20Sopenharmony_ci err = platform_driver_register(&snd_aica_driver); 6088c2ecf20Sopenharmony_ci if (unlikely(err < 0)) 6098c2ecf20Sopenharmony_ci return err; 6108c2ecf20Sopenharmony_ci pd = platform_device_register_simple(SND_AICA_DRIVER, -1, 6118c2ecf20Sopenharmony_ci aica_memory_space, 2); 6128c2ecf20Sopenharmony_ci if (IS_ERR(pd)) { 6138c2ecf20Sopenharmony_ci platform_driver_unregister(&snd_aica_driver); 6148c2ecf20Sopenharmony_ci return PTR_ERR(pd); 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci /* Load the firmware */ 6178c2ecf20Sopenharmony_ci return load_aica_firmware(); 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cistatic void __exit aica_exit(void) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci platform_device_unregister(pd); 6238c2ecf20Sopenharmony_ci platform_driver_unregister(&snd_aica_driver); 6248c2ecf20Sopenharmony_ci /* Kill any sound still playing and reset ARM7 to safe state */ 6258c2ecf20Sopenharmony_ci spu_reset(); 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_cimodule_init(aica_init); 6298c2ecf20Sopenharmony_cimodule_exit(aica_exit); 630