Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
8019adb6
Commit
8019adb6
authored
Jul 11, 2003
by
Alan Cox
Committed by
Steve French
Jul 11, 2003
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[PATCH] Add Forte Media OSS driver
parent
f6538692
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
2147 additions
and
0 deletions
+2147
-0
sound/oss/forte.c
sound/oss/forte.c
+2147
-0
No files found.
sound/oss/forte.c
0 → 100644
View file @
8019adb6
/*
* forte.c - ForteMedia FM801 OSS Driver
*
* Written by Martin K. Petersen <mkp@mkp.net>
* Copyright (C) 2002 Hewlett-Packard Company
* Portions Copyright (C) 2003 Martin K. Petersen
*
* Latest version: http://mkp.net/forte/
*
* Based upon the ALSA FM801 driver by Jaroslav Kysela and OSS drivers
* by Thomas Sailer, Alan Cox, Zach Brown, and Jeff Garzik. Thanks
* guys!
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/kernel.h>
#include <linux/sound.h>
#include <linux/ac97_codec.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <asm/hardirq.h>
#include <asm/io.h>
#define DRIVER_NAME "forte"
#define DRIVER_VERSION "$Id: forte.c,v 1.63 2003/03/01 05:32:42 mkp Exp $"
#define PFX DRIVER_NAME ": "
#undef M_DEBUG
#ifdef M_DEBUG
#define DPRINTK(args...) printk(KERN_WARNING args)
#else
#define DPRINTK(args...)
#endif
/* Card capabilities */
#define FORTE_CAPS (DSP_CAP_MMAP | DSP_CAP_TRIGGER)
/* Supported audio formats */
#define FORTE_FMTS (AFMT_U8 | AFMT_S16_LE)
/* Buffers */
#define FORTE_MIN_FRAG_SIZE 256
#define FORTE_MAX_FRAG_SIZE PAGE_SIZE
#define FORTE_DEF_FRAG_SIZE 256
#define FORTE_MIN_FRAGMENTS 2
#define FORTE_MAX_FRAGMENTS 256
#define FORTE_DEF_FRAGMENTS 2
#define FORTE_MIN_BUF_MSECS 500
#define FORTE_MAX_BUF_MSECS 1000
/* PCI BARs */
#define FORTE_PCM_VOL 0x00
/* PCM Output Volume */
#define FORTE_FM_VOL 0x02
/* FM Output Volume */
#define FORTE_I2S_VOL 0x04
/* I2S Volume */
#define FORTE_REC_SRC 0x06
/* Record Source */
#define FORTE_PLY_CTRL 0x08
/* Playback Control */
#define FORTE_PLY_COUNT 0x0a
/* Playback Count */
#define FORTE_PLY_BUF1 0x0c
/* Playback Buffer I */
#define FORTE_PLY_BUF2 0x10
/* Playback Buffer II */
#define FORTE_CAP_CTRL 0x14
/* Capture Control */
#define FORTE_CAP_COUNT 0x16
/* Capture Count */
#define FORTE_CAP_BUF1 0x18
/* Capture Buffer I */
#define FORTE_CAP_BUF2 0x1c
/* Capture Buffer II */
#define FORTE_CODEC_CTRL 0x22
/* Codec Control */
#define FORTE_I2S_MODE 0x24
/* I2S Mode Control */
#define FORTE_VOLUME 0x26
/* Volume Up/Down/Mute Status */
#define FORTE_I2C_CTRL 0x29
/* I2C Control */
#define FORTE_AC97_CMD 0x2a
/* AC'97 Command */
#define FORTE_AC97_DATA 0x2c
/* AC'97 Data */
#define FORTE_MPU401_DATA 0x30
/* MPU401 Data */
#define FORTE_MPU401_CMD 0x31
/* MPU401 Command */
#define FORTE_GPIO_CTRL 0x52
/* General Purpose I/O Control */
#define FORTE_GEN_CTRL 0x54
/* General Control */
#define FORTE_IRQ_MASK 0x56
/* Interrupt Mask */
#define FORTE_IRQ_STATUS 0x5a
/* Interrupt Status */
#define FORTE_OPL3_BANK0 0x68
/* OPL3 Status Read / Bank 0 Write */
#define FORTE_OPL3_DATA0 0x69
/* OPL3 Data 0 Write */
#define FORTE_OPL3_BANK1 0x6a
/* OPL3 Bank 1 Write */
#define FORTE_OPL3_DATA1 0x6b
/* OPL3 Bank 1 Write */
#define FORTE_POWERDOWN 0x70
/* Blocks Power Down Control */
#define FORTE_CAP_OFFSET FORTE_CAP_CTRL - FORTE_PLY_CTRL
#define FORTE_AC97_ADDR_SHIFT 10
/* Playback and record control register bits */
#define FORTE_BUF1_LAST (1<<1)
#define FORTE_BUF2_LAST (1<<2)
#define FORTE_START (1<<5)
#define FORTE_PAUSE (1<<6)
#define FORTE_IMMED_STOP (1<<7)
#define FORTE_RATE_SHIFT 8
#define FORTE_RATE_MASK (15 << FORTE_RATE_SHIFT)
#define FORTE_CHANNELS_4 (1<<12)
/* Playback only */
#define FORTE_CHANNELS_6 (2<<12)
/* Playback only */
#define FORTE_CHANNELS_6MS (3<<12)
/* Playback only */
#define FORTE_CHANNELS_MASK (3<<12)
#define FORTE_16BIT (1<<14)
#define FORTE_STEREO (1<<15)
/* IRQ status bits */
#define FORTE_IRQ_PLAYBACK (1<<8)
#define FORTE_IRQ_CAPTURE (1<<9)
#define FORTE_IRQ_VOLUME (1<<14)
#define FORTE_IRQ_MPU (1<<15)
/* CODEC control */
#define FORTE_CC_CODEC_RESET (1<<5)
#define FORTE_CC_AC97_RESET (1<<6)
/* AC97 cmd */
#define FORTE_AC97_WRITE (0<<7)
#define FORTE_AC97_READ (1<<7)
#define FORTE_AC97_DP_INVALID (0<<8)
#define FORTE_AC97_DP_VALID (1<<8)
#define FORTE_AC97_PORT_RDY (0<<9)
#define FORTE_AC97_PORT_BSY (1<<9)
struct
forte_channel
{
const
char
*
name
;
unsigned
short
ctrl
;
/* Ctrl BAR contents */
unsigned
long
iobase
;
/* Ctrl BAR address */
wait_queue_head_t
wait
;
void
*
buf
;
/* Buffer */
dma_addr_t
buf_handle
;
/* Buffer handle */
unsigned
int
record
;
unsigned
int
format
;
unsigned
int
rate
;
unsigned
int
stereo
;
unsigned
int
frag_sz
;
/* Current fragment size */
unsigned
int
frag_num
;
/* Current # of fragments */
unsigned
int
frag_msecs
;
/* Milliseconds per frag */
unsigned
int
buf_sz
;
/* Current buffer size */
unsigned
int
hwptr
;
/* Tail */
unsigned
int
swptr
;
/* Head */
unsigned
int
filled_frags
;
/* Fragments currently full */
unsigned
int
next_buf
;
/* Index of next buffer */
unsigned
int
active
;
/* Channel currently in use */
unsigned
int
mapped
;
/* mmap */
unsigned
int
buf_pages
;
/* Real size of buffer */
unsigned
int
nr_irqs
;
/* Number of interrupts */
unsigned
int
bytes
;
/* Total bytes */
unsigned
int
residue
;
/* Partial fragment */
};
struct
forte_chip
{
struct
pci_dev
*
pci_dev
;
unsigned
long
iobase
;
int
irq
;
struct
semaphore
open_sem
;
/* Device access */
spinlock_t
lock
;
/* State */
spinlock_t
ac97_lock
;
struct
ac97_codec
*
ac97
;
int
multichannel
;
int
dsp
;
/* OSS handle */
int
trigger
;
/* mmap I/O trigger */
struct
forte_channel
play
;
struct
forte_channel
rec
;
};
static
int
channels
[]
=
{
2
,
4
,
6
,
};
static
int
rates
[]
=
{
5500
,
8000
,
9600
,
11025
,
16000
,
19200
,
22050
,
32000
,
38400
,
44100
,
48000
,
};
static
struct
forte_chip
*
forte
;
static
int
found
;
/* AC97 Codec -------------------------------------------------------------- */
/**
* forte_ac97_wait:
* @chip: fm801 instance whose AC97 codec to wait on
*
* FIXME:
* Stop busy-waiting
*/
static
inline
int
forte_ac97_wait
(
struct
forte_chip
*
chip
)
{
int
i
=
10000
;
while
(
(
inw
(
chip
->
iobase
+
FORTE_AC97_CMD
)
&
FORTE_AC97_PORT_BSY
)
&&
i
--
)
cpu_relax
();
return
i
==
0
;
}
/**
* forte_ac97_read:
* @codec: AC97 codec to read from
* @reg: register to read
*/
u16
forte_ac97_read
(
struct
ac97_codec
*
codec
,
u8
reg
)
{
u16
ret
=
0
;
struct
forte_chip
*
chip
=
codec
->
private_data
;
spin_lock
(
&
chip
->
ac97_lock
);
/* Knock, knock */
if
(
forte_ac97_wait
(
chip
))
{
printk
(
KERN_ERR
PFX
"ac97_read: Serial bus busy
\n
"
);
goto
out
;
}
/* Send read command */
outw
(
reg
|
(
1
<<
7
),
chip
->
iobase
+
FORTE_AC97_CMD
);
if
(
forte_ac97_wait
(
chip
))
{
printk
(
KERN_ERR
PFX
"ac97_read: Bus busy reading reg 0x%x
\n
"
,
reg
);
goto
out
;
}
/* Sanity checking */
if
(
inw
(
chip
->
iobase
+
FORTE_AC97_CMD
)
&
FORTE_AC97_DP_INVALID
)
{
printk
(
KERN_ERR
PFX
"ac97_read: Invalid data port"
);
goto
out
;
}
/* Fetch result */
ret
=
inw
(
chip
->
iobase
+
FORTE_AC97_DATA
);
out:
spin_unlock
(
&
chip
->
ac97_lock
);
return
ret
;
}
/**
* forte_ac97_write:
* @codec: AC97 codec to send command to
* @reg: register to write
* @val: value to write
*/
void
forte_ac97_write
(
struct
ac97_codec
*
codec
,
u8
reg
,
u16
val
)
{
struct
forte_chip
*
chip
=
codec
->
private_data
;
spin_lock
(
&
chip
->
ac97_lock
);
/* Knock, knock */
if
(
forte_ac97_wait
(
chip
))
{
printk
(
KERN_ERR
PFX
"ac97_write: Serial bus busy
\n
"
);
goto
out
;
}
outw
(
val
,
chip
->
iobase
+
FORTE_AC97_DATA
);
outb
(
reg
|
FORTE_AC97_WRITE
,
chip
->
iobase
+
FORTE_AC97_CMD
);
/* Wait for completion */
if
(
forte_ac97_wait
(
chip
))
{
printk
(
KERN_ERR
PFX
"ac97_write: Bus busy after write
\n
"
);
goto
out
;
}
out:
spin_unlock
(
&
chip
->
ac97_lock
);
}
/* Mixer ------------------------------------------------------------------- */
/**
* forte_mixer_open:
* @inode:
* @file:
*/
static
int
forte_mixer_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
struct
forte_chip
*
chip
=
forte
;
file
->
private_data
=
chip
->
ac97
;
return
0
;
}
/**
* forte_mixer_release:
* @inode:
* @file:
*/
static
int
forte_mixer_release
(
struct
inode
*
inode
,
struct
file
*
file
)
{
/* We will welease Wodewick */
return
0
;
}
/**
* forte_mixer_ioctl:
* @inode:
* @file:
*/
static
int
forte_mixer_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
struct
ac97_codec
*
codec
=
(
struct
ac97_codec
*
)
file
->
private_data
;
return
codec
->
mixer_ioctl
(
codec
,
cmd
,
arg
);
}
static
struct
file_operations
forte_mixer_fops
=
{
owner:
THIS_MODULE
,
llseek:
no_llseek
,
ioctl:
forte_mixer_ioctl
,
open:
forte_mixer_open
,
release:
forte_mixer_release
,
};
/* Channel ----------------------------------------------------------------- */
/**
* forte_channel_reset:
* @channel: Channel to reset
*
* Locking: Must be called with lock held.
*/
static
void
forte_channel_reset
(
struct
forte_channel
*
channel
)
{
if
(
!
channel
||
!
channel
->
iobase
)
return
;
DPRINTK
(
"%s: channel = %s
\n
"
,
__FUNCTION__
,
channel
->
name
);
channel
->
ctrl
&=
~
FORTE_START
;
outw
(
channel
->
ctrl
,
channel
->
iobase
+
FORTE_PLY_CTRL
);
/* We always play at least two fragments, hence these defaults */
channel
->
hwptr
=
channel
->
frag_sz
;
channel
->
next_buf
=
1
;
channel
->
swptr
=
0
;
channel
->
filled_frags
=
0
;
channel
->
active
=
0
;
channel
->
bytes
=
0
;
channel
->
nr_irqs
=
0
;
channel
->
mapped
=
0
;
channel
->
residue
=
0
;
}
/**
* forte_channel_start:
* @channel: Channel to start (record/playback)
*
* Locking: Must be called with lock held.
*/
static
void
inline
forte_channel_start
(
struct
forte_channel
*
channel
)
{
if
(
!
channel
||
!
channel
->
iobase
||
channel
->
active
)
return
;
channel
->
ctrl
&=
~
(
FORTE_PAUSE
|
FORTE_BUF1_LAST
|
FORTE_BUF2_LAST
|
FORTE_IMMED_STOP
);
channel
->
ctrl
|=
FORTE_START
;
channel
->
active
=
1
;
outw
(
channel
->
ctrl
,
channel
->
iobase
+
FORTE_PLY_CTRL
);
}
/**
* forte_channel_stop:
* @channel: Channel to stop
*
* Locking: Must be called with lock held.
*/
static
void
inline
forte_channel_stop
(
struct
forte_channel
*
channel
)
{
if
(
!
channel
||
!
channel
->
iobase
)
return
;
channel
->
ctrl
&=
~
(
FORTE_START
|
FORTE_PAUSE
);
channel
->
ctrl
|=
FORTE_IMMED_STOP
;
channel
->
active
=
0
;
outw
(
channel
->
ctrl
,
channel
->
iobase
+
FORTE_PLY_CTRL
);
}
/**
* forte_channel_pause:
* @channel: Channel to pause
*
* Locking: Must be called with lock held.
*/
static
void
inline
forte_channel_pause
(
struct
forte_channel
*
channel
)
{
if
(
!
channel
||
!
channel
->
iobase
)
return
;
channel
->
ctrl
|=
FORTE_PAUSE
;
channel
->
active
=
0
;
outw
(
channel
->
ctrl
,
channel
->
iobase
+
FORTE_PLY_CTRL
);
}
/**
* forte_channel_rate:
* @channel: Channel whose rate to set. Playback and record are
* independent.
* @rate: Channel rate in Hz
*
* Locking: Must be called with lock held.
*/
static
int
forte_channel_rate
(
struct
forte_channel
*
channel
,
unsigned
int
rate
)
{
int
new_rate
;
if
(
!
channel
||
!
channel
->
iobase
)
return
-
EINVAL
;
/* The FM801 only supports a handful of fixed frequencies.
* We find the value closest to what userland requested.
*/
if
(
rate
<=
6250
)
{
rate
=
5500
;
new_rate
=
0
;
}
else
if
(
rate
<=
8800
)
{
rate
=
8000
;
new_rate
=
1
;
}
else
if
(
rate
<=
10312
)
{
rate
=
9600
;
new_rate
=
2
;
}
else
if
(
rate
<=
13512
)
{
rate
=
11025
;
new_rate
=
3
;
}
else
if
(
rate
<=
17600
)
{
rate
=
16000
;
new_rate
=
4
;
}
else
if
(
rate
<=
20625
)
{
rate
=
19200
;
new_rate
=
5
;
}
else
if
(
rate
<=
27025
)
{
rate
=
22050
;
new_rate
=
6
;
}
else
if
(
rate
<=
35200
)
{
rate
=
32000
;
new_rate
=
7
;
}
else
if
(
rate
<=
41250
)
{
rate
=
38400
;
new_rate
=
8
;
}
else
if
(
rate
<=
46050
)
{
rate
=
44100
;
new_rate
=
9
;
}
else
{
rate
=
48000
;
new_rate
=
10
;
}
channel
->
ctrl
&=
~
FORTE_RATE_MASK
;
channel
->
ctrl
|=
new_rate
<<
FORTE_RATE_SHIFT
;
channel
->
rate
=
rate
;
DPRINTK
(
"%s: %s rate = %d
\n
"
,
__FUNCTION__
,
channel
->
name
,
rate
);
return
rate
;
}
/**
* forte_channel_format:
* @channel: Channel whose audio format to set
* @format: OSS format ID
*
* Locking: Must be called with lock held.
*/
static
int
forte_channel_format
(
struct
forte_channel
*
channel
,
int
format
)
{
if
(
!
channel
||
!
channel
->
iobase
)
return
-
EINVAL
;
switch
(
format
)
{
case
AFMT_QUERY
:
break
;
case
AFMT_U8
:
channel
->
ctrl
&=
~
FORTE_16BIT
;
channel
->
format
=
AFMT_U8
;
break
;
case
AFMT_S16_LE
:
default:
channel
->
ctrl
|=
FORTE_16BIT
;
channel
->
format
=
AFMT_S16_LE
;
break
;
}
DPRINTK
(
"%s: %s want %d format, got %d
\n
"
,
__FUNCTION__
,
channel
->
name
,
format
,
channel
->
format
);
return
channel
->
format
;
}
/**
* forte_channel_stereo:
* @channel: Channel to toggle
* @stereo: 0 for Mono, 1 for Stereo
*
* Locking: Must be called with lock held.
*/
static
int
forte_channel_stereo
(
struct
forte_channel
*
channel
,
unsigned
int
stereo
)
{
int
ret
;
if
(
!
channel
||
!
channel
->
iobase
)
return
-
EINVAL
;
DPRINTK
(
"%s: %s stereo = %d
\n
"
,
__FUNCTION__
,
channel
->
name
,
stereo
);
switch
(
stereo
)
{
case
0
:
channel
->
ctrl
&=
~
(
FORTE_STEREO
|
FORTE_CHANNELS_MASK
);
channel
->
stereo
=
stereo
;
ret
=
stereo
;
break
;
case
1
:
channel
->
ctrl
&=
~
FORTE_CHANNELS_MASK
;
channel
->
ctrl
|=
FORTE_STEREO
;
channel
->
stereo
=
stereo
;
ret
=
stereo
;
break
;
default:
DPRINTK
(
"Unsupported channel format"
);
ret
=
-
EINVAL
;
break
;
}
return
ret
;
}
/**
* forte_channel_buffer:
* @channel: Channel whose buffer to set up
*
* Locking: Must be called with lock held.
*/
static
void
forte_channel_buffer
(
struct
forte_channel
*
channel
,
int
sz
,
int
num
)
{
unsigned
int
msecs
,
shift
;
/* Go away, I'm busy */
if
(
channel
->
filled_frags
||
channel
->
bytes
)
return
;
/* Fragment size must be a power of 2 */
shift
=
0
;
sz
++
;
while
(
sz
>>=
1
)
shift
++
;
channel
->
frag_sz
=
1
<<
shift
;
/* Round fragment size to something reasonable */
if
(
channel
->
frag_sz
<
FORTE_MIN_FRAG_SIZE
)
channel
->
frag_sz
=
FORTE_MIN_FRAG_SIZE
;
if
(
channel
->
frag_sz
>
FORTE_MAX_FRAG_SIZE
)
channel
->
frag_sz
=
FORTE_MAX_FRAG_SIZE
;
/* Find fragment length in milliseconds */
msecs
=
channel
->
frag_sz
/
(
channel
->
format
==
AFMT_S16_LE
?
2
:
1
)
/
(
channel
->
stereo
?
2
:
1
)
/
(
channel
->
rate
/
1000
);
channel
->
frag_msecs
=
msecs
;
/* Pick a suitable number of fragments */
if
(
msecs
*
num
<
FORTE_MIN_BUF_MSECS
)
num
=
FORTE_MIN_BUF_MSECS
/
msecs
;
if
(
msecs
*
num
>
FORTE_MAX_BUF_MSECS
)
num
=
FORTE_MAX_BUF_MSECS
/
msecs
;
/* Fragment number must be a power of 2 */
shift
=
0
;
while
(
num
>>=
1
)
shift
++
;
channel
->
frag_num
=
1
<<
(
shift
+
1
);
/* Round fragment number to something reasonable */
if
(
channel
->
frag_num
<
FORTE_MIN_FRAGMENTS
)
channel
->
frag_num
=
FORTE_MIN_FRAGMENTS
;
if
(
channel
->
frag_num
>
FORTE_MAX_FRAGMENTS
)
channel
->
frag_num
=
FORTE_MAX_FRAGMENTS
;
channel
->
buf_sz
=
channel
->
frag_sz
*
channel
->
frag_num
;
DPRINTK
(
"%s: %s frag_sz = %d, frag_num = %d, buf_sz = %d
\n
"
,
__FUNCTION__
,
channel
->
name
,
channel
->
frag_sz
,
channel
->
frag_num
,
channel
->
buf_sz
);
}
/**
* forte_channel_prep:
* @channel: Channel whose buffer to prepare
*
* Locking: Lock held.
*/
static
void
forte_channel_prep
(
struct
forte_channel
*
channel
)
{
struct
page
*
page
;
int
i
;
if
(
channel
->
buf
)
return
;
forte_channel_buffer
(
channel
,
channel
->
frag_sz
,
channel
->
frag_num
);
channel
->
buf_pages
=
channel
->
buf_sz
>>
PAGE_SHIFT
;
if
(
channel
->
buf_sz
%
PAGE_SIZE
)
channel
->
buf_pages
++
;
DPRINTK
(
"%s: %s frag_sz = %d, frag_num = %d, buf_sz = %d, pg = %d
\n
"
,
__FUNCTION__
,
channel
->
name
,
channel
->
frag_sz
,
channel
->
frag_num
,
channel
->
buf_sz
,
channel
->
buf_pages
);
/* DMA buffer */
channel
->
buf
=
pci_alloc_consistent
(
forte
->
pci_dev
,
channel
->
buf_pages
*
PAGE_SIZE
,
&
channel
->
buf_handle
);
if
(
!
channel
->
buf
||
!
channel
->
buf_handle
)
BUG
();
page
=
virt_to_page
(
channel
->
buf
);
/* FIXME: can this go away ? */
for
(
i
=
0
;
i
<
channel
->
buf_pages
;
i
++
)
SetPageReserved
(
page
++
);
/* Prep buffer registers */
outw
(
channel
->
frag_sz
-
1
,
channel
->
iobase
+
FORTE_PLY_COUNT
);
outl
(
channel
->
buf_handle
,
channel
->
iobase
+
FORTE_PLY_BUF1
);
outl
(
channel
->
buf_handle
+
channel
->
frag_sz
,
channel
->
iobase
+
FORTE_PLY_BUF2
);
/* Reset hwptr */
channel
->
hwptr
=
channel
->
frag_sz
;
channel
->
next_buf
=
1
;
DPRINTK
(
"%s: %s buffer @ %p (%p)
\n
"
,
__FUNCTION__
,
channel
->
name
,
channel
->
buf
,
channel
->
buf_handle
);
}
/**
* forte_channel_drain:
* @chip:
* @channel:
*
* Locking: Don't hold the lock.
*/
static
inline
int
forte_channel_drain
(
struct
forte_channel
*
channel
)
{
DECLARE_WAITQUEUE
(
wait
,
current
);
unsigned
long
flags
;
DPRINTK
(
"%s
\n
"
,
__FUNCTION__
);
if
(
channel
->
mapped
)
{
spin_lock_irqsave
(
&
forte
->
lock
,
flags
);
forte_channel_stop
(
channel
);
spin_unlock_irqrestore
(
&
forte
->
lock
,
flags
);
return
0
;
}
spin_lock_irqsave
(
&
forte
->
lock
,
flags
);
add_wait_queue
(
&
channel
->
wait
,
&
wait
);
for
(;;)
{
if
(
channel
->
active
==
0
||
channel
->
filled_frags
==
1
)
break
;
spin_unlock_irqrestore
(
&
forte
->
lock
,
flags
);
__set_current_state
(
TASK_INTERRUPTIBLE
);
schedule
();
spin_lock_irqsave
(
&
forte
->
lock
,
flags
);
}
forte_channel_stop
(
channel
);
forte_channel_reset
(
channel
);
set_current_state
(
TASK_RUNNING
);
remove_wait_queue
(
&
channel
->
wait
,
&
wait
);
spin_unlock_irqrestore
(
&
forte
->
lock
,
flags
);
return
0
;
}
/**
* forte_channel_init:
* @chip: Forte chip instance the channel hangs off
* @channel: Channel to initialize
*
* Description:
* Initializes a channel, sets defaults, and allocates
* buffers.
*
* Locking: No lock held.
*/
static
int
forte_channel_init
(
struct
forte_chip
*
chip
,
struct
forte_channel
*
channel
)
{
DPRINTK
(
"%s: chip iobase @ %p
\n
"
,
__FUNCTION__
,
(
void
*
)
chip
->
iobase
);
spin_lock_irq
(
&
chip
->
lock
);
memset
(
channel
,
0x0
,
sizeof
(
*
channel
));
if
(
channel
==
&
chip
->
play
)
{
channel
->
name
=
"PCM_OUT"
;
channel
->
iobase
=
chip
->
iobase
;
DPRINTK
(
"%s: PCM-OUT iobase @ %p
\n
"
,
__FUNCTION__
,
(
void
*
)
channel
->
iobase
);
}
else
if
(
channel
==
&
chip
->
rec
)
{
channel
->
name
=
"PCM_IN"
;
channel
->
iobase
=
chip
->
iobase
+
FORTE_CAP_OFFSET
;
channel
->
record
=
1
;
DPRINTK
(
"%s: PCM-IN iobase @ %p
\n
"
,
__FUNCTION__
,
(
void
*
)
channel
->
iobase
);
}
else
BUG
();
init_waitqueue_head
(
&
channel
->
wait
);
/* Defaults: 48kHz, 16-bit, stereo */
channel
->
ctrl
=
inw
(
channel
->
iobase
+
FORTE_PLY_CTRL
);
forte_channel_reset
(
channel
);
forte_channel_stereo
(
channel
,
1
);
forte_channel_format
(
channel
,
AFMT_S16_LE
);
forte_channel_rate
(
channel
,
48000
);
channel
->
frag_sz
=
FORTE_DEF_FRAG_SIZE
;
channel
->
frag_num
=
FORTE_DEF_FRAGMENTS
;
chip
->
trigger
=
0
;
spin_unlock_irq
(
&
chip
->
lock
);
return
0
;
}
/**
* forte_channel_free:
* @chip: Chip this channel hangs off
* @channel: Channel to nuke
*
* Description:
* Resets channel and frees buffers.
*
* Locking: Hold your horses.
*/
static
void
forte_channel_free
(
struct
forte_chip
*
chip
,
struct
forte_channel
*
channel
)
{
DPRINTK
(
"%s: %s
\n
"
,
__FUNCTION__
,
channel
->
name
);
if
(
!
channel
->
buf_handle
)
return
;
pci_free_consistent
(
chip
->
pci_dev
,
channel
->
buf_pages
*
PAGE_SIZE
,
channel
->
buf
,
channel
->
buf_handle
);
memset
(
channel
,
0x0
,
sizeof
(
*
channel
));
}
/* DSP --------------------------------------------------------------------- */
/**
* forte_dsp_ioctl:
*/
static
int
forte_dsp_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
int
ival
=
0
,
ret
,
rval
=
0
,
rd
,
wr
,
count
;
struct
forte_chip
*
chip
;
struct
audio_buf_info
abi
;
struct
count_info
cinfo
;
chip
=
file
->
private_data
;
if
(
file
->
f_mode
&
FMODE_WRITE
)
wr
=
1
;
else
wr
=
0
;
if
(
file
->
f_mode
&
FMODE_READ
)
rd
=
1
;
else
rd
=
0
;
switch
(
cmd
)
{
case
OSS_GETVERSION
:
return
put_user
(
SOUND_VERSION
,
(
int
*
)
arg
);
case
SNDCTL_DSP_GETCAPS
:
DPRINTK
(
"%s: GETCAPS
\n
"
,
__FUNCTION__
);
ival
=
FORTE_CAPS
;
/* DUPLEX */
return
put_user
(
ival
,
(
int
*
)
arg
);
case
SNDCTL_DSP_GETFMTS
:
DPRINTK
(
"%s: GETFMTS
\n
"
,
__FUNCTION__
);
ival
=
FORTE_FMTS
;
/* U8, 16LE */
return
put_user
(
ival
,
(
int
*
)
arg
);
case
SNDCTL_DSP_SETFMT
:
/* U8, 16LE */
DPRINTK
(
"%s: SETFMT
\n
"
,
__FUNCTION__
);
if
(
get_user
(
ival
,
(
int
*
)
arg
))
return
-
EFAULT
;
spin_lock_irq
(
&
chip
->
lock
);
if
(
rd
)
{
forte_channel_stop
(
&
chip
->
rec
);
rval
=
forte_channel_format
(
&
chip
->
rec
,
ival
);
}
if
(
wr
)
{
forte_channel_stop
(
&
chip
->
rec
);
rval
=
forte_channel_format
(
&
chip
->
play
,
ival
);
}
spin_unlock_irq
(
&
chip
->
lock
);
return
put_user
(
rval
,
(
int
*
)
arg
);
case
SNDCTL_DSP_STEREO
:
/* 0 - mono, 1 - stereo */
DPRINTK
(
"%s: STEREO
\n
"
,
__FUNCTION__
);
if
(
get_user
(
ival
,
(
int
*
)
arg
))
return
-
EFAULT
;
spin_lock_irq
(
&
chip
->
lock
);
if
(
rd
)
{
forte_channel_stop
(
&
chip
->
rec
);
rval
=
forte_channel_stereo
(
&
chip
->
rec
,
ival
);
}
if
(
wr
)
{
forte_channel_stop
(
&
chip
->
rec
);
rval
=
forte_channel_stereo
(
&
chip
->
play
,
ival
);
}
spin_unlock_irq
(
&
chip
->
lock
);
return
put_user
(
rval
,
(
int
*
)
arg
);
case
SNDCTL_DSP_CHANNELS
:
/* 1 - mono, 2 - stereo */
DPRINTK
(
"%s: CHANNELS
\n
"
,
__FUNCTION__
);
if
(
get_user
(
ival
,
(
int
*
)
arg
))
return
-
EFAULT
;
spin_lock_irq
(
&
chip
->
lock
);
if
(
rd
)
{
forte_channel_stop
(
&
chip
->
rec
);
rval
=
forte_channel_stereo
(
&
chip
->
rec
,
ival
-
1
)
+
1
;
}
if
(
wr
)
{
forte_channel_stop
(
&
chip
->
play
);
rval
=
forte_channel_stereo
(
&
chip
->
play
,
ival
-
1
)
+
1
;
}
spin_unlock_irq
(
&
chip
->
lock
);
return
put_user
(
rval
,
(
int
*
)
arg
);
case
SNDCTL_DSP_SPEED
:
DPRINTK
(
"%s: SPEED
\n
"
,
__FUNCTION__
);
if
(
get_user
(
ival
,
(
int
*
)
arg
))
return
-
EFAULT
;
spin_lock_irq
(
&
chip
->
lock
);
if
(
rd
)
{
forte_channel_stop
(
&
chip
->
rec
);
rval
=
forte_channel_rate
(
&
chip
->
rec
,
ival
);
}
if
(
wr
)
{
forte_channel_stop
(
&
chip
->
play
);
rval
=
forte_channel_rate
(
&
chip
->
play
,
ival
);
}
spin_unlock_irq
(
&
chip
->
lock
);
return
put_user
(
rval
,
(
int
*
)
arg
);
case
SNDCTL_DSP_GETBLKSIZE
:
DPRINTK
(
"%s: GETBLKSIZE
\n
"
,
__FUNCTION__
);
spin_lock_irq
(
&
chip
->
lock
);
if
(
rd
)
ival
=
chip
->
rec
.
frag_sz
;
if
(
wr
)
ival
=
chip
->
play
.
frag_sz
;
spin_unlock_irq
(
&
chip
->
lock
);
return
put_user
(
ival
,
(
int
*
)
arg
);
case
SNDCTL_DSP_RESET
:
DPRINTK
(
"%s: RESET
\n
"
,
__FUNCTION__
);
spin_lock_irq
(
&
chip
->
lock
);
if
(
rd
)
forte_channel_reset
(
&
chip
->
rec
);
if
(
wr
)
forte_channel_reset
(
&
chip
->
play
);
spin_unlock_irq
(
&
chip
->
lock
);
return
0
;
case
SNDCTL_DSP_SYNC
:
DPRINTK
(
"%s: SYNC
\n
"
,
__FUNCTION__
);
if
(
wr
)
ret
=
forte_channel_drain
(
&
chip
->
play
);
return
0
;
case
SNDCTL_DSP_POST
:
DPRINTK
(
"%s: POST
\n
"
,
__FUNCTION__
);
if
(
wr
)
{
spin_lock_irq
(
&
chip
->
lock
);
if
(
chip
->
play
.
filled_frags
)
forte_channel_start
(
&
chip
->
play
);
spin_unlock_irq
(
&
chip
->
lock
);
}
return
0
;
case
SNDCTL_DSP_SETFRAGMENT
:
DPRINTK
(
"%s: SETFRAGMENT
\n
"
,
__FUNCTION__
);
if
(
get_user
(
ival
,
(
int
*
)
arg
))
return
-
EFAULT
;
spin_lock_irq
(
&
chip
->
lock
);
if
(
rd
)
{
forte_channel_buffer
(
&
chip
->
rec
,
ival
&
0xffff
,
(
ival
>>
16
)
&
0xffff
);
ival
=
(
chip
->
rec
.
frag_num
<<
16
)
+
chip
->
rec
.
frag_sz
;
}
if
(
wr
)
{
forte_channel_buffer
(
&
chip
->
play
,
ival
&
0xffff
,
(
ival
>>
16
)
&
0xffff
);
ival
=
(
chip
->
play
.
frag_num
<<
16
)
+
chip
->
play
.
frag_sz
;
}
spin_unlock_irq
(
&
chip
->
lock
);
return
put_user
(
ival
,
(
int
*
)
arg
);
case
SNDCTL_DSP_GETISPACE
:
DPRINTK
(
"%s: GETISPACE
\n
"
,
__FUNCTION__
);
if
(
!
rd
)
return
-
EINVAL
;
spin_lock_irq
(
&
chip
->
lock
);
abi
.
fragstotal
=
chip
->
rec
.
frag_num
;
abi
.
fragsize
=
chip
->
rec
.
frag_sz
;
if
(
chip
->
rec
.
mapped
)
{
abi
.
fragments
=
chip
->
rec
.
frag_num
-
2
;
abi
.
bytes
=
abi
.
fragments
*
abi
.
fragsize
;
}
else
{
abi
.
fragments
=
chip
->
rec
.
filled_frags
;
abi
.
bytes
=
abi
.
fragments
*
abi
.
fragsize
;
}
spin_unlock_irq
(
&
chip
->
lock
);
return
copy_to_user
((
void
*
)
arg
,
&
abi
,
sizeof
(
abi
));
case
SNDCTL_DSP_GETIPTR
:
DPRINTK
(
"%s: GETIPTR
\n
"
,
__FUNCTION__
);
if
(
!
rd
)
return
-
EINVAL
;
spin_lock_irq
(
&
chip
->
lock
);
if
(
chip
->
rec
.
active
)
cinfo
.
ptr
=
chip
->
rec
.
hwptr
;
else
cinfo
.
ptr
=
0
;
cinfo
.
bytes
=
chip
->
rec
.
bytes
;
cinfo
.
blocks
=
chip
->
rec
.
nr_irqs
;
chip
->
rec
.
nr_irqs
=
0
;
spin_unlock_irq
(
&
chip
->
lock
);
return
copy_to_user
((
void
*
)
arg
,
&
cinfo
,
sizeof
(
cinfo
));
case
SNDCTL_DSP_GETOSPACE
:
if
(
!
wr
)
return
-
EINVAL
;
spin_lock_irq
(
&
chip
->
lock
);
abi
.
fragstotal
=
chip
->
play
.
frag_num
;
abi
.
fragsize
=
chip
->
play
.
frag_sz
;
if
(
chip
->
play
.
mapped
)
{
abi
.
fragments
=
chip
->
play
.
frag_num
-
2
;
abi
.
bytes
=
chip
->
play
.
buf_sz
;
}
else
{
abi
.
fragments
=
chip
->
play
.
frag_num
-
chip
->
play
.
filled_frags
;
if
(
chip
->
play
.
residue
)
abi
.
fragments
--
;
abi
.
bytes
=
abi
.
fragments
*
abi
.
fragsize
+
chip
->
play
.
residue
;
}
spin_unlock_irq
(
&
chip
->
lock
);
return
copy_to_user
((
void
*
)
arg
,
&
abi
,
sizeof
(
abi
));
case
SNDCTL_DSP_GETOPTR
:
if
(
!
wr
)
return
-
EINVAL
;
spin_lock_irq
(
&
chip
->
lock
);
if
(
chip
->
play
.
active
)
cinfo
.
ptr
=
chip
->
play
.
hwptr
;
else
cinfo
.
ptr
=
0
;
cinfo
.
bytes
=
chip
->
play
.
bytes
;
cinfo
.
blocks
=
chip
->
play
.
nr_irqs
;
chip
->
play
.
nr_irqs
=
0
;
spin_unlock_irq
(
&
chip
->
lock
);
return
copy_to_user
((
void
*
)
arg
,
&
cinfo
,
sizeof
(
cinfo
));
case
SNDCTL_DSP_GETODELAY
:
if
(
!
wr
)
return
-
EINVAL
;
spin_lock_irq
(
&
chip
->
lock
);
if
(
!
chip
->
play
.
active
)
{
ival
=
0
;
}
else
if
(
chip
->
play
.
mapped
)
{
count
=
inw
(
chip
->
play
.
iobase
+
FORTE_PLY_COUNT
)
+
1
;
ival
=
chip
->
play
.
frag_sz
-
count
;
}
else
{
ival
=
chip
->
play
.
filled_frags
*
chip
->
play
.
frag_sz
;
if
(
chip
->
play
.
residue
)
ival
+=
chip
->
play
.
frag_sz
-
chip
->
play
.
residue
;
}
spin_unlock_irq
(
&
chip
->
lock
);
return
put_user
(
ival
,
(
int
*
)
arg
);
case
SNDCTL_DSP_SETDUPLEX
:
DPRINTK
(
"%s: SETDUPLEX
\n
"
,
__FUNCTION__
);
return
-
EINVAL
;
case
SNDCTL_DSP_GETTRIGGER
:
DPRINTK
(
"%s: GETTRIGGER
\n
"
,
__FUNCTION__
);
return
put_user
(
chip
->
trigger
,
(
int
*
)
arg
);
case
SNDCTL_DSP_SETTRIGGER
:
if
(
get_user
(
ival
,
(
int
*
)
arg
))
return
-
EFAULT
;
DPRINTK
(
"%s: SETTRIGGER %d
\n
"
,
__FUNCTION__
,
ival
);
if
(
wr
)
{
spin_lock_irq
(
&
chip
->
lock
);
if
(
ival
&
PCM_ENABLE_OUTPUT
)
forte_channel_start
(
&
chip
->
play
);
else
{
chip
->
trigger
=
1
;
forte_channel_prep
(
&
chip
->
play
);
forte_channel_stop
(
&
chip
->
play
);
}
spin_unlock_irq
(
&
chip
->
lock
);
}
else
if
(
rd
)
{
spin_lock_irq
(
&
chip
->
lock
);
if
(
ival
&
PCM_ENABLE_INPUT
)
forte_channel_start
(
&
chip
->
rec
);
else
{
chip
->
trigger
=
1
;
forte_channel_prep
(
&
chip
->
rec
);
forte_channel_stop
(
&
chip
->
rec
);
}
spin_unlock_irq
(
&
chip
->
lock
);
}
return
0
;
case
SOUND_PCM_READ_RATE
:
DPRINTK
(
"%s: PCM_READ_RATE
\n
"
,
__FUNCTION__
);
return
put_user
(
chip
->
play
.
rate
,
(
int
*
)
arg
);
case
SOUND_PCM_READ_CHANNELS
:
DPRINTK
(
"%s: PCM_READ_CHANNELS
\n
"
,
__FUNCTION__
);
return
put_user
(
chip
->
play
.
stereo
,
(
int
*
)
arg
);
case
SOUND_PCM_READ_BITS
:
DPRINTK
(
"%s: PCM_READ_BITS
\n
"
,
__FUNCTION__
);
return
put_user
(
chip
->
play
.
format
,
(
int
*
)
arg
);
case
SNDCTL_DSP_NONBLOCK
:
DPRINTK
(
"%s: DSP_NONBLOCK
\n
"
,
__FUNCTION__
);
file
->
f_flags
|=
O_NONBLOCK
;
return
0
;
default:
DPRINTK
(
"Unsupported ioctl: %x (%p)
\n
"
,
cmd
,
(
void
*
)
arg
);
break
;
}
return
-
EINVAL
;
}
/**
* forte_dsp_open:
*/
static
int
forte_dsp_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
struct
forte_chip
*
chip
=
forte
;
/* FIXME: HACK FROM HELL! */
if
(
file
->
f_flags
&
O_NONBLOCK
)
{
if
(
down_trylock
(
&
chip
->
open_sem
))
{
DPRINTK
(
"%s: returning -EAGAIN
\n
"
,
__FUNCTION__
);
return
-
EAGAIN
;
}
}
else
{
if
(
down_interruptible
(
&
chip
->
open_sem
))
{
DPRINTK
(
"%s: returning -ERESTARTSYS
\n
"
,
__FUNCTION__
);
return
-
ERESTARTSYS
;
}
}
file
->
private_data
=
forte
;
DPRINTK
(
"%s: dsp opened by %d
\n
"
,
__FUNCTION__
,
current
->
pid
);
if
(
file
->
f_mode
&
FMODE_WRITE
)
forte_channel_init
(
forte
,
&
forte
->
play
);
if
(
file
->
f_mode
&
FMODE_READ
)
forte_channel_init
(
forte
,
&
forte
->
rec
);
return
0
;
}
/**
* forte_dsp_release:
*/
static
int
forte_dsp_release
(
struct
inode
*
inode
,
struct
file
*
file
)
{
struct
forte_chip
*
chip
=
file
->
private_data
;
int
ret
=
0
;
DPRINTK
(
"%s: chip @ %p
\n
"
,
__FUNCTION__
,
chip
);
if
(
file
->
f_mode
&
FMODE_WRITE
)
{
forte_channel_drain
(
&
chip
->
play
);
spin_lock_irq
(
&
chip
->
lock
);
forte_channel_free
(
chip
,
&
chip
->
play
);
spin_unlock_irq
(
&
chip
->
lock
);
}
if
(
file
->
f_mode
&
FMODE_READ
)
{
while
(
chip
->
rec
.
filled_frags
>
0
)
interruptible_sleep_on
(
&
chip
->
rec
.
wait
);
spin_lock_irq
(
&
chip
->
lock
);
forte_channel_stop
(
&
chip
->
rec
);
forte_channel_free
(
chip
,
&
chip
->
rec
);
spin_unlock_irq
(
&
chip
->
lock
);
}
up
(
&
chip
->
open_sem
);
return
ret
;
}
/**
* forte_dsp_poll:
*
*/
static
unsigned
int
forte_dsp_poll
(
struct
file
*
file
,
struct
poll_table_struct
*
wait
)
{
struct
forte_chip
*
chip
;
struct
forte_channel
*
channel
;
unsigned
int
mask
=
0
;
chip
=
file
->
private_data
;
if
(
file
->
f_mode
&
FMODE_WRITE
)
{
channel
=
&
chip
->
play
;
if
(
channel
->
active
)
poll_wait
(
file
,
&
channel
->
wait
,
wait
);
spin_lock_irq
(
&
chip
->
lock
);
if
(
channel
->
frag_num
-
channel
->
filled_frags
>
0
)
mask
|=
POLLOUT
|
POLLWRNORM
;
spin_unlock_irq
(
&
chip
->
lock
);
}
if
(
file
->
f_mode
&
FMODE_READ
)
{
channel
=
&
chip
->
rec
;
if
(
channel
->
active
)
poll_wait
(
file
,
&
channel
->
wait
,
wait
);
spin_lock_irq
(
&
chip
->
lock
);
if
(
channel
->
filled_frags
>
0
)
mask
|=
POLLIN
|
POLLRDNORM
;
spin_unlock_irq
(
&
chip
->
lock
);
}
return
mask
;
}
/**
* forte_dsp_mmap:
*/
static
int
forte_dsp_mmap
(
struct
file
*
file
,
struct
vm_area_struct
*
vma
)
{
struct
forte_chip
*
chip
;
struct
forte_channel
*
channel
;
unsigned
long
size
;
int
ret
;
chip
=
file
->
private_data
;
DPRINTK
(
"%s: start %lXh, size %ld, pgoff %ld
\n
"
,
__FUNCTION__
,
vma
->
vm_start
,
vma
->
vm_end
-
vma
->
vm_start
,
vma
->
vm_pgoff
);
spin_lock_irq
(
&
chip
->
lock
);
if
(
vma
->
vm_flags
&
VM_WRITE
&&
chip
->
play
.
active
)
{
ret
=
-
EBUSY
;
goto
out
;
}
if
(
vma
->
vm_flags
&
VM_READ
&&
chip
->
rec
.
active
)
{
ret
=
-
EBUSY
;
goto
out
;
}
if
(
file
->
f_mode
&
FMODE_WRITE
)
channel
=
&
chip
->
play
;
else
if
(
file
->
f_mode
&
FMODE_READ
)
channel
=
&
chip
->
rec
;
else
{
ret
=
-
EINVAL
;
goto
out
;
}
forte_channel_prep
(
channel
);
channel
->
mapped
=
1
;
if
(
vma
->
vm_pgoff
!=
0
)
{
ret
=
-
EINVAL
;
goto
out
;
}
size
=
vma
->
vm_end
-
vma
->
vm_start
;
if
(
size
>
channel
->
buf_pages
*
PAGE_SIZE
)
{
DPRINTK
(
"%s: size (%ld) > buf_sz (%d)
\n
"
,
__FUNCTION__
,
size
,
channel
->
buf_sz
);
ret
=
-
EINVAL
;
goto
out
;
}
if
(
remap_page_range
(
vma
,
vma
->
vm_start
,
virt_to_phys
(
channel
->
buf
),
size
,
vma
->
vm_page_prot
))
{
DPRINTK
(
"%s: remap el a no worko
\n
"
,
__FUNCTION__
);
ret
=
-
EAGAIN
;
goto
out
;
}
ret
=
0
;
out:
spin_unlock_irq
(
&
chip
->
lock
);
return
ret
;
}
/**
* forte_dsp_write:
*/
static
ssize_t
forte_dsp_write
(
struct
file
*
file
,
const
char
*
buffer
,
size_t
bytes
,
loff_t
*
ppos
)
{
struct
forte_chip
*
chip
;
struct
forte_channel
*
channel
;
unsigned
int
i
=
bytes
,
sz
=
0
;
unsigned
long
flags
;
if
(
ppos
!=
&
file
->
f_pos
)
return
-
ESPIPE
;
if
(
!
access_ok
(
VERIFY_READ
,
buffer
,
bytes
))
return
-
EFAULT
;
chip
=
(
struct
forte_chip
*
)
file
->
private_data
;
if
(
!
chip
)
BUG
();
channel
=
&
chip
->
play
;
if
(
!
channel
)
BUG
();
spin_lock_irqsave
(
&
chip
->
lock
,
flags
);
/* Set up buffers with the right fragment size */
forte_channel_prep
(
channel
);
while
(
i
)
{
/* All fragment buffers in use -> wait */
if
(
channel
->
frag_num
-
channel
->
filled_frags
==
0
)
{
DECLARE_WAITQUEUE
(
wait
,
current
);
/* For trigger or non-blocking operation, get out */
if
(
chip
->
trigger
||
file
->
f_flags
&
O_NONBLOCK
)
{
spin_unlock_irqrestore
(
&
chip
->
lock
,
flags
);
return
-
EAGAIN
;
}
/* Otherwise wait for buffers */
add_wait_queue
(
&
channel
->
wait
,
&
wait
);
for
(;;)
{
spin_unlock_irqrestore
(
&
chip
->
lock
,
flags
);
set_current_state
(
TASK_INTERRUPTIBLE
);
schedule
();
spin_lock_irqsave
(
&
chip
->
lock
,
flags
);
if
(
channel
->
frag_num
-
channel
->
filled_frags
)
break
;
}
remove_wait_queue
(
&
channel
->
wait
,
&
wait
);
set_current_state
(
TASK_RUNNING
);
if
(
signal_pending
(
current
))
{
spin_unlock_irqrestore
(
&
chip
->
lock
,
flags
);
return
-
ERESTARTSYS
;
}
}
if
(
channel
->
residue
)
sz
=
channel
->
residue
;
else
if
(
i
>
channel
->
frag_sz
)
sz
=
channel
->
frag_sz
;
else
sz
=
i
;
spin_unlock_irqrestore
(
&
chip
->
lock
,
flags
);
if
(
copy_from_user
((
void
*
)
channel
->
buf
+
channel
->
swptr
,
buffer
,
sz
))
return
-
EFAULT
;
spin_lock_irqsave
(
&
chip
->
lock
,
flags
);
/* Advance software pointer */
buffer
+=
sz
;
channel
->
swptr
+=
sz
;
channel
->
swptr
%=
channel
->
buf_sz
;
i
-=
sz
;
/* Only bump filled_frags if a full fragment has been written */
if
(
channel
->
swptr
%
channel
->
frag_sz
==
0
)
{
channel
->
filled_frags
++
;
channel
->
residue
=
0
;
}
else
channel
->
residue
=
channel
->
frag_sz
-
sz
;
/* If playback isn't active, start it */
if
(
channel
->
active
==
0
&&
chip
->
trigger
==
0
)
forte_channel_start
(
channel
);
}
spin_unlock_irqrestore
(
&
chip
->
lock
,
flags
);
return
bytes
-
i
;
}
/**
* forte_dsp_read:
*/
static
ssize_t
forte_dsp_read
(
struct
file
*
file
,
char
*
buffer
,
size_t
bytes
,
loff_t
*
ppos
)
{
struct
forte_chip
*
chip
;
struct
forte_channel
*
channel
;
unsigned
int
i
=
bytes
,
sz
;
unsigned
long
flags
;
if
(
ppos
!=
&
file
->
f_pos
)
return
-
ESPIPE
;
if
(
!
access_ok
(
VERIFY_WRITE
,
buffer
,
bytes
))
return
-
EFAULT
;
chip
=
(
struct
forte_chip
*
)
file
->
private_data
;
if
(
!
chip
)
BUG
();
channel
=
&
chip
->
rec
;
if
(
!
channel
)
BUG
();
spin_lock_irqsave
(
&
chip
->
lock
,
flags
);
/* Set up buffers with the right fragment size */
forte_channel_prep
(
channel
);
/* Start recording */
if
(
!
chip
->
trigger
)
forte_channel_start
(
channel
);
while
(
i
)
{
/* No fragment buffers in use -> wait */
if
(
channel
->
filled_frags
==
0
)
{
DECLARE_WAITQUEUE
(
wait
,
current
);
/* For trigger mode operation, get out */
if
(
chip
->
trigger
)
{
spin_unlock_irqrestore
(
&
chip
->
lock
,
flags
);
return
-
EAGAIN
;
}
add_wait_queue
(
&
channel
->
wait
,
&
wait
);
for
(;;)
{
if
(
channel
->
active
==
0
)
break
;
if
(
channel
->
filled_frags
)
break
;
spin_unlock_irqrestore
(
&
chip
->
lock
,
flags
);
set_current_state
(
TASK_INTERRUPTIBLE
);
schedule
();
spin_lock_irqsave
(
&
chip
->
lock
,
flags
);
}
set_current_state
(
TASK_RUNNING
);
remove_wait_queue
(
&
channel
->
wait
,
&
wait
);
}
if
(
i
>
channel
->
frag_sz
)
sz
=
channel
->
frag_sz
;
else
sz
=
i
;
spin_unlock_irqrestore
(
&
chip
->
lock
,
flags
);
if
(
copy_to_user
(
buffer
,
(
void
*
)
channel
->
buf
+
channel
->
swptr
,
sz
))
{
DPRINTK
(
"%s: copy_to_user failed
\n
"
,
__FUNCTION__
);
return
-
EFAULT
;
}
spin_lock_irqsave
(
&
chip
->
lock
,
flags
);
/* Advance software pointer */
buffer
+=
sz
;
if
(
channel
->
filled_frags
>
0
)
channel
->
filled_frags
--
;
channel
->
swptr
+=
channel
->
frag_sz
;
channel
->
swptr
%=
channel
->
buf_sz
;
i
-=
sz
;
}
spin_unlock_irqrestore
(
&
chip
->
lock
,
flags
);
return
bytes
-
i
;
}
static
struct
file_operations
forte_dsp_fops
=
{
owner:
THIS_MODULE
,
llseek:
&
no_llseek
,
read:
&
forte_dsp_read
,
write:
&
forte_dsp_write
,
poll:
&
forte_dsp_poll
,
ioctl:
&
forte_dsp_ioctl
,
open:
&
forte_dsp_open
,
release:
&
forte_dsp_release
,
mmap:
&
forte_dsp_mmap
,
};
/* Common ------------------------------------------------------------------ */
/**
* forte_interrupt:
*/
static
irqreturn_t
forte_interrupt
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
{
struct
forte_chip
*
chip
=
dev_id
;
struct
forte_channel
*
channel
=
NULL
;
u16
status
,
count
;
status
=
inw
(
chip
->
iobase
+
FORTE_IRQ_STATUS
);
/* If this is not for us, get outta here ASAP */
if
((
status
&
(
FORTE_IRQ_PLAYBACK
|
FORTE_IRQ_CAPTURE
))
==
0
)
return
IRQ_NONE
;
if
(
status
&
FORTE_IRQ_PLAYBACK
)
{
channel
=
&
chip
->
play
;
spin_lock
(
&
chip
->
lock
);
if
(
channel
->
frag_sz
==
0
)
goto
pack
;
/* Declare a fragment done */
if
(
channel
->
filled_frags
>
0
)
channel
->
filled_frags
--
;
channel
->
bytes
+=
channel
->
frag_sz
;
channel
->
nr_irqs
++
;
/* Flip-flop between buffer I and II */
channel
->
next_buf
^=
1
;
/* Advance hardware pointer by fragment size and wrap around */
channel
->
hwptr
+=
channel
->
frag_sz
;
channel
->
hwptr
%=
channel
->
buf_sz
;
/* Buffer I or buffer II BAR */
outl
(
channel
->
buf_handle
+
channel
->
hwptr
,
channel
->
next_buf
==
0
?
channel
->
iobase
+
FORTE_PLY_BUF1
:
channel
->
iobase
+
FORTE_PLY_BUF2
);
/* If the currently playing fragment is last, schedule pause */
if
(
channel
->
filled_frags
==
1
)
forte_channel_pause
(
channel
);
pack:
/* Acknowledge interrupt */
outw
(
FORTE_IRQ_PLAYBACK
,
chip
->
iobase
+
FORTE_IRQ_STATUS
);
if
(
waitqueue_active
(
&
channel
->
wait
))
wake_up_all
(
&
channel
->
wait
);
spin_unlock
(
&
chip
->
lock
);
}
if
(
status
&
FORTE_IRQ_CAPTURE
)
{
channel
=
&
chip
->
rec
;
spin_lock
(
&
chip
->
lock
);
/* One fragment filled */
channel
->
filled_frags
++
;
/* Get # of completed bytes */
count
=
inw
(
channel
->
iobase
+
FORTE_PLY_COUNT
)
+
1
;
if
(
count
==
0
)
{
DPRINTK
(
"%s: last, filled_frags = %d
\n
"
,
__FUNCTION__
,
channel
->
filled_frags
);
channel
->
filled_frags
=
0
;
goto
rack
;
}
/* Buffer I or buffer II BAR */
outl
(
channel
->
buf_handle
+
channel
->
hwptr
,
channel
->
next_buf
==
0
?
channel
->
iobase
+
FORTE_PLY_BUF1
:
channel
->
iobase
+
FORTE_PLY_BUF2
);
/* Flip-flop between buffer I and II */
channel
->
next_buf
^=
1
;
/* Advance hardware pointer by fragment size and wrap around */
channel
->
hwptr
+=
channel
->
frag_sz
;
channel
->
hwptr
%=
channel
->
buf_sz
;
/* Out of buffers */
if
(
channel
->
filled_frags
==
channel
->
frag_num
-
1
)
forte_channel_stop
(
channel
);
rack:
/* Acknowledge interrupt */
outw
(
FORTE_IRQ_CAPTURE
,
chip
->
iobase
+
FORTE_IRQ_STATUS
);
spin_unlock
(
&
chip
->
lock
);
if
(
waitqueue_active
(
&
channel
->
wait
))
wake_up_all
(
&
channel
->
wait
);
}
return
IRQ_HANDLED
;
}
/**
* forte_proc_read:
*/
static
int
forte_proc_read
(
char
*
page
,
char
**
start
,
off_t
off
,
int
count
,
int
*
eof
,
void
*
data
)
{
int
i
=
0
,
p_rate
,
p_chan
,
r_rate
;
unsigned
short
p_reg
,
r_reg
;
i
+=
sprintf
(
page
,
"ForteMedia FM801 OSS Lite driver
\n
%s
\n
\n
"
,
DRIVER_VERSION
);
if
(
!
forte
->
iobase
)
return
i
;
p_rate
=
p_chan
=
-
1
;
p_reg
=
inw
(
forte
->
iobase
+
FORTE_PLY_CTRL
);
p_rate
=
(
p_reg
>>
8
)
&
15
;
p_chan
=
(
p_reg
>>
12
)
&
3
;
if
(
p_rate
>=
0
||
p_rate
<=
10
)
p_rate
=
rates
[
p_rate
];
if
(
p_chan
>=
0
||
p_chan
<=
2
)
p_chan
=
channels
[
p_chan
];
r_rate
=
-
1
;
r_reg
=
inw
(
forte
->
iobase
+
FORTE_CAP_CTRL
);
r_rate
=
(
r_reg
>>
8
)
&
15
;
if
(
r_rate
>=
0
||
r_rate
<=
10
)
r_rate
=
rates
[
r_rate
];
i
+=
sprintf
(
page
+
i
,
" Playback Capture
\n
"
"FIFO empty : %-3s %-3s
\n
"
"Buf1 Last : %-3s %-3s
\n
"
"Buf2 Last : %-3s %-3s
\n
"
"Started : %-3s %-3s
\n
"
"Paused : %-3s %-3s
\n
"
"Immed Stop : %-3s %-3s
\n
"
"Rate : %-5d %-5d
\n
"
"Channels : %-5d -
\n
"
"16-bit : %-3s %-3s
\n
"
"Stereo : %-3s %-3s
\n
"
"
\n
"
"Buffer Sz : %-6d %-6d
\n
"
"Frag Sz : %-6d %-6d
\n
"
"Frag Num : %-6d %-6d
\n
"
"Frag msecs : %-6d %-6d
\n
"
"Used Frags : %-6d %-6d
\n
"
"Mapped : %-3s %-3s
\n
"
,
p_reg
&
1
<<
0
?
"yes"
:
"no"
,
r_reg
&
1
<<
0
?
"yes"
:
"no"
,
p_reg
&
1
<<
1
?
"yes"
:
"no"
,
r_reg
&
1
<<
1
?
"yes"
:
"no"
,
p_reg
&
1
<<
2
?
"yes"
:
"no"
,
r_reg
&
1
<<
2
?
"yes"
:
"no"
,
p_reg
&
1
<<
5
?
"yes"
:
"no"
,
r_reg
&
1
<<
5
?
"yes"
:
"no"
,
p_reg
&
1
<<
6
?
"yes"
:
"no"
,
r_reg
&
1
<<
6
?
"yes"
:
"no"
,
p_reg
&
1
<<
7
?
"yes"
:
"no"
,
r_reg
&
1
<<
7
?
"yes"
:
"no"
,
p_rate
,
r_rate
,
p_chan
,
p_reg
&
1
<<
14
?
"yes"
:
"no"
,
r_reg
&
1
<<
14
?
"yes"
:
"no"
,
p_reg
&
1
<<
15
?
"yes"
:
"no"
,
r_reg
&
1
<<
15
?
"yes"
:
"no"
,
forte
->
play
.
buf_sz
,
forte
->
rec
.
buf_sz
,
forte
->
play
.
frag_sz
,
forte
->
rec
.
frag_sz
,
forte
->
play
.
frag_num
,
forte
->
rec
.
frag_num
,
forte
->
play
.
frag_msecs
,
forte
->
rec
.
frag_msecs
,
forte
->
play
.
filled_frags
,
forte
->
rec
.
filled_frags
,
forte
->
play
.
mapped
?
"yes"
:
"no"
,
forte
->
rec
.
mapped
?
"yes"
:
"no"
);
return
i
;
}
/**
* forte_proc_init:
*
* Creates driver info entries in /proc
*/
static
int
__init
forte_proc_init
(
void
)
{
if
(
!
proc_mkdir
(
"driver/forte"
,
0
))
return
-
EIO
;
if
(
!
create_proc_read_entry
(
"driver/forte/chip"
,
0
,
0
,
forte_proc_read
,
forte
))
{
remove_proc_entry
(
"driver/forte"
,
NULL
);
return
-
EIO
;
}
if
(
!
create_proc_read_entry
(
"driver/forte/ac97"
,
0
,
0
,
ac97_read_proc
,
forte
->
ac97
))
{
remove_proc_entry
(
"driver/forte/chip"
,
NULL
);
remove_proc_entry
(
"driver/forte"
,
NULL
);
return
-
EIO
;
}
return
0
;
}
/**
* forte_proc_remove:
*
* Removes driver info entries in /proc
*/
static
void
forte_proc_remove
(
void
)
{
remove_proc_entry
(
"driver/forte/ac97"
,
NULL
);
remove_proc_entry
(
"driver/forte/chip"
,
NULL
);
remove_proc_entry
(
"driver/forte"
,
NULL
);
}
/**
* forte_chip_init:
* @chip: Chip instance to initialize
*
* Description:
* Resets chip, configures codec and registers the driver with
* the sound subsystem.
*
* Press and hold Start for 8 secs, then switch on Run
* and hold for 4 seconds. Let go of Start. Numbers
* assume a properly oiled TWG.
*/
static
int
__devinit
forte_chip_init
(
struct
forte_chip
*
chip
)
{
u8
revision
;
u16
cmdw
;
struct
ac97_codec
*
codec
;
pci_read_config_byte
(
chip
->
pci_dev
,
PCI_REVISION_ID
,
&
revision
);
if
(
revision
>=
0xB1
)
{
chip
->
multichannel
=
1
;
printk
(
KERN_INFO
PFX
"Multi-channel device detected.
\n
"
);
}
/* Reset chip */
outw
(
FORTE_CC_CODEC_RESET
|
FORTE_CC_AC97_RESET
,
chip
->
iobase
+
FORTE_CODEC_CTRL
);
udelay
(
100
);
outw
(
0
,
chip
->
iobase
+
FORTE_CODEC_CTRL
);
/* Request read from AC97 */
outw
(
FORTE_AC97_READ
|
(
0
<<
FORTE_AC97_ADDR_SHIFT
),
chip
->
iobase
+
FORTE_AC97_CMD
);
mdelay
(
750
);
if
((
inw
(
chip
->
iobase
+
FORTE_AC97_CMD
)
&
(
3
<<
8
))
!=
(
1
<<
8
))
{
printk
(
KERN_INFO
PFX
"AC97 codec not responding"
);
return
-
EIO
;
}
/* Init volume */
outw
(
0x0808
,
chip
->
iobase
+
FORTE_PCM_VOL
);
outw
(
0x9f1f
,
chip
->
iobase
+
FORTE_FM_VOL
);
outw
(
0x8808
,
chip
->
iobase
+
FORTE_I2S_VOL
);
/* I2S control - I2S mode */
outw
(
0x0003
,
chip
->
iobase
+
FORTE_I2S_MODE
);
/* Interrupt setup - unmask PLAYBACK & CAPTURE */
cmdw
=
inw
(
chip
->
iobase
+
FORTE_IRQ_MASK
);
cmdw
&=
~
0x0003
;
outw
(
cmdw
,
chip
->
iobase
+
FORTE_IRQ_MASK
);
/* Interrupt clear */
outw
(
FORTE_IRQ_PLAYBACK
|
FORTE_IRQ_CAPTURE
,
chip
->
iobase
+
FORTE_IRQ_STATUS
);
/* Set up the AC97 codec */
if
((
codec
=
ac97_alloc_codec
())
==
NULL
)
return
-
ENOMEM
;
codec
->
private_data
=
chip
;
codec
->
codec_read
=
forte_ac97_read
;
codec
->
codec_write
=
forte_ac97_write
;
codec
->
id
=
0
;
if
(
ac97_probe_codec
(
codec
)
==
0
)
{
printk
(
KERN_ERR
PFX
"codec probe failed
\n
"
);
ac97_release_codec
(
codec
);
return
-
1
;
}
/* Register mixer */
if
((
codec
->
dev_mixer
=
register_sound_mixer
(
&
forte_mixer_fops
,
-
1
))
<
0
)
{
printk
(
KERN_ERR
PFX
"couldn't register mixer!
\n
"
);
ac97_release_codec
(
codec
);
return
-
1
;
}
chip
->
ac97
=
codec
;
/* Register DSP */
if
((
chip
->
dsp
=
register_sound_dsp
(
&
forte_dsp_fops
,
-
1
)
)
<
0
)
{
printk
(
KERN_ERR
PFX
"couldn't register dsp!
\n
"
);
return
-
1
;
}
/* Register with /proc */
if
(
forte_proc_init
())
{
printk
(
KERN_ERR
PFX
"couldn't add entries to /proc!
\n
"
);
return
-
1
;
}
return
0
;
}
/**
* forte_probe:
* @pci_dev: PCI struct for probed device
* @pci_id:
*
* Description:
* Allocates chip instance, I/O region, and IRQ
*/
static
int
__init
forte_probe
(
struct
pci_dev
*
pci_dev
,
const
struct
pci_device_id
*
pci_id
)
{
struct
forte_chip
*
chip
;
int
ret
=
0
;
/* FIXME: Support more than one chip */
if
(
found
++
)
return
-
EIO
;
/* Ignition */
if
(
pci_enable_device
(
pci_dev
))
return
-
EIO
;
pci_set_master
(
pci_dev
);
/* Allocate chip instance and configure */
forte
=
(
struct
forte_chip
*
)
kmalloc
(
sizeof
(
struct
forte_chip
),
GFP_KERNEL
);
chip
=
forte
;
if
(
chip
==
NULL
)
{
printk
(
KERN_WARNING
PFX
"Out of memory"
);
return
-
ENOMEM
;
}
memset
(
chip
,
0
,
sizeof
(
struct
forte_chip
));
chip
->
pci_dev
=
pci_dev
;
init_MUTEX
(
&
chip
->
open_sem
);
spin_lock_init
(
&
chip
->
lock
);
spin_lock_init
(
&
chip
->
ac97_lock
);
if
(
!
request_region
(
pci_resource_start
(
pci_dev
,
0
),
pci_resource_len
(
pci_dev
,
0
),
DRIVER_NAME
))
{
printk
(
KERN_WARNING
PFX
"Unable to reserve I/O space"
);
ret
=
-
ENOMEM
;
goto
error
;
}
chip
->
iobase
=
pci_resource_start
(
pci_dev
,
0
);
chip
->
irq
=
pci_dev
->
irq
;
if
(
request_irq
(
chip
->
irq
,
forte_interrupt
,
SA_SHIRQ
,
DRIVER_NAME
,
chip
))
{
printk
(
KERN_WARNING
PFX
"Unable to reserve IRQ"
);
ret
=
-
EIO
;
goto
error
;
}
pci_set_drvdata
(
pci_dev
,
chip
);
printk
(
KERN_INFO
PFX
"FM801 chip found at 0x%04lX-0x%04lX IRQ %u
\n
"
,
chip
->
iobase
,
pci_resource_end
(
pci_dev
,
0
),
chip
->
irq
);
/* Power it up */
if
((
ret
=
forte_chip_init
(
chip
))
==
0
)
return
0
;
error:
if
(
chip
->
irq
)
free_irq
(
chip
->
irq
,
chip
);
if
(
chip
->
iobase
)
release_region
(
pci_resource_start
(
pci_dev
,
0
),
pci_resource_len
(
pci_dev
,
0
));
kfree
(
chip
);
return
ret
;
}
/**
* forte_remove:
* @pci_dev: PCI device to unclaim
*
*/
static
void
forte_remove
(
struct
pci_dev
*
pci_dev
)
{
struct
forte_chip
*
chip
=
pci_get_drvdata
(
pci_dev
);
if
(
chip
==
NULL
)
return
;
/* Turn volume down to avoid popping */
outw
(
0x1f1f
,
chip
->
iobase
+
FORTE_PCM_VOL
);
outw
(
0x1f1f
,
chip
->
iobase
+
FORTE_FM_VOL
);
outw
(
0x1f1f
,
chip
->
iobase
+
FORTE_I2S_VOL
);
forte_proc_remove
();
free_irq
(
chip
->
irq
,
chip
);
release_region
(
chip
->
iobase
,
pci_resource_len
(
pci_dev
,
0
));
unregister_sound_dsp
(
chip
->
dsp
);
unregister_sound_mixer
(
chip
->
ac97
->
dev_mixer
);
ac97_release_codec
(
chip
->
ac97
);
kfree
(
chip
);
printk
(
KERN_INFO
PFX
"driver released
\n
"
);
}
static
struct
pci_device_id
forte_pci_ids
[]
__devinitdata
=
{
{
0x1319
,
0x0801
,
PCI_ANY_ID
,
PCI_ANY_ID
,
0
,
0
,
0
,
},
{
0
,
}
};
static
struct
pci_driver
forte_pci_driver
=
{
name:
DRIVER_NAME
,
id_table:
forte_pci_ids
,
probe:
forte_probe
,
remove:
forte_remove
,
};
/**
* forte_init_module:
*
*/
static
int
__init
forte_init_module
(
void
)
{
printk
(
KERN_INFO
PFX
DRIVER_VERSION
"
\n
"
);
if
(
!
pci_register_driver
(
&
forte_pci_driver
))
{
pci_unregister_driver
(
&
forte_pci_driver
);
return
-
ENODEV
;
}
return
0
;
}
/**
* forte_cleanup_module:
*
*/
static
void
__exit
forte_cleanup_module
(
void
)
{
pci_unregister_driver
(
&
forte_pci_driver
);
}
module_init
(
forte_init_module
);
module_exit
(
forte_cleanup_module
);
MODULE_AUTHOR
(
"Martin K. Petersen <mkp@mkp.net>"
);
MODULE_DESCRIPTION
(
"ForteMedia FM801 OSS Driver"
);
MODULE_LICENSE
(
"GPL"
);
MODULE_DEVICE_TABLE
(
pci
,
forte_pci_ids
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment