Commit e12a2bd0 authored by Gerd Knorr's avatar Gerd Knorr Committed by Linus Torvalds

[PATCH] new v4l2 driver: saa7134

This patch adds a new device driver to the linux kernel.  It is for TV
cards based on the Philips SAA7134 chip.  It supports the v4l2 API and
thus depends on the v4l2 patches of the previous mails.
parent 8354eb8a
......@@ -212,5 +212,17 @@ config VIDEO_MEYE
whenever you want). If you want to compile it as a module, say M
here and read <file:Documentation/modules.txt>.
config VIDEO_SAA7134
tristate "Philips SAA7134 support"
depends on VIDEO_DEV && PCI && I2C
---help---
This is a video4linux driver for Philips SAA7130/7134 based
TV cards.
This driver is available as a module called saa7134.o ( = code
which can be inserted in and removed from the running kernel
whenever you want). If you want to compile it as a module, say M
here and read <file:Documentation/modules.txt>.
endmenu
......@@ -35,6 +35,7 @@ obj-$(CONFIG_VIDEO_CPIA) += cpia.o
obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o
obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o
obj-$(CONFIG_VIDEO_MEYE) += meye.o
obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ tuner.o tda9887.o
obj-$(CONFIG_TUNER_3036) += tuner-3036.o
include $(TOPDIR)/Rules.make
saa7134-objs := saa7134-cards.o saa7134-core.o saa7134-i2c.o \
saa7134-oss.o saa7134-ts.o saa7134-tvaudio.o \
saa7134-vbi.o saa7134-video.o
obj-$(CONFIG_VIDEO_SAA7134) += saa7134.o
EXTRA_CFLAGS = -I$(src)/..
include $(TOPDIR)/Rules.make
/*
* device driver for philips saa7134 based TV cards
* card-specific stuff.
*
* (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
*
* 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 option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/init.h>
#include <linux/module.h>
#include "saa7134-reg.h"
#include "saa7134.h"
#include "tuner.h"
/* commly used strings */
static char name_mute[] = "mute";
static char name_radio[] = "Radio";
static char name_tv[] = "Television";
static char name_comp1[] = "Composite1";
static char name_comp2[] = "Composite2";
static char name_svideo[] = "S-Video";
/* ------------------------------------------------------------------ */
/* board config info */
struct saa7134_board saa7134_boards[] = {
[SAA7134_BOARD_UNKNOWN] = {
name: "UNKNOWN/GENERIC",
audio_clock: 0x00187de7,
tuner_type: TUNER_ABSENT,
inputs: {{
name: "default",
vmux: 0,
amux: LINE1,
}},
},
[SAA7134_BOARD_PROTEUS_PRO] = {
/* /me */
name: "Proteus Pro [philips reference design]",
audio_clock: 0x00187de7,
tuner_type: TUNER_PHILIPS_PAL,
inputs: {{
name: name_comp1,
vmux: 0,
amux: LINE1,
},{
name: name_tv,
vmux: 1,
amux: TV,
tv: 1,
}},
},
[SAA7134_BOARD_FLYVIDEO3000] = {
/* "Marco d'Itri" <md@Linux.IT> */
name: "LifeView FlyVIDEO3000",
audio_clock: 0x00200000,
tuner_type: TUNER_PHILIPS_PAL,
inputs: {{
name: name_tv,
vmux: 1,
amux: TV,
tv: 1,
},{
name: name_comp1,
vmux: 0,
amux: LINE1,
},{
name: name_comp2,
vmux: 3,
amux: LINE1,
},{
name: name_svideo,
vmux: 8,
amux: LINE1,
}},
radio: {
name: name_radio,
amux: LINE2,
},
},
[SAA7134_BOARD_FLYVIDEO2000] = {
/* "TC Wan" <tcwan@cs.usm.my> */
name: "LifeView FlyVIDEO2000",
audio_clock: 0x00200000,
tuner_type: TUNER_LG_PAL_NEW_TAPC,
gpiomask: 0x6000,
inputs: {{
name: name_tv,
vmux: 1,
amux: LINE2,
gpio: 0x0000,
tv: 1,
},{
name: name_comp1,
vmux: 0,
amux: LINE2,
gpio: 0x4000,
},{
name: name_comp2,
vmux: 3,
amux: LINE2,
gpio: 0x4000,
},{
name: name_svideo,
vmux: 8,
amux: LINE2,
gpio: 0x4000,
}},
radio: {
name: name_radio,
amux: LINE2,
},
mute: {
name: name_mute,
amux: LINE1,
},
},
[SAA7134_BOARD_EMPRESS] = {
/* "Gert Vervoort" <gert.vervoort@philips.com> */
name: "EMPRESS",
audio_clock: 0x00187de7,
tuner_type: TUNER_PHILIPS_PAL,
inputs: {{
name: name_comp1,
vmux: 0,
amux: LINE1,
},{
name: name_svideo,
vmux: 8,
amux: LINE1,
},{
name: name_tv,
vmux: 1,
amux: LINE2,
tv: 1,
}},
radio: {
name: name_radio,
amux: LINE2,
},
i2s_rate: 48000,
has_ts: 1,
video_out: CCIR656,
},
[SAA7134_BOARD_MONSTERTV] = {
/* "K.Ohta" <alpha292@bremen.or.jp> */
name: "SKNet Monster TV",
audio_clock: 0x00187de7,
tuner_type: TUNER_PHILIPS_NTSC_M,
inputs: {{
name: name_tv,
vmux: 1,
amux: TV,
tv: 1,
},{
name: name_comp1,
vmux: 0,
amux: LINE1,
},{
name: name_svideo,
vmux: 8,
amux: LINE1,
}},
radio: {
name: name_radio,
amux: LINE2,
},
},
[SAA7134_BOARD_MD9717] = {
name: "Tevion MD 9717",
audio_clock: 0x00200000,
tuner_type: TUNER_PHILIPS_PAL,
inputs: {{
name: name_tv,
vmux: 1,
amux: TV,
tv: 1,
},{
/* workaround for problems with normal TV sound */
name: "TV (mono only)",
vmux: 1,
amux: LINE2,
tv: 1,
},{
name: name_comp1,
vmux: 2,
amux: LINE1,
},{
name: name_comp2,
vmux: 3,
amux: LINE1,
},{
name: name_svideo,
vmux: 8,
amux: LINE1,
}},
radio: {
name: name_radio,
amux: LINE2,
},
},
[SAA7134_BOARD_TVSTATION_RDS] = {
name: "KNC One TV-Station RDS",
audio_clock: 0x00200000,
tuner_type: TUNER_PHILIPS_FM1216ME_MK3,
need_tda9887: 1,
inputs: {{
name: name_tv,
vmux: 1,
amux: TV,
tv: 1,
},{
name: name_comp1,
vmux: 2,
amux: LINE1,
},{
name: name_comp2,
vmux: 3,
amux: LINE1,
}},
radio: {
name: name_radio,
amux: LINE2,
},
},
[SAA7134_BOARD_CINERGY400] = {
name: "Terratec Cinergy 400 TV",
audio_clock: 0x00200000,
tuner_type: TUNER_PHILIPS_PAL,
inputs: {{
name: name_tv,
vmux: 1,
amux: TV,
tv: 1,
},{
name: name_comp1,
vmux: 4,
amux: LINE1,
},{
name: name_svideo,
vmux: 8,
amux: LINE1,
},{
name: name_comp2, // CVideo over SVideo Connector
vmux: 0,
amux: LINE1,
}}
},
[SAA7134_BOARD_MD5044] = {
name: "Medion 5044",
audio_clock: 0x00200000,
tuner_type: TUNER_PHILIPS_FM1216ME_MK3,
need_tda9887: 1,
inputs: {{
name: name_tv,
vmux: 1,
amux: TV,
tv: 1,
},{
name: name_comp1,
vmux: 0,
amux: LINE2,
},{
name: name_comp2,
vmux: 3,
amux: LINE2,
},{
name: name_svideo,
vmux: 8,
amux: LINE2,
}},
radio: {
name: name_radio,
amux: LINE2,
},
},
};
const int saa7134_bcount = (sizeof(saa7134_boards)/sizeof(struct saa7134_board));
/* ------------------------------------------------------------------ */
/* PCI ids + subsystem IDs */
struct pci_device_id __devinitdata saa7134_pci_tbl[] = {
{
vendor: PCI_VENDOR_ID_PHILIPS,
device: PCI_DEVICE_ID_PHILIPS_SAA7134,
subvendor: PCI_VENDOR_ID_PHILIPS,
subdevice: 0x2001,
driver_data: SAA7134_BOARD_PROTEUS_PRO,
},{
vendor: PCI_VENDOR_ID_PHILIPS,
device: PCI_DEVICE_ID_PHILIPS_SAA7134,
subvendor: PCI_VENDOR_ID_PHILIPS,
subdevice: 0x6752,
driver_data: SAA7134_BOARD_EMPRESS,
},{
vendor: PCI_VENDOR_ID_PHILIPS,
device: PCI_DEVICE_ID_PHILIPS_SAA7134,
subvendor: 0x1131,
subdevice: 0x4e85,
driver_data: SAA7134_BOARD_MONSTERTV,
},{
vendor: PCI_VENDOR_ID_PHILIPS,
device: PCI_DEVICE_ID_PHILIPS_SAA7134,
subvendor: 0x153B,
subdevice: 0x1142,
driver_data: SAA7134_BOARD_CINERGY400,
},{
/* --- boards without eeprom + subsystem ID --- */
vendor: PCI_VENDOR_ID_PHILIPS,
device: PCI_DEVICE_ID_PHILIPS_SAA7134,
subvendor: PCI_VENDOR_ID_PHILIPS,
subdevice: 0,
driver_data: SAA7134_BOARD_NOAUTO,
},{
vendor: PCI_VENDOR_ID_PHILIPS,
device: PCI_DEVICE_ID_PHILIPS_SAA7130,
subvendor: PCI_VENDOR_ID_PHILIPS,
subdevice: 0,
driver_data: SAA7134_BOARD_NOAUTO,
},{
/* --- default catch --- */
vendor: PCI_VENDOR_ID_PHILIPS,
device: PCI_DEVICE_ID_PHILIPS_SAA7130,
subvendor: PCI_ANY_ID,
subdevice: PCI_ANY_ID,
driver_data: SAA7134_BOARD_UNKNOWN,
},{
vendor: PCI_VENDOR_ID_PHILIPS,
device: PCI_DEVICE_ID_PHILIPS_SAA7134,
subvendor: PCI_ANY_ID,
subdevice: PCI_ANY_ID,
driver_data: SAA7134_BOARD_UNKNOWN,
},{
/* --- end of list --- */
}
};
MODULE_DEVICE_TABLE(pci, saa7134_pci_tbl);
/* ----------------------------------------------------------- */
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* device driver for philips saa7134 based TV cards
* video4linux video interface
*
* (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
*
* 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 option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define __NO_VERSION__ 1
#include <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include "saa7134-reg.h"
#include "saa7134.h"
/* ------------------------------------------------------------------ */
static unsigned int vbi_debug = 0;
MODULE_PARM(vbi_debug,"i");
MODULE_PARM_DESC(vbi_debug,"enable debug messages [vbi]");
static unsigned int vbibufs = 4;
MODULE_PARM(vbibufs,"i");
MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32");
#define dprintk(fmt, arg...) if (vbi_debug) \
printk(KERN_DEBUG "%s/vbi: " fmt, dev->name, ## arg)
/* ------------------------------------------------------------------ */
#define VBI_LINE_COUNT 16
#define VBI_LINE_LENGTH 2048
#define VBI_SCALE 0x200
static void task_init(struct saa7134_dev *dev, struct saa7134_buf *buf,
int task)
{
struct saa7134_tvnorm *norm = dev->tvnorm;
/* setup video scaler */
saa_writeb(SAA7134_VBI_H_START1(task), norm->h_start & 0xff);
saa_writeb(SAA7134_VBI_H_START2(task), norm->h_start >> 8);
saa_writeb(SAA7134_VBI_H_STOP1(task), norm->h_stop & 0xff);
saa_writeb(SAA7134_VBI_H_STOP2(task), norm->h_stop >> 8);
saa_writeb(SAA7134_VBI_V_START1(task), norm->vbi_v_start & 0xff);
saa_writeb(SAA7134_VBI_V_START2(task), norm->vbi_v_start >> 8);
saa_writeb(SAA7134_VBI_V_STOP1(task), norm->vbi_v_stop & 0xff);
saa_writeb(SAA7134_VBI_V_STOP2(task), norm->vbi_v_stop >> 8);
saa_writeb(SAA7134_VBI_H_SCALE_INC1(task), VBI_SCALE & 0xff);
saa_writeb(SAA7134_VBI_H_SCALE_INC2(task), VBI_SCALE >> 8);
saa_writeb(SAA7134_VBI_PHASE_OFFSET_LUMA(task), 0x00);
saa_writeb(SAA7134_VBI_PHASE_OFFSET_CHROMA(task), 0x00);
saa_writeb(SAA7134_VBI_H_LEN1(task), buf->vb.width & 0xff);
saa_writeb(SAA7134_VBI_H_LEN2(task), buf->vb.width >> 8);
saa_writeb(SAA7134_VBI_V_LEN1(task), buf->vb.height & 0xff);
saa_writeb(SAA7134_VBI_V_LEN2(task), buf->vb.height >> 8);
saa_andorb(SAA7134_DATA_PATH(task), 0xc0, 0x00);
}
/* ------------------------------------------------------------------ */
static int buffer_activate(struct saa7134_dev *dev,
struct saa7134_buf *buf,
struct saa7134_buf *next)
{
unsigned long control,base;
dprintk("buffer_activate [%p]\n",buf);
buf->vb.state = STATE_ACTIVE;
task_init(dev,buf,TASK_A);
task_init(dev,buf,TASK_B);
saa_writeb(SAA7134_OFMT_DATA_A, 0x06);
saa_writeb(SAA7134_OFMT_DATA_B, 0x06);
/* DMA: setup channel 2+3 (= VBI Task A+B) */
base = saa7134_buffer_base(buf);
control = SAA7134_RS_CONTROL_BURST_16 |
SAA7134_RS_CONTROL_ME |
(buf->pt->dma >> 12);
saa_writel(SAA7134_RS_BA1(2),base);
saa_writel(SAA7134_RS_BA2(2),base + buf->vb.size/2);
saa_writel(SAA7134_RS_PITCH(2),buf->vb.width);
saa_writel(SAA7134_RS_CONTROL(2),control);
saa_writel(SAA7134_RS_BA1(3),base);
saa_writel(SAA7134_RS_BA2(3),base + buf->vb.size/2);
saa_writel(SAA7134_RS_PITCH(3),buf->vb.width);
saa_writel(SAA7134_RS_CONTROL(3),control);
/* start DMA */
saa7134_set_dmabits(dev);
mod_timer(&dev->vbi_q.timeout, jiffies+BUFFER_TIMEOUT);
return 0;
}
static int buffer_prepare(struct file *file, struct videobuf_buffer *vb)
{
struct saa7134_fh *fh = file->private_data;
struct saa7134_dev *dev = fh->dev;
struct saa7134_buf *buf = (struct saa7134_buf *)vb;
struct saa7134_tvnorm *norm = dev->tvnorm;
int lines, llength, size, err;
lines = norm->vbi_v_stop - norm->vbi_v_start +1;
if (lines > VBI_LINE_COUNT)
lines = VBI_LINE_COUNT;
#if 1
llength = VBI_LINE_LENGTH;
#else
llength = (norm->h_stop - norm->h_start +1) * 2;
if (llength > VBI_LINE_LENGTH)
llength = VBI_LINE_LENGTH;
#endif
size = lines * llength * 2;
if (0 != buf->vb.baddr && buf->vb.bsize < size)
return -EINVAL;
if (buf->vb.size != size)
saa7134_dma_free(dev,buf);
if (STATE_NEEDS_INIT == buf->vb.state) {
buf->vb.width = llength;
buf->vb.height = lines;
buf->vb.size = size;
buf->pt = &fh->pt_vbi;
err = videobuf_iolock(dev->pci,&buf->vb);
if (err)
goto oops;
err = saa7134_pgtable_build(dev->pci,buf->pt,
buf->vb.dma.sglist,
buf->vb.dma.sglen,
saa7134_buffer_startpage(buf));
if (err)
goto oops;
}
buf->vb.state = STATE_PREPARED;
buf->top_seen = 0;
buf->activate = buffer_activate;
buf->vb.field = V4L2_FIELD_SEQ_TB;
return 0;
oops:
saa7134_dma_free(dev,buf);
return err;
}
static int
buffer_setup(struct file *file, int *count, int *size)
{
struct saa7134_fh *fh = file->private_data;
struct saa7134_dev *dev = fh->dev;
int llength,lines;
lines = dev->tvnorm->vbi_v_stop - dev->tvnorm->vbi_v_start +1;
#if 1
llength = VBI_LINE_LENGTH;
#else
llength = (norm->h_stop - norm->h_start +1) * 2;
if (llength > VBI_LINE_LENGTH)
llength = VBI_LINE_LENGTH;
#endif
*size = lines * llength * 2;
if (0 == *count)
*count = vbibufs;
*count = saa7134_buffer_count(*size,*count);
return 0;
}
static void buffer_queue(struct file *file, struct videobuf_buffer *vb)
{
struct saa7134_fh *fh = file->private_data;
struct saa7134_dev *dev = fh->dev;
struct saa7134_buf *buf = (struct saa7134_buf *)vb;
saa7134_buffer_queue(dev,&dev->vbi_q,buf);
}
static void buffer_release(struct file *file, struct videobuf_buffer *vb)
{
struct saa7134_fh *fh = file->private_data;
struct saa7134_dev *dev = fh->dev;
struct saa7134_buf *buf = (struct saa7134_buf *)vb;
saa7134_dma_free(dev,buf);
}
struct videobuf_queue_ops saa7134_vbi_qops = {
buf_setup: buffer_setup,
buf_prepare: buffer_prepare,
buf_queue: buffer_queue,
buf_release: buffer_release,
};
/* ------------------------------------------------------------------ */
int saa7134_vbi_init(struct saa7134_dev *dev)
{
INIT_LIST_HEAD(&dev->vbi_q.queue);
dev->vbi_q.timeout.function = saa7134_buffer_timeout;
dev->vbi_q.timeout.data = (unsigned long)(&dev->vbi_q);
dev->vbi_q.dev = dev;
if (vbibufs < 2)
vbibufs = 2;
if (vbibufs > VIDEO_MAX_FRAME)
vbibufs = VIDEO_MAX_FRAME;
return 0;
}
int saa7134_vbi_fini(struct saa7134_dev *dev)
{
/* nothing */
return 0;
}
void saa7134_irq_vbi_done(struct saa7134_dev *dev, unsigned long status)
{
spin_lock(&dev->slock);
if (dev->vbi_q.curr) {
dev->vbi_fieldcount++;
/* make sure we have seen both fields */
if ((status & 0x10) == 0x10) {
dev->vbi_q.curr->top_seen = 1;
goto done;
}
if (!dev->vbi_q.curr->top_seen)
goto done;
dev->vbi_q.curr->vb.field_count = dev->vbi_fieldcount;
saa7134_buffer_finish(dev,&dev->vbi_q,STATE_DONE);
}
saa7134_buffer_next(dev,&dev->vbi_q);
done:
spin_unlock(&dev->slock);
}
/* ----------------------------------------------------------- */
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
This diff is collapsed.
This diff is collapsed.
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