xref: /freebsd/sys/dev/sound/pci/fm801.c (revision 77a0943ded95b9e6438f7db70c4a28e4d93946d4)
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$
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 */
106 static void *fm801ch_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
107 static int fm801ch_setformat(void *data, u_int32_t format);
108 static int fm801ch_setspeed(void *data, u_int32_t speed);
109 static int fm801ch_setblocksize(void *data, u_int32_t blocksize);
110 static int fm801ch_trigger(void *data, int go);
111 static int fm801ch_getptr(void *data);
112 static pcmchan_caps *fm801ch_getcaps(void *data);
113 /*
114 static int fm801ch_setup(pcm_channel *c);
115 */
116 
117 static u_int32_t fmts[] = {
118 	AFMT_U8,
119 	AFMT_STEREO | AFMT_U8,
120 	AFMT_S16_LE,
121 	AFMT_STEREO | AFMT_S16_LE,
122 	0
123 };
124 
125 static pcmchan_caps fm801ch_caps = {
126 	4000, 48000,
127 	fmts, 0
128 };
129 
130 static pcm_channel fm801_chantemplate = {
131 	fm801ch_init,
132 	NULL, 			/* setdir */
133 	fm801ch_setformat,
134 	fm801ch_setspeed,
135 	fm801ch_setblocksize,
136 	fm801ch_trigger,
137 	fm801ch_getptr,
138 	fm801ch_getcaps,
139 	NULL, 			/* free */
140 	NULL, 			/* nop1 */
141 	NULL, 			/* nop2 */
142 	NULL, 			/* nop3 */
143 	NULL, 			/* nop4 */
144 	NULL, 			/* nop5 */
145 	NULL, 			/* nop6 */
146 	NULL, 			/* nop7 */
147 };
148 
149 struct fm801_info;
150 
151 struct fm801_chinfo {
152 	struct fm801_info 	*parent;
153 	pcm_channel 		*channel;
154 	snd_dbuf 		*buffer;
155 	u_int32_t 		spd, dir, fmt;	/* speed, direction, format */
156 	u_int32_t		shift;
157 };
158 
159 struct fm801_info {
160 	int 			type;
161 	bus_space_tag_t 	st;
162 	bus_space_handle_t 	sh;
163 	bus_dma_tag_t   	parent_dmat;
164 
165 	device_t 		dev;
166 	int 			num;
167 	u_int32_t 		unit;
168 
169 	struct resource 	*reg, *irq;
170 	int             	regtype, regid, irqid;
171 	void            	*ih;
172 
173 	u_int32_t		play_flip,
174 				play_nextblk,
175 				play_start,
176 				play_blksize,
177 				play_fmt,
178 				play_shift,
179 				play_size;
180 
181 	u_int32_t		rec_flip,
182 				rec_nextblk,
183 				rec_start,
184 				rec_blksize,
185 				rec_fmt,
186 				rec_shift,
187 				rec_size;
188 
189 	struct fm801_chinfo 	pch, rch;
190 };
191 
192 /* Bus Read / Write routines */
193 static u_int32_t
194 fm801_rd(struct fm801_info *fm801, int regno, int size)
195 {
196 	switch(size) {
197 	case 1:
198 		return (bus_space_read_1(fm801->st, fm801->sh, regno));
199 	case 2:
200 		return (bus_space_read_2(fm801->st, fm801->sh, regno));
201 	case 4:
202 		return (bus_space_read_4(fm801->st, fm801->sh, regno));
203 	default:
204 		return 0xffffffff;
205 	}
206 }
207 
208 static void
209 fm801_wr(struct fm801_info *fm801, int regno, u_int32_t data, int size)
210 {
211 	switch(size) {
212 	case 1:
213 		return bus_space_write_1(fm801->st, fm801->sh, regno, data);
214 	case 2:
215 		return bus_space_write_2(fm801->st, fm801->sh, regno, data);
216 	case 4:
217 		return bus_space_write_4(fm801->st, fm801->sh, regno, data);
218 	default:
219 		return;
220 	}
221 }
222 
223 /*
224  *  ac97 codec routines
225  */
226 #define TIMO 50
227 static u_int32_t
228 fm801_rdcd(void *devinfo, int regno)
229 {
230 	struct fm801_info *fm801 = (struct fm801_info *)devinfo;
231 	int i;
232 
233 	for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
234 		DELAY(10000);
235 		DPRINT("fm801 rdcd: 1 - DELAY\n");
236 	}
237 	if (i >= TIMO) {
238 		printf("fm801 rdcd: codec busy\n");
239 		return 0;
240 	}
241 
242 	fm801_wr(fm801,FM_CODEC_CMD, regno|FM_CODEC_CMD_READ,2);
243 
244 	for (i = 0; i < TIMO && !(fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_VALID); i++)
245 	{
246 		DELAY(10000);
247 		DPRINT("fm801 rdcd: 2 - DELAY\n");
248 	}
249 	if (i >= TIMO) {
250 		printf("fm801 rdcd: write codec invalid\n");
251 		return 0;
252 	}
253 
254 	return fm801_rd(fm801,FM_CODEC_DATA,2);
255 }
256 
257 static void
258 fm801_wrcd(void *devinfo, int regno, u_int32_t data)
259 {
260 	struct fm801_info *fm801 = (struct fm801_info *)devinfo;
261 	int i;
262 
263 	DPRINT("fm801_wrcd reg 0x%x val 0x%x\n",regno, data);
264 /*
265 	if(regno == AC97_REG_RECSEL)	return;
266 */
267 	/* Poll until codec is ready */
268 	for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
269 		DELAY(10000);
270 		DPRINT("fm801 rdcd: 1 - DELAY\n");
271 	}
272 	if (i >= TIMO) {
273 		printf("fm801 wrcd: read codec busy\n");
274 		return;
275 	}
276 
277 	fm801_wr(fm801,FM_CODEC_DATA,data, 2);
278 	fm801_wr(fm801,FM_CODEC_CMD, regno,2);
279 
280 	/* wait until codec is ready */
281 	for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
282 		DELAY(10000);
283 		DPRINT("fm801 wrcd: 2 - DELAY\n");
284 	}
285 	if (i >= TIMO) {
286 		printf("fm801 wrcd: read codec busy\n");
287 		return;
288 	}
289 	DPRINT("fm801 wrcd release reg 0x%x val 0x%x\n",regno, data);
290 	return;
291 }
292 
293 /*
294  * The interrupt handler
295  */
296 static void
297 fm801_intr(void *p)
298 {
299 	struct fm801_info 	*fm801 = (struct fm801_info *)p;
300 	u_int32_t       	intsrc = fm801_rd(fm801, FM_INTSTATUS, 2);
301 	struct fm801_chinfo	*ch = &(fm801->pch);
302 	snd_dbuf 		*b = ch->buffer;
303 
304 	DPRINT("\nfm801_intr intsrc 0x%x ", intsrc);
305 	DPRINT("rp %d, rl %d, fp %d fl %d, size=%d\n",
306 		b->rp,b->rl, b->fp,b->fl, b->blksz);
307 
308 	if(intsrc & FM_INTSTATUS_PLAY) {
309 		fm801->play_flip++;
310 		if(fm801->play_flip & 1) {
311 			fm801_wr(fm801, FM_PLAY_DMABUF1, fm801->play_start,4);
312 		} else
313 			fm801_wr(fm801, FM_PLAY_DMABUF2, fm801->play_nextblk,4);
314 		chn_intr(fm801->pch.channel);
315 	}
316 
317 	if(intsrc & FM_INTSTATUS_REC) {
318 		fm801->rec_flip++;
319 		if(fm801->rec_flip & 1) {
320 			fm801_wr(fm801, FM_REC_DMABUF1, fm801->rec_start,4);
321 		} else
322 			fm801_wr(fm801, FM_REC_DMABUF2, fm801->rec_nextblk,4);
323 		chn_intr(fm801->rch.channel);
324 	}
325 
326 	if ( intsrc & FM_INTSTATUS_MPU ) {
327 		/* This is a TODOish thing... */
328 		fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_MPU,2);
329 	}
330 
331 	if ( intsrc & FM_INTSTATUS_VOL ) {
332 		/* This is a TODOish thing... */
333 		fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_VOL,2);
334 	}
335 
336 	DPRINT("fm801_intr clear\n\n");
337 	fm801_wr(fm801, FM_INTSTATUS, intsrc & (FM_INTSTATUS_PLAY | FM_INTSTATUS_REC), 2);
338 }
339 
340 /*
341  *  Init routine is taken from an original NetBSD driver
342  */
343 static int
344 fm801_init(struct fm801_info *fm801)
345 {
346 	u_int32_t k1;
347 
348 	/* reset codec */
349 	fm801_wr(fm801, FM_CODEC_CTL, 0x0020,2);
350 	DELAY(100000);
351 	fm801_wr(fm801, FM_CODEC_CTL, 0x0000,2);
352 	DELAY(100000);
353 
354 	fm801_wr(fm801, FM_PCM_VOLUME, 0x0808,2);
355 	fm801_wr(fm801, FM_FM_VOLUME, 0x0808,2);
356 	fm801_wr(fm801, FM_I2S_VOLUME, 0x0808,2);
357 	fm801_wr(fm801, 0x40,0x107f,2);	/* enable legacy audio */
358 
359 	fm801_wr((void *)fm801, FM_RECORD_SOURCE, 0x0000,2);
360 
361 	/* Unmask playback, record and mpu interrupts, mask the rest */
362 	k1 = fm801_rd((void *)fm801, FM_INTMASK,2);
363 	fm801_wr(fm801, FM_INTMASK,
364 		(k1 & ~(FM_INTMASK_PLAY | FM_INTMASK_REC | FM_INTMASK_MPU)) |
365 		FM_INTMASK_VOL,2);
366 	fm801_wr(fm801, FM_INTSTATUS,
367 		FM_INTSTATUS_PLAY | FM_INTSTATUS_REC | FM_INTSTATUS_MPU |
368 		FM_INTSTATUS_VOL,2);
369 
370 	DPRINT("FM801 init Ok\n");
371 	return 0;
372 }
373 
374 static int
375 fm801_pci_attach(device_t dev)
376 {
377 	u_int32_t 		data;
378 	struct ac97_info 	*codec = 0;
379 	struct fm801_info 	*fm801;
380 	int 			i;
381 	int 			mapped = 0;
382 	char 			status[SND_STATUSLEN];
383 
384 	if ((fm801 = (struct fm801_info *)malloc(sizeof(*fm801),M_DEVBUF, M_NOWAIT)) == NULL) {
385 		device_printf(dev, "cannot allocate softc\n");
386 		return ENXIO;
387 	}
388 
389 	bzero(fm801, sizeof(*fm801));
390 	fm801->type = pci_get_devid(dev);
391 
392 	data = pci_read_config(dev, PCIR_COMMAND, 2);
393 	data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
394 	pci_write_config(dev, PCIR_COMMAND, data, 2);
395 	data = pci_read_config(dev, PCIR_COMMAND, 2);
396 
397 	for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) {
398 		fm801->regid = PCIR_MAPS + i*4;
399 		fm801->regtype = SYS_RES_MEMORY;
400 		fm801->reg = bus_alloc_resource(dev, fm801->regtype, &fm801->regid,
401 						0, ~0, 1, RF_ACTIVE);
402 		if(!fm801->reg)
403 		{
404 			fm801->regtype = SYS_RES_IOPORT;
405 			fm801->reg = bus_alloc_resource(dev, fm801->regtype, &fm801->regid,
406 						0, ~0, 1, RF_ACTIVE);
407 		}
408 
409 		if(fm801->reg) {
410 			fm801->st = rman_get_bustag(fm801->reg);
411 			fm801->sh = rman_get_bushandle(fm801->reg);
412 			mapped++;
413 		}
414 	}
415 
416 	if (mapped == 0) {
417 		device_printf(dev, "unable to map register space\n");
418 		goto oops;
419 	}
420 
421 	fm801_init(fm801);
422 
423 	codec = ac97_create(dev, (void *)fm801, NULL, fm801_rdcd, fm801_wrcd);
424 	if (codec == NULL) goto oops;
425 
426 	if (mixer_init(dev, &ac97_mixer, codec) == -1) goto oops;
427 
428 	fm801->irqid = 0;
429 	fm801->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &fm801->irqid,
430 				0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
431 	if (!fm801->irq ||
432 		bus_setup_intr(dev, fm801->irq, INTR_TYPE_TTY,
433 					fm801_intr, fm801, &fm801->ih)) {
434 		device_printf(dev, "unable to map interrupt\n");
435 		goto oops;
436 	}
437 
438 	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
439 		/*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
440 		/*highaddr*/BUS_SPACE_MAXADDR,
441 		/*filter*/NULL, /*filterarg*/NULL,
442 		/*maxsize*/FM801_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
443 		/*flags*/0, &fm801->parent_dmat) != 0) {
444 		device_printf(dev, "unable to create dma tag\n");
445 		goto oops;
446 	}
447 
448 	snprintf(status, 64, "at %s 0x%lx irq %ld",
449 		(fm801->regtype == SYS_RES_IOPORT)? "io" : "memory",
450 		rman_get_start(fm801->reg), rman_get_start(fm801->irq));
451 
452 #define FM801_MAXPLAYCH	1
453 	if (pcm_register(dev, fm801, FM801_MAXPLAYCH, 1)) goto oops;
454 	pcm_addchan(dev, PCMDIR_PLAY, &fm801_chantemplate, fm801);
455 	pcm_addchan(dev, PCMDIR_REC, &fm801_chantemplate, fm801);
456 	pcm_setstatus(dev, status);
457 
458 	return 0;
459 
460 oops:
461 	if (codec) ac97_destroy(codec);
462 	if (fm801->reg) bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg);
463 	if (fm801->ih) bus_teardown_intr(dev, fm801->irq, fm801->ih);
464 	if (fm801->irq) bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq);
465 	if (fm801->parent_dmat) bus_dma_tag_destroy(fm801->parent_dmat);
466 	free(fm801, M_DEVBUF);
467 	return ENXIO;
468 }
469 
470 static int
471 fm801_pci_detach(device_t dev)
472 {
473 	int r;
474 	struct fm801_info *fm801;
475 
476 	DPRINT("Forte Media FM801 detach\n");
477 
478 	r = pcm_unregister(dev);
479 	if (r)
480 		return r;
481 
482 	fm801 = pcm_getdevinfo(dev);
483 	bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg);
484 	bus_teardown_intr(dev, fm801->irq, fm801->ih);
485 	bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq);
486 	bus_dma_tag_destroy(fm801->parent_dmat);
487 	free(fm801, M_DEVBUF);
488 	return 0;
489 }
490 
491 static int
492 fm801_pci_probe( device_t dev )
493 {
494 	int id;
495 	if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA1 ) {
496 		device_set_desc(dev, "Forte Media FM801 Audio Controller");
497 		return 0;
498 	}
499 /*
500 	if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA2 ) {
501 		device_set_desc(dev, "Forte Media FM801 Joystick (Not Supported)");
502 		return ENXIO;
503 	}
504 */
505 	return ENXIO;
506 }
507 
508 
509 
510 /* channel interface */
511 static void *
512 fm801ch_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
513 {
514 	struct fm801_info *fm801 = (struct fm801_info *)devinfo;
515 	struct fm801_chinfo *ch = (dir == PCMDIR_PLAY)? &fm801->pch : &fm801->rch;
516 
517 	DPRINT("fm801ch_init, direction = %d\n", dir);
518 	ch->parent = fm801;
519 	ch->channel = c;
520 	ch->buffer = b;
521 	ch->buffer->bufsize = FM801_BUFFSIZE;
522 	ch->dir = dir;
523 	if( chn_allocbuf(ch->buffer, fm801->parent_dmat) == -1) return NULL;
524 	return (void *)ch;
525 }
526 
527 static int
528 fm801ch_setformat(void *data, u_int32_t format)
529 {
530 	struct fm801_chinfo *ch = data;
531 	struct fm801_info *fm801 = ch->parent;
532 
533 	DPRINT("fm801ch_setformat 0x%x : %s, %s, %s, %s\n", format,
534 		(format & AFMT_STEREO)?"stereo":"mono",
535 		(format & (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)) ? "16bit":"8bit",
536 		(format & AFMT_SIGNED)? "signed":"unsigned",
537 		(format & AFMT_BIGENDIAN)?"bigendiah":"littleendian" );
538 
539 	if(ch->dir == PCMDIR_PLAY) {
540 		fm801->play_fmt =  (format & AFMT_STEREO)? FM_PLAY_STEREO : 0;
541 		fm801->play_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0;
542 		return 0;
543 	}
544 
545 	if(ch->dir == PCMDIR_REC ) {
546 		fm801->rec_fmt = (format & AFMT_STEREO)? FM_REC_STEREO:0;
547 		fm801->rec_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0;
548 		return 0;
549 	}
550 
551 	return 0;
552 }
553 
554 struct {
555 	int limit;
556 	int rate;
557 } fm801_rates[11] = {
558 	{  6600,  5500 },
559 	{  8750,  8000 },
560 	{ 10250,  9600 },
561 	{ 13200, 11025 },
562 	{ 17500, 16000 },
563 	{ 20500, 19200 },
564 	{ 26500, 22050 },
565 	{ 35000, 32000 },
566 	{ 41000, 38400 },
567 	{ 46000, 44100 },
568 	{ 48000, 48000 },
569 /* anything above -> 48000 */
570 };
571 
572 static int
573 fm801ch_setspeed(void *data, u_int32_t speed)
574 {
575 	struct fm801_chinfo *ch = data;
576 	struct fm801_info *fm801 = ch->parent;
577 	register int i;
578 
579 
580 	for (i = 0; i < 10 && fm801_rates[i].limit <= speed; i++) ;
581 
582 	if(ch->dir == PCMDIR_PLAY) {
583 		fm801->pch.spd = fm801_rates[i].rate;
584 		fm801->play_shift = (i<<8);
585 		fm801->play_shift &= FM_PLAY_RATE_MASK;
586 	}
587 
588 	if(ch->dir == PCMDIR_REC ) {
589 		fm801->rch.spd = fm801_rates[i].rate;
590 		fm801->rec_shift = (i<<8);
591 		fm801->rec_shift &= FM_REC_RATE_MASK;
592 	}
593 
594 	ch->spd = fm801_rates[i].rate;
595 
596 	return fm801_rates[i].rate;
597 }
598 
599 static int
600 fm801ch_setblocksize(void *data, u_int32_t blocksize)
601 {
602 	struct fm801_chinfo *ch = data;
603 	struct fm801_info *fm801 = ch->parent;
604 
605 	if(ch->dir == PCMDIR_PLAY) {
606 		if(fm801->play_flip) return fm801->play_blksize;
607 		fm801->play_blksize = blocksize;
608 	}
609 
610 	if(ch->dir == PCMDIR_REC) {
611 		if(fm801->rec_flip) return fm801->rec_blksize;
612 		fm801->rec_blksize = blocksize;
613 	}
614 
615 	DPRINT("fm801ch_setblocksize %d (dir %d)\n",blocksize, ch->dir);
616 
617 	return blocksize;
618 }
619 
620 static int
621 fm801ch_trigger(void *data, int go)
622 {
623 	struct fm801_chinfo *ch = data;
624 	struct fm801_info *fm801 = ch->parent;
625 	u_int32_t baseaddr = vtophys(ch->buffer->buf);
626 	snd_dbuf *b = ch->buffer;
627 	u_int32_t k1;
628 
629 	DPRINT("fm801ch_trigger go %d , ", go);
630 	DPRINT("rp %d, rl %d, fp %d fl %d, dl %d, blksize=%d\n",
631 		b->rp,b->rl, b->fp,b->fl, b->dl, b->blksz);
632 
633 	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) {
634 		return 0;
635 	}
636 
637 	if (ch->dir == PCMDIR_PLAY) {
638 		if (go == PCMTRIG_START) {
639 
640 			fm801->play_start = baseaddr;
641 			fm801->play_nextblk = fm801->play_start + fm801->play_blksize;
642 			fm801->play_flip = 0;
643 			fm801_wr(fm801, FM_PLAY_DMALEN, fm801->play_blksize - 1, 2);
644 			fm801_wr(fm801, FM_PLAY_DMABUF1,fm801->play_start,4);
645 			fm801_wr(fm801, FM_PLAY_DMABUF2,fm801->play_nextblk,4);
646 			fm801_wr(fm801, FM_PLAY_CTL,
647 					FM_PLAY_START | FM_PLAY_STOPNOW | fm801->play_fmt | fm801->play_shift,
648 					2 );
649 			} else {
650 			fm801->play_flip = 0;
651 			k1 = fm801_rd(fm801, FM_PLAY_CTL,2);
652 			fm801_wr(fm801, FM_PLAY_CTL,
653 				(k1 & ~(FM_PLAY_STOPNOW | FM_PLAY_START)) |
654 				FM_PLAY_BUF1_LAST | FM_PLAY_BUF2_LAST, 2 );
655 		}
656 	} else if(ch->dir == PCMDIR_REC) {
657 		if (go == PCMTRIG_START) {
658 			fm801->rec_start = baseaddr;
659 			fm801->rec_nextblk = fm801->rec_start + fm801->rec_blksize;
660 			fm801->rec_flip = 0;
661 			fm801_wr(fm801, FM_REC_DMALEN, fm801->rec_blksize - 1, 2);
662 			fm801_wr(fm801, FM_REC_DMABUF1,fm801->rec_start,4);
663 			fm801_wr(fm801, FM_REC_DMABUF2,fm801->rec_nextblk,4);
664 			fm801_wr(fm801, FM_REC_CTL,
665 					FM_REC_START | FM_REC_STOPNOW | fm801->rec_fmt | fm801->rec_shift,
666 					2 );
667 			} else {
668 			fm801->rec_flip = 0;
669 			k1 = fm801_rd(fm801, FM_REC_CTL,2);
670 			fm801_wr(fm801, FM_REC_CTL,
671 				(k1 & ~(FM_REC_STOPNOW | FM_REC_START)) |
672 				FM_REC_BUF1_LAST | FM_REC_BUF2_LAST, 2);
673 		}
674 	}
675 
676 	return 0;
677 }
678 
679 /* Almost ALSA copy */
680 static int
681 fm801ch_getptr(void *data)
682 {
683 	struct fm801_chinfo *ch = data;
684 	struct fm801_info *fm801 = ch->parent;
685 	int result = 0;
686 	snd_dbuf *b = ch->buffer;
687 
688 	if (ch->dir == PCMDIR_PLAY) {
689 		result = fm801_rd(fm801,
690 			(fm801->play_flip&1) ?
691 			FM_PLAY_DMABUF2:FM_PLAY_DMABUF1, 4) - fm801->play_start;
692 	}
693 
694 	if (ch->dir == PCMDIR_REC) {
695 		result = fm801_rd(fm801,
696 			(fm801->rec_flip&1) ?
697 			FM_REC_DMABUF2:FM_REC_DMABUF1, 4) - fm801->rec_start;
698 	}
699 
700 	DPRINT("fm801ch_getptr:%d,  rp %d, rl %d, fp %d fl %d\n",
701 	                result, b->rp,b->rl, b->fp,b->fl);
702 
703 	return result;
704 }
705 
706 static pcmchan_caps *
707 fm801ch_getcaps(void *data)
708 {
709 	return &fm801ch_caps;
710 }
711 
712 static device_method_t fm801_methods[] = {
713 	/* Device interface */
714 	DEVMETHOD(device_probe,		fm801_pci_probe),
715 	DEVMETHOD(device_attach,	fm801_pci_attach),
716 	DEVMETHOD(device_detach,	fm801_pci_detach),
717 	{ 0, 0}
718 };
719 
720 static driver_t fm801_driver = {
721 	"pcm",
722 	fm801_methods,
723 	sizeof(snddev_info),
724 };
725 
726 static devclass_t pcm_devclass;
727 
728 DRIVER_MODULE(fm801, pci, fm801_driver, pcm_devclass, 0, 0);
729