diff options
Diffstat (limited to 'drivers/media/video/ivtv/ivtv-firmware.c')
-rw-r--r-- | drivers/media/video/ivtv/ivtv-firmware.c | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/drivers/media/video/ivtv/ivtv-firmware.c b/drivers/media/video/ivtv/ivtv-firmware.c index efb288d..d8bf2b0 100644 --- a/drivers/media/video/ivtv/ivtv-firmware.c +++ b/drivers/media/video/ivtv/ivtv-firmware.c @@ -23,7 +23,10 @@ #include "ivtv-mailbox.h" #include "ivtv-firmware.h" #include "ivtv-yuv.h" +#include "ivtv-ioctl.h" +#include "ivtv-cards.h" #include <linux/firmware.h> +#include <media/saa7127.h> #define IVTV_MASK_SPU_ENABLE 0xFFFFFFFE #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); } +/* 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 allowing multiple failures to be logged. */ 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; } |