1
0
Fork 0

V4L/DVB (5345): ivtv driver for Conexant cx23416/cx23415 MPEG encoder/decoder

It took three core maintainers, over four years of work, eight new i2c
modules, eleven new V4L2 ioctls, three new DVB video ioctls, a Sliced
VBI API, a new MPEG encoder API, an enhanced DVB video MPEG decoding
API, major YUV/OSD contributions from Ian and John, web/wiki/svn/trac
support from Axel Thimm, (hardware) support from Hauppauge, support and
assistance from the v4l-dvb people and the many, many users of ivtv to
finally make it possible to merge this driver into the kernel.
Thank you all!

Signed-off-by: Kevin Thayer <nufan_wfk@yahoo.com>
Signed-off-by: Chris Kennedy <c@groovy.org>
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: John P Harvey <john.p.harvey@btinternet.com>
Signed-off-by: Ian Armstrong <ian@iarmst.demon.co.uk>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
hifive-unleashed-5.1
Hans Verkuil 2007-04-27 12:31:25 -03:00 committed by Mauro Carvalho Chehab
parent ac52ea3c3c
commit 1a0adaf37c
40 changed files with 12637 additions and 0 deletions

View File

@ -647,6 +647,8 @@ config VIDEO_HEXIUM_GEMINI
source "drivers/media/video/cx88/Kconfig"
source "drivers/media/video/ivtv/Kconfig"
config VIDEO_M32R_AR
tristate "AR devices"
depends on M32R && VIDEO_V4L1

View File

@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o
obj-$(CONFIG_VIDEO_MEYE) += meye.o
obj-$(CONFIG_VIDEO_SAA7134) += ir-kbd-i2c.o saa7134/
obj-$(CONFIG_VIDEO_CX88) += cx88/
obj-$(CONFIG_VIDEO_IVTV) += ivtv/
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o

View File

@ -0,0 +1,26 @@
config VIDEO_IVTV
tristate "Conexant cx23416/cx23415 MPEG encoder/decoder support"
depends on VIDEO_V4L2 && USB && I2C && EXPERIMENTAL
select FW_LOADER
select VIDEO_TUNER
select VIDEO_TVEEPROM
select VIDEO_CX2341X
select VIDEO_MSP3400
select VIDEO_SAA711X
select VIDEO_SAA7127
select VIDEO_TVAUDIO
select VIDEO_CS53L32A
select VIDEO_TLV320AIC23B
select VIDEO_WM8775
select VIDEO_WM8739
select VIDEO_UPD64031A
select VIDEO_UPD64083
---help---
This is a video4linux driver for Conexant cx23416 or cx23416 based
PCI personal video recorder devices.
This is used in devices such as the Hauppauge PVR-150/250/350/500
cards.
To compile this driver as a module, choose M here: the
module will be called ivtv.

View File

@ -0,0 +1,7 @@
ivtv-objs := ivtv-audio.o ivtv-cards.o ivtv-controls.o \
ivtv-driver.o ivtv-fileops.o ivtv-firmware.o \
ivtv-gpio.o ivtv-i2c.o ivtv-ioctl.o ivtv-irq.o \
ivtv-mailbox.o ivtv-queue.o ivtv-streams.o ivtv-udma.o \
ivtv-vbi.o ivtv-video.o ivtv-yuv.o
obj-$(CONFIG_VIDEO_IVTV) += ivtv.o

View File

@ -0,0 +1,74 @@
/*
Audio-related ivtv functions.
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ivtv-driver.h"
#include "ivtv-mailbox.h"
#include "ivtv-i2c.h"
#include "ivtv-gpio.h"
#include "ivtv-cards.h"
#include "ivtv-audio.h"
#include <media/msp3400.h>
#include <linux/videodev.h>
/* Selects the audio input and output according to the current
settings. */
int ivtv_audio_set_io(struct ivtv *itv)
{
struct v4l2_routing route;
u32 audio_input;
int mux_input;
/* Determine which input to use */
if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
audio_input = itv->card->radio_input.audio_input;
mux_input = itv->card->radio_input.muxer_input;
} else {
audio_input = itv->card->audio_inputs[itv->audio_input].audio_input;
mux_input = itv->card->audio_inputs[itv->audio_input].muxer_input;
}
/* handle muxer chips */
route.input = mux_input;
route.output = 0;
ivtv_i2c_hw(itv, itv->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route);
route.input = audio_input;
if (itv->card->hw_audio & IVTV_HW_MSP34XX) {
route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
}
return ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, &route);
}
void ivtv_audio_set_route(struct ivtv *itv, struct v4l2_routing *route)
{
ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, route);
}
void ivtv_audio_set_audio_clock_freq(struct ivtv *itv, u8 freq)
{
static u32 freqs[3] = { 44100, 48000, 32000 };
/* The audio clock of the digitizer must match the codec sample
rate otherwise you get some very strange effects. */
if (freq > 2)
return;
ivtv_call_i2c_clients(itv, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[freq]);
}

View File

@ -0,0 +1,23 @@
/*
Audio-related ivtv functions.
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
int ivtv_audio_set_io(struct ivtv *itv);
void ivtv_audio_set_route(struct ivtv *itv, struct v4l2_routing *route);
void ivtv_audio_set_audio_clock_freq(struct ivtv *itv, u8 freq);

View File

@ -0,0 +1,964 @@
/*
Functions to query card hardware
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ivtv-driver.h"
#include "ivtv-cards.h"
#include "ivtv-i2c.h"
#include <media/msp3400.h>
#include <media/wm8775.h>
#include <media/cs53l32a.h>
#include <media/cx25840.h>
#include <media/upd64031a.h>
#define MSP_TUNER MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \
MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER)
#define MSP_SCART1 MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \
MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
#define MSP_SCART2 MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, \
MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
#define MSP_SCART3 MSP_INPUT(MSP_IN_SCART3, MSP_IN_TUNER1, \
MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
#define MSP_MONO MSP_INPUT(MSP_IN_MONO, MSP_IN_TUNER1, \
MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
/********************** card configuration *******************************/
/* Please add new PCI IDs to: http://pci-ids.ucw.cz/iii
This keeps the PCI ID database up to date. Note that the entries
must be added under vendor 0x4444 (Conexant) as subsystem IDs.
New vendor IDs should still be added to the vendor ID list. */
/* Hauppauge PVR-250 cards */
/* Note: for Hauppauge cards the tveeprom information is used instead of PCI IDs */
static const struct ivtv_card ivtv_card_pvr250 = {
.type = IVTV_CARD_PVR_250,
.name = "Hauppauge WinTV PVR-250",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_SAA7115,
.hw_audio = IVTV_HW_MSP34XX,
.hw_audio_ctrl = IVTV_HW_MSP34XX,
.hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 |
IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
{ IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
{ IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 },
{ IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 },
{ IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER },
{ IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 },
{ IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 },
},
.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 },
};
/* ------------------------------------------------------------------------- */
/* Hauppauge PVR-350 cards */
/* Outputs for Hauppauge PVR350 cards */
static struct ivtv_card_output ivtv_pvr350_outputs[] = {
{
.name = "S-Video + Composite",
.video_output = 0,
}, {
.name = "Composite",
.video_output = 1,
}, {
.name = "S-Video",
.video_output = 2,
}, {
.name = "RGB",
.video_output = 3,
}, {
.name = "YUV C",
.video_output = 4,
}, {
.name = "YUV V",
.video_output = 5,
}
};
static const struct ivtv_card ivtv_card_pvr350 = {
.type = IVTV_CARD_PVR_350,
.name = "Hauppauge WinTV PVR-350",
.v4l2_capabilities = IVTV_CAP_ENCODER | IVTV_CAP_DECODER,
.video_outputs = ivtv_pvr350_outputs,
.nof_outputs = ARRAY_SIZE(ivtv_pvr350_outputs),
.hw_video = IVTV_HW_SAA7115,
.hw_audio = IVTV_HW_MSP34XX,
.hw_audio_ctrl = IVTV_HW_MSP34XX,
.hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 |
IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
{ IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
{ IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 },
{ IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 },
{ IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER },
{ IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 },
{ IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 },
},
.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 },
};
/* PVR-350 V1 boards have a different audio tuner input and use a
saa7114 instead of a saa7115.
Note that the info below comes from a pre-production model so it may
not be correct. Especially the audio behaves strangely (mono only it seems) */
static const struct ivtv_card ivtv_card_pvr350_v1 = {
.type = IVTV_CARD_PVR_350_V1,
.name = "Hauppauge WinTV PVR-350 (V1)",
.v4l2_capabilities = IVTV_CAP_ENCODER | IVTV_CAP_DECODER,
.video_outputs = ivtv_pvr350_outputs,
.nof_outputs = ARRAY_SIZE(ivtv_pvr350_outputs),
.hw_video = IVTV_HW_SAA7114,
.hw_audio = IVTV_HW_MSP34XX,
.hw_audio_ctrl = IVTV_HW_MSP34XX,
.hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7114 |
IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
{ IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
{ IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 },
{ IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 },
{ IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER, MSP_MONO },
{ IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 },
{ IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 },
},
.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 },
};
/* ------------------------------------------------------------------------- */
/* Hauppauge PVR-150/PVR-500 cards */
static const struct ivtv_card ivtv_card_pvr150 = {
.type = IVTV_CARD_PVR_150,
.name = "Hauppauge WinTV PVR-150",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_CX25840,
.hw_audio = IVTV_HW_CX25840,
.hw_audio_ctrl = IVTV_HW_CX25840,
.hw_muxer = IVTV_HW_WM8775,
.hw_all = IVTV_HW_WM8775 | IVTV_HW_CX25840 |
IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE7 },
{ IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO1 },
{ IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE3 },
{ IVTV_CARD_INPUT_SVIDEO2, 2, CX25840_SVIDEO2 },
{ IVTV_CARD_INPUT_COMPOSITE2, 2, CX25840_COMPOSITE4 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER,
CX25840_AUDIO8, WM8775_AIN2 },
{ IVTV_CARD_INPUT_LINE_IN1,
CX25840_AUDIO_SERIAL, WM8775_AIN2 },
{ IVTV_CARD_INPUT_LINE_IN2,
CX25840_AUDIO_SERIAL, WM8775_AIN3 },
},
.radio_input = { IVTV_CARD_INPUT_AUD_TUNER,
CX25840_AUDIO_SERIAL, WM8775_AIN4 },
/* apparently needed for the IR blaster */
.gpio_init = { .direction = 0x1f01, .initial_value = 0x26f3 },
};
/* ------------------------------------------------------------------------- */
/* AVerMedia M179 cards */
static const struct ivtv_card_pci_info ivtv_pci_m179[] = {
{ PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_AVERMEDIA, 0xa3cf },
{ PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_AVERMEDIA, 0xa3ce },
{ 0, 0, 0 }
};
static const struct ivtv_card ivtv_card_m179 = {
.type = IVTV_CARD_M179,
.name = "AVerMedia M179",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_SAA7114,
.hw_audio = IVTV_HW_GPIO,
.hw_audio_ctrl = IVTV_HW_GPIO,
.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
{ IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
{ IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
},
.gpio_init = { .direction = 0xe380, .initial_value = 0x8290 },
.gpio_audio_input = { .mask = 0x8040, .tuner = 0x8000, .linein = 0x0000 },
.gpio_audio_mute = { .mask = 0x2000, .mute = 0x2000 },
.gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200,
.lang1 = 0x0200, .lang2 = 0x0100, .both = 0x0000 },
.gpio_audio_freq = { .mask = 0x0018, .f32000 = 0x0000,
.f44100 = 0x0008, .f48000 = 0x0010 },
.gpio_audio_detect = { .mask = 0x4000, .stereo = 0x0000 },
.tuners = {
/* As far as we know all M179 cards use this tuner */
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_NTSC },
},
.pci_list = ivtv_pci_m179,
};
/* ------------------------------------------------------------------------- */
/* Yuan MPG600/Kuroutoshikou ITVC16-STVLP cards */
static const struct ivtv_card_pci_info ivtv_pci_mpg600[] = {
{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0xfff3 },
{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0xffff },
{ 0, 0, 0 }
};
static const struct ivtv_card ivtv_card_mpg600 = {
.type = IVTV_CARD_MPG600,
.name = "Yuan MPG600, Kuroutoshikou ITVC16-STVLP",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_SAA7115,
.hw_audio = IVTV_HW_GPIO,
.hw_audio_ctrl = IVTV_HW_GPIO,
.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
{ IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
{ IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
},
.gpio_init = { .direction = 0x3080, .initial_value = 0x0004 },
.gpio_audio_input = { .mask = 0x3000, .tuner = 0x0000, .linein = 0x2000 },
.gpio_audio_mute = { .mask = 0x0001, .mute = 0x0001 },
.gpio_audio_mode = { .mask = 0x000e, .mono = 0x0006, .stereo = 0x0004,
.lang1 = 0x0004, .lang2 = 0x0000, .both = 0x0008 },
.gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 },
.tuners = {
/* The PAL tuner is confirmed */
{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME },
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
},
.pci_list = ivtv_pci_mpg600,
};
/* ------------------------------------------------------------------------- */
/* Yuan MPG160/Kuroutoshikou ITVC15-STVLP cards */
static const struct ivtv_card_pci_info ivtv_pci_mpg160[] = {
{ PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_YUAN1, 0 },
{ PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_IODATA, 0x40a0 },
{ 0, 0, 0 }
};
static const struct ivtv_card ivtv_card_mpg160 = {
.type = IVTV_CARD_MPG160,
.name = "YUAN MPG160, Kuroutoshikou ITVC15-STVLP, I/O Data GV-M2TV/PCI",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_SAA7114,
.hw_audio = IVTV_HW_GPIO,
.hw_audio_ctrl = IVTV_HW_GPIO,
.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
{ IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
{ IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
},
.gpio_init = { .direction = 0x7080, .initial_value = 0x400c },
.gpio_audio_input = { .mask = 0x3000, .tuner = 0x0000, .linein = 0x2000 },
.gpio_audio_mute = { .mask = 0x0001, .mute = 0x0001 },
.gpio_audio_mode = { .mask = 0x000e, .mono = 0x0006, .stereo = 0x0004,
.lang1 = 0x0004, .lang2 = 0x0000, .both = 0x0008 },
.gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 },
.tuners = {
{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME },
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
},
.pci_list = ivtv_pci_mpg160,
};
/* ------------------------------------------------------------------------- */
/* Yuan PG600/Diamond PVR-550 cards */
static const struct ivtv_card_pci_info ivtv_pci_pg600[] = {
{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_DIAMONDMM, 0x0070 },
{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3, 0x0600 },
{ 0, 0, 0 }
};
static const struct ivtv_card ivtv_card_pg600 = {
.type = IVTV_CARD_PG600,
.name = "Yuan PG600, Diamond PVR-550",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_CX25840,
.hw_audio = IVTV_HW_CX25840,
.hw_audio_ctrl = IVTV_HW_CX25840,
.hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
{ IVTV_CARD_INPUT_SVIDEO1, 1,
CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
{ IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
{ IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL },
},
.tuners = {
{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME },
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
},
.pci_list = ivtv_pci_pg600,
};
/* ------------------------------------------------------------------------- */
/* Adaptec VideOh! AVC-2410 card */
static const struct ivtv_card_pci_info ivtv_pci_avc2410[] = {
{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ADAPTEC, 0x0093 },
{ 0, 0, 0 }
};
static const struct ivtv_card ivtv_card_avc2410 = {
.type = IVTV_CARD_AVC2410,
.name = "Adaptec VideOh! AVC-2410",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_SAA7115,
.hw_audio = IVTV_HW_MSP34XX,
.hw_audio_ctrl = IVTV_HW_MSP34XX,
.hw_muxer = IVTV_HW_CS53L32A,
.hw_all = IVTV_HW_MSP34XX | IVTV_HW_CS53L32A |
IVTV_HW_SAA7115 | IVTV_HW_TUNER,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
{ IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER,
MSP_TUNER, CS53L32A_IN0 },
{ IVTV_CARD_INPUT_LINE_IN1,
MSP_SCART1, CS53L32A_IN2 },
},
/* This card has no eeprom and in fact the Windows driver relies
on the country/region setting of the user to decide which tuner
is available. */
.tuners = {
/* This tuner has been verified for the AVC2410 */
{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
/* This is a good guess, but I'm not totally sure this is
the correct tuner for NTSC. */
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
},
.pci_list = ivtv_pci_avc2410,
};
/* ------------------------------------------------------------------------- */
/* Adaptec VideOh! AVC-2010 card */
static const struct ivtv_card_pci_info ivtv_pci_avc2010[] = {
{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ADAPTEC, 0x0092 },
{ 0, 0, 0 }
};
static const struct ivtv_card ivtv_card_avc2010 = {
.type = IVTV_CARD_AVC2010,
.name = "Adaptec VideOh! AVC-2010",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_SAA7115,
.hw_audio = IVTV_HW_CS53L32A,
.hw_audio_ctrl = IVTV_HW_CS53L32A,
.hw_all = IVTV_HW_CS53L32A | IVTV_HW_SAA7115,
.video_inputs = {
{ IVTV_CARD_INPUT_SVIDEO1, 0, IVTV_SAA71XX_SVIDEO0 },
{ IVTV_CARD_INPUT_COMPOSITE1, 0, IVTV_SAA71XX_COMPOSITE3 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_LINE_IN1, CS53L32A_IN2 },
},
/* Does not have a tuner */
.pci_list = ivtv_pci_avc2010,
};
/* ------------------------------------------------------------------------- */
/* Nagase Transgear 5000TV card */
static const struct ivtv_card_pci_info ivtv_pci_tg5000tv[] = {
{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xbfff },
{ 0, 0, 0 }
};
static const struct ivtv_card ivtv_card_tg5000tv = {
.type = IVTV_CARD_TG5000TV,
.name = "Nagase Transgear 5000TV",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_SAA7114 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X |
IVTV_HW_GPIO,
.hw_audio = IVTV_HW_GPIO,
.hw_audio_ctrl = IVTV_HW_GPIO,
.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER |
IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 },
{ IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO2 },
{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
{ IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
},
.gr_config = UPD64031A_VERTICAL_EXTERNAL,
.gpio_init = { .direction = 0xe080, .initial_value = 0x8000 },
.gpio_audio_input = { .mask = 0x8080, .tuner = 0x8000, .linein = 0x0080 },
.gpio_audio_mute = { .mask = 0x6000, .mute = 0x6000 },
.gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200,
.lang1 = 0x0300, .lang2 = 0x0000, .both = 0x0200 },
.gpio_video_input = { .mask = 0x0030, .tuner = 0x0000,
.composite = 0x0010, .svideo = 0x0020 },
.tuners = {
{ .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
},
.pci_list = ivtv_pci_tg5000tv,
};
/* ------------------------------------------------------------------------- */
/* AOpen VA2000MAX-SNT6 card */
static const struct ivtv_card_pci_info ivtv_pci_va2000[] = {
{ PCI_DEVICE_ID_IVTV16, 0, 0xff5f },
{ 0, 0, 0 }
};
static const struct ivtv_card ivtv_card_va2000 = {
.type = IVTV_CARD_VA2000MAX_SNT6,
.name = "AOpen VA2000MAX-SNT6",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD6408X,
.hw_audio = IVTV_HW_MSP34XX,
.hw_audio_ctrl = IVTV_HW_MSP34XX,
.hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 |
IVTV_HW_UPD6408X | IVTV_HW_TUNER,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER },
},
.tuners = {
{ .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
},
.pci_list = ivtv_pci_va2000,
};
/* ------------------------------------------------------------------------- */
/* Yuan MPG600GR/Kuroutoshikou CX23416GYC-STVLP cards */
static const struct ivtv_card_pci_info ivtv_pci_cx23416gyc[] = {
{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0x0600 },
{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN4, 0x0600 },
{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_MELCO, 0x0523 },
{ 0, 0, 0 }
};
static const struct ivtv_card ivtv_card_cx23416gyc = {
.type = IVTV_CARD_CX23416GYC,
.name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO |
IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
.hw_audio = IVTV_HW_SAA717X,
.hw_audio_ctrl = IVTV_HW_SAA717X,
.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER |
IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO3 |
IVTV_SAA717X_TUNER_FLAG },
{ IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO3 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 },
{ IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 },
},
.gr_config = UPD64031A_VERTICAL_EXTERNAL,
.gpio_init = { .direction = 0xf880, .initial_value = 0x8800 },
.gpio_video_input = { .mask = 0x0020, .tuner = 0x0000,
.composite = 0x0020, .svideo = 0x0020 },
.gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000,
.f44100 = 0x4000, .f48000 = 0x8000 },
.tuners = {
{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
},
.pci_list = ivtv_pci_cx23416gyc,
};
static const struct ivtv_card ivtv_card_cx23416gyc_nogr = {
.type = IVTV_CARD_CX23416GYC_NOGR,
.name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP (no GR)",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO | IVTV_HW_UPD6408X,
.hw_audio = IVTV_HW_SAA717X,
.hw_audio_ctrl = IVTV_HW_SAA717X,
.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER |
IVTV_HW_UPD6408X,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 |
IVTV_SAA717X_TUNER_FLAG },
{ IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 },
{ IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 },
},
.gpio_init = { .direction = 0xf880, .initial_value = 0x8800 },
.gpio_video_input = { .mask = 0x0020, .tuner = 0x0000,
.composite = 0x0020, .svideo = 0x0020 },
.gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000,
.f44100 = 0x4000, .f48000 = 0x8000 },
.tuners = {
{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
},
};
static const struct ivtv_card ivtv_card_cx23416gyc_nogrycs = {
.type = IVTV_CARD_CX23416GYC_NOGRYCS,
.name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP (no GR/YCS)",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO,
.hw_audio = IVTV_HW_SAA717X,
.hw_audio_ctrl = IVTV_HW_SAA717X,
.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 |
IVTV_SAA717X_TUNER_FLAG },
{ IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 },
{ IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 },
},
.gpio_init = { .direction = 0xf880, .initial_value = 0x8800 },
.gpio_video_input = { .mask = 0x0020, .tuner = 0x0000,
.composite = 0x0020, .svideo = 0x0020 },
.gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000,
.f44100 = 0x4000, .f48000 = 0x8000 },
.tuners = {
{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
},
};
/* ------------------------------------------------------------------------- */
/* I/O Data GV-MVP/RX & GV-MVP/RX2W (dual tuner) cards */
static const struct ivtv_card_pci_info ivtv_pci_gv_mvprx[] = {
{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd01e },
{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd038 }, /* 2W unit #1 */
{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd039 }, /* 2W unit #2 */
{ 0, 0, 0 }
};
static const struct ivtv_card ivtv_card_gv_mvprx = {
.type = IVTV_CARD_GV_MVPRX,
.name = "I/O Data GV-MVP/RX, GV-MVP/RX2W (dual tuner)",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
.hw_audio = IVTV_HW_GPIO,
.hw_audio_ctrl = IVTV_HW_WM8739,
.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TVAUDIO |
IVTV_HW_TUNER | IVTV_HW_WM8739 |
IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 },
{ IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO1 },
{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
{ IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
},
.gpio_init = { .direction = 0xc301, .initial_value = 0x0200 },
.gpio_audio_input = { .mask = 0xffff, .tuner = 0x0200, .linein = 0x0300 },
.tuners = {
/* This card has the Panasonic VP27 tuner */
{ .std = V4L2_STD_525_60, .tuner = TUNER_PANASONIC_VP27 },
},
.pci_list = ivtv_pci_gv_mvprx,
};
/* ------------------------------------------------------------------------- */
/* I/O Data GV-MVP/RX2E card */
static const struct ivtv_card_pci_info ivtv_pci_gv_mvprx2e[] = {
{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd025 },
{0, 0, 0}
};
static const struct ivtv_card ivtv_card_gv_mvprx2e = {
.type = IVTV_CARD_GV_MVPRX2E,
.name = "I/O Data GV-MVP/RX2E",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_SAA7115,
.hw_audio = IVTV_HW_GPIO,
.hw_audio_ctrl = IVTV_HW_WM8739,
.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER |
IVTV_HW_TVAUDIO | IVTV_HW_WM8739,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
{ IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
{ IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
},
.gpio_init = { .direction = 0xc301, .initial_value = 0x0200 },
.gpio_audio_input = { .mask = 0xffff, .tuner = 0x0200, .linein = 0x0300 },
.tuners = {
/* This card has the Panasonic VP27 tuner */
{ .std = V4L2_STD_525_60, .tuner = TUNER_PANASONIC_VP27 },
},
.pci_list = ivtv_pci_gv_mvprx2e,
};
/* ------------------------------------------------------------------------- */
/* GotVIEW PCI DVD card */
static const struct ivtv_card_pci_info ivtv_pci_gotview_pci_dvd[] = {
{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0x0600 },
{ 0, 0, 0 }
};
static const struct ivtv_card ivtv_card_gotview_pci_dvd = {
.type = IVTV_CARD_GOTVIEW_PCI_DVD,
.name = "GotView PCI DVD",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_SAA717X,
.hw_audio = IVTV_HW_SAA717X,
.hw_audio_ctrl = IVTV_HW_SAA717X,
.hw_all = IVTV_HW_SAA717X | IVTV_HW_TUNER,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE1 }, /* pin 116 */
{ IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, /* pin 114/109 */
{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, /* pin 118 */
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN0 },
{ IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN2 },
},
.gpio_init = { .direction = 0xf000, .initial_value = 0xA000 },
.tuners = {
/* This card has a Philips FQ1216ME MK3 tuner */
{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
},
.pci_list = ivtv_pci_gotview_pci_dvd,
};
/* ------------------------------------------------------------------------- */
/* GotVIEW PCI DVD2 Deluxe card */
static const struct ivtv_card_pci_info ivtv_pci_gotview_pci_dvd2[] = {
{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_GOTVIEW1, 0x0600 },
{ 0, 0, 0 }
};
static const struct ivtv_card ivtv_card_gotview_pci_dvd2 = {
.type = IVTV_CARD_GOTVIEW_PCI_DVD2,
.name = "GotView PCI DVD2 Deluxe",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_CX25840,
.hw_audio = IVTV_HW_CX25840,
.hw_audio_ctrl = IVTV_HW_CX25840,
.hw_muxer = IVTV_HW_GPIO,
.hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
{ IVTV_CARD_INPUT_SVIDEO1, 1,
CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
{ IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5, 0 },
{ IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 },
},
.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 },
.gpio_init = { .direction = 0x0800, .initial_value = 0 },
.gpio_audio_input = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 },
.tuners = {
/* This card has a Philips FQ1216ME MK5 tuner */
{ .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
},
.pci_list = ivtv_pci_gotview_pci_dvd2,
};
/* ------------------------------------------------------------------------- */
/* Yuan MPC622 miniPCI card */
static const struct ivtv_card_pci_info ivtv_pci_yuan_mpc622[] = {
{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN2, 0xd998 },
{ 0, 0, 0 }
};
static const struct ivtv_card ivtv_card_yuan_mpc622 = {
.type = IVTV_CARD_YUAN_MPC622,
.name = "Yuan MPC622",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_CX25840,
.hw_audio = IVTV_HW_CX25840,
.hw_audio_ctrl = IVTV_HW_CX25840,
.hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
{ IVTV_CARD_INPUT_SVIDEO1, 1,
CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
{ IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
{ IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL },
},
.gpio_init = { .direction = 0x00ff, .initial_value = 0x0002 },
.tuners = {
/* This card has the TDA8290/TDA8275 tuner chips */
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_TDA8290 },
},
.pci_list = ivtv_pci_yuan_mpc622,
};
/* ------------------------------------------------------------------------- */
/* DIGITAL COWBOY DCT-MTVP1 card */
static const struct ivtv_card_pci_info ivtv_pci_dctmvtvp1[] = {
{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xbfff },
{ 0, 0, 0 }
};
static const struct ivtv_card ivtv_card_dctmvtvp1 = {
.type = IVTV_CARD_DCTMTVP1,
.name = "Digital Cowboy DCT-MTVP1",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X |
IVTV_HW_GPIO,
.hw_audio = IVTV_HW_GPIO,
.hw_audio_ctrl = IVTV_HW_GPIO,
.hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER |
IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 },
{ IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO2 },
{ IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
{ IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
},
.gpio_init = { .direction = 0xe080, .initial_value = 0x8000 },
.gpio_audio_input = { .mask = 0x8080, .tuner = 0x8000, .linein = 0x0080 },
.gpio_audio_mute = { .mask = 0x6000, .mute = 0x6000 },
.gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200,
.lang1 = 0x0300, .lang2 = 0x0000, .both = 0x0200 },
.gpio_video_input = { .mask = 0x0030, .tuner = 0x0000,
.composite = 0x0010, .svideo = 0x0020},
.tuners = {
{ .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
},
.pci_list = ivtv_pci_dctmvtvp1,
};
/* ------------------------------------------------------------------------- */
#ifdef HAVE_XC3028
/* Yuan PG600-2/GotView PCI DVD Lite/Club3D ZAP-TV1x01 cards */
static const struct ivtv_card_pci_info ivtv_pci_pg600v2[] = {
{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3, 0x0600 },
{ PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_GOTVIEW2, 0x0600 },
{ 0, 0, 0 }
};
static const struct ivtv_card ivtv_card_pg600v2 = {
.type = IVTV_CARD_PG600V2,
.name = "Yuan PG600-2, GotView PCI DVD Lite, Club3D ZAP-TV1x01",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_CX25840,
.hw_audio = IVTV_HW_CX25840,
.hw_audio_ctrl = IVTV_HW_CX25840,
.hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
{ IVTV_CARD_INPUT_SVIDEO1, 1,
CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
},
.audio_inputs = {
{ IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
{ IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL },
},
.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
.tuners = {
{ .std = V4L2_STD_ALL, .tuner = TUNER_XCEIVE_XC3028 },
},
.gpio_init = { .direction = 0x1000, .initial_value = 0x1000 }, /* tuner reset */
.pci_list = ivtv_pci_pg600v2,
};
#endif
static const struct ivtv_card *ivtv_card_list[] = {
&ivtv_card_pvr250,
&ivtv_card_pvr350,
&ivtv_card_pvr150,
&ivtv_card_m179,
&ivtv_card_mpg600,
&ivtv_card_mpg160,
&ivtv_card_pg600,
&ivtv_card_avc2410,
&ivtv_card_avc2010,
&ivtv_card_tg5000tv,
&ivtv_card_va2000,
&ivtv_card_cx23416gyc,
&ivtv_card_gv_mvprx,
&ivtv_card_gv_mvprx2e,
&ivtv_card_gotview_pci_dvd,
&ivtv_card_gotview_pci_dvd2,
&ivtv_card_yuan_mpc622,
&ivtv_card_dctmvtvp1,
#ifdef HAVE_XC3028
&ivtv_card_pg600v2,
#endif
/* Variations of standard cards but with the same PCI IDs.
These cards must come last in this list. */
&ivtv_card_pvr350_v1,
&ivtv_card_cx23416gyc_nogr,
&ivtv_card_cx23416gyc_nogrycs,
};
const struct ivtv_card *ivtv_get_card(u16 index)
{
if (index >= ARRAY_SIZE(ivtv_card_list))
return NULL;
return ivtv_card_list[index];
}
int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input)
{
const struct ivtv_card_video_input *card_input = itv->card->video_inputs + index;
static const char * const input_strs[] = {
"Tuner 1",
"S-Video 1",
"S-Video 2",
"Composite 1",
"Composite 2",
"Composite 3"
};
memset(input, 0, sizeof(*input));
if (index >= itv->nof_inputs)
return -EINVAL;
input->index = index;
strcpy(input->name, input_strs[card_input->video_type - 1]);
input->type = (card_input->video_type == IVTV_CARD_INPUT_VID_TUNER ?
V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA);
input->audioset = (1 << itv->nof_audio_inputs) - 1;
input->std = (input->type == V4L2_INPUT_TYPE_TUNER) ?
itv->tuner_std : V4L2_STD_ALL;
return 0;
}
int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output)
{
const struct ivtv_card_output *card_output = itv->card->video_outputs + index;
memset(output, 0, sizeof(*output));
if (index >= itv->card->nof_outputs)
return -EINVAL;
output->index = index;
strcpy(output->name, card_output->name);
output->type = V4L2_OUTPUT_TYPE_ANALOG;
output->audioset = 1;
output->std = V4L2_STD_ALL;
return 0;
}
int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *audio)
{
const struct ivtv_card_audio_input *aud_input = itv->card->audio_inputs + index;
static const char * const input_strs[] = {
"Tuner 1",
"Line In 1",
"Line In 2"
};
memset(audio, 0, sizeof(*audio));
if (index >= itv->nof_audio_inputs)
return -EINVAL;
strcpy(audio->name, input_strs[aud_input->audio_type - 1]);
audio->index = index;
audio->capability = V4L2_AUDCAP_STEREO;
return 0;
}
int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *aud_output)
{
memset(aud_output, 0, sizeof(*aud_output));
if (itv->card->video_outputs == NULL || index != 0)
return -EINVAL;
strcpy(aud_output->name, "A/V Audio Out");
return 0;
}

View File

@ -0,0 +1,207 @@
/*
Functions to query card hardware
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* hardware flags */
#define IVTV_HW_CX25840 (1 << 0)
#define IVTV_HW_SAA7115 (1 << 1)
#define IVTV_HW_SAA7127 (1 << 2)
#define IVTV_HW_MSP34XX (1 << 3)
#define IVTV_HW_TUNER (1 << 4)
#define IVTV_HW_WM8775 (1 << 5)
#define IVTV_HW_CS53L32A (1 << 6)
#define IVTV_HW_TVEEPROM (1 << 7)
#define IVTV_HW_SAA7114 (1 << 8)
#define IVTV_HW_TVAUDIO (1 << 9)
#define IVTV_HW_UPD64031A (1 << 10)
#define IVTV_HW_UPD6408X (1 << 11)
#define IVTV_HW_SAA717X (1 << 12)
#define IVTV_HW_WM8739 (1 << 13)
#define IVTV_HW_GPIO (1 << 14)
#define IVTV_HW_SAA711X (IVTV_HW_SAA7115 | IVTV_HW_SAA7114)
/* video inputs */
#define IVTV_CARD_INPUT_VID_TUNER 1
#define IVTV_CARD_INPUT_SVIDEO1 2
#define IVTV_CARD_INPUT_SVIDEO2 3
#define IVTV_CARD_INPUT_COMPOSITE1 4
#define IVTV_CARD_INPUT_COMPOSITE2 5
#define IVTV_CARD_INPUT_COMPOSITE3 6
/* audio inputs */
#define IVTV_CARD_INPUT_AUD_TUNER 1
#define IVTV_CARD_INPUT_LINE_IN1 2
#define IVTV_CARD_INPUT_LINE_IN2 3
#define IVTV_CARD_MAX_VIDEO_INPUTS 6
#define IVTV_CARD_MAX_AUDIO_INPUTS 3
#define IVTV_CARD_MAX_TUNERS 2
/* SAA71XX HW inputs */
#define IVTV_SAA71XX_COMPOSITE0 0
#define IVTV_SAA71XX_COMPOSITE1 1
#define IVTV_SAA71XX_COMPOSITE2 2
#define IVTV_SAA71XX_COMPOSITE3 3
#define IVTV_SAA71XX_COMPOSITE4 4
#define IVTV_SAA71XX_COMPOSITE5 5
#define IVTV_SAA71XX_SVIDEO0 6
#define IVTV_SAA71XX_SVIDEO1 7
#define IVTV_SAA71XX_SVIDEO2 8
#define IVTV_SAA71XX_SVIDEO3 9
/* SAA717X needs to mark the tuner input by ORing with this flag */
#define IVTV_SAA717X_TUNER_FLAG 0x80
/* Dummy HW input */
#define IVTV_DUMMY_AUDIO 0
/* GPIO HW inputs */
#define IVTV_GPIO_TUNER 0
#define IVTV_GPIO_LINE_IN 1
/* SAA717X HW inputs */
#define IVTV_SAA717X_IN0 0
#define IVTV_SAA717X_IN1 1
#define IVTV_SAA717X_IN2 2
/* V4L2 capability aliases */
#define IVTV_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \
V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE | \
V4L2_CAP_SLICED_VBI_CAPTURE)
#define IVTV_CAP_DECODER (V4L2_CAP_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT | \
V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_OVERLAY | V4L2_CAP_VIDEO_OUTPUT_POS)
struct ivtv_card_video_input {
u8 video_type; /* video input type */
u8 audio_index; /* index in ivtv_card_audio_input array */
u16 video_input; /* hardware video input */
};
struct ivtv_card_audio_input {
u8 audio_type; /* audio input type */
u32 audio_input; /* hardware audio input */
u16 muxer_input; /* hardware muxer input for boards with a
multiplexer chip */
};
struct ivtv_card_output {
u8 name[32];
u16 video_output; /* hardware video output */
};
struct ivtv_card_pci_info {
u16 device;
u16 subsystem_vendor;
u16 subsystem_device;
};
/* GPIO definitions */
/* The mask is the set of bits used by the operation */
struct ivtv_gpio_init { /* set initial GPIO DIR and OUT values */
u16 direction; /* DIR setting. Leave to 0 if no init is needed */
u16 initial_value;
};
struct ivtv_gpio_video_input { /* select tuner/line in input */
u16 mask; /* leave to 0 if not supported */
u16 tuner;
u16 composite;
u16 svideo;
};
struct ivtv_gpio_audio_input { /* select tuner/line in input */
u16 mask; /* leave to 0 if not supported */
u16 tuner;
u16 linein;
u16 radio;
};
struct ivtv_gpio_audio_mute {
u16 mask; /* leave to 0 if not supported */
u16 mute; /* set this value to mute, 0 to unmute */
};
struct ivtv_gpio_audio_mode {
u16 mask; /* leave to 0 if not supported */
u16 mono; /* set audio to mono */
u16 stereo; /* set audio to stereo */
u16 lang1; /* set audio to the first language */
u16 lang2; /* set audio to the second language */
u16 both; /* both languages are output */
};
struct ivtv_gpio_audio_freq {
u16 mask; /* leave to 0 if not supported */
u16 f32000;
u16 f44100;
u16 f48000;
};
struct ivtv_gpio_audio_detect {
u16 mask; /* leave to 0 if not supported */
u16 stereo; /* if the input matches this value then
stereo is detected */
};
struct ivtv_card_tuner {
v4l2_std_id std; /* standard for which the tuner is suitable */
int tuner; /* tuner ID (from tuner.h) */
};
/* for card information/parameters */
struct ivtv_card {
int type;
char *name;
u32 v4l2_capabilities;
u32 hw_video; /* hardware used to process video */
u32 hw_audio; /* hardware used to process audio */
u32 hw_audio_ctrl; /* hardware used for the V4L2 controls (only 1 dev allowed) */
u32 hw_muxer; /* hardware used to multiplex audio input */
u32 hw_all; /* all hardware used by the board */
struct ivtv_card_video_input video_inputs[IVTV_CARD_MAX_VIDEO_INPUTS];
struct ivtv_card_audio_input audio_inputs[IVTV_CARD_MAX_AUDIO_INPUTS];
struct ivtv_card_audio_input radio_input;
int nof_outputs;
const struct ivtv_card_output *video_outputs;
u8 gr_config; /* config byte for the ghost reduction device */
/* GPIO card-specific settings */
struct ivtv_gpio_init gpio_init;
struct ivtv_gpio_video_input gpio_video_input;
struct ivtv_gpio_audio_input gpio_audio_input;
struct ivtv_gpio_audio_mute gpio_audio_mute;
struct ivtv_gpio_audio_mode gpio_audio_mode;
struct ivtv_gpio_audio_freq gpio_audio_freq;
struct ivtv_gpio_audio_detect gpio_audio_detect;
struct ivtv_card_tuner tuners[IVTV_CARD_MAX_TUNERS];
/* list of device and subsystem vendor/devices that
correspond to this card type. */
const struct ivtv_card_pci_info *pci_list;
};
int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input);
int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output);
int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *input);
int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *output);
const struct ivtv_card *ivtv_get_card(u16 index);

View File

@ -0,0 +1,303 @@
/*
ioctl control functions
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ivtv-driver.h"
#include "ivtv-cards.h"
#include "ivtv-ioctl.h"
#include "ivtv-audio.h"
#include "ivtv-i2c.h"
#include "ivtv-mailbox.h"
#include "ivtv-controls.h"
static const u32 user_ctrls[] = {
V4L2_CID_USER_CLASS,
V4L2_CID_BRIGHTNESS,
V4L2_CID_CONTRAST,
V4L2_CID_SATURATION,
V4L2_CID_HUE,
V4L2_CID_AUDIO_VOLUME,
V4L2_CID_AUDIO_BALANCE,
V4L2_CID_AUDIO_BASS,
V4L2_CID_AUDIO_TREBLE,
V4L2_CID_AUDIO_MUTE,
V4L2_CID_AUDIO_LOUDNESS,
0
};
static const u32 *ctrl_classes[] = {
user_ctrls,
cx2341x_mpeg_ctrls,
NULL
};
static int ivtv_queryctrl(struct ivtv *itv, struct v4l2_queryctrl *qctrl)
{
const char *name;
IVTV_DEBUG_IOCTL("VIDIOC_QUERYCTRL(%08x)\n", qctrl->id);
qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
if (qctrl->id == 0)
return -EINVAL;
switch (qctrl->id) {
/* Standard V4L2 controls */
case V4L2_CID_BRIGHTNESS:
case V4L2_CID_HUE:
case V4L2_CID_SATURATION:
case V4L2_CID_CONTRAST:
if (itv->video_dec_func(itv, VIDIOC_QUERYCTRL, qctrl))
qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
return 0;
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_MUTE:
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_LOUDNESS:
if (ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl))
qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
return 0;
default:
if (cx2341x_ctrl_query(&itv->params, qctrl))
qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
return 0;
}
strncpy(qctrl->name, name, sizeof(qctrl->name) - 1);
qctrl->name[sizeof(qctrl->name) - 1] = 0;
return 0;
}
static int ivtv_querymenu(struct ivtv *itv, struct v4l2_querymenu *qmenu)
{
struct v4l2_queryctrl qctrl;
qctrl.id = qmenu->id;
ivtv_queryctrl(itv, &qctrl);
return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id));
}
static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
{
s32 v = vctrl->value;
IVTV_DEBUG_IOCTL("VIDIOC_S_CTRL(%08x, %x)\n", vctrl->id, v);
switch (vctrl->id) {
/* Standard V4L2 controls */
case V4L2_CID_BRIGHTNESS:
case V4L2_CID_HUE:
case V4L2_CID_SATURATION:
case V4L2_CID_CONTRAST:
return itv->video_dec_func(itv, VIDIOC_S_CTRL, vctrl);
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_MUTE:
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_LOUDNESS:
return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl);
default:
IVTV_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
return -EINVAL;
}
return 0;
}
static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
{
IVTV_DEBUG_IOCTL("VIDIOC_G_CTRL(%08x)\n", vctrl->id);
switch (vctrl->id) {
/* Standard V4L2 controls */
case V4L2_CID_BRIGHTNESS:
case V4L2_CID_HUE:
case V4L2_CID_SATURATION:
case V4L2_CID_CONTRAST:
return itv->video_dec_func(itv, VIDIOC_G_CTRL, vctrl);
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_MUTE:
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_LOUDNESS:
return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl);
default:
IVTV_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
return -EINVAL;
}
return 0;
}
static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fmt)
{
if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE))
return -EINVAL;
if (atomic_read(&itv->capturing) > 0)
return -EBUSY;
/* First try to allocate sliced VBI buffers if needed. */
if (fmt && itv->vbi.sliced_mpeg_data[0] == NULL) {
int i;
for (i = 0; i < IVTV_VBI_FRAMES; i++) {
/* Yuck, hardcoded. Needs to be a define */
itv->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL);
if (itv->vbi.sliced_mpeg_data[i] == NULL) {
while (--i >= 0) {
kfree(itv->vbi.sliced_mpeg_data[i]);
itv->vbi.sliced_mpeg_data[i] = NULL;
}
return -ENOMEM;
}
}
}
itv->vbi.insert_mpeg = fmt;
if (itv->vbi.insert_mpeg == 0) {
return 0;
}
/* Need sliced data for mpeg insertion */
if (get_service_set(itv->vbi.sliced_in) == 0) {
if (itv->is_60hz)
itv->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525;
else
itv->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625;
expand_service_set(itv->vbi.sliced_in, itv->is_50hz);
}
return 0;
}
int ivtv_control_ioctls(struct ivtv *itv, unsigned int cmd, void *arg)
{
struct v4l2_control ctrl;
switch (cmd) {
case VIDIOC_QUERYMENU:
IVTV_DEBUG_IOCTL("VIDIOC_QUERYMENU\n");
return ivtv_querymenu(itv, arg);
case VIDIOC_QUERYCTRL:
return ivtv_queryctrl(itv, arg);
case VIDIOC_S_CTRL:
return ivtv_s_ctrl(itv, arg);
case VIDIOC_G_CTRL:
return ivtv_g_ctrl(itv, arg);
case VIDIOC_S_EXT_CTRLS:
{
struct v4l2_ext_controls *c = arg;
if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
int i;
int err = 0;
for (i = 0; i < c->count; i++) {
ctrl.id = c->controls[i].id;
ctrl.value = c->controls[i].value;
err = ivtv_s_ctrl(itv, &ctrl);
c->controls[i].value = ctrl.value;
if (err) {
c->error_idx = i;
break;
}
}
return err;
}
IVTV_DEBUG_IOCTL("VIDIOC_S_EXT_CTRLS\n");
if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
struct cx2341x_mpeg_params p = itv->params;
int err = cx2341x_ext_ctrls(&p, arg, cmd);
if (err)
return err;
if (p.video_encoding != itv->params.video_encoding) {
int is_mpeg1 = p.video_encoding ==
V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
struct v4l2_format fmt;
/* fix videodecoder resolution */
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = itv->params.width / (is_mpeg1 ? 2 : 1);
fmt.fmt.pix.height = itv->params.height;
itv->video_dec_func(itv, VIDIOC_S_FMT, &fmt);
}
err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p);
if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt) {
err = ivtv_setup_vbi_fmt(itv, p.stream_vbi_fmt);
}
itv->params = p;
itv->dualwatch_stereo_mode = p.audio_properties & 0x0300;
ivtv_audio_set_audio_clock_freq(itv, p.audio_properties & 0x03);
return err;
}
return -EINVAL;
}
case VIDIOC_G_EXT_CTRLS:
{
struct v4l2_ext_controls *c = arg;
if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
int i;
int err = 0;
for (i = 0; i < c->count; i++) {
ctrl.id = c->controls[i].id;
ctrl.value = c->controls[i].value;
err = ivtv_g_ctrl(itv, &ctrl);
c->controls[i].value = ctrl.value;
if (err) {
c->error_idx = i;
break;
}
}
return err;
}
IVTV_DEBUG_IOCTL("VIDIOC_G_EXT_CTRLS\n");
if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
return cx2341x_ext_ctrls(&itv->params, arg, cmd);
return -EINVAL;
}
case VIDIOC_TRY_EXT_CTRLS:
{
struct v4l2_ext_controls *c = arg;
IVTV_DEBUG_IOCTL("VIDIOC_TRY_EXT_CTRLS\n");
if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
return cx2341x_ext_ctrls(&itv->params, arg, cmd);
return -EINVAL;
}
default:
return -EINVAL;
}
return 0;
}

View File

@ -0,0 +1,21 @@
/*
ioctl control functions
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
int ivtv_control_ioctls(struct ivtv *itv, unsigned int cmd, void *arg);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,866 @@
/*
ivtv driver internal defines and structures
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2004 Chris Kennedy <c@groovy.org>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef IVTV_DRIVER_H
#define IVTV_DRIVER_H
/* Internal header for ivtv project:
* Driver for the cx23415/6 chip.
* Author: Kevin Thayer (nufan_wfk at yahoo.com)
* License: GPL
* http://www.ivtvdriver.org
*
* -----
* MPG600/MPG160 support by T.Adachi <tadachi@tadachi-net.com>
* and Takeru KOMORIYA<komoriya@paken.org>
*
* AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
* using information provided by Jiun-Kuei Jung @ AVerMedia.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/list.h>
#include <linux/unistd.h>
#include <linux/byteorder/swab.h>
#include <linux/pagemap.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/dvb/video.h>
#include <linux/dvb/audio.h>
#include <media/v4l2-common.h>
#include <media/tuner.h>
#include <media/cx2341x.h>
/* #define HAVE_XC3028 1 */
#include <media/ivtv.h>
#ifdef CONFIG_LIRC_I2C
# error "This driver is not compatible with the LIRC I2C kernel configuration option."
#endif /* CONFIG_LIRC_I2C */
#ifndef CONFIG_PCI
# error "This driver requires kernel PCI support."
#endif /* CONFIG_PCI */
#define IVTV_ENCODER_OFFSET 0x00000000
#define IVTV_ENCODER_SIZE 0x00800000 /* Last half isn't needed 0x01000000 */
#define IVTV_DECODER_OFFSET 0x01000000
#define IVTV_DECODER_SIZE 0x00800000 /* Last half isn't needed 0x01000000 */
#define IVTV_REG_OFFSET 0x02000000
#define IVTV_REG_SIZE 0x00010000
/* Buffers on hardware offsets */
#define IVTV_YUV_BUFFER_OFFSET 0x001a8600 /* First YUV Buffer */
#define IVTV_YUV_BUFFER_OFFSET_1 0x00240400 /* Second YUV Buffer */
#define IVTV_YUV_BUFFER_OFFSET_2 0x002d8200 /* Third YUV Buffer */
#define IVTV_YUV_BUFFER_OFFSET_3 0x00370000 /* Fourth YUV Buffer */
#define IVTV_YUV_BUFFER_UV_OFFSET 0x65400 /* Offset to UV Buffer */
/* Offset to filter table in firmware */
#define IVTV_YUV_HORIZONTAL_FILTER_OFFSET 0x025d8
#define IVTV_YUV_VERTICAL_FILTER_OFFSET 0x03358
extern const u32 yuv_offset[4];
/* Maximum ivtv driver instances.
Based on 6 PVR500s each with two PVR15s...
TODO: make this dynamic. I believe it is only a global in order to support
ivtv-fb. There must be a better way to do that. */
#define IVTV_MAX_CARDS 12
/* Supported cards */
#define IVTV_CARD_PVR_250 0 /* WinTV PVR 250 */
#define IVTV_CARD_PVR_350 1 /* encoder, decoder, tv-out */
#define IVTV_CARD_PVR_150 2 /* WinTV PVR 150 and PVR 500 (really just two
PVR150s on one PCI board) */
#define IVTV_CARD_M179 3 /* AVerMedia M179 (encoder only) */
#define IVTV_CARD_MPG600 4 /* Kuroutoshikou ITVC16-STVLP/YUAN MPG600, encoder only */
#define IVTV_CARD_MPG160 5 /* Kuroutoshikou ITVC15-STVLP/YUAN MPG160
cx23415 based, but does not have tv-out */
#define IVTV_CARD_PG600 6 /* YUAN PG600/DIAMONDMM PVR-550 based on the CX Falcon 2 */
#define IVTV_CARD_AVC2410 7 /* Adaptec AVC-2410 */
#define IVTV_CARD_AVC2010 8 /* Adaptec AVD-2010 (No Tuner) */
#define IVTV_CARD_TG5000TV 9 /* NAGASE TRANSGEAR 5000TV, encoder only */
#define IVTV_CARD_VA2000MAX_SNT6 10 /* VA2000MAX-STN6 */
#define IVTV_CARD_CX23416GYC 11 /* Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */
#define IVTV_CARD_GV_MVPRX 12 /* I/O Data GV-MVP/RX, RX2, RX2W */
#define IVTV_CARD_GV_MVPRX2E 13 /* I/O Data GV-MVP/RX2E */
#define IVTV_CARD_GOTVIEW_PCI_DVD 14 /* GotView PCI DVD */
#define IVTV_CARD_GOTVIEW_PCI_DVD2 15 /* GotView PCI DVD2 */
#define IVTV_CARD_YUAN_MPC622 16 /* Yuan MPC622 miniPCI */
#define IVTV_CARD_DCTMTVP1 17 /* DIGITAL COWBOY DCT-MTVP1 */
#ifdef HAVE_XC3028
#define IVTV_CARD_PG600V2 18 /* Yuan PG600V2/GotView PCI DVD Lite/Club3D ZAP-TV1x01 */
#define IVTV_CARD_LAST 18
#else
#define IVTV_CARD_LAST 17
#endif
/* Variants of existing cards but with the same PCI IDs. The driver
detects these based on other device information.
These cards must always come last.
New cards must be inserted above, and the indices of the cards below
must be adjusted accordingly. */
/* PVR-350 V1 (uses saa7114) */
#define IVTV_CARD_PVR_350_V1 (IVTV_CARD_LAST+1)
/* 2 variants of Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */
#define IVTV_CARD_CX23416GYC_NOGR (IVTV_CARD_LAST+2)
#define IVTV_CARD_CX23416GYC_NOGRYCS (IVTV_CARD_LAST+3)
#define IVTV_ENC_STREAM_TYPE_MPG 0
#define IVTV_ENC_STREAM_TYPE_YUV 1
#define IVTV_ENC_STREAM_TYPE_VBI 2
#define IVTV_ENC_STREAM_TYPE_PCM 3
#define IVTV_ENC_STREAM_TYPE_RAD 4
#define IVTV_DEC_STREAM_TYPE_MPG 5
#define IVTV_DEC_STREAM_TYPE_VBI 6
#define IVTV_DEC_STREAM_TYPE_VOUT 7
#define IVTV_DEC_STREAM_TYPE_YUV 8
#define IVTV_MAX_STREAMS 9
#define IVTV_V4L2_DEC_MPG_OFFSET 16 /* offset from 0 to register decoder mpg v4l2 minors on */
#define IVTV_V4L2_ENC_PCM_OFFSET 24 /* offset from 0 to register pcm v4l2 minors on */
#define IVTV_V4L2_ENC_YUV_OFFSET 32 /* offset from 0 to register yuv v4l2 minors on */
#define IVTV_V4L2_DEC_YUV_OFFSET 48 /* offset from 0 to register decoder yuv v4l2 minors on */
#define IVTV_V4L2_DEC_VBI_OFFSET 8 /* offset from 0 to register decoder vbi input v4l2 minors on */
#define IVTV_V4L2_DEC_VOUT_OFFSET 16 /* offset from 0 to register vbi output v4l2 minors on */
#define IVTV_ENC_MEM_START 0x00000000
#define IVTV_DEC_MEM_START 0x01000000
/* system vendor and device IDs */
#define PCI_VENDOR_ID_ICOMP 0x4444
#define PCI_DEVICE_ID_IVTV15 0x0803
#define PCI_DEVICE_ID_IVTV16 0x0016
/* subsystem vendor ID */
#define IVTV_PCI_ID_HAUPPAUGE 0x0070
#define IVTV_PCI_ID_HAUPPAUGE_ALT1 0x0270
#define IVTV_PCI_ID_HAUPPAUGE_ALT2 0x4070
#define IVTV_PCI_ID_ADAPTEC 0x9005
#define IVTV_PCI_ID_AVERMEDIA 0x1461
#define IVTV_PCI_ID_YUAN1 0x12ab
#define IVTV_PCI_ID_YUAN2 0xff01
#define IVTV_PCI_ID_YUAN3 0xffab
#define IVTV_PCI_ID_YUAN4 0xfbab
#define IVTV_PCI_ID_DIAMONDMM 0xff92
#define IVTV_PCI_ID_IODATA 0x10fc
#define IVTV_PCI_ID_MELCO 0x1154
#define IVTV_PCI_ID_GOTVIEW1 0xffac
#define IVTV_PCI_ID_GOTVIEW2 0xffad
/* Decoder Buffer hardware size on Chip */
#define IVTV_DEC_MAX_BUF 0x00100000 /* max bytes in decoder buffer */
#define IVTV_DEC_MIN_BUF 0x00010000 /* min bytes in dec buffer */
/* ======================================================================== */
/* ========================== START USER SETTABLE DMA VARIABLES =========== */
/* ======================================================================== */
#define IVTV_DMA_SG_OSD_ENT (2883584/PAGE_SIZE) /* sg entities */
/* DMA Buffers, Default size in MB allocated */
#define IVTV_DEFAULT_ENC_MPG_BUFFERS 4
#define IVTV_DEFAULT_ENC_YUV_BUFFERS 2
#define IVTV_DEFAULT_ENC_VBI_BUFFERS 1
#define IVTV_DEFAULT_ENC_PCM_BUFFERS 1
#define IVTV_DEFAULT_DEC_MPG_BUFFERS 1
#define IVTV_DEFAULT_DEC_YUV_BUFFERS 1
#define IVTV_DEFAULT_DEC_VBI_BUFFERS 1
/* ======================================================================== */
/* ========================== END USER SETTABLE DMA VARIABLES ============= */
/* ======================================================================== */
/* Decoder Status Register */
#define IVTV_DMA_ERR_LIST 0x00000010
#define IVTV_DMA_ERR_WRITE 0x00000008
#define IVTV_DMA_ERR_READ 0x00000004
#define IVTV_DMA_SUCCESS_WRITE 0x00000002
#define IVTV_DMA_SUCCESS_READ 0x00000001
#define IVTV_DMA_READ_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_READ)
#define IVTV_DMA_WRITE_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_WRITE)
#define IVTV_DMA_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_WRITE | IVTV_DMA_ERR_READ)
/* DMA Registers */
#define IVTV_REG_DMAXFER (0x0000)
#define IVTV_REG_DMASTATUS (0x0004)
#define IVTV_REG_DECDMAADDR (0x0008)
#define IVTV_REG_ENCDMAADDR (0x000c)
#define IVTV_REG_DMACONTROL (0x0010)
#define IVTV_REG_IRQSTATUS (0x0040)
#define IVTV_REG_IRQMASK (0x0048)
/* Setup Registers */
#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8)
#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC)
#define IVTV_REG_DEC_SDRAM_REFRESH (0x08F8)
#define IVTV_REG_DEC_SDRAM_PRECHARGE (0x08FC)
#define IVTV_REG_VDM (0x2800)
#define IVTV_REG_AO (0x2D00)
#define IVTV_REG_BYTEFLUSH (0x2D24)
#define IVTV_REG_SPU (0x9050)
#define IVTV_REG_HW_BLOCKS (0x9054)
#define IVTV_REG_VPU (0x9058)
#define IVTV_REG_APU (0xA064)
#define IVTV_IRQ_ENC_START_CAP (0x1 << 31)
#define IVTV_IRQ_ENC_EOS (0x1 << 30)
#define IVTV_IRQ_ENC_VBI_CAP (0x1 << 29)
#define IVTV_IRQ_ENC_VIM_RST (0x1 << 28)
#define IVTV_IRQ_ENC_DMA_COMPLETE (0x1 << 27)
#define IVTV_IRQ_DEC_AUD_MODE_CHG (0x1 << 24)
#define IVTV_IRQ_DEC_DATA_REQ (0x1 << 22)
#define IVTV_IRQ_DEC_DMA_COMPLETE (0x1 << 20)
#define IVTV_IRQ_DEC_VBI_RE_INSERT (0x1 << 19)
#define IVTV_IRQ_DMA_ERR (0x1 << 18)
#define IVTV_IRQ_DMA_WRITE (0x1 << 17)
#define IVTV_IRQ_DMA_READ (0x1 << 16)
#define IVTV_IRQ_DEC_VSYNC (0x1 << 10)
/* IRQ Masks */
#define IVTV_IRQ_MASK_INIT (IVTV_IRQ_DMA_ERR|IVTV_IRQ_ENC_DMA_COMPLETE|IVTV_IRQ_DMA_READ)
#define IVTV_IRQ_MASK_CAPTURE (IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_EOS)
#define IVTV_IRQ_MASK_DECODE (IVTV_IRQ_DEC_DATA_REQ|IVTV_IRQ_DEC_AUD_MODE_CHG)
/* i2c stuff */
#define I2C_CLIENTS_MAX 16
/* debugging */
#define IVTV_DBGFLG_WARN (1 << 0)
#define IVTV_DBGFLG_INFO (1 << 1)
#define IVTV_DBGFLG_API (1 << 2)
#define IVTV_DBGFLG_DMA (1 << 3)
#define IVTV_DBGFLG_IOCTL (1 << 4)
#define IVTV_DBGFLG_I2C (1 << 5)
#define IVTV_DBGFLG_IRQ (1 << 6)
#define IVTV_DBGFLG_DEC (1 << 7)
#define IVTV_DBGFLG_YUV (1 << 8)
/* NOTE: extra space before comma in 'itv->num , ## args' is required for
gcc-2.95, otherwise it won't compile. */
#define IVTV_DEBUG(x, type, fmt, args...) \
do { \
if ((x) & ivtv_debug) \
printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \
} while (0)
#define IVTV_DEBUG_WARN(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_WARN, "warning", fmt , ## args)
#define IVTV_DEBUG_INFO(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_INFO, "info",fmt , ## args)
#define IVTV_DEBUG_API(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_API, "api", fmt , ## args)
#define IVTV_DEBUG_DMA(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args)
#define IVTV_DEBUG_IOCTL(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args)
#define IVTV_DEBUG_I2C(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args)
#define IVTV_DEBUG_IRQ(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args)
#define IVTV_DEBUG_DEC(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args)
#define IVTV_DEBUG_YUV(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args)
#define IVTV_FB_DEBUG(x, type, fmt, args...) \
do { \
if ((x) & ivtv_debug) \
printk(KERN_INFO "ivtv%d-fb " type ": " fmt, itv->num , ## args); \
} while (0)
#define IVTV_FB_DEBUG_WARN(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_WARN, "warning", fmt , ## args)
#define IVTV_FB_DEBUG_INFO(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_INFO, "info", fmt , ## args)
#define IVTV_FB_DEBUG_API(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_API, "api", fmt , ## args)
#define IVTV_FB_DEBUG_DMA(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args)
#define IVTV_FB_DEBUG_IOCTL(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args)
#define IVTV_FB_DEBUG_I2C(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args)
#define IVTV_FB_DEBUG_IRQ(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args)
#define IVTV_FB_DEBUG_DEC(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args)
#define IVTV_FB_DEBUG_YUV(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args)
/* Standard kernel messages */
#define IVTV_ERR(fmt, args...) printk(KERN_ERR "ivtv%d: " fmt, itv->num , ## args)
#define IVTV_WARN(fmt, args...) printk(KERN_WARNING "ivtv%d: " fmt, itv->num , ## args)
#define IVTV_INFO(fmt, args...) printk(KERN_INFO "ivtv%d: " fmt, itv->num , ## args)
#define IVTV_FB_ERR(fmt, args...) printk(KERN_ERR "ivtv%d-fb: " fmt, itv->num , ## args)
#define IVTV_FB_INFO(fmt, args...) printk(KERN_INFO "ivtv%d-fb: " fmt, itv->num , ## args)
/* Values for IVTV_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */
#define MPEG_FRAME_TYPE_IFRAME 1
#define MPEG_FRAME_TYPE_IFRAME_PFRAME 3
#define MPEG_FRAME_TYPE_ALL 7
/* output modes (cx23415 only) */
#define OUT_NONE 0
#define OUT_MPG 1
#define OUT_YUV 2
#define OUT_UDMA_YUV 3
#define OUT_PASSTHROUGH 4
#define IVTV_MAX_PGM_INDEX (400)
extern int ivtv_debug;
struct ivtv_options {
int megabytes[IVTV_MAX_STREAMS]; /* Size in megabytes of each stream */
int cardtype; /* force card type on load */
int tuner; /* set tuner on load */
int radio; /* enable/disable radio */
int newi2c; /* New I2C algorithm */
};
#define IVTV_MBOX_DMA_START 6
#define IVTV_MBOX_DMA_END 8
#define IVTV_MBOX_DMA 9
#define IVTV_MBOX_FIELD_DISPLAYED 8
/* ivtv-specific mailbox template */
struct ivtv_mailbox {
u32 flags;
u32 cmd;
u32 retval;
u32 timeout;
u32 data[CX2341X_MBOX_MAX_DATA];
};
struct ivtv_api_cache {
unsigned long last_jiffies; /* when last command was issued */
u32 data[CX2341X_MBOX_MAX_DATA]; /* last sent api data */
};
struct ivtv_mailbox_data {
volatile struct ivtv_mailbox __iomem *mbox;
/* Bits 0-2 are for the encoder mailboxes, 0-1 are for the decoder mailboxes.
If the bit is set, then the corresponding mailbox is in use by the driver. */
unsigned long busy;
u8 max_mbox;
};
/* per-buffer bit flags */
#define IVTV_F_B_NEED_BUF_SWAP 0 /* this buffer should be byte swapped */
/* per-stream, s_flags */
#define IVTV_F_S_DMA_PENDING 0 /* this stream has pending DMA */
#define IVTV_F_S_DMA_HAS_VBI 1 /* the current DMA request also requests VBI data */
#define IVTV_F_S_NEEDS_DATA 2 /* this decoding stream needs more data */
#define IVTV_F_S_CLAIMED 3 /* this stream is claimed */
#define IVTV_F_S_STREAMING 4 /* the fw is decoding/encoding this stream */
#define IVTV_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */
#define IVTV_F_S_PASSTHROUGH 6 /* this stream is in passthrough mode */
#define IVTV_F_S_STREAMOFF 7 /* signal end of stream EOS */
#define IVTV_F_S_APPL_IO 8 /* this stream is used read/written by an application */
/* per-ivtv, i_flags */
#define IVTV_F_I_DMA 0 /* DMA in progress */
#define IVTV_F_I_UDMA 1 /* UDMA in progress */
#define IVTV_F_I_UDMA_PENDING 2 /* UDMA pending */
#define IVTV_F_I_SPEED_CHANGE 3 /* A speed change is in progress */
#define IVTV_F_I_EOS 4 /* End of encoder stream reached */
#define IVTV_F_I_RADIO_USER 5 /* The radio tuner is selected */
#define IVTV_F_I_DIG_RST 6 /* Reset digitizer */
#define IVTV_F_I_DEC_YUV 7 /* YUV instead of MPG is being decoded */
#define IVTV_F_I_ENC_VBI 8 /* VBI DMA */
#define IVTV_F_I_UPDATE_CC 9 /* CC should be updated */
#define IVTV_F_I_UPDATE_WSS 10 /* WSS should be updated */
#define IVTV_F_I_UPDATE_VPS 11 /* VPS should be updated */
#define IVTV_F_I_DECODING_YUV 12 /* this stream is YUV frame decoding */
#define IVTV_F_I_ENC_PAUSED 13 /* the encoder is paused */
#define IVTV_F_I_VALID_DEC_TIMINGS 14 /* last_dec_timing is valid */
/* Event notifications */
#define IVTV_F_I_EV_DEC_STOPPED 28 /* decoder stopped event */
#define IVTV_F_I_EV_VSYNC 29 /* VSYNC event */
#define IVTV_F_I_EV_VSYNC_FIELD 30 /* VSYNC event field (0 = first, 1 = second field) */
#define IVTV_F_I_EV_VSYNC_ENABLED 31 /* VSYNC event enabled */
/* Scatter-Gather array element, used in DMA transfers */
struct ivtv_SG_element {
u32 src;
u32 dst;
u32 size;
};
struct ivtv_user_dma {
struct mutex lock;
int page_count;
struct page *map[IVTV_DMA_SG_OSD_ENT];
/* Base Dev SG Array for cx23415/6 */
struct ivtv_SG_element SGarray[IVTV_DMA_SG_OSD_ENT];
dma_addr_t SG_handle;
int SG_length;
/* SG List of Buffers */
struct scatterlist SGlist[IVTV_DMA_SG_OSD_ENT];
};
struct ivtv_dma_page_info {
unsigned long uaddr;
unsigned long first;
unsigned long last;
unsigned int offset;
unsigned int tail;
int page_count;
};
struct ivtv_buffer {
struct list_head list;
dma_addr_t dma_handle;
unsigned long b_flags;
char *buf;
u32 bytesused;
u32 readpos;
};
struct ivtv_queue {
struct list_head list;
u32 buffers;
u32 length;
u32 bytesused;
};
struct ivtv; /* forward reference */
struct ivtv_stream {
/* These first four fields are always set, even if the stream
is not actually created. */
struct video_device *v4l2dev; /* NULL when stream not created */
struct ivtv *itv; /* for ease of use */
const char *name; /* name of the stream */
int type; /* stream type */
u32 id;
spinlock_t qlock; /* locks access to the queues */
unsigned long s_flags; /* status flags, see above */
int dma; /* can be PCI_DMA_TODEVICE,
PCI_DMA_FROMDEVICE or
PCI_DMA_NONE */
u32 dma_offset;
u32 dma_backup;
u64 dma_pts;
int subtype;
wait_queue_head_t waitq;
u32 dma_last_offset;
/* Buffer Stats */
u32 buffers;
u32 buf_size;
u32 buffers_stolen;
/* Buffer Queues */
struct ivtv_queue q_free; /* free buffers */
struct ivtv_queue q_full; /* full buffers */
struct ivtv_queue q_io; /* waiting for I/O */
struct ivtv_queue q_dma; /* waiting for DMA */
struct ivtv_queue q_predma; /* waiting for DMA */
/* Base Dev SG Array for cx23415/6 */
struct ivtv_SG_element *SGarray;
dma_addr_t SG_handle;
int SG_length;
/* SG List of Buffers */
struct scatterlist *SGlist;
};
struct ivtv_open_id {
u32 open_id;
int type;
struct ivtv *itv;
};
#define IVTV_YUV_UPDATE_HORIZONTAL 0x01
#define IVTV_YUV_UPDATE_VERTICAL 0x02
struct yuv_frame_info
{
u32 update;
int src_x;
int src_y;
unsigned int src_w;
unsigned int src_h;
int dst_x;
int dst_y;
unsigned int dst_w;
unsigned int dst_h;
int pan_x;
int pan_y;
u32 vis_w;
u32 vis_h;
u32 interlaced_y;
u32 interlaced_uv;
int tru_x;
u32 tru_w;
u32 tru_h;
u32 offset_y;
};
#define IVTV_YUV_MODE_INTERLACED 0x00
#define IVTV_YUV_MODE_PROGRESSIVE 0x01
#define IVTV_YUV_MODE_AUTO 0x02
#define IVTV_YUV_MODE_MASK 0x03
#define IVTV_YUV_SYNC_EVEN 0x00
#define IVTV_YUV_SYNC_ODD 0x04
#define IVTV_YUV_SYNC_MASK 0x04
struct yuv_playback_info
{
u32 reg_2834;
u32 reg_2838;
u32 reg_283c;
u32 reg_2840;
u32 reg_2844;
u32 reg_2848;
u32 reg_2854;
u32 reg_285c;
u32 reg_2864;
u32 reg_2870;
u32 reg_2874;
u32 reg_2890;
u32 reg_2898;
u32 reg_289c;
u32 reg_2918;
u32 reg_291c;
u32 reg_2920;
u32 reg_2924;
u32 reg_2928;
u32 reg_292c;
u32 reg_2930;
u32 reg_2934;
u32 reg_2938;
u32 reg_293c;
u32 reg_2940;
u32 reg_2944;
u32 reg_2948;
u32 reg_294c;
u32 reg_2950;
u32 reg_2954;
u32 reg_2958;
u32 reg_295c;
u32 reg_2960;
u32 reg_2964;
u32 reg_2968;
u32 reg_296c;
u32 reg_2970;
int v_filter_1;
int v_filter_2;
int h_filter;
u32 osd_x_offset;
u32 osd_y_offset;
u32 osd_x_pan;
u32 osd_y_pan;
u32 osd_vis_w;
u32 osd_vis_h;
int decode_height;
int frame_interlaced;
int frame_interlaced_last;
int lace_mode;
int lace_threshold;
int lace_threshold_last;
int lace_sync_field;
atomic_t next_dma_frame;
atomic_t next_fill_frame;
u32 yuv_forced_update;
int update_frame;
struct workqueue_struct *work_queues;
struct work_struct work_queue;
struct yuv_frame_info new_frame_info[4];
struct yuv_frame_info old_frame_info;
struct yuv_frame_info old_frame_info_args;
void *blanking_ptr;
dma_addr_t blanking_dmaptr;
};
#define IVTV_VBI_FRAMES 32
/* VBI data */
struct vbi_info {
u32 dec_start;
u32 enc_start, enc_size;
int fpi;
u32 frame;
u32 dma_offset;
u8 cc_data_odd[256];
u8 cc_data_even[256];
int cc_pos;
u8 cc_no_update;
u8 vps[5];
u8 vps_found;
int wss;
u8 wss_found;
u8 wss_no_update;
u32 raw_decoder_line_size;
u8 raw_decoder_sav_odd_field;
u8 raw_decoder_sav_even_field;
u32 sliced_decoder_line_size;
u8 sliced_decoder_sav_odd_field;
u8 sliced_decoder_sav_even_field;
struct v4l2_format in;
/* convenience pointer to sliced struct in vbi_in union */
struct v4l2_sliced_vbi_format *sliced_in;
u32 service_set_in;
u32 service_set_out;
int insert_mpeg;
/* Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines.
One for /dev/vbi0 and one for /dev/vbi8 */
struct v4l2_sliced_vbi_data sliced_data[36];
struct v4l2_sliced_vbi_data sliced_dec_data[36];
/* Buffer for VBI data inserted into MPEG stream.
The first byte is a dummy byte that's never used.
The next 16 bytes contain the MPEG header for the VBI data,
the remainder is the actual VBI data.
The max size accepted by the MPEG VBI reinsertion turns out
to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes,
where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is
a single line header byte and 2 * 18 is the number of VBI lines per frame.
However, it seems that the data must be 1K aligned, so we have to
pad the data until the 1 or 2 K boundary.
This pointer array will allocate 2049 bytes to store each VBI frame. */
u8 *sliced_mpeg_data[IVTV_VBI_FRAMES];
u32 sliced_mpeg_size[IVTV_VBI_FRAMES];
struct ivtv_buffer sliced_mpeg_buf;
u32 inserted_frame;
struct workqueue_struct *work_queues;
struct work_struct work_queue;
u32 start[2], count;
u32 raw_size;
u32 sliced_size;
};
/* forward declaration of struct defined in ivtv-cards.h */
struct ivtv_card;
/* Struct to hold info about ivtv cards */
struct ivtv {
int num; /* board number, -1 during init! */
char name[8]; /* board name for printk and interrupts (e.g. 'ivtv0') */
struct pci_dev *dev; /* PCI device */
const struct ivtv_card *card; /* card information */
const char *card_name; /* full name of the card */
u8 has_cx23415; /* 1 if it is a cx23415 based card, 0 for cx23416 */
u8 is_50hz;
u8 is_60hz;
u8 is_out_50hz;
u8 is_out_60hz;
u8 pvr150_workaround; /* 1 if the cx25840 needs to workaround a PVR150 bug */
u8 nof_inputs; /* number of video inputs */
u8 nof_audio_inputs; /* number of audio inputs */
u32 v4l2_cap; /* V4L2 capabilities of card */
u32 hw_flags; /* Hardware description of the board */
/* controlling Video decoder function */
int (*video_dec_func)(struct ivtv *, unsigned int, void *);
struct ivtv_options options; /* User options */
int stream_buf_size[IVTV_MAX_STREAMS]; /* Stream buffer size */
struct ivtv_stream streams[IVTV_MAX_STREAMS]; /* Stream data */
int speed;
u8 speed_mute_audio;
unsigned long i_flags; /* global ivtv flags */
atomic_t capturing; /* count number of active capture streams */
atomic_t decoding; /* count number of active decoding streams */
u32 irq_rr_idx; /* Round-robin stream index */
int cur_dma_stream; /* index of stream doing DMA */
u32 dma_data_req_offset;
u32 dma_data_req_size;
int output_mode; /* NONE, MPG, YUV, UDMA YUV, passthrough */
spinlock_t lock; /* lock access to this struct */
int search_pack_header;
spinlock_t dma_reg_lock; /* lock access to DMA engine registers */
/* User based DMA for OSD */
struct ivtv_user_dma udma;
int open_id; /* incremented each time an open occurs, used as unique ID.
starts at 1, so 0 can be used as uninitialized value
in the stream->id. */
u32 base_addr;
u32 irqmask;
struct timer_list dma_timer; /* Timer used to catch unfinished DMAs */
struct vbi_info vbi;
struct ivtv_mailbox_data enc_mbox;
struct ivtv_mailbox_data dec_mbox;
struct ivtv_api_cache api_cache[256]; /* Cached API Commands */
u8 card_rev;
volatile void __iomem *enc_mem, *dec_mem, *reg_mem;
u32 pgm_info_offset;
u32 pgm_info_num;
u32 pgm_info_write_idx;
u32 pgm_info_read_idx;
struct v4l2_enc_idx_entry pgm_info[IVTV_MAX_PGM_INDEX];
u64 mpg_data_received;
u64 vbi_data_inserted;
wait_queue_head_t cap_w;
/* when the next decoder event arrives this queue is woken up */
wait_queue_head_t event_waitq;
/* when the next decoder vsync arrives this queue is woken up */
wait_queue_head_t vsync_waitq;
/* when the current DMA is finished this queue is woken up */
wait_queue_head_t dma_waitq;
/* OSD support */
unsigned long osd_video_pbase;
int osd_global_alpha_state; /* 0=off : 1=on */
int osd_local_alpha_state; /* 0=off : 1=on */
int osd_color_key_state; /* 0=off : 1=on */
u8 osd_global_alpha; /* Current global alpha */
u32 osd_color_key; /* Current color key */
u32 osd_pixelformat; /* Current pixel format */
struct v4l2_rect osd_rect; /* Current OSD position and size */
struct v4l2_rect main_rect; /* Current Main window position and size */
u32 last_dec_timing[3]; /* Store last retrieved pts/scr/frame values */
/* i2c */
struct i2c_adapter i2c_adap;
struct i2c_algo_bit_data i2c_algo;
struct i2c_client i2c_client;
struct mutex i2c_bus_lock;
int i2c_state;
struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];
/* v4l2 and User settings */
/* codec settings */
struct cx2341x_mpeg_params params;
u32 audio_input;
u32 active_input;
u32 active_output;
v4l2_std_id std;
v4l2_std_id std_out;
v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */
u8 audio_stereo_mode;
u8 audio_bilingual_mode;
/* dualwatch */
unsigned long dualwatch_jiffies;
u16 dualwatch_stereo_mode;
/* Digitizer type */
int digitizer; /* 0x00EF = saa7114 0x00FO = saa7115 0x0106 = mic */
u32 lastVsyncFrame;
struct yuv_playback_info yuv_info;
struct osd_info *osd_info;
};
/* Globals */
extern struct ivtv *ivtv_cards[];
extern int ivtv_cards_active;
extern int ivtv_first_minor;
extern spinlock_t ivtv_cards_lock;
/*==============Prototypes==================*/
/* Hardware/IRQ */
void ivtv_set_irq_mask(struct ivtv *itv, u32 mask);
void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask);
/* try to set output mode, return current mode. */
int ivtv_set_output_mode(struct ivtv *itv, int mode);
/* return current output stream based on current mode */
struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv);
/* Return non-zero if a signal is pending */
int ivtv_sleep_timeout(int timeout, int intr);
/* Wait on queue, returns -EINTR if interrupted */
int ivtv_waitq(wait_queue_head_t *waitq);
/* Read Hauppauge eeprom */
struct tveeprom; /* forward reference */
void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv);
/* This is a PCI post thing, where if the pci register is not read, then
the write doesn't always take effect right away. By reading back the
register any pending PCI writes will be performed (in order), and so
you can be sure that the writes are guaranteed to be done.
Rarely needed, only in some timing sensitive cases.
Apparently if this is not done some motherboards seem
to kill the firmware and get into the broken state until computer is
rebooted. */
#define write_sync(val, reg) \
do { writel(val, reg); readl(reg); } while (0)
#define read_reg(reg) readl(itv->reg_mem + (reg))
#define write_reg(val, reg) writel(val, itv->reg_mem + (reg))
#define write_reg_sync(val, reg) \
do { write_reg(val, reg); read_reg(reg); } while (0)
#define read_enc(addr) readl(itv->enc_mem + (u32)(addr))
#define write_enc(val, addr) writel(val, itv->enc_mem + (u32)(addr))
#define write_enc_sync(val, addr) \
do { write_enc(val, addr); read_enc(addr); } while (0)
#define read_dec(addr) readl(itv->dec_mem + (u32)(addr))
#define write_dec(val, addr) writel(val, itv->dec_mem + (u32)(addr))
#define write_dec_sync(val, addr) \
do { write_dec(val, addr); read_dec(addr); } while (0)
#endif /* IVTV_DRIVER_H */

View File

@ -0,0 +1,918 @@
/*
file operation functions
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2004 Chris Kennedy <c@groovy.org>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ivtv-driver.h"
#include "ivtv-fileops.h"
#include "ivtv-i2c.h"
#include "ivtv-queue.h"
#include "ivtv-udma.h"
#include "ivtv-irq.h"
#include "ivtv-vbi.h"
#include "ivtv-mailbox.h"
#include "ivtv-audio.h"
#include "ivtv-streams.h"
#include "ivtv-yuv.h"
#include "ivtv-controls.h"
#include "ivtv-ioctl.h"
/* This function tries to claim the stream for a specific file descriptor.
If no one else is using this stream then the stream is claimed and
associated VBI streams are also automatically claimed.
Possible error returns: -EBUSY if someone else has claimed
the stream or 0 on success. */
int ivtv_claim_stream(struct ivtv_open_id *id, int type)
{
struct ivtv *itv = id->itv;
struct ivtv_stream *s = &itv->streams[type];
struct ivtv_stream *s_vbi;
int vbi_type;
if (test_and_set_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
/* someone already claimed this stream */
if (s->id == id->open_id) {
/* yes, this file descriptor did. So that's OK. */
return 0;
}
if (s->id == -1 && (type == IVTV_DEC_STREAM_TYPE_VBI ||
type == IVTV_ENC_STREAM_TYPE_VBI)) {
/* VBI is handled already internally, now also assign
the file descriptor to this stream for external
reading of the stream. */
s->id = id->open_id;
IVTV_DEBUG_INFO("Start Read VBI\n");
return 0;
}
/* someone else is using this stream already */
IVTV_DEBUG_INFO("Stream %d is busy\n", type);
return -EBUSY;
}
s->id = id->open_id;
if (type == IVTV_DEC_STREAM_TYPE_VBI) {
/* Enable reinsertion interrupt */
ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
}
/* IVTV_DEC_STREAM_TYPE_MPG needs to claim IVTV_DEC_STREAM_TYPE_VBI,
IVTV_ENC_STREAM_TYPE_MPG needs to claim IVTV_ENC_STREAM_TYPE_VBI
(provided VBI insertion is on and sliced VBI is selected), for all
other streams we're done */
if (type == IVTV_DEC_STREAM_TYPE_MPG) {
vbi_type = IVTV_DEC_STREAM_TYPE_VBI;
} else if (type == IVTV_ENC_STREAM_TYPE_MPG &&
itv->vbi.insert_mpeg && itv->vbi.sliced_in->service_set) {
vbi_type = IVTV_ENC_STREAM_TYPE_VBI;
} else {
return 0;
}
s_vbi = &itv->streams[vbi_type];
if (!test_and_set_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags)) {
/* Enable reinsertion interrupt */
if (vbi_type == IVTV_DEC_STREAM_TYPE_VBI)
ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
}
/* mark that it is used internally */
set_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags);
return 0;
}
/* This function releases a previously claimed stream. It will take into
account associated VBI streams. */
void ivtv_release_stream(struct ivtv_stream *s)
{
struct ivtv *itv = s->itv;
struct ivtv_stream *s_vbi;
s->id = -1;
if ((s->type == IVTV_DEC_STREAM_TYPE_VBI || s->type == IVTV_ENC_STREAM_TYPE_VBI) &&
test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) {
/* this stream is still in use internally */
return;
}
if (!test_and_clear_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
IVTV_DEBUG_WARN("Release stream %s not in use!\n", s->name);
return;
}
ivtv_flush_queues(s);
/* disable reinsertion interrupt */
if (s->type == IVTV_DEC_STREAM_TYPE_VBI)
ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
/* IVTV_DEC_STREAM_TYPE_MPG needs to release IVTV_DEC_STREAM_TYPE_VBI,
IVTV_ENC_STREAM_TYPE_MPG needs to release IVTV_ENC_STREAM_TYPE_VBI,
for all other streams we're done */
if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
s_vbi = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI];
else if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
else
return;
/* clear internal use flag */
if (!test_and_clear_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags)) {
/* was already cleared */
return;
}
if (s_vbi->id != -1) {
/* VBI stream still claimed by a file descriptor */
return;
}
/* disable reinsertion interrupt */
if (s_vbi->type == IVTV_DEC_STREAM_TYPE_VBI)
ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
clear_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags);
ivtv_flush_queues(s_vbi);
}
static void ivtv_dualwatch(struct ivtv *itv)
{
struct v4l2_tuner vt;
u16 new_bitmap;
u16 new_stereo_mode;
const u16 stereo_mask = 0x0300;
const u16 dual = 0x0200;
new_stereo_mode = itv->params.audio_properties & stereo_mask;
memset(&vt, 0, sizeof(vt));
ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, &vt);
if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
new_stereo_mode = dual;
if (new_stereo_mode == itv->dualwatch_stereo_mode)
return;
new_bitmap = new_stereo_mode | (itv->params.audio_properties & ~stereo_mask);
IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n",
itv->dualwatch_stereo_mode, new_stereo_mode, new_bitmap);
if (ivtv_vapi(itv, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new_bitmap) == 0) {
itv->dualwatch_stereo_mode = new_stereo_mode;
return;
}
IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
}
static void ivtv_update_pgm_info(struct ivtv *itv)
{
u32 wr_idx = (read_enc(itv->pgm_info_offset) - itv->pgm_info_offset - 4) / 24;
int cnt;
int i = 0;
if (wr_idx >= itv->pgm_info_num) {
IVTV_DEBUG_WARN("Invalid PGM index %d (>= %d)\n", wr_idx, itv->pgm_info_num);
return;
}
cnt = (wr_idx + itv->pgm_info_num - itv->pgm_info_write_idx) % itv->pgm_info_num;
while (i < cnt) {
int idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num;
struct v4l2_enc_idx_entry *e = itv->pgm_info + idx;
u32 addr = itv->pgm_info_offset + 4 + idx * 24;
const int mapping[] = { V4L2_ENC_IDX_FRAME_P, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_B, 0 };
e->offset = read_enc(addr + 4) + ((u64)read_enc(addr + 8) << 32);
if (e->offset > itv->mpg_data_received) {
break;
}
e->offset += itv->vbi_data_inserted;
e->length = read_enc(addr);
e->pts = read_enc(addr + 16) + ((u64)(read_enc(addr + 20) & 1) << 32);
e->flags = mapping[read_enc(addr + 12) & 3];
i++;
}
itv->pgm_info_write_idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num;
}
static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block, int *err)
{
struct ivtv *itv = s->itv;
struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
struct ivtv_buffer *buf;
DEFINE_WAIT(wait);
*err = 0;
while (1) {
if (s->type == IVTV_ENC_STREAM_TYPE_MPG) {
/* Process pending program info updates and pending VBI data */
ivtv_update_pgm_info(itv);
if (jiffies - itv->dualwatch_jiffies > HZ) {
itv->dualwatch_jiffies = jiffies;
ivtv_dualwatch(itv);
}
if (test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
!test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) {
while ((buf = ivtv_dequeue(s_vbi, &s_vbi->q_full))) {
/* byteswap and process VBI data */
ivtv_process_vbi_data(itv, buf, s_vbi->dma_pts, s_vbi->type);
ivtv_enqueue(s_vbi, buf, &s_vbi->q_free);
}
}
buf = &itv->vbi.sliced_mpeg_buf;
if (buf->readpos != buf->bytesused) {
return buf;
}
}
/* do we have leftover data? */
buf = ivtv_dequeue(s, &s->q_io);
if (buf)
return buf;
/* do we have new data? */
buf = ivtv_dequeue(s, &s->q_full);
if (buf) {
if (!test_and_clear_bit(IVTV_F_B_NEED_BUF_SWAP, &buf->b_flags))
return buf;
if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
/* byteswap MPG data */
ivtv_buf_swap(buf);
else if (s->type != IVTV_DEC_STREAM_TYPE_VBI) {
/* byteswap and process VBI data */
ivtv_process_vbi_data(itv, buf, s->dma_pts, s->type);
}
return buf;
}
/* return if file was opened with O_NONBLOCK */
if (non_block) {
*err = -EAGAIN;
return NULL;
}
/* return if end of stream */
if (s->type != IVTV_DEC_STREAM_TYPE_VBI && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
IVTV_DEBUG_INFO("EOS %s\n", s->name);
return NULL;
}
/* wait for more data to arrive */
prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
/* New buffers might have become available before we were added to the waitqueue */
if (!s->q_full.buffers)
schedule();
finish_wait(&s->waitq, &wait);
if (signal_pending(current)) {
/* return if a signal was received */
IVTV_DEBUG_INFO("User stopped %s\n", s->name);
*err = -EINTR;
return NULL;
}
}
}
static void ivtv_setup_sliced_vbi_buf(struct ivtv *itv)
{
int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES;
itv->vbi.sliced_mpeg_buf.buf = itv->vbi.sliced_mpeg_data[idx];
itv->vbi.sliced_mpeg_buf.bytesused = itv->vbi.sliced_mpeg_size[idx];
itv->vbi.sliced_mpeg_buf.readpos = 0;
}
static size_t ivtv_copy_buf_to_user(struct ivtv_stream *s, struct ivtv_buffer *buf,
char __user *ubuf, size_t ucount)
{
struct ivtv *itv = s->itv;
size_t len = buf->bytesused - buf->readpos;
if (len > ucount) len = ucount;
if (itv->vbi.insert_mpeg && s->type == IVTV_ENC_STREAM_TYPE_MPG &&
itv->vbi.sliced_in->service_set && buf != &itv->vbi.sliced_mpeg_buf) {
const char *start = buf->buf + buf->readpos;
const char *p = start + 1;
const u8 *q;
u8 ch = itv->search_pack_header ? 0xba : 0xe0;
int stuffing, i;
while (start + len > p && (q = memchr(p, 0, start + len - p))) {
p = q + 1;
if ((char *)q + 15 >= buf->buf + buf->bytesused ||
q[1] != 0 || q[2] != 1 || q[3] != ch) {
continue;
}
if (!itv->search_pack_header) {
if ((q[6] & 0xc0) != 0x80)
continue;
if (((q[7] & 0xc0) == 0x80 && (q[9] & 0xf0) == 0x20) ||
((q[7] & 0xc0) == 0xc0 && (q[9] & 0xf0) == 0x30)) {
ch = 0xba;
itv->search_pack_header = 1;
p = q + 9;
}
continue;
}
stuffing = q[13] & 7;
/* all stuffing bytes must be 0xff */
for (i = 0; i < stuffing; i++)
if (q[14 + i] != 0xff)
break;
if (i == stuffing && (q[4] & 0xc4) == 0x44 && (q[12] & 3) == 3 &&
q[14 + stuffing] == 0 && q[15 + stuffing] == 0 &&
q[16 + stuffing] == 1) {
itv->search_pack_header = 0;
len = (char *)q - start;
ivtv_setup_sliced_vbi_buf(itv);
break;
}
}
}
if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) {
IVTV_DEBUG_WARN("copy %zd bytes to user failed for %s\n", len, s->name);
return -EFAULT;
}
/*IVTV_INFO("copied %lld %d %d %d %d %d vbi %d\n", itv->mpg_data_received, len, ucount,
buf->readpos, buf->bytesused, buf->bytesused - buf->readpos - len,
buf == &itv->vbi.sliced_mpeg_buf); */
buf->readpos += len;
if (s->type == IVTV_ENC_STREAM_TYPE_MPG && buf != &itv->vbi.sliced_mpeg_buf)
itv->mpg_data_received += len;
return len;
}
static ssize_t ivtv_read(struct ivtv_stream *s, char __user *ubuf, size_t tot_count, int non_block)
{
struct ivtv *itv = s->itv;
size_t tot_written = 0;
int single_frame = 0;
if (atomic_read(&itv->capturing) == 0 && s->id == -1) {
/* shouldn't happen */
IVTV_DEBUG_WARN("Stream %s not initialized before read\n", s->name);
return -EIO;
}
/* Each VBI buffer is one frame, the v4l2 API says that for VBI the frames should
arrive one-by-one, so make sure we never output more than one VBI frame at a time */
if (s->type == IVTV_DEC_STREAM_TYPE_VBI ||
(s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set))
single_frame = 1;
for (;;) {
struct ivtv_buffer *buf;
int rc;
buf = ivtv_get_buffer(s, non_block, &rc);
if (buf == NULL && rc == -EAGAIN && tot_written)
break;
if (buf == NULL)
return rc;
rc = ivtv_copy_buf_to_user(s, buf, ubuf + tot_written, tot_count - tot_written);
if (buf != &itv->vbi.sliced_mpeg_buf) {
ivtv_enqueue(s, buf, (buf->readpos == buf->bytesused) ? &s->q_free : &s->q_io);
}
else if (buf->readpos == buf->bytesused) {
int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES;
itv->vbi.sliced_mpeg_size[idx] = 0;
itv->vbi.inserted_frame++;
itv->vbi_data_inserted += buf->bytesused;
}
if (rc < 0)
return rc;
tot_written += rc;
if (tot_written == tot_count || single_frame)
break;
}
return tot_written;
}
static ssize_t ivtv_read_pos(struct ivtv_stream *s, char __user *ubuf, size_t count,
loff_t *pos, int non_block)
{
ssize_t rc = count ? ivtv_read(s, ubuf, count, non_block) : 0;
struct ivtv *itv = s->itv;
IVTV_DEBUG_INFO("read %zd from %s, got %zd\n", count, s->name, rc);
if (rc > 0)
pos += rc;
return rc;
}
int ivtv_start_capture(struct ivtv_open_id *id)
{
struct ivtv *itv = id->itv;
struct ivtv_stream *s = &itv->streams[id->type];
struct ivtv_stream *s_vbi;
if (s->type == IVTV_ENC_STREAM_TYPE_RAD ||
s->type == IVTV_DEC_STREAM_TYPE_MPG ||
s->type == IVTV_DEC_STREAM_TYPE_YUV ||
s->type == IVTV_DEC_STREAM_TYPE_VOUT) {
/* you cannot read from these stream types. */
return -EPERM;
}
/* Try to claim this stream. */
if (ivtv_claim_stream(id, s->type))
return -EBUSY;
/* This stream does not need to start capturing */
if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
return 0;
}
/* If capture is already in progress, then we also have to
do nothing extra. */
if (test_bit(IVTV_F_S_STREAMOFF, &s->s_flags) || test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
return 0;
}
/* Start VBI capture if required */
s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
if (s->type == IVTV_ENC_STREAM_TYPE_MPG &&
test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
!test_and_set_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) {
/* Note: the IVTV_ENC_STREAM_TYPE_VBI is claimed
automatically when the MPG stream is claimed.
We only need to start the VBI capturing. */
if (ivtv_start_v4l2_encode_stream(s_vbi)) {
IVTV_DEBUG_WARN("VBI capture start failed\n");
/* Failure, clean up and return an error */
clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags);
clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
/* also releases the associated VBI stream */
ivtv_release_stream(s);
return -EIO;
}
IVTV_DEBUG_INFO("VBI insertion started\n");
}
/* Tell the card to start capturing */
if (!ivtv_start_v4l2_encode_stream(s)) {
/* We're done */
set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
/* Resume a possibly paused encoder */
if (test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1);
return 0;
}
/* failure, clean up */
IVTV_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name);
/* Note: the IVTV_ENC_STREAM_TYPE_VBI is released
automatically when the MPG stream is released.
We only need to stop the VBI capturing. */
if (s->type == IVTV_ENC_STREAM_TYPE_MPG &&
test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) {
ivtv_stop_v4l2_encode_stream(s_vbi, 0);
clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags);
}
clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
ivtv_release_stream(s);
return -EIO;
}
ssize_t ivtv_v4l2_read(struct file * filp, char __user *buf, size_t count, loff_t * pos)
{
struct ivtv_open_id *id = filp->private_data;
struct ivtv *itv = id->itv;
struct ivtv_stream *s = &itv->streams[id->type];
int rc;
IVTV_DEBUG_IOCTL("read %zd bytes from %s\n", count, s->name);
rc = ivtv_start_capture(id);
if (rc)
return rc;
return ivtv_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK);
}
int ivtv_start_decoding(struct ivtv_open_id *id, int speed)
{
struct ivtv *itv = id->itv;
struct ivtv_stream *s = &itv->streams[id->type];
if (atomic_read(&itv->decoding) == 0) {
if (ivtv_claim_stream(id, s->type)) {
/* someone else is using this stream already */
IVTV_DEBUG_WARN("start decode, stream already claimed\n");
return -EBUSY;
}
ivtv_start_v4l2_decode_stream(s, 0);
}
if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
return ivtv_set_speed(itv, speed);
return 0;
}
ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *pos)
{
struct ivtv_open_id *id = filp->private_data;
struct ivtv *itv = id->itv;
struct ivtv_stream *s = &itv->streams[id->type];
struct ivtv_buffer *buf;
struct ivtv_queue q;
int bytes_written = 0;
int mode;
int rc;
DEFINE_WAIT(wait);
IVTV_DEBUG_IOCTL("write %zd bytes to %s\n", count, s->name);
if (s->type != IVTV_DEC_STREAM_TYPE_MPG &&
s->type != IVTV_DEC_STREAM_TYPE_YUV &&
s->type != IVTV_DEC_STREAM_TYPE_VOUT)
/* not decoder streams */
return -EPERM;
/* Try to claim this stream */
if (ivtv_claim_stream(id, s->type))
return -EBUSY;
/* This stream does not need to start any decoding */
if (s->type == IVTV_DEC_STREAM_TYPE_VOUT) {
set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
return ivtv_write_vbi(itv, user_buf, count);
}
mode = s->type == IVTV_DEC_STREAM_TYPE_MPG ? OUT_MPG : OUT_YUV;
if (ivtv_set_output_mode(itv, mode) != mode) {
ivtv_release_stream(s);
return -EBUSY;
}
ivtv_queue_init(&q);
set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
retry:
for (;;) {
/* Gather buffers */
while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_io)))
ivtv_enqueue(s, buf, &q);
while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_free))) {
ivtv_enqueue(s, buf, &q);
}
if (q.buffers)
break;
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
/* New buffers might have become free before we were added to the waitqueue */
if (!s->q_free.buffers)
schedule();
finish_wait(&s->waitq, &wait);
if (signal_pending(current)) {
IVTV_DEBUG_INFO("User stopped %s\n", s->name);
return -EINTR;
}
}
/* copy user data into buffers */
while ((buf = ivtv_dequeue(s, &q))) {
/* Make sure we really got all the user data */
rc = ivtv_buf_copy_from_user(s, buf, user_buf, count);
if (rc < 0) {
ivtv_queue_move(s, &q, NULL, &s->q_free, 0);
return rc;
}
user_buf += rc;
count -= rc;
bytes_written += rc;
if (buf->bytesused != s->buf_size) {
/* incomplete, leave in q_io for next time */
ivtv_enqueue(s, buf, &s->q_io);
break;
}
/* Byteswap MPEG buffer */
if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
ivtv_buf_swap(buf);
ivtv_enqueue(s, buf, &s->q_full);
}
/* Start decoder (returns 0 if already started) */
rc = ivtv_start_decoding(id, itv->speed);
if (rc) {
IVTV_DEBUG_WARN("Failed start decode stream %s\n", s->name);
/* failure, clean up */
clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
return rc;
}
if (test_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags)) {
if (s->q_full.length >= itv->dma_data_req_size) {
int got_sig;
prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
while (!(got_sig = signal_pending(current)) &&
test_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) {
schedule();
}
finish_wait(&itv->dma_waitq, &wait);
if (got_sig) {
IVTV_DEBUG_INFO("User interrupted %s\n", s->name);
return -EINTR;
}
clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size);
ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 1);
}
}
/* more user data is available, wait until buffers become free
to transfer the rest. */
if (count && !(filp->f_flags & O_NONBLOCK))
goto retry;
IVTV_DEBUG_INFO("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused);
return bytes_written;
}
unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait)
{
struct ivtv_open_id *id = filp->private_data;
struct ivtv *itv = id->itv;
struct ivtv_stream *s = &itv->streams[id->type];
int res = 0;
/* add stream's waitq to the poll list */
poll_wait(filp, &s->waitq, wait);
set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
if (test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags) ||
test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags))
res = POLLPRI;
/* Allow write if buffers are available for writing */
if (s->q_free.buffers)
res |= POLLOUT | POLLWRNORM;
return res;
}
unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait)
{
struct ivtv_open_id *id = filp->private_data;
struct ivtv *itv = id->itv;
struct ivtv_stream *s = &itv->streams[id->type];
int eof = test_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
/* Start a capture if there is none */
if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
int rc = ivtv_start_capture(id);
if (rc) {
IVTV_DEBUG_INFO("Could not start capture for %s (%d)\n",
s->name, rc);
return POLLERR;
}
}
/* add stream's waitq to the poll list */
poll_wait(filp, &s->waitq, wait);
if (eof || s->q_full.length)
return POLLIN | POLLRDNORM;
return 0;
}
void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end)
{
struct ivtv *itv = id->itv;
struct ivtv_stream *s = &itv->streams[id->type];
IVTV_DEBUG_IOCTL("close() of %s\n", s->name);
/* 'Unclaim' this stream */
/* Stop capturing */
if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
IVTV_DEBUG_INFO("close stopping capture\n");
/* Special case: a running VBI capture for VBI insertion
in the mpeg stream. Need to stop that too. */
if (id->type == IVTV_ENC_STREAM_TYPE_MPG &&
test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags) &&
!test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) {
IVTV_DEBUG_INFO("close stopping embedded VBI capture\n");
ivtv_stop_v4l2_encode_stream(s_vbi, 0);
}
if ((id->type == IVTV_DEC_STREAM_TYPE_VBI ||
id->type == IVTV_ENC_STREAM_TYPE_VBI) &&
test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) {
/* Also used internally, don't stop capturing */
s->id = -1;
}
else {
ivtv_stop_v4l2_encode_stream(s, gop_end);
}
}
clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
ivtv_release_stream(s);
}
void ivtv_stop_decoding(struct ivtv_open_id *id, int flags, u64 pts)
{
struct ivtv *itv = id->itv;
struct ivtv_stream *s = &itv->streams[id->type];
IVTV_DEBUG_IOCTL("close() of %s\n", s->name);
/* Stop decoding */
if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
IVTV_DEBUG_INFO("close stopping decode\n");
ivtv_stop_v4l2_decode_stream(s, flags, pts);
}
clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
if (id->type == IVTV_DEC_STREAM_TYPE_YUV && test_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags)) {
/* Restore registers we've changed & clean up any mess we've made */
ivtv_yuv_close(itv);
}
if (s->type == IVTV_DEC_STREAM_TYPE_YUV && itv->output_mode == OUT_YUV)
itv->output_mode = OUT_NONE;
else if (s->type == IVTV_DEC_STREAM_TYPE_MPG && itv->output_mode == OUT_MPG)
itv->output_mode = OUT_NONE;
itv->speed = 0;
ivtv_release_stream(s);
}
int ivtv_v4l2_close(struct inode *inode, struct file *filp)
{
struct ivtv_open_id *id = filp->private_data;
struct ivtv *itv = id->itv;
struct ivtv_stream *s = &itv->streams[id->type];
IVTV_DEBUG_IOCTL("close() of %s\n", s->name);
/* Easy case first: this stream was never claimed by us */
if (s->id != id->open_id) {
kfree(id);
return 0;
}
/* 'Unclaim' this stream */
/* Stop radio */
if (id->type == IVTV_ENC_STREAM_TYPE_RAD) {
/* Closing radio device, return to TV mode */
ivtv_mute(itv);
/* Mark that the radio is no longer in use */
clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
/* Switch tuner to TV */
ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std);
/* Select correct audio input (i.e. TV tuner or Line in) */
ivtv_audio_set_io(itv);
/* Done! Unmute and continue. */
ivtv_unmute(itv);
ivtv_release_stream(s);
} else if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) {
ivtv_stop_decoding(id, VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0);
} else {
ivtv_stop_capture(id, 0);
}
kfree(id);
return 0;
}
int ivtv_v4l2_open(struct inode *inode, struct file *filp)
{
int x, y = 0;
struct ivtv_open_id *item;
struct ivtv *itv = NULL;
struct ivtv_stream *s = NULL;
int minor = MINOR(inode->i_rdev);
/* Find which card this open was on */
spin_lock(&ivtv_cards_lock);
for (x = 0; itv == NULL && x < ivtv_cards_active; x++) {
/* find out which stream this open was on */
for (y = 0; y < IVTV_MAX_STREAMS; y++) {
s = &ivtv_cards[x]->streams[y];
if (s->v4l2dev && s->v4l2dev->minor == minor) {
itv = ivtv_cards[x];
break;
}
}
}
spin_unlock(&ivtv_cards_lock);
if (itv == NULL) {
/* Couldn't find a device registered
on that minor, shouldn't happen! */
printk(KERN_WARNING "ivtv: no ivtv device found on minor %d\n", minor);
return -ENXIO;
}
if (y == IVTV_DEC_STREAM_TYPE_MPG &&
test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_YUV].s_flags))
return -EBUSY;
if (y == IVTV_DEC_STREAM_TYPE_YUV &&
test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_MPG].s_flags))
return -EBUSY;
if (y == IVTV_DEC_STREAM_TYPE_YUV) {
if (read_reg(0x82c) == 0) {
IVTV_ERR("Tried to open YUV output device but need to send data to mpeg decoder before it can be used\n");
/* return -ENODEV; */
}
ivtv_udma_alloc(itv);
}
/* Allocate memory */
item = kmalloc(sizeof(struct ivtv_open_id), GFP_KERNEL);
if (NULL == item) {
IVTV_DEBUG_WARN("nomem on v4l2 open\n");
return -ENOMEM;
}
item->itv = itv;
item->type = y;
item->open_id = itv->open_id++;
filp->private_data = item;
if (item->type == IVTV_ENC_STREAM_TYPE_RAD) {
/* Try to claim this stream */
if (ivtv_claim_stream(item, item->type)) {
/* No, it's already in use */
kfree(item);
return -EBUSY;
}
/* We have the radio */
ivtv_mute(itv);
/* Switch tuner to radio */
ivtv_call_i2c_clients(itv, AUDC_SET_RADIO, NULL);
/* Mark that the radio is being used. */
set_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
/* Select the correct audio input (i.e. radio tuner) */
ivtv_audio_set_io(itv);
/* Done! Unmute and continue. */
ivtv_unmute(itv);
}
/* YUV or MPG Decoding Mode? */
if (y == IVTV_DEC_STREAM_TYPE_MPG)
clear_bit(IVTV_F_I_DEC_YUV, &itv->i_flags);
else if (y == IVTV_DEC_STREAM_TYPE_YUV)
{
set_bit(IVTV_F_I_DEC_YUV, &itv->i_flags);
}
return 0;
}
void ivtv_mute(struct ivtv *itv)
{
struct v4l2_control ctrl = { V4L2_CID_AUDIO_MUTE, 1 };
/* Mute sound to avoid pop */
ivtv_control_ioctls(itv, VIDIOC_S_CTRL, &ctrl);
if (atomic_read(&itv->capturing))
ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 1);
IVTV_DEBUG_INFO("Mute\n");
}
void ivtv_unmute(struct ivtv *itv)
{
struct v4l2_control ctrl = { V4L2_CID_AUDIO_MUTE, 0 };
/* initialize or refresh input */
if (atomic_read(&itv->capturing) == 0)
ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
ivtv_sleep_timeout(HZ / 10, 0);
if (atomic_read(&itv->capturing)) {
ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12);
ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 0);
}
/* Unmute */
ivtv_control_ioctls(itv, VIDIOC_S_CTRL, &ctrl);
IVTV_DEBUG_INFO("Unmute\n");
}

View File

@ -0,0 +1,45 @@
/*
file operation functions
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Testing/Debugging */
int ivtv_v4l2_open(struct inode *inode, struct file *filp);
ssize_t ivtv_v4l2_read(struct file *filp, char __user *buf, size_t count,
loff_t * pos);
ssize_t ivtv_v4l2_write(struct file *filp, const char __user *buf, size_t count,
loff_t * pos);
int ivtv_v4l2_close(struct inode *inode, struct file *filp);
unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait);
unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table * wait);
int ivtv_start_capture(struct ivtv_open_id *id);
void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end);
int ivtv_start_decoding(struct ivtv_open_id *id, int speed);
void ivtv_stop_decoding(struct ivtv_open_id *id, int flags, u64 pts);
void ivtv_mute(struct ivtv *itv);
void ivtv_unmute(struct ivtv *itv);
/* Utilities */
/* Try to claim a stream for the filehandle. Return 0 on success,
-EBUSY if stream already claimed. Once a stream is claimed, it
remains claimed until the associated filehandle is closed. */
int ivtv_claim_stream(struct ivtv_open_id *id, int type);
/* Release a previously claimed stream. */
void ivtv_release_stream(struct ivtv_stream *s);

View File

@ -0,0 +1,272 @@
/*
ivtv firmware functions.
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2004 Chris Kennedy <c@groovy.org>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ivtv-driver.h"
#include "ivtv-mailbox.h"
#include "ivtv-firmware.h"
#include <linux/firmware.h>
#define IVTV_MASK_SPU_ENABLE 0xFFFFFFFE
#define IVTV_MASK_VPU_ENABLE15 0xFFFFFFF6
#define IVTV_MASK_VPU_ENABLE16 0xFFFFFFFB
#define IVTV_CMD_VDM_STOP 0x00000000
#define IVTV_CMD_AO_STOP 0x00000005
#define IVTV_CMD_APU_PING 0x00000000
#define IVTV_CMD_VPU_STOP15 0xFFFFFFFE
#define IVTV_CMD_VPU_STOP16 0xFFFFFFEE
#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF
#define IVTV_CMD_SPU_STOP 0x00000001
#define IVTV_CMD_SDRAM_PRECHARGE_INIT 0x0000001A
#define IVTV_CMD_SDRAM_REFRESH_INIT 0x80000640
#define IVTV_SDRAM_SLEEPTIME (60 * HZ / 100) /* 600 ms */
#define IVTV_DECODE_INIT_MPEG_FILENAME "v4l-cx2341x-init.mpg"
#define IVTV_DECODE_INIT_MPEG_SIZE (152*1024)
/* Encoder/decoder firmware sizes */
#define IVTV_FW_ENC_SIZE (376836)
#define IVTV_FW_DEC_SIZE (256*1024)
static int load_fw_direct(const char *fn, volatile u8 __iomem *mem, struct ivtv *itv, long size)
{
const struct firmware *fw = NULL;
int retries = 3;
retry:
if (retries && request_firmware(&fw, fn, &itv->dev->dev) == 0) {
int i;
volatile u32 __iomem *dst = (volatile u32 __iomem *)mem;
const u32 *src = (const u32 *)fw->data;
/* temporarily allow 256 KB encoding firmwares as well for
compatibility with blackbird cards */
if (fw->size != size && fw->size != 256 * 1024) {
/* Due to race conditions in firmware loading (esp. with udev <0.95)
the wrong file was sometimes loaded. So we check filesizes to
see if at least the right-sized file was loaded. If not, then we
retry. */
IVTV_INFO("retry: file loaded was not %s (expected size %ld, got %zd)\n", fn, size, fw->size);
release_firmware(fw);
retries--;
goto retry;
}
for (i = 0; i < fw->size; i += 4) {
/* no need for endianness conversion on the ppc */
__raw_writel(*src, dst);
dst++;
src++;
}
release_firmware(fw);
IVTV_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size);
return size;
}
IVTV_ERR("unable to open firmware %s (must be %ld bytes)\n", fn, size);
IVTV_ERR("did you put the firmware in the hotplug firmware directory?\n");
return -ENOMEM;
}
void ivtv_halt_firmware(struct ivtv *itv)
{
IVTV_DEBUG_INFO("Preparing for firmware halt.\n");
if (itv->has_cx23415 && itv->dec_mbox.mbox)
ivtv_vapi(itv, CX2341X_DEC_HALT_FW, 0);
if (itv->enc_mbox.mbox)
ivtv_vapi(itv, CX2341X_ENC_HALT_FW, 0);
ivtv_sleep_timeout(HZ / 100, 0);
itv->enc_mbox.mbox = itv->dec_mbox.mbox = NULL;
IVTV_DEBUG_INFO("Stopping VDM\n");
write_reg(IVTV_CMD_VDM_STOP, IVTV_REG_VDM);
IVTV_DEBUG_INFO("Stopping AO\n");
write_reg(IVTV_CMD_AO_STOP, IVTV_REG_AO);
IVTV_DEBUG_INFO("pinging (?) APU\n");
write_reg(IVTV_CMD_APU_PING, IVTV_REG_APU);
IVTV_DEBUG_INFO("Stopping VPU\n");
if (!itv->has_cx23415)
write_reg(IVTV_CMD_VPU_STOP16, IVTV_REG_VPU);
else
write_reg(IVTV_CMD_VPU_STOP15, IVTV_REG_VPU);
IVTV_DEBUG_INFO("Resetting Hw Blocks\n");
write_reg(IVTV_CMD_HW_BLOCKS_RST, IVTV_REG_HW_BLOCKS);
IVTV_DEBUG_INFO("Stopping SPU\n");
write_reg(IVTV_CMD_SPU_STOP, IVTV_REG_SPU);
ivtv_sleep_timeout(HZ / 100, 0);
IVTV_DEBUG_INFO("init Encoder SDRAM pre-charge\n");
write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_ENC_SDRAM_PRECHARGE);
IVTV_DEBUG_INFO("init Encoder SDRAM refresh to 1us\n");
write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_ENC_SDRAM_REFRESH);
if (itv->has_cx23415) {
IVTV_DEBUG_INFO("init Decoder SDRAM pre-charge\n");
write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_DEC_SDRAM_PRECHARGE);
IVTV_DEBUG_INFO("init Decoder SDRAM refresh to 1us\n");
write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_DEC_SDRAM_REFRESH);
}
IVTV_DEBUG_INFO("Sleeping for %dms (600 recommended)\n",
(int)(IVTV_SDRAM_SLEEPTIME * 1000 / HZ));
ivtv_sleep_timeout(IVTV_SDRAM_SLEEPTIME, 0);
}
void ivtv_firmware_versions(struct ivtv *itv)
{
u32 data[CX2341X_MBOX_MAX_DATA];
/* Encoder */
ivtv_vapi_result(itv, data, CX2341X_ENC_GET_VERSION, 0);
IVTV_INFO("Encoder revision: 0x%08x\n", data[0]);
if (data[0] != 0x02060039)
IVTV_WARN("Recommended firmware version is 0x02060039.\n");
if (itv->has_cx23415) {
/* Decoder */
ivtv_vapi_result(itv, data, CX2341X_DEC_GET_VERSION, 0);
IVTV_INFO("Decoder revision: 0x%08x\n", data[0]);
}
}
static int ivtv_firmware_copy(struct ivtv *itv)
{
IVTV_DEBUG_INFO("Loading encoder image\n");
if (load_fw_direct(CX2341X_FIRM_ENC_FILENAME,
itv->enc_mem, itv, IVTV_FW_ENC_SIZE) != IVTV_FW_ENC_SIZE) {
IVTV_DEBUG_WARN("failed loading encoder firmware\n");
return -3;
}
if (!itv->has_cx23415)
return 0;
IVTV_DEBUG_INFO("Loading decoder image\n");
if (load_fw_direct(CX2341X_FIRM_DEC_FILENAME,
itv->dec_mem, itv, IVTV_FW_DEC_SIZE) != IVTV_FW_DEC_SIZE) {
IVTV_DEBUG_WARN("failed loading decoder firmware\n");
return -1;
}
return 0;
}
static volatile struct ivtv_mailbox __iomem *ivtv_search_mailbox(const volatile u8 __iomem *mem, u32 size)
{
int i;
/* mailbox is preceeded by a 16 byte 'magic cookie' starting at a 256-byte
address boundary */
for (i = 0; i < size; i += 0x100) {
if (readl(mem + i) == 0x12345678 &&
readl(mem + i + 4) == 0x34567812 &&
readl(mem + i + 8) == 0x56781234 &&
readl(mem + i + 12) == 0x78123456) {
return (volatile struct ivtv_mailbox __iomem *)(mem + i + 16);
}
}
return NULL;
}
int ivtv_firmware_init(struct ivtv *itv)
{
int err;
ivtv_halt_firmware(itv);
/* load firmware */
err = ivtv_firmware_copy(itv);
if (err) {
IVTV_DEBUG_WARN("Error %d loading firmware\n", err);
return err;
}
/* start firmware */
write_reg(read_reg(IVTV_REG_SPU) & IVTV_MASK_SPU_ENABLE, IVTV_REG_SPU);
ivtv_sleep_timeout(HZ / 10, 0);
if (itv->has_cx23415)
write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE15, IVTV_REG_VPU);
else
write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE16, IVTV_REG_VPU);
ivtv_sleep_timeout(HZ / 10, 0);
/* find mailboxes and ping firmware */
itv->enc_mbox.mbox = ivtv_search_mailbox(itv->enc_mem, IVTV_ENCODER_SIZE);
if (itv->enc_mbox.mbox == NULL)
IVTV_ERR("Encoder mailbox not found\n");
else if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0)) {
IVTV_ERR("Encoder firmware dead!\n");
itv->enc_mbox.mbox = NULL;
}
if (itv->enc_mbox.mbox == NULL)
return -ENODEV;
if (!itv->has_cx23415)
return 0;
itv->dec_mbox.mbox = ivtv_search_mailbox(itv->dec_mem, IVTV_DECODER_SIZE);
if (itv->dec_mbox.mbox == NULL)
IVTV_ERR("Decoder mailbox not found\n");
else if (itv->has_cx23415 && ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0)) {
IVTV_ERR("Decoder firmware dead!\n");
itv->dec_mbox.mbox = NULL;
}
return itv->dec_mbox.mbox ? 0 : -ENODEV;
}
void ivtv_init_mpeg_decoder(struct ivtv *itv)
{
u32 data[CX2341X_MBOX_MAX_DATA];
long readbytes;
volatile u8 __iomem *mem_offset;
data[0] = 0;
data[1] = itv->params.width; /* YUV source width */
data[2] = itv->params.height;
data[3] = itv->params.audio_properties; /* Audio settings to use,
bitmap. see docs. */
if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) {
IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n");
return;
}
if (ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1) != 0) {
IVTV_ERR("ivtv_init_mpeg_decoder failed to start playback\n");
return;
}
ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data);
mem_offset = itv->dec_mem + data[1];
if ((readbytes = load_fw_direct(IVTV_DECODE_INIT_MPEG_FILENAME,
mem_offset, itv, IVTV_DECODE_INIT_MPEG_SIZE)) <= 0) {
IVTV_DEBUG_WARN("failed to read mpeg decoder initialisation file %s\n",
IVTV_DECODE_INIT_MPEG_FILENAME);
} else {
ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, readbytes, 0);
ivtv_sleep_timeout(HZ / 10, 0);
}
ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 4, 0, 0, 0, 1);
}

View File

@ -0,0 +1,25 @@
/*
ivtv firmware functions.
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2004 Chris Kennedy <c@groovy.org>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
int ivtv_firmware_init(struct ivtv *itv);
void ivtv_firmware_versions(struct ivtv *itv);
void ivtv_halt_firmware(struct ivtv *itv);
void ivtv_init_mpeg_decoder(struct ivtv *itv);

View File

@ -0,0 +1,307 @@
/*
gpio functions.
Merging GPIO support into driver:
Copyright (C) 2004 Chris Kennedy <c@groovy.org>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ivtv-driver.h"
#include "ivtv-cards.h"
#include "ivtv-gpio.h"
#include <media/tuner.h>
/*
* GPIO assignment of Yuan MPG600/MPG160
*
* bit 15 14 13 12 | 11 10 9 8 | 7 6 5 4 | 3 2 1 0
* OUTPUT IN1 IN0 AM3 AM2 AM1 AM0
* INPUT DM1 DM0
*
* IN* : Input selection
* IN1 IN0
* 1 1 N/A
* 1 0 Line
* 0 1 N/A
* 0 0 Tuner
*
* AM* : Audio Mode
* AM3 0: Normal 1: Mixed(Sub+Main channel)
* AM2 0: Subchannel 1: Main channel
* AM1 0: Stereo 1: Mono
* AM0 0: Normal 1: Mute
*
* DM* : Detected tuner audio Mode
* DM1 0: Stereo 1: Mono
* DM0 0: Multiplex 1: Normal
*
* GPIO Initial Settings
* MPG600 MPG160
* DIR 0x3080 0x7080
* OUTPUT 0x000C 0x400C
*
* Special thanks to Makoto Iguchi <iguchi@tahoo.org> and Mr. Anonymous
* for analyzing GPIO of MPG160.
*
*****************************************************************************
*
* GPIO assignment of Avermedia M179 (per information direct from AVerMedia)
*
* bit 15 14 13 12 | 11 10 9 8 | 7 6 5 4 | 3 2 1 0
* OUTPUT IN0 AM0 IN1 AM1 AM2 IN2 BR0 BR1
* INPUT
*
* IN* : Input selection
* IN0 IN1 IN2
* * 1 * Mute
* 0 0 0 Line-In
* 1 0 0 TV Tuner Audio
* 0 0 1 FM Audio
* 1 0 1 Mute
*
* AM* : Audio Mode
* AM0 AM1 AM2
* 0 0 0 TV Tuner Audio: L_OUT=(L+R)/2, R_OUT=SAP
* 0 0 1 TV Tuner Audio: L_OUT=R_OUT=SAP (SAP)
* 0 1 0 TV Tuner Audio: L_OUT=L, R_OUT=R (stereo)
* 0 1 1 TV Tuner Audio: mute
* 1 * * TV Tuner Audio: L_OUT=R_OUT=(L+R)/2 (mono)
*
* BR* : Audio Sample Rate (BR stands for bitrate for some reason)
* BR0 BR1
* 0 0 32 kHz
* 0 1 44.1 kHz
* 1 0 48 kHz
*
* DM* : Detected tuner audio Mode
* Unknown currently
*
* Special thanks to AVerMedia Technologies, Inc. and Jiun-Kuei Jung at
* AVerMedia for providing the GPIO information used to add support
* for the M179 cards.
*/
/********************* GPIO stuffs *********************/
/* GPIO registers */
#define IVTV_REG_GPIO_IN 0x9008
#define IVTV_REG_GPIO_OUT 0x900c
#define IVTV_REG_GPIO_DIR 0x9020
void ivtv_reset_ir_gpio(struct ivtv *itv)
{
int curdir, curout;
if (itv->card->type != IVTV_CARD_PVR_150)
return;
IVTV_DEBUG_INFO("Resetting PVR150 IR\n");
curout = read_reg(IVTV_REG_GPIO_OUT);
curdir = read_reg(IVTV_REG_GPIO_DIR);
curdir |= 0x80;
write_reg(curdir, IVTV_REG_GPIO_DIR);
curout = (curout & ~0xF) | 1;
write_reg(curout, IVTV_REG_GPIO_OUT);
/* We could use something else for smaller time */
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(1);
curout |= 2;
write_reg(curout, IVTV_REG_GPIO_OUT);
curdir &= ~0x80;
write_reg(curdir, IVTV_REG_GPIO_DIR);
}
#ifdef HAVE_XC3028
int ivtv_reset_tuner_gpio(enum v4l2_tuner_type mode, void *priv, int ptr)
{
int curdir, curout;
struct ivtv *itv = (struct ivtv *) priv;
if (itv->card->type != IVTV_CARD_PG600V2 || itv->options.tuner != TUNER_XCEIVE_XC3028)
return -EINVAL;
IVTV_INFO("Resetting tuner.\n");
curout = read_reg(IVTV_REG_GPIO_OUT);
curdir = read_reg(IVTV_REG_GPIO_DIR);
curdir |= (1 << 12); /* GPIO bit 12 */
curout &= ~(1 << 12);
write_reg(curout, IVTV_REG_GPIO_OUT);
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(1);
curout |= (1 << 12);
write_reg(curout, IVTV_REG_GPIO_OUT);
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(1);
return 0;
}
#endif
void ivtv_gpio_init(struct ivtv *itv)
{
if (itv->card->gpio_init.direction == 0)
return;
IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n",
read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT));
/* init output data then direction */
write_reg(itv->card->gpio_init.initial_value, IVTV_REG_GPIO_OUT);
write_reg(itv->card->gpio_init.direction, IVTV_REG_GPIO_DIR);
}
static struct v4l2_queryctrl gpio_ctrl_mute = {
.id = V4L2_CID_AUDIO_MUTE,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Mute",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1,
.flags = 0,
};
int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg)
{
struct v4l2_tuner *tuner = arg;
struct v4l2_control *ctrl = arg;
struct v4l2_routing *route = arg;
u16 mask, data;
switch (command) {
case VIDIOC_INT_AUDIO_CLOCK_FREQ:
mask = itv->card->gpio_audio_freq.mask;
switch (*(u32 *)arg) {
case 32000:
data = itv->card->gpio_audio_freq.f32000;
break;
case 44100:
data = itv->card->gpio_audio_freq.f44100;
break;
case 48000:
default:
data = itv->card->gpio_audio_freq.f48000;
break;
}
break;
case VIDIOC_G_TUNER:
mask = itv->card->gpio_audio_detect.mask;
if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask))
tuner->rxsubchans = V4L2_TUNER_MODE_STEREO |
V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
else
tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
return 0;
case VIDIOC_S_TUNER:
mask = itv->card->gpio_audio_mode.mask;
switch (tuner->audmode) {
case V4L2_TUNER_MODE_LANG1:
data = itv->card->gpio_audio_mode.lang1;
break;
case V4L2_TUNER_MODE_LANG2:
data = itv->card->gpio_audio_mode.lang2;
break;
case V4L2_TUNER_MODE_MONO:
data = itv->card->gpio_audio_mode.mono;
break;
case V4L2_TUNER_MODE_STEREO:
case V4L2_TUNER_MODE_LANG1_LANG2:
default:
data = itv->card->gpio_audio_mode.stereo;
break;
}
break;
case AUDC_SET_RADIO:
mask = itv->card->gpio_audio_input.mask;
data = itv->card->gpio_audio_input.radio;
break;
case VIDIOC_S_STD:
mask = itv->card->gpio_audio_input.mask;
data = itv->card->gpio_audio_input.tuner;
break;
case VIDIOC_INT_S_AUDIO_ROUTING:
if (route->input > 2)
return -EINVAL;
mask = itv->card->gpio_audio_input.mask;
switch (route->input) {
case 0:
data = itv->card->gpio_audio_input.tuner;
break;
case 1:
data = itv->card->gpio_audio_input.linein;
break;
case 2:
default:
data = itv->card->gpio_audio_input.radio;
break;
}
break;
case VIDIOC_G_CTRL:
if (ctrl->id != V4L2_CID_AUDIO_MUTE)
return -EINVAL;
mask = itv->card->gpio_audio_mute.mask;
data = itv->card->gpio_audio_mute.mute;
ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data;
return 0;
case VIDIOC_S_CTRL:
if (ctrl->id != V4L2_CID_AUDIO_MUTE)
return -EINVAL;
mask = itv->card->gpio_audio_mute.mask;
data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0;
break;
case VIDIOC_QUERYCTRL:
{
struct v4l2_queryctrl *qc = arg;
if (qc->id != V4L2_CID_AUDIO_MUTE)
return -EINVAL;
*qc = gpio_ctrl_mute;
return 0;
}
case VIDIOC_LOG_STATUS:
IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n",
read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT),
read_reg(IVTV_REG_GPIO_IN));
return 0;
case VIDIOC_INT_S_VIDEO_ROUTING:
if (route->input > 2) /* 0:Tuner 1:Composite 2:S-Video */
return -EINVAL;
mask = itv->card->gpio_video_input.mask;
if (route->input == 0)
data = itv->card->gpio_video_input.tuner;
else if (route->input == 1)
data = itv->card->gpio_video_input.composite;
else
data = itv->card->gpio_video_input.svideo;
break;
default:
return -EINVAL;
}
if (mask)
write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
return 0;
}

View File

@ -0,0 +1,25 @@
/*
gpio functions.
Copyright (C) 2004 Chris Kennedy <c@groovy.org>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* GPIO stuff */
void ivtv_gpio_init(struct ivtv *itv);
void ivtv_reset_ir_gpio(struct ivtv *itv);
int ivtv_reset_tuner_gpio(enum v4l2_tuner_type mode, void *priv, int ptr);
int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg);

View File

@ -0,0 +1,750 @@
/*
I2C functions
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
This file includes an i2c implementation that was reverse engineered
from the Hauppauge windows driver. Older ivtv versions used i2c-algo-bit,
which whilst fine under most circumstances, had trouble with the Zilog
CPU on the PVR-150 which handles IR functions (occasional inability to
communicate with the chip until it was reset) and also with the i2c
bus being completely unreachable when multiple PVR cards were present.
The implementation is very similar to i2c-algo-bit, but there are enough
subtle differences that the two are hard to merge. The general strategy
employed by i2c-algo-bit is to use udelay() to implement the timing
when putting out bits on the scl/sda lines. The general strategy taken
here is to poll the lines for state changes (see ivtv_waitscl and
ivtv_waitsda). In addition there are small delays at various locations
which poll the SCL line 5 times (ivtv_scldelay). I would guess that
since this is memory mapped I/O that the length of those delays is tied
to the PCI bus clock. There is some extra code to do with recovery
and retries. Since it is not known what causes the actual i2c problems
in the first place, the only goal if one was to attempt to use
i2c-algo-bit would be to try to make it follow the same code path.
This would be a lot of work, and I'm also not convinced that it would
provide a generic benefit to i2c-algo-bit. Therefore consider this
an engineering solution -- not pretty, but it works.
Some more general comments about what we are doing:
The i2c bus is a 2 wire serial bus, with clock (SCL) and data (SDA)
lines. To communicate on the bus (as a master, we don't act as a slave),
we first initiate a start condition (ivtv_start). We then write the
address of the device that we want to communicate with, along with a flag
that indicates whether this is a read or a write. The slave then issues
an ACK signal (ivtv_ack), which tells us that it is ready for reading /
writing. We then proceed with reading or writing (ivtv_read/ivtv_write),
and finally issue a stop condition (ivtv_stop) to make the bus available
to other masters.
There is an additional form of transaction where a write may be
immediately followed by a read. In this case, there is no intervening
stop condition. (Only the msp3400 chip uses this method of data transfer).
*/
#include "ivtv-driver.h"
#include "ivtv-cards.h"
#include "ivtv-gpio.h"
#include <media/ir-kbd-i2c.h>
/* i2c implementation for cx23415/6 chip, ivtv project.
* Author: Kevin Thayer (nufan_wfk at yahoo.com)
*/
/* i2c stuff */
#define IVTV_REG_I2C_SETSCL_OFFSET 0x7000
#define IVTV_REG_I2C_SETSDA_OFFSET 0x7004
#define IVTV_REG_I2C_GETSCL_OFFSET 0x7008
#define IVTV_REG_I2C_GETSDA_OFFSET 0x700c
#ifndef I2C_ADAP_CLASS_TV_ANALOG
#define I2C_ADAP_CLASS_TV_ANALOG I2C_CLASS_TV_ANALOG
#endif /* I2C_ADAP_CLASS_TV_ANALOG */
#define IVTV_CS53L32A_I2C_ADDR 0x11
#define IVTV_CX25840_I2C_ADDR 0x44
#define IVTV_SAA7115_I2C_ADDR 0x21
#define IVTV_SAA7127_I2C_ADDR 0x44
#define IVTV_SAA717x_I2C_ADDR 0x21
#define IVTV_MSP3400_I2C_ADDR 0x40
#define IVTV_HAUPPAUGE_I2C_ADDR 0x50
#define IVTV_WM8739_I2C_ADDR 0x1a
#define IVTV_WM8775_I2C_ADDR 0x1b
#define IVTV_TEA5767_I2C_ADDR 0x60
#define IVTV_UPD64031A_I2C_ADDR 0x12
#define IVTV_UPD64083_I2C_ADDR 0x5c
#define IVTV_TDA985X_I2C_ADDR 0x5b
/* This array should match the IVTV_HW_ defines */
static const u8 hw_driverids[] = {
I2C_DRIVERID_CX25840,
I2C_DRIVERID_SAA711X,
I2C_DRIVERID_SAA7127,
I2C_DRIVERID_MSP3400,
I2C_DRIVERID_TUNER,
I2C_DRIVERID_WM8775,
I2C_DRIVERID_CS53L32A,
I2C_DRIVERID_TVEEPROM,
I2C_DRIVERID_SAA711X,
I2C_DRIVERID_TVAUDIO,
I2C_DRIVERID_UPD64031A,
I2C_DRIVERID_UPD64083,
I2C_DRIVERID_SAA717X,
I2C_DRIVERID_WM8739,
0 /* IVTV_HW_GPIO dummy driver ID */
};
/* This array should match the IVTV_HW_ defines */
static const char * const hw_drivernames[] = {
"cx2584x",
"saa7115",
"saa7127",
"msp3400",
"tuner",
"wm8775",
"cs53l32a",
"tveeprom",
"saa7114",
"tvaudio",
"upd64031a",
"upd64083",
"saa717x",
"wm8739",
"gpio",
};
static int attach_inform(struct i2c_client *client)
{
struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter);
int i;
IVTV_DEBUG_I2C("i2c client attach\n");
for (i = 0; i < I2C_CLIENTS_MAX; i++) {
if (itv->i2c_clients[i] == NULL) {
itv->i2c_clients[i] = client;
break;
}
}
if (i == I2C_CLIENTS_MAX) {
IVTV_ERR("insufficient room for new I2C client!\n");
}
return 0;
}
static int detach_inform(struct i2c_client *client)
{
int i;
struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter);
IVTV_DEBUG_I2C("i2c client detach\n");
for (i = 0; i < I2C_CLIENTS_MAX; i++) {
if (itv->i2c_clients[i] == client) {
itv->i2c_clients[i] = NULL;
break;
}
}
IVTV_DEBUG_I2C("i2c detach [client=%s,%s]\n",
client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed");
return 0;
}
/* Set the serial clock line to the desired state */
static void ivtv_setscl(struct ivtv *itv, int state)
{
/* write them out */
/* write bits are inverted */
write_reg(~state, IVTV_REG_I2C_SETSCL_OFFSET);
}
/* Set the serial data line to the desired state */
static void ivtv_setsda(struct ivtv *itv, int state)
{
/* write them out */
/* write bits are inverted */
write_reg(~state & 1, IVTV_REG_I2C_SETSDA_OFFSET);
}
/* Read the serial clock line */
static int ivtv_getscl(struct ivtv *itv)
{
return read_reg(IVTV_REG_I2C_GETSCL_OFFSET) & 1;
}
/* Read the serial data line */
static int ivtv_getsda(struct ivtv *itv)
{
return read_reg(IVTV_REG_I2C_GETSDA_OFFSET) & 1;
}
/* Implement a short delay by polling the serial clock line */
static void ivtv_scldelay(struct ivtv *itv)
{
int i;
for (i = 0; i < 5; ++i)
ivtv_getscl(itv);
}
/* Wait for the serial clock line to become set to a specific value */
static int ivtv_waitscl(struct ivtv *itv, int val)
{
int i;
ivtv_scldelay(itv);
for (i = 0; i < 1000; ++i) {
if (ivtv_getscl(itv) == val)
return 1;
}
return 0;
}
/* Wait for the serial data line to become set to a specific value */
static int ivtv_waitsda(struct ivtv *itv, int val)
{
int i;
ivtv_scldelay(itv);
for (i = 0; i < 1000; ++i) {
if (ivtv_getsda(itv) == val)
return 1;
}
return 0;
}
/* Wait for the slave to issue an ACK */
static int ivtv_ack(struct ivtv *itv)
{
int ret = 0;
if (ivtv_getscl(itv) == 1) {
IVTV_DEBUG_I2C("SCL was high starting an ack\n");
ivtv_setscl(itv, 0);
if (!ivtv_waitscl(itv, 0)) {
IVTV_DEBUG_I2C("Could not set SCL low starting an ack\n");
return -EREMOTEIO;
}
}
ivtv_setsda(itv, 1);
ivtv_scldelay(itv);
ivtv_setscl(itv, 1);
if (!ivtv_waitsda(itv, 0)) {
IVTV_DEBUG_I2C("Slave did not ack\n");
ret = -EREMOTEIO;
}
ivtv_setscl(itv, 0);
if (!ivtv_waitscl(itv, 0)) {
IVTV_DEBUG_I2C("Failed to set SCL low after ACK\n");
ret = -EREMOTEIO;
}
return ret;
}
/* Write a single byte to the i2c bus and wait for the slave to ACK */
static int ivtv_sendbyte(struct ivtv *itv, unsigned char byte)
{
int i, bit;
IVTV_DEBUG_I2C("write %x\n",byte);
for (i = 0; i < 8; ++i, byte<<=1) {
ivtv_setscl(itv, 0);
if (!ivtv_waitscl(itv, 0)) {
IVTV_DEBUG_I2C("Error setting SCL low\n");
return -EREMOTEIO;
}
bit = (byte>>7)&1;
ivtv_setsda(itv, bit);
if (!ivtv_waitsda(itv, bit)) {
IVTV_DEBUG_I2C("Error setting SDA\n");
return -EREMOTEIO;
}
ivtv_setscl(itv, 1);
if (!ivtv_waitscl(itv, 1)) {
IVTV_DEBUG_I2C("Slave not ready for bit\n");
return -EREMOTEIO;
}
}
ivtv_setscl(itv, 0);
if (!ivtv_waitscl(itv, 0)) {
IVTV_DEBUG_I2C("Error setting SCL low\n");
return -EREMOTEIO;
}
return ivtv_ack(itv);
}
/* Read a byte from the i2c bus and send a NACK if applicable (i.e. for the
final byte) */
static int ivtv_readbyte(struct ivtv *itv, unsigned char *byte, int nack)
{
int i;
*byte = 0;
ivtv_setsda(itv, 1);
ivtv_scldelay(itv);
for (i = 0; i < 8; ++i) {
ivtv_setscl(itv, 0);
ivtv_scldelay(itv);
ivtv_setscl(itv, 1);
if (!ivtv_waitscl(itv, 1)) {
IVTV_DEBUG_I2C("Error setting SCL high\n");
return -EREMOTEIO;
}
*byte = ((*byte)<<1)|ivtv_getsda(itv);
}
ivtv_setscl(itv, 0);
ivtv_scldelay(itv);
ivtv_setsda(itv, nack);
ivtv_scldelay(itv);
ivtv_setscl(itv, 1);
ivtv_scldelay(itv);
ivtv_setscl(itv, 0);
ivtv_scldelay(itv);
IVTV_DEBUG_I2C("read %x\n",*byte);
return 0;
}
/* Issue a start condition on the i2c bus to alert slaves to prepare for
an address write */
static int ivtv_start(struct ivtv *itv)
{
int sda;
sda = ivtv_getsda(itv);
if (sda != 1) {
IVTV_DEBUG_I2C("SDA was low at start\n");
ivtv_setsda(itv, 1);
if (!ivtv_waitsda(itv, 1)) {
IVTV_DEBUG_I2C("SDA stuck low\n");
return -EREMOTEIO;
}
}
if (ivtv_getscl(itv) != 1) {
ivtv_setscl(itv, 1);
if (!ivtv_waitscl(itv, 1)) {
IVTV_DEBUG_I2C("SCL stuck low at start\n");
return -EREMOTEIO;
}
}
ivtv_setsda(itv, 0);
ivtv_scldelay(itv);
return 0;
}
/* Issue a stop condition on the i2c bus to release it */
static int ivtv_stop(struct ivtv *itv)
{
int i;
if (ivtv_getscl(itv) != 0) {
IVTV_DEBUG_I2C("SCL not low when stopping\n");
ivtv_setscl(itv, 0);
if (!ivtv_waitscl(itv, 0)) {
IVTV_DEBUG_I2C("SCL could not be set low\n");
}
}
ivtv_setsda(itv, 0);
ivtv_scldelay(itv);
ivtv_setscl(itv, 1);
if (!ivtv_waitscl(itv, 1)) {
IVTV_DEBUG_I2C("SCL could not be set high\n");
return -EREMOTEIO;
}
ivtv_scldelay(itv);
ivtv_setsda(itv, 1);
if (!ivtv_waitsda(itv, 1)) {
IVTV_DEBUG_I2C("resetting I2C\n");
for (i = 0; i < 16; ++i) {
ivtv_setscl(itv, 0);
ivtv_scldelay(itv);
ivtv_setscl(itv, 1);
ivtv_scldelay(itv);
ivtv_setsda(itv, 1);
}
ivtv_waitsda(itv, 1);
return -EREMOTEIO;
}
return 0;
}
/* Write a message to the given i2c slave. do_stop may be 0 to prevent
issuing the i2c stop condition (when following with a read) */
static int ivtv_write(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len, int do_stop)
{
int retry, ret = -EREMOTEIO;
u32 i;
for (retry = 0; ret != 0 && retry < 8; ++retry) {
ret = ivtv_start(itv);
if (ret == 0) {
ret = ivtv_sendbyte(itv, addr<<1);
for (i = 0; ret == 0 && i < len; ++i)
ret = ivtv_sendbyte(itv, data[i]);
}
if (ret != 0 || do_stop) {
ivtv_stop(itv);
}
}
if (ret)
IVTV_DEBUG_I2C("i2c write to %x failed\n", addr);
return ret;
}
/* Read data from the given i2c slave. A stop condition is always issued. */
static int ivtv_read(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len)
{
int retry, ret = -EREMOTEIO;
u32 i;
for (retry = 0; ret != 0 && retry < 8; ++retry) {
ret = ivtv_start(itv);
if (ret == 0)
ret = ivtv_sendbyte(itv, (addr << 1) | 1);
for (i = 0; ret == 0 && i < len; ++i) {
ret = ivtv_readbyte(itv, &data[i], i == len - 1);
}
ivtv_stop(itv);
}
if (ret)
IVTV_DEBUG_I2C("i2c read from %x failed\n", addr);
return ret;
}
/* Kernel i2c transfer implementation. Takes a number of messages to be read
or written. If a read follows a write, this will occur without an
intervening stop condition */
static int ivtv_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
{
struct ivtv *itv = i2c_get_adapdata(i2c_adap);
int retval;
int i;
mutex_lock(&itv->i2c_bus_lock);
for (i = retval = 0; retval == 0 && i < num; i++) {
if (msgs[i].flags & I2C_M_RD)
retval = ivtv_read(itv, msgs[i].addr, msgs[i].buf, msgs[i].len);
else {
/* if followed by a read, don't stop */
int stop = !(i + 1 < num && msgs[i + 1].flags == I2C_M_RD);
retval = ivtv_write(itv, msgs[i].addr, msgs[i].buf, msgs[i].len, stop);
}
}
mutex_unlock(&itv->i2c_bus_lock);
return retval ? retval : num;
}
/* Kernel i2c capabilities */
static u32 ivtv_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static struct i2c_algorithm ivtv_algo = {
.master_xfer = ivtv_xfer,
.functionality = ivtv_functionality,
};
/* template for our-bit banger */
static struct i2c_adapter ivtv_i2c_adap_hw_template = {
.name = "ivtv i2c driver",
.id = I2C_HW_B_CX2341X,
.algo = &ivtv_algo,
.algo_data = NULL, /* filled from template */
.client_register = attach_inform,
.client_unregister = detach_inform,
.owner = THIS_MODULE,
#ifdef I2C_ADAP_CLASS_TV_ANALOG
.class = I2C_ADAP_CLASS_TV_ANALOG,
#endif
};
static void ivtv_setscl_old(void *data, int state)
{
struct ivtv *itv = (struct ivtv *)data;
if (state)
itv->i2c_state |= 0x01;
else
itv->i2c_state &= ~0x01;
/* write them out */
/* write bits are inverted */
write_reg(~itv->i2c_state, IVTV_REG_I2C_SETSCL_OFFSET);
}
static void ivtv_setsda_old(void *data, int state)
{
struct ivtv *itv = (struct ivtv *)data;
if (state)
itv->i2c_state |= 0x01;
else
itv->i2c_state &= ~0x01;
/* write them out */
/* write bits are inverted */
write_reg(~itv->i2c_state, IVTV_REG_I2C_SETSDA_OFFSET);
}
static int ivtv_getscl_old(void *data)
{
struct ivtv *itv = (struct ivtv *)data;
return read_reg(IVTV_REG_I2C_GETSCL_OFFSET) & 1;
}
static int ivtv_getsda_old(void *data)
{
struct ivtv *itv = (struct ivtv *)data;
return read_reg(IVTV_REG_I2C_GETSDA_OFFSET) & 1;
}
/* template for i2c-bit-algo */
static struct i2c_adapter ivtv_i2c_adap_template = {
.name = "ivtv i2c driver",
.id = I2C_HW_B_CX2341X, /* algo-bit is OR'd with this */
.algo = NULL, /* set by i2c-algo-bit */
.algo_data = NULL, /* filled from template */
.client_register = attach_inform,
.client_unregister = detach_inform,
.owner = THIS_MODULE,
#ifdef I2C_ADAP_CLASS_TV_ANALOG
.class = I2C_ADAP_CLASS_TV_ANALOG,
#endif
};
static struct i2c_algo_bit_data ivtv_i2c_algo_template = {
NULL, /* ?? */
ivtv_setsda_old, /* setsda function */
ivtv_setscl_old, /* " */
ivtv_getsda_old, /* " */
ivtv_getscl_old, /* " */
10, /* udelay */
200 /* timeout */
};
static struct i2c_client ivtv_i2c_client_template = {
.name = "ivtv internal use only",
};
int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg)
{
struct i2c_client *client;
int retval;
int i;
IVTV_DEBUG_I2C("call_i2c_client addr=%02x\n", addr);
for (i = 0; i < I2C_CLIENTS_MAX; i++) {
client = itv->i2c_clients[i];
if (client == NULL) {
continue;
}
if (client->driver->command == NULL) {
continue;
}
if (addr == client->addr) {
retval = client->driver->command(client, cmd, arg);
return retval;
}
}
IVTV_ERR("i2c addr 0x%02x not found for command 0x%x!\n", addr, cmd);
return -ENODEV;
}
/* Find the i2c device based on the driver ID and return
its i2c address or -ENODEV if no matching device was found. */
int ivtv_i2c_id_addr(struct ivtv *itv, u32 id)
{
struct i2c_client *client;
int retval = -ENODEV;
int i;
for (i = 0; i < I2C_CLIENTS_MAX; i++) {
client = itv->i2c_clients[i];
if (client == NULL)
continue;
if (id == client->driver->id) {
retval = client->addr;
break;
}
}
return retval;
}
/* Find the i2c device name matching the DRIVERID */
static const char *ivtv_i2c_id_name(u32 id)
{
int i;
for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
if (hw_driverids[i] == id)
return hw_drivernames[i];
return "unknown device";
}
/* Find the i2c device name matching the IVTV_HW_ flag */
static const char *ivtv_i2c_hw_name(u32 hw)
{
int i;
for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
if (1 << i == hw)
return hw_drivernames[i];
return "unknown device";
}
/* Find the i2c device matching the IVTV_HW_ flag and return
its i2c address or -ENODEV if no matching device was found. */
int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw)
{
int i;
for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
if (1 << i == hw)
return ivtv_i2c_id_addr(itv, hw_driverids[i]);
return -ENODEV;
}
/* Calls i2c device based on IVTV_HW_ flag. If hw == 0, then do nothing.
If hw == IVTV_HW_GPIO then call the gpio handler. */
int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg)
{
int addr;
if (hw == IVTV_HW_GPIO)
return ivtv_gpio(itv, cmd, arg);
if (hw == 0)
return 0;
addr = ivtv_i2c_hw_addr(itv, hw);
if (addr < 0) {
IVTV_ERR("i2c hardware 0x%08x (%s) not found for command 0x%x!\n",
hw, ivtv_i2c_hw_name(hw), cmd);
return addr;
}
return ivtv_call_i2c_client(itv, addr, cmd, arg);
}
/* Calls i2c device based on I2C driver ID. */
int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg)
{
int addr;
addr = ivtv_i2c_id_addr(itv, id);
if (addr < 0) {
IVTV_ERR("i2c ID 0x%08x (%s) not found for command 0x%x!\n",
id, ivtv_i2c_id_name(id), cmd);
return addr;
}
return ivtv_call_i2c_client(itv, addr, cmd, arg);
}
int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg)
{
return ivtv_call_i2c_client(itv, IVTV_CX25840_I2C_ADDR, cmd, arg);
}
int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg)
{
return ivtv_call_i2c_client(itv, IVTV_SAA7115_I2C_ADDR, cmd, arg);
}
int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg)
{
return ivtv_call_i2c_client(itv, IVTV_SAA7127_I2C_ADDR, cmd, arg);
}
int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg)
{
return ivtv_call_i2c_client(itv, IVTV_SAA717x_I2C_ADDR, cmd, arg);
}
int ivtv_msp34xx(struct ivtv *itv, unsigned int cmd, void *arg)
{
return ivtv_call_i2c_client(itv, IVTV_MSP3400_I2C_ADDR, cmd, arg);
}
int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg)
{
return ivtv_call_i2c_client(itv, IVTV_UPD64031A_I2C_ADDR, cmd, arg);
}
int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg)
{
return ivtv_call_i2c_client(itv, IVTV_UPD64083_I2C_ADDR, cmd, arg);
}
/* broadcast cmd for all I2C clients and for the gpio subsystem */
void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg)
{
if (itv->i2c_adap.algo == NULL) {
IVTV_ERR("adapter is not set");
return;
}
i2c_clients_command(&itv->i2c_adap, cmd, arg);
if (itv->hw_flags & IVTV_HW_GPIO)
ivtv_gpio(itv, cmd, arg);
}
/* init + register i2c algo-bit adapter */
int __devinit init_ivtv_i2c(struct ivtv *itv)
{
IVTV_DEBUG_I2C("i2c init\n");
if (itv->options.newi2c > 0) {
memcpy(&itv->i2c_adap, &ivtv_i2c_adap_hw_template,
sizeof(struct i2c_adapter));
} else {
memcpy(&itv->i2c_adap, &ivtv_i2c_adap_template,
sizeof(struct i2c_adapter));
memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template,
sizeof(struct i2c_algo_bit_data));
itv->i2c_algo.data = itv;
itv->i2c_adap.algo_data = &itv->i2c_algo;
}
sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name), " #%d",
itv->num);
i2c_set_adapdata(&itv->i2c_adap, itv);
memcpy(&itv->i2c_client, &ivtv_i2c_client_template,
sizeof(struct i2c_client));
itv->i2c_client.adapter = &itv->i2c_adap;
itv->i2c_adap.dev.parent = &itv->dev->dev;
IVTV_DEBUG_I2C("setting scl and sda to 1\n");
ivtv_setscl(itv, 1);
ivtv_setsda(itv, 1);
if (itv->options.newi2c > 0)
return i2c_add_adapter(&itv->i2c_adap);
else
return i2c_bit_add_bus(&itv->i2c_adap);
}
void __devexit exit_ivtv_i2c(struct ivtv *itv)
{
IVTV_DEBUG_I2C("i2c exit\n");
i2c_del_adapter(&itv->i2c_adap);
}

View File

@ -0,0 +1,38 @@
/*
I2C functions
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg);
int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg);
int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg);
int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg);
int ivtv_msp34xx(struct ivtv *itv, unsigned int cmd, void *arg);
int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg);
int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg);
int ivtv_i2c_id_addr(struct ivtv *itv, u32 id);
int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw);
int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg);
int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg);
int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg);
void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg);
/* init + register i2c algo-bit adapter */
int __devinit init_ivtv_i2c(struct ivtv *itv);
void __devexit exit_ivtv_i2c(struct ivtv *itv);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
/*
ioctl system call
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
u16 service2vbi(int type);
void expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal);
u16 get_service_set(struct v4l2_sliced_vbi_format *fmt);
int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
unsigned long arg);
int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void *arg);
void ivtv_set_osd_alpha(struct ivtv *itv);
int ivtv_set_speed(struct ivtv *itv, int speed);

View File

@ -0,0 +1,818 @@
/* interrupt handling
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2004 Chris Kennedy <c@groovy.org>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ivtv-driver.h"
#include "ivtv-firmware.h"
#include "ivtv-fileops.h"
#include "ivtv-queue.h"
#include "ivtv-udma.h"
#include "ivtv-irq.h"
#include "ivtv-ioctl.h"
#include "ivtv-mailbox.h"
#include "ivtv-vbi.h"
#define DMA_MAGIC_COOKIE 0x000001fe
#define SLICED_VBI_PIO 1
static void ivtv_dma_dec_start(struct ivtv_stream *s);
static const int ivtv_stream_map[] = {
IVTV_ENC_STREAM_TYPE_MPG,
IVTV_ENC_STREAM_TYPE_YUV,
IVTV_ENC_STREAM_TYPE_PCM,
IVTV_ENC_STREAM_TYPE_VBI,
};
static inline int ivtv_use_pio(struct ivtv_stream *s)
{
struct ivtv *itv = s->itv;
return s->dma == PCI_DMA_NONE ||
(SLICED_VBI_PIO && s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set);
}
/* Determine the required DMA size, setup enough buffers in the predma queue and
actually copy the data from the card to the buffers in case a PIO transfer is
required for this stream.
*/
static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MAX_DATA])
{
struct ivtv *itv = s->itv;
struct ivtv_buffer *buf;
struct list_head *p;
u32 bytes_needed = 0;
u32 offset, size;
u32 UVoffset = 0, UVsize = 0;
int skip_bufs = s->q_predma.buffers;
int idx = s->SG_length;
int rc;
/* sanity checks */
if (s->v4l2dev == NULL) {
IVTV_DEBUG_WARN("Stream %s not started\n", s->name);
return -1;
}
if (!test_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
IVTV_DEBUG_WARN("Stream %s not open\n", s->name);
return -1;
}
/* determine offset, size and PTS for the various streams */
switch (s->type) {
case IVTV_ENC_STREAM_TYPE_MPG:
offset = data[1];
size = data[2];
s->dma_pts = 0;
break;
case IVTV_ENC_STREAM_TYPE_YUV:
offset = data[1];
size = data[2];
UVoffset = data[3];
UVsize = data[4];
s->dma_pts = ((u64) data[5] << 32) | data[6];
break;
case IVTV_ENC_STREAM_TYPE_PCM:
offset = data[1] + 12;
size = data[2] - 12;
s->dma_pts = read_dec(offset - 8) |
((u64)(read_dec(offset - 12)) << 32);
if (itv->has_cx23415)
offset += IVTV_DECODER_OFFSET;
break;
case IVTV_ENC_STREAM_TYPE_VBI:
size = itv->vbi.enc_size * itv->vbi.fpi;
offset = read_enc(itv->vbi.enc_start - 4) + 12;
if (offset == 12) {
IVTV_DEBUG_INFO("VBI offset == 0\n");
return -1;
}
s->dma_pts = read_enc(offset - 4) | ((u64)read_enc(offset - 8) << 32);
break;
case IVTV_DEC_STREAM_TYPE_VBI:
size = read_dec(itv->vbi.dec_start + 4) + 8;
offset = read_dec(itv->vbi.dec_start) + itv->vbi.dec_start;
s->dma_pts = 0;
offset += IVTV_DECODER_OFFSET;
break;
default:
/* shouldn't happen */
return -1;
}
/* if this is the start of the DMA then fill in the magic cookie */
if (s->SG_length == 0) {
if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM ||
s->type == IVTV_DEC_STREAM_TYPE_VBI)) {
s->dma_backup = read_dec(offset - IVTV_DECODER_OFFSET);
write_dec_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset - IVTV_DECODER_OFFSET);
}
else {
s->dma_backup = read_enc(offset);
write_enc_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset);
}
s->dma_offset = offset;
}
bytes_needed = size;
if (s->type == IVTV_ENC_STREAM_TYPE_YUV) {
/* The size for the Y samples needs to be rounded upwards to a
multiple of the buf_size. The UV samples then start in the
next buffer. */
bytes_needed = s->buf_size * ((bytes_needed + s->buf_size - 1) / s->buf_size);
bytes_needed += UVsize;
}
IVTV_DEBUG_DMA("%s %s: 0x%08x bytes at 0x%08x\n",
ivtv_use_pio(s) ? "PIO" : "DMA", s->name, bytes_needed, offset);
rc = ivtv_queue_move(s, &s->q_free, &s->q_full, &s->q_predma, bytes_needed);
if (rc < 0) { /* Insufficient buffers */
IVTV_DEBUG_WARN("Cannot obtain %d bytes for %s data transfer\n",
bytes_needed, s->name);
return -1;
}
if (rc && !s->buffers_stolen && (s->s_flags & IVTV_F_S_APPL_IO)) {
IVTV_WARN("All %s stream buffers are full. Dropping data.\n", s->name);
IVTV_WARN("Cause: the application is not reading fast enough.\n");
}
s->buffers_stolen = rc;
/* got the buffers, now fill in SGarray (DMA) or copy the data from the card
to the buffers (PIO). */
buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list);
memset(buf->buf, 0, 128);
list_for_each(p, &s->q_predma.list) {
struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list);
if (skip_bufs-- > 0)
continue;
if (!ivtv_use_pio(s)) {
s->SGarray[idx].dst = cpu_to_le32(buf->dma_handle);
s->SGarray[idx].src = cpu_to_le32(offset);
s->SGarray[idx].size = cpu_to_le32(s->buf_size);
}
buf->bytesused = (size < s->buf_size) ? size : s->buf_size;
/* If PIO, then copy the data from the card to the buffer */
if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
memcpy_fromio(buf->buf, itv->dec_mem + offset - IVTV_DECODER_OFFSET, buf->bytesused);
}
else if (ivtv_use_pio(s)) {
memcpy_fromio(buf->buf, itv->enc_mem + offset, buf->bytesused);
}
s->q_predma.bytesused += buf->bytesused;
size -= buf->bytesused;
offset += s->buf_size;
/* Sync SG buffers */
ivtv_buf_sync_for_device(s, buf);
if (size == 0) { /* YUV */
/* process the UV section */
offset = UVoffset;
size = UVsize;
}
idx++;
}
s->SG_length = idx;
return 0;
}
static void dma_post(struct ivtv_stream *s)
{
struct ivtv *itv = s->itv;
struct ivtv_buffer *buf = NULL;
struct list_head *p;
u32 offset;
u32 *u32buf;
int x = 0;
if (ivtv_use_pio(s)) {
if (s->q_predma.bytesused)
ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
s->SG_length = 0;
}
IVTV_DEBUG_DMA("%s %s completed (%x)\n", ivtv_use_pio(s) ? "PIO" : "DMA",
s->name, s->dma_offset);
list_for_each(p, &s->q_dma.list) {
buf = list_entry(p, struct ivtv_buffer, list);
u32buf = (u32 *)buf->buf;
/* Sync Buffer */
ivtv_buf_sync_for_cpu(s, buf);
if (x == 0) {
offset = s->dma_last_offset;
if (u32buf[offset / 4] != DMA_MAGIC_COOKIE)
{
for (offset = 0; offset < 64; offset++) {
if (u32buf[offset] == DMA_MAGIC_COOKIE) {
break;
}
}
offset *= 4;
if (offset == 256) {
IVTV_DEBUG_WARN("%s: Couldn't find start of buffer within the first 256 bytes\n", s->name);
offset = s->dma_last_offset;
}
if (s->dma_last_offset != offset)
IVTV_DEBUG_WARN("%s: offset %d -> %d\n", s->name, s->dma_last_offset, offset);
s->dma_last_offset = offset;
}
if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM ||
s->type == IVTV_DEC_STREAM_TYPE_VBI)) {
write_dec_sync(0, s->dma_offset - IVTV_DECODER_OFFSET);
}
else {
write_enc_sync(0, s->dma_offset);
}
if (offset) {
buf->bytesused -= offset;
memcpy(buf->buf, buf->buf + offset, buf->bytesused + offset);
}
*u32buf = cpu_to_le32(s->dma_backup);
}
x++;
/* flag byteswap ABCD -> DCBA for MPG & VBI data outside irq */
if (s->type == IVTV_ENC_STREAM_TYPE_MPG ||
s->type == IVTV_ENC_STREAM_TYPE_VBI)
set_bit(IVTV_F_B_NEED_BUF_SWAP, &buf->b_flags);
}
if (buf)
buf->bytesused += s->dma_last_offset;
if (buf && s->type == IVTV_DEC_STREAM_TYPE_VBI) {
/* Parse and Groom VBI Data */
s->q_dma.bytesused -= buf->bytesused;
ivtv_process_vbi_data(itv, buf, 0, s->type);
s->q_dma.bytesused += buf->bytesused;
if (s->id == -1) {
ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0);
return;
}
}
ivtv_queue_move(s, &s->q_dma, NULL, &s->q_full, s->q_dma.bytesused);
if (s->id != -1)
wake_up(&s->waitq);
}
void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock)
{
struct ivtv *itv = s->itv;
struct ivtv_buffer *buf;
struct list_head *p;
u32 y_size = itv->params.height * itv->params.width;
u32 uv_offset = offset + IVTV_YUV_BUFFER_UV_OFFSET;
int y_done = 0;
int bytes_written = 0;
unsigned long flags = 0;
int idx = 0;
IVTV_DEBUG_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset);
buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list);
list_for_each(p, &s->q_predma.list) {
struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list);
/* YUV UV Offset from Y Buffer */
if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done && bytes_written >= y_size) {
offset = uv_offset;
y_done = 1;
}
s->SGarray[idx].src = cpu_to_le32(buf->dma_handle);
s->SGarray[idx].dst = cpu_to_le32(offset);
s->SGarray[idx].size = cpu_to_le32(buf->bytesused);
offset += buf->bytesused;
bytes_written += buf->bytesused;
/* Sync SG buffers */
ivtv_buf_sync_for_device(s, buf);
idx++;
}
s->SG_length = idx;
/* Mark last buffer size for Interrupt flag */
s->SGarray[s->SG_length - 1].size |= cpu_to_le32(0x80000000);
/* Sync Hardware SG List of buffers */
ivtv_stream_sync_for_device(s);
if (lock)
spin_lock_irqsave(&itv->dma_reg_lock, flags);
if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
ivtv_dma_dec_start(s);
}
else {
set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
}
if (lock)
spin_unlock_irqrestore(&itv->dma_reg_lock, flags);
}
/* start the encoder DMA */
static void ivtv_dma_enc_start(struct ivtv_stream *s)
{
struct ivtv *itv = s->itv;
struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
int i;
if (s->q_predma.bytesused)
ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
IVTV_DEBUG_DMA("start DMA for %s\n", s->name);
s->SGarray[s->SG_length - 1].size = cpu_to_le32(le32_to_cpu(s->SGarray[s->SG_length - 1].size) + 256);
/* If this is an MPEG stream, and VBI data is also pending, then append the
VBI DMA to the MPEG DMA and transfer both sets of data at once.
VBI DMA is a second class citizen compared to MPEG and mixing them together
will confuse the firmware (the end of a VBI DMA is seen as the end of a
MPEG DMA, thus effectively dropping an MPEG frame). So instead we make
sure we only use the MPEG DMA to transfer the VBI DMA if both are in
use. This way no conflicts occur. */
clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);
if (s->type == IVTV_ENC_STREAM_TYPE_MPG && s_vbi->SG_length &&
s->SG_length + s_vbi->SG_length <= s->buffers) {
ivtv_queue_move(s_vbi, &s_vbi->q_predma, NULL, &s_vbi->q_dma, s_vbi->q_predma.bytesused);
s_vbi->SGarray[s_vbi->SG_length - 1].size = cpu_to_le32(le32_to_cpu(s_vbi->SGarray[s->SG_length - 1].size) + 256);
for (i = 0; i < s_vbi->SG_length; i++) {
s->SGarray[s->SG_length++] = s_vbi->SGarray[i];
}
itv->vbi.dma_offset = s_vbi->dma_offset;
s_vbi->SG_length = 0;
set_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);
IVTV_DEBUG_DMA("include DMA for %s\n", s->name);
}
/* Mark last buffer size for Interrupt flag */
s->SGarray[s->SG_length - 1].size |= cpu_to_le32(0x80000000);
/* Sync Hardware SG List of buffers */
ivtv_stream_sync_for_device(s);
write_reg(s->SG_handle, IVTV_REG_ENCDMAADDR);
write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x02, IVTV_REG_DMAXFER);
set_bit(IVTV_F_I_DMA, &itv->i_flags);
itv->cur_dma_stream = s->type;
itv->dma_timer.expires = jiffies + HZ / 10;
add_timer(&itv->dma_timer);
}
static void ivtv_dma_dec_start(struct ivtv_stream *s)
{
struct ivtv *itv = s->itv;
if (s->q_predma.bytesused)
ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
IVTV_DEBUG_DMA("start DMA for %s\n", s->name);
/* put SG Handle into register 0x0c */
write_reg(s->SG_handle, IVTV_REG_DECDMAADDR);
write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);
set_bit(IVTV_F_I_DMA, &itv->i_flags);
itv->cur_dma_stream = s->type;
itv->dma_timer.expires = jiffies + HZ / 10;
add_timer(&itv->dma_timer);
}
static void ivtv_irq_dma_read(struct ivtv *itv)
{
struct ivtv_stream *s = NULL;
struct ivtv_buffer *buf;
int hw_stream_type;
IVTV_DEBUG_IRQ("DEC DMA READ\n");
del_timer(&itv->dma_timer);
if (read_reg(IVTV_REG_DMASTATUS) & 0x14) {
IVTV_DEBUG_WARN("DEC DMA ERROR %x\n", read_reg(IVTV_REG_DMASTATUS));
write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
}
if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) {
s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
hw_stream_type = 2;
}
else {
s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
hw_stream_type = 0;
}
IVTV_DEBUG_DMA("DEC DATA READ %s: %d\n", s->name, s->q_dma.bytesused);
ivtv_stream_sync_for_cpu(s);
/* For some reason must kick the firmware, like PIO mode,
I think this tells the firmware we are done and the size
of the xfer so it can calculate what we need next.
I think we can do this part ourselves but would have to
fully calculate xfer info ourselves and not use interrupts
*/
ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, s->q_dma.bytesused,
hw_stream_type);
/* Free last DMA call */
while ((buf = ivtv_dequeue(s, &s->q_dma)) != NULL) {
ivtv_buf_sync_for_cpu(s, buf);
ivtv_enqueue(s, buf, &s->q_free);
}
wake_up(&s->waitq);
}
clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
clear_bit(IVTV_F_I_DMA, &itv->i_flags);
itv->cur_dma_stream = -1;
wake_up(&itv->dma_waitq);
}
static void ivtv_irq_enc_dma_complete(struct ivtv *itv)
{
u32 data[CX2341X_MBOX_MAX_DATA];
struct ivtv_stream *s;
del_timer(&itv->dma_timer);
ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data);
IVTV_DEBUG_IRQ("ENC DMA COMPLETE %x %d\n", data[0], data[1]);
if (test_and_clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags))
data[1] = 3;
else if (data[1] > 2)
return;
s = &itv->streams[ivtv_stream_map[data[1]]];
if (data[0] & 0x18) {
IVTV_DEBUG_WARN("ENC DMA ERROR %x\n", data[0]);
write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, data[1]);
}
s->SG_length = 0;
clear_bit(IVTV_F_I_DMA, &itv->i_flags);
itv->cur_dma_stream = -1;
dma_post(s);
ivtv_stream_sync_for_cpu(s);
if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) {
u32 tmp;
s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
tmp = s->dma_offset;
s->dma_offset = itv->vbi.dma_offset;
dma_post(s);
s->dma_offset = tmp;
}
wake_up(&itv->dma_waitq);
}
static void ivtv_irq_dma_err(struct ivtv *itv)
{
u32 data[CX2341X_MBOX_MAX_DATA];
del_timer(&itv->dma_timer);
ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data);
IVTV_DEBUG_WARN("DMA ERROR %08x %08x %08x %d\n", data[0], data[1],
read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream);
if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) &&
itv->cur_dma_stream >= 0 && itv->cur_dma_stream < IVTV_MAX_STREAMS) {
struct ivtv_stream *s = &itv->streams[itv->cur_dma_stream];
/* retry */
write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
if (s->type >= IVTV_DEC_STREAM_TYPE_MPG)
ivtv_dma_dec_start(s);
else
ivtv_dma_enc_start(s);
return;
}
clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
clear_bit(IVTV_F_I_DMA, &itv->i_flags);
itv->cur_dma_stream = -1;
wake_up(&itv->dma_waitq);
}
static void ivtv_irq_enc_start_cap(struct ivtv *itv)
{
u32 data[CX2341X_MBOX_MAX_DATA];
struct ivtv_stream *s;
/* Get DMA destination and size arguments from card */
ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA, data);
IVTV_DEBUG_IRQ("ENC START CAP %d: %08x %08x\n", data[0], data[1], data[2]);
if (data[0] > 2 || data[1] == 0 || data[2] == 0) {
IVTV_DEBUG_WARN("Unknown input: %08x %08x %08x\n",
data[0], data[1], data[2]);
return;
}
clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags);
s = &itv->streams[ivtv_stream_map[data[0]]];
if (!stream_enc_dma_append(s, data)) {
if (ivtv_use_pio(s)) {
dma_post(s);
ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, data[0]);
}
else {
set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
}
}
}
static void ivtv_irq_enc_vbi_cap(struct ivtv *itv)
{
struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG];
u32 data[CX2341X_MBOX_MAX_DATA];
struct ivtv_stream *s;
IVTV_DEBUG_IRQ("ENC START VBI CAP\n");
s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
if (ivtv_use_pio(s)) {
if (stream_enc_dma_append(s, data))
return;
if (s->q_predma.bytesused)
ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
s->SG_length = 0;
dma_post(s);
return;
}
/* If more than two VBI buffers are pending, then
clear the old ones and start with this new one.
This can happen during transition stages when MPEG capturing is
started, but the first interrupts haven't arrived yet. During
that period VBI requests can accumulate without being able to
DMA the data. Since at most four VBI DMA buffers are available,
we just drop the old requests when there are already three
requests queued. */
if (s->SG_length > 2) {
struct list_head *p;
list_for_each(p, &s->q_predma.list) {
struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list);
ivtv_buf_sync_for_cpu(s, buf);
}
ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0);
s->SG_length = 0;
}
/* if we can append the data, and the MPEG stream isn't capturing,
then start a DMA request for just the VBI data. */
if (!stream_enc_dma_append(s, data) &&
!test_bit(IVTV_F_S_STREAMING, &s_mpg->s_flags)) {
set_bit(IVTV_F_I_ENC_VBI, &itv->i_flags);
set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
}
}
static void ivtv_irq_dev_vbi_reinsert(struct ivtv *itv)
{
u32 data[CX2341X_MBOX_MAX_DATA];
struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI];
IVTV_DEBUG_IRQ("DEC VBI REINSERT\n");
if (test_bit(IVTV_F_S_CLAIMED, &s->s_flags) &&
!stream_enc_dma_append(s, data)) {
dma_post(s);
}
}
static void ivtv_irq_dec_data_req(struct ivtv *itv)
{
u32 data[CX2341X_MBOX_MAX_DATA];
struct ivtv_stream *s;
/* YUV or MPG */
ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data);
if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) {
itv->dma_data_req_size = itv->params.width * itv->params.height * 3 / 2;
itv->dma_data_req_offset = data[1] ? data[1] : yuv_offset[0];
s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
}
else {
itv->dma_data_req_size = data[2] >= 0x10000 ? 0x10000 : data[2];
itv->dma_data_req_offset = data[1];
s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
}
IVTV_DEBUG_IRQ("DEC DATA REQ %s: %d %08x %u\n", s->name, s->q_full.bytesused,
itv->dma_data_req_offset, itv->dma_data_req_size);
if (itv->dma_data_req_size == 0 || s->q_full.bytesused < itv->dma_data_req_size) {
set_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
}
else {
clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size);
ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 0);
}
}
static void ivtv_irq_vsync(struct ivtv *itv)
{
/* The vsync interrupt is unusual in that it won't clear until
* the end of the first line for the current field, at which
* point it clears itself. This can result in repeated vsync
* interrupts, or a missed vsync. Read some of the registers
* to determine the line being displayed and ensure we handle
* one vsync per frame.
*/
unsigned int frame = read_reg(0x28c0) & 1;
int last_dma_frame = atomic_read(&itv->yuv_info.next_dma_frame);
if (0) IVTV_DEBUG_IRQ("DEC VSYNC\n");
if (((frame ^ itv->yuv_info.lace_sync_field) == 0 && ((itv->lastVsyncFrame & 1) ^ itv->yuv_info.lace_sync_field)) ||
(frame != (itv->lastVsyncFrame & 1) && !itv->yuv_info.frame_interlaced)) {
int next_dma_frame = last_dma_frame;
if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&itv->yuv_info.next_fill_frame)) {
write_reg(yuv_offset[next_dma_frame] >> 4, 0x82c);
write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830);
write_reg(yuv_offset[next_dma_frame] >> 4, 0x834);
write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838);
next_dma_frame = (next_dma_frame + 1) & 0x3;
atomic_set(&itv->yuv_info.next_dma_frame, next_dma_frame);
}
}
if (frame != (itv->lastVsyncFrame & 1)) {
struct ivtv_stream *s = ivtv_get_output_stream(itv);
itv->lastVsyncFrame += 1;
if (frame == 0) {
clear_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
clear_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags);
}
else {
set_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags);
}
if (test_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags)) {
set_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags);
wake_up(&itv->event_waitq);
}
wake_up(&itv->vsync_waitq);
if (s)
wake_up(&s->waitq);
/* Send VBI to saa7127 */
if (frame)
vbi_schedule_work(itv);
/* Check if we need to update the yuv registers */
if ((itv->yuv_info.yuv_forced_update || itv->yuv_info.new_frame_info[last_dma_frame].update) && last_dma_frame != -1) {
if (!itv->yuv_info.new_frame_info[last_dma_frame].update)
last_dma_frame = (last_dma_frame - 1) & 3;
if (itv->yuv_info.new_frame_info[last_dma_frame].src_w) {
itv->yuv_info.update_frame = last_dma_frame;
itv->yuv_info.new_frame_info[last_dma_frame].update = 0;
itv->yuv_info.yuv_forced_update = 0;
queue_work(itv->yuv_info.work_queues, &itv->yuv_info.work_queue);
}
}
}
}
#define IVTV_IRQ_DMA (IVTV_IRQ_DMA_READ | IVTV_IRQ_ENC_DMA_COMPLETE | IVTV_IRQ_DMA_ERR | IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_VBI_CAP | IVTV_IRQ_DEC_DATA_REQ)
irqreturn_t ivtv_irq_handler(int irq, void *dev_id)
{
struct ivtv *itv = (struct ivtv *)dev_id;
u32 combo;
u32 stat;
int i;
u8 vsync_force = 0;
spin_lock(&itv->dma_reg_lock);
/* get contents of irq status register */
stat = read_reg(IVTV_REG_IRQSTATUS);
combo = ~itv->irqmask & stat;
/* Clear out IRQ */
if (combo) write_reg(combo, IVTV_REG_IRQSTATUS);
if (0 == combo) {
/* The vsync interrupt is unusual and clears itself. If we
* took too long, we may have missed it. Do some checks
*/
if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) {
/* vsync is enabled, see if we're in a new field */
if ((itv->lastVsyncFrame & 1) != (read_reg(0x28c0) & 1)) {
/* New field, looks like we missed it */
IVTV_DEBUG_YUV("VSync interrupt missed %d\n",read_reg(0x28c0)>>16);
vsync_force = 1;
}
}
if (!vsync_force) {
/* No Vsync expected, wasn't for us */
spin_unlock(&itv->dma_reg_lock);
return IRQ_NONE;
}
}
/* Exclude interrupts noted below from the output, otherwise the log is flooded with
these messages */
if (combo & ~0xff6d0400)
IVTV_DEBUG_IRQ("======= valid IRQ bits: 0x%08x ======\n", combo);
if (combo & IVTV_IRQ_DEC_DMA_COMPLETE) {
IVTV_DEBUG_IRQ("DEC DMA COMPLETE\n");
}
if (combo & IVTV_IRQ_DMA_READ) {
ivtv_irq_dma_read(itv);
}
if (combo & IVTV_IRQ_ENC_DMA_COMPLETE) {
ivtv_irq_enc_dma_complete(itv);
}
if (combo & IVTV_IRQ_DMA_ERR) {
ivtv_irq_dma_err(itv);
}
if (combo & IVTV_IRQ_ENC_START_CAP) {
ivtv_irq_enc_start_cap(itv);
}
if (combo & IVTV_IRQ_ENC_VBI_CAP) {
ivtv_irq_enc_vbi_cap(itv);
}
if (combo & IVTV_IRQ_DEC_VBI_RE_INSERT) {
ivtv_irq_dev_vbi_reinsert(itv);
}
if (combo & IVTV_IRQ_ENC_EOS) {
IVTV_DEBUG_IRQ("ENC EOS\n");
set_bit(IVTV_F_I_EOS, &itv->i_flags);
wake_up(&itv->cap_w);
}
if (combo & IVTV_IRQ_DEC_DATA_REQ) {
ivtv_irq_dec_data_req(itv);
}
/* Decoder Vertical Sync - We can't rely on 'combo', so check if vsync enabled */
if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) {
ivtv_irq_vsync(itv);
}
if (combo & IVTV_IRQ_ENC_VIM_RST) {
IVTV_DEBUG_IRQ("VIM RST\n");
/*ivtv_vapi(itv, CX2341X_ENC_REFRESH_INPUT, 0); */
}
if (combo & IVTV_IRQ_DEC_AUD_MODE_CHG) {
IVTV_DEBUG_INFO("Stereo mode changed\n");
}
if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
for (i = 0; i < IVTV_MAX_STREAMS; i++) {
int idx = (i + itv->irq_rr_idx++) % IVTV_MAX_STREAMS;
struct ivtv_stream *s = &itv->streams[idx];
if (!test_and_clear_bit(IVTV_F_S_DMA_PENDING, &s->s_flags))
continue;
if (s->type >= IVTV_DEC_STREAM_TYPE_MPG)
ivtv_dma_dec_start(s);
else
ivtv_dma_enc_start(s);
break;
}
if (i == IVTV_MAX_STREAMS && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) {
ivtv_udma_start(itv);
}
}
spin_unlock(&itv->dma_reg_lock);
/* If we've just handled a 'forced' vsync, it's safest to say it
* wasn't ours. Another device may have triggered it at just
* the right time.
*/
return vsync_force ? IRQ_NONE : IRQ_HANDLED;
}
void ivtv_unfinished_dma(unsigned long arg)
{
struct ivtv *itv = (struct ivtv *)arg;
if (!test_bit(IVTV_F_I_DMA, &itv->i_flags))
return;
IVTV_ERR("DMA TIMEOUT %08x %d\n", read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream);
write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
clear_bit(IVTV_F_I_DMA, &itv->i_flags);
itv->cur_dma_stream = -1;
wake_up(&itv->dma_waitq);
}

View File

@ -0,0 +1,24 @@
/*
interrupt handling
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2004 Chris Kennedy <c@groovy.org>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
irqreturn_t ivtv_irq_handler(int irq, void *dev_id);
void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock);
void ivtv_unfinished_dma(unsigned long arg);

View File

@ -0,0 +1,360 @@
/*
mailbox functions
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2004 Chris Kennedy <c@groovy.org>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdarg.h>
#include "ivtv-driver.h"
#include "ivtv-mailbox.h"
/* Firmware mailbox flags*/
#define IVTV_MBOX_FIRMWARE_DONE 0x00000004
#define IVTV_MBOX_DRIVER_DONE 0x00000002
#define IVTV_MBOX_DRIVER_BUSY 0x00000001
#define IVTV_MBOX_FREE 0x00000000
/* Firmware mailbox standard timeout */
#define IVTV_API_STD_TIMEOUT 0x02000000
#define API_CACHE (1 << 0) /* Allow the command to be stored in the cache */
#define API_RESULT (1 << 1) /* Allow 1 second for this cmd to end */
#define API_FAST_RESULT (3 << 1) /* Allow 0.1 second for this cmd to end */
#define API_DMA (1 << 3) /* DMA mailbox, has special handling */
#define API_NO_WAIT_MB (1 << 4) /* Command may not wait for a free mailbox */
#define API_NO_WAIT_RES (1 << 5) /* Command may not wait for the result */
struct ivtv_api_info {
int flags; /* Flags, see above */
const char *name; /* The name of the command */
};
#define API_ENTRY(x, f) [x] = { (f), #x }
static const struct ivtv_api_info api_info[256] = {
/* MPEG encoder API */
API_ENTRY(CX2341X_ENC_PING_FW, API_FAST_RESULT),
API_ENTRY(CX2341X_ENC_START_CAPTURE, API_RESULT),
API_ENTRY(CX2341X_ENC_STOP_CAPTURE, API_RESULT),
API_ENTRY(CX2341X_ENC_SET_AUDIO_ID, API_CACHE),
API_ENTRY(CX2341X_ENC_SET_VIDEO_ID, API_CACHE),
API_ENTRY(CX2341X_ENC_SET_PCR_ID, API_CACHE),
API_ENTRY(CX2341X_ENC_SET_FRAME_RATE, API_CACHE),
API_ENTRY(CX2341X_ENC_SET_FRAME_SIZE, API_CACHE),
API_ENTRY(CX2341X_ENC_SET_BIT_RATE, API_CACHE),
API_ENTRY(CX2341X_ENC_SET_GOP_PROPERTIES, API_CACHE),
API_ENTRY(CX2341X_ENC_SET_ASPECT_RATIO, API_CACHE),
API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_MODE, API_CACHE),
API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_PROPS, API_CACHE),
API_ENTRY(CX2341X_ENC_SET_CORING_LEVELS, API_CACHE),
API_ENTRY(CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, API_CACHE),
API_ENTRY(CX2341X_ENC_SET_VBI_LINE, API_RESULT),
API_ENTRY(CX2341X_ENC_SET_STREAM_TYPE, API_CACHE),
API_ENTRY(CX2341X_ENC_SET_OUTPUT_PORT, API_CACHE),
API_ENTRY(CX2341X_ENC_SET_AUDIO_PROPERTIES, API_CACHE),
API_ENTRY(CX2341X_ENC_HALT_FW, API_FAST_RESULT),
API_ENTRY(CX2341X_ENC_GET_VERSION, API_FAST_RESULT),
API_ENTRY(CX2341X_ENC_SET_GOP_CLOSURE, API_CACHE),
API_ENTRY(CX2341X_ENC_GET_SEQ_END, API_RESULT),
API_ENTRY(CX2341X_ENC_SET_PGM_INDEX_INFO, API_FAST_RESULT),
API_ENTRY(CX2341X_ENC_SET_VBI_CONFIG, API_RESULT),
API_ENTRY(CX2341X_ENC_SET_DMA_BLOCK_SIZE, API_CACHE),
API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_10, API_FAST_RESULT),
API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_9, API_FAST_RESULT),
API_ENTRY(CX2341X_ENC_SCHED_DMA_TO_HOST, API_DMA),
API_ENTRY(CX2341X_ENC_INITIALIZE_INPUT, API_RESULT),
API_ENTRY(CX2341X_ENC_SET_FRAME_DROP_RATE, API_CACHE),
API_ENTRY(CX2341X_ENC_PAUSE_ENCODER, API_RESULT),
API_ENTRY(CX2341X_ENC_REFRESH_INPUT, API_NO_WAIT_MB),
API_ENTRY(CX2341X_ENC_SET_COPYRIGHT, API_CACHE),
API_ENTRY(CX2341X_ENC_SET_EVENT_NOTIFICATION, API_RESULT),
API_ENTRY(CX2341X_ENC_SET_NUM_VSYNC_LINES, API_CACHE),
API_ENTRY(CX2341X_ENC_SET_PLACEHOLDER, API_CACHE),
API_ENTRY(CX2341X_ENC_MUTE_VIDEO, API_RESULT),
API_ENTRY(CX2341X_ENC_MUTE_AUDIO, API_RESULT),
API_ENTRY(CX2341X_ENC_SET_VERT_CROP_LINE, API_FAST_RESULT),
API_ENTRY(CX2341X_ENC_MISC, API_FAST_RESULT),
/* Obsolete PULLDOWN API command */
API_ENTRY(0xb1, API_CACHE),
/* MPEG decoder API */
API_ENTRY(CX2341X_DEC_PING_FW, API_FAST_RESULT),
API_ENTRY(CX2341X_DEC_START_PLAYBACK, API_RESULT),
API_ENTRY(CX2341X_DEC_STOP_PLAYBACK, API_RESULT),
API_ENTRY(CX2341X_DEC_SET_PLAYBACK_SPEED, API_RESULT),
API_ENTRY(CX2341X_DEC_STEP_VIDEO, API_RESULT),
API_ENTRY(CX2341X_DEC_SET_DMA_BLOCK_SIZE, API_CACHE),
API_ENTRY(CX2341X_DEC_GET_XFER_INFO, API_FAST_RESULT),
API_ENTRY(CX2341X_DEC_GET_DMA_STATUS, API_FAST_RESULT),
API_ENTRY(CX2341X_DEC_SCHED_DMA_FROM_HOST, API_DMA),
API_ENTRY(CX2341X_DEC_PAUSE_PLAYBACK, API_RESULT),
API_ENTRY(CX2341X_DEC_HALT_FW, API_FAST_RESULT),
API_ENTRY(CX2341X_DEC_SET_STANDARD, API_CACHE),
API_ENTRY(CX2341X_DEC_GET_VERSION, API_FAST_RESULT),
API_ENTRY(CX2341X_DEC_SET_STREAM_INPUT, API_CACHE),
API_ENTRY(CX2341X_DEC_GET_TIMING_INFO, API_RESULT /*| API_NO_WAIT_RES*/),
API_ENTRY(CX2341X_DEC_SET_AUDIO_MODE, API_CACHE),
API_ENTRY(CX2341X_DEC_SET_EVENT_NOTIFICATION, API_RESULT),
API_ENTRY(CX2341X_DEC_SET_DISPLAY_BUFFERS, API_CACHE),
API_ENTRY(CX2341X_DEC_EXTRACT_VBI, API_RESULT),
API_ENTRY(CX2341X_DEC_SET_DECODER_SOURCE, API_FAST_RESULT),
API_ENTRY(CX2341X_DEC_SET_PREBUFFERING, API_CACHE),
/* OSD API */
API_ENTRY(CX2341X_OSD_GET_FRAMEBUFFER, API_FAST_RESULT),
API_ENTRY(CX2341X_OSD_GET_PIXEL_FORMAT, API_FAST_RESULT),
API_ENTRY(CX2341X_OSD_SET_PIXEL_FORMAT, API_CACHE),
API_ENTRY(CX2341X_OSD_GET_STATE, API_FAST_RESULT),
API_ENTRY(CX2341X_OSD_SET_STATE, API_CACHE),
API_ENTRY(CX2341X_OSD_GET_OSD_COORDS, API_FAST_RESULT),
API_ENTRY(CX2341X_OSD_SET_OSD_COORDS, API_CACHE),
API_ENTRY(CX2341X_OSD_GET_SCREEN_COORDS, API_FAST_RESULT),
API_ENTRY(CX2341X_OSD_SET_SCREEN_COORDS, API_CACHE),
API_ENTRY(CX2341X_OSD_GET_GLOBAL_ALPHA, API_FAST_RESULT),
API_ENTRY(CX2341X_OSD_SET_GLOBAL_ALPHA, API_CACHE),
API_ENTRY(CX2341X_OSD_SET_BLEND_COORDS, API_CACHE),
API_ENTRY(CX2341X_OSD_GET_FLICKER_STATE, API_FAST_RESULT),
API_ENTRY(CX2341X_OSD_SET_FLICKER_STATE, API_CACHE),
API_ENTRY(CX2341X_OSD_BLT_COPY, API_RESULT),
API_ENTRY(CX2341X_OSD_BLT_FILL, API_RESULT),
API_ENTRY(CX2341X_OSD_BLT_TEXT, API_RESULT),
API_ENTRY(CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, API_CACHE),
API_ENTRY(CX2341X_OSD_SET_CHROMA_KEY, API_CACHE),
API_ENTRY(CX2341X_OSD_GET_ALPHA_CONTENT_INDEX, API_FAST_RESULT),
API_ENTRY(CX2341X_OSD_SET_ALPHA_CONTENT_INDEX, API_CACHE)
};
static int try_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int mb)
{
u32 flags = readl(&mbdata->mbox[mb].flags);
int is_free = flags == IVTV_MBOX_FREE || (flags & IVTV_MBOX_FIRMWARE_DONE);
/* if the mailbox is free, then try to claim it */
if (is_free && !test_and_set_bit(mb, &mbdata->busy)) {
write_sync(IVTV_MBOX_DRIVER_BUSY, &mbdata->mbox[mb].flags);
return 1;
}
return 0;
}
/* Try to find a free mailbox. Note mailbox 0 is reserved for DMA and so is not
attempted here. */
static int get_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int flags)
{
unsigned long then = jiffies;
int i, mb;
int max_mbox = mbdata->max_mbox;
int retries = 100;
/* All slow commands use the same mailbox, serializing them and also
leaving the other mailbox free for simple fast commands. */
if ((flags & API_FAST_RESULT) == API_RESULT)
max_mbox = 1;
/* find free non-DMA mailbox */
for (i = 0; i < retries; i++) {
for (mb = 1; mb <= max_mbox; mb++)
if (try_mailbox(itv, mbdata, mb))
return mb;
/* Sleep before a retry, if not atomic */
if (!(flags & API_NO_WAIT_MB)) {
if (jiffies - then > retries * HZ / 100)
break;
ivtv_sleep_timeout(HZ / 100, 0);
}
}
return -ENODEV;
}
static void write_mailbox(volatile struct ivtv_mailbox __iomem *mbox, int cmd, int args, u32 data[])
{
int i;
write_sync(cmd, &mbox->cmd);
write_sync(IVTV_API_STD_TIMEOUT, &mbox->timeout);
for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++)
write_sync(data[i], &mbox->data[i]);
write_sync(IVTV_MBOX_DRIVER_DONE | IVTV_MBOX_DRIVER_BUSY, &mbox->flags);
}
static void clear_all_mailboxes(struct ivtv *itv, struct ivtv_mailbox_data *mbdata)
{
int i;
for (i = 0; i <= mbdata->max_mbox; i++) {
IVTV_DEBUG_WARN("Clearing mailbox %d: cmd 0x%08x flags 0x%08x\n",
i, readl(&mbdata->mbox[i].cmd), readl(&mbdata->mbox[i].flags));
write_sync(0, &mbdata->mbox[i].flags);
clear_bit(i, &mbdata->busy);
}
}
static int ivtv_api_call(struct ivtv *itv, int cmd, int args, u32 data[])
{
struct ivtv_mailbox_data *mbdata = (cmd >= 128) ? &itv->enc_mbox : &itv->dec_mbox;
volatile struct ivtv_mailbox __iomem *mbox;
int api_timeout = HZ;
int flags, mb, i;
unsigned long then;
/* sanity checks */
if (NULL == mbdata) {
IVTV_ERR("No mailbox allocated\n");
return -ENODEV;
}
if (args < 0 || args > CX2341X_MBOX_MAX_DATA ||
cmd < 0 || cmd > 255 || api_info[cmd].name == NULL) {
IVTV_ERR("Invalid API call: cmd = 0x%02x, args = %d\n", cmd, args);
return -EINVAL;
}
IVTV_DEBUG_API("API Call: %s\n", api_info[cmd].name);
/* clear possibly uninitialized part of data array */
for (i = args; i < CX2341X_MBOX_MAX_DATA; i++)
data[i] = 0;
/* If this command was issued within the last 30 minutes and with identical
data, then just return 0 as there is no need to issue this command again.
Just an optimization to prevent unnecessary use of mailboxes. */
if (itv->api_cache[cmd].last_jiffies &&
jiffies - itv->api_cache[cmd].last_jiffies < HZ * 1800 &&
!memcmp(data, itv->api_cache[cmd].data, sizeof(itv->api_cache[cmd].data))) {
itv->api_cache[cmd].last_jiffies = jiffies;
return 0;
}
flags = api_info[cmd].flags;
if (flags & API_DMA) {
for (i = 0; i < 100; i++) {
mb = i % (mbdata->max_mbox + 1);
if (try_mailbox(itv, mbdata, mb)) {
write_mailbox(&mbdata->mbox[mb], cmd, args, data);
clear_bit(mb, &mbdata->busy);
return 0;
}
IVTV_DEBUG_WARN("%s: mailbox %d not free %08x\n",
api_info[cmd].name, mb, readl(&mbdata->mbox[mb].flags));
}
IVTV_WARN("Could not find free DMA mailbox for %s\n", api_info[cmd].name);
clear_all_mailboxes(itv, mbdata);
return -EBUSY;
}
if ((flags & API_FAST_RESULT) == API_FAST_RESULT)
api_timeout = HZ / 10;
mb = get_mailbox(itv, mbdata, flags);
if (mb < 0) {
IVTV_DEBUG_WARN("No free mailbox found (%s)\n", api_info[cmd].name);
clear_all_mailboxes(itv, mbdata);
return -EBUSY;
}
mbox = &mbdata->mbox[mb];
write_mailbox(mbox, cmd, args, data);
if (flags & API_CACHE) {
memcpy(itv->api_cache[cmd].data, data, sizeof(itv->api_cache[cmd].data));
itv->api_cache[cmd].last_jiffies = jiffies;
}
if ((flags & API_RESULT) == 0) {
clear_bit(mb, &mbdata->busy);
return 0;
}
/* Get results */
then = jiffies;
while (!(readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE)) {
if (jiffies - then > api_timeout) {
IVTV_DEBUG_WARN("Could not get result (%s)\n", api_info[cmd].name);
/* reset the mailbox, but it is likely too late already */
write_sync(0, &mbox->flags);
clear_bit(mb, &mbdata->busy);
return -EIO;
}
if (flags & API_NO_WAIT_RES)
mdelay(1);
else
ivtv_sleep_timeout(HZ / 100, 0);
}
if (jiffies - then > HZ / 10)
IVTV_DEBUG_WARN("%s took %lu jiffies (%d per HZ)\n",
api_info[cmd].name, jiffies - then, HZ);
for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++)
data[i] = readl(&mbox->data[i]);
write_sync(0, &mbox->flags);
clear_bit(mb, &mbdata->busy);
return 0;
}
int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[])
{
int res = ivtv_api_call(itv, cmd, args, data);
/* Allow a single retry, probably already too late though.
If there is no free mailbox then that is usually an indication
of a more serious problem. */
return (res == -EBUSY) ? ivtv_api_call(itv, cmd, args, data) : res;
}
int ivtv_api_func(void *priv, int cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA])
{
return ivtv_api(priv, cmd, in, data);
}
int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...)
{
va_list ap;
int i;
va_start(ap, args);
for (i = 0; i < args; i++) {
data[i] = va_arg(ap, u32);
}
va_end(ap);
return ivtv_api(itv, cmd, args, data);
}
int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...)
{
u32 data[CX2341X_MBOX_MAX_DATA];
va_list ap;
int i;
va_start(ap, args);
for (i = 0; i < args; i++) {
data[i] = va_arg(ap, u32);
}
va_end(ap);
return ivtv_api(itv, cmd, args, data);
}
/* This one is for stuff that can't sleep.. irq handlers, etc.. */
void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb, u32 data[])
{
int i;
for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++)
data[i] = readl(&mbdata->mbox[mb].data[i]);
}

View File

@ -0,0 +1,25 @@
/*
mailbox functions
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
void ivtv_api_get_data(struct ivtv_mailbox_data *mbox, int mb, u32 data[]);
int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]);
int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...);
int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...);
int ivtv_api_func(void *priv, int cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]);

View File

@ -0,0 +1,262 @@
/*
buffer queues.
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2004 Chris Kennedy <c@groovy.org>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ivtv-driver.h"
#include "ivtv-streams.h"
#include "ivtv-queue.h"
#include "ivtv-mailbox.h"
int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes)
{
if (s->buf_size - buf->bytesused < copybytes)
copybytes = s->buf_size - buf->bytesused;
if (copy_from_user(buf->buf + buf->bytesused, src, copybytes)) {
return -EFAULT;
}
buf->bytesused += copybytes;
return copybytes;
}
void ivtv_buf_swap(struct ivtv_buffer *buf)
{
int i;
for (i = 0; i < buf->bytesused; i += 4)
swab32s((u32 *)(buf->buf + i));
}
void ivtv_queue_init(struct ivtv_queue *q)
{
INIT_LIST_HEAD(&q->list);
q->buffers = 0;
q->length = 0;
q->bytesused = 0;
}
void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q)
{
unsigned long flags = 0;
/* clear the buffer if it is going to be enqueued to the free queue */
if (q == &s->q_free) {
buf->bytesused = 0;
buf->readpos = 0;
buf->b_flags = 0;
}
spin_lock_irqsave(&s->qlock, flags);
list_add_tail(&buf->list, &q->list);
q->buffers++;
q->length += s->buf_size;
q->bytesused += buf->bytesused - buf->readpos;
spin_unlock_irqrestore(&s->qlock, flags);
}
struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q)
{
struct ivtv_buffer *buf = NULL;
unsigned long flags = 0;
spin_lock_irqsave(&s->qlock, flags);
if (!list_empty(&q->list)) {
buf = list_entry(q->list.next, struct ivtv_buffer, list);
list_del_init(q->list.next);
q->buffers--;
q->length -= s->buf_size;
q->bytesused -= buf->bytesused - buf->readpos;
}
spin_unlock_irqrestore(&s->qlock, flags);
return buf;
}
static void ivtv_queue_move_buf(struct ivtv_stream *s, struct ivtv_queue *from,
struct ivtv_queue *to, int clear, int full)
{
struct ivtv_buffer *buf = list_entry(from->list.next, struct ivtv_buffer, list);
list_move_tail(from->list.next, &to->list);
from->buffers--;
from->length -= s->buf_size;
from->bytesused -= buf->bytesused - buf->readpos;
/* special handling for q_free */
if (clear)
buf->bytesused = buf->readpos = buf->b_flags = 0;
else if (full) {
/* special handling for stolen buffers, assume
all bytes are used. */
buf->bytesused = s->buf_size;
buf->readpos = buf->b_flags = 0;
}
to->buffers++;
to->length += s->buf_size;
to->bytesused += buf->bytesused - buf->readpos;
}
/* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'.
If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'.
If 'steal' != NULL, then buffers may also taken from that queue if
needed.
The buffer is automatically cleared if it goes to the free queue. It is
also cleared if buffers need to be taken from the 'steal' queue and
the 'from' queue is the free queue.
When 'from' is q_free, then needed_bytes is compared to the total
available buffer length, otherwise needed_bytes is compared to the
bytesused value. For the 'steal' queue the total available buffer
length is always used.
-ENOMEM is returned if the buffers could not be obtained, 0 if all
buffers where obtained from the 'from' list and if non-zero then
the number of stolen buffers is returned. */
int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal,
struct ivtv_queue *to, int needed_bytes)
{
unsigned long flags;
int rc = 0;
int from_free = from == &s->q_free;
int to_free = to == &s->q_free;
int bytes_available;
spin_lock_irqsave(&s->qlock, flags);
if (needed_bytes == 0) {
from_free = 1;
needed_bytes = from->length;
}
bytes_available = from_free ? from->length : from->bytesused;
bytes_available += steal ? steal->length : 0;
if (bytes_available < needed_bytes) {
spin_unlock_irqrestore(&s->qlock, flags);
return -ENOMEM;
}
if (from_free) {
u32 old_length = to->length;
while (to->length - old_length < needed_bytes) {
if (list_empty(&from->list))
from = steal;
if (from == steal)
rc++; /* keep track of 'stolen' buffers */
ivtv_queue_move_buf(s, from, to, 1, 0);
}
}
else {
u32 old_bytesused = to->bytesused;
while (to->bytesused - old_bytesused < needed_bytes) {
if (list_empty(&from->list))
from = steal;
if (from == steal)
rc++; /* keep track of 'stolen' buffers */
ivtv_queue_move_buf(s, from, to, to_free, rc);
}
}
spin_unlock_irqrestore(&s->qlock, flags);
return rc;
}
void ivtv_flush_queues(struct ivtv_stream *s)
{
ivtv_queue_move(s, &s->q_io, NULL, &s->q_free, 0);
ivtv_queue_move(s, &s->q_full, NULL, &s->q_free, 0);
ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0);
ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0);
}
int ivtv_stream_alloc(struct ivtv_stream *s)
{
struct ivtv *itv = s->itv;
int SGsize = sizeof(struct ivtv_SG_element) * s->buffers;
int i;
if (s->buffers == 0)
return 0;
IVTV_DEBUG_INFO("Allocate %s%s stream: %d x %d buffers (%dkB total)\n",
s->dma != PCI_DMA_NONE ? "DMA " : "",
s->name, s->buffers, s->buf_size, s->buffers * s->buf_size / 1024);
/* Allocate DMA SG Arrays */
if (s->dma != PCI_DMA_NONE) {
s->SGarray = (struct ivtv_SG_element *)kzalloc(SGsize, GFP_KERNEL);
if (s->SGarray == NULL) {
IVTV_ERR("Could not allocate SGarray for %s stream\n", s->name);
return -ENOMEM;
}
s->SG_length = 0;
s->SG_handle = pci_map_single(itv->dev, s->SGarray, SGsize, s->dma);
ivtv_stream_sync_for_cpu(s);
}
/* allocate stream buffers. Initially all buffers are in q_free. */
for (i = 0; i < s->buffers; i++) {
struct ivtv_buffer *buf = kzalloc(sizeof(struct ivtv_buffer), GFP_KERNEL);
if (buf == NULL)
break;
buf->buf = kmalloc(s->buf_size + 256, GFP_KERNEL);
if (buf->buf == NULL) {
kfree(buf);
break;
}
INIT_LIST_HEAD(&buf->list);
if (s->dma != PCI_DMA_NONE) {
buf->dma_handle = pci_map_single(s->itv->dev,
buf->buf, s->buf_size + 256, s->dma);
ivtv_buf_sync_for_cpu(s, buf);
}
ivtv_enqueue(s, buf, &s->q_free);
}
if (i == s->buffers)
return 0;
IVTV_ERR("Couldn't allocate buffers for %s stream\n", s->name);
ivtv_stream_free(s);
return -ENOMEM;
}
void ivtv_stream_free(struct ivtv_stream *s)
{
struct ivtv_buffer *buf;
/* move all buffers to q_free */
ivtv_flush_queues(s);
/* empty q_free */
while ((buf = ivtv_dequeue(s, &s->q_free))) {
if (s->dma != PCI_DMA_NONE)
pci_unmap_single(s->itv->dev, buf->dma_handle,
s->buf_size + 256, s->dma);
kfree(buf->buf);
kfree(buf);
}
/* Free SG Array/Lists */
if (s->SGarray != NULL) {
if (s->SG_handle != IVTV_DMA_UNMAPPED) {
pci_unmap_single(s->itv->dev, s->SG_handle,
sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE);
s->SG_handle = IVTV_DMA_UNMAPPED;
}
s->SGarray = NULL;
s->SG_length = 0;
}
}

View File

@ -0,0 +1,64 @@
/*
buffer queues.
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2004 Chris Kennedy <c@groovy.org>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define IVTV_DMA_UNMAPPED ((u32) -1)
/* ivtv_buffer utility functions */
static inline void ivtv_buf_sync_for_cpu(struct ivtv_stream *s, struct ivtv_buffer *buf)
{
if (s->dma != PCI_DMA_NONE)
pci_dma_sync_single_for_cpu(s->itv->dev, buf->dma_handle,
s->buf_size + 256, s->dma);
}
static inline void ivtv_buf_sync_for_device(struct ivtv_stream *s, struct ivtv_buffer *buf)
{
if (s->dma != PCI_DMA_NONE)
pci_dma_sync_single_for_device(s->itv->dev, buf->dma_handle,
s->buf_size + 256, s->dma);
}
int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes);
void ivtv_buf_swap(struct ivtv_buffer *buf);
/* ivtv_queue utility functions */
void ivtv_queue_init(struct ivtv_queue *q);
void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q);
struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q);
int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal,
struct ivtv_queue *to, int needed_bytes);
void ivtv_flush_queues(struct ivtv_stream *s);
/* ivtv_stream utility functions */
int ivtv_stream_alloc(struct ivtv_stream *s);
void ivtv_stream_free(struct ivtv_stream *s);
static inline void ivtv_stream_sync_for_cpu(struct ivtv_stream *s)
{
pci_dma_sync_single_for_cpu(s->itv->dev, s->SG_handle,
sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE);
}
static inline void ivtv_stream_sync_for_device(struct ivtv_stream *s)
{
pci_dma_sync_single_for_device(s->itv->dev, s->SG_handle,
sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE);
}

View File

@ -0,0 +1,977 @@
/*
init/start/stop/exit stream functions
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2004 Chris Kennedy <c@groovy.org>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* License: GPL
* Author: Kevin Thayer <nufan_wfk at yahoo dot com>
*
* This file will hold API related functions, both internal (firmware api)
* and external (v4l2, etc)
*
* -----
* MPG600/MPG160 support by T.Adachi <tadachi@tadachi-net.com>
* and Takeru KOMORIYA<komoriya@paken.org>
*
* AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
* using information provided by Jiun-Kuei Jung @ AVerMedia.
*/
#include "ivtv-driver.h"
#include "ivtv-fileops.h"
#include "ivtv-i2c.h"
#include "ivtv-queue.h"
#include "ivtv-mailbox.h"
#include "ivtv-audio.h"
#include "ivtv-video.h"
#include "ivtv-vbi.h"
#include "ivtv-ioctl.h"
#include "ivtv-irq.h"
#include "ivtv-streams.h"
#include "ivtv-cards.h"
static struct file_operations ivtv_v4l2_enc_fops = {
.owner = THIS_MODULE,
.read = ivtv_v4l2_read,
.write = ivtv_v4l2_write,
.open = ivtv_v4l2_open,
.ioctl = ivtv_v4l2_ioctl,
.release = ivtv_v4l2_close,
.poll = ivtv_v4l2_enc_poll,
};
static struct file_operations ivtv_v4l2_dec_fops = {
.owner = THIS_MODULE,
.read = ivtv_v4l2_read,
.write = ivtv_v4l2_write,
.open = ivtv_v4l2_open,
.ioctl = ivtv_v4l2_ioctl,
.release = ivtv_v4l2_close,
.poll = ivtv_v4l2_dec_poll,
};
struct {
const char *name;
int vfl_type;
int minor_offset;
int dma, pio;
enum v4l2_buf_type buf_type;
struct file_operations *fops;
} ivtv_stream_info[] = {
{ /* IVTV_ENC_STREAM_TYPE_MPG */
"encoder MPEG",
VFL_TYPE_GRABBER, 0,
PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE,
&ivtv_v4l2_enc_fops
},
{ /* IVTV_ENC_STREAM_TYPE_YUV */
"encoder YUV",
VFL_TYPE_GRABBER, IVTV_V4L2_ENC_YUV_OFFSET,
PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE,
&ivtv_v4l2_enc_fops
},
{ /* IVTV_ENC_STREAM_TYPE_VBI */
"encoder VBI",
VFL_TYPE_VBI, 0,
PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VBI_CAPTURE,
&ivtv_v4l2_enc_fops
},
{ /* IVTV_ENC_STREAM_TYPE_PCM */
"encoder PCM audio",
VFL_TYPE_GRABBER, IVTV_V4L2_ENC_PCM_OFFSET,
PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_PRIVATE,
&ivtv_v4l2_enc_fops
},
{ /* IVTV_ENC_STREAM_TYPE_RAD */
"encoder radio",
VFL_TYPE_RADIO, 0,
PCI_DMA_NONE, 1, V4L2_BUF_TYPE_PRIVATE,
&ivtv_v4l2_enc_fops
},
{ /* IVTV_DEC_STREAM_TYPE_MPG */
"decoder MPEG",
VFL_TYPE_GRABBER, IVTV_V4L2_DEC_MPG_OFFSET,
PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT,
&ivtv_v4l2_dec_fops
},
{ /* IVTV_DEC_STREAM_TYPE_VBI */
"decoder VBI",
VFL_TYPE_VBI, IVTV_V4L2_DEC_VBI_OFFSET,
PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_CAPTURE,
&ivtv_v4l2_enc_fops
},
{ /* IVTV_DEC_STREAM_TYPE_VOUT */
"decoder VOUT",
VFL_TYPE_VBI, IVTV_V4L2_DEC_VOUT_OFFSET,
PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_OUTPUT,
&ivtv_v4l2_dec_fops
},
{ /* IVTV_DEC_STREAM_TYPE_YUV */
"decoder YUV",
VFL_TYPE_GRABBER, IVTV_V4L2_DEC_YUV_OFFSET,
PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT,
&ivtv_v4l2_dec_fops
}
};
static void ivtv_stream_init(struct ivtv *itv, int type)
{
struct ivtv_stream *s = &itv->streams[type];
struct video_device *dev = s->v4l2dev;
/* we need to keep v4l2dev, so restore it afterwards */
memset(s, 0, sizeof(*s));
s->v4l2dev = dev;
/* initialize ivtv_stream fields */
s->itv = itv;
s->type = type;
s->name = ivtv_stream_info[type].name;
if (ivtv_stream_info[type].pio)
s->dma = PCI_DMA_NONE;
else
s->dma = ivtv_stream_info[type].dma;
s->buf_size = itv->stream_buf_size[type];
if (s->buf_size)
s->buffers = itv->options.megabytes[type] * 1024 * 1024 / s->buf_size;
spin_lock_init(&s->qlock);
init_waitqueue_head(&s->waitq);
s->id = -1;
s->SG_handle = IVTV_DMA_UNMAPPED;
ivtv_queue_init(&s->q_free);
ivtv_queue_init(&s->q_full);
ivtv_queue_init(&s->q_dma);
ivtv_queue_init(&s->q_predma);
ivtv_queue_init(&s->q_io);
}
static int ivtv_reg_dev(struct ivtv *itv, int type)
{
struct ivtv_stream *s = &itv->streams[type];
int vfl_type = ivtv_stream_info[type].vfl_type;
int minor_offset = ivtv_stream_info[type].minor_offset;
int minor;
/* These four fields are always initialized. If v4l2dev == NULL, then
this stream is not in use. In that case no other fields but these
four can be used. */
s->v4l2dev = NULL;
s->itv = itv;
s->type = type;
s->name = ivtv_stream_info[type].name;
/* Check whether the radio is supported */
if (type == IVTV_ENC_STREAM_TYPE_RAD && !(itv->v4l2_cap & V4L2_CAP_RADIO))
return 0;
if (type >= IVTV_DEC_STREAM_TYPE_MPG && !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
return 0;
if (minor_offset >= 0)
/* card number + user defined offset + device offset */
minor = itv->num + ivtv_first_minor + minor_offset;
else
minor = -1;
/* User explicitly selected 0 buffers for these streams, so don't
create them. */
if (minor >= 0 && ivtv_stream_info[type].dma != PCI_DMA_NONE &&
itv->options.megabytes[type] == 0) {
IVTV_INFO("Disabled %s device\n", ivtv_stream_info[type].name);
return 0;
}
ivtv_stream_init(itv, type);
/* allocate and initialize the v4l2 video device structure */
s->v4l2dev = video_device_alloc();
if (s->v4l2dev == NULL) {
IVTV_ERR("Couldn't allocate v4l2 video_device for %s\n", s->name);
return -ENOMEM;
}
s->v4l2dev->type = VID_TYPE_CAPTURE | VID_TYPE_TUNER | VID_TYPE_TELETEXT |
VID_TYPE_CLIPPING | VID_TYPE_SCALES | VID_TYPE_MPEG_ENCODER;
if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
s->v4l2dev->type |= VID_TYPE_MPEG_DECODER;
}
snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "ivtv%d %s",
itv->num, s->name);
s->v4l2dev->minor = minor;
s->v4l2dev->dev = &itv->dev->dev;
s->v4l2dev->fops = ivtv_stream_info[type].fops;
s->v4l2dev->release = video_device_release;
if (minor >= 0) {
/* Register device. First try the desired minor, then any free one. */
if (video_register_device(s->v4l2dev, vfl_type, minor) &&
video_register_device(s->v4l2dev, vfl_type, -1)) {
IVTV_ERR("Couldn't register v4l2 device for %s minor %d\n",
s->name, minor);
video_device_release(s->v4l2dev);
s->v4l2dev = NULL;
return -ENOMEM;
}
}
else {
/* Don't register a 'hidden' stream (OSD) */
IVTV_INFO("Created framebuffer stream for %s\n", s->name);
return 0;
}
switch (vfl_type) {
case VFL_TYPE_GRABBER:
IVTV_INFO("Registered device video%d for %s (%d MB)\n",
s->v4l2dev->minor, s->name, itv->options.megabytes[type]);
break;
case VFL_TYPE_RADIO:
IVTV_INFO("Registered device radio%d for %s\n",
s->v4l2dev->minor - MINOR_VFL_TYPE_RADIO_MIN, s->name);
break;
case VFL_TYPE_VBI:
if (itv->options.megabytes[type])
IVTV_INFO("Registered device vbi%d for %s (%d MB)\n",
s->v4l2dev->minor - MINOR_VFL_TYPE_VBI_MIN,
s->name, itv->options.megabytes[type]);
else
IVTV_INFO("Registered device vbi%d for %s\n",
s->v4l2dev->minor - MINOR_VFL_TYPE_VBI_MIN, s->name);
break;
}
return 0;
}
/* Initialize v4l2 variables and register v4l2 devices */
int ivtv_streams_setup(struct ivtv *itv)
{
int type;
/* Setup V4L2 Devices */
for (type = 0; type < IVTV_MAX_STREAMS; type++) {
/* Register Device */
if (ivtv_reg_dev(itv, type))
break;
if (itv->streams[type].v4l2dev == NULL)
continue;
/* Allocate Stream */
if (ivtv_stream_alloc(&itv->streams[type]))
break;
}
if (type == IVTV_MAX_STREAMS) {
return 0;
}
/* One or more streams could not be initialized. Clean 'em all up. */
ivtv_streams_cleanup(itv);
return -ENOMEM;
}
/* Unregister v4l2 devices */
void ivtv_streams_cleanup(struct ivtv *itv)
{
int type;
/* Teardown all streams */
for (type = 0; type < IVTV_MAX_STREAMS; type++) {
struct video_device *vdev = itv->streams[type].v4l2dev;
itv->streams[type].v4l2dev = NULL;
if (vdev == NULL)
continue;
ivtv_stream_free(&itv->streams[type]);
/* Free Device */
if (vdev->minor == -1) /* 'Hidden' never registered stream (OSD) */
video_device_release(vdev);
else /* All others, just unregister. */
video_unregister_device(vdev);
}
}
static void ivtv_vbi_setup(struct ivtv *itv)
{
int raw = itv->vbi.sliced_in->service_set == 0;
u32 data[CX2341X_MBOX_MAX_DATA];
int lines;
int i;
/* If Embed then streamtype must be Program */
/* TODO: should we really do this? */
if (0 && !raw && itv->vbi.insert_mpeg) {
itv->params.stream_type = 0;
/* assign stream type */
ivtv_vapi(itv, CX2341X_ENC_SET_STREAM_TYPE, 1, itv->params.stream_type);
}
/* Reset VBI */
ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0);
if (itv->is_60hz) {
itv->vbi.count = 12;
itv->vbi.start[0] = 10;
itv->vbi.start[1] = 273;
} else { /* PAL/SECAM */
itv->vbi.count = 18;
itv->vbi.start[0] = 6;
itv->vbi.start[1] = 318;
}
/* setup VBI registers */
itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in);
/* determine number of lines and total number of VBI bytes.
A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1
The '- 1' byte is probably an unused U or V byte. Or something...
A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal
header, 42 data bytes + checksum (to be confirmed) */
if (raw) {
lines = itv->vbi.count * 2;
} else {
lines = itv->is_60hz ? 24 : 38;
if (itv->is_60hz && (itv->hw_flags & IVTV_HW_CX25840))
lines += 2;
}
itv->vbi.enc_size = lines * (raw ? itv->vbi.raw_size : itv->vbi.sliced_size);
/* Note: sliced vs raw flag doesn't seem to have any effect
TODO: check mode (0x02) value with older ivtv versions. */
data[0] = raw | 0x02 | (0xbd << 8);
/* Every X number of frames a VBI interrupt arrives (frames as in 25 or 30 fps) */
data[1] = 1;
/* The VBI frames are stored in a ringbuffer with this size (with a VBI frame as unit) */
data[2] = raw ? 4 : 8;
/* The start/stop codes determine which VBI lines end up in the raw VBI data area.
The codes are from table 24 in the saa7115 datasheet. Each raw/sliced/video line
is framed with codes FF0000XX where XX is the SAV/EAV (Start/End of Active Video)
code. These values for raw VBI are obtained from a driver disassembly. The sliced
start/stop codes was deduced from this, but they do not appear in the driver.
Other code pairs that I found are: 0x250E6249/0x13545454 and 0x25256262/0x38137F54.
However, I have no idea what these values are for. */
if (itv->hw_flags & IVTV_HW_CX25840) {
/* Setup VBI for the cx25840 digitizer */
if (raw) {
data[3] = 0x20602060;
data[4] = 0x30703070;
} else {
data[3] = 0xB0F0B0F0;
data[4] = 0xA0E0A0E0;
}
/* Lines per frame */
data[5] = lines;
/* bytes per line */
data[6] = (raw ? itv->vbi.raw_size : itv->vbi.sliced_size);
} else {
/* Setup VBI for the saa7115 digitizer */
if (raw) {
data[3] = 0x25256262;
data[4] = 0x387F7F7F;
} else {
data[3] = 0xABABECEC;
data[4] = 0xB6F1F1F1;
}
/* Lines per frame */
data[5] = lines;
/* bytes per line */
data[6] = itv->vbi.enc_size / lines;
}
IVTV_DEBUG_INFO(
"Setup VBI API header 0x%08x pkts %d buffs %d ln %d sz %d\n",
data[0], data[1], data[2], data[5], data[6]);
ivtv_api(itv, CX2341X_ENC_SET_VBI_CONFIG, 7, data);
/* returns the VBI encoder memory area. */
itv->vbi.enc_start = data[2];
itv->vbi.fpi = data[0];
if (!itv->vbi.fpi)
itv->vbi.fpi = 1;
IVTV_DEBUG_INFO("Setup VBI start 0x%08x frames %d fpi %d lines 0x%08x\n",
itv->vbi.enc_start, data[1], itv->vbi.fpi, itv->digitizer);
/* select VBI lines.
Note that the sliced argument seems to have no effect. */
for (i = 2; i <= 24; i++) {
int valid;
if (itv->is_60hz) {
valid = i >= 10 && i < 22;
} else {
valid = i >= 6 && i < 24;
}
ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, i - 1,
valid, 0 , 0, 0);
ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, (i - 1) | 0x80000000,
valid, 0, 0, 0);
}
/* Remaining VBI questions:
- Is it possible to select particular VBI lines only for inclusion in the MPEG
stream? Currently you can only get the first X lines.
- Is mixed raw and sliced VBI possible?
- What's the meaning of the raw/sliced flag?
- What's the meaning of params 2, 3 & 4 of the Select VBI command? */
}
int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
{
u32 data[CX2341X_MBOX_MAX_DATA];
struct ivtv *itv = s->itv;
int captype = 0, subtype = 0;
int enable_passthrough = 0;
if (s->v4l2dev == NULL)
return -EINVAL;
IVTV_DEBUG_INFO("Start encoder stream %s\n", s->name);
switch (s->type) {
case IVTV_ENC_STREAM_TYPE_MPG:
captype = 0;
subtype = 3;
/* Stop Passthrough */
if (itv->output_mode == OUT_PASSTHROUGH) {
ivtv_passthrough_mode(itv, 0);
enable_passthrough = 1;
}
itv->mpg_data_received = itv->vbi_data_inserted = 0;
itv->dualwatch_jiffies = jiffies;
itv->dualwatch_stereo_mode = itv->params.audio_properties & 0x0300;
itv->search_pack_header = 0;
break;
case IVTV_ENC_STREAM_TYPE_YUV:
if (itv->output_mode == OUT_PASSTHROUGH) {
captype = 2;
subtype = 11; /* video+audio+decoder */
break;
}
captype = 1;
subtype = 1;
break;
case IVTV_ENC_STREAM_TYPE_PCM:
captype = 1;
subtype = 2;
break;
case IVTV_ENC_STREAM_TYPE_VBI:
captype = 1;
subtype = 4;
itv->vbi.frame = 0;
itv->vbi.inserted_frame = 0;
memset(itv->vbi.sliced_mpeg_size,
0, sizeof(itv->vbi.sliced_mpeg_size));
break;
default:
return -EINVAL;
}
s->subtype = subtype;
s->buffers_stolen = 0;
/* mute/unmute video */
ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1, test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? 1 : 0);
/* Clear Streamoff flags in case left from last capture */
clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
if (atomic_read(&itv->capturing) == 0) {
/* Always use frame based mode. Experiments have demonstrated that byte
stream based mode results in dropped frames and corruption. Not often,
but occasionally. Many thanks go to Leonard Orb who spent a lot of
effort and time trying to trace the cause of the drop outs. */
/* 1 frame per DMA */
/*ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 128, 0); */
ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 1, 1);
/* Stuff from Windows, we don't know what it is */
ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1, 0);
/* According to the docs, this should be correct. However, this is
untested. I don't dare enable this without having tested it.
Only very few old cards actually have this hardware combination.
ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1,
((itv->hw_flags & IVTV_HW_SAA7114) && itv->is_60hz) ? 10001 : 0);
*/
ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 3, !itv->has_cx23415);
ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 8, 0);
ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 4, 1);
ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12);
/* assign placeholder */
ivtv_vapi(itv, CX2341X_ENC_SET_PLACEHOLDER, 12,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
ivtv_vapi(itv, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, itv->digitizer, itv->digitizer);
/* Setup VBI */
if (itv->v4l2_cap & V4L2_CAP_VBI_CAPTURE) {
ivtv_vbi_setup(itv);
}
/* assign program index info. Mask 7: select I/P/B, Num_req: 400 max */
ivtv_vapi_result(itv, data, CX2341X_ENC_SET_PGM_INDEX_INFO, 2, 7, 400);
itv->pgm_info_offset = data[0];
itv->pgm_info_num = data[1];
itv->pgm_info_write_idx = 0;
itv->pgm_info_read_idx = 0;
IVTV_DEBUG_INFO("PGM Index at 0x%08x with %d elements\n",
itv->pgm_info_offset, itv->pgm_info_num);
/* Setup API for Stream */
cx2341x_update(itv, ivtv_api_func, NULL, &itv->params);
}
/* Vsync Setup */
if (itv->has_cx23415 && !test_and_set_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) {
/* event notification (on) */
ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_ENC_VIM_RST, -1);
ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST);
}
if (atomic_read(&itv->capturing) == 0) {
/* Clear all Pending Interrupts */
ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
clear_bit(IVTV_F_I_EOS, &itv->i_flags);
/* Initialize Digitizer for Capture */
ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
ivtv_sleep_timeout(HZ / 10, 0);
}
/* begin_capture */
if (ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, captype, subtype))
{
IVTV_DEBUG_WARN( "Error starting capture!\n");
return -EINVAL;
}
/* Start Passthrough */
if (enable_passthrough) {
ivtv_passthrough_mode(itv, 1);
}
if (s->type == IVTV_ENC_STREAM_TYPE_VBI)
ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP);
else
ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
/* you're live! sit back and await interrupts :) */
atomic_inc(&itv->capturing);
return 0;
}
static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
{
u32 data[CX2341X_MBOX_MAX_DATA];
struct ivtv *itv = s->itv;
int datatype;
if (s->v4l2dev == NULL)
return -EINVAL;
IVTV_DEBUG_INFO("Setting some initial decoder settings\n");
/* disable VBI signals, if the MPEG stream contains VBI data,
then that data will be processed automatically for you. */
ivtv_disable_vbi(itv);
/* set audio mode to left/stereo for dual/stereo mode. */
ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
/* set number of internal decoder buffers */
ivtv_vapi(itv, CX2341X_DEC_SET_DISPLAY_BUFFERS, 1, 0);
/* prebuffering */
ivtv_vapi(itv, CX2341X_DEC_SET_PREBUFFERING, 1, 1);
/* extract from user packets */
ivtv_vapi_result(itv, data, CX2341X_DEC_EXTRACT_VBI, 1, 1);
itv->vbi.dec_start = data[0];
IVTV_DEBUG_INFO("Decoder VBI RE-Insert start 0x%08x size 0x%08x\n",
itv->vbi.dec_start, data[1]);
/* set decoder source settings */
/* Data type: 0 = mpeg from host,
1 = yuv from encoder,
2 = yuv_from_host */
switch (s->type) {
case IVTV_DEC_STREAM_TYPE_YUV:
datatype = itv->output_mode == OUT_PASSTHROUGH ? 1 : 2;
IVTV_DEBUG_INFO("Setup DEC YUV Stream data[0] = %d\n", datatype);
break;
case IVTV_DEC_STREAM_TYPE_MPG:
default:
datatype = 0;
break;
}
if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype,
itv->params.width, itv->params.height, itv->params.audio_properties)) {
IVTV_DEBUG_WARN("COULDN'T INITIALIZE DECODER SOURCE\n");
}
return 0;
}
int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset)
{
struct ivtv *itv = s->itv;
if (s->v4l2dev == NULL)
return -EINVAL;
if (test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags))
return 0; /* already started */
IVTV_DEBUG_INFO("Starting decode stream %s (gop_offset %d)\n", s->name, gop_offset);
/* Clear Streamoff */
if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
/* Initialize Decoder */
/* Reprogram Decoder YUV Buffers for YUV */
write_reg(yuv_offset[0] >> 4, 0x82c);
write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830);
write_reg(yuv_offset[0] >> 4, 0x834);
write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838);
write_reg_sync(0x00000000 | (0x0c << 16) | (0x0b << 8), 0x2d24);
write_reg_sync(0x00108080, 0x2898);
/* Enable YUV decoder output */
write_reg_sync(0x01, IVTV_REG_VDM);
}
ivtv_setup_v4l2_decode_stream(s);
/* set dma size to 65536 bytes */
ivtv_vapi(itv, CX2341X_DEC_SET_DMA_BLOCK_SIZE, 1, 65536);
clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
/* Zero out decoder counters */
writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[0]);
writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[1]);
writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[2]);
writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[3]);
writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[0]);
writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[1]);
writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[2]);
writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[3]);
/* turn on notification of dual/stereo mode change */
ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_DEC_AUD_MODE_CHG, -1);
/* start playback */
ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, gop_offset, 0);
/* Clear the following Interrupt mask bits for decoding */
ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_DECODE);
IVTV_DEBUG_IRQ("IRQ Mask is now: 0x%08x\n", itv->irqmask);
/* you're live! sit back and await interrupts :) */
atomic_inc(&itv->decoding);
return 0;
}
void ivtv_stop_all_captures(struct ivtv *itv)
{
int i;
for (i = IVTV_MAX_STREAMS - 1; i >= 0; i--) {
struct ivtv_stream *s = &itv->streams[i];
if (s->v4l2dev == NULL)
continue;
if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
ivtv_stop_v4l2_encode_stream(s, 0);
}
}
}
int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end)
{
struct ivtv *itv = s->itv;
DECLARE_WAITQUEUE(wait, current);
int cap_type;
unsigned long then;
int stopmode;
u32 data[CX2341X_MBOX_MAX_DATA];
if (s->v4l2dev == NULL)
return -EINVAL;
/* This function assumes that you are allowed to stop the capture
and that we are actually capturing */
IVTV_DEBUG_INFO("Stop Capture\n");
if (s->type == IVTV_DEC_STREAM_TYPE_VOUT)
return 0;
if (atomic_read(&itv->capturing) == 0)
return 0;
switch (s->type) {
case IVTV_ENC_STREAM_TYPE_YUV:
cap_type = 1;
break;
case IVTV_ENC_STREAM_TYPE_PCM:
cap_type = 1;
break;
case IVTV_ENC_STREAM_TYPE_VBI:
cap_type = 1;
break;
case IVTV_ENC_STREAM_TYPE_MPG:
default:
cap_type = 0;
break;
}
/* Stop Capture Mode */
if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) {
stopmode = 0;
} else {
stopmode = 1;
}
/* end_capture */
/* when: 0 = end of GOP 1 = NOW!, type: 0 = mpeg, subtype: 3 = video+audio */
ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, stopmode, cap_type, s->subtype);
/* only run these if we're shutting down the last cap */
if (atomic_read(&itv->capturing) - 1 == 0) {
/* event notification (off) */
if (test_and_clear_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) {
/* type: 0 = refresh */
/* on/off: 0 = off, intr: 0x10000000, mbox_id: -1: none */
ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_ENC_VIM_RST, -1);
ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST);
}
}
then = jiffies;
if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) {
if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) {
/* only run these if we're shutting down the last cap */
unsigned long duration;
then = jiffies;
add_wait_queue(&itv->cap_w, &wait);
set_current_state(TASK_INTERRUPTIBLE);
/* wait 2s for EOS interrupt */
while (!test_bit(IVTV_F_I_EOS, &itv->i_flags) && jiffies < then + 2 * HZ) {
schedule_timeout(HZ / 100);
}
/* To convert jiffies to ms, we must multiply by 1000
* and divide by HZ. To avoid runtime division, we
* convert this to multiplication by 1000/HZ.
* Since integer division truncates, we get the best
* accuracy if we do a rounding calculation of the constant.
* Think of the case where HZ is 1024.
*/
duration = ((1000 + HZ / 2) / HZ) * (jiffies - then);
if (!test_bit(IVTV_F_I_EOS, &itv->i_flags)) {
IVTV_DEBUG_WARN("%s: EOS interrupt not received! stopping anyway.\n", s->name);
IVTV_DEBUG_WARN("%s: waited %lu ms.\n", s->name, duration);
} else {
IVTV_DEBUG_INFO("%s: EOS took %lu ms to occur.\n", s->name, duration);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&itv->cap_w, &wait);
}
then = jiffies;
/* Make sure DMA is complete */
add_wait_queue(&s->waitq, &wait);
set_current_state(TASK_INTERRUPTIBLE);
do {
/* check if DMA is pending */
if ((s->type == IVTV_ENC_STREAM_TYPE_MPG) && /* MPG Only */
(read_reg(IVTV_REG_DMASTATUS) & 0x02)) {
/* Check for last DMA */
ivtv_vapi_result(itv, data, CX2341X_ENC_GET_SEQ_END, 2, 0, 0);
if (data[0] == 1) {
IVTV_DEBUG_DMA("%s: Last DMA of size 0x%08x\n", s->name, data[1]);
break;
}
} else if (read_reg(IVTV_REG_DMASTATUS) & 0x02) {
break;
}
ivtv_sleep_timeout(HZ / 100, 1);
} while (then + HZ * 2 > jiffies);
set_current_state(TASK_RUNNING);
remove_wait_queue(&s->waitq, &wait);
}
atomic_dec(&itv->capturing);
/* Clear capture and no-read bits */
clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
if (s->type == IVTV_ENC_STREAM_TYPE_VBI)
ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP);
if (atomic_read(&itv->capturing) > 0) {
return 0;
}
/* Set the following Interrupt mask bits for capture */
ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
wake_up(&s->waitq);
return 0;
}
int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts)
{
struct ivtv *itv = s->itv;
if (s->v4l2dev == NULL)
return -EINVAL;
if (s->type != IVTV_DEC_STREAM_TYPE_YUV && s->type != IVTV_DEC_STREAM_TYPE_MPG)
return -EINVAL;
if (!test_bit(IVTV_F_S_STREAMING, &s->s_flags))
return 0;
IVTV_DEBUG_INFO("Stop Decode at %llu, flags: %x\n", pts, flags);
/* Stop Decoder */
if (!(flags & VIDEO_CMD_STOP_IMMEDIATELY) || pts) {
u32 tmp = 0;
/* Wait until the decoder is no longer running */
if (pts) {
ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3,
0, (u32)(pts & 0xffffffff), (u32)(pts >> 32));
}
while (1) {
u32 data[CX2341X_MBOX_MAX_DATA];
ivtv_vapi_result(itv, data, CX2341X_DEC_GET_XFER_INFO, 0);
if (s->q_full.buffers + s->q_dma.buffers == 0) {
if (tmp == data[3])
break;
tmp = data[3];
}
if (ivtv_sleep_timeout(HZ/10, 1))
break;
}
}
ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & VIDEO_CMD_STOP_TO_BLACK, 0, 0);
/* turn off notification of dual/stereo mode change */
ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_DEC_AUD_MODE_CHG, -1);
ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_DECODE);
clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
ivtv_flush_queues(s);
if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) {
/* disable VBI on TV-out */
ivtv_disable_vbi(itv);
}
/* decrement decoding */
atomic_dec(&itv->decoding);
set_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags);
wake_up(&itv->event_waitq);
/* wake up wait queues */
wake_up(&s->waitq);
return 0;
}
int ivtv_passthrough_mode(struct ivtv *itv, int enable)
{
struct ivtv_stream *yuv_stream = &itv->streams[IVTV_ENC_STREAM_TYPE_YUV];
struct ivtv_stream *dec_stream = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
if (yuv_stream->v4l2dev == NULL || dec_stream->v4l2dev == NULL)
return -EINVAL;
IVTV_DEBUG_INFO("ivtv ioctl: Select passthrough mode\n");
/* Prevent others from starting/stopping streams while we
initiate/terminate passthrough mode */
if (enable) {
if (itv->output_mode == OUT_PASSTHROUGH) {
return 0;
}
if (ivtv_set_output_mode(itv, OUT_PASSTHROUGH) != OUT_PASSTHROUGH)
return -EBUSY;
/* Fully initialize stream, and then unflag init */
set_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
set_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
/* Setup YUV Decoder */
ivtv_setup_v4l2_decode_stream(dec_stream);
/* Start Decoder */
ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1);
atomic_inc(&itv->decoding);
/* Setup capture if not already done */
if (atomic_read(&itv->capturing) == 0) {
cx2341x_update(itv, ivtv_api_func, NULL, &itv->params);
}
/* Start Passthrough Mode */
ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, 2, 11);
atomic_inc(&itv->capturing);
return 0;
}
if (itv->output_mode != OUT_PASSTHROUGH)
return 0;
/* Stop Passthrough Mode */
ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 11);
ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, 1, 0, 0);
atomic_dec(&itv->capturing);
atomic_dec(&itv->decoding);
clear_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
itv->output_mode = OUT_NONE;
return 0;
}

View File

@ -0,0 +1,31 @@
/*
init/start/stop/exit stream functions
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
int ivtv_streams_setup(struct ivtv *itv);
void ivtv_streams_cleanup(struct ivtv *itv);
/* Capture related */
int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s);
int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end);
int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset);
int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts);
void ivtv_stop_all_captures(struct ivtv *itv);
int ivtv_passthrough_mode(struct ivtv *itv, int enable);

View File

@ -0,0 +1,200 @@
/*
User DMA
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2004 Chris Kennedy <c@groovy.org>
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ivtv-driver.h"
#include "ivtv-streams.h"
#include "ivtv-udma.h"
void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size)
{
dma_page->uaddr = first & PAGE_MASK;
dma_page->offset = first & ~PAGE_MASK;
dma_page->tail = 1 + ((first+size-1) & ~PAGE_MASK);
dma_page->first = (first & PAGE_MASK) >> PAGE_SHIFT;
dma_page->last = ((first+size-1) & PAGE_MASK) >> PAGE_SHIFT;
dma_page->page_count = dma_page->last - dma_page->first + 1;
if (dma_page->page_count == 1) dma_page->tail -= dma_page->offset;
}
int ivtv_udma_fill_sg_list (struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset)
{
int i, offset;
offset = dma_page->offset;
/* Fill SG Array with new values */
for (i = 0; i < dma_page->page_count; i++) {
if (i == dma_page->page_count - 1) {
dma->SGlist[map_offset].length = dma_page->tail;
}
else {
dma->SGlist[map_offset].length = PAGE_SIZE - offset;
}
dma->SGlist[map_offset].offset = offset;
dma->SGlist[map_offset].page = dma->map[map_offset];
offset = 0;
map_offset++;
}
return map_offset;
}
void ivtv_udma_fill_sg_array (struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split) {
int i;
struct scatterlist *sg;
for (i = 0, sg = dma->SGlist; i < dma->SG_length; i++, sg++) {
dma->SGarray[i].size = cpu_to_le32(sg_dma_len(sg));
dma->SGarray[i].src = cpu_to_le32(sg_dma_address(sg));
dma->SGarray[i].dst = cpu_to_le32(buffer_offset);
buffer_offset += sg_dma_len(sg);
split -= sg_dma_len(sg);
if (split == 0)
buffer_offset = buffer_offset_2;
}
}
/* User DMA Buffers */
void ivtv_udma_alloc(struct ivtv *itv)
{
if (itv->udma.SG_handle == 0) {
/* Map DMA Page Array Buffer */
itv->udma.SG_handle = pci_map_single(itv->dev, itv->udma.SGarray,
sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
ivtv_udma_sync_for_cpu(itv);
}
}
int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
void __user *userbuf, int size_in_bytes)
{
struct ivtv_dma_page_info user_dma;
struct ivtv_user_dma *dma = &itv->udma;
int err;
IVTV_DEBUG_DMA("ivtv_udma_setup, dst: 0x%08x\n", (unsigned int)ivtv_dest_addr);
/* Still in USE */
if (dma->SG_length || dma->page_count) {
IVTV_DEBUG_WARN("ivtv_udma_setup: SG_length %d page_count %d still full?\n",
dma->SG_length, dma->page_count);
return -EBUSY;
}
ivtv_udma_get_page_info(&user_dma, (unsigned long)userbuf, size_in_bytes);
if (user_dma.page_count <= 0) {
IVTV_DEBUG_WARN("ivtv_udma_setup: Error %d page_count from %d bytes %d offset\n",
user_dma.page_count, size_in_bytes, user_dma.offset);
return -EINVAL;
}
/* Get user pages for DMA Xfer */
down_read(&current->mm->mmap_sem);
err = get_user_pages(current, current->mm,
user_dma.uaddr, user_dma.page_count, 0, 1, dma->map, NULL);
up_read(&current->mm->mmap_sem);
if (user_dma.page_count != err) {
IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n",
err, user_dma.page_count);
return -EINVAL;
}
dma->page_count = user_dma.page_count;
/* Fill SG List with new values */
ivtv_udma_fill_sg_list(dma, &user_dma, 0);
/* Map SG List */
dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
/* Fill SG Array with new values */
ivtv_udma_fill_sg_array (dma, ivtv_dest_addr, 0, -1);
/* Tag SG Array with Interrupt Bit */
dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000);
ivtv_udma_sync_for_device(itv);
return dma->page_count;
}
void ivtv_udma_unmap(struct ivtv *itv)
{
struct ivtv_user_dma *dma = &itv->udma;
int i;
IVTV_DEBUG_INFO("ivtv_unmap_user_dma\n");
/* Nothing to free */
if (dma->page_count == 0)
return;
/* Unmap Scatterlist */
if (dma->SG_length) {
pci_unmap_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
dma->SG_length = 0;
}
/* sync DMA */
ivtv_udma_sync_for_cpu(itv);
/* Release User Pages */
for (i = 0; i < dma->page_count; i++) {
put_page(dma->map[i]);
}
dma->page_count = 0;
}
void ivtv_udma_free(struct ivtv *itv)
{
/* Unmap SG Array */
if (itv->udma.SG_handle) {
pci_unmap_single(itv->dev, itv->udma.SG_handle,
sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
}
/* Unmap Scatterlist */
if (itv->udma.SG_length) {
pci_unmap_sg(itv->dev, itv->udma.SGlist, itv->udma.page_count, PCI_DMA_TODEVICE);
}
}
void ivtv_udma_start(struct ivtv *itv)
{
IVTV_DEBUG_DMA("start UDMA\n");
write_reg(itv->udma.SG_handle, IVTV_REG_DECDMAADDR);
write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);
set_bit(IVTV_F_I_DMA, &itv->i_flags);
set_bit(IVTV_F_I_UDMA, &itv->i_flags);
}
void ivtv_udma_prepare(struct ivtv *itv)
{
unsigned long flags;
spin_lock_irqsave(&itv->dma_reg_lock, flags);
if (!test_bit(IVTV_F_I_DMA, &itv->i_flags))
ivtv_udma_start(itv);
else
set_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags);
spin_unlock_irqrestore(&itv->dma_reg_lock, flags);
}

View File

@ -0,0 +1,43 @@
/*
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2004 Chris Kennedy <c@groovy.org>
Copyright (C) 2006-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* User DMA functions */
void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size);
int ivtv_udma_fill_sg_list(struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset);
void ivtv_udma_fill_sg_array(struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split);
int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
void __user *userbuf, int size_in_bytes);
void ivtv_udma_unmap(struct ivtv *itv);
void ivtv_udma_free(struct ivtv *itv);
void ivtv_udma_alloc(struct ivtv *itv);
void ivtv_udma_prepare(struct ivtv *itv);
void ivtv_udma_start(struct ivtv *itv);
static inline void ivtv_udma_sync_for_device(struct ivtv *itv)
{
pci_dma_sync_single_for_device((struct pci_dev *)itv->dev, itv->udma.SG_handle,
sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
}
static inline void ivtv_udma_sync_for_cpu(struct ivtv *itv)
{
pci_dma_sync_single_for_cpu((struct pci_dev *)itv->dev, itv->udma.SG_handle,
sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
}

View File

@ -0,0 +1,545 @@
/*
Vertical Blank Interval support functions
Copyright (C) 2004-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ivtv-driver.h"
#include "ivtv-video.h"
#include "ivtv-vbi.h"
#include "ivtv-ioctl.h"
#include "ivtv-queue.h"
static int odd_parity(u8 c)
{
c ^= (c >> 4);
c ^= (c >> 2);
c ^= (c >> 1);
return c & 1;
}
void vbi_schedule_work(struct ivtv *itv)
{
queue_work(itv->vbi.work_queues, &itv->vbi.work_queue);
}
static void passthrough_vbi_data(struct ivtv *itv, int cnt)
{
int wss = 0;
u8 cc[4] = { 0x80, 0x80, 0x80, 0x80 };
u8 vps[13];
int found_cc = 0;
int found_wss = 0;
int found_vps = 0;
int cc_pos = itv->vbi.cc_pos;
int i;
for (i = 0; i < cnt; i++) {
struct v4l2_sliced_vbi_data *d = itv->vbi.sliced_dec_data + i;
if (d->id == V4L2_SLICED_CAPTION_525 && d->line == 21) {
found_cc = 1;
if (d->field) {
cc[2] = d->data[0];
cc[3] = d->data[1];
} else {
cc[0] = d->data[0];
cc[1] = d->data[1];
}
}
else if (d->id == V4L2_SLICED_VPS && d->line == 16 && d->field == 0) {
memcpy(vps, d->data, sizeof(vps));
found_vps = 1;
}
else if (d->id == V4L2_SLICED_WSS_625 && d->line == 23 && d->field == 0) {
wss = d->data[0] | d->data[1] << 8;
found_wss = 1;
}
}
if (itv->vbi.wss_found != found_wss || itv->vbi.wss != wss) {
itv->vbi.wss = wss;
itv->vbi.wss_found = found_wss;
set_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags);
}
if (found_vps || itv->vbi.vps_found) {
itv->vbi.vps[0] = vps[2];
itv->vbi.vps[1] = vps[8];
itv->vbi.vps[2] = vps[9];
itv->vbi.vps[3] = vps[10];
itv->vbi.vps[4] = vps[11];
itv->vbi.vps_found = found_vps;
set_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags);
}
if (found_cc && cc_pos < sizeof(itv->vbi.cc_data_even)) {
itv->vbi.cc_data_odd[cc_pos] = cc[0];
itv->vbi.cc_data_odd[cc_pos + 1] = cc[1];
itv->vbi.cc_data_even[cc_pos] = cc[2];
itv->vbi.cc_data_even[cc_pos + 1] = cc[3];
itv->vbi.cc_pos = cc_pos + 2;
set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
}
}
static void copy_vbi_data(struct ivtv *itv, int lines, u32 pts_stamp)
{
int line = 0;
int i;
u32 linemask[2] = { 0, 0 };
unsigned short size;
static const u8 mpeg_hdr_data[] = {
0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x0c, 0x66,
0x24, 0x01, 0x01, 0xd1, 0xd3, 0xfa, 0xff, 0xff,
0x00, 0x00, 0x01, 0xbd, 0x00, 0x1a, 0x84, 0x80,
0x07, 0x21, 0x00, 0x5d, 0x63, 0xa7, 0xff, 0xff
};
const int sd = sizeof(mpeg_hdr_data); /* start of vbi data */
int idx = itv->vbi.frame % IVTV_VBI_FRAMES;
u8 *dst = &itv->vbi.sliced_mpeg_data[idx][0];
for (i = 0; i < lines; i++) {
int f, l;
if (itv->vbi.sliced_data[i].id == 0)
continue;
l = itv->vbi.sliced_data[i].line - 6;
f = itv->vbi.sliced_data[i].field;
if (f)
l += 18;
if (l < 32)
linemask[0] |= (1 << l);
else
linemask[1] |= (1 << (l - 32));
dst[sd + 12 + line * 43] = service2vbi(itv->vbi.sliced_data[i].id);
memcpy(dst + sd + 12 + line * 43 + 1, itv->vbi.sliced_data[i].data, 42);
line++;
}
memcpy(dst, mpeg_hdr_data, sizeof(mpeg_hdr_data));
if (line == 36) {
/* All lines are used, so there is no space for the linemask
(the max size of the VBI data is 36 * 43 + 4 bytes).
So in this case we use the magic number 'ITV0'. */
memcpy(dst + sd, "ITV0", 4);
memcpy(dst + sd + 4, dst + sd + 12, line * 43);
size = 4 + ((43 * line + 3) & ~3);
} else {
memcpy(dst + sd, "itv0", 4);
memcpy(dst + sd + 4, &linemask[0], 8);
size = 12 + ((43 * line + 3) & ~3);
}
dst[4+16] = (size + 10) >> 8;
dst[5+16] = (size + 10) & 0xff;
dst[9+16] = 0x21 | ((pts_stamp >> 29) & 0x6);
dst[10+16] = (pts_stamp >> 22) & 0xff;
dst[11+16] = 1 | ((pts_stamp >> 14) & 0xff);
dst[12+16] = (pts_stamp >> 7) & 0xff;
dst[13+16] = 1 | ((pts_stamp & 0x7f) << 1);
itv->vbi.sliced_mpeg_size[idx] = sd + size;
}
static int ivtv_convert_ivtv_vbi(struct ivtv *itv, u8 *p)
{
u32 linemask[2];
int i, l, id2;
int line = 0;
if (!memcmp(p, "itv0", 4)) {
memcpy(linemask, p + 4, 8);
p += 12;
} else if (!memcmp(p, "ITV0", 4)) {
linemask[0] = 0xffffffff;
linemask[1] = 0xf;
p += 4;
} else {
/* unknown VBI data stream */
return 0;
}
for (i = 0; i < 36; i++) {
int err = 0;
if (i < 32 && !(linemask[0] & (1 << i)))
continue;
if (i >= 32 && !(linemask[1] & (1 << (i - 32))))
continue;
id2 = *p & 0xf;
switch (id2) {
case IVTV_SLICED_TYPE_TELETEXT_B:
id2 = V4L2_SLICED_TELETEXT_B;
break;
case IVTV_SLICED_TYPE_CAPTION_525:
id2 = V4L2_SLICED_CAPTION_525;
err = !odd_parity(p[1]) || !odd_parity(p[2]);
break;
case IVTV_SLICED_TYPE_VPS:
id2 = V4L2_SLICED_VPS;
break;
case IVTV_SLICED_TYPE_WSS_625:
id2 = V4L2_SLICED_WSS_625;
break;
default:
id2 = 0;
break;
}
if (err == 0) {
l = (i < 18) ? i + 6 : i - 18 + 6;
itv->vbi.sliced_dec_data[line].line = l;
itv->vbi.sliced_dec_data[line].field = i >= 18;
itv->vbi.sliced_dec_data[line].id = id2;
memcpy(itv->vbi.sliced_dec_data[line].data, p + 1, 42);
line++;
}
p += 43;
}
while (line < 36) {
itv->vbi.sliced_dec_data[line].id = 0;
itv->vbi.sliced_dec_data[line].line = 0;
itv->vbi.sliced_dec_data[line].field = 0;
line++;
}
return line * sizeof(itv->vbi.sliced_dec_data[0]);
}
ssize_t ivtv_write_vbi(struct ivtv *itv, const char __user *ubuf, size_t count)
{
/* Should be a __user pointer, but sparse doesn't parse this bit correctly. */
const struct v4l2_sliced_vbi_data *p = (const struct v4l2_sliced_vbi_data *)ubuf;
u8 cc[4] = { 0x80, 0x80, 0x80, 0x80 };
int found_cc = 0;
int cc_pos = itv->vbi.cc_pos;
if (itv->vbi.service_set_out == 0)
return -EPERM;
while (count >= sizeof(struct v4l2_sliced_vbi_data)) {
switch (p->id) {
case V4L2_SLICED_CAPTION_525:
if (p->id == V4L2_SLICED_CAPTION_525 &&
p->line == 21 &&
(itv->vbi.service_set_out &
V4L2_SLICED_CAPTION_525) == 0) {
break;
}
found_cc = 1;
if (p->field) {
cc[2] = p->data[0];
cc[3] = p->data[1];
} else {
cc[0] = p->data[0];
cc[1] = p->data[1];
}
break;
case V4L2_SLICED_VPS:
if (p->line == 16 && p->field == 0 &&
(itv->vbi.service_set_out & V4L2_SLICED_VPS)) {
itv->vbi.vps[0] = p->data[2];
itv->vbi.vps[1] = p->data[8];
itv->vbi.vps[2] = p->data[9];
itv->vbi.vps[3] = p->data[10];
itv->vbi.vps[4] = p->data[11];
itv->vbi.vps_found = 1;
set_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags);
}
break;
case V4L2_SLICED_WSS_625:
if (p->line == 23 && p->field == 0 &&
(itv->vbi.service_set_out & V4L2_SLICED_WSS_625)) {
/* No lock needed for WSS */
itv->vbi.wss = p->data[0] | (p->data[1] << 8);
itv->vbi.wss_found = 1;
set_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags);
}
break;
default:
break;
}
count -= sizeof(*p);
p++;
}
if (found_cc && cc_pos < sizeof(itv->vbi.cc_data_even)) {
itv->vbi.cc_data_odd[cc_pos] = cc[0];
itv->vbi.cc_data_odd[cc_pos + 1] = cc[1];
itv->vbi.cc_data_even[cc_pos] = cc[2];
itv->vbi.cc_data_even[cc_pos + 1] = cc[3];
itv->vbi.cc_pos = cc_pos + 2;
set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
}
return (const char __user *)p - ubuf;
}
/* Compress raw VBI format, removes leading SAV codes and surplus space after the
field.
Returns new compressed size. */
static u32 compress_raw_buf(struct ivtv *itv, u8 *buf, u32 size)
{
u32 line_size = itv->vbi.raw_decoder_line_size;
u32 lines = itv->vbi.count;
u8 sav1 = itv->vbi.raw_decoder_sav_odd_field;
u8 sav2 = itv->vbi.raw_decoder_sav_even_field;
u8 *q = buf;
u8 *p;
int i;
for (i = 0; i < lines; i++) {
p = buf + i * line_size;
/* Look for SAV code */
if (p[0] != 0xff || p[1] || p[2] || (p[3] != sav1 && p[3] != sav2)) {
break;
}
memcpy(q, p + 4, line_size - 4);
q += line_size - 4;
}
return lines * (line_size - 4);
}
/* Compressed VBI format, all found sliced blocks put next to one another
Returns new compressed size */
static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 sav)
{
u32 line_size = itv->vbi.sliced_decoder_line_size;
struct v4l2_decode_vbi_line vbi;
int i;
/* find the first valid line */
for (i = 0; i < size; i++, buf++) {
if (buf[0] == 0xff && !buf[1] && !buf[2] && buf[3] == sav)
break;
}
size -= i;
if (size < line_size) {
return line;
}
for (i = 0; i < size / line_size; i++) {
u8 *p = buf + i * line_size;
/* Look for SAV code */
if (p[0] != 0xff || p[1] || p[2] || p[3] != sav) {
continue;
}
vbi.p = p + 4;
itv->video_dec_func(itv, VIDIOC_INT_DECODE_VBI_LINE, &vbi);
if (vbi.type) {
itv->vbi.sliced_data[line].id = vbi.type;
itv->vbi.sliced_data[line].field = vbi.is_second_field;
itv->vbi.sliced_data[line].line = vbi.line;
memcpy(itv->vbi.sliced_data[line].data, vbi.p, 42);
line++;
}
}
return line;
}
void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf,
u64 pts_stamp, int streamtype)
{
u8 *p = (u8 *) buf->buf;
u32 size = buf->bytesused;
int y;
/* Raw VBI data */
if (streamtype == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set == 0) {
u8 type;
ivtv_buf_swap(buf);
type = p[3];
size = buf->bytesused = compress_raw_buf(itv, p, size);
/* second field of the frame? */
if (type == itv->vbi.raw_decoder_sav_even_field) {
/* Dirty hack needed for backwards
compatibility of old VBI software. */
p += size - 4;
memcpy(p, &itv->vbi.frame, 4);
itv->vbi.frame++;
}
return;
}
/* Sliced VBI data with data insertion */
if (streamtype == IVTV_ENC_STREAM_TYPE_VBI) {
int lines;
ivtv_buf_swap(buf);
/* first field */
lines = compress_sliced_buf(itv, 0, p, size / 2,
itv->vbi.sliced_decoder_sav_odd_field);
/* second field */
/* experimentation shows that the second half does not always begin
at the exact address. So start a bit earlier (hence 32). */
lines = compress_sliced_buf(itv, lines, p + size / 2 - 32, size / 2 + 32,
itv->vbi.sliced_decoder_sav_even_field);
/* always return at least one empty line */
if (lines == 0) {
itv->vbi.sliced_data[0].id = 0;
itv->vbi.sliced_data[0].line = 0;
itv->vbi.sliced_data[0].field = 0;
lines = 1;
}
buf->bytesused = size = lines * sizeof(itv->vbi.sliced_data[0]);
memcpy(p, &itv->vbi.sliced_data[0], size);
if (itv->vbi.insert_mpeg) {
copy_vbi_data(itv, lines, pts_stamp);
}
itv->vbi.frame++;
return;
}
/* Sliced VBI re-inserted from an MPEG stream */
if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) {
/* If the size is not 4-byte aligned, then the starting address
for the swapping is also shifted. After swapping the data the
real start address of the VBI data is exactly 4 bytes after the
original start. It's a bit fiddly but it works like a charm.
Non-4-byte alignment happens when an lseek is done on the input
mpeg file to a non-4-byte aligned position. So on arrival here
the VBI data is also non-4-byte aligned. */
int offset = size & 3;
int cnt;
if (offset) {
p += 4 - offset;
}
/* Swap Buffer */
for (y = 0; y < size; y += 4) {
swab32s((u32 *)(p + y));
}
cnt = ivtv_convert_ivtv_vbi(itv, p + offset);
memcpy(buf->buf, itv->vbi.sliced_dec_data, cnt);
buf->bytesused = cnt;
passthrough_vbi_data(itv, cnt / sizeof(itv->vbi.sliced_dec_data[0]));
return;
}
}
void ivtv_disable_vbi(struct ivtv *itv)
{
clear_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags);
clear_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags);
clear_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
ivtv_set_wss(itv, 0, 0);
ivtv_set_cc(itv, 0, 0, 0, 0, 0);
ivtv_set_vps(itv, 0, 0, 0, 0, 0, 0);
itv->vbi.vps_found = itv->vbi.wss_found = 0;
itv->vbi.wss = 0;
itv->vbi.cc_pos = 0;
}
void vbi_work_handler(struct work_struct *work)
{
struct vbi_info *info = container_of(work, struct vbi_info, work_queue);
struct ivtv *itv = container_of(info, struct ivtv, vbi);
struct v4l2_sliced_vbi_data data;
DEFINE_WAIT(wait);
/* Lock */
if (itv->output_mode == OUT_PASSTHROUGH) {
/* Note: currently only the saa7115 is used in a PVR350,
so these commands are for now saa7115 specific. */
if (itv->is_50hz) {
data.id = V4L2_SLICED_WSS_625;
data.field = 0;
if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
ivtv_set_wss(itv, 1, data.data[0] & 0xf);
itv->vbi.wss_no_update = 0;
} else if (itv->vbi.wss_no_update == 4) {
ivtv_set_wss(itv, 1, 0x8); /* 4x3 full format */
} else {
itv->vbi.wss_no_update++;
}
}
else {
u8 c1 = 0, c2 = 0, c3 = 0, c4 = 0;
int mode = 0;
data.id = V4L2_SLICED_CAPTION_525;
data.field = 0;
if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
mode |= 1;
c1 = data.data[0];
c2 = data.data[1];
}
data.field = 1;
if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
mode |= 2;
c3 = data.data[0];
c4 = data.data[1];
}
if (mode) {
itv->vbi.cc_no_update = 0;
ivtv_set_cc(itv, mode, c1, c2, c3, c4);
} else if (itv->vbi.cc_no_update == 4) {
ivtv_set_cc(itv, 0, 0, 0, 0, 0);
} else {
itv->vbi.cc_no_update++;
}
}
return;
}
if (test_and_clear_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags)) {
/* Lock */
ivtv_set_wss(itv, itv->vbi.wss_found, itv->vbi.wss & 0xf);
}
if (test_and_clear_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags)) {
if (itv->vbi.cc_pos == 0) {
ivtv_set_cc(itv, 3, 0x80, 0x80, 0x80, 0x80);
}
while (itv->vbi.cc_pos) {
u8 cc_odd0 = itv->vbi.cc_data_odd[0];
u8 cc_odd1 = itv->vbi.cc_data_odd[1];
u8 cc_even0 = itv->vbi.cc_data_even[0];
u8 cc_even1 = itv->vbi.cc_data_even[1];
memcpy(itv->vbi.cc_data_odd, itv->vbi.cc_data_odd + 2, sizeof(itv->vbi.cc_data_odd) - 2);
memcpy(itv->vbi.cc_data_even, itv->vbi.cc_data_even + 2, sizeof(itv->vbi.cc_data_even) - 2);
itv->vbi.cc_pos -= 2;
if (itv->vbi.cc_pos && cc_odd0 == 0x80 && cc_odd1 == 0x80)
continue;
/* Send to Saa7127 */
ivtv_set_cc(itv, 3, cc_odd0, cc_odd1, cc_even0, cc_even1);
if (itv->vbi.cc_pos == 0)
set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
break;
}
}
if (test_and_clear_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags)) {
/* Lock */
ivtv_set_vps(itv, itv->vbi.vps_found,
itv->vbi.vps[0], itv->vbi.vps[1],
itv->vbi.vps[2], itv->vbi.vps[3], itv->vbi.vps[4]);
}
}

View File

@ -0,0 +1,27 @@
/*
Vertical Blank Interval support functions
Copyright (C) 2004-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
ssize_t ivtv_write_vbi(struct ivtv *itv, const char __user *ubuf, size_t count);
void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf,
u64 pts_stamp, int streamtype);
int ivtv_used_line(struct ivtv *itv, int line, int field);
void ivtv_disable_vbi(struct ivtv *itv);
void ivtv_set_vbi(unsigned long arg);
void vbi_work_handler(struct work_struct *work);
void vbi_schedule_work(struct ivtv *itv);

View File

@ -0,0 +1,26 @@
/*
ivtv driver version information
Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define IVTV_DRIVER_NAME "ivtv"
#define IVTV_DRIVER_VERSION_MAJOR 1
#define IVTV_DRIVER_VERSION_MINOR 0
#define IVTV_DRIVER_VERSION_PATCHLEVEL 0
#define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL)
#define IVTV_DRIVER_VERSION KERNEL_VERSION(IVTV_DRIVER_VERSION_MAJOR,IVTV_DRIVER_VERSION_MINOR,IVTV_DRIVER_VERSION_PATCHLEVEL)

View File

@ -0,0 +1,150 @@
/*
saa7127 interface functions
Copyright (C) 2004-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ivtv-driver.h"
#include "ivtv-video.h"
#include "ivtv-i2c.h"
#include "ivtv-gpio.h"
#include "ivtv-cards.h"
#include <media/upd64031a.h>
#include <media/upd64083.h>
void ivtv_set_vps(struct ivtv *itv, int enabled, u8 vps1, u8 vps2, u8 vps3,
u8 vps4, u8 vps5)
{
struct v4l2_sliced_vbi_data data;
if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
return;
data.id = V4L2_SLICED_VPS;
data.field = 0;
data.line = enabled ? 16 : 0;
data.data[4] = vps1;
data.data[10] = vps2;
data.data[11] = vps3;
data.data[12] = vps4;
data.data[13] = vps5;
ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
}
void ivtv_set_cc(struct ivtv *itv, int mode, u8 cc1, u8 cc2, u8 cc3, u8 cc4)
{
struct v4l2_sliced_vbi_data data;
if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
return;
data.id = V4L2_SLICED_CAPTION_525;
data.field = 0;
data.line = (mode & 1) ? 21 : 0;
data.data[0] = cc1;
data.data[1] = cc2;
ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
data.field = 1;
data.line = (mode & 2) ? 21 : 0;
data.data[0] = cc3;
data.data[1] = cc4;
ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
}
void ivtv_set_wss(struct ivtv *itv, int enabled, int mode)
{
struct v4l2_sliced_vbi_data data;
if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
return;
/* When using a 50 Hz system, always turn on the
wide screen signal with 4x3 ratio as the default.
Turning this signal on and off can confuse certain
TVs. As far as I can tell there is no reason not to
transmit this signal. */
if ((itv->std & V4L2_STD_625_50) && !enabled) {
enabled = 1;
mode = 0x08; /* 4x3 full format */
}
data.id = V4L2_SLICED_WSS_625;
data.field = 0;
data.line = enabled ? 23 : 0;
data.data[0] = mode & 0xff;
data.data[1] = (mode >> 8) & 0xff;
ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
}
void ivtv_encoder_enable(struct ivtv *itv, int enabled)
{
if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
ivtv_saa7127(itv, enabled ? VIDIOC_STREAMON : VIDIOC_STREAMOFF,
&enabled);
}
}
void ivtv_video_set_io(struct ivtv *itv)
{
struct v4l2_routing route;
int inp = itv->active_input;
u32 type;
route.input = itv->card->video_inputs[inp].video_input;
route.output = 0;
itv->video_dec_func(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
type = itv->card->video_inputs[inp].video_type;
if (type == IVTV_CARD_INPUT_VID_TUNER) {
route.input = 0; /* Tuner */
} else if (type < IVTV_CARD_INPUT_COMPOSITE1) {
route.input = 2; /* S-Video */
} else {
route.input = 1; /* Composite */
}
if (itv->card->hw_video & IVTV_HW_GPIO)
ivtv_gpio(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
if (itv->card->hw_video & IVTV_HW_UPD64031A) {
if (type == IVTV_CARD_INPUT_VID_TUNER ||
type >= IVTV_CARD_INPUT_COMPOSITE1) {
/* Composite: GR on, connect to 3DYCS */
route.input = UPD64031A_GR_ON | UPD64031A_3DYCS_COMPOSITE;
} else {
/* S-Video: GR bypassed, turn it off */
route.input = UPD64031A_GR_OFF | UPD64031A_3DYCS_DISABLE;
}
route.input |= itv->card->gr_config;
ivtv_upd64031a(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
}
if (itv->card->hw_video & IVTV_HW_UPD6408X) {
route.input = UPD64083_YCS_MODE;
if (type > IVTV_CARD_INPUT_VID_TUNER &&
type < IVTV_CARD_INPUT_COMPOSITE1) {
/* S-Video uses YCNR mode and internal Y-ADC, the upd64031a
is not used. */
route.input |= UPD64083_YCNR_MODE;
}
else if (itv->card->hw_video & IVTV_HW_UPD64031A) {
/* Use upd64031a output for tuner and composite(CX23416GYC only) inputs */
if ((type == IVTV_CARD_INPUT_VID_TUNER)||
(itv->card->type == IVTV_CARD_CX23416GYC)) {
route.input |= UPD64083_EXT_Y_ADC;
}
}
ivtv_upd64083(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
}
}

View File

@ -0,0 +1,25 @@
/*
saa7127 interface functions
Copyright (C) 2004-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
void ivtv_set_wss(struct ivtv *itv, int enabled, int mode);
void ivtv_set_cc(struct ivtv *itv, int mode, u8 cc1, u8 cc2, u8 cc3, u8 cc4);
void ivtv_set_vps(struct ivtv *itv, int enabled, u8 vps1, u8 vps2, u8 vps3,
u8 vps4, u8 vps5);
void ivtv_encoder_enable(struct ivtv *itv, int enabled);
void ivtv_video_set_io(struct ivtv *itv);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,24 @@
/*
yuv support
Copyright (C) 2007 Ian Armstrong <ian@iarmst.demon.co.uk>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
int ivtv_yuv_filter_check(struct ivtv *itv);
int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args);
void ivtv_yuv_close(struct ivtv *itv);
void ivtv_yuv_work_handler (struct work_struct *work);

View File

@ -0,0 +1,65 @@
/*
Public ivtv API header
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
Copyright (C) 2004-2007 Hans Verkuil <hverkuil@xs4all.nl>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _LINUX_IVTV_H
#define _LINUX_IVTV_H
/* ivtv knows several distinct output modes: MPEG streaming,
YUV streaming, YUV updates through user DMA and the passthrough
mode.
In order to clearly tell the driver that we are in user DMA
YUV mode you need to call IVTV_IOC_DMA_FRAME with y_source == NULL
first (althrough if you don't then the first time
DMA_FRAME is called the mode switch is done automatically).
When you close the file handle the user DMA mode is exited again.
While in one mode, you cannot use another mode (EBUSY is returned).
All this means that if you want to change the YUV interlacing
for the user DMA YUV mode you first need to do call IVTV_IOC_DMA_FRAME
with y_source == NULL before you can set the correct format using
VIDIOC_S_FMT.
Eventually all this should be replaced with a proper V4L2 API,
but for now we have to do it this way. */
struct ivtv_dma_frame {
enum v4l2_buf_type type; /* V4L2_BUF_TYPE_VIDEO_OUTPUT */
__u32 pixelformat; /* 0 == same as destination */
void __user *y_source; /* if NULL and type == V4L2_BUF_TYPE_VIDEO_OUTPUT,
then just switch to user DMA YUV output mode */
void __user *uv_source; /* Unused for RGB pixelformats */
struct v4l2_rect src;
struct v4l2_rect dst;
__u32 src_width;
__u32 src_height;
};
#define IVTV_IOC_DMA_FRAME _IOW ('V', BASE_VIDIOC_PRIVATE+0, struct ivtv_dma_frame)
/* These are the VBI types as they appear in the embedded VBI private packets. */
#define IVTV_SLICED_TYPE_TELETEXT_B (1)
#define IVTV_SLICED_TYPE_CAPTION_525 (4)
#define IVTV_SLICED_TYPE_WSS_625 (5)
#define IVTV_SLICED_TYPE_VPS (7)
#endif /* _LINUX_IVTV_H */