📄 ivtvfb.c
字号:
/* If transfer size > threshold and both src/dst addresses are aligned, use DMA */ if (count >= 4096 && ((unsigned long)buf & 3) == ((unsigned long)dst & 3)) { /* Odd address = can't DMA. Align */ if ((unsigned long)dst & 3) { lead = 4 - ((unsigned long)dst & 3); if (copy_from_user(dst, buf, lead)) return -EFAULT; buf += lead; dst += lead; } /* DMA resolution is 32 bits */ if ((count - lead) & 3) tail = (count - lead) & 3; /* DMA the data */ dma_size = count - lead - tail; dma_err = ivtvfb_prep_dec_dma_to_device(itv, p + lead + dma_offset, (void __user *)buf, dma_size); if (dma_err) return dma_err; dst += dma_size; buf += dma_size; /* Copy any leftover data */ if (tail && copy_from_user(dst, buf, tail)) return -EFAULT; } else if (copy_from_user(dst, buf, count)) { return -EFAULT; } if (!err) *ppos += count; return (err) ? err : count;}static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg){ DEFINE_WAIT(wait); struct ivtv *itv = (struct ivtv *)info->par; int rc = 0; switch (cmd) { case FBIOGET_VBLANK: { struct fb_vblank vblank; u32 trace; vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_VSYNC; trace = read_reg(0x028c0) >> 16; if (itv->is_50hz && trace > 312) trace -= 312; else if (itv->is_60hz && trace > 262) trace -= 262; if (trace == 1) vblank.flags |= FB_VBLANK_VSYNCING; vblank.count = itv->last_vsync_field; vblank.vcount = trace; vblank.hcount = 0; if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank))) return -EFAULT; return 0; } case FBIO_WAITFORVSYNC: prepare_to_wait(&itv->vsync_waitq, &wait, TASK_INTERRUPTIBLE); if (!schedule_timeout(msecs_to_jiffies(50))) rc = -ETIMEDOUT; finish_wait(&itv->vsync_waitq, &wait); return rc; case IVTVFB_IOC_DMA_FRAME: { struct ivtvfb_dma_frame args; IVTVFB_DEBUG_INFO("IVTVFB_IOC_DMA_FRAME\n"); if (copy_from_user(&args, (void __user *)arg, sizeof(args))) return -EFAULT; return ivtvfb_prep_frame(itv, cmd, args.source, args.dest_offset, args.count); } default: IVTVFB_DEBUG_INFO("Unknown ioctl %08x\n", cmd); return -EINVAL; } return 0;}/* Framebuffer device handling */static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var){ struct osd_info *oi = itv->osd_info; struct ivtv_osd_coords ivtv_osd; struct v4l2_rect ivtv_window; int osd_mode = -1; IVTVFB_DEBUG_INFO("ivtvfb_set_var\n"); /* Select color space */ if (var->nonstd) /* YUV */ write_reg(read_reg(0x02a00) | 0x0002000, 0x02a00); else /* RGB */ write_reg(read_reg(0x02a00) & ~0x0002000, 0x02a00); /* Set the color mode */ switch (var->bits_per_pixel) { case 8: osd_mode = IVTV_OSD_BPP_8; break; case 32: osd_mode = IVTV_OSD_BPP_32; break; case 16: switch (var->green.length) { case 4: osd_mode = IVTV_OSD_BPP_16_444; break; case 5: osd_mode = IVTV_OSD_BPP_16_555; break; case 6: osd_mode = IVTV_OSD_BPP_16_565; break; default: IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n"); } break; default: IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n"); } /* Set video mode. Although rare, the display can become scrambled even if we don't change mode. Always 'bounce' to osd_mode via mode 0 */ if (osd_mode != -1) { ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0); ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, osd_mode); } oi->bits_per_pixel = var->bits_per_pixel; oi->bytes_per_pixel = var->bits_per_pixel / 8; /* Set the flicker filter */ switch (var->vmode & FB_VMODE_MASK) { case FB_VMODE_NONINTERLACED: /* Filter on */ ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 1); break; case FB_VMODE_INTERLACED: /* Filter off */ ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 0); break; default: IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid video mode\n"); } /* Read the current osd info */ ivtvfb_get_osd_coords(itv, &ivtv_osd); /* Now set the OSD to the size we want */ ivtv_osd.pixel_stride = var->xres_virtual; ivtv_osd.lines = var->yres_virtual; ivtv_osd.x = 0; ivtv_osd.y = 0; ivtvfb_set_osd_coords(itv, &ivtv_osd); /* Can't seem to find the right API combo for this. Use another function which does what we need through direct register access. */ ivtv_window.width = var->xres; ivtv_window.height = var->yres; /* Minimum margin cannot be 0, as X won't allow such a mode */ if (!var->upper_margin) var->upper_margin++; if (!var->left_margin) var->left_margin++; ivtv_window.top = var->upper_margin - 1; ivtv_window.left = var->left_margin - 1; ivtvfb_set_display_window(itv, &ivtv_window); /* Pass screen size back to yuv handler */ itv->yuv_info.osd_full_w = ivtv_osd.pixel_stride; itv->yuv_info.osd_full_h = ivtv_osd.lines; /* Force update of yuv registers */ itv->yuv_info.yuv_forced_update = 1; IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n", var->xres, var->yres, var->xres_virtual, var->yres_virtual, var->bits_per_pixel); IVTVFB_DEBUG_INFO("Display position: %d, %d\n", var->left_margin, var->upper_margin); IVTVFB_DEBUG_INFO("Display filter: %s\n", (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off"); IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB"); return 0;}static int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix){ struct osd_info *oi = itv->osd_info; IVTVFB_DEBUG_INFO("ivtvfb_get_fix\n"); memset(fix, 0, sizeof(struct fb_fix_screeninfo)); strlcpy(fix->id, "cx23415 TV out", sizeof(fix->id)); fix->smem_start = oi->video_pbase; fix->smem_len = oi->video_buffer_size; fix->type = FB_TYPE_PACKED_PIXELS; fix->visual = (oi->bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; fix->xpanstep = 1; fix->ypanstep = 1; fix->ywrapstep = 0; fix->line_length = oi->display_byte_stride; fix->accel = FB_ACCEL_NONE; return 0;}/* Check the requested display mode, returning -EINVAL if we can't handle it. */static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv){ struct osd_info *oi = itv->osd_info; int osd_height_limit; u32 pixclock, hlimit, vlimit; IVTVFB_DEBUG_INFO("ivtvfb_check_var\n"); /* Set base references for mode calcs. */ if (itv->is_50hz) { pixclock = 84316; hlimit = 776; vlimit = 591; osd_height_limit = 576; } else { pixclock = 83926; hlimit = 776; vlimit = 495; osd_height_limit = 480; } if (var->bits_per_pixel == 8 || var->bits_per_pixel == 32) { var->transp.offset = 24; var->transp.length = 8; var->red.offset = 16; var->red.length = 8; var->green.offset = 8; var->green.length = 8; var->blue.offset = 0; var->blue.length = 8; } else if (var->bits_per_pixel == 16) { /* To find out the true mode, check green length */ switch (var->green.length) { case 4: var->red.offset = 8; var->red.length = 4; var->green.offset = 4; var->green.length = 4; var->blue.offset = 0; var->blue.length = 4; var->transp.offset = 12; var->transp.length = 1; break; case 5: var->red.offset = 10; var->red.length = 5; var->green.offset = 5; var->green.length = 5; var->blue.offset = 0; var->blue.length = 5; var->transp.offset = 15; var->transp.length = 1; break; default: var->red.offset = 11; var->red.length = 5; var->green.offset = 5; var->green.length = 6; var->blue.offset = 0; var->blue.length = 5; var->transp.offset = 0; var->transp.length = 0; break; } } else { IVTVFB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel); return -EINVAL; } /* Check the resolution */ if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit) { IVTVFB_DEBUG_WARN("Invalid resolution: %dx%d\n", var->xres, var->yres); return -EINVAL; } /* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */ if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) || var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8) > oi->video_buffer_size || var->xres_virtual < var->xres || var->yres_virtual < var->yres) { IVTVFB_DEBUG_WARN("Invalid virtual resolution: %dx%d\n", var->xres_virtual, var->yres_virtual); return -EINVAL; } /* Some extra checks if in 8 bit mode */ if (var->bits_per_pixel == 8) { /* Width must be a multiple of 4 */ if (var->xres & 3) { IVTVFB_DEBUG_WARN("Invalid resolution for 8bpp: %d\n", var->xres); return -EINVAL; } if (var->xres_virtual & 3) { IVTVFB_DEBUG_WARN("Invalid virtual resolution for 8bpp: %d)\n", var->xres_virtual); return -EINVAL; } } else if (var->bits_per_pixel == 16) { /* Width must be a multiple of 2 */ if (var->xres & 1) { IVTVFB_DEBUG_WARN("Invalid resolution for 16bpp: %d\n", var->xres); return -EINVAL; } if (var->xres_virtual & 1) { IVTVFB_DEBUG_WARN("Invalid virtual resolution for 16bpp: %d)\n", var->xres_virtual); return -EINVAL; } } /* Now check the offsets */ if (var->xoffset >= var->xres_virtual || var->yoffset >= var->yres_virtual) { IVTVFB_DEBUG_WARN("Invalid offset: %d (%d) %d (%d)\n", var->xoffset, var->xres_virtual, var->yoffset, var->yres_virtual); return -EINVAL; } /* Check pixel format */ if (var->nonstd > 1) { IVTVFB_DEBUG_WARN("Invalid nonstd % d\n", var->nonstd); return -EINVAL; } /* Check video mode */ if (((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) && ((var->vmode & FB_VMODE_MASK) != FB_VMODE_INTERLACED)) { IVTVFB_DEBUG_WARN("Invalid video mode: %d\n", var->vmode & FB_VMODE_MASK); return -EINVAL; } /* Check the left & upper margins If the margins are too large, just center the screen (enforcing margins causes too many problems) */ if (var->left_margin + var->xres > IVTV_OSD_MAX_WIDTH + 1) { var->left_margin = 1 + ((IVTV_OSD_MAX_WIDTH - var->xres) / 2); } if (var->upper_margin + var->yres > (itv->is_50hz ? 577 : 481)) { var->upper_margin = 1 + (((itv->is_50hz ? 576 : 480) - var->yres) / 2); } /* Maintain overall 'size' for a constant refresh rate */ var->right_margin = hlimit - var->left_margin - var->xres; var->lower_margin = vlimit - var->upper_margin - var->yres; /* Fixed sync times */ var->hsync_len = 24; var->vsync_len = 2; /* Non-interlaced / interlaced mode is used to switch the OSD filter on or off. Adjust the clock timings to maintain a constant vertical refresh rate. */ if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED) var->pixclock = pixclock / 2; else var->pixclock = pixclock; itv->osd_rect.width = var->xres; itv->osd_rect.height = var->yres; IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n", var->xres, var->yres, var->xres_virtual, var->yres_virtual, var->bits_per_pixel); IVTVFB_DEBUG_INFO("Display position: %d, %d\n", var->left_margin, var->upper_margin); IVTVFB_DEBUG_INFO("Display filter: %s\n", (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off"); IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB"); return 0;}static int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info){ struct ivtv *itv = (struct ivtv *) info->par; IVTVFB_DEBUG_INFO("ivtvfb_check_var\n"); return _ivtvfb_check_var(var, itv);}static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info){ u32 osd_pan_index; struct ivtv *itv = (struct ivtv *) info->par; osd_pan_index = (var->xoffset + (var->yoffset * var->xres_virtual))*var->bits_per_pixel/8; write_reg(osd_pan_index, 0x02A0C); /* Pass this info back the yuv handler */ itv->yuv_info.osd_x_pan = var->xoffset; itv->yuv_info.osd_y_pan = var->yoffset; /* Force update of yuv registers */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -