📄 ivtv-yuv.c
字号:
reg_2964_base = (src_minor_y * ((f->dst_h << 16) / f->src_h)) >> 14; reg_2968_base = (src_minor_uv * ((f->dst_h << 16) / f->src_h)) >> 14; if (f->dst_h / 2 >= f->src_h && !f->interlaced_y) { master_height = (f->src_h * 0x00400000) / f->dst_h; if ((f->src_h * 0x00400000) - (master_height * f->dst_h) >= f->dst_h / 2) master_height++; reg_2920 = master_height >> 2; reg_2928 = master_height >> 3; reg_2930 = master_height; reg_2940 = master_height >> 1; reg_2964_base >>= 3; reg_2968_base >>= 3; reg_296c = 0x00000000; } else if (f->dst_h >= f->src_h) { master_height = (f->src_h * 0x00400000) / f->dst_h; master_height = (master_height >> 1) + (master_height & 1); reg_2920 = master_height >> 2; reg_2928 = master_height >> 2; reg_2930 = master_height; reg_2940 = master_height >> 1; reg_296c = 0x00000000; if (f->interlaced_y) { reg_2964_base >>= 3; } else { reg_296c++; reg_2964_base >>= 2; } if (f->interlaced_uv) reg_2928 >>= 1; reg_2968_base >>= 3; } else if (f->dst_h >= f->src_h / 2) { master_height = (f->src_h * 0x00200000) / f->dst_h; master_height = (master_height >> 1) + (master_height & 1); reg_2920 = master_height >> 2; reg_2928 = master_height >> 2; reg_2930 = master_height; reg_2940 = master_height; reg_296c = 0x00000101; if (f->interlaced_y) { reg_2964_base >>= 2; } else { reg_296c++; reg_2964_base >>= 1; } if (f->interlaced_uv) reg_2928 >>= 1; reg_2968_base >>= 2; } else { master_height = (f->src_h * 0x00100000) / f->dst_h; master_height = (master_height >> 1) + (master_height & 1); reg_2920 = master_height >> 2; reg_2928 = master_height >> 2; reg_2930 = master_height; reg_2940 = master_height; reg_2964_base >>= 1; reg_2968_base >>= 2; reg_296c = 0x00000102; } /* FIXME These registers change depending on scaled / unscaled output We really need to work out what they should be */ if (f->src_h == f->dst_h) { reg_2934 = 0x00020000; reg_293c = 0x00100000; reg_2944 = 0x00040000; reg_294c = 0x000b0000; } else { reg_2934 = 0x00000FF0; reg_293c = 0x00000FF0; reg_2944 = 0x00000FF0; reg_294c = 0x00000FF0; } /* The first line to be displayed */ reg_2950 = 0x00010000 + src_major_y; if (f->interlaced_y) reg_2950 += 0x00010000; reg_2954 = reg_2950 + 1; reg_2958 = 0x00010000 + (src_major_y >> 1); if (f->interlaced_uv) reg_2958 += 0x00010000; reg_295c = reg_2958 + 1; if (yi->decode_height == 480) reg_289c = 0x011e0017; else reg_289c = 0x01500017; if (f->dst_y < 0) reg_289c = (reg_289c - ((f->dst_y & ~1)<<15))-(f->dst_y >>1); else reg_289c = (reg_289c + ((f->dst_y & ~1)<<15))+(f->dst_y >>1); /* How much of the source to decode. Take into account the source offset */ reg_2960 = ((src_minor_y + f->src_h + src_major_y) - 1) | (((src_minor_uv + f->src_h + src_major_uv - 1) & ~1) << 15); /* Calculate correct value for register 2964 */ if (f->src_h == f->dst_h) { reg_2964 = 1; } else { reg_2964 = 2 + ((f->dst_h << 1) / f->src_h); reg_2964 = (reg_2964 >> 1) + (reg_2964 & 1); } reg_2968 = (reg_2964 << 16) + reg_2964 + (reg_2964 >> 1); reg_2964 = (reg_2964 << 16) + reg_2964 + (reg_2964 * 46 / 94); /* Okay, we've wasted time working out the correct value, but if we use it, it fouls the the window alignment. Fudge it to what we want... */ reg_2964 = 0x00010001 + ((reg_2964 & 0x0000FFFF) - (reg_2964 >> 16)); reg_2968 = 0x00010001 + ((reg_2968 & 0x0000FFFF) - (reg_2968 >> 16)); /* Deviate further from what it should be. I find the flicker headache inducing so try to reduce it slightly. Leave 2968 as-is otherwise colours foul. */ if ((reg_2964 != 0x00010001) && (f->dst_h / 2 <= f->src_h)) reg_2964 = (reg_2964 & 0xFFFF0000) + ((reg_2964 & 0x0000FFFF) / 2); if (!f->interlaced_y) reg_2964 -= 0x00010001; if (!f->interlaced_uv) reg_2968 -= 0x00010001; reg_2964 += ((reg_2964_base << 16) | reg_2964_base); reg_2968 += ((reg_2968_base << 16) | reg_2968_base); /* Select the vertical filter */ if (f->src_h == f->dst_h) { /* 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 = ((f->src_h << 16) / f->dst_h) >> 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; } write_reg(reg_2934, 0x02934); write_reg(reg_293c, 0x0293c); IVTV_DEBUG_YUV("Update reg 0x2934 %08x->%08x 0x293c %08x->%08x\n", yi->reg_2934, reg_2934, yi->reg_293c, reg_293c); write_reg(reg_2944, 0x02944); write_reg(reg_294c, 0x0294c); IVTV_DEBUG_YUV("Update reg 0x2944 %08x->%08x 0x294c %08x->%08x\n", yi->reg_2944, reg_2944, yi->reg_294c, reg_294c); /* Ensure 2970 is 0 (does it ever change ?) *//* write_reg(0,0x02970); *//* IVTV_DEBUG_YUV("Update reg 0x2970 %08x->%08x\n", yi->reg_2970, 0); */ write_reg(reg_2930, 0x02938); write_reg(reg_2930, 0x02930); IVTV_DEBUG_YUV("Update reg 0x2930 %08x->%08x 0x2938 %08x->%08x\n", yi->reg_2930, reg_2930, yi->reg_2938, reg_2930); write_reg(reg_2928, 0x02928); write_reg(reg_2928 + 0x514, 0x0292C); IVTV_DEBUG_YUV("Update reg 0x2928 %08x->%08x 0x292c %08x->%08x\n", yi->reg_2928, reg_2928, yi->reg_292c, reg_2928 + 0x514); write_reg(reg_2920, 0x02920); write_reg(reg_2920 + 0x514, 0x02924); IVTV_DEBUG_YUV("Update reg 0x2920 %08x->%08x 0x2924 %08x->%08x\n", yi->reg_2920, reg_2920, yi->reg_2924, reg_2920 + 0x514); write_reg(reg_2918, 0x02918); write_reg(reg_291c, 0x0291C); IVTV_DEBUG_YUV("Update reg 0x2918 %08x->%08x 0x291C %08x->%08x\n", yi->reg_2918, reg_2918, yi->reg_291c, reg_291c); write_reg(reg_296c, 0x0296c); IVTV_DEBUG_YUV("Update reg 0x296c %08x->%08x\n", yi->reg_296c, reg_296c); write_reg(reg_2940, 0x02948); write_reg(reg_2940, 0x02940); IVTV_DEBUG_YUV("Update reg 0x2940 %08x->%08x 0x2948 %08x->%08x\n", yi->reg_2940, reg_2940, yi->reg_2948, reg_2940); write_reg(reg_2950, 0x02950); write_reg(reg_2954, 0x02954); IVTV_DEBUG_YUV("Update reg 0x2950 %08x->%08x 0x2954 %08x->%08x\n", yi->reg_2950, reg_2950, yi->reg_2954, reg_2954); write_reg(reg_2958, 0x02958); write_reg(reg_295c, 0x0295C); IVTV_DEBUG_YUV("Update reg 0x2958 %08x->%08x 0x295C %08x->%08x\n", yi->reg_2958, reg_2958, yi->reg_295c, reg_295c); write_reg(reg_2960, 0x02960); IVTV_DEBUG_YUV("Update reg 0x2960 %08x->%08x \n", yi->reg_2960, reg_2960); write_reg(reg_2964, 0x02964); write_reg(reg_2968, 0x02968); IVTV_DEBUG_YUV("Update reg 0x2964 %08x->%08x 0x2968 %08x->%08x\n", yi->reg_2964, reg_2964, yi->reg_2968, reg_2968); write_reg(reg_289c, 0x0289c); IVTV_DEBUG_YUV("Update reg 0x289c %08x->%08x\n", yi->reg_289c, reg_289c); /* Only update filter 1 if we really need to */ if (v_filter_1 != yi->v_filter_1) { ivtv_yuv_filter(itv, -1, v_filter_1, -1); yi->v_filter_1 = v_filter_1; } /* Only update filter 2 if we really need to */ if (v_filter_2 != yi->v_filter_2) { ivtv_yuv_filter(itv, -1, -1, v_filter_2); yi->v_filter_2 = v_filter_2; }}/* Modify the supplied coordinate information to fit the visible osd area */static u32 ivtv_yuv_window_setup(struct ivtv *itv, struct yuv_frame_info *f){ struct yuv_frame_info *of = &itv->yuv_info.old_frame_info; int osd_crop; u32 osd_scale; u32 yuv_update = 0; /* Sorry, but no negative coords for src */ if (f->src_x < 0) f->src_x = 0; if (f->src_y < 0) f->src_y = 0; /* Can only reduce width down to 1/4 original size */ if ((osd_crop = f->src_w - 4 * f->dst_w) > 0) { f->src_x += osd_crop / 2; f->src_w = (f->src_w - osd_crop) & ~3; f->dst_w = f->src_w / 4; f->dst_w += f->dst_w & 1; } /* Can only reduce height down to 1/4 original size */ if (f->src_h / f->dst_h >= 2) { /* Overflow may be because we're running progressive, so force mode switch */ f->interlaced_y = 1; /* Make sure we're still within limits for interlace */ if ((osd_crop = f->src_h - 4 * f->dst_h) > 0) { /* If we reach here we'll have to force the height. */ f->src_y += osd_crop / 2; f->src_h = (f->src_h - osd_crop) & ~3; f->dst_h = f->src_h / 4; f->dst_h += f->dst_h & 1; } } /* If there's nothing to safe to display, we may as well stop now */ if ((int)f->dst_w <= 2 || (int)f->dst_h <= 2 || (int)f->src_w <= 2 || (int)f->src_h <= 2) { return IVTV_YUV_UPDATE_INVALID; } /* Ensure video remains inside OSD area */ osd_scale = (f->src_h << 16) / f->dst_h; if ((osd_crop = f->pan_y - f->dst_y) > 0) { /* Falls off the upper edge - crop */ f->src_y += (osd_scale * osd_crop) >> 16; f->src_h -= (osd_scale * osd_crop) >> 16; f->dst_h -= osd_crop; f->dst_y = 0; } else { f->dst_y -= f->pan_y; } if ((osd_crop = f->dst_h + f->dst_y - f->vis_h) > 0) { /* Falls off the lower edge - crop */ f->dst_h -= osd_crop; f->src_h -= (osd_scale * osd_crop) >> 16; } osd_scale = (f->src_w << 16) / f->dst_w; if ((osd_crop = f->pan_x - f->dst_x) > 0) { /* Fall off the left edge - crop */ f->src_x += (osd_scale * osd_crop) >> 16; f->src_w -= (osd_scale * osd_crop) >> 16; f->dst_w -= osd_crop; f->dst_x = 0; } else { f->dst_x -= f->pan_x; } if ((osd_crop = f->dst_w + f->dst_x - f->vis_w) > 0) { /* Falls off the right edge - crop */ f->dst_w -= osd_crop; f->src_w -= (osd_scale * osd_crop) >> 16; } if (itv->yuv_info.track_osd) { /* The OSD can be moved. Track to it */ f->dst_x += itv->yuv_info.osd_x_offset; f->dst_y += itv->yuv_info.osd_y_offset; } /* Width & height for both src & dst must be even. Same for coordinates. */ f->dst_w &= ~1; f->dst_x &= ~1; f->src_w += f->src_x & 1; f->src_x &= ~1; f->src_w &= ~1; f->dst_w &= ~1; f->dst_h &= ~1; f->dst_y &= ~1; f->src_h += f->src_y & 1; f->src_y &= ~1; f->src_h &= ~1; f->dst_h &= ~1; /* Due to rounding, we may have reduced the output size to <1/4 of the source. Check again, but this time just resize. Don't change source coordinates */ if (f->dst_w < f->src_w / 4) { f->src_w &= ~3; f->dst_w = f->src_w / 4; f->dst_w += f->dst_w & 1; } if (f->dst_h < f->src_h / 4) { f->src_h &= ~3; f->dst_h = f->src_h / 4; f->dst_h += f->dst_h & 1; } /* Check again. If there's nothing to safe to display, stop now */ if ((int)f->dst_w <= 2 || (int)f->dst_h <= 2 || (int)f->src_w <= 2 || (int)f->src_h <= 2) { return IVTV_YUV_UPDATE_INVALID; } /* Both x offset & width are linked, so they have to be done together */ if ((of->dst_w != f->dst_w) || (of->src_w != f->src_w) || (of->dst_x != f->dst_x) || (of->src_x != f->src_x) || (of->pan_x != f->pan_x) || (of->vis_w != f->vis_w)) { yuv_update |= IVTV_YUV_UPDATE_HORIZONTAL; } if ((of->src_h != f->src_h) || (of->dst_h != f->dst_h) || (of->dst_y != f->dst_y) || (of->src_y != f->src_y) || (of->pan_y != f->pan_y) || (of->vis_h != f->vis_h) || (of->lace_mode != f->lace_mode) || (of->interlaced_y != f->interlaced_y) || (of->interlaced_uv != f->interlaced_uv)) { yuv_update |= IVTV_YUV_UPDATE_VERTICAL; } return yuv_update;}/* Update the scaling register to the requested value */void ivtv_yuv_work_handler(struct ivtv *itv){ struct yuv_playback_info *yi = &itv->yuv_info; struct yuv_frame_info f; int frame = yi->update_frame; u32 yuv_update; IVTV_DEBUG_YUV("Update yuv registers for frame %d\n", frame); f = yi->new_frame_info[frame]; if (yi->track_osd) { /* Snapshot the osd pan info */ f.pan_x = yi->osd_x_pan; f.pan_y = yi->osd_y_pan; f.vis_w = yi->osd_vis_w; f.vis_h = yi->osd_vis_h; } else { /* Not tracking the osd, so assume full screen */ f.pan_x = 0; f.pan_y = 0; f.vis_w = 720; f.vis_h = yi->decode_height; } /* Calculate the display window coordinates. Exit if nothing left */ if (!(yuv_update = ivtv_yuv_window_setup(itv, &f))) return; if (yuv_update & IVTV_YUV_UPDATE_INVALID) { write_reg(0x01008080, 0x2898); } else if (yuv_update) { write_reg(0x00108080, 0x2898); if (yuv_update & IVTV_YUV_UPDATE_HORIZONTAL) ivtv_yuv_handle_horizontal(itv, &f); if (yuv_update & IVTV_YUV_UPDATE_VERTICAL) ivtv_yuv_handle_vertical(itv, &f); } yi->old_frame_info = f;}static void ivtv_yuv_init(struct ivtv *itv){ struct yuv_playback_info *yi = &itv->yuv_info; IVTV_DEBUG_YUV("ivtv_yuv_init\n"); /* Take a snapshot of the current register settings */ yi->reg_2834 = read_reg(0x02834);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -