📄 vino.c
字号:
} if (i == 0) { /* no buffers to process */ return 0; } fb = vino_queue_peek(&vcs->fb_queue, &i); if (fb == NULL) { dprintk("vino_queue_peek() failed\n"); return -EINVAL; } spin_lock_irqsave(&fb->state_lock, flags); if (fb->state == VINO_FRAMEBUFFER_IN_USE) { fb->state = VINO_FRAMEBUFFER_UNUSED; vino_queue_transfer(&vcs->fb_queue); vino_queue_remove(&vcs->fb_queue, &i); /* we should actually discard the newest frame, * but who cares ... */ } spin_unlock_irqrestore(&fb->state_lock, flags); return 0;}#endifstatic void vino_skip_frame(struct vino_channel_settings *vcs){ struct vino_framebuffer *fb; unsigned long flags; unsigned int id; spin_lock_irqsave(&vcs->capture_lock, flags); fb = vino_queue_peek(&vcs->fb_queue, &id); if (!fb) { spin_unlock_irqrestore(&vcs->capture_lock, flags); dprintk("vino_skip_frame(): vino_queue_peek() failed!\n"); return; } spin_unlock_irqrestore(&vcs->capture_lock, flags); spin_lock_irqsave(&fb->state_lock, flags); fb->state = VINO_FRAMEBUFFER_UNUSED; spin_unlock_irqrestore(&fb->state_lock, flags); vino_capture_next(vcs, 0);}static void vino_frame_done(struct vino_channel_settings *vcs){ struct vino_framebuffer *fb; unsigned long flags; spin_lock_irqsave(&vcs->capture_lock, flags); fb = vino_queue_transfer(&vcs->fb_queue); if (!fb) { spin_unlock_irqrestore(&vcs->capture_lock, flags); dprintk("vino_frame_done(): vino_queue_transfer() failed!\n"); return; } spin_unlock_irqrestore(&vcs->capture_lock, flags); fb->frame_counter = vcs->int_data.frame_counter; memcpy(&fb->timestamp, &vcs->int_data.timestamp, sizeof(struct timeval)); spin_lock_irqsave(&fb->state_lock, flags); if (fb->state == VINO_FRAMEBUFFER_IN_USE) fb->state = VINO_FRAMEBUFFER_READY; spin_unlock_irqrestore(&fb->state_lock, flags); wake_up(&vcs->fb_queue.frame_wait_queue); vino_capture_next(vcs, 0);}static void vino_capture_tasklet(unsigned long channel) { struct vino_channel_settings *vcs; vcs = (channel == VINO_CHANNEL_A) ? &vino_drvdata->a : &vino_drvdata->b; if (vcs->int_data.skip) vcs->int_data.skip_count++; if (vcs->int_data.skip && (vcs->int_data.skip_count <= VINO_MAX_FRAME_SKIP_COUNT)) { vino_skip_frame(vcs); } else { vcs->int_data.skip_count = 0; vino_frame_done(vcs); }}static irqreturn_t vino_interrupt(int irq, void *dev_id, struct pt_regs *regs){ u32 ctrl, intr; unsigned int fc_a, fc_b; int handled_a = 0, skip_a = 0, done_a = 0; int handled_b = 0, skip_b = 0, done_b = 0;#ifdef VINO_DEBUG_INT int loop = 0; unsigned int line_count = vino->a.line_count, page_index = vino->a.page_index, field_counter = vino->a.field_counter, start_desc_tbl = vino->a.start_desc_tbl, next_4_desc = vino->a.next_4_desc; unsigned int line_count_2, page_index_2, field_counter_2, start_desc_tbl_2, next_4_desc_2;#endif spin_lock(&vino_drvdata->vino_lock); while ((intr = vino->intr_status)) { fc_a = vino->a.field_counter >> 1; fc_b = vino->b.field_counter >> 1; /* handle error-interrupts in some special way ? * --> skips frames */ if (intr & VINO_INTSTAT_A) { if (intr & VINO_INTSTAT_A_EOF) { vino_drvdata->a.field++; if (vino_drvdata->a.field > 1) { vino_dma_stop(&vino_drvdata->a); vino_clear_interrupt(&vino_drvdata->a); vino_drvdata->a.field = 0; done_a = 1; } else { if (vino->a.page_index != vino_drvdata->a.line_size) { vino->a.line_count = 0; vino->a.page_index = vino_drvdata-> a.line_size; vino->a.next_4_desc = vino->a.start_desc_tbl; } } dprintk("channel A end-of-field " "interrupt: %04x\n", intr); } else { vino_dma_stop(&vino_drvdata->a); vino_clear_interrupt(&vino_drvdata->a); vino_drvdata->a.field = 0; skip_a = 1; dprintk("channel A error interrupt: %04x\n", intr); }#ifdef VINO_DEBUG_INT line_count_2 = vino->a.line_count; page_index_2 = vino->a.page_index; field_counter_2 = vino->a.field_counter; start_desc_tbl_2 = vino->a.start_desc_tbl; next_4_desc_2 = vino->a.next_4_desc; printk("intr = %04x, loop = %d, field = %d\n", intr, loop, vino_drvdata->a.field); printk("1- line count = %04d, page index = %04d, " "start = %08x, next = %08x\n" " fieldc = %d, framec = %d\n", line_count, page_index, start_desc_tbl, next_4_desc, field_counter, fc_a); printk("12-line count = %04d, page index = %04d, " " start = %08x, next = %08x\n", line_count_2, page_index_2, start_desc_tbl_2, next_4_desc_2); if (done_a) printk("\n");#endif } if (intr & VINO_INTSTAT_B) { if (intr & VINO_INTSTAT_B_EOF) { vino_drvdata->b.field++; if (vino_drvdata->b.field > 1) { vino_dma_stop(&vino_drvdata->b); vino_clear_interrupt(&vino_drvdata->b); vino_drvdata->b.field = 0; done_b = 1; } dprintk("channel B end-of-field " "interrupt: %04x\n", intr); } else { vino_dma_stop(&vino_drvdata->b); vino_clear_interrupt(&vino_drvdata->b); vino_drvdata->b.field = 0; skip_b = 1; dprintk("channel B error interrupt: %04x\n", intr); } } /* Always remember to clear interrupt status. * Disable VINO interrupts while we do this. */ ctrl = vino->control; vino->control = ctrl & ~(VINO_CTRL_A_INT | VINO_CTRL_B_INT); vino->intr_status = ~intr; vino->control = ctrl; spin_unlock(&vino_drvdata->vino_lock); if ((!handled_a) && (done_a || skip_a)) { if (!skip_a) { do_gettimeofday(&vino_drvdata-> a.int_data.timestamp); vino_drvdata->a.int_data.frame_counter = fc_a; } vino_drvdata->a.int_data.skip = skip_a; dprintk("channel A %s, interrupt: %d\n", skip_a ? "skipping frame" : "frame done", intr); tasklet_hi_schedule(&vino_tasklet_a); handled_a = 1; } if ((!handled_b) && (done_b || skip_b)) { if (!skip_b) { do_gettimeofday(&vino_drvdata-> b.int_data.timestamp); vino_drvdata->b.int_data.frame_counter = fc_b; } vino_drvdata->b.int_data.skip = skip_b; dprintk("channel B %s, interrupt: %d\n", skip_b ? "skipping frame" : "frame done", intr); tasklet_hi_schedule(&vino_tasklet_b); handled_b = 1; }#ifdef VINO_DEBUG_INT loop++;#endif spin_lock(&vino_drvdata->vino_lock); } spin_unlock(&vino_drvdata->vino_lock); return IRQ_HANDLED;}/* VINO video input management */static int vino_get_saa7191_input(int input){ switch (input) { case VINO_INPUT_COMPOSITE: return SAA7191_INPUT_COMPOSITE; case VINO_INPUT_SVIDEO: return SAA7191_INPUT_SVIDEO; default: printk(KERN_ERR "VINO: vino_get_saa7191_input(): " "invalid input!\n"); return -1; }}static int vino_get_saa7191_norm(unsigned int data_norm){ switch (data_norm) { case VINO_DATA_NORM_AUTO: return SAA7191_NORM_AUTO; case VINO_DATA_NORM_AUTO_EXT: return SAA7191_NORM_AUTO_EXT; case VINO_DATA_NORM_PAL: return SAA7191_NORM_PAL; case VINO_DATA_NORM_NTSC: return SAA7191_NORM_NTSC; case VINO_DATA_NORM_SECAM: return SAA7191_NORM_SECAM; default: printk(KERN_ERR "VINO: vino_get_saa7191_norm(): " "invalid norm!\n"); return -1; }}static int vino_get_from_saa7191_norm(int saa7191_norm){ switch (saa7191_norm) { case SAA7191_NORM_PAL: return VINO_DATA_NORM_PAL; case SAA7191_NORM_NTSC: return VINO_DATA_NORM_NTSC; case SAA7191_NORM_SECAM: return VINO_DATA_NORM_SECAM; default: printk(KERN_ERR "VINO: vino_get_from_saa7191_norm(): " "invalid norm!\n"); return VINO_DATA_NORM_NONE; }}static int vino_saa7191_set_norm(unsigned int *data_norm){ int saa7191_norm, new_data_norm; int err = 0; saa7191_norm = vino_get_saa7191_norm(*data_norm); err = i2c_decoder_command(DECODER_SAA7191_SET_NORM, &saa7191_norm); if (err) goto out; if ((*data_norm == VINO_DATA_NORM_AUTO) || (*data_norm == VINO_DATA_NORM_AUTO_EXT)) { struct saa7191_status status; err = i2c_decoder_command(DECODER_SAA7191_GET_STATUS, &status); if (err) goto out; new_data_norm = vino_get_from_saa7191_norm(status.norm); if (new_data_norm == VINO_DATA_NORM_NONE) { err = -EINVAL; goto out; } *data_norm = (unsigned int)new_data_norm; }out: return err;}/* execute with input_lock locked */static int vino_is_input_owner(struct vino_channel_settings *vcs){ switch(vcs->input) { case VINO_INPUT_COMPOSITE: case VINO_INPUT_SVIDEO: return (vino_drvdata->decoder.owner == vcs->channel); case VINO_INPUT_D1: return (vino_drvdata->camera.owner == vcs->channel); default: return 0; }}static int vino_acquire_input(struct vino_channel_settings *vcs){ unsigned long flags; int ret = 0; dprintk("vino_acquire_input():\n"); spin_lock_irqsave(&vino_drvdata->input_lock, flags); /* First try D1 and then SAA7191 */ if (vino_drvdata->camera.driver && (vino_drvdata->camera.owner == VINO_NO_CHANNEL)) { if (i2c_use_client(vino_drvdata->camera.driver)) { ret = -ENODEV; goto out; } vino_drvdata->camera.owner = vcs->channel; vcs->input = VINO_INPUT_D1; vcs->data_norm = VINO_DATA_NORM_D1; } else if (vino_drvdata->decoder.driver && (vino_drvdata->decoder.owner == VINO_NO_CHANNEL)) { int input, data_norm; int saa7191_input; if (i2c_use_client(vino_drvdata->decoder.driver)) { ret = -ENODEV; goto out; } input = VINO_INPUT_COMPOSITE; saa7191_input = vino_get_saa7191_input(input); ret = i2c_decoder_command(DECODER_SET_INPUT, &saa7191_input); if (ret) { ret = -EINVAL; goto out; } spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); /* Don't hold spinlocks while auto-detecting norm * as it may take a while... */ data_norm = VINO_DATA_NORM_AUTO_EXT; ret = vino_saa7191_set_norm(&data_norm); if ((ret == -EBUSY) || (ret == -EAGAIN)) { data_norm = VINO_DATA_NORM_PAL; ret = vino_saa7191_set_norm(&data_norm); } spin_lock_irqsave(&vino_drvdata->input_lock, flags); if (ret) { ret = -EINVAL; goto out; } vino_drvdata->decoder.owner = vcs->channel; vcs->input = input; vcs->data_norm = data_norm; } else { vcs->input = (vcs->channel == VINO_CHANNEL_A) ? vino_drvdata->b.input : vino_drvdata->a.input; vcs->data_norm = (vcs->channel == VINO_CHANNEL_A) ? vino_drvdata->b.data_norm : vino_drvdata->a.data_norm; } if (vcs->input == VINO_INPUT_NONE) { ret = -ENODEV; goto out; } vino_set_default_clipping(vcs); vino_set_default_scaling(vcs); vino_set_default_framerate(vcs); dprintk("vino_acquire_input(): %s\n", vino_inputs[vcs->input].name);out: spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); return ret;}static int vino_set_input(struct vino_channel_settings *vcs, int input){ struct vino_channel_settings *vcs2 = (vcs->channel == VINO_CHANNEL_A) ? &vino_drvdata->b : &vino_drvdata->a; unsigned long flags; int ret = 0; dprintk("vino_set_input():\n"); spin_lock_irqsave(&vino_drvdata->input_lock, flags); if (vcs->input == input) goto out; switch (input) { case VINO_INPUT_COMPOSITE: case VINO_INPUT_SVIDEO: if (!vino_drvdata->decoder.driver) { ret = -EINVAL; goto out; } if (vino_drvdata->decoder.owner == VINO_NO_CHANNEL) { if (i2c_use_client(vino_drvdata->decoder.driver)) { ret = -ENODEV; goto out; } vino_drvdata->decoder.owner = vcs->channel; } if (vino_drvdata->decoder.owner == vcs->channel) { int data_norm; int saa7191_input; saa7191_input = vino_get_saa7191_input(input); ret = i2c_decoder_command(DECODER_SET_INPUT, &saa7191_input); if (ret) { vino_drvdata->decoder.owner = VINO_NO_CHANNEL; ret = -EINVAL; goto out; } spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); /* Don't hold spinlocks while auto-detecting norm * as it may take a while... */ data_norm = VINO_DATA_NORM_AUTO_EXT; ret = vino_saa7191_set_norm(&data_norm); if ((ret == -EBUSY) || (ret == -EAGAIN)) { data_norm = VINO_DATA_NORM_PAL; ret = vino_saa7191_set_norm(&data_norm); } spin_lock_irqsave(&vino_drvdata->input_lock, flags); if (ret) { vino_drvdata->decoder.owner = VINO_NO_CHANNEL; ret = -EINVAL; goto out; } vcs->input = input; vcs->data_norm = data_norm; } else { if (input != vcs2->input) { ret = -EBUSY; goto out; } vcs->input = input; vcs->data_norm = vcs2->data_norm; } if (vino_drvdata->camera.owner == vcs->channel) { /* Transfer the ownership or release the input */ if (vcs2->input == VINO_INPUT_D1) { vino_drvdata->camera.owner = vcs2->channel; } else { i2c_release_client(vino_drvdata-> camera.driver); vino_drvdata->camera.owner = VINO_NO_CHANNEL; } } break; case VINO_INPUT_D1: if (!vino_drvdata->camera.driver) { ret = -EINVAL; goto out; } if (vino_drvdata->camera.owner == VINO_NO_CHANNEL) { if (i2c_use_client(vino_drvdata->camera.driver)) { ret = -ENODEV; goto out; } vino_drvdata->camera.owner = vcs->channel; } if (vino_drvdata->decoder.owner == vcs->channel) { /* Transfer the ownership or release the input */ if ((vcs2->input == VINO_INPUT_COMPOSITE) || (vcs2->input == VINO_INPUT_SVIDEO)) { vino_drvdata->decoder.owner = vcs2->channel; } else { i2c_release_client(vino_drvdata-> decoder.driver); vino_drvdata->decoder.owner = VINO_NO_CHANNEL; } } vcs->input = input; vcs->data_norm = VINO_DATA_NORM_D1; break; default: ret = -EINVAL; goto out; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -