Commit 1c1e45d1 authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab

V4L/DVB (7786): cx18: new driver for the Conexant CX23418 MPEG encoder chip

Many thanks to Steve Toth from Hauppauge and Nattu Dakshinamurthy from
Conexant for their support. I am in particular thankful to Hauppauge
since without their help this driver would not exist. It should also
be noted that Steve did the work to get the DVB part up and running.
Thank you!
Signed-off-by: default avatarHans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: default avatarSteven Toth <stoth@hauppauge.com>
Signed-off-by: default avatarMichael Krufky <mkrufky@linuxtv.org>
Signed-off-by: default avatarG. Andrew Walls <awalls@radix.net>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent d74bee8b
Some notes regarding the cx18 driver for the Conexant CX23418 MPEG
encoder chip:
1) The only hardware currently supported is the Hauppauge HVR-1600.
2) Some people have problems getting the i2c bus to work. Cause unknown.
The symptom is that the eeprom cannot be read and the card is
unusable.
3) The audio from the analog tuner is mono only. Probably caused by
incorrect audio register information in the datasheet. We are
waiting for updated information from Conexant.
4) VBI (raw or sliced) has not yet been implemented.
5) MPEG indexing is not yet implemented.
6) The driver is still a bit rough around the edges, this should
improve over time.
Firmware:
The firmware needs to be extracted from the Windows Hauppauge HVR-1600
driver, available here:
http://hauppauge.lightpath.net/software/install_cd/hauppauge_cd_3.4d1.zip
Unzip, then copy the following files to the firmware directory
and rename them as follows:
Drivers/Driver18/hcw18apu.rom -> v4l-cx23418-apu.fw
Drivers/Driver18/hcw18enc.rom -> v4l-cx23418-cpu.fw
Drivers/Driver18/hcw18mlC.rom -> v4l-cx23418-dig.fw
......@@ -748,6 +748,8 @@ source "drivers/media/video/au0828/Kconfig"
source "drivers/media/video/ivtv/Kconfig"
source "drivers/media/video/cx18/Kconfig"
config VIDEO_M32R_AR
tristate "AR devices"
depends on M32R && VIDEO_V4L1
......
......@@ -124,6 +124,7 @@ obj-$(CONFIG_USB_VICAM) += usbvideo/
obj-$(CONFIG_USB_QUICKCAM_MESSENGER) += usbvideo/
obj-$(CONFIG_VIDEO_IVTV) += ivtv/
obj-$(CONFIG_VIDEO_CX18) += cx18/
obj-$(CONFIG_VIDEO_VIVI) += vivi.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885/
......
config VIDEO_CX18
tristate "Conexant cx23418 MPEG encoder support"
depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C && EXPERIMENTAL
select I2C_ALGOBIT
select FW_LOADER
select VIDEO_IR
select VIDEO_TUNER
select VIDEO_TVEEPROM
select VIDEO_CX2341X
select VIDEO_CS5345
select DVB_S5H1409
---help---
This is a video4linux driver for Conexant cx23418 based
PCI combo video recorder devices.
This is used in devices such as the Hauppauge HVR-1600
cards.
To compile this driver as a module, choose M here: the
module will be called cx18.
cx18-objs := cx18-driver.o cx18-cards.o cx18-i2c.o cx18-firmware.o cx18-gpio.o \
cx18-queue.o cx18-streams.o cx18-fileops.o cx18-ioctl.o cx18-controls.o \
cx18-mailbox.o cx18-vbi.o cx18-audio.o cx18-video.o cx18-irq.o \
cx18-av-core.o cx18-av-audio.o cx18-av-firmware.o cx18-av-vbi.o cx18-scb.o \
cx18-dvb.o
obj-$(CONFIG_VIDEO_CX18) += cx18.o
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
EXTRA_CFLAGS += -Idrivers/media/common/tuners
/*
* cx18 audio-related functions
*
* Derived from ivtv-audio.c
*
* Copyright (C) 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 "cx18-driver.h"
#include "cx18-i2c.h"
#include "cx18-cards.h"
#include "cx18-audio.h"
/* Selects the audio input and output according to the current
settings. */
int cx18_audio_set_io(struct cx18 *cx)
{
struct v4l2_routing route;
u32 audio_input;
int mux_input;
/* Determine which input to use */
if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
audio_input = cx->card->radio_input.audio_input;
mux_input = cx->card->radio_input.muxer_input;
} else {
audio_input =
cx->card->audio_inputs[cx->audio_input].audio_input;
mux_input =
cx->card->audio_inputs[cx->audio_input].muxer_input;
}
/* handle muxer chips */
route.input = mux_input;
route.output = 0;
cx18_i2c_hw(cx, cx->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route);
route.input = audio_input;
return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl,
VIDIOC_INT_S_AUDIO_ROUTING, &route);
}
void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route)
{
cx18_i2c_hw(cx, cx->card->hw_audio_ctrl,
VIDIOC_INT_S_AUDIO_ROUTING, route);
}
void cx18_audio_set_audio_clock_freq(struct cx18 *cx, 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;
cx18_call_i2c_clients(cx, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[freq]);
}
/*
* cx18 audio-related functions
*
* Derived from ivtv-audio.c
*
* Copyright (C) 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 cx18_audio_set_io(struct cx18 *cx);
void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route);
void cx18_audio_set_audio_clock_freq(struct cx18 *cx, u8 freq);
/*
* cx18 ADEC audio functions
*
* Derived from cx25840-audio.c
*
* Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "cx18-driver.h"
static int set_audclk_freq(struct cx18 *cx, u32 freq)
{
struct cx18_av_state *state = &cx->av_state;
if (freq != 32000 && freq != 44100 && freq != 48000)
return -EINVAL;
/* common for all inputs and rates */
/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */
cx18_av_write(cx, 0x127, 0x50);
if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
switch (freq) {
case 32000:
/* VID_PLL and AUX_PLL */
cx18_av_write4(cx, 0x108, 0x1006040f);
/* AUX_PLL_FRAC */
cx18_av_write4(cx, 0x110, 0x01bb39ee);
/* src3/4/6_ctl = 0x0801f77f */
cx18_av_write4(cx, 0x900, 0x0801f77f);
cx18_av_write4(cx, 0x904, 0x0801f77f);
cx18_av_write4(cx, 0x90c, 0x0801f77f);
break;
case 44100:
/* VID_PLL and AUX_PLL */
cx18_av_write4(cx, 0x108, 0x1009040f);
/* AUX_PLL_FRAC */
cx18_av_write4(cx, 0x110, 0x00ec6bd6);
/* src3/4/6_ctl = 0x08016d59 */
cx18_av_write4(cx, 0x900, 0x08016d59);
cx18_av_write4(cx, 0x904, 0x08016d59);
cx18_av_write4(cx, 0x90c, 0x08016d59);
break;
case 48000:
/* VID_PLL and AUX_PLL */
cx18_av_write4(cx, 0x108, 0x100a040f);
/* AUX_PLL_FRAC */
cx18_av_write4(cx, 0x110, 0x0098d6e5);
/* src3/4/6_ctl = 0x08014faa */
cx18_av_write4(cx, 0x900, 0x08014faa);
cx18_av_write4(cx, 0x904, 0x08014faa);
cx18_av_write4(cx, 0x90c, 0x08014faa);
break;
}
} else {
switch (freq) {
case 32000:
/* VID_PLL and AUX_PLL */
cx18_av_write4(cx, 0x108, 0x1e08040f);
/* AUX_PLL_FRAC */
cx18_av_write4(cx, 0x110, 0x012a0869);
/* src1_ctl = 0x08010000 */
cx18_av_write4(cx, 0x8f8, 0x08010000);
/* src3/4/6_ctl = 0x08020000 */
cx18_av_write4(cx, 0x900, 0x08020000);
cx18_av_write4(cx, 0x904, 0x08020000);
cx18_av_write4(cx, 0x90c, 0x08020000);
/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */
cx18_av_write(cx, 0x127, 0x54);
break;
case 44100:
/* VID_PLL and AUX_PLL */
cx18_av_write4(cx, 0x108, 0x1809040f);
/* AUX_PLL_FRAC */
cx18_av_write4(cx, 0x110, 0x00ec6bd6);
/* src1_ctl = 0x08010000 */
cx18_av_write4(cx, 0x8f8, 0x080160cd);
/* src3/4/6_ctl = 0x08020000 */
cx18_av_write4(cx, 0x900, 0x08017385);
cx18_av_write4(cx, 0x904, 0x08017385);
cx18_av_write4(cx, 0x90c, 0x08017385);
break;
case 48000:
/* VID_PLL and AUX_PLL */
cx18_av_write4(cx, 0x108, 0x180a040f);
/* AUX_PLL_FRAC */
cx18_av_write4(cx, 0x110, 0x0098d6e5);
/* src1_ctl = 0x08010000 */
cx18_av_write4(cx, 0x8f8, 0x08018000);
/* src3/4/6_ctl = 0x08020000 */
cx18_av_write4(cx, 0x900, 0x08015555);
cx18_av_write4(cx, 0x904, 0x08015555);
cx18_av_write4(cx, 0x90c, 0x08015555);
break;
}
}
state->audclk_freq = freq;
return 0;
}
void cx18_av_audio_set_path(struct cx18 *cx)
{
struct cx18_av_state *state = &cx->av_state;
/* stop microcontroller */
cx18_av_and_or(cx, 0x803, ~0x10, 0);
/* assert soft reset */
cx18_av_and_or(cx, 0x810, ~0x1, 0x01);
/* Mute everything to prevent the PFFT! */
cx18_av_write(cx, 0x8d3, 0x1f);
if (state->aud_input == CX18_AV_AUDIO_SERIAL) {
/* Set Path1 to Serial Audio Input */
cx18_av_write4(cx, 0x8d0, 0x01011012);
/* The microcontroller should not be started for the
* non-tuner inputs: autodetection is specific for
* TV audio. */
} else {
/* Set Path1 to Analog Demod Main Channel */
cx18_av_write4(cx, 0x8d0, 0x1f063870);
}
set_audclk_freq(cx, state->audclk_freq);
/* deassert soft reset */
cx18_av_and_or(cx, 0x810, ~0x1, 0x00);
if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
/* When the microcontroller detects the
* audio format, it will unmute the lines */
cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
}
}
static int get_volume(struct cx18 *cx)
{
/* Volume runs +18dB to -96dB in 1/2dB steps
* change to fit the msp3400 -114dB to +12dB range */
/* check PATH1_VOLUME */
int vol = 228 - cx18_av_read(cx, 0x8d4);
vol = (vol / 2) + 23;
return vol << 9;
}
static void set_volume(struct cx18 *cx, int volume)
{
/* First convert the volume to msp3400 values (0-127) */
int vol = volume >> 9;
/* now scale it up to cx18_av values
* -114dB to -96dB maps to 0
* this should be 19, but in my testing that was 4dB too loud */
if (vol <= 23)
vol = 0;
else
vol -= 23;
/* PATH1_VOLUME */
cx18_av_write(cx, 0x8d4, 228 - (vol * 2));
}
static int get_bass(struct cx18 *cx)
{
/* bass is 49 steps +12dB to -12dB */
/* check PATH1_EQ_BASS_VOL */
int bass = cx18_av_read(cx, 0x8d9) & 0x3f;
bass = (((48 - bass) * 0xffff) + 47) / 48;
return bass;
}
static void set_bass(struct cx18 *cx, int bass)
{
/* PATH1_EQ_BASS_VOL */
cx18_av_and_or(cx, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff));
}
static int get_treble(struct cx18 *cx)
{
/* treble is 49 steps +12dB to -12dB */
/* check PATH1_EQ_TREBLE_VOL */
int treble = cx18_av_read(cx, 0x8db) & 0x3f;
treble = (((48 - treble) * 0xffff) + 47) / 48;
return treble;
}
static void set_treble(struct cx18 *cx, int treble)
{
/* PATH1_EQ_TREBLE_VOL */
cx18_av_and_or(cx, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff));
}
static int get_balance(struct cx18 *cx)
{
/* balance is 7 bit, 0 to -96dB */
/* check PATH1_BAL_LEVEL */
int balance = cx18_av_read(cx, 0x8d5) & 0x7f;
/* check PATH1_BAL_LEFT */
if ((cx18_av_read(cx, 0x8d5) & 0x80) == 0)
balance = 0x80 - balance;
else
balance = 0x80 + balance;
return balance << 8;
}
static void set_balance(struct cx18 *cx, int balance)
{
int bal = balance >> 8;
if (bal > 0x80) {
/* PATH1_BAL_LEFT */
cx18_av_and_or(cx, 0x8d5, 0x7f, 0x80);
/* PATH1_BAL_LEVEL */
cx18_av_and_or(cx, 0x8d5, ~0x7f, bal & 0x7f);
} else {
/* PATH1_BAL_LEFT */
cx18_av_and_or(cx, 0x8d5, 0x7f, 0x00);
/* PATH1_BAL_LEVEL */
cx18_av_and_or(cx, 0x8d5, ~0x7f, 0x80 - bal);
}
}
static int get_mute(struct cx18 *cx)
{
/* check SRC1_MUTE_EN */
return cx18_av_read(cx, 0x8d3) & 0x2 ? 1 : 0;
}
static void set_mute(struct cx18 *cx, int mute)
{
struct cx18_av_state *state = &cx->av_state;
if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
/* Must turn off microcontroller in order to mute sound.
* Not sure if this is the best method, but it does work.
* If the microcontroller is running, then it will undo any
* changes to the mute register. */
if (mute) {
/* disable microcontroller */
cx18_av_and_or(cx, 0x803, ~0x10, 0x00);
cx18_av_write(cx, 0x8d3, 0x1f);
} else {
/* enable microcontroller */
cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
}
} else {
/* SRC1_MUTE_EN */
cx18_av_and_or(cx, 0x8d3, ~0x2, mute ? 0x02 : 0x00);
}
}
int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg)
{
struct cx18_av_state *state = &cx->av_state;
struct v4l2_control *ctrl = arg;
int retval;
switch (cmd) {
case VIDIOC_INT_AUDIO_CLOCK_FREQ:
if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
cx18_av_and_or(cx, 0x803, ~0x10, 0);
cx18_av_write(cx, 0x8d3, 0x1f);
}
cx18_av_and_or(cx, 0x810, ~0x1, 1);
retval = set_audclk_freq(cx, *(u32 *)arg);
cx18_av_and_or(cx, 0x810, ~0x1, 0);
if (state->aud_input != CX18_AV_AUDIO_SERIAL)
cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
return retval;
case VIDIOC_G_CTRL:
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
ctrl->value = get_volume(cx);
break;
case V4L2_CID_AUDIO_BASS:
ctrl->value = get_bass(cx);
break;
case V4L2_CID_AUDIO_TREBLE:
ctrl->value = get_treble(cx);
break;
case V4L2_CID_AUDIO_BALANCE:
ctrl->value = get_balance(cx);
break;
case V4L2_CID_AUDIO_MUTE:
ctrl->value = get_mute(cx);
break;
default:
return -EINVAL;
}
break;
case VIDIOC_S_CTRL:
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
set_volume(cx, ctrl->value);
break;
case V4L2_CID_AUDIO_BASS:
set_bass(cx, ctrl->value);
break;
case V4L2_CID_AUDIO_TREBLE:
set_treble(cx, ctrl->value);
break;
case V4L2_CID_AUDIO_BALANCE:
set_balance(cx, ctrl->value);
break;
case V4L2_CID_AUDIO_MUTE:
set_mute(cx, ctrl->value);
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
}
return 0;
}
This diff is collapsed.
This diff is collapsed.
/*
* cx18 ADEC firmware functions
*
* Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "cx18-driver.h"
#include <linux/firmware.h>
#define FWFILE "v4l-cx23418-dig.fw"
int cx18_av_loadfw(struct cx18 *cx)
{
const struct firmware *fw = NULL;
u32 size;
u32 v;
u8 *ptr;
int i;
if (request_firmware(&fw, FWFILE, &cx->dev->dev) != 0) {
CX18_ERR("unable to open firmware %s\n", FWFILE);
return -EINVAL;
}
cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000);
cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6); /* Byte 0 */
/* Reset the Mako core (Register is undocumented.) */
cx18_av_write4(cx, 0x8100, 0x00010000);
/* Put the 8051 in reset and enable firmware upload */
cx18_av_write4(cx, CXADEC_DL_CTL, 0x0F000000);
ptr = fw->data;
size = fw->size;
for (i = 0; i < size; i++) {
u32 dl_control = 0x0F000000 | ((u32)ptr[i] << 16);
u32 value = 0;
int retries;
for (retries = 0; retries < 5; retries++) {
cx18_av_write4(cx, CXADEC_DL_CTL, dl_control);
value = cx18_av_read4(cx, CXADEC_DL_CTL);
if ((value & 0x3F00) == (dl_control & 0x3F00))
break;
}
if (retries >= 5) {
CX18_ERR("unable to load firmware %s\n", FWFILE);
release_firmware(fw);
return -EIO;
}
}
cx18_av_write4(cx, CXADEC_DL_CTL, 0x13000000 | fw->size);
/* Output to the 416 */
cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x78000);
/* Audio input control 1 set to Sony mode */
/* Audio output input 2 is 0 for slave operation input */
/* 0xC4000914[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */
/* 0xC4000914[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge
after WS transition for first bit of audio word. */
cx18_av_write4(cx, CXADEC_I2S_IN_CTL, 0x000000A0);
/* Audio output control 1 is set to Sony mode */
/* Audio output control 2 is set to 1 for master mode */
/* 0xC4000918[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */
/* 0xC4000918[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge
after WS transition for first bit of audio word. */
/* 0xC4000918[8]: 0 = slave operation, 1 = master (SCK_OUT and WS_OUT
are generated) */
cx18_av_write4(cx, CXADEC_I2S_OUT_CTL, 0x000001A0);
/* set alt I2s master clock to /16 and enable alt divider i2s
passthrough */
cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5000B687);
cx18_av_write4(cx, CXADEC_STD_DET_CTL, 0x000000F6);
/* CxDevWrReg(CXADEC_STD_DET_CTL, 0x000000FF); */
/* Set bit 0 in register 0x9CC to signify that this is MiniMe. */
/* Register 0x09CC is defined by the Merlin firmware, and doesn't
have a name in the spec. */
cx18_av_write4(cx, 0x09CC, 1);
#define CX18_AUDIO_ENABLE 0xc72014
v = read_reg(CX18_AUDIO_ENABLE);
/* If bit 11 is 1 */
if (v & 0x800)
write_reg(v & 0xFFFFFBFF, CX18_AUDIO_ENABLE); /* Clear bit 10 */
/* Enable WW auto audio standard detection */
v = cx18_av_read4(cx, CXADEC_STD_DET_CTL);
v |= 0xFF; /* Auto by default */
v |= 0x400; /* Stereo by default */
v |= 0x14000000;
cx18_av_write4(cx, CXADEC_STD_DET_CTL, v);
release_firmware(fw);
CX18_INFO("loaded %s firmware (%d bytes)\n", FWFILE, size);
return 0;
}
This diff is collapsed.
/*
* cx18 functions to query card hardware
*
* Derived from ivtv-cards.c
*
* Copyright (C) 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 "cx18-driver.h"
#include "cx18-cards.h"
#include "cx18-i2c.h"
#include <media/cs5345.h>
/********************** card configuration *******************************/
/* usual i2c tuner addresses to probe */
static struct cx18_card_tuner_i2c cx18_i2c_std = {
.radio = { I2C_CLIENT_END },
.demod = { 0x43, I2C_CLIENT_END },
.tv = { 0x61, 0x60, I2C_CLIENT_END },
};
/* 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 HVR-1600 cards */
/* Note: for Hauppauge cards the tveeprom information is used instead
of PCI IDs */
static const struct cx18_card cx18_card_hvr1600_esmt = {
.type = CX18_CARD_HVR_1600_ESMT,
.name = "Hauppauge HVR-1600",
.comment = "DVB & VBI are not yet supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_muxer = CX18_HW_CS5345,
.hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER | CX18_HW_CS5345,
.video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 },
{ CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 },
{ CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 },
{ CX18_CARD_INPUT_SVIDEO2, 2, CX23418_SVIDEO2 },
{ CX18_CARD_INPUT_COMPOSITE2, 2, CX23418_COMPOSITE4 },
},
.audio_inputs = {
{ CX18_CARD_INPUT_AUD_TUNER,
CX23418_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 },
{ CX18_CARD_INPUT_LINE_IN1,
CX23418_AUDIO_SERIAL, CS5345_IN_2 },
{ CX18_CARD_INPUT_LINE_IN2,
CX23418_AUDIO_SERIAL, CS5345_IN_2 },
},
.radio_input = { CX18_CARD_INPUT_AUD_TUNER,
CX23418_AUDIO_SERIAL, 0 },
.ddr = {
/* ESMT M13S128324A-5B memory */
.chip_config = 0x003,
.refresh = 0x30c,
.timing1 = 0x44220e82,
.timing2 = 0x08,
.tune_lane = 0,
.initial_emrs = 0,
},
.gpio_init.initial_value = 0x3001,
.gpio_init.direction = 0x3001,
.i2c = &cx18_i2c_std,
};
static const struct cx18_card cx18_card_hvr1600_samsung = {
.type = CX18_CARD_HVR_1600_SAMSUNG,
.name = "Hauppauge HVR-1600 (Preproduction)",
.comment = "DVB & VBI are not yet supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_muxer = CX18_HW_CS5345,
.hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER | CX18_HW_CS5345,
.video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 },
{ CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 },
{ CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 },
{ CX18_CARD_INPUT_SVIDEO2, 2, CX23418_SVIDEO2 },
{ CX18_CARD_INPUT_COMPOSITE2, 2, CX23418_COMPOSITE4 },
},
.audio_inputs = {
{ CX18_CARD_INPUT_AUD_TUNER,
CX23418_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 },
{ CX18_CARD_INPUT_LINE_IN1,
CX23418_AUDIO_SERIAL, CS5345_IN_2 },
{ CX18_CARD_INPUT_LINE_IN2,
CX23418_AUDIO_SERIAL, CS5345_IN_2 },
},
.radio_input = { CX18_CARD_INPUT_AUD_TUNER,
CX23418_AUDIO_SERIAL, 0 },
.ddr = {
/* Samsung K4D263238G-VC33 memory */
.chip_config = 0x003,
.refresh = 0x30c,
.timing1 = 0x23230b73,
.timing2 = 0x08,
.tune_lane = 0,
.initial_emrs = 2,
},
.gpio_init.initial_value = 0x3001,
.gpio_init.direction = 0x3001,
.i2c = &cx18_i2c_std,
};
/* ------------------------------------------------------------------------- */
/* Compro VideoMate H900: not working at the moment! */
static const struct cx18_card_pci_info cx18_pci_h900[] = {
{ PCI_DEVICE_ID_CX23418, CX18_PCI_ID_COMPRO, 0xe100 },
{ 0, 0, 0 }
};
static const struct cx18_card cx18_card_h900 = {
.type = CX18_CARD_COMPRO_H900,
.name = "Compro VideoMate H900",
.comment = "Not yet supported!\n",
.v4l2_capabilities = 0,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_all = CX18_HW_TUNER,
.video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 },
{ CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 },
{ CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 },
},
.audio_inputs = {
{ CX18_CARD_INPUT_AUD_TUNER,
CX23418_AUDIO8, 0 },
{ CX18_CARD_INPUT_LINE_IN1,
CX23418_AUDIO_SERIAL, 0 },
},
.radio_input = { CX18_CARD_INPUT_AUD_TUNER,
CX23418_AUDIO_SERIAL, 0 },
.tuners = {
{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
},
.ddr = {
/* EtronTech EM6A9160TS-5G memory */
.chip_config = 0x50003,
.refresh = 0x753,
.timing1 = 0x24330e84,
.timing2 = 0x1f,
.tune_lane = 0,
.initial_emrs = 0,
},
.pci_list = cx18_pci_h900,
.i2c = &cx18_i2c_std,
};
/* ------------------------------------------------------------------------- */
/* Yuan MPC718: not working at the moment! */
static const struct cx18_card_pci_info cx18_pci_mpc718[] = {
{ PCI_DEVICE_ID_CX23418, CX18_PCI_ID_YUAN, 0x0718 },
{ 0, 0, 0 }
};
static const struct cx18_card cx18_card_mpc718 = {
.type = CX18_CARD_YUAN_MPC718,
.name = "Yuan MPC718",
.comment = "Not yet supported!\n",
.v4l2_capabilities = 0,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_all = CX18_HW_TUNER,
.video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 },
{ CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 },
{ CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 },
},
.audio_inputs = {
{ CX18_CARD_INPUT_AUD_TUNER,
CX23418_AUDIO8, 0 },
{ CX18_CARD_INPUT_LINE_IN1,
CX23418_AUDIO_SERIAL, 0 },
},
.radio_input = { CX18_CARD_INPUT_AUD_TUNER,
CX23418_AUDIO_SERIAL, 0 },
.tuners = {
/* XC3028 tuner */
{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
},
/* tuner reset */
.gpio_init = { .direction = 0x1000, .initial_value = 0x1000 },
.ddr = {
/* Probably Samsung K4D263238G-VC33 memory */
.chip_config = 0x003,
.refresh = 0x30c,
.timing1 = 0x23230b73,
.timing2 = 0x08,
.tune_lane = 0,
.initial_emrs = 2,
},
.pci_list = cx18_pci_mpc718,
.i2c = &cx18_i2c_std,
};
static const struct cx18_card *cx18_card_list[] = {
&cx18_card_hvr1600_esmt,
&cx18_card_hvr1600_samsung,
&cx18_card_h900,
&cx18_card_mpc718,
};
const struct cx18_card *cx18_get_card(u16 index)
{
if (index >= ARRAY_SIZE(cx18_card_list))
return NULL;
return cx18_card_list[index];
}
int cx18_get_input(struct cx18 *cx, u16 index, struct v4l2_input *input)
{
const struct cx18_card_video_input *card_input =
cx->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 >= cx->nof_inputs)
return -EINVAL;
input->index = index;
strlcpy(input->name, input_strs[card_input->video_type - 1],
sizeof(input->name));
input->type = (card_input->video_type == CX18_CARD_INPUT_VID_TUNER ?
V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA);
input->audioset = (1 << cx->nof_audio_inputs) - 1;
input->std = (input->type == V4L2_INPUT_TYPE_TUNER) ?
cx->tuner_std : V4L2_STD_ALL;
return 0;
}
int cx18_get_audio_input(struct cx18 *cx, u16 index, struct v4l2_audio *audio)
{
const struct cx18_card_audio_input *aud_input =
cx->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 >= cx->nof_audio_inputs)
return -EINVAL;
strlcpy(audio->name, input_strs[aud_input->audio_type - 1],
sizeof(audio->name));
audio->index = index;
audio->capability = V4L2_AUDCAP_STEREO;
return 0;
}
/*
* cx18 functions to query card hardware
*
* Derived from ivtv-cards.c
*
* Copyright (C) 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 CX18_HW_TUNER (1 << 0)
#define CX18_HW_TVEEPROM (1 << 1)
#define CX18_HW_CS5345 (1 << 2)
#define CX18_HW_GPIO (1 << 3)
#define CX18_HW_CX23418 (1 << 4)
#define CX18_HW_DVB (1 << 5)
/* video inputs */
#define CX18_CARD_INPUT_VID_TUNER 1
#define CX18_CARD_INPUT_SVIDEO1 2
#define CX18_CARD_INPUT_SVIDEO2 3
#define CX18_CARD_INPUT_COMPOSITE1 4
#define CX18_CARD_INPUT_COMPOSITE2 5
#define CX18_CARD_INPUT_COMPOSITE3 6
enum cx34180_video_input {
/* Composite video inputs In1-In8 */
CX23418_COMPOSITE1 = 1,
CX23418_COMPOSITE2,
CX23418_COMPOSITE3,
CX23418_COMPOSITE4,
CX23418_COMPOSITE5,
CX23418_COMPOSITE6,
CX23418_COMPOSITE7,
CX23418_COMPOSITE8,
/* S-Video inputs consist of one luma input (In1-In4) ORed with one
chroma input (In5-In8) */
CX23418_SVIDEO_LUMA1 = 0x10,
CX23418_SVIDEO_LUMA2 = 0x20,
CX23418_SVIDEO_LUMA3 = 0x30,
CX23418_SVIDEO_LUMA4 = 0x40,
CX23418_SVIDEO_CHROMA4 = 0x400,
CX23418_SVIDEO_CHROMA5 = 0x500,
CX23418_SVIDEO_CHROMA6 = 0x600,
CX23418_SVIDEO_CHROMA7 = 0x700,
CX23418_SVIDEO_CHROMA8 = 0x800,
/* S-Video aliases for common luma/chroma combinations */
CX23418_SVIDEO1 = 0x510,
CX23418_SVIDEO2 = 0x620,
CX23418_SVIDEO3 = 0x730,
CX23418_SVIDEO4 = 0x840,
};
/* audio inputs */
#define CX18_CARD_INPUT_AUD_TUNER 1
#define CX18_CARD_INPUT_LINE_IN1 2
#define CX18_CARD_INPUT_LINE_IN2 3
#define CX18_CARD_MAX_VIDEO_INPUTS 6
#define CX18_CARD_MAX_AUDIO_INPUTS 3
#define CX18_CARD_MAX_TUNERS 2
enum cx23418_audio_input {
/* Audio inputs: serial or In4-In8 */
CX23418_AUDIO_SERIAL,
CX23418_AUDIO4 = 4,
CX23418_AUDIO5,
CX23418_AUDIO6,
CX23418_AUDIO7,
CX23418_AUDIO8,
};
/* V4L2 capability aliases */
#define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \
V4L2_CAP_AUDIO | V4L2_CAP_READWRITE)
/* | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE) not yet */
struct cx18_card_video_input {
u8 video_type; /* video input type */
u8 audio_index; /* index in cx18_card_audio_input array */
u16 video_input; /* hardware video input */
};
struct cx18_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 cx18_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 cx18_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 cx18_card_tuner {
v4l2_std_id std; /* standard for which the tuner is suitable */
int tuner; /* tuner ID (from tuner.h) */
};
struct cx18_card_tuner_i2c {
unsigned short radio[2];/* radio tuner i2c address to probe */
unsigned short demod[2];/* demodulator i2c address to probe */
unsigned short tv[4]; /* tv tuner i2c addresses to probe */
};
struct cx18_ddr { /* DDR config data */
u32 chip_config;
u32 refresh;
u32 timing1;
u32 timing2;
u32 tune_lane;
u32 initial_emrs;
};
/* for card information/parameters */
struct cx18_card {
int type;
char *name;
char *comment;
u32 v4l2_capabilities;
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 cx18_card_video_input video_inputs[CX18_CARD_MAX_VIDEO_INPUTS];
struct cx18_card_audio_input audio_inputs[CX18_CARD_MAX_AUDIO_INPUTS];
struct cx18_card_audio_input radio_input;
/* GPIO card-specific settings */
struct cx18_gpio_init gpio_init;
struct cx18_card_tuner tuners[CX18_CARD_MAX_TUNERS];
struct cx18_card_tuner_i2c *i2c;
struct cx18_ddr ddr;
/* list of device and subsystem vendor/devices that
correspond to this card type. */
const struct cx18_card_pci_info *pci_list;
};
int cx18_get_input(struct cx18 *cx, u16 index, struct v4l2_input *input);
int cx18_get_audio_input(struct cx18 *cx, u16 index, struct v4l2_audio *input);
const struct cx18_card *cx18_get_card(u16 index);
/*
* cx18 ioctl control functions
*
* Derived from ivtv-controls.c
*
* Copyright (C) 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 "cx18-driver.h"
#include "cx18-av-core.h"
#include "cx18-cards.h"
#include "cx18-ioctl.h"
#include "cx18-audio.h"
#include "cx18-i2c.h"
#include "cx18-mailbox.h"
#include "cx18-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 cx18_queryctrl(struct cx18 *cx, struct v4l2_queryctrl *qctrl)
{
const char *name;
CX18_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 (cx18_av_cmd(cx, 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 (cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl))
qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
return 0;
default:
if (cx2341x_ctrl_query(&cx->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 cx18_querymenu(struct cx18 *cx, struct v4l2_querymenu *qmenu)
{
struct v4l2_queryctrl qctrl;
qctrl.id = qmenu->id;
cx18_queryctrl(cx, &qctrl);
return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id));
}
static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
{
s32 v = vctrl->value;
CX18_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 cx18_av_cmd(cx, 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 cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl);
default:
CX18_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
return -EINVAL;
}
return 0;
}
static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
{
CX18_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 cx18_av_cmd(cx, 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 cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl);
default:
CX18_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
return -EINVAL;
}
return 0;
}
static int cx18_setup_vbi_fmt(struct cx18 *cx, enum v4l2_mpeg_stream_vbi_fmt fmt)
{
if (!(cx->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE))
return -EINVAL;
if (atomic_read(&cx->capturing) > 0)
return -EBUSY;
/* First try to allocate sliced VBI buffers if needed. */
if (fmt && cx->vbi.sliced_mpeg_data[0] == NULL) {
int i;
for (i = 0; i < CX18_VBI_FRAMES; i++) {
/* Yuck, hardcoded. Needs to be a define */
cx->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL);
if (cx->vbi.sliced_mpeg_data[i] == NULL) {
while (--i >= 0) {
kfree(cx->vbi.sliced_mpeg_data[i]);
cx->vbi.sliced_mpeg_data[i] = NULL;
}
return -ENOMEM;
}
}
}
cx->vbi.insert_mpeg = fmt;
if (cx->vbi.insert_mpeg == 0)
return 0;
/* Need sliced data for mpeg insertion */
if (get_service_set(cx->vbi.sliced_in) == 0) {
if (cx->is_60hz)
cx->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525;
else
cx->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625;
expand_service_set(cx->vbi.sliced_in, cx->is_50hz);
}
return 0;
}
int cx18_control_ioctls(struct cx18 *cx, unsigned int cmd, void *arg)
{
struct v4l2_control ctrl;
switch (cmd) {
case VIDIOC_QUERYMENU:
CX18_DEBUG_IOCTL("VIDIOC_QUERYMENU\n");
return cx18_querymenu(cx, arg);
case VIDIOC_QUERYCTRL:
return cx18_queryctrl(cx, arg);
case VIDIOC_S_CTRL:
return cx18_s_ctrl(cx, arg);
case VIDIOC_G_CTRL:
return cx18_g_ctrl(cx, 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 = cx18_s_ctrl(cx, &ctrl);
c->controls[i].value = ctrl.value;
if (err) {
c->error_idx = i;
break;
}
}
return err;
}
CX18_DEBUG_IOCTL("VIDIOC_S_EXT_CTRLS\n");
if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
struct cx2341x_mpeg_params p = cx->params;
int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->capturing), arg, cmd);
if (err)
return err;
if (p.video_encoding != cx->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 = cx->params.width / (is_mpeg1 ? 2 : 1);
fmt.fmt.pix.height = cx->params.height;
cx18_av_cmd(cx, VIDIOC_S_FMT, &fmt);
}
err = cx2341x_update(cx, cx18_api_func, &cx->params, &p);
if (!err && cx->params.stream_vbi_fmt != p.stream_vbi_fmt)
err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt);
cx->params = p;
cx->dualwatch_stereo_mode = p.audio_properties & 0x0300;
cx18_audio_set_audio_clock_freq(cx, 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 = cx18_g_ctrl(cx, &ctrl);
c->controls[i].value = ctrl.value;
if (err) {
c->error_idx = i;
break;
}
}
return err;
}
CX18_DEBUG_IOCTL("VIDIOC_G_EXT_CTRLS\n");
if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
return cx2341x_ext_ctrls(&cx->params, 0, arg, cmd);
return -EINVAL;
}
case VIDIOC_TRY_EXT_CTRLS:
{
struct v4l2_ext_controls *c = arg;
CX18_DEBUG_IOCTL("VIDIOC_TRY_EXT_CTRLS\n");
if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
return cx2341x_ext_ctrls(&cx->params,
atomic_read(&cx->capturing), arg, cmd);
return -EINVAL;
}
default:
return -EINVAL;
}
return 0;
}
/*
* cx18 ioctl control functions
*
* Derived from ivtv-controls.h
*
* Copyright (C) 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 cx18_control_ioctls(struct cx18 *cx, unsigned int cmd, void *arg);
This diff is collapsed.
This diff is collapsed.
/*
* cx18 functions for DVB support
*
* Copyright (c) 2008 Steven Toth <stoth@hauppauge.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx18-version.h"
#include "cx18-dvb.h"
#include "cx18-streams.h"
#include "cx18-cards.h"
#include "s5h1409.h"
/* Wait until the MXL500X driver is merged */
#ifdef HAVE_MXL500X
#include "mxl500x.h"
#endif
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
#define CX18_REG_DMUX_NUM_PORT_0_CONTROL 0xd5a000
#ifdef HAVE_MXL500X
static struct mxl500x_config hauppauge_hvr1600_tuner = {
.delsys = MXL500x_MODE_ATSC,
.octf = MXL500x_OCTF_CH,
.xtal_freq = 16000000,
.iflo_freq = 5380000,
.ref_freq = 322800000,
.rssi_ena = MXL_RSSI_ENABLE,
.addr = 0xC6 >> 1,
};
static struct s5h1409_config hauppauge_hvr1600_config = {
.demod_address = 0x32 >> 1,
.output_mode = S5H1409_SERIAL_OUTPUT,
.gpio = S5H1409_GPIO_ON,
.qam_if = 44000,
.inversion = S5H1409_INVERSION_OFF,
.status_mode = S5H1409_DEMODLOCKING,
.mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK
};
#endif
static int dvb_register(struct cx18_stream *stream);
/* Kernel DVB framework calls this when the feed needs to start.
* The CX18 framework should enable the transport DMA handling
* and queue processing.
*/
static int cx18_dvb_start_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct cx18_stream *stream = (struct cx18_stream *) demux->priv;
struct cx18 *cx = stream->cx;
int ret = -EINVAL;
u32 v;
CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n",
feed->pid, feed->index);
switch (cx->card->type) {
case CX18_CARD_HVR_1600_ESMT:
case CX18_CARD_HVR_1600_SAMSUNG:
v = read_reg(CX18_REG_DMUX_NUM_PORT_0_CONTROL);
v |= 0x00400000; /* Serial Mode */
v |= 0x00002000; /* Data Length - Byte */
v |= 0x00010000; /* Error - Polarity */
v |= 0x00020000; /* Error - Passthru */
v |= 0x000c0000; /* Error - Ignore */
write_reg(v, CX18_REG_DMUX_NUM_PORT_0_CONTROL);
break;
default:
/* Assumption - Parallel transport - Signalling
* undefined or default.
*/
break;
}
if (!demux->dmx.frontend)
return -EINVAL;
if (stream) {
mutex_lock(&stream->dvb.feedlock);
if (stream->dvb.feeding++ == 0) {
CX18_DEBUG_INFO("Starting Transport DMA\n");
ret = cx18_start_v4l2_encode_stream(stream);
} else
ret = 0;
mutex_unlock(&stream->dvb.feedlock);
}
return ret;
}
/* Kernel DVB framework calls this when the feed needs to stop. */
static int cx18_dvb_stop_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct cx18_stream *stream = (struct cx18_stream *)demux->priv;
struct cx18 *cx = stream->cx;
int ret = -EINVAL;
CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n",
feed->pid, feed->index);
if (stream) {
mutex_lock(&stream->dvb.feedlock);
if (--stream->dvb.feeding == 0) {
CX18_DEBUG_INFO("Stopping Transport DMA\n");
ret = cx18_stop_v4l2_encode_stream(stream, 0);
} else
ret = 0;
mutex_unlock(&stream->dvb.feedlock);
}
return ret;
}
int cx18_dvb_register(struct cx18_stream *stream)
{
struct cx18 *cx = stream->cx;
struct cx18_dvb *dvb = &stream->dvb;
struct dvb_adapter *dvb_adapter;
struct dvb_demux *dvbdemux;
struct dmx_demux *dmx;
int ret;
if (!dvb)
return -EINVAL;
ret = dvb_register_adapter(&dvb->dvb_adapter,
CX18_DRIVER_NAME,
THIS_MODULE, &cx->dev->dev, adapter_nr);
if (ret < 0)
goto err_out;
dvb_adapter = &dvb->dvb_adapter;
dvbdemux = &dvb->demux;
dvbdemux->priv = (void *)stream;
dvbdemux->filternum = 256;
dvbdemux->feednum = 256;
dvbdemux->start_feed = cx18_dvb_start_feed;
dvbdemux->stop_feed = cx18_dvb_stop_feed;
dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
ret = dvb_dmx_init(dvbdemux);
if (ret < 0)
goto err_dvb_unregister_adapter;
dmx = &dvbdemux->dmx;
dvb->hw_frontend.source = DMX_FRONTEND_0;
dvb->mem_frontend.source = DMX_MEMORY_FE;
dvb->dmxdev.filternum = 256;
dvb->dmxdev.demux = dmx;
ret = dvb_dmxdev_init(&dvb->dmxdev, dvb_adapter);
if (ret < 0)
goto err_dvb_dmx_release;
ret = dmx->add_frontend(dmx, &dvb->hw_frontend);
if (ret < 0)
goto err_dvb_dmxdev_release;
ret = dmx->add_frontend(dmx, &dvb->mem_frontend);
if (ret < 0)
goto err_remove_hw_frontend;
ret = dmx->connect_frontend(dmx, &dvb->hw_frontend);
if (ret < 0)
goto err_remove_mem_frontend;
ret = dvb_register(stream);
if (ret < 0)
goto err_disconnect_frontend;
dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx);
CX18_INFO("DVB Frontend registered\n");
mutex_init(&dvb->feedlock);
dvb->enabled = 1;
return ret;
err_disconnect_frontend:
dmx->disconnect_frontend(dmx);
err_remove_mem_frontend:
dmx->remove_frontend(dmx, &dvb->mem_frontend);
err_remove_hw_frontend:
dmx->remove_frontend(dmx, &dvb->hw_frontend);
err_dvb_dmxdev_release:
dvb_dmxdev_release(&dvb->dmxdev);
err_dvb_dmx_release:
dvb_dmx_release(dvbdemux);
err_dvb_unregister_adapter:
dvb_unregister_adapter(dvb_adapter);
err_out:
return ret;
}
void cx18_dvb_unregister(struct cx18_stream *stream)
{
struct cx18 *cx = stream->cx;
struct cx18_dvb *dvb = &stream->dvb;
struct dvb_adapter *dvb_adapter;
struct dvb_demux *dvbdemux;
struct dmx_demux *dmx;
CX18_INFO("unregister DVB\n");
dvb_adapter = &dvb->dvb_adapter;
dvbdemux = &dvb->demux;
dmx = &dvbdemux->dmx;
dmx->close(dmx);
dvb_net_release(&dvb->dvbnet);
dmx->remove_frontend(dmx, &dvb->mem_frontend);
dmx->remove_frontend(dmx, &dvb->hw_frontend);
dvb_dmxdev_release(&dvb->dmxdev);
dvb_dmx_release(dvbdemux);
dvb_unregister_frontend(dvb->fe);
dvb_frontend_detach(dvb->fe);
dvb_unregister_adapter(dvb_adapter);
}
/* All the DVB attach calls go here, this function get's modified
* for each new card. No other function in this file needs
* to change.
*/
static int dvb_register(struct cx18_stream *stream)
{
struct cx18_dvb *dvb = &stream->dvb;
struct cx18 *cx = stream->cx;
int ret = 0;
switch (cx->card->type) {
/* Wait until the MXL500X driver is merged */
#ifdef HAVE_MXL500X
case CX18_CARD_HVR_1600_ESMT:
case CX18_CARD_HVR_1600_SAMSUNG:
dvb->fe = dvb_attach(s5h1409_attach,
&hauppauge_hvr1600_config,
&cx->i2c_adap[0]);
if (dvb->fe != NULL) {
dvb_attach(mxl500x_attach, dvb->fe,
&hauppauge_hvr1600_tuner,
&cx->i2c_adap[0]);
ret = 0;
}
break;
#endif
default:
/* No Digital Tv Support */
break;
}
if (dvb->fe == NULL) {
CX18_ERR("frontend initialization failed\n");
return -1;
}
ret = dvb_register_frontend(&dvb->dvb_adapter, dvb->fe);
if (ret < 0) {
if (dvb->fe->ops.release)
dvb->fe->ops.release(dvb->fe);
return ret;
}
return ret;
}
/*
* cx18 functions for DVB support
*
* Copyright (c) 2008 Steven Toth <stoth@hauppauge.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cx18-driver.h"
int cx18_dvb_register(struct cx18_stream *stream);
void cx18_dvb_unregister(struct cx18_stream *stream);
This diff is collapsed.
/*
* cx18 file operation functions
*
* Derived from ivtv-fileops.h
*
* Copyright (C) 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 cx18_v4l2_open(struct inode *inode, struct file *filp);
ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count,
loff_t *pos);
ssize_t cx18_v4l2_write(struct file *filp, const char __user *buf, size_t count,
loff_t *pos);
int cx18_v4l2_close(struct inode *inode, struct file *filp);
unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait);
int cx18_start_capture(struct cx18_open_id *id);
void cx18_stop_capture(struct cx18_open_id *id, int gop_end);
void cx18_mute(struct cx18 *cx);
void cx18_unmute(struct cx18 *cx);
/* 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 cx18_claim_stream(struct cx18_open_id *id, int type);
/* Release a previously claimed stream. */
void cx18_release_stream(struct cx18_stream *s);
This diff is collapsed.
/*
* cx18 firmware functions
*
* Copyright (C) 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 cx18_firmware_init(struct cx18 *cx);
void cx18_halt_firmware(struct cx18 *cx);
void cx18_init_memory(struct cx18 *cx);
void cx18_init_power(struct cx18 *cx, int lowpwr);
/*
* cx18 gpio functions
*
* Derived from ivtv-gpio.c
*
* Copyright (C) 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 "cx18-driver.h"
#include "cx18-cards.h"
#include "cx18-gpio.h"
#include "tuner-xc2028.h"
/********************* GPIO stuffs *********************/
/* GPIO registers */
#define CX18_REG_GPIO_IN 0xc72010
#define CX18_REG_GPIO_OUT1 0xc78100
#define CX18_REG_GPIO_DIR1 0xc78108
#define CX18_REG_GPIO_OUT2 0xc78104
#define CX18_REG_GPIO_DIR2 0xc7810c
/*
* HVR-1600 GPIO pins, courtesy of Hauppauge:
*
* gpio0: zilog ir process reset pin
* gpio1: zilog programming pin (you should never use this)
* gpio12: cx24227 reset pin
* gpio13: cs5345 reset pin
*/
void cx18_gpio_init(struct cx18 *cx)
{
if (cx->card->gpio_init.direction == 0)
return;
CX18_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n",
read_reg(CX18_REG_GPIO_DIR1), read_reg(CX18_REG_GPIO_OUT1));
/* init output data then direction */
write_reg(cx->card->gpio_init.direction << 16, CX18_REG_GPIO_DIR1);
write_reg(0, CX18_REG_GPIO_DIR2);
write_reg((cx->card->gpio_init.direction << 16) |
cx->card->gpio_init.initial_value, CX18_REG_GPIO_OUT1);
write_reg(0, CX18_REG_GPIO_OUT2);
}
/* Xceive tuner reset function */
int cx18_reset_tuner_gpio(void *dev, int cmd, int value)
{
struct i2c_algo_bit_data *algo = dev;
struct cx18 *cx = algo->data;
/* int curdir, curout;*/
if (cmd != XC2028_TUNER_RESET)
return 0;
CX18_DEBUG_INFO("Resetting tuner\n");
return 0;
}
/*
* cx18 gpio functions
*
* Derived from ivtv-gpio.h
*
* Copyright (C) 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 cx18_gpio_init(struct cx18 *cx);
int cx18_reset_tuner_gpio(void *dev, int cmd, int value);
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment