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
Kirill Smelkov
linux
Commits
5cb39380
Commit
5cb39380
authored
Nov 23, 2007
by
Linus Torvalds
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Import 2.3.99pre7
parent
0f0078de
Changes
19
Show whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
3676 additions
and
1405 deletions
+3676
-1405
Documentation/sound/via82cxxx.txt
Documentation/sound/via82cxxx.txt
+50
-37
Documentation/usb/ov511.txt
Documentation/usb/ov511.txt
+9
-2
drivers/cdrom/cdrom.c
drivers/cdrom/cdrom.c
+17
-15
drivers/ide/ide-cd.c
drivers/ide/ide-cd.c
+26
-20
drivers/ide/ide-cd.h
drivers/ide/ide-cd.h
+1
-11
drivers/net/starfire.c
drivers/net/starfire.c
+1
-1
drivers/sound/Config.in
drivers/sound/Config.in
+2
-1
drivers/sound/Makefile
drivers/sound/Makefile
+1
-1
drivers/sound/emu10k1/main.c
drivers/sound/emu10k1/main.c
+2
-2
drivers/sound/soundmodule.h
drivers/sound/soundmodule.h
+1
-1
drivers/sound/via82cxxx_audio.c
drivers/sound/via82cxxx_audio.c
+2240
-426
drivers/usb/ov511.c
drivers/usb/ov511.c
+1172
-799
drivers/usb/ov511.h
drivers/usb/ov511.h
+68
-49
drivers/video/clgenfb.c
drivers/video/clgenfb.c
+5
-4
drivers/video/vesafb.c
drivers/video/vesafb.c
+9
-2
fs/buffer.c
fs/buffer.c
+1
-3
include/linux/swap.h
include/linux/swap.h
+7
-2
mm/filemap.c
mm/filemap.c
+62
-25
mm/vmscan.c
mm/vmscan.c
+2
-4
No files found.
Documentation/sound/via82cxxx.txt
View file @
5cb39380
...
...
@@ -13,12 +13,11 @@ The via82cxxx audio driver found in the drivers/sound directory
of the kernel source tree is a PCI audio driver for audio chips
found on Via-based motherboards, such as the MVP4.
Currently the driver provides audio via SoundBlaster Pro compatibility,
and MIDI via MPU-401 compatibility. An AC97 mixing device is also
supported, and is generally preferred over the SoundBlaster mixer.
Currently the driver exports the following features:
IMPORTANT NOTE: Some users report that the SoundBlaster mixer does
not work at all -- use the AC97 mixer if possible.
* /dev/dsp and /dev/audio support
* 16-bit stereo PCM output channel
* AC97 mixer
Please send bug reports to the mailing list linux-via@gtf.org.
To subscribe, e-mail majordomo@gtf.org with "subscribe linux-via" in the
...
...
@@ -27,7 +26,7 @@ body of the message.
Thanks
------------------------------------------------------------------------
Via for providing e-mail support, specs, and NDA'
s
source code.
Via for providing e-mail support, specs, and NDA'
d
source code.
MandrakeSoft for providing hacking time.
...
...
@@ -40,15 +39,10 @@ Installation
If the driver is being statically compiled into the kernel, no
configuration should be necessary.
If the driver is being compiled as a module, generally
two lines
must
If the driver is being compiled as a module, generally
one line
must
be added to your /etc/conf.modules (or /etc/modules.conf) file:
alias sound via82cxxx
options sb support=1
The second line is very important: it tells the required 'sb' module
not to load SoundBlaster support, but to instead let the Via driver
do so at a later time.
alias sound via82cxxx_audio
...
...
@@ -63,8 +57,8 @@ Some architecture remains for multiple cards, feel free to submit
a patch to clean some of that up. Ideally,
No consideration for SMP, this chipset is not known to be found on
any SMP motherboards. However,
this will change when we start handling
our own interrupts in "native mode."
any SMP motherboards. However,
spin_locks must be used anyway in order
to handle interrupts correctly.
GNU indent formatting options: -kr -i8 -pcs
...
...
@@ -76,29 +70,17 @@ The following is an _incomplete_ list of motherboards supported by this
audio driver. If your motherboard (or notebook) is not listed here,
please e-mail the maintainer with details.
AOpen MX59 Pro (Apollo MVP4)
AOpen MX59 Pro
Compaq Presario 1247
The Future
Random Developer Notes / Comments
------------------------------------------------------------------------
Via has graciously donated e-mail support and source code to help further
the development of this driver. Their assistance has been invaluable
in the design and coding of the next major version of this driver.
This audio chip supports a DirectSound(tm)-style hardware interface,
with a single 16-bit stereo input channel, and a single 16-bit stereo
output channel. Data is transferred to/from the hardware using
table-driven scatter-gather DMA buffers.
Work is currently underway to support this "native mode" of the chip.
When complete, SoundBlaster legacy mode will be completely removed.
After a round of testing, this code will become version 2.0.0.
Following the 2.0.0 release, the last major task to complete is
MIDI support. MPU-401 legacy support is available currently, but
not well tested at all.
The Via audio chip apparently provides a second PCM scatter-gather
DMA channel just for FM data, but does not have a full hardware MIDI
processor. I haven't put much thought towards a solution here, but it
...
...
@@ -109,11 +91,15 @@ support altogether and using the FM PCM channel as a second (input? output?)
General To-do List (patches/suggestions welcome)
------------------------------------------------------------------------
Better docs
Recording support
Code review by sound guru(s)
mmap support
Native DSP audio driver using scatter-gather DMA, as described above
Other advanced ioctls
Better docs
Code review
Native MIDI driver, as described above
...
...
@@ -121,8 +107,35 @@ Native MIDI driver, as described above
Known bugs (patches/suggestions welcome)
------------------------------------------------------------------------
1) Two MIDI devices are loaded by the sound driver. Eliminate one of them.
1) Volume too low on many systems. Workaround: use mixer program
such as xmixer to increase volume.
2) RealPlayer output very scratchy.
3) Applications which attempt to open the sound device in read/write
mode (O_RDWR) will fail. This is incorrect OSS behavior, but since
this driver will eventually support recording as well as playback,
we will be able to (in the future) support even broken programs which
unconditionally use O_RDWR.
Submitting a bug report
------------------------------------------------------------------------
Describe the application you were using to play/record sound, and how
to reproduce the problem.
Obtain the via-audio-diag diagnostics program from
http://gtf.org/garzik/drivers/via82cxxx/ and provide a dump of the
audio chip's registers while the problem is occurring. Sample command line:
./via-audio-diag -aps > diag-output.txt
Define "VIA_DEBUG" at the beginning of the driver, then capture and email
the kernel log output. This can be viewed in the system kernel log (if
enabled), or via the 'dmesg' program.
If you wish to increase the size of the buffer displayed by 'dmesg', then
change the LOG_BUF_LEN macro at the top of linux/kernel/printk.c, recompile
your kernel, and pass the "-s <size>" option to 'dmesg'.
2) Two mixer devices are loaded by the sound driver. Eliminate one of
them. At least one bug report says that SB mixer does not work at all,
only AC97 mixer.
Documentation/usb/ov511.txt
View file @
5cb39380
...
...
@@ -6,8 +6,11 @@ Author: Mark McClelland
Homepage: http://alpha.dyndns.org/ov511
NEW IN THIS VERSION:
o Support for OV511+
o Support for OV7620
o Improvements to sensor detection code
o Added "i2c_detect_tries" and "aperture" parameters
o proc filesystem status support
o read() fixed partially
o code cleanups and minor fixes
INTRODUCTION:
...
...
@@ -151,11 +154,13 @@ WORKING FEATURES:
o Monochrome
o Setting/getting of saturation, contrast and brightness (no hue yet; only
works with OV7610, not the OV7620 or OV7620AE)
o proc status reporting
EXPERIMENTAL FEATURES:
o fix_rgb_offset: Sometimes works, but other times causes errors with xawtv and
corrupted frames.
o Snapshot mode (only works with some read() based apps; see below for more)
o read() support
TODO:
o Fix the noise / grainy image problem.
...
...
@@ -180,6 +185,8 @@ TODO:
o Get rid of the memory management functions (put them in videodev.c??)
o Setting of contrast and brightness not working with 7620
o Driver/camera state save/restore for when USB supports suspend/resume
o Multiple cameras reportedly do not work simultaneously
o Problems with OHCI
HOW TO CONTACT ME:
...
...
drivers/cdrom/cdrom.c
View file @
5cb39380
...
...
@@ -200,10 +200,21 @@
-- Clear header length in mode_select unconditionally.
-- Removed the register_disk() that was added, not needed here.
3.08 May 1, 2000 - Jens Axboe <axboe@suse.de>
-- Fix direction flag in setup_send_key and setup_report_key. This
gave some SCSI adapters problems.
-- Always return -EROFS for write opens
-- Convert to module_init/module_exit style init and remove some
of the #ifdef MODULE stuff
-- Fix several dvd errors - DVD_LU_SEND_ASF should pass agid,
DVD_HOST_SEND_RPC_STATE did not set buffer size in cdb, and
dvd_do_auth passed uninitialized data to drive because init_cdrom_command
did not clear a 0 sized buffer.
-------------------------------------------------------------------------*/
#define REVISION "Revision: 3.0
7
"
#define VERSION "Id: cdrom.c 3.0
7 2000/02/02
"
#define REVISION "Revision: 3.0
8
"
#define VERSION "Id: cdrom.c 3.0
8 2000/05/01
"
/* I use an error-log mask to give fine grain control over the type of
messages dumped to the system logs. The available masks include: */
...
...
@@ -432,17 +443,6 @@ struct cdrom_device_info *cdrom_find_device(kdev_t dev)
while
(
cdi
!=
NULL
&&
cdi
->
dev
!=
dev
)
cdi
=
cdi
->
next
;
/* we need to find the device this way when IDE devices such
* as /dev/hdc2 are opened. SCSI drives will be found above and
* so will /dev/hdc, for instance.
*/
if
(
cdi
==
NULL
)
{
kdev_t
cd_dev
=
MKDEV
(
MAJOR
(
dev
),
MINOR
(
dev
)
|
CD_PART_MASK
);
cdi
=
topCdromPtr
;
while
(
cdi
!=
NULL
&&
cdi
->
dev
!=
cd_dev
)
cdi
=
cdi
->
next
;
}
return
cdi
;
}
...
...
@@ -834,7 +834,7 @@ static int cdrom_media_changed(kdev_t dev)
/* This talks to the VFS, which doesn't like errors - just 1 or 0.
* Returning "0" is always safe (media hasn't been changed). Do that
* if the low-level cdrom driver dosn't support media changed. */
if
(
cdi
->
ops
->
media_changed
==
NULL
)
if
(
cdi
==
NULL
||
cdi
->
ops
->
media_changed
==
NULL
)
return
0
;
if
(
!
CDROM_CAN
(
CDC_MEDIA_CHANGED
))
return
0
;
...
...
@@ -994,6 +994,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai)
struct
cdrom_generic_command
cgc
;
struct
cdrom_device_ops
*
cdo
=
cdi
->
ops
;
memset
(
buf
,
0
,
sizeof
(
buf
));
init_cdrom_command
(
&
cgc
,
buf
,
0
,
CGC_DATA_READ
);
switch
(
ai
->
type
)
{
...
...
@@ -1052,7 +1053,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai)
case
DVD_LU_SEND_ASF
:
cdinfo
(
CD_DVD
,
"entering DVD_LU_SEND_ASF
\n
"
);
setup_report_key
(
&
cgc
,
ai
->
lsasf
.
a
sf
,
5
);
setup_report_key
(
&
cgc
,
ai
->
lsasf
.
a
gid
,
5
);
if
((
ret
=
cdo
->
generic_packet
(
cdi
,
&
cgc
)))
return
ret
;
...
...
@@ -1113,6 +1114,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai)
case
DVD_HOST_SEND_RPC_STATE
:
cdinfo
(
CD_DVD
,
"entering DVD_HOST_SEND_RPC_STATE
\n
"
);
setup_send_key
(
&
cgc
,
0
,
6
);
buf
[
1
]
=
6
;
buf
[
4
]
=
ai
->
hrpcs
.
pdrc
;
if
((
ret
=
cdo
->
generic_packet
(
cdi
,
&
cgc
)))
...
...
drivers/ide/ide-cd.c
View file @
5cb39380
...
...
@@ -282,9 +282,12 @@
* - cdrom_read_capacity returns one frame too little.
* - Fix real capacity reporting.
*
* 4.58 May 1, 2000 - Clean up ACER50 stuff.
* - Fix small problem with ide_cdrom_capacity
*
*************************************************************************/
#define IDECD_VERSION "4.5
7
"
#define IDECD_VERSION "4.5
8
"
#include <linux/config.h>
#include <linux/module.h>
...
...
@@ -1521,7 +1524,7 @@ cdrom_lockdoor(ide_drive_t *drive, int lockflag, struct request_sense *sense)
memset
(
&
pc
,
0
,
sizeof
(
pc
));
pc
.
sense
=
sense
;
pc
.
c
[
0
]
=
GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL
;
pc
.
c
[
4
]
=
(
lockflag
!=
0
)
;
pc
.
c
[
4
]
=
lockflag
?
3
:
0
;
stat
=
cdrom_queue_packet_command
(
drive
,
&
pc
);
}
...
...
@@ -1857,6 +1860,10 @@ static int ide_cdrom_packet(struct cdrom_device_info *cdi,
pc
.
buffer
=
cgc
->
buffer
;
pc
.
buflen
=
cgc
->
buflen
;
cgc
->
stat
=
cdrom_queue_packet_command
(
drive
,
&
pc
);
/*
* FIXME: copy sense, don't just assign pointer!!
*/
cgc
->
sense
=
pc
.
sense
;
return
cgc
->
stat
;
...
...
@@ -2159,9 +2166,9 @@ static int ide_cdrom_register (ide_drive_t *drive, int nslots)
{
struct
cdrom_info
*
info
=
drive
->
driver_data
;
struct
cdrom_device_info
*
devinfo
=
&
info
->
devinfo
;
int
minor
=
(
drive
->
select
.
b
.
unit
)
<<
PARTN_BITS
;
int
minor
=
(
drive
->
select
.
b
.
unit
)
<<
PARTN_BITS
;
devinfo
->
dev
=
MKDEV
(
HWIF
(
drive
)
->
major
,
minor
|
CD_PART_MASK
);
devinfo
->
dev
=
MKDEV
(
HWIF
(
drive
)
->
major
,
minor
);
devinfo
->
ops
=
&
ide_cdrom_dops
;
devinfo
->
mask
=
0
;
*
(
int
*
)
&
devinfo
->
speed
=
CDROM_STATE_FLAGS
(
drive
)
->
current_speed
;
...
...
@@ -2195,22 +2202,23 @@ static int ide_cdrom_register (ide_drive_t *drive, int nslots)
return
register_cdrom
(
devinfo
);
}
/*
* the buffer struct used by ide_cdrom_get_capabilities()
*/
struct
get_capabilities_buf
{
char
pad
[
8
];
struct
atapi_capabilities_page
cap
;
char
extra_cap
[
4
];
};
static
int
ide_cdrom_get_capabilities
(
ide_drive_t
*
drive
,
struct
atapi_capabilities_page
*
cap
)
{
struct
cdrom_info
*
info
=
drive
->
driver_data
;
struct
cdrom_device_info
*
cdi
=
&
info
->
devinfo
;
struct
cdrom_generic_command
cgc
;
int
stat
,
attempts
=
3
;
int
stat
,
attempts
=
3
,
size
=
sizeof
(
*
cap
);
/*
* ACER50 (and others?) require the full spec length mode sense
* page capabilities size, but older drives break.
*/
if
(
drive
->
id
)
{
if
(
!
(
!
strcmp
(
drive
->
id
->
model
,
"ATAPI CD ROM DRIVE 50X MAX"
)
||
!
strcmp
(
drive
->
id
->
model
,
"WPI CDS-32X"
)))
size
-=
sizeof
(
cap
->
pad
);
}
/* we have to cheat a little here. the packet will eventually
* be queued with ide_cdrom_packet(), which extracts the
...
...
@@ -2220,7 +2228,7 @@ int ide_cdrom_get_capabilities(ide_drive_t *drive, struct atapi_capabilities_pag
*/
cdi
->
handle
=
(
ide_drive_t
*
)
drive
;
cdi
->
ops
=
&
ide_cdrom_dops
;
init_cdrom_command
(
&
cgc
,
cap
,
size
of
(
*
cap
)
,
CGC_DATA_UNKNOWN
);
init_cdrom_command
(
&
cgc
,
cap
,
size
,
CGC_DATA_UNKNOWN
);
do
{
/* we seem to get stat=0x01,err=0x00 the first time (??) */
stat
=
cdrom_mode_sense
(
cdi
,
&
cgc
,
GPMODE_CAPABILITIES_PAGE
,
0
);
if
(
!
stat
)
...
...
@@ -2513,9 +2521,8 @@ void ide_cdrom_release (struct inode *inode, struct file *file,
static
int
ide_cdrom_check_media_change
(
ide_drive_t
*
drive
)
{
return
cdrom_fops
.
check_media_change
(
MKDEV
(
HWIF
(
drive
)
->
major
,
(
drive
->
select
.
b
.
unit
)
<<
PARTN_BITS
));
return
cdrom_fops
.
check_media_change
(
MKDEV
(
HWIF
(
drive
)
->
major
,
(
drive
->
select
.
b
.
unit
)
<<
PARTN_BITS
));
}
static
...
...
@@ -2545,8 +2552,7 @@ unsigned long ide_cdrom_capacity (ide_drive_t *drive)
{
unsigned
capacity
;
capacity
=
cdrom_read_capacity
(
drive
,
&
capacity
,
NULL
);
return
capacity
?
0
:
capacity
*
SECTORS_PER_FRAME
;
return
cdrom_read_capacity
(
drive
,
&
capacity
,
NULL
)
?
0
:
capacity
*
SECTORS_PER_FRAME
;
}
static
...
...
drivers/ide/ide-cd.h
View file @
5cb39380
...
...
@@ -10,15 +10,6 @@
#include <linux/cdrom.h>
#include <asm/byteorder.h>
/*
* Apparently older drives have problems with filling out the entire
* mode_sense capability structure. Define this to 1 if your drive isn't
* probed correctly.
*/
#ifndef BROKEN_CAP_PAGE
#define BROKEN_CAP_PAGE 0
#endif
/* Turn this on to have the driver print out the meanings of the
ATAPI error codes. This will use up additional kernel-space
memory, though. */
...
...
@@ -114,6 +105,7 @@ struct packet_command {
char
*
buffer
;
int
buflen
;
int
stat
;
int
quiet
;
struct
request_sense
*
sense
;
unsigned
char
c
[
12
];
};
...
...
@@ -410,9 +402,7 @@ struct atapi_capabilities_page {
unsigned
short
buffer_size
;
/* Current speed (in kB/s). */
unsigned
short
curspeed
;
#if !BROKEN_CAP_PAGE
char
pad
[
4
];
#endif
};
...
...
drivers/net/starfire.c
View file @
5cb39380
...
...
@@ -102,7 +102,7 @@ static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
static
char
version1
[]
__devinitdata
=
"starfire.c:v0.15+LK1.1.2 4/28/2000 Written by Donald Becker <becker@scyld.com>
\n
"
;
static
char
version2
[]
__devinitdata
=
" U
n
dates and info at http://www.scyld.com/network/starfire.html
\n
"
;
" U
p
dates and info at http://www.scyld.com/network/starfire.html
\n
"
;
MODULE_AUTHOR
(
"Donald Becker <becker@scyld.com>"
);
MODULE_DESCRIPTION
(
"Adaptec Starfire Ethernet driver"
);
...
...
drivers/sound/Config.in
View file @
5cb39380
...
...
@@ -79,6 +79,8 @@ if [ "$CONFIG_SOUND_MSNDPIN" = "y" -o "$CONFIG_SOUND_MSNDCLAS" = "y" ]; then
int 'MSND buffer size (kB)' CONFIG_MSND_FIFOSIZE 128
fi
tristate ' VIA 82C686 Audio Codec' CONFIG_SOUND_VIA82CXXX
dep_tristate ' OSS sound modules' CONFIG_SOUND_OSS $CONFIG_SOUND
if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND_OSS" = "m" ]; then
...
...
@@ -144,7 +146,6 @@ if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND_OSS" = "m" ]; then
fi
fi
dep_tristate ' VIA 82C686 Audio Codec' CONFIG_SOUND_VIA82CXXX $CONFIG_SOUND_OSS
dep_tristate ' Yamaha FM synthesizer (YM3812/OPL-3) support' CONFIG_SOUND_YM3812 $CONFIG_SOUND_OSS
dep_tristate ' Yamaha OPL3-SA1 audio controller' CONFIG_SOUND_OPL3SA1 $CONFIG_SOUND_OSS
dep_tristate ' Yamaha OPL3-SA2, SA3, and SAx based PnP cards' CONFIG_SOUND_OPL3SA2 $CONFIG_SOUND_OSS
...
...
drivers/sound/Makefile
View file @
5cb39380
...
...
@@ -66,7 +66,7 @@ obj-$(CONFIG_SOUND_AD1816) += ad1816.o
obj-$(CONFIG_SOUND_ACI_MIXER)
+=
aci.o
obj-$(CONFIG_SOUND_AWE32_SYNTH)
+=
awe_wave.o
obj-$(CONFIG_SOUND_VIA82CXXX)
+=
via82cxxx_audio.o
sb_lib.o uart401.o ac97
.o
obj-$(CONFIG_SOUND_VIA82CXXX)
+=
via82cxxx_audio.o
ac97_codec
.o
obj-$(CONFIG_SOUND_MSNDCLAS)
+=
msnd.o msnd_classic.o
obj-$(CONFIG_SOUND_MSNDPIN)
+=
msnd.o msnd_pinnacle.o
obj-$(CONFIG_SOUND_VWSND)
+=
vwsnd.o
...
...
drivers/sound/emu10k1/main.c
View file @
5cb39380
...
...
@@ -80,7 +80,7 @@ static char *card_names[] __devinitdata = {
"EMU10K1"
,
};
static
struct
pci_device_id
emu10k1_pci_tbl
[]
__initdata
=
{
static
struct
pci_device_id
emu10k1_pci_tbl
[]
=
{
{
PCI_VENDOR_ID_CREATIVE
,
PCI_DEVICE_ID_CREATIVE_EMU10K1
,
PCI_ANY_ID
,
PCI_ANY_ID
,
0
,
0
,
EMU10K1
},
{
0
,}
...
...
@@ -763,7 +763,7 @@ static void __devexit emu10k1_remove(struct pci_dev *pci_dev)
MODULE_AUTHOR
(
"Bertrand Lee, Cai Ying. (Email to: emu10k1-devel@opensource.creative.com)"
);
MODULE_DESCRIPTION
(
"Creative EMU10K1 PCI Audio Driver v"
DRIVER_VERSION
"
\n
Copyright (C) 1999 Creative Technology Ltd."
);
static
struct
pci_driver
emu10k1_pci_driver
__initdata
=
{
static
struct
pci_driver
emu10k1_pci_driver
=
{
name:
"emu10k1"
,
id_table:
emu10k1_pci_tbl
,
probe:
emu10k1_probe
,
...
...
drivers/sound/soundmodule.h
View file @
5cb39380
...
...
@@ -26,4 +26,4 @@ static struct notifier_block sound_notifier=
0
};
#endif
#endif
/* _SOUNDMODULE_H */
drivers/sound/via82cxxx_audio.c
View file @
5cb39380
...
...
@@ -5,51 +5,45 @@
* Distributed under the GNU GENERAL PUBLIC LICENSE (GPL) Version 2.
* See the "COPYING" file distributed with this software for more info.
*
*
Documentation for this driver available as
*
linux/Documentation/sound/via82cxxx.txt
.
*
For a list of known bugs (errata) and documentation,
*
see via82cxxx.txt in linux/Documentation/sound
.
*
* Since the mixer is called from the OSS glue the kernel lock is always held
* on our AC97 mixing
*/
#define VIA_VERSION "1.1.
2.1
"
#define VIA_VERSION "1.1.
5
"
#include <linux/module.h>
#include <linux/config.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/sound.h>
#include <linux/soundcard.h>
#include <linux/ac97_codec.h>
#include <asm/io.h>
#include <asm/delay.h>
#include <asm/uaccess.h>
#include <asm/hardirq.h>
#include "sound_config.h"
#include "soundmodule.h"
#include "sb.h"
#include "ac97.h"
#include "mpu401.h"
#ifndef SOUND_LOCK
#define SOUND_LOCK do {} while (0)
#define SOUND_LOCK_END do {} while (0)
#endif
#
define VIA_DEBUG 0
/* define to 1
to enable debugging output and checks */
#if VIA_DEBUG
#
undef VIA_DEBUG
/* define
to enable debugging output and checks */
#if
def
VIA_DEBUG
/* note: prints function name for you */
#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
#else
#define DPRINTK(fmt, args...)
#endif
#define VIA_NDEBUG
0
/* define to 1
to disable lightweight runtime checks */
#if VIA_NDEBUG
#define VIA_NDEBUG
/* define
to disable lightweight runtime checks */
#if
def
VIA_NDEBUG
#define assert(expr)
#else
#define assert(expr) \
...
...
@@ -59,29 +53,109 @@
}
#endif
/* user switch: undefine to exclude /proc data */
#define VIA_PROC_FS 1
/* don't mess with this */
#ifndef CONFIG_PROC_FS
#undef VIA_PROC_FS
#endif
#define arraysize(x) (sizeof(x)/sizeof(*(x)))
#define MAX_CARDS
2
#define MAX_CARDS
1
#define LINE_SIZE 10
#define VIA_CARD_NAME "VIA 82Cxxx Audio driver " VIA_VERSION
#define VIA_MODULE_NAME "via
_audio
"
#define VIA_MODULE_NAME "via
82cxxx
"
#define PFX VIA_MODULE_NAME ": "
#define VIA_COUNTER_LIMIT 100000
/* size of DMA buffers */
#define VIA_DMA_BUFFERS 16
#define VIA_DMA_BUF_SIZE PAGE_SIZE
/* 82C686 function 5 (audio codec) PCI configuration registers */
#define VIA_ACLINK_CTRL 0x41
#define VIA_FUNC_ENABLE 0x42
#define VIA_PNP_CONTROL 0x43
#define VIA_AC97_CTRL 0x80
#define VIA_FM_NMI_CTRL 0x48
/*
* controller base 0 (scatter-gather) registers
*
* NOTE: Via datasheet lists first channel as "read"
* channel and second channel as "write" channel.
* I changed the naming of the constants to be more
* clear than I felt the datasheet to be.
*/
#define VIA_BASE0_PCM_OUT_CHAN 0x00
/* output PCM to user */
#define VIA_BASE0_PCM_OUT_CHAN_STATUS 0x00
#define VIA_BASE0_PCM_OUT_CHAN_CTRL 0x01
#define VIA_BASE0_PCM_OUT_CHAN_TYPE 0x02
#define VIA_BASE0_PCM_OUT_BLOCK_COUNT 0x0C
#define VIA_BASE0_PCM_IN_CHAN 0x10
/* input PCM from user */
#define VIA_BASE0_PCM_IN_CHAN_STATUS 0x10
#define VIA_BASE0_PCM_IN_CHAN_CTRL 0x11
#define VIA_BASE0_PCM_IN_CHAN_TYPE 0x12
/* offsets from base */
#define VIA_PCM_STATUS 0x00
#define VIA_PCM_CONTROL 0x01
#define VIA_PCM_TYPE 0x02
#define VIA_PCM_TABLE_ADDR 0x04
/* XXX unused DMA channel for FM PCM data */
#define VIA_BASE0_FM_OUT_CHAN 0x20
#define VIA_BASE0_FM_OUT_CHAN_STATUS 0x20
#define VIA_BASE0_FM_OUT_CHAN_CTRL 0x21
#define VIA_BASE0_FM_OUT_CHAN_TYPE 0x22
#define VIA_BASE0_AC97_CTRL 0x80
#define VIA_BASE0_SGD_STATUS_SHADOW 0x84
#define VIA_BASE0_GPI_INT_ENABLE 0x8C
/* VIA_BASE0_AUDIO_xxx_CHAN_TYPE bits */
#define VIA_IRQ_ON_FLAG (1<<0)
/* int on each flagged scatter block */
#define VIA_IRQ_ON_EOL (1<<1)
/* int at end of scatter list */
#define VIA_INT_SEL_PCI_LAST_LINE_READ (0)
/* int at PCI read of last line */
#define VIA_INT_SEL_LAST_SAMPLE_SENT (1<<2)
/* int at last sample sent */
#define VIA_INT_SEL_ONE_LINE_LEFT (1<<3)
/* int at less than one line to send */
#define VIA_PCM_FMT_STEREO (1<<4)
/* PCM stereo format (bit clear == mono) */
#define VIA_PCM_FMT_16BIT (1<<5)
/* PCM 16-bit format (bit clear == 8-bit) */
#define VIA_PCM_FIFO (1<<6)
/* enable FIFO? documented as "reserved" */
#define VIA_RESTART_SGD_ON_EOL (1<<7)
/* restart scatter-gather at EOL */
#define VIA_PCM_FMT_MASK (VIA_PCM_FMT_STEREO|VIA_PCM_FMT_16BIT)
#define VIA_CHAN_TYPE_MASK (VIA_RESTART_SGD_ON_EOL | \
VIA_PCM_FIFO | \
VIA_IRQ_ON_FLAG | \
VIA_IRQ_ON_EOL)
#define VIA_CHAN_TYPE_INT_SELECT (VIA_INT_SEL_LAST_SAMPLE_SENT)
/* PCI configuration register bits and masks */
#define VIA_CR40_AC97_READY 0x01
#define VIA_CR40_AC97_LOW_POWER 0x02
#define VIA_CR40_SECONDARY_READY 0x04
#define VIA_CR41_ACLINK_ENABLE 0x80
#define VIA_CR41_AC97_ENABLE 0x80
/* enable AC97 codec */
#define VIA_CR41_AC97_RESET 0x40
/* clear bit to reset AC97 */
#define VIA_CR41_AC97_WAKEUP 0x20
/* wake up from power-down mode */
#define VIA_CR41_AC97_SDO 0x10
/* force Serial Data Out (SDO) high */
#define VIA_CR41_VRA 0x08
/* enable variable sample rate */
#define VIA_CR41_PCM_ENABLE 0x04
/* AC Link SGD Read Channel PCM Data Output */
#define VIA_CR41_FM_PCM_ENABLE 0x02
/* AC Link FM Channel PCM Data Out */
#define VIA_CR41_SB_PCM_ENABLE 0x01
/* AC Link SB PCM Data Output */
#define VIA_CR41_BOOT_MASK (VIA_CR41_AC97_ENABLE | \
VIA_CR41_AC97_WAKEUP | \
VIA_CR41_AC97_SDO)
#define VIA_CR41_RUN_MASK (VIA_CR41_AC97_ENABLE | \
VIA_CR41_AC97_RESET | \
VIA_CR41_VRA | \
VIA_CR41_PCM_ENABLE)
#define VIA_CR42_SB_ENABLE 0x01
#define VIA_CR42_MIDI_ENABLE 0x02
...
...
@@ -91,272 +165,1937 @@
#define VIA_CR44_SECOND_CODEC_SUPPORT (1 << 6)
#define VIA_CR44_AC_LINK_ACCESS (1 << 7)
#define VIA_CR48_FM_TRAP_TO_NMI (1 << 2)
/* controller base 0 register bitmasks */
#define VIA_INT_DISABLE_MASK (~(0x01|0x02))
#define VIA_SGD_STOPPED (1 << 2)
#define VIA_SGD_ACTIVE (1 << 7)
#define VIA_SGD_TERMINATE (1 << 6)
#define VIA_SGD_FLAG (1 << 0)
#define VIA_SGD_EOL (1 << 1)
#define VIA_SGD_START (1 << 7)
#define VIA_CR80_FIRST_CODEC 0
#define VIA_CR80_SECOND_CODEC (1 << 30)
#define VIA_CR80_FIRST_CODEC_VALID (1 << 25)
#define VIA_CR80_VALID (1 << 25)
#define VIA_CR80_SECOND_CODEC_VALID (1 << 27)
#define VIA_CR80_BUSY (1 << 24)
#define VIA_CR80_READ_MODE (1 << 23)
#define VIA_CR83_BUSY (1)
#define VIA_CR83_FIRST_CODEC_VALID (1 << 1)
#define VIA_CR80_READ (1 << 23)
#define VIA_CR80_WRITE_MODE 0
#define VIA_CR80_REG_IDX(idx) (((idx) & 0x7E) << 16)
#define VIA_CR80_REG_IDX(idx) ((((idx) & 0xFF) >> 1) << 16)
/* h r puff n stuff */
#define VIA_FMT_STEREO 0x01
#define VIA_FMT_16BIT 0x02
#define VIA_FMT_MASK 0x03
#define VIA_DAC_SHIFT 0
#define VIA_ADC_SHIFT 4
/* undocumented(?) values for setting rate, from Via's source */
#define VIA_SET_RATE_IN 0x00320000
/* set input rate */
#define VIA_SET_RATE_OUT 0x002c0000
/* set output rate */
/* scatter-gather DMA table entry, exactly as passed to hardware */
struct
via_sgd_table
{
u32
addr
;
u32
count
;
/* includes additional bits also */
};
#define VIA_EOL (1 << 31)
#define VIA_FLAG (1 << 30)
enum
via_channel_states
{
sgd_stopped
=
0
,
sgd_in_progress
=
1
,
};
struct
via_sgd_data
{
dma_addr_t
handle
;
volatile
void
*
cpuaddr
;
};
struct
via_channel
{
unsigned
rate
;
/* sample rate */
u8
pcm_fmt
;
/* VIA_PCM_FMT_xxx */
atomic_t
state
;
atomic_t
buf_in_use
;
atomic_t
next_buf
;
volatile
struct
via_sgd_table
*
sgtable
;
dma_addr_t
sgt_handle
;
struct
via_sgd_data
sgbuf
[
VIA_DMA_BUFFERS
];
wait_queue_head_t
wait
;
long
iobase
;
};
/* data stored for each chip */
struct
via_info
{
struct
address_info
sb_data
;
struct
address_info
opl3_data
;
struct
pci_dev
*
pdev
;
struct
ac97_hwint
ac97
;
int
mixer_oss_dev
;
int
have_ac97
;
long
baseaddr
;
struct
ac97_codec
ac97
;
spinlock_t
lock
;
int
card_num
;
/* unique card number, from 0 */
int
dev_dsp
;
/* /dev/dsp index from register_sound_dsp() */
unsigned
rev_h
:
1
;
wait_queue_head_t
open_wait
;
int
open_mode
;
struct
via_channel
ch_in
;
struct
via_channel
ch_out
;
};
/* number of cards, used for assigning unique numbers to cards */
static
unsigned
via_num_cards
=
0
;
/****************************************************************
*
* prototypes
*
*
*/
static
int
via_init_one
(
struct
pci_dev
*
dev
,
const
struct
pci_device_id
*
id
);
static
void
via_remove_one
(
struct
pci_dev
*
pdev
);
static
ssize_t
via_dsp_read
(
struct
file
*
file
,
char
*
buffer
,
size_t
count
,
loff_t
*
ppos
);
static
ssize_t
via_dsp_write
(
struct
file
*
file
,
const
char
*
buffer
,
size_t
count
,
loff_t
*
ppos
);
static
unsigned
int
via_dsp_poll
(
struct
file
*
file
,
struct
poll_table_struct
*
wait
);
static
int
via_dsp_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
);
static
int
via_dsp_open
(
struct
inode
*
inode
,
struct
file
*
file
);
static
int
via_dsp_release
(
struct
inode
*
inode
,
struct
file
*
file
);
static
u16
via_ac97_read_reg
(
struct
ac97_codec
*
codec
,
u8
reg
);
static
void
via_ac97_write_reg
(
struct
ac97_codec
*
codec
,
u8
reg
,
u16
value
);
static
u8
via_ac97_wait_idle
(
struct
via_info
*
card
);
static
u32
via_ac97_wait_valid
(
struct
via_info
*
card
);
static
void
via_chan_free
(
struct
via_info
*
card
,
struct
via_channel
*
chan
);
static
void
via_chan_clear
(
struct
via_channel
*
chan
);
static
void
via_chan_pcm_fmt
(
struct
via_info
*
card
,
struct
via_channel
*
chan
,
int
reset
);
/****************************************************************
*
* Various data the driver needs
*
*
*/
static
struct
pci_device_id
via_pci_tbl
[]
__initdata
=
{
{
PCI_VENDOR_ID_VIA
,
PCI_DEVICE_ID_VIA_82C686_5
,
PCI_ANY_ID
,
PCI_ANY_ID
,
0
,
0
,
0
},
{
0
,
},
};
static
struct
via_info
cards
[
MAX_CARDS
];
static
unsigned
num_cards
=
0
;
static
const
struct
{
int
revision
;
const
char
*
rev_name
;
}
via_chip_revs
[]
__initdata
=
{
{
0x10
,
"A"
},
{
0x11
,
"B"
},
{
0x12
,
"C"
},
{
0x13
,
"D"
},
{
0x14
,
"E"
},
{
0x20
,
"H"
},
MODULE_DEVICE_TABLE
(
pci
,
via_pci_tbl
);
static
struct
pci_driver
via_driver
=
{
name:
VIA_MODULE_NAME
,
id_table:
via_pci_tbl
,
probe:
via_init_one
,
remove:
via_remove_one
,
};
static
inline
void
via_ac97_write32
(
struct
pci_dev
*
pdev
,
int
port
,
u32
data
)
/****************************************************************
*
* Low-level base 0 register read/write helpers
*
*
*/
static
inline
void
via_chan_stop
(
int
iobase
)
{
if
(
inb
(
iobase
+
VIA_PCM_STATUS
)
&
VIA_SGD_ACTIVE
)
outb
(
VIA_SGD_TERMINATE
,
iobase
+
VIA_PCM_CONTROL
);
}
static
inline
void
via_chan_status_clear
(
int
iobase
)
{
u8
tmp
=
inb
(
iobase
+
VIA_PCM_STATUS
);
if
(
tmp
!=
0
)
outb
(
tmp
,
iobase
+
VIA_PCM_STATUS
);
}
static
inline
void
sg_begin
(
struct
via_channel
*
chan
)
{
outb
(
VIA_SGD_START
,
chan
->
iobase
+
VIA_PCM_CONTROL
);
}
static
inline
int
via_chan_bufs_in_use
(
struct
via_channel
*
chan
)
{
return
atomic_read
(
&
chan
->
next_buf
)
-
atomic_read
(
&
chan
->
buf_in_use
);
}
static
inline
int
via_chan_full
(
struct
via_channel
*
chan
)
{
struct
resource
*
rsrc
=
&
pdev
->
resource
[
0
];
outw
((
u16
)
data
,
rsrc
->
start
+
port
);
outw
((
u16
)(
data
>>
16
),
rsrc
->
start
+
port
+
2
);
return
(
via_chan_bufs_in_use
(
chan
)
==
VIA_DMA_BUFFERS
);
}
static
inline
u32
via_ac97_read32
(
struct
pci_dev
*
pdev
,
int
port
)
static
inline
int
via_chan_empty
(
struct
via_channel
*
chan
)
{
struct
resource
*
rsrc
=
&
pdev
->
resource
[
0
];
return
((
u32
)
inw
(
rsrc
->
start
+
port
))
|
(((
u32
)
inw
(
rsrc
->
start
+
port
+
2
))
<<
16
);
return
(
atomic_read
(
&
chan
->
next_buf
)
==
atomic_read
(
&
chan
->
buf_in_use
));
}
/****************************************************************
*
*
Intel Audio Codec '97 interface
*
Miscellaneous debris
*
*
*/
static
inline
void
via_ac97_wait_idle
(
struct
pci_dev
*
pdev
)
static
void
via_stop_everything
(
struct
via_info
*
card
)
{
u32
tmp
;
int
counter
=
VIA_COUNTER_LIMIT
;
DPRINTK
(
"ENTER
\n
"
);
assert
(
card
!=
NULL
);
/*
* terminate any existing operations on audio read/write channels
*/
via_chan_stop
(
card
->
baseaddr
+
VIA_BASE0_PCM_OUT_CHAN
);
via_chan_stop
(
card
->
baseaddr
+
VIA_BASE0_PCM_IN_CHAN
);
via_chan_stop
(
card
->
baseaddr
+
VIA_BASE0_FM_OUT_CHAN
);
/*
* clear any existing stops / flags (sanity check mainly)
*/
via_chan_status_clear
(
card
->
baseaddr
+
VIA_BASE0_PCM_OUT_CHAN
);
via_chan_status_clear
(
card
->
baseaddr
+
VIA_BASE0_PCM_IN_CHAN
);
via_chan_status_clear
(
card
->
baseaddr
+
VIA_BASE0_FM_OUT_CHAN
);
/*
* clear any enabled interrupt bits, reset to 8-bit mono PCM mode
*/
outb
(
0
,
card
->
baseaddr
+
VIA_BASE0_PCM_OUT_CHAN
);
outb
(
0
,
card
->
baseaddr
+
VIA_BASE0_PCM_IN_CHAN
);
outb
(
0
,
card
->
baseaddr
+
VIA_BASE0_FM_OUT_CHAN
);
DPRINTK
(
"EXIT
\n
"
);
}
static
int
via_set_rate
(
struct
via_info
*
card
,
unsigned
rate
,
int
inhale_deeply
)
{
#if 0
unsigned long flags;
u32 status;
u8 status8;
#endif
DPRINTK
(
"ENTER, rate = %d, inhale = %s
\n
"
,
rate
,
inhale_deeply
?
"yes"
:
"no"
);
if
(
rate
>
48000
)
rate
=
48000
;
if
(
rate
<
4000
)
rate
=
4000
;
#if 0
status8 = via_ac97_wait_idle (card);
if (status8 & VIA_CR83_BUSY) {
DPRINTK ("EXIT, status=0x%X, returning -EIO\n", status);
return -EIO;
}
if (inhale_deeply) {
card->ch_in.rate = rate;
spin_lock_irqsave (&card->lock, flags);
outl (VIA_SET_RATE_IN + rate,
card->baseaddr + VIA_BASE0_AC97_CTRL);
spin_unlock_irqrestore (&card->lock, flags);
} else {
card->ch_out.rate = rate;
spin_lock_irqsave (&card->lock, flags);
outl (VIA_SET_RATE_OUT + rate,
card->baseaddr + VIA_BASE0_AC97_CTRL);
spin_unlock_irqrestore (&card->lock, flags);
}
#else
via_ac97_write_reg
(
&
card
->
ac97
,
AC97_POWER_CONTROL
,
(
via_ac97_read_reg
(
&
card
->
ac97
,
AC97_POWER_CONTROL
)
&
~
0x0200
)
|
0x0200
);
via_ac97_write_reg
(
&
card
->
ac97
,
AC97_PCM_FRONT_DAC_RATE
,
rate
);
via_ac97_write_reg
(
&
card
->
ac97
,
AC97_POWER_CONTROL
,
via_ac97_read_reg
(
&
card
->
ac97
,
AC97_POWER_CONTROL
)
&
~
0x0200
);
#endif
DPRINTK
(
"EXIT, returning 0
\n
"
);
return
rate
;
}
static
inline
int
via_set_adc_rate
(
struct
via_info
*
card
,
int
rate
)
{
return
via_set_rate
(
card
,
rate
,
1
);
}
static
inline
int
via_set_dac_rate
(
struct
via_info
*
card
,
int
rate
)
{
return
via_set_rate
(
card
,
rate
,
0
);
}
/****************************************************************
*
* Channel-specific operations
*
*
*/
static
int
via_chan_init
(
struct
via_info
*
card
,
struct
via_channel
*
chan
,
long
chan_ofs
)
{
int
i
;
unsigned
long
flags
;
DPRINTK
(
"ENTER
\n
"
);
memset
(
chan
,
0
,
sizeof
(
*
chan
));
/* alloc DMA-able memory for scatter-gather table */
chan
->
sgtable
=
pci_alloc_consistent
(
card
->
pdev
,
(
sizeof
(
struct
via_sgd_table
)
*
VIA_DMA_BUFFERS
),
&
chan
->
sgt_handle
);
if
(
!
chan
->
sgtable
)
{
printk
(
KERN_ERR
PFX
"DMA table alloc fail, aborting
\n
"
);
DPRINTK
(
"EXIT
\n
"
);
return
-
ENOMEM
;
}
memset
((
void
*
)
chan
->
sgtable
,
0
,
(
sizeof
(
struct
via_sgd_table
)
*
VIA_DMA_BUFFERS
));
/* alloc DMA-able memory for scatter-gather buffers */
for
(
i
=
0
;
i
<
VIA_DMA_BUFFERS
;
i
++
)
{
chan
->
sgbuf
[
i
].
cpuaddr
=
pci_alloc_consistent
(
card
->
pdev
,
VIA_DMA_BUF_SIZE
,
&
chan
->
sgbuf
[
i
].
handle
);
if
(
!
chan
->
sgbuf
[
i
].
cpuaddr
)
goto
err_out_nomem
;
if
(
i
<
(
VIA_DMA_BUFFERS
-
1
))
chan
->
sgtable
[
i
].
count
=
cpu_to_le32
(
VIA_DMA_BUF_SIZE
|
VIA_FLAG
);
else
chan
->
sgtable
[
i
].
count
=
cpu_to_le32
(
VIA_DMA_BUF_SIZE
|
VIA_EOL
);
chan
->
sgtable
[
i
].
addr
=
cpu_to_le32
(
chan
->
sgbuf
[
i
].
handle
);
#ifndef VIA_NDEBUG
memset
(
chan
->
sgbuf
[
i
].
cpuaddr
,
0xBC
,
VIA_DMA_BUF_SIZE
);
#endif
#if 1
DPRINTK
(
"dmabuf #%d (h=%lx, 32(h)=%lx, v2p=%lx, a=%p)
\n
"
,
i
,
(
long
)
chan
->
sgbuf
[
i
].
handle
,
(
long
)
chan
->
sgtable
[
i
].
addr
,
virt_to_phys
(
chan
->
sgbuf
[
i
].
cpuaddr
),
chan
->
sgbuf
[
i
].
cpuaddr
);
#endif
}
init_waitqueue_head
(
&
chan
->
wait
);
chan
->
pcm_fmt
=
VIA_PCM_FMT_MASK
;
chan
->
iobase
=
card
->
baseaddr
+
chan_ofs
;
spin_lock_irqsave
(
&
card
->
lock
,
flags
);
/* stop any existing channel output */
via_chan_clear
(
chan
);
via_chan_status_clear
(
chan
->
iobase
);
via_chan_pcm_fmt
(
card
,
chan
,
1
);
spin_unlock_irqrestore
(
&
card
->
lock
,
flags
);
/* set location of DMA-able scatter-gather info table */
DPRINTK
(
"outl (0x%X, 0x%04lX)
\n
"
,
cpu_to_le32
(
chan
->
sgt_handle
),
card
->
baseaddr
+
chan_ofs
+
VIA_PCM_TABLE_ADDR
);
via_ac97_wait_idle
(
card
);
outl
(
cpu_to_le32
(
chan
->
sgt_handle
),
card
->
baseaddr
+
chan_ofs
+
VIA_PCM_TABLE_ADDR
);
udelay
(
20
);
via_ac97_wait_idle
(
card
);
DPRINTK
(
"inl (0x%lX) = %x
\n
"
,
chan
->
iobase
+
VIA_PCM_TABLE_ADDR
,
inl
(
chan
->
iobase
+
VIA_PCM_TABLE_ADDR
));
DPRINTK
(
"EXIT
\n
"
);
return
0
;
err_out_nomem:
printk
(
KERN_ERR
PFX
"DMA buffer alloc fail, aborting
\n
"
);
via_chan_free
(
card
,
chan
);
DPRINTK
(
"EXIT
\n
"
);
return
-
ENOMEM
;
}
static
void
via_chan_free
(
struct
via_info
*
card
,
struct
via_channel
*
chan
)
{
int
i
;
unsigned
long
flags
;
DPRINTK
(
"ENTER
\n
"
);
synchronize_irq
();
spin_lock_irqsave
(
&
card
->
lock
,
flags
);
/* stop any existing channel output */
via_chan_stop
(
chan
->
iobase
);
via_chan_status_clear
(
chan
->
iobase
);
via_chan_pcm_fmt
(
card
,
chan
,
1
);
spin_unlock_irqrestore
(
&
card
->
lock
,
flags
);
/* zero location of DMA-able scatter-gather info table */
via_ac97_wait_idle
(
card
);
outl
(
0
,
chan
->
iobase
+
VIA_PCM_TABLE_ADDR
);
for
(
i
=
0
;
i
<
VIA_DMA_BUFFERS
;
i
++
)
if
(
chan
->
sgbuf
[
i
].
cpuaddr
)
{
pci_free_consistent
(
card
->
pdev
,
VIA_DMA_BUF_SIZE
,
(
void
*
)
chan
->
sgbuf
[
i
].
cpuaddr
,
chan
->
sgbuf
[
i
].
handle
);
chan
->
sgbuf
[
i
].
cpuaddr
=
NULL
;
chan
->
sgbuf
[
i
].
handle
=
0
;
}
if
(
chan
->
sgtable
)
{
pci_free_consistent
(
card
->
pdev
,
(
sizeof
(
struct
via_sgd_table
)
*
VIA_DMA_BUFFERS
),
(
void
*
)
chan
->
sgtable
,
chan
->
sgt_handle
);
chan
->
sgtable
=
NULL
;
}
DPRINTK
(
"EXIT
\n
"
);
}
static
void
via_chan_pcm_fmt
(
struct
via_info
*
card
,
struct
via_channel
*
chan
,
int
reset
)
{
DPRINTK
(
"ENTER, pcm_fmt=0x%02X, reset=%s
\n
"
,
chan
->
pcm_fmt
,
reset
?
"yes"
:
"no"
);
assert
(
card
!=
NULL
);
assert
(
chan
!=
NULL
);
if
(
reset
)
/* reset to 8-bit mono mode */
chan
->
pcm_fmt
=
0
;
/* enable interrupts on FLAG and EOL */
chan
->
pcm_fmt
|=
VIA_CHAN_TYPE_MASK
;
/* set interrupt select bits where applicable (PCM & FM out channels) */
if
(
chan
==
&
card
->
ch_out
)
chan
->
pcm_fmt
|=
VIA_CHAN_TYPE_INT_SELECT
;
outb
(
chan
->
pcm_fmt
,
chan
->
iobase
+
2
);
DPRINTK
(
"EXIT, pcm_fmt = 0x%02X, reg = 0x%02X
\n
"
,
chan
->
pcm_fmt
,
inb
(
chan
->
iobase
+
2
));
}
static
void
via_chan_clear
(
struct
via_channel
*
chan
)
{
via_chan_stop
(
chan
->
iobase
);
atomic_set
(
&
chan
->
state
,
sgd_stopped
);
atomic_set
(
&
chan
->
buf_in_use
,
0
);
atomic_set
(
&
chan
->
next_buf
,
0
);
}
static
int
via_chan_set_speed
(
struct
via_info
*
card
,
struct
via_channel
*
chan
,
int
val
)
{
DPRINTK
(
"ENTER, requested rate = %d
\n
"
,
val
);
via_chan_clear
(
chan
);
val
=
via_set_rate
(
card
,
val
,
chan
==
&
card
->
ch_in
);
DPRINTK
(
"EXIT, returning %d
\n
"
,
val
);
return
val
;
}
static
int
via_chan_set_fmt
(
struct
via_info
*
card
,
struct
via_channel
*
chan
,
int
val
)
{
DPRINTK
(
"ENTER, val=%s
\n
"
,
val
==
AFMT_U8
?
"AFMT_U8"
:
val
==
AFMT_S16_LE
?
"AFMT_S16_LE"
:
"unknown"
);
via_chan_clear
(
chan
);
switch
(
val
)
{
case
AFMT_U8
:
chan
->
pcm_fmt
&=
~
VIA_PCM_FMT_16BIT
;
via_chan_pcm_fmt
(
card
,
chan
,
0
);
break
;
case
AFMT_S16_LE
:
chan
->
pcm_fmt
|=
VIA_PCM_FMT_16BIT
;
via_chan_pcm_fmt
(
card
,
chan
,
0
);
break
;
default:
printk
(
KERN_WARNING
PFX
"unknown AFMT
\n
"
);
val
=
-
EINVAL
;
break
;
}
DPRINTK
(
"EXIT, returning %d
\n
"
,
val
);
return
val
;
}
static
int
via_chan_set_stereo
(
struct
via_info
*
card
,
struct
via_channel
*
chan
,
int
val
)
{
DPRINTK
(
"ENTER, channels = %d
\n
"
,
val
);
via_chan_clear
(
chan
);
switch
(
val
)
{
/* mono */
case
1
:
chan
->
pcm_fmt
&=
~
VIA_PCM_FMT_STEREO
;
via_chan_pcm_fmt
(
card
,
chan
,
0
);
break
;
/* stereo */
case
2
:
chan
->
pcm_fmt
|=
VIA_PCM_FMT_STEREO
;
via_chan_pcm_fmt
(
card
,
chan
,
0
);
break
;
/* unknown */
default:
printk
(
KERN_WARNING
PFX
"unknown number of channels
\n
"
);
val
=
-
EINVAL
;
break
;
}
DPRINTK
(
"EXIT, returning %d
\n
"
,
val
);
return
val
;
}
#if 0
static void via_chan_dump_bufs (struct via_channel *chan)
{
int i;
for (i = 0; i < VIA_DMA_BUFFERS; i++) {
DPRINTK ("#%02d: addr=%x, count=%u, flag=%d, eol=%d\n",
i, chan->sgtable[i].addr,
chan->sgtable[i].count & 0x00FFFFFF,
chan->sgtable[i].count & VIA_FLAG ? 1 : 0,
chan->sgtable[i].count & VIA_EOL ? 1 : 0);
}
DPRINTK ("buf_in_use = %d, nextbuf = %d\n",
atomic_read (&chan->buf_in_use),
atomic_read (&chan->next_buf));
}
#endif
/****************************************************************
*
* Interface to ac97-codec module
*
*
*/
static
u8
via_ac97_wait_idle
(
struct
via_info
*
card
)
{
u8
tmp8
;
int
counter
=
VIA_COUNTER_LIMIT
;
DPRINTK
(
"ENTER/EXIT
\n
"
);
assert
(
card
!=
NULL
);
assert
(
card
->
pdev
!=
NULL
);
do
{
if
(
current
->
need_resched
)
schedule
();
else
udelay
(
10
);
spin_lock_irq
(
&
card
->
lock
);
tmp8
=
inb
(
card
->
baseaddr
+
0x83
);
spin_unlock_irq
(
&
card
->
lock
);
}
while
((
tmp8
&
VIA_CR83_BUSY
)
&&
(
counter
--
>
0
));
return
tmp8
;
}
static
u32
via_ac97_wait_valid
(
struct
via_info
*
card
)
{
u32
tmp
;
int
counter
=
VIA_COUNTER_LIMIT
;
DPRINTK
(
"ENTER
\n
"
);
assert
(
card
!=
NULL
);
assert
(
card
->
pdev
!=
NULL
);
do
{
if
(
current
->
need_resched
)
schedule
();
spin_lock_irq
(
&
card
->
lock
);
tmp
=
inl
(
card
->
baseaddr
+
VIA_BASE0_AC97_CTRL
);
spin_unlock_irq
(
&
card
->
lock
);
udelay
(
10
);
if
(
tmp
&
VIA_CR80_FIRST_CODEC_VALID
)
{
DPRINTK
(
"EXIT valid, tmp=0x%X, cnt=%d
\n
"
,
tmp
,
counter
);
return
tmp
;
}
}
while
((
tmp
&
VIA_CR80_BUSY
)
&&
(
counter
--
>
0
));
DPRINTK
(
"EXIT, tmp=0x%X, cnt=%d%s
\n
"
,
tmp
,
counter
,
counter
>
0
?
""
:
", counter limit reached"
);
return
tmp
;
}
static
u16
via_ac97_read_reg
(
struct
ac97_codec
*
codec
,
u8
reg
)
{
u32
data
;
struct
via_info
*
card
;
int
counter
;
DPRINTK
(
"ENTER
\n
"
);
assert
(
codec
!=
NULL
);
assert
(
codec
->
private_data
!=
NULL
);
card
=
codec
->
private_data
;
data
=
(
reg
<<
16
)
|
VIA_CR80_READ
;
outl
(
data
,
card
->
baseaddr
+
VIA_BASE0_AC97_CTRL
);
udelay
(
20
);
for
(
counter
=
VIA_COUNTER_LIMIT
;
counter
>
0
;
counter
--
)
{
if
(
inl
(
card
->
baseaddr
+
0x80
)
&
VIA_CR80_VALID
)
goto
out
;
udelay
(
10
);
if
(
current
->
need_resched
)
schedule
();
}
printk
(
KERN_WARNING
PFX
"timeout while reading AC97 codec
\n
"
);
goto
err_out
;
out:
data
=
inl
(
card
->
baseaddr
+
0x80
);
outb
(
0x02
,
card
->
baseaddr
+
0x83
);
if
(((
data
&
0x007F0000
)
>>
16
)
==
reg
)
{
DPRINTK
(
"EXIT, success, data=0x%x, retval=0x%x
\n
"
,
data
,
data
&
0x0000FFFF
);
return
data
&
0x0000FFFF
;
}
DPRINTK
(
"WARNING: not our index: reg=0x%x, newreg=0x%x
\n
"
,
reg
,
((
data
&
0x007F0000
)
>>
16
));
err_out:
DPRINTK
(
"EXIT, returning 0
\n
"
);
return
0
;
}
static
void
via_ac97_write_reg
(
struct
ac97_codec
*
codec
,
u8
reg
,
u16
value
)
{
u32
data
;
struct
via_info
*
card
;
int
counter
;
DPRINTK
(
"ENTER
\n
"
);
assert
(
codec
!=
NULL
);
assert
(
codec
->
private_data
!=
NULL
);
card
=
codec
->
private_data
;
data
=
(
reg
<<
16
)
+
value
;
outl
(
data
,
card
->
baseaddr
+
VIA_BASE0_AC97_CTRL
);
udelay
(
20
);
for
(
counter
=
VIA_COUNTER_LIMIT
;
counter
>
0
;
counter
--
)
{
if
((
inb
(
card
->
baseaddr
+
0x83
)
&
VIA_CR83_BUSY
)
==
0
)
goto
out
;
udelay
(
10
);
if
(
current
->
need_resched
)
schedule
();
}
printk
(
KERN_WARNING
PFX
"timeout after AC97 codec write
\n
"
);
out:
DPRINTK
(
"EXIT
\n
"
);
}
static
int
via_mixer_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
int
minor
=
MINOR
(
inode
->
i_rdev
);
struct
via_info
*
card
;
struct
pci_dev
*
pdev
;
struct
pci_driver
*
drvr
;
DPRINTK
(
"ENTER
\n
"
);
MOD_INC_USE_COUNT
;
pci_for_each_dev
(
pdev
)
{
drvr
=
pci_dev_driver
(
pdev
);
if
(
drvr
==
&
via_driver
)
{
assert
(
pdev
->
driver_data
!=
NULL
);
card
=
pdev
->
driver_data
;
if
(
card
->
ac97
.
dev_mixer
==
minor
)
goto
match
;
}
}
DPRINTK
(
"EXIT, returning -ENODEV
\n
"
);
MOD_DEC_USE_COUNT
;
return
-
ENODEV
;
match:
file
->
private_data
=
&
card
->
ac97
;
DPRINTK
(
"EXIT, returning 0
\n
"
);
return
0
;
}
static
int
via_mixer_release
(
struct
inode
*
inode
,
struct
file
*
file
)
{
DPRINTK
(
"ENTER
\n
"
);
MOD_DEC_USE_COUNT
;
DPRINTK
(
"EXIT, returning 0
\n
"
);
return
0
;
}
static
int
via_mixer_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
struct
ac97_codec
*
codec
=
file
->
private_data
;
int
rc
;
assert
(
codec
!=
NULL
);
rc
=
codec
->
mixer_ioctl
(
codec
,
cmd
,
arg
);
return
rc
;
}
static
loff_t
via_llseek
(
struct
file
*
file
,
loff_t
offset
,
int
origin
)
{
DPRINTK
(
"ENTER
\n
"
);
DPRINTK
(
"EXIT, returning -ESPIPE
\n
"
);
return
-
ESPIPE
;
}
static
struct
file_operations
via_mixer_fops
=
{
open:
via_mixer_open
,
release:
via_mixer_release
,
llseek:
via_llseek
,
ioctl:
via_mixer_ioctl
,
};
static
struct
{
u8
reg
;
u16
data
;
}
mixer_init_vals
[]
__devinitdata
=
{
{
0x2
,
0x404
},
{
0x4
,
0x404
},
{
0x6
,
0x404
},
{
0x18
,
0x404
},
{
0x10
,
0x404
},
{
0x1a
,
0x404
},
{
0x1c
,
0x404
},
{
0x1a
,
0x404
},
{
0x1c
,
0xc0c
},
{
0x12
,
0x808
},
{
0x10
,
0x808
},
{
0xe
,
0x2
},
{
0x2
,
0x808
},
{
0x18
,
0x808
},
};
static
int
__init
via_ac97_reset
(
struct
via_info
*
card
)
{
struct
pci_dev
*
pdev
=
card
->
pdev
;
u8
tmp8
;
u16
tmp16
;
size_t
idx
;
DPRINTK
(
"ENTER
\n
"
);
assert
(
pdev
!=
NULL
);
#ifndef NDEBUG
{
u8
r40
,
r41
,
r42
,
r43
,
r44
,
r48
;
pci_read_config_byte
(
card
->
pdev
,
0x40
,
&
r40
);
pci_read_config_byte
(
card
->
pdev
,
0x41
,
&
r41
);
pci_read_config_byte
(
card
->
pdev
,
0x42
,
&
r42
);
pci_read_config_byte
(
card
->
pdev
,
0x43
,
&
r43
);
pci_read_config_byte
(
card
->
pdev
,
0x44
,
&
r44
);
pci_read_config_byte
(
card
->
pdev
,
0x48
,
&
r48
);
DPRINTK
(
"PCI config: %02X %02X %02X %02X %02X %02X
\n
"
,
r40
,
r41
,
r42
,
r43
,
r44
,
r48
);
spin_lock_irq
(
&
card
->
lock
);
DPRINTK
(
"regs==%02X %02X %02X %08X %08X %08X %08X
\n
"
,
inb
(
card
->
baseaddr
+
0x00
),
inb
(
card
->
baseaddr
+
0x01
),
inb
(
card
->
baseaddr
+
0x02
),
inl
(
card
->
baseaddr
+
0x04
),
inl
(
card
->
baseaddr
+
0x0C
),
inl
(
card
->
baseaddr
+
0x80
),
inl
(
card
->
baseaddr
+
0x84
));
spin_unlock_irq
(
&
card
->
lock
);
}
#endif
#if 1
/*
* reset AC97 controller: enable, disable, enable
* pause after each command for good luck
*/
pci_write_config_byte
(
pdev
,
VIA_ACLINK_CTRL
,
VIA_CR41_AC97_ENABLE
|
VIA_CR41_AC97_RESET
|
VIA_CR41_AC97_WAKEUP
);
udelay
(
100
);
pci_write_config_byte
(
pdev
,
VIA_ACLINK_CTRL
,
0
);
udelay
(
100
);
pci_write_config_byte
(
pdev
,
VIA_ACLINK_CTRL
,
VIA_CR41_AC97_ENABLE
|
VIA_CR41_PCM_ENABLE
|
VIA_CR41_VRA
|
VIA_CR41_AC97_RESET
);
udelay
(
100
);
#endif
#if 0
/*
* reset AC97 controller
*/
pci_read_config_byte(pdev, 0x08, &tmp8);
if ((tmp8 & 0xff) >= 0x20 ) {
pci_read_config_byte(pdev, 0x42, &tmp8);
pci_write_config_byte(pdev, 0x42,(tmp8 & 0x3f));
}
udelay (100);
/* init other mixer defaults */
for (idx = 0; idx < arraysize(mixer_init_vals); idx++) {
via_ac97_write_reg (&card->ac97,
mixer_init_vals[idx].reg,
mixer_init_vals[idx].data);
udelay (20);
}
pci_write_config_byte (pdev, 0x41, 0xc0);
udelay (10);
pci_read_config_byte (pdev, 0x41, &tmp8);
udelay (10);
pci_write_config_byte (pdev, 0x41, tmp8 | 0x0f);
udelay (10);
#endif
/* disable legacy stuff */
pci_write_config_byte
(
pdev
,
0x42
,
0x00
);
udelay
(
10
);
/* route FM trap to IRQ, disable FM trap */
pci_write_config_byte
(
pdev
,
0x48
,
0x05
);
udelay
(
10
);
/* disable all codec GPI interrupts */
outl
(
0
,
pci_resource_start
(
pdev
,
0
)
+
0x8C
);
#if 0 /* ALSA */
via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS, 0x0009);
#if 0
via_ac97_write_reg (&card->ac97, AC97_POWER_CONTROL,
via_ac97_read_reg (&card->ac97, AC97_POWER_CONTROL) | 0x0300);
#endif
via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS,
via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS) | 0xe800);
#endif
/* enable variable rate */
tmp16
=
via_ac97_read_reg
(
&
card
->
ac97
,
0x2A
);
via_ac97_write_reg
(
&
card
->
ac97
,
0x2A
,
tmp16
|
0x01
);
/* boost headphone vol if disabled */
tmp16
=
via_ac97_read_reg
(
&
card
->
ac97
,
AC97_HEADPHONE_VOL
);
if
(
tmp16
==
0
)
via_ac97_write_reg
(
&
card
->
ac97
,
AC97_HEADPHONE_VOL
,
0x1F1F
);
pci_read_config_byte
(
pdev
,
VIA_ACLINK_CTRL
,
&
tmp8
);
if
((
tmp8
&
(
VIA_CR41_AC97_ENABLE
|
VIA_CR41_AC97_RESET
))
==
0
)
{
printk
(
KERN_ERR
PFX
"cannot enable AC97 controller, aborting
\n
"
);
DPRINTK
(
"EXIT, tmp8=%X, returning -ENODEV
\n
"
,
tmp8
);
return
-
ENODEV
;
}
DPRINTK
(
"EXIT, returning 0
\n
"
);
return
0
;
}
static
int
__init
via_ac97_init
(
struct
via_info
*
card
)
{
int
rc
;
u16
tmp16
;
DPRINTK
(
"ENTER
\n
"
);
assert
(
card
!=
NULL
);
memset
(
&
card
->
ac97
,
0
,
sizeof
(
card
->
ac97
));
card
->
ac97
.
private_data
=
card
;
card
->
ac97
.
codec_read
=
via_ac97_read_reg
;
card
->
ac97
.
codec_write
=
via_ac97_write_reg
;
card
->
ac97
.
dev_mixer
=
register_sound_mixer
(
&
via_mixer_fops
,
-
1
);
if
(
card
->
ac97
.
dev_mixer
<
0
)
{
printk
(
KERN_ERR
PFX
"unable to register AC97 mixer, aborting
\n
"
);
DPRINTK
(
"EXIT, returning -EIO
\n
"
);
return
-
EIO
;
}
rc
=
via_ac97_reset
(
card
);
if
(
rc
)
{
printk
(
KERN_ERR
PFX
"unable to reset AC97 codec, aborting
\n
"
);
goto
err_out
;
}
if
(
ac97_probe_codec
(
&
card
->
ac97
)
==
0
)
{
printk
(
KERN_ERR
PFX
"unable to probe AC97 codec, aborting
\n
"
);
rc
=
-
EIO
;
goto
err_out
;
}
tmp16
=
via_ac97_read_reg
(
&
card
->
ac97
,
0x2A
);
via_ac97_write_reg
(
&
card
->
ac97
,
0x2A
,
tmp16
|
0x01
);
DPRINTK
(
"EXIT, returning 0
\n
"
);
return
0
;
err_out:
unregister_sound_mixer
(
card
->
ac97
.
dev_mixer
);
DPRINTK
(
"EXIT, returning %d
\n
"
,
rc
);
return
rc
;
}
static
void
via_ac97_cleanup
(
struct
via_info
*
card
)
{
DPRINTK
(
"ENTER
\n
"
);
assert
(
card
!=
NULL
);
assert
(
card
->
ac97
.
dev_mixer
>=
0
);
unregister_sound_mixer
(
card
->
ac97
.
dev_mixer
);
DPRINTK
(
"EXIT
\n
"
);
}
/****************************************************************
*
* Interrupt-related code
*
*/
static
inline
void
via_interrupt_write
(
struct
via_channel
*
chan
)
{
assert
(
atomic_read
(
&
chan
->
buf_in_use
)
<
atomic_read
(
&
chan
->
next_buf
));
/* XXX sanity check: read h/w counter to
* ensure no lost frames */
atomic_inc
(
&
chan
->
buf_in_use
);
/* if SG ptr catches up with userland ptr, stop playback */
if
(
atomic_read
(
&
chan
->
buf_in_use
)
==
atomic_read
(
&
chan
->
next_buf
))
{
atomic_set
(
&
chan
->
state
,
sgd_stopped
);
via_chan_stop
(
chan
->
iobase
);
}
/* wake up anybody listening */
if
(
waitqueue_active
(
&
chan
->
wait
))
wake_up
(
&
chan
->
wait
);
}
static
void
via_interrupt
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
{
struct
via_info
*
card
=
dev_id
;
struct
via_channel
*
chan
;
u8
status
;
int
unhandled
=
1
;
static
long
intcount
=
0
;
assert
(
irq
==
card
->
pdev
->
irq
);
intcount
++
;
status
=
inb
(
card
->
baseaddr
+
0x00
);
if
(
status
)
{
assert
(
card
->
open_mode
&
FMODE_WRITE
);
chan
=
&
card
->
ch_out
;
unhandled
=
0
;
if
(
status
&
VIA_SGD_FLAG
)
{
assert
((
status
&
VIA_SGD_EOL
)
==
0
);
outb
(
VIA_SGD_FLAG
,
chan
->
iobase
+
0x00
);
DPRINTK
(
"FLAG intr, status=0x%02X, intcount=%ld
\n
"
,
status
,
intcount
);
via_interrupt_write
(
chan
);
}
if
(
status
&
VIA_SGD_EOL
)
{
assert
((
status
&
VIA_SGD_FLAG
)
==
0
);
outb
(
VIA_SGD_EOL
,
chan
->
iobase
+
0x00
);
DPRINTK
(
"EOL intr, status=0x%02X, intcount=%ld
\n
"
,
status
,
intcount
);
via_interrupt_write
(
chan
);
}
if
(
status
&
VIA_SGD_STOPPED
)
{
outb
(
VIA_SGD_STOPPED
,
chan
->
iobase
+
0x00
);
DPRINTK
(
"STOPPED intr, status=0x%02X, intcount=%ld
\n
"
,
status
,
intcount
);
}
#if 0
via_chan_dump_bufs (&card->ch_out);
#endif
}
if
(
unhandled
)
printk
(
KERN_WARNING
PFX
"unhandled interrupt, st=%02x, st32=%08x
\n
"
,
status
,
inl
(
card
->
baseaddr
+
0x84
));
}
static
void
via_interrupt_disable
(
struct
via_info
*
card
)
{
u8
tmp8
;
unsigned
long
flags
;
DPRINTK
(
"ENTER
\n
"
);
assert
(
card
!=
NULL
);
spin_lock_irqsave
(
&
card
->
lock
,
flags
);
pci_read_config_byte
(
card
->
pdev
,
VIA_FM_NMI_CTRL
,
&
tmp8
);
if
((
tmp8
&
VIA_CR48_FM_TRAP_TO_NMI
)
==
0
)
{
tmp8
|=
VIA_CR48_FM_TRAP_TO_NMI
;
pci_write_config_byte
(
card
->
pdev
,
VIA_FM_NMI_CTRL
,
tmp8
);
}
outb
(
inb
(
card
->
baseaddr
+
VIA_BASE0_PCM_OUT_CHAN_TYPE
)
&
VIA_INT_DISABLE_MASK
,
card
->
baseaddr
+
VIA_BASE0_PCM_OUT_CHAN_TYPE
);
outb
(
inb
(
card
->
baseaddr
+
VIA_BASE0_PCM_IN_CHAN_TYPE
)
&
VIA_INT_DISABLE_MASK
,
card
->
baseaddr
+
VIA_BASE0_PCM_IN_CHAN_TYPE
);
outb
(
inb
(
card
->
baseaddr
+
VIA_BASE0_FM_OUT_CHAN_TYPE
)
&
VIA_INT_DISABLE_MASK
,
card
->
baseaddr
+
VIA_BASE0_FM_OUT_CHAN_TYPE
);
spin_unlock_irqrestore
(
&
card
->
lock
,
flags
);
DPRINTK
(
"EXIT
\n
"
);
}
static
int
via_interrupt_init
(
struct
via_info
*
card
)
{
DPRINTK
(
"ENTER
\n
"
);
assert
(
card
!=
NULL
);
assert
(
card
->
pdev
!=
NULL
);
/* check for sane IRQ number. can this ever happen? */
if
(
card
->
pdev
->
irq
<
2
)
{
printk
(
KERN_ERR
PFX
"insane IRQ %d, aborting
\n
"
,
card
->
pdev
->
irq
);
DPRINTK
(
"EXIT, returning -EIO
\n
"
);
return
-
EIO
;
}
if
(
request_irq
(
card
->
pdev
->
irq
,
via_interrupt
,
SA_SHIRQ
,
VIA_MODULE_NAME
,
card
))
{
printk
(
KERN_ERR
PFX
"unable to obtain IRQ %d, aborting
\n
"
,
card
->
pdev
->
irq
);
DPRINTK
(
"EXIT, returning -EBUSY
\n
"
);
return
-
EBUSY
;
}
/* we don't want interrupts until we're opened */
via_interrupt_disable
(
card
);
DPRINTK
(
"EXIT, returning 0
\n
"
);
return
0
;
}
static
void
via_interrupt_cleanup
(
struct
via_info
*
card
)
{
DPRINTK
(
"ENTER
\n
"
);
assert
(
card
!=
NULL
);
assert
(
card
->
pdev
!=
NULL
);
via_interrupt_disable
(
card
);
free_irq
(
card
->
pdev
->
irq
,
card
);
DPRINTK
(
"EXIT
\n
"
);
}
/****************************************************************
*
* OSS DSP device
*
*/
static
struct
file_operations
via_dsp_fops
=
{
open:
via_dsp_open
,
release:
via_dsp_release
,
read:
via_dsp_read
,
write:
via_dsp_write
,
poll:
via_dsp_poll
,
llseek:
via_llseek
,
ioctl:
via_dsp_ioctl
,
};
static
int
__init
via_dsp_init
(
struct
via_info
*
card
)
{
u8
tmp8
;
DPRINTK
(
"ENTER
\n
"
);
assert
(
card
!=
NULL
);
/* turn off legacy features, if not already */
pci_read_config_byte
(
card
->
pdev
,
VIA_FUNC_ENABLE
,
&
tmp8
);
tmp8
&=
~
(
VIA_CR42_SB_ENABLE
|
VIA_CR42_MIDI_ENABLE
|
VIA_CR42_FM_ENABLE
);
pci_write_config_byte
(
card
->
pdev
,
VIA_FUNC_ENABLE
,
tmp8
);
via_stop_everything
(
card
);
card
->
dev_dsp
=
register_sound_dsp
(
&
via_dsp_fops
,
-
1
);
if
(
card
->
dev_dsp
<
0
)
{
DPRINTK
(
"EXIT, returning -ENODEV
\n
"
);
return
-
ENODEV
;
}
DPRINTK
(
"EXIT, returning 0
\n
"
);
return
0
;
}
static
void
via_dsp_cleanup
(
struct
via_info
*
card
)
{
DPRINTK
(
"ENTER
\n
"
);
assert
(
card
!=
NULL
);
assert
(
card
->
dev_dsp
>=
0
);
via_stop_everything
(
card
);
unregister_sound_dsp
(
card
->
dev_dsp
);
DPRINTK
(
"EXIT
\n
"
);
}
static
ssize_t
via_dsp_read
(
struct
file
*
file
,
char
*
buffer
,
size_t
count
,
loff_t
*
ppos
)
{
struct
via_info
*
card
;
DPRINTK
(
"ENTER
\n
"
);
assert
(
file
!=
NULL
);
assert
(
buffer
!=
NULL
);
card
=
file
->
private_data
;
assert
(
card
!=
NULL
);
DPRINTK
(
"EXIT, returning -EINVAL
\n
"
);
return
-
EINVAL
;
}
#define sgcount(n) (sgtable[(n)].count & 0x00FFFFFF)
#define NEXTBUF (atomic_read(&chan->next_buf) % VIA_DMA_BUFFERS)
#define BUF_IN_USE (atomic_read(&chan->buf_in_use) % VIA_DMA_BUFFERS)
#define STATE_STOPPED (atomic_read (state) == sgd_stopped)
#define STATE_STARTED (atomic_read (state) == sgd_in_progress)
static
ssize_t
via_dsp_do_write
(
struct
via_info
*
card
,
const
char
*
userbuf
,
size_t
count
,
int
non_blocking
)
{
const
char
*
orig_userbuf
=
userbuf
;
struct
via_channel
*
chan
=
&
card
->
ch_out
;
volatile
struct
via_sgd_table
*
sgtable
=
chan
->
sgtable
;
atomic_t
*
state
=
&
chan
->
state
;
size_t
size
;
int
nextbuf
,
prevbuf
,
n
,
realcount
;
ssize_t
rc
;
while
(
count
>
0
)
{
if
(
current
->
need_resched
)
schedule
();
spin_lock_irq
(
&
card
->
lock
);
DPRINTK
(
"regs==%02X %02X %02X %08X %08X %08X %08X
\n
"
,
inb
(
card
->
baseaddr
+
0x00
),
inb
(
card
->
baseaddr
+
0x01
),
inb
(
card
->
baseaddr
+
0x02
),
inl
(
card
->
baseaddr
+
0x04
),
inl
(
card
->
baseaddr
+
0x0C
),
inl
(
card
->
baseaddr
+
0x80
),
inl
(
card
->
baseaddr
+
0x84
));
spin_unlock_irq
(
&
card
->
lock
);
size
=
(
count
<
VIA_DMA_BUF_SIZE
)
?
count
:
VIA_DMA_BUF_SIZE
;
/* case 1: SGD not active, list is ours for the mangling */
if
(
STATE_STOPPED
)
{
DPRINTK
(
"case 1
\n
"
);
if
(
copy_from_user
((
void
*
)
chan
->
sgbuf
[
0
].
cpuaddr
,
userbuf
,
size
))
return
-
EFAULT
;
assert
(
sgtable
[
0
].
addr
==
cpu_to_le32
(
chan
->
sgbuf
[
0
].
handle
));
sgtable
[
0
].
count
=
size
|
VIA_FLAG
;
atomic_set
(
state
,
sgd_in_progress
);
atomic_set
(
&
chan
->
buf_in_use
,
0
);
atomic_set
(
&
chan
->
next_buf
,
1
);
count
-=
size
;
userbuf
+=
size
;
spin_lock_irq
(
&
card
->
lock
);
sg_begin
(
chan
);
spin_unlock_irq
(
&
card
->
lock
);
continue
;
}
nextbuf
=
NEXTBUF
;
if
(
nextbuf
)
prevbuf
=
nextbuf
-
1
;
else
prevbuf
=
VIA_DMA_BUFFERS
-
1
;
/* case 2: if final buffer is (a) a fragment, and (b) not
* currently being consumed by the SGD engine, then append
* as much data as possible to the fragment. */
realcount
=
sgcount
(
prevbuf
);
if
(
STATE_STARTED
&&
(
prevbuf
!=
BUF_IN_USE
)
&&
(
realcount
<
VIA_DMA_BUF_SIZE
))
{
DPRINTK
(
"case 2
\n
"
);
DPRINTK
(
"st=%d, fb=%d -- nb=%d, pb=%d, n=%d, rc=%d
\n
"
,
atomic_read
(
state
),
BUF_IN_USE
,
nextbuf
,
prevbuf
,
prevbuf
/* n */
,
realcount
);
n
=
prevbuf
;
if
((
VIA_DMA_BUF_SIZE
-
realcount
)
<
size
)
size
=
VIA_DMA_BUF_SIZE
-
realcount
;
if
(
copy_from_user
((
void
*
)(
chan
->
sgbuf
[
n
].
cpuaddr
+
realcount
),
userbuf
,
size
))
return
-
EFAULT
;
/* slack way to try and prevent races */
if
(
prevbuf
==
BUF_IN_USE
||
!
STATE_STARTED
)
continue
;
assert
(
sgtable
[
n
].
addr
==
cpu_to_le32
(
chan
->
sgbuf
[
n
].
handle
));
if
(
n
==
(
VIA_DMA_BUFFERS
-
1
))
sgtable
[
n
].
count
=
(
realcount
+
size
)
|
VIA_EOL
;
else
sgtable
[
n
].
count
=
(
realcount
+
size
)
|
VIA_FLAG
;
count
-=
size
;
userbuf
+=
size
;
continue
;
}
/* case 3: if there are buffers left, use one
* XXX needs more review for possible races */
else
if
(
STATE_STARTED
&&
!
via_chan_full
(
chan
))
{
DPRINTK
(
"case 3
\n
"
);
DPRINTK
(
"st=%d, fb=%d -- nb=%d, pb=%d, n=%d
\n
"
,
atomic_read
(
state
),
BUF_IN_USE
,
nextbuf
,
prevbuf
,
nextbuf
/* n */
);
n
=
nextbuf
;
if
(
copy_from_user
((
void
*
)
chan
->
sgbuf
[
n
].
cpuaddr
,
userbuf
,
size
))
return
-
EFAULT
;
if
(
n
==
(
VIA_DMA_BUFFERS
-
1
))
sgtable
[
n
].
count
=
size
|
VIA_EOL
;
else
sgtable
[
n
].
count
=
size
|
VIA_FLAG
;
/* if SGD stopped during data copy or SG table update,
* then loop back to the beginning without updating
* any pointers.
* ie. slack way to prevent race */
if
(
!
STATE_STARTED
)
continue
;
atomic_inc
(
&
chan
->
next_buf
);
count
-=
size
;
userbuf
+=
size
;
continue
;
}
/* case 4, final SGT active case: no free buffers, wait for one */
else
{
DPRINTK
(
"case 4
\n
"
);
DPRINTK
(
"st=%d, fb=%d -- nb=%d, pb=%d
\n
"
,
atomic_read
(
state
),
BUF_IN_USE
,
nextbuf
,
prevbuf
);
/* if playback stopped, no need to sleep */
if
(
!
STATE_STARTED
)
continue
;
/* if buffer free, no need to sleep */
if
(
!
via_chan_full
(
chan
))
continue
;
if
(
non_blocking
)
{
rc
=
userbuf
-
orig_userbuf
;
if
(
rc
==
0
)
rc
=
-
EAGAIN
;
return
rc
;
}
DPRINTK
(
"sleeping
\n
"
);
interruptible_sleep_on
(
&
chan
->
wait
);
if
(
signal_pending
(
current
))
return
-
ERESTARTSYS
;
}
}
#if 0
{
u8 r40,r41,r42,r43,r44,r48;
pci_read_config_byte (card->pdev, 0x40, &r40);
pci_read_config_byte (card->pdev, 0x41, &r41);
pci_read_config_byte (card->pdev, 0x42, &r42);
pci_read_config_byte (card->pdev, 0x43, &r43);
pci_read_config_byte (card->pdev, 0x44, &r44);
pci_read_config_byte (card->pdev, 0x48, &r48);
DPRINTK("PCI config: %02X %02X %02X %02X %02X %02X\n",
r40,r41,r42,r43,r44,r48);
}
#endif
DPRINTK
(
"EXIT, returning %d
\n
"
,
userbuf
-
orig_userbuf
);
return
userbuf
-
orig_userbuf
;
}
#undef sgcount
#undef NEXTBUF
#undef BUF_IN_USE
#undef STATE_STOPPED
#undef STATE_STARTED
static
ssize_t
via_dsp_write
(
struct
file
*
file
,
const
char
*
buffer
,
size_t
count
,
loff_t
*
ppos
)
{
struct
via_info
*
card
;
ssize_t
rc
;
DPRINTK
(
"ENTER, file=%p, buffer=%p, count=%u, ppos=%lu
\n
"
,
file
,
buffer
,
count
,
ppos
?
((
unsigned
long
)
*
ppos
)
:
0
);
DPRINTK
(
"ENTER
\n
"
);
assert
(
file
!=
NULL
);
assert
(
buffer
!=
NULL
);
card
=
file
->
private_data
;
assert
(
card
!=
NULL
);
assert
(
pdev
!=
NULL
);
if
(
ppos
!=
&
file
->
f_pos
)
{
DPRINTK
(
"EXIT, returning -ESPIPE
\n
"
);
return
-
ESPIPE
;
}
do
{
tmp
=
via_ac97_read32
(
pdev
,
VIA_AC97_CTRL
);
}
while
((
tmp
&
VIA_CR80_BUSY
)
&&
(
counter
--
>
0
));
rc
=
via_dsp_do_write
(
card
,
buffer
,
count
,
(
file
->
f_flags
&
O_NONBLOCK
));
DPRINTK
(
"EXIT%s
\n
"
,
counter
>
0
?
""
:
", counter limit reached"
);
DPRINTK
(
"EXIT, returning %ld
\n
"
,(
long
)
rc
);
return
rc
;
}
static
int
via_ac97_read_reg
(
struct
ac97_hwint
*
dev
,
u8
reg
)
static
unsigned
int
via_dsp_poll
(
struct
file
*
file
,
struct
poll_table_struct
*
wait
)
{
u32
data
;
struct
via_info
*
card
;
struct
pci_dev
*
pdev
;
unsigned
int
mask
=
0
;
DPRINTK
(
"ENTER
\n
"
);
assert
(
dev
!=
NULL
);
assert
(
dev
->
driver_private
!=
NULL
);
card
=
(
struct
via_info
*
)
dev
->
driver_private
;
pdev
=
card
->
pdev
;
assert
(
pdev
!=
NULL
);
assert
(
file
!=
NULL
);
assert
(
wait
!=
NULL
);
card
=
file
->
private_data
;
assert
(
card
!=
NULL
);
via_ac97_wait_idle
(
pdev
);
data
=
VIA_CR80_FIRST_CODEC
|
VIA_CR80_FIRST_CODEC_VALID
|
VIA_CR80_READ_MODE
|
VIA_CR80_REG_IDX
(
reg
);
via_ac97_write32
(
pdev
,
VIA_AC97_CTRL
,
data
);
via_ac97_wait_idle
(
pdev
);
data
=
via_ac97_read32
(
pdev
,
VIA_AC97_CTRL
);
if
((
file
->
f_mode
&
FMODE_WRITE
)
&&
(
atomic_read
(
&
card
->
ch_out
.
state
)
!=
sgd_stopped
))
{
poll_wait
(
file
,
&
card
->
ch_out
.
wait
,
wait
);
#if 0
if (! (data & VIA_CR80_FIRST_CODEC_VALID)) {
DPRINTK ("EXIT, first codec not valid, returning -1\n");
return -1
;
/* XXX is this correct */
if
(
atomic_read
(
&
card
->
ch_out
.
buf_in_use
)
<
atomic_read
(
&
card
->
ch_out
.
next_buf
))
mask
|=
POLLOUT
|
POLLWRNORM
;
}
#endif
DPRINTK
(
"EXIT, returning %d
\n
"
,
data
&
0xFFFF
);
return
data
&
0xFFFF
;
DPRINTK
(
"EXIT, returning %u
\n
"
,
mask
);
return
mask
;
}
static
int
via_
ac97_write_reg
(
struct
ac97_hwint
*
dev
,
u8
reg
,
u16
value
)
static
int
via_
dsp_drain_dac
(
struct
via_info
*
card
,
int
non_block
)
{
u32
data
;
struct
via_info
*
card
;
struct
pci_dev
*
pdev
;
DPRINTK
(
"ENTER
\n
"
);
assert
(
dev
!=
NULL
);
assert
(
dev
->
driver_private
!=
NULL
);
DPRINTK
(
"ENTER, non_block = %d
\n
"
,
non_block
);
card
=
(
struct
via_info
*
)
dev
->
driver_private
;
pdev
=
card
->
pdev
;
assert
(
pdev
!=
NULL
);
while
(
!
via_chan_empty
(
&
card
->
ch_out
))
{
if
(
non_block
)
{
DPRINTK
(
"EXIT, returning -EBUSY
\n
"
);
return
-
EBUSY
;
}
if
(
signal_pending
(
current
))
{
DPRINTK
(
"EXIT, returning -ERESTARTSYS
\n
"
);
return
-
ERESTARTSYS
;
}
via_ac97_wait_idle
(
pdev
);
data
=
VIA_CR80_FIRST_CODEC
|
VIA_CR80_FIRST_CODEC_VALID
|
VIA_CR80_WRITE_MODE
|
VIA_CR80_REG_IDX
(
reg
)
|
value
;
via_ac97_write32
(
pdev
,
VIA_AC97_CTRL
,
data
);
#ifndef NDEBUG
{
u8
r40
,
r41
,
r42
,
r43
,
r44
,
r48
;
pci_read_config_byte
(
card
->
pdev
,
0x40
,
&
r40
);
pci_read_config_byte
(
card
->
pdev
,
0x41
,
&
r41
);
pci_read_config_byte
(
card
->
pdev
,
0x42
,
&
r42
);
pci_read_config_byte
(
card
->
pdev
,
0x43
,
&
r43
);
pci_read_config_byte
(
card
->
pdev
,
0x44
,
&
r44
);
pci_read_config_byte
(
card
->
pdev
,
0x48
,
&
r48
);
DPRINTK
(
"PCI config: %02X %02X %02X %02X %02X %02X
\n
"
,
r40
,
r41
,
r42
,
r43
,
r44
,
r48
);
spin_lock_irq
(
&
card
->
lock
);
DPRINTK
(
"regs==%02X %02X %02X %08X %08X %08X %08X
\n
"
,
inb
(
card
->
baseaddr
+
0x00
),
inb
(
card
->
baseaddr
+
0x01
),
inb
(
card
->
baseaddr
+
0x02
),
inl
(
card
->
baseaddr
+
0x04
),
inl
(
card
->
baseaddr
+
0x0C
),
inl
(
card
->
baseaddr
+
0x80
),
inl
(
card
->
baseaddr
+
0x84
));
spin_unlock_irq
(
&
card
->
lock
);
#if 0
if (! (data & VIA_CR80_FIRST_CODEC_VALID)) {
DPRINTK ("EXIT, first codec invalid, returning -1\n");
return -1;
}
#endif
DPRINTK
(
"EXIT, returning 0
\n
"
);
DPRINTK
(
"sleeping
\n
"
);
interruptible_sleep_on
(
&
card
->
ch_out
.
wait
);
}
DPRINTK
(
"EXIT
\n
"
);
return
0
;
}
static
int
via_ac97_reset
(
struct
ac97_hwint
*
dev
)
static
int
via_dsp_ioctl_space
(
struct
via_info
*
card
,
struct
via_channel
*
chan
,
void
*
arg
)
{
struct
via_info
*
card
;
struct
pci_dev
*
pdev
;
audio_buf_info
info
;
int
n
;
DPRINTK
(
"ENTER
\n
"
);
info
.
fragstotal
=
VIA_DMA_BUFFERS
;
info
.
fragsize
=
VIA_DMA_BUF_SIZE
;
assert
(
dev
!=
NULL
);
assert
(
dev
->
driver_private
!=
NULL
);
/* number of full fragments we can read without blocking */
n
=
atomic_read
(
&
chan
->
next_buf
)
-
atomic_read
(
&
chan
->
buf_in_use
);
info
.
fragments
=
VIA_DMA_BUFFERS
-
n
;
card
=
(
struct
via_info
*
)
dev
->
driver_private
;
pdev
=
card
->
pdev
;
assert
(
pdev
!=
NULL
);
/* number of bytes that can be read or written immediately
* without blocking. FIXME: we are lazy and ignore partially-full
* buffers.
*/
info
.
bytes
=
info
.
fragments
*
VIA_DMA_BUF_SIZE
;
pci_write_config_word
(
pdev
,
PCI_COMMAND
,
PCI_COMMAND_IO
);
DPRINTK
(
"EXIT, returning fragstotal=%d, fragsize=%d, fragments=%d, bytes=%d
\n
"
,
info
.
fragstotal
,
info
.
fragsize
,
info
.
fragments
,
info
.
bytes
);
DPRINTK
(
"EXIT, returning 0
\n
"
);
return
0
;
return
copy_to_user
(
arg
,
&
info
,
sizeof
(
info
));
}
static
struct
via_info
*
via_ac97_find_card_for_mixer
(
int
dev
)
static
int
via_dsp_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
int
x
;
int
rc
=
-
EINVAL
,
rd
=
0
,
wr
=
0
,
val
=
0
;
struct
via_info
*
card
;
DPRINTK
(
"ENTER
\n
"
);
DPRINTK
(
"ENTER, cmd = 0x%08X
\n
"
,
cmd
);
assert
(
file
!=
NULL
);
card
=
file
->
private_data
;
assert
(
card
!=
NULL
);
for
(
x
=
0
;
x
<
num_cards
;
x
++
)
if
(
cards
[
x
].
mixer_oss_dev
==
dev
)
{
DPRINTK
(
"EXIT, returning %p
\n
"
,
cards
+
x
);
return
cards
+
x
;
if
(
file
->
f_mode
&
FMODE_WRITE
)
wr
=
1
;
if
(
file
->
f_mode
&
FMODE_READ
)
rd
=
1
;
switch
(
cmd
)
{
/* OSS API version. XXX unverified */
case
OSS_GETVERSION
:
DPRINTK
(
"EXIT, returning SOUND_VERSION
\n
"
);
return
put_user
(
SOUND_VERSION
,
(
int
*
)
arg
);
/* list of supported PCM data formats */
case
SNDCTL_DSP_GETFMTS
:
DPRINTK
(
"EXIT, returning AFMT U8|S16_LE
\n
"
);
return
put_user
(
AFMT_U8
|
AFMT_S16_LE
,
(
int
*
)
arg
);
/* query or set current channel's PCM data format */
case
SNDCTL_DSP_SETFMT
:
get_user_ret
(
val
,
(
int
*
)
arg
,
-
EFAULT
);
if
(
val
!=
AFMT_QUERY
)
{
rc
=
0
;
spin_lock_irq
(
&
card
->
lock
);
if
(
rc
==
0
&&
rd
)
rc
=
via_chan_set_fmt
(
card
,
&
card
->
ch_in
,
val
);
if
(
rc
==
0
&&
wr
)
rc
=
via_chan_set_fmt
(
card
,
&
card
->
ch_out
,
val
);
spin_unlock_irq
(
&
card
->
lock
);
if
(
rc
<=
0
)
return
rc
?
rc
:
-
EINVAL
;
val
=
rc
;
}
else
{
spin_lock_irq
(
&
card
->
lock
);
if
((
rd
&&
(
card
->
ch_in
.
pcm_fmt
&
VIA_PCM_FMT_16BIT
))
||
(
wr
&&
(
card
->
ch_out
.
pcm_fmt
&
VIA_PCM_FMT_16BIT
)))
val
=
AFMT_S16_LE
;
else
val
=
AFMT_U8
;
spin_unlock_irq
(
&
card
->
lock
);
}
DPRINTK
(
"SETFMT EXIT, returning %d
\n
"
,
val
);
return
put_user
(
val
,
(
int
*
)
arg
);
/* query or set number of channels (1=mono, 2=stereo) */
case
SNDCTL_DSP_CHANNELS
:
get_user_ret
(
val
,
(
int
*
)
arg
,
-
EFAULT
);
if
(
val
!=
0
)
{
rc
=
0
;
spin_lock_irq
(
&
card
->
lock
);
if
(
rc
==
0
&&
rd
)
rc
=
via_chan_set_stereo
(
card
,
&
card
->
ch_in
,
val
);
if
(
rc
==
0
&&
wr
)
rc
=
via_chan_set_stereo
(
card
,
&
card
->
ch_out
,
val
);
spin_unlock_irq
(
&
card
->
lock
);
if
(
rc
<=
0
)
return
rc
?
rc
:
-
EINVAL
;
val
=
rc
;
}
else
{
spin_lock_irq
(
&
card
->
lock
);
if
((
rd
&&
(
card
->
ch_in
.
pcm_fmt
&
VIA_PCM_FMT_STEREO
))
||
(
wr
&&
(
card
->
ch_out
.
pcm_fmt
&
VIA_PCM_FMT_STEREO
)))
val
=
2
;
else
val
=
1
;
spin_unlock_irq
(
&
card
->
lock
);
}
DPRINTK
(
"CHANNELS EXIT, returning %d
\n
"
,
val
);
return
put_user
(
val
,
(
int
*
)
arg
);
/* enable (val is not zero) or disable (val == 0) stereo */
case
SNDCTL_DSP_STEREO
:
get_user_ret
(
val
,
(
int
*
)
arg
,
-
EFAULT
);
rc
=
0
;
spin_lock_irq
(
&
card
->
lock
);
if
(
rc
==
0
&&
rd
)
rc
=
via_chan_set_stereo
(
card
,
&
card
->
ch_in
,
val
?
2
:
1
);
if
(
rc
==
0
&&
wr
)
rc
=
via_chan_set_stereo
(
card
,
&
card
->
ch_out
,
val
?
2
:
1
);
spin_unlock_irq
(
&
card
->
lock
);
if
(
rc
<=
0
)
return
rc
?
rc
:
-
EINVAL
;
DPRINTK
(
"STEREO EXIT, returning %d
\n
"
,
val
);
return
0
;
DPRINTK
(
"EXIT, returning 0
\n
"
);
return
NULL
;
}
/* query or set sampling rate */
case
SNDCTL_DSP_SPEED
:
get_user_ret
(
val
,
(
int
*
)
arg
,
-
EFAULT
);
if
(
val
<
0
)
return
-
EINVAL
;
if
(
val
>
0
)
{
rc
=
0
;
spin_lock_irqsave
(
&
card
->
lock
,
flags
);
if
(
rc
==
0
&&
rd
)
rc
=
via_chan_set_speed
(
card
,
&
card
->
ch_in
,
val
);
if
(
rc
==
0
&&
wr
)
rc
=
via_chan_set_speed
(
card
,
&
card
->
ch_out
,
val
);
spin_unlock_irqrestore
(
&
card
->
lock
,
flags
);
if
(
rc
<=
0
)
return
rc
?
rc
:
-
EINVAL
;
val
=
rc
;
}
else
{
spin_lock_irqsave
(
&
card
->
lock
,
flags
);
if
(
rd
)
val
=
card
->
ch_in
.
rate
;
else
if
(
wr
)
val
=
card
->
ch_out
.
rate
;
else
val
=
0
;
spin_unlock_irqrestore
(
&
card
->
lock
,
flags
);
}
DPRINTK
(
"SPEED EXIT, returning %d
\n
"
,
val
);
return
put_user
(
val
,
(
int
*
)
arg
);
/* wait until all buffers have been played, and then stop device */
case
SNDCTL_DSP_SYNC
:
if
(
wr
)
{
DPRINTK
(
"SYNC EXIT (after calling via_dsp_drain_dac)
\n
"
);
return
via_dsp_drain_dac
(
card
,
file
->
f_flags
&
O_NONBLOCK
);
}
break
;
/* stop recording/playback immediately */
case
SNDCTL_DSP_RESET
:
if
(
rd
)
{
spin_lock_irqsave
(
&
card
->
lock
,
flags
);
via_chan_clear
(
&
card
->
ch_in
);
via_chan_pcm_fmt
(
card
,
&
card
->
ch_in
,
1
);
spin_unlock_irqrestore
(
&
card
->
lock
,
flags
);
}
if
(
wr
)
{
spin_lock_irqsave
(
&
card
->
lock
,
flags
);
via_chan_clear
(
&
card
->
ch_out
);
via_chan_pcm_fmt
(
card
,
&
card
->
ch_out
,
1
);
spin_unlock_irqrestore
(
&
card
->
lock
,
flags
);
}
DPRINTK
(
"RESET EXIT, returning 0
\n
"
);
return
0
;
static
int
via_ac97_default_mixer_ioctl
(
int
dev
,
unsigned
int
cmd
,
caddr_t
arg
)
{
int
rc
;
struct
via_info
*
card
=
via_ac97_find_card_for_mixer
(
dev
);
/* obtain bitmask of device capabilities, such as mmap, full duplex, etc. */
case
SNDCTL_DSP_GETCAPS
:
DPRINTK
(
"GETCAPS EXIT
\n
"
);
return
put_user
(
DSP_CAP_REVISION
,
(
int
*
)
arg
);
DPRINTK
(
"ENTER
\n
"
);
/* obtain bitmask of device capabilities, such as mmap, full duplex, etc. */
case
SNDCTL_DSP_GETBLKSIZE
:
DPRINTK
(
"GETBLKSIZE EXIT
\n
"
);
return
put_user
(
VIA_DMA_BUF_SIZE
,
(
int
*
)
arg
);
if
(
card
!=
NULL
)
{
rc
=
ac97_mixer_ioctl
(
&
card
->
ac97
,
cmd
,
arg
);
DPRINTK
(
"EXIT, returning %d
\n
"
,
rc
);
return
rc
;
/* obtain information about input buffering */
case
SNDCTL_DSP_GETISPACE
:
DPRINTK
(
"GETISPACE EXIT
\n
"
);
return
via_dsp_ioctl_space
(
card
,
&
card
->
ch_in
,
(
void
*
)
arg
);
/* obtain information about output buffering */
case
SNDCTL_DSP_GETOSPACE
:
DPRINTK
(
"GETOSPACE EXIT
\n
"
);
return
via_dsp_ioctl_space
(
card
,
&
card
->
ch_out
,
(
void
*
)
arg
);
/* return number of bytes remaining to be played by DMA engine */
case
SNDCTL_DSP_GETODELAY
:
{
int
n
;
n
=
atomic_read
(
&
card
->
ch_out
.
next_buf
)
-
atomic_read
(
&
card
->
ch_out
.
buf_in_use
);
assert
(
n
>=
0
);
if
(
n
==
0
)
val
=
0
;
else
{
val
=
(
n
-
1
)
*
VIA_DMA_BUF_SIZE
;
val
+=
inl
(
card
->
ch_out
.
iobase
+
VIA_BASE0_PCM_OUT_BLOCK_COUNT
);
}
DPRINTK
(
"EXIT, returning -ENODEV
\n
"
);
return
-
ENODEV
;
DPRINTK
(
"GETODELAY EXIT, val = %d bytes
\n
"
,
val
);
return
put_user
(
val
,
(
int
*
)
arg
);
}
/* set fragment size. implemented as a successful no-op for now */
case
SNDCTL_DSP_SETFRAGMENT
:
get_user_ret
(
val
,
(
int
*
)
arg
,
-
EFAULT
);
DPRINTK
(
"SNDCTL_DSP_SETFRAGMENT (fragshift==0x%04X (%d), maxfrags==0x%04X (%d))
\n
"
,
val
&
0xFFFF
,
val
&
0xFFFF
,
(
val
>>
16
)
&
0xFFFF
,
(
val
>>
16
)
&
0xFFFF
);
/* just to shut up some programs */
return
0
;
/* inform device of an upcoming pause in input (or output). not implemented */
case
SNDCTL_DSP_POST
:
DPRINTK
(
"POST EXIT (null ioctl, returning -EINVAL)
\n
"
);
return
-
EINVAL
;
/* not implemented */
default:
DPRINTK
(
"unhandled ioctl
\n
"
);
break
;
}
DPRINTK
(
"EXIT, returning %d
\n
"
,
rc
);
return
rc
;
}
static
struct
mixer_operations
via_ac97_mixer_operations
=
{
"VIA82Cxxx"
,
"via82cxxxAC97Mixer"
,
via_ac97_default_mixer_ioctl
};
static
int
__init
via_attach_ac97
(
struct
via_info
*
card
)
static
int
via_dsp_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
int
mixer
;
struct
ac97_hwint
*
mdev
;
int
open_mode
,
rc
=
-
EINVAL
,
minor
=
MINOR
(
inode
->
i_rdev
);
int
got_read_chan
=
0
,
is_busy
;
struct
via_info
*
card
;
struct
pci_dev
*
pdev
;
struct
pci_driver
*
drvr
;
unsigned
long
flags
;
DPRINTK
(
"ENTER
\n
"
);
DPRINTK
(
"ENTER
, minor=%d, file->f_mode=0x%x
\n
"
,
minor
,
file
->
f_mode
);
assert
(
card
!=
NULL
);
MOD_INC_USE_COUNT
;
if
(
file
->
f_mode
&
FMODE_READ
)
/* no input ATM */
goto
err_out
;
card
=
NULL
;
pci_for_each_dev
(
pdev
)
{
drvr
=
pci_dev_driver
(
pdev
);
if
(
drvr
==
&
via_driver
)
{
assert
(
pdev
->
driver_data
!=
NULL
);
mdev
=
&
card
->
ac97
;
card
=
pdev
->
driver_data
;
DPRINTK
(
"dev_dsp = %d, minor = %d, assn = %d
\n
"
,
card
->
dev_dsp
,
minor
,
(
card
->
dev_dsp
^
minor
)
&
~
0xf
);
memset
(
mdev
,
0
,
sizeof
(
*
mdev
));
mdev
->
reset_device
=
via_ac97_reset
;
mdev
->
read_reg
=
via_ac97_read_reg
;
mdev
->
write_reg
=
via_ac97_write_reg
;
mdev
->
driver_private
=
(
void
*
)
card
;
if
(((
card
->
dev_dsp
^
minor
)
&
~
0xf
)
==
0
)
goto
match
;
}
}
if
(
ac97_init
(
mdev
))
{
printk
(
KERN_ERR
PFX
"Unable to init AC97
\n
"
);
DPRINTK
(
"EXIT, returning -1
\n
"
);
return
-
1
;
DPRINTK
(
"no matching %s found
\n
"
,
card
?
"minor"
:
"driver"
);
rc
=
-
ENODEV
;
goto
err_out
;
match:
file
->
private_data
=
card
;
/* wait for device to become free */
spin_lock_irqsave
(
&
card
->
lock
,
flags
);
open_mode
=
card
->
open_mode
;
if
(
open_mode
&
file
->
f_mode
)
is_busy
=
1
;
else
{
is_busy
=
0
;
card
->
open_mode
|=
file
->
f_mode
;
open_mode
=
card
->
open_mode
;
}
mixer
=
sound_alloc_mixerdev
();
if
(
mixer
<
0
||
num_mixers
>=
MAX_MIXER_DEV
)
{
printk
(
KERN_ERR
PFX
"Unable to alloc mixerdev
\n
"
);
DPRINTK
(
"EXIT, returning -1
\n
"
);
return
-
1
;
spin_unlock_irqrestore
(
&
card
->
lock
,
flags
);
if
(
is_busy
)
{
rc
=
-
EBUSY
;
goto
err_out
;
}
DPRINTK
(
"open_mode now 0x%x
\n
"
,
open_mode
);
/* handle input from analog source */
if
(
file
->
f_mode
&
FMODE_READ
)
{
rc
=
via_chan_init
(
card
,
&
card
->
ch_in
,
0x10
);
if
(
rc
)
goto
err_out_clear_mode
;
got_read_chan
=
1
;
/* why is this forced to 16-bit stereo in all drivers? */
card
->
ch_in
.
pcm_fmt
=
VIA_PCM_FMT_16BIT
|
VIA_PCM_FMT_STEREO
;
spin_lock_irqsave
(
&
card
->
lock
,
flags
);
via_chan_pcm_fmt
(
card
,
&
card
->
ch_out
,
0
);
spin_unlock_irqrestore
(
&
card
->
lock
,
flags
);
via_set_adc_rate
(
card
,
8000
);
}
mixer_devs
[
mixer
]
=
&
via_ac97_mixer_operations
;
card
->
mixer_oss_dev
=
mixer
;
/* Some reasonable default values. */
ac97_set_mixer
(
mdev
,
SOUND_MIXER_VOLUME
,
(
85
<<
8
)
|
85
);
ac97_set_mixer
(
mdev
,
SOUND_MIXER_SPEAKER
,
100
);
ac97_set_mixer
(
mdev
,
SOUND_MIXER_PCM
,
(
65
<<
8
)
|
65
);
ac97_set_mixer
(
mdev
,
SOUND_MIXER_CD
,
(
65
<<
8
)
|
65
);
/* handle output to analog source */
if
(
file
->
f_mode
&
FMODE_WRITE
)
{
rc
=
via_chan_init
(
card
,
&
card
->
ch_out
,
0x00
);
if
(
rc
)
goto
err_out_clear_mode
;
if
((
minor
&
0xf
)
==
SND_DEV_DSP16
)
card
->
ch_out
.
pcm_fmt
|=
VIA_PCM_FMT_16BIT
;
printk
(
KERN_INFO
PFX
"Initialized AC97 mixer
\n
"
);
spin_lock_irqsave
(
&
card
->
lock
,
flags
);
via_chan_pcm_fmt
(
card
,
&
card
->
ch_out
,
0
);
spin_unlock_irqrestore
(
&
card
->
lock
,
flags
);
card
->
have_ac97
=
mixer
;
via_set_dac_rate
(
card
,
8000
);
}
DPRINTK
(
"EXIT, returning 0
\n
"
);
return
0
;
err_out_clear_mode:
if
(
got_read_chan
)
via_chan_free
(
card
,
&
card
->
ch_in
);
spin_lock_irqsave
(
&
card
->
lock
,
flags
);
card
->
open_mode
&=
~
file
->
f_mode
;
spin_unlock_irqrestore
(
&
card
->
lock
,
flags
);
err_out:
MOD_DEC_USE_COUNT
;
DPRINTK
(
"ERROR EXIT, returning %d
\n
"
,
rc
);
return
rc
;
}
static
void
via_unload_ac97
(
struct
via_info
*
card
)
static
int
via_dsp_release
(
struct
inode
*
inode
,
struct
file
*
file
)
{
struct
via_info
*
card
;
unsigned
long
flags
;
DPRINTK
(
"ENTER
\n
"
);
assert
(
file
!=
NULL
);
card
=
file
->
private_data
;
assert
(
card
!=
NULL
);
if
(
card
->
have_ac97
>=
0
)
sound_unload_mixerdev
(
card
->
have_ac97
);
if
(
file
->
f_mode
&
FMODE_READ
)
via_chan_free
(
card
,
&
card
->
ch_in
);
DPRINTK
(
"EXIT
\n
"
);
if
(
file
->
f_mode
&
FMODE_WRITE
)
{
via_dsp_drain_dac
(
card
,
file
->
f_flags
&
O_NONBLOCK
);
via_chan_free
(
card
,
&
card
->
ch_out
);
}
spin_lock_irqsave
(
&
card
->
lock
,
flags
);
card
->
open_mode
&=
~
(
file
->
f_mode
);
spin_unlock_irqrestore
(
&
card
->
lock
,
flags
);
wake_up
(
&
card
->
open_wait
);
MOD_DEC_USE_COUNT
;
DPRINTK
(
"EXIT, returning 0
\n
"
);
return
0
;
}
#ifdef
CONFIG
_PROC_FS
#ifdef
VIA
_PROC_FS
/****************************************************************
*
* /proc/driver/via
82cxxx
/info
* /proc/driver/via/info
*
*
*/
...
...
@@ -365,32 +2104,45 @@ static int via_info_read_proc (char *page, char **start, off_t off,
int
count
,
int
*
eof
,
void
*
data
)
{
#define YN(val,bit) (((val) & (bit)) ? "yes" : "no")
#define ED(val,bit) (((val) & (bit)) ? "enable" : "disable")
int
len
=
0
,
i
;
int
len
=
0
;
u8
r40
,
r41
,
r42
,
r44
;
struct
via_info
*
card
=
data
;
u16
tmp16
;
DPRINTK
(
"ENTER
\n
"
);
assert
(
card
!=
NULL
);
len
+=
sprintf
(
page
+
len
,
VIA_CARD_NAME
"
\n\n
"
);
for
(
i
=
0
;
i
<
num_cards
;
i
++
)
{
pci_read_config_byte
(
cards
[
i
].
pdev
,
0x40
,
&
r40
);
pci_read_config_byte
(
cards
[
i
].
pdev
,
0x42
,
&
r41
);
pci_read_config_byte
(
cards
[
i
].
pdev
,
0x42
,
&
r42
);
pci_read_config_byte
(
cards
[
i
].
pdev
,
0x44
,
&
r44
);
pci_read_config_byte
(
card
->
pdev
,
0x40
,
&
r40
);
pci_read_config_byte
(
card
->
pdev
,
0x41
,
&
r41
);
pci_read_config_byte
(
card
->
pdev
,
0x42
,
&
r42
);
pci_read_config_byte
(
card
->
pdev
,
0x44
,
&
r44
);
len
+=
sprintf
(
page
+
len
,
"40 AC97 Codec Ready: %s
\n
"
" AC97 Codec Low-power: %s
\n
"
"Via 82Cxxx PCI registers:
\n
"
"
\n
"
"40 Codec Ready: %s
\n
"
" Codec Low-power: %s
\n
"
" Secondary Codec Ready: %s
\n
"
"41 AC-Link Interface Enable: %s
\n
"
"
\n
"
"41 Interface Enable: %s
\n
"
" De-Assert Reset: %s
\n
"
" Force SYNC high: %s
\n
"
" Force SDO high: %s
\n
"
" Variable Sample Rate On-Demand Mode: %s
\n
"
" SGD Read Channel PCM Data Out: %s
\n
"
" FM Channel PCM Data Out: %s
\n
"
" SB PCM Data Out: %s
\n
"
"
\n
"
"42 Game port enabled: %s
\n
"
" SoundBlaster enabled: %s
\n
"
" FM enabled: %s
\n
"
" MIDI enabled: %s
\n
"
"
\n
"
"44 AC-Link Interface Access: %s
\n
"
" Secondary Codec Support: %s
\n
"
...
...
@@ -400,7 +2152,14 @@ static int via_info_read_proc (char *page, char **start, off_t off,
YN
(
r40
,
VIA_CR40_AC97_LOW_POWER
),
YN
(
r40
,
VIA_CR40_SECONDARY_READY
),
YN
(
r41
,
VIA_CR41_ACLINK_ENABLE
),
ED
(
r41
,
VIA_CR41_AC97_ENABLE
),
YN
(
r41
,
(
1
<<
6
)),
YN
(
r41
,
(
1
<<
5
)),
YN
(
r41
,
(
1
<<
4
)),
ED
(
r41
,
(
1
<<
3
)),
ED
(
r41
,
(
1
<<
2
)),
ED
(
r41
,
(
1
<<
1
)),
ED
(
r41
,
(
1
<<
0
)),
YN
(
r42
,
VIA_CR42_GAME_ENABLE
),
YN
(
r42
,
VIA_CR42_SB_ENABLE
),
...
...
@@ -411,18 +2170,18 @@ static int via_info_read_proc (char *page, char **start, off_t off,
YN
(
r44
,
VIA_CR44_SECOND_CODEC_SUPPORT
)
);
}
DPRINTK
(
"EXIT, returning %d
\n
"
,
len
);
return
len
;
#undef YN
#undef ED
}
/****************************************************************
*
* /proc/driver/via
82cxxx
* /proc/driver/via
/... setup and cleanup
*
*
*/
...
...
@@ -431,87 +2190,93 @@ static int __init via_init_proc (void)
{
DPRINTK
(
"ENTER
\n
"
);
proc_mkdir
(
"driver/via_audio"
,
0
);
create_proc_read_entry
(
"driver/via_audio/info"
,
0
,
0
,
via_info_read_proc
,
NULL
)
;
if
(
!
proc_mkdir
(
"driver/via"
,
0
))
return
-
EIO
;
DPRINTK
(
"EXIT
\n
"
);
DPRINTK
(
"EXIT, returning 0
\n
"
);
return
0
;
}
static
void
__exit
via_cleanup_proc
(
void
)
static
void
via_cleanup_proc
(
void
)
{
DPRINTK
(
"ENTER
\n
"
);
remove_proc_entry
(
"driver/via_audio/info"
,
NULL
);
remove_proc_entry
(
"driver/via_audio"
,
NULL
);
DPRINTK
(
"EXIT
\n
"
);
}
remove_proc_entry
(
"driver/via"
,
NULL
);
#else
DPRINTK
(
"EXIT
\n
"
);
}
static
inline
int
via_init_proc
(
void
)
{
return
0
;
}
static
inline
void
via_cleanup_proc
(
void
)
{}
#endif
/* CONFIG_PROC_FS */
static
int
__init
via_card_init_proc
(
struct
via_info
*
card
)
{
char
s
[
32
];
int
rc
;
DPRINTK
(
"ENTER
\n
"
);
/****************************************************************
*
* Legacy SoundBlaster Pro, FM support via OSS
*
*
*/
sprintf
(
s
,
"driver/via/%d"
,
card
->
card_num
);
if
(
!
proc_mkdir
(
s
,
0
))
{
rc
=
-
EIO
;
goto
err_out_none
;
}
static
void
__init
via_attach_sb
(
struct
address_info
*
hw_config
)
{
DPRINTK
(
"ENTER
\n
"
);
sprintf
(
s
,
"driver/via/%d/info"
,
card
->
card_num
);
if
(
!
create_proc_read_entry
(
s
,
0
,
0
,
via_info_read_proc
,
card
))
{
rc
=
-
EIO
;
goto
err_out_dir
;
}
if
(
!
sb_dsp_init
(
hw_config
))
hw_config
->
slots
[
0
]
=
-
1
;
sprintf
(
s
,
"driver/via/%d/ac97"
,
card
->
card_num
);
if
(
!
create_proc_read_entry
(
s
,
0
,
0
,
ac97_read_proc
,
&
card
->
ac97
))
{
rc
=
-
EIO
;
goto
err_out_info
;
}
DPRINTK
(
"EXIT
\n
"
);
}
DPRINTK
(
"EXIT, returning 0
\n
"
);
return
0
;
err_out_info:
sprintf
(
s
,
"driver/via/%d/info"
,
card
->
card_num
);
remove_proc_entry
(
s
,
NULL
);
static
int
__init
via_probe_sb
(
struct
address_info
*
hw_config
)
{
DPRINTK
(
"ENTER
\n
"
);
err_out_dir:
sprintf
(
s
,
"driver/via/%d"
,
card
->
card_num
);
remove_proc_entry
(
s
,
NULL
);
if
(
check_region
(
hw_config
->
io_base
,
16
))
{
printk
(
KERN_DEBUG
PFX
"SBPro port 0x%x is already in use
\n
"
,
hw_config
->
io_base
);
return
0
;
}
DPRINTK
(
"EXIT after sb_dsp_detect
\n
"
);
return
sb_dsp_detect
(
hw_config
,
0
,
0
,
NULL
);
err_out_none:
DPRINTK
(
"EXIT, returning %d
\n
"
,
rc
);
return
rc
;
}
static
void
__exit
via_unload_sb
(
struct
address_info
*
hw_config
)
static
void
via_card_cleanup_proc
(
struct
via_info
*
card
)
{
char
s
[
32
];
DPRINTK
(
"ENTER
\n
"
);
if
(
hw_config
->
slots
[
0
]
!=
-
1
)
sb_dsp_unload
(
hw_config
,
1
);
sprintf
(
s
,
"driver/via/%d/ac97"
,
card
->
card_num
);
remove_proc_entry
(
s
,
NULL
);
DPRINTK
(
"EXIT
\n
"
);
sprintf
(
s
,
"driver/via/%d/info"
,
card
->
card_num
);
remove_proc_entry
(
s
,
NULL
);
sprintf
(
s
,
"driver/via/%d"
,
card
->
card_num
);
remove_proc_entry
(
s
,
NULL
);
DPRINTK
(
"EXIT
\n
"
);
}
static
const
struct
{
int
sb_irq
,
sb_dma
,
midi_base
,
sb_io_base
;
}
via_pnp_data
[]
__initdata
=
{
{
5
,
0
,
0x300
,
0x220
},
{
7
,
1
,
0x310
,
0x240
},
{
9
,
2
,
0x320
,
0x260
},
{
10
,
3
,
0x330
,
0x280
},
};
#else
static
inline
int
via_init_proc
(
void
)
{
return
0
;
}
static
inline
void
via_cleanup_proc
(
void
)
{}
static
inline
int
via_card_init_proc
(
struct
via_info
*
card
)
{
return
0
;
}
static
inline
void
via_card_cleanup_proc
(
struct
via_info
*
card
)
{}
#endif
/* VIA_PROC_FS */
/****************************************************************
...
...
@@ -521,202 +2286,251 @@ static const struct {
*
*/
static
int
__init
via
82cxxx_install
(
struct
pci_dev
*
pcidev
)
static
int
__init
via
_init_one
(
struct
pci_dev
*
pdev
,
const
struct
pci_device_id
*
id
)
{
int
sb_io_base
;
int
sb_irq
;
int
sb_dma
;
int
midi_base
,
rc
;
u8
tmp8
;
struct
via_info
*
card
=
&
cards
[
num_cards
];
int
rc
;
struct
via_info
*
card
;
u8
tmp
;
static
int
printed_version
=
0
;
DPRINTK
(
"ENTER
\n
"
);
card
->
pdev
=
pcidev
;
card
->
have_ac97
=
-
1
;
if
(
printed_version
++
==
0
)
printk
(
KERN_INFO
"Via 686a audio driver "
VIA_VERSION
"
\n
"
)
;
/* turn off legacy features, if not already */
pci_read_config_byte
(
pcidev
,
VIA_FUNC_ENABLE
,
&
tmp8
);
tmp8
&=
~
(
VIA_CR42_SB_ENABLE
|
VIA_CR42_MIDI_ENABLE
|
VIA_CR42_FM_ENABLE
);
pci_write_config_byte
(
pcidev
,
VIA_FUNC_ENABLE
,
tmp8
);
if
(
!
request_region
(
pci_resource_start
(
pdev
,
0
),
pci_resource_len
(
pdev
,
0
),
VIA_MODULE_NAME
))
{
printk
(
KERN_ERR
PFX
"unable to obtain I/O resources, aborting
\n
"
);
rc
=
-
EBUSY
;
goto
err_out
;
}
#if 0
/* chipset init copied from via */
pci_write_config_byte(pdev, 0x41,(0xc0));
udelay(10);
pci_read_config_byte(pdev, 0x41, &tmp);
udelay(10);
pci_write_config_byte(pdev, 0x41,(tmp | 0x0f));
udelay(10);
pci_read_config_byte(pdev, 0x42, &tmp);
udelay(10);
pci_write_config_byte(pdev, 0x42,(tmp | 0x3f));
#endif
if
(
pci_enable_device
(
pdev
))
{
rc
=
-
EIO
;
goto
err_out_none
;
}
#if 0
/* chipset init copied from ALSA */
pci_write_config_byte(pdev, 0x42, 0);
pci_write_config_byte(pdev, 0x41, 0x40);
udelay(100);
pci_write_config_byte(pdev, 0x41, 0x60);
udelay(10);
pci_write_config_byte(pdev, 0x41, 0xCC);
udelay(10);
{
u32 data = ((AC97_POWER_CONTROL & 0x7F) << 16) | 0x0000;
udelay(100);
outl (data, pci_resource_start (pdev, 0) + VIA_BASE0_AC97_CTRL);
}
#endif
card
=
kmalloc
(
sizeof
(
*
card
),
GFP_KERNEL
);
if
(
!
card
)
{
printk
(
KERN_ERR
PFX
"out of memory, aborting
\n
"
);
rc
=
-
ENOMEM
;
goto
err_out_none
;
}
pdev
->
driver_data
=
card
;
memset
(
card
,
0
,
sizeof
(
*
card
));
card
->
pdev
=
pdev
;
card
->
baseaddr
=
pci_resource_start
(
pdev
,
0
);
card
->
card_num
=
via_num_cards
++
;
spin_lock_init
(
&
card
->
lock
);
init_waitqueue_head
(
&
card
->
open_wait
);
/* if BAR 2 is present, chip is Rev H or later,
* which means it has a few extra features */
if
(
pci_resource_start
(
pdev
,
2
)
>
0
)
card
->
rev_h
=
1
;
if
(
pdev
->
irq
<
1
)
{
printk
(
KERN_ERR
PFX
"invalid PCI IRQ %d, aborting
\n
"
,
pdev
->
irq
);
rc
=
-
ENODEV
;
goto
err_out_kfree
;
}
if
(
!
(
pci_resource_flags
(
pdev
,
0
)
&
IORESOURCE_IO
))
{
printk
(
KERN_ERR
PFX
"unable to locate I/O resources, aborting
\n
"
);
rc
=
-
ENODEV
;
goto
err_out_kfree
;
}
/*
*
try to init AC97 mixer device
*
init AC97 mixer and codec
*/
rc
=
via_a
ttach_ac97
(
card
);
rc
=
via_a
c97_init
(
card
);
if
(
rc
)
{
printk
(
KERN_
WARNING
PFX
"AC97 init failed, SB legacy mode only
\n
"
)
;
printk
(
KERN_
ERR
PFX
"AC97 init failed, aborting
\n
"
);
goto
err_out_kfree
;
}
/* turn on legacy features */
pci_read_config_byte
(
pcidev
,
VIA_FUNC_ENABLE
,
&
tmp8
);
tmp8
|=
VIA_CR42_SB_ENABLE
|
VIA_CR42_MIDI_ENABLE
|
VIA_CR42_FM_ENABLE
;
pci_write_config_byte
(
pcidev
,
VIA_FUNC_ENABLE
,
tmp8
);
/* read legacy PNP info byte */
pci_read_config_byte
(
pcidev
,
VIA_PNP_CONTROL
,
&
tmp8
);
pci_write_config_byte
(
pcidev
,
VIA_PNP_CONTROL
,
tmp8
);
/*
* init DSP device
*/
rc
=
via_dsp_init
(
card
);
if
(
rc
)
{
printk
(
KERN_ERR
PFX
"DSP device init failed, aborting
\n
"
);
goto
err_out_have_mixer
;
}
sb_irq
=
via_pnp_data
[((
tmp8
>>
6
)
&
0x03
)].
sb_irq
;
sb_dma
=
via_pnp_data
[((
tmp8
>>
4
)
&
0x03
)].
sb_dma
;
midi_base
=
via_pnp_data
[((
tmp8
>>
2
)
&
0x03
)].
midi_base
;
sb_io_base
=
via_pnp_data
[(
tmp8
&
0x03
)].
sb_io_base
;
/*
* per-card /proc info
*/
rc
=
via_card_init_proc
(
card
);
if
(
rc
)
{
printk
(
KERN_ERR
PFX
"card-specific /proc init failed, aborting
\n
"
);
goto
err_out_have_dsp
;
}
udelay
(
100
);
/*
* init and turn on interrupts, as the last thing we do
*/
rc
=
via_interrupt_init
(
card
);
if
(
rc
)
{
printk
(
KERN_ERR
PFX
"interrupt init failed, aborting
\n
"
);
goto
err_out_have_proc
;
}
printk
(
KERN_INFO
PFX
"legacy "
"MIDI: 0x%X, SB: 0x%X / %d IRQ / %d DMA
\n
"
,
midi_base
,
sb_io_base
,
sb_irq
,
sb_dma
);
card
->
sb_data
.
name
=
VIA_CARD_NAME
;
card
->
sb_data
.
card_subtype
=
MDL_SBPRO
;
card
->
sb_data
.
io_base
=
sb_io_base
;
card
->
sb_data
.
irq
=
sb_irq
;
card
->
sb_data
.
dma
=
sb_dma
;
/* register legacy SoundBlaster Pro */
if
(
!
via_probe_sb
(
&
card
->
sb_data
))
{
printk
(
KERN_ERR
PFX
"SB probe @ 0x%X failed, aborting
\n
"
,
sb_io_base
);
DPRINTK
(
"EXIT, returning -1
\n
"
);
return
-
1
;
}
via_attach_sb
(
&
card
->
sb_data
);
card
->
opl3_data
.
name
=
card
->
sb_data
.
name
;
card
->
opl3_data
.
io_base
=
midi_base
;
card
->
opl3_data
.
irq
=
-
1
;
/* register legacy MIDI */
if
(
!
probe_uart401
(
&
card
->
opl3_data
))
{
printk
(
KERN_WARNING
PFX
"MIDI probe @ 0x%X failed, continuing
\n
"
,
midi_base
);
card
->
opl3_data
.
io_base
=
0
;
pci_read_config_byte
(
pdev
,
0x3C
,
&
tmp
);
if
((
tmp
&
0x0F
)
!=
pdev
->
irq
)
{
printk
(
KERN_WARNING
PFX
"IRQ fixup, 0x3C==0x%02X
\n
"
,
tmp
);
tmp
&=
0xF0
;
tmp
|=
pdev
->
irq
;
pci_write_config_byte
(
pdev
,
0x3C
,
tmp
);
DPRINTK
(
"new 0x3c==0x%02x
\n
"
,
tmp
);
}
else
{
attach_uart401
(
&
card
->
opl3_data
);
DPRINTK
(
"IRQ reg 0x3c==0x%02x, irq==%d
\n
"
,
tmp
,
tmp
&
0x0F
);
}
num_cards
++
;
printk
(
KERN_INFO
PFX
"board #%d at 0x%04lX, IRQ %d
\n
"
,
card
->
card_num
+
1
,
card
->
baseaddr
,
pdev
->
irq
);
DPRINTK
(
"EXIT, returning 0
\n
"
);
return
0
;
}
err_out_have_proc:
via_card_cleanup_proc
(
card
);
/*
* This loop walks the PCI configuration database and finds where
* the sound cards are.
*
* Note - only a single PCI scan occurs, eliminating possibility
* of multiple audio chips
*
*/
err_out_have_dsp:
via_dsp_cleanup
(
card
);
err_out_have_mixer:
via_ac97_cleanup
(
card
);
err_out_kfree:
#ifndef VIA_NDEBUG
memset
(
card
,
0xAB
,
sizeof
(
*
card
));
/* poison memory */
#endif
kfree
(
card
);
err_out_none:
release_region
(
pci_resource_start
(
pdev
,
0
),
pci_resource_len
(
pdev
,
0
));
err_out:
pdev
->
driver_data
=
NULL
;
DPRINTK
(
"EXIT - returning %d
\n
"
,
rc
);
return
rc
;
}
static
int
__init
probe_via82cxxx
(
void
)
static
void
__exit
via_remove_one
(
struct
pci_dev
*
pdev
)
{
struct
pci_dev
*
pcidev
=
NULL
;
struct
via_info
*
card
;
DPRINTK
(
"ENTER
\n
"
);
pcidev
=
pci_find_device
(
PCI_VENDOR_ID_VIA
,
PCI_DEVICE_ID_VIA_82C686_5
,
NULL
);
assert
(
pdev
!=
NULL
);
card
=
pdev
->
driver_data
;
assert
(
card
!=
NULL
);
via_interrupt_cleanup
(
card
);
via_card_cleanup_proc
(
card
);
via_dsp_cleanup
(
card
);
if
(
!
pcidev
||
via82cxxx_install
(
pcidev
)
!=
0
)
{
printk
(
KERN_ERR
PFX
"audio init failed
\n
"
);
DPRINTK
(
"EXIT, returning -1
\n
"
);
return
-
1
;
}
release_region
(
pci_resource_start
(
pdev
,
0
),
pci_resource_len
(
pdev
,
0
));
DPRINTK
(
"EXIT, returning 0
\n
"
);
return
0
;
#ifndef VIA_NDEBUG
memset
(
card
,
0xAB
,
sizeof
(
*
card
));
/* poison memory */
#endif
kfree
(
card
);
pdev
->
driver_data
=
NULL
;
pci_set_power_state
(
pdev
,
3
);
/* ...zzzzzz */
DPRINTK
(
"EXIT
\n
"
);
return
;
}
/*
* This function is called when the user or kernel loads the
* module into memory.
/****************************************************************
*
* Driver initialization and cleanup
*
*
*/
static
int
__init
init_via82cxxx_module
(
void
)
static
int
__init
init_via82cxxx_audio
(
void
)
{
u8
tmp
;
int
i
;
const
char
*
rev
=
"unknown!"
;
memset
(
cards
,
0
,
sizeof
(
cards
));
int
rc
;
DPRINTK
(
"ENTER
\n
"
);
if
(
!
pci_present
())
{
printk
(
KERN_DEBUG
PFX
"PCI not present, exiting
\n
"
);
DPRINTK
(
"EXIT, returning -ENODEV
\n
"
);
return
-
ENODEV
;
MOD_INC_USE_COUNT
;
rc
=
via_init_proc
();
if
(
rc
)
{
MOD_DEC_USE_COUNT
;
DPRINTK
(
"EXIT, returning %d
\n
"
,
rc
);
return
rc
;
}
if
(
probe_via82cxxx
()
!=
0
)
{
printk
(
KERN_ERR
PFX
"probe failed, aborting
\n
"
);
/* XXX unload cards registered so far, if any */
rc
=
pci_register_driver
(
&
via_driver
);
if
(
rc
<
1
)
{
if
(
rc
==
0
)
pci_unregister_driver
(
&
via_driver
);
via_cleanup_proc
();
MOD_DEC_USE_COUNT
;
DPRINTK
(
"EXIT, returning -ENODEV
\n
"
);
return
-
ENODEV
;
}
pci_read_config_byte
(
cards
[
0
].
pdev
,
PCI_REVISION_ID
,
&
tmp
);
for
(
i
=
0
;
i
<
arraysize
(
via_chip_revs
);
i
++
)
if
(
via_chip_revs
[
i
].
revision
==
tmp
)
{
rev
=
via_chip_revs
[
i
].
rev_name
;
break
;
}
printk
(
KERN_INFO
PFX
VIA_CARD_NAME
" loaded
\n
"
);
printk
(
KERN_INFO
PFX
"Chip rev %s. Features: SBPro compat%s%s
\n
"
,
rev
,
cards
[
0
].
opl3_data
.
io_base
==
0
?
""
:
", MPU-401 MIDI"
,
cards
[
0
].
have_ac97
==
-
1
?
""
:
", AC97 mixer"
);
if
(
via_init_proc
()
!=
0
)
{
printk
(
KERN_WARNING
PFX
"Unable to init experimental /proc, ignoring
\n
"
);
}
MOD_DEC_USE_COUNT
;
/*
* Binds us to the sound subsystem
*/
SOUND_LOCK
;
DPRINTK
(
"EXIT, returning 0
\n
"
);
return
0
;
}
/*
* This is called when it is removed. It will only be removed
* when its use count is 0. For sound the SOUND_LOCK/SOUND_UNLOCK
* macros hide the entire work for this.
*/
static
void
__exit
cleanup_via82cxxx_
module
(
void
)
static
void
__exit
cleanup_via82cxxx_
audio
(
void
)
{
DPRINTK
(
"ENTER
\n
"
);
if
(
cards
[
0
].
opl3_data
.
io_base
)
unload_uart401
(
&
cards
[
0
].
opl3_data
);
via_unload_sb
(
&
cards
[
0
].
sb_data
);
via_unload_ac97
(
&
cards
[
0
]);
pci_unregister_driver
(
&
via_driver
);
via_cleanup_proc
();
/*
* Final clean up with the sound layer
*/
SOUND_LOCK_END
;
DPRINTK
(
"EXIT
\n
"
);
}
module_init
(
init_via82cxxx_module
);
module_exit
(
cleanup_via82cxxx_module
);
module_init
(
init_via82cxxx_audio
);
module_exit
(
cleanup_via82cxxx_audio
);
drivers/usb/ov511.c
View file @
5cb39380
/*
* OmniVision OV511 Camera-to-USB Bridge Driver
* Copyright (c) 1999/2000 Mark W. McClelland
*
* Copyright (c) 1999-2000 Mark W. McClelland
* Many improvements by Bret Wallach
* Color fixes by by Orion Sky Lawlor, olawlor@acm.org, 2/26/2000
* Snapshot code by Kevin Moore
* OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
* Changes by Claudio Matsuoka, claudio@conectiva.com, 3/26/2000
*
* Based on the Linux CPiA driver.
*
* Released under GPL v.2 license.
*
* Version: 1.11
* Based on the Linux CPiA driver written by Peter Pregler,
* Scott J. Bertin and Johannes Erdfelt.
*
* Please see the file: linux/Documentation/usb/ov511.txt
* and the website at: http://alpha.dyndns.org/ov511
* for more info.
*/
/*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
...
...
@@ -33,23 +30,28 @@
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
static
const
char
version
[]
=
"1.13"
;
#define __NO_VERSION__
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/list.h>
#include <linux/malloc.h>
#include <linux/mm.h>
#include <linux/smp_lock.h>
#include <linux/videodev.h>
#include <linux/vmalloc.h>
#include <linux/wrapper.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/time.h>
#include <linux/fs.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/ctype.h>
#include <linux/pagemap.h>
#include <linux/usb.h>
#include <asm/io.h>
#include <asm/semaphore.h>
#include <linux/wrapper.h>
#ifdef CONFIG_KMOD
#include <linux/kmod.h>
#endif
#include "ov511.h"
...
...
@@ -59,11 +61,10 @@
#define MAX_FRAME_SIZE (640 * 480 * 3)
#define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval))
// FIXME - Should find a better way to do this.
#define DEFAULT_WIDTH 640
#define DEFAULT_HEIGHT 480
/
/ PARAMETER VARIABLES:
/
* PARAMETER VARIABLES: */
static
int
autoadjust
=
1
;
/* CCD dynamically changes exposure, etc... */
/* 0=no debug messages
...
...
@@ -85,24 +86,61 @@ static int snapshot = 0;
/* Sensor detection override (global for all attached cameras) */
static
int
sensor
=
0
;
/* Increase this if you are getting "Failed to read sensor ID..." */
static
int
i2c_detect_tries
=
5
;
/* For legal values, see the OV7610/7620 specs under register Common F,
upper nybble (set to 0-F) */
static
int
aperture
=
-
1
;
MODULE_PARM
(
autoadjust
,
"i"
);
MODULE_PARM
(
debug
,
"i"
);
MODULE_PARM
(
fix_rgb_offset
,
"i"
);
MODULE_PARM
(
snapshot
,
"i"
);
MODULE_PARM
(
sensor
,
"i"
);
MODULE_PARM
(
i2c_detect_tries
,
"i"
);
MODULE_PARM
(
aperture
,
"i"
);
MODULE_AUTHOR
(
"Mark McClelland (and others)"
);
MODULE_DESCRIPTION
(
"OV511 USB Camera Driver"
);
char
kernel_version
[]
=
UTS_RELEASE
;
/*******************************/
/* Memory management functions */
/*******************************/
static
struct
usb_driver
ov511_driver
;
#define MDEBUG(x) do { } while(0)
/* Debug memory management */
/**********************************************************************
* List of known OV511-based cameras
**********************************************************************/
static
struct
cam_list
clist
[]
=
{
{
0
,
"generic model (no ID)"
},
{
3
,
"D-Link DSB-C300"
},
{
4
,
"generic OV511/OV7610"
},
{
5
,
"Puretek PT-6007"
},
{
21
,
"Creative Labs WebCam 3"
},
{
36
,
"Koala-Cam"
},
{
38
,
"Lifeview USB Life TV"
},
/* No support yet! */
{
100
,
"Lifeview RoboCam"
},
{
102
,
"AverMedia InterCam Elite"
},
{
112
,
"MediaForte MV300"
},
/* or OV7110 evaluation kit */
{
-
1
,
NULL
}
};
static
struct
usb_driver
ov511_driver
;
/**********************************************************************
*
* Memory management
*
* This is a shameless copy from the USB-cpia driver (linux kernel
* version 2.3.29 or so, I have no idea what this code actually does ;).
* Actually it seems to be a copy of a shameless copy of the bttv-driver.
* Or that is a copy of a shameless copy of ... (To the powers: is there
* no generic kernel-function to do this sort of stuff?)
*
* Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says
* there will be one, but apparentely not yet -jerdfelt
*
* So I copied it again for the OV511 driver -claudio
**********************************************************************/
/* Given PGD from the address space's page table, return the kernel
* virtual mapping of the physical memory mapped at ADR.
...
...
@@ -119,31 +157,11 @@ static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr)
ptep
=
pte_offset
(
pmd
,
adr
);
pte
=
*
ptep
;
if
(
pte_present
(
pte
))
ret
=
page_address
(
pte_page
(
pte
))
|
(
adr
&
(
PAGE_SIZE
-
1
));
ret
=
page_address
(
pte_page
(
pte
))
|
(
adr
&
(
PAGE_SIZE
-
1
));
}
}
MDEBUG
(
printk
(
"uv2kva(%lx-->%lx)"
,
adr
,
ret
));
return
ret
;
}
static
inline
unsigned
long
uvirt_to_bus
(
unsigned
long
adr
)
{
unsigned
long
kva
,
ret
;
kva
=
uvirt_to_kva
(
pgd_offset
(
current
->
mm
,
adr
),
adr
);
ret
=
virt_to_bus
((
void
*
)
kva
);
MDEBUG
(
printk
(
"uv2b(%lx-->%lx)"
,
adr
,
ret
));
return
ret
;
}
static
inline
unsigned
long
kvirt_to_bus
(
unsigned
long
adr
)
{
unsigned
long
va
,
kva
,
ret
;
va
=
VMALLOC_VMADDR
(
adr
);
kva
=
uvirt_to_kva
(
pgd_offset_k
(
va
),
va
);
ret
=
virt_to_bus
((
void
*
)
kva
);
MDEBUG
(
printk
(
"kv2b(%lx-->%lx)"
,
adr
,
ret
));
return
ret
;
}
...
...
@@ -158,7 +176,6 @@ static inline unsigned long kvirt_to_pa(unsigned long adr)
va
=
VMALLOC_VMADDR
(
adr
);
kva
=
uvirt_to_kva
(
pgd_offset_k
(
va
),
va
);
ret
=
__pa
(
kva
);
MDEBUG
(
printk
(
"kv2pa(%lx-->%lx)"
,
adr
,
ret
));
return
ret
;
}
...
...
@@ -213,6 +230,266 @@ static void rvfree(void *mem, unsigned long size)
vfree
(
mem
);
}
/**********************************************************************
* /proc interface
* Based on the CPiA driver version 0.7.4 -claudio
**********************************************************************/
#ifdef CONFIG_PROC_FS
static
struct
proc_dir_entry
*
ov511_proc_root
=
NULL
;
#define YES_NO(x) ((x) ? "yes" : "no")
static
int
ov511_read_proc
(
char
*
page
,
char
**
start
,
off_t
off
,
int
count
,
int
*
eof
,
void
*
data
)
{
char
*
out
=
page
;
int
i
,
len
;
struct
usb_ov511
*
ov511
=
data
;
/* IMPORTANT: This output MUST be kept under PAGE_SIZE
* or we need to get more sophisticated. */
out
+=
sprintf
(
out
,
"custom_id : %d
\n
"
,
ov511
->
customid
);
out
+=
sprintf
(
out
,
"model : %s
\n
"
,
ov511
->
desc
?
clist
[
ov511
->
desc
].
description
:
"unknown"
);
out
+=
sprintf
(
out
,
"streaming : %s
\n
"
,
YES_NO
(
ov511
->
streaming
));
out
+=
sprintf
(
out
,
"grabbing : %s
\n
"
,
YES_NO
(
ov511
->
grabbing
));
out
+=
sprintf
(
out
,
"compress : %s
\n
"
,
YES_NO
(
ov511
->
compress
));
out
+=
sprintf
(
out
,
"subcapture : %s
\n
"
,
YES_NO
(
ov511
->
sub_flag
));
out
+=
sprintf
(
out
,
"sub_size : %d %d %d %d
\n
"
,
ov511
->
subx
,
ov511
->
suby
,
ov511
->
subw
,
ov511
->
subh
);
out
+=
sprintf
(
out
,
"num_frames : %d
\n
"
,
OV511_NUMFRAMES
);
for
(
i
=
0
;
i
<
OV511_NUMFRAMES
;
i
++
)
{
out
+=
sprintf
(
out
,
"frame : %d
\n
"
,
i
);
out
+=
sprintf
(
out
,
" depth : %d
\n
"
,
ov511
->
frame
[
i
].
depth
);
out
+=
sprintf
(
out
,
" size : %d %d
\n
"
,
ov511
->
frame
[
i
].
width
,
ov511
->
frame
[
i
].
height
);
out
+=
sprintf
(
out
,
" hdr_size : %d %d
\n
"
,
ov511
->
frame
[
i
].
hdrwidth
,
ov511
->
frame
[
i
].
hdrheight
);
out
+=
sprintf
(
out
,
" format : %d
\n
"
,
ov511
->
frame
[
i
].
format
);
out
+=
sprintf
(
out
,
" segsize : %d
\n
"
,
ov511
->
frame
[
i
].
segsize
);
#if 0
out += sprintf (out, " curline : %d\n",
ov511->frame[i].curline);
out += sprintf (out, " segment : %d\n",
ov511->frame[i].segment);
out += sprintf (out, " scanlength : %ld\n",
ov511->frame[i].scanlength);
out += sprintf (out, " bytesread : %ld\n",
ov511->frame[i].bytes_read);
#endif
}
out
+=
sprintf
(
out
,
"snap_enabled : %s
\n
"
,
YES_NO
(
ov511
->
snap_enabled
));
out
+=
sprintf
(
out
,
"bridge : %d
\n
"
,
ov511
->
bridge
);
out
+=
sprintf
(
out
,
"sensor : %d
\n
"
,
ov511
->
sensor
);
out
+=
sprintf
(
out
,
"packet_size : %d
\n
"
,
ov511
->
packet_size
);
len
=
out
-
page
;
len
-=
off
;
if
(
len
<
count
)
{
*
eof
=
1
;
if
(
len
<=
0
)
return
0
;
}
else
len
=
count
;
*
start
=
page
+
off
;
return
len
;
}
static
int
ov511_write_proc
(
struct
file
*
file
,
const
char
*
buffer
,
unsigned
long
count
,
void
*
data
)
{
int
retval
=
-
EINVAL
;
#if 0
/* struct cam_data *cam = data; */
struct usb_ov511 new_params;
int size = count;
int find_colon;
unsigned long val;
u32 command_flags = 0;
u8 new_mains;
if (down_interruptible(&cam->param_lock))
return -ERESTARTSYS;
/*
* Skip over leading whitespace
*/
while (count && isspace(*buffer)) {
--count;
++buffer;
}
#define MATCH(x) \
({ \
int _len = strlen(x), _ret, _colon_found; \
_ret = (_len <= count && strncmp(buffer, x, _len) == 0); \
if (_ret) { \
buffer += _len; \
count -= _len; \
if (find_colon) { \
_colon_found = 0; \
while (count && (*buffer == ' ' || *buffer == '\t' || \
(!_colon_found && *buffer == ':'))) { \
if (*buffer == ':') \
_colon_found = 1; \
--count; \
++buffer; \
} \
if (!count || !_colon_found) \
retval = -EINVAL; \
find_colon = 0; \
} \
} \
_ret; \
})
#define VALUE \
({ \
char *_p; \
unsigned long int _ret; \
_ret = simple_strtoul(buffer, &_p, 0); \
if (_p == buffer) \
retval = -EINVAL; \
else { \
count -= _p - buffer; \
buffer = _p; \
} \
_ret; \
})
retval = 0;
while (count && !retval) {
find_colon = 1;
if (MATCH("")) {
if (!retval)
val = VALUE;
if (!retval) {
if (val <= 0xff)
/* ... = val */ ;
else
retval = -EINVAL;
}
} else {
DBG("No match found\n");
retval = -EINVAL;
}
if (!retval) {
while (count && isspace(*buffer) && *buffer != '\n') {
--count;
++buffer;
}
if (count) {
if (*buffer != '\n' && *buffer != ';')
retval = -EINVAL;
else {
--count;
++buffer;
}
}
}
}
#undef MATCH
#undef FIRMWARE_VERSION
#undef VALUE
#undef FIND_VALUE
#undef FIND_END
if (!retval) {
if (command_flags & COMMAND_SETCOLOURPARAMS) {
/* Adjust cam->vp to reflect these changes */
cam->vp.brightness =
new_params.colourParams.brightness*65535/100;
cam->vp.contrast =
new_params.colourParams.contrast*65535/100;
cam->vp.colour =
new_params.colourParams.saturation*65535/100;
}
memcpy(&cam->params, &new_params, sizeof(struct cam_params));
cam->mainsFreq = new_mains;
cam->cmd_queue |= command_flags;
retval = size;
} else
PDEBUG(3, "error: %d\n", retval);
up(&cam->param_lock);
#endif
return
retval
;
}
static
void
create_proc_ov511_cam
(
struct
usb_ov511
*
ov511
)
{
char
name
[
7
];
struct
proc_dir_entry
*
ent
;
PDEBUG
(
4
,
"***************"
);
if
(
!
ov511_proc_root
||
!
ov511
)
return
;
sprintf
(
name
,
"video%d"
,
ov511
->
vdev
.
minor
);
PDEBUG
(
4
,
"==== name: %s"
,
name
);
ent
=
create_proc_entry
(
name
,
S_IFREG
|
S_IRUGO
|
S_IWUSR
,
ov511_proc_root
);
if
(
!
ent
)
return
;
ent
->
data
=
ov511
;
ent
->
read_proc
=
ov511_read_proc
;
ent
->
write_proc
=
ov511_write_proc
;
ent
->
size
=
3626
;
/* FIXME */
ov511
->
proc_entry
=
ent
;
}
static
void
destroy_proc_ov511_cam
(
struct
usb_ov511
*
ov511
)
{
char
name
[
7
];
if
(
!
ov511
||
!
ov511
->
proc_entry
)
return
;
sprintf
(
name
,
"video%d"
,
ov511
->
vdev
.
minor
);
PDEBUG
(
4
,
"==== name: %s"
,
name
);
#if 0
remove_proc_entry(name, ov511_proc_root);
ov511->proc_entry = NULL;
#endif
}
static
void
proc_ov511_create
(
void
)
{
ov511_proc_root
=
create_proc_entry
(
"ov511"
,
S_IFDIR
,
0
);
if
(
ov511_proc_root
)
ov511_proc_root
->
owner
=
THIS_MODULE
;
else
printk
(
"Unable to initialise /proc/ov511
\n
"
);
/***********/
}
static
void
proc_ov511_destroy
(
void
)
{
remove_proc_entry
(
"ov511"
,
0
);
}
#endif
/* CONFIG_PROC_FS */
/**********************************************************************
*
* Camera interface
*
**********************************************************************/
static
int
ov511_reg_write
(
struct
usb_device
*
dev
,
unsigned
char
reg
,
unsigned
char
value
)
...
...
@@ -233,6 +510,7 @@ static int ov511_reg_write(struct usb_device *dev,
return
rc
;
}
/* returns: negative is error, pos or zero is data */
static
int
ov511_reg_read
(
struct
usb_device
*
dev
,
unsigned
char
reg
)
{
...
...
@@ -247,14 +525,15 @@ static int ov511_reg_read(struct usb_device *dev, unsigned char reg)
PDEBUG
(
5
,
"reg read: 0x%02X:0x%02X"
,
reg
,
buffer
[
0
]);
if
(
rc
<
0
)
{
if
(
rc
<
0
)
{
err
(
"ov511_reg_read: error %d"
,
rc
);
return
rc
;
}
else
}
else
{
return
buffer
[
0
];
}
}
static
int
ov511_i2c_write
(
struct
usb_device
*
dev
,
unsigned
char
reg
,
unsigned
char
value
)
...
...
@@ -264,7 +543,7 @@ static int ov511_i2c_write(struct usb_device *dev,
PDEBUG
(
5
,
"i2c write: 0x%02X:0x%02X"
,
reg
,
value
);
/* Three byte write cycle */
for
(
retries
=
OV511_I2C_RETRIES
;;
)
{
for
(
retries
=
OV511_I2C_RETRIES
;
;
)
{
/* Select camera register */
rc
=
ov511_reg_write
(
dev
,
OV511_REG_I2C_SUB_ADDRESS_3_BYTE
,
reg
);
if
(
rc
<
0
)
goto
error
;
...
...
@@ -278,10 +557,10 @@ static int ov511_i2c_write(struct usb_device *dev,
if
(
rc
<
0
)
goto
error
;
do
rc
=
ov511_reg_read
(
dev
,
OV511_REG_I2C_CONTROL
);
while
(
rc
>
0
&&
((
rc
&
1
)
==
0
));
/* Retry until idle */
while
(
rc
>
0
&&
((
rc
&
1
)
==
0
));
/* Retry until idle */
if
(
rc
<
0
)
goto
error
;
if
((
rc
&
2
)
==
0
)
/* Ack? */
if
((
rc
&
2
)
==
0
)
/* Ack? */
break
;
#if 0
/* I2C abort */
...
...
@@ -301,13 +580,14 @@ static int ov511_i2c_write(struct usb_device *dev,
return
rc
;
}
/* returns: negative is error, pos or zero is data */
static
int
ov511_i2c_read
(
struct
usb_device
*
dev
,
unsigned
char
reg
)
{
int
rc
,
value
,
retries
;
/* Two byte write cycle */
for
(
retries
=
OV511_I2C_RETRIES
;;
)
{
for
(
retries
=
OV511_I2C_RETRIES
;
;
)
{
/* Select camera register */
rc
=
ov511_reg_write
(
dev
,
OV511_REG_I2C_SUB_ADDRESS_2_BYTE
,
reg
);
if
(
rc
<
0
)
goto
error
;
...
...
@@ -317,10 +597,10 @@ static int ov511_i2c_read(struct usb_device *dev, unsigned char reg)
if
(
rc
<
0
)
goto
error
;
do
rc
=
ov511_reg_read
(
dev
,
OV511_REG_I2C_CONTROL
);
while
(
rc
>
0
&&
((
rc
&
1
)
==
0
));
/* Retry until idle */
while
(
rc
>
0
&&
((
rc
&
1
)
==
0
));
/* Retry until idle */
if
(
rc
<
0
)
goto
error
;
if
((
rc
&
2
)
==
0
)
/* Ack? */
if
((
rc
&
2
)
==
0
)
/* Ack? */
break
;
/* I2C abort */
...
...
@@ -334,16 +614,16 @@ static int ov511_i2c_read(struct usb_device *dev, unsigned char reg)
}
/* Two byte read cycle */
for
(
retries
=
OV511_I2C_RETRIES
;;
)
{
for
(
retries
=
OV511_I2C_RETRIES
;
;
)
{
/* Initiate 2-byte read cycle */
rc
=
ov511_reg_write
(
dev
,
OV511_REG_I2C_CONTROL
,
0x05
);
if
(
rc
<
0
)
goto
error
;
do
rc
=
ov511_reg_read
(
dev
,
OV511_REG_I2C_CONTROL
);
while
(
rc
>
0
&&
((
rc
&
1
)
==
0
));
/* Retry until idle */
while
(
rc
>
0
&&
((
rc
&
1
)
==
0
));
/* Retry until idle */
if
(
rc
<
0
)
goto
error
;
if
((
rc
&
2
)
==
0
)
/* Ack? */
if
((
rc
&
2
)
==
0
)
/* Ack? */
break
;
/* I2C abort */
...
...
@@ -363,7 +643,8 @@ static int ov511_i2c_read(struct usb_device *dev, unsigned char reg)
/* This is needed to make ov511_i2c_write() work */
rc
=
ov511_reg_write
(
dev
,
OV511_REG_I2C_CONTROL
,
0x05
);
if
(
rc
<
0
)
goto
error
;
if
(
rc
<
0
)
goto
error
;
return
value
;
...
...
@@ -372,12 +653,13 @@ static int ov511_i2c_read(struct usb_device *dev, unsigned char reg)
return
rc
;
}
static
int
ov511_write_regvals
(
struct
usb_device
*
dev
,
struct
ov511_regvals
*
pRegvals
)
{
int
rc
;
while
(
pRegvals
->
bus
!=
OV511_DONE_BUS
)
{
while
(
pRegvals
->
bus
!=
OV511_DONE_BUS
)
{
if
(
pRegvals
->
bus
==
OV511_REG_BUS
)
{
if
((
rc
=
ov511_reg_write
(
dev
,
pRegvals
->
reg
,
pRegvals
->
val
))
<
0
)
...
...
@@ -400,6 +682,7 @@ static int ov511_write_regvals(struct usb_device *dev,
return
rc
;
}
#if 0
static void ov511_dump_i2c_range( struct usb_device *dev, int reg1, int regn)
{
...
...
@@ -411,12 +694,14 @@ static void ov511_dump_i2c_range( struct usb_device *dev, int reg1, int regn)
}
}
static void ov511_dump_i2c_regs( struct usb_device *dev)
{
PDEBUG(3, "I2C REGS");
ov511_dump_i2c_range(dev, 0x00, 0x38);
}
static void ov511_dump_reg_range( struct usb_device *dev, int reg1, int regn)
{
int i;
...
...
@@ -427,6 +712,7 @@ static void ov511_dump_reg_range( struct usb_device *dev, int reg1, int regn)
}
}
static void ov511_dump_regs( struct usb_device *dev)
{
PDEBUG(1, "CAMERA INTERFACE REGS");
...
...
@@ -451,6 +737,7 @@ static void ov511_dump_regs( struct usb_device *dev)
}
#endif
static
int
ov511_reset
(
struct
usb_device
*
dev
,
unsigned
char
reset_type
)
{
int
rc
;
...
...
@@ -465,6 +752,7 @@ static int ov511_reset(struct usb_device *dev, unsigned char reset_type)
return
rc
;
}
/* Temporarily stops OV511 from functioning. Must do this before changing
* registers while the camera is streaming */
static
inline
int
ov511_stop
(
struct
usb_device
*
dev
)
...
...
@@ -473,6 +761,7 @@ static inline int ov511_stop(struct usb_device *dev)
return
(
ov511_reg_write
(
dev
,
OV511_REG_SYSTEM_RESET
,
0x3d
));
}
/* Restarts OV511 after ov511_stop() is called */
static
inline
int
ov511_restart
(
struct
usb_device
*
dev
)
{
...
...
@@ -480,6 +769,7 @@ static inline int ov511_restart(struct usb_device *dev)
return
(
ov511_reg_write
(
dev
,
OV511_REG_SYSTEM_RESET
,
0x00
));
}
static
int
ov511_set_packet_size
(
struct
usb_ov511
*
ov511
,
int
size
)
{
int
alt
,
mult
;
...
...
@@ -487,7 +777,7 @@ static int ov511_set_packet_size(struct usb_ov511 *ov511, int size)
if
(
ov511_stop
(
ov511
->
dev
)
<
0
)
return
-
EIO
;
mult
=
size
/
32
;
mult
=
size
>>
5
;
if
(
ov511
->
bridge
==
BRG_OV511
)
{
if
(
size
==
0
)
alt
=
OV511_ALT_SIZE_0
;
...
...
@@ -502,8 +792,7 @@ static int ov511_set_packet_size(struct usb_ov511 *ov511, int size)
err
(
"Set packet size: invalid size (%d)"
,
size
);
return
-
EINVAL
;
}
}
else
if
(
ov511
->
bridge
==
BRG_OV511PLUS
)
{
}
else
if
(
ov511
->
bridge
==
BRG_OV511PLUS
)
{
if
(
size
==
0
)
alt
=
OV511PLUS_ALT_SIZE_0
;
else
if
(
size
==
33
)
alt
=
OV511PLUS_ALT_SIZE_33
;
else
if
(
size
==
129
)
alt
=
OV511PLUS_ALT_SIZE_129
;
...
...
@@ -516,16 +805,14 @@ static int ov511_set_packet_size(struct usb_ov511 *ov511, int size)
err
(
"Set packet size: invalid size (%d)"
,
size
);
return
-
EINVAL
;
}
}
else
{
}
else
{
err
(
"Set packet size: Invalid bridge type"
);
return
-
EINVAL
;
}
PDEBUG
(
3
,
"set packet size: %d, mult=%d, alt=%d"
,
size
,
mult
,
alt
);
if
(
ov511_reg_write
(
ov511
->
dev
,
OV511_REG_FIFO_PACKET_SIZE
,
mult
)
<
0
)
if
(
ov511_reg_write
(
ov511
->
dev
,
OV511_REG_FIFO_PACKET_SIZE
,
mult
)
<
0
)
return
-
ENOMEM
;
if
(
usb_set_interface
(
ov511
->
dev
,
ov511
->
iface
,
alt
)
<
0
)
{
...
...
@@ -545,8 +832,9 @@ static int ov511_set_packet_size(struct usb_ov511 *ov511, int size)
return
0
;
}
static
inline
int
ov7610_set_picture
(
struct
usb_ov511
*
ov511
,
struct
video_picture
*
p
)
static
inline
int
ov7610_set_picture
(
struct
usb_ov511
*
ov511
,
struct
video_picture
*
p
)
{
int
ret
;
struct
usb_device
*
dev
=
ov511
->
dev
;
...
...
@@ -556,10 +844,10 @@ static inline int ov7610_set_picture(struct usb_ov511 *ov511,
if
(
ov511_stop
(
dev
)
<
0
)
return
-
EIO
;
if
((
ret
=
ov511_i2c_read
(
dev
,
OV7610_REG_COM_B
))
<
0
)
if
((
ret
=
ov511_i2c_read
(
dev
,
OV7610_REG_COM_B
))
<
0
)
return
-
EIO
;
#if 0
if(ov511_i2c_write(dev, OV7610_REG_COM_B, ret & 0xfe) < 0)
if
(ov511_i2c_write(dev, OV7610_REG_COM_B, ret & 0xfe) < 0)
return -EIO;
#endif
if
(
ov511
->
sensor
==
SEN_OV7610
||
ov511
->
sensor
==
SEN_OV7620AE
)
...
...
@@ -572,17 +860,18 @@ static inline int ov7610_set_picture(struct usb_ov511 *ov511,
if
(
ov511_i2c_write
(
dev
,
OV7610_REG_BRT
,
p
->
brightness
>>
8
)
<
0
)
return
-
EIO
;
}
else
if
((
ov511
->
sensor
==
SEN_OV7620
)
}
else
if
((
ov511
->
sensor
==
SEN_OV7620
)
||
(
ov511
->
sensor
==
SEN_OV7620AE
))
{
// cur_con = ov511_i2c_read(dev, OV7610_REG_CNT);
// cur_brt = ov511_i2c_read(dev, OV7610_REG_BRT);
// // DEBUG_CODE
// PDEBUG(1, "con=%d brt=%d", ov511_i2c_read(dev, OV7610_REG_CNT),
// ov511_i2c_read(dev, OV7610_REG_BRT));
//
// if(ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0)
// return -EIO;
#if 0
cur_con = ov511_i2c_read(dev, OV7610_REG_CNT);
cur_brt = ov511_i2c_read(dev, OV7610_REG_BRT);
// DEBUG_CODE
PDEBUG(1, "con=%d brt=%d", ov511_i2c_read(dev, OV7610_REG_CNT),
ov511_i2c_read(dev, OV7610_REG_BRT));
if(ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0)
return -EIO;
#endif
}
if
(
ov511_restart
(
dev
)
<
0
)
...
...
@@ -591,8 +880,9 @@ static inline int ov7610_set_picture(struct usb_ov511 *ov511,
return
0
;
}
static
inline
int
ov7610_get_picture
(
struct
usb_ov511
*
ov511
,
struct
video_picture
*
p
)
static
inline
int
ov7610_get_picture
(
struct
usb_ov511
*
ov511
,
struct
video_picture
*
p
)
{
int
ret
;
struct
usb_device
*
dev
=
ov511
->
dev
;
...
...
@@ -613,7 +903,11 @@ static inline int ov7610_get_picture(struct usb_ov511 *ov511,
p
->
hue
=
0x8000
;
p
->
whiteness
=
105
<<
8
;
#if 0
p->depth = 3; /* Don't know if this is right */
#else
p
->
depth
=
24
;
#endif
p
->
palette
=
VIDEO_PALETTE_RGB24
;
if
(
ov511_restart
(
dev
)
<
0
)
...
...
@@ -622,7 +916,9 @@ static inline int ov7610_get_picture(struct usb_ov511 *ov511,
return
0
;
}
static
int
ov511_mode_init_regs
(
struct
usb_ov511
*
ov511
,
static
int
ov511_mode_init_regs
(
struct
usb_ov511
*
ov511
,
int
width
,
int
height
,
int
mode
,
int
sub_flag
)
{
int
rc
=
0
;
...
...
@@ -630,7 +926,7 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
int
hwsbase
=
0
;
int
hwebase
=
0
;
PDEBUG
(
3
,
"
ov511_mode_init_regs(ov511, w:%d, h:%d, mode:%d, sub:%d)
"
,
PDEBUG
(
3
,
"
width:%d, height:%d, mode:%d, sub:%d
"
,
width
,
height
,
mode
,
sub_flag
);
if
(
ov511_stop
(
ov511
->
dev
)
<
0
)
...
...
@@ -644,6 +940,7 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
ov511_i2c_write
(
dev
,
0x0e
,
0x44
);
}
ov511_i2c_write
(
dev
,
0x13
,
autoadjust
?
0x21
:
0x20
);
/* For snapshot */
ov511_reg_write
(
dev
,
0x1e
,
0x00
);
ov511_reg_write
(
dev
,
0x1f
,
0x01
);
...
...
@@ -655,6 +952,7 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
ov511_i2c_write
(
dev
,
0x0e
,
0x04
);
}
ov511_i2c_write
(
dev
,
0x13
,
autoadjust
?
0x01
:
0x00
);
/* For snapshot */
ov511_reg_write
(
dev
,
0x1e
,
0x01
);
ov511_reg_write
(
dev
,
0x1f
,
0x03
);
...
...
@@ -665,13 +963,16 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
case
SEN_OV7610
:
case
SEN_OV7620AE
:
hwsbase
=
0x38
;
hwebase
=
0x3a
;
break
;
hwebase
=
0x3a
;
break
;
case
SEN_OV7620
:
hwsbase
=
0x2c
;
hwebase
=
0x2d
;
break
;
hwebase
=
0x2d
;
break
;
default:
hwsbase
=
0
;
hwebase
=
0
;
break
;
hwebase
=
0
;
break
;
}
if
(
width
==
640
&&
height
==
480
)
{
...
...
@@ -763,38 +1064,43 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
}
/*************************************************************
Turn a YUV4:2:0 block into an RGB block
Video4Linux seems to use the blue, green, red channel
order convention-- rgb[0] is blue, rgb[1] is green, rgb[2] is red.
Color space conversion coefficients taken from the excellent
http://www.inforamp.net/~poynton/ColorFAQ.html
In his terminology, this is a CCIR 601.1 YCbCr -> RGB.
Y values are given for all 4 pixels, but the U (Pb)
and V (Pr) are assumed constant over the 2x2 block.
/**********************************************************************
*
* Color correction functions
*
**********************************************************************/
To avoid floating point arithmetic, the color conversion
coefficients are scaled into 16.16 fixed-point integers.
/*
* Turn a YUV4:2:0 block into an RGB block
*
* Video4Linux seems to use the blue, green, red channel
* order convention-- rgb[0] is blue, rgb[1] is green, rgb[2] is red.
*
* Color space conversion coefficients taken from the excellent
* http://www.inforamp.net/~poynton/ColorFAQ.html
* In his terminology, this is a CCIR 601.1 YCbCr -> RGB.
* Y values are given for all 4 pixels, but the U (Pb)
* and V (Pr) are assumed constant over the 2x2 block.
*
* To avoid floating point arithmetic, the color conversion
* coefficients are scaled into 16.16 fixed-point integers.
*/
*************************************************************/
// LIMIT: convert a 16.16 fixed-point value to a byte, with clipping.
/* LIMIT: convert a 16.16 fixed-point value to a byte, with clipping. */
#define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16)))
static
inline
void
ov511_move_420_block
(
int
yTL
,
int
yTR
,
int
yBL
,
int
yBR
,
int
u
,
int
v
,
static
inline
void
ov511_move_420_block
(
int
yTL
,
int
yTR
,
int
yBL
,
int
yBR
,
int
u
,
int
v
,
int
rowPixels
,
unsigned
char
*
rgb
)
{
const
double
brightness
=
1
.
0
;
//
0->black; 1->full scale
const
double
saturation
=
1
.
0
;
//
0->greyscale; 1->full color
const
double
fixScale
=
brightness
*
256
*
256
;
const
int
rvScale
=
(
int
)(
1
.
402
*
saturation
*
fixScale
);
const
int
guScale
=
(
int
)(
-
0
.
344136
*
saturation
*
fixScale
);
const
int
gvScale
=
(
int
)(
-
0
.
714136
*
saturation
*
fixScale
);
const
int
buScale
=
(
int
)(
1
.
772
*
saturation
*
fixScale
);
const
int
yScale
=
(
int
)(
fixScale
);
const
double
brightness
=
1
.
0
;
//
0->black; 1->full scale
const
double
saturation
=
1
.
0
;
//
0->greyscale; 1->full color
const
double
fixScale
=
brightness
*
256
*
256
;
const
int
rvScale
=
(
int
)(
1
.
402
*
saturation
*
fixScale
);
const
int
guScale
=
(
int
)(
-
0
.
344136
*
saturation
*
fixScale
);
const
int
gvScale
=
(
int
)(
-
0
.
714136
*
saturation
*
fixScale
);
const
int
buScale
=
(
int
)(
1
.
772
*
saturation
*
fixScale
);
const
int
yScale
=
(
int
)(
fixScale
);
int
r
=
rvScale
*
v
;
int
g
=
guScale
*
u
+
gvScale
*
v
;
...
...
@@ -803,57 +1109,54 @@ static inline void ov511_move_420_block(
yBL
*=
yScale
;
yBR
*=
yScale
;
//Write out top two pixels
rgb
[
0
]
=
LIMIT
(
b
+
yTL
);
rgb
[
1
]
=
LIMIT
(
g
+
yTL
);
rgb
[
2
]
=
LIMIT
(
r
+
yTL
);
rgb
[
3
]
=
LIMIT
(
b
+
yTR
);
rgb
[
4
]
=
LIMIT
(
g
+
yTR
);
rgb
[
5
]
=
LIMIT
(
r
+
yTR
);
rgb
+=
3
*
rowPixels
;
//Skip down to next line to write out bottom two pixels
rgb
[
0
]
=
LIMIT
(
b
+
yBL
);
rgb
[
1
]
=
LIMIT
(
g
+
yBL
);
rgb
[
2
]
=
LIMIT
(
r
+
yBL
);
rgb
[
3
]
=
LIMIT
(
b
+
yBR
);
rgb
[
4
]
=
LIMIT
(
g
+
yBR
);
rgb
[
5
]
=
LIMIT
(
r
+
yBR
);
}
rgb
[
0
]
=
LIMIT
(
b
+
yTL
);
rgb
[
1
]
=
LIMIT
(
g
+
yTL
);
rgb
[
2
]
=
LIMIT
(
r
+
yTL
);
rgb
[
3
]
=
LIMIT
(
b
+
yTR
);
rgb
[
4
]
=
LIMIT
(
g
+
yTR
);
rgb
[
5
]
=
LIMIT
(
r
+
yTR
);
//Skip down to next line to write out bottom two pixels
rgb
+=
3
*
rowPixels
;
rgb
[
0
]
=
LIMIT
(
b
+
yBL
);
rgb
[
1
]
=
LIMIT
(
g
+
yBL
);
rgb
[
2
]
=
LIMIT
(
r
+
yBL
);
rgb
[
3
]
=
LIMIT
(
b
+
yBR
);
rgb
[
4
]
=
LIMIT
(
g
+
yBR
);
rgb
[
5
]
=
LIMIT
(
r
+
yBR
);
}
/***************************************************************
For a 640x480 YUV4:2:0 images, data shows up in 1200 384 byte segments. The
first 64 bytes of each segment are U, the next 64 are V. The U and
V are arranged as follows:
0 1 ... 7
8 9 ... 15
...
56 57 ... 63
U and V are shipped at half resolution (1 U,V sample -> one 2x2 block).
The next 256 bytes are full resolution Y data and represent 4
squares of 8x8 pixels as follows:
0 1 ... 7 64 65 ... 71 ... 192 193 ... 199
8 9 ... 15 72 73 ... 79 200 201 ... 207
... ... ...
56 57 ... 63 120 121 127 248 249 ... 255
Note that the U and V data in one segment represents a 16 x 16 pixel
area, but the Y data represents a 32 x 8 pixel area.
If OV511_DUMPPIX is defined, _parse_data just dumps the
incoming segments, verbatim, in order, into the frame.
When used with vidcat -f ppm -s 640x480 this puts the data
on the standard output and can be analyzed with the parseppm.c
utility I wrote. That's a much faster way for figuring out how
this data is scrambled.
/*
* For a 640x480 YUV4:2:0 images, data shows up in 1200 384 byte segments.
* The first 64 bytes of each segment are U, the next 64 are V. The U and
* V are arranged as follows:
*
* 0 1 ... 7
* 8 9 ... 15
* ...
* 56 57 ... 63
*
* U and V are shipped at half resolution (1 U,V sample -> one 2x2 block).
*
* The next 256 bytes are full resolution Y data and represent 4 squares
* of 8x8 pixels as follows:
*
* 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199
* 8 9 ... 15 72 73 ... 79 200 201 ... 207
* ... ... ...
* 56 57 ... 63 120 121 127 248 249 ... 255
*
* Note that the U and V data in one segment represents a 16 x 16 pixel
* area, but the Y data represents a 32 x 8 pixel area.
*
* If OV511_DUMPPIX is defined, _parse_data just dumps the incoming segments,
* verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480
* this puts the data on the standard output and can be analyzed with the
* parseppm.c utility I wrote. That's a much faster way for figuring out how
* this data is scrambled.
*/
****************************************************************/
#define HDIV 8
#define WDIV (256/HDIV)
#undef OV511_DUMPPIX
static
void
ov511_parse_data_rgb24
(
unsigned
char
*
pIn0
,
unsigned
char
*
pOut0
,
int
iOutY
,
int
iOutUV
,
int
iHalf
,
int
iWidth
)
static
void
ov511_parse_data_rgb24
(
unsigned
char
*
pIn0
,
unsigned
char
*
pOut0
,
int
iOutY
,
int
iOutUV
,
int
iHalf
,
int
iWidth
)
{
#ifndef OV511_DUMPPIX
int
k
,
l
,
m
;
...
...
@@ -864,10 +1167,10 @@ static void ov511_parse_data_rgb24(unsigned char * pIn0,
if
(
!
iHalf
)
{
pIn
=
pIn0
+
128
;
pOut
=
pOut0
+
iOutY
;
for
(
k
=
0
;
k
<
4
;
k
++
)
{
for
(
k
=
0
;
k
<
4
;
k
++
)
{
pOut1
=
pOut
;
for
(
l
=
0
;
l
<
8
;
l
++
)
{
for
(
m
=
0
;
m
<
8
;
m
++
)
{
for
(
l
=
0
;
l
<
8
;
l
++
)
{
for
(
m
=
0
;
m
<
8
;
m
++
)
{
*
pOut1
=
*
pIn
++
;
pOut1
+=
3
;
}
...
...
@@ -880,23 +1183,24 @@ static void ov511_parse_data_rgb24(unsigned char * pIn0,
/* Use the first half of VUs to calculate value */
pIn
=
pIn0
;
pOut
=
pOut0
+
iOutUV
;
for
(
l
=
0
;
l
<
4
;
l
++
)
{
for
(
m
=
0
;
m
<
8
;
m
++
)
{
for
(
l
=
0
;
l
<
4
;
l
++
)
{
for
(
m
=
0
;
m
<
8
;
m
++
)
{
int
y00
=
*
(
pOut
);
int
y01
=
*
(
pOut
+
3
);
int
y10
=
*
(
pOut
+
iWidth
*
3
);
int
y11
=
*
(
pOut
+
iWidth
*
3
+
3
);
int
v
=
*
(
pIn
+
64
)
-
128
;
int
u
=
*
pIn
++
-
128
;
ov511_move_420_block
(
y00
,
y01
,
y10
,
y11
,
u
,
v
,
iWidth
,
pOut
);
ov511_move_420_block
(
y00
,
y01
,
y10
,
y11
,
u
,
v
,
iWidth
,
pOut
);
pOut
+=
6
;
}
pOut
+=
(
iWidth
*
2
-
16
)
*
3
;
}
/* Just copy the other UV rows */
for
(
l
=
0
;
l
<
4
;
l
++
)
{
for
(
m
=
0
;
m
<
8
;
m
++
)
{
for
(
l
=
0
;
l
<
4
;
l
++
)
{
for
(
m
=
0
;
m
<
8
;
m
++
)
{
*
pOut
++
=
*
(
pIn
+
64
);
*
pOut
=
*
pIn
++
;
pOut
+=
5
;
...
...
@@ -908,17 +1212,18 @@ static void ov511_parse_data_rgb24(unsigned char * pIn0,
if
(
iHalf
)
{
pIn
=
pIn0
+
128
;
pOut
=
pOut0
+
iOutY
;
for
(
k
=
0
;
k
<
4
;
k
++
)
{
for
(
k
=
0
;
k
<
4
;
k
++
)
{
pOut1
=
pOut
;
for
(
l
=
0
;
l
<
4
;
l
++
)
{
for
(
m
=
0
;
m
<
4
;
m
++
)
{
for
(
l
=
0
;
l
<
4
;
l
++
)
{
for
(
m
=
0
;
m
<
4
;
m
++
)
{
int
y10
=
*
(
pIn
+
8
);
int
y00
=
*
pIn
++
;
int
y11
=
*
(
pIn
+
8
);
int
y01
=
*
pIn
++
;
int
v
=
*
pOut1
-
128
;
int
u
=
*
(
pOut1
+
1
)
-
128
;
ov511_move_420_block
(
y00
,
y01
,
y10
,
y11
,
u
,
v
,
iWidth
,
pOut1
);
ov511_move_420_block
(
y00
,
y01
,
y10
,
y11
,
u
,
v
,
iWidth
,
pOut1
);
pOut1
+=
6
;
}
pOut1
+=
(
iWidth
*
2
-
8
)
*
3
;
...
...
@@ -930,42 +1235,45 @@ static void ov511_parse_data_rgb24(unsigned char * pIn0,
#else
/* Just dump pix data straight out for debug */
int
i
;
pOut0
+=
iSegmentY
*
384
;
for
(
i
=
0
;
i
<
384
;
i
++
)
{
int
i
,
j
;
pOut0
+=
iOutY
;
for
(
i
=
0
;
i
<
HDIV
;
i
++
)
{
for
(
j
=
0
;
j
<
WDIV
;
j
++
)
{
*
pOut0
++
=
*
pIn0
++
;
*
pOut0
++
=
*
pIn0
++
;
*
pOut0
++
=
*
pIn0
++
;
}
pOut0
+=
(
iWidth
-
WDIV
)
*
3
;
}
#endif
}
/***************************************************************
For 640x480 RAW BW images, data shows up in 1200 256 byte segments.
The segments represent 4 squares of 8x8 pixels as
follows:
0 1 ... 7 64 65 ... 71 ... 192 193 ... 199
8 9 ... 15 72 73 ... 79 200 201 ... 207
... ... ...
56 57 ... 63 120 121 127 248 249 ... 255
****************************************************************/
static
void
ov511_parse_data_grey
(
unsigned
char
*
pIn0
,
unsigned
char
*
pOut0
,
int
iOutY
,
int
iWidth
)
/*
* For 640x480 RAW BW images, data shows up in 1200 256 byte segments.
* The segments represent 4 squares of 8x8 pixels as follows:
*
* 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199
* 8 9 ... 15 72 73 ... 79 200 201 ... 207
* ... ... ...
* 56 57 ... 63 120 121 127 248 249 ... 255
*
*/
static
void
ov511_parse_data_grey
(
unsigned
char
*
pIn0
,
unsigned
char
*
pOut0
,
int
iOutY
,
int
iWidth
)
{
int
k
,
l
,
m
;
unsigned
char
*
pIn
;
unsigned
char
*
pOut
,
*
pOut1
;
unsigned
char
*
pIn
;
unsigned
char
*
pOut
,
*
pOut1
;
pIn
=
pIn0
;
pOut
=
pOut0
+
iOutY
;
for
(
k
=
0
;
k
<
4
;
k
++
)
{
for
(
k
=
0
;
k
<
4
;
k
++
)
{
pOut1
=
pOut
;
for
(
l
=
0
;
l
<
8
;
l
++
)
{
for
(
m
=
0
;
m
<
8
;
m
++
)
{
for
(
l
=
0
;
l
<
8
;
l
++
)
{
for
(
m
=
0
;
m
<
8
;
m
++
)
{
*
pOut1
++
=
*
pIn
++
;
}
pOut1
+=
iWidth
-
8
;
...
...
@@ -975,42 +1283,47 @@ static void ov511_parse_data_grey(unsigned char * pIn0,
}
/*
*************************************************************
/*
* fixFrameRGBoffset--
* My camera seems to return the red channel about 1 pixel
* low, and the blue channel about 1 pixel high. After YUV->RGB
* conversion, we can correct this easily. OSL 2/24/2000.
*
************************************************************
/
*/
static
void
fixFrameRGBoffset
(
struct
ov511_frame
*
frame
)
{
int
x
,
y
;
int
rowBytes
=
frame
->
width
*
3
,
w
=
frame
->
width
;
unsigned
char
*
rgb
=
frame
->
data
;
const
int
shift
=
1
;
//Distance to shift pixels by, vertically
const
int
shift
=
1
;
/* Distance to shift pixels by, vertically */
/* Don't bother with little images */
if
(
frame
->
width
<
400
)
return
;
//Don't bother with little images
return
;
//Shift red channel up
for
(
y
=
shift
;
y
<
frame
->
height
;
y
++
)
{
int
lp
=
(
y
-
shift
)
*
rowBytes
;
//Previous line offset
int
lc
=
y
*
rowBytes
;
//Current line offset
/* Shift red channel up */
for
(
y
=
shift
;
y
<
frame
->
height
;
y
++
)
{
int
lp
=
(
y
-
shift
)
*
rowBytes
;
/* Previous line offset */
int
lc
=
y
*
rowBytes
;
/* Current line offset */
for
(
x
=
0
;
x
<
w
;
x
++
)
rgb
[
lp
+
x
*
3
+
2
]
=
rgb
[
lc
+
x
*
3
+
2
];
//Shift red up
rgb
[
lp
+
x
*
3
+
2
]
=
rgb
[
lc
+
x
*
3
+
2
];
/* Shift red up */
}
//Shift blue channel down
for
(
y
=
frame
->
height
-
shift
-
1
;
y
>=
0
;
y
--
)
{
int
ln
=
(
y
+
shift
)
*
rowBytes
;
//Next line offset
int
lc
=
y
*
rowBytes
;
//Current line offset
/* Shift blue channel down */
for
(
y
=
frame
->
height
-
shift
-
1
;
y
>=
0
;
y
--
)
{
int
ln
=
(
y
+
shift
)
*
rowBytes
;
/* Next line offset */
int
lc
=
y
*
rowBytes
;
/* Current line offset */
for
(
x
=
0
;
x
<
w
;
x
++
)
rgb
[
ln
+
x
*
3
+
0
]
=
rgb
[
lc
+
x
*
3
+
0
];
//Shift blue down
rgb
[
ln
+
x
*
3
+
0
]
=
rgb
[
lc
+
x
*
3
+
0
];
/* Shift blue down */
}
}
/**********************************************************************
*
* OV511 data transfer, IRQ handler
*
**********************************************************************/
static
int
ov511_move_data
(
struct
usb_ov511
*
ov511
,
urb_t
*
urb
)
{
unsigned
char
*
cdata
;
...
...
@@ -1175,6 +1488,7 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
return
totlen
;
}
static
void
ov511_isoc_irq
(
struct
urb
*
urb
)
{
int
len
;
...
...
@@ -1187,15 +1501,18 @@ static void ov511_isoc_irq(struct urb *urb)
if
(
!
ov511
->
streaming
)
{
PDEBUG
(
2
,
"hmmm... not streaming, but got interrupt"
);
return
;
}
else
{
PDEBUG
(
5
,
"streaming. got interrupt"
);
}
sbuf
=
&
ov511
->
sbuf
[
ov511
->
cursbuf
];
/* Copy the data received into our scratch buffer */
if
(
ov511
->
curframe
>=
0
)
if
(
ov511
->
curframe
>=
0
)
{
len
=
ov511_move_data
(
ov511
,
urb
);
else
if
(
waitqueue_active
(
&
ov511
->
wq
))
}
else
if
(
waitqueue_active
(
&
ov511
->
wq
))
{
wake_up_interruptible
(
&
ov511
->
wq
);
}
/* Move to the next sbuf */
ov511
->
cursbuf
=
(
ov511
->
cursbuf
+
1
)
%
OV511_NUMSBUF
;
...
...
@@ -1203,12 +1520,13 @@ static void ov511_isoc_irq(struct urb *urb)
return
;
}
static
int
ov511_init_isoc
(
struct
usb_ov511
*
ov511
)
{
urb_t
*
urb
;
int
fx
,
err
;
PDEBUG
(
4
,
"ov511_init_isoc
"
);
PDEBUG
(
3
,
"*** Initializing capture ***
"
);
ov511
->
compress
=
0
;
ov511
->
curframe
=
-
1
;
...
...
@@ -1280,10 +1598,11 @@ static int ov511_init_isoc(struct usb_ov511 *ov511)
static
void
ov511_stop_isoc
(
struct
usb_ov511
*
ov511
)
{
PDEBUG
(
4
,
"ov511_stop_isoc"
);
if
(
!
ov511
->
streaming
||
!
ov511
->
dev
)
return
;
PDEBUG
(
3
,
"*** Stopping capture ***"
);
ov511_set_packet_size
(
ov511
,
0
);
ov511
->
streaming
=
0
;
...
...
@@ -1303,13 +1622,14 @@ static void ov511_stop_isoc(struct usb_ov511 *ov511)
}
}
static
int
ov511_new_frame
(
struct
usb_ov511
*
ov511
,
int
framenum
)
{
struct
ov511_frame
*
frame
;
int
width
,
height
;
PDEBUG
(
4
,
"ov511
_new_frame"
);
PDEBUG
(
4
,
"ov511
->curframe = %d, framenum = %d"
,
ov511
->
curframe
,
framenum
);
if
(
!
ov511
->
dev
)
return
-
1
;
...
...
@@ -1325,6 +1645,9 @@ static int ov511_new_frame(struct usb_ov511 *ov511, int framenum)
width
=
frame
->
width
;
height
=
frame
->
height
;
PDEBUG
(
4
,
"framenum = %d, width = %d, height = %d"
,
framenum
,
width
,
height
);
frame
->
grabstate
=
FRAME_GRABBING
;
frame
->
scanstate
=
STATE_SCANNING
;
frame
->
scanlength
=
0
;
/* accumulated in ov511_parse_data() */
...
...
@@ -1335,11 +1658,17 @@ static int ov511_new_frame(struct usb_ov511 *ov511, int framenum)
/* Make sure it's not too big */
if
(
width
>
DEFAULT_WIDTH
)
width
=
DEFAULT_WIDTH
;
#if 0
width = (width / 8) * 8; /* Multiple of 8 */
#endif
width
&=
~
7L
;
if
(
height
>
DEFAULT_HEIGHT
)
height
=
DEFAULT_HEIGHT
;
#if 0
height = (height / 4) * 4; /* Multiple of 4 */
#endif
width
&=
~
3L
;
// /* We want a fresh frame every 30 we get */
// ov511->compress = (ov511->compress + 1) % 30;
...
...
@@ -1348,7 +1677,12 @@ static int ov511_new_frame(struct usb_ov511 *ov511, int framenum)
}
/* Video 4 Linux API */
/****************************************************************************
*
* V4L API
*
***************************************************************************/
static
int
ov511_open
(
struct
video_device
*
dev
,
int
flags
)
{
int
err
=
-
EBUSY
;
...
...
@@ -1409,9 +1743,9 @@ static int ov511_open(struct video_device *dev, int flags)
out_unlock:
up
(
&
ov511
->
lock
);
return
err
;
}
static
void
ov511_close
(
struct
video_device
*
dev
)
{
struct
usb_ov511
*
ov511
=
(
struct
usb_ov511
*
)
dev
;
...
...
@@ -1438,16 +1772,23 @@ static void ov511_close(struct video_device *dev)
}
}
static
int
ov511_init_done
(
struct
video_device
*
dev
)
{
#ifdef CONFIG_PROC_FS
create_proc_ov511_cam
((
struct
usb_ov511
*
)
dev
);
#endif
return
0
;
}
static
long
ov511_write
(
struct
video_device
*
dev
,
const
char
*
buf
,
unsigned
long
count
,
int
noblock
)
{
return
-
EINVAL
;
}
static
int
ov511_ioctl
(
struct
video_device
*
vdev
,
unsigned
int
cmd
,
void
*
arg
)
{
struct
usb_ov511
*
ov511
=
(
struct
usb_ov511
*
)
vdev
;
...
...
@@ -1507,7 +1848,6 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
return
0
;
}
case
VIDIOCGPICT
:
{
struct
video_picture
p
;
...
...
@@ -1752,17 +2092,20 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
return
-
EINVAL
;
default:
return
-
ENOIOCTLCMD
;
}
}
/* End switch(cmd) */
return
0
;
}
static
long
ov511_read
(
struct
video_device
*
dev
,
char
*
buf
,
unsigned
long
count
,
int
noblock
)
{
struct
usb_ov511
*
ov511
=
(
struct
usb_ov511
*
)
dev
;
int
i
;
int
frmx
=
-
1
;
volatile
struct
ov511_frame
*
frame
;
PDEBUG
(
4
,
"
ov511_read:
%ld bytes, noblock=%d"
,
count
,
noblock
);
PDEBUG
(
4
,
"%ld bytes, noblock=%d"
,
count
,
noblock
);
if
(
!
dev
||
!
buf
)
return
-
EFAULT
;
...
...
@@ -1776,6 +2119,7 @@ static long ov511_read(struct video_device *dev, char *buf, unsigned long count,
else
if
(
ov511
->
frame
[
1
].
grabstate
>=
FRAME_DONE
)
/* _DONE or _ERROR */
frmx
=
1
;
/* If nonblocking we return immediately */
if
(
noblock
&&
(
frmx
==
-
1
))
return
-
EAGAIN
;
...
...
@@ -1794,19 +2138,26 @@ static long ov511_read(struct video_device *dev, char *buf, unsigned long count,
frame
=
&
ov511
->
frame
[
frmx
];
/* FIXME */
frame
->
segsize
=
frame
->
format
==
VIDEO_PALETTE_RGB24
?
384
:
256
;
frame
->
depth
=
frame
->
format
==
VIDEO_PALETTE_RGB24
?
24
:
8
;
restart:
if
(
!
ov511
->
dev
)
return
-
EIO
;
/* Wait while we're grabbing the image */
PDEBUG
(
4
,
"Waiting image grabbing"
);
while
(
frame
->
grabstate
==
FRAME_GRABBING
)
{
interruptible_sleep_on
(
&
ov511
->
frame
[
frmx
].
wq
);
if
(
signal_pending
(
current
))
return
-
EINTR
;
}
PDEBUG
(
4
,
"Got image, frame->grabstate = %d"
,
frame
->
grabstate
);
if
(
frame
->
grabstate
==
FRAME_ERROR
)
{
frame
->
bytes_read
=
0
;
err
(
"
ov511_read: e
rrored frame %d"
,
ov511
->
curframe
);
err
(
"
** ick! ** E
rrored frame %d"
,
ov511
->
curframe
);
if
(
ov511_new_frame
(
ov511
,
frmx
))
err
(
"ov511_read: ov511_new_frame error"
);
goto
restart
;
...
...
@@ -1814,14 +2165,21 @@ static long ov511_read(struct video_device *dev, char *buf, unsigned long count,
/* Repeat until we get a snapshot frame */
if
(
!
ov511
->
snap_enabled
)
{
PDEBUG
(
4
,
"snap disabled"
);
}
else
{
PDEBUG
(
4
,
"Waiting snapshot frame"
);
}
if
(
ov511
->
snap_enabled
&&
!
frame
->
snapshot
)
{
frame
->
bytes_read
=
0
;
if
(
ov511_new_frame
(
ov511
,
frmx
))
err
(
"ov511_
read: ov511_
new_frame error"
);
err
(
"ov511_new_frame error"
);
goto
restart
;
}
/* Clear the snapshot */
if
(
ov511
->
snap_enabled
)
PDEBUG
(
4
,
"Clear snapshot"
);
if
(
ov511
->
snap_enabled
&&
frame
->
snapshot
)
{
frame
->
snapshot
=
0
;
ov511_reg_write
(
ov511
->
dev
,
0x52
,
0x01
);
...
...
@@ -1829,20 +2187,24 @@ static long ov511_read(struct video_device *dev, char *buf, unsigned long count,
ov511_reg_write
(
ov511
->
dev
,
0x52
,
0x01
);
}
PDEBUG
(
4
,
"
ov511_read:
frmx=%d, bytes_read=%ld, scanlength=%ld"
,
frmx
,
PDEBUG
(
4
,
"frmx=%d, bytes_read=%ld, scanlength=%ld"
,
frmx
,
frame
->
bytes_read
,
frame
->
scanlength
);
/* copy bytes to user space; we allow for partials reads */
// if ((count + frame->bytes_read) > frame->scanlength)
// count = frame->scanlength - frame->bytes_read;
/* FIXME - count hardwired to be one frame... */
count
=
frame
->
width
*
frame
->
height
*
frame
->
depth
;
count
=
frame
->
width
*
frame
->
height
*
(
frame
->
depth
>>
3
)
;
if
(
copy_to_user
(
buf
,
frame
->
data
+
frame
->
bytes_read
,
count
))
PDEBUG
(
4
,
"Copy to user space: %ld bytes"
,
count
);
if
((
i
=
copy_to_user
(
buf
,
frame
->
data
+
frame
->
bytes_read
,
count
)))
{
PDEBUG
(
4
,
"Copy failed! %d bytes not copied"
,
i
);
return
-
EFAULT
;
}
frame
->
bytes_read
+=
count
;
PDEBUG
(
4
,
"
ov511_read:
{copy} count used=%ld, new bytes_read=%ld"
,
PDEBUG
(
4
,
"{copy} count used=%ld, new bytes_read=%ld"
,
count
,
frame
->
bytes_read
);
if
(
frame
->
bytes_read
>=
frame
->
scanlength
)
{
/* All data has been read */
...
...
@@ -1851,13 +2213,17 @@ static long ov511_read(struct video_device *dev, char *buf, unsigned long count,
/* Mark it as available to be used again. */
ov511
->
frame
[
frmx
].
grabstate
=
FRAME_UNUSED
;
if
(
ov511_new_frame
(
ov511
,
frmx
?
0
:
1
))
err
(
"ov511_
read: ov511_
new_frame returned error"
);
err
(
"ov511_new_frame returned error"
);
}
PDEBUG
(
4
,
"read finished, returning %ld (sweet)"
,
count
);
return
count
;
}
static
int
ov511_mmap
(
struct
video_device
*
dev
,
const
char
*
adr
,
unsigned
long
size
)
static
int
ov511_mmap
(
struct
video_device
*
dev
,
const
char
*
adr
,
unsigned
long
size
)
{
struct
usb_ov511
*
ov511
=
(
struct
usb_ov511
*
)
dev
;
unsigned
long
start
=
(
unsigned
long
)
adr
;
...
...
@@ -1888,6 +2254,7 @@ static int ov511_mmap(struct video_device *dev, const char *adr, unsigned long s
return
0
;
}
static
struct
video_device
ov511_template
=
{
name:
"OV511 USB Camera"
,
type:
VID_TYPE_CAPTURE
,
...
...
@@ -1901,12 +2268,81 @@ static struct video_device ov511_template = {
initialize:
ov511_init_done
,
};
static
int
ov7610_configure
(
struct
usb_ov511
*
ov511
)
/****************************************************************************
*
* OV511/OV7610 configuration
*
***************************************************************************/
static
int
ov76xx_configure
(
struct
usb_ov511
*
ov511
)
{
struct
usb_device
*
dev
=
ov511
->
dev
;
int
trie
s
;
int
i
,
succes
s
;
int
rc
;
static
struct
ov511_regvals
aRegvalsNorm7610
[]
=
{{
OV511_I2C_BUS
,
0x10
,
0xff
},
{
OV511_I2C_BUS
,
0x16
,
0x06
},
{
OV511_I2C_BUS
,
0x28
,
0x24
},
{
OV511_I2C_BUS
,
0x2b
,
0xac
},
{
OV511_I2C_BUS
,
0x05
,
0x00
},
{
OV511_I2C_BUS
,
0x06
,
0x00
},
{
OV511_I2C_BUS
,
0x12
,
0x00
},
{
OV511_I2C_BUS
,
0x38
,
0x81
},
{
OV511_I2C_BUS
,
0x28
,
0x24
},
/* 0c */
{
OV511_I2C_BUS
,
0x05
,
0x00
},
{
OV511_I2C_BUS
,
0x0f
,
0x05
},
{
OV511_I2C_BUS
,
0x15
,
0x01
},
{
OV511_I2C_BUS
,
0x20
,
0x1c
},
{
OV511_I2C_BUS
,
0x23
,
0x2a
},
{
OV511_I2C_BUS
,
0x24
,
0x10
},
{
OV511_I2C_BUS
,
0x25
,
0x8a
},
{
OV511_I2C_BUS
,
0x27
,
0xc2
},
{
OV511_I2C_BUS
,
0x29
,
0x03
},
/* 91 */
{
OV511_I2C_BUS
,
0x2a
,
0x04
},
{
OV511_I2C_BUS
,
0x2c
,
0xfe
},
{
OV511_I2C_BUS
,
0x30
,
0x71
},
{
OV511_I2C_BUS
,
0x31
,
0x60
},
{
OV511_I2C_BUS
,
0x32
,
0x26
},
{
OV511_I2C_BUS
,
0x33
,
0x20
},
{
OV511_I2C_BUS
,
0x34
,
0x48
},
{
OV511_I2C_BUS
,
0x12
,
0x24
},
{
OV511_I2C_BUS
,
0x11
,
0x01
},
{
OV511_I2C_BUS
,
0x0c
,
0x24
},
{
OV511_I2C_BUS
,
0x0d
,
0x24
},
{
OV511_DONE_BUS
,
0x0
,
0x00
},
};
static
struct
ov511_regvals
aRegvalsNorm7620
[]
=
{{
OV511_I2C_BUS
,
0x10
,
0xff
},
{
OV511_I2C_BUS
,
0x16
,
0x06
},
{
OV511_I2C_BUS
,
0x28
,
0x24
},
{
OV511_I2C_BUS
,
0x2b
,
0xac
},
{
OV511_I2C_BUS
,
0x12
,
0x00
},
{
OV511_I2C_BUS
,
0x28
,
0x24
},
{
OV511_I2C_BUS
,
0x05
,
0x00
},
{
OV511_I2C_BUS
,
0x0f
,
0x05
},
{
OV511_I2C_BUS
,
0x15
,
0x01
},
{
OV511_I2C_BUS
,
0x23
,
0x00
},
{
OV511_I2C_BUS
,
0x24
,
0x10
},
{
OV511_I2C_BUS
,
0x25
,
0x8a
},
{
OV511_I2C_BUS
,
0x27
,
0xe2
},
{
OV511_I2C_BUS
,
0x29
,
0x03
},
{
OV511_I2C_BUS
,
0x2a
,
0x00
},
{
OV511_I2C_BUS
,
0x2c
,
0xfe
},
{
OV511_I2C_BUS
,
0x30
,
0x71
},
{
OV511_I2C_BUS
,
0x31
,
0x60
},
{
OV511_I2C_BUS
,
0x32
,
0x26
},
{
OV511_I2C_BUS
,
0x33
,
0x20
},
{
OV511_I2C_BUS
,
0x34
,
0x48
},
{
OV511_I2C_BUS
,
0x12
,
0x24
},
{
OV511_I2C_BUS
,
0x11
,
0x01
},
{
OV511_I2C_BUS
,
0x0c
,
0x24
},
{
OV511_I2C_BUS
,
0x0d
,
0x24
},
{
OV511_DONE_BUS
,
0x0
,
0x00
},
};
if
(
ov511_reg_write
(
dev
,
OV511_REG_I2C_SLAVE_ID_WRITE
,
OV7610_I2C_WRITE_ID
)
<
0
)
return
-
1
;
...
...
@@ -1918,64 +2354,90 @@ static int ov7610_configure(struct usb_ov511 *ov511)
if
(
ov511_reset
(
dev
,
OV511_RESET_NOREGS
)
<
0
)
return
-
1
;
/* Reset the 76
10 and wait a bit for it to initialize
*/
/* Reset the 76
xx
*/
if
(
ov511_i2c_write
(
dev
,
0x12
,
0x80
)
<
0
)
return
-
1
;
schedule_timeout
(
1
+
150
*
HZ
/
1000
);
/* Dummy read to sync I2C */
if
(
ov511_i2c_read
(
dev
,
0x00
)
<
0
)
return
-
1
;
for
(
i
=
0
,
success
=
0
;
i
<
i2c_detect_tries
&&
!
success
;
i
++
)
{
if
((
ov511_i2c_read
(
dev
,
OV7610_REG_ID_HIGH
)
==
0x7F
)
&&
(
ov511_i2c_read
(
dev
,
OV7610_REG_ID_LOW
)
==
0xA2
))
success
=
1
;
tries
=
5
;
while
((
tries
>
0
)
&&
((
ov511_i2c_read
(
dev
,
OV7610_REG_ID_HIGH
)
!=
0x7F
)
||
(
ov511_i2c_read
(
dev
,
OV7610_REG_ID_LOW
)
!=
0xA2
)))
{
--
tries
;
/* Dummy read to sync I2C */
if
(
ov511_i2c_read
(
dev
,
0x00
)
<
0
)
return
-
1
;
/* Wait for it to initialize */
schedule_timeout
(
1
+
150
*
HZ
/
1000
);
}
if
(
tries
==
1
)
{
err
(
"Failed to read sensor ID. You might not have an OV7610/20,"
);
if
(
success
)
{
PDEBUG
(
1
,
"I2C synced in %d attempt(s)"
,
i
);
}
else
{
err
(
"Failed to read sensor ID. You might not have an OV76xx,"
);
err
(
"or it may be not responding. Report this to"
);
err
(
"mmcclelland@delphi.com"
);
return
-
1
;
}
/* Detect sensor if user didn't use override param */
if
(
sensor
==
0
)
{
rc
=
ov511_i2c_read
(
dev
,
OV7610_REG_COM_I
);
if
(
rc
<
0
)
{
err
(
"Error detecting sensor type"
);
return
-
1
;
}
else
if
((
rc
&
3
)
==
3
)
{
}
else
if
((
rc
&
3
)
==
3
)
{
printk
(
"ov511: Sensor is an OV7610
\n
"
);
ov511
->
sensor
=
SEN_OV7610
;
}
else
if
((
rc
&
3
)
==
1
)
{
}
else
if
((
rc
&
3
)
==
1
)
{
printk
(
"ov511: Sensor is an OV7620AE
\n
"
);
ov511
->
sensor
=
SEN_OV7620AE
;
}
else
if
((
rc
&
3
)
==
0
)
{
}
else
if
((
rc
&
3
)
==
0
)
{
printk
(
"ov511: Sensor is an OV7620
\n
"
);
ov511
->
sensor
=
SEN_OV7620
;
}
else
{
}
else
{
err
(
"Unknown image sensor version: %d"
,
rc
&
3
);
return
-
1
;
}
}
else
{
/* sensor != 0; user overrode detection */
}
else
{
/* sensor != 0; user overrode detection */
ov511
->
sensor
=
sensor
;
printk
(
"ov511: Sensor set to type %d
\n
"
,
ov511
->
sensor
);
}
if
(
ov511
->
sensor
==
SEN_OV7620
)
{
if
(
ov511_write_regvals
(
dev
,
aRegvalsNorm7620
))
return
-
1
;
}
else
{
if
(
ov511_write_regvals
(
dev
,
aRegvalsNorm7610
))
return
-
1
;
}
if
(
aperture
<
0
)
{
/* go with the default */
if
(
ov511_i2c_write
(
dev
,
0x26
,
0xa2
)
<
0
)
return
-
1
;
}
else
if
(
aperture
<=
0xf
)
{
/* user overrode default */
if
(
ov511_i2c_write
(
dev
,
0x26
,
(
aperture
<<
4
)
+
2
)
<
0
)
return
-
1
;
}
else
{
err
(
"Invalid setting for aperture; legal value: 0 - 15"
);
return
-
1
;
}
if
(
autoadjust
)
{
if
(
ov511_i2c_write
(
dev
,
0x13
,
0x01
)
<
0
)
return
-
1
;
if
(
ov511_i2c_write
(
dev
,
0x2d
,
ov511
->
sensor
==
SEN_OV7620
?
0x91
:
0x93
)
<
0
)
return
-
1
;
}
else
{
if
(
ov511_i2c_write
(
dev
,
0x13
,
0x00
)
<
0
)
return
-
1
;
if
(
ov511_i2c_write
(
dev
,
0x2d
,
ov511
->
sensor
==
SEN_OV7620
?
0x81
:
0x83
)
<
0
)
return
-
1
;
ov511_i2c_write
(
dev
,
0x28
,
ov511_i2c_read
(
dev
,
0x28
)
|
8
);
}
return
0
;
}
static
int
ov511_configure
(
struct
usb_ov511
*
ov511
)
{
struct
usb_device
*
dev
=
ov511
->
dev
;
int
rc
;
static
struct
ov511_regvals
aRegvalsInit
[]
=
{{
OV511_REG_BUS
,
OV511_REG_SYSTEM_RESET
,
0x7f
},
...
...
@@ -1988,7 +2450,7 @@ static int ov511_configure(struct usb_ov511 *ov511)
{
OV511_DONE_BUS
,
0x0
,
0x00
},
};
static
struct
ov511_regvals
aRegvalsNorm
7610
[]
=
static
struct
ov511_regvals
aRegvalsNorm
511
[]
=
{{
OV511_REG_BUS
,
0x20
,
0x01
},
{
OV511_REG_BUS
,
0x52
,
0x02
},
{
OV511_REG_BUS
,
0x52
,
0x00
},
...
...
@@ -2003,85 +2465,6 @@ static int ov511_configure(struct usb_ov511 *ov511)
{
OV511_REG_BUS
,
0x77
,
0x01
},
{
OV511_REG_BUS
,
0x78
,
0x06
},
{
OV511_REG_BUS
,
0x79
,
0x03
},
{
OV511_I2C_BUS
,
0x10
,
0xff
},
{
OV511_I2C_BUS
,
0x16
,
0x06
},
{
OV511_I2C_BUS
,
0x28
,
0x24
},
{
OV511_I2C_BUS
,
0x2b
,
0xac
},
{
OV511_I2C_BUS
,
0x05
,
0x00
},
{
OV511_I2C_BUS
,
0x06
,
0x00
},
{
OV511_I2C_BUS
,
0x12
,
0x00
},
{
OV511_I2C_BUS
,
0x38
,
0x81
},
{
OV511_I2C_BUS
,
0x28
,
0x24
},
/* 0c */
{
OV511_I2C_BUS
,
0x05
,
0x00
},
{
OV511_I2C_BUS
,
0x0f
,
0x05
},
{
OV511_I2C_BUS
,
0x15
,
0x01
},
{
OV511_I2C_BUS
,
0x20
,
0x1c
},
{
OV511_I2C_BUS
,
0x23
,
0x2a
},
{
OV511_I2C_BUS
,
0x24
,
0x10
},
{
OV511_I2C_BUS
,
0x25
,
0x8a
},
{
OV511_I2C_BUS
,
0x26
,
0x90
},
{
OV511_I2C_BUS
,
0x27
,
0xc2
},
{
OV511_I2C_BUS
,
0x29
,
0x03
},
/* 91 */
{
OV511_I2C_BUS
,
0x2a
,
0x04
},
{
OV511_I2C_BUS
,
0x2c
,
0xfe
},
{
OV511_I2C_BUS
,
0x30
,
0x71
},
{
OV511_I2C_BUS
,
0x31
,
0x60
},
{
OV511_I2C_BUS
,
0x32
,
0x26
},
{
OV511_I2C_BUS
,
0x33
,
0x20
},
{
OV511_I2C_BUS
,
0x34
,
0x48
},
{
OV511_I2C_BUS
,
0x12
,
0x24
},
{
OV511_I2C_BUS
,
0x11
,
0x01
},
{
OV511_I2C_BUS
,
0x0c
,
0x24
},
{
OV511_I2C_BUS
,
0x0d
,
0x24
},
{
OV511_DONE_BUS
,
0x0
,
0x00
},
};
static
struct
ov511_regvals
aRegvalsNorm7620
[]
=
{{
OV511_REG_BUS
,
0x20
,
0x01
},
{
OV511_REG_BUS
,
0x52
,
0x02
},
{
OV511_REG_BUS
,
0x52
,
0x00
},
{
OV511_REG_BUS
,
0x31
,
0x1f
},
{
OV511_REG_BUS
,
0x70
,
0x3f
},
{
OV511_REG_BUS
,
0x71
,
0x3f
},
{
OV511_REG_BUS
,
0x72
,
0x01
},
{
OV511_REG_BUS
,
0x73
,
0x01
},
{
OV511_REG_BUS
,
0x74
,
0x01
},
{
OV511_REG_BUS
,
0x75
,
0x01
},
{
OV511_REG_BUS
,
0x76
,
0x01
},
{
OV511_REG_BUS
,
0x77
,
0x01
},
{
OV511_REG_BUS
,
0x78
,
0x06
},
{
OV511_REG_BUS
,
0x79
,
0x03
},
{
OV511_I2C_BUS
,
0x10
,
0xff
},
{
OV511_I2C_BUS
,
0x16
,
0x06
},
{
OV511_I2C_BUS
,
0x28
,
0x24
},
{
OV511_I2C_BUS
,
0x2b
,
0xac
},
{
OV511_I2C_BUS
,
0x12
,
0x00
},
{
OV511_I2C_BUS
,
0x28
,
0x24
},
{
OV511_I2C_BUS
,
0x05
,
0x00
},
{
OV511_I2C_BUS
,
0x0f
,
0x05
},
{
OV511_I2C_BUS
,
0x15
,
0x01
},
{
OV511_I2C_BUS
,
0x23
,
0x00
},
{
OV511_I2C_BUS
,
0x24
,
0x10
},
{
OV511_I2C_BUS
,
0x25
,
0x8a
},
{
OV511_I2C_BUS
,
0x26
,
0xa2
},
{
OV511_I2C_BUS
,
0x27
,
0xe2
},
{
OV511_I2C_BUS
,
0x29
,
0x03
},
{
OV511_I2C_BUS
,
0x2a
,
0x00
},
{
OV511_I2C_BUS
,
0x2c
,
0xfe
},
{
OV511_I2C_BUS
,
0x30
,
0x71
},
{
OV511_I2C_BUS
,
0x31
,
0x60
},
{
OV511_I2C_BUS
,
0x32
,
0x26
},
{
OV511_I2C_BUS
,
0x33
,
0x20
},
{
OV511_I2C_BUS
,
0x34
,
0x48
},
{
OV511_I2C_BUS
,
0x12
,
0x24
},
{
OV511_I2C_BUS
,
0x11
,
0x01
},
{
OV511_I2C_BUS
,
0x0c
,
0x24
},
{
OV511_I2C_BUS
,
0x0d
,
0x24
},
{
OV511_DONE_BUS
,
0x0
,
0x00
},
};
...
...
@@ -2096,54 +2479,36 @@ static int ov511_configure(struct usb_ov511 *ov511)
return
-
EBUSY
;
}
if
((
rc
=
ov511_write_regvals
(
dev
,
aRegvalsInit
)))
return
rc
;
if
(
ov7610_configure
(
ov511
)
<
0
)
{
err
(
"failed to configure OV7610"
);
goto
error
;
}
if
(
ov511_write_regvals
(
dev
,
aRegvalsInit
))
goto
error
;
if
(
ov511_write_regvals
(
dev
,
aRegvalsNorm511
))
goto
error
;
ov511_set_packet_size
(
ov511
,
0
);
/* Disable compression */
if
(
ov511_reg_write
(
dev
,
OV511_OMNICE_ENABLE
,
0x00
)
<
0
)
goto
error
;
ov511
->
snap_enabled
=
snapshot
;
/* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
* (using read() instead). */
ov511
->
frame
[
0
].
width
=
DEFAULT_WIDTH
;
ov511
->
frame
[
0
].
height
=
DEFAULT_HEIGHT
;
ov511
->
frame
[
0
].
depth
=
24
;
/**************/
ov511
->
frame
[
0
].
bytes_read
=
0
;
ov511
->
frame
[
0
].
segment
=
0
;
ov511
->
frame
[
1
].
width
=
DEFAULT_WIDTH
;
ov511
->
frame
[
1
].
height
=
DEFAULT_HEIGHT
;
ov511
->
frame
[
1
].
depth
=
24
;
ov511
->
frame
[
1
].
bytes_read
=
0
;
ov511
->
frame
[
1
].
segment
=
0
;
/* Initialize to DEFAULT_WIDTH, DEFAULT_HEIGHT, YUV4:2:0 */
if
(
ov511
->
sensor
==
SEN_OV7620
)
{
if
(
ov511_write_regvals
(
dev
,
aRegvalsNorm7620
))
goto
error
;
}
else
{
if
(
ov511_write_regvals
(
dev
,
aRegvalsNorm7610
))
goto
error
;
if
(
ov76xx_configure
(
ov511
)
<
0
)
{
err
(
"failed to configure OV76xx"
);
goto
error
;
}
if
(
ov511_mode_init_regs
(
ov511
,
DEFAULT_WIDTH
,
DEFAULT_HEIGHT
,
VIDEO_PALETTE_RGB24
,
0
)
<
0
)
goto
error
;
if
(
autoadjust
)
{
if
(
ov511_i2c_write
(
dev
,
0x13
,
0x01
)
<
0
)
goto
error
;
if
(
ov511_i2c_write
(
dev
,
0x2d
,
ov511
->
sensor
==
SEN_OV7620
?
0x91
:
0x93
)
<
0
)
goto
error
;
}
else
{
if
(
ov511_i2c_write
(
dev
,
0x13
,
0x00
)
<
0
)
goto
error
;
if
(
ov511_i2c_write
(
dev
,
0x2d
,
ov511
->
sensor
==
SEN_OV7620
?
0x81
:
0x83
)
<
0
)
goto
error
;
ov511_i2c_write
(
dev
,
0x28
,
ov511_i2c_read
(
dev
,
0x28
)
|
8
);
}
VIDEO_PALETTE_RGB24
,
0
)
<
0
)
goto
error
;
return
0
;
...
...
@@ -2158,11 +2523,18 @@ static int ov511_configure(struct usb_ov511 *ov511)
return
-
EBUSY
;
}
/****************************************************************************
*
* USB routines
*
***************************************************************************/
static
void
*
ov511_probe
(
struct
usb_device
*
dev
,
unsigned
int
ifnum
)
{
struct
usb_interface_descriptor
*
interface
;
struct
usb_ov511
*
ov511
;
int
rc
;
int
i
;
PDEBUG
(
1
,
"probing for device..."
);
...
...
@@ -2195,71 +2567,50 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum)
ov511
->
dev
=
dev
;
ov511
->
iface
=
interface
->
bInterfaceNumber
;
if
(
dev
->
descriptor
.
idProduct
==
0x0511
)
{
switch
(
dev
->
descriptor
.
idProduct
)
{
case
0x0511
:
info
(
"USB OV511 camera found"
);
ov511
->
bridge
=
BRG_OV511
;
}
else
if
(
dev
->
descriptor
.
idProduct
==
0xA511
)
{
break
;
case
0xA511
:
info
(
"USB OV511+ camera found"
);
ov511
->
bridge
=
BRG_OV511PLUS
;
break
;
}
rc
=
ov511_reg_read
(
dev
,
OV511_REG_SYSTEM_CUSTOM_ID
);
if
(
rc
<
0
)
{
ov511
->
customid
=
ov511_reg_read
(
dev
,
OV511_REG_SYSTEM_CUSTOM_ID
);
if
(
ov511
->
customid
<
0
)
{
err
(
"Unable to read camera bridge registers"
);
goto
error
;
}
switch
(
ov511
->
customid
=
rc
)
{
case
0
:
/* This also means that no custom ID was set */
printk
(
"ov511: Camera is a generic model (no ID)
\n
"
);
break
;
case
3
:
printk
(
"ov511: Camera is a D-Link DSB-C300
\n
"
);
break
;
case
4
:
printk
(
"ov511: Camera is a generic OV511/OV7610
\n
"
);
break
;
case
5
:
printk
(
"ov511: Camera is a Puretek PT-6007
\n
"
);
break
;
case
21
:
printk
(
"ov511: Camera is a Creative Labs WebCam 3
\n
"
);
break
;
case
36
:
printk
(
"ov511: Camera is a Koala-Cam
\n
"
);
ov511
->
desc
=
-
1
;
PDEBUG
(
4
,
"CustomID = %d"
,
ov511
->
customid
);
for
(
i
=
0
;
clist
[
i
].
id
>=
0
;
i
++
)
{
if
(
ov511
->
customid
==
clist
[
i
].
id
)
{
printk
(
"Camera: %s
\n
"
,
clist
[
i
].
description
);
ov511
->
desc
=
i
;
break
;
case
38
:
printk
(
"ov511: Camera is a Lifeview USB Life TV
\n
"
);
printk
(
"ov511: This device is not supported, exiting...
\n
"
);
goto
error
;
break
;
case
100
:
printk
(
"ov511: Camera is a Lifeview RoboCam
\n
"
);
break
;
case
102
:
printk
(
"ov511: Camera is a AverMedia InterCam Elite
\n
"
);
break
;
case
112
:
/* The OmniVision OV7110 evaluation kit uses this too */
printk
(
"ov511: Camera is a MediaForte MV300
\n
"
);
break
;
default:
err
(
"Specific camera type (%d) not recognized"
,
rc
);
}
}
/* Lifeview USB Life TV not supported */
if
(
clist
[
i
].
id
==
38
)
{
err
(
"This device is not supported yet."
);
return
NULL
;
}
if
(
clist
[
i
].
id
==
-
1
)
{
err
(
"Camera type (%d) not recognized"
,
ov511
->
customid
);
err
(
"Please contact mmcclelland@delphi.com to request"
);
err
(
"support for your camera."
);
}
// if (ov511->bridge == BRG_OV511PLUS) {
// err("Sorry, the OV511+ chip is not supported yet");
// goto error;
// }
if
(
!
ov511_configure
(
ov511
))
{
ov511
->
user
=
0
;
ov511
->
user
=
0
;
init_MUTEX
(
&
ov511
->
lock
);
/* to 1 == available */
return
ov511
;
}
else
{
}
else
{
err
(
"Failed to configure camera"
);
goto
error
;
}
...
...
@@ -2275,9 +2626,9 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum)
return
NULL
;
}
static
void
ov511_disconnect
(
struct
usb_device
*
dev
,
void
*
ptr
)
{
struct
usb_ov511
*
ov511
=
(
struct
usb_ov511
*
)
ptr
;
// video_unregister_device(&ov511->vdev);
...
...
@@ -2318,6 +2669,11 @@ static void ov511_disconnect(struct usb_device *dev, void *ptr)
ov511
->
sbuf
[
0
].
urb
=
NULL
;
}
#ifdef CONFIG_PROC_FS
PDEBUG
(
3
,
"destroying /proc/ov511/video%d"
,
ov511
->
vdev
.
minor
);
destroy_proc_ov511_cam
(
ov511
);
#endif
/* Free the memory */
if
(
!
ov511
->
user
)
{
kfree
(
ov511
);
...
...
@@ -2332,18 +2688,35 @@ static struct usb_driver ov511_driver = {
{
NULL
,
NULL
}
};
/****************************************************************************
*
* Module routines
*
***************************************************************************/
static
int
__init
usb_ov511_init
(
void
)
{
#ifdef CONFIG_PROC_FS
PDEBUG
(
3
,
"creating /proc/ov511"
);
proc_ov511_create
();
#endif
if
(
usb_register
(
&
ov511_driver
)
<
0
)
return
-
1
;
info
(
"ov511 driver
registered"
);
info
(
"ov511 driver
version %s registered"
,
version
);
return
0
;
}
static
void
__exit
usb_ov511_exit
(
void
)
{
#ifdef CONFIG_PROC_FS
PDEBUG
(
3
,
"destroying /proc/ov511"
);
proc_ov511_destroy
();
#endif
usb_deregister
(
&
ov511_driver
);
info
(
"ov511 driver deregistered"
);
}
...
...
drivers/usb/ov511.h
View file @
5cb39380
#ifndef __LINUX_OV511_H
#define __LINUX_OV511_H
#include <asm/uaccess.h>
#include <linux/videodev.h>
#include <linux/smp_lock.h>
#define OV511_DEBUG
/* Turn on debug messages */
#ifdef OV511_DEBUG
# define PDEBUG(level, fmt, args...) \
if (debug >= level) printk("ov511:
" fmt "\n"
, ## args)
if (debug >= level) printk("ov511:
[" __PRETTY_FUNCTION__ ":%d] " fmt "\n", __LINE__
, ## args)
#else
# define PDEBUG(level, fmt, args...) do {} while(0)
#endif
...
...
@@ -114,7 +118,7 @@ if (debug >= level) printk("ov511: " fmt "\n" , ## args)
#define OV511PLUS_ALT_SIZE_769 6
#define OV511PLUS_ALT_SIZE_961 7
/*
ov
7610 registers */
/*
OV
7610 registers */
#define OV7610_REG_GAIN 0x00
#define OV7610_REG_BLUE 0x01
#define OV7610_REG_RED 0x02
...
...
@@ -265,7 +269,12 @@ struct usb_ov511 {
/* Device structure */
struct
usb_device
*
dev
;
#if 0
unsigned char customid; /* Type of camera */
#else
int
customid
;
int
desc
;
#endif
unsigned
char
iface
;
...
...
@@ -303,6 +312,16 @@ struct usb_ov511 {
int
sensor
;
/* Type of image sensor chip */
int
packet_size
;
/* Frame size per isoc desc */
/* proc interface */
struct
semaphore
param_lock
;
/* params lock for this camera */
struct
proc_dir_entry
*
proc_entry
;
/* /proc/ov511/videoX */
};
struct
cam_list
{
int
id
;
char
*
description
;
};
#endif
...
...
drivers/video/clgenfb.c
View file @
5cb39380
...
...
@@ -31,9 +31,10 @@
*
*/
#define CLGEN_VERSION "1.9.
5
"
#define CLGEN_VERSION "1.9.
6
"
#include <linux/config.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
...
...
@@ -2652,14 +2653,14 @@ static int __init clgen_pci_setup (struct clgenfb_info *info,
if
(
!
request_mem_region
(
board_addr
,
board_size
,
"clgenfb"
))
{
pci_write_config_word
(
pdev
,
PCI_COMMAND
,
tmp16
);
printk
(
KERN_ERR
"clgen: cannot reserve region 0x%l
u
, abort
\n
"
,
printk
(
KERN_ERR
"clgen: cannot reserve region 0x%l
x
, abort
\n
"
,
board_addr
);
return
-
1
;
}
#if 0 /* if the system didn't claim this region, we would... */
if (!request_mem_region(0xA0000, 65535, "clgenfb")) {
pci_write_config_word (pdev, PCI_COMMAND, tmp16);
printk(KERN_ERR "clgen: cannot reserve region 0x%l
u
, abort\n",
printk(KERN_ERR "clgen: cannot reserve region 0x%l
x
, abort\n",
0xA0000L);
release_mem_region(board_addr, board_size);
return -1;
...
...
@@ -2760,7 +2761,7 @@ static int __init clgen_zorro_setup (struct clgenfb_info *info,
info
->
board_size
=
board_size
=
z
->
resource
.
end
-
z
->
resource
.
start
+
1
;
if
(
!
request_mem_region
(
board_addr
,
board_size
,
"clgenfb"
))
{
printk
(
KERN_ERR
"clgen: cannot reserve region 0x%l
u
, abort
\n
"
,
printk
(
KERN_ERR
"clgen: cannot reserve region 0x%l
x
, abort
\n
"
,
board_addr
);
return
-
1
;
}
...
...
drivers/video/vesafb.c
View file @
5cb39380
...
...
@@ -543,12 +543,19 @@ int __init vesafb_init(void)
if
(
!
request_mem_region
(
video_base
,
video_size
,
"vesafb"
))
{
printk
(
KERN_ERR
"vesafb: abort, cannot reserve video memory at 0x%l
u
\n
"
,
"vesafb: abort, cannot reserve video memory at 0x%l
x
\n
"
,
video_base
);
return
-
1
;
return
-
EBUSY
;
}
video_vbase
=
ioremap
(
video_base
,
video_size
);
if
(
!
video_vbase
)
{
release_mem_region
(
video_base
,
video_size
);
printk
(
KERN_ERR
"vesafb: abort, cannot ioremap video memory 0x%lx @ 0x%lx
\n
"
,
video_size
,
video_base
);
return
-
EIO
;
}
printk
(
KERN_INFO
"vesafb: framebuffer at 0x%lx, mapped to 0x%p, size %dk
\n
"
,
video_base
,
video_vbase
,
video_size
/
1024
);
...
...
fs/buffer.c
View file @
5cb39380
...
...
@@ -2129,9 +2129,7 @@ static void sync_page_buffers(struct buffer_head *bh)
do
{
struct
buffer_head
*
p
=
tmp
;
tmp
=
tmp
->
b_this_page
;
if
(
buffer_locked
(
p
))
__wait_on_buffer
(
p
);
else
if
(
buffer_dirty
(
p
))
if
(
buffer_dirty
(
p
)
&&
!
buffer_locked
(
p
))
ll_rw_block
(
WRITE
,
1
,
&
p
);
}
while
(
tmp
!=
bh
);
}
...
...
include/linux/swap.h
View file @
5cb39380
...
...
@@ -171,13 +171,18 @@ do { \
spin_unlock(&pagemap_lru_lock); \
} while (0)
#define __lru_cache_del(page) \
do { \
list_del(&(page)->lru); \
nr_lru_pages--; \
} while (0)
#define lru_cache_del(page) \
do { \
if (!PageLocked(page)) \
BUG(); \
spin_lock(&pagemap_lru_lock); \
list_del(&(page)->lru); \
nr_lru_pages--; \
__lru_cache_del(page); \
spin_unlock(&pagemap_lru_lock); \
} while (0)
...
...
mm/filemap.c
View file @
5cb39380
...
...
@@ -67,7 +67,7 @@ void __add_page_to_hash_queue(struct page * page, struct page **p)
PAGE_BUG
(
page
);
}
static
void
remove_page_from_hash_queue
(
struct
page
*
page
)
static
inline
void
remove_page_from_hash_queue
(
struct
page
*
page
)
{
if
(
page
->
pprev_hash
)
{
if
(
page
->
next_hash
)
...
...
@@ -92,44 +92,71 @@ static inline int sync_page(struct page *page)
* sure the page is locked and that nobody else uses it - or that usage
* is safe.
*/
static
inline
void
__remove_inode_page
(
struct
page
*
page
)
{
remove_page_from_inode_queue
(
page
);
remove_page_from_hash_queue
(
page
);
page
->
mapping
=
NULL
;
}
void
remove_inode_page
(
struct
page
*
page
)
{
if
(
!
PageLocked
(
page
))
PAGE_BUG
(
page
);
spin_lock
(
&
pagecache_lock
);
remove_page_from_inode_queue
(
page
);
remove_page_from_hash_queue
(
page
);
page
->
mapping
=
NULL
;
__remove_inode_page
(
page
);
spin_unlock
(
&
pagecache_lock
);
}
#define ITERATIONS 100
void
invalidate_inode_pages
(
struct
inode
*
inode
)
{
struct
list_head
*
head
,
*
curr
;
struct
page
*
page
;
int
count
;
repeat:
head
=
&
inode
->
i_mapping
->
pages
;
while
(
head
!=
head
->
next
)
{
spin_lock
(
&
pagecache_lock
);
spin_lock
(
&
pagemap_lru_lock
);
head
=
&
inode
->
i_mapping
->
pages
;
curr
=
head
->
next
;
count
=
0
;
while
(
curr
!=
head
)
{
while
((
curr
!=
head
)
&&
(
count
++
<
ITERATIONS
)
)
{
page
=
list_entry
(
curr
,
struct
page
,
list
);
curr
=
curr
->
next
;
/* We cannot invalidate a locked page */
if
(
TryLockPage
(
page
))
continue
;
spin_unlock
(
&
pagecache_lock
);
lru_cache_del
(
page
);
remove_inode_page
(
page
);
__
lru_cache_del
(
page
);
__
remove_inode_page
(
page
);
UnlockPage
(
page
);
page_cache_release
(
page
);
goto
repeat
;
}
/* At this stage we have passed through the list
* once, and there may still be locked pages. */
if
(
head
->
next
!=
head
)
{
page
=
list_entry
(
head
->
next
,
struct
page
,
list
);
get_page
(
page
);
spin_unlock
(
&
pagemap_lru_lock
);
spin_unlock
(
&
pagecache_lock
);
/* We need to block */
lock_page
(
page
);
UnlockPage
(
page
);
page_cache_release
(
page
);
}
else
{
spin_unlock
(
&
pagemap_lru_lock
);
spin_unlock
(
&
pagecache_lock
);
}
}
}
/*
...
...
@@ -160,10 +187,10 @@ void truncate_inode_pages(struct address_space * mapping, loff_t lstart)
/* page wholly truncated - free it */
if
(
offset
>=
start
)
{
if
(
TryLockPage
(
page
))
{
spin_unlock
(
&
pagecache_lock
);
get_page
(
page
);
spin_unlock
(
&
pagecache_lock
);
wait_on_page
(
page
);
p
ut_pag
e
(
page
);
p
age_cache_releas
e
(
page
);
goto
repeat
;
}
get_page
(
page
);
...
...
@@ -253,7 +280,19 @@ int shrink_mmap(int priority, int gfp_mask)
goto
dispose_continue
;
count
--
;
/*
* I'm ambivalent on this one.. Should we try to
* maintain LRU on the LRU list, and put pages that
* are old at the end of the queue, even if that
* means that we'll re-scan then again soon and
* often waste CPU time? Or should be just let any
* pages we do not want to touch now for one reason
* or another percolate to be "young"?
*
dispose = &old;
*
*/
/*
* Avoid unscalable SMP locking for pages we can
...
...
@@ -323,9 +362,7 @@ int shrink_mmap(int priority, int gfp_mask)
/* is it a page-cache page? */
if
(
page
->
mapping
)
{
if
(
!
PageDirty
(
page
)
&&
!
pgcache_under_min
())
{
remove_page_from_inode_queue
(
page
);
remove_page_from_hash_queue
(
page
);
page
->
mapping
=
NULL
;
__remove_inode_page
(
page
);
spin_unlock
(
&
pagecache_lock
);
goto
made_inode_progress
;
}
...
...
mm/vmscan.c
View file @
5cb39380
...
...
@@ -347,7 +347,6 @@ static int swap_out(unsigned int priority, int gfp_mask)
struct
task_struct
*
p
;
int
counter
;
int
__ret
=
0
;
int
assign
=
0
;
lock_kernel
();
/*
...
...
@@ -364,7 +363,7 @@ static int swap_out(unsigned int priority, int gfp_mask)
* Think of swap_cnt as a "shadow rss" - it tells us which process
* we want to page out (always try largest first).
*/
counter
=
nr_threads
/
(
priority
+
1
);
counter
=
(
nr_threads
<<
1
)
>>
(
priority
>>
1
);
if
(
counter
<
1
)
counter
=
1
;
...
...
@@ -372,6 +371,7 @@ static int swap_out(unsigned int priority, int gfp_mask)
unsigned
long
max_cnt
=
0
;
struct
mm_struct
*
best
=
NULL
;
int
pid
=
0
;
int
assign
=
0
;
select:
read_lock
(
&
tasklist_lock
);
p
=
init_task
.
next_task
;
...
...
@@ -391,8 +391,6 @@ static int swap_out(unsigned int priority, int gfp_mask)
}
}
read_unlock
(
&
tasklist_lock
);
if
(
assign
==
1
)
assign
=
2
;
if
(
!
best
)
{
if
(
!
assign
)
{
assign
=
1
;
...
...
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