summaryrefslogtreecommitdiffstats
path: root/drivers/media/video/cx18
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/cx18')
-rw-r--r--drivers/media/video/cx18/Kconfig2
-rw-r--r--drivers/media/video/cx18/cx18-audio.c52
-rw-r--r--drivers/media/video/cx18/cx18-audio.h2
-rw-r--r--drivers/media/video/cx18/cx18-av-audio.c120
-rw-r--r--drivers/media/video/cx18/cx18-av-core.c796
-rw-r--r--drivers/media/video/cx18/cx18-av-core.h49
-rw-r--r--drivers/media/video/cx18/cx18-av-firmware.c16
-rw-r--r--drivers/media/video/cx18/cx18-av-vbi.c367
-rw-r--r--drivers/media/video/cx18/cx18-cards.c50
-rw-r--r--drivers/media/video/cx18/cx18-cards.h18
-rw-r--r--drivers/media/video/cx18/cx18-controls.c70
-rw-r--r--drivers/media/video/cx18/cx18-driver.c416
-rw-r--r--drivers/media/video/cx18/cx18-driver.h258
-rw-r--r--drivers/media/video/cx18/cx18-dvb.c2
-rw-r--r--drivers/media/video/cx18/cx18-fileops.c107
-rw-r--r--drivers/media/video/cx18/cx18-firmware.c22
-rw-r--r--drivers/media/video/cx18/cx18-gpio.c319
-rw-r--r--drivers/media/video/cx18/cx18-gpio.h10
-rw-r--r--drivers/media/video/cx18/cx18-i2c.c296
-rw-r--r--drivers/media/video/cx18/cx18-i2c.h5
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.c273
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.c44
-rw-r--r--drivers/media/video/cx18/cx18-queue.c4
-rw-r--r--drivers/media/video/cx18/cx18-queue.h4
-rw-r--r--drivers/media/video/cx18/cx18-streams.c210
-rw-r--r--drivers/media/video/cx18/cx18-vbi.c155
-rw-r--r--drivers/media/video/cx18/cx18-vbi.h2
-rw-r--r--drivers/media/video/cx18/cx18-version.h4
-rw-r--r--drivers/media/video/cx18/cx18-video.c3
-rw-r--r--drivers/media/video/cx18/cx23418.h16
30 files changed, 2182 insertions, 1510 deletions
diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig
index 8940b5387dec..e8a50a611ebc 100644
--- a/drivers/media/video/cx18/Kconfig
+++ b/drivers/media/video/cx18/Kconfig
@@ -9,7 +9,7 @@ config VIDEO_CX18
select VIDEO_CX2341X
select VIDEO_CS5345
select DVB_S5H1409 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMIZE
+ select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
---help---
This is a video4linux driver for Conexant cx23418 based
PCI combo video recorder devices.
diff --git a/drivers/media/video/cx18/cx18-audio.c b/drivers/media/video/cx18/cx18-audio.c
index 57beddf0af4d..bb5c5165dd5f 100644
--- a/drivers/media/video/cx18/cx18-audio.c
+++ b/drivers/media/video/cx18/cx18-audio.c
@@ -23,7 +23,6 @@
#include "cx18-driver.h"
#include "cx18-io.h"
-#include "cx18-i2c.h"
#include "cx18-cards.h"
#include "cx18-audio.h"
@@ -33,55 +32,32 @@
settings. */
int cx18_audio_set_io(struct cx18 *cx)
{
+ const struct cx18_card_audio_input *in;
struct v4l2_routing route;
- u32 audio_input;
u32 val;
- int mux_input;
int err;
/* 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;
- }
+ if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags))
+ in = &cx->card->radio_input;
+ else
+ in = &cx->card->audio_inputs[cx->audio_input];
/* handle muxer chips */
- route.input = mux_input;
+ route.input = in->muxer_input;
route.output = 0;
- cx18_i2c_hw(cx, cx->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route);
+ v4l2_subdev_call(cx->sd_extmux, audio, s_routing, &route);
- route.input = audio_input;
- err = cx18_i2c_hw(cx, cx->card->hw_audio_ctrl,
- VIDIOC_INT_S_AUDIO_ROUTING, &route);
+ route.input = in->audio_input;
+ err = cx18_call_hw_err(cx, cx->card->hw_audio_ctrl,
+ audio, s_routing, &route);
if (err)
return err;
+ /* FIXME - this internal mux should be abstracted to a subdev */
val = cx18_read_reg(cx, CX18_AUDIO_ENABLE) & ~0x30;
- val |= (audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 :
- (audio_input << 4);
- cx18_write_reg(cx, val | 0xb00, CX18_AUDIO_ENABLE);
- cx18_vapi(cx, CX18_APU_RESETAI, 1, 0);
+ val |= (in->audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 :
+ (in->audio_input << 4);
+ cx18_write_reg_expect(cx, val | 0xb00, CX18_AUDIO_ENABLE, val, 0x30);
return 0;
}
-
-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]);
-}
diff --git a/drivers/media/video/cx18/cx18-audio.h b/drivers/media/video/cx18/cx18-audio.h
index cb569a69379c..2731d29b0ab9 100644
--- a/drivers/media/video/cx18/cx18-audio.h
+++ b/drivers/media/video/cx18/cx18-audio.h
@@ -22,5 +22,3 @@
*/
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);
diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c
index a2f0ad570434..9e30983f2ff6 100644
--- a/drivers/media/video/cx18/cx18-av-audio.c
+++ b/drivers/media/video/cx18/cx18-av-audio.c
@@ -464,82 +464,76 @@ static void set_mute(struct cx18 *cx, int mute)
}
}
-int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg)
+int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
struct cx18_av_state *state = &cx->av_state;
- struct v4l2_control *ctrl = arg;
int retval;
+ u8 v;
- switch (cmd) {
- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- {
- u8 v;
- if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
- v = cx18_av_read(cx, 0x803) & ~0x10;
- cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
- cx18_av_write(cx, 0x8d3, 0x1f);
- }
- v = cx18_av_read(cx, 0x810) | 0x1;
- cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
+ if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
+ v = cx18_av_read(cx, 0x803) & ~0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+ cx18_av_write(cx, 0x8d3, 0x1f);
+ }
+ v = cx18_av_read(cx, 0x810) | 0x1;
+ cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
- retval = set_audclk_freq(cx, *(u32 *)arg);
+ retval = set_audclk_freq(cx, freq);
- v = cx18_av_read(cx, 0x810) & ~0x1;
- cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
- if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
- v = cx18_av_read(cx, 0x803) | 0x10;
- cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
- }
- return retval;
+ v = cx18_av_read(cx, 0x810) & ~0x1;
+ cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
+ if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
+ v = cx18_av_read(cx, 0x803) | 0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
}
+ 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;
- }
+int cx18_av_audio_g_ctrl(struct cx18 *cx, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ ctrl->value = get_volume(cx);
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;
- }
+ 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;
}
+ return 0;
+}
+int cx18_av_audio_s_ctrl(struct cx18 *cx, struct v4l2_control *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;
+ }
return 0;
}
diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c
index 0b1c84b4ddd6..f4dd9d78eb3d 100644
--- a/drivers/media/video/cx18/cx18-av-core.c
+++ b/drivers/media/video/cx18/cx18-av-core.c
@@ -22,8 +22,10 @@
* 02110-1301, USA.
*/
+#include <media/v4l2-chip-ident.h>
#include "cx18-driver.h"
#include "cx18-io.h"
+#include "cx18-cards.h"
int cx18_av_write(struct cx18 *cx, u16 addr, u8 value)
{
@@ -97,15 +99,6 @@ int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask,
or_value);
}
-/* ----------------------------------------------------------------------- */
-
-static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
- enum cx18_av_audio_input aud_input);
-static void log_audio_status(struct cx18 *cx);
-static void log_video_status(struct cx18 *cx);
-
-/* ----------------------------------------------------------------------- */
-
static void cx18_av_initialize(struct cx18 *cx)
{
struct cx18_av_state *state = &cx->av_state;
@@ -169,9 +162,14 @@ static void cx18_av_initialize(struct cx18 *cx)
/* Set VGA_TRACK_RANGE to 0x20 */
cx18_av_and_or4(cx, CXADEC_DFE_CTRL2, 0xFFFF00FF, 0x00002000);
- /* Enable VBI capture */
- cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4010253F);
- /* cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4010253E); */
+ /*
+ * Initial VBI setup
+ * VIP-1.1, 10 bit mode, enable Raw, disable sliced,
+ * don't clamp raw samples when codes are in use, 1 byte user D-words,
+ * IDID0 has line #, RP code V bit transition on VBLANK, data during
+ * blanking intervals
+ */
+ cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4013252e);
/* Set the video input.
The setting in MODE_CTRL gets lost when we do the above setup */
@@ -195,11 +193,61 @@ static void cx18_av_initialize(struct cx18 *cx)
state->default_volume = ((state->default_volume / 2) + 23) << 9;
}
-/* ----------------------------------------------------------------------- */
+static int cx18_av_reset(struct v4l2_subdev *sd, u32 val)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+ cx18_av_initialize(cx);
+ return 0;
+}
+
+static int cx18_av_init(struct v4l2_subdev *sd, u32 val)
+{
+ struct cx18_av_state *state = to_cx18_av_state(sd);
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+ switch (val) {
+ case CX18_AV_INIT_PLLS:
+ /*
+ * The crystal freq used in calculations in this driver will be
+ * 28.636360 MHz.
+ * Aim to run the PLLs' VCOs near 400 MHz to minimze errors.
+ */
+
+ /*
+ * VDCLK Integer = 0x0f, Post Divider = 0x04
+ * AIMCLK Integer = 0x0e, Post Divider = 0x16
+ */
+ cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f);
+
+ /* VDCLK Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */
+ cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe);
+
+ /* AIMCLK Fraction = 0x05227ad */
+ /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz pre post-div*/
+ cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad);
+
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
+ cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56);
+ break;
+
+ case CX18_AV_INIT_NORMAL:
+ default:
+ if (!state->is_initialized) {
+ /* initialize on first use */
+ state->is_initialized = 1;
+ cx18_av_initialize(cx);
+ }
+ break;
+ }
+ return 0;
+}
void cx18_av_std_setup(struct cx18 *cx)
{
struct cx18_av_state *state = &cx->av_state;
+ struct v4l2_subdev *sd = &state->sd;
v4l2_std_id std = state->std;
int hblank, hactive, burst, vblank, vactive, sc;
int vblank656, src_decimation;
@@ -213,6 +261,7 @@ void cx18_av_std_setup(struct cx18 *cx)
cx18_av_write(cx, 0x49f, 0x14);
if (std & V4L2_STD_625_50) {
+ /* FIXME - revisit these for Sliced VBI */
hblank = 132;
hactive = 720;
burst = 93;
@@ -236,13 +285,40 @@ void cx18_av_std_setup(struct cx18 *cx)
sc = 672351;
}
} else {
+ /*
+ * The following relationships of half line counts should hold:
+ * 525 = vsync + vactive + vblank656
+ * 12 = vblank656 - vblank
+ *
+ * vsync: always 6 half-lines of vsync pulses
+ * vactive: half lines of active video
+ * vblank656: half lines, after line 3/mid-266, of blanked video
+ * vblank: half lines, after line 9/272, of blanked video
+ *
+ * As far as I can tell:
+ * vblank656 starts counting from the falling edge of the first
+ * vsync pulse (start of line 4 or mid-266)
+ * vblank starts counting from the after the 6 vsync pulses and
+ * 6 or 5 equalization pulses (start of line 10 or 272)
+ *
+ * For 525 line systems the driver will extract VBI information
+ * from lines 10-21 and lines 273-284.
+ */
+ vblank656 = 38; /* lines 4 - 22 & 266 - 284 */
+ vblank = 26; /* lines 10 - 22 & 272 - 284 */
+ vactive = 481; /* lines 23 - 263 & 285 - 525 */
+
+ /*
+ * For a 13.5 Mpps clock and 15,734.26 Hz line rate, a line is
+ * is 858 pixels = 720 active + 138 blanking. The Hsync leading
+ * edge should happen 1.2 us * 13.5 Mpps ~= 16 pixels after the
+ * end of active video, leaving 122 pixels of hblank to ignore
+ * before active video starts.
+ */
hactive = 720;
hblank = 122;
- vactive = 487;
luma_lpf = 1;
uv_lpf = 1;
- vblank = 26;
- vblank656 = 26;
src_decimation = 0x21f;
if (std == V4L2_STD_PAL_60) {
@@ -265,33 +341,35 @@ void cx18_av_std_setup(struct cx18 *cx)
pll_int = cx18_av_read(cx, 0x108);
pll_frac = cx18_av_read4(cx, 0x10c) & 0x1ffffff;
pll_post = cx18_av_read(cx, 0x109);
- CX18_DEBUG_INFO("PLL regs = int: %u, frac: %u, post: %u\n",
- pll_int, pll_frac, pll_post);
+ CX18_DEBUG_INFO_DEV(sd, "PLL regs = int: %u, frac: %u, post: %u\n",
+ pll_int, pll_frac, pll_post);
if (pll_post) {
int fin, fsc, pll;
pll = (28636360L * ((((u64)pll_int) << 25) + pll_frac)) >> 25;
pll /= pll_post;
- CX18_DEBUG_INFO("PLL = %d.%06d MHz\n",
- pll / 1000000, pll % 1000000);
- CX18_DEBUG_INFO("PLL/8 = %d.%06d MHz\n",
- pll / 8000000, (pll / 8) % 1000000);
+ CX18_DEBUG_INFO_DEV(sd, "PLL = %d.%06d MHz\n",
+ pll / 1000000, pll % 1000000);
+ CX18_DEBUG_INFO_DEV(sd, "PLL/8 = %d.%06d MHz\n",
+ pll / 8000000, (pll / 8) % 1000000);
fin = ((u64)src_decimation * pll) >> 12;
- CX18_DEBUG_INFO("ADC Sampling freq = %d.%06d MHz\n",
- fin / 1000000, fin % 1000000);
+ CX18_DEBUG_INFO_DEV(sd, "ADC Sampling freq = %d.%06d MHz\n",
+ fin / 1000000, fin % 1000000);
fsc = (((u64)sc) * pll) >> 24L;
- CX18_DEBUG_INFO("Chroma sub-carrier freq = %d.%06d MHz\n",
- fsc / 1000000, fsc % 1000000);
-
- CX18_DEBUG_INFO("hblank %i, hactive %i, "
- "vblank %i , vactive %i, vblank656 %i, src_dec %i,"
- "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x,"
- " sc 0x%06x\n",
- hblank, hactive, vblank, vactive, vblank656,
- src_decimation, burst, luma_lpf, uv_lpf, comb, sc);
+ CX18_DEBUG_INFO_DEV(sd,
+ "Chroma sub-carrier freq = %d.%06d MHz\n",
+ fsc / 1000000, fsc % 1000000);
+
+ CX18_DEBUG_INFO_DEV(sd, "hblank %i, hactive %i, vblank %i, "
+ "vactive %i, vblank656 %i, src_dec %i, "
+ "burst 0x%02x, luma_lpf %i, uv_lpf %i, "
+ "comb 0x%02x, sc 0x%06x\n",
+ hblank, hactive, vblank, vactive, vblank656,
+ src_decimation, burst, luma_lpf, uv_lpf,
+ comb, sc);
}
/* Sets horizontal blanking delay and active lines */
@@ -325,18 +403,16 @@ void cx18_av_std_setup(struct cx18 *cx)
cx18_av_write(cx, 0x47d, 0xff & sc >> 8);
cx18_av_write(cx, 0x47e, 0xff & sc >> 16);
- /* Sets VBI parameters */
if (std & V4L2_STD_625_50) {
- cx18_av_write(cx, 0x47f, 0x01);
- state->vbi_line_offset = 5;
+ state->slicer_line_delay = 1;
+ state->slicer_line_offset = (6 + state->slicer_line_delay - 2);
} else {
- cx18_av_write(cx, 0x47f, 0x00);
- state->vbi_line_offset = 8;
+ state->slicer_line_delay = 0;
+ state->slicer_line_offset = (10 + state->slicer_line_delay - 2);
}
+ cx18_av_write(cx, 0x47f, state->slicer_line_delay);
}
-/* ----------------------------------------------------------------------- */
-
static void input_change(struct cx18 *cx)
{
struct cx18_av_state *state = &cx->av_state;
@@ -382,17 +458,26 @@ static void input_change(struct cx18 *cx)
}
}
+static int cx18_av_s_frequency(struct v4l2_subdev *sd,
+ struct v4l2_frequency *freq)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ input_change(cx);
+ return 0;
+}
+
static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
enum cx18_av_audio_input aud_input)
{
struct cx18_av_state *state = &cx->av_state;
+ struct v4l2_subdev *sd = &state->sd;
u8 is_composite = (vid_input >= CX18_AV_COMPOSITE1 &&
vid_input <= CX18_AV_COMPOSITE8);
u8 reg;
u8 v;
- CX18_DEBUG_INFO("decoder set video input %d, audio input %d\n",
- vid_input, aud_input);
+ CX18_DEBUG_INFO_DEV(sd, "decoder set video input %d, audio input %d\n",
+ vid_input, aud_input);
if (is_composite) {
reg = 0xf0 + (vid_input - CX18_AV_COMPOSITE1);
@@ -405,8 +490,8 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
luma > CX18_AV_SVIDEO_LUMA8 ||
chroma < CX18_AV_SVIDEO_CHROMA4 ||
chroma > CX18_AV_SVIDEO_CHROMA8) {
- CX18_ERR("0x%04x is not a valid video input!\n",
- vid_input);
+ CX18_ERR_DEV(sd, "0x%04x is not a valid video input!\n",
+ vid_input);
return -EINVAL;
}
reg = 0xf0 + ((luma - CX18_AV_SVIDEO_LUMA1) >> 4);
@@ -431,7 +516,8 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
case CX18_AV_AUDIO8: reg &= ~0xc0; reg |= 0x40; break;
default:
- CX18_ERR("0x%04x is not a valid audio input!\n", aud_input);
+ CX18_ERR_DEV(sd, "0x%04x is not a valid audio input!\n",
+ aud_input);
return -EINVAL;
}
@@ -461,14 +547,118 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
return 0;
}
-/* ----------------------------------------------------------------------- */
+static int cx18_av_s_video_routing(struct v4l2_subdev *sd,
+ const struct v4l2_routing *route)
+{
+ struct cx18_av_state *state = to_cx18_av_state(sd);
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ return set_input(cx, route->input, state->aud_input);
+}
+
+static int cx18_av_s_audio_routing(struct v4l2_subdev *sd,
+ const struct v4l2_routing *route)
+{
+ struct cx18_av_state *state = to_cx18_av_state(sd);
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ return set_input(cx, state->vid_input, route->input);
+}
-static int set_v4lstd(struct cx18 *cx)
+static int cx18_av_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
{
- struct cx18_av_state *state = &cx->av_state;
+ struct cx18_av_state *state = to_cx18_av_state(sd);
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ u8 vpres;
+ u8 mode;
+ int val = 0;
+
+ if (state->radio)
+ return 0;
+
+ vpres = cx18_av_read(cx, 0x40e) & 0x20;
+ vt->signal = vpres ? 0xffff : 0x0;
+
+ vt->capability |=
+ V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
+ V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+
+ mode = cx18_av_read(cx, 0x804);
+
+ /* get rxsubchans and audmode */
+ if ((mode & 0xf) == 1)
+ val |= V4L2_TUNER_SUB_STEREO;
+ else
+ val |= V4L2_TUNER_SUB_MONO;
+
+ if (mode == 2 || mode == 4)
+ val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+
+ if (mode & 0x10)
+ val |= V4L2_TUNER_SUB_SAP;
+
+ vt->rxsubchans = val;
+ vt->audmode = state->audmode;
+ return 0;
+}
+
+static int cx18_av_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct cx18_av_state *state = to_cx18_av_state(sd);
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ u8 v;
+
+ if (state->radio)
+ return 0;
+
+ v = cx18_av_read(cx, 0x809);
+ v &= ~0xf;
+
+ switch (vt->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ /* mono -> mono
+ stereo -> mono
+ bilingual -> lang1 */
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1:
+ /* mono -> mono
+ stereo -> stereo
+ bilingual -> lang1 */
+ v |= 0x4;
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ /* mono -> mono
+ stereo -> stereo
+ bilingual -> lang1/lang2 */
+ v |= 0x7;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ /* mono -> mono
+ stereo -> stereo
+ bilingual -> lang2 */
+ v |= 0x1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ cx18_av_write_expect(cx, 0x809, v, v, 0xff);
+ state->audmode = vt->audmode;
+ return 0;
+}
+
+static int cx18_av_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+ struct cx18_av_state *state = to_cx18_av_state(sd);
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
u8 fmt = 0; /* zero is autodetect */
u8 pal_m = 0;
+ if (state->radio == 0 && state->std == norm)
+ return 0;
+
+ state->radio = 0;
+ state->std = norm;
+
/* First tests should be against specific std */
if (state->std == V4L2_STD_NTSC_M_JP) {
fmt = 0x2;
@@ -493,7 +683,7 @@ static int set_v4lstd(struct cx18 *cx)
fmt = 0xc;
}
- CX18_DEBUG_INFO("changing video std to fmt %i\n", fmt);
+ CX18_DEBUG_INFO_DEV(sd, "changing video std to fmt %i\n", fmt);
/* Follow step 9 of section 3.16 in the cx18_av datasheet.
Without this PAL may display a vertical ghosting effect.
@@ -511,15 +701,22 @@ static int set_v4lstd(struct cx18 *cx)
return 0;
}
-/* ----------------------------------------------------------------------- */
+static int cx18_av_s_radio(struct v4l2_subdev *sd)
+{
+ struct cx18_av_state *state = to_cx18_av_state(sd);
+ state->radio = 1;
+ return 0;
+}
-static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl)
+static int cx18_av_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
if (ctrl->value < 0 || ctrl->value > 255) {
- CX18_ERR("invalid brightness setting %d\n",
- ctrl->value);
+ CX18_ERR_DEV(sd, "invalid brightness setting %d\n",
+ ctrl->value);
return -ERANGE;
}
@@ -528,8 +725,8 @@ static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl)
case V4L2_CID_CONTRAST:
if (ctrl->value < 0 || ctrl->value > 127) {
- CX18_ERR("invalid contrast setting %d\n",
- ctrl->value);
+ CX18_ERR_DEV(sd, "invalid contrast setting %d\n",
+ ctrl->value);
return -ERANGE;
}
@@ -538,8 +735,8 @@ static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl)
case V4L2_CID_SATURATION:
if (ctrl->value < 0 || ctrl->value > 127) {
- CX18_ERR("invalid saturation setting %d\n",
- ctrl->value);
+ CX18_ERR_DEV(sd, "invalid saturation setting %d\n",
+ ctrl->value);
return -ERANGE;
}
@@ -548,8 +745,9 @@ static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl)
break;
case V4L2_CID_HUE:
- if (ctrl->value < -127 || ctrl->value > 127) {
- CX18_ERR("invalid hue setting %d\n", ctrl->value);
+ if (ctrl->value < -128 || ctrl->value > 127) {
+ CX18_ERR_DEV(sd, "invalid hue setting %d\n",
+ ctrl->value);
return -ERANGE;
}
@@ -561,17 +759,18 @@ static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl)
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_MUTE:
- return cx18_av_audio(cx, VIDIOC_S_CTRL, ctrl);
+ return cx18_av_audio_s_ctrl(cx, ctrl);
default:
return -EINVAL;
}
-
return 0;
}
-static int get_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl)
+static int cx18_av_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
ctrl->value = (s8)cx18_av_read(cx, 0x414) + 128;
@@ -590,31 +789,57 @@ static int get_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl)
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_MUTE:
- return cx18_av_audio(cx, VIDIOC_G_CTRL, ctrl);
+ return cx18_av_audio_g_ctrl(cx, ctrl);
default:
return -EINVAL;
}
-
return 0;
}
-/* ----------------------------------------------------------------------- */
-
-static int get_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt)
+static int cx18_av_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
{
- switch (fmt->type) {
- case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- return cx18_av_vbi(cx, VIDIOC_G_FMT, fmt);
+ struct cx18_av_state *state = to_cx18_av_state(sd);
+
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
+ return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
+ default:
+ break;
+ }
+
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ return v4l2_ctrl_query_fill(qc, 0, 65535,
+ 65535 / 100, state->default_volume);
+ case V4L2_CID_AUDIO_MUTE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
default:
return -EINVAL;
}
+ return -EINVAL;
+}
- return 0;
+static int cx18_av_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+ return cx18_av_vbi_g_fmt(cx, fmt);
}
-static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt)
+static int cx18_av_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
{
- struct cx18_av_state *state = &cx->av_state;
+ struct cx18_av_state *state = to_cx18_av_state(sd);
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
struct v4l2_pix_format *pix;
int HSC, VSC, Vsrc, Hsrc, filter, Vlines;
int is_50Hz = !(state->std & V4L2_STD_525_60);
@@ -629,12 +854,26 @@ static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt)
Hsrc = (cx18_av_read(cx, 0x472) & 0x3f) << 4;
Hsrc |= (cx18_av_read(cx, 0x471) & 0xf0) >> 4;
- Vlines = pix->height + (is_50Hz ? 4 : 7);
-
+ /*
+ * This adjustment reflects the excess of vactive, set in
+ * cx18_av_std_setup(), above standard values:
+ *
+ * 480 + 1 for 60 Hz systems
+ * 576 + 4 for 50 Hz systems
+ */
+ Vlines = pix->height + (is_50Hz ? 4 : 1);
+
+ /*
+ * Invalid height and width scaling requests are:
+ * 1. width less than 1/16 of the source width
+ * 2. width greater than the source width
+ * 3. height less than 1/8 of the source height
+ * 4. height greater than the source height
+ */
if ((pix->width * 16 < Hsrc) || (Hsrc < pix->width) ||
(Vlines * 8 < Vsrc) || (Vsrc < Vlines)) {
- CX18_ERR("%dx%d is not a valid size!\n",
- pix->width, pix->height);
+ CX18_ERR_DEV(sd, "%dx%d is not a valid size!\n",
+ pix->width, pix->height);
return -ERANGE;
}
@@ -651,8 +890,9 @@ static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt)
else
filter = 3;
- CX18_DEBUG_INFO("decoder set size %dx%d -> scale %ux%u\n",
- pix->width, pix->height, HSC, VSC);
+ CX18_DEBUG_INFO_DEV(sd,
+ "decoder set size %dx%d -> scale %ux%u\n",
+ pix->width, pix->height, HSC, VSC);
/* HSCALE=HSC */
cx18_av_write(cx, 0x418, HSC & 0xff);
@@ -666,231 +906,32 @@ static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt)
break;
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- return cx18_av_vbi(cx, VIDIOC_S_FMT, fmt);
+ return cx18_av_vbi_s_fmt(cx, fmt);
case V4L2_BUF_TYPE_VBI_CAPTURE:
- return cx18_av_vbi(cx, VIDIOC_S_FMT, fmt);
+ return cx18_av_vbi_s_fmt(cx, fmt);
default:
return -EINVAL;
}
-
return 0;
}
-/* ----------------------------------------------------------------------- */
-
-int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg)
+static int cx18_av_s_stream(struct v4l2_subdev *sd, int enable)
{
- struct cx18_av_state *state = &cx->av_state;
- struct v4l2_tuner *vt = arg;
- struct v4l2_routing *route = arg;
-
- /* ignore these commands */
- switch (cmd) {
- case TUNER_SET_TYPE_ADDR:
- return 0;
- }
-
- if (!state->is_initialized) {
- CX18_DEBUG_INFO("cmd %08x triggered fw load\n", cmd);
- /* initialize on first use */
- state->is_initialized = 1;
- cx18_av_initialize(cx);
- }
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
- switch (cmd) {
- case VIDIOC_INT_DECODE_VBI_LINE:
- return cx18_av_vbi(cx, cmd, arg);
-
- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- return cx18_av_audio(cx, cmd, arg);
-
- case VIDIOC_STREAMON:
- CX18_DEBUG_INFO("enable output\n");
+ CX18_DEBUG_INFO_DEV(sd, "%s output\n", enable ? "enable" : "disable");
+ if (enable) {
cx18_av_write(cx, 0x115, 0x8c);
cx18_av_write(cx, 0x116, 0x07);
- break;
-
- case VIDIOC_STREAMOFF:
- CX18_DEBUG_INFO("disable output\n");
+ } else {
cx18_av_write(cx, 0x115, 0x00);
cx18_av_write(cx, 0x116, 0x00);
- break;
-
- case VIDIOC_LOG_STATUS:
- log_video_status(cx);
- log_audio_status(cx);
- break;
-
- case VIDIOC_G_CTRL:
- return get_v4lctrl(cx, (struct v4l2_control *)arg);
-
- case VIDIOC_S_CTRL:
- return set_v4lctrl(cx, (struct v4l2_control *)arg);
-
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
-
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill_std(qc);
- default:
- break;
- }
-
- switch (qc->id) {
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 65535,
- 65535 / 100, state->default_volume);
- case V4L2_CID_AUDIO_MUTE:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- return v4l2_ctrl_query_fill_std(qc);
- default:
- return -EINVAL;
- }
- return -EINVAL;
- }
-
- case VIDIOC_G_STD:
- *(v4l2_std_id *)arg = state->std;
- break;
-
- case VIDIOC_S_STD:
- if (state->radio == 0 && state->std == *(v4l2_std_id *)arg)
- return 0;
- state->radio = 0;
- state->std = *(v4l2_std_id *)arg;
- return set_v4lstd(cx);
-
- case AUDC_SET_RADIO:
- state->radio = 1;
- break;
-
- case VIDIOC_INT_G_VIDEO_ROUTING:
- route->input = state->vid_input;
- route->output = 0;
- break;
-
- case VIDIOC_INT_S_VIDEO_ROUTING:
- return set_input(cx, route->input, state->aud_input);
-
- case VIDIOC_INT_G_AUDIO_ROUTING:
- route->input = state->aud_input;
- route->output = 0;
- break;
-
- case VIDIOC_INT_S_AUDIO_ROUTING:
- return set_input(cx, state->vid_input, route->input);
-
- case VIDIOC_S_FREQUENCY:
- input_change(cx);
- break;
-
- case VIDIOC_G_TUNER:
- {
- u8 vpres = cx18_av_read(cx, 0x40e) & 0x20;
- u8 mode;
- int val = 0;
-
- if (state->radio)
- break;
-
- vt->signal = vpres ? 0xffff : 0x0;
-
- vt->capability |=
- V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
- V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
-
- mode = cx18_av_read(cx, 0x804);
-
- /* get rxsubchans and audmode */
- if ((mode & 0xf) == 1)
- val |= V4L2_TUNER_SUB_STEREO;
- else
- val |= V4L2_TUNER_SUB_MONO;
-
- if (mode == 2 || mode == 4)
- val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
-
- if (mode & 0x10)
- val |= V4L2_TUNER_SUB_SAP;
-
- vt->rxsubchans = val;
- vt->audmode = state->audmode;
- break;
- }
-
- case VIDIOC_S_TUNER:
- {
- u8 v;
-
- if (state->radio)
- break;
-
- v = cx18_av_read(cx, 0x809);
- v &= ~0xf;
-
- switch (vt->audmode) {
- case V4L2_TUNER_MODE_MONO:
- /* mono -> mono
- stereo -> mono
- bilingual -> lang1 */
- break;
- case V4L2_TUNER_MODE_STEREO:
- case V4L2_TUNER_MODE_LANG1:
- /* mono -> mono
- stereo -> stereo
- bilingual -> lang1 */
- v |= 0x4;
- break;
- case V4L2_TUNER_MODE_LANG1_LANG2:
- /* mono -> mono
- stereo -> stereo
- bilingual -> lang1/lang2 */
- v |= 0x7;
- break;
- case V4L2_TUNER_MODE_LANG2:
- /* mono -> mono
- stereo -> stereo
- bilingual -> lang2 */
- v |= 0x1;
- break;
- default:
- return -EINVAL;
- }
- cx18_av_write_expect(cx, 0x809, v, v, 0xff);
- state->audmode = vt->audmode;
- break;
}
-
- case VIDIOC_G_FMT:
- return get_v4lfmt(cx, (struct v4l2_format *)arg);
-
- case VIDIOC_S_FMT:
- return set_v4lfmt(cx, (struct v4l2_format *)arg);
-
- case VIDIOC_INT_RESET:
- cx18_av_initialize(cx);
- break;
-
- default:
- return -EINVAL;
- }
-
return 0;
}
-/* ----------------------------------------------------------------------- */
-
-/* ----------------------------------------------------------------------- */
-
static void log_video_status(struct cx18 *cx)
{
static const char *const fmt_strs[] = {
@@ -903,36 +944,40 @@ static void log_video_status(struct cx18 *cx)
};
struct cx18_av_state *state = &cx->av_state;
+ struct v4l2_subdev *sd = &state->sd;
u8 vidfmt_sel = cx18_av_read(cx, 0x400) & 0xf;
u8 gen_stat1 = cx18_av_read(cx, 0x40d);
u8 gen_stat2 = cx18_av_read(cx, 0x40e);
int vid_input = state->vid_input;
- CX18_INFO("Video signal: %spresent\n",
- (gen_stat2 & 0x20) ? "" : "not ");
- CX18_INFO("Detected format: %s\n",
- fmt_strs[gen_stat1 & 0xf]);
+ CX18_INFO_DEV(sd, "Video signal: %spresent\n",
+ (gen_stat2 & 0x20) ? "" : "not ");
+ CX18_INFO_DEV(sd, "Detected format: %s\n",
+ fmt_strs[gen_stat1 & 0xf]);
- CX18_INFO("Specified standard: %s\n",
- vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection");
+ CX18_INFO_DEV(sd, "Specified standard: %s\n",
+ vidfmt_sel ? fmt_strs[vidfmt_sel]
+ : "automatic detection");
if (vid_input >= CX18_AV_COMPOSITE1 &&
vid_input <= CX18_AV_COMPOSITE8) {
- CX18_INFO("Specified video input: Composite %d\n",
- vid_input - CX18_AV_COMPOSITE1 + 1);
+ CX18_INFO_DEV(sd, "Specified video input: Composite %d\n",
+ vid_input - CX18_AV_COMPOSITE1 + 1);
} else {
- CX18_INFO("Specified video input: S-Video (Luma In%d, Chroma In%d)\n",
- (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8);
+ CX18_INFO_DEV(sd, "Specified video input: "
+ "S-Video (Luma In%d, Chroma In%d)\n",
+ (vid_input & 0xf0) >> 4,
+ (vid_input & 0xf00) >> 8);
}
- CX18_INFO("Specified audioclock freq: %d Hz\n", state->audclk_freq);
+ CX18_INFO_DEV(sd, "Specified audioclock freq: %d Hz\n",
+ state->audclk_freq);
}
-/* ----------------------------------------------------------------------- */
-
static void log_audio_status(struct cx18 *cx)
{
struct cx18_av_state *state = &cx->av_state;
+ struct v4l2_subdev *sd = &state->sd;
u8 download_ctl = cx18_av_read(cx, 0x803);
u8 mod_det_stat0 = cx18_av_read(cx, 0x804);
u8 mod_det_stat1 = cx18_av_read(cx, 0x805);
@@ -955,7 +1000,7 @@ static void log_audio_status(struct cx18 *cx)
case 0xfe: p = "forced mode"; break;
default: p = "not defined"; break;
}
- CX18_INFO("Detected audio mode: %s\n", p);
+ CX18_INFO_DEV(sd, "Detected audio mode: %s\n", p);
switch (mod_det_stat1) {
case 0x00: p = "not defined"; break;
@@ -980,11 +1025,11 @@ static void log_audio_status(struct cx18 *cx)
case 0xff: p = "no detected audio standard"; break;
default: p = "not defined"; break;
}
- CX18_INFO("Detected audio standard: %s\n", p);
- CX18_INFO("Audio muted: %s\n",
- (mute_ctl & 0x2) ? "yes" : "no");
- CX18_INFO("Audio microcontroller: %s\n",
- (download_ctl & 0x10) ? "running" : "stopped");
+ CX18_INFO_DEV(sd, "Detected audio standard: %s\n", p);
+ CX18_INFO_DEV(sd, "Audio muted: %s\n",
+ (mute_ctl & 0x2) ? "yes" : "no");
+ CX18_INFO_DEV(sd, "Audio microcontroller: %s\n",
+ (download_ctl & 0x10) ? "running" : "stopped");
switch (audio_config >> 4) {
case 0x00: p = "undefined"; break;
@@ -1005,7 +1050,7 @@ static void log_audio_status(struct cx18 *cx)
case 0x0f: p = "automatic detection"; break;
default: p = "undefined"; break;
}
- CX18_INFO("Configured audio standard: %s\n", p);
+ CX18_INFO_DEV(sd, "Configured audio standard: %s\n", p);
if ((audio_config >> 4) < 0xF) {
switch (audio_config & 0xF) {
@@ -1019,7 +1064,7 @@ static void log_audio_status(struct cx18 *cx)
case 0x07: p = "DUAL3 (AB)"; break;
default: p = "undefined";
}
- CX18_INFO("Configured audio mode: %s\n", p);
+ CX18_INFO_DEV(sd, "Configured audio mode: %s\n", p);
} else {
switch (audio_config & 0xF) {
case 0x00: p = "BG"; break;
@@ -1037,14 +1082,14 @@ static void log_audio_status(struct cx18 *cx)
case 0x0f: p = "automatic standard and mode detection"; break;
default: p = "undefined"; break;
}
- CX18_INFO("Configured audio system: %s\n", p);
+ CX18_INFO_DEV(sd, "Configured audio system: %s\n", p);
}
if (aud_input)
- CX18_INFO("Specified audio input: Tuner (In%d)\n",
- aud_input);
+ CX18_INFO_DEV(sd, "Specified audio input: Tuner (In%d)\n",
+ aud_input);
else
- CX18_INFO("Specified audio input: External\n");
+ CX18_INFO_DEV(sd, "Specified audio input: External\n");
switch (pref_mode & 0xf) {
case 0: p = "mono/language A"; break;
@@ -1057,14 +1102,14 @@ static void log_audio_status(struct cx18 *cx)
case 7: p = "language AB"; break;
default: p = "undefined"; break;
}
- CX18_INFO("Preferred audio mode: %s\n", p);
+ CX18_INFO_DEV(sd, "Preferred audio mode: %s\n", p);
if ((audio_config & 0xf) == 0xf) {
switch ((afc0 >> 3) & 0x1) {
case 0: p = "system DK"; break;
case 1: p = "system L"; break;
}
- CX18_INFO("Selected 65 MHz format: %s\n", p);
+ CX18_INFO_DEV(sd, "Selected 65 MHz format: %s\n", p);
switch (afc0 & 0x7) {
case 0: p = "Chroma"; break;
@@ -1074,6 +1119,131 @@ static void log_audio_status(struct cx18 *cx)
case 4: p = "autodetect"; break;
default: p = "undefined"; break;
}
- CX18_INFO("Selected 45 MHz format: %s\n", p);
+ CX18_INFO_DEV(sd, "Selected 45 MHz format: %s\n", p);
}
}
+
+static int cx18_av_log_status(struct v4l2_subdev *sd)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ log_video_status(cx);
+ log_audio_status(cx);
+ return 0;
+}
+
+static inline int cx18_av_dbg_match(const struct v4l2_dbg_match *match)
+{
+ return match->type == V4L2_CHIP_MATCH_HOST && match->addr == 1;
+}
+
+static int cx18_av_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct cx18_av_state *state = to_cx18_av_state(sd);
+
+ if (cx18_av_dbg_match(&chip->match)) {
+ chip->ident = state->id;
+ chip->revision = state->rev;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cx18_av_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+ if (!cx18_av_dbg_match(&reg->match))
+ return -EINVAL;
+ if ((reg->reg & 0x3) != 0)
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->size = 4;
+ reg->val = cx18_av_read4(cx, reg->reg & 0x00000ffc);
+ return 0;
+}
+
+static int cx18_av_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+ if (!cx18_av_dbg_match(&reg->match))
+ return -EINVAL;
+ if ((reg->reg & 0x3) != 0)
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ cx18_av_write4(cx, reg->reg & 0x00000ffc, reg->val);
+ return 0;
+}
+#endif
+
+static const struct v4l2_subdev_core_ops cx18_av_general_ops = {
+ .g_chip_ident = cx18_av_g_chip_ident,
+ .log_status = cx18_av_log_status,
+ .init = cx18_av_init,
+ .reset = cx18_av_reset,
+ .queryctrl = cx18_av_queryctrl,
+ .g_ctrl = cx18_av_g_ctrl,
+ .s_ctrl = cx18_av_s_ctrl,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = cx18_av_g_register,
+ .s_register = cx18_av_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops cx18_av_tuner_ops = {
+ .s_radio = cx18_av_s_radio,
+ .s_frequency = cx18_av_s_frequency,
+ .g_tuner = cx18_av_g_tuner,
+ .s_tuner = cx18_av_s_tuner,
+ .s_std = cx18_av_s_std,
+};
+
+static const struct v4l2_subdev_audio_ops cx18_av_audio_ops = {
+ .s_clock_freq = cx18_av_s_clock_freq,
+ .s_routing = cx18_av_s_audio_routing,
+};
+
+static const struct v4l2_subdev_video_ops cx18_av_video_ops = {
+ .s_routing = cx18_av_s_video_routing,
+ .decode_vbi_line = cx18_av_decode_vbi_line,
+ .s_stream = cx18_av_s_stream,
+ .g_fmt = cx18_av_g_fmt,
+ .s_fmt = cx18_av_s_fmt,
+};
+
+static const struct v4l2_subdev_ops cx18_av_ops = {
+ .core = &cx18_av_general_ops,
+ .tuner = &cx18_av_tuner_ops,
+ .audio = &cx18_av_audio_ops,
+ .video = &cx18_av_video_ops,
+};
+
+int cx18_av_probe(struct cx18 *cx)
+{
+ struct cx18_av_state *state = &cx->av_state;
+ struct v4l2_subdev *sd;
+
+ state->rev = cx18_av_read4(cx, CXADEC_CHIP_CTRL) & 0xffff;
+ state->id = ((state->rev >> 4) == CXADEC_CHIP_TYPE_MAKO)
+ ? V4L2_IDENT_CX23418_843 : V4L2_IDENT_UNKNOWN;
+
+ state->vid_input = CX18_AV_COMPOSITE7;
+ state->aud_input = CX18_AV_AUDIO8;
+ state->audclk_freq = 48000;
+ state->audmode = V4L2_TUNER_MODE_LANG1;
+ state->slicer_line_delay = 0;
+ state->slicer_line_offset = (10 + state->slicer_line_delay - 2);
+
+ sd = &state->sd;
+ v4l2_subdev_init(sd, &cx18_av_ops);
+ v4l2_set_subdevdata(sd, cx);
+ snprintf(sd->name, sizeof(sd->name),
+ "%s %03x", cx->v4l2_dev.name, (state->rev >> 4));
+ sd->grp_id = CX18_HW_418_AV;
+ return v4l2_device_register_subdev(&cx->v4l2_dev, sd);
+}
diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h
index cf68a6039091..c458120e8c90 100644
--- a/drivers/media/video/cx18/cx18-av-core.h
+++ b/drivers/media/video/cx18/cx18-av-core.h
@@ -25,6 +25,8 @@
#ifndef _CX18_AV_CORE_H_
#define _CX18_AV_CORE_H_
+#include <media/v4l2-device.h>
+
struct cx18;
enum cx18_av_video_input {
@@ -73,17 +75,40 @@ enum cx18_av_audio_input {
};
struct cx18_av_state {
+ struct v4l2_subdev sd;
int radio;
v4l2_std_id std;
enum cx18_av_video_input vid_input;
enum cx18_av_audio_input aud_input;
u32 audclk_freq;
int audmode;
- int vbi_line_offset;
int default_volume;
u32 id;
u32 rev;
int is_initialized;
+
+ /*
+ * The VBI slicer starts operating and counting lines, begining at
+ * slicer line count of 1, at D lines after the deassertion of VRESET.
+ * This staring field line, S, is 6 (& 319) or 10 (& 273) for 625 or 525
+ * line systems respectively. Sliced ancillary data captured on VBI
+ * slicer line M is inserted after the VBI slicer is done with line M,
+ * when VBI slicer line count is N = M+1. Thus when the VBI slicer
+ * reports a VBI slicer line number with ancillary data, the IDID0 byte
+ * indicates VBI slicer line N. The actual field line that the captured
+ * data comes from is
+ *
+ * L = M+(S+D-1) = N-1+(S+D-1) = N + (S+D-2).
+ *
+ * L is the line in the field, not frame, from which the VBI data came.
+ * N is the line reported by the slicer in the ancillary data.
+ * D is the slicer_line_delay value programmed into register 0x47f.
+ * S is 6 for 625 line systems or 10 for 525 line systems
+ * (S+D-2) is the slicer_line_offset used to convert slicer reported
+ * line counts to actual field lines.
+ */
+ int slicer_line_delay;
+ int slicer_line_offset;
};
@@ -298,6 +323,16 @@ struct cx18_av_state {
#define CXADEC_SELECT_AUDIO_STANDARD_FM 0xF9 /* FM radio */
#define CXADEC_SELECT_AUDIO_STANDARD_AUTO 0xFF /* Auto detect */
+static inline struct cx18_av_state *to_cx18_av_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct cx18_av_state, sd);
+}
+
+enum cx18_av_subdev_init_arg {
+ CX18_AV_INIT_NORMAL = 0,
+ CX18_AV_INIT_PLLS = 1,
+};
+
/* ----------------------------------------------------------------------- */
/* cx18_av-core.c */
int cx18_av_write(struct cx18 *cx, u16 addr, u8 value);
@@ -310,20 +345,26 @@ u8 cx18_av_read(struct cx18 *cx, u16 addr);
u32 cx18_av_read4(struct cx18 *cx, u16 addr);
int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value);
int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value);
-int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg);
void cx18_av_std_setup(struct cx18 *cx);
+int cx18_av_probe(struct cx18 *cx);
+
/* ----------------------------------------------------------------------- */
/* cx18_av-firmware.c */
int cx18_av_loadfw(struct cx18 *cx);
/* ----------------------------------------------------------------------- */
/* cx18_av-audio.c */
-int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg);
+int cx18_av_audio_g_ctrl(struct cx18 *cx, struct v4l2_control *ctrl);
+int cx18_av_audio_s_ctrl(struct cx18 *cx, struct v4l2_control *ctrl);
+int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq);
void cx18_av_audio_set_path(struct cx18 *cx);
/* ----------------------------------------------------------------------- */
/* cx18_av-vbi.c */
-int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg);
+int cx18_av_decode_vbi_line(struct v4l2_subdev *sd,
+ struct v4l2_decode_vbi_line *vbi);
+int cx18_av_vbi_g_fmt(struct cx18 *cx, struct v4l2_format *fmt);
+int cx18_av_vbi_s_fmt(struct cx18 *cx, struct v4l2_format *fmt);
#endif
diff --git a/drivers/media/video/cx18/cx18-av-firmware.c b/drivers/media/video/cx18/cx18-av-firmware.c
index c64fd0a05a97..49a55cc8d839 100644
--- a/drivers/media/video/cx18/cx18-av-firmware.c
+++ b/drivers/media/video/cx18/cx18-av-firmware.c
@@ -29,6 +29,7 @@
int cx18_av_loadfw(struct cx18 *cx)
{
+ struct v4l2_subdev *sd = &cx->av_state.sd;
const struct firmware *fw = NULL;
u32 size;
u32 v;
@@ -36,8 +37,8 @@ int cx18_av_loadfw(struct cx18 *cx)
int i;
int retries1 = 0;
- if (request_firmware(&fw, FWFILE, &cx->dev->dev) != 0) {
- CX18_ERR("unable to open firmware %s\n", FWFILE);
+ if (request_firmware(&fw, FWFILE, &cx->pci_dev->dev) != 0) {
+ CX18_ERR_DEV(sd, "unable to open firmware %s\n", FWFILE);
return -EINVAL;
}
@@ -88,7 +89,7 @@ int cx18_av_loadfw(struct cx18 *cx)
retries1++;
}
if (retries1 >= 5) {
- CX18_ERR("unable to load firmware %s\n", FWFILE);
+ CX18_ERR_DEV(sd, "unable to load firmware %s\n", FWFILE);
release_firmware(fw);
return -EIO;
}
@@ -115,9 +116,9 @@ int cx18_av_loadfw(struct cx18 *cx)
are generated) */
cx18_av_write4(cx, CXADEC_I2S_OUT_CTL, 0x000001A0);
- /* set alt I2s master clock to /16 and enable alt divider i2s
+ /* set alt I2s master clock to /0x16 and enable alt divider i2s
passthrough */
- cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5000B687);
+ cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5600B687);
cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, 0x000000F6, 0x000000F6,
0x3F00FFFF);
@@ -131,7 +132,8 @@ int cx18_av_loadfw(struct cx18 *cx)
v = cx18_read_reg(cx, CX18_AUDIO_ENABLE);
/* If bit 11 is 1, clear bit 10 */
if (v & 0x800)
- cx18_write_reg(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE);
+ cx18_write_reg_expect(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE,
+ 0, 0x400);
/* Enable WW auto audio standard detection */
v = cx18_av_read4(cx, CXADEC_STD_DET_CTL);
@@ -142,6 +144,6 @@ int cx18_av_loadfw(struct cx18 *cx)
release_firmware(fw);
- CX18_INFO("loaded %s firmware (%d bytes)\n", FWFILE, size);
+ CX18_INFO_DEV(sd, "loaded %s firmware (%d bytes)\n", FWFILE, size);
return 0;
}
diff --git a/drivers/media/video/cx18/cx18-av-vbi.c b/drivers/media/video/cx18/cx18-av-vbi.c
index 1527ea4f6b06..23b31670bf1d 100644
--- a/drivers/media/video/cx18/cx18-av-vbi.c
+++ b/drivers/media/video/cx18/cx18-av-vbi.c
@@ -24,6 +24,52 @@
#include "cx18-driver.h"
+/*
+ * For sliced VBI output, we set up to use VIP-1.1, 8-bit mode,
+ * NN counts 1 byte Dwords, an IDID with the VBI line # in it.
+ * Thus, according to the VIP-2 Spec, our VBI ancillary data lines
+ * (should!) look like:
+ * 4 byte EAV code: 0xff 0x00 0x00 0xRP
+ * unknown number of possible idle bytes
+ * 3 byte Anc data preamble: 0x00 0xff 0xff
+ * 1 byte data identifier: ne010iii (parity bits, 010, DID bits)
+ * 1 byte secondary data id: nessssss (parity bits, SDID bits)
+ * 1 byte data word count: necccccc (parity bits, NN Dword count)
+ * 2 byte Internal DID: VBI-line-# 0x80
+ * NN data bytes
+ * 1 byte checksum
+ * Fill bytes needed to fil out to 4*NN bytes of payload
+ *
+ * The RP codes for EAVs when in VIP-1.1 mode, not in raw mode, &
+ * in the vertical blanking interval are:
+ * 0xb0 (Task 0 VerticalBlank HorizontalBlank 0 0 0 0)
+ * 0xf0 (Task EvenField VerticalBlank HorizontalBlank 0 0 0 0)
+ *
+ * Since the V bit is only allowed to toggle in the EAV RP code, just
+ * before the first active region line and for active lines, they are:
+ * 0x90 (Task 0 0 HorizontalBlank 0 0 0 0)
+ * 0xd0 (Task EvenField 0 HorizontalBlank 0 0 0 0)
+ *
+ * The user application DID bytes we care about are:
+ * 0x91 (1 0 010 0 !ActiveLine AncDataPresent)
+ * 0x55 (0 1 010 2ndField !ActiveLine AncDataPresent)
+ *
+ */
+static const u8 sliced_vbi_did[2] = { 0x91, 0x55 };
+
+struct vbi_anc_data {
+ /* u8 eav[4]; */
+ /* u8 idle[]; Variable number of idle bytes */
+ u8 preamble[3];
+ u8 did;
+ u8 sdid;
+ u8 data_count;
+ u8 idid[2];
+ u8 payload[1]; /* data_count of payload */
+ /* u8 checksum; */
+ /* u8 fill[]; Variable number of fill bytes */
+};
+
static int odd_parity(u8 c)
{
c ^= (c >> 4);
@@ -83,188 +129,189 @@ static int decode_vps(u8 *dst, u8 *p)
return err & 0xf0;
}
-int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg)
+int cx18_av_vbi_g_fmt(struct cx18 *cx, struct v4l2_format *fmt)
{
struct cx18_av_state *state = &cx->av_state;
- struct v4l2_format *fmt;
struct v4l2_sliced_vbi_format *svbi;
+ static const u16 lcr2vbi[] = {
+ 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
+ 0, V4L2_SLICED_WSS_625, 0, /* 4 */
+ V4L2_SLICED_CAPTION_525, /* 6 */
+ 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */
+ 0, 0, 0, 0
+ };
+ int is_pal = !(state->std & V4L2_STD_525_60);
+ int i;
- switch (cmd) {
- case VIDIOC_G_FMT:
- {
- static u16 lcr2vbi[] = {
- 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
- 0, V4L2_SLICED_WSS_625, 0, /* 4 */
- V4L2_SLICED_CAPTION_525, /* 6 */
- 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */
- 0, 0, 0, 0
- };
- int is_pal = !(state->std & V4L2_STD_525_60);
- int i;
-
- fmt = arg;
- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
- return -EINVAL;
- svbi = &fmt->fmt.sliced;
- memset(svbi, 0, sizeof(*svbi));
- /* we're done if raw VBI is active */
- if ((cx18_av_read(cx, 0x404) & 0x10) == 0)
- break;
-
- if (is_pal) {
- for (i = 7; i <= 23; i++) {
- u8 v = cx18_av_read(cx, 0x424 + i - 7);
-
- svbi->service_lines[0][i] = lcr2vbi[v >> 4];
- svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
- svbi->service_set |= svbi->service_lines[0][i] |
- svbi->service_lines[1][i];
- }
- } else {
- for (i = 10; i <= 21; i++) {
- u8 v = cx18_av_read(cx, 0x424 + i - 10);
-
- svbi->service_lines[0][i] = lcr2vbi[v >> 4];
- svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
- svbi->service_set |= svbi->service_lines[0][i] |
- svbi->service_lines[1][i];
- }
- }
- break;
- }
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
+ svbi = &fmt->fmt.sliced;
+ memset(svbi, 0, sizeof(*svbi));
+ /* we're done if raw VBI is active */
+ if ((cx18_av_read(cx, 0x404) & 0x10) == 0)
+ return 0;
- case VIDIOC_S_FMT:
- {
- int is_pal = !(state->std & V4L2_STD_525_60);
- int vbi_offset = is_pal ? 1 : 0;
- int i, x;
- u8 lcr[24];
-
- fmt = arg;
- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE &&
- fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE)
- return -EINVAL;
- svbi = &fmt->fmt.sliced;
- if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
- /* raw VBI */
- memset(svbi, 0, sizeof(*svbi));
-
- /* Setup standard */
- cx18_av_std_setup(cx);
-
- /* VBI Offset */
- cx18_av_write(cx, 0x47f, vbi_offset);
- cx18_av_write(cx, 0x404, 0x2e);
- break;
+ if (is_pal) {
+ for (i = 7; i <= 23; i++) {
+ u8 v = cx18_av_read(cx, 0x424 + i - 7);
+
+ svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+ svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+ svbi->service_set |= svbi->service_lines[0][i] |
+ svbi->service_lines[1][i];
+ }
+ } else {
+ for (i = 10; i <= 21; i++) {
+ u8 v = cx18_av_read(cx, 0x424 + i - 10);
+
+ svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+ svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+ svbi->service_set |= svbi->service_lines[0][i] |
+ svbi->service_lines[1][i];
}
+ }
+ return 0;
+}
- for (x = 0; x <= 23; x++)
- lcr[x] = 0x00;
+int cx18_av_vbi_s_fmt(struct cx18 *cx, struct v4l2_format *fmt)
+{
+ struct cx18_av_state *state = &cx->av_state;
+ struct v4l2_sliced_vbi_format *svbi;
+ int is_pal = !(state->std & V4L2_STD_525_60);
+ int i, x;
+ u8 lcr[24];
+
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE &&
+ fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE)
+ return -EINVAL;
+ svbi = &fmt->fmt.sliced;
+ if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ /* raw VBI */
+ memset(svbi, 0, sizeof(*svbi));
/* Setup standard */
cx18_av_std_setup(cx);
- /* Sliced VBI */
- cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */
- cx18_av_write(cx, 0x406, 0x13);
- cx18_av_write(cx, 0x47f, vbi_offset);
-
- if (is_pal) {
- for (i = 0; i <= 6; i++)
- svbi->service_lines[0][i] =
- svbi->service_lines[1][i] = 0;
- } else {
- for (i = 0; i <= 9; i++)
- svbi->service_lines[0][i] =
- svbi->service_lines[1][i] = 0;
-
- for (i = 22; i <= 23; i++)
- svbi->service_lines[0][i] =
- svbi->service_lines[1][i] = 0;
- }
+ /* VBI Offset */
+ cx18_av_write(cx, 0x47f, state->slicer_line_delay);
+ cx18_av_write(cx, 0x404, 0x2e);
+ return 0;
+ }
- for (i = 7; i <= 23; i++) {
- for (x = 0; x <= 1; x++) {
- switch (svbi->service_lines[1-x][i]) {
- case V4L2_SLICED_TELETEXT_B:
- lcr[i] |= 1 << (4 * x);
- break;
- case V4L2_SLICED_WSS_625:
- lcr[i] |= 4 << (4 * x);
- break;
- case V4L2_SLICED_CAPTION_525:
- lcr[i] |= 6 << (4 * x);
- break;
- case V4L2_SLICED_VPS:
- lcr[i] |= 9 << (4 * x);
- break;
- }
- }
- }
+ for (x = 0; x <= 23; x++)
+ lcr[x] = 0x00;
+
+ /* Setup standard */
+ cx18_av_std_setup(cx);
+
+ /* Sliced VBI */
+ cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */
+ cx18_av_write(cx, 0x406, 0x13);
+ cx18_av_write(cx, 0x47f, state->slicer_line_delay);
+
+ /* Force impossible lines to 0 */
+ if (is_pal) {
+ for (i = 0; i <= 6; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+ } else {
+ for (i = 0; i <= 9; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+
+ for (i = 22; i <= 23; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+ }
- if (is_pal) {
- for (x = 1, i = 0x424; i <= 0x434; i++, x++)
- cx18_av_write(cx, i, lcr[6 + x]);
- } else {
- for (x = 1, i = 0x424; i <= 0x430; i++, x++)
- cx18_av_write(cx, i, lcr[9 + x]);
- for (i = 0x431; i <= 0x434; i++)
- cx18_av_write(cx, i, 0);
+ /* Build register values for requested service lines */
+ for (i = 7; i <= 23; i++) {
+ for (x = 0; x <= 1; x++) {
+ switch (svbi->service_lines[1-x][i]) {
+ case V4L2_SLICED_TELETEXT_B:
+ lcr[i] |= 1 << (4 * x);
+ break;
+ case V4L2_SLICED_WSS_625:
+ lcr[i] |= 4 << (4 * x);
+ break;
+ case V4L2_SLICED_CAPTION_525:
+ lcr[i] |= 6 << (4 * x);
+ break;
+ case V4L2_SLICED_VPS:
+ lcr[i] |= 9 << (4 * x);
+ break;
+ }
}
+ }
- cx18_av_write(cx, 0x43c, 0x16);
- cx18_av_write(cx, 0x474, is_pal ? 0x2a : 0x22);
- break;
+ if (is_pal) {
+ for (x = 1, i = 0x424; i <= 0x434; i++, x++)
+ cx18_av_write(cx, i, lcr[6 + x]);
+ } else {
+ for (x = 1, i = 0x424; i <= 0x430; i++, x++)
+ cx18_av_write(cx, i, lcr[9 + x]);
+ for (i = 0x431; i <= 0x434; i++)
+ cx18_av_write(cx, i, 0);
}
- case VIDIOC_INT_DECODE_VBI_LINE:
- {
- struct v4l2_decode_vbi_line *vbi = arg;
- u8 *p = vbi->p;
- int id1, id2, l, err = 0;
+ cx18_av_write(cx, 0x43c, 0x16);
+ /* FIXME - should match vblank set in cx18_av_std_setup() */
+ cx18_av_write(cx, 0x474, is_pal ? 0x2a : 26);
+ return 0;
+}
+
+int cx18_av_decode_vbi_line(struct v4l2_subdev *sd,
+ struct v4l2_decode_vbi_line *vbi)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ struct cx18_av_state *state = &cx->av_state;
+ struct vbi_anc_data *anc = (struct vbi_anc_data *)vbi->p;
+ u8 *p;
+ int did, sdid, l, err = 0;
+
+ /*
+ * Check for the ancillary data header for sliced VBI
+ */
+ if (anc->preamble[0] ||
+ anc->preamble[1] != 0xff || anc->preamble[2] != 0xff ||
+ (anc->did != sliced_vbi_did[0] &&
+ anc->did != sliced_vbi_did[1])) {
+ vbi->line = vbi->type = 0;
+ return 0;
+ }
- if (p[0] || p[1] != 0xff || p[2] != 0xff ||
- (p[3] != 0x55 && p[3] != 0x91)) {
- vbi->line = vbi->type = 0;
- break;
- }
+ did = anc->did;
+ sdid = anc->sdid & 0xf;
+ l = anc->idid[0] & 0x3f;
+ l += state->slicer_line_offset;
+ p = anc->payload;
- p += 4;
- id1 = p[-1];
- id2 = p[0] & 0xf;
- l = p[2] & 0x3f;
- l += state->vbi_line_offset;
- p += 4;
-
- switch (id2) {
- case 1:
- id2 = V4L2_SLICED_TELETEXT_B;
- break;
- case 4:
- id2 = V4L2_SLICED_WSS_625;
- break;
- case 6:
- id2 = V4L2_SLICED_CAPTION_525;
- err = !odd_parity(p[0]) || !odd_parity(p[1]);
- break;
- case 9:
- id2 = V4L2_SLICED_VPS;
- if (decode_vps(p, p) != 0)
- err = 1;
- break;
- default:
- id2 = 0;
+ /* Decode the SDID set by the slicer */
+ switch (sdid) {
+ case 1:
+ sdid = V4L2_SLICED_TELETEXT_B;
+ break;
+ case 4:
+ sdid = V4L2_SLICED_WSS_625;
+ break;
+ case 6:
+ sdid = V4L2_SLICED_CAPTION_525;
+ err = !odd_parity(p[0]) || !odd_parity(p[1]);
+ break;
+ case 9:
+ sdid = V4L2_SLICED_VPS;
+ if (decode_vps(p, p) != 0)
err = 1;
- break;
- }
-
- vbi->type = err ? 0 : id2;
- vbi->line = err ? 0 : l;
- vbi->is_second_field = err ? 0 : (id1 == 0x55);
- vbi->p = p;
break;
- }
+ default:
+ sdid = 0;
+ err = 1;
+ break;
}
+ vbi->type = err ? 0 : sdid;
+ vbi->line = err ? 0 : l;
+ vbi->is_second_field = err ? 0 : (did == sliced_vbi_did[1]);
+ vbi->p = p;
return 0;
}
diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c
index e274043657dd..9bc221837847 100644
--- a/drivers/media/video/cx18/cx18-cards.c
+++ b/drivers/media/video/cx18/cx18-cards.c
@@ -51,12 +51,12 @@ static struct cx18_card_tuner_i2c cx18_i2c_std = {
static const struct cx18_card cx18_card_hvr1600_esmt = {
.type = CX18_CARD_HVR_1600_ESMT,
.name = "Hauppauge HVR-1600",
- .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
+ .comment = "Simultaneous Digital and Analog TV capture supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
- .hw_audio_ctrl = CX18_HW_CX23418,
+ .hw_audio_ctrl = CX18_HW_418_AV,
.hw_muxer = CX18_HW_CS5345,
- .hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER |
- CX18_HW_CS5345 | CX18_HW_DVB,
+ .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
+ CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL,
.video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 },
{ CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 },
@@ -97,12 +97,12 @@ static const struct cx18_card cx18_card_hvr1600_esmt = {
static const struct cx18_card cx18_card_hvr1600_samsung = {
.type = CX18_CARD_HVR_1600_SAMSUNG,
.name = "Hauppauge HVR-1600 (Preproduction)",
- .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
+ .comment = "Simultaneous Digital and Analog TV capture supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
- .hw_audio_ctrl = CX18_HW_CX23418,
+ .hw_audio_ctrl = CX18_HW_418_AV,
.hw_muxer = CX18_HW_CS5345,
- .hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER |
- CX18_HW_CS5345 | CX18_HW_DVB,
+ .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
+ CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL,
.video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 },
{ CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 },
@@ -152,10 +152,10 @@ static const struct cx18_card_pci_info cx18_pci_h900[] = {
static const struct cx18_card cx18_card_h900 = {
.type = CX18_CARD_COMPRO_H900,
.name = "Compro VideoMate H900",
- .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
+ .comment = "Analog TV capture supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
- .hw_audio_ctrl = CX18_HW_CX23418,
- .hw_all = CX18_HW_TUNER,
+ .hw_audio_ctrl = CX18_HW_418_AV,
+ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL,
.video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
{ CX18_CARD_INPUT_SVIDEO1, 1,
@@ -201,8 +201,8 @@ static const struct cx18_card cx18_card_mpc718 = {
.name = "Yuan MPC718",
.comment = "Analog video capture works; some audio line in may not.\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
- .hw_audio_ctrl = CX18_HW_CX23418,
- .hw_all = CX18_HW_TUNER,
+ .hw_audio_ctrl = CX18_HW_418_AV,
+ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL,
.video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
{ CX18_CARD_INPUT_SVIDEO1, 1,
@@ -249,11 +249,11 @@ static const struct cx18_card_pci_info cx18_pci_cnxt_raptor_pal[] = {
static const struct cx18_card cx18_card_cnxt_raptor_pal = {
.type = CX18_CARD_CNXT_RAPTOR_PAL,
.name = "Conexant Raptor PAL/SECAM",
- .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
+ .comment = "Analog TV capture supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
- .hw_audio_ctrl = CX18_HW_CX23418,
- .hw_muxer = CX18_HW_GPIO,
- .hw_all = CX18_HW_TUNER | CX18_HW_GPIO,
+ .hw_audio_ctrl = CX18_HW_418_AV,
+ .hw_muxer = CX18_HW_GPIO_MUX,
+ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX,
.video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
{ CX18_CARD_INPUT_SVIDEO1, 1,
@@ -306,8 +306,8 @@ static const struct cx18_card cx18_card_toshiba_qosmio_dvbt = {
.comment = "Experimenters and photos needed for device to work well.\n"
"\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
- .hw_audio_ctrl = CX18_HW_CX23418,
- .hw_all = CX18_HW_TUNER,
+ .hw_audio_ctrl = CX18_HW_418_AV,
+ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL,
.video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE6 },
{ CX18_CARD_INPUT_SVIDEO1, 1,
@@ -339,19 +339,21 @@ static const struct cx18_card cx18_card_toshiba_qosmio_dvbt = {
/* Leadtek WinFast PVR2100 */
static const struct cx18_card_pci_info cx18_pci_leadtek_pvr2100[] = {
- { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6f27 },
+ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6f27 }, /* PVR2100 */
+ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6690 }, /* DVR3100 H */
{ 0, 0, 0 }
};
static const struct cx18_card cx18_card_leadtek_pvr2100 = {
.type = CX18_CARD_LEADTEK_PVR2100,
- .name = "Leadtek WinFast PVR2100",
+ .name = "Leadtek WinFast PVR2100/DVR3100 H",
.comment = "Experimenters and photos needed for device to work well.\n"
"\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
- .hw_audio_ctrl = CX18_HW_CX23418,
- .hw_muxer = CX18_HW_GPIO,
- .hw_all = CX18_HW_TUNER | CX18_HW_GPIO,
+ .hw_audio_ctrl = CX18_HW_418_AV,
+ .hw_muxer = CX18_HW_GPIO_MUX,
+ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX |
+ CX18_HW_GPIO_RESET_CTRL,
.video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
{ CX18_CARD_INPUT_SVIDEO1, 1,
diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h
index 6fa7bcb42dde..3c552b6b7c4d 100644
--- a/drivers/media/video/cx18/cx18-cards.h
+++ b/drivers/media/video/cx18/cx18-cards.h
@@ -22,12 +22,13 @@
*/
/* 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)
+#define CX18_HW_TUNER (1 << 0)
+#define CX18_HW_TVEEPROM (1 << 1)
+#define CX18_HW_CS5345 (1 << 2)
+#define CX18_HW_DVB (1 << 3)
+#define CX18_HW_418_AV (1 << 4)
+#define CX18_HW_GPIO_MUX (1 << 5)
+#define CX18_HW_GPIO_RESET_CTRL (1 << 6)
/* video inputs */
#define CX18_CARD_INPUT_VID_TUNER 1
@@ -49,8 +50,7 @@
/* 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 */
+ V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE)
struct cx18_card_video_input {
u8 video_type; /* video input type */
@@ -122,7 +122,7 @@ struct cx18_card {
char *comment;
u32 v4l2_capabilities;
u32 hw_audio_ctrl; /* hardware used for the V4L2 controls (only
- 1 dev allowed) */
+ 1 dev allowed currently) */
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];
diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c
index 17edf305d649..82fc2f9d4021 100644
--- a/drivers/media/video/cx18/cx18-controls.c
+++ b/drivers/media/video/cx18/cx18-controls.c
@@ -22,14 +22,13 @@
*/
#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"
+/* Must be sorted from low to high control ID! */
static const u32 user_ctrls[] = {
V4L2_CID_USER_CLASS,
V4L2_CID_BRIGHTNESS,
@@ -66,7 +65,7 @@ int cx18_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl)
case V4L2_CID_HUE:
case V4L2_CID_SATURATION:
case V4L2_CID_CONTRAST:
- if (cx18_av_cmd(cx, VIDIOC_QUERYCTRL, qctrl))
+ if (v4l2_subdev_call(cx->sd_av, core, queryctrl, qctrl))
qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
return 0;
@@ -76,7 +75,7 @@ int cx18_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl)
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))
+ if (v4l2_subdev_call(cx->sd_av, core, queryctrl, qctrl))
qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
return 0;
@@ -125,7 +124,7 @@ static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
case V4L2_CID_HUE:
case V4L2_CID_SATURATION:
case V4L2_CID_CONTRAST:
- return cx18_av_cmd(cx, VIDIOC_S_CTRL, vctrl);
+ return v4l2_subdev_call(cx->sd_av, core, s_ctrl, vctrl);
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_MUTE:
@@ -133,7 +132,7 @@ static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
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);
+ return v4l2_subdev_call(cx->sd_av, core, s_ctrl, vctrl);
default:
CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
@@ -150,7 +149,7 @@ static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
case V4L2_CID_HUE:
case V4L2_CID_SATURATION:
case V4L2_CID_CONTRAST:
- return cx18_av_cmd(cx, VIDIOC_G_CTRL, vctrl);
+ return v4l2_subdev_call(cx->sd_av, core, g_ctrl, vctrl);
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_MUTE:
@@ -158,7 +157,8 @@ static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
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);
+ return v4l2_subdev_call(cx->sd_av, core, g_ctrl, vctrl);
+
default:
CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
return -EINVAL;
@@ -166,38 +166,57 @@ static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
return 0;
}
-static int cx18_setup_vbi_fmt(struct cx18 *cx, enum v4l2_mpeg_stream_vbi_fmt fmt)
+static int cx18_setup_vbi_fmt(struct cx18 *cx,
+ enum v4l2_mpeg_stream_vbi_fmt fmt,
+ enum v4l2_mpeg_stream_type type)
{
if (!(cx->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE))
return -EINVAL;
if (atomic_read(&cx->ana_capturing) > 0)
return -EBUSY;
- /* First try to allocate sliced VBI buffers if needed. */
- if (fmt && cx->vbi.sliced_mpeg_data[0] == NULL) {
+ if (fmt != V4L2_MPEG_STREAM_VBI_FMT_IVTV ||
+ type != V4L2_MPEG_STREAM_TYPE_MPEG2_PS) {
+ /* We don't do VBI insertion aside from IVTV format in a PS */
+ cx->vbi.insert_mpeg = V4L2_MPEG_STREAM_VBI_FMT_NONE;
+ CX18_DEBUG_INFO("disabled insertion of sliced VBI data into "
+ "the MPEG stream\n");
+ return 0;
+ }
+
+ /* Allocate sliced VBI buffers if needed. */
+ if (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);
+ cx->vbi.sliced_mpeg_data[i] =
+ kmalloc(CX18_SLICED_MPEG_DATA_BUFSZ, 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;
}
+ cx->vbi.insert_mpeg =
+ V4L2_MPEG_STREAM_VBI_FMT_NONE;
+ CX18_WARN("Unable to allocate buffers for "
+ "sliced VBI data insertion\n");
return -ENOMEM;
}
}
}
cx->vbi.insert_mpeg = fmt;
+ CX18_DEBUG_INFO("enabled insertion of sliced VBI data into the MPEG PS,"
+ "when sliced VBI is enabled\n");
- if (cx->vbi.insert_mpeg == 0)
- return 0;
- /* Need sliced data for mpeg insertion */
+ /*
+ * If our current settings have no lines set for capture, store a valid,
+ * default set of service lines to capture, in our current settings.
+ */
if (cx18_get_service_set(cx->vbi.sliced_in) == 0) {
if (cx->is_60hz)
- cx->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525;
+ cx->vbi.sliced_in->service_set =
+ V4L2_SLICED_CAPTION_525;
else
cx->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625;
cx18_expand_service_set(cx->vbi.sliced_in, cx->is_50hz);
@@ -259,10 +278,12 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
return err;
}
if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+ static u32 freqs[3] = { 44100, 48000, 32000 };
struct cx18_api_func_private priv;
struct cx2341x_mpeg_params p = cx->params;
int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing),
c, VIDIOC_S_EXT_CTRLS);
+ unsigned int idx;
if (err)
return err;
@@ -277,16 +298,23 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
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);
+ v4l2_subdev_call(cx->sd_av, video, s_fmt, &fmt);
}
priv.cx = cx;
priv.s = &cx->streams[id->type];
err = cx2341x_update(&priv, 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);
+ if (!err &&
+ (cx->params.stream_vbi_fmt != p.stream_vbi_fmt ||
+ cx->params.stream_type != p.stream_type))
+ err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt,
+ p.stream_type);
cx->params = p;
cx->dualwatch_stereo_mode = p.audio_properties & 0x0300;
- cx18_audio_set_audio_clock_freq(cx, p.audio_properties & 0x03);
+ idx = p.audio_properties & 0x03;
+ /* The audio clock of the digitizer must match the codec sample
+ rate otherwise you get some very strange effects. */
+ if (idx < sizeof(freqs))
+ cx18_call_all(cx, audio, s_clock_freq, freqs[idx]);
return err;
}
return -EINVAL;
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index f50cf2167adc..210c68aaae00 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -39,10 +39,6 @@
#include <media/tveeprom.h>
-
-/* var to keep track of the number of array elements in use */
-int cx18_cards_active;
-
/* If you have already X v4l cards, then set this to X. This way
the device numbers stay matched. Example: you have a WinTV card
without radio and a Compro H900 with. Normally this would give a
@@ -50,12 +46,6 @@ int cx18_cards_active;
setting this to 1 you ensure that radio0 is now also radio1. */
int cx18_first_minor;
-/* Master variable for all cx18 info */
-struct cx18 *cx18_cards[CX18_MAX_CARDS];
-
-/* Protects cx18_cards_active */
-DEFINE_SPINLOCK(cx18_cards_lock);
-
/* add your revision and whatnot here */
static struct pci_device_id cx18_pci_tbl[] __devinitdata = {
{PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418,
@@ -65,6 +55,8 @@ static struct pci_device_id cx18_pci_tbl[] __devinitdata = {
MODULE_DEVICE_TABLE(pci, cx18_pci_tbl);
+static atomic_t cx18_instance = ATOMIC_INIT(0);
+
/* Parameter declarations */
static int cardtype[CX18_MAX_CARDS];
static int tuner[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
@@ -159,7 +151,7 @@ MODULE_PARM_DESC(cardtype,
"\t\t\t 4 = Yuan MPC718\n"
"\t\t\t 5 = Conexant Raptor PAL/SECAM\n"
"\t\t\t 6 = Toshiba Qosmio DVB-T/Analog\n"
- "\t\t\t 7 = Leadtek WinFast PVR2100\n"
+ "\t\t\t 7 = Leadtek WinFast PVR2100/DVR3100 H\n"
"\t\t\t 0 = Autodetect (default)\n"
"\t\t\t-1 = Ignore this card\n\t\t");
MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60");
@@ -277,11 +269,16 @@ static void cx18_iounmap(struct cx18 *cx)
/* Hauppauge card? get values from tveeprom */
void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv)
{
+ struct i2c_client c;
u8 eedata[256];
- cx->i2c_client[0].addr = 0xA0 >> 1;
- tveeprom_read(&cx->i2c_client[0], eedata, sizeof(eedata));
- tveeprom_hauppauge_analog(&cx->i2c_client[0], tv, eedata);
+ memset(&c, 0, sizeof(c));
+ strlcpy(c.name, "cx18 tveeprom tmp", sizeof(c.name));
+ c.adapter = &cx->i2c_adap[0];
+ c.addr = 0xA0 >> 1;
+
+ tveeprom_read(&c, eedata, sizeof(eedata));
+ tveeprom_hauppauge_analog(&c, tv, eedata);
}
static void cx18_process_eeprom(struct cx18 *cx)
@@ -448,34 +445,38 @@ static void cx18_process_options(struct cx18 *cx)
cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufsize;
cx->stream_buf_size[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufsize;
cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufsize;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = 0; /* computed later */
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_active_samples * 36;
cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufsize;
cx->stream_buf_size[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control no data */
- /* Except for VBI ensure stream_buffers & stream_buf_size are valid */
+ /* Ensure stream_buffers & stream_buf_size are valid */
for (i = 0; i < CX18_MAX_STREAMS; i++) {
- /* User said to use 0 buffers */
- if (cx->stream_buffers[i] == 0) {
- cx->options.megabytes[i] = 0;
- cx->stream_buf_size[i] = 0;
- continue;
- }
- /* User said to use 0 MB total */
- if (cx->options.megabytes[i] <= 0) {
+ if (cx->stream_buffers[i] == 0 || /* User said 0 buffers */
+ cx->options.megabytes[i] <= 0 || /* User said 0 MB total */
+ cx->stream_buf_size[i] <= 0) { /* User said buf size 0 */
cx->options.megabytes[i] = 0;
cx->stream_buffers[i] = 0;
cx->stream_buf_size[i] = 0;
continue;
}
- /* VBI is computed later or user said buffer has size 0 */
- if (cx->stream_buf_size[i] <= 0) {
- if (i != CX18_ENC_STREAM_TYPE_VBI) {
- cx->options.megabytes[i] = 0;
- cx->stream_buffers[i] = 0;
- cx->stream_buf_size[i] = 0;
+ /*
+ * VBI is a special case where the stream_buf_size is fixed
+ * and already in bytes
+ */
+ if (i == CX18_ENC_STREAM_TYPE_VBI) {
+ if (cx->stream_buffers[i] < 0) {
+ cx->stream_buffers[i] =
+ cx->options.megabytes[i] * 1024 * 1024
+ / cx->stream_buf_size[i];
+ } else {
+ /* N.B. This might round down to 0 */
+ cx->options.megabytes[i] =
+ cx->stream_buffers[i]
+ * cx->stream_buf_size[i]/(1024 * 1024);
}
continue;
}
+ /* All other streams have stream_buf_size in kB at this point */
if (cx->stream_buffers[i] < 0) {
cx->stream_buffers[i] = cx->options.megabytes[i] * 1024
/ cx->stream_buf_size[i];
@@ -487,9 +488,9 @@ static void cx18_process_options(struct cx18 *cx)
cx->stream_buf_size[i] *= 1024; /* convert from kB to bytes */
}
- cx->options.cardtype = cardtype[cx->num];
- cx->options.tuner = tuner[cx->num];
- cx->options.radio = radio[cx->num];
+ cx->options.cardtype = cardtype[cx->instance];
+ cx->options.tuner = tuner[cx->instance];
+ cx->options.radio = radio[cx->instance];
cx->std = cx18_parse_std(cx);
if (cx->options.cardtype == -1) {
@@ -502,7 +503,7 @@ static void cx18_process_options(struct cx18 *cx)
else if (cx->options.cardtype != 0)
CX18_ERR("Unknown user specified type, trying to autodetect card\n");
if (cx->card == NULL) {
- if (cx->dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) {
+ if (cx->pci_dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) {
cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
CX18_INFO("Autodetected Hauppauge card\n");
}
@@ -512,13 +513,13 @@ static void cx18_process_options(struct cx18 *cx)
if (cx->card->pci_list == NULL)
continue;
for (j = 0; cx->card->pci_list[j].device; j++) {
- if (cx->dev->device !=
+ if (cx->pci_dev->device !=
cx->card->pci_list[j].device)
continue;
- if (cx->dev->subsystem_vendor !=
+ if (cx->pci_dev->subsystem_vendor !=
cx->card->pci_list[j].subsystem_vendor)
continue;
- if (cx->dev->subsystem_device !=
+ if (cx->pci_dev->subsystem_device !=
cx->card->pci_list[j].subsystem_device)
continue;
CX18_INFO("Autodetected %s card\n", cx->card->name);
@@ -531,9 +532,10 @@ done:
if (cx->card == NULL) {
cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
CX18_ERR("Unknown card: vendor/device: [%04x:%04x]\n",
- cx->dev->vendor, cx->dev->device);
+ cx->pci_dev->vendor, cx->pci_dev->device);
CX18_ERR(" subsystem vendor/device: [%04x:%04x]\n",
- cx->dev->subsystem_vendor, cx->dev->subsystem_device);
+ cx->pci_dev->subsystem_vendor,
+ cx->pci_dev->subsystem_device);
CX18_ERR("Defaulting to %s card\n", cx->card->name);
CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
CX18_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n");
@@ -545,7 +547,7 @@ done:
}
/* Precondition: the cx18 structure has been memset to 0. Only
- the dev and num fields have been filled in.
+ the dev and instance fields have been filled in.
No assumptions on the card type may be made here (see cx18_init_struct2
for that).
*/
@@ -553,18 +555,14 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
{
int i;
- cx->base_addr = pci_resource_start(cx->dev, 0);
+ cx->base_addr = pci_resource_start(cx->pci_dev, 0);
mutex_init(&cx->serialize_lock);
- mutex_init(&cx->i2c_bus_lock[0]);
- mutex_init(&cx->i2c_bus_lock[1]);
mutex_init(&cx->gpio_lock);
mutex_init(&cx->epu2apu_mb_lock);
mutex_init(&cx->epu2cpu_mb_lock);
- spin_lock_init(&cx->lock);
-
- cx->work_queue = create_singlethread_workqueue(cx->name);
+ cx->work_queue = create_singlethread_workqueue(cx->v4l2_dev.name);
if (cx->work_queue == NULL) {
CX18_ERR("Unable to create work hander thread\n");
return -ENOMEM;
@@ -587,7 +585,8 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
(cx->params.video_temporal_filter_mode << 1) |
(cx->params.video_median_filter_type << 2);
cx->params.port = CX2341X_PORT_MEMORY;
- cx->params.capabilities = CX2341X_CAP_HAS_TS;
+ cx->params.capabilities =
+ CX2341X_CAP_HAS_TS | CX2341X_CAP_HAS_SLICED_VBI;
init_waitqueue_head(&cx->cap_w);
init_waitqueue_head(&cx->mb_apu_waitq);
init_waitqueue_head(&cx->mb_cpu_waitq);
@@ -597,49 +596,6 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced;
- /*
- * The VBI line sizes depend on the pixel clock and the horiz rate
- *
- * (1/Fh)*(2*Fp) = Samples/line
- * = 4 bytes EAV + Anc data in hblank + 4 bytes SAV + active samples
- *
- * Sliced VBI is sent as ancillary data during horizontal blanking
- * Raw VBI is sent as active video samples during vertcal blanking
- *
- * We use a BT.656 pxiel clock of 13.5 MHz and a BT.656 active line
- * length of 720 pixels @ 4:2:2 sampling. Thus...
- *
- * For systems that use a 15.734 kHz horizontal rate, such as
- * NTSC-M, PAL-M, PAL-60, and other 60 Hz/525 line systems, we have:
- *
- * (1/15.734 kHz) * 2 * 13.5 MHz = 1716 samples/line =
- * 4 bytes SAV + 268 bytes anc data + 4 bytes SAV + 1440 active samples
- *
- * For systems that use a 15.625 kHz horizontal rate, such as
- * PAL-B/G/H, PAL-I, SECAM-L and other 50 Hz/625 line systems, we have:
- *
- * (1/15.625 kHz) * 2 * 13.5 MHz = 1728 samples/line =
- * 4 bytes SAV + 280 bytes anc data + 4 bytes SAV + 1440 active samples
- *
- */
-
- /* FIXME: init these based on tuner std & modify when std changes */
- /* CX18-AV-Core number of VBI samples output per horizontal line */
- cx->vbi.raw_decoder_line_size = 1444; /* 4 byte SAV + 2 * 720 */
- cx->vbi.sliced_decoder_line_size = 272; /* 60 Hz: 268+4, 50 Hz: 280+4 */
-
- /* CX18-AV-Core VBI samples/line possibly rounded up */
- cx->vbi.raw_size = 1444; /* Real max size is 1444 */
- cx->vbi.sliced_size = 284; /* Real max size is 284 */
-
- /*
- * CX18-AV-Core SAV/EAV RP codes in VIP 1.x mode
- * Task Field VerticalBlank HorizontalBlank 0 0 0 0
- */
- cx->vbi.raw_decoder_sav_odd_field = 0x20; /* V */
- cx->vbi.raw_decoder_sav_even_field = 0x60; /* FV */
- cx->vbi.sliced_decoder_sav_odd_field = 0xB0; /* T VH - actually EAV */
- cx->vbi.sliced_decoder_sav_even_field = 0xF0; /* TFVH - actually EAV */
return 0;
}
@@ -668,15 +624,9 @@ static void __devinit cx18_init_struct2(struct cx18 *cx)
i = 0;
cx->active_input = i;
cx->audio_input = cx->card->video_inputs[i].audio_index;
- cx->av_state.vid_input = CX18_AV_COMPOSITE7;
- cx->av_state.aud_input = CX18_AV_AUDIO8;
- cx->av_state.audclk_freq = 48000;
- cx->av_state.audmode = V4L2_TUNER_MODE_LANG1;
- /* FIXME - 8 is NTSC value, investigate */
- cx->av_state.vbi_line_offset = 8;
}
-static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *dev,
+static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev,
const struct pci_device_id *pci_id)
{
u16 cmd;
@@ -684,124 +634,125 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *dev,
CX18_DEBUG_INFO("Enabling pci device\n");
- if (pci_enable_device(dev)) {
- CX18_ERR("Can't enable device %d!\n", cx->num);
+ if (pci_enable_device(pci_dev)) {
+ CX18_ERR("Can't enable device %d!\n", cx->instance);
return -EIO;
}
- if (pci_set_dma_mask(dev, 0xffffffff)) {
- CX18_ERR("No suitable DMA available on card %d.\n", cx->num);
+ if (pci_set_dma_mask(pci_dev, 0xffffffff)) {
+ CX18_ERR("No suitable DMA available, card %d\n", cx->instance);
return -EIO;
}
if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) {
- CX18_ERR("Cannot request encoder memory region on card %d.\n", cx->num);
+ CX18_ERR("Cannot request encoder memory region, card %d\n",
+ cx->instance);
return -EIO;
}
/* Enable bus mastering and memory mapped IO for the CX23418 */
- pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ pci_read_config_word(pci_dev, PCI_COMMAND, &cmd);
cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
- pci_write_config_word(dev, PCI_COMMAND, cmd);
+ pci_write_config_word(pci_dev, PCI_COMMAND, cmd);
- pci_read_config_byte(dev, PCI_CLASS_REVISION, &cx->card_rev);
- pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency);
+ pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &cx->card_rev);
+ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency);
if (pci_latency < 64 && cx18_pci_latency) {
CX18_INFO("Unreasonably low latency timer, "
"setting to 64 (was %d)\n", pci_latency);
- pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
- pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency);
+ pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, 64);
+ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency);
}
CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, "
"irq: %d, latency: %d, memory: 0x%lx\n",
- cx->dev->device, cx->card_rev, dev->bus->number,
- PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
- cx->dev->irq, pci_latency, (unsigned long)cx->base_addr);
+ cx->pci_dev->device, cx->card_rev, pci_dev->bus->number,
+ PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn),
+ cx->pci_dev->irq, pci_latency, (unsigned long)cx->base_addr);
return 0;
}
-#ifdef MODULE
-static u32 cx18_request_module(struct cx18 *cx, u32 hw,
- const char *name, u32 id)
-{
- if ((hw & id) == 0)
- return hw;
- if (request_module(name) != 0) {
- CX18_ERR("Failed to load module %s\n", name);
- return hw & ~id;
- }
- CX18_DEBUG_INFO("Loaded module %s\n", name);
- return hw;
-}
-#endif
-
-static void cx18_load_and_init_modules(struct cx18 *cx)
+static void cx18_init_subdevs(struct cx18 *cx)
{
u32 hw = cx->card->hw_all;
+ u32 device;
int i;
-#ifdef MODULE
- /* load modules */
-#ifdef CONFIG_MEDIA_TUNER_MODULE
- hw = cx18_request_module(cx, hw, "tuner", CX18_HW_TUNER);
-#endif
-#ifdef CONFIG_VIDEO_CS5345_MODULE
- hw = cx18_request_module(cx, hw, "cs5345", CX18_HW_CS5345);
-#endif
-#endif
-
- /* check which i2c devices are actually found */
- for (i = 0; i < 32; i++) {
- u32 device = 1 << i;
+ for (i = 0, device = 1; i < 32; i++, device <<= 1) {
if (!(device & hw))
continue;
- if (device == CX18_HW_GPIO || device == CX18_HW_TVEEPROM ||
- device == CX18_HW_CX23418 || device == CX18_HW_DVB) {
- /* These 'devices' do not use i2c probing */
+
+ switch (device) {
+ case CX18_HW_DVB:
+ case CX18_HW_TVEEPROM:
+ /* These subordinate devices do not use probing */
cx->hw_flags |= device;
- continue;
- }
- cx18_i2c_register(cx, i);
- if (cx18_i2c_hw_addr(cx, device) > 0)
+ break;
+ case CX18_HW_418_AV:
+ /* The A/V decoder gets probed earlier to set PLLs */
+ /* Just note that the card uses it (i.e. has analog) */
cx->hw_flags |= device;
+ break;
+ case CX18_HW_GPIO_RESET_CTRL:
+ /*
+ * The Reset Controller gets probed and added to
+ * hw_flags earlier for i2c adapter/bus initialization
+ */
+ break;
+ case CX18_HW_GPIO_MUX:
+ if (cx18_gpio_register(cx, device) == 0)
+ cx->hw_flags |= device;
+ break;
+ default:
+ if (cx18_i2c_register(cx, i) == 0)
+ cx->hw_flags |= device;
+ break;
+ }
}
- hw = cx->hw_flags;
+ if (cx->hw_flags & CX18_HW_418_AV)
+ cx->sd_av = cx18_find_hw(cx, CX18_HW_418_AV);
+
+ if (cx->card->hw_muxer != 0)
+ cx->sd_extmux = cx18_find_hw(cx, cx->card->hw_muxer);
}
-static int __devinit cx18_probe(struct pci_dev *dev,
+static int __devinit cx18_probe(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id)
{
int retval = 0;
int i;
- int vbi_buf_size;
u32 devtype;
struct cx18 *cx;
- spin_lock(&cx18_cards_lock);
-
- /* Make sure we've got a place for this card */
- if (cx18_cards_active == CX18_MAX_CARDS) {
- printk(KERN_ERR "cx18: Maximum number of cards detected (%d).\n",
- cx18_cards_active);
- spin_unlock(&cx18_cards_lock);
+ /* FIXME - module parameter arrays constrain max instances */
+ i = atomic_inc_return(&cx18_instance) - 1;
+ if (i >= CX18_MAX_CARDS) {
+ printk(KERN_ERR "cx18: cannot manage card %d, driver has a "
+ "limit of 0 - %d\n", i, CX18_MAX_CARDS - 1);
return -ENOMEM;
}
cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC);
- if (!cx) {
- spin_unlock(&cx18_cards_lock);
+ if (cx == NULL) {
+ printk(KERN_ERR "cx18: cannot manage card %d, out of memory\n",
+ i);
return -ENOMEM;
}
- cx18_cards[cx18_cards_active] = cx;
- cx->dev = dev;
- cx->num = cx18_cards_active++;
- snprintf(cx->name, sizeof(cx->name), "cx18-%d", cx->num);
- CX18_INFO("Initializing card #%d\n", cx->num);
+ cx->pci_dev = pci_dev;
+ cx->instance = i;
- spin_unlock(&cx18_cards_lock);
+ retval = v4l2_device_register(&pci_dev->dev, &cx->v4l2_dev);
+ if (retval) {
+ printk(KERN_ERR "cx18: v4l2_device_register of card %d failed"
+ "\n", cx->instance);
+ kfree(cx);
+ return retval;
+ }
+ snprintf(cx->v4l2_dev.name, sizeof(cx->v4l2_dev.name), "cx18-%d",
+ cx->instance);
+ CX18_INFO("Initializing card %d\n", cx->instance);
cx18_process_options(cx);
if (cx->options.cardtype == -1) {
@@ -816,13 +767,10 @@ static int __devinit cx18_probe(struct pci_dev *dev,
CX18_DEBUG_INFO("base addr: 0x%08x\n", cx->base_addr);
/* PCI Device Setup */
- retval = cx18_setup_pci(cx, dev, pci_id);
+ retval = cx18_setup_pci(cx, pci_dev, pci_id);
if (retval != 0)
goto free_workqueue;
- /* save cx in the pci struct for later use */
- pci_set_drvdata(dev, cx);
-
/* map io memory */
CX18_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
cx->base_addr + CX18_MEM_OFFSET, CX18_MEM_SIZE);
@@ -856,6 +804,23 @@ static int __devinit cx18_probe(struct pci_dev *dev,
cx18_gpio_init(cx);
+ /* Initialize integrated A/V decoder early to set PLLs, just in case */
+ retval = cx18_av_probe(cx);
+ if (retval) {
+ CX18_ERR("Could not register A/V decoder subdevice\n");
+ goto free_map;
+ }
+ cx18_call_hw(cx, CX18_HW_418_AV, core, init, (u32) CX18_AV_INIT_PLLS);
+
+ /* Initialize GPIO Reset Controller to do chip resets during i2c init */
+ if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) {
+ if (cx18_gpio_register(cx, CX18_HW_GPIO_RESET_CTRL) != 0)
+ CX18_WARN("Could not register GPIO reset controller"
+ "subdevice; proceeding anyway.\n");
+ else
+ cx->hw_flags |= CX18_HW_GPIO_RESET_CTRL;
+ }
+
/* active i2c */
CX18_DEBUG_INFO("activating i2c...\n");
retval = init_cx18_i2c(cx);
@@ -864,8 +829,6 @@ static int __devinit cx18_probe(struct pci_dev *dev,
goto free_map;
}
- CX18_DEBUG_INFO("Active card count: %d.\n", cx18_cards_active);
-
if (cx->card->hw_all & CX18_HW_TVEEPROM) {
/* Based on the model number the cardtype may be changed.
The PCI IDs are not always reliable. */
@@ -881,8 +844,9 @@ static int __devinit cx18_probe(struct pci_dev *dev,
cx18_init_scb(cx);
/* Register IRQ */
- retval = request_irq(cx->dev->irq, cx18_irq_handler,
- IRQF_SHARED | IRQF_DISABLED, cx->name, (void *)cx);
+ retval = request_irq(cx->pci_dev->irq, cx18_irq_handler,
+ IRQF_SHARED | IRQF_DISABLED,
+ cx->v4l2_dev.name, (void *)cx);
if (retval) {
CX18_ERR("Failed to register irq %d\n", retval);
goto free_i2c;
@@ -917,33 +881,14 @@ static int __devinit cx18_probe(struct pci_dev *dev,
initialization. */
cx18_init_struct2(cx);
- cx18_load_and_init_modules(cx);
+ cx18_init_subdevs(cx);
- if (cx->std & V4L2_STD_525_60) {
+ if (cx->std & V4L2_STD_525_60)
cx->is_60hz = 1;
- cx->is_out_60hz = 1;
- } else {
+ else
cx->is_50hz = 1;
- cx->is_out_50hz = 1;
- }
- cx->params.video_gop_size = cx->is_60hz ? 15 : 12;
-
- /*
- * FIXME: setting the buffer size based on the tuner standard is
- * suboptimal, as the CVBS and SVideo inputs could use a different std
- * and the buffer could end up being too small in that case.
- */
- vbi_buf_size = cx->vbi.raw_size * (cx->is_60hz ? 24 : 36) / 2;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_buf_size;
- if (cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] < 0)
- cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] =
- cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] * 1024 * 1024
- / vbi_buf_size;
- else
- cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] =
- cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] * vbi_buf_size
- / (1024 * 1024);
+ cx->params.video_gop_size = cx->is_60hz ? 15 : 12;
if (cx->options.radio > 0)
cx->v4l2_cap |= V4L2_CAP_RADIO;
@@ -956,7 +901,7 @@ static int __devinit cx18_probe(struct pci_dev *dev,
setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
setup.tuner_callback = (setup.type == TUNER_XC2028) ?
cx18_reset_tuner_gpio : NULL;
- cx18_call_i2c_clients(cx, TUNER_SET_TYPE_ADDR, &setup);
+ cx18_call_all(cx, tuner, s_type_addr, &setup);
if (setup.type == TUNER_XC2028) {
static struct xc2028_ctrl ctrl = {
.fname = XC2028_DEFAULT_FIRMWARE,
@@ -966,7 +911,7 @@ static int __devinit cx18_probe(struct pci_dev *dev,
.tuner = cx->options.tuner,
.priv = &ctrl,
};
- cx18_call_i2c_clients(cx, TUNER_SET_CONFIG, &cfg);
+ cx18_call_all(cx, tuner, s_config, &cfg);
}
}
@@ -985,14 +930,13 @@ static int __devinit cx18_probe(struct pci_dev *dev,
goto free_streams;
}
- CX18_INFO("Initialized card #%d: %s\n", cx->num, cx->card_name);
-
+ CX18_INFO("Initialized card: %s\n", cx->card_name);
return 0;
free_streams:
cx18_streams_cleanup(cx, 1);
free_irq:
- free_irq(cx->dev->irq, (void *)cx);
+ free_irq(cx->pci_dev->irq, (void *)cx);
free_i2c:
exit_cx18_i2c(cx);
free_map:
@@ -1006,11 +950,8 @@ err:
retval = -ENODEV;
CX18_ERR("Error %d on initialization\n", retval);
- i = cx->num;
- spin_lock(&cx18_cards_lock);
- kfree(cx18_cards[i]);
- cx18_cards[i] = NULL;
- spin_unlock(&cx18_cards_lock);
+ v4l2_device_unregister(&cx->v4l2_dev);
+ kfree(cx);
return retval;
}
@@ -1043,8 +984,21 @@ int cx18_init_on_first_open(struct cx18 *cx)
}
set_bit(CX18_F_I_LOADED_FW, &cx->i_flags);
- /* Init the firmware twice to work around a silicon bug
- * transport related. */
+ /*
+ * Init the firmware twice to work around a silicon bug
+ * with the digital TS.
+ *
+ * The second firmware load requires us to normalize the APU state,
+ * or the audio for the first analog capture will be badly incorrect.
+ *
+ * I can't seem to call APU_RESETAI and have it succeed without the
+ * APU capturing audio, so we start and stop it here to do the reset
+ */
+
+ /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */
+ cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0);
+ cx18_vapi(cx, CX18_APU_RESETAI, 0);
+ cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
fw_retry_count = 3;
while (--fw_retry_count > 0) {
@@ -1060,6 +1014,22 @@ int cx18_init_on_first_open(struct cx18 *cx)
return -ENXIO;
}
+ /*
+ * The second firmware load requires us to normalize the APU state,
+ * or the audio for the first analog capture will be badly incorrect.
+ *
+ * I can't seem to call APU_RESETAI and have it succeed without the
+ * APU capturing audio, so we start and stop it here to do the reset
+ */
+
+ /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */
+ cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0);
+ cx18_vapi(cx, CX18_APU_RESETAI, 0);
+ cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
+
+ /* Init the A/V decoder, if it hasn't been already */
+ v4l2_subdev_call(cx->sd_av, core, init, (u32) CX18_AV_INIT_NORMAL);
+
vf.tuner = 0;
vf.type = V4L2_TUNER_ANALOG_TV;
vf.frequency = 6400; /* the tuner 'baseline' frequency */
@@ -1092,9 +1062,11 @@ static void cx18_cancel_epu_work_orders(struct cx18 *cx)
static void cx18_remove(struct pci_dev *pci_dev)
{
- struct cx18 *cx = pci_get_drvdata(pci_dev);
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+ struct cx18 *cx = to_cx18(v4l2_dev);
+ int i;
- CX18_DEBUG_INFO("Removing Card #%d\n", cx->num);
+ CX18_DEBUG_INFO("Removing Card\n");
/* Stop all captures */
CX18_DEBUG_INFO("Stopping all streams\n");
@@ -1115,15 +1087,22 @@ static void cx18_remove(struct pci_dev *pci_dev)
exit_cx18_i2c(cx);
- free_irq(cx->dev->irq, (void *)cx);
+ free_irq(cx->pci_dev->irq, (void *)cx);
cx18_iounmap(cx);
release_mem_region(cx->base_addr, CX18_MEM_SIZE);
- pci_disable_device(cx->dev);
+ pci_disable_device(cx->pci_dev);
+
+ if (cx->vbi.sliced_mpeg_data[0] != NULL)
+ for (i = 0; i < CX18_VBI_FRAMES; i++)
+ kfree(cx->vbi.sliced_mpeg_data[i]);
+
+ CX18_INFO("Removed %s\n", cx->card_name);
- CX18_INFO("Removed %s, card #%d\n", cx->card_name, cx->num);
+ v4l2_device_unregister(v4l2_dev);
+ kfree(cx);
}
/* define a pci_driver for card detection */
@@ -1138,8 +1117,6 @@ static int module_start(void)
{
printk(KERN_INFO "cx18: Start initialization, version %s\n", CX18_VERSION);
- memset(cx18_cards, 0, sizeof(cx18_cards));
-
/* Validate parameters */
if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) {
printk(KERN_ERR "cx18: Exiting, cx18_first_minor must be between 0 and %d\n",
@@ -1162,16 +1139,7 @@ static int module_start(void)
static void module_cleanup(void)
{
- int i;
-
pci_unregister_driver(&cx18_pci_driver);
-
- for (i = 0; i < cx18_cards_active; i++) {
- if (cx18_cards[i] == NULL)
- continue;
- kfree(cx18_cards[i]);
- }
-
}
module_init(module_start);
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index 0d2edebc39b4..ece4f281ef42 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -48,6 +48,7 @@
#include <linux/dvb/audio.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
#include <media/tuner.h>
#include "cx18-mailbox.h"
#include "cx18-av-core.h"
@@ -79,7 +80,7 @@
#define CX18_CARD_YUAN_MPC718 3 /* Yuan MPC718 */
#define CX18_CARD_CNXT_RAPTOR_PAL 4 /* Conexant Raptor PAL */
#define CX18_CARD_TOSHIBA_QOSMIO_DVBT 5 /* Toshiba Qosmio Interal DVB-T/Analog*/
-#define CX18_CARD_LEADTEK_PVR2100 6 /* Leadtek WinFast PVR2100 */
+#define CX18_CARD_LEADTEK_PVR2100 6 /* Leadtek WinFast PVR2100/DVR3100 H */
#define CX18_CARD_LAST 6
#define CX18_ENC_STREAM_TYPE_MPG 0
@@ -143,12 +144,12 @@
/* Flag to turn on high volume debugging */
#define CX18_DBGFLG_HIGHVOL (1 << 8)
-/* NOTE: extra space before comma in 'cx->num , ## args' is required for
+/* NOTE: extra space before comma in 'fmt , ## args' is required for
gcc-2.95, otherwise it won't compile. */
#define CX18_DEBUG(x, type, fmt, args...) \
do { \
if ((x) & cx18_debug) \
- printk(KERN_INFO "cx18-%d " type ": " fmt, cx->num , ## args); \
+ v4l2_info(&cx->v4l2_dev, " " type ": " fmt , ## args); \
} while (0)
#define CX18_DEBUG_WARN(fmt, args...) CX18_DEBUG(CX18_DBGFLG_WARN, "warning", fmt , ## args)
#define CX18_DEBUG_INFO(fmt, args...) CX18_DEBUG(CX18_DBGFLG_INFO, "info", fmt , ## args)
@@ -162,7 +163,7 @@
#define CX18_DEBUG_HIGH_VOL(x, type, fmt, args...) \
do { \
if (((x) & cx18_debug) && (cx18_debug & CX18_DBGFLG_HIGHVOL)) \
- printk(KERN_INFO "cx18%d " type ": " fmt, cx->num , ## args); \
+ v4l2_info(&cx->v4l2_dev, " " type ": " fmt , ## args); \
} while (0)
#define CX18_DEBUG_HI_WARN(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_WARN, "warning", fmt , ## args)
#define CX18_DEBUG_HI_INFO(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_INFO, "info", fmt , ## args)
@@ -174,9 +175,58 @@
#define CX18_DEBUG_HI_IRQ(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IRQ, "irq", fmt , ## args)
/* Standard kernel messages */
-#define CX18_ERR(fmt, args...) printk(KERN_ERR "cx18-%d: " fmt, cx->num , ## args)
-#define CX18_WARN(fmt, args...) printk(KERN_WARNING "cx18-%d: " fmt, cx->num , ## args)
-#define CX18_INFO(fmt, args...) printk(KERN_INFO "cx18-%d: " fmt, cx->num , ## args)
+#define CX18_ERR(fmt, args...) v4l2_err(&cx->v4l2_dev, fmt , ## args)
+#define CX18_WARN(fmt, args...) v4l2_warn(&cx->v4l2_dev, fmt , ## args)
+#define CX18_INFO(fmt, args...) v4l2_info(&cx->v4l2_dev, fmt , ## args)
+
+/* Messages for internal subdevs to use */
+#define CX18_DEBUG_DEV(x, dev, type, fmt, args...) \
+ do { \
+ if ((x) & cx18_debug) \
+ v4l2_info(dev, " " type ": " fmt , ## args); \
+ } while (0)
+#define CX18_DEBUG_WARN_DEV(dev, fmt, args...) \
+ CX18_DEBUG_DEV(CX18_DBGFLG_WARN, dev, "warning", fmt , ## args)
+#define CX18_DEBUG_INFO_DEV(dev, fmt, args...) \
+ CX18_DEBUG_DEV(CX18_DBGFLG_INFO, dev, "info", fmt , ## args)
+#define CX18_DEBUG_API_DEV(dev, fmt, args...) \
+ CX18_DEBUG_DEV(CX18_DBGFLG_API, dev, "api", fmt , ## args)
+#define CX18_DEBUG_DMA_DEV(dev, fmt, args...) \
+ CX18_DEBUG_DEV(CX18_DBGFLG_DMA, dev, "dma", fmt , ## args)
+#define CX18_DEBUG_IOCTL_DEV(dev, fmt, args...) \
+ CX18_DEBUG_DEV(CX18_DBGFLG_IOCTL, dev, "ioctl", fmt , ## args)
+#define CX18_DEBUG_FILE_DEV(dev, fmt, args...) \
+ CX18_DEBUG_DEV(CX18_DBGFLG_FILE, dev, "file", fmt , ## args)
+#define CX18_DEBUG_I2C_DEV(dev, fmt, args...) \
+ CX18_DEBUG_DEV(CX18_DBGFLG_I2C, dev, "i2c", fmt , ## args)
+#define CX18_DEBUG_IRQ_DEV(dev, fmt, args...) \
+ CX18_DEBUG_DEV(CX18_DBGFLG_IRQ, dev, "irq", fmt , ## args)
+
+#define CX18_DEBUG_HIGH_VOL_DEV(x, dev, type, fmt, args...) \
+ do { \
+ if (((x) & cx18_debug) && (cx18_debug & CX18_DBGFLG_HIGHVOL)) \
+ v4l2_info(dev, " " type ": " fmt , ## args); \
+ } while (0)
+#define CX18_DEBUG_HI_WARN_DEV(dev, fmt, args...) \
+ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_WARN, dev, "warning", fmt , ## args)
+#define CX18_DEBUG_HI_INFO_DEV(dev, fmt, args...) \
+ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_INFO, dev, "info", fmt , ## args)
+#define CX18_DEBUG_HI_API_DEV(dev, fmt, args...) \
+ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_API, dev, "api", fmt , ## args)
+#define CX18_DEBUG_HI_DMA_DEV(dev, fmt, args...) \
+ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_DMA, dev, "dma", fmt , ## args)
+#define CX18_DEBUG_HI_IOCTL_DEV(dev, fmt, args...) \
+ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_IOCTL, dev, "ioctl", fmt , ## args)
+#define CX18_DEBUG_HI_FILE_DEV(dev, fmt, args...) \
+ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_FILE, dev, "file", fmt , ## args)
+#define CX18_DEBUG_HI_I2C_DEV(dev, fmt, args...) \
+ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_I2C, dev, "i2c", fmt , ## args)
+#define CX18_DEBUG_HI_IRQ_DEV(dev, fmt, args...) \
+ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_IRQ, dev, "irq", fmt , ## args)
+
+#define CX18_ERR_DEV(dev, fmt, args...) v4l2_err(dev, fmt , ## args)
+#define CX18_WARN_DEV(dev, fmt, args...) v4l2_warn(dev, fmt , ## args)
+#define CX18_INFO_DEV(dev, fmt, args...) v4l2_info(dev, fmt , ## args)
/* Values for CX18_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */
#define MPEG_FRAME_TYPE_IFRAME 1
@@ -279,7 +329,7 @@ struct cx18_epu_work_order {
struct cx18_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 video_device *video_dev; /* NULL when stream not created */
struct cx18 *cx; /* for ease of use */
const char *name; /* name of the stream */
int type; /* stream type */
@@ -292,7 +342,6 @@ struct cx18_stream {
int dma; /* can be PCI_DMA_TODEVICE,
PCI_DMA_FROMDEVICE or
PCI_DMA_NONE */
- u64 dma_pts;
wait_queue_head_t waitq;
/* Buffer Stats */
@@ -318,59 +367,121 @@ struct cx18_open_id {
/* forward declaration of struct defined in cx18-cards.h */
struct cx18_card;
+/*
+ * A note about "sliced" VBI data as implemented in this driver:
+ *
+ * Currently we collect the sliced VBI in the form of Ancillary Data
+ * packets, inserted by the AV core decoder/digitizer/slicer in the
+ * horizontal blanking region of the VBI lines, in "raw" mode as far as
+ * the Encoder is concerned. We don't ever tell the Encoder itself
+ * to provide sliced VBI. (AV Core: sliced mode - Encoder: raw mode)
+ *
+ * We then process the ancillary data ourselves to send the sliced data
+ * to the user application directly or build up MPEG-2 private stream 1
+ * packets to splice into (only!) MPEG-2 PS streams for the user app.
+ *
+ * (That's how ivtv essentially does it.)
+ *
+ * The Encoder should be able to extract certain sliced VBI data for
+ * us and provide it in a separate stream or splice it into any type of
+ * MPEG PS or TS stream, but this isn't implemented yet.
+ */
+
+/*
+ * Number of "raw" VBI samples per horizontal line we tell the Encoder to
+ * grab from the decoder/digitizer/slicer output for raw or sliced VBI.
+ * It depends on the pixel clock and the horiz rate:
+ *
+ * (1/Fh)*(2*Fp) = Samples/line
+ * = 4 bytes EAV + Anc data in hblank + 4 bytes SAV + active samples
+ *
+ * Sliced VBI data is sent as ancillary data during horizontal blanking
+ * Raw VBI is sent as active video samples during vertcal blanking
+ *
+ * We use a BT.656 pxiel clock of 13.5 MHz and a BT.656 active line
+ * length of 720 pixels @ 4:2:2 sampling. Thus...
+ *
+ * For systems that use a 15.734 kHz horizontal rate, such as
+ * NTSC-M, PAL-M, PAL-60, and other 60 Hz/525 line systems, we have:
+ *
+ * (1/15.734 kHz) * 2 * 13.5 MHz = 1716 samples/line =
+ * 4 bytes SAV + 268 bytes anc data + 4 bytes SAV + 1440 active samples
+ *
+ * For systems that use a 15.625 kHz horizontal rate, such as
+ * PAL-B/G/H, PAL-I, SECAM-L and other 50 Hz/625 line systems, we have:
+ *
+ * (1/15.625 kHz) * 2 * 13.5 MHz = 1728 samples/line =
+ * 4 bytes SAV + 280 bytes anc data + 4 bytes SAV + 1440 active samples
+ */
+static const u32 vbi_active_samples = 1444; /* 4 byte SAV + 720 Y + 720 U/V */
+static const u32 vbi_hblank_samples_60Hz = 272; /* 4 byte EAV + 268 anc/fill */
+static const u32 vbi_hblank_samples_50Hz = 284; /* 4 byte EAV + 280 anc/fill */
#define CX18_VBI_FRAMES 32
-/* VBI data */
struct vbi_info {
- u32 enc_size;
- u32 frame;
- 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;
+ /* Current state of v4l2 VBI settings for this device */
struct v4l2_format in;
- /* convenience pointer to sliced struct in vbi_in union */
- struct v4l2_sliced_vbi_format *sliced_in;
- u32 service_set_in;
- int insert_mpeg;
+ struct v4l2_sliced_vbi_format *sliced_in; /* pointer to in.fmt.sliced */
+ u32 count; /* Count of VBI data lines: 60 Hz: 12 or 50 Hz: 18 */
+ u32 start[2]; /* First VBI data line per field: 10 & 273 or 6 & 318 */
- /* 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];
+ u32 frame; /* Count of VBI buffers/frames received from Encoder */
- /* 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.
+ /*
+ * Vars for creation and insertion of MPEG Private Stream 1 packets
+ * of sliced VBI data into an MPEG PS
+ */
- However, it seems that the data must be 1K aligned, so we have to
- pad the data until the 1 or 2 K boundary.
+ /* Boolean: create and insert Private Stream 1 packets into the PS */
+ int insert_mpeg;
+
+ /*
+ * Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines.
+ * Used in cx18-vbi.c only for collecting sliced data, and as a source
+ * during conversion of sliced VBI data into MPEG Priv Stream 1 packets.
+ * We don't need to save state here, but the array may have been a bit
+ * too big (2304 bytes) to alloc from the stack.
+ */
+ struct v4l2_sliced_vbi_data sliced_data[36];
- This pointer array will allocate 2049 bytes to store each VBI frame. */
+ /*
+ * A ring buffer of driver-generated MPEG-2 PS
+ * Program Pack/Private Stream 1 packets for sliced VBI data insertion
+ * into the MPEG PS stream.
+ *
+ * In each sliced_mpeg_data[] buffer is:
+ * 16 byte MPEG-2 PS Program Pack Header
+ * 16 byte MPEG-2 Private Stream 1 PES Header
+ * 4 byte magic number: "itv0" or "ITV0"
+ * 4 byte first field line mask, if "itv0"
+ * 4 byte second field line mask, if "itv0"
+ * 36 lines, if "ITV0"; or <36 lines, if "itv0"; of sliced VBI data
+ *
+ * Each line in the payload is
+ * 1 byte line header derived from the SDID (WSS, CC, VPS, etc.)
+ * 42 bytes of line data
+ *
+ * That's a maximum 1552 bytes of payload in the Private Stream 1 packet
+ * which is the payload size a PVR-350 (CX23415) MPEG decoder will
+ * accept for VBI data. So, including the headers, it's a maximum 1584
+ * bytes total.
+ */
+#define CX18_SLICED_MPEG_DATA_MAXSZ 1584
+ /* copy_vbi_buf() needs 8 temp bytes on the end for the worst case */
+#define CX18_SLICED_MPEG_DATA_BUFSZ (CX18_SLICED_MPEG_DATA_MAXSZ+8)
u8 *sliced_mpeg_data[CX18_VBI_FRAMES];
u32 sliced_mpeg_size[CX18_VBI_FRAMES];
- struct cx18_buffer sliced_mpeg_buf;
+
+ /* Count of Program Pack/Program Stream 1 packets inserted into PS */
u32 inserted_frame;
- u32 start[2], count;
- u32 raw_size;
- u32 sliced_size;
+ /*
+ * A dummy driver stream transfer buffer with a copy of the next
+ * sliced_mpeg_data[] buffer for output to userland apps.
+ * Only used in cx18-fileops.c, but its state needs to persist at times.
+ */
+ struct cx18_buffer sliced_mpeg_buf;
};
/* Per cx23418, per I2C bus private algo callback data */
@@ -383,16 +494,17 @@ struct cx18_i2c_algo_callback_data {
/* Struct to hold info about cx18 cards */
struct cx18 {
- int num; /* board number, -1 during init! */
- char name[8]; /* board name for printk and interrupts (e.g. 'cx180') */
- struct pci_dev *dev; /* PCI device */
+ int instance;
+ struct pci_dev *pci_dev;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_subdev *sd_av; /* A/V decoder/digitizer sub-device */
+ struct v4l2_subdev *sd_extmux; /* External multiplexer sub-dev */
+
const struct cx18_card *card; /* card information */
const char *card_name; /* full name of the card */
const struct cx18_card_tuner_i2c *card_i2c; /* i2c addresses to probe for tuner */
u8 is_50hz;
u8 is_60hz;
- u8 is_out_50hz;
- u8 is_out_60hz;
u8 nof_inputs; /* number of video inputs */
u8 nof_audio_inputs; /* number of audio inputs */
u16 buffer_id; /* buffer ID counter */
@@ -413,10 +525,7 @@ struct cx18 {
/* dualwatch */
unsigned long dualwatch_jiffies;
- u16 dualwatch_stereo_mode;
-
- /* Digitizer type */
- int digitizer; /* 0x00EF = saa7114 0x00FO = saa7115 0x0106 = mic */
+ u32 dualwatch_stereo_mode;
struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */
struct cx18_options options; /* User options */
@@ -426,7 +535,6 @@ struct cx18 {
unsigned long i_flags; /* global cx18 flags */
atomic_t ana_capturing; /* count number of active analog capture streams */
atomic_t tot_capturing; /* total count number of active capture streams */
- spinlock_t lock; /* lock access to this struct */
int search_pack_header;
int open_id; /* incremented each time an open occurs, used as
@@ -468,30 +576,30 @@ struct cx18 {
struct i2c_adapter i2c_adap[2];
struct i2c_algo_bit_data i2c_algo[2];
struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2];
- struct i2c_client i2c_client[2];
- struct mutex i2c_bus_lock[2];
- struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];
/* gpio */
u32 gpio_dir;
u32 gpio_val;
struct mutex gpio_lock;
+ struct v4l2_subdev sd_gpiomux;
+ struct v4l2_subdev sd_resetctrl;
/* v4l2 and User settings */
/* codec settings */
u32 audio_input;
u32 active_input;
- u32 active_output;
v4l2_std_id std;
v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */
};
+static inline struct cx18 *to_cx18(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct cx18, v4l2_dev);
+}
+
/* Globals */
-extern struct cx18 *cx18_cards[];
-extern int cx18_cards_active;
extern int cx18_first_minor;
-extern spinlock_t cx18_cards_lock;
/*==============Prototypes==================*/
@@ -511,4 +619,22 @@ static inline int cx18_raw_vbi(const struct cx18 *cx)
return cx->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE;
}
+/* Call the specified callback for all subdevs with a grp_id bit matching the
+ * mask in hw (if 0, then match them all). Ignore any errors. */
+#define cx18_call_hw(cx, hw, o, f, args...) \
+ __v4l2_device_call_subdevs(&(cx)->v4l2_dev, \
+ !(hw) || (sd->grp_id & (hw)), o, f , ##args)
+
+#define cx18_call_all(cx, o, f, args...) cx18_call_hw(cx, 0, o, f , ##args)
+
+/* Call the specified callback for all subdevs with a grp_id bit matching the
+ * mask in hw (if 0, then match them all). If the callback returns an error
+ * other than 0 or -ENOIOCTLCMD, then return with that error code. */
+#define cx18_call_hw_err(cx, hw, o, f, args...) \
+ __v4l2_device_call_subdevs_until_err( \
+ &(cx)->v4l2_dev, !(hw) || (sd->grp_id & (hw)), o, f , ##args)
+
+#define cx18_call_all_err(cx, o, f, args...) \
+ cx18_call_hw_err(cx, 0, o, f , ##args)
+
#endif /* CX18_DRIVER_H */
diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c
index bd5e6f3fd4d0..3b86f57cd15a 100644
--- a/drivers/media/video/cx18/cx18-dvb.c
+++ b/drivers/media/video/cx18/cx18-dvb.c
@@ -167,7 +167,7 @@ int cx18_dvb_register(struct cx18_stream *stream)
ret = dvb_register_adapter(&dvb->dvb_adapter,
CX18_DRIVER_NAME,
- THIS_MODULE, &cx->dev->dev, adapter_nr);
+ THIS_MODULE, &cx->pci_dev->dev, adapter_nr);
if (ret < 0)
goto err_out;
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
index 055f6e004b2d..4d7d6d5a7f86 100644
--- a/drivers/media/video/cx18/cx18-fileops.c
+++ b/drivers/media/video/cx18/cx18-fileops.c
@@ -128,15 +128,15 @@ static void cx18_release_stream(struct cx18_stream *s)
static void cx18_dualwatch(struct cx18 *cx)
{
struct v4l2_tuner vt;
- u16 new_bitmap;
- u16 new_stereo_mode;
- const u16 stereo_mask = 0x0300;
- const u16 dual = 0x0200;
+ u32 new_bitmap;
+ u32 new_stereo_mode;
+ const u32 stereo_mask = 0x0300;
+ const u32 dual = 0x0200;
u32 h;
new_stereo_mode = cx->params.audio_properties & stereo_mask;
memset(&vt, 0, sizeof(vt));
- cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, &vt);
+ cx18_call_all(cx, tuner, g_tuner, &vt);
if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 &&
(vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
new_stereo_mode = dual;
@@ -176,6 +176,8 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
*err = 0;
while (1) {
if (s->type == CX18_ENC_STREAM_TYPE_MPG) {
+ /* Process pending program info updates and pending
+ VBI data */
if (time_after(jiffies, cx->dualwatch_jiffies + msecs_to_jiffies(1000))) {
cx->dualwatch_jiffies = jiffies;
@@ -186,7 +188,6 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) {
/* byteswap and process VBI data */
cx18_process_vbi_data(cx, buf,
- s_vbi->dma_pts,
s_vbi->type);
cx18_stream_put_buf_fw(s_vbi, buf);
}
@@ -207,8 +208,7 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
cx18_buf_swap(buf);
else {
/* byteswap and process VBI data */
- cx18_process_vbi_data(cx, buf,
- s->dma_pts, s->type);
+ cx18_process_vbi_data(cx, buf, s->type);
}
return buf;
}
@@ -260,6 +260,20 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
len = ucount;
if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG &&
!cx18_raw_vbi(cx) && buf != &cx->vbi.sliced_mpeg_buf) {
+ /*
+ * Try to find a good splice point in the PS, just before
+ * an MPEG-2 Program Pack start code, and provide only
+ * up to that point to the user, so it's easy to insert VBI data
+ * the next time around.
+ */
+ /* FIXME - This only works for an MPEG-2 PS, not a TS */
+ /*
+ * An MPEG-2 Program Stream (PS) is a series of
+ * MPEG-2 Program Packs terminated by an
+ * MPEG Program End Code after the last Program Pack.
+ * A Program Pack may hold a PS System Header packet and any
+ * number of Program Elementary Stream (PES) Packets
+ */
const char *start = buf->buf + buf->readpos;
const char *p = start + 1;
const u8 *q;
@@ -267,38 +281,54 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
int stuffing, i;
while (start + len > p) {
+ /* Scan for a 0 to find a potential MPEG-2 start code */
q = memchr(p, 0, start + len - p);
if (q == NULL)
break;
p = q + 1;
+ /*
+ * Keep looking if not a
+ * MPEG-2 Pack header start code: 0x00 0x00 0x01 0xba
+ * or MPEG-2 video PES start code: 0x00 0x00 0x01 0xe0
+ */
if ((char *)q + 15 >= buf->buf + buf->bytesused ||
q[1] != 0 || q[2] != 1 || q[3] != ch)
continue;
+
+ /* If expecting the primary video PES */
if (!cx->search_pack_header) {
+ /* Continue if it couldn't be a PES packet */
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;
+ /* Check if a PTS or PTS & DTS follow */
+ if (((q[7] & 0xc0) == 0x80 && /* PTS only */
+ (q[9] & 0xf0) == 0x20) || /* PTS only */
+ ((q[7] & 0xc0) == 0xc0 && /* PTS & DTS */
+ (q[9] & 0xf0) == 0x30)) { /* DTS follows */
+ /* Assume we found the video PES hdr */
+ ch = 0xba; /* next want a Program Pack*/
cx->search_pack_header = 1;
- p = q + 9;
+ p = q + 9; /* Skip this video PES hdr */
}
continue;
}
+
+ /* We may have found a Program Pack start code */
+
+ /* Get the count of stuffing bytes & verify them */
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 &&
+ if (i == stuffing && /* right number of stuffing bytes*/
+ (q[4] & 0xc4) == 0x44 && /* marker check */
+ (q[12] & 3) == 3 && /* marker check */
+ q[14 + stuffing] == 0 && /* PES Pack or Sys Hdr */
q[15 + stuffing] == 0 &&
q[16 + stuffing] == 1) {
- cx->search_pack_header = 0;
+ /* We declare we actually found a Program Pack*/
+ cx->search_pack_header = 0; /* expect vid PES */
len = (char *)q - start;
cx18_setup_sliced_vbi_buf(cx);
break;
@@ -578,7 +608,7 @@ int cx18_v4l2_close(struct file *filp)
/* Mark that the radio is no longer in use */
clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags);
/* Switch tuner to TV */
- cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std);
+ cx18_call_all(cx, tuner, s_std, cx->std);
/* Select correct audio input (i.e. TV tuner or Line in) */
cx18_audio_set_io(cx);
if (atomic_read(&cx->ana_capturing) > 0) {
@@ -641,7 +671,7 @@ static int cx18_serialized_open(struct cx18_stream *s, struct file *filp)
/* We have the radio */
cx18_mute(cx);
/* Switch tuner to radio */
- cx18_call_i2c_clients(cx, AUDC_SET_RADIO, NULL);
+ cx18_call_all(cx, tuner, s_radio);
/* Select the correct audio input (i.e. radio tuner) */
cx18_audio_set_io(cx);
/* Done! Unmute and continue. */
@@ -652,38 +682,15 @@ static int cx18_serialized_open(struct cx18_stream *s, struct file *filp)
int cx18_v4l2_open(struct file *filp)
{
- int res, x, y = 0;
- struct cx18 *cx = NULL;
- struct cx18_stream *s = NULL;
- int minor = video_devdata(filp)->minor;
-
- /* Find which card this open was on */
- spin_lock(&cx18_cards_lock);
- for (x = 0; cx == NULL && x < cx18_cards_active; x++) {
- /* find out which stream this open was on */
- for (y = 0; y < CX18_MAX_STREAMS; y++) {
- if (cx18_cards[x] == NULL)
- continue;
- s = &cx18_cards[x]->streams[y];
- if (s->v4l2dev && s->v4l2dev->minor == minor) {
- cx = cx18_cards[x];
- break;
- }
- }
- }
- spin_unlock(&cx18_cards_lock);
-
- if (cx == NULL) {
- /* Couldn't find a device registered
- on that minor, shouldn't happen! */
- printk(KERN_WARNING "No cx18 device found on minor %d\n",
- minor);
- return -ENXIO;
- }
+ int res;
+ struct video_device *video_dev = video_devdata(filp);
+ struct cx18_stream *s = video_get_drvdata(video_dev);
+ struct cx18 *cx = s->cx;;
mutex_lock(&cx->serialize_lock);
if (cx18_init_on_first_open(cx)) {
- CX18_ERR("Failed to initialize on minor %d\n", minor);
+ CX18_ERR("Failed to initialize on minor %d\n",
+ video_dev->minor);
mutex_unlock(&cx->serialize_lock);
return -ENXIO;
}
diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c
index 1fa95da1575e..83cd559cc609 100644
--- a/drivers/media/video/cx18/cx18-firmware.c
+++ b/drivers/media/video/cx18/cx18-firmware.c
@@ -26,7 +26,6 @@
#include "cx18-irq.h"
#include "cx18-firmware.h"
#include "cx18-cards.h"
-#include "cx18-av-core.h"
#include <linux/firmware.h>
#define CX18_PROC_SOFT_RESET 0xc70010
@@ -107,7 +106,7 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx)
u32 __iomem *dst = (u32 __iomem *)mem;
const u32 *src;
- if (request_firmware(&fw, fn, &cx->dev->dev)) {
+ if (request_firmware(&fw, fn, &cx->pci_dev->dev)) {
CX18_ERR("Unable to open firmware %s\n", fn);
CX18_ERR("Did you put the firmware in the hotplug firmware directory?\n");
return -ENOMEM;
@@ -151,7 +150,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx,
u32 apu_version = 0;
int sz;
- if (request_firmware(&fw, fn, &cx->dev->dev)) {
+ if (request_firmware(&fw, fn, &cx->pci_dev->dev)) {
CX18_ERR("unable to open firmware %s\n", fn);
CX18_ERR("did you put the firmware in the hotplug firmware directory?\n");
cx18_setup_page(cx, 0);
@@ -286,23 +285,6 @@ void cx18_init_power(struct cx18 *cx, int lowpwr)
cx18_write_reg(cx, 0x2BE2FE, CX18_MPEG_CLOCK_PLL_FRAC);
cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST);
- /*
- * VDCLK Integer = 0x0f, Post Divider = 0x04
- * AIMCLK Integer = 0x0e, Post Divider = 0x16
- */
- cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f);
-
- /* VDCLK Fraction = 0x2be2fe */
- /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */
- cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe);
-
- /* AIMCLK Fraction = 0x05227ad */
- /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz before post-divide */
- cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad);
-
- /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
- cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56);
-
/* Defaults */
/* APU = SC or SC/2 = 125/62.5 */
/* EPU = SC = 125 */
diff --git a/drivers/media/video/cx18/cx18-gpio.c b/drivers/media/video/cx18/cx18-gpio.c
index 1a99329f33cb..5518d1424f8f 100644
--- a/drivers/media/video/cx18/cx18-gpio.c
+++ b/drivers/media/video/cx18/cx18-gpio.c
@@ -46,6 +46,9 @@
* gpio13: cs5345 reset pin
*/
+/*
+ * File scope utility functions
+ */
static void gpio_write(struct cx18 *cx)
{
u32 dir_lo = cx->gpio_dir & 0xffff;
@@ -63,73 +66,201 @@ static void gpio_write(struct cx18 *cx)
CX18_REG_GPIO_OUT2, val_hi, dir_hi);
}
-void cx18_reset_i2c_slaves_gpio(struct cx18 *cx)
+static void gpio_update(struct cx18 *cx, u32 mask, u32 data)
{
- const struct cx18_gpio_i2c_slave_reset *p;
+ if (mask == 0)
+ return;
- p = &cx->card->gpio_i2c_slave_reset;
+ mutex_lock(&cx->gpio_lock);
+ cx->gpio_val = (cx->gpio_val & ~mask) | (data & mask);
+ gpio_write(cx);
+ mutex_unlock(&cx->gpio_lock);
+}
+
+static void gpio_reset_seq(struct cx18 *cx, u32 active_lo, u32 active_hi,
+ unsigned int assert_msecs,
+ unsigned int recovery_msecs)
+{
+ u32 mask;
- if ((p->active_lo_mask | p->active_hi_mask) == 0)
+ mask = active_lo | active_hi;
+ if (mask == 0)
return;
- /* Assuming that the masks are a subset of the bits in gpio_dir */
+ /*
+ * Assuming that active_hi and active_lo are a subsets of the bits in
+ * gpio_dir. Also assumes that active_lo and active_hi don't overlap
+ * in any bit position
+ */
/* Assert */
- mutex_lock(&cx->gpio_lock);
- cx->gpio_val =
- (cx->gpio_val | p->active_hi_mask) & ~(p->active_lo_mask);
- gpio_write(cx);
- schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_asserted));
+ gpio_update(cx, mask, ~active_lo);
+ schedule_timeout_uninterruptible(msecs_to_jiffies(assert_msecs));
/* Deassert */
- cx->gpio_val =
- (cx->gpio_val | p->active_lo_mask) & ~(p->active_hi_mask);
- gpio_write(cx);
- schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery));
+ gpio_update(cx, mask, ~active_hi);
+ schedule_timeout_uninterruptible(msecs_to_jiffies(recovery_msecs));
+}
+
+/*
+ * GPIO Multiplexer - logical device
+ */
+static int gpiomux_log_status(struct v4l2_subdev *sd)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+ mutex_lock(&cx->gpio_lock);
+ CX18_INFO_DEV(sd, "GPIO: direction 0x%08x, value 0x%08x\n",
+ cx->gpio_dir, cx->gpio_val);
mutex_unlock(&cx->gpio_lock);
+ return 0;
}
-void cx18_reset_ir_gpio(void *data)
+static int gpiomux_s_radio(struct v4l2_subdev *sd)
{
- struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
- const struct cx18_gpio_i2c_slave_reset *p;
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
- p = &cx->card->gpio_i2c_slave_reset;
+ /*
+ * FIXME - work out the cx->active/audio_input mess - this is
+ * intended to handle the switch to radio mode and set the
+ * audio routing, but we need to update the state in cx
+ */
+ gpio_update(cx, cx->card->gpio_audio_input.mask,
+ cx->card->gpio_audio_input.radio);
+ return 0;
+}
- if (p->ir_reset_mask == 0)
- return;
+static int gpiomux_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ u32 data;
- CX18_DEBUG_INFO("Resetting IR microcontroller\n");
+ switch (cx->card->audio_inputs[cx->audio_input].muxer_input) {
+ case 1:
+ data = cx->card->gpio_audio_input.linein;
+ break;
+ case 0:
+ data = cx->card->gpio_audio_input.tuner;
+ break;
+ default:
+ /*
+ * FIXME - work out the cx->active/audio_input mess - this is
+ * intended to handle the switch from radio mode and set the
+ * audio routing, but we need to update the state in cx
+ */
+ data = cx->card->gpio_audio_input.tuner;
+ break;
+ }
+ gpio_update(cx, cx->card->gpio_audio_input.mask, data);
+ return 0;
+}
- /*
- Assert timing for the Z8F0811 on HVR-1600 boards:
- 1. Assert RESET for min of 4 clock cycles at 18.432 MHz to initiate
- 2. Reset then takes 66 WDT cycles at 10 kHz + 16 xtal clock cycles
- (6,601,085 nanoseconds ~= 7 milliseconds)
- 3. DBG pin must be high before chip exits reset for normal operation.
- DBG is open drain and hopefully pulled high since we don't
- normally drive it (GPIO 1?) for the HVR-1600
- 4. Z8F0811 won't exit reset until RESET is deasserted
- */
- mutex_lock(&cx->gpio_lock);
- cx->gpio_val = cx->gpio_val & ~p->ir_reset_mask;
- gpio_write(cx);
- mutex_unlock(&cx->gpio_lock);
- schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_asserted));
+static int gpiomux_s_audio_routing(struct v4l2_subdev *sd,
+ const struct v4l2_routing *route)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ u32 data;
+
+ switch (route->input) {
+ case 0:
+ data = cx->card->gpio_audio_input.tuner;
+ break;
+ case 1:
+ data = cx->card->gpio_audio_input.linein;
+ break;
+ case 2:
+ data = cx->card->gpio_audio_input.radio;
+ break;
+ default:
+ return -EINVAL;
+ }
+ gpio_update(cx, cx->card->gpio_audio_input.mask, data);
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops gpiomux_core_ops = {
+ .log_status = gpiomux_log_status,
+};
+
+static const struct v4l2_subdev_tuner_ops gpiomux_tuner_ops = {
+ .s_std = gpiomux_s_std,
+ .s_radio = gpiomux_s_radio,
+};
+
+static const struct v4l2_subdev_audio_ops gpiomux_audio_ops = {
+ .s_routing = gpiomux_s_audio_routing,
+};
+
+static const struct v4l2_subdev_ops gpiomux_ops = {
+ .core = &gpiomux_core_ops,
+ .tuner = &gpiomux_tuner_ops,
+ .audio = &gpiomux_audio_ops,
+};
+
+/*
+ * GPIO Reset Controller - logical device
+ */
+static int resetctrl_log_status(struct v4l2_subdev *sd)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
- /*
- Zilog comes out of reset, loads reset vector address and executes
- from there. Required recovery delay unknown.
- */
mutex_lock(&cx->gpio_lock);
- cx->gpio_val = cx->gpio_val | p->ir_reset_mask;
- gpio_write(cx);
+ CX18_INFO_DEV(sd, "GPIO: direction 0x%08x, value 0x%08x\n",
+ cx->gpio_dir, cx->gpio_val);
mutex_unlock(&cx->gpio_lock);
- schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery));
+ return 0;
}
-EXPORT_SYMBOL(cx18_reset_ir_gpio);
-/* This symbol is exported for use by an infrared module for the IR-blaster */
+static int resetctrl_reset(struct v4l2_subdev *sd, u32 val)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ const struct cx18_gpio_i2c_slave_reset *p;
+
+ p = &cx->card->gpio_i2c_slave_reset;
+ switch (val) {
+ case CX18_GPIO_RESET_I2C:
+ gpio_reset_seq(cx, p->active_lo_mask, p->active_hi_mask,
+ p->msecs_asserted, p->msecs_recovery);
+ break;
+ case CX18_GPIO_RESET_Z8F0811:
+ /*
+ * Assert timing for the Z8F0811 on HVR-1600 boards:
+ * 1. Assert RESET for min of 4 clock cycles at 18.432 MHz to
+ * initiate
+ * 2. Reset then takes 66 WDT cycles at 10 kHz + 16 xtal clock
+ * cycles (6,601,085 nanoseconds ~= 7 milliseconds)
+ * 3. DBG pin must be high before chip exits reset for normal
+ * operation. DBG is open drain and hopefully pulled high
+ * since we don't normally drive it (GPIO 1?) for the
+ * HVR-1600
+ * 4. Z8F0811 won't exit reset until RESET is deasserted
+ * 5. Zilog comes out of reset, loads reset vector address and
+ * executes from there. Required recovery delay unknown.
+ */
+ gpio_reset_seq(cx, p->ir_reset_mask, 0,
+ p->msecs_asserted, p->msecs_recovery);
+ break;
+ case CX18_GPIO_RESET_XC2028:
+ if (cx->card->tuners[0].tuner == TUNER_XC2028)
+ gpio_reset_seq(cx, (1 << cx->card->xceive_pin), 0,
+ 1, 1);
+ break;
+ }
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops resetctrl_core_ops = {
+ .log_status = resetctrl_log_status,
+ .reset = resetctrl_reset,
+};
+
+static const struct v4l2_subdev_ops resetctrl_ops = {
+ .core = &resetctrl_core_ops,
+};
+
+/*
+ * External entry points
+ */
void cx18_gpio_init(struct cx18 *cx)
{
mutex_lock(&cx->gpio_lock);
@@ -156,6 +287,49 @@ void cx18_gpio_init(struct cx18 *cx)
mutex_unlock(&cx->gpio_lock);
}
+int cx18_gpio_register(struct cx18 *cx, u32 hw)
+{
+ struct v4l2_subdev *sd;
+ const struct v4l2_subdev_ops *ops;
+ char *str;
+
+ switch (hw) {
+ case CX18_HW_GPIO_MUX:
+ sd = &cx->sd_gpiomux;
+ ops = &gpiomux_ops;
+ str = "gpio-mux";
+ break;
+ case CX18_HW_GPIO_RESET_CTRL:
+ sd = &cx->sd_resetctrl;
+ ops = &resetctrl_ops;
+ str = "gpio-reset-ctrl";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ v4l2_subdev_init(sd, ops);
+ v4l2_set_subdevdata(sd, cx);
+ snprintf(sd->name, sizeof(sd->name), "%s %s", cx->v4l2_dev.name, str);
+ sd->grp_id = hw;
+ return v4l2_device_register_subdev(&cx->v4l2_dev, sd);
+}
+
+void cx18_reset_ir_gpio(void *data)
+{
+ struct cx18 *cx = to_cx18((struct v4l2_device *)data);
+
+ if (cx->card->gpio_i2c_slave_reset.ir_reset_mask == 0)
+ return;
+
+ CX18_DEBUG_INFO("Resetting IR microcontroller\n");
+
+ v4l2_subdev_call(&cx->sd_resetctrl,
+ core, reset, CX18_GPIO_RESET_Z8F0811);
+}
+EXPORT_SYMBOL(cx18_reset_ir_gpio);
+/* This symbol is exported for use by lirc_pvr150 for the IR-blaster */
+
/* Xceive tuner reset function */
int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value)
{
@@ -163,56 +337,11 @@ int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value)
struct cx18_i2c_algo_callback_data *cb_data = algo->data;
struct cx18 *cx = cb_data->cx;
- if (cmd != XC2028_TUNER_RESET)
+ if (cmd != XC2028_TUNER_RESET ||
+ cx->card->tuners[0].tuner != TUNER_XC2028)
return 0;
- CX18_DEBUG_INFO("Resetting tuner\n");
- mutex_lock(&cx->gpio_lock);
- cx->gpio_val &= ~(1 << cx->card->xceive_pin);
- gpio_write(cx);
- mutex_unlock(&cx->gpio_lock);
- schedule_timeout_interruptible(msecs_to_jiffies(1));
-
- mutex_lock(&cx->gpio_lock);
- cx->gpio_val |= 1 << cx->card->xceive_pin;
- gpio_write(cx);
- mutex_unlock(&cx->gpio_lock);
- schedule_timeout_interruptible(msecs_to_jiffies(1));
- return 0;
-}
-
-int cx18_gpio(struct cx18 *cx, unsigned int command, void *arg)
-{
- struct v4l2_routing *route = arg;
- u32 mask, data;
-
- switch (command) {
- case VIDIOC_INT_S_AUDIO_ROUTING:
- if (route->input > 2)
- return -EINVAL;
- mask = cx->card->gpio_audio_input.mask;
- switch (route->input) {
- case 0:
- data = cx->card->gpio_audio_input.tuner;
- break;
- case 1:
- data = cx->card->gpio_audio_input.linein;
- break;
- case 2:
- default:
- data = cx->card->gpio_audio_input.radio;
- break;
- }
- break;
-
- default:
- return -EINVAL;
- }
- if (mask) {
- mutex_lock(&cx->gpio_lock);
- cx->gpio_val = (cx->gpio_val & ~mask) | (data & mask);
- gpio_write(cx);
- mutex_unlock(&cx->gpio_lock);
- }
- return 0;
+ CX18_DEBUG_INFO("Resetting XCeive tuner\n");
+ return v4l2_subdev_call(&cx->sd_resetctrl,
+ core, reset, CX18_GPIO_RESET_XC2028);
}
diff --git a/drivers/media/video/cx18/cx18-gpio.h b/drivers/media/video/cx18/cx18-gpio.h
index 39ffccc19d8a..f9a5ca3566af 100644
--- a/drivers/media/video/cx18/cx18-gpio.h
+++ b/drivers/media/video/cx18/cx18-gpio.h
@@ -22,7 +22,13 @@
*/
void cx18_gpio_init(struct cx18 *cx);
-void cx18_reset_i2c_slaves_gpio(struct cx18 *cx);
+int cx18_gpio_register(struct cx18 *cx, u32 hw);
+
+enum cx18_gpio_reset_type {
+ CX18_GPIO_RESET_I2C = 0,
+ CX18_GPIO_RESET_Z8F0811 = 1,
+ CX18_GPIO_RESET_XC2028 = 2,
+};
+
void cx18_reset_ir_gpio(void *data);
int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value);
-int cx18_gpio(struct cx18 *cx, unsigned int command, void *arg);
diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c
index 83e1c6333126..d092643faf46 100644
--- a/drivers/media/video/cx18/cx18-i2c.c
+++ b/drivers/media/video/cx18/cx18-i2c.c
@@ -26,7 +26,6 @@
#include "cx18-io.h"
#include "cx18-cards.h"
#include "cx18-gpio.h"
-#include "cx18-av-core.h"
#include "cx18-i2c.h"
#include "cx18-irq.h"
@@ -43,31 +42,37 @@
#define CX18_CS5345_I2C_ADDR 0x4c
/* This array should match the CX18_HW_ defines */
-static const u8 hw_driverids[] = {
- I2C_DRIVERID_TUNER,
- I2C_DRIVERID_TVEEPROM,
- I2C_DRIVERID_CS5345,
- 0, /* CX18_HW_GPIO dummy driver ID */
- 0 /* CX18_HW_CX23418 dummy driver ID */
-};
-
-/* This array should match the CX18_HW_ defines */
static const u8 hw_addrs[] = {
- 0,
- 0,
- CX18_CS5345_I2C_ADDR,
- 0, /* CX18_HW_GPIO dummy driver ID */
- 0, /* CX18_HW_CX23418 dummy driver ID */
+ 0, /* CX18_HW_TUNER */
+ 0, /* CX18_HW_TVEEPROM */
+ CX18_CS5345_I2C_ADDR, /* CX18_HW_CS5345 */
+ 0, /* CX18_HW_DVB */
+ 0, /* CX18_HW_418_AV */
+ 0, /* CX18_HW_GPIO_MUX */
+ 0, /* CX18_HW_GPIO_RESET_CTRL */
};
/* This array should match the CX18_HW_ defines */
/* This might well become a card-specific array */
static const u8 hw_bus[] = {
- 0,
- 0,
- 0,
- 0, /* CX18_HW_GPIO dummy driver ID */
- 0, /* CX18_HW_CX23418 dummy driver ID */
+ 1, /* CX18_HW_TUNER */
+ 0, /* CX18_HW_TVEEPROM */
+ 0, /* CX18_HW_CS5345 */
+ 0, /* CX18_HW_DVB */
+ 0, /* CX18_HW_418_AV */
+ 0, /* CX18_HW_GPIO_MUX */
+ 0, /* CX18_HW_GPIO_RESET_CTRL */
+};
+
+/* This array should match the CX18_HW_ defines */
+static const char * const hw_modules[] = {
+ "tuner", /* CX18_HW_TUNER */
+ NULL, /* CX18_HW_TVEEPROM */
+ "cs5345", /* CX18_HW_CS5345 */
+ NULL, /* CX18_HW_DVB */
+ NULL, /* CX18_HW_418_AV */
+ NULL, /* CX18_HW_GPIO_MUX */
+ NULL, /* CX18_HW_GPIO_RESET_CTRL */
};
/* This array should match the CX18_HW_ defines */
@@ -75,83 +80,67 @@ static const char * const hw_devicenames[] = {
"tuner",
"tveeprom",
"cs5345",
- "gpio",
- "cx23418",
+ "cx23418_DTV",
+ "cx23418_AV",
+ "gpio_mux",
+ "gpio_reset_ctrl",
};
int cx18_i2c_register(struct cx18 *cx, unsigned idx)
{
- struct i2c_board_info info;
- struct i2c_client *c;
- u8 id, bus;
- int i;
-
- CX18_DEBUG_I2C("i2c client register\n");
- if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0)
+ struct v4l2_subdev *sd;
+ int bus = hw_bus[idx];
+ struct i2c_adapter *adap = &cx->i2c_adap[bus];
+ const char *mod = hw_modules[idx];
+ const char *type = hw_devicenames[idx];
+ u32 hw = 1 << idx;
+
+ if (idx >= ARRAY_SIZE(hw_addrs))
return -1;
- id = hw_driverids[idx];
- bus = hw_bus[idx];
- memset(&info, 0, sizeof(info));
- strlcpy(info.type, hw_devicenames[idx], sizeof(info.type));
- info.addr = hw_addrs[idx];
- for (i = 0; i < I2C_CLIENTS_MAX; i++)
- if (cx->i2c_clients[i] == NULL)
- break;
-
- if (i == I2C_CLIENTS_MAX) {
- CX18_ERR("insufficient room for new I2C client!\n");
- return -ENOMEM;
- }
- if (id != I2C_DRIVERID_TUNER) {
- c = i2c_new_device(&cx->i2c_adap[bus], &info);
- if (c->driver == NULL)
- i2c_unregister_device(c);
- else
- cx->i2c_clients[i] = c;
- return cx->i2c_clients[i] ? 0 : -ENODEV;
+ if (hw == CX18_HW_TUNER) {
+ /* special tuner group handling */
+ sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
+ cx->card_i2c->radio);
+ if (sd != NULL)
+ sd->grp_id = hw;
+ sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
+ cx->card_i2c->demod);
+ if (sd != NULL)
+ sd->grp_id = hw;
+ sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
+ cx->card_i2c->tv);
+ if (sd != NULL)
+ sd->grp_id = hw;
+ return sd != NULL ? 0 : -1;
}
- /* special tuner handling */
- c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->radio);
- if (c && c->driver == NULL)
- i2c_unregister_device(c);
- else if (c)
- cx->i2c_clients[i++] = c;
- c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->demod);
- if (c && c->driver == NULL)
- i2c_unregister_device(c);
- else if (c)
- cx->i2c_clients[i++] = c;
- c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->tv);
- if (c && c->driver == NULL)
- i2c_unregister_device(c);
- else if (c)
- cx->i2c_clients[i++] = c;
- return 0;
-}
+ /* Is it not an I2C device or one we do not wish to register? */
+ if (!hw_addrs[idx])
+ return -1;
-static int attach_inform(struct i2c_client *client)
-{
- return 0;
+ /* It's an I2C device other than an analog tuner */
+ sd = v4l2_i2c_new_subdev(adap, mod, type, hw_addrs[idx]);
+ if (sd != NULL)
+ sd->grp_id = hw;
+ return sd != NULL ? 0 : -1;
}
-static int detach_inform(struct i2c_client *client)
+/* Find the first member of the subdev group id in hw */
+struct v4l2_subdev *cx18_find_hw(struct cx18 *cx, u32 hw)
{
- int i;
- struct cx18 *cx = (struct cx18 *)i2c_get_adapdata(client->adapter);
+ struct v4l2_subdev *result = NULL;
+ struct v4l2_subdev *sd;
- CX18_DEBUG_I2C("i2c client detach\n");
- for (i = 0; i < I2C_CLIENTS_MAX; i++) {
- if (cx->i2c_clients[i] == client) {
- cx->i2c_clients[i] = NULL;
+ spin_lock(&cx->v4l2_dev.lock);
+ v4l2_device_for_each_subdev(sd, &cx->v4l2_dev) {
+ if (sd->grp_id == hw) {
+ result = sd;
break;
}
}
- CX18_DEBUG_I2C("i2c detach [client=%s,%s]\n",
- client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed");
-
- return 0;
+ spin_unlock(&cx->v4l2_dev.lock);
+ return result;
}
static void cx18_setscl(void *data, int state)
@@ -204,8 +193,6 @@ static struct i2c_adapter cx18_i2c_adap_template = {
.id = I2C_HW_B_CX2341X,
.algo = NULL, /* set by i2c-algo-bit */
.algo_data = NULL, /* filled from template */
- .client_register = attach_inform,
- .client_unregister = detach_inform,
.owner = THIS_MODULE,
};
@@ -221,152 +208,28 @@ static struct i2c_algo_bit_data cx18_i2c_algo_template = {
.timeout = CX18_ALGO_BIT_TIMEOUT*HZ /* jiffies */
};
-static struct i2c_client cx18_i2c_client_template = {
- .name = "cx18 internal",
-};
-
-int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg)
-{
- struct i2c_client *client;
- int retval;
- int i;
-
- CX18_DEBUG_I2C("call_i2c_client addr=%02x\n", addr);
- for (i = 0; i < I2C_CLIENTS_MAX; i++) {
- client = cx->i2c_clients[i];
- if (client == NULL || client->driver == NULL ||
- client->driver->command == NULL)
- continue;
- if (addr == client->addr) {
- retval = client->driver->command(client, cmd, arg);
- return retval;
- }
- }
- if (cmd != VIDIOC_DBG_G_CHIP_IDENT)
- CX18_ERR("i2c addr 0x%02x not found for cmd 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. */
-static int cx18_i2c_id_addr(struct cx18 *cx, u32 id)
-{
- struct i2c_client *client;
- int retval = -ENODEV;
- int i;
-
- for (i = 0; i < I2C_CLIENTS_MAX; i++) {
- client = cx->i2c_clients[i];
- if (client == NULL || client->driver == NULL)
- continue;
- if (id == client->driver->id) {
- retval = client->addr;
- break;
- }
- }
- return retval;
-}
-
-/* Find the i2c device name matching the CX18_HW_ flag */
-static const char *cx18_i2c_hw_name(u32 hw)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
- if (1 << i == hw)
- return hw_devicenames[i];
- return "unknown device";
-}
-
-/* Find the i2c device matching the CX18_HW_ flag and return
- its i2c address or -ENODEV if no matching device was found. */
-int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
- if (1 << i == hw)
- return cx18_i2c_id_addr(cx, hw_driverids[i]);
- return -ENODEV;
-}
-
-/* Calls i2c device based on CX18_HW_ flag. If hw == 0, then do nothing.
- If hw == CX18_HW_GPIO then call the gpio handler. */
-int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg)
-{
- int addr;
-
- if (hw == 0)
- return 0;
-
- if (hw == CX18_HW_GPIO)
- return cx18_gpio(cx, cmd, arg);
-
- if (hw == CX18_HW_CX23418)
- return cx18_av_cmd(cx, cmd, arg);
-
- addr = cx18_i2c_hw_addr(cx, hw);
- if (addr < 0) {
- CX18_ERR("i2c hardware 0x%08x (%s) not found for cmd 0x%x!\n",
- hw, cx18_i2c_hw_name(hw), cmd);
- return addr;
- }
- return cx18_call_i2c_client(cx, addr, cmd, arg);
-}
-
-/* broadcast cmd for all I2C clients and for the gpio subsystem */
-void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg)
-{
- if (cx->i2c_adap[0].algo == NULL || cx->i2c_adap[1].algo == NULL) {
- CX18_ERR("adapter is not set\n");
- return;
- }
- cx18_av_cmd(cx, cmd, arg);
- i2c_clients_command(&cx->i2c_adap[0], cmd, arg);
- i2c_clients_command(&cx->i2c_adap[1], cmd, arg);
- if (cx->hw_flags & CX18_HW_GPIO)
- cx18_gpio(cx, cmd, arg);
-}
-
/* init + register i2c algo-bit adapter */
int init_cx18_i2c(struct cx18 *cx)
{
int i;
CX18_DEBUG_I2C("i2c init\n");
- /* Sanity checks for the I2C hardware arrays. They must be the
- * same size and GPIO/CX23418 must be the last entries.
- */
- if (ARRAY_SIZE(hw_driverids) != ARRAY_SIZE(hw_addrs) ||
- ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) ||
- CX18_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 2)) ||
- CX18_HW_CX23418 != (1 << (ARRAY_SIZE(hw_addrs) - 1)) ||
- hw_driverids[ARRAY_SIZE(hw_addrs) - 1]) {
- CX18_ERR("Mismatched I2C hardware arrays\n");
- return -ENODEV;
- }
-
for (i = 0; i < 2; i++) {
- memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template,
- sizeof(struct i2c_adapter));
+ /* Setup algorithm for adapter */
memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template,
sizeof(struct i2c_algo_bit_data));
cx->i2c_algo_cb_data[i].cx = cx;
cx->i2c_algo_cb_data[i].bus_index = i;
cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i];
- cx->i2c_adap[i].algo_data = &cx->i2c_algo[i];
+ /* Setup adapter */
+ memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template,
+ sizeof(struct i2c_adapter));
+ cx->i2c_adap[i].algo_data = &cx->i2c_algo[i];
sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name),
- " #%d-%d", cx->num, i);
- i2c_set_adapdata(&cx->i2c_adap[i], cx);
-
- memcpy(&cx->i2c_client[i], &cx18_i2c_client_template,
- sizeof(struct i2c_client));
- sprintf(cx->i2c_client[i].name +
- strlen(cx->i2c_client[i].name), "%d", i);
- cx->i2c_client[i].adapter = &cx->i2c_adap[i];
- cx->i2c_adap[i].dev.parent = &cx->dev->dev;
+ " #%d-%d", cx->instance, i);
+ i2c_set_adapdata(&cx->i2c_adap[i], &cx->v4l2_dev);
+ cx->i2c_adap[i].dev.parent = &cx->pci_dev->dev;
}
if (cx18_read_reg(cx, CX18_REG_I2C_2_WR) != 0x0003c02f) {
@@ -402,7 +265,8 @@ int init_cx18_i2c(struct cx18 *cx)
cx18_setscl(&cx->i2c_algo_cb_data[1], 1);
cx18_setsda(&cx->i2c_algo_cb_data[1], 1);
- cx18_reset_i2c_slaves_gpio(cx);
+ cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL,
+ core, reset, (u32) CX18_GPIO_RESET_I2C);
return i2c_bit_add_bus(&cx->i2c_adap[0]) ||
i2c_bit_add_bus(&cx->i2c_adap[1]);
diff --git a/drivers/media/video/cx18/cx18-i2c.h b/drivers/media/video/cx18/cx18-i2c.h
index 4869739013bd..bdfd1921e300 100644
--- a/drivers/media/video/cx18/cx18-i2c.h
+++ b/drivers/media/video/cx18/cx18-i2c.h
@@ -21,11 +21,8 @@
* 02111-1307 USA
*/
-int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw);
-int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg);
-int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg);
-void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg);
int cx18_i2c_register(struct cx18 *cx, unsigned idx);
+struct v4l2_subdev *cx18_find_hw(struct cx18 *cx, u32 hw);
/* init + register i2c algo-bit adapter */
int init_cx18_i2c(struct cx18 *cx);
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index 7086aaba77d6..e4c9e3d8bacd 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -58,12 +58,21 @@ u16 cx18_service2vbi(int type)
}
}
+/* Check if VBI services are allowed on the (field, line) for the video std */
static int valid_service_line(int field, int line, int is_pal)
{
- return (is_pal && line >= 6 && (line != 23 || field == 0)) ||
+ return (is_pal && line >= 6 &&
+ ((field == 0 && line <= 23) || (field == 1 && line <= 22))) ||
(!is_pal && line >= 10 && line < 22);
}
+/*
+ * For a (field, line, std) and inbound potential set of services for that line,
+ * return the first valid service of those passed in the incoming set for that
+ * line in priority order:
+ * CC, VPS, or WSS over TELETEXT for well known lines
+ * TELETEXT, before VPS, before CC, before WSS, for other lines
+ */
static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
{
u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);
@@ -90,6 +99,10 @@ static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
return 0;
}
+/*
+ * Expand the service_set of *fmt into valid service_lines for the std,
+ * and clear the passed in fmt->service_set
+ */
void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
{
u16 set = fmt->service_set;
@@ -102,7 +115,25 @@ void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
}
}
+/*
+ * Sanitize the service_lines in *fmt per the video std, and return 1
+ * if any service_line is left as valid after santization
+ */
+static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+ int f, l;
+ u16 set = 0;
+
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++) {
+ fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
+ set |= fmt->service_lines[f][l];
+ }
+ }
+ return set != 0;
+}
+/* Compute the service_set from the assumed valid service_lines of *fmt */
u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt)
{
int f, l;
@@ -129,10 +160,8 @@ static int cx18_g_fmt_vid_cap(struct file *file, void *fh,
pixfmt->priv = 0;
if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
pixfmt->pixelformat = V4L2_PIX_FMT_HM12;
- /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
- pixfmt->sizeimage =
- pixfmt->height * pixfmt->width +
- pixfmt->height * (pixfmt->width / 2);
+ /* YUV size is (Y=(h*720) + UV=(h*(720/2))) */
+ pixfmt->sizeimage = pixfmt->height * 720 * 3 / 2;
pixfmt->bytesperline = 720;
} else {
pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
@@ -149,8 +178,8 @@ static int cx18_g_fmt_vbi_cap(struct file *file, void *fh,
struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi;
vbifmt->sampling_rate = 27000000;
- vbifmt->offset = 248;
- vbifmt->samples_per_line = cx->vbi.raw_decoder_line_size - 4;
+ vbifmt->offset = 248; /* FIXME - slightly wrong for both 50 & 60 Hz */
+ vbifmt->samples_per_line = vbi_active_samples - 4;
vbifmt->sample_format = V4L2_PIX_FMT_GREY;
vbifmt->start[0] = cx->vbi.start[0];
vbifmt->start[1] = cx->vbi.start[1];
@@ -164,7 +193,30 @@ static int cx18_g_fmt_vbi_cap(struct file *file, void *fh,
static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh,
struct v4l2_format *fmt)
{
- return -EINVAL;
+ struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+ /* sane, V4L2 spec compliant, defaults */
+ vbifmt->reserved[0] = 0;
+ vbifmt->reserved[1] = 0;
+ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+ memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
+ vbifmt->service_set = 0;
+
+ /*
+ * Fetch the configured service_lines and total service_set from the
+ * digitizer/slicer. Note, cx18_av_vbi() wipes the passed in
+ * fmt->fmt.sliced under valid calling conditions
+ */
+ if (v4l2_subdev_call(cx->sd_av, video, g_fmt, fmt))
+ return -EINVAL;
+
+ /* Ensure V4L2 spec compliant output */
+ vbifmt->reserved[0] = 0;
+ vbifmt->reserved[1] = 0;
+ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+ vbifmt->service_set = cx18_get_service_set(vbifmt);
+ return 0;
}
static int cx18_try_fmt_vid_cap(struct file *file, void *fh,
@@ -174,11 +226,18 @@ static int cx18_try_fmt_vid_cap(struct file *file, void *fh,
struct cx18 *cx = id->cx;
int w = fmt->fmt.pix.width;
int h = fmt->fmt.pix.height;
+ int min_h = 2;
w = min(w, 720);
- w = max(w, 1);
+ w = max(w, 2);
+ if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
+ /* YUV height must be a multiple of 32 */
+ h &= ~0x1f;
+ min_h = 32;
+ }
h = min(h, cx->is_50hz ? 576 : 480);
- h = max(h, 2);
+ h = max(h, min_h);
+
cx18_g_fmt_vid_cap(file, fh, fmt);
fmt->fmt.pix.width = w;
fmt->fmt.pix.height = h;
@@ -194,7 +253,20 @@ static int cx18_try_fmt_vbi_cap(struct file *file, void *fh,
static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh,
struct v4l2_format *fmt)
{
- return -EINVAL;
+ struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+ vbifmt->reserved[0] = 0;
+ vbifmt->reserved[1] = 0;
+
+ /* If given a service set, expand it validly & clear passed in set */
+ if (vbifmt->service_set)
+ cx18_expand_service_set(vbifmt, cx->is_50hz);
+ /* Sanitize the service_lines, and compute the new set if any valid */
+ if (check_service_set(vbifmt, cx->is_50hz))
+ vbifmt->service_set = cx18_get_service_set(vbifmt);
+ return 0;
}
static int cx18_s_fmt_vid_cap(struct file *file, void *fh,
@@ -223,7 +295,7 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh,
cx->params.width = w;
cx->params.height = h;
- cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
+ v4l2_subdev_call(cx->sd_av, video, s_fmt, fmt);
return cx18_g_fmt_vid_cap(file, fh, fmt);
}
@@ -238,54 +310,131 @@ static int cx18_s_fmt_vbi_cap(struct file *file, void *fh,
if (ret)
return ret;
+ /*
+ * Changing the Encoder's Raw VBI parameters won't have any effect
+ * if any analog capture is ongoing
+ */
if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
return -EBUSY;
+ /*
+ * Set the digitizer registers for raw active VBI.
+ * Note cx18_av_vbi_wipes out alot of the passed in fmt under valid
+ * calling conditions
+ */
+ ret = v4l2_subdev_call(cx->sd_av, video, s_fmt, fmt);
+ if (ret)
+ return ret;
+
+ /* Store our new v4l2 (non-)sliced VBI state */
cx->vbi.sliced_in->service_set = 0;
cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
- cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
+
return cx18_g_fmt_vbi_cap(file, fh, fmt);
}
static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh,
struct v4l2_format *fmt)
{
- return -EINVAL;
+ struct cx18_open_id *id = fh;
+ struct cx18 *cx = id->cx;
+ int ret;
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+ ret = v4l2_prio_check(&cx->prio, &id->prio);
+ if (ret)
+ return ret;
+
+ cx18_try_fmt_sliced_vbi_cap(file, fh, fmt);
+
+ /*
+ * Changing the Encoder's Raw VBI parameters won't have any effect
+ * if any analog capture is ongoing
+ */
+ if (cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
+ return -EBUSY;
+
+ /*
+ * Set the service_lines requested in the digitizer/slicer registers.
+ * Note, cx18_av_vbi() wipes some "impossible" service lines in the
+ * passed in fmt->fmt.sliced under valid calling conditions
+ */
+ ret = v4l2_subdev_call(cx->sd_av, video, s_fmt, fmt);
+ if (ret)
+ return ret;
+ /* Store our current v4l2 sliced VBI settings */
+ cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+ memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in));
+ return 0;
}
static int cx18_g_chip_ident(struct file *file, void *fh,
struct v4l2_dbg_chip_ident *chip)
{
struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
+ int err = 0;
chip->ident = V4L2_IDENT_NONE;
chip->revision = 0;
- if (v4l2_chip_match_host(&chip->match)) {
- chip->ident = V4L2_IDENT_CX23418;
- return 0;
+ switch (chip->match.type) {
+ case V4L2_CHIP_MATCH_HOST:
+ switch (chip->match.addr) {
+ case 0:
+ chip->ident = V4L2_IDENT_CX23418;
+ chip->revision = cx18_read_reg(cx, 0xC72028);
+ break;
+ case 1:
+ /*
+ * The A/V decoder is always present, but in the rare
+ * case that the card doesn't have analog, we don't
+ * use it. We find it w/o using the cx->sd_av pointer
+ */
+ cx18_call_hw(cx, CX18_HW_418_AV,
+ core, g_chip_ident, chip);
+ break;
+ default:
+ /*
+ * Could return ident = V4L2_IDENT_UNKNOWN if we had
+ * other host chips at higher addresses, but we don't
+ */
+ err = -EINVAL; /* per V4L2 spec */
+ break;
+ }
+ break;
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ /* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */
+ cx18_call_all(cx, core, g_chip_ident, chip);
+ break;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ /*
+ * We could return V4L2_IDENT_UNKNOWN, but we don't do the work
+ * to look if a chip is at the address with no driver. That's a
+ * dangerous thing to do with EEPROMs anyway.
+ */
+ cx18_call_all(cx, core, g_chip_ident, chip);
+ break;
+ default:
+ err = -EINVAL;
+ break;
}
- cx18_call_i2c_clients(cx, VIDIOC_DBG_G_CHIP_IDENT, chip);
- return 0;
+ return err;
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg)
{
struct v4l2_dbg_register *regs = arg;
- unsigned long flags;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (regs->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE)
return -EINVAL;
- spin_lock_irqsave(&cx18_cards_lock, flags);
regs->size = 4;
- if (cmd == VIDIOC_DBG_G_REGISTER)
- regs->val = cx18_read_enc(cx, regs->reg);
- else
+ if (cmd == VIDIOC_DBG_S_REGISTER)
cx18_write_enc(cx, regs->val, regs->reg);
- spin_unlock_irqrestore(&cx18_cards_lock, flags);
+ else
+ regs->val = cx18_read_enc(cx, regs->reg);
return 0;
}
@@ -296,7 +445,8 @@ static int cx18_g_register(struct file *file, void *fh,
if (v4l2_chip_match_host(&reg->match))
return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg);
- cx18_call_i2c_clients(cx, VIDIOC_DBG_G_REGISTER, reg);
+ /* FIXME - errors shouldn't be ignored */
+ cx18_call_all(cx, core, g_register, reg);
return 0;
}
@@ -307,7 +457,8 @@ static int cx18_s_register(struct file *file, void *fh,
if (v4l2_chip_match_host(&reg->match))
return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg);
- cx18_call_i2c_clients(cx, VIDIOC_DBG_S_REGISTER, reg);
+ /* FIXME - errors shouldn't be ignored */
+ cx18_call_all(cx, core, s_register, reg);
return 0;
}
#endif
@@ -335,7 +486,8 @@ static int cx18_querycap(struct file *file, void *fh,
strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver));
strlcpy(vcap->card, cx->card_name, sizeof(vcap->card));
- snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(cx->dev));
+ snprintf(vcap->bus_info, sizeof(vcap->bus_info),
+ "PCI:%s", pci_name(cx->pci_dev));
vcap->version = CX18_DRIVER_VERSION; /* version */
vcap->capabilities = cx->v4l2_cap; /* capabilities */
return 0;
@@ -403,7 +555,8 @@ static int cx18_s_crop(struct file *file, void *fh, struct v4l2_crop *crop)
if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- return cx18_av_cmd(cx, VIDIOC_S_CROP, crop);
+ CX18_DEBUG_WARN("VIDIOC_S_CROP not implemented\n");
+ return -EINVAL;
}
static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
@@ -412,7 +565,8 @@ static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- return cx18_av_cmd(cx, VIDIOC_G_CROP, crop);
+ CX18_DEBUG_WARN("VIDIOC_G_CROP not implemented\n");
+ return -EINVAL;
}
static int cx18_enum_fmt_vid_cap(struct file *file, void *fh,
@@ -483,7 +637,7 @@ static int cx18_g_frequency(struct file *file, void *fh,
if (vf->tuner != 0)
return -EINVAL;
- cx18_call_i2c_clients(cx, VIDIOC_G_FREQUENCY, vf);
+ cx18_call_all(cx, tuner, g_frequency, vf);
return 0;
}
@@ -502,7 +656,7 @@ int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
cx18_mute(cx);
CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);
- cx18_call_i2c_clients(cx, VIDIOC_S_FREQUENCY, vf);
+ cx18_call_all(cx, tuner, s_frequency, vf);
cx18_unmute(cx);
return 0;
}
@@ -547,12 +701,11 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std)
cx->vbi.count = cx->is_50hz ? 18 : 12;
cx->vbi.start[0] = cx->is_50hz ? 6 : 10;
cx->vbi.start[1] = cx->is_50hz ? 318 : 273;
- cx->vbi.sliced_decoder_line_size = cx->is_60hz ? 272 : 284;
CX18_DEBUG_INFO("Switching standard to %llx.\n",
(unsigned long long) cx->std);
/* Tuner */
- cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std);
+ cx18_call_all(cx, tuner, s_std, cx->std);
return 0;
}
@@ -569,9 +722,7 @@ static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
if (vt->index != 0)
return -EINVAL;
- /* Setting tuner can only set audio mode */
- cx18_call_i2c_clients(cx, VIDIOC_S_TUNER, vt);
-
+ cx18_call_all(cx, tuner, s_tuner, vt);
return 0;
}
@@ -582,7 +733,7 @@ static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
if (vt->index != 0)
return -EINVAL;
- cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, vt);
+ cx18_call_all(cx, tuner, g_tuner, vt);
if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name));
@@ -598,7 +749,30 @@ static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
static int cx18_g_sliced_vbi_cap(struct file *file, void *fh,
struct v4l2_sliced_vbi_cap *cap)
{
- return -EINVAL;
+ struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
+ int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
+ int f, l;
+
+ if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
+
+ cap->service_set = 0;
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++) {
+ if (valid_service_line(f, l, cx->is_50hz)) {
+ /*
+ * We can find all v4l2 supported vbi services
+ * for the standard, on a valid line for the std
+ */
+ cap->service_lines[f][l] = set;
+ cap->service_set |= set;
+ } else
+ cap->service_lines[f][l] = 0;
+ }
+ }
+ for (f = 0; f < 3; f++)
+ cap->reserved[f] = 0;
+ return 0;
}
static int cx18_g_enc_index(struct file *file, void *fh,
@@ -708,13 +882,15 @@ static int cx18_log_status(struct file *file, void *fh)
struct v4l2_audio audin;
int i;
- CX18_INFO("================= START STATUS CARD #%d =================\n", cx->num);
+ CX18_INFO("================= START STATUS CARD #%d "
+ "=================\n", cx->instance);
+ CX18_INFO("Version: %s Card: %s\n", CX18_VERSION, cx->card_name);
if (cx->hw_flags & CX18_HW_TVEEPROM) {
struct tveeprom tv;
cx18_read_eeprom(cx, &tv);
}
- cx18_call_i2c_clients(cx, VIDIOC_LOG_STATUS, NULL);
+ cx18_call_all(cx, core, log_status);
cx18_get_input(cx, cx->active_input, &vidin);
cx18_get_audio_input(cx, cx->audio_input, &audin);
CX18_INFO("Video Input: %s\n", vidin.name);
@@ -725,12 +901,12 @@ static int cx18_log_status(struct file *file, void *fh)
mutex_unlock(&cx->gpio_lock);
CX18_INFO("Tuner: %s\n",
test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ? "Radio" : "TV");
- cx2341x_log_status(&cx->params, cx->name);
+ cx2341x_log_status(&cx->params, cx->v4l2_dev.name);
CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags);
for (i = 0; i < CX18_MAX_STREAMS; i++) {
struct cx18_stream *s = &cx->streams[i];
- if (s->v4l2dev == NULL || s->buffers == 0)
+ if (s->video_dev == NULL || s->buffers == 0)
continue;
CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
s->name, s->s_flags,
@@ -740,7 +916,8 @@ static int cx18_log_status(struct file *file, void *fh)
CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
(long long)cx->mpg_data_received,
(long long)cx->vbi_data_inserted);
- CX18_INFO("================== END STATUS CARD #%d ==================\n", cx->num);
+ CX18_INFO("================== END STATUS CARD #%d "
+ "==================\n", cx->instance);
return 0;
}
@@ -754,7 +931,8 @@ static long cx18_default(struct file *file, void *fh, int cmd, void *arg)
CX18_DEBUG_IOCTL("VIDIOC_INT_S_AUDIO_ROUTING(%d, %d)\n",
route->input, route->output);
- cx18_audio_set_route(cx, route);
+ cx18_call_hw(cx, cx->card->hw_audio_ctrl, audio, s_routing,
+ route);
break;
}
@@ -762,7 +940,8 @@ static long cx18_default(struct file *file, void *fh, int cmd, void *arg)
u32 val = *(u32 *)arg;
if ((val == 0) || (val & 0x01))
- cx18_reset_ir_gpio(&cx->i2c_algo_cb_data[0]);
+ cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, core, reset,
+ (u32) CX18_GPIO_RESET_Z8F0811);
break;
}
@@ -782,6 +961,8 @@ long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd,
mutex_lock(&cx->serialize_lock);
+ /* FIXME - consolidate v4l2_prio_check()'s here */
+
if (cx18_debug & CX18_DBGFLG_IOCTL)
vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
res = video_ioctl2(filp, cmd, arg);
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
index de5e723fdf44..2226e5791e99 100644
--- a/drivers/media/video/cx18/cx18-mailbox.c
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -83,6 +83,8 @@ static const struct cx18_api_info api_info[] = {
API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0),
API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST),
API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW),
+ API_ENTRY(APU, CX18_APU_START, 0),
+ API_ENTRY(APU, CX18_APU_STOP, 0),
API_ENTRY(APU, CX18_APU_RESETAI, 0),
API_ENTRY(CPU, CX18_CPU_DEBUG_PEEK32, 0),
API_ENTRY(0, 0, 0),
@@ -98,21 +100,30 @@ static const struct cx18_api_info *find_api_info(u32 cmd)
return NULL;
}
-static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name)
+/* Call with buf of n*11+1 bytes */
+static char *u32arr2hex(u32 data[], int n, char *buf)
{
- char argstr[MAX_MB_ARGUMENTS*11+1];
char *p;
int i;
+ for (i = 0, p = buf; i < n; i++, p += 11) {
+ /* kernel snprintf() appends '\0' always */
+ snprintf(p, 12, " %#010x", data[i]);
+ }
+ *p = '\0';
+ return buf;
+}
+
+static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name)
+{
+ char argstr[MAX_MB_ARGUMENTS*11+1];
+
if (!(cx18_debug & CX18_DBGFLG_API))
return;
- for (i = 0, p = argstr; i < MAX_MB_ARGUMENTS; i++, p += 11) {
- /* kernel snprintf() appends '\0' always */
- snprintf(p, 12, " %#010x", mb->args[i]);
- }
CX18_DEBUG_API("%s: req %#010x ack %#010x cmd %#010x err %#010x args%s"
- "\n", name, mb->request, mb->ack, mb->cmd, mb->error, argstr);
+ "\n", name, mb->request, mb->ack, mb->cmd, mb->error,
+ u32arr2hex(mb->args, MAX_MB_ARGUMENTS, argstr));
}
@@ -439,7 +450,8 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu)
"incoming %s to EPU mailbox (sequence no. %u)"
"\n",
rpu_str[rpu], rpu_str[rpu], order_mb->request);
- dump_mb(cx, order_mb, "incoming");
+ if (cx18_debug & CX18_DBGFLG_WARN)
+ dump_mb(cx, order_mb, "incoming");
order->flags = CX18_F_EWO_MB_STALE_UPON_RECEIPT;
}
@@ -468,16 +480,24 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
struct mutex *mb_lock;
long int timeout, ret;
int i;
+ char argstr[MAX_MB_ARGUMENTS*11+1];
if (info == NULL) {
CX18_WARN("unknown cmd %x\n", cmd);
return -EINVAL;
}
- if (cmd == CX18_CPU_DE_SET_MDL)
- CX18_DEBUG_HI_API("%s\n", info->name);
- else
- CX18_DEBUG_API("%s\n", info->name);
+ if (cx18_debug & CX18_DBGFLG_API) { /* only call u32arr2hex if needed */
+ if (cmd == CX18_CPU_DE_SET_MDL) {
+ if (cx18_debug & CX18_DBGFLG_HIGHVOL)
+ CX18_DEBUG_HI_API("%s\tcmd %#010x args%s\n",
+ info->name, cmd,
+ u32arr2hex(data, args, argstr));
+ } else
+ CX18_DEBUG_API("%s\tcmd %#010x args%s\n",
+ info->name, cmd,
+ u32arr2hex(data, args, argstr));
+ }
switch (info->rpu) {
case APU:
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c
index 8d9441e88c4e..3046b8e74345 100644
--- a/drivers/media/video/cx18/cx18-queue.c
+++ b/drivers/media/video/cx18/cx18-queue.c
@@ -204,7 +204,7 @@ int cx18_stream_alloc(struct cx18_stream *s)
}
buf->id = cx->buffer_id++;
INIT_LIST_HEAD(&buf->list);
- buf->dma_handle = pci_map_single(s->cx->dev,
+ buf->dma_handle = pci_map_single(s->cx->pci_dev,
buf->buf, s->buf_size, s->dma);
cx18_buf_sync_for_cpu(s, buf);
cx18_enqueue(s, buf, &s->q_free);
@@ -227,7 +227,7 @@ void cx18_stream_free(struct cx18_stream *s)
/* empty q_free */
while ((buf = cx18_dequeue(s, &s->q_free))) {
- pci_unmap_single(s->cx->dev, buf->dma_handle,
+ pci_unmap_single(s->cx->pci_dev, buf->dma_handle,
s->buf_size, s->dma);
kfree(buf->buf);
kfree(buf);
diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h
index 456cec3bc28f..4de06269d88f 100644
--- a/drivers/media/video/cx18/cx18-queue.h
+++ b/drivers/media/video/cx18/cx18-queue.h
@@ -29,14 +29,14 @@
static inline void cx18_buf_sync_for_cpu(struct cx18_stream *s,
struct cx18_buffer *buf)
{
- pci_dma_sync_single_for_cpu(s->cx->dev, buf->dma_handle,
+ pci_dma_sync_single_for_cpu(s->cx->pci_dev, buf->dma_handle,
s->buf_size, s->dma);
}
static inline void cx18_buf_sync_for_device(struct cx18_stream *s,
struct cx18_buffer *buf)
{
- pci_dma_sync_single_for_device(s->cx->dev, buf->dma_handle,
+ pci_dma_sync_single_for_device(s->cx->pci_dev, buf->dma_handle,
s->buf_size, s->dma);
}
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index 89c1ec94f335..0932b76b2373 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -32,7 +32,6 @@
#include "cx18-streams.h"
#include "cx18-cards.h"
#include "cx18-scb.h"
-#include "cx18-av-core.h"
#include "cx18-dvb.h"
#define CX18_DSP0_INTERRUPT_MASK 0xd0004C
@@ -101,11 +100,11 @@ static struct {
static void cx18_stream_init(struct cx18 *cx, int type)
{
struct cx18_stream *s = &cx->streams[type];
- struct video_device *dev = s->v4l2dev;
+ struct video_device *video_dev = s->video_dev;
- /* we need to keep v4l2dev, so restore it afterwards */
+ /* we need to keep video_dev, so restore it afterwards */
memset(s, 0, sizeof(*s));
- s->v4l2dev = dev;
+ s->video_dev = video_dev;
/* initialize cx18_stream fields */
s->cx = cx;
@@ -130,12 +129,12 @@ static int cx18_prep_dev(struct cx18 *cx, int type)
struct cx18_stream *s = &cx->streams[type];
u32 cap = cx->v4l2_cap;
int num_offset = cx18_stream_info[type].num_offset;
- int num = cx->num + cx18_first_minor + num_offset;
+ int num = cx->instance + cx18_first_minor + num_offset;
- /* These four fields are always initialized. If v4l2dev == NULL, then
+ /* These four fields are always initialized. If video_dev == NULL, then
this stream is not in use. In that case no other fields but these
four can be used. */
- s->v4l2dev = NULL;
+ s->video_dev = NULL;
s->cx = cx;
s->type = type;
s->name = cx18_stream_info[type].name;
@@ -163,22 +162,22 @@ static int cx18_prep_dev(struct cx18 *cx, int type)
return 0;
/* allocate and initialize the v4l2 video device structure */
- s->v4l2dev = video_device_alloc();
- if (s->v4l2dev == NULL) {
+ s->video_dev = video_device_alloc();
+ if (s->video_dev == NULL) {
CX18_ERR("Couldn't allocate v4l2 video_device for %s\n",
s->name);
return -ENOMEM;
}
- snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "cx18-%d",
- cx->num);
+ snprintf(s->video_dev->name, sizeof(s->video_dev->name), "%s %s",
+ cx->v4l2_dev.name, s->name);
- s->v4l2dev->num = num;
- s->v4l2dev->parent = &cx->dev->dev;
- s->v4l2dev->fops = &cx18_v4l2_enc_fops;
- s->v4l2dev->release = video_device_release;
- s->v4l2dev->tvnorms = V4L2_STD_ALL;
- cx18_set_funcs(s->v4l2dev);
+ s->video_dev->num = num;
+ s->video_dev->v4l2_dev = &cx->v4l2_dev;
+ s->video_dev->fops = &cx18_v4l2_enc_fops;
+ s->video_dev->release = video_device_release;
+ s->video_dev->tvnorms = V4L2_STD_ALL;
+ cx18_set_funcs(s->video_dev);
return 0;
}
@@ -227,28 +226,30 @@ static int cx18_reg_dev(struct cx18 *cx, int type)
}
}
- if (s->v4l2dev == NULL)
+ if (s->video_dev == NULL)
return 0;
- num = s->v4l2dev->num;
+ num = s->video_dev->num;
/* card number + user defined offset + device offset */
if (type != CX18_ENC_STREAM_TYPE_MPG) {
struct cx18_stream *s_mpg = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
- if (s_mpg->v4l2dev)
- num = s_mpg->v4l2dev->num + cx18_stream_info[type].num_offset;
+ if (s_mpg->video_dev)
+ num = s_mpg->video_dev->num
+ + cx18_stream_info[type].num_offset;
}
+ video_set_drvdata(s->video_dev, s);
/* Register device. First try the desired minor, then any free one. */
- ret = video_register_device(s->v4l2dev, vfl_type, num);
+ ret = video_register_device(s->video_dev, vfl_type, num);
if (ret < 0) {
CX18_ERR("Couldn't register v4l2 device for %s kernel number %d\n",
s->name, num);
- video_device_release(s->v4l2dev);
- s->v4l2dev = NULL;
+ video_device_release(s->video_dev);
+ s->video_dev = NULL;
return ret;
}
- num = s->v4l2dev->num;
+ num = s->video_dev->num;
switch (vfl_type) {
case VFL_TYPE_GRABBER:
@@ -312,9 +313,9 @@ void cx18_streams_cleanup(struct cx18 *cx, int unregister)
cx->streams[type].dvb.enabled = false;
}
- vdev = cx->streams[type].v4l2dev;
+ vdev = cx->streams[type].video_dev;
- cx->streams[type].v4l2dev = NULL;
+ cx->streams[type].video_dev = NULL;
if (vdev == NULL)
continue;
@@ -346,46 +347,88 @@ static void cx18_vbi_setup(struct cx18_stream *s)
}
/* setup VBI registers */
- cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in);
-
- /* determine number of lines and total number of VBI bytes.
- A raw line takes 1444 bytes: 4 byte SAV code + 2 * 720
- A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal
- header, 42 data bytes + checksum (to be confirmed) */
+ v4l2_subdev_call(cx->sd_av, video, s_fmt, &cx->vbi.in);
+
+ /*
+ * Send the CX18_CPU_SET_RAW_VBI_PARAM API command to setup Encoder Raw
+ * VBI when the first analog capture channel starts, as once it starts
+ * (e.g. MPEG), we can't effect any change in the Encoder Raw VBI setup
+ * (i.e. for the VBI capture channels). We also send it for each
+ * analog capture channel anyway just to make sure we get the proper
+ * behavior
+ */
if (raw) {
lines = cx->vbi.count * 2;
} else {
- lines = cx->is_60hz ? 24 : 38;
- if (cx->is_60hz)
- lines += 2;
+ /*
+ * For 525/60 systems, according to the VIP 2 & BT.656 std:
+ * The EAV RP code's Field bit toggles on line 4, a few lines
+ * after the Vertcal Blank bit has already toggled.
+ * Tell the encoder to capture 21-4+1=18 lines per field,
+ * since we want lines 10 through 21.
+ *
+ * FIXME - revisit for 625/50 systems
+ */
+ lines = cx->is_60hz ? (21 - 4 + 1) * 2 : 38;
}
- cx->vbi.enc_size = lines *
- (raw ? cx->vbi.raw_size : cx->vbi.sliced_size);
-
data[0] = s->handle;
/* Lines per field */
data[1] = (lines / 2) | ((lines / 2) << 16);
/* bytes per line */
- data[2] = (raw ? cx->vbi.raw_decoder_line_size
- : cx->vbi.sliced_decoder_line_size);
+ data[2] = (raw ? vbi_active_samples
+ : (cx->is_60hz ? vbi_hblank_samples_60Hz
+ : vbi_hblank_samples_50Hz));
/* Every X number of frames a VBI interrupt arrives
(frames as in 25 or 30 fps) */
data[3] = 1;
- /* Setup VBI for the cx25840 digitizer */
+ /*
+ * Set the SAV/EAV RP codes to look for as start/stop points
+ * when in VIP-1.1 mode
+ */
if (raw) {
+ /*
+ * Start codes for beginning of "active" line in vertical blank
+ * 0x20 ( VerticalBlank )
+ * 0x60 ( EvenField VerticalBlank )
+ */
data[4] = 0x20602060;
+ /*
+ * End codes for end of "active" raw lines and regular lines
+ * 0x30 ( VerticalBlank HorizontalBlank)
+ * 0x70 ( EvenField VerticalBlank HorizontalBlank)
+ * 0x90 (Task HorizontalBlank)
+ * 0xd0 (Task EvenField HorizontalBlank)
+ */
data[5] = 0x307090d0;
} else {
+ /*
+ * End codes for active video, we want data in the hblank region
+ * 0xb0 (Task 0 VerticalBlank HorizontalBlank)
+ * 0xf0 (Task EvenField VerticalBlank HorizontalBlank)
+ *
+ * Since the V bit is only allowed to toggle in the EAV RP code,
+ * just before the first active region line, these two
+ * are problematic:
+ * 0x90 (Task HorizontalBlank)
+ * 0xd0 (Task EvenField HorizontalBlank)
+ *
+ * We have set the digitzer such that we don't have to worry
+ * about these problem codes.
+ */
data[4] = 0xB0F0B0F0;
+ /*
+ * Start codes for beginning of active line in vertical blank
+ * 0xa0 (Task VerticalBlank )
+ * 0xe0 (Task EvenField VerticalBlank )
+ */
data[5] = 0xA0E0A0E0;
}
CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n",
data[0], data[1], data[2], data[3], data[4], data[5]);
- if (s->type == CX18_ENC_STREAM_TYPE_VBI)
- cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data);
+ cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data);
}
struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s,
@@ -434,10 +477,10 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
u32 data[MAX_MB_ARGUMENTS];
struct cx18 *cx = s->cx;
struct cx18_buffer *buf;
- int ts = 0;
int captype = 0;
+ struct cx18_api_func_private priv;
- if (s->v4l2dev == NULL && s->dvb.enabled == 0)
+ if (s->video_dev == NULL && s->dvb.enabled == 0)
return -EINVAL;
CX18_DEBUG_INFO("Start encoder stream %s\n", s->name);
@@ -453,7 +496,6 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
case CX18_ENC_STREAM_TYPE_TS:
captype = CAPTURE_CHANNEL_TYPE_TS;
- ts = 1;
break;
case CX18_ENC_STREAM_TYPE_YUV:
captype = CAPTURE_CHANNEL_TYPE_YUV;
@@ -462,8 +504,16 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
captype = CAPTURE_CHANNEL_TYPE_PCM;
break;
case CX18_ENC_STREAM_TYPE_VBI:
+#ifdef CX18_ENCODER_PARSES_SLICED
captype = cx18_raw_vbi(cx) ?
CAPTURE_CHANNEL_TYPE_VBI : CAPTURE_CHANNEL_TYPE_SLICED_VBI;
+#else
+ /*
+ * Currently we set things up so that Sliced VBI from the
+ * digitizer is handled as Raw VBI by the encoder
+ */
+ captype = CAPTURE_CHANNEL_TYPE_VBI;
+#endif
cx->vbi.frame = 0;
cx->vbi.inserted_frame = 0;
memset(cx->vbi.sliced_mpeg_size,
@@ -473,10 +523,6 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
return -EINVAL;
}
- /* mute/unmute video */
- cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2,
- s->handle, !!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags));
-
/* Clear Streamoff flags in case left from last capture */
clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
@@ -484,31 +530,63 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
s->handle = data[0];
cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype);
- if (atomic_read(&cx->ana_capturing) == 0 && !ts) {
- struct cx18_api_func_private priv;
-
- /* Stuff from Windows, we don't know what it is */
+ /*
+ * For everything but CAPTURE_CHANNEL_TYPE_TS, play it safe and
+ * set up all the parameters, as it is not obvious which parameters the
+ * firmware shares across capture channel types and which it does not.
+ *
+ * Some of the cx18_vapi() calls below apply to only certain capture
+ * channel types. We're hoping there's no harm in calling most of them
+ * anyway, as long as the values are all consistent. Setting some
+ * shared parameters will have no effect once an analog capture channel
+ * has started streaming.
+ */
+ if (captype != CAPTURE_CHANNEL_TYPE_TS) {
cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0);
cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1);
cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0);
cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1);
- cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, s->handle, 12);
+ /*
+ * Audio related reset according to
+ * Documentation/video4linux/cx2341x/fw-encoder-api.txt
+ */
+ if (atomic_read(&cx->ana_capturing) == 0)
+ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2,
+ s->handle, 12);
+
+ /*
+ * Number of lines for Field 1 & Field 2 according to
+ * Documentation/video4linux/cx2341x/fw-encoder-api.txt
+ * Field 1 is 312 for 625 line systems in BT.656
+ * Field 2 is 313 for 625 line systems in BT.656
+ */
cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3,
- s->handle, cx->digitizer, cx->digitizer);
+ s->handle, 312, 313);
- /* Setup VBI */
if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE)
cx18_vbi_setup(s);
- /* assign program index info.
- Mask 7: select I/P/B, Num_req: 400 max */
+ /*
+ * assign program index info.
+ * Mask 7: select I/P/B, Num_req: 400 max
+ * FIXME - currently we have this hardcoded as disabled
+ */
cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 1, 0);
- /* Setup API for Stream */
+ /* Call out to the common CX2341x API setup for user controls */
priv.cx = cx;
priv.s = s;
cx2341x_update(&priv, cx18_api_func, NULL, &cx->params);
+
+ /*
+ * When starting a capture and we're set for radio,
+ * ensure the video is muted, despite the user control.
+ */
+ if (!cx->params.video_mute &&
+ test_bit(CX18_F_I_RADIO_USER, &cx->i_flags))
+ cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle,
+ (cx->params.video_mute_yuv << 8) | 1);
}
if (atomic_read(&cx->tot_capturing) == 0) {
@@ -552,7 +630,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
}
/* you're live! sit back and await interrupts :) */
- if (!ts)
+ if (captype != CAPTURE_CHANNEL_TYPE_TS)
atomic_inc(&cx->ana_capturing);
atomic_inc(&cx->tot_capturing);
return 0;
@@ -565,7 +643,7 @@ void cx18_stop_all_captures(struct cx18 *cx)
for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) {
struct cx18_stream *s = &cx->streams[i];
- if (s->v4l2dev == NULL && s->dvb.enabled == 0)
+ if (s->video_dev == NULL && s->dvb.enabled == 0)
continue;
if (test_bit(CX18_F_S_STREAMING, &s->s_flags))
cx18_stop_v4l2_encode_stream(s, 0);
@@ -577,7 +655,7 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
struct cx18 *cx = s->cx;
unsigned long then;
- if (s->v4l2dev == NULL && s->dvb.enabled == 0)
+ if (s->video_dev == NULL && s->dvb.enabled == 0)
return -EINVAL;
/* This function assumes that you are allowed to stop the capture
@@ -629,7 +707,7 @@ u32 cx18_find_handle(struct cx18 *cx)
for (i = 0; i < CX18_MAX_STREAMS; i++) {
struct cx18_stream *s = &cx->streams[i];
- if (s->v4l2dev && (s->handle != CX18_INVALID_TASK_HANDLE))
+ if (s->video_dev && (s->handle != CX18_INVALID_TASK_HANDLE))
return s->handle;
}
return CX18_INVALID_TASK_HANDLE;
@@ -647,7 +725,7 @@ struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle)
s = &cx->streams[i];
if (s->handle != handle)
continue;
- if (s->v4l2dev || s->dvb.enabled)
+ if (s->video_dev || s->dvb.enabled)
return s;
}
return NULL;
diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c
index fb595bd548e8..c2aef4add31d 100644
--- a/drivers/media/video/cx18/cx18-vbi.c
+++ b/drivers/media/video/cx18/cx18-vbi.c
@@ -25,7 +25,16 @@
#include "cx18-vbi.h"
#include "cx18-ioctl.h"
#include "cx18-queue.h"
-#include "cx18-av-core.h"
+
+/*
+ * Raster Reference/Protection (RP) bytes, used in Start/End Active
+ * Video codes emitted from the digitzer in VIP 1.x mode, that flag the start
+ * of VBI sample or VBI ancilliary data regions in the digitial ratser line.
+ *
+ * Task FieldEven VerticalBlank HorizontalBlank 0 0 0 0
+ */
+static const u8 raw_vbi_sav_rp[2] = { 0x20, 0x60 }; /* __V_, _FV_ */
+static const u8 sliced_vbi_eav_rp[2] = { 0xb0, 0xf0 }; /* T_VH, TFVH */
static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp)
{
@@ -34,10 +43,17 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp)
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
+ /* MPEG-2 Program Pack */
+ 0x00, 0x00, 0x01, 0xba, /* Prog Pack start code */
+ 0x44, 0x00, 0x0c, 0x66, 0x24, 0x01, /* SCR, SCR Ext, markers */
+ 0x01, 0xd1, 0xd3, /* Mux Rate, markers */
+ 0xfa, 0xff, 0xff, /* Res, Suff cnt, Stuff */
+ /* MPEG-2 Private Stream 1 PES Packet */
+ 0x00, 0x00, 0x01, 0xbd, /* Priv Stream 1 start */
+ 0x00, 0x1a, /* length */
+ 0x84, 0x80, 0x07, /* flags, hdr data len */
+ 0x21, 0x00, 0x5d, 0x63, 0xa7, /* PTS, markers */
+ 0xff, 0xff /* stuffing */
};
const int sd = sizeof(mpeg_hdr_data); /* start of vbi data */
int idx = cx->vbi.frame % CX18_VBI_FRAMES;
@@ -71,7 +87,9 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp)
memcpy(dst + sd + 4, dst + sd + 12, line * 43);
size = 4 + ((43 * line + 3) & ~3);
} else {
- memcpy(dst + sd, "cx0", 4);
+ memcpy(dst + sd, "itv0", 4);
+ cpu_to_le32s(&linemask[0]);
+ cpu_to_le32s(&linemask[1]);
memcpy(dst + sd + 4, &linemask[0], 8);
size = 12 + ((43 * line + 3) & ~3);
}
@@ -86,58 +104,76 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp)
}
/* Compress raw VBI format, removes leading SAV codes and surplus space
- after the field.
- Returns new compressed size. */
-static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size)
+ after the frame. Returns new compressed size. */
+static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size, u32 hdr_size)
{
- u32 line_size = cx->vbi.raw_decoder_line_size;
- u32 lines = cx->vbi.count;
- u8 sav1 = cx->vbi.raw_decoder_sav_odd_field;
- u8 sav2 = cx->vbi.raw_decoder_sav_even_field;
+ u32 line_size = vbi_active_samples;
+ u32 lines = cx->vbi.count * 2;
u8 *q = buf;
u8 *p;
int i;
+ /* Skip the header */
+ buf += hdr_size;
+
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))
+ (p[3] != raw_vbi_sav_rp[0] &&
+ p[3] != raw_vbi_sav_rp[1]))
break;
- memcpy(q, p + 4, line_size - 4);
- q += line_size - 4;
+ if (i == lines - 1) {
+ /* last line is hdr_size bytes short - extrapolate it */
+ memcpy(q, p + 4, line_size - 4 - hdr_size);
+ q += line_size - 4 - hdr_size;
+ p += line_size - hdr_size - 1;
+ memset(q, (int) *p, hdr_size);
+ } else {
+ 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 cx18 *cx, u32 line, u8 *buf,
- u32 size, u8 sav)
+static u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size,
+ const u32 hdr_size)
{
- u32 line_size = cx->vbi.sliced_decoder_line_size;
struct v4l2_decode_vbi_line vbi;
int i;
+ u32 line = 0;
+ u32 line_size = cx->is_60hz ? vbi_hblank_samples_60Hz
+ : vbi_hblank_samples_50Hz;
/* find the first valid line */
- for (i = 0; i < size; i++, buf++) {
- if (buf[0] == 0xff && !buf[1] && !buf[2] && buf[3] == sav)
+ for (i = hdr_size, buf += hdr_size; i < size; i++, buf++) {
+ if (buf[0] == 0xff && !buf[1] && !buf[2] &&
+ (buf[3] == sliced_vbi_eav_rp[0] ||
+ buf[3] == sliced_vbi_eav_rp[1]))
break;
}
- size -= i;
+ /*
+ * The last line is short by hdr_size bytes, but for the remaining
+ * checks against size, we pretend that it is not, by counting the
+ * header bytes we knowingly skipped
+ */
+ size -= (i - hdr_size);
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)
+ /* Look for EAV code */
+ if (p[0] != 0xff || p[1] || p[2] ||
+ (p[3] != sliced_vbi_eav_rp[0] &&
+ p[3] != sliced_vbi_eav_rp[1]))
continue;
vbi.p = p + 4;
- cx18_av_cmd(cx, VIDIOC_INT_DECODE_VBI_LINE, &vbi);
+ v4l2_subdev_call(cx->sd_av, video, decode_vbi_line, &vbi);
if (vbi.type) {
cx->vbi.sliced_data[line].id = vbi.type;
cx->vbi.sliced_data[line].field = vbi.is_second_field;
@@ -150,51 +186,56 @@ static u32 compress_sliced_buf(struct cx18 *cx, u32 line, u8 *buf,
}
void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
- u64 pts_stamp, int streamtype)
+ int streamtype)
{
+ /*
+ * The CX23418 provides a 12 byte header in its raw VBI buffers to us:
+ * 0x3fffffff [4 bytes of something] [4 byte presentation time stamp]
+ */
+ struct vbi_data_hdr {
+ __be32 magic;
+ __be32 unknown;
+ __be32 pts;
+ } *hdr = (struct vbi_data_hdr *) buf->buf;
+
u8 *p = (u8 *) buf->buf;
u32 size = buf->bytesused;
+ u32 pts;
int lines;
if (streamtype != CX18_ENC_STREAM_TYPE_VBI)
return;
+ /*
+ * The CX23418 sends us data that is 32 bit little-endian swapped,
+ * but we want the raw VBI bytes in the order they were in the raster
+ * line. This has a side effect of making the header big endian
+ */
+ cx18_buf_swap(buf);
+
/* Raw VBI data */
if (cx18_raw_vbi(cx)) {
- u8 type;
-
- cx18_buf_swap(buf);
-
- /* Skip 12 bytes of header that gets stuffed in */
- size -= 12;
- memcpy(p, &buf->buf[12], size);
- type = p[3];
- size = buf->bytesused = compress_raw_buf(cx, p, size);
+ size = buf->bytesused =
+ compress_raw_buf(cx, p, size, sizeof(struct vbi_data_hdr));
- /* second field of the frame? */
- if (type == cx->vbi.raw_decoder_sav_even_field) {
- /* Dirty hack needed for backwards
- compatibility of old VBI software. */
- p += size - 4;
- memcpy(p, &cx->vbi.frame, 4);
- cx->vbi.frame++;
- }
+ /*
+ * Hack needed for compatibility with old VBI software.
+ * Write the frame # at the last 4 bytes of the frame
+ */
+ p += size - 4;
+ memcpy(p, &cx->vbi.frame, 4);
+ cx->vbi.frame++;
return;
}
/* Sliced VBI data with data insertion */
- cx18_buf_swap(buf);
- /* first field */
- lines = compress_sliced_buf(cx, 0, p, size / 2,
- cx->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(cx, lines, p + size / 2 - 32,
- size / 2 + 32, cx->vbi.sliced_decoder_sav_even_field);
+ pts = (be32_to_cpu(hdr->magic) == 0x3fffffff) ? be32_to_cpu(hdr->pts)
+ : 0;
+
+ lines = compress_sliced_buf(cx, p, size, sizeof(struct vbi_data_hdr));
+
/* always return at least one empty line */
if (lines == 0) {
cx->vbi.sliced_data[0].id = 0;
@@ -206,6 +247,6 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
memcpy(p, &cx->vbi.sliced_data[0], size);
if (cx->vbi.insert_mpeg)
- copy_vbi_data(cx, lines, pts_stamp);
+ copy_vbi_data(cx, lines, pts);
cx->vbi.frame++;
}
diff --git a/drivers/media/video/cx18/cx18-vbi.h b/drivers/media/video/cx18/cx18-vbi.h
index c56ff7d28f20..e7e1ae427f34 100644
--- a/drivers/media/video/cx18/cx18-vbi.h
+++ b/drivers/media/video/cx18/cx18-vbi.h
@@ -22,5 +22,5 @@
*/
void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
- u64 pts_stamp, int streamtype);
+ int streamtype);
int cx18_used_line(struct cx18 *cx, int line, int field);
diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h
index 84c0ff13b607..bd9bd44da791 100644
--- a/drivers/media/video/cx18/cx18-version.h
+++ b/drivers/media/video/cx18/cx18-version.h
@@ -24,8 +24,8 @@
#define CX18_DRIVER_NAME "cx18"
#define CX18_DRIVER_VERSION_MAJOR 1
-#define CX18_DRIVER_VERSION_MINOR 0
-#define CX18_DRIVER_VERSION_PATCHLEVEL 4
+#define CX18_DRIVER_VERSION_MINOR 1
+#define CX18_DRIVER_VERSION_PATCHLEVEL 0
#define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL)
#define CX18_DRIVER_VERSION KERNEL_VERSION(CX18_DRIVER_VERSION_MAJOR, \
diff --git a/drivers/media/video/cx18/cx18-video.c b/drivers/media/video/cx18/cx18-video.c
index 2e5c41939330..6fdadedf17a8 100644
--- a/drivers/media/video/cx18/cx18-video.c
+++ b/drivers/media/video/cx18/cx18-video.c
@@ -21,7 +21,6 @@
#include "cx18-driver.h"
#include "cx18-video.h"
-#include "cx18-av-core.h"
#include "cx18-cards.h"
void cx18_video_set_io(struct cx18 *cx)
@@ -32,7 +31,7 @@ void cx18_video_set_io(struct cx18 *cx)
route.input = cx->card->video_inputs[inp].video_input;
route.output = 0;
- cx18_av_cmd(cx, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ v4l2_subdev_call(cx->sd_av, video, s_routing, &route);
type = cx->card->video_inputs[inp].video_type;
diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h
index 601f3a2ab742..9956abf576c5 100644
--- a/drivers/media/video/cx18/cx23418.h
+++ b/drivers/media/video/cx18/cx23418.h
@@ -56,6 +56,22 @@
#define APU_CMD_MASK 0x10000000
#define APU_CMD_MASK_ACK (APU_CMD_MASK | 0x80000000)
+#define CX18_APU_ENCODING_METHOD_MPEG (0 << 28)
+#define CX18_APU_ENCODING_METHOD_AC3 (1 << 28)
+
+/* Description: Command APU to start audio
+ IN[0] - audio parameters (same as CX18_CPU_SET_AUDIO_PARAMETERS?)
+ IN[1] - caller buffer address, or 0
+ ReturnCode - ??? */
+#define CX18_APU_START (APU_CMD_MASK | 0x01)
+
+/* Description: Command APU to stop audio
+ IN[0] - encoding method to stop
+ ReturnCode - ??? */
+#define CX18_APU_STOP (APU_CMD_MASK | 0x02)
+
+/* Description: Command APU to reset the AI
+ ReturnCode - ??? */
#define CX18_APU_RESETAI (APU_CMD_MASK | 0x05)
/* Description: This command indicates that a Memory Descriptor List has been