70a459064d
Submitted by: Jim Lowe <james@miller.cs.uwm.edu>
1781 lines
54 KiB
C
1781 lines
54 KiB
C
/*
|
|
* Copyright (c) 1995 Mark Tinguely and Jim Lowe
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Mark Tinguely and Jim Lowe
|
|
* 4. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/* Change History:
|
|
8/21/95 Release
|
|
8/23/95 On advice from Stefan Esser, added volatile to PCI
|
|
memory pointers to remove PCI caching .
|
|
8/29/95 Fixes suggested by Bruce Evans.
|
|
meteor_mmap should return -1 on error rather than 0.
|
|
unit # > NMETEOR should be unit # >= NMETEOR.
|
|
10/24/95 Turn 50 Hz processing for SECAM and 60 Hz processing
|
|
off for AUTOMODE.
|
|
11/11/95 Change UV from always begin signed to ioctl selected
|
|
to either signed or unsigned.
|
|
12/07/95 Changed 7196 startup codes for 50 Hz as recommended
|
|
by Luigi Rizzo (luigi@iet.unipi.it)
|
|
12/08/95 Clear SECAM bit in PAL/NTSC and set input field count
|
|
bits for 50 Hz mode (PAL/SECAM) before I was setting the
|
|
output count bits. by Luigi Rizzo (luigi@iet.unipi.it)
|
|
12/18/95 Correct odd DMA field (never exceed, but good for safety
|
|
Changed 7196 startup codes for 50 Hz as recommended
|
|
by Luigi Rizzo (luigi@iet.unipi.it)
|
|
12/19/95 Changed field toggle mode to enable (offset 0x3c)
|
|
recommended by luigi@iet.unipi.it
|
|
Added in prototyping, include file, staticizing,
|
|
and DEVFS changes from FreeBSD team.
|
|
Changed the default allocated pages from 151 (NTSC)
|
|
to 217 (PAL).
|
|
Cleaned up some old comments in iic_write().
|
|
Added a Field (even or odd) only capture mode to
|
|
eliminate the high frequency problems with compression
|
|
algorithms. Recommended by luigi@iet.unipi.it.
|
|
Changed geometry ioctl so if it couldn't allocated a
|
|
large enough contiguous space, it wouldn't free the
|
|
stuff it already had.
|
|
Added new mode called YUV_422 which delivers the
|
|
data in planer Y followed by U followed by V. This
|
|
differs from the standard YUV_PACKED mode in that
|
|
the chrominance (UV) data is in the correct (different)
|
|
order. This is for programs like vic and mpeg_encode
|
|
so they don't have to reorder the chrominance data.
|
|
Added field count to stats.
|
|
Increment frame count stat if capturing continuous on
|
|
even frame grabs.
|
|
Added my email address to these comments
|
|
(james@cs.uwm.edu) suggested by (luigi@iet.unipt.it :-).
|
|
Changed the user mode signal mechanism to allow the
|
|
user program to be interrupted at the end of a frame
|
|
in any one of the modes. Added SSIGNAL ioctl.
|
|
Added a SFPS/GFPS ioctl so one may set the frames per
|
|
second that the card catpures. This code needs to be
|
|
completed.
|
|
Changed the interrupt routine so synchronous capture
|
|
will work on fields or frames and the starting frame
|
|
can be either even or odd.
|
|
Added HALT_N_FRAMES and CONT_N_FRAMES so one could
|
|
stop and continue synchronous capture mode.
|
|
Change the tsleep/wakeup function to wait on mtr
|
|
rather than &read_intr_wait.
|
|
1/22/96 Add option (METEOR_FreeBSD_210) for FreeBSD 2.1
|
|
to compile.
|
|
Changed intr so it only printed errors every 50 times.
|
|
Added unit number to error messages.
|
|
Added get_meteor_mem and enabled range checking.
|
|
1/30/96 Added prelim test stuff for direct video dma transfers
|
|
from Amancio Hasty (hasty@rah.star-gate.com). Until
|
|
we get some stuff sorted out, this will be ifdef'ed
|
|
with METEOR_DIRECT_VIDEO. This is very dangerous to
|
|
use at present since we don't check the address that
|
|
is passed by the user!!!!!
|
|
|
|
*/
|
|
|
|
#include "meteor.h"
|
|
|
|
#if NMETEOR > 0
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/protosw.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/signalvar.h>
|
|
#include <sys/devconf.h>
|
|
#include <sys/mman.h>
|
|
#ifdef DEVFS
|
|
#include <sys/devfsext.h>
|
|
#endif /* DEVFS */
|
|
#include <machine/clock.h>
|
|
#include <machine/cpu.h> /* bootverbose */
|
|
|
|
#include <vm/vm.h>
|
|
#include <vm/vm_kern.h>
|
|
#include <vm/vm_param.h>
|
|
#include <vm/pmap.h>
|
|
#include <vm/vm_extern.h>
|
|
|
|
#include <pci.h>
|
|
#if NPCI > 0
|
|
#include <pci/pcivar.h>
|
|
#include <pci/pcireg.h>
|
|
#endif
|
|
#include <machine/ioctl_meteor.h>
|
|
|
|
|
|
static void meteor_intr __P((void *arg));
|
|
|
|
/*
|
|
* Allocate enough memory for:
|
|
* 768x576 RGB 16 or YUV (16 storage bits/pixel) = 884736 = 216 pages
|
|
*
|
|
* You may override this using the options "METEOR_ALLOC_PAGES=value" in your
|
|
* kernel configuration file.
|
|
*/
|
|
#ifndef METEOR_ALLOC_PAGES
|
|
#define METEOR_ALLOC_PAGES 217
|
|
#endif
|
|
#define METEOR_ALLOC (METEOR_ALLOC_PAGES * PAGE_SIZE)
|
|
|
|
#define NUM_SAA7116_PCI_REGS 37
|
|
#define NUM_SAA7196_I2C_REGS 49
|
|
|
|
typedef struct {
|
|
vm_offset_t virt_baseaddr; /* saa7116 register virtual address */
|
|
vm_offset_t phys_baseaddr; /* saa7116 register physical address */
|
|
vm_offset_t capt_cntrl; /* capture control register offset 0x40 */
|
|
vm_offset_t stat_reg; /* status register offset 0x60 */
|
|
vm_offset_t iic_virt_addr; /* ICC bus register offset 0x64 */
|
|
pcici_t tag; /* PCI tag, for doing PCI commands */
|
|
vm_offset_t bigbuf; /* buffer that holds the captured image */
|
|
int alloc_pages; /* number of pages in bigbuf */
|
|
struct proc *proc; /* process to receive raised signal */
|
|
int signal; /* signal to send to process */
|
|
struct meteor_mem *mem; /* used to control sync. multi-frame output */
|
|
u_long synch_wait; /* wait for free buffer before continuing */
|
|
short current; /* frame number in buffer (1-frames) */
|
|
short rows; /* number of rows in a frame */
|
|
short cols; /* number of columns in a frame */
|
|
short depth; /* number of byte per pixel */
|
|
short frames; /* number of frames allocated */
|
|
int frame_size; /* number of bytes in a frame */
|
|
u_long fifo_errors; /* number of fifo capture errors since open */
|
|
u_long dma_errors; /* number of DMA capture errors since open */
|
|
u_long frames_captured;/* number of frames captured since open */
|
|
u_long even_fields_captured; /* number of even fields captured */
|
|
u_long odd_fields_captured; /* number of odd fields captured */
|
|
u_long range_enable; /* enable range checking ?? */
|
|
unsigned flags;
|
|
#define METEOR_INITALIZED 0x00000001
|
|
#define METEOR_OPEN 0x00000002
|
|
#define METEOR_MMAP 0x00000004
|
|
#define METEOR_INTR 0x00000008
|
|
#define METEOR_READ 0x00000010 /* XXX never gets referenced */
|
|
#define METEOR_SINGLE 0x00000020 /* get single frame */
|
|
#define METEOR_CONTIN 0x00000040 /* continuously get frames */
|
|
#define METEOR_SYNCAP 0x00000080 /* synchronously get frames */
|
|
#define METEOR_CAP_MASK 0x000000f0
|
|
#define METEOR_NTSC 0x00000100
|
|
#define METEOR_PAL 0x00000200
|
|
#define METEOR_SECAM 0x00000400
|
|
#define METEOR_AUTOMODE 0x00000800
|
|
#define METEOR_FORM_MASK 0x00000f00
|
|
#define METEOR_DEV0 0x00001000
|
|
#define METEOR_DEV1 0x00002000
|
|
#define METEOR_DEV2 0x00004000
|
|
#define METEOR_DEV3 0x00008000
|
|
#define METEOR_DEV_MASK 0x0000f000
|
|
#define METEOR_RGB16 0x00010000
|
|
#define METEOR_RGB24 0x00020000
|
|
#define METEOR_YUV_PACKED 0x00040000
|
|
#define METEOR_YUV_PLANER 0x00080000
|
|
#define METEOR_WANT_EVEN 0x00100000 /* want even frame */
|
|
#define METEOR_WANT_ODD 0x00200000 /* want odd frame */
|
|
#define METEOR_WANT_MASK 0x00300000
|
|
#define METEOR_ONLY_EVEN_FIELDS 0x01000000
|
|
#define METEOR_ONLY_ODD_FIELDS 0x02000000
|
|
#define METEOR_ONLY_FIELDS_MASK 0x03000000
|
|
#define METEOR_YUV_422 0x04000000
|
|
#define METEOR_OUTPUT_FMT_MASK 0x040f0000
|
|
u_char saa7196_i2c[NUM_SAA7196_I2C_REGS]; /* saa7196 register values */
|
|
u_short fps; /* frames per second */
|
|
#ifdef DEVFS
|
|
void *devfs_token;
|
|
#endif
|
|
#ifdef METEOR_TEST_VIDEO
|
|
struct meteor_video video;
|
|
#endif
|
|
} meteor_reg_t;
|
|
|
|
static meteor_reg_t meteor[NMETEOR];
|
|
#define METEOR_NUM(mtr) ((mtr - &meteor[0])/sizeof(meteor_reg_t))
|
|
|
|
#define METPRI (PZERO+8)|PCATCH
|
|
|
|
/*---------------------------------------------------------
|
|
**
|
|
** Meteor PCI probe and initialization routines
|
|
**
|
|
**---------------------------------------------------------
|
|
*/
|
|
|
|
static char* met_probe (pcici_t tag, pcidi_t type);
|
|
static void met_attach(pcici_t tag, int unit);
|
|
static u_long met_count;
|
|
|
|
static struct pci_device met_device = {
|
|
"meteor",
|
|
met_probe,
|
|
met_attach,
|
|
&met_count
|
|
};
|
|
|
|
DATA_SET (pcidevice_set, met_device);
|
|
|
|
#if defined(METEOR_FreeBSD_210) /* XXX */
|
|
d_open_t meteor_open;
|
|
d_close_t meteor_close;
|
|
d_read_t meteor_read;
|
|
d_write_t meteor_write;
|
|
d_ioctl_t meteor_ioctl;
|
|
d_mmap_t meteor_mmap;
|
|
#else
|
|
static d_open_t meteor_open;
|
|
static d_close_t meteor_close;
|
|
static d_read_t meteor_read;
|
|
static d_write_t meteor_write;
|
|
static d_ioctl_t meteor_ioctl;
|
|
static d_mmap_t meteor_mmap;
|
|
|
|
#define CDEV_MAJOR 67
|
|
static struct cdevsw meteor_cdevsw =
|
|
{ meteor_open, meteor_close, meteor_read, meteor_write, /*67*/
|
|
meteor_ioctl, nostop, nullreset, nodevtotty,/* Meteor */
|
|
seltrue, meteor_mmap, NULL, "meteor", NULL, -1 };
|
|
#endif
|
|
|
|
static u_long saa7116_pci_default[NUM_SAA7116_PCI_REGS] = {
|
|
/* PCI Memory registers */
|
|
/* BITS Type Description */
|
|
/* 0x00 */ 0x00000000, /* 31:1 e*RW DMA 1 (Even)
|
|
0 RO 0x0 */
|
|
/* 0x04 */ 0x00000000, /* 31:2 e*RW DMA 2 (Even)
|
|
1:0 RO 0x0 */
|
|
/* 0x08 */ 0x00000000, /* 31:2 e*RW DMA 3 (Even)
|
|
1:0 RO 0x0 */
|
|
/* 0x0c */ 0x00000000, /* 31:1 o*RW DMA 1 (Odd)
|
|
0 RO 0x0 */
|
|
/* 0x10 */ 0x00000000, /* 31:2 o*RW DMA 2 (Odd)
|
|
1:0 RO 0x0 */
|
|
/* 0x14 */ 0x00000000, /* 31:2 o*RW DMA 3 (Odd)
|
|
1:0 RO 0x0 */
|
|
/* 0x18 */ 0x00000500, /* 15:2 e*RW Stride 1 (Even)
|
|
1:0 RO 0x0 */
|
|
/* 0x1c */ 0x00000000, /* 15:2 e*RW Stride 2 (Even)
|
|
1:0 RO 0x0 */
|
|
/* 0x20 */ 0x00000000, /* 15:2 e*RW Stride 3 (Even)
|
|
1:0 RO 0x0 */
|
|
/* 0x24 */ 0x00000500, /* 15:2 o*RW Stride 1 (Odd)
|
|
1:0 RO 0x0 */
|
|
/* 0x28 */ 0x00000000, /* 15:2 o*RW Stride 2 (Odd)
|
|
1:0 RO 0x0 */
|
|
/* 0x2c */ 0x00000000, /* 15:2 o*RW Stride 3 (Odd)
|
|
1:0 RO 0x0 */
|
|
/* 0x30 */ 0xeeeeee01, /* 31:8 *RW Route (Even)
|
|
7:0 *RW Mode (Even) */
|
|
/* 0x34 */ 0xeeeeee01, /* 31:8 *RW Route (Odd)
|
|
7:0 *RW Mode (Odd) */
|
|
/* 0x38 */ 0x00200020, /* 22:16 *RW FIFO Trigger Planer Mode,
|
|
6:0 *RW FIFO Trigger Packed Mode */
|
|
/* 0x3c */ 0x00000107, /* 9:8 *RW Reserved (0x0)
|
|
2 *RW Field Toggle
|
|
1 *RW Reserved (0x1)
|
|
0 *RW Reserved (0x1) */
|
|
/* 0x40 */ 0x000000c0, /* 15 *RW Range Enable
|
|
14 *RW Corrupt Disable
|
|
11 *RR Address Error (Odd)
|
|
10 *RR Address Error (Even)
|
|
9 *RR Field Corrupt (Odd)
|
|
8 *RR Field Corrupt (Even)
|
|
7 *RW Fifo Enable
|
|
6 *RW VRSTN#
|
|
5 *RR Field Done (Odd)
|
|
4 *RR Field Done (Even)
|
|
3 *RS Single Field Capture (Odd)
|
|
2 *RS Single Field Capture (Even)
|
|
1 *RW Capture (ODD) Continous
|
|
0 *RW Capture (Even) Continous */
|
|
/* 0x44 */ 0x00000000, /* 7:0 *RW Retry Wait Counter */
|
|
/* 0x48 */ 0x00000307, /* 10 *RW Interrupt mask, start of field
|
|
9 *RW Interrupt mask, end odd field
|
|
8 *RW Interrupt mask, end even field
|
|
2 *RR Interrupt status, start of field
|
|
1 *RR Interrupt status, end of odd
|
|
0 *RR Interrupt status, end of even */
|
|
/* 0x4c */ 0x00000001, /* 31:0 *RW Field Mask (Even) continous */
|
|
/* 0x50 */ 0x00000001, /* 31:0 *RW Field Mask (Odd) continous */
|
|
/* 0x54 */ 0x00000000, /* 20:16 *RW Mask Length (Odd)
|
|
4:0 *RW Mask Length (Even) */
|
|
/* 0x58 */ 0x0005007c, /* 22:16 *RW FIFO almost empty
|
|
6:0 *RW FIFO almost full */
|
|
/* 0x5c */ 0x461e1e0f, /* 31:24 *RW I2C Phase 4
|
|
23:16 *RW I2C Phase 3
|
|
15:8 *RW I2C Phase 2
|
|
7:0 *RW I2C Phase 1 */
|
|
/* 0x60 */ 0x00000300, /* 31:24 *RO I2C Read Data
|
|
23:16 **RW I2C Auto Address
|
|
11 RO I2C SCL Input
|
|
10 RO I2C SDA Input
|
|
9 RR I2C Direct Abort
|
|
8 RR I2C Auto Abort
|
|
3 RW I2C SCL Output
|
|
2 RW I2C SDA Output
|
|
1 RW I2C Bypass
|
|
0 RW I2C Auto Enable */
|
|
/* 0x64 */ 0x00000000, /* 24 RS I2C New Cycle
|
|
23:16 **RW I2C Direct Address
|
|
15:8 **RW I2C Direct Sub-address
|
|
7:0 **RW I2C Direct Write Address */
|
|
/* 0x68 */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 1 (Even)
|
|
23:16 **RW I2C Auto Data 1 (Even)
|
|
15:8 **RW I2C Auto Sub-address 0 (Even)
|
|
7:0 **RW I2C Auto Data 0 (Even) */
|
|
/* 0x6c */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 3 (Even)
|
|
23:16 **RW I2C Auto Data 3 (Even)
|
|
15:8 **RW I2C Auto Sub-address 2 (Even)
|
|
7:0 **RW I2C Auto Data 2 (Even) */
|
|
/* 0x70 */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 5 (Even)
|
|
23:16 **RW I2C Auto Data 5 (Even)
|
|
15:8 **RW I2C Auto Sub-address 4 (Even)
|
|
7:0 **RW I2C Auto Data 4 (Even) */
|
|
/* 0x74 */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 7 (Even)
|
|
23:16 **RW I2C Auto Data 7 (Even)
|
|
15:8 **RW I2C Auto Sub-address 6 (Even)
|
|
7:0 **RW I2C Auto Data 6 (Even) */
|
|
/* 0x78 */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 1 (Odd)
|
|
23:16 **RW I2C Auto Data 1 (Odd)
|
|
15:8 **RW I2C Auto Sub-address 0 (Odd)
|
|
7:0 **RW I2C Auto Data 0 (Odd) */
|
|
/* 0x7c */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 3 (Odd)
|
|
23:16 **RW I2C Auto Data 3 (Odd)
|
|
15:8 **RW I2C Auto Sub-address 2 (Odd)
|
|
7:0 **RW I2C Auto Data 2 (Odd) */
|
|
/* 0x80 */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 5 (Odd)
|
|
23:16 **RW I2C Auto Data 5 (Odd)
|
|
15:8 **RW I2C Auto Sub-address 4 (Odd)
|
|
7:0 **RW I2C Auto Data 4 (Odd) */
|
|
/* 0x84 */ 0x00000000, /* 31:24 **RW I2C Auto Sub-address 7 (Odd)
|
|
23:16 **RW I2C Auto Data 7 (Odd)
|
|
15:8 **RW I2C Auto Sub-address 6 (Odd)
|
|
7:0 **RW I2C Auto Data 6 (Odd) */
|
|
/* 0x88 */ 0x00000000, /* 23:16 **RW I2C Register Enable (Odd)
|
|
7:0 **RW I2C Register Enable (Even) */
|
|
/* 0x8c */ 0x00000000, /* 23:2 e*RW DMA End (Even)
|
|
1:0 RO 0x0 */
|
|
/* 0x90 */ 0x00000000 /* 23:2 e*RW DMA End (Odd)
|
|
1:0 RO 0x0 */
|
|
};
|
|
|
|
static u_char saa7196_i2c_default[NUM_SAA7196_I2C_REGS] = {
|
|
/* SAA7196 I2C bus control */
|
|
/* BITS Function */
|
|
/* 00 */ 0x50, /* 7:0 Increment Delay */
|
|
/* 01 */ 0x30, /* 7:0 Horizontal Sync Begin for 50hz */
|
|
/* 02 */ 0x00, /* 7:0 Horizontal Sync Stop for 50hz */
|
|
/* 03 */ 0xe8, /* 7:0 Horizontal Sync Clamp Start for 50hz */
|
|
/* 04 */ 0xb6, /* 7:0 Horizontal Sync Clamp Stop for 50hz */
|
|
/* 05 */ 0xf4, /* 7:0 Horizontal Sync Start after PH1 for 50hz */
|
|
/* 06 */ 0x46, /* 7 Input mode =0 CVBS, =1 S-Video
|
|
6 Pre filter
|
|
5:4 Aperture Bandpass characteristics
|
|
3:2 Coring range for high freq
|
|
1:0 Aperture bandpass filter weights */
|
|
/* 07 */ 0x00, /* 7:0 Hue */
|
|
/* 08 */ 0x7f, /* 7:3 Colour-killer threshold QAM (PAL, NTSC) */
|
|
/* 09 */ 0x7f, /* 7:3 Colour-killer threshold SECAM */
|
|
/* 0a */ 0x7f, /* 7:0 PAL switch sensitivity */
|
|
/* 0b */ 0x7f, /* 7:0 SECAM switch sensitivity */
|
|
/* 0c */ 0x40, /* 7 Colour-on bit
|
|
6:5 AGC filter */
|
|
/* 0d */ 0x84, /* 7 VTR/TV mode bit = 1->VTR mode
|
|
3 Realtime output mode select bit
|
|
2 HREF position select
|
|
1 Status byte select
|
|
0 SECAM mode bit */
|
|
/* 0e */ 0x38, /* 7 Horizontal clock PLL
|
|
5 Select interal/external clock source
|
|
4 Output enable of Horizontal/Vertical sync
|
|
3 Data output YUV enable
|
|
2 S-VHS bit
|
|
1 GPSW2
|
|
0 GPSW1 */
|
|
/* 0f */ 0x50, /* 7 Automatic Field detection
|
|
6 Field Select 0 = 50hz, 1=60hz
|
|
5 SECAM cross-colour reduction
|
|
4 Enable sync and clamping pulse
|
|
3:1 Luminance delay compensation */
|
|
/* 10 */ 0x00, /* 2 Select HREF Position
|
|
1:0 Vertical noise reduction */
|
|
/* 11 */ 0x2c, /* 7:0 Chrominance gain conrtol for QAM */
|
|
/* 12 */ 0x40, /* 7:0 Chrominance saturation control for VRAM port */
|
|
/* 13 */ 0x40, /* 7:0 Luminance contract control for VRAM port */
|
|
/* 14 */ 0x34, /* 7:0 Horizontal sync begin for 60hz */
|
|
#ifdef notdef
|
|
/* 15 */ 0x0c, /* 7:0 Horizontal sync stop for 60hz */
|
|
/* 16 */ 0xfb, /* 7:0 Horizontal clamp begin for 60hz */
|
|
/* 17 */ 0xd4, /* 7:0 Horizontal clamp stop for 60hz */
|
|
/* 18 */ 0xec, /* 7:0 Horizontal sync start after PH1 for 60hz */
|
|
#else
|
|
0x0a, 0xf4, 0xce, 0xf4,
|
|
#endif
|
|
/* 19 */ 0x80, /* 7:0 Luminance brightness control for VRAM port */
|
|
/* 1a */ 0x00,
|
|
/* 1b */ 0x00,
|
|
/* 1c */ 0x00,
|
|
/* 1d */ 0x00,
|
|
/* 1e */ 0x00,
|
|
/* 1f */ 0x00,
|
|
/* 20 */ 0x90, /* 7 ROM table bypass switch
|
|
6:5 Set output field mode
|
|
4 VRAM port outputs enable
|
|
3:2 First pixel position in VRO data
|
|
1:0 FIFO output register select */
|
|
/* 21 */ 0x80, /* 7:0 [7:0] Pixel number per line on output */
|
|
/* 22 */ 0x80, /* 7:0 [7:0] Pixel number per line on input */
|
|
/* 23 */ 0x03, /* 7:0 [7:0] Horizontal start position of scaling win*/
|
|
/* 24 */ 0x8a, /* 7:5 Horizontal decimation filter
|
|
4 [8] Horizontal start position of scaling win
|
|
3:2 [9:8] Pixel number per line on input
|
|
1:0 [9:8] Pixel number per line on output */
|
|
/* 25 */ 0xf0, /* 7:0 [7:0] Line number per output field */
|
|
/* 26 */ 0xf0, /* 7:0 [7:0] Line number per input field */
|
|
/* 27 */ 0x0f, /* 7:0 [7:0] Vertical start of scaling window */
|
|
/* 28 */ 0x80, /* 7 Adaptive filter switch
|
|
6:5 Vertical luminance data processing
|
|
4 [8] Vertical start of scaling window
|
|
3:2 [9:8] Line number per input field
|
|
1:0 [9:8] Line number per output field */
|
|
/* 29 */ 0x16, /* 7:0 [7:0] Vertical bypass start */
|
|
/* 2a */ 0x00, /* 7:0 [7:0] Vertical bypass count */
|
|
/* 2b */ 0x00, /* 4 [8] Vertical bypass start
|
|
2 [8] Vertical bypass count
|
|
0 Polarity, internally detected odd even flag */
|
|
/* 2c */ 0x80, /* 7:0 Set lower limit V for colour-keying */
|
|
/* 2d */ 0x7f, /* 7:0 Set upper limit V for colour-keying */
|
|
/* 2e */ 0x80, /* 7:0 Set lower limit U for colour-keying */
|
|
/* 2f */ 0x7f, /* 7:0 Set upper limit U for colour-keying */
|
|
/* 30 */ 0xbf /* 7 VRAM bus output format
|
|
6 Adaptive geometrical filter
|
|
5 Luminance limiting value
|
|
4 Monochrome and two's complement output data sel
|
|
3 Line quailifier flag
|
|
2 Pixel qualifier flag
|
|
1 Transparent data transfer
|
|
0 Extended formats enable bit */
|
|
};
|
|
|
|
/*
|
|
* i2c_write:
|
|
* Returns 0 Succesful completion.
|
|
* Returns 1 If transfer aborted or timeout occured.
|
|
*
|
|
*/
|
|
#define SAA7196_I2C_ADDR 0x40
|
|
#define I2C_WRITE 0x00
|
|
#define I2C_READ 0x01
|
|
#define SAA7116_IIC_NEW_CYCLE 0x1000000L
|
|
#define IIC_DIRECT_TRANSFER_ABORTED 0x0000200L
|
|
|
|
#define SAA7196_WRITE(mtr, reg, data) \
|
|
i2c_write(mtr, SAA7196_I2C_ADDR, I2C_WRITE, reg, data), \
|
|
mtr->saa7196_i2c[reg] = data
|
|
#define SAA7196_REG(mtr, reg) mtr->saa7196_i2c[reg]
|
|
#define SAA7196_READ(mtr) \
|
|
i2c_write(mtr, SAA7196_I2C_ADDR, I2C_READ, 0x0, 0x0)
|
|
|
|
static int
|
|
i2c_write(meteor_reg_t * mtr, u_char slave, u_char rw, u_char reg, u_char data)
|
|
{
|
|
register unsigned long wait_counter = 0x0001ffff;
|
|
register volatile u_long *iic_write_loc = (volatile u_long *)mtr->iic_virt_addr;
|
|
register int err = 0;
|
|
|
|
/* Write the data the the i2c write register */
|
|
*iic_write_loc = SAA7116_IIC_NEW_CYCLE |
|
|
(((u_long)slave|(u_long)rw) << 16) |
|
|
((u_long)reg << 8) | (u_long)data;
|
|
|
|
/* Wait until the i2c cycle is compeleted */
|
|
while((*iic_write_loc & SAA7116_IIC_NEW_CYCLE)) {
|
|
if(!wait_counter) break;
|
|
wait_counter--;
|
|
}
|
|
|
|
/* 1ffff should be enough delay time for the i2c cycle to complete */
|
|
if(!wait_counter) {
|
|
printf("meteor%d: saa7116 i2c %s transfer timeout 0x%x",
|
|
METEOR_NUM(mtr),
|
|
rw ? "read" : "write", *iic_write_loc);
|
|
|
|
err=1;
|
|
}
|
|
|
|
/* Check for error on direct write, clear if any */
|
|
if((*((volatile u_long *)mtr->stat_reg)) & IIC_DIRECT_TRANSFER_ABORTED){
|
|
printf("meteor%d: saa7116 i2c %s tranfer aborted",
|
|
METEOR_NUM(mtr),
|
|
rw ? "read" : "write" );
|
|
err= 1;
|
|
}
|
|
|
|
if(err) {
|
|
printf(" - reg=0x%x, value=0x%x.\n", reg, data);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
static char*
|
|
met_probe (pcici_t tag, pcidi_t type)
|
|
{
|
|
switch (type) {
|
|
|
|
case 0x12238086ul: /* meteor */
|
|
return ("Matrox Meteor");
|
|
|
|
};
|
|
return ((char*)0);
|
|
}
|
|
|
|
/* interrupt handling routine
|
|
complete meteor_read() if using interrupts
|
|
*/
|
|
static void
|
|
meteor_intr(void *arg)
|
|
{
|
|
meteor_reg_t *mtr = (meteor_reg_t *) arg;
|
|
volatile u_long *cap = (volatile u_long *)mtr->capt_cntrl,
|
|
*base = (volatile u_long *)mtr->virt_baseaddr,
|
|
*stat = base + 18; /* mtr->virt_base + 0x48*/
|
|
u_long status = *stat,
|
|
cap_err = *cap & 0x00000f00,
|
|
#ifdef METEOR_CHECK_PCI_BUS
|
|
pci_err = pci_conf_read(mtr->tag,
|
|
PCI_COMMAND_STATUS_REG),
|
|
#endif
|
|
next_base = (u_long)(vtophys(mtr->bigbuf));
|
|
|
|
/*
|
|
* Disable future interrupts if a capture mode is not selected.
|
|
* This can happen when we are in the process of closing or
|
|
* changing capture modes, otherwise it shouldn't happen.
|
|
*/
|
|
if(!(mtr->flags & METEOR_CAP_MASK)) {
|
|
*cap &= 0x8ff0; /* disable future interrupts */
|
|
}
|
|
#ifdef METEOR_CHECK_PCI_BUS
|
|
/*
|
|
* Check for pci bus errors.
|
|
*/
|
|
#define METEOR_MASTER_ABORT 0x20000000
|
|
#define METEOR_TARGET_ABORT 0x10000000
|
|
if(pci_err & METEOR_MASTER_ABORT) {
|
|
printf("meteor%d: intr: pci bus master dma abort: 0x%x 0x%x.\n",
|
|
METEOR_NUM(mtr), *base, *(base+3));
|
|
pci_conf_write(mtr->tag, PCI_COMMAND_STATUS_REG, pci_err);
|
|
}
|
|
if(pci_err & METEOR_TARGET_ABORT) {
|
|
printf("meteor%d: intr: pci bus target dma abort: 0x%x 0x%x.\n",
|
|
METEOR_NUM(mtr), *base, *(base+3));
|
|
pci_conf_write(mtr->tag, PCI_COMMAND_STATUS_REG, pci_err);
|
|
}
|
|
#endif
|
|
/*
|
|
* Check for errors.
|
|
*/
|
|
if (cap_err) {
|
|
if (cap_err & 0x300) {
|
|
if(mtr->fifo_errors % 50 == 0) {
|
|
printf("meteor%d: capture error", METEOR_NUM(mtr));
|
|
printf(": %s FIFO overflow.\n",
|
|
cap_err&0x0100? "even" : "odd");
|
|
}
|
|
mtr->fifo_errors++ ; /* increment fifo capture errors cnt */
|
|
}
|
|
if (cap_err & 0xc00) {
|
|
if(mtr->dma_errors % 50 == 0) {
|
|
printf("meteor%d: capture error", METEOR_NUM(mtr));
|
|
printf(": %s DMA address.\n",
|
|
cap_err&0x0400? "even" : "odd");
|
|
}
|
|
mtr->dma_errors++ ; /* increment DMA capture errors cnt */
|
|
}
|
|
}
|
|
*cap |= 0x0f30; /* clear error and field done */
|
|
|
|
/*
|
|
* In synchronous capture mode we need to know what the address
|
|
* offset for the next field/frame will be. next_base holds the
|
|
* value for the even dma buffers (for odd, one must add stride).
|
|
*/
|
|
if((mtr->flags & METEOR_SYNCAP) && !mtr->synch_wait &&
|
|
(mtr->current < mtr->frames)) { /* could be !=, but < is safer */
|
|
/* next_base is initialized to mtr->bigbuf */
|
|
next_base += mtr->frame_size * mtr->current;
|
|
}
|
|
|
|
/*
|
|
* Count the field and clear the field flag.
|
|
*
|
|
* In single mode capture, clear the continuous capture mode.
|
|
*
|
|
* In synchronous capture mode, if we have room for another field,
|
|
* adjust DMA buffer pointers.
|
|
* When we are above the hi water mark (hiwat), mtr->synch_wait will
|
|
* be set and we will not bump the DMA buffer pointers. Thus, once
|
|
* we reach the hi water mark, the driver acts like a continuous mode
|
|
* capture on the mtr->current frame until we hit the low water
|
|
* mark (lowat). The user had the option of stopping or halting
|
|
* the capture if this is not the desired effect.
|
|
*/
|
|
if (status & 0x1) { /* even field */
|
|
mtr->even_fields_captured++;
|
|
mtr->flags &= ~METEOR_WANT_EVEN;
|
|
if((mtr->flags & METEOR_SYNCAP) && !mtr->synch_wait) {
|
|
*base = next_base;
|
|
/* XXX should add adjustments for YUV_422 & PLANER */
|
|
}
|
|
}
|
|
if (status & 0x2) { /* odd field */
|
|
mtr->odd_fields_captured++;
|
|
mtr->flags &= ~METEOR_WANT_ODD;
|
|
if((mtr->flags & METEOR_SYNCAP) && !mtr->synch_wait) {
|
|
*(base+3) = next_base + *(base+6);
|
|
/* XXX should add adjustments for YUV_422 & PLANER */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we have a complete frame.
|
|
*/
|
|
if(!(mtr->flags & METEOR_WANT_MASK)) {
|
|
mtr->frames_captured++;
|
|
/*
|
|
* Wake up the user in single capture mode.
|
|
*/
|
|
if(mtr->flags & METEOR_SINGLE)
|
|
wakeup((caddr_t)mtr);
|
|
/*
|
|
* If the user requested to be notified via signal,
|
|
* let them know the frame is complete.
|
|
*/
|
|
if(mtr->proc && mtr->signal)
|
|
psignal(mtr->proc, mtr->signal);
|
|
/*
|
|
* Reset the want flags if in continuous or
|
|
* synchronous capture mode.
|
|
*/
|
|
if(mtr->flags & (METEOR_CONTIN|METEOR_SYNCAP)) {
|
|
switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) {
|
|
case METEOR_ONLY_ODD_FIELDS:
|
|
mtr->flags |= METEOR_WANT_ODD;
|
|
break;
|
|
case METEOR_ONLY_EVEN_FIELDS:
|
|
mtr->flags |= METEOR_WANT_EVEN;
|
|
break;
|
|
default:
|
|
mtr->flags |= METEOR_WANT_MASK;
|
|
break;
|
|
}
|
|
}
|
|
/*
|
|
* Special handling for synchronous capture mode.
|
|
*/
|
|
if(mtr->flags & METEOR_SYNCAP) {
|
|
struct meteor_mem *mm = mtr->mem;
|
|
/*
|
|
* Mark the current frame as active. It is up to
|
|
* the user to clear this, but we will clear it
|
|
* for the user for the current frame being captured
|
|
* if we are within the water marks (see below).
|
|
*/
|
|
mm->active |= 1 << (mtr->current - 1);
|
|
|
|
/*
|
|
* Since the user can muck with these values, we need
|
|
* to check and see if they are sane. If they don't
|
|
* pass the sanity check, disable the capture mode.
|
|
* This is rather rude, but then so was the user.
|
|
*
|
|
* Do we really need all of this or should we just
|
|
* eliminate the possiblity of allowing the
|
|
* user to change hi and lo water marks while it
|
|
* is running? XXX
|
|
*/
|
|
if(mm->num_active_bufs < 0 ||
|
|
mm->num_active_bufs > mtr->frames ||
|
|
mm->lowat < 1 || mm->lowat >= mtr->frames ||
|
|
mm->hiwat < 1 || mm->hiwat >= mtr->frames ||
|
|
mm->lowat > mm->hiwat ) {
|
|
*cap &= 0x8ff0;
|
|
mtr->flags &= ~(METEOR_SYNCAP|METEOR_WANT_MASK);
|
|
} else {
|
|
/*
|
|
* Ok, they are sane, now we want to
|
|
* check the water marks.
|
|
*/
|
|
if(mm->num_active_bufs <= mm->lowat)
|
|
mtr->synch_wait = 0;
|
|
if(mm->num_active_bufs >= mm->hiwat)
|
|
mtr->synch_wait = 1;
|
|
/*
|
|
* Clear the active frame bit for this frame
|
|
* and advance the counters if we are within
|
|
* the banks of the water marks.
|
|
*/
|
|
if(!mtr->synch_wait) {
|
|
mm->active &= ~(1 << mtr->current);
|
|
mtr->current++;
|
|
if(mtr->current > mtr->frames)
|
|
mtr->current = 1;
|
|
mm->num_active_bufs++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
*stat |= 0x7; /* clear interrupt status */
|
|
return;
|
|
}
|
|
|
|
static void
|
|
set_fps(meteor_reg_t *mtr, u_short fps)
|
|
{
|
|
volatile u_long *field_mask_even =
|
|
(volatile u_long *)mtr->virt_baseaddr + 0x4c;
|
|
volatile u_long *field_mask_odd = field_mask_even + 1;
|
|
volatile u_long *field_mask_length = field_mask_odd + 1;
|
|
int is_ntsc=1; /* assume ntsc or 30fps */
|
|
unsigned status;
|
|
|
|
SAA7196_WRITE(mtr, 0x0d, SAA7196_REG(mtr, 0x0d) | 0x02);
|
|
SAA7196_READ(mtr);
|
|
status = ((*((volatile u_long *)mtr->stat_reg)) & 0xff000000L) >> 24;
|
|
if((status & 0x40) == 0)
|
|
is_ntsc = ((status & 0x20) != 0) ;
|
|
|
|
/*
|
|
* A little sanity checking...
|
|
*/
|
|
if(fps < 1) fps = 1;
|
|
if(!is_ntsc && fps > 25) fps = 25;
|
|
if( is_ntsc && fps > 30) fps = 30;
|
|
mtr->fps = fps;
|
|
/*
|
|
* Set the fps using the mask/length.
|
|
*/
|
|
/* XXX we need some code to actually do this here... */
|
|
|
|
}
|
|
|
|
/*
|
|
* There is also a problem with range checking on the 7116.
|
|
* It seems to only work for 22 bits, so the max size we can allocate
|
|
* is 22 bits long or 4194304 bytes assuming that we put the beginning
|
|
* of the buffer on a 2^24 bit boundary. The range registers will use
|
|
* the top 8 bits of the dma start registers along with the bottom 22
|
|
* bits of the range register to determine if we go out of range.
|
|
* This makes getting memory a real kludge.
|
|
*
|
|
*/
|
|
#define RANGE_BOUNDARY (1<<22)
|
|
static vm_offset_t
|
|
get_meteor_mem(int unit, unsigned size)
|
|
{
|
|
vm_offset_t addr = NULL;
|
|
|
|
addr = vm_page_alloc_contig(size, 0x100000, 0xffffffff, 1<<24);
|
|
if(addr == NULL)
|
|
addr = vm_page_alloc_contig(size, 0x100000, 0xffffffff,
|
|
PAGE_SIZE);
|
|
if(addr == NULL) {
|
|
printf("meteor%d: Unable to allocate %d bytes of memory.\n",
|
|
unit, size);
|
|
}
|
|
|
|
return addr;
|
|
}
|
|
|
|
/*
|
|
* Initialize the capture card to NTSC RGB 16 640x480
|
|
*/
|
|
static void
|
|
meteor_init ( meteor_reg_t *mtr )
|
|
{
|
|
volatile u_long *vbase_addr;
|
|
int i;
|
|
|
|
*((volatile u_long *)(mtr->capt_cntrl)) = 0x00000040L;
|
|
|
|
vbase_addr = (volatile u_long *) mtr->virt_baseaddr;
|
|
for (i= 0 ; i < NUM_SAA7116_PCI_REGS; i++)
|
|
*vbase_addr++ = saa7116_pci_default[i];
|
|
|
|
for (i = 0; i < NUM_SAA7196_I2C_REGS; i++) {
|
|
SAA7196_WRITE(mtr, i, saa7196_i2c_default[i]);
|
|
}
|
|
set_fps(mtr, 30);
|
|
|
|
}
|
|
|
|
static void met_attach(pcici_t tag, int unit)
|
|
{
|
|
#ifdef METEOR_IRQ
|
|
u_long old_irq, new_irq;
|
|
#endif METEOR_IRQ
|
|
meteor_reg_t *mtr;
|
|
vm_offset_t buf;
|
|
u_long latency;
|
|
|
|
if (unit >= NMETEOR) {
|
|
printf("meteor%d: attach: invalid unit number\n", unit);
|
|
return ;
|
|
}
|
|
|
|
mtr = &meteor[unit];
|
|
pci_map_mem(tag, 0x10, &(mtr->virt_baseaddr),
|
|
&(mtr->phys_baseaddr));
|
|
/* IIC addres at 0x64 offset bytes */
|
|
mtr->capt_cntrl = mtr->virt_baseaddr + 0x40;
|
|
mtr->stat_reg = mtr->virt_baseaddr + 0x60;
|
|
mtr->iic_virt_addr = mtr->virt_baseaddr + 0x64;
|
|
|
|
#ifdef METEOR_IRQ /* from the configuration file */
|
|
old_irq = pci_conf_read(tag, PCI_INTERRUPT_REG);
|
|
pci_conf_write(tag, PCI_INTERRUPT_REG, METEOR_IRQ);
|
|
new_irq = pci_conf_read(tag, PCI_INTERRUPT_REG);
|
|
printf("meteor%d: attach: irq changed from %d to %d\n",
|
|
unit, (old_irq & 0xff), (new_irq & 0xff));
|
|
#endif METEOR_IRQ
|
|
/* set latency timer */
|
|
#define PCI_LATENCY_TIMER 0x0c
|
|
#ifndef DEF_LATENCY_VALUE
|
|
#define DEF_LATENCY_VALUE 32 /* is this value ok? */
|
|
#endif
|
|
latency = pci_conf_read(tag, PCI_LATENCY_TIMER);
|
|
latency = (latency >> 8) & 0xff;
|
|
if(bootverbose) {
|
|
if(latency)
|
|
printf("meteor%d: PCI bus latency is", unit);
|
|
else
|
|
printf("meteor%d: PCI bus latency was 0 changing to",
|
|
unit);
|
|
}
|
|
if(!latency) {
|
|
latency = DEF_LATENCY_VALUE;
|
|
pci_conf_write(tag, PCI_LATENCY_TIMER, latency<<8);
|
|
}
|
|
if(bootverbose) {
|
|
printf(" %d.\n", latency);
|
|
}
|
|
|
|
meteor_init(mtr); /* set up saa7116 and saa7196 chips */
|
|
mtr->tag = tag;
|
|
/* setup the interrupt handling routine */
|
|
pci_map_int (tag, meteor_intr, (void*) mtr, &net_imask);
|
|
|
|
/* 640*240*3 round up to nearest pag e*/
|
|
if(METEOR_ALLOC)
|
|
buf = get_meteor_mem(unit, METEOR_ALLOC);
|
|
else
|
|
buf = NULL;
|
|
if(bootverbose) {
|
|
printf("meteor%d: buffer size %d, addr 0x%x\n",
|
|
unit, METEOR_ALLOC, vtophys(buf));
|
|
}
|
|
|
|
mtr->bigbuf = buf;
|
|
mtr->alloc_pages = METEOR_ALLOC_PAGES;
|
|
if(buf != NULL) {
|
|
bzero((caddr_t) buf, METEOR_ALLOC);
|
|
buf = vtophys(buf);
|
|
*((volatile u_long *) mtr->virt_baseaddr) = buf;
|
|
/* 640x480 RGB 16 */
|
|
*((volatile u_long *) mtr->virt_baseaddr + 3) = buf + 0x500;
|
|
*((volatile u_long *) mtr->virt_baseaddr + 36) =
|
|
*((volatile u_long *) mtr->virt_baseaddr + 35) = buf +
|
|
METEOR_ALLOC;
|
|
}
|
|
mtr->flags = METEOR_INITALIZED | METEOR_NTSC | METEOR_DEV0 |
|
|
METEOR_RGB16;
|
|
/* 1 frame of 640x480 RGB 16 */
|
|
mtr->cols = 640;
|
|
mtr->rows = 480;
|
|
mtr->depth = 2; /* two bytes per pixel */
|
|
mtr->frames = 1; /* one frame */
|
|
#ifdef DEVFS
|
|
mtr->devfs_token = devfs_add_devsw( "/", "meteor", &meteor_cdevsw, unit,
|
|
DV_CHR, 0, 0, 0644);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
meteor_reset(meteor_reg_t * const sc)
|
|
{
|
|
|
|
}
|
|
|
|
/*---------------------------------------------------------
|
|
**
|
|
** Meteor character device driver routines
|
|
**
|
|
**---------------------------------------------------------
|
|
*/
|
|
|
|
#define UNIT(x) ((x) & 0x07)
|
|
|
|
int
|
|
meteor_open(dev_t dev, int flags, int fmt, struct proc *p)
|
|
{
|
|
meteor_reg_t *mtr;
|
|
int unit;
|
|
int i;
|
|
|
|
unit = UNIT(minor(dev));
|
|
if (unit >= NMETEOR) /* unit out of range */
|
|
return(ENXIO);
|
|
|
|
mtr = &(meteor[unit]);
|
|
|
|
if (!(mtr->flags & METEOR_INITALIZED)) /* device not found */
|
|
return(ENXIO);
|
|
|
|
if (mtr->flags & METEOR_OPEN) /* device is busy */
|
|
return(EBUSY);
|
|
|
|
mtr->flags |= METEOR_OPEN;
|
|
/*
|
|
* Make sure that the i2c regs are set the same for each open.
|
|
*/
|
|
for(i=0; i< NUM_SAA7196_I2C_REGS; i++) {
|
|
SAA7196_WRITE(mtr, i, saa7196_i2c_default[i]);
|
|
}
|
|
|
|
mtr->fifo_errors = 0;
|
|
mtr->dma_errors = 0;
|
|
mtr->frames_captured = 0;
|
|
mtr->even_fields_captured = 0;
|
|
mtr->odd_fields_captured = 0;
|
|
mtr->proc = (struct proc *)0;
|
|
set_fps(mtr, 30);
|
|
#ifdef METEOR_TEST_VIDEO
|
|
mtr->video.addr = 0;
|
|
mtr->video.width = 0;
|
|
mtr->video.banksize = 0;
|
|
mtr->video.ramsize = 0;
|
|
#endif
|
|
|
|
return(0);
|
|
}
|
|
|
|
int
|
|
meteor_close(dev_t dev, int flags, int fmt, struct proc *p)
|
|
{
|
|
meteor_reg_t *mtr;
|
|
int unit;
|
|
int temp;
|
|
|
|
unit = UNIT(minor(dev));
|
|
if (unit >= NMETEOR) /* unit out of range */
|
|
return(ENXIO);
|
|
|
|
mtr = &(meteor[unit]);
|
|
mtr->flags &= ~METEOR_OPEN;
|
|
|
|
if(mtr->flags & METEOR_SINGLE)
|
|
/* this should not happen, the read capture
|
|
should have completed or in the very least
|
|
recieved a signal before close is called. */
|
|
wakeup((caddr_t)mtr); /* continue read */
|
|
/*
|
|
* Turn off capture mode.
|
|
*/
|
|
*((volatile u_long *) mtr->capt_cntrl) = 0x8ff0;
|
|
mtr->flags &= ~(METEOR_CAP_MASK|METEOR_WANT_MASK);
|
|
|
|
#ifdef METEOR_DEALLOC_PAGES
|
|
if (mtr->bigbuf != NULL) {
|
|
kmem_free(kernel_map,mtr->bigbuf,(mtr->alloc_pages*PAGE_SIZE));
|
|
mtr->bigbuf = NULL;
|
|
mtr->alloc_pages = 0;
|
|
}
|
|
#else
|
|
#ifdef METEOR_DEALLOC_ABOVE
|
|
if (mtr->bigbuf != NULL && mtr->alloc_pages > METEOR_DEALLOC_ABOVE) {
|
|
temp = METEOR_DEALLOC_ABOVE - mtr->alloc_pages;
|
|
kmem_free(kernel_map,
|
|
mtr->bigbuf+((mtr->alloc_pages - temp) * PAGE_SIZE),
|
|
(temp * PAGE_SIZE));
|
|
mtr->alloc_pages = METEOR_DEALLOC_ABOVE;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
return(0);
|
|
}
|
|
|
|
static void
|
|
start_capture(meteor_reg_t *mtr, unsigned type)
|
|
{
|
|
volatile u_long *cap = (volatile u_long *)mtr->capt_cntrl;
|
|
volatile u_long *p =(volatile u_long *)mtr->virt_baseaddr;
|
|
|
|
mtr->flags |= type;
|
|
switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) {
|
|
case METEOR_ONLY_EVEN_FIELDS:
|
|
mtr->flags |= METEOR_WANT_EVEN;
|
|
if(type == METEOR_SINGLE)
|
|
*cap = 0x0ff4 | mtr->range_enable;
|
|
else
|
|
*cap = 0x0ff1 | mtr->range_enable;
|
|
break;
|
|
case METEOR_ONLY_ODD_FIELDS:
|
|
mtr->flags |= METEOR_WANT_ODD;
|
|
if(type == METEOR_SINGLE)
|
|
*cap = 0x0ff8 | mtr->range_enable;
|
|
else
|
|
*cap = 0x0ff2 | mtr->range_enable;
|
|
break;
|
|
default:
|
|
mtr->flags |= METEOR_WANT_MASK;
|
|
if(type == METEOR_SINGLE)
|
|
*cap = 0x0ffc | mtr->range_enable;
|
|
else
|
|
*cap = 0x0ff3 | mtr->range_enable;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int
|
|
meteor_read(dev_t dev, struct uio *uio, int ioflag)
|
|
{
|
|
meteor_reg_t *mtr;
|
|
int unit;
|
|
int status;
|
|
int count;
|
|
|
|
unit = UNIT(minor(dev));
|
|
if (unit >= NMETEOR) /* unit out of range */
|
|
return(ENXIO);
|
|
|
|
mtr = &(meteor[unit]);
|
|
if (mtr->bigbuf == NULL)/* no frame buffer allocated (ioctl failed) */
|
|
return(ENOMEM);
|
|
|
|
if (mtr->flags & METEOR_CAP_MASK)
|
|
return(EIO); /* already capturing */
|
|
|
|
count = mtr->rows * mtr->cols * mtr->depth;
|
|
if (uio->uio_iov->iov_len < count)
|
|
return(EINVAL);
|
|
|
|
/* Start capture */
|
|
start_capture(mtr, METEOR_SINGLE);
|
|
|
|
status=tsleep((caddr_t)mtr, METPRI, "capturing", 0);
|
|
if (!status) /* successful capture */
|
|
status = uiomove((caddr_t)mtr->bigbuf, count, uio);
|
|
else
|
|
printf ("meteor%d: read: tsleep error %d\n", unit, status);
|
|
|
|
mtr->flags &= ~(METEOR_SINGLE | METEOR_WANT_MASK);
|
|
|
|
return(status);
|
|
}
|
|
|
|
int
|
|
meteor_write(dev_t dev, struct uio *uio, int ioflag)
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
int
|
|
meteor_ioctl(dev_t dev, int cmd, caddr_t arg, int flag, struct proc *pr)
|
|
{
|
|
int error;
|
|
int unit;
|
|
unsigned int temp;
|
|
meteor_reg_t *mtr;
|
|
struct meteor_counts *cnt;
|
|
struct meteor_geomet *geo;
|
|
struct meteor_mem *mem;
|
|
struct meteor_capframe *frame;
|
|
#ifdef METEOR_TEST_VIDEO
|
|
struct meteor_video *video;
|
|
#endif
|
|
volatile u_long *p;
|
|
vm_offset_t buf;
|
|
|
|
error = 0;
|
|
|
|
if (!arg) return(EINVAL);
|
|
unit = UNIT(minor(dev));
|
|
if (unit >= NMETEOR) /* unit out of range */
|
|
return(ENXIO);
|
|
|
|
mtr = &(meteor[unit]);
|
|
|
|
switch (cmd) {
|
|
#ifdef METEOR_TEST_VIDEO
|
|
case METEORGVIDEO:
|
|
video = (struct meteor_video *)arg;
|
|
video->addr = mtr->video.addr;
|
|
video->width = mtr->video.width;
|
|
video->banksize = mtr->video.banksize;
|
|
video->ramsize = mtr->video.ramsize;
|
|
break;
|
|
case METEORSVIDEO:
|
|
video = (struct meteor_video *)arg;
|
|
mtr->video.addr = video->addr;
|
|
mtr->video.width = video->width;
|
|
mtr->video.banksize = video->banksize;
|
|
mtr->video.ramsize = video->ramsize;
|
|
break;
|
|
#endif
|
|
case METEORSFPS:
|
|
set_fps(mtr, *(u_short *)arg);
|
|
break;
|
|
case METEORGFPS:
|
|
*(u_short *)arg = mtr->fps;
|
|
break;
|
|
case METEORSSIGNAL:
|
|
mtr->signal = *(int *) arg;
|
|
mtr->proc = pr;
|
|
break;
|
|
case METEORGSIGNAL:
|
|
*(int *)arg = mtr->signal;
|
|
break;
|
|
case METEORSTATUS: /* get 7196 status */
|
|
temp = 0;
|
|
SAA7196_WRITE(mtr, 0x0d, SAA7196_REG(mtr, 0x0d) | 0x02);
|
|
SAA7196_READ(mtr);
|
|
temp |= ((*((volatile u_long *)mtr->stat_reg)) & 0xff000000L) >> 24;
|
|
SAA7196_WRITE(mtr, 0x0d, SAA7196_REG(mtr, 0x0d) & 0x02);
|
|
SAA7196_READ(mtr);
|
|
temp |= ((*((volatile u_long *)mtr->stat_reg)) & 0xff000000L) >> 16;
|
|
*(u_short *)arg = temp;
|
|
break;
|
|
case METEORSHUE: /* set hue */
|
|
SAA7196_WRITE(mtr, 0x07, *(char *)arg);
|
|
break;
|
|
case METEORGHUE: /* get hue */
|
|
*(char *)arg = SAA7196_REG(mtr, 0x07);
|
|
break;
|
|
case METEORSCHCV: /* set chrominance gain */
|
|
SAA7196_WRITE(mtr, 0x11, *(char *)arg);
|
|
break;
|
|
case METEORGCHCV: /* get chrominance gain */
|
|
*(char *)arg = SAA7196_REG(mtr, 0x11);
|
|
break;
|
|
case METEORSINPUT: /* set input device */
|
|
switch(*(unsigned long *)arg & METEOR_DEV_MASK) {
|
|
case 0: /* default */
|
|
case METEOR_INPUT_DEV0:
|
|
mtr->flags = (mtr->flags & ~METEOR_DEV_MASK)
|
|
| METEOR_DEV0;
|
|
|
|
SAA7196_WRITE(mtr, 0x0e,
|
|
(SAA7196_REG(mtr, 0x0e) & ~0x3) | 0x0);
|
|
break;
|
|
case METEOR_INPUT_DEV1:
|
|
mtr->flags = (mtr->flags & ~METEOR_DEV_MASK)
|
|
| METEOR_DEV1;
|
|
SAA7196_WRITE(mtr, 0x0e,
|
|
(SAA7196_REG(mtr, 0x0e) & ~0x3) | 0x1);
|
|
break;
|
|
case METEOR_INPUT_DEV2:
|
|
mtr->flags = (mtr->flags & ~METEOR_DEV_MASK)
|
|
| METEOR_DEV2;
|
|
SAA7196_WRITE(mtr, 0x0e,
|
|
(SAA7196_REG(mtr, 0x0e) & ~0x3) | 0x2);
|
|
break;
|
|
case METEOR_INPUT_DEV3:
|
|
mtr->flags = (mtr->flags & ~METEOR_DEV_MASK)
|
|
| METEOR_DEV3;
|
|
SAA7196_WRITE(mtr, 0x0e,
|
|
(SAA7196_REG(mtr, 0x0e) | 0x3));
|
|
break;
|
|
default:
|
|
return EINVAL;
|
|
}
|
|
break;
|
|
case METEORGINPUT: /* get input device */
|
|
*(u_long *)arg = mtr->flags & METEOR_DEV_MASK;
|
|
break;
|
|
case METEORSFMT: /* set input format */
|
|
switch(*(unsigned long *)arg & METEOR_FORM_MASK ) {
|
|
case 0: /* default */
|
|
case METEOR_FMT_NTSC:
|
|
mtr->flags = (mtr->flags & ~METEOR_FORM_MASK) |
|
|
METEOR_NTSC;
|
|
SAA7196_WRITE(mtr, 0x0d,
|
|
(SAA7196_REG(mtr, 0x0d) & ~0x01));
|
|
SAA7196_WRITE(mtr, 0x0f,
|
|
(SAA7196_REG(mtr, 0x0f) & ~0xe0) | 0x40);
|
|
SAA7196_WRITE(mtr, 0x22, 0x80);
|
|
SAA7196_WRITE(mtr, 0x24,
|
|
(SAA7196_REG(mtr, 0x24) & ~0x0c) | 0x08);
|
|
SAA7196_WRITE(mtr, 0x26, 0xf0);
|
|
SAA7196_WRITE(mtr, 0x28,
|
|
(SAA7196_REG(mtr, 0x28) & ~0x0c)) ;
|
|
break;
|
|
case METEOR_FMT_PAL:
|
|
mtr->flags = (mtr->flags & ~METEOR_FORM_MASK) |
|
|
METEOR_PAL;
|
|
SAA7196_WRITE(mtr, 0x0d,
|
|
(SAA7196_REG(mtr, 0x0d) & ~0x01));
|
|
SAA7196_WRITE(mtr, 0x0f,
|
|
(SAA7196_REG(mtr, 0x0f) & ~0xe0));
|
|
SAA7196_WRITE(mtr, 0x22, 0x00);
|
|
SAA7196_WRITE(mtr, 0x24,
|
|
(SAA7196_REG(mtr, 0x24) | 0x0c));
|
|
SAA7196_WRITE(mtr, 0x26, 0x20);
|
|
SAA7196_WRITE(mtr, 0x28,
|
|
(SAA7196_REG(mtr, 0x28) & ~0x0c) | 0x04) ;
|
|
break;
|
|
case METEOR_FMT_SECAM:
|
|
mtr->flags = (mtr->flags & ~METEOR_FORM_MASK) |
|
|
METEOR_SECAM;
|
|
SAA7196_WRITE(mtr, 0x0d,
|
|
(SAA7196_REG(mtr, 0x0d) & ~0x01) | 0x1);
|
|
SAA7196_WRITE(mtr, 0x0f,
|
|
(SAA7196_REG(mtr, 0x0f) & ~0xe0) | 0x20);
|
|
SAA7196_WRITE(mtr, 0x22, 0x00);
|
|
SAA7196_WRITE(mtr, 0x24,
|
|
(SAA7196_REG(mtr, 0x24) | 0x0c));
|
|
SAA7196_WRITE(mtr, 0x26, 0x20);
|
|
SAA7196_WRITE(mtr, 0x28,
|
|
(SAA7196_REG(mtr, 0x28) & ~0x0c) | 0x04) ;
|
|
break;
|
|
case METEOR_FMT_AUTOMODE:
|
|
mtr->flags = (mtr->flags & ~METEOR_FORM_MASK) |
|
|
METEOR_AUTOMODE;
|
|
SAA7196_WRITE(mtr, 0x0d,
|
|
(SAA7196_REG(mtr, 0x0d) & ~0x01));
|
|
SAA7196_WRITE(mtr, 0x0f,
|
|
(SAA7196_REG(mtr, 0x0f) & ~0xe0) | 0x80);
|
|
break;
|
|
default:
|
|
return EINVAL;
|
|
}
|
|
break;
|
|
case METEORGFMT: /* get input format */
|
|
*(u_long *)arg = mtr->flags & METEOR_FORM_MASK;
|
|
break;
|
|
case METEORCAPTUR:
|
|
temp = mtr->flags;
|
|
switch (*(int *) arg) {
|
|
case METEOR_CAP_SINGLE:
|
|
if (mtr->bigbuf==NULL) /* no frame buffer allocated */
|
|
return(ENOMEM);
|
|
|
|
if (temp & METEOR_CAP_MASK)
|
|
return(EIO); /* already capturing */
|
|
|
|
start_capture(mtr, METEOR_SINGLE);
|
|
|
|
/* wait for capture to complete */
|
|
error=tsleep((caddr_t)mtr, METPRI, "capturing", 0);
|
|
if(error)
|
|
printf("meteor%d: ioctl: tsleep error %d\n",
|
|
unit, error);
|
|
mtr->flags &= ~(METEOR_SINGLE|METEOR_WANT_MASK);
|
|
break;
|
|
case METEOR_CAP_CONTINOUS:
|
|
if (mtr->bigbuf==NULL) /* no frame buffer allocated */
|
|
return(ENOMEM);
|
|
|
|
if (temp & METEOR_CAP_MASK)
|
|
return(EIO); /* already capturing */
|
|
|
|
start_capture(mtr, METEOR_CONTIN);
|
|
|
|
break;
|
|
case METEOR_CAP_STOP_CONT:
|
|
if (mtr->flags & METEOR_CONTIN) {
|
|
/* turn off capture */
|
|
*((volatile u_long *) mtr->capt_cntrl) = 0x8ff0;
|
|
mtr->flags &= ~(METEOR_CONTIN|METEOR_WANT_MASK);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
break;
|
|
case METEORCAPFRM:
|
|
frame = (struct meteor_capframe *) arg;
|
|
if (!frame)
|
|
return(EINVAL);
|
|
switch (frame->command) {
|
|
case METEOR_CAP_N_FRAMES:
|
|
if (mtr->flags & METEOR_CAP_MASK)
|
|
return(EIO);
|
|
if (mtr->flags & (METEOR_YUV_PLANER | METEOR_YUV_422)) /* XXX*/
|
|
return(EINVAL); /* should fix intr so we allow these */
|
|
if (mtr->bigbuf == NULL)
|
|
return(ENOMEM);
|
|
if ((mtr->frames < 2) ||
|
|
(frame->lowat < 1 || frame->lowat >= mtr->frames) ||
|
|
(frame->hiwat < 1 || frame->hiwat >= mtr->frames) ||
|
|
(frame->lowat > frame->hiwat))
|
|
return(EINVAL);
|
|
/* meteor_mem structure is on the page after the data */
|
|
mem = mtr->mem = (struct meteor_mem *) (mtr->bigbuf +
|
|
((mtr->rows*mtr->cols * mtr->depth *
|
|
mtr->frames+PAGE_SIZE-1)/PAGE_SIZE)*PAGE_SIZE);
|
|
mtr->current = 1;
|
|
mtr->synch_wait = 0;
|
|
mem->num_bufs = mtr->frames;
|
|
mem->frame_size=
|
|
mtr->frame_size = mtr->rows * mtr->cols * mtr->depth;
|
|
/* user and kernel change these */
|
|
mem->lowat = frame->lowat;
|
|
mem->hiwat = frame->hiwat;
|
|
mem->active = 0;
|
|
mem->num_active_bufs = 0;
|
|
/* Start capture */
|
|
start_capture(mtr, METEOR_SYNCAP);
|
|
break;
|
|
case METEOR_CAP_STOP_FRAMES:
|
|
if (mtr->flags & METEOR_SYNCAP) {
|
|
/* turn off capture */
|
|
*((volatile u_long *) mtr->capt_cntrl) = 0x8ff0;
|
|
mtr->flags &= ~(METEOR_SYNCAP|METEOR_WANT_MASK);
|
|
}
|
|
break;
|
|
case METEOR_HALT_N_FRAMES:
|
|
if(mtr->flags & METEOR_SYNCAP) {
|
|
*((volatile u_long *) mtr->capt_cntrl) = 0x8ff0;
|
|
mtr->flags &= ~(METEOR_WANT_MASK);
|
|
}
|
|
break;
|
|
case METEOR_CONT_N_FRAMES:
|
|
if(!(mtr->flags & METEOR_SYNCAP)) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
start_capture(mtr, METEOR_SYNCAP);
|
|
break;
|
|
default:
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case METEORSETGEO:
|
|
geo = (struct meteor_geomet *) arg;
|
|
|
|
/* Either even or odd, if even & odd, then these a zero */
|
|
if((geo->oformat & METEOR_GEO_ODD_ONLY) &&
|
|
(geo->oformat & METEOR_GEO_EVEN_ONLY)) {
|
|
printf("meteor%d: ioctl: Geometry odd or even only.\n",
|
|
unit);
|
|
return EINVAL;
|
|
}
|
|
/* set/clear even/odd flags */
|
|
if(geo->oformat & METEOR_GEO_ODD_ONLY)
|
|
mtr->flags |= METEOR_ONLY_ODD_FIELDS;
|
|
else
|
|
mtr->flags &= ~METEOR_ONLY_ODD_FIELDS;
|
|
if(geo->oformat & METEOR_GEO_EVEN_ONLY)
|
|
mtr->flags |= METEOR_ONLY_EVEN_FIELDS;
|
|
else
|
|
mtr->flags &= ~METEOR_ONLY_EVEN_FIELDS;
|
|
|
|
/* can't change parameters while capturing */
|
|
if (mtr->flags & METEOR_CAP_MASK)
|
|
return(EBUSY);
|
|
|
|
if ((geo->columns & 0x3fe) != geo->columns) {
|
|
printf(
|
|
"meteor%d: ioctl: %d: columns too large or not even.\n",
|
|
unit, geo->columns);
|
|
error = EINVAL;
|
|
}
|
|
if (((geo->rows & 0x7fe) != geo->rows) ||
|
|
((geo->oformat & METEOR_GEO_FIELD_MASK) &&
|
|
((geo->rows & 0x3fe) != geo->rows)) ) {
|
|
printf(
|
|
"meteor%d: ioctl: %d: rows too large or not even.\n",
|
|
unit, geo->rows);
|
|
error = EINVAL;
|
|
}
|
|
if (geo->frames > 32) {
|
|
printf("meteor%d: ioctl: too many frames.\n", unit);
|
|
error = EINVAL;
|
|
}
|
|
if(error) return error;
|
|
|
|
if (temp=geo->rows * geo->columns * geo->frames * 2) {
|
|
if (geo->oformat & METEOR_GEO_RGB24) temp = temp * 2;
|
|
|
|
/* meteor_mem structure for SYNC Capture */
|
|
if (geo->frames > 1) temp += PAGE_SIZE;
|
|
|
|
temp = (temp + PAGE_SIZE -1)/PAGE_SIZE;
|
|
if (temp > mtr->alloc_pages
|
|
#ifdef METEOR_TEST_VIDEO
|
|
&& mtr->video.addr == 0
|
|
#endif
|
|
) {
|
|
buf = get_meteor_mem(unit, temp*PAGE_SIZE);
|
|
if(buf != NULL) {
|
|
kmem_free(kernel_map, mtr->bigbuf,
|
|
(mtr->alloc_pages * PAGE_SIZE));
|
|
mtr->bigbuf = buf;
|
|
mtr->alloc_pages = temp;
|
|
if(bootverbose)
|
|
printf(
|
|
"meteor%d: ioctl: Allocating %d bytes\n",
|
|
unit, temp*PAGE_SIZE);
|
|
} else {
|
|
error = ENOMEM;
|
|
}
|
|
}
|
|
}
|
|
if(error) return error;
|
|
|
|
mtr->rows = geo->rows;
|
|
mtr->cols = geo->columns;
|
|
mtr->frames = geo->frames;
|
|
|
|
p = (volatile u_long *) mtr->virt_baseaddr;
|
|
#ifdef METEOR_TEST_VIDEO
|
|
if(mtr->video.addr)
|
|
buf = vtophys(mtr->video.addr);
|
|
else
|
|
#endif
|
|
buf = vtophys(mtr->bigbuf);
|
|
|
|
/* set defaults and end of buffer locations */
|
|
*(p+0) = buf; /* DMA 1 even */
|
|
*(p+1) = buf; /* DMA 2 even */
|
|
*(p+2) = buf; /* DMA 3 even */
|
|
*(p+3) = buf; /* DMA 1 odd */
|
|
*(p+4) = buf; /* DMA 2 odd */
|
|
*(p+5) = buf; /* DMA 3 odd */
|
|
*(p+6) = 0; /* Stride 1 even */
|
|
*(p+7) = 0; /* Stride 2 even */
|
|
*(p+8) = 0; /* Stride 3 even */
|
|
*(p+9) = 0; /* Stride 1 odd */
|
|
*(p+10) = 0; /* Stride 2 odd */
|
|
*(p+11) = 0; /* Stride 3 odd */
|
|
/* set end of DMA location, even/odd */
|
|
if(mtr->range_enable)
|
|
*(p+35) = *(p+36) = buf + mtr->alloc_pages * PAGE_SIZE;
|
|
else
|
|
*(p+35) = *(p+36) = 0xffffffff;
|
|
|
|
/*
|
|
* Determine if we can use the hardware range detect.
|
|
*/
|
|
if(mtr->alloc_pages * PAGE_SIZE < RANGE_BOUNDARY &&
|
|
((buf & 0xff000000) | *(p+35)) == (buf + mtr->alloc_pages *
|
|
PAGE_SIZE) )
|
|
mtr->range_enable = 0x8000;
|
|
else
|
|
mtr->range_enable = 0x0;
|
|
|
|
|
|
switch (geo->oformat & METEOR_GEO_OUTPUT_MASK) {
|
|
case 0: /* default */
|
|
case METEOR_GEO_RGB16:
|
|
mtr->depth = 2;
|
|
mtr->flags &= ~METEOR_OUTPUT_FMT_MASK;
|
|
mtr->flags |= METEOR_RGB16;
|
|
temp = mtr->cols * mtr->depth;
|
|
/* recal stride and starting point */
|
|
switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) {
|
|
case METEOR_ONLY_ODD_FIELDS:
|
|
*(p+3) = buf; /*dma 1 o */
|
|
#ifdef METEOR_TEST_VIDEO
|
|
if(mtr->video.addr && mtr->video.width)
|
|
*(p+9) = mtr->video.width - temp;
|
|
#endif
|
|
SAA7196_WRITE(mtr, 0x20, 0xd0);
|
|
break;
|
|
case METEOR_ONLY_EVEN_FIELDS:
|
|
*(p+0) = buf; /*dma 1 e */
|
|
#ifdef METEOR_TEST_VIDEO
|
|
if(mtr->video.addr && mtr->video.width)
|
|
*(p+6) = mtr->video.width - temp;
|
|
#endif
|
|
SAA7196_WRITE(mtr, 0x20, 0xf0);
|
|
break;
|
|
default: /* interlaced even/odd */
|
|
*(p+0) = buf;
|
|
*(p+3) = buf + temp;
|
|
*(p+6) = *(p+9) = temp;
|
|
#ifdef METEOR_TEST_VIDEO
|
|
if(mtr->video.addr && mtr->video.width) {
|
|
*(p+3) = buf + mtr->video.width;
|
|
*(p+6) = *(p+9) = mtr->video.width -
|
|
temp + mtr->video.width;
|
|
}
|
|
#endif
|
|
SAA7196_WRITE(mtr, 0x20, 0x90);
|
|
break;
|
|
}
|
|
*(p+12) = *(p+13) = 0xeeeeee01; /* routes */
|
|
break;
|
|
case METEOR_GEO_RGB24:
|
|
mtr->depth = 4;
|
|
mtr->flags &= ~METEOR_OUTPUT_FMT_MASK;
|
|
mtr->flags |= METEOR_RGB24;
|
|
temp = mtr->cols * mtr->depth;
|
|
/* recal stride and starting point */
|
|
switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) {
|
|
case METEOR_ONLY_ODD_FIELDS:
|
|
*(p+3) = buf; /*dma 1 o */
|
|
#ifdef METEOR_TEST_VIDEO
|
|
if(mtr->video.addr && mtr->video.width)
|
|
*(p+9) = mtr->video.width - temp;
|
|
#endif
|
|
SAA7196_WRITE(mtr, 0x20, 0xd2);
|
|
break;
|
|
case METEOR_ONLY_EVEN_FIELDS:
|
|
*(p+0) = buf; /*dma 1 e */
|
|
#ifdef METEOR_TEST_VIDEO
|
|
if(mtr->video.addr && mtr->video.width)
|
|
*(p+6) = mtr->video.width - temp;
|
|
#endif
|
|
SAA7196_WRITE(mtr, 0x20, 0xf2);
|
|
break;
|
|
default: /* interlaced even/odd */
|
|
*(p+0) = buf;
|
|
*(p+3) = buf + mtr->cols * mtr->depth;
|
|
*(p+6) = *(p+9) = mtr->cols * mtr->depth;
|
|
#ifdef METEOR_TEST_VIDEO
|
|
if(mtr->video.addr && mtr->video.width) {
|
|
*(p+3) = buf + mtr->video.width;
|
|
*(p+6) = *(p+9) = mtr->video.width -
|
|
temp + mtr->video.width;
|
|
}
|
|
#endif
|
|
SAA7196_WRITE(mtr, 0x20, 0x92);
|
|
break;
|
|
}
|
|
*(p+12) = *(p+13) = 0x39393900; /* routes */
|
|
break;
|
|
case METEOR_GEO_YUV_PLANER:
|
|
mtr->depth = 2;
|
|
mtr->flags &= ~METEOR_OUTPUT_FMT_MASK;
|
|
mtr->flags |= METEOR_YUV_PLANER;
|
|
/* recal stride and starting point */
|
|
temp = mtr->rows * mtr->cols; /* compute frame size */
|
|
switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) {
|
|
case METEOR_ONLY_ODD_FIELDS:
|
|
*(p+3) = buf; /* Y Odd */
|
|
*(p+4) = buf + temp; /* U Odd */
|
|
temp >>= 1;
|
|
*(p+5) = *(p+4) + temp; /* V Odd */
|
|
SAA7196_WRITE(mtr, 0x20, 0xd1);
|
|
break;
|
|
case METEOR_ONLY_EVEN_FIELDS:
|
|
*(p+0) = buf; /* Y Even */
|
|
*(p+1) = buf + temp; /* U Even */
|
|
temp >>= 1;
|
|
*(p+2) = *(p+1) + temp; /* V Even */
|
|
SAA7196_WRITE(mtr, 0x20, 0xf1);
|
|
break;
|
|
default: /* interlaced even/odd */
|
|
*(p+0) = buf; /* Y Even */
|
|
*(p+1) = buf + temp; /* U Even */
|
|
temp >>= 2;
|
|
*(p+2) = *(p+1) + temp; /* V Even */
|
|
*(p+3) = *(p+0) + mtr->cols; /* Y Odd */
|
|
*(p+4) = *(p+2) + temp; /* U Odd */
|
|
*(p+5) = *(p+4) + temp; /* V Odd */
|
|
*(p+6) = *(p+9) = mtr->cols; /* Y Stride */
|
|
SAA7196_WRITE(mtr, 0x20, 0x91);
|
|
break;
|
|
}
|
|
*(p+12) = *(p+13) = 0xaaaaffc1; /* routes */
|
|
break;
|
|
case METEOR_GEO_YUV_422:/* same as planer, different uv order */
|
|
mtr->depth = 2;
|
|
mtr->flags &= ~METEOR_OUTPUT_FMT_MASK;
|
|
mtr->flags |= METEOR_YUV_422;
|
|
temp = mtr->rows * mtr->cols; /* compute frame size */
|
|
switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) {
|
|
case METEOR_ONLY_ODD_FIELDS:
|
|
*(p+3) = buf;
|
|
*(p+4) = buf + temp;
|
|
*(p+5) = *(p+4) + (temp >> 1);
|
|
SAA7196_WRITE(mtr, 0x20, 0xd1);
|
|
break;
|
|
case METEOR_ONLY_EVEN_FIELDS:
|
|
*(p+0) = buf;
|
|
*(p+1) = buf + temp;
|
|
*(p+2) = *(p+1) + (temp >> 1);
|
|
SAA7196_WRITE(mtr, 0x20, 0xf1);
|
|
break;
|
|
default: /* interlaced even/odd */
|
|
*(p+0) = buf; /* Y even */
|
|
*(p+1) = buf + temp; /* U even */
|
|
*(p+2) = *(p+1) + (temp >> 1); /* V even */
|
|
*(p+3) = *(p+0) + mtr->cols; /* Y odd */
|
|
temp = mtr->cols >> 1;
|
|
*(p+4) = *(p+1) + temp; /* U odd */
|
|
*(p+5) = *(p+2) + temp; /* V odd */
|
|
*(p+6) = *(p+9) = mtr->cols; /* Y stride */
|
|
*(p+7) = *(p+10) = temp; /* U stride */
|
|
*(p+8) = *(p+11) = temp; /* V stride */
|
|
SAA7196_WRITE(mtr, 0x20, 0x91);
|
|
break;
|
|
}
|
|
*(p+12) = *(p+13) = 0xaaaaffc1; /* routes */
|
|
break;
|
|
case METEOR_GEO_YUV_PACKED:
|
|
mtr->depth = 2;
|
|
mtr->flags &= ~METEOR_OUTPUT_FMT_MASK;
|
|
mtr->flags |= METEOR_YUV_PACKED;
|
|
/* recal stride and odd starting point */
|
|
switch(mtr->flags & METEOR_ONLY_FIELDS_MASK) {
|
|
case METEOR_ONLY_ODD_FIELDS:
|
|
*(p+3) = buf;
|
|
SAA7196_WRITE(mtr, 0x20, 0xd1);
|
|
break;
|
|
case METEOR_ONLY_EVEN_FIELDS:
|
|
*(p+0) = buf;
|
|
SAA7196_WRITE(mtr, 0x20, 0xf1);
|
|
break;
|
|
default: /* interlaced even/odd */
|
|
*(p+0) = buf;
|
|
*(p+3) = buf + mtr->cols * mtr->depth;
|
|
*(p+6) = *(p+9) = mtr->cols * mtr->depth;
|
|
SAA7196_WRITE(mtr, 0x20, 0x91);
|
|
break;
|
|
}
|
|
*(p+12) = *(p+13) = 0xeeeeee41; /* routes */
|
|
break;
|
|
default:
|
|
error = EINVAL; /* invalid argument */
|
|
printf("meteor%d: ioctl: invalid output format\n",unit);
|
|
break;
|
|
}
|
|
/* set cols */
|
|
SAA7196_WRITE(mtr, 0x21, mtr->cols & 0xff);
|
|
SAA7196_WRITE(mtr, 0x24,
|
|
((SAA7196_REG(mtr, 0x24) & ~0x03) |
|
|
((mtr->cols >> 8) & 0x03)));
|
|
/* set rows */
|
|
if(mtr->flags & METEOR_ONLY_FIELDS_MASK) {
|
|
SAA7196_WRITE(mtr, 0x25, ((mtr->rows) & 0xff));
|
|
SAA7196_WRITE(mtr, 0x28,
|
|
((SAA7196_REG(mtr, 0x28) & ~0x03) |
|
|
((mtr->rows >> 8) & 0x03)));
|
|
} else { /* Interlaced */
|
|
SAA7196_WRITE(mtr, 0x25, ((mtr->rows >> 1) & 0xff));
|
|
SAA7196_WRITE(mtr, 0x28,
|
|
((SAA7196_REG(mtr, 0x28) & ~0x03) |
|
|
((mtr->rows >> 9) & 0x03)));
|
|
}
|
|
/* set signed/unsigned chrominance */
|
|
SAA7196_WRITE(mtr, 0x30, (SAA7196_REG(mtr, 0x30) & ~0x10) |
|
|
((geo->oformat&METEOR_GEO_UNSIGNED)?0:0x10));
|
|
break;
|
|
case METEORGETGEO:
|
|
geo = (struct meteor_geomet *) arg;
|
|
geo->rows = mtr->rows;
|
|
geo->columns = mtr->cols;
|
|
geo->frames = mtr->frames;
|
|
geo->oformat = (mtr->flags & METEOR_OUTPUT_FMT_MASK) |
|
|
(mtr->flags & METEOR_ONLY_FIELDS_MASK) |
|
|
(SAA7196_REG(mtr, 0x30) & 0x10 ?
|
|
0:METEOR_GEO_UNSIGNED);
|
|
break;
|
|
case METEORSCOUNT: /* (re)set error counts */
|
|
cnt = (struct meteor_counts *) arg;
|
|
mtr->fifo_errors = cnt->fifo_errors;
|
|
mtr->dma_errors = cnt->dma_errors;
|
|
mtr->frames_captured = cnt->frames_captured;
|
|
mtr->even_fields_captured = cnt->even_fields_captured;
|
|
mtr->odd_fields_captured = cnt->odd_fields_captured;
|
|
break;
|
|
case METEORGCOUNT: /* get error counts */
|
|
cnt = (struct meteor_counts *) arg;
|
|
cnt->fifo_errors = mtr->fifo_errors;
|
|
cnt->dma_errors = mtr->dma_errors;
|
|
cnt->frames_captured = mtr->frames_captured;
|
|
cnt->even_fields_captured = mtr->even_fields_captured;
|
|
cnt->odd_fields_captured = mtr->odd_fields_captured;
|
|
break;
|
|
default:
|
|
printf("meteor%d: ioctl: invalid ioctl request\n", unit);
|
|
error = ENOTTY;
|
|
break;
|
|
}
|
|
return(error);
|
|
}
|
|
|
|
int
|
|
meteor_mmap(dev_t dev, int offset, int nprot)
|
|
{
|
|
|
|
int unit;
|
|
meteor_reg_t *mtr;
|
|
|
|
unit = UNIT(minor(dev));
|
|
if (unit >= NMETEOR) /* at this point could this happen? */
|
|
return(-1);
|
|
|
|
mtr = &(meteor[unit]);
|
|
|
|
|
|
if(nprot & PROT_EXEC)
|
|
return -1;
|
|
|
|
if(offset >= mtr->alloc_pages * PAGE_SIZE)
|
|
return -1;
|
|
|
|
return i386_btop(vtophys(mtr->bigbuf) + offset);
|
|
}
|
|
|
|
|
|
#if !defined(METEOR_FreeBSD_210) /* XXX */
|
|
static meteor_devsw_installed = 0;
|
|
|
|
static void meteor_drvinit(void *unused)
|
|
{
|
|
dev_t dev;
|
|
|
|
if( ! meteor_devsw_installed ) {
|
|
dev = makedev(CDEV_MAJOR, 0);
|
|
cdevsw_add(&dev,&meteor_cdevsw, NULL);
|
|
meteor_devsw_installed = 1;
|
|
}
|
|
}
|
|
|
|
SYSINIT(meteordev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,meteor_drvinit,NULL)
|
|
#endif
|
|
|
|
#endif /* NMETEOR > 0 */
|