/* We rely on a table held in the firmware - Quick check. */ int ivtv_yuv_filter_check(struct ivtv *itv) { int i, offset_y, offset_uv; for (i=0, offset_y = 16, offset_uv = 4; i<16; i++, offset_y += 24, offset_uv += 12) { if ((read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + offset_y) != i << 16) || (read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + offset_uv) != i << 16)) { IVTV_WARN ("YUV filter table not found in firmware.\n"); return -1; } } return 0; }
void ivtv_firmware_versions(struct ivtv *itv) { u32 data[CX2341X_MBOX_MAX_DATA]; /* Encoder */ ivtv_vapi_result(itv, data, CX2341X_ENC_GET_VERSION, 0); IVTV_INFO("Encoder revision: 0x%08x\n", data[0]); if (data[0] != 0x02060039) IVTV_WARN("Recommended firmware version is 0x02060039.\n"); if (itv->has_cx23415) { /* Decoder */ ivtv_vapi_result(itv, data, CX2341X_DEC_GET_VERSION, 0); IVTV_INFO("Decoder revision: 0x%08x\n", data[0]); } }
/* Check firmware running state. The checks fall through allowing multiple failures to be logged. */ int ivtv_firmware_check(struct ivtv *itv, char *where) { int res = 0; /* Check encoder is still running */ if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0) < 0) { IVTV_WARN("Encoder has died : %s\n", where); res = -1; } /* Also check audio. Only check if not in use & encoder is okay */ if (!res && !atomic_read(&itv->capturing) && (!atomic_read(&itv->decoding) || (atomic_read(&itv->decoding) < 2 && test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)))) { if (ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12) < 0) { IVTV_WARN("Audio has died (Encoder OK) : %s\n", where); res = -2; } } if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { /* Second audio check. Skip if audio already failed */ if (res != -2 && read_dec(0x100) != read_dec(0x104)) { /* Wait & try again to be certain. */ ivtv_msleep_timeout(14, 0); if (read_dec(0x100) != read_dec(0x104)) { IVTV_WARN("Audio has died (Decoder) : %s\n", where); res = -1; } } /* Check decoder is still running */ if (ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0) < 0) { IVTV_WARN("Decoder has died : %s\n", where); res = -1; } } /* 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 occurred. * 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; }
static int ivtv_open(struct file *filp) { struct video_device *vdev = video_devdata(filp); struct ivtv_stream *s = video_get_drvdata(vdev); struct ivtv *itv = s->itv; struct ivtv_open_id *item; int res = 0; IVTV_DEBUG_FILE("open %s\n", s->name); if (ivtv_init_on_first_open(itv)) { IVTV_ERR("Failed to initialize on device %s\n", video_device_node_name(vdev)); return -ENXIO; } #ifdef CONFIG_VIDEO_ADV_DEBUG /* Unless ivtv_fw_debug is set, error out if firmware dead. */ if (ivtv_fw_debug) { IVTV_WARN("Opening %s with dead firmware lockout disabled\n", video_device_node_name(vdev)); IVTV_WARN("Selected firmware errors will be ignored\n"); } else { #else if (1) { #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 && test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_YUV].s_flags)) return -EBUSY; if (s->type == IVTV_DEC_STREAM_TYPE_YUV && test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_MPG].s_flags)) return -EBUSY; if (s->type == IVTV_DEC_STREAM_TYPE_YUV) { if (read_reg(0x82c) == 0) { IVTV_ERR("Tried to open YUV output device but need to send data to mpeg decoder before it can be used\n"); /* return -ENODEV; */ } ivtv_udma_alloc(itv); } /* Allocate memory */ item = kzalloc(sizeof(struct ivtv_open_id), GFP_KERNEL); if (NULL == item) { IVTV_DEBUG_WARN("nomem on v4l2 open\n"); return -ENOMEM; } v4l2_fh_init(&item->fh, &s->vdev); item->itv = itv; item->type = s->type; filp->private_data = &item->fh; v4l2_fh_add(&item->fh); if (item->type == IVTV_ENC_STREAM_TYPE_RAD && v4l2_fh_is_singular_file(filp)) { if (!test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) { if (atomic_read(&itv->capturing) > 0) { /* switching to radio while capture is in progress is not polite */ v4l2_fh_del(&item->fh); v4l2_fh_exit(&item->fh); kfree(item); return -EBUSY; } } /* Mark that the radio is being used. */ set_bit(IVTV_F_I_RADIO_USER, &itv->i_flags); /* We have the radio */ ivtv_mute(itv); /* Switch tuner to radio */ ivtv_call_all(itv, tuner, s_radio); /* Select the correct audio input (i.e. radio tuner) */ ivtv_audio_set_io(itv); if (itv->hw_flags & IVTV_HW_SAA711X) { ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq, SAA7115_FREQ_32_11_MHZ, SAA7115_FREQ_FL_APLL); } /* Done! Unmute and continue. */ ivtv_unmute(itv); } /* YUV or MPG Decoding Mode? */ if (s->type == IVTV_DEC_STREAM_TYPE_MPG) { clear_bit(IVTV_F_I_DEC_YUV, &itv->i_flags); } else if (s->type == IVTV_DEC_STREAM_TYPE_YUV) { set_bit(IVTV_F_I_DEC_YUV, &itv->i_flags); /* For yuv, we need to know the dma size before we start */ itv->dma_data_req_size = 1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31); itv->yuv_info.stream_size = 0; } return 0; } int ivtv_v4l2_open(struct file *filp) { struct video_device *vdev = video_devdata(filp); int res; if (mutex_lock_interruptible(vdev->lock)) return -ERESTARTSYS; res = ivtv_open(filp); mutex_unlock(vdev->lock); return res; } void ivtv_mute(struct ivtv *itv) { if (atomic_read(&itv->capturing)) ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 1); IVTV_DEBUG_INFO("Mute\n"); } void ivtv_unmute(struct ivtv *itv) { if (atomic_read(&itv->capturing)) { ivtv_msleep_timeout(100, 0); ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12); ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 0); } IVTV_DEBUG_INFO("Unmute\n"); }
int ivtv_firmware_check(struct ivtv *itv, char *where) { int res = 0; /* */ if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0) < 0) { IVTV_WARN("Encoder has died : %s\n", where); res = -1; } /* */ if (!res && !atomic_read(&itv->capturing) && (!atomic_read(&itv->decoding) || (atomic_read(&itv->decoding) < 2 && test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)))) { if (ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12) < 0) { IVTV_WARN("Audio has died (Encoder OK) : %s\n", where); res = -2; } } if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { /* */ if (res != -2 && read_dec(0x100) != read_dec(0x104)) { /* */ ivtv_msleep_timeout(14, 0); if (read_dec(0x100) != read_dec(0x104)) { IVTV_WARN("Audio has died (Decoder) : %s\n", where); res = -1; } } /* */ if (ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0) < 0) { IVTV_WARN("Decoder has died : %s\n", where); res = -1; } } /* */ 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); /* */ if (!res) { IVTV_INFO("Firmware restart okay\n"); res = -EAGAIN; } else { IVTV_INFO("Firmware restart failed\n"); } } else if (res) { res = -EIO; } return res; }
static int ivtv_api_call(struct ivtv *itv, int cmd, int args, u32 data[]) { struct ivtv_mailbox_data *mbdata = (cmd >= 128) ? &itv->enc_mbox : &itv->dec_mbox; volatile struct ivtv_mailbox __iomem *mbox; int api_timeout = msecs_to_jiffies(1000); int flags, mb, i; unsigned long then; /* sanity checks */ if (NULL == mbdata) { IVTV_ERR("No mailbox allocated\n"); return -ENODEV; } if (args < 0 || args > CX2341X_MBOX_MAX_DATA || cmd < 0 || cmd > 255 || api_info[cmd].name == NULL) { IVTV_ERR("Invalid MB call: cmd = 0x%02x, args = %d\n", cmd, args); return -EINVAL; } if (api_info[cmd].flags & API_HIGH_VOL) { IVTV_DEBUG_HI_MB("MB Call: %s\n", api_info[cmd].name); } else { IVTV_DEBUG_MB("MB Call: %s\n", api_info[cmd].name); } /* clear possibly uninitialized part of data array */ for (i = args; i < CX2341X_MBOX_MAX_DATA; i++) data[i] = 0; /* If this command was issued within the last 30 minutes and with identical data, then just return 0 as there is no need to issue this command again. Just an optimization to prevent unnecessary use of mailboxes. */ if (itv->api_cache[cmd].last_jiffies && time_before(jiffies, itv->api_cache[cmd].last_jiffies + msecs_to_jiffies(1800000)) && !memcmp(data, itv->api_cache[cmd].data, sizeof(itv->api_cache[cmd].data))) { itv->api_cache[cmd].last_jiffies = jiffies; return 0; } flags = api_info[cmd].flags; if (flags & API_DMA) { for (i = 0; i < 100; i++) { mb = i % (mbdata->max_mbox + 1); if (try_mailbox(itv, mbdata, mb)) { write_mailbox(&mbdata->mbox[mb], cmd, args, data); clear_bit(mb, &mbdata->busy); return 0; } IVTV_DEBUG_WARN("%s: mailbox %d not free %08x\n", api_info[cmd].name, mb, readl(&mbdata->mbox[mb].flags)); } IVTV_WARN("Could not find free DMA mailbox for %s\n", api_info[cmd].name); clear_all_mailboxes(itv, mbdata); return -EBUSY; } if ((flags & API_FAST_RESULT) == API_FAST_RESULT) api_timeout = msecs_to_jiffies(100); mb = get_mailbox(itv, mbdata, flags); if (mb < 0) { IVTV_DEBUG_WARN("No free mailbox found (%s)\n", api_info[cmd].name); clear_all_mailboxes(itv, mbdata); return -EBUSY; } mbox = &mbdata->mbox[mb]; write_mailbox(mbox, cmd, args, data); if (flags & API_CACHE) { memcpy(itv->api_cache[cmd].data, data, sizeof(itv->api_cache[cmd].data)); itv->api_cache[cmd].last_jiffies = jiffies; } if ((flags & API_RESULT) == 0) { clear_bit(mb, &mbdata->busy); return 0; } /* Get results */ then = jiffies; if (!(flags & API_NO_POLL)) { /* First try to poll, then switch to delays */ for (i = 0; i < 100; i++) { if (readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE) break; } } while (!(readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE)) { if (time_after(jiffies, then + api_timeout)) { IVTV_DEBUG_WARN("Could not get result (%s)\n", api_info[cmd].name); /* reset the mailbox, but it is likely too late already */ write_sync(0, &mbox->flags); clear_bit(mb, &mbdata->busy); return -EIO; } if (flags & API_NO_WAIT_RES) mdelay(1); else ivtv_msleep_timeout(1, 0); } if (time_after(jiffies, then + msecs_to_jiffies(100))) IVTV_DEBUG_WARN("%s took %u jiffies\n", api_info[cmd].name, jiffies_to_msecs(jiffies - then)); for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++) data[i] = readl(&mbox->data[i]); write_sync(0, &mbox->flags); clear_bit(mb, &mbdata->busy); return 0; }