Path: blob/master/drivers/media/video/ivtv/ivtvfb.c
17701 views
/*1On Screen Display cx23415 Framebuffer driver23This module presents the cx23415 OSD (onscreen display) framebuffer memory4as a standard Linux /dev/fb style framebuffer device. The framebuffer has5support for 8, 16 & 32 bpp packed pixel formats with alpha channel. In 16bpp6mode, there is a choice of a three color depths (12, 15 or 16 bits), but no7local alpha. The colorspace is selectable between rgb & yuv.8Depending on the TV standard configured in the ivtv module at load time,9the initial resolution is either 640x400 (NTSC) or 640x480 (PAL) at 8bpp.10Video timings are locked to ensure a vertical refresh rate of 50Hz (PAL)11or 59.94 (NTSC)1213Copyright (c) 2003 Matt T. Yourst <[email protected]>1415Derived from drivers/video/vesafb.c16Portions (c) 1998 Gerd Knorr <[email protected]>17182.6 kernel port:19Copyright (C) 2004 Matthias Badaire2021Copyright (C) 2004 Chris Kennedy <[email protected]>2223Copyright (C) 2006 Ian Armstrong <[email protected]>2425This program is free software; you can redistribute it and/or modify26it under the terms of the GNU General Public License as published by27the Free Software Foundation; either version 2 of the License, or28(at your option) any later version.2930This program is distributed in the hope that it will be useful,31but WITHOUT ANY WARRANTY; without even the implied warranty of32MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the33GNU General Public License for more details.3435You should have received a copy of the GNU General Public License36along with this program; if not, write to the Free Software37Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA38*/3940#include <linux/module.h>41#include <linux/kernel.h>42#include <linux/fb.h>43#include <linux/ivtvfb.h>44#include <linux/slab.h>4546#ifdef CONFIG_MTRR47#include <asm/mtrr.h>48#endif4950#include "ivtv-driver.h"51#include "ivtv-cards.h"52#include "ivtv-i2c.h"53#include "ivtv-udma.h"54#include "ivtv-mailbox.h"55#include "ivtv-firmware.h"5657/* card parameters */58static int ivtvfb_card_id = -1;59static int ivtvfb_debug = 0;60static int osd_laced;61static int osd_depth;62static int osd_upper;63static int osd_left;64static int osd_yres;65static int osd_xres;6667module_param(ivtvfb_card_id, int, 0444);68module_param_named(debug,ivtvfb_debug, int, 0644);69module_param(osd_laced, bool, 0444);70module_param(osd_depth, int, 0444);71module_param(osd_upper, int, 0444);72module_param(osd_left, int, 0444);73module_param(osd_yres, int, 0444);74module_param(osd_xres, int, 0444);7576MODULE_PARM_DESC(ivtvfb_card_id,77"Only use framebuffer of the specified ivtv card (0-31)\n"78"\t\t\tdefault -1: initialize all available framebuffers");7980MODULE_PARM_DESC(debug,81"Debug level (bitmask). Default: errors only\n"82"\t\t\t(debug = 3 gives full debugging)");8384/* Why upper, left, xres, yres, depth, laced ? To match terminology used85by fbset.86Why start at 1 for left & upper coordinate ? Because X doesn't allow 0 */8788MODULE_PARM_DESC(osd_laced,89"Interlaced mode\n"90"\t\t\t0=off\n"91"\t\t\t1=on\n"92"\t\t\tdefault off");9394MODULE_PARM_DESC(osd_depth,95"Bits per pixel - 8, 16, 32\n"96"\t\t\tdefault 8");9798MODULE_PARM_DESC(osd_upper,99"Vertical start position\n"100"\t\t\tdefault 0 (Centered)");101102MODULE_PARM_DESC(osd_left,103"Horizontal start position\n"104"\t\t\tdefault 0 (Centered)");105106MODULE_PARM_DESC(osd_yres,107"Display height\n"108"\t\t\tdefault 480 (PAL)\n"109"\t\t\t 400 (NTSC)");110111MODULE_PARM_DESC(osd_xres,112"Display width\n"113"\t\t\tdefault 640");114115MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil, John Harvey, Ian Armstrong");116MODULE_LICENSE("GPL");117118/* --------------------------------------------------------------------- */119120#define IVTVFB_DBGFLG_WARN (1 << 0)121#define IVTVFB_DBGFLG_INFO (1 << 1)122123#define IVTVFB_DEBUG(x, type, fmt, args...) \124do { \125if ((x) & ivtvfb_debug) \126printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->instance , ## args); \127} while (0)128#define IVTVFB_DEBUG_WARN(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_WARN, "warning", fmt , ## args)129#define IVTVFB_DEBUG_INFO(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_INFO, "info", fmt , ## args)130131/* Standard kernel messages */132#define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->instance , ## args)133#define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->instance , ## args)134#define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->instance , ## args)135136/* --------------------------------------------------------------------- */137138#define IVTV_OSD_MAX_WIDTH 720139#define IVTV_OSD_MAX_HEIGHT 576140141#define IVTV_OSD_BPP_8 0x00142#define IVTV_OSD_BPP_16_444 0x03143#define IVTV_OSD_BPP_16_555 0x02144#define IVTV_OSD_BPP_16_565 0x01145#define IVTV_OSD_BPP_32 0x04146147struct osd_info {148/* Physical base address */149unsigned long video_pbase;150/* Relative base address (relative to start of decoder memory) */151u32 video_rbase;152/* Mapped base address */153volatile char __iomem *video_vbase;154/* Buffer size */155u32 video_buffer_size;156157#ifdef CONFIG_MTRR158/* video_base rounded down as required by hardware MTRRs */159unsigned long fb_start_aligned_physaddr;160/* video_base rounded up as required by hardware MTRRs */161unsigned long fb_end_aligned_physaddr;162#endif163164/* Store the buffer offset */165int set_osd_coords_x;166int set_osd_coords_y;167168/* Current dimensions (NOT VISIBLE SIZE!) */169int display_width;170int display_height;171int display_byte_stride;172173/* Current bits per pixel */174int bits_per_pixel;175int bytes_per_pixel;176177/* Frame buffer stuff */178struct fb_info ivtvfb_info;179struct fb_var_screeninfo ivtvfb_defined;180struct fb_fix_screeninfo ivtvfb_fix;181182/* Used for a warm start */183struct fb_var_screeninfo fbvar_cur;184int blank_cur;185u32 palette_cur[256];186u32 pan_cur;187};188189struct ivtv_osd_coords {190unsigned long offset;191unsigned long max_offset;192int pixel_stride;193int lines;194int x;195int y;196};197198/* --------------------------------------------------------------------- */199200/* ivtv API calls for framebuffer related support */201202static int ivtvfb_get_framebuffer(struct ivtv *itv, u32 *fbbase,203u32 *fblength)204{205u32 data[CX2341X_MBOX_MAX_DATA];206int rc;207208ivtv_firmware_check(itv, "ivtvfb_get_framebuffer");209rc = ivtv_vapi_result(itv, data, CX2341X_OSD_GET_FRAMEBUFFER, 0);210*fbbase = data[0];211*fblength = data[1];212return rc;213}214215static int ivtvfb_get_osd_coords(struct ivtv *itv,216struct ivtv_osd_coords *osd)217{218struct osd_info *oi = itv->osd_info;219u32 data[CX2341X_MBOX_MAX_DATA];220221ivtv_vapi_result(itv, data, CX2341X_OSD_GET_OSD_COORDS, 0);222223osd->offset = data[0] - oi->video_rbase;224osd->max_offset = oi->display_width * oi->display_height * 4;225osd->pixel_stride = data[1];226osd->lines = data[2];227osd->x = data[3];228osd->y = data[4];229return 0;230}231232static int ivtvfb_set_osd_coords(struct ivtv *itv, const struct ivtv_osd_coords *osd)233{234struct osd_info *oi = itv->osd_info;235236oi->display_width = osd->pixel_stride;237oi->display_byte_stride = osd->pixel_stride * oi->bytes_per_pixel;238oi->set_osd_coords_x += osd->x;239oi->set_osd_coords_y = osd->y;240241return ivtv_vapi(itv, CX2341X_OSD_SET_OSD_COORDS, 5,242osd->offset + oi->video_rbase,243osd->pixel_stride,244osd->lines, osd->x, osd->y);245}246247static int ivtvfb_set_display_window(struct ivtv *itv, struct v4l2_rect *ivtv_window)248{249int osd_height_limit = itv->is_out_50hz ? 576 : 480;250251/* Only fail if resolution too high, otherwise fudge the start coords. */252if ((ivtv_window->height > osd_height_limit) || (ivtv_window->width > IVTV_OSD_MAX_WIDTH))253return -EINVAL;254255/* Ensure we don't exceed display limits */256if (ivtv_window->top + ivtv_window->height > osd_height_limit) {257IVTVFB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid height setting (%d, %d)\n",258ivtv_window->top, ivtv_window->height);259ivtv_window->top = osd_height_limit - ivtv_window->height;260}261262if (ivtv_window->left + ivtv_window->width > IVTV_OSD_MAX_WIDTH) {263IVTVFB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid width setting (%d, %d)\n",264ivtv_window->left, ivtv_window->width);265ivtv_window->left = IVTV_OSD_MAX_WIDTH - ivtv_window->width;266}267268/* Set the OSD origin */269write_reg((ivtv_window->top << 16) | ivtv_window->left, 0x02a04);270271/* How much to display */272write_reg(((ivtv_window->top+ivtv_window->height) << 16) | (ivtv_window->left+ivtv_window->width), 0x02a08);273274/* Pass this info back the yuv handler */275itv->yuv_info.osd_vis_w = ivtv_window->width;276itv->yuv_info.osd_vis_h = ivtv_window->height;277itv->yuv_info.osd_x_offset = ivtv_window->left;278itv->yuv_info.osd_y_offset = ivtv_window->top;279280return 0;281}282283static int ivtvfb_prep_dec_dma_to_device(struct ivtv *itv,284unsigned long ivtv_dest_addr, void __user *userbuf,285int size_in_bytes)286{287DEFINE_WAIT(wait);288int got_sig = 0;289290mutex_lock(&itv->udma.lock);291/* Map User DMA */292if (ivtv_udma_setup(itv, ivtv_dest_addr, userbuf, size_in_bytes) <= 0) {293mutex_unlock(&itv->udma.lock);294IVTVFB_WARN("ivtvfb_prep_dec_dma_to_device, "295"Error with get_user_pages: %d bytes, %d pages returned\n",296size_in_bytes, itv->udma.page_count);297298/* get_user_pages must have failed completely */299return -EIO;300}301302IVTVFB_DEBUG_INFO("ivtvfb_prep_dec_dma_to_device, %d bytes, %d pages\n",303size_in_bytes, itv->udma.page_count);304305ivtv_udma_prepare(itv);306prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);307/* if no UDMA is pending and no UDMA is in progress, then the DMA308is finished */309while (test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags) ||310test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {311/* don't interrupt if the DMA is in progress but break off312a still pending DMA. */313got_sig = signal_pending(current);314if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))315break;316got_sig = 0;317schedule();318}319finish_wait(&itv->dma_waitq, &wait);320321/* Unmap Last DMA Xfer */322ivtv_udma_unmap(itv);323mutex_unlock(&itv->udma.lock);324if (got_sig) {325IVTV_DEBUG_INFO("User stopped OSD\n");326return -EINTR;327}328329return 0;330}331332static int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source,333unsigned long dest_offset, int count)334{335DEFINE_WAIT(wait);336struct osd_info *oi = itv->osd_info;337338/* Nothing to do */339if (count == 0) {340IVTVFB_DEBUG_WARN("ivtvfb_prep_frame: Nothing to do. count = 0\n");341return -EINVAL;342}343344/* Check Total FB Size */345if ((dest_offset + count) > oi->video_buffer_size) {346IVTVFB_WARN("ivtvfb_prep_frame: Overflowing the framebuffer %ld, only %d available\n",347dest_offset + count, oi->video_buffer_size);348return -E2BIG;349}350351/* Not fatal, but will have undesirable results */352if ((unsigned long)source & 3)353IVTVFB_WARN("ivtvfb_prep_frame: Source address not 32 bit aligned (0x%08lx)\n",354(unsigned long)source);355356if (dest_offset & 3)357IVTVFB_WARN("ivtvfb_prep_frame: Dest offset not 32 bit aligned (%ld)\n", dest_offset);358359if (count & 3)360IVTVFB_WARN("ivtvfb_prep_frame: Count not a multiple of 4 (%d)\n", count);361362/* Check Source */363if (!access_ok(VERIFY_READ, source + dest_offset, count)) {364IVTVFB_WARN("Invalid userspace pointer 0x%08lx\n",365(unsigned long)source);366367IVTVFB_DEBUG_WARN("access_ok() failed for offset 0x%08lx source 0x%08lx count %d\n",368dest_offset, (unsigned long)source,369count);370return -EINVAL;371}372373/* OSD Address to send DMA to */374dest_offset += IVTV_DECODER_OFFSET + oi->video_rbase;375376/* Fill Buffers */377return ivtvfb_prep_dec_dma_to_device(itv, dest_offset, source, count);378}379380static ssize_t ivtvfb_write(struct fb_info *info, const char __user *buf,381size_t count, loff_t *ppos)382{383unsigned long p = *ppos;384void *dst;385int err = 0;386int dma_err;387unsigned long total_size;388struct ivtv *itv = (struct ivtv *) info->par;389unsigned long dma_offset =390IVTV_DECODER_OFFSET + itv->osd_info->video_rbase;391unsigned long dma_size;392u16 lead = 0, tail = 0;393394if (info->state != FBINFO_STATE_RUNNING)395return -EPERM;396397total_size = info->screen_size;398399if (total_size == 0)400total_size = info->fix.smem_len;401402if (p > total_size)403return -EFBIG;404405if (count > total_size) {406err = -EFBIG;407count = total_size;408}409410if (count + p > total_size) {411if (!err)412err = -ENOSPC;413count = total_size - p;414}415416dst = (void __force *) (info->screen_base + p);417418if (info->fbops->fb_sync)419info->fbops->fb_sync(info);420421/* If transfer size > threshold and both src/dst422addresses are aligned, use DMA */423if (count >= 4096 &&424((unsigned long)buf & 3) == ((unsigned long)dst & 3)) {425/* Odd address = can't DMA. Align */426if ((unsigned long)dst & 3) {427lead = 4 - ((unsigned long)dst & 3);428if (copy_from_user(dst, buf, lead))429return -EFAULT;430buf += lead;431dst += lead;432}433/* DMA resolution is 32 bits */434if ((count - lead) & 3)435tail = (count - lead) & 3;436/* DMA the data */437dma_size = count - lead - tail;438dma_err = ivtvfb_prep_dec_dma_to_device(itv,439p + lead + dma_offset, (void __user *)buf, dma_size);440if (dma_err)441return dma_err;442dst += dma_size;443buf += dma_size;444/* Copy any leftover data */445if (tail && copy_from_user(dst, buf, tail))446return -EFAULT;447} else if (copy_from_user(dst, buf, count)) {448return -EFAULT;449}450451if (!err)452*ppos += count;453454return (err) ? err : count;455}456457static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)458{459DEFINE_WAIT(wait);460struct ivtv *itv = (struct ivtv *)info->par;461int rc = 0;462463switch (cmd) {464case FBIOGET_VBLANK: {465struct fb_vblank vblank;466u32 trace;467468memset(&vblank, 0, sizeof(struct fb_vblank));469470vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT |471FB_VBLANK_HAVE_VSYNC;472trace = read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16;473if (itv->is_out_50hz && trace > 312)474trace -= 312;475else if (itv->is_out_60hz && trace > 262)476trace -= 262;477if (trace == 1)478vblank.flags |= FB_VBLANK_VSYNCING;479vblank.count = itv->last_vsync_field;480vblank.vcount = trace;481vblank.hcount = 0;482if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank)))483return -EFAULT;484return 0;485}486487case FBIO_WAITFORVSYNC:488prepare_to_wait(&itv->vsync_waitq, &wait, TASK_INTERRUPTIBLE);489if (!schedule_timeout(msecs_to_jiffies(50)))490rc = -ETIMEDOUT;491finish_wait(&itv->vsync_waitq, &wait);492return rc;493494case IVTVFB_IOC_DMA_FRAME: {495struct ivtvfb_dma_frame args;496497IVTVFB_DEBUG_INFO("IVTVFB_IOC_DMA_FRAME\n");498if (copy_from_user(&args, (void __user *)arg, sizeof(args)))499return -EFAULT;500501return ivtvfb_prep_frame(itv, cmd, args.source, args.dest_offset, args.count);502}503504default:505IVTVFB_DEBUG_INFO("Unknown ioctl %08x\n", cmd);506return -EINVAL;507}508return 0;509}510511/* Framebuffer device handling */512513static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var)514{515struct osd_info *oi = itv->osd_info;516struct ivtv_osd_coords ivtv_osd;517struct v4l2_rect ivtv_window;518int osd_mode = -1;519520IVTVFB_DEBUG_INFO("ivtvfb_set_var\n");521522/* Select color space */523if (var->nonstd) /* YUV */524write_reg(read_reg(0x02a00) | 0x0002000, 0x02a00);525else /* RGB */526write_reg(read_reg(0x02a00) & ~0x0002000, 0x02a00);527528/* Set the color mode */529switch (var->bits_per_pixel) {530case 8:531osd_mode = IVTV_OSD_BPP_8;532break;533case 32:534osd_mode = IVTV_OSD_BPP_32;535break;536case 16:537switch (var->green.length) {538case 4:539osd_mode = IVTV_OSD_BPP_16_444;540break;541case 5:542osd_mode = IVTV_OSD_BPP_16_555;543break;544case 6:545osd_mode = IVTV_OSD_BPP_16_565;546break;547default:548IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");549}550break;551default:552IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");553}554555/* Set video mode. Although rare, the display can become scrambled even556if we don't change mode. Always 'bounce' to osd_mode via mode 0 */557if (osd_mode != -1) {558ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0);559ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, osd_mode);560}561562oi->bits_per_pixel = var->bits_per_pixel;563oi->bytes_per_pixel = var->bits_per_pixel / 8;564565/* Set the flicker filter */566switch (var->vmode & FB_VMODE_MASK) {567case FB_VMODE_NONINTERLACED: /* Filter on */568ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 1);569break;570case FB_VMODE_INTERLACED: /* Filter off */571ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 0);572break;573default:574IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid video mode\n");575}576577/* Read the current osd info */578ivtvfb_get_osd_coords(itv, &ivtv_osd);579580/* Now set the OSD to the size we want */581ivtv_osd.pixel_stride = var->xres_virtual;582ivtv_osd.lines = var->yres_virtual;583ivtv_osd.x = 0;584ivtv_osd.y = 0;585ivtvfb_set_osd_coords(itv, &ivtv_osd);586587/* Can't seem to find the right API combo for this.588Use another function which does what we need through direct register access. */589ivtv_window.width = var->xres;590ivtv_window.height = var->yres;591592/* Minimum margin cannot be 0, as X won't allow such a mode */593if (!var->upper_margin)594var->upper_margin++;595if (!var->left_margin)596var->left_margin++;597ivtv_window.top = var->upper_margin - 1;598ivtv_window.left = var->left_margin - 1;599600ivtvfb_set_display_window(itv, &ivtv_window);601602/* Pass screen size back to yuv handler */603itv->yuv_info.osd_full_w = ivtv_osd.pixel_stride;604itv->yuv_info.osd_full_h = ivtv_osd.lines;605606/* Force update of yuv registers */607itv->yuv_info.yuv_forced_update = 1;608609/* Keep a copy of these settings */610memcpy(&oi->fbvar_cur, var, sizeof(oi->fbvar_cur));611612IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",613var->xres, var->yres,614var->xres_virtual, var->yres_virtual,615var->bits_per_pixel);616617IVTVFB_DEBUG_INFO("Display position: %d, %d\n",618var->left_margin, var->upper_margin);619620IVTVFB_DEBUG_INFO("Display filter: %s\n",621(var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");622IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");623624return 0;625}626627static int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix)628{629struct osd_info *oi = itv->osd_info;630631IVTVFB_DEBUG_INFO("ivtvfb_get_fix\n");632memset(fix, 0, sizeof(struct fb_fix_screeninfo));633strlcpy(fix->id, "cx23415 TV out", sizeof(fix->id));634fix->smem_start = oi->video_pbase;635fix->smem_len = oi->video_buffer_size;636fix->type = FB_TYPE_PACKED_PIXELS;637fix->visual = (oi->bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;638fix->xpanstep = 1;639fix->ypanstep = 1;640fix->ywrapstep = 0;641fix->line_length = oi->display_byte_stride;642fix->accel = FB_ACCEL_NONE;643return 0;644}645646/* Check the requested display mode, returning -EINVAL if we can't647handle it. */648649static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv)650{651struct osd_info *oi = itv->osd_info;652int osd_height_limit;653u32 pixclock, hlimit, vlimit;654655IVTVFB_DEBUG_INFO("ivtvfb_check_var\n");656657/* Set base references for mode calcs. */658if (itv->is_out_50hz) {659pixclock = 84316;660hlimit = 776;661vlimit = 591;662osd_height_limit = 576;663}664else {665pixclock = 83926;666hlimit = 776;667vlimit = 495;668osd_height_limit = 480;669}670671if (var->bits_per_pixel == 8 || var->bits_per_pixel == 32) {672var->transp.offset = 24;673var->transp.length = 8;674var->red.offset = 16;675var->red.length = 8;676var->green.offset = 8;677var->green.length = 8;678var->blue.offset = 0;679var->blue.length = 8;680}681else if (var->bits_per_pixel == 16) {682/* To find out the true mode, check green length */683switch (var->green.length) {684case 4:685var->red.offset = 8;686var->red.length = 4;687var->green.offset = 4;688var->green.length = 4;689var->blue.offset = 0;690var->blue.length = 4;691var->transp.offset = 12;692var->transp.length = 1;693break;694case 5:695var->red.offset = 10;696var->red.length = 5;697var->green.offset = 5;698var->green.length = 5;699var->blue.offset = 0;700var->blue.length = 5;701var->transp.offset = 15;702var->transp.length = 1;703break;704default:705var->red.offset = 11;706var->red.length = 5;707var->green.offset = 5;708var->green.length = 6;709var->blue.offset = 0;710var->blue.length = 5;711var->transp.offset = 0;712var->transp.length = 0;713break;714}715}716else {717IVTVFB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel);718return -EINVAL;719}720721/* Check the resolution */722if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit) {723IVTVFB_DEBUG_WARN("Invalid resolution: %dx%d\n",724var->xres, var->yres);725return -EINVAL;726}727728/* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */729if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) ||730var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8) > oi->video_buffer_size ||731var->xres_virtual < var->xres ||732var->yres_virtual < var->yres) {733IVTVFB_DEBUG_WARN("Invalid virtual resolution: %dx%d\n",734var->xres_virtual, var->yres_virtual);735return -EINVAL;736}737738/* Some extra checks if in 8 bit mode */739if (var->bits_per_pixel == 8) {740/* Width must be a multiple of 4 */741if (var->xres & 3) {742IVTVFB_DEBUG_WARN("Invalid resolution for 8bpp: %d\n", var->xres);743return -EINVAL;744}745if (var->xres_virtual & 3) {746IVTVFB_DEBUG_WARN("Invalid virtual resolution for 8bpp: %d)\n", var->xres_virtual);747return -EINVAL;748}749}750else if (var->bits_per_pixel == 16) {751/* Width must be a multiple of 2 */752if (var->xres & 1) {753IVTVFB_DEBUG_WARN("Invalid resolution for 16bpp: %d\n", var->xres);754return -EINVAL;755}756if (var->xres_virtual & 1) {757IVTVFB_DEBUG_WARN("Invalid virtual resolution for 16bpp: %d)\n", var->xres_virtual);758return -EINVAL;759}760}761762/* Now check the offsets */763if (var->xoffset >= var->xres_virtual || var->yoffset >= var->yres_virtual) {764IVTVFB_DEBUG_WARN("Invalid offset: %d (%d) %d (%d)\n",765var->xoffset, var->xres_virtual, var->yoffset, var->yres_virtual);766return -EINVAL;767}768769/* Check pixel format */770if (var->nonstd > 1) {771IVTVFB_DEBUG_WARN("Invalid nonstd % d\n", var->nonstd);772return -EINVAL;773}774775/* Check video mode */776if (((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) &&777((var->vmode & FB_VMODE_MASK) != FB_VMODE_INTERLACED)) {778IVTVFB_DEBUG_WARN("Invalid video mode: %d\n", var->vmode & FB_VMODE_MASK);779return -EINVAL;780}781782/* Check the left & upper margins783If the margins are too large, just center the screen784(enforcing margins causes too many problems) */785786if (var->left_margin + var->xres > IVTV_OSD_MAX_WIDTH + 1)787var->left_margin = 1 + ((IVTV_OSD_MAX_WIDTH - var->xres) / 2);788789if (var->upper_margin + var->yres > (itv->is_out_50hz ? 577 : 481))790var->upper_margin = 1 + (((itv->is_out_50hz ? 576 : 480) -791var->yres) / 2);792793/* Maintain overall 'size' for a constant refresh rate */794var->right_margin = hlimit - var->left_margin - var->xres;795var->lower_margin = vlimit - var->upper_margin - var->yres;796797/* Fixed sync times */798var->hsync_len = 24;799var->vsync_len = 2;800801/* Non-interlaced / interlaced mode is used to switch the OSD filter802on or off. Adjust the clock timings to maintain a constant803vertical refresh rate. */804if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED)805var->pixclock = pixclock / 2;806else807var->pixclock = pixclock;808809itv->osd_rect.width = var->xres;810itv->osd_rect.height = var->yres;811812IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",813var->xres, var->yres,814var->xres_virtual, var->yres_virtual,815var->bits_per_pixel);816817IVTVFB_DEBUG_INFO("Display position: %d, %d\n",818var->left_margin, var->upper_margin);819820IVTVFB_DEBUG_INFO("Display filter: %s\n",821(var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");822IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");823return 0;824}825826static int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)827{828struct ivtv *itv = (struct ivtv *) info->par;829IVTVFB_DEBUG_INFO("ivtvfb_check_var\n");830return _ivtvfb_check_var(var, itv);831}832833static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)834{835u32 osd_pan_index;836struct ivtv *itv = (struct ivtv *) info->par;837838if (var->yoffset + info->var.yres > info->var.yres_virtual ||839var->xoffset + info->var.xres > info->var.xres_virtual)840return -EINVAL;841842osd_pan_index = var->yoffset * info->fix.line_length843+ var->xoffset * info->var.bits_per_pixel / 8;844write_reg(osd_pan_index, 0x02A0C);845846/* Pass this info back the yuv handler */847itv->yuv_info.osd_x_pan = var->xoffset;848itv->yuv_info.osd_y_pan = var->yoffset;849/* Force update of yuv registers */850itv->yuv_info.yuv_forced_update = 1;851/* Remember this value */852itv->osd_info->pan_cur = osd_pan_index;853return 0;854}855856static int ivtvfb_set_par(struct fb_info *info)857{858int rc = 0;859struct ivtv *itv = (struct ivtv *) info->par;860861IVTVFB_DEBUG_INFO("ivtvfb_set_par\n");862863rc = ivtvfb_set_var(itv, &info->var);864ivtvfb_pan_display(&info->var, info);865ivtvfb_get_fix(itv, &info->fix);866ivtv_firmware_check(itv, "ivtvfb_set_par");867return rc;868}869870static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green,871unsigned blue, unsigned transp,872struct fb_info *info)873{874u32 color, *palette;875struct ivtv *itv = (struct ivtv *)info->par;876877if (regno >= info->cmap.len)878return -EINVAL;879880color = ((transp & 0xFF00) << 16) |((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8);881if (info->var.bits_per_pixel <= 8) {882write_reg(regno, 0x02a30);883write_reg(color, 0x02a34);884itv->osd_info->palette_cur[regno] = color;885return 0;886}887if (regno >= 16)888return -EINVAL;889890palette = info->pseudo_palette;891if (info->var.bits_per_pixel == 16) {892switch (info->var.green.length) {893case 4:894color = ((red & 0xf000) >> 4) |895((green & 0xf000) >> 8) |896((blue & 0xf000) >> 12);897break;898case 5:899color = ((red & 0xf800) >> 1) |900((green & 0xf800) >> 6) |901((blue & 0xf800) >> 11);902break;903case 6:904color = (red & 0xf800 ) |905((green & 0xfc00) >> 5) |906((blue & 0xf800) >> 11);907break;908}909}910palette[regno] = color;911return 0;912}913914/* We don't really support blanking. All this does is enable or915disable the OSD. */916static int ivtvfb_blank(int blank_mode, struct fb_info *info)917{918struct ivtv *itv = (struct ivtv *)info->par;919920IVTVFB_DEBUG_INFO("Set blanking mode : %d\n", blank_mode);921switch (blank_mode) {922case FB_BLANK_UNBLANK:923ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1);924ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);925break;926case FB_BLANK_NORMAL:927case FB_BLANK_HSYNC_SUSPEND:928case FB_BLANK_VSYNC_SUSPEND:929ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);930ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);931break;932case FB_BLANK_POWERDOWN:933ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);934ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);935break;936}937itv->osd_info->blank_cur = blank_mode;938return 0;939}940941static struct fb_ops ivtvfb_ops = {942.owner = THIS_MODULE,943.fb_write = ivtvfb_write,944.fb_check_var = ivtvfb_check_var,945.fb_set_par = ivtvfb_set_par,946.fb_setcolreg = ivtvfb_setcolreg,947.fb_fillrect = cfb_fillrect,948.fb_copyarea = cfb_copyarea,949.fb_imageblit = cfb_imageblit,950.fb_cursor = NULL,951.fb_ioctl = ivtvfb_ioctl,952.fb_pan_display = ivtvfb_pan_display,953.fb_blank = ivtvfb_blank,954};955956/* Restore hardware after firmware restart */957static void ivtvfb_restore(struct ivtv *itv)958{959struct osd_info *oi = itv->osd_info;960int i;961962ivtvfb_set_var(itv, &oi->fbvar_cur);963ivtvfb_blank(oi->blank_cur, &oi->ivtvfb_info);964for (i = 0; i < 256; i++) {965write_reg(i, 0x02a30);966write_reg(oi->palette_cur[i], 0x02a34);967}968write_reg(oi->pan_cur, 0x02a0c);969}970971/* Initialization */972973974/* Setup our initial video mode */975static int ivtvfb_init_vidmode(struct ivtv *itv)976{977struct osd_info *oi = itv->osd_info;978struct v4l2_rect start_window;979int max_height;980981/* Color mode */982983if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32)984osd_depth = 8;985oi->bits_per_pixel = osd_depth;986oi->bytes_per_pixel = oi->bits_per_pixel / 8;987988/* Horizontal size & position */989990if (osd_xres > 720)991osd_xres = 720;992993/* Must be a multiple of 4 for 8bpp & 2 for 16bpp */994if (osd_depth == 8)995osd_xres &= ~3;996else if (osd_depth == 16)997osd_xres &= ~1;998999start_window.width = osd_xres ? osd_xres : 640;10001001/* Check horizontal start (osd_left). */1002if (osd_left && osd_left + start_window.width > 721) {1003IVTVFB_ERR("Invalid osd_left - assuming default\n");1004osd_left = 0;1005}10061007/* Hardware coords start at 0, user coords start at 1. */1008osd_left--;10091010start_window.left = osd_left >= 0 ?1011osd_left : ((IVTV_OSD_MAX_WIDTH - start_window.width) / 2);10121013oi->display_byte_stride =1014start_window.width * oi->bytes_per_pixel;10151016/* Vertical size & position */10171018max_height = itv->is_out_50hz ? 576 : 480;10191020if (osd_yres > max_height)1021osd_yres = max_height;10221023start_window.height = osd_yres ?1024osd_yres : itv->is_out_50hz ? 480 : 400;10251026/* Check vertical start (osd_upper). */1027if (osd_upper + start_window.height > max_height + 1) {1028IVTVFB_ERR("Invalid osd_upper - assuming default\n");1029osd_upper = 0;1030}10311032/* Hardware coords start at 0, user coords start at 1. */1033osd_upper--;10341035start_window.top = osd_upper >= 0 ? osd_upper : ((max_height - start_window.height) / 2);10361037oi->display_width = start_window.width;1038oi->display_height = start_window.height;10391040/* Generate a valid fb_var_screeninfo */10411042oi->ivtvfb_defined.xres = oi->display_width;1043oi->ivtvfb_defined.yres = oi->display_height;1044oi->ivtvfb_defined.xres_virtual = oi->display_width;1045oi->ivtvfb_defined.yres_virtual = oi->display_height;1046oi->ivtvfb_defined.bits_per_pixel = oi->bits_per_pixel;1047oi->ivtvfb_defined.vmode = (osd_laced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED);1048oi->ivtvfb_defined.left_margin = start_window.left + 1;1049oi->ivtvfb_defined.upper_margin = start_window.top + 1;1050oi->ivtvfb_defined.accel_flags = FB_ACCEL_NONE;1051oi->ivtvfb_defined.nonstd = 0;10521053/* We've filled in the most data, let the usual mode check1054routine fill in the rest. */1055_ivtvfb_check_var(&oi->ivtvfb_defined, itv);10561057/* Generate valid fb_fix_screeninfo */10581059ivtvfb_get_fix(itv, &oi->ivtvfb_fix);10601061/* Generate valid fb_info */10621063oi->ivtvfb_info.node = -1;1064oi->ivtvfb_info.flags = FBINFO_FLAG_DEFAULT;1065oi->ivtvfb_info.fbops = &ivtvfb_ops;1066oi->ivtvfb_info.par = itv;1067oi->ivtvfb_info.var = oi->ivtvfb_defined;1068oi->ivtvfb_info.fix = oi->ivtvfb_fix;1069oi->ivtvfb_info.screen_base = (u8 __iomem *)oi->video_vbase;1070oi->ivtvfb_info.fbops = &ivtvfb_ops;10711072/* Supply some monitor specs. Bogus values will do for now */1073oi->ivtvfb_info.monspecs.hfmin = 8000;1074oi->ivtvfb_info.monspecs.hfmax = 70000;1075oi->ivtvfb_info.monspecs.vfmin = 10;1076oi->ivtvfb_info.monspecs.vfmax = 100;10771078/* Allocate color map */1079if (fb_alloc_cmap(&oi->ivtvfb_info.cmap, 256, 1)) {1080IVTVFB_ERR("abort, unable to alloc cmap\n");1081return -ENOMEM;1082}10831084/* Allocate the pseudo palette */1085oi->ivtvfb_info.pseudo_palette =1086kmalloc(sizeof(u32) * 16, GFP_KERNEL|__GFP_NOWARN);10871088if (!oi->ivtvfb_info.pseudo_palette) {1089IVTVFB_ERR("abort, unable to alloc pseudo palette\n");1090return -ENOMEM;1091}10921093return 0;1094}10951096/* Find OSD buffer base & size. Add to mtrr. Zero osd buffer. */10971098static int ivtvfb_init_io(struct ivtv *itv)1099{1100struct osd_info *oi = itv->osd_info;11011102mutex_lock(&itv->serialize_lock);1103if (ivtv_init_on_first_open(itv)) {1104mutex_unlock(&itv->serialize_lock);1105IVTVFB_ERR("Failed to initialize ivtv\n");1106return -ENXIO;1107}1108mutex_unlock(&itv->serialize_lock);11091110if (ivtvfb_get_framebuffer(itv, &oi->video_rbase,1111&oi->video_buffer_size) < 0) {1112IVTVFB_ERR("Firmware failed to respond\n");1113return -EIO;1114}11151116/* The osd buffer size depends on the number of video buffers allocated1117on the PVR350 itself. For now we'll hardcode the smallest osd buffer1118size to prevent any overlap. */1119oi->video_buffer_size = 1704960;11201121oi->video_pbase = itv->base_addr + IVTV_DECODER_OFFSET + oi->video_rbase;1122oi->video_vbase = itv->dec_mem + oi->video_rbase;11231124if (!oi->video_vbase) {1125IVTVFB_ERR("abort, video memory 0x%x @ 0x%lx isn't mapped!\n",1126oi->video_buffer_size, oi->video_pbase);1127return -EIO;1128}11291130IVTVFB_INFO("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n",1131oi->video_pbase, oi->video_vbase,1132oi->video_buffer_size / 1024);11331134#ifdef CONFIG_MTRR1135{1136/* Find the largest power of two that maps the whole buffer */1137int size_shift = 31;11381139while (!(oi->video_buffer_size & (1 << size_shift))) {1140size_shift--;1141}1142size_shift++;1143oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1);1144oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size;1145oi->fb_end_aligned_physaddr += (1 << size_shift) - 1;1146oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1);1147if (mtrr_add(oi->fb_start_aligned_physaddr,1148oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr,1149MTRR_TYPE_WRCOMB, 1) < 0) {1150IVTVFB_INFO("disabled mttr\n");1151oi->fb_start_aligned_physaddr = 0;1152oi->fb_end_aligned_physaddr = 0;1153}1154}1155#endif11561157/* Blank the entire osd. */1158memset_io(oi->video_vbase, 0, oi->video_buffer_size);11591160return 0;1161}11621163/* Release any memory we've grabbed & remove mtrr entry */1164static void ivtvfb_release_buffers (struct ivtv *itv)1165{1166struct osd_info *oi = itv->osd_info;11671168/* Release cmap */1169if (oi->ivtvfb_info.cmap.len)1170fb_dealloc_cmap(&oi->ivtvfb_info.cmap);11711172/* Release pseudo palette */1173if (oi->ivtvfb_info.pseudo_palette)1174kfree(oi->ivtvfb_info.pseudo_palette);11751176#ifdef CONFIG_MTRR1177if (oi->fb_end_aligned_physaddr) {1178mtrr_del(-1, oi->fb_start_aligned_physaddr,1179oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr);1180}1181#endif11821183kfree(oi);1184itv->osd_info = NULL;1185}11861187/* Initialize the specified card */11881189static int ivtvfb_init_card(struct ivtv *itv)1190{1191int rc;11921193if (itv->osd_info) {1194IVTVFB_ERR("Card %d already initialised\n", ivtvfb_card_id);1195return -EBUSY;1196}11971198itv->osd_info = kzalloc(sizeof(struct osd_info),1199GFP_ATOMIC|__GFP_NOWARN);1200if (itv->osd_info == NULL) {1201IVTVFB_ERR("Failed to allocate memory for osd_info\n");1202return -ENOMEM;1203}12041205/* Find & setup the OSD buffer */1206rc = ivtvfb_init_io(itv);1207if (rc) {1208ivtvfb_release_buffers(itv);1209return rc;1210}12111212/* Set the startup video mode information */1213if ((rc = ivtvfb_init_vidmode(itv))) {1214ivtvfb_release_buffers(itv);1215return rc;1216}12171218/* Register the framebuffer */1219if (register_framebuffer(&itv->osd_info->ivtvfb_info) < 0) {1220ivtvfb_release_buffers(itv);1221return -EINVAL;1222}12231224itv->osd_video_pbase = itv->osd_info->video_pbase;12251226/* Set the card to the requested mode */1227ivtvfb_set_par(&itv->osd_info->ivtvfb_info);12281229/* Set color 0 to black */1230write_reg(0, 0x02a30);1231write_reg(0, 0x02a34);12321233/* Enable the osd */1234ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info);12351236/* Enable restart */1237itv->ivtvfb_restore = ivtvfb_restore;12381239/* Allocate DMA */1240ivtv_udma_alloc(itv);1241return 0;12421243}12441245static int __init ivtvfb_callback_init(struct device *dev, void *p)1246{1247struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);1248struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev);12491250if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {1251if (ivtvfb_init_card(itv) == 0) {1252IVTVFB_INFO("Framebuffer registered on %s\n",1253itv->v4l2_dev.name);1254(*(int *)p)++;1255}1256}1257return 0;1258}12591260static int ivtvfb_callback_cleanup(struct device *dev, void *p)1261{1262struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);1263struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev);1264struct osd_info *oi = itv->osd_info;12651266if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {1267if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) {1268IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n",1269itv->instance);1270return 0;1271}1272IVTVFB_INFO("Unregister framebuffer %d\n", itv->instance);1273itv->ivtvfb_restore = NULL;1274ivtvfb_blank(FB_BLANK_VSYNC_SUSPEND, &oi->ivtvfb_info);1275ivtvfb_release_buffers(itv);1276itv->osd_video_pbase = 0;1277}1278return 0;1279}12801281static int __init ivtvfb_init(void)1282{1283struct device_driver *drv;1284int registered = 0;1285int err;12861287if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) {1288printk(KERN_ERR "ivtvfb: ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n",1289IVTV_MAX_CARDS - 1);1290return -EINVAL;1291}12921293drv = driver_find("ivtv", &pci_bus_type);1294err = driver_for_each_device(drv, NULL, ®istered, ivtvfb_callback_init);1295put_driver(drv);1296if (!registered) {1297printk(KERN_ERR "ivtvfb: no cards found\n");1298return -ENODEV;1299}1300return 0;1301}13021303static void ivtvfb_cleanup(void)1304{1305struct device_driver *drv;1306int err;13071308printk(KERN_INFO "ivtvfb: Unloading framebuffer module\n");13091310drv = driver_find("ivtv", &pci_bus_type);1311err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup);1312put_driver(drv);1313}13141315module_init(ivtvfb_init);1316module_exit(ivtvfb_cleanup);131713181319