V4L/DVB: ivtv: Automatic firmware reload

If the firmware has failed, this patch will automatically reload &
restart the card. The previous card state will be restored on a
successful restart.  Firmware reload will only happen if neither the
encoder or decoder is active.  If the card is busy then behaviour is as
before, returning -EIO on device access until the reload can occur. On
cards that support video output, coloured bars will be displayed during
the reload.

Andy Walls (ivtv maintainer and patch committer) made minor tweaks to
comments and the logged messages, but nothing substantial otherwise.

Signed-off-by: Ian Armstrong <ian@iarmst.demon.co.uk>
Signed-off-by: Andy Walls <awalls@md.metrocast.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Ian Armstrong 2010-06-12 13:41:57 -03:00 committed by Mauro Carvalho Chehab
parent 914610e8c5
commit 215659d14f
8 changed files with 142 additions and 15 deletions

View file

@ -1444,6 +1444,7 @@ EXPORT_SYMBOL(ivtv_udma_unmap);
EXPORT_SYMBOL(ivtv_udma_alloc); EXPORT_SYMBOL(ivtv_udma_alloc);
EXPORT_SYMBOL(ivtv_udma_prepare); EXPORT_SYMBOL(ivtv_udma_prepare);
EXPORT_SYMBOL(ivtv_init_on_first_open); EXPORT_SYMBOL(ivtv_init_on_first_open);
EXPORT_SYMBOL(ivtv_firmware_check);
module_init(module_start); module_init(module_start);
module_exit(module_cleanup); module_exit(module_cleanup);

View file

@ -737,6 +737,7 @@ struct ivtv {
struct v4l2_rect osd_rect; /* current OSD position and size */ struct v4l2_rect osd_rect; /* current OSD position and size */
struct v4l2_rect main_rect; /* current Main window position and size */ struct v4l2_rect main_rect; /* current Main window position and size */
struct osd_info *osd_info; /* ivtvfb private OSD info */ struct osd_info *osd_info; /* ivtvfb private OSD info */
void (*ivtvfb_restore)(struct ivtv *itv); /* Used for a warm start */
}; };
static inline struct ivtv *to_ivtv(struct v4l2_device *v4l2_dev) static inline struct ivtv *to_ivtv(struct v4l2_device *v4l2_dev)

View file

@ -536,8 +536,12 @@ int ivtv_start_decoding(struct ivtv_open_id *id, int speed)
return -EBUSY; return -EBUSY;
} }
rc = ivtv_start_v4l2_decode_stream(s, 0); rc = ivtv_start_v4l2_decode_stream(s, 0);
if (rc < 0) if (rc < 0) {
return rc; if (rc == -EAGAIN)
rc = ivtv_start_v4l2_decode_stream(s, 0);
if (rc < 0)
return rc;
}
} }
if (s->type == IVTV_DEC_STREAM_TYPE_MPG) if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
return ivtv_set_speed(itv, speed); return ivtv_set_speed(itv, speed);
@ -926,19 +930,21 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp)
IVTV_DEBUG_FILE("open %s\n", s->name); IVTV_DEBUG_FILE("open %s\n", s->name);
#ifdef CONFIG_VIDEO_ADV_DEBUG #ifdef CONFIG_VIDEO_ADV_DEBUG
/* Unless ivtv_fw_debug is set, error out if firmware dead. */
if (ivtv_fw_debug) { if (ivtv_fw_debug) {
IVTV_WARN("Opening %s with dead firmware lockout disabled\n", IVTV_WARN("Opening %s with dead firmware lockout disabled\n",
video_device_node_name(vdev)); video_device_node_name(vdev));
IVTV_WARN("Selected firmware errors will be ignored\n"); IVTV_WARN("Selected firmware errors will be ignored\n");
} } else {
/* Unless ivtv_fw_debug is set, error out if firmware dead. */
if (ivtv_firmware_check(itv, "ivtv_serialized_open") && !ivtv_fw_debug)
return -EIO;
#else #else
if (ivtv_firmware_check(itv, "ivtv_serialized_open")) if (1) {
return -EIO;
#endif #endif
res = ivtv_firmware_check(itv, "ivtv_serialized_open");
if (res == -EAGAIN)
res = ivtv_firmware_check(itv, "ivtv_serialized_open");
if (res < 0)
return -EIO;
}
if (s->type == IVTV_DEC_STREAM_TYPE_MPG && if (s->type == IVTV_DEC_STREAM_TYPE_MPG &&
test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_YUV].s_flags)) test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_YUV].s_flags))

View file

@ -23,7 +23,10 @@
#include "ivtv-mailbox.h" #include "ivtv-mailbox.h"
#include "ivtv-firmware.h" #include "ivtv-firmware.h"
#include "ivtv-yuv.h" #include "ivtv-yuv.h"
#include "ivtv-ioctl.h"
#include "ivtv-cards.h"
#include <linux/firmware.h> #include <linux/firmware.h>
#include <media/saa7127.h>
#define IVTV_MASK_SPU_ENABLE 0xFFFFFFFE #define IVTV_MASK_SPU_ENABLE 0xFFFFFFFE
#define IVTV_MASK_VPU_ENABLE15 0xFFFFFFF6 #define IVTV_MASK_VPU_ENABLE15 0xFFFFFFF6
@ -272,6 +275,58 @@ void ivtv_init_mpeg_decoder(struct ivtv *itv)
ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 4, 0, 0, 0, 1); ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 4, 0, 0, 0, 1);
} }
/* Try to restart the card & restore previous settings */
int ivtv_firmware_restart(struct ivtv *itv)
{
int rc = 0;
v4l2_std_id std;
struct ivtv_open_id fh;
fh.itv = itv;
if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
/* Display test image during restart */
ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing,
SAA7127_INPUT_TYPE_TEST_IMAGE,
itv->card->video_outputs[itv->active_output].video_output,
0);
mutex_lock(&itv->udma.lock);
rc = ivtv_firmware_init(itv);
if (rc) {
mutex_unlock(&itv->udma.lock);
return rc;
}
/* Allow settings to reload */
ivtv_mailbox_cache_invalidate(itv);
/* Restore video standard */
std = itv->std;
itv->std = 0;
ivtv_s_std(NULL, &fh, &std);
if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
ivtv_init_mpeg_decoder(itv);
/* Restore framebuffer if active */
if (itv->ivtvfb_restore)
itv->ivtvfb_restore(itv);
/* Restore alpha settings */
ivtv_set_osd_alpha(itv);
/* Restore normal output */
ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing,
SAA7127_INPUT_TYPE_NORMAL,
itv->card->video_outputs[itv->active_output].video_output,
0);
}
mutex_unlock(&itv->udma.lock);
return rc;
}
/* Check firmware running state. The checks fall through /* Check firmware running state. The checks fall through
allowing multiple failures to be logged. */ allowing multiple failures to be logged. */
int ivtv_firmware_check(struct ivtv *itv, char *where) int ivtv_firmware_check(struct ivtv *itv, char *where)
@ -315,5 +370,26 @@ int ivtv_firmware_check(struct ivtv *itv, char *where)
} }
} }
/* If something failed & currently idle, try to reload */
if (res && !atomic_read(&itv->capturing) &&
!atomic_read(&itv->decoding)) {
IVTV_INFO("Detected in %s that firmware had failed - "
"Reloading\n", where);
res = ivtv_firmware_restart(itv);
/*
* Even if restarted ok, still signal a problem had occured.
* The caller can come through this function again to check
* if things are really ok after the restart.
*/
if (!res) {
IVTV_INFO("Firmware restart okay\n");
res = -EAGAIN;
} else {
IVTV_INFO("Firmware restart failed\n");
}
} else if (res) {
res = -EIO;
}
return res; return res;
} }

View file

@ -377,3 +377,11 @@ void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb,
for (i = 0; i < argc; i++, p++) for (i = 0; i < argc; i++, p++)
data[i] = readl(p); data[i] = readl(p);
} }
/* Wipe api cache */
void ivtv_mailbox_cache_invalidate(struct ivtv *itv)
{
int i;
for (i = 0; i < 256; i++)
itv->api_cache[i].last_jiffies = 0;
}

View file

@ -30,5 +30,6 @@ int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]);
int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...); int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...);
int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...); int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...);
int ivtv_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]); int ivtv_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]);
void ivtv_mailbox_cache_invalidate(struct ivtv *itv);
#endif #endif

View file

@ -676,10 +676,7 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
ivtv_msleep_timeout(10, 0); ivtv_msleep_timeout(10, 0);
/* Known failure point for firmware, so check */ /* Known failure point for firmware, so check */
if (ivtv_firmware_check(itv, "ivtv_setup_v4l2_decode_stream") < 0) return ivtv_firmware_check(itv, "ivtv_setup_v4l2_decode_stream");
return -EIO;
return 0;
} }
int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset) int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset)

View file

@ -53,6 +53,7 @@
#include "ivtv-i2c.h" #include "ivtv-i2c.h"
#include "ivtv-udma.h" #include "ivtv-udma.h"
#include "ivtv-mailbox.h" #include "ivtv-mailbox.h"
#include "ivtv-firmware.h"
/* card parameters */ /* card parameters */
static int ivtvfb_card_id = -1; static int ivtvfb_card_id = -1;
@ -178,6 +179,12 @@ struct osd_info {
struct fb_info ivtvfb_info; struct fb_info ivtvfb_info;
struct fb_var_screeninfo ivtvfb_defined; struct fb_var_screeninfo ivtvfb_defined;
struct fb_fix_screeninfo ivtvfb_fix; struct fb_fix_screeninfo ivtvfb_fix;
/* Used for a warm start */
struct fb_var_screeninfo fbvar_cur;
int blank_cur;
u32 palette_cur[256];
u32 pan_cur;
}; };
struct ivtv_osd_coords { struct ivtv_osd_coords {
@ -199,6 +206,7 @@ static int ivtvfb_get_framebuffer(struct ivtv *itv, u32 *fbbase,
u32 data[CX2341X_MBOX_MAX_DATA]; u32 data[CX2341X_MBOX_MAX_DATA];
int rc; int rc;
ivtv_firmware_check(itv, "ivtvfb_get_framebuffer");
rc = ivtv_vapi_result(itv, data, CX2341X_OSD_GET_FRAMEBUFFER, 0); rc = ivtv_vapi_result(itv, data, CX2341X_OSD_GET_FRAMEBUFFER, 0);
*fbbase = data[0]; *fbbase = data[0];
*fblength = data[1]; *fblength = data[1];
@ -581,8 +589,10 @@ static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var)
ivtv_window.height = var->yres; ivtv_window.height = var->yres;
/* Minimum margin cannot be 0, as X won't allow such a mode */ /* Minimum margin cannot be 0, as X won't allow such a mode */
if (!var->upper_margin) var->upper_margin++; if (!var->upper_margin)
if (!var->left_margin) var->left_margin++; var->upper_margin++;
if (!var->left_margin)
var->left_margin++;
ivtv_window.top = var->upper_margin - 1; ivtv_window.top = var->upper_margin - 1;
ivtv_window.left = var->left_margin - 1; ivtv_window.left = var->left_margin - 1;
@ -595,6 +605,9 @@ static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var)
/* Force update of yuv registers */ /* Force update of yuv registers */
itv->yuv_info.yuv_forced_update = 1; itv->yuv_info.yuv_forced_update = 1;
/* Keep a copy of these settings */
memcpy(&oi->fbvar_cur, var, sizeof(oi->fbvar_cur));
IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n", IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
var->xres, var->yres, var->xres, var->yres,
var->xres_virtual, var->yres_virtual, var->xres_virtual, var->yres_virtual,
@ -829,6 +842,8 @@ static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *inf
itv->yuv_info.osd_y_pan = var->yoffset; itv->yuv_info.osd_y_pan = var->yoffset;
/* Force update of yuv registers */ /* Force update of yuv registers */
itv->yuv_info.yuv_forced_update = 1; itv->yuv_info.yuv_forced_update = 1;
/* Remember this value */
itv->osd_info->pan_cur = osd_pan_index;
return 0; return 0;
} }
@ -842,6 +857,7 @@ static int ivtvfb_set_par(struct fb_info *info)
rc = ivtvfb_set_var(itv, &info->var); rc = ivtvfb_set_var(itv, &info->var);
ivtvfb_pan_display(&info->var, info); ivtvfb_pan_display(&info->var, info);
ivtvfb_get_fix(itv, &info->fix); ivtvfb_get_fix(itv, &info->fix);
ivtv_firmware_check(itv, "ivtvfb_set_par");
return rc; return rc;
} }
@ -859,6 +875,7 @@ static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green,
if (info->var.bits_per_pixel <= 8) { if (info->var.bits_per_pixel <= 8) {
write_reg(regno, 0x02a30); write_reg(regno, 0x02a30);
write_reg(color, 0x02a34); write_reg(color, 0x02a34);
itv->osd_info->palette_cur[regno] = color;
return 0; return 0;
} }
if (regno >= 16) if (regno >= 16)
@ -911,6 +928,7 @@ static int ivtvfb_blank(int blank_mode, struct fb_info *info)
ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0); ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
break; break;
} }
itv->osd_info->blank_cur = blank_mode;
return 0; return 0;
} }
@ -929,6 +947,21 @@ static struct fb_ops ivtvfb_ops = {
.fb_blank = ivtvfb_blank, .fb_blank = ivtvfb_blank,
}; };
/* Restore hardware after firmware restart */
static void ivtvfb_restore(struct ivtv *itv)
{
struct osd_info *oi = itv->osd_info;
int i;
ivtvfb_set_var(itv, &oi->fbvar_cur);
ivtvfb_blank(oi->blank_cur, &oi->ivtvfb_info);
for (i = 0; i < 256; i++) {
write_reg(i, 0x02a30);
write_reg(oi->palette_cur[i], 0x02a34);
}
write_reg(oi->pan_cur, 0x02a0c);
}
/* Initialization */ /* Initialization */
@ -1192,6 +1225,9 @@ static int ivtvfb_init_card(struct ivtv *itv)
/* Enable the osd */ /* Enable the osd */
ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info); ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info);
/* Enable restart */
itv->ivtvfb_restore = ivtvfb_restore;
/* Allocate DMA */ /* Allocate DMA */
ivtv_udma_alloc(itv); ivtv_udma_alloc(itv);
return 0; return 0;
@ -1226,6 +1262,7 @@ static int ivtvfb_callback_cleanup(struct device *dev, void *p)
return 0; return 0;
} }
IVTVFB_INFO("Unregister framebuffer %d\n", itv->instance); IVTVFB_INFO("Unregister framebuffer %d\n", itv->instance);
itv->ivtvfb_restore = NULL;
ivtvfb_blank(FB_BLANK_VSYNC_SUSPEND, &oi->ivtvfb_info); ivtvfb_blank(FB_BLANK_VSYNC_SUSPEND, &oi->ivtvfb_info);
ivtvfb_release_buffers(itv); ivtvfb_release_buffers(itv);
itv->osd_video_pbase = 0; itv->osd_video_pbase = 0;