fm801.c revision 65340
1/*
2 * Copyright (c) 2000 Dmitry Dicky diwil@dataart.com
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS `AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/dev/sound/pci/fm801.c 65340 2000-09-01 20:09:24Z cg $
27 */
28
29#include <dev/sound/pcm/sound.h>
30#include <dev/sound/pcm/ac97.h>
31#include <pci/pcireg.h>
32#include <pci/pcivar.h>
33
34#define PCI_VENDOR_FORTEMEDIA	0x1319
35#define PCI_DEVICE_FORTEMEDIA1	0x08011319
36#define PCI_DEVICE_FORTEMEDIA2	0x08021319	/* ??? have no idea what's this... */
37
38#define FM_PCM_VOLUME           0x00
39#define FM_FM_VOLUME            0x02
40#define FM_I2S_VOLUME           0x04
41#define FM_RECORD_SOURCE        0x06
42
43#define FM_PLAY_CTL             0x08
44#define  FM_PLAY_RATE_MASK              0x0f00
45#define  FM_PLAY_BUF1_LAST              0x0001
46#define  FM_PLAY_BUF2_LAST              0x0002
47#define  FM_PLAY_START                  0x0020
48#define  FM_PLAY_PAUSE                  0x0040
49#define  FM_PLAY_STOPNOW                0x0080
50#define  FM_PLAY_16BIT                  0x4000
51#define  FM_PLAY_STEREO                 0x8000
52
53#define FM_PLAY_DMALEN          0x0a
54#define FM_PLAY_DMABUF1         0x0c
55#define FM_PLAY_DMABUF2         0x10
56
57
58#define FM_REC_CTL              0x14
59#define  FM_REC_RATE_MASK               0x0f00
60#define  FM_REC_BUF1_LAST               0x0001
61#define  FM_REC_BUF2_LAST               0x0002
62#define  FM_REC_START                   0x0020
63#define  FM_REC_PAUSE                   0x0040
64#define  FM_REC_STOPNOW                 0x0080
65#define  FM_REC_16BIT                   0x4000
66#define  FM_REC_STEREO                  0x8000
67
68
69#define FM_REC_DMALEN           0x16
70#define FM_REC_DMABUF1          0x18
71#define FM_REC_DMABUF2          0x1c
72
73#define FM_CODEC_CTL            0x22
74#define FM_VOLUME               0x26
75#define  FM_VOLUME_MUTE                 0x8000
76
77#define FM_CODEC_CMD            0x2a
78#define  FM_CODEC_CMD_READ              0x0080
79#define  FM_CODEC_CMD_VALID             0x0100
80#define  FM_CODEC_CMD_BUSY              0x0200
81
82#define FM_CODEC_DATA           0x2c
83
84#define FM_IO_CTL               0x52
85#define FM_CARD_CTL             0x54
86
87#define FM_INTMASK              0x56
88#define  FM_INTMASK_PLAY                0x0001
89#define  FM_INTMASK_REC                 0x0002
90#define  FM_INTMASK_VOL                 0x0040
91#define  FM_INTMASK_MPU                 0x0080
92
93#define FM_INTSTATUS            0x5a
94#define  FM_INTSTATUS_PLAY              0x0100
95#define  FM_INTSTATUS_REC               0x0200
96#define  FM_INTSTATUS_VOL               0x4000
97#define  FM_INTSTATUS_MPU               0x8000
98
99#define FM801_BUFFSIZE 1024*4	/* Other values do not work!!! */
100
101/* debug purposes */
102#define DPRINT	 if(0) printf
103
104
105/* channel interface */
106static void *fm801ch_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
107static int fm801ch_setdir(void *data, int dir);
108static int fm801ch_setformat(void *data, u_int32_t format);
109static int fm801ch_setspeed(void *data, u_int32_t speed);
110static int fm801ch_setblocksize(void *data, u_int32_t blocksize);
111static int fm801ch_trigger(void *data, int go);
112static int fm801ch_getptr(void *data);
113static pcmchan_caps *fm801ch_getcaps(void *data);
114/*
115static int fm801ch_setup(pcm_channel *c);
116*/
117
118static u_int32_t fmts[] = {
119	AFMT_U8,
120	AFMT_STEREO | AFMT_U8,
121	AFMT_S16_LE,
122	AFMT_STEREO | AFMT_S16_LE, /*
123	AFMT_STEREO | (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE),
124	(AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE), */
125	0
126};
127
128static pcmchan_caps fm801ch_caps = {
129	4000, 48000,
130	fmts, 0
131};
132
133static pcm_channel fm801_chantemplate = {
134	fm801ch_init,
135	fm801ch_setdir,
136	fm801ch_setformat,
137	fm801ch_setspeed,
138	fm801ch_setblocksize,
139	fm801ch_trigger,
140	fm801ch_getptr,
141	fm801ch_getcaps,
142	NULL, 			/* free */
143	NULL, 			/* nop1 */
144	NULL, 			/* nop2 */
145	NULL, 			/* nop3 */
146	NULL, 			/* nop4 */
147	NULL, 			/* nop5 */
148	NULL, 			/* nop6 */
149	NULL, 			/* nop7 */
150};
151
152struct fm801_info;
153
154struct fm801_chinfo {
155	struct fm801_info 	*parent;
156	pcm_channel 		*channel;
157	snd_dbuf 		*buffer;
158	u_int32_t 		spd, dir, fmt;	/* speed, direction, format */
159	u_int32_t		shift;
160};
161
162struct fm801_info {
163	int 			type;
164	bus_space_tag_t 	st;
165	bus_space_handle_t 	sh;
166	bus_dma_tag_t   	parent_dmat;
167
168	device_t 		dev;
169	int 			num;
170	u_int32_t 		unit;
171
172	struct resource 	*reg, *irq;
173	int             	regtype, regid, irqid;
174	void            	*ih;
175
176	u_int32_t		play_flip,
177				play_nextblk,
178				play_start,
179				play_blksize,
180				play_fmt,
181				play_shift,
182				play_size;
183
184	u_int32_t		rec_flip,
185				rec_nextblk,
186				rec_start,
187				rec_blksize,
188				rec_fmt,
189				rec_shift,
190				rec_size;
191
192	struct fm801_chinfo 	pch, rch;
193};
194
195
196/* several procedures to release the thing properly if compiled as module */
197static struct fm801_info *save801;
198struct fm801_info *fm801_get __P((void ));
199
200static void
201fm801_save(struct fm801_info *fm801)
202{
203	save801 = fm801;
204}
205
206struct fm801_info *
207fm801_get(void )
208{
209	return save801;
210}
211
212/* Bus Read / Write routines */
213static u_int32_t
214fm801_rd(struct fm801_info *fm801, int regno, int size)
215{
216	switch(size) {
217	case 1:
218		return (bus_space_read_1(fm801->st, fm801->sh, regno));
219	case 2:
220		return (bus_space_read_2(fm801->st, fm801->sh, regno));
221	case 4:
222		return (bus_space_read_4(fm801->st, fm801->sh, regno));
223	default:
224		return 0xffffffff;
225	}
226}
227
228static void
229fm801_wr(struct fm801_info *fm801, int regno, u_int32_t data, int size)
230{
231	switch(size) {
232	case 1:
233		return bus_space_write_1(fm801->st, fm801->sh, regno, data);
234	case 2:
235		return bus_space_write_2(fm801->st, fm801->sh, regno, data);
236	case 4:
237		return bus_space_write_4(fm801->st, fm801->sh, regno, data);
238	default:
239		return;
240	}
241}
242
243/*
244 *  ac97 codec routines
245 */
246#define TIMO 50
247static u_int32_t
248fm801_rdcd(void *devinfo, int regno)
249{
250	struct fm801_info *fm801 = (struct fm801_info *)devinfo;
251	int i;
252
253	for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
254		DELAY(10000);
255		DPRINT("fm801 rdcd: 1 - DELAY\n");
256	}
257	if (i >= TIMO) {
258		printf("fm801 rdcd: codec busy\n");
259		return 0;
260	}
261
262	fm801_wr(fm801,FM_CODEC_CMD, regno|FM_CODEC_CMD_READ,2);
263
264	for (i = 0; i < TIMO && !(fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_VALID); i++)
265	{
266		DELAY(10000);
267		DPRINT("fm801 rdcd: 2 - DELAY\n");
268	}
269	if (i >= TIMO) {
270		printf("fm801 rdcd: write codec invalid\n");
271		return 0;
272	}
273
274	return fm801_rd(fm801,FM_CODEC_DATA,2);
275}
276
277static void
278fm801_wrcd(void *devinfo, int regno, u_int32_t data)
279{
280	struct fm801_info *fm801 = (struct fm801_info *)devinfo;
281	int i;
282
283	DPRINT("fm801_wrcd reg 0x%x val 0x%x\n",regno, data);
284/*
285	if(regno == AC97_REG_RECSEL)	return;
286*/
287	/* Poll until codec is ready */
288	for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
289		DELAY(10000);
290		DPRINT("fm801 rdcd: 1 - DELAY\n");
291	}
292	if (i >= TIMO) {
293		printf("fm801 wrcd: read codec busy\n");
294		return;
295	}
296
297	fm801_wr(fm801,FM_CODEC_DATA,data, 2);
298	fm801_wr(fm801,FM_CODEC_CMD, regno,2);
299
300	/* wait until codec is ready */
301	for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
302		DELAY(10000);
303		DPRINT("fm801 wrcd: 2 - DELAY\n");
304	}
305	if (i >= TIMO) {
306		printf("fm801 wrcd: read codec busy\n");
307		return;
308	}
309	DPRINT("fm801 wrcd release reg 0x%x val 0x%x\n",regno, data);
310	return;
311}
312
313/*
314 * The interrupt handler
315 */
316static void
317fm801_intr(void *p)
318{
319	struct fm801_info 	*fm801 = (struct fm801_info *)p;
320	u_int32_t       	intsrc = fm801_rd(fm801, FM_INTSTATUS, 2);
321	struct fm801_chinfo	*ch = &(fm801->pch);
322	snd_dbuf 		*b = ch->buffer;
323
324	DPRINT("\nfm801_intr intsrc 0x%x ", intsrc);
325	DPRINT("rp %d, rl %d, fp %d fl %d, size=%d\n",
326		b->rp,b->rl, b->fp,b->fl, b->blksz);
327
328	if(intsrc & FM_INTSTATUS_PLAY) {
329		fm801->play_flip++;
330		if(fm801->play_flip & 1) {
331			fm801_wr(fm801, FM_PLAY_DMABUF1, fm801->play_start,4);
332		} else
333			fm801_wr(fm801, FM_PLAY_DMABUF2, fm801->play_nextblk,4);
334		chn_intr(fm801->pch.channel);
335	}
336
337	if(intsrc & FM_INTSTATUS_REC) {
338		fm801->rec_flip++;
339		if(fm801->rec_flip & 1) {
340			fm801_wr(fm801, FM_REC_DMABUF1, fm801->rec_start,4);
341		} else
342			fm801_wr(fm801, FM_REC_DMABUF2, fm801->rec_nextblk,4);
343		chn_intr(fm801->rch.channel);
344	}
345
346	if ( intsrc & FM_INTSTATUS_MPU ) {
347		/* This is a TODOish thing... */
348		fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_MPU,2);
349	}
350
351	if ( intsrc & FM_INTSTATUS_VOL ) {
352		/* This is a TODOish thing... */
353		fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_VOL,2);
354	}
355
356	DPRINT("fm801_intr clear\n\n");
357	fm801_wr(fm801, FM_INTSTATUS, intsrc & (FM_INTSTATUS_PLAY | FM_INTSTATUS_REC), 2);
358}
359
360/*
361 *  Init routine is taken from an original NetBSD driver
362 */
363static int
364fm801_init(struct fm801_info *fm801)
365{
366	u_int32_t k1;
367
368	/* reset codec */
369	fm801_wr(fm801, FM_CODEC_CTL, 0x0020,2);
370	DELAY(100000);
371	fm801_wr(fm801, FM_CODEC_CTL, 0x0000,2);
372	DELAY(100000);
373
374	fm801_wr(fm801, FM_PCM_VOLUME, 0x0808,2);
375	fm801_wr(fm801, FM_FM_VOLUME, 0x0808,2);
376	fm801_wr(fm801, FM_I2S_VOLUME, 0x0808,2);
377	fm801_wr(fm801, 0x40,0x107f,2);	/* enable legacy audio */
378
379	fm801_wr((void *)fm801, FM_RECORD_SOURCE, 0x0000,2);
380
381	/* Unmask playback, record and mpu interrupts, mask the rest */
382	k1 = fm801_rd((void *)fm801, FM_INTMASK,2);
383	fm801_wr(fm801, FM_INTMASK,
384		(k1 & ~(FM_INTMASK_PLAY | FM_INTMASK_REC | FM_INTMASK_MPU)) |
385		FM_INTMASK_VOL,2);
386	fm801_wr(fm801, FM_INTSTATUS,
387		FM_INTSTATUS_PLAY | FM_INTSTATUS_REC | FM_INTSTATUS_MPU |
388		FM_INTSTATUS_VOL,2);
389
390	DPRINT("FM801 init Ok\n");
391	return 0;
392}
393
394static int
395fm801_pci_attach(device_t dev)
396{
397	u_int32_t 		data;
398	struct ac97_info 	*codec;
399	struct fm801_info 	*fm801;
400	int 			i;
401	int 			mapped = 0;
402	char 			status[SND_STATUSLEN];
403
404	if ((fm801 = (struct fm801_info *)malloc(sizeof(*fm801),M_DEVBUF, M_NOWAIT)) == NULL) {
405		device_printf(dev, "cannot allocate softc\n");
406		return ENXIO;
407	}
408
409	bzero(fm801, sizeof(*fm801));
410	fm801->type = pci_get_devid(dev);
411
412	data = pci_read_config(dev, PCIR_COMMAND, 2);
413	data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
414	pci_write_config(dev, PCIR_COMMAND, data, 2);
415	data = pci_read_config(dev, PCIR_COMMAND, 2);
416
417	for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) {
418		fm801->regid = PCIR_MAPS + i*4;
419		fm801->regtype = SYS_RES_MEMORY;
420		fm801->reg = bus_alloc_resource(dev, fm801->regtype, &fm801->regid,
421						0, ~0, 1, RF_ACTIVE);
422		if(!fm801->reg)
423		{
424			fm801->regtype = SYS_RES_IOPORT;
425			fm801->reg = bus_alloc_resource(dev, fm801->regtype, &fm801->regid,
426						0, ~0, 1, RF_ACTIVE);
427		}
428
429		if(fm801->reg) {
430			fm801->st = rman_get_bustag(fm801->reg);
431			fm801->sh = rman_get_bushandle(fm801->reg);
432			mapped++;
433		}
434	}
435
436	if (mapped == 0) {
437		device_printf(dev, "unable to map register space\n");
438		goto oops;
439	}
440
441	fm801_init(fm801);
442
443	codec = ac97_create(dev, (void *)fm801, NULL, fm801_rdcd, fm801_wrcd);
444	if (codec == NULL) goto oops;
445
446	if (mixer_init(dev, &ac97_mixer, codec) == -1) goto oops;
447
448	fm801->irqid = 0;
449	fm801->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &fm801->irqid,
450				0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
451	if (!fm801->irq ||
452		bus_setup_intr(dev, fm801->irq, INTR_TYPE_TTY,
453					fm801_intr, fm801, &fm801->ih)) {
454		device_printf(dev, "unable to map interrupt\n");
455		goto oops;
456	}
457
458	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
459		/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
460		/*highaddr*/BUS_SPACE_MAXADDR,
461		/*filter*/NULL, /*filterarg*/NULL,
462		/*maxsize*/FM801_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
463		/*flags*/0, &fm801->parent_dmat) != 0) {
464		device_printf(dev, "unable to create dma tag\n");
465		goto oops;
466	}
467
468	snprintf(status, 64, "at %s 0x%lx irq %ld",
469		(fm801->regtype == SYS_RES_IOPORT)? "io" : "memory",
470		rman_get_start(fm801->reg), rman_get_start(fm801->irq));
471
472#define FM801_MAXPLAYCH	1
473	if (pcm_register(dev, fm801, FM801_MAXPLAYCH, 1)) goto oops;
474	pcm_addchan(dev, PCMDIR_PLAY, &fm801_chantemplate, fm801);
475	pcm_addchan(dev, PCMDIR_REC, &fm801_chantemplate, fm801);
476	pcm_setstatus(dev, status);
477
478	fm801_save(fm801);
479	return 0;
480
481oops:
482	printf("Forte Media FM801 initialization failed\n");
483	if (fm801->reg) bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg);
484	if (fm801->ih) bus_teardown_intr(dev, fm801->irq, fm801->ih);
485	if (fm801->irq) bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq);
486	free(fm801, M_DEVBUF);
487	return ENXIO;
488}
489
490static int
491fm801_pci_probe( device_t dev )
492{
493	int id;
494	if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA1 ) {
495		device_set_desc(dev, "Forte Media FM801 Audio Controller");
496		return 0;
497	}
498/*
499	if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA2 ) {
500		device_set_desc(dev, "Forte Media FM801 Joystick (Not Supported)");
501		return ENXIO;
502	}
503*/
504	return ENXIO;
505}
506
507
508
509/* channel interface */
510static void *
511fm801ch_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
512{
513	struct fm801_info *fm801 = (struct fm801_info *)devinfo;
514	struct fm801_chinfo *ch = (dir == PCMDIR_PLAY)? &fm801->pch : &fm801->rch;
515
516	DPRINT("fm801ch_init, direction = %d\n", dir);
517	ch->parent = fm801;
518	ch->channel = c;
519	ch->buffer = b;
520	ch->buffer->bufsize = FM801_BUFFSIZE;
521	ch->dir = dir;
522	if( chn_allocbuf(ch->buffer, fm801->parent_dmat) == -1) return NULL;
523	return (void *)ch;
524}
525
526static int
527fm801ch_setdir(void *data, int dir)
528{
529	struct fm801_chinfo *ch = data;
530	ch->dir = dir;
531	return 0;
532}
533
534static int
535fm801ch_setformat(void *data, u_int32_t format)
536{
537	struct fm801_chinfo *ch = data;
538	struct fm801_info *fm801 = ch->parent;
539
540	DPRINT("fm801ch_setformat 0x%x : %s, %s, %s, %s\n", format,
541		(format & AFMT_STEREO)?"stereo":"mono",
542		(format & (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)) ? "16bit":"8bit",
543		(format & AFMT_SIGNED)? "signed":"unsigned",
544		(format & AFMT_BIGENDIAN)?"bigendiah":"littleendian" );
545
546	if(ch->dir == PCMDIR_PLAY) {
547		fm801->play_fmt =  (format & AFMT_STEREO)? FM_PLAY_STEREO : 0;
548		fm801->play_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0;
549		return 0;
550	}
551
552	if(ch->dir == PCMDIR_REC ) {
553		fm801->rec_fmt = (format & AFMT_STEREO)? FM_REC_STEREO:0;
554		fm801->rec_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0;
555		return 0;
556	}
557
558	return 0;
559}
560
561struct {
562	int limit;
563	int rate;
564} fm801_rates[11] = {
565	{  6600,  5500 },
566	{  8750,  8000 },
567	{ 10250,  9600 },
568	{ 13200, 11025 },
569	{ 17500, 16000 },
570	{ 20500, 19200 },
571	{ 26500, 22050 },
572	{ 35000, 32000 },
573	{ 41000, 38400 },
574	{ 46000, 44100 },
575	{ 48000, 48000 },
576/* anything above -> 48000 */
577};
578
579static int
580fm801ch_setspeed(void *data, u_int32_t speed)
581{
582	struct fm801_chinfo *ch = data;
583	struct fm801_info *fm801 = ch->parent;
584	register int i;
585
586
587	for (i = 0; i < 10 && fm801_rates[i].limit <= speed; i++) ;
588
589	if(ch->dir == PCMDIR_PLAY) {
590		fm801->pch.spd = fm801_rates[i].rate;
591		fm801->play_shift = (i<<8);
592		fm801->play_shift &= FM_PLAY_RATE_MASK;
593	}
594
595	if(ch->dir == PCMDIR_REC ) {
596		fm801->rch.spd = fm801_rates[i].rate;
597		fm801->rec_shift = (i<<8);
598		fm801->rec_shift &= FM_REC_RATE_MASK;
599	}
600
601	ch->spd = fm801_rates[i].rate;
602
603	return fm801_rates[i].rate;
604}
605
606static int
607fm801ch_setblocksize(void *data, u_int32_t blocksize)
608{
609	struct fm801_chinfo *ch = data;
610	struct fm801_info *fm801 = ch->parent;
611
612	if(ch->dir == PCMDIR_PLAY) {
613		if(fm801->play_flip) return fm801->play_blksize;
614		fm801->play_blksize = blocksize;
615	}
616
617	if(ch->dir == PCMDIR_REC) {
618		if(fm801->rec_flip) return fm801->rec_blksize;
619		fm801->rec_blksize = blocksize;
620	}
621
622	DPRINT("fm801ch_setblocksize %d (dir %d)\n",blocksize, ch->dir);
623
624	return blocksize;
625}
626
627static int
628fm801ch_trigger(void *data, int go)
629{
630	struct fm801_chinfo *ch = data;
631	struct fm801_info *fm801 = ch->parent;
632	u_int32_t baseaddr = vtophys(ch->buffer->buf);
633	snd_dbuf *b = ch->buffer;
634	u_int32_t k1;
635
636	DPRINT("fm801ch_trigger go %d , ", go);
637	DPRINT("rp %d, rl %d, fp %d fl %d, dl %d, blksize=%d\n",
638		b->rp,b->rl, b->fp,b->fl, b->dl, b->blksz);
639
640	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) {
641		return 0;
642	}
643
644	if (ch->dir == PCMDIR_PLAY) {
645		if (go == PCMTRIG_START) {
646
647			fm801->play_start = baseaddr;
648			fm801->play_nextblk = fm801->play_start + fm801->play_blksize;
649			fm801->play_flip = 0;
650			fm801_wr(fm801, FM_PLAY_DMALEN, fm801->play_blksize - 1, 2);
651			fm801_wr(fm801, FM_PLAY_DMABUF1,fm801->play_start,4);
652			fm801_wr(fm801, FM_PLAY_DMABUF2,fm801->play_nextblk,4);
653			fm801_wr(fm801, FM_PLAY_CTL,
654					FM_PLAY_START | FM_PLAY_STOPNOW | fm801->play_fmt | fm801->play_shift,
655					2 );
656			} else {
657			fm801->play_flip = 0;
658			k1 = fm801_rd(fm801, FM_PLAY_CTL,2);
659			fm801_wr(fm801, FM_PLAY_CTL,
660				(k1 & ~(FM_PLAY_STOPNOW | FM_PLAY_START)) |
661				FM_PLAY_BUF1_LAST | FM_PLAY_BUF2_LAST, 2 );
662		}
663	} else if(ch->dir == PCMDIR_REC) {
664		if (go == PCMTRIG_START) {
665			fm801->rec_start = baseaddr;
666			fm801->rec_nextblk = fm801->rec_start + fm801->rec_blksize;
667			fm801->rec_flip = 0;
668			fm801_wr(fm801, FM_REC_DMALEN, fm801->rec_blksize - 1, 2);
669			fm801_wr(fm801, FM_REC_DMABUF1,fm801->rec_start,4);
670			fm801_wr(fm801, FM_REC_DMABUF2,fm801->rec_nextblk,4);
671			fm801_wr(fm801, FM_REC_CTL,
672					FM_REC_START | FM_REC_STOPNOW | fm801->rec_fmt | fm801->rec_shift,
673					2 );
674			} else {
675			fm801->rec_flip = 0;
676			k1 = fm801_rd(fm801, FM_REC_CTL,2);
677			fm801_wr(fm801, FM_REC_CTL,
678				(k1 & ~(FM_REC_STOPNOW | FM_REC_START)) |
679				FM_REC_BUF1_LAST | FM_REC_BUF2_LAST, 2);
680		}
681	}
682
683	return 0;
684}
685
686/* Almost ALSA copy */
687static int
688fm801ch_getptr(void *data)
689{
690	struct fm801_chinfo *ch = data;
691	struct fm801_info *fm801 = ch->parent;
692	int result = 0;
693	snd_dbuf *b = ch->buffer;
694
695	if (ch->dir == PCMDIR_PLAY) {
696		result = fm801_rd(fm801,
697			(fm801->play_flip&1) ?
698			FM_PLAY_DMABUF2:FM_PLAY_DMABUF1, 4) - fm801->play_start;
699	}
700
701	if (ch->dir == PCMDIR_REC) {
702		result = fm801_rd(fm801,
703			(fm801->rec_flip&1) ?
704			FM_REC_DMABUF2:FM_REC_DMABUF1, 4) - fm801->rec_start;
705	}
706
707	DPRINT("fm801ch_getptr:%d,  rp %d, rl %d, fp %d fl %d\n",
708	                result, b->rp,b->rl, b->fp,b->fl);
709
710	return result;
711}
712
713static pcmchan_caps *
714fm801ch_getcaps(void *data)
715{
716	return &fm801ch_caps;
717}
718
719static int
720fm801_pci_detach(device_t dev)
721{
722	struct fm801_info *fm801 = fm801_get();
723
724	DPRINT("Forte Media FM801 detach\n");
725
726	if (fm801->reg) bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg);
727	if (fm801->ih) bus_teardown_intr(dev, fm801->irq, fm801->ih);
728	if (fm801->irq) bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq);
729	free(fm801, M_DEVBUF);
730	return 0;
731}
732
733static device_method_t fm801_methods[] = {
734	/* Device interface */
735	DEVMETHOD(device_probe,		fm801_pci_probe),
736	DEVMETHOD(device_attach,	fm801_pci_attach),
737	DEVMETHOD(device_detach,	fm801_pci_detach),
738	{ 0, 0}
739};
740
741static driver_t fm801_driver = {
742	"pcm",
743	fm801_methods,
744	sizeof(snddev_info),
745};
746
747static devclass_t pcm_devclass;
748
749DRIVER_MODULE(fm801, pci, fm801_driver, pcm_devclass,0, 0);
750