1/* 2 ********************************************************************** 3 * passthrough.c -- Emu10k1 digital passthrough 4 * Copyright (C) 2001 Juha Yrj�l� <jyrjola@cc.hut.fi> 5 * 6 ********************************************************************** 7 * 8 * Date Author Summary of changes 9 * ---- ------ ------------------ 10 * May 15, 2001 Juha Yrj�l� base code release 11 * 12 ********************************************************************** 13 * 14 * This program is free software; you can redistribute it and/or 15 * modify it under the terms of the GNU General Public License as 16 * published by the Free Software Foundation; either version 2 of 17 * the License, or (at your option) any later version. 18 * 19 * This program is distributed in the hope that it will be useful, 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * GNU General Public License for more details. 23 * 24 * You should have received a copy of the GNU General Public 25 * License along with this program; if not, write to the Free 26 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 27 * USA. 28 * 29 ********************************************************************** 30 */ 31 32#define __NO_VERSION__ 33#include <linux/module.h> 34#include <linux/poll.h> 35#include <linux/slab.h> 36#include <linux/version.h> 37#include <linux/bitops.h> 38#include <asm/io.h> 39#include <linux/sched.h> 40#include <linux/smp_lock.h> 41#include <linux/wrapper.h> 42 43#include "hwaccess.h" 44#include "cardwo.h" 45#include "cardwi.h" 46#include "recmgr.h" 47#include "irqmgr.h" 48#include "audio.h" 49#include "8010.h" 50 51static void pt_putsamples(struct pt_data *pt, u16 *ptr, u16 left, u16 right) 52{ 53 unsigned int idx; 54 55 ptr[pt->copyptr] = left; 56 idx = pt->copyptr + PT_SAMPLES/2; 57 idx %= PT_SAMPLES; 58 ptr[idx] = right; 59} 60 61static inline int pt_can_write(struct pt_data *pt) 62{ 63 return pt->blocks_copied < pt->blocks_played + 8; 64} 65 66static int pt_wait_for_write(struct emu10k1_wavedevice *wavedev, int nonblock) 67{ 68 struct emu10k1_card *card = wavedev->card; 69 struct pt_data *pt = &card->pt; 70 71 if (nonblock && !pt_can_write(pt)) 72 return -EAGAIN; 73 while (!pt_can_write(pt) && pt->state != PT_STATE_INACTIVE) { 74 interruptible_sleep_on(&pt->wait); 75 if (signal_pending(current)) 76 return -ERESTARTSYS; 77 } 78 if (pt->state == PT_STATE_INACTIVE) 79 return -EAGAIN; 80 81 return 0; 82} 83 84static int pt_putblock(struct emu10k1_wavedevice *wave_dev, u16 *block, int nonblock) 85{ 86 struct woinst *woinst = wave_dev->woinst; 87 struct emu10k1_card *card = wave_dev->card; 88 struct pt_data *pt = &card->pt; 89 u16 *ptr = (u16 *) card->tankmem.addr; 90 int i = 0, r; 91 unsigned long flags; 92 93 r = pt_wait_for_write(wave_dev, nonblock); 94 if (r < 0) 95 return r; 96 spin_lock_irqsave(&card->pt.lock, flags); 97 while (i < PT_BLOCKSAMPLES) { 98 pt_putsamples(pt, ptr, block[2*i], block[2*i+1]); 99 if (pt->copyptr == 0) 100 pt->copyptr = PT_SAMPLES; 101 pt->copyptr--; 102 i++; 103 } 104 woinst->total_copied += PT_BLOCKSIZE; 105 pt->blocks_copied++; 106 if (pt->blocks_copied >= 4 && pt->state != PT_STATE_PLAYING) { 107 DPF(2, "activating digital pass-through playback\n"); 108 sblive_writeptr(card, GPR_BASE + pt->enable_gpr, 0, 1); 109 pt->state = PT_STATE_PLAYING; 110 } 111 spin_unlock_irqrestore(&card->pt.lock, flags); 112 return 0; 113} 114 115static int pt_setup(struct emu10k1_wavedevice *wave_dev) 116{ 117 u32 bits; 118 struct emu10k1_card *card = wave_dev->card; 119 struct pt_data *pt = &card->pt; 120 int i; 121 122 for (i = 0; i < 3; i++) { 123 pt->old_spcs[i] = sblive_readptr(card, SPCS0 + i, 0); 124 if (pt->spcs_to_use & (1 << i)) { 125 DPD(2, "using S/PDIF port %d\n", i); 126 bits = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | 127 SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 128 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT; 129 if (pt->ac3data) 130 bits |= SPCS_NOTAUDIODATA; 131 sblive_writeptr(card, SPCS0 + i, 0, bits); 132 } 133 } 134 return 0; 135} 136 137ssize_t emu10k1_pt_write(struct file *file, const char *buffer, size_t count) 138{ 139 struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; 140 struct emu10k1_card *card = wave_dev->card; 141 struct pt_data *pt = &card->pt; 142 int nonblock, i, r, blocks, blocks_copied, bytes_copied = 0; 143 144 DPD(3, "emu10k1_pt_write(): %d bytes\n", count); 145 146 nonblock = file->f_flags & O_NONBLOCK; 147 148 if (card->tankmem.size < PT_SAMPLES*2) 149 return -EFAULT; 150 if (pt->state == PT_STATE_INACTIVE) { 151 DPF(2, "bufptr init\n"); 152 pt->playptr = PT_SAMPLES-1; 153 pt->copyptr = PT_INITPTR; 154 pt->blocks_played = pt->blocks_copied = 0; 155 memset(card->tankmem.addr, 0, card->tankmem.size); 156 pt->state = PT_STATE_ACTIVATED; 157 pt->buf = kmalloc(PT_BLOCKSIZE, GFP_KERNEL); 158 pt->prepend_size = 0; 159 if (pt->buf == NULL) 160 return -ENOMEM; 161 pt_setup(wave_dev); 162 } 163 if (pt->prepend_size) { 164 int needed = PT_BLOCKSIZE - pt->prepend_size; 165 166 DPD(3, "prepend size %d, prepending %d bytes\n", pt->prepend_size, needed); 167 if (count < needed) { 168 copy_from_user(pt->buf + pt->prepend_size, buffer, count); 169 pt->prepend_size += count; 170 DPD(3, "prepend size now %d\n", pt->prepend_size); 171 return count; 172 } 173 copy_from_user(pt->buf + pt->prepend_size, buffer, needed); 174 r = pt_putblock(wave_dev, (u16 *) pt->buf, nonblock); 175 if (r) 176 return r; 177 bytes_copied += needed; 178 pt->prepend_size = 0; 179 } 180 blocks = (count-bytes_copied)/PT_BLOCKSIZE; 181 blocks_copied = 0; 182 while (blocks > 0) { 183 u16 *bufptr = (u16 *) buffer + (bytes_copied/2); 184 copy_from_user(pt->buf, bufptr, PT_BLOCKSIZE); 185 bufptr = (u16 *) pt->buf; 186 r = pt_putblock(wave_dev, bufptr, nonblock); 187 if (r) { 188 if (bytes_copied) 189 return bytes_copied; 190 else 191 return r; 192 } 193 bytes_copied += PT_BLOCKSIZE; 194 blocks--; 195 blocks_copied++; 196 } 197 i = count - bytes_copied; 198 if (i) { 199 pt->prepend_size = i; 200 copy_from_user(pt->buf, buffer + bytes_copied, i); 201 bytes_copied += i; 202 DPD(3, "filling prepend buffer with %d bytes", i); 203 } 204 return bytes_copied; 205} 206 207void emu10k1_pt_stop(struct emu10k1_card *card) 208{ 209 struct pt_data *pt = &card->pt; 210 int i; 211 212 if (pt->state != PT_STATE_INACTIVE) { 213 DPF(2, "digital pass-through stopped\n"); 214 sblive_writeptr(card, GPR_BASE + pt->enable_gpr, 0, 0); 215 for (i = 0; i < 3; i++) { 216 if (pt->spcs_to_use & (1 << i)) 217 sblive_writeptr(card, SPCS0 + i, 0, pt->old_spcs[i]); 218 } 219 pt->state = PT_STATE_INACTIVE; 220 kfree(pt->buf); 221 } 222} 223 224void emu10k1_pt_waveout_update(struct emu10k1_wavedevice *wave_dev) 225{ 226 struct woinst *woinst = wave_dev->woinst; 227 struct pt_data *pt = &wave_dev->card->pt; 228 u32 pos; 229 230 if (pt->state == PT_STATE_PLAYING && pt->pos_gpr >= 0) { 231 pos = sblive_readptr(wave_dev->card, GPR_BASE + pt->pos_gpr, 0); 232 if (pos > PT_BLOCKSAMPLES) 233 pos = PT_BLOCKSAMPLES; 234 pos = 4 * (PT_BLOCKSAMPLES - pos); 235 } else 236 pos = 0; 237 woinst->total_played = pt->blocks_played * woinst->buffer.fragment_size + pos; 238 woinst->buffer.hw_pos = pos; 239} 240