Commit 5cb39380 authored by Linus Torvalds's avatar Linus Torvalds

Import 2.3.99pre7

parent 0f0078de
......@@ -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.
......@@ -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:
......
......@@ -199,11 +199,22 @@
home now.
-- 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.07"
#define VERSION "Id: cdrom.c 3.07 2000/02/02"
#define REVISION "Revision: 3.08"
#define VERSION "Id: cdrom.c 3.08 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.asf, 5);
setup_report_key(&cgc, ai->lsasf.agid, 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)))
......
......@@ -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.57"
#define IDECD_VERSION "4.58"
#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, sizeof(*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
......
......@@ -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
};
......
......@@ -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 =
" Undates and info at http://www.scyld.com/network/starfire.html\n";
" Updates and info at http://www.scyld.com/network/starfire.html\n";
MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
MODULE_DESCRIPTION("Adaptec Starfire Ethernet driver");
......
......@@ -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
......
......@@ -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
......
......@@ -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 "\nCopyright (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,
......
......@@ -26,4 +26,4 @@ static struct notifier_block sound_notifier=
0
};
#endif
#endif /* _SOUNDMODULE_H */
......@@ -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 */
#ifdef 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 */
#ifdef 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 "via82cxxx"
#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)
{
return (via_chan_bufs_in_use (chan) == VIA_DMA_BUFFERS);
}
static inline int via_chan_empty (struct via_channel *chan)
{
return (atomic_read(&chan->next_buf) ==
atomic_read(&chan->buf_in_use));
}
/****************************************************************
*
* Miscellaneous debris
*
*
*/
static void via_stop_everything (struct via_info *card)
{
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)
{
struct resource *rsrc = &pdev->resource[0];
outw ((u16)data,rsrc->start+port);
outw ((u16)(data>>16),rsrc->start+port+2);
return via_set_rate (card, rate, 1);
}
static inline u32 via_ac97_read32 (struct pci_dev *pdev, int port)
static inline int via_set_dac_rate (struct via_info *card, int rate)
{
struct resource *rsrc = &pdev->resource[0];
return
((u32)inw (rsrc->start+port)) |
(((u32)inw (rsrc->start+port+2)) << 16);
return via_set_rate (card, rate, 0);
}
/****************************************************************
*
* Intel Audio Codec '97 interface
* Channel-specific operations
*
*
*/
static inline void via_ac97_wait_idle (struct pci_dev *pdev)
static int via_chan_init (struct via_info *card,
struct via_channel *chan, long chan_ofs)
{
u32 tmp;
int counter = VIA_COUNTER_LIMIT;
int i;
unsigned long flags;
DPRINTK ("ENTER\n");
assert (pdev != NULL);
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 {
tmp = via_ac97_read32 (pdev,VIA_AC97_CTRL);
} while ((tmp & VIA_CR80_BUSY) && (counter-- > 0));
if (current->need_resched)
schedule ();
else
udelay (10);
DPRINTK ("EXIT%s\n", counter > 0 ? "" : ", counter limit reached");
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 int via_ac97_read_reg (struct ac97_hwint *dev, u8 reg)
static u32 via_ac97_wait_valid (struct via_info *card)
{
u32 data;
struct via_info *card;
struct pci_dev *pdev;
u32 tmp;
int counter = VIA_COUNTER_LIMIT;
DPRINTK ("ENTER\n");
assert (dev != NULL);
assert (dev->driver_private != NULL);
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);
assert (file != NULL);
assert (buffer != NULL);
card = file->private_data;
assert (card != NULL);
if (ppos != &file->f_pos) {
DPRINTK ("EXIT, returning -ESPIPE\n");
return -ESPIPE;
}
rc = via_dsp_do_write (card, buffer, count, (file->f_flags & O_NONBLOCK));
DPRINTK("EXIT, returning %ld\n",(long) rc);
return rc;
}
static unsigned int via_dsp_poll(struct file *file, struct poll_table_struct *wait)
{
struct via_info *card;
unsigned int mask = 0;
DPRINTK ("ENTER\n");
assert (file != NULL);
assert (wait != NULL);
card = file->private_data;
assert (card != NULL);
if ((file->f_mode & FMODE_WRITE) &&
(atomic_read (&card->ch_out.state) != sgd_stopped)) {
poll_wait(file, &card->ch_out.wait, wait);
/* XXX is this correct */
if (atomic_read (&card->ch_out.buf_in_use) <
atomic_read (&card->ch_out.next_buf))
mask |= POLLOUT | POLLWRNORM;
}
card = (struct via_info *) dev->driver_private;
pdev = card->pdev;
assert (pdev != NULL);
DPRINTK("EXIT, returning %u\n", mask);
return mask;
}
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 0
if (! (data & VIA_CR80_FIRST_CODEC_VALID)) {
DPRINTK ("EXIT, first codec not valid, returning -1\n");
return -1;
static int via_dsp_drain_dac (struct via_info *card, int non_block)
{
DPRINTK ("ENTER, non_block = %d\n", non_block);
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;
}
#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
DPRINTK ("EXIT, returning %d\n", data & 0xFFFF);
return data & 0xFFFF;
DPRINTK ("sleeping\n");
interruptible_sleep_on (&card->ch_out.wait);
}
DPRINTK ("EXIT\n");
return 0;
}
static int via_ac97_write_reg (struct ac97_hwint *dev, u8 reg, u16 value)
static int via_dsp_ioctl_space (struct via_info *card,
struct via_channel *chan,
void *arg)
{
u32 data;
struct via_info *card;
struct pci_dev *pdev;
DPRINTK ("ENTER\n");
audio_buf_info info;
int n;
assert (dev != NULL);
assert (dev->driver_private != NULL);
info.fragstotal = VIA_DMA_BUFFERS;
info.fragsize = VIA_DMA_BUF_SIZE;
card = (struct via_info *) dev->driver_private;
pdev = card->pdev;
assert (pdev != 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;
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);
/* 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;
#if 0
if (! (data & VIA_CR80_FIRST_CODEC_VALID)) {
DPRINTK ("EXIT, first codec invalid, returning -1\n");
return -1;
}
#endif
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 int via_ac97_reset (struct ac97_hwint *dev)
static int via_dsp_ioctl (struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int rc = -EINVAL, rd=0, wr=0, val=0;
struct via_info *card;
struct pci_dev *pdev;
DPRINTK ("ENTER\n");
assert (dev != NULL);
assert (dev->driver_private != NULL);
DPRINTK ("ENTER, cmd = 0x%08X\n", cmd);
card = (struct via_info *) dev->driver_private;
pdev = card->pdev;
assert (pdev != NULL);
assert (file != NULL);
card = file->private_data;
assert (card != NULL);
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;
/* 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);
pci_write_config_word (pdev, PCI_COMMAND, PCI_COMMAND_IO);
/* 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;
DPRINTK ("EXIT, returning 0\n");
return 0;
}
/* 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);
/* 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);
/* 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("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);
static struct via_info *via_ac97_find_card_for_mixer (int dev)
{
int x;
DPRINTK ("SNDCTL_DSP_SETFRAGMENT (fragshift==0x%04X (%d), maxfrags==0x%04X (%d))\n",
val & 0xFFFF,
val & 0xFFFF,
(val >> 16) & 0xFFFF,
(val >> 16) & 0xFFFF);
DPRINTK ("ENTER\n");
/* just to shut up some programs */
return 0;
for (x = 0; x < num_cards; x++)
if (cards[x].mixer_oss_dev == dev) {
DPRINTK ("EXIT, returning %p\n", cards + x);
return cards + x;
}
/* 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;
DPRINTK ("EXIT, returning 0\n");
return NULL;
/* not implemented */
default:
DPRINTK ("unhandled ioctl\n");
break;
}
DPRINTK("EXIT, returning %d\n", rc);
return rc;
}
static int
via_ac97_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
static int via_dsp_open (struct inode *inode, struct file *file)
{
int rc;
struct via_info *card = via_ac97_find_card_for_mixer (dev);
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);
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);
card = pdev->driver_data;
DPRINTK ("dev_dsp = %d, minor = %d, assn = %d\n",
card->dev_dsp, minor,
(card->dev_dsp ^ minor) & ~0xf);
if (card != NULL) {
rc = ac97_mixer_ioctl (&card->ac97, cmd, arg);
DPRINTK ("EXIT, returning %d\n", rc);
return rc;
if (((card->dev_dsp ^ minor) & ~0xf) == 0)
goto match;
}
}
DPRINTK ("EXIT, returning -ENODEV\n");
return -ENODEV;
}
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;
}
spin_unlock_irqrestore (&card->lock, flags);
if (is_busy) {
rc = -EBUSY;
goto err_out;
}
static struct mixer_operations via_ac97_mixer_operations =
{
"VIA82Cxxx",
"via82cxxxAC97Mixer",
via_ac97_default_mixer_ioctl
};
DPRINTK("open_mode now 0x%x\n", open_mode);
static int __init via_attach_ac97 (struct via_info *card)
{
int mixer;
struct ac97_hwint *mdev;
/* 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;
DPRINTK ("ENTER\n");
spin_lock_irqsave (&card->lock, flags);
via_chan_pcm_fmt (card, &card->ch_out, 0);
spin_unlock_irqrestore (&card->lock, flags);
assert (card != NULL);
via_set_adc_rate (card, 8000);
}
mdev = &card->ac97;
/* 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;
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;
spin_lock_irqsave (&card->lock, flags);
via_chan_pcm_fmt (card, &card->ch_out, 0);
spin_unlock_irqrestore (&card->lock, flags);
if (ac97_init (mdev)) {
printk (KERN_ERR PFX "Unable to init AC97\n");
DPRINTK ("EXIT, returning -1\n");
return -1;
}
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;
via_set_dac_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);
printk (KERN_INFO PFX "Initialized AC97 mixer\n");
card->have_ac97 = mixer;
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)
{
DPRINTK ("ENTER\n");
struct via_info *card;
unsigned long flags;
DPRINTK ("ENTER\n");
assert (file != NULL);
card = file->private_data;
assert (card != NULL);
if (file->f_mode & FMODE_READ)
via_chan_free (card, &card->ch_in);
if (card->have_ac97 >= 0)
sound_unload_mixerdev (card->have_ac97);
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);
DPRINTK ("EXIT\n");
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/via82cxxx/info
* /proc/driver/via/info
*
*
*/
......@@ -365,64 +2104,84 @@ 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"
" Secondary Codec Ready: %s\n"
"41 AC-Link Interface Enable: %s\n"
"42 Game port enabled: %s\n"
" SoundBlaster enabled: %s\n"
" FM enabled: %s\n"
" MIDI enabled: %s\n"
len += sprintf (page+len,
"Via 82Cxxx PCI registers:\n"
"\n"
"40 Codec Ready: %s\n"
" Codec Low-power: %s\n"
" Secondary Codec Ready: %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"
"44 AC-Link Interface Access: %s\n"
" Secondary Codec Support: %s\n"
"\n",
"\n",
YN (r40, VIA_CR40_AC97_READY),
YN (r40, VIA_CR40_AC97_LOW_POWER),
YN (r40, VIA_CR40_SECONDARY_READY),
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),
YN (r42, VIA_CR42_FM_ENABLE),
YN (r42, VIA_CR42_MIDI_ENABLE),
YN (r44, VIA_CR44_AC_LINK_ACCESS),
YN (r44, VIA_CR44_SECOND_CODEC_SUPPORT)
YN (r40, VIA_CR40_AC97_READY),
YN (r40, VIA_CR40_AC97_LOW_POWER),
YN (r40, VIA_CR40_SECONDARY_READY),
YN (r41, VIA_CR41_ACLINK_ENABLE),
YN (r42, VIA_CR42_GAME_ENABLE),
YN (r42, VIA_CR42_SB_ENABLE),
YN (r42, VIA_CR42_FM_ENABLE),
YN (r42, VIA_CR42_MIDI_ENABLE),
);
YN (r44, VIA_CR44_AC_LINK_ACCESS),
YN (r44, VIA_CR44_SECOND_CODEC_SUPPORT)
);
}
DPRINTK("EXIT, returning %d\n", len);
return len;
#undef YN
#undef ED
}
/****************************************************************
*
* /proc/driver/via82cxxx
* /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);
DPRINTK("EXIT\n");
if (!proc_mkdir ("driver/via", 0))
return -EIO;
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");
}
#else
static inline int via_init_proc (void) { return 0; }
static inline void via_cleanup_proc (void) {}
#endif /* CONFIG_PROC_FS */
remove_proc_entry ("driver/via", NULL);
DPRINTK ("EXIT\n");
}
/****************************************************************
*
* Legacy SoundBlaster Pro, FM support via OSS
*
*
*/
static void __init via_attach_sb(struct address_info *hw_config)
static int __init via_card_init_proc (struct via_info *card)
{
char s[32];
int rc;
DPRINTK ("ENTER\n");
if(!sb_dsp_init(hw_config))
hw_config->slots[0] = -1;
sprintf (s, "driver/via/%d", card->card_num);
if (!proc_mkdir (s, 0)) {
rc = -EIO;
goto err_out_none;
}
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;
}
DPRINTK("EXIT\n");
}
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, returning 0\n");
return 0;
static int __init via_probe_sb(struct address_info *hw_config)
{
DPRINTK ("ENTER\n");
err_out_info:
sprintf (s, "driver/via/%d/info", 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_dir:
sprintf (s, "driver/via/%d", card->card_num);
remove_proc_entry (s, 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 via82cxxx_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");
if (printed_version++ == 0)
printk (KERN_INFO "Via 686a audio driver " VIA_VERSION "\n");
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;
}
card->pdev = pcidev;
card->have_ac97 = -1;
#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;
}
/* 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 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_attach_ac97 (card);
rc = via_ac97_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);
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;
}
/*
* 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
*
*/
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");
assert (pdev != NULL);
card = pdev->driver_data;
assert (card != NULL);
via_interrupt_cleanup (card);
via_card_cleanup_proc (card);
via_dsp_cleanup (card);
pcidev = pci_find_device (PCI_VENDOR_ID_VIA,
PCI_DEVICE_ID_VIA_82C686_5, NULL);
release_region (pci_resource_start (pdev, 0), pci_resource_len (pdev, 0));
if (!pcidev || via82cxxx_install (pcidev) != 0) {
printk (KERN_ERR PFX "audio init failed\n");
DPRINTK ("EXIT, returning -1\n");
return -1;
}
#ifndef VIA_NDEBUG
memset (card, 0xAB, sizeof (*card)); /* poison memory */
#endif
kfree (card);
DPRINTK ("EXIT, returning 0\n");
return 0;
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");
MOD_INC_USE_COUNT;
if (!pci_present ()) {
printk (KERN_DEBUG PFX "PCI not present, exiting\n");
DPRINTK ("EXIT, returning -ENODEV\n");
return -ENODEV;
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);
/*
* 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.
* Based on the Linux CPiA driver written by Peter Pregler,
* Scott J. Bertin and Johannes Erdfelt.
*
* Released under GPL v.2 license.
*
* Version: 1.11
*
* Please see the file: linux/Documentation/usb/ov511.txt
* and the website at: http://alpha.dyndns.org/ov511
* 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,9 +230,269 @@ 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)
unsigned char reg,
unsigned char value)
{
int rc;
......@@ -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,24 +525,25 @@ 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)
unsigned char reg,
unsigned char value)
{
int rc, retries;
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,15 +916,17 @@ static inline int ov7610_get_picture(struct usb_ov511 *ov511,
return 0;
}
static int ov511_mode_init_regs(struct usb_ov511 *ov511,
int width, int height, int mode, int sub_flag)
static int
ov511_mode_init_regs(struct usb_ov511 *ov511,
int width, int height, int mode, int sub_flag)
{
int rc = 0;
struct usb_device *dev = ov511->dev;
int hwsbase = 0;
int hwebase = 0;
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,34 +940,39 @@ 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);
} else {
ov511_reg_write(dev, 0x16, 0x01);
if (ov511->sensor == SEN_OV7610
if (ov511->sensor == SEN_OV7610
|| ov511->sensor == SEN_OV7620AE) {
/* not valid on the OV7620 */
ov511_i2c_write(dev, 0x0e, 0x04);
/* not valid on the OV7620 */
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);
}
/* The different sensor ICs handle setting up of window differently */
switch (ov511->sensor) {
case SEN_OV7610:
case SEN_OV7620AE:
hwsbase = 0x38;
hwebase = 0x3a; break;
case SEN_OV7620:
hwsbase = 0x2c;
hwebase = 0x2d; break;
default:
hwsbase = 0;
hwebase = 0; break;
switch (ov511->sensor) {
case SEN_OV7610:
case SEN_OV7620AE:
hwsbase = 0x38;
hwebase = 0x3a;
break;
case SEN_OV7620:
hwsbase = 0x2c;
hwebase = 0x2d;
break;
default:
hwsbase = 0;
hwebase = 0;
break;
}
if (width == 640 && height == 480) {
......@@ -680,12 +981,12 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>2));
/* horizontal window end */
ov511_i2c_write(dev, 0x18,
hwebase+((ov511->subx+ov511->subw)>>2));
hwebase+((ov511->subx+ov511->subw)>>2));
/* vertical window start */
ov511_i2c_write(dev, 0x19, 0x5+(ov511->suby>>1));
ov511_i2c_write(dev, 0x19, 0x5+(ov511->suby>>1));
/* vertical window end */
ov511_i2c_write(dev, 0x1a,
0x5+((ov511->suby+ov511->subh)>>1));
ov511_i2c_write(dev, 0x1a,
0x5+((ov511->suby+ov511->subh)>>1));
ov511_reg_write(dev, 0x12, (ov511->subw>>3)-1);
ov511_reg_write(dev, 0x13, (ov511->subh>>3)-1);
/* clock rate control */
......@@ -762,255 +1063,267 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511,
return rc;
}
/*************************************************************
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,
int rowPixels, unsigned char * rgb)
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);
int r = rvScale * v;
int g = guScale * u + gvScale * v;
int b = buScale * u;
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;
int b = buScale * u;
yTL *= yScale; yTR *= yScale;
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);
}
/***************************************************************
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:
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);
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
//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);
}
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;
unsigned char * pIn;
unsigned char * pOut, * pOut1;
int k, l, m;
unsigned char * pIn;
unsigned char * pOut, * pOut1;
/* Just copy the Y's if in the first stripe */
if (!iHalf) {
pIn = pIn0 + 128;
pOut = pOut0 + iOutY;
for (k = 0; k < 4; k++) {
pOut1 = pOut;
for (l = 0; l < 8; l++) {
for (m = 0; m < 8; m++) {
*pOut1 = *pIn++;
pOut1 += 3;
}
pOut1 += (iWidth - 8) * 3;
}
pOut += 8 * 3;
}
}
/* Just copy the Y's if in the first stripe */
if (!iHalf) {
pIn = pIn0 + 128;
pOut = pOut0 + iOutY;
for(k=0; k<4; k++) {
pOut1 = pOut;
for(l=0; l<8; l++) {
for(m=0; m<8; m++) {
*pOut1 = *pIn++;
pOut1 += 3;
}
pOut1 += (iWidth - 8) * 3;
}
pOut += 8 * 3;
}
}
/* 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++) {
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);
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++) {
*pOut++ = *(pIn + 64);
*pOut = *pIn++;
pOut += 5;
}
pOut += (iWidth*2 - 16) * 3;
}
/* Calculate values if it's the second half */
if (iHalf) {
pIn = pIn0 + 128;
pOut = pOut0 + iOutY;
for(k=0; k<4; k++) {
pOut1 = pOut;
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);
pOut1 += 6;
}
pOut1 += (iWidth*2 - 8) * 3;
pIn += 8;
}
pOut += 8 * 3;
}
}
/* 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++) {
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);
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++) {
*pOut++ = *(pIn + 64);
*pOut = *pIn++;
pOut += 5;
}
pOut += (iWidth*2 - 16) * 3;
}
/* Calculate values if it's the second half */
if (iHalf) {
pIn = pIn0 + 128;
pOut = pOut0 + iOutY;
for (k = 0; k < 4; k++) {
pOut1 = pOut;
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);
pOut1 += 6;
}
pOut1 += (iWidth*2 - 8) * 3;
pIn += 8;
}
pOut += 8 * 3;
}
}
#else
/* Just dump pix data straight out for debug */
int i;
pOut0 += iSegmentY * 384;
for(i=0; i<384; i++) {
*pOut0++ = *pIn0++;
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;
pIn = pIn0;
pOut = pOut0 + iOutY;
for(k=0; k<4; k++) {
pOut1 = pOut;
for(l=0; l<8; l++) {
for(m=0; m<8; m++) {
*pOut1++ = *pIn++;
}
pOut1 += iWidth - 8;
}
pOut += 8;
}
int k, l, m;
unsigned char *pIn;
unsigned char *pOut, *pOut1;
pIn = pIn0;
pOut = pOut0 + iOutY;
for (k = 0; k < 4; k++) {
pOut1 = pOut;
for (l = 0; l < 8; l++) {
for (m = 0; m < 8; m++) {
*pOut1++ = *pIn++;
}
pOut1 += iWidth - 8;
}
pOut += 8;
}
}
/**************************************************************
/*
* 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
if (frame->width < 400)
return; //Don't bother with little images
//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
}
//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
}
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 */
/* Don't bother with little images */
if (frame->width < 400)
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 */
for (x = 0; x < w; x++)
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 */
for (x = 0; x < w; x++)
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,28 +1501,32 @@ 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)
len = ov511_move_data(ov511, urb);
else if (waitqueue_active(&ov511->wq))
wake_up_interruptible(&ov511->wq);
if (ov511->curframe >= 0) {
len = ov511_move_data(ov511, urb);
} else if (waitqueue_active(&ov511->wq)) {
wake_up_interruptible(&ov511->wq);
}
/* Move to the next sbuf */
ov511->cursbuf = (ov511->cursbuf + 1) % OV511_NUMSBUF;
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;
......@@ -1235,11 +1553,11 @@ static int ov511_init_isoc(struct usb_ov511 *ov511)
urb->pipe = usb_rcvisocpipe(ov511->dev, OV511_ENDPOINT_ADDRESS);
urb->transfer_flags = USB_ISO_ASAP;
urb->transfer_buffer = ov511->sbuf[0].data;
urb->complete = ov511_isoc_irq;
urb->number_of_packets = FRAMES_PER_DESC;
urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC;
for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
urb->iso_frame_desc[fx].offset = ov511->packet_size * fx;
urb->complete = ov511_isoc_irq;
urb->number_of_packets = FRAMES_PER_DESC;
urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC;
for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
urb->iso_frame_desc[fx].offset = ov511->packet_size * fx;
urb->iso_frame_desc[fx].length = ov511->packet_size;
}
......@@ -1254,11 +1572,11 @@ static int ov511_init_isoc(struct usb_ov511 *ov511)
urb->pipe = usb_rcvisocpipe(ov511->dev, OV511_ENDPOINT_ADDRESS);
urb->transfer_flags = USB_ISO_ASAP;
urb->transfer_buffer = ov511->sbuf[1].data;
urb->complete = ov511_isoc_irq;
urb->number_of_packets = FRAMES_PER_DESC;
urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC;
for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
urb->iso_frame_desc[fx].offset = ov511->packet_size * fx;
urb->complete = ov511_isoc_irq;
urb->number_of_packets = FRAMES_PER_DESC;
urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC;
for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
urb->iso_frame_desc[fx].offset = ov511->packet_size * fx;
urb->iso_frame_desc[fx].length = ov511->packet_size;
}
......@@ -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,18 +1622,19 @@ 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;
/* If we're not grabbing a frame right now and the other frame is */
/* ready to be grabbed into, then use it instead */
/* ready to be grabbed into, then use it instead */
if (ov511->curframe == -1) {
if (ov511->frame[(framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES].grabstate == FRAME_READY)
framenum = (framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES;
......@@ -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;
......@@ -1458,311 +1799,313 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
return -EIO;
switch (cmd) {
case VIDIOCGCAP:
{
struct video_capability b;
strcpy(b.name, "OV511 USB Camera");
b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
b.channels = 1;
b.audios = 0;
b.maxwidth = DEFAULT_WIDTH;
b.maxheight = DEFAULT_HEIGHT;
b.minwidth = 32;
b.minheight = 16;
if (copy_to_user(arg, &b, sizeof(b)))
return -EFAULT;
case VIDIOCGCAP:
{
struct video_capability b;
strcpy(b.name, "OV511 USB Camera");
b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
b.channels = 1;
b.audios = 0;
b.maxwidth = DEFAULT_WIDTH;
b.maxheight = DEFAULT_HEIGHT;
b.minwidth = 32;
b.minheight = 16;
if (copy_to_user(arg, &b, sizeof(b)))
return -EFAULT;
return 0;
}
case VIDIOCGCHAN:
{
struct video_channel v;
return 0;
}
case VIDIOCGCHAN:
{
struct video_channel v;
if (copy_from_user(&v, arg, sizeof(v)))
return -EFAULT;
if (v.channel != 0)
return -EINVAL;
if (copy_from_user(&v, arg, sizeof(v)))
return -EFAULT;
if (v.channel != 0)
return -EINVAL;
v.flags = 0;
v.tuners = 0;
v.type = VIDEO_TYPE_CAMERA;
strcpy(v.name, "Camera");
v.flags = 0;
v.tuners = 0;
v.type = VIDEO_TYPE_CAMERA;
strcpy(v.name, "Camera");
if (copy_to_user(arg, &v, sizeof(v)))
return -EFAULT;
if (copy_to_user(arg, &v, sizeof(v)))
return -EFAULT;
return 0;
}
case VIDIOCSCHAN:
{
int v;
if (copy_from_user(&v, arg, sizeof(v)))
return -EFAULT;
return 0;
}
case VIDIOCSCHAN:
{
int v;
if (v != 0)
return -EINVAL;
if (copy_from_user(&v, arg, sizeof(v)))
return -EFAULT;
return 0;
}
if (v != 0)
return -EINVAL;
case VIDIOCGPICT:
{
struct video_picture p;
return 0;
}
case VIDIOCGPICT:
{
struct video_picture p;
if (ov7610_get_picture(ov511, &p))
return -EIO;
if (ov7610_get_picture(ov511, &p))
return -EIO;
if (copy_to_user(arg, &p, sizeof(p)))
return -EFAULT;
return 0;
}
case VIDIOCSPICT:
{
struct video_picture p;
if (copy_to_user(arg, &p, sizeof(p)))
return -EFAULT;
if (copy_from_user(&p, arg, sizeof(p)))
return -EFAULT;
if (ov7610_set_picture(ov511, &p))
return -EIO;
return 0;
}
case VIDIOCSPICT:
{
struct video_picture p;
return 0;
}
case VIDIOCGCAPTURE:
{
int vf;
if (copy_from_user(&vf, arg, sizeof(vf)))
return -EFAULT;
ov511->sub_flag = vf;
return 0;
}
case VIDIOCSCAPTURE:
{
struct video_capture vc;
if (copy_from_user(&p, arg, sizeof(p)))
return -EFAULT;
if (ov7610_set_picture(ov511, &p))
return -EIO;
if (copy_from_user(&vc, arg, sizeof(vc)))
return -EFAULT;
if (vc.flags)
return -EINVAL;
if (vc.decimation)
return -EINVAL;
vc.x /= 4;
vc.x *= 4;
vc.y /= 2;
vc.y *= 2;
vc.width /= 32;
vc.width *= 32;
if (vc.width == 0) vc.width = 32;
vc.height /= 16;
vc.height *= 16;
if (vc.height == 0) vc.height = 16;
ov511->subx = vc.x;
ov511->suby = vc.y;
ov511->subw = vc.width;
ov511->subh = vc.height;
return 0;
}
case VIDIOCSWIN:
{
struct video_window vw;
return 0;
}
case VIDIOCGCAPTURE:
{
int vf;
if (copy_from_user(&vf, arg, sizeof(vf)))
return -EFAULT;
ov511->sub_flag = vf;
return 0;
}
case VIDIOCSCAPTURE:
{
struct video_capture vc;
if (copy_from_user(&vw, arg, sizeof(vw)))
return -EFAULT;
if (vw.flags)
return -EINVAL;
if (vw.clipcount)
return -EINVAL;
if (vw.height != DEFAULT_HEIGHT)
return -EINVAL;
if (vw.width != DEFAULT_WIDTH)
return -EINVAL;
if (copy_from_user(&vc, arg, sizeof(vc)))
return -EFAULT;
if (vc.flags)
return -EINVAL;
if (vc.decimation)
return -EINVAL;
vc.x /= 4;
vc.x *= 4;
vc.y /= 2;
vc.y *= 2;
vc.width /= 32;
vc.width *= 32;
if (vc.width == 0) vc.width = 32;
vc.height /= 16;
vc.height *= 16;
if (vc.height == 0) vc.height = 16;
ov511->subx = vc.x;
ov511->suby = vc.y;
ov511->subw = vc.width;
ov511->subh = vc.height;
ov511->compress = 0;
return 0;
}
case VIDIOCSWIN:
{
struct video_window vw;
return 0;
}
case VIDIOCGWIN:
{
struct video_window vw;
if (copy_from_user(&vw, arg, sizeof(vw)))
return -EFAULT;
if (vw.flags)
return -EINVAL;
if (vw.clipcount)
return -EINVAL;
if (vw.height != DEFAULT_HEIGHT)
return -EINVAL;
if (vw.width != DEFAULT_WIDTH)
return -EINVAL;
vw.x = 0;
vw.y = 0;
vw.width = DEFAULT_WIDTH;
vw.height = DEFAULT_HEIGHT;
vw.chromakey = 0;
vw.flags = 30;
ov511->compress = 0;
if (copy_to_user(arg, &vw, sizeof(vw)))
return -EFAULT;
return 0;
}
case VIDIOCGWIN:
{
struct video_window vw;
return 0;
}
case VIDIOCGMBUF:
{
struct video_mbuf vm;
vw.x = 0;
vw.y = 0;
vw.width = DEFAULT_WIDTH;
vw.height = DEFAULT_HEIGHT;
vw.chromakey = 0;
vw.flags = 30;
memset(&vm, 0, sizeof(vm));
vm.size = 2 * MAX_DATA_SIZE;
vm.frames = 2;
vm.offsets[0] = 0;
vm.offsets[1] = MAX_FRAME_SIZE + sizeof (struct timeval);
if (copy_to_user(arg, &vw, sizeof(vw)))
return -EFAULT;
if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
return -EFAULT;
return 0;
}
case VIDIOCGMBUF:
{
struct video_mbuf vm;
return 0;
}
case VIDIOCMCAPTURE:
{
struct video_mmap vm;
memset(&vm, 0, sizeof(vm));
vm.size = 2 * MAX_DATA_SIZE;
vm.frames = 2;
vm.offsets[0] = 0;
vm.offsets[1] = MAX_FRAME_SIZE + sizeof (struct timeval);
if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
return -EFAULT;
if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
return -EFAULT;
PDEBUG(4, "MCAPTURE");
PDEBUG(4, "frame: %d, size: %dx%d, format: %d",
vm.frame, vm.width, vm.height, vm.format);
return 0;
}
case VIDIOCMCAPTURE:
{
struct video_mmap vm;
if (vm.format != VIDEO_PALETTE_RGB24 &&
vm.format != VIDEO_PALETTE_GREY)
return -EINVAL;
if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
return -EFAULT;
if ((vm.frame != 0) && (vm.frame != 1))
return -EINVAL;
PDEBUG(4, "MCAPTURE");
PDEBUG(4, "frame: %d, size: %dx%d, format: %d",
vm.frame, vm.width, vm.height, vm.format);
if (vm.format != VIDEO_PALETTE_RGB24 &&
vm.format != VIDEO_PALETTE_GREY)
return -EINVAL;
if ((vm.frame != 0) && (vm.frame != 1))
return -EINVAL;
if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING)
return -EBUSY;
/* Don't compress if the size changed */
if ((ov511->frame[vm.frame].width != vm.width) ||
(ov511->frame[vm.frame].height != vm.height) ||
(ov511->frame[vm.frame].format != vm.format) ||
(ov511->frame[vm.frame].sub_flag !=
ov511->sub_flag)) {
/* If we're collecting previous frame wait
before changing modes */
interruptible_sleep_on(&ov511->wq);
if (signal_pending(current)) return -EINTR;
ov511_mode_init_regs(ov511,
vm.width, vm.height,
vm.format, ov511->sub_flag);
}
if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING)
return -EBUSY;
/* Don't compress if the size changed */
if ((ov511->frame[vm.frame].width != vm.width) ||
(ov511->frame[vm.frame].height != vm.height) ||
(ov511->frame[vm.frame].format != vm.format) ||
(ov511->frame[vm.frame].sub_flag !=
ov511->sub_flag)) {
/* If we're collecting previous frame wait
before changing modes */
interruptible_sleep_on(&ov511->wq);
if (signal_pending(current)) return -EINTR;
ov511_mode_init_regs(ov511,
vm.width, vm.height,
vm.format, ov511->sub_flag);
}
ov511->frame[vm.frame].width = vm.width;
ov511->frame[vm.frame].height = vm.height;
ov511->frame[vm.frame].format = vm.format;
ov511->frame[vm.frame].sub_flag = ov511->sub_flag;
ov511->frame[vm.frame].segsize =
vm.format == VIDEO_PALETTE_RGB24 ? 384 : 256;
ov511->frame[vm.frame].depth =
vm.format == VIDEO_PALETTE_RGB24 ? 3 : 1;
ov511->frame[vm.frame].width = vm.width;
ov511->frame[vm.frame].height = vm.height;
ov511->frame[vm.frame].format = vm.format;
ov511->frame[vm.frame].sub_flag = ov511->sub_flag;
ov511->frame[vm.frame].segsize =
vm.format == VIDEO_PALETTE_RGB24 ? 384 : 256;
ov511->frame[vm.frame].depth =
vm.format == VIDEO_PALETTE_RGB24 ? 3 : 1;
/* Mark it as ready */
ov511->frame[vm.frame].grabstate = FRAME_READY;
/* Mark it as ready */
ov511->frame[vm.frame].grabstate = FRAME_READY;
return ov511_new_frame(ov511, vm.frame);
}
case VIDIOCSYNC:
{
int frame;
if (copy_from_user((void *)&frame, arg, sizeof(int)))
return -EFAULT;
PDEBUG(4, "syncing to frame %d, grabstate = %d", frame,
ov511->frame[frame].grabstate);
switch (ov511->frame[frame].grabstate) {
case FRAME_UNUSED:
return -EINVAL;
case FRAME_READY:
case FRAME_GRABBING:
case FRAME_ERROR:
return ov511_new_frame(ov511, vm.frame);
}
case VIDIOCSYNC:
{
int frame;
if (copy_from_user((void *)&frame, arg, sizeof(int)))
return -EFAULT;
PDEBUG(4, "syncing to frame %d, grabstate = %d", frame,
ov511->frame[frame].grabstate);
switch (ov511->frame[frame].grabstate) {
case FRAME_UNUSED:
return -EINVAL;
case FRAME_READY:
case FRAME_GRABBING:
case FRAME_ERROR:
redo:
if (!ov511->dev)
return -EIO;
if (!ov511->dev)
return -EIO;
do {
do {
#if 0
init_waitqueue_head(&ov511->frame[frame].wq);
init_waitqueue_head(&ov511->frame[frame].wq);
#endif
interruptible_sleep_on(&ov511->frame[frame].wq);
if (signal_pending(current))
return -EINTR;
} while (ov511->frame[frame].grabstate == FRAME_GRABBING);
if (ov511->frame[frame].grabstate == FRAME_ERROR) {
int ret;
if ((ret = ov511_new_frame(ov511, frame)) < 0)
return ret;
goto redo;
}
case FRAME_DONE:
ov511->frame[frame].grabstate = FRAME_UNUSED;
break;
}
ov511->frame[frame].grabstate = FRAME_UNUSED;
interruptible_sleep_on(&ov511->frame[frame].wq);
if (signal_pending(current))
return -EINTR;
} while (ov511->frame[frame].grabstate == FRAME_GRABBING);
if (ov511->frame[frame].grabstate == FRAME_ERROR) {
int ret;
if ((ret = ov511_new_frame(ov511, frame)) < 0)
return ret;
goto redo;
}
case FRAME_DONE:
ov511->frame[frame].grabstate = FRAME_UNUSED;
break;
}
/* Reset the hardware snapshot button */
/* FIXME - Is this the best place for this? */
if ((ov511->snap_enabled) &&
(ov511->frame[frame].snapshot)) {
ov511->frame[frame].snapshot = 0;
ov511_reg_write(ov511->dev, 0x52, 0x01);
ov511_reg_write(ov511->dev, 0x52, 0x03);
ov511_reg_write(ov511->dev, 0x52, 0x01);
}
ov511->frame[frame].grabstate = FRAME_UNUSED;
return 0;
/* Reset the hardware snapshot button */
/* FIXME - Is this the best place for this? */
if ((ov511->snap_enabled) &&
(ov511->frame[frame].snapshot)) {
ov511->frame[frame].snapshot = 0;
ov511_reg_write(ov511->dev, 0x52, 0x01);
ov511_reg_write(ov511->dev, 0x52, 0x03);
ov511_reg_write(ov511->dev, 0x52, 0x01);
}
case VIDIOCGFBUF:
{
struct video_buffer vb;
memset(&vb, 0, sizeof(vb));
vb.base = NULL; /* frame buffer not supported, not used */
return 0;
}
case VIDIOCGFBUF:
{
struct video_buffer vb;
if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb)))
return -EFAULT;
memset(&vb, 0, sizeof(vb));
vb.base = NULL; /* frame buffer not supported, not used */
return 0;
}
case VIDIOCKEY:
return 0;
case VIDIOCCAPTURE:
return -EINVAL;
case VIDIOCSFBUF:
return -EINVAL;
case VIDIOCGTUNER:
case VIDIOCSTUNER:
return -EINVAL;
case VIDIOCGFREQ:
case VIDIOCSFREQ:
return -EINVAL;
case VIDIOCGAUDIO:
case VIDIOCSAUDIO:
return -EINVAL;
default:
return -ENOIOCTLCMD;
if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb)))
return -EFAULT;
return 0;
}
case VIDIOCKEY:
return 0;
case VIDIOCCAPTURE:
return -EINVAL;
case VIDIOCSFBUF:
return -EINVAL;
case VIDIOCGTUNER:
case VIDIOCSTUNER:
return -EINVAL;
case VIDIOCGFREQ:
case VIDIOCSFREQ:
return -EINVAL;
case VIDIOCGAUDIO:
case VIDIOCSAUDIO:
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: errored frame %d", ov511->curframe);
err("** ick! ** Errored 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 tries;
int i, success;
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 7610 and wait a bit for it to initialize */
/* Reset the 76xx */
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;
tries = 5;
while((tries > 0) &&
((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) != 0x7F) ||
(ov511_i2c_read(dev, OV7610_REG_ID_LOW) != 0xA2))) {
--tries;
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;
/* 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 aRegvalsNorm7610[] =
static struct ov511_regvals aRegvalsNorm511[] =
{{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...");
......@@ -2191,79 +2563,58 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum)
}
memset(ov511, 0, sizeof(*ov511));
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");
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);
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;
}
}
/* 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;
}
return ov511;
error:
......@@ -2272,12 +2623,12 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum)
ov511 = NULL;
}
return NULL;
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");
}
......
#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
/* Camera interface register numbers */
#define OV511_REG_CAMERA_DELAY_MODE 0x10
#define OV511_REG_CAMERA_EDGE_MODE 0x11
#define OV511_REG_CAMERA_DELAY_MODE 0x10
#define OV511_REG_CAMERA_EDGE_MODE 0x11
#define OV511_REG_CAMERA_CLAMPED_PIXEL_NUM 0x12
#define OV511_REG_CAMERA_CLAMPED_LINE_NUM 0x13
#define OV511_REG_CAMERA_PIXEL_DIVISOR 0x14
#define OV511_REG_CAMERA_LINE_DIVISOR 0x15
#define OV511_REG_CAMERA_DATA_INPUT_SELECT 0x16
#define OV511_REG_CAMERA_RESERVED_LINE_MODE 0x17
#define OV511_REG_CAMERA_BITMASK 0x18
#define OV511_REG_CAMERA_BITMASK 0x18
/* Snapshot mode camera interface register numbers */
#define OV511_REG_SNAP_CAPTURED_FRAME 0x19
#define OV511_REG_SNAP_CLAMPED_PIXEL_NUM 0x1A
#define OV511_REG_SNAP_CLAMPED_LINE_NUM 0x1B
#define OV511_REG_SNAP_PIXEL_DIVISOR 0x1C
#define OV511_REG_SNAP_LINE_DIVISOR 0x1D
#define OV511_REG_SNAP_LINE_DIVISOR 0x1D
#define OV511_REG_SNAP_DATA_INPUT_SELECT 0x1E
#define OV511_REG_SNAP_BITMASK 0x1F
#define OV511_REG_SNAP_BITMASK 0x1F
/* DRAM register numbers */
#define OV511_REG_DRAM_ENABLE_FLOW_CONTROL 0x20
......@@ -37,21 +41,21 @@ if (debug >= level) printk("ov511: " fmt "\n" , ## args)
#define OV511_REG_DRAM_REFRESH_COUNTER 0x23
/* ISO FIFO register numbers */
#define OV511_REG_FIFO_PACKET_SIZE 0x30
#define OV511_REG_FIFO_BITMASK 0x31
#define OV511_REG_FIFO_PACKET_SIZE 0x30
#define OV511_REG_FIFO_BITMASK 0x31
/* PIO register numbers */
#define OV511_REG_PIO_BITMASK 0x38
#define OV511_REG_PIO_DATA_PORT 0x39
#define OV511_REG_PIO_BIST 0x3E
#define OV511_REG_PIO_BITMASK 0x38
#define OV511_REG_PIO_DATA_PORT 0x39
#define OV511_REG_PIO_BIST 0x3E
/* I2C register numbers */
#define OV511_REG_I2C_CONTROL 0x40
#define OV511_REG_I2C_CONTROL 0x40
#define OV511_REG_I2C_SLAVE_ID_WRITE 0x41
#define OV511_REG_I2C_SUB_ADDRESS_3_BYTE 0x42
#define OV511_REG_I2C_SUB_ADDRESS_2_BYTE 0x43
#define OV511_REG_I2C_SLAVE_ID_READ 0x44
#define OV511_REG_I2C_DATA_PORT 0x45
#define OV511_REG_I2C_SLAVE_ID_READ 0x44
#define OV511_REG_I2C_DATA_PORT 0x45
#define OV511_REG_I2C_CLOCK_PRESCALER 0x46
#define OV511_REG_I2C_TIME_OUT_COUNTER 0x47
......@@ -60,39 +64,39 @@ if (debug >= level) printk("ov511: " fmt "\n" , ## args)
#define OV511_REG_I2C_SNAP_DATA_PORT 0x49
/* System control register numbers */
#define OV511_REG_SYSTEM_RESET 0x50
#define OV511_RESET_UDC 0x01
#define OV511_RESET_I2C 0x02
#define OV511_RESET_FIFO 0x04
#define OV511_RESET_OMNICE 0x08
#define OV511_REG_SYSTEM_RESET 0x50
#define OV511_RESET_UDC 0x01
#define OV511_RESET_I2C 0x02
#define OV511_RESET_FIFO 0x04
#define OV511_RESET_OMNICE 0x08
#define OV511_RESET_DRAM_INTF 0x10
#define OV511_RESET_CAMERA_INTF 0x20
#define OV511_RESET_OV511 0x40
#define OV511_RESET_NOREGS 0x3F /* All but OV511 & regs */
#define OV511_RESET_ALL 0x7F
#define OV511_RESET_OV511 0x40
#define OV511_RESET_NOREGS 0x3F /* All but OV511 & regs */
#define OV511_RESET_ALL 0x7F
#define OV511_REG_SYSTEM_CLOCK_DIVISOR 0x51
#define OV511_REG_SYSTEM_SNAPSHOT 0x52
#define OV511_REG_SYSTEM_SNAPSHOT 0x52
#define OV511_REG_SYSTEM_INIT 0x53
#define OV511_REG_SYSTEM_PWR_CLK 0x54 /* OV511+ only */
#define OV511_REG_SYSTEM_LED_CTL 0x55 /* OV511+ only */
#define OV511_REG_SYSTEM_PWR_CLK 0x54 /* OV511+ only */
#define OV511_REG_SYSTEM_LED_CTL 0x55 /* OV511+ only */
#define OV511_REG_SYSTEM_USER_DEFINED 0x5E
#define OV511_REG_SYSTEM_CUSTOM_ID 0x5F
#define OV511_REG_SYSTEM_CUSTOM_ID 0x5F
/* OmniCE register numbers */
#define OV511_OMNICE_PREDICTION_HORIZ_Y 0x70
#define OV511_OMNICE_PREDICTION_HORIZ_Y 0x70
#define OV511_OMNICE_PREDICTION_HORIZ_UV 0x71
#define OV511_OMNICE_PREDICTION_VERT_Y 0x72
#define OV511_OMNICE_PREDICTION_VERT_UV 0x73
#define OV511_OMNICE_PREDICTION_VERT_UV 0x73
#define OV511_OMNICE_QUANTIZATION_HORIZ_Y 0x74
#define OV511_OMNICE_QUANTIZATION_HORIZ_UV 0x75
#define OV511_OMNICE_QUANTIZATION_VERT_Y 0x76
#define OV511_OMNICE_QUANTIZATION_VERT_UV 0x77
#define OV511_OMNICE_ENABLE 0x78
#define OV511_OMNICE_LUT_ENABLE 0x79
#define OV511_OMNICE_Y_LUT_BEGIN 0x80
#define OV511_OMNICE_Y_LUT_END 0x9F
#define OV511_OMNICE_UV_LUT_BEGIN 0xA0
#define OV511_OMNICE_UV_LUT_END 0xBF
#define OV511_OMNICE_ENABLE 0x78
#define OV511_OMNICE_LUT_ENABLE 0x79
#define OV511_OMNICE_Y_LUT_BEGIN 0x80
#define OV511_OMNICE_Y_LUT_END 0x9F
#define OV511_OMNICE_UV_LUT_BEGIN 0xA0
#define OV511_OMNICE_UV_LUT_END 0xBF
/* Alternate numbers for various max packet sizes (OV511 only) */
#define OV511_ALT_SIZE_992 0
......@@ -114,7 +118,7 @@ if (debug >= level) printk("ov511: " fmt "\n" , ## args)
#define OV511PLUS_ALT_SIZE_769 6
#define OV511PLUS_ALT_SIZE_961 7
/* ov7610 registers */
/* OV7610 registers */
#define OV7610_REG_GAIN 0x00
#define OV7610_REG_BLUE 0x01
#define OV7610_REG_RED 0x02
......@@ -163,12 +167,12 @@ if (debug >= level) printk("ov511: " fmt "\n" , ## args)
#define SCRATCH_BUF_SIZE 384
#define FRAMES_PER_DESC 10 /* FIXME - What should this be? */
#define FRAMES_PER_DESC 10 /* FIXME - What should this be? */
#define FRAME_SIZE_PER_DESC 993 /* FIXME - Deprecated */
#define MAX_FRAME_SIZE_PER_DESC 993 /* For statically allocated stuff */
#define MAX_FRAME_SIZE_PER_DESC 993 /* For statically allocated stuff */
// FIXME - should this be 0x81 (endpoint address) or 0x01 (endpoint number)?
#define OV511_ENDPOINT_ADDRESS 0x81 /* Address of isoc endpoint */
#define OV511_ENDPOINT_ADDRESS 0x81 /* Address of isoc endpoint */
// CAMERA SPECIFIC
// FIXME - these can vary between specific models
......@@ -213,7 +217,7 @@ enum {
FRAME_UNUSED, /* Unused (no MCAPTURE) */
FRAME_READY, /* Ready to start grabbing */
FRAME_GRABBING, /* In the process of being grabbed into */
FRAME_DONE, /* Finished grabbing, but not been synced yet */
FRAME_DONE, /* Finished grabbing, but not been synced yet */
FRAME_ERROR, /* Something bad happened while processing */
};
......@@ -238,7 +242,7 @@ struct ov511_frame {
int hdrheight; /* Height */
int sub_flag; /* Sub-capture mode for this frame? */
int format; /* Format for this frame */
int format; /* Format for this frame */
int segsize; /* How big is each segment from the camera? */
volatile int grabstate; /* State of grabbing */
......@@ -253,7 +257,7 @@ struct ov511_frame {
wait_queue_head_t wq; /* Processes waiting */
int snapshot; /* True if frame was a snapshot */
int snapshot; /* True if frame was a snapshot */
};
#define OV511_NUMFRAMES 2
......@@ -261,23 +265,28 @@ struct ov511_frame {
struct usb_ov511 {
struct video_device vdev;
/* Device structure */
struct usb_device *dev;
#if 0
unsigned char customid; /* Type of camera */
#else
int customid;
int desc;
#endif
unsigned char iface;
struct semaphore lock;
int user; /* user count for exclusive use */
int user; /* user count for exclusive use */
int streaming; /* Are we streaming Isochronous? */
int grabbing; /* Are we grabbing? */
int compress; /* Should the next frame be compressed? */
char *fbuf; /* Videodev buffer area */
char *fbuf; /* Videodev buffer area */
int sub_flag; /* Pix Array subcapture on flag */
int subx; /* Pix Array subcapture x offset */
......@@ -297,12 +306,22 @@ struct usb_ov511 {
wait_queue_head_t wq; /* Processes waiting */
int snap_enabled; /* Snapshot mode enabled */
int snap_enabled; /* Snapshot mode enabled */
int bridge; /* Type of bridge (OV511 or OV511+) */
int sensor; /* Type of image sensor chip */
int bridge; /* Type of bridge (OV511 or 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 */
};
int packet_size; /* Frame size per isoc desc */
struct cam_list {
int id;
char *description;
};
#endif
......
......@@ -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%lu, abort\n",
printk(KERN_ERR "clgen: cannot reserve region 0x%lx, 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%lu, abort\n",
printk(KERN_ERR "clgen: cannot reserve region 0x%lx, 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%lu, abort\n",
printk(KERN_ERR "clgen: cannot reserve region 0x%lx, abort\n",
board_addr);
return -1;
}
......
......@@ -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%lu\n",
"vesafb: abort, cannot reserve video memory at 0x%lx\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);
......
......@@ -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);
}
......
......@@ -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)
......
......@@ -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;
spin_lock(&pagecache_lock);
curr = head->next;
while (curr != head) {
page = list_entry(curr, struct page, list);
curr = curr->next;
while (head != head->next) {
spin_lock(&pagecache_lock);
spin_lock(&pagemap_lru_lock);
head = &inode->i_mapping->pages;
curr = head->next;
count = 0;
/* We cannot invalidate a locked page */
if (TryLockPage(page))
continue;
spin_unlock(&pagecache_lock);
while ((curr != head) && (count++ < ITERATIONS)) {
page = list_entry(curr, struct page, list);
curr = curr->next;
lru_cache_del(page);
remove_inode_page(page);
UnlockPage(page);
page_cache_release(page);
goto repeat;
/* We cannot invalidate a locked page */
if (TryLockPage(page))
continue;
__lru_cache_del(page);
__remove_inode_page(page);
UnlockPage(page);
page_cache_release(page);
}
/* 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);
}
}
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);
put_page(page);
page_cache_release(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;
}
......
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment