681c739944
This is a new implementation for video pipeline control within the pvrusb2 driver. Actual start/stop of the pipeline is moved to the driver's kernel thread. Pipeline stages are controlled autonomously based on surrounding pipeline or application control state. Kernel thread management is also cleaned up and moved into the internal control structure of the driver, solving a set up / tear down race along the way. Better failure recovery is implemented with this new control strategy. Also with this change comes better control of the cx23416 encoder, building on additional information learned about the peculiarities of controlling this part (this information was the original trigger for this rework). With this change, overall encoder stability should be considerably improved. Yes, this is a large change for this driver, but due to the nature of the feature being worked on, the changes are fairly pervasive and would be difficult to break into smaller pieces with any semblence of step-wise stability. Signed-off-by: Mike Isely <isely@pobox.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
238 lines
5.7 KiB
C
238 lines
5.7 KiB
C
/*
|
|
*
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
|
|
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
|
|
/*
|
|
|
|
This source file is specifically designed to interface with the
|
|
saa711x support that is available in the v4l available starting
|
|
with linux 2.6.15.
|
|
|
|
*/
|
|
|
|
#include "pvrusb2-video-v4l.h"
|
|
#include "pvrusb2-i2c-cmd-v4l2.h"
|
|
|
|
|
|
#include "pvrusb2-hdw-internal.h"
|
|
#include "pvrusb2-debug.h"
|
|
#include <linux/videodev2.h>
|
|
#include <media/v4l2-common.h>
|
|
#include <media/saa7115.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/slab.h>
|
|
|
|
struct pvr2_v4l_decoder {
|
|
struct pvr2_i2c_handler handler;
|
|
struct pvr2_decoder_ctrl ctrl;
|
|
struct pvr2_i2c_client *client;
|
|
struct pvr2_hdw *hdw;
|
|
unsigned long stale_mask;
|
|
};
|
|
|
|
|
|
static void set_input(struct pvr2_v4l_decoder *ctxt)
|
|
{
|
|
struct pvr2_hdw *hdw = ctxt->hdw;
|
|
struct v4l2_routing route;
|
|
|
|
pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_input(%d)",hdw->input_val);
|
|
switch(hdw->input_val) {
|
|
case PVR2_CVAL_INPUT_TV:
|
|
route.input = SAA7115_COMPOSITE4;
|
|
break;
|
|
case PVR2_CVAL_INPUT_COMPOSITE:
|
|
route.input = SAA7115_COMPOSITE5;
|
|
break;
|
|
case PVR2_CVAL_INPUT_SVIDEO:
|
|
route.input = SAA7115_SVIDEO2;
|
|
break;
|
|
case PVR2_CVAL_INPUT_RADIO:
|
|
// In radio mode, we mute the video, but point at one
|
|
// spot just to stay consistent
|
|
route.input = SAA7115_COMPOSITE5;
|
|
default:
|
|
return;
|
|
}
|
|
route.output = 0;
|
|
pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_VIDEO_ROUTING,&route);
|
|
}
|
|
|
|
|
|
static int check_input(struct pvr2_v4l_decoder *ctxt)
|
|
{
|
|
struct pvr2_hdw *hdw = ctxt->hdw;
|
|
return hdw->input_dirty != 0;
|
|
}
|
|
|
|
|
|
static void set_audio(struct pvr2_v4l_decoder *ctxt)
|
|
{
|
|
u32 val;
|
|
struct pvr2_hdw *hdw = ctxt->hdw;
|
|
|
|
pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_audio %d",
|
|
hdw->srate_val);
|
|
switch (hdw->srate_val) {
|
|
default:
|
|
case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000:
|
|
val = 48000;
|
|
break;
|
|
case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100:
|
|
val = 44100;
|
|
break;
|
|
case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000:
|
|
val = 32000;
|
|
break;
|
|
}
|
|
pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_AUDIO_CLOCK_FREQ,&val);
|
|
}
|
|
|
|
|
|
static int check_audio(struct pvr2_v4l_decoder *ctxt)
|
|
{
|
|
struct pvr2_hdw *hdw = ctxt->hdw;
|
|
return hdw->srate_dirty != 0;
|
|
}
|
|
|
|
|
|
struct pvr2_v4l_decoder_ops {
|
|
void (*update)(struct pvr2_v4l_decoder *);
|
|
int (*check)(struct pvr2_v4l_decoder *);
|
|
};
|
|
|
|
|
|
static const struct pvr2_v4l_decoder_ops decoder_ops[] = {
|
|
{ .update = set_input, .check = check_input},
|
|
{ .update = set_audio, .check = check_audio},
|
|
};
|
|
|
|
|
|
static void decoder_detach(struct pvr2_v4l_decoder *ctxt)
|
|
{
|
|
ctxt->client->handler = NULL;
|
|
pvr2_hdw_set_decoder(ctxt->hdw,NULL);
|
|
kfree(ctxt);
|
|
}
|
|
|
|
|
|
static int decoder_check(struct pvr2_v4l_decoder *ctxt)
|
|
{
|
|
unsigned long msk;
|
|
unsigned int idx;
|
|
|
|
for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) {
|
|
msk = 1 << idx;
|
|
if (ctxt->stale_mask & msk) continue;
|
|
if (decoder_ops[idx].check(ctxt)) {
|
|
ctxt->stale_mask |= msk;
|
|
}
|
|
}
|
|
return ctxt->stale_mask != 0;
|
|
}
|
|
|
|
|
|
static void decoder_update(struct pvr2_v4l_decoder *ctxt)
|
|
{
|
|
unsigned long msk;
|
|
unsigned int idx;
|
|
|
|
for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) {
|
|
msk = 1 << idx;
|
|
if (!(ctxt->stale_mask & msk)) continue;
|
|
ctxt->stale_mask &= ~msk;
|
|
decoder_ops[idx].update(ctxt);
|
|
}
|
|
}
|
|
|
|
|
|
static int decoder_detect(struct pvr2_i2c_client *cp)
|
|
{
|
|
/* Attempt to query the decoder - let's see if it will answer */
|
|
struct v4l2_tuner vt;
|
|
int ret;
|
|
|
|
memset(&vt,0,sizeof(vt));
|
|
ret = pvr2_i2c_client_cmd(cp,VIDIOC_G_TUNER,&vt);
|
|
return ret == 0; /* Return true if it answered */
|
|
}
|
|
|
|
|
|
static void decoder_enable(struct pvr2_v4l_decoder *ctxt,int fl)
|
|
{
|
|
pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 decoder_enable(%d)",fl);
|
|
pvr2_v4l2_cmd_stream(ctxt->client,fl);
|
|
}
|
|
|
|
|
|
static unsigned int decoder_describe(struct pvr2_v4l_decoder *ctxt,char *buf,unsigned int cnt)
|
|
{
|
|
return scnprintf(buf,cnt,"handler: pvrusb2-video-v4l");
|
|
}
|
|
|
|
|
|
static const struct pvr2_i2c_handler_functions hfuncs = {
|
|
.detach = (void (*)(void *))decoder_detach,
|
|
.check = (int (*)(void *))decoder_check,
|
|
.update = (void (*)(void *))decoder_update,
|
|
.describe = (unsigned int (*)(void *,char *,unsigned int))decoder_describe,
|
|
};
|
|
|
|
|
|
int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *hdw,
|
|
struct pvr2_i2c_client *cp)
|
|
{
|
|
struct pvr2_v4l_decoder *ctxt;
|
|
|
|
if (hdw->decoder_ctrl) return 0;
|
|
if (cp->handler) return 0;
|
|
if (!decoder_detect(cp)) return 0;
|
|
|
|
ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL);
|
|
if (!ctxt) return 0;
|
|
|
|
ctxt->handler.func_data = ctxt;
|
|
ctxt->handler.func_table = &hfuncs;
|
|
ctxt->ctrl.ctxt = ctxt;
|
|
ctxt->ctrl.detach = (void (*)(void *))decoder_detach;
|
|
ctxt->ctrl.enable = (void (*)(void *,int))decoder_enable;
|
|
ctxt->client = cp;
|
|
ctxt->hdw = hdw;
|
|
ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1;
|
|
pvr2_hdw_set_decoder(hdw,&ctxt->ctrl);
|
|
cp->handler = &ctxt->handler;
|
|
pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x saa711x V4L2 handler set up",
|
|
cp->client->addr);
|
|
return !0;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
Stuff for Emacs to see, in order to encourage consistent editing style:
|
|
*** Local Variables: ***
|
|
*** mode: c ***
|
|
*** fill-column: 70 ***
|
|
*** tab-width: 8 ***
|
|
*** c-basic-offset: 8 ***
|
|
*** End: ***
|
|
*/
|