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