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>
This commit is contained in:
parent
ac52ea3c3c
commit
1a0adaf37c
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
|
@ -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
|
|
@ -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]);
|
||||
}
|
||||
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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
|
@ -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 */
|
|
@ -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");
|
||||
}
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
|
@ -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
|
@ -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);
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
|
@ -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]);
|
||||
}
|
|
@ -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]);
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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(¤t->mm->mmap_sem);
|
||||
err = get_user_pages(current, current->mm,
|
||||
user_dma.uaddr, user_dma.page_count, 0, 1, dma->map, NULL);
|
||||
up_read(¤t->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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
}
|
|
@ -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);
|
|
@ -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)
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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);
|
|
@ -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 */
|
Loading…
Reference in New Issue