📄 ivtv-yuv.c
字号:
yi->reg_2838 = read_reg(0x02838); yi->reg_283c = read_reg(0x0283c); yi->reg_2840 = read_reg(0x02840); yi->reg_2844 = read_reg(0x02844); yi->reg_2848 = read_reg(0x02848); yi->reg_2854 = read_reg(0x02854); yi->reg_285c = read_reg(0x0285c); yi->reg_2864 = read_reg(0x02864); yi->reg_2870 = read_reg(0x02870); yi->reg_2874 = read_reg(0x02874); yi->reg_2898 = read_reg(0x02898); yi->reg_2890 = read_reg(0x02890); yi->reg_289c = read_reg(0x0289c); yi->reg_2918 = read_reg(0x02918); yi->reg_291c = read_reg(0x0291c); yi->reg_2920 = read_reg(0x02920); yi->reg_2924 = read_reg(0x02924); yi->reg_2928 = read_reg(0x02928); yi->reg_292c = read_reg(0x0292c); yi->reg_2930 = read_reg(0x02930); yi->reg_2934 = read_reg(0x02934); yi->reg_2938 = read_reg(0x02938); yi->reg_293c = read_reg(0x0293c); yi->reg_2940 = read_reg(0x02940); yi->reg_2944 = read_reg(0x02944); yi->reg_2948 = read_reg(0x02948); yi->reg_294c = read_reg(0x0294c); yi->reg_2950 = read_reg(0x02950); yi->reg_2954 = read_reg(0x02954); yi->reg_2958 = read_reg(0x02958); yi->reg_295c = read_reg(0x0295c); yi->reg_2960 = read_reg(0x02960); yi->reg_2964 = read_reg(0x02964); yi->reg_2968 = read_reg(0x02968); yi->reg_296c = read_reg(0x0296c); yi->reg_2970 = read_reg(0x02970); yi->v_filter_1 = -1; yi->v_filter_2 = -1; yi->h_filter = -1; /* Set some valid size info */ yi->osd_x_offset = read_reg(0x02a04) & 0x00000FFF; yi->osd_y_offset = (read_reg(0x02a04) >> 16) & 0x00000FFF; /* Bit 2 of reg 2878 indicates current decoder output format 0 : NTSC 1 : PAL */ if (read_reg(0x2878) & 4) yi->decode_height = 576; else yi->decode_height = 480; if (!itv->osd_info) { yi->osd_vis_w = 720 - yi->osd_x_offset; yi->osd_vis_h = yi->decode_height - yi->osd_y_offset; } else { /* If no visible size set, assume full size */ if (!yi->osd_vis_w) yi->osd_vis_w = 720 - yi->osd_x_offset; if (!yi->osd_vis_h) { yi->osd_vis_h = yi->decode_height - yi->osd_y_offset; } else if (yi->osd_vis_h + yi->osd_y_offset > yi->decode_height) { /* If output video standard has changed, requested height may not be legal */ IVTV_DEBUG_WARN("Clipping yuv output - fb size (%d) exceeds video standard limit (%d)\n", yi->osd_vis_h + yi->osd_y_offset, yi->decode_height); yi->osd_vis_h = yi->decode_height - yi->osd_y_offset; } } /* We need a buffer for blanking when Y plane is offset - non-fatal if we can't get one */ yi->blanking_ptr = kzalloc(720 * 16, GFP_KERNEL|__GFP_NOWARN); if (yi->blanking_ptr) { yi->blanking_dmaptr = pci_map_single(itv->dev, yi->blanking_ptr, 720*16, PCI_DMA_TODEVICE); } else { yi->blanking_dmaptr = 0; IVTV_DEBUG_WARN("Failed to allocate yuv blanking buffer\n"); } /* Enable YUV decoder output */ write_reg_sync(0x01, IVTV_REG_VDM); set_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags); atomic_set(&yi->next_dma_frame, 0);}/* Get next available yuv buffer on PVR350 */static void ivtv_yuv_next_free(struct ivtv *itv){ int draw, display; struct yuv_playback_info *yi = &itv->yuv_info; if (atomic_read(&yi->next_dma_frame) == -1) ivtv_yuv_init(itv); draw = atomic_read(&yi->next_fill_frame); display = atomic_read(&yi->next_dma_frame); if (display > draw) display -= IVTV_YUV_BUFFERS; if (draw - display >= yi->max_frames_buffered) draw = (u8)(draw - 1) % IVTV_YUV_BUFFERS; else yi->new_frame_info[draw].update = 0; yi->draw_frame = draw;}/* Set up frame according to ivtv_dma_frame parameters */static void ivtv_yuv_setup_frame(struct ivtv *itv, struct ivtv_dma_frame *args){ struct yuv_playback_info *yi = &itv->yuv_info; u8 frame = yi->draw_frame; u8 last_frame = (u8)(frame - 1) % IVTV_YUV_BUFFERS; struct yuv_frame_info *nf = &yi->new_frame_info[frame]; struct yuv_frame_info *of = &yi->new_frame_info[last_frame]; int lace_threshold = yi->lace_threshold; /* Preserve old update flag in case we're overwriting a queued frame */ int update = nf->update; /* Take a snapshot of the yuv coordinate information */ nf->src_x = args->src.left; nf->src_y = args->src.top; nf->src_w = args->src.width; nf->src_h = args->src.height; nf->dst_x = args->dst.left; nf->dst_y = args->dst.top; nf->dst_w = args->dst.width; nf->dst_h = args->dst.height; nf->tru_x = args->dst.left; nf->tru_w = args->src_width; nf->tru_h = args->src_height; /* Are we going to offset the Y plane */ nf->offset_y = (nf->tru_h + nf->src_x < 512 - 16) ? 1 : 0; nf->update = 0; nf->interlaced_y = 0; nf->interlaced_uv = 0; nf->delay = 0; nf->sync_field = 0; nf->lace_mode = yi->lace_mode & IVTV_YUV_MODE_MASK; if (lace_threshold < 0) lace_threshold = yi->decode_height - 1; /* Work out the lace settings */ switch (nf->lace_mode) { case IVTV_YUV_MODE_PROGRESSIVE: /* Progressive mode */ nf->interlaced = 0; if (nf->tru_h < 512 || (nf->tru_h > 576 && nf->tru_h < 1021)) nf->interlaced_y = 0; else nf->interlaced_y = 1; if (nf->tru_h < 1021 && (nf->dst_h >= nf->src_h / 2)) nf->interlaced_uv = 0; else nf->interlaced_uv = 1; break; case IVTV_YUV_MODE_AUTO: if (nf->tru_h <= lace_threshold || nf->tru_h > 576 || nf->tru_w > 720) { nf->interlaced = 0; if ((nf->tru_h < 512) || (nf->tru_h > 576 && nf->tru_h < 1021) || (nf->tru_w > 720 && nf->tru_h < 1021)) nf->interlaced_y = 0; else nf->interlaced_y = 1; if (nf->tru_h < 1021 && (nf->dst_h >= nf->src_h / 2)) nf->interlaced_uv = 0; else nf->interlaced_uv = 1; } else { nf->interlaced = 1; nf->interlaced_y = 1; nf->interlaced_uv = 1; } break; case IVTV_YUV_MODE_INTERLACED: /* Interlace mode */ default: nf->interlaced = 1; nf->interlaced_y = 1; nf->interlaced_uv = 1; break; } if (memcmp(&yi->old_frame_info_args, nf, sizeof(*nf))) { yi->old_frame_info_args = *nf; nf->update = 1; IVTV_DEBUG_YUV("Requesting reg update for frame %d\n", frame); } nf->update |= update; nf->sync_field = yi->lace_sync_field; nf->delay = nf->sync_field != of->sync_field;}/* Frame is complete & ready for display */void ivtv_yuv_frame_complete(struct ivtv *itv){ atomic_set(&itv->yuv_info.next_fill_frame, (itv->yuv_info.draw_frame + 1) % IVTV_YUV_BUFFERS);}static int ivtv_yuv_udma_frame(struct ivtv *itv, struct ivtv_dma_frame *args){ DEFINE_WAIT(wait); int rc = 0; int got_sig = 0; /* DMA the frame */ mutex_lock(&itv->udma.lock); if ((rc = ivtv_yuv_prep_user_dma(itv, &itv->udma, args)) != 0) { mutex_unlock(&itv->udma.lock); return rc; } ivtv_udma_prepare(itv); prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); /* if no UDMA is pending and no UDMA is in progress, then the DMA is finished */ while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) { /* don't interrupt if the DMA is in progress but break off a still pending DMA. */ got_sig = signal_pending(current); if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) break; got_sig = 0; schedule(); } finish_wait(&itv->dma_waitq, &wait); /* Unmap Last DMA Xfer */ ivtv_udma_unmap(itv); if (got_sig) { IVTV_DEBUG_INFO("User stopped YUV UDMA\n"); mutex_unlock(&itv->udma.lock); return -EINTR; } ivtv_yuv_frame_complete(itv); mutex_unlock(&itv->udma.lock); return rc;}/* Setup frame according to V4L2 parameters */void ivtv_yuv_setup_stream_frame(struct ivtv *itv){ struct yuv_playback_info *yi = &itv->yuv_info; struct ivtv_dma_frame dma_args; ivtv_yuv_next_free(itv); /* Copy V4L2 parameters to an ivtv_dma_frame struct... */ dma_args.y_source = NULL; dma_args.uv_source = NULL; dma_args.src.left = 0; dma_args.src.top = 0; dma_args.src.width = yi->v4l2_src_w; dma_args.src.height = yi->v4l2_src_h; dma_args.dst = yi->main_rect; dma_args.src_width = yi->v4l2_src_w; dma_args.src_height = yi->v4l2_src_h; /* ... and use the same setup routine as ivtv_yuv_prep_frame */ ivtv_yuv_setup_frame(itv, &dma_args); if (!itv->dma_data_req_offset) itv->dma_data_req_offset = yuv_offset[yi->draw_frame];}/* Attempt to dma a frame from a user buffer */int ivtv_yuv_udma_stream_frame(struct ivtv *itv, void __user *src){ struct yuv_playback_info *yi = &itv->yuv_info; struct ivtv_dma_frame dma_args; ivtv_yuv_setup_stream_frame(itv); /* We only need to supply source addresses for this */ dma_args.y_source = src; dma_args.uv_source = src + 720 * ((yi->v4l2_src_h + 31) & ~31); return ivtv_yuv_udma_frame(itv, &dma_args);}/* IVTV_IOC_DMA_FRAME ioctl handler */int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args){/* IVTV_DEBUG_INFO("yuv_prep_frame\n"); */ ivtv_yuv_next_free(itv); ivtv_yuv_setup_frame(itv, args); return ivtv_yuv_udma_frame(itv, args);}void ivtv_yuv_close(struct ivtv *itv){ struct yuv_playback_info *yi = &itv->yuv_info; int h_filter, v_filter_1, v_filter_2; IVTV_DEBUG_YUV("ivtv_yuv_close\n"); ivtv_waitq(&itv->vsync_waitq); yi->running = 0; atomic_set(&yi->next_dma_frame, -1); atomic_set(&yi->next_fill_frame, 0); /* Reset registers we have changed so mpeg playback works */ /* If we fully restore this register, the display may remain active. Restore, but set one bit to blank the video. Firmware will always clear this bit when needed, so not a problem. */ write_reg(yi->reg_2898 | 0x01000000, 0x2898); write_reg(yi->reg_2834, 0x02834); write_reg(yi->reg_2838, 0x02838); write_reg(yi->reg_283c, 0x0283c); write_reg(yi->reg_2840, 0x02840); write_reg(yi->reg_2844, 0x02844); write_reg(yi->reg_2848, 0x02848); write_reg(yi->reg_2854, 0x02854); write_reg(yi->reg_285c, 0x0285c); write_reg(yi->reg_2864, 0x02864); write_reg(yi->reg_2870, 0x02870); write_reg(yi->reg_2874, 0x02874); write_reg(yi->reg_2890, 0x02890); write_reg(yi->reg_289c, 0x0289c); write_reg(yi->reg_2918, 0x02918); write_reg(yi->reg_291c, 0x0291c); write_reg(yi->reg_2920, 0x02920); write_reg(yi->reg_2924, 0x02924); write_reg(yi->reg_2928, 0x02928); write_reg(yi->reg_292c, 0x0292c); write_reg(yi->reg_2930, 0x02930); write_reg(yi->reg_2934, 0x02934); write_reg(yi->reg_2938, 0x02938); write_reg(yi->reg_293c, 0x0293c); write_reg(yi->reg_2940, 0x02940); write_reg(yi->reg_2944, 0x02944); write_reg(yi->reg_2948, 0x02948); write_reg(yi->reg_294c, 0x0294c); write_reg(yi->reg_2950, 0x02950); write_reg(yi->reg_2954, 0x02954); write_reg(yi->reg_2958, 0x02958); write_reg(yi->reg_295c, 0x0295c); write_reg(yi->reg_2960, 0x02960); write_reg(yi->reg_2964, 0x02964); write_reg(yi->reg_2968, 0x02968); write_reg(yi->reg_296c, 0x0296c); write_reg(yi->reg_2970, 0x02970); /* Prepare to restore filters */ /* First the horizontal filter */ if ((yi->reg_2834 & 0x0000FFFF) == (yi->reg_2834 >> 16)) { /* An exact size match uses filter 0 */ h_filter = 0; } else { /* Figure out which filter to use */ h_filter = ((yi->reg_2834 << 16) / (yi->reg_2834 >> 16)) >> 15; h_filter = (h_filter >> 1) + (h_filter & 1); /* Only an exact size match can use filter 0. */ h_filter += !h_filter; } /* Now the vertical filter */ if ((yi->reg_2918 & 0x0000FFFF) == (yi->reg_2918 >> 16)) { /* An exact size match uses filter 0/1 */ v_filter_1 = 0; v_filter_2 = 1; } else { /* Figure out which filter to use */ v_filter_1 = ((yi->reg_2918 << 16) / (yi->reg_2918 >> 16)) >> 15; v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1); /* Only an exact size match can use filter 0 */ v_filter_1 += !v_filter_1; v_filter_2 = v_filter_1; } /* Now restore the filters */ ivtv_yuv_filter(itv, h_filter, v_filter_1, v_filter_2); /* and clear a few registers */ write_reg(0, 0x02814); write_reg(0, 0x0282c); write_reg(0, 0x02904); write_reg(0, 0x02910); /* Release the blanking buffer */ if (yi->blanking_ptr) { kfree(yi->blanking_ptr); yi->blanking_ptr = NULL; pci_unmap_single(itv->dev, yi->blanking_dmaptr, 720*16, PCI_DMA_TODEVICE); } /* Invalidate the old dimension information */ yi->old_frame_info.src_w = 0; yi->old_frame_info.src_h = 0; yi->old_frame_info_args.src_w = 0; yi->old_frame_info_args.src_h = 0; /* All done. */ clear_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -