fm801.c revision 65644
1284677Sdim/*
2284677Sdim * Copyright (c) 2000 Dmitry Dicky diwil@dataart.com
3284677Sdim * All rights reserved.
4284677Sdim *
5284677Sdim * Redistribution and use in source and binary forms, with or without
6284677Sdim * modification, are permitted provided that the following conditions
7284677Sdim * are met:
8284677Sdim * 1. Redistributions of source code must retain the above copyright
9284677Sdim *    notice, this list of conditions and the following disclaimer.
10284677Sdim * 2. Redistributions in binary form must reproduce the above copyright
11284677Sdim *    notice, this list of conditions and the following disclaimer in the
12284677Sdim *    documentation and/or other materials provided with the distribution.
13284677Sdim *
14284677Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS `AS IS'' AND
15284677Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16284677Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17284677Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18284677Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19284677Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20284677Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21284677Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22284677Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23284677Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24284677Sdim * SUCH DAMAGE.
25284677Sdim *
26284677Sdim * $FreeBSD: head/sys/dev/sound/pci/fm801.c 65644 2000-09-09 19:21:04Z cg $
27284677Sdim */
28284677Sdim
29284677Sdim#include <dev/sound/pcm/sound.h>
30284677Sdim#include <dev/sound/pcm/ac97.h>
31284677Sdim#include <pci/pcireg.h>
32284677Sdim#include <pci/pcivar.h>
33284677Sdim
34284677Sdim#define PCI_VENDOR_FORTEMEDIA	0x1319
35284677Sdim#define PCI_DEVICE_FORTEMEDIA1	0x08011319
36284677Sdim#define PCI_DEVICE_FORTEMEDIA2	0x08021319	/* ??? have no idea what's this... */
37284677Sdim
38284677Sdim#define FM_PCM_VOLUME           0x00
39284677Sdim#define FM_FM_VOLUME            0x02
40286684Sdim#define FM_I2S_VOLUME           0x04
41286684Sdim#define FM_RECORD_SOURCE        0x06
42286684Sdim
43284677Sdim#define FM_PLAY_CTL             0x08
44284677Sdim#define  FM_PLAY_RATE_MASK              0x0f00
45284677Sdim#define  FM_PLAY_BUF1_LAST              0x0001
46284677Sdim#define  FM_PLAY_BUF2_LAST              0x0002
47284677Sdim#define  FM_PLAY_START                  0x0020
48284677Sdim#define  FM_PLAY_PAUSE                  0x0040
49284677Sdim#define  FM_PLAY_STOPNOW                0x0080
50284677Sdim#define  FM_PLAY_16BIT                  0x4000
51284677Sdim#define  FM_PLAY_STEREO                 0x8000
52284677Sdim
53284677Sdim#define FM_PLAY_DMALEN          0x0a
54284677Sdim#define FM_PLAY_DMABUF1         0x0c
55284677Sdim#define FM_PLAY_DMABUF2         0x10
56284677Sdim
57284677Sdim
58284677Sdim#define FM_REC_CTL              0x14
59284677Sdim#define  FM_REC_RATE_MASK               0x0f00
60284677Sdim#define  FM_REC_BUF1_LAST               0x0001
61284677Sdim#define  FM_REC_BUF2_LAST               0x0002
62284677Sdim#define  FM_REC_START                   0x0020
63296417Sdim#define  FM_REC_PAUSE                   0x0040
64296417Sdim#define  FM_REC_STOPNOW                 0x0080
65296417Sdim#define  FM_REC_16BIT                   0x4000
66296417Sdim#define  FM_REC_STEREO                  0x8000
67296417Sdim
68284677Sdim
69284677Sdim#define FM_REC_DMALEN           0x16
70284677Sdim#define FM_REC_DMABUF1          0x18
71284677Sdim#define FM_REC_DMABUF2          0x1c
72284677Sdim
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_setformat(void *data, u_int32_t format);
108static int fm801ch_setspeed(void *data, u_int32_t speed);
109static int fm801ch_setblocksize(void *data, u_int32_t blocksize);
110static int fm801ch_trigger(void *data, int go);
111static int fm801ch_getptr(void *data);
112static pcmchan_caps *fm801ch_getcaps(void *data);
113/*
114static int fm801ch_setup(pcm_channel *c);
115*/
116
117static 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
125static pcmchan_caps fm801ch_caps = {
126	4000, 48000,
127	fmts, 0
128};
129
130static 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
149struct fm801_info;
150
151struct 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
159struct 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 */
193static u_int32_t
194fm801_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
208static void
209fm801_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
227static u_int32_t
228fm801_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
257static void
258fm801_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 */
296static void
297fm801_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 */
343static int
344fm801_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
374static int
375fm801_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
460oops:
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
470static int
471fm801_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
491static int
492fm801_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 */
511static void *
512fm801ch_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
527static int
528fm801ch_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
554struct {
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
572static int
573fm801ch_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
599static int
600fm801ch_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
620static int
621fm801ch_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 */
680static int
681fm801ch_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
706static pcmchan_caps *
707fm801ch_getcaps(void *data)
708{
709	return &fm801ch_caps;
710}
711
712static 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
720static driver_t fm801_driver = {
721	"pcm",
722	fm801_methods,
723	sizeof(snddev_info),
724};
725
726static devclass_t pcm_devclass;
727
728DRIVER_MODULE(fm801, pci, fm801_driver, pcm_devclass, 0, 0);
729