1*b285192aSMauro Carvalho Chehab /* 2*b285192aSMauro Carvalho Chehab On Screen Display cx23415 Framebuffer driver 3*b285192aSMauro Carvalho Chehab 4*b285192aSMauro Carvalho Chehab This module presents the cx23415 OSD (onscreen display) framebuffer memory 5*b285192aSMauro Carvalho Chehab as a standard Linux /dev/fb style framebuffer device. The framebuffer has 6*b285192aSMauro Carvalho Chehab support for 8, 16 & 32 bpp packed pixel formats with alpha channel. In 16bpp 7*b285192aSMauro Carvalho Chehab mode, there is a choice of a three color depths (12, 15 or 16 bits), but no 8*b285192aSMauro Carvalho Chehab local alpha. The colorspace is selectable between rgb & yuv. 9*b285192aSMauro Carvalho Chehab Depending on the TV standard configured in the ivtv module at load time, 10*b285192aSMauro Carvalho Chehab the initial resolution is either 640x400 (NTSC) or 640x480 (PAL) at 8bpp. 11*b285192aSMauro Carvalho Chehab Video timings are locked to ensure a vertical refresh rate of 50Hz (PAL) 12*b285192aSMauro Carvalho Chehab or 59.94 (NTSC) 13*b285192aSMauro Carvalho Chehab 14*b285192aSMauro Carvalho Chehab Copyright (c) 2003 Matt T. Yourst <yourst@yourst.com> 15*b285192aSMauro Carvalho Chehab 16*b285192aSMauro Carvalho Chehab Derived from drivers/video/vesafb.c 17*b285192aSMauro Carvalho Chehab Portions (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de> 18*b285192aSMauro Carvalho Chehab 19*b285192aSMauro Carvalho Chehab 2.6 kernel port: 20*b285192aSMauro Carvalho Chehab Copyright (C) 2004 Matthias Badaire 21*b285192aSMauro Carvalho Chehab 22*b285192aSMauro Carvalho Chehab Copyright (C) 2004 Chris Kennedy <c@groovy.org> 23*b285192aSMauro Carvalho Chehab 24*b285192aSMauro Carvalho Chehab Copyright (C) 2006 Ian Armstrong <ian@iarmst.demon.co.uk> 25*b285192aSMauro Carvalho Chehab 26*b285192aSMauro Carvalho Chehab This program is free software; you can redistribute it and/or modify 27*b285192aSMauro Carvalho Chehab it under the terms of the GNU General Public License as published by 28*b285192aSMauro Carvalho Chehab the Free Software Foundation; either version 2 of the License, or 29*b285192aSMauro Carvalho Chehab (at your option) any later version. 30*b285192aSMauro Carvalho Chehab 31*b285192aSMauro Carvalho Chehab This program is distributed in the hope that it will be useful, 32*b285192aSMauro Carvalho Chehab but WITHOUT ANY WARRANTY; without even the implied warranty of 33*b285192aSMauro Carvalho Chehab MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 34*b285192aSMauro Carvalho Chehab GNU General Public License for more details. 35*b285192aSMauro Carvalho Chehab 36*b285192aSMauro Carvalho Chehab You should have received a copy of the GNU General Public License 37*b285192aSMauro Carvalho Chehab along with this program; if not, write to the Free Software 38*b285192aSMauro Carvalho Chehab Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 39*b285192aSMauro Carvalho Chehab */ 40*b285192aSMauro Carvalho Chehab 41*b285192aSMauro Carvalho Chehab #include <linux/module.h> 42*b285192aSMauro Carvalho Chehab #include <linux/kernel.h> 43*b285192aSMauro Carvalho Chehab #include <linux/fb.h> 44*b285192aSMauro Carvalho Chehab #include <linux/ivtvfb.h> 45*b285192aSMauro Carvalho Chehab #include <linux/slab.h> 46*b285192aSMauro Carvalho Chehab 47*b285192aSMauro Carvalho Chehab #ifdef CONFIG_MTRR 48*b285192aSMauro Carvalho Chehab #include <asm/mtrr.h> 49*b285192aSMauro Carvalho Chehab #endif 50*b285192aSMauro Carvalho Chehab 51*b285192aSMauro Carvalho Chehab #include "ivtv-driver.h" 52*b285192aSMauro Carvalho Chehab #include "ivtv-cards.h" 53*b285192aSMauro Carvalho Chehab #include "ivtv-i2c.h" 54*b285192aSMauro Carvalho Chehab #include "ivtv-udma.h" 55*b285192aSMauro Carvalho Chehab #include "ivtv-mailbox.h" 56*b285192aSMauro Carvalho Chehab #include "ivtv-firmware.h" 57*b285192aSMauro Carvalho Chehab 58*b285192aSMauro Carvalho Chehab /* card parameters */ 59*b285192aSMauro Carvalho Chehab static int ivtvfb_card_id = -1; 60*b285192aSMauro Carvalho Chehab static int ivtvfb_debug = 0; 61*b285192aSMauro Carvalho Chehab static bool osd_laced; 62*b285192aSMauro Carvalho Chehab static int osd_depth; 63*b285192aSMauro Carvalho Chehab static int osd_upper; 64*b285192aSMauro Carvalho Chehab static int osd_left; 65*b285192aSMauro Carvalho Chehab static int osd_yres; 66*b285192aSMauro Carvalho Chehab static int osd_xres; 67*b285192aSMauro Carvalho Chehab 68*b285192aSMauro Carvalho Chehab module_param(ivtvfb_card_id, int, 0444); 69*b285192aSMauro Carvalho Chehab module_param_named(debug,ivtvfb_debug, int, 0644); 70*b285192aSMauro Carvalho Chehab module_param(osd_laced, bool, 0444); 71*b285192aSMauro Carvalho Chehab module_param(osd_depth, int, 0444); 72*b285192aSMauro Carvalho Chehab module_param(osd_upper, int, 0444); 73*b285192aSMauro Carvalho Chehab module_param(osd_left, int, 0444); 74*b285192aSMauro Carvalho Chehab module_param(osd_yres, int, 0444); 75*b285192aSMauro Carvalho Chehab module_param(osd_xres, int, 0444); 76*b285192aSMauro Carvalho Chehab 77*b285192aSMauro Carvalho Chehab MODULE_PARM_DESC(ivtvfb_card_id, 78*b285192aSMauro Carvalho Chehab "Only use framebuffer of the specified ivtv card (0-31)\n" 79*b285192aSMauro Carvalho Chehab "\t\t\tdefault -1: initialize all available framebuffers"); 80*b285192aSMauro Carvalho Chehab 81*b285192aSMauro Carvalho Chehab MODULE_PARM_DESC(debug, 82*b285192aSMauro Carvalho Chehab "Debug level (bitmask). Default: errors only\n" 83*b285192aSMauro Carvalho Chehab "\t\t\t(debug = 3 gives full debugging)"); 84*b285192aSMauro Carvalho Chehab 85*b285192aSMauro Carvalho Chehab /* Why upper, left, xres, yres, depth, laced ? To match terminology used 86*b285192aSMauro Carvalho Chehab by fbset. 87*b285192aSMauro Carvalho Chehab Why start at 1 for left & upper coordinate ? Because X doesn't allow 0 */ 88*b285192aSMauro Carvalho Chehab 89*b285192aSMauro Carvalho Chehab MODULE_PARM_DESC(osd_laced, 90*b285192aSMauro Carvalho Chehab "Interlaced mode\n" 91*b285192aSMauro Carvalho Chehab "\t\t\t0=off\n" 92*b285192aSMauro Carvalho Chehab "\t\t\t1=on\n" 93*b285192aSMauro Carvalho Chehab "\t\t\tdefault off"); 94*b285192aSMauro Carvalho Chehab 95*b285192aSMauro Carvalho Chehab MODULE_PARM_DESC(osd_depth, 96*b285192aSMauro Carvalho Chehab "Bits per pixel - 8, 16, 32\n" 97*b285192aSMauro Carvalho Chehab "\t\t\tdefault 8"); 98*b285192aSMauro Carvalho Chehab 99*b285192aSMauro Carvalho Chehab MODULE_PARM_DESC(osd_upper, 100*b285192aSMauro Carvalho Chehab "Vertical start position\n" 101*b285192aSMauro Carvalho Chehab "\t\t\tdefault 0 (Centered)"); 102*b285192aSMauro Carvalho Chehab 103*b285192aSMauro Carvalho Chehab MODULE_PARM_DESC(osd_left, 104*b285192aSMauro Carvalho Chehab "Horizontal start position\n" 105*b285192aSMauro Carvalho Chehab "\t\t\tdefault 0 (Centered)"); 106*b285192aSMauro Carvalho Chehab 107*b285192aSMauro Carvalho Chehab MODULE_PARM_DESC(osd_yres, 108*b285192aSMauro Carvalho Chehab "Display height\n" 109*b285192aSMauro Carvalho Chehab "\t\t\tdefault 480 (PAL)\n" 110*b285192aSMauro Carvalho Chehab "\t\t\t 400 (NTSC)"); 111*b285192aSMauro Carvalho Chehab 112*b285192aSMauro Carvalho Chehab MODULE_PARM_DESC(osd_xres, 113*b285192aSMauro Carvalho Chehab "Display width\n" 114*b285192aSMauro Carvalho Chehab "\t\t\tdefault 640"); 115*b285192aSMauro Carvalho Chehab 116*b285192aSMauro Carvalho Chehab MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil, John Harvey, Ian Armstrong"); 117*b285192aSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 118*b285192aSMauro Carvalho Chehab 119*b285192aSMauro Carvalho Chehab /* --------------------------------------------------------------------- */ 120*b285192aSMauro Carvalho Chehab 121*b285192aSMauro Carvalho Chehab #define IVTVFB_DBGFLG_WARN (1 << 0) 122*b285192aSMauro Carvalho Chehab #define IVTVFB_DBGFLG_INFO (1 << 1) 123*b285192aSMauro Carvalho Chehab 124*b285192aSMauro Carvalho Chehab #define IVTVFB_DEBUG(x, type, fmt, args...) \ 125*b285192aSMauro Carvalho Chehab do { \ 126*b285192aSMauro Carvalho Chehab if ((x) & ivtvfb_debug) \ 127*b285192aSMauro Carvalho Chehab printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->instance , ## args); \ 128*b285192aSMauro Carvalho Chehab } while (0) 129*b285192aSMauro Carvalho Chehab #define IVTVFB_DEBUG_WARN(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_WARN, "warning", fmt , ## args) 130*b285192aSMauro Carvalho Chehab #define IVTVFB_DEBUG_INFO(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_INFO, "info", fmt , ## args) 131*b285192aSMauro Carvalho Chehab 132*b285192aSMauro Carvalho Chehab /* Standard kernel messages */ 133*b285192aSMauro Carvalho Chehab #define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->instance , ## args) 134*b285192aSMauro Carvalho Chehab #define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->instance , ## args) 135*b285192aSMauro Carvalho Chehab #define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->instance , ## args) 136*b285192aSMauro Carvalho Chehab 137*b285192aSMauro Carvalho Chehab /* --------------------------------------------------------------------- */ 138*b285192aSMauro Carvalho Chehab 139*b285192aSMauro Carvalho Chehab #define IVTV_OSD_MAX_WIDTH 720 140*b285192aSMauro Carvalho Chehab #define IVTV_OSD_MAX_HEIGHT 576 141*b285192aSMauro Carvalho Chehab 142*b285192aSMauro Carvalho Chehab #define IVTV_OSD_BPP_8 0x00 143*b285192aSMauro Carvalho Chehab #define IVTV_OSD_BPP_16_444 0x03 144*b285192aSMauro Carvalho Chehab #define IVTV_OSD_BPP_16_555 0x02 145*b285192aSMauro Carvalho Chehab #define IVTV_OSD_BPP_16_565 0x01 146*b285192aSMauro Carvalho Chehab #define IVTV_OSD_BPP_32 0x04 147*b285192aSMauro Carvalho Chehab 148*b285192aSMauro Carvalho Chehab struct osd_info { 149*b285192aSMauro Carvalho Chehab /* Physical base address */ 150*b285192aSMauro Carvalho Chehab unsigned long video_pbase; 151*b285192aSMauro Carvalho Chehab /* Relative base address (relative to start of decoder memory) */ 152*b285192aSMauro Carvalho Chehab u32 video_rbase; 153*b285192aSMauro Carvalho Chehab /* Mapped base address */ 154*b285192aSMauro Carvalho Chehab volatile char __iomem *video_vbase; 155*b285192aSMauro Carvalho Chehab /* Buffer size */ 156*b285192aSMauro Carvalho Chehab u32 video_buffer_size; 157*b285192aSMauro Carvalho Chehab 158*b285192aSMauro Carvalho Chehab #ifdef CONFIG_MTRR 159*b285192aSMauro Carvalho Chehab /* video_base rounded down as required by hardware MTRRs */ 160*b285192aSMauro Carvalho Chehab unsigned long fb_start_aligned_physaddr; 161*b285192aSMauro Carvalho Chehab /* video_base rounded up as required by hardware MTRRs */ 162*b285192aSMauro Carvalho Chehab unsigned long fb_end_aligned_physaddr; 163*b285192aSMauro Carvalho Chehab #endif 164*b285192aSMauro Carvalho Chehab 165*b285192aSMauro Carvalho Chehab /* Store the buffer offset */ 166*b285192aSMauro Carvalho Chehab int set_osd_coords_x; 167*b285192aSMauro Carvalho Chehab int set_osd_coords_y; 168*b285192aSMauro Carvalho Chehab 169*b285192aSMauro Carvalho Chehab /* Current dimensions (NOT VISIBLE SIZE!) */ 170*b285192aSMauro Carvalho Chehab int display_width; 171*b285192aSMauro Carvalho Chehab int display_height; 172*b285192aSMauro Carvalho Chehab int display_byte_stride; 173*b285192aSMauro Carvalho Chehab 174*b285192aSMauro Carvalho Chehab /* Current bits per pixel */ 175*b285192aSMauro Carvalho Chehab int bits_per_pixel; 176*b285192aSMauro Carvalho Chehab int bytes_per_pixel; 177*b285192aSMauro Carvalho Chehab 178*b285192aSMauro Carvalho Chehab /* Frame buffer stuff */ 179*b285192aSMauro Carvalho Chehab struct fb_info ivtvfb_info; 180*b285192aSMauro Carvalho Chehab struct fb_var_screeninfo ivtvfb_defined; 181*b285192aSMauro Carvalho Chehab struct fb_fix_screeninfo ivtvfb_fix; 182*b285192aSMauro Carvalho Chehab 183*b285192aSMauro Carvalho Chehab /* Used for a warm start */ 184*b285192aSMauro Carvalho Chehab struct fb_var_screeninfo fbvar_cur; 185*b285192aSMauro Carvalho Chehab int blank_cur; 186*b285192aSMauro Carvalho Chehab u32 palette_cur[256]; 187*b285192aSMauro Carvalho Chehab u32 pan_cur; 188*b285192aSMauro Carvalho Chehab }; 189*b285192aSMauro Carvalho Chehab 190*b285192aSMauro Carvalho Chehab struct ivtv_osd_coords { 191*b285192aSMauro Carvalho Chehab unsigned long offset; 192*b285192aSMauro Carvalho Chehab unsigned long max_offset; 193*b285192aSMauro Carvalho Chehab int pixel_stride; 194*b285192aSMauro Carvalho Chehab int lines; 195*b285192aSMauro Carvalho Chehab int x; 196*b285192aSMauro Carvalho Chehab int y; 197*b285192aSMauro Carvalho Chehab }; 198*b285192aSMauro Carvalho Chehab 199*b285192aSMauro Carvalho Chehab /* --------------------------------------------------------------------- */ 200*b285192aSMauro Carvalho Chehab 201*b285192aSMauro Carvalho Chehab /* ivtv API calls for framebuffer related support */ 202*b285192aSMauro Carvalho Chehab 203*b285192aSMauro Carvalho Chehab static int ivtvfb_get_framebuffer(struct ivtv *itv, u32 *fbbase, 204*b285192aSMauro Carvalho Chehab u32 *fblength) 205*b285192aSMauro Carvalho Chehab { 206*b285192aSMauro Carvalho Chehab u32 data[CX2341X_MBOX_MAX_DATA]; 207*b285192aSMauro Carvalho Chehab int rc; 208*b285192aSMauro Carvalho Chehab 209*b285192aSMauro Carvalho Chehab ivtv_firmware_check(itv, "ivtvfb_get_framebuffer"); 210*b285192aSMauro Carvalho Chehab rc = ivtv_vapi_result(itv, data, CX2341X_OSD_GET_FRAMEBUFFER, 0); 211*b285192aSMauro Carvalho Chehab *fbbase = data[0]; 212*b285192aSMauro Carvalho Chehab *fblength = data[1]; 213*b285192aSMauro Carvalho Chehab return rc; 214*b285192aSMauro Carvalho Chehab } 215*b285192aSMauro Carvalho Chehab 216*b285192aSMauro Carvalho Chehab static int ivtvfb_get_osd_coords(struct ivtv *itv, 217*b285192aSMauro Carvalho Chehab struct ivtv_osd_coords *osd) 218*b285192aSMauro Carvalho Chehab { 219*b285192aSMauro Carvalho Chehab struct osd_info *oi = itv->osd_info; 220*b285192aSMauro Carvalho Chehab u32 data[CX2341X_MBOX_MAX_DATA]; 221*b285192aSMauro Carvalho Chehab 222*b285192aSMauro Carvalho Chehab ivtv_vapi_result(itv, data, CX2341X_OSD_GET_OSD_COORDS, 0); 223*b285192aSMauro Carvalho Chehab 224*b285192aSMauro Carvalho Chehab osd->offset = data[0] - oi->video_rbase; 225*b285192aSMauro Carvalho Chehab osd->max_offset = oi->display_width * oi->display_height * 4; 226*b285192aSMauro Carvalho Chehab osd->pixel_stride = data[1]; 227*b285192aSMauro Carvalho Chehab osd->lines = data[2]; 228*b285192aSMauro Carvalho Chehab osd->x = data[3]; 229*b285192aSMauro Carvalho Chehab osd->y = data[4]; 230*b285192aSMauro Carvalho Chehab return 0; 231*b285192aSMauro Carvalho Chehab } 232*b285192aSMauro Carvalho Chehab 233*b285192aSMauro Carvalho Chehab static int ivtvfb_set_osd_coords(struct ivtv *itv, const struct ivtv_osd_coords *osd) 234*b285192aSMauro Carvalho Chehab { 235*b285192aSMauro Carvalho Chehab struct osd_info *oi = itv->osd_info; 236*b285192aSMauro Carvalho Chehab 237*b285192aSMauro Carvalho Chehab oi->display_width = osd->pixel_stride; 238*b285192aSMauro Carvalho Chehab oi->display_byte_stride = osd->pixel_stride * oi->bytes_per_pixel; 239*b285192aSMauro Carvalho Chehab oi->set_osd_coords_x += osd->x; 240*b285192aSMauro Carvalho Chehab oi->set_osd_coords_y = osd->y; 241*b285192aSMauro Carvalho Chehab 242*b285192aSMauro Carvalho Chehab return ivtv_vapi(itv, CX2341X_OSD_SET_OSD_COORDS, 5, 243*b285192aSMauro Carvalho Chehab osd->offset + oi->video_rbase, 244*b285192aSMauro Carvalho Chehab osd->pixel_stride, 245*b285192aSMauro Carvalho Chehab osd->lines, osd->x, osd->y); 246*b285192aSMauro Carvalho Chehab } 247*b285192aSMauro Carvalho Chehab 248*b285192aSMauro Carvalho Chehab static int ivtvfb_set_display_window(struct ivtv *itv, struct v4l2_rect *ivtv_window) 249*b285192aSMauro Carvalho Chehab { 250*b285192aSMauro Carvalho Chehab int osd_height_limit = itv->is_out_50hz ? 576 : 480; 251*b285192aSMauro Carvalho Chehab 252*b285192aSMauro Carvalho Chehab /* Only fail if resolution too high, otherwise fudge the start coords. */ 253*b285192aSMauro Carvalho Chehab if ((ivtv_window->height > osd_height_limit) || (ivtv_window->width > IVTV_OSD_MAX_WIDTH)) 254*b285192aSMauro Carvalho Chehab return -EINVAL; 255*b285192aSMauro Carvalho Chehab 256*b285192aSMauro Carvalho Chehab /* Ensure we don't exceed display limits */ 257*b285192aSMauro Carvalho Chehab if (ivtv_window->top + ivtv_window->height > osd_height_limit) { 258*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid height setting (%d, %d)\n", 259*b285192aSMauro Carvalho Chehab ivtv_window->top, ivtv_window->height); 260*b285192aSMauro Carvalho Chehab ivtv_window->top = osd_height_limit - ivtv_window->height; 261*b285192aSMauro Carvalho Chehab } 262*b285192aSMauro Carvalho Chehab 263*b285192aSMauro Carvalho Chehab if (ivtv_window->left + ivtv_window->width > IVTV_OSD_MAX_WIDTH) { 264*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid width setting (%d, %d)\n", 265*b285192aSMauro Carvalho Chehab ivtv_window->left, ivtv_window->width); 266*b285192aSMauro Carvalho Chehab ivtv_window->left = IVTV_OSD_MAX_WIDTH - ivtv_window->width; 267*b285192aSMauro Carvalho Chehab } 268*b285192aSMauro Carvalho Chehab 269*b285192aSMauro Carvalho Chehab /* Set the OSD origin */ 270*b285192aSMauro Carvalho Chehab write_reg((ivtv_window->top << 16) | ivtv_window->left, 0x02a04); 271*b285192aSMauro Carvalho Chehab 272*b285192aSMauro Carvalho Chehab /* How much to display */ 273*b285192aSMauro Carvalho Chehab write_reg(((ivtv_window->top+ivtv_window->height) << 16) | (ivtv_window->left+ivtv_window->width), 0x02a08); 274*b285192aSMauro Carvalho Chehab 275*b285192aSMauro Carvalho Chehab /* Pass this info back the yuv handler */ 276*b285192aSMauro Carvalho Chehab itv->yuv_info.osd_vis_w = ivtv_window->width; 277*b285192aSMauro Carvalho Chehab itv->yuv_info.osd_vis_h = ivtv_window->height; 278*b285192aSMauro Carvalho Chehab itv->yuv_info.osd_x_offset = ivtv_window->left; 279*b285192aSMauro Carvalho Chehab itv->yuv_info.osd_y_offset = ivtv_window->top; 280*b285192aSMauro Carvalho Chehab 281*b285192aSMauro Carvalho Chehab return 0; 282*b285192aSMauro Carvalho Chehab } 283*b285192aSMauro Carvalho Chehab 284*b285192aSMauro Carvalho Chehab static int ivtvfb_prep_dec_dma_to_device(struct ivtv *itv, 285*b285192aSMauro Carvalho Chehab unsigned long ivtv_dest_addr, void __user *userbuf, 286*b285192aSMauro Carvalho Chehab int size_in_bytes) 287*b285192aSMauro Carvalho Chehab { 288*b285192aSMauro Carvalho Chehab DEFINE_WAIT(wait); 289*b285192aSMauro Carvalho Chehab int got_sig = 0; 290*b285192aSMauro Carvalho Chehab 291*b285192aSMauro Carvalho Chehab mutex_lock(&itv->udma.lock); 292*b285192aSMauro Carvalho Chehab /* Map User DMA */ 293*b285192aSMauro Carvalho Chehab if (ivtv_udma_setup(itv, ivtv_dest_addr, userbuf, size_in_bytes) <= 0) { 294*b285192aSMauro Carvalho Chehab mutex_unlock(&itv->udma.lock); 295*b285192aSMauro Carvalho Chehab IVTVFB_WARN("ivtvfb_prep_dec_dma_to_device, " 296*b285192aSMauro Carvalho Chehab "Error with get_user_pages: %d bytes, %d pages returned\n", 297*b285192aSMauro Carvalho Chehab size_in_bytes, itv->udma.page_count); 298*b285192aSMauro Carvalho Chehab 299*b285192aSMauro Carvalho Chehab /* get_user_pages must have failed completely */ 300*b285192aSMauro Carvalho Chehab return -EIO; 301*b285192aSMauro Carvalho Chehab } 302*b285192aSMauro Carvalho Chehab 303*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_INFO("ivtvfb_prep_dec_dma_to_device, %d bytes, %d pages\n", 304*b285192aSMauro Carvalho Chehab size_in_bytes, itv->udma.page_count); 305*b285192aSMauro Carvalho Chehab 306*b285192aSMauro Carvalho Chehab ivtv_udma_prepare(itv); 307*b285192aSMauro Carvalho Chehab prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); 308*b285192aSMauro Carvalho Chehab /* if no UDMA is pending and no UDMA is in progress, then the DMA 309*b285192aSMauro Carvalho Chehab is finished */ 310*b285192aSMauro Carvalho Chehab while (test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags) || 311*b285192aSMauro Carvalho Chehab test_bit(IVTV_F_I_UDMA, &itv->i_flags)) { 312*b285192aSMauro Carvalho Chehab /* don't interrupt if the DMA is in progress but break off 313*b285192aSMauro Carvalho Chehab a still pending DMA. */ 314*b285192aSMauro Carvalho Chehab got_sig = signal_pending(current); 315*b285192aSMauro Carvalho Chehab if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) 316*b285192aSMauro Carvalho Chehab break; 317*b285192aSMauro Carvalho Chehab got_sig = 0; 318*b285192aSMauro Carvalho Chehab schedule(); 319*b285192aSMauro Carvalho Chehab } 320*b285192aSMauro Carvalho Chehab finish_wait(&itv->dma_waitq, &wait); 321*b285192aSMauro Carvalho Chehab 322*b285192aSMauro Carvalho Chehab /* Unmap Last DMA Xfer */ 323*b285192aSMauro Carvalho Chehab ivtv_udma_unmap(itv); 324*b285192aSMauro Carvalho Chehab mutex_unlock(&itv->udma.lock); 325*b285192aSMauro Carvalho Chehab if (got_sig) { 326*b285192aSMauro Carvalho Chehab IVTV_DEBUG_INFO("User stopped OSD\n"); 327*b285192aSMauro Carvalho Chehab return -EINTR; 328*b285192aSMauro Carvalho Chehab } 329*b285192aSMauro Carvalho Chehab 330*b285192aSMauro Carvalho Chehab return 0; 331*b285192aSMauro Carvalho Chehab } 332*b285192aSMauro Carvalho Chehab 333*b285192aSMauro Carvalho Chehab static int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source, 334*b285192aSMauro Carvalho Chehab unsigned long dest_offset, int count) 335*b285192aSMauro Carvalho Chehab { 336*b285192aSMauro Carvalho Chehab DEFINE_WAIT(wait); 337*b285192aSMauro Carvalho Chehab struct osd_info *oi = itv->osd_info; 338*b285192aSMauro Carvalho Chehab 339*b285192aSMauro Carvalho Chehab /* Nothing to do */ 340*b285192aSMauro Carvalho Chehab if (count == 0) { 341*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_WARN("ivtvfb_prep_frame: Nothing to do. count = 0\n"); 342*b285192aSMauro Carvalho Chehab return -EINVAL; 343*b285192aSMauro Carvalho Chehab } 344*b285192aSMauro Carvalho Chehab 345*b285192aSMauro Carvalho Chehab /* Check Total FB Size */ 346*b285192aSMauro Carvalho Chehab if ((dest_offset + count) > oi->video_buffer_size) { 347*b285192aSMauro Carvalho Chehab IVTVFB_WARN("ivtvfb_prep_frame: Overflowing the framebuffer %ld, only %d available\n", 348*b285192aSMauro Carvalho Chehab dest_offset + count, oi->video_buffer_size); 349*b285192aSMauro Carvalho Chehab return -E2BIG; 350*b285192aSMauro Carvalho Chehab } 351*b285192aSMauro Carvalho Chehab 352*b285192aSMauro Carvalho Chehab /* Not fatal, but will have undesirable results */ 353*b285192aSMauro Carvalho Chehab if ((unsigned long)source & 3) 354*b285192aSMauro Carvalho Chehab IVTVFB_WARN("ivtvfb_prep_frame: Source address not 32 bit aligned (0x%08lx)\n", 355*b285192aSMauro Carvalho Chehab (unsigned long)source); 356*b285192aSMauro Carvalho Chehab 357*b285192aSMauro Carvalho Chehab if (dest_offset & 3) 358*b285192aSMauro Carvalho Chehab IVTVFB_WARN("ivtvfb_prep_frame: Dest offset not 32 bit aligned (%ld)\n", dest_offset); 359*b285192aSMauro Carvalho Chehab 360*b285192aSMauro Carvalho Chehab if (count & 3) 361*b285192aSMauro Carvalho Chehab IVTVFB_WARN("ivtvfb_prep_frame: Count not a multiple of 4 (%d)\n", count); 362*b285192aSMauro Carvalho Chehab 363*b285192aSMauro Carvalho Chehab /* Check Source */ 364*b285192aSMauro Carvalho Chehab if (!access_ok(VERIFY_READ, source + dest_offset, count)) { 365*b285192aSMauro Carvalho Chehab IVTVFB_WARN("Invalid userspace pointer 0x%08lx\n", 366*b285192aSMauro Carvalho Chehab (unsigned long)source); 367*b285192aSMauro Carvalho Chehab 368*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_WARN("access_ok() failed for offset 0x%08lx source 0x%08lx count %d\n", 369*b285192aSMauro Carvalho Chehab dest_offset, (unsigned long)source, 370*b285192aSMauro Carvalho Chehab count); 371*b285192aSMauro Carvalho Chehab return -EINVAL; 372*b285192aSMauro Carvalho Chehab } 373*b285192aSMauro Carvalho Chehab 374*b285192aSMauro Carvalho Chehab /* OSD Address to send DMA to */ 375*b285192aSMauro Carvalho Chehab dest_offset += IVTV_DECODER_OFFSET + oi->video_rbase; 376*b285192aSMauro Carvalho Chehab 377*b285192aSMauro Carvalho Chehab /* Fill Buffers */ 378*b285192aSMauro Carvalho Chehab return ivtvfb_prep_dec_dma_to_device(itv, dest_offset, source, count); 379*b285192aSMauro Carvalho Chehab } 380*b285192aSMauro Carvalho Chehab 381*b285192aSMauro Carvalho Chehab static ssize_t ivtvfb_write(struct fb_info *info, const char __user *buf, 382*b285192aSMauro Carvalho Chehab size_t count, loff_t *ppos) 383*b285192aSMauro Carvalho Chehab { 384*b285192aSMauro Carvalho Chehab unsigned long p = *ppos; 385*b285192aSMauro Carvalho Chehab void *dst; 386*b285192aSMauro Carvalho Chehab int err = 0; 387*b285192aSMauro Carvalho Chehab int dma_err; 388*b285192aSMauro Carvalho Chehab unsigned long total_size; 389*b285192aSMauro Carvalho Chehab struct ivtv *itv = (struct ivtv *) info->par; 390*b285192aSMauro Carvalho Chehab unsigned long dma_offset = 391*b285192aSMauro Carvalho Chehab IVTV_DECODER_OFFSET + itv->osd_info->video_rbase; 392*b285192aSMauro Carvalho Chehab unsigned long dma_size; 393*b285192aSMauro Carvalho Chehab u16 lead = 0, tail = 0; 394*b285192aSMauro Carvalho Chehab 395*b285192aSMauro Carvalho Chehab if (info->state != FBINFO_STATE_RUNNING) 396*b285192aSMauro Carvalho Chehab return -EPERM; 397*b285192aSMauro Carvalho Chehab 398*b285192aSMauro Carvalho Chehab total_size = info->screen_size; 399*b285192aSMauro Carvalho Chehab 400*b285192aSMauro Carvalho Chehab if (total_size == 0) 401*b285192aSMauro Carvalho Chehab total_size = info->fix.smem_len; 402*b285192aSMauro Carvalho Chehab 403*b285192aSMauro Carvalho Chehab if (p > total_size) 404*b285192aSMauro Carvalho Chehab return -EFBIG; 405*b285192aSMauro Carvalho Chehab 406*b285192aSMauro Carvalho Chehab if (count > total_size) { 407*b285192aSMauro Carvalho Chehab err = -EFBIG; 408*b285192aSMauro Carvalho Chehab count = total_size; 409*b285192aSMauro Carvalho Chehab } 410*b285192aSMauro Carvalho Chehab 411*b285192aSMauro Carvalho Chehab if (count + p > total_size) { 412*b285192aSMauro Carvalho Chehab if (!err) 413*b285192aSMauro Carvalho Chehab err = -ENOSPC; 414*b285192aSMauro Carvalho Chehab count = total_size - p; 415*b285192aSMauro Carvalho Chehab } 416*b285192aSMauro Carvalho Chehab 417*b285192aSMauro Carvalho Chehab dst = (void __force *) (info->screen_base + p); 418*b285192aSMauro Carvalho Chehab 419*b285192aSMauro Carvalho Chehab if (info->fbops->fb_sync) 420*b285192aSMauro Carvalho Chehab info->fbops->fb_sync(info); 421*b285192aSMauro Carvalho Chehab 422*b285192aSMauro Carvalho Chehab /* If transfer size > threshold and both src/dst 423*b285192aSMauro Carvalho Chehab addresses are aligned, use DMA */ 424*b285192aSMauro Carvalho Chehab if (count >= 4096 && 425*b285192aSMauro Carvalho Chehab ((unsigned long)buf & 3) == ((unsigned long)dst & 3)) { 426*b285192aSMauro Carvalho Chehab /* Odd address = can't DMA. Align */ 427*b285192aSMauro Carvalho Chehab if ((unsigned long)dst & 3) { 428*b285192aSMauro Carvalho Chehab lead = 4 - ((unsigned long)dst & 3); 429*b285192aSMauro Carvalho Chehab if (copy_from_user(dst, buf, lead)) 430*b285192aSMauro Carvalho Chehab return -EFAULT; 431*b285192aSMauro Carvalho Chehab buf += lead; 432*b285192aSMauro Carvalho Chehab dst += lead; 433*b285192aSMauro Carvalho Chehab } 434*b285192aSMauro Carvalho Chehab /* DMA resolution is 32 bits */ 435*b285192aSMauro Carvalho Chehab if ((count - lead) & 3) 436*b285192aSMauro Carvalho Chehab tail = (count - lead) & 3; 437*b285192aSMauro Carvalho Chehab /* DMA the data */ 438*b285192aSMauro Carvalho Chehab dma_size = count - lead - tail; 439*b285192aSMauro Carvalho Chehab dma_err = ivtvfb_prep_dec_dma_to_device(itv, 440*b285192aSMauro Carvalho Chehab p + lead + dma_offset, (void __user *)buf, dma_size); 441*b285192aSMauro Carvalho Chehab if (dma_err) 442*b285192aSMauro Carvalho Chehab return dma_err; 443*b285192aSMauro Carvalho Chehab dst += dma_size; 444*b285192aSMauro Carvalho Chehab buf += dma_size; 445*b285192aSMauro Carvalho Chehab /* Copy any leftover data */ 446*b285192aSMauro Carvalho Chehab if (tail && copy_from_user(dst, buf, tail)) 447*b285192aSMauro Carvalho Chehab return -EFAULT; 448*b285192aSMauro Carvalho Chehab } else if (copy_from_user(dst, buf, count)) { 449*b285192aSMauro Carvalho Chehab return -EFAULT; 450*b285192aSMauro Carvalho Chehab } 451*b285192aSMauro Carvalho Chehab 452*b285192aSMauro Carvalho Chehab if (!err) 453*b285192aSMauro Carvalho Chehab *ppos += count; 454*b285192aSMauro Carvalho Chehab 455*b285192aSMauro Carvalho Chehab return (err) ? err : count; 456*b285192aSMauro Carvalho Chehab } 457*b285192aSMauro Carvalho Chehab 458*b285192aSMauro Carvalho Chehab static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) 459*b285192aSMauro Carvalho Chehab { 460*b285192aSMauro Carvalho Chehab DEFINE_WAIT(wait); 461*b285192aSMauro Carvalho Chehab struct ivtv *itv = (struct ivtv *)info->par; 462*b285192aSMauro Carvalho Chehab int rc = 0; 463*b285192aSMauro Carvalho Chehab 464*b285192aSMauro Carvalho Chehab switch (cmd) { 465*b285192aSMauro Carvalho Chehab case FBIOGET_VBLANK: { 466*b285192aSMauro Carvalho Chehab struct fb_vblank vblank; 467*b285192aSMauro Carvalho Chehab u32 trace; 468*b285192aSMauro Carvalho Chehab 469*b285192aSMauro Carvalho Chehab memset(&vblank, 0, sizeof(struct fb_vblank)); 470*b285192aSMauro Carvalho Chehab 471*b285192aSMauro Carvalho Chehab vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT | 472*b285192aSMauro Carvalho Chehab FB_VBLANK_HAVE_VSYNC; 473*b285192aSMauro Carvalho Chehab trace = read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16; 474*b285192aSMauro Carvalho Chehab if (itv->is_out_50hz && trace > 312) 475*b285192aSMauro Carvalho Chehab trace -= 312; 476*b285192aSMauro Carvalho Chehab else if (itv->is_out_60hz && trace > 262) 477*b285192aSMauro Carvalho Chehab trace -= 262; 478*b285192aSMauro Carvalho Chehab if (trace == 1) 479*b285192aSMauro Carvalho Chehab vblank.flags |= FB_VBLANK_VSYNCING; 480*b285192aSMauro Carvalho Chehab vblank.count = itv->last_vsync_field; 481*b285192aSMauro Carvalho Chehab vblank.vcount = trace; 482*b285192aSMauro Carvalho Chehab vblank.hcount = 0; 483*b285192aSMauro Carvalho Chehab if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank))) 484*b285192aSMauro Carvalho Chehab return -EFAULT; 485*b285192aSMauro Carvalho Chehab return 0; 486*b285192aSMauro Carvalho Chehab } 487*b285192aSMauro Carvalho Chehab 488*b285192aSMauro Carvalho Chehab case FBIO_WAITFORVSYNC: 489*b285192aSMauro Carvalho Chehab prepare_to_wait(&itv->vsync_waitq, &wait, TASK_INTERRUPTIBLE); 490*b285192aSMauro Carvalho Chehab if (!schedule_timeout(msecs_to_jiffies(50))) 491*b285192aSMauro Carvalho Chehab rc = -ETIMEDOUT; 492*b285192aSMauro Carvalho Chehab finish_wait(&itv->vsync_waitq, &wait); 493*b285192aSMauro Carvalho Chehab return rc; 494*b285192aSMauro Carvalho Chehab 495*b285192aSMauro Carvalho Chehab case IVTVFB_IOC_DMA_FRAME: { 496*b285192aSMauro Carvalho Chehab struct ivtvfb_dma_frame args; 497*b285192aSMauro Carvalho Chehab 498*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_INFO("IVTVFB_IOC_DMA_FRAME\n"); 499*b285192aSMauro Carvalho Chehab if (copy_from_user(&args, (void __user *)arg, sizeof(args))) 500*b285192aSMauro Carvalho Chehab return -EFAULT; 501*b285192aSMauro Carvalho Chehab 502*b285192aSMauro Carvalho Chehab return ivtvfb_prep_frame(itv, cmd, args.source, args.dest_offset, args.count); 503*b285192aSMauro Carvalho Chehab } 504*b285192aSMauro Carvalho Chehab 505*b285192aSMauro Carvalho Chehab default: 506*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_INFO("Unknown ioctl %08x\n", cmd); 507*b285192aSMauro Carvalho Chehab return -EINVAL; 508*b285192aSMauro Carvalho Chehab } 509*b285192aSMauro Carvalho Chehab return 0; 510*b285192aSMauro Carvalho Chehab } 511*b285192aSMauro Carvalho Chehab 512*b285192aSMauro Carvalho Chehab /* Framebuffer device handling */ 513*b285192aSMauro Carvalho Chehab 514*b285192aSMauro Carvalho Chehab static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var) 515*b285192aSMauro Carvalho Chehab { 516*b285192aSMauro Carvalho Chehab struct osd_info *oi = itv->osd_info; 517*b285192aSMauro Carvalho Chehab struct ivtv_osd_coords ivtv_osd; 518*b285192aSMauro Carvalho Chehab struct v4l2_rect ivtv_window; 519*b285192aSMauro Carvalho Chehab int osd_mode = -1; 520*b285192aSMauro Carvalho Chehab 521*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_INFO("ivtvfb_set_var\n"); 522*b285192aSMauro Carvalho Chehab 523*b285192aSMauro Carvalho Chehab /* Select color space */ 524*b285192aSMauro Carvalho Chehab if (var->nonstd) /* YUV */ 525*b285192aSMauro Carvalho Chehab write_reg(read_reg(0x02a00) | 0x0002000, 0x02a00); 526*b285192aSMauro Carvalho Chehab else /* RGB */ 527*b285192aSMauro Carvalho Chehab write_reg(read_reg(0x02a00) & ~0x0002000, 0x02a00); 528*b285192aSMauro Carvalho Chehab 529*b285192aSMauro Carvalho Chehab /* Set the color mode */ 530*b285192aSMauro Carvalho Chehab switch (var->bits_per_pixel) { 531*b285192aSMauro Carvalho Chehab case 8: 532*b285192aSMauro Carvalho Chehab osd_mode = IVTV_OSD_BPP_8; 533*b285192aSMauro Carvalho Chehab break; 534*b285192aSMauro Carvalho Chehab case 32: 535*b285192aSMauro Carvalho Chehab osd_mode = IVTV_OSD_BPP_32; 536*b285192aSMauro Carvalho Chehab break; 537*b285192aSMauro Carvalho Chehab case 16: 538*b285192aSMauro Carvalho Chehab switch (var->green.length) { 539*b285192aSMauro Carvalho Chehab case 4: 540*b285192aSMauro Carvalho Chehab osd_mode = IVTV_OSD_BPP_16_444; 541*b285192aSMauro Carvalho Chehab break; 542*b285192aSMauro Carvalho Chehab case 5: 543*b285192aSMauro Carvalho Chehab osd_mode = IVTV_OSD_BPP_16_555; 544*b285192aSMauro Carvalho Chehab break; 545*b285192aSMauro Carvalho Chehab case 6: 546*b285192aSMauro Carvalho Chehab osd_mode = IVTV_OSD_BPP_16_565; 547*b285192aSMauro Carvalho Chehab break; 548*b285192aSMauro Carvalho Chehab default: 549*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n"); 550*b285192aSMauro Carvalho Chehab } 551*b285192aSMauro Carvalho Chehab break; 552*b285192aSMauro Carvalho Chehab default: 553*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n"); 554*b285192aSMauro Carvalho Chehab } 555*b285192aSMauro Carvalho Chehab 556*b285192aSMauro Carvalho Chehab /* Set video mode. Although rare, the display can become scrambled even 557*b285192aSMauro Carvalho Chehab if we don't change mode. Always 'bounce' to osd_mode via mode 0 */ 558*b285192aSMauro Carvalho Chehab if (osd_mode != -1) { 559*b285192aSMauro Carvalho Chehab ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0); 560*b285192aSMauro Carvalho Chehab ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, osd_mode); 561*b285192aSMauro Carvalho Chehab } 562*b285192aSMauro Carvalho Chehab 563*b285192aSMauro Carvalho Chehab oi->bits_per_pixel = var->bits_per_pixel; 564*b285192aSMauro Carvalho Chehab oi->bytes_per_pixel = var->bits_per_pixel / 8; 565*b285192aSMauro Carvalho Chehab 566*b285192aSMauro Carvalho Chehab /* Set the flicker filter */ 567*b285192aSMauro Carvalho Chehab switch (var->vmode & FB_VMODE_MASK) { 568*b285192aSMauro Carvalho Chehab case FB_VMODE_NONINTERLACED: /* Filter on */ 569*b285192aSMauro Carvalho Chehab ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 1); 570*b285192aSMauro Carvalho Chehab break; 571*b285192aSMauro Carvalho Chehab case FB_VMODE_INTERLACED: /* Filter off */ 572*b285192aSMauro Carvalho Chehab ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 0); 573*b285192aSMauro Carvalho Chehab break; 574*b285192aSMauro Carvalho Chehab default: 575*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid video mode\n"); 576*b285192aSMauro Carvalho Chehab } 577*b285192aSMauro Carvalho Chehab 578*b285192aSMauro Carvalho Chehab /* Read the current osd info */ 579*b285192aSMauro Carvalho Chehab ivtvfb_get_osd_coords(itv, &ivtv_osd); 580*b285192aSMauro Carvalho Chehab 581*b285192aSMauro Carvalho Chehab /* Now set the OSD to the size we want */ 582*b285192aSMauro Carvalho Chehab ivtv_osd.pixel_stride = var->xres_virtual; 583*b285192aSMauro Carvalho Chehab ivtv_osd.lines = var->yres_virtual; 584*b285192aSMauro Carvalho Chehab ivtv_osd.x = 0; 585*b285192aSMauro Carvalho Chehab ivtv_osd.y = 0; 586*b285192aSMauro Carvalho Chehab ivtvfb_set_osd_coords(itv, &ivtv_osd); 587*b285192aSMauro Carvalho Chehab 588*b285192aSMauro Carvalho Chehab /* Can't seem to find the right API combo for this. 589*b285192aSMauro Carvalho Chehab Use another function which does what we need through direct register access. */ 590*b285192aSMauro Carvalho Chehab ivtv_window.width = var->xres; 591*b285192aSMauro Carvalho Chehab ivtv_window.height = var->yres; 592*b285192aSMauro Carvalho Chehab 593*b285192aSMauro Carvalho Chehab /* Minimum margin cannot be 0, as X won't allow such a mode */ 594*b285192aSMauro Carvalho Chehab if (!var->upper_margin) 595*b285192aSMauro Carvalho Chehab var->upper_margin++; 596*b285192aSMauro Carvalho Chehab if (!var->left_margin) 597*b285192aSMauro Carvalho Chehab var->left_margin++; 598*b285192aSMauro Carvalho Chehab ivtv_window.top = var->upper_margin - 1; 599*b285192aSMauro Carvalho Chehab ivtv_window.left = var->left_margin - 1; 600*b285192aSMauro Carvalho Chehab 601*b285192aSMauro Carvalho Chehab ivtvfb_set_display_window(itv, &ivtv_window); 602*b285192aSMauro Carvalho Chehab 603*b285192aSMauro Carvalho Chehab /* Pass screen size back to yuv handler */ 604*b285192aSMauro Carvalho Chehab itv->yuv_info.osd_full_w = ivtv_osd.pixel_stride; 605*b285192aSMauro Carvalho Chehab itv->yuv_info.osd_full_h = ivtv_osd.lines; 606*b285192aSMauro Carvalho Chehab 607*b285192aSMauro Carvalho Chehab /* Force update of yuv registers */ 608*b285192aSMauro Carvalho Chehab itv->yuv_info.yuv_forced_update = 1; 609*b285192aSMauro Carvalho Chehab 610*b285192aSMauro Carvalho Chehab /* Keep a copy of these settings */ 611*b285192aSMauro Carvalho Chehab memcpy(&oi->fbvar_cur, var, sizeof(oi->fbvar_cur)); 612*b285192aSMauro Carvalho Chehab 613*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n", 614*b285192aSMauro Carvalho Chehab var->xres, var->yres, 615*b285192aSMauro Carvalho Chehab var->xres_virtual, var->yres_virtual, 616*b285192aSMauro Carvalho Chehab var->bits_per_pixel); 617*b285192aSMauro Carvalho Chehab 618*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_INFO("Display position: %d, %d\n", 619*b285192aSMauro Carvalho Chehab var->left_margin, var->upper_margin); 620*b285192aSMauro Carvalho Chehab 621*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_INFO("Display filter: %s\n", 622*b285192aSMauro Carvalho Chehab (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off"); 623*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB"); 624*b285192aSMauro Carvalho Chehab 625*b285192aSMauro Carvalho Chehab return 0; 626*b285192aSMauro Carvalho Chehab } 627*b285192aSMauro Carvalho Chehab 628*b285192aSMauro Carvalho Chehab static int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix) 629*b285192aSMauro Carvalho Chehab { 630*b285192aSMauro Carvalho Chehab struct osd_info *oi = itv->osd_info; 631*b285192aSMauro Carvalho Chehab 632*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_INFO("ivtvfb_get_fix\n"); 633*b285192aSMauro Carvalho Chehab memset(fix, 0, sizeof(struct fb_fix_screeninfo)); 634*b285192aSMauro Carvalho Chehab strlcpy(fix->id, "cx23415 TV out", sizeof(fix->id)); 635*b285192aSMauro Carvalho Chehab fix->smem_start = oi->video_pbase; 636*b285192aSMauro Carvalho Chehab fix->smem_len = oi->video_buffer_size; 637*b285192aSMauro Carvalho Chehab fix->type = FB_TYPE_PACKED_PIXELS; 638*b285192aSMauro Carvalho Chehab fix->visual = (oi->bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; 639*b285192aSMauro Carvalho Chehab fix->xpanstep = 1; 640*b285192aSMauro Carvalho Chehab fix->ypanstep = 1; 641*b285192aSMauro Carvalho Chehab fix->ywrapstep = 0; 642*b285192aSMauro Carvalho Chehab fix->line_length = oi->display_byte_stride; 643*b285192aSMauro Carvalho Chehab fix->accel = FB_ACCEL_NONE; 644*b285192aSMauro Carvalho Chehab return 0; 645*b285192aSMauro Carvalho Chehab } 646*b285192aSMauro Carvalho Chehab 647*b285192aSMauro Carvalho Chehab /* Check the requested display mode, returning -EINVAL if we can't 648*b285192aSMauro Carvalho Chehab handle it. */ 649*b285192aSMauro Carvalho Chehab 650*b285192aSMauro Carvalho Chehab static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv) 651*b285192aSMauro Carvalho Chehab { 652*b285192aSMauro Carvalho Chehab struct osd_info *oi = itv->osd_info; 653*b285192aSMauro Carvalho Chehab int osd_height_limit; 654*b285192aSMauro Carvalho Chehab u32 pixclock, hlimit, vlimit; 655*b285192aSMauro Carvalho Chehab 656*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_INFO("ivtvfb_check_var\n"); 657*b285192aSMauro Carvalho Chehab 658*b285192aSMauro Carvalho Chehab /* Set base references for mode calcs. */ 659*b285192aSMauro Carvalho Chehab if (itv->is_out_50hz) { 660*b285192aSMauro Carvalho Chehab pixclock = 84316; 661*b285192aSMauro Carvalho Chehab hlimit = 776; 662*b285192aSMauro Carvalho Chehab vlimit = 591; 663*b285192aSMauro Carvalho Chehab osd_height_limit = 576; 664*b285192aSMauro Carvalho Chehab } 665*b285192aSMauro Carvalho Chehab else { 666*b285192aSMauro Carvalho Chehab pixclock = 83926; 667*b285192aSMauro Carvalho Chehab hlimit = 776; 668*b285192aSMauro Carvalho Chehab vlimit = 495; 669*b285192aSMauro Carvalho Chehab osd_height_limit = 480; 670*b285192aSMauro Carvalho Chehab } 671*b285192aSMauro Carvalho Chehab 672*b285192aSMauro Carvalho Chehab if (var->bits_per_pixel == 8 || var->bits_per_pixel == 32) { 673*b285192aSMauro Carvalho Chehab var->transp.offset = 24; 674*b285192aSMauro Carvalho Chehab var->transp.length = 8; 675*b285192aSMauro Carvalho Chehab var->red.offset = 16; 676*b285192aSMauro Carvalho Chehab var->red.length = 8; 677*b285192aSMauro Carvalho Chehab var->green.offset = 8; 678*b285192aSMauro Carvalho Chehab var->green.length = 8; 679*b285192aSMauro Carvalho Chehab var->blue.offset = 0; 680*b285192aSMauro Carvalho Chehab var->blue.length = 8; 681*b285192aSMauro Carvalho Chehab } 682*b285192aSMauro Carvalho Chehab else if (var->bits_per_pixel == 16) { 683*b285192aSMauro Carvalho Chehab /* To find out the true mode, check green length */ 684*b285192aSMauro Carvalho Chehab switch (var->green.length) { 685*b285192aSMauro Carvalho Chehab case 4: 686*b285192aSMauro Carvalho Chehab var->red.offset = 8; 687*b285192aSMauro Carvalho Chehab var->red.length = 4; 688*b285192aSMauro Carvalho Chehab var->green.offset = 4; 689*b285192aSMauro Carvalho Chehab var->green.length = 4; 690*b285192aSMauro Carvalho Chehab var->blue.offset = 0; 691*b285192aSMauro Carvalho Chehab var->blue.length = 4; 692*b285192aSMauro Carvalho Chehab var->transp.offset = 12; 693*b285192aSMauro Carvalho Chehab var->transp.length = 1; 694*b285192aSMauro Carvalho Chehab break; 695*b285192aSMauro Carvalho Chehab case 5: 696*b285192aSMauro Carvalho Chehab var->red.offset = 10; 697*b285192aSMauro Carvalho Chehab var->red.length = 5; 698*b285192aSMauro Carvalho Chehab var->green.offset = 5; 699*b285192aSMauro Carvalho Chehab var->green.length = 5; 700*b285192aSMauro Carvalho Chehab var->blue.offset = 0; 701*b285192aSMauro Carvalho Chehab var->blue.length = 5; 702*b285192aSMauro Carvalho Chehab var->transp.offset = 15; 703*b285192aSMauro Carvalho Chehab var->transp.length = 1; 704*b285192aSMauro Carvalho Chehab break; 705*b285192aSMauro Carvalho Chehab default: 706*b285192aSMauro Carvalho Chehab var->red.offset = 11; 707*b285192aSMauro Carvalho Chehab var->red.length = 5; 708*b285192aSMauro Carvalho Chehab var->green.offset = 5; 709*b285192aSMauro Carvalho Chehab var->green.length = 6; 710*b285192aSMauro Carvalho Chehab var->blue.offset = 0; 711*b285192aSMauro Carvalho Chehab var->blue.length = 5; 712*b285192aSMauro Carvalho Chehab var->transp.offset = 0; 713*b285192aSMauro Carvalho Chehab var->transp.length = 0; 714*b285192aSMauro Carvalho Chehab break; 715*b285192aSMauro Carvalho Chehab } 716*b285192aSMauro Carvalho Chehab } 717*b285192aSMauro Carvalho Chehab else { 718*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel); 719*b285192aSMauro Carvalho Chehab return -EINVAL; 720*b285192aSMauro Carvalho Chehab } 721*b285192aSMauro Carvalho Chehab 722*b285192aSMauro Carvalho Chehab /* Check the resolution */ 723*b285192aSMauro Carvalho Chehab if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit) { 724*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_WARN("Invalid resolution: %dx%d\n", 725*b285192aSMauro Carvalho Chehab var->xres, var->yres); 726*b285192aSMauro Carvalho Chehab return -EINVAL; 727*b285192aSMauro Carvalho Chehab } 728*b285192aSMauro Carvalho Chehab 729*b285192aSMauro Carvalho Chehab /* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */ 730*b285192aSMauro Carvalho Chehab if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) || 731*b285192aSMauro Carvalho Chehab var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8) > oi->video_buffer_size || 732*b285192aSMauro Carvalho Chehab var->xres_virtual < var->xres || 733*b285192aSMauro Carvalho Chehab var->yres_virtual < var->yres) { 734*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_WARN("Invalid virtual resolution: %dx%d\n", 735*b285192aSMauro Carvalho Chehab var->xres_virtual, var->yres_virtual); 736*b285192aSMauro Carvalho Chehab return -EINVAL; 737*b285192aSMauro Carvalho Chehab } 738*b285192aSMauro Carvalho Chehab 739*b285192aSMauro Carvalho Chehab /* Some extra checks if in 8 bit mode */ 740*b285192aSMauro Carvalho Chehab if (var->bits_per_pixel == 8) { 741*b285192aSMauro Carvalho Chehab /* Width must be a multiple of 4 */ 742*b285192aSMauro Carvalho Chehab if (var->xres & 3) { 743*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_WARN("Invalid resolution for 8bpp: %d\n", var->xres); 744*b285192aSMauro Carvalho Chehab return -EINVAL; 745*b285192aSMauro Carvalho Chehab } 746*b285192aSMauro Carvalho Chehab if (var->xres_virtual & 3) { 747*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_WARN("Invalid virtual resolution for 8bpp: %d)\n", var->xres_virtual); 748*b285192aSMauro Carvalho Chehab return -EINVAL; 749*b285192aSMauro Carvalho Chehab } 750*b285192aSMauro Carvalho Chehab } 751*b285192aSMauro Carvalho Chehab else if (var->bits_per_pixel == 16) { 752*b285192aSMauro Carvalho Chehab /* Width must be a multiple of 2 */ 753*b285192aSMauro Carvalho Chehab if (var->xres & 1) { 754*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_WARN("Invalid resolution for 16bpp: %d\n", var->xres); 755*b285192aSMauro Carvalho Chehab return -EINVAL; 756*b285192aSMauro Carvalho Chehab } 757*b285192aSMauro Carvalho Chehab if (var->xres_virtual & 1) { 758*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_WARN("Invalid virtual resolution for 16bpp: %d)\n", var->xres_virtual); 759*b285192aSMauro Carvalho Chehab return -EINVAL; 760*b285192aSMauro Carvalho Chehab } 761*b285192aSMauro Carvalho Chehab } 762*b285192aSMauro Carvalho Chehab 763*b285192aSMauro Carvalho Chehab /* Now check the offsets */ 764*b285192aSMauro Carvalho Chehab if (var->xoffset >= var->xres_virtual || var->yoffset >= var->yres_virtual) { 765*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_WARN("Invalid offset: %d (%d) %d (%d)\n", 766*b285192aSMauro Carvalho Chehab var->xoffset, var->xres_virtual, var->yoffset, var->yres_virtual); 767*b285192aSMauro Carvalho Chehab return -EINVAL; 768*b285192aSMauro Carvalho Chehab } 769*b285192aSMauro Carvalho Chehab 770*b285192aSMauro Carvalho Chehab /* Check pixel format */ 771*b285192aSMauro Carvalho Chehab if (var->nonstd > 1) { 772*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_WARN("Invalid nonstd % d\n", var->nonstd); 773*b285192aSMauro Carvalho Chehab return -EINVAL; 774*b285192aSMauro Carvalho Chehab } 775*b285192aSMauro Carvalho Chehab 776*b285192aSMauro Carvalho Chehab /* Check video mode */ 777*b285192aSMauro Carvalho Chehab if (((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) && 778*b285192aSMauro Carvalho Chehab ((var->vmode & FB_VMODE_MASK) != FB_VMODE_INTERLACED)) { 779*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_WARN("Invalid video mode: %d\n", var->vmode & FB_VMODE_MASK); 780*b285192aSMauro Carvalho Chehab return -EINVAL; 781*b285192aSMauro Carvalho Chehab } 782*b285192aSMauro Carvalho Chehab 783*b285192aSMauro Carvalho Chehab /* Check the left & upper margins 784*b285192aSMauro Carvalho Chehab If the margins are too large, just center the screen 785*b285192aSMauro Carvalho Chehab (enforcing margins causes too many problems) */ 786*b285192aSMauro Carvalho Chehab 787*b285192aSMauro Carvalho Chehab if (var->left_margin + var->xres > IVTV_OSD_MAX_WIDTH + 1) 788*b285192aSMauro Carvalho Chehab var->left_margin = 1 + ((IVTV_OSD_MAX_WIDTH - var->xres) / 2); 789*b285192aSMauro Carvalho Chehab 790*b285192aSMauro Carvalho Chehab if (var->upper_margin + var->yres > (itv->is_out_50hz ? 577 : 481)) 791*b285192aSMauro Carvalho Chehab var->upper_margin = 1 + (((itv->is_out_50hz ? 576 : 480) - 792*b285192aSMauro Carvalho Chehab var->yres) / 2); 793*b285192aSMauro Carvalho Chehab 794*b285192aSMauro Carvalho Chehab /* Maintain overall 'size' for a constant refresh rate */ 795*b285192aSMauro Carvalho Chehab var->right_margin = hlimit - var->left_margin - var->xres; 796*b285192aSMauro Carvalho Chehab var->lower_margin = vlimit - var->upper_margin - var->yres; 797*b285192aSMauro Carvalho Chehab 798*b285192aSMauro Carvalho Chehab /* Fixed sync times */ 799*b285192aSMauro Carvalho Chehab var->hsync_len = 24; 800*b285192aSMauro Carvalho Chehab var->vsync_len = 2; 801*b285192aSMauro Carvalho Chehab 802*b285192aSMauro Carvalho Chehab /* Non-interlaced / interlaced mode is used to switch the OSD filter 803*b285192aSMauro Carvalho Chehab on or off. Adjust the clock timings to maintain a constant 804*b285192aSMauro Carvalho Chehab vertical refresh rate. */ 805*b285192aSMauro Carvalho Chehab if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED) 806*b285192aSMauro Carvalho Chehab var->pixclock = pixclock / 2; 807*b285192aSMauro Carvalho Chehab else 808*b285192aSMauro Carvalho Chehab var->pixclock = pixclock; 809*b285192aSMauro Carvalho Chehab 810*b285192aSMauro Carvalho Chehab itv->osd_rect.width = var->xres; 811*b285192aSMauro Carvalho Chehab itv->osd_rect.height = var->yres; 812*b285192aSMauro Carvalho Chehab 813*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n", 814*b285192aSMauro Carvalho Chehab var->xres, var->yres, 815*b285192aSMauro Carvalho Chehab var->xres_virtual, var->yres_virtual, 816*b285192aSMauro Carvalho Chehab var->bits_per_pixel); 817*b285192aSMauro Carvalho Chehab 818*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_INFO("Display position: %d, %d\n", 819*b285192aSMauro Carvalho Chehab var->left_margin, var->upper_margin); 820*b285192aSMauro Carvalho Chehab 821*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_INFO("Display filter: %s\n", 822*b285192aSMauro Carvalho Chehab (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off"); 823*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB"); 824*b285192aSMauro Carvalho Chehab return 0; 825*b285192aSMauro Carvalho Chehab } 826*b285192aSMauro Carvalho Chehab 827*b285192aSMauro Carvalho Chehab static int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 828*b285192aSMauro Carvalho Chehab { 829*b285192aSMauro Carvalho Chehab struct ivtv *itv = (struct ivtv *) info->par; 830*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_INFO("ivtvfb_check_var\n"); 831*b285192aSMauro Carvalho Chehab return _ivtvfb_check_var(var, itv); 832*b285192aSMauro Carvalho Chehab } 833*b285192aSMauro Carvalho Chehab 834*b285192aSMauro Carvalho Chehab static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) 835*b285192aSMauro Carvalho Chehab { 836*b285192aSMauro Carvalho Chehab u32 osd_pan_index; 837*b285192aSMauro Carvalho Chehab struct ivtv *itv = (struct ivtv *) info->par; 838*b285192aSMauro Carvalho Chehab 839*b285192aSMauro Carvalho Chehab if (var->yoffset + info->var.yres > info->var.yres_virtual || 840*b285192aSMauro Carvalho Chehab var->xoffset + info->var.xres > info->var.xres_virtual) 841*b285192aSMauro Carvalho Chehab return -EINVAL; 842*b285192aSMauro Carvalho Chehab 843*b285192aSMauro Carvalho Chehab osd_pan_index = var->yoffset * info->fix.line_length 844*b285192aSMauro Carvalho Chehab + var->xoffset * info->var.bits_per_pixel / 8; 845*b285192aSMauro Carvalho Chehab write_reg(osd_pan_index, 0x02A0C); 846*b285192aSMauro Carvalho Chehab 847*b285192aSMauro Carvalho Chehab /* Pass this info back the yuv handler */ 848*b285192aSMauro Carvalho Chehab itv->yuv_info.osd_x_pan = var->xoffset; 849*b285192aSMauro Carvalho Chehab itv->yuv_info.osd_y_pan = var->yoffset; 850*b285192aSMauro Carvalho Chehab /* Force update of yuv registers */ 851*b285192aSMauro Carvalho Chehab itv->yuv_info.yuv_forced_update = 1; 852*b285192aSMauro Carvalho Chehab /* Remember this value */ 853*b285192aSMauro Carvalho Chehab itv->osd_info->pan_cur = osd_pan_index; 854*b285192aSMauro Carvalho Chehab return 0; 855*b285192aSMauro Carvalho Chehab } 856*b285192aSMauro Carvalho Chehab 857*b285192aSMauro Carvalho Chehab static int ivtvfb_set_par(struct fb_info *info) 858*b285192aSMauro Carvalho Chehab { 859*b285192aSMauro Carvalho Chehab int rc = 0; 860*b285192aSMauro Carvalho Chehab struct ivtv *itv = (struct ivtv *) info->par; 861*b285192aSMauro Carvalho Chehab 862*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_INFO("ivtvfb_set_par\n"); 863*b285192aSMauro Carvalho Chehab 864*b285192aSMauro Carvalho Chehab rc = ivtvfb_set_var(itv, &info->var); 865*b285192aSMauro Carvalho Chehab ivtvfb_pan_display(&info->var, info); 866*b285192aSMauro Carvalho Chehab ivtvfb_get_fix(itv, &info->fix); 867*b285192aSMauro Carvalho Chehab ivtv_firmware_check(itv, "ivtvfb_set_par"); 868*b285192aSMauro Carvalho Chehab return rc; 869*b285192aSMauro Carvalho Chehab } 870*b285192aSMauro Carvalho Chehab 871*b285192aSMauro Carvalho Chehab static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green, 872*b285192aSMauro Carvalho Chehab unsigned blue, unsigned transp, 873*b285192aSMauro Carvalho Chehab struct fb_info *info) 874*b285192aSMauro Carvalho Chehab { 875*b285192aSMauro Carvalho Chehab u32 color, *palette; 876*b285192aSMauro Carvalho Chehab struct ivtv *itv = (struct ivtv *)info->par; 877*b285192aSMauro Carvalho Chehab 878*b285192aSMauro Carvalho Chehab if (regno >= info->cmap.len) 879*b285192aSMauro Carvalho Chehab return -EINVAL; 880*b285192aSMauro Carvalho Chehab 881*b285192aSMauro Carvalho Chehab color = ((transp & 0xFF00) << 16) |((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8); 882*b285192aSMauro Carvalho Chehab if (info->var.bits_per_pixel <= 8) { 883*b285192aSMauro Carvalho Chehab write_reg(regno, 0x02a30); 884*b285192aSMauro Carvalho Chehab write_reg(color, 0x02a34); 885*b285192aSMauro Carvalho Chehab itv->osd_info->palette_cur[regno] = color; 886*b285192aSMauro Carvalho Chehab return 0; 887*b285192aSMauro Carvalho Chehab } 888*b285192aSMauro Carvalho Chehab if (regno >= 16) 889*b285192aSMauro Carvalho Chehab return -EINVAL; 890*b285192aSMauro Carvalho Chehab 891*b285192aSMauro Carvalho Chehab palette = info->pseudo_palette; 892*b285192aSMauro Carvalho Chehab if (info->var.bits_per_pixel == 16) { 893*b285192aSMauro Carvalho Chehab switch (info->var.green.length) { 894*b285192aSMauro Carvalho Chehab case 4: 895*b285192aSMauro Carvalho Chehab color = ((red & 0xf000) >> 4) | 896*b285192aSMauro Carvalho Chehab ((green & 0xf000) >> 8) | 897*b285192aSMauro Carvalho Chehab ((blue & 0xf000) >> 12); 898*b285192aSMauro Carvalho Chehab break; 899*b285192aSMauro Carvalho Chehab case 5: 900*b285192aSMauro Carvalho Chehab color = ((red & 0xf800) >> 1) | 901*b285192aSMauro Carvalho Chehab ((green & 0xf800) >> 6) | 902*b285192aSMauro Carvalho Chehab ((blue & 0xf800) >> 11); 903*b285192aSMauro Carvalho Chehab break; 904*b285192aSMauro Carvalho Chehab case 6: 905*b285192aSMauro Carvalho Chehab color = (red & 0xf800 ) | 906*b285192aSMauro Carvalho Chehab ((green & 0xfc00) >> 5) | 907*b285192aSMauro Carvalho Chehab ((blue & 0xf800) >> 11); 908*b285192aSMauro Carvalho Chehab break; 909*b285192aSMauro Carvalho Chehab } 910*b285192aSMauro Carvalho Chehab } 911*b285192aSMauro Carvalho Chehab palette[regno] = color; 912*b285192aSMauro Carvalho Chehab return 0; 913*b285192aSMauro Carvalho Chehab } 914*b285192aSMauro Carvalho Chehab 915*b285192aSMauro Carvalho Chehab /* We don't really support blanking. All this does is enable or 916*b285192aSMauro Carvalho Chehab disable the OSD. */ 917*b285192aSMauro Carvalho Chehab static int ivtvfb_blank(int blank_mode, struct fb_info *info) 918*b285192aSMauro Carvalho Chehab { 919*b285192aSMauro Carvalho Chehab struct ivtv *itv = (struct ivtv *)info->par; 920*b285192aSMauro Carvalho Chehab 921*b285192aSMauro Carvalho Chehab IVTVFB_DEBUG_INFO("Set blanking mode : %d\n", blank_mode); 922*b285192aSMauro Carvalho Chehab switch (blank_mode) { 923*b285192aSMauro Carvalho Chehab case FB_BLANK_UNBLANK: 924*b285192aSMauro Carvalho Chehab ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1); 925*b285192aSMauro Carvalho Chehab ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1); 926*b285192aSMauro Carvalho Chehab break; 927*b285192aSMauro Carvalho Chehab case FB_BLANK_NORMAL: 928*b285192aSMauro Carvalho Chehab case FB_BLANK_HSYNC_SUSPEND: 929*b285192aSMauro Carvalho Chehab case FB_BLANK_VSYNC_SUSPEND: 930*b285192aSMauro Carvalho Chehab ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0); 931*b285192aSMauro Carvalho Chehab ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1); 932*b285192aSMauro Carvalho Chehab break; 933*b285192aSMauro Carvalho Chehab case FB_BLANK_POWERDOWN: 934*b285192aSMauro Carvalho Chehab ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0); 935*b285192aSMauro Carvalho Chehab ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0); 936*b285192aSMauro Carvalho Chehab break; 937*b285192aSMauro Carvalho Chehab } 938*b285192aSMauro Carvalho Chehab itv->osd_info->blank_cur = blank_mode; 939*b285192aSMauro Carvalho Chehab return 0; 940*b285192aSMauro Carvalho Chehab } 941*b285192aSMauro Carvalho Chehab 942*b285192aSMauro Carvalho Chehab static struct fb_ops ivtvfb_ops = { 943*b285192aSMauro Carvalho Chehab .owner = THIS_MODULE, 944*b285192aSMauro Carvalho Chehab .fb_write = ivtvfb_write, 945*b285192aSMauro Carvalho Chehab .fb_check_var = ivtvfb_check_var, 946*b285192aSMauro Carvalho Chehab .fb_set_par = ivtvfb_set_par, 947*b285192aSMauro Carvalho Chehab .fb_setcolreg = ivtvfb_setcolreg, 948*b285192aSMauro Carvalho Chehab .fb_fillrect = cfb_fillrect, 949*b285192aSMauro Carvalho Chehab .fb_copyarea = cfb_copyarea, 950*b285192aSMauro Carvalho Chehab .fb_imageblit = cfb_imageblit, 951*b285192aSMauro Carvalho Chehab .fb_cursor = NULL, 952*b285192aSMauro Carvalho Chehab .fb_ioctl = ivtvfb_ioctl, 953*b285192aSMauro Carvalho Chehab .fb_pan_display = ivtvfb_pan_display, 954*b285192aSMauro Carvalho Chehab .fb_blank = ivtvfb_blank, 955*b285192aSMauro Carvalho Chehab }; 956*b285192aSMauro Carvalho Chehab 957*b285192aSMauro Carvalho Chehab /* Restore hardware after firmware restart */ 958*b285192aSMauro Carvalho Chehab static void ivtvfb_restore(struct ivtv *itv) 959*b285192aSMauro Carvalho Chehab { 960*b285192aSMauro Carvalho Chehab struct osd_info *oi = itv->osd_info; 961*b285192aSMauro Carvalho Chehab int i; 962*b285192aSMauro Carvalho Chehab 963*b285192aSMauro Carvalho Chehab ivtvfb_set_var(itv, &oi->fbvar_cur); 964*b285192aSMauro Carvalho Chehab ivtvfb_blank(oi->blank_cur, &oi->ivtvfb_info); 965*b285192aSMauro Carvalho Chehab for (i = 0; i < 256; i++) { 966*b285192aSMauro Carvalho Chehab write_reg(i, 0x02a30); 967*b285192aSMauro Carvalho Chehab write_reg(oi->palette_cur[i], 0x02a34); 968*b285192aSMauro Carvalho Chehab } 969*b285192aSMauro Carvalho Chehab write_reg(oi->pan_cur, 0x02a0c); 970*b285192aSMauro Carvalho Chehab } 971*b285192aSMauro Carvalho Chehab 972*b285192aSMauro Carvalho Chehab /* Initialization */ 973*b285192aSMauro Carvalho Chehab 974*b285192aSMauro Carvalho Chehab 975*b285192aSMauro Carvalho Chehab /* Setup our initial video mode */ 976*b285192aSMauro Carvalho Chehab static int ivtvfb_init_vidmode(struct ivtv *itv) 977*b285192aSMauro Carvalho Chehab { 978*b285192aSMauro Carvalho Chehab struct osd_info *oi = itv->osd_info; 979*b285192aSMauro Carvalho Chehab struct v4l2_rect start_window; 980*b285192aSMauro Carvalho Chehab int max_height; 981*b285192aSMauro Carvalho Chehab 982*b285192aSMauro Carvalho Chehab /* Color mode */ 983*b285192aSMauro Carvalho Chehab 984*b285192aSMauro Carvalho Chehab if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32) 985*b285192aSMauro Carvalho Chehab osd_depth = 8; 986*b285192aSMauro Carvalho Chehab oi->bits_per_pixel = osd_depth; 987*b285192aSMauro Carvalho Chehab oi->bytes_per_pixel = oi->bits_per_pixel / 8; 988*b285192aSMauro Carvalho Chehab 989*b285192aSMauro Carvalho Chehab /* Horizontal size & position */ 990*b285192aSMauro Carvalho Chehab 991*b285192aSMauro Carvalho Chehab if (osd_xres > 720) 992*b285192aSMauro Carvalho Chehab osd_xres = 720; 993*b285192aSMauro Carvalho Chehab 994*b285192aSMauro Carvalho Chehab /* Must be a multiple of 4 for 8bpp & 2 for 16bpp */ 995*b285192aSMauro Carvalho Chehab if (osd_depth == 8) 996*b285192aSMauro Carvalho Chehab osd_xres &= ~3; 997*b285192aSMauro Carvalho Chehab else if (osd_depth == 16) 998*b285192aSMauro Carvalho Chehab osd_xres &= ~1; 999*b285192aSMauro Carvalho Chehab 1000*b285192aSMauro Carvalho Chehab start_window.width = osd_xres ? osd_xres : 640; 1001*b285192aSMauro Carvalho Chehab 1002*b285192aSMauro Carvalho Chehab /* Check horizontal start (osd_left). */ 1003*b285192aSMauro Carvalho Chehab if (osd_left && osd_left + start_window.width > 721) { 1004*b285192aSMauro Carvalho Chehab IVTVFB_ERR("Invalid osd_left - assuming default\n"); 1005*b285192aSMauro Carvalho Chehab osd_left = 0; 1006*b285192aSMauro Carvalho Chehab } 1007*b285192aSMauro Carvalho Chehab 1008*b285192aSMauro Carvalho Chehab /* Hardware coords start at 0, user coords start at 1. */ 1009*b285192aSMauro Carvalho Chehab osd_left--; 1010*b285192aSMauro Carvalho Chehab 1011*b285192aSMauro Carvalho Chehab start_window.left = osd_left >= 0 ? 1012*b285192aSMauro Carvalho Chehab osd_left : ((IVTV_OSD_MAX_WIDTH - start_window.width) / 2); 1013*b285192aSMauro Carvalho Chehab 1014*b285192aSMauro Carvalho Chehab oi->display_byte_stride = 1015*b285192aSMauro Carvalho Chehab start_window.width * oi->bytes_per_pixel; 1016*b285192aSMauro Carvalho Chehab 1017*b285192aSMauro Carvalho Chehab /* Vertical size & position */ 1018*b285192aSMauro Carvalho Chehab 1019*b285192aSMauro Carvalho Chehab max_height = itv->is_out_50hz ? 576 : 480; 1020*b285192aSMauro Carvalho Chehab 1021*b285192aSMauro Carvalho Chehab if (osd_yres > max_height) 1022*b285192aSMauro Carvalho Chehab osd_yres = max_height; 1023*b285192aSMauro Carvalho Chehab 1024*b285192aSMauro Carvalho Chehab start_window.height = osd_yres ? 1025*b285192aSMauro Carvalho Chehab osd_yres : itv->is_out_50hz ? 480 : 400; 1026*b285192aSMauro Carvalho Chehab 1027*b285192aSMauro Carvalho Chehab /* Check vertical start (osd_upper). */ 1028*b285192aSMauro Carvalho Chehab if (osd_upper + start_window.height > max_height + 1) { 1029*b285192aSMauro Carvalho Chehab IVTVFB_ERR("Invalid osd_upper - assuming default\n"); 1030*b285192aSMauro Carvalho Chehab osd_upper = 0; 1031*b285192aSMauro Carvalho Chehab } 1032*b285192aSMauro Carvalho Chehab 1033*b285192aSMauro Carvalho Chehab /* Hardware coords start at 0, user coords start at 1. */ 1034*b285192aSMauro Carvalho Chehab osd_upper--; 1035*b285192aSMauro Carvalho Chehab 1036*b285192aSMauro Carvalho Chehab start_window.top = osd_upper >= 0 ? osd_upper : ((max_height - start_window.height) / 2); 1037*b285192aSMauro Carvalho Chehab 1038*b285192aSMauro Carvalho Chehab oi->display_width = start_window.width; 1039*b285192aSMauro Carvalho Chehab oi->display_height = start_window.height; 1040*b285192aSMauro Carvalho Chehab 1041*b285192aSMauro Carvalho Chehab /* Generate a valid fb_var_screeninfo */ 1042*b285192aSMauro Carvalho Chehab 1043*b285192aSMauro Carvalho Chehab oi->ivtvfb_defined.xres = oi->display_width; 1044*b285192aSMauro Carvalho Chehab oi->ivtvfb_defined.yres = oi->display_height; 1045*b285192aSMauro Carvalho Chehab oi->ivtvfb_defined.xres_virtual = oi->display_width; 1046*b285192aSMauro Carvalho Chehab oi->ivtvfb_defined.yres_virtual = oi->display_height; 1047*b285192aSMauro Carvalho Chehab oi->ivtvfb_defined.bits_per_pixel = oi->bits_per_pixel; 1048*b285192aSMauro Carvalho Chehab oi->ivtvfb_defined.vmode = (osd_laced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED); 1049*b285192aSMauro Carvalho Chehab oi->ivtvfb_defined.left_margin = start_window.left + 1; 1050*b285192aSMauro Carvalho Chehab oi->ivtvfb_defined.upper_margin = start_window.top + 1; 1051*b285192aSMauro Carvalho Chehab oi->ivtvfb_defined.accel_flags = FB_ACCEL_NONE; 1052*b285192aSMauro Carvalho Chehab oi->ivtvfb_defined.nonstd = 0; 1053*b285192aSMauro Carvalho Chehab 1054*b285192aSMauro Carvalho Chehab /* We've filled in the most data, let the usual mode check 1055*b285192aSMauro Carvalho Chehab routine fill in the rest. */ 1056*b285192aSMauro Carvalho Chehab _ivtvfb_check_var(&oi->ivtvfb_defined, itv); 1057*b285192aSMauro Carvalho Chehab 1058*b285192aSMauro Carvalho Chehab /* Generate valid fb_fix_screeninfo */ 1059*b285192aSMauro Carvalho Chehab 1060*b285192aSMauro Carvalho Chehab ivtvfb_get_fix(itv, &oi->ivtvfb_fix); 1061*b285192aSMauro Carvalho Chehab 1062*b285192aSMauro Carvalho Chehab /* Generate valid fb_info */ 1063*b285192aSMauro Carvalho Chehab 1064*b285192aSMauro Carvalho Chehab oi->ivtvfb_info.node = -1; 1065*b285192aSMauro Carvalho Chehab oi->ivtvfb_info.flags = FBINFO_FLAG_DEFAULT; 1066*b285192aSMauro Carvalho Chehab oi->ivtvfb_info.fbops = &ivtvfb_ops; 1067*b285192aSMauro Carvalho Chehab oi->ivtvfb_info.par = itv; 1068*b285192aSMauro Carvalho Chehab oi->ivtvfb_info.var = oi->ivtvfb_defined; 1069*b285192aSMauro Carvalho Chehab oi->ivtvfb_info.fix = oi->ivtvfb_fix; 1070*b285192aSMauro Carvalho Chehab oi->ivtvfb_info.screen_base = (u8 __iomem *)oi->video_vbase; 1071*b285192aSMauro Carvalho Chehab oi->ivtvfb_info.fbops = &ivtvfb_ops; 1072*b285192aSMauro Carvalho Chehab 1073*b285192aSMauro Carvalho Chehab /* Supply some monitor specs. Bogus values will do for now */ 1074*b285192aSMauro Carvalho Chehab oi->ivtvfb_info.monspecs.hfmin = 8000; 1075*b285192aSMauro Carvalho Chehab oi->ivtvfb_info.monspecs.hfmax = 70000; 1076*b285192aSMauro Carvalho Chehab oi->ivtvfb_info.monspecs.vfmin = 10; 1077*b285192aSMauro Carvalho Chehab oi->ivtvfb_info.monspecs.vfmax = 100; 1078*b285192aSMauro Carvalho Chehab 1079*b285192aSMauro Carvalho Chehab /* Allocate color map */ 1080*b285192aSMauro Carvalho Chehab if (fb_alloc_cmap(&oi->ivtvfb_info.cmap, 256, 1)) { 1081*b285192aSMauro Carvalho Chehab IVTVFB_ERR("abort, unable to alloc cmap\n"); 1082*b285192aSMauro Carvalho Chehab return -ENOMEM; 1083*b285192aSMauro Carvalho Chehab } 1084*b285192aSMauro Carvalho Chehab 1085*b285192aSMauro Carvalho Chehab /* Allocate the pseudo palette */ 1086*b285192aSMauro Carvalho Chehab oi->ivtvfb_info.pseudo_palette = 1087*b285192aSMauro Carvalho Chehab kmalloc(sizeof(u32) * 16, GFP_KERNEL|__GFP_NOWARN); 1088*b285192aSMauro Carvalho Chehab 1089*b285192aSMauro Carvalho Chehab if (!oi->ivtvfb_info.pseudo_palette) { 1090*b285192aSMauro Carvalho Chehab IVTVFB_ERR("abort, unable to alloc pseudo palette\n"); 1091*b285192aSMauro Carvalho Chehab return -ENOMEM; 1092*b285192aSMauro Carvalho Chehab } 1093*b285192aSMauro Carvalho Chehab 1094*b285192aSMauro Carvalho Chehab return 0; 1095*b285192aSMauro Carvalho Chehab } 1096*b285192aSMauro Carvalho Chehab 1097*b285192aSMauro Carvalho Chehab /* Find OSD buffer base & size. Add to mtrr. Zero osd buffer. */ 1098*b285192aSMauro Carvalho Chehab 1099*b285192aSMauro Carvalho Chehab static int ivtvfb_init_io(struct ivtv *itv) 1100*b285192aSMauro Carvalho Chehab { 1101*b285192aSMauro Carvalho Chehab struct osd_info *oi = itv->osd_info; 1102*b285192aSMauro Carvalho Chehab 1103*b285192aSMauro Carvalho Chehab mutex_lock(&itv->serialize_lock); 1104*b285192aSMauro Carvalho Chehab if (ivtv_init_on_first_open(itv)) { 1105*b285192aSMauro Carvalho Chehab mutex_unlock(&itv->serialize_lock); 1106*b285192aSMauro Carvalho Chehab IVTVFB_ERR("Failed to initialize ivtv\n"); 1107*b285192aSMauro Carvalho Chehab return -ENXIO; 1108*b285192aSMauro Carvalho Chehab } 1109*b285192aSMauro Carvalho Chehab mutex_unlock(&itv->serialize_lock); 1110*b285192aSMauro Carvalho Chehab 1111*b285192aSMauro Carvalho Chehab if (ivtvfb_get_framebuffer(itv, &oi->video_rbase, 1112*b285192aSMauro Carvalho Chehab &oi->video_buffer_size) < 0) { 1113*b285192aSMauro Carvalho Chehab IVTVFB_ERR("Firmware failed to respond\n"); 1114*b285192aSMauro Carvalho Chehab return -EIO; 1115*b285192aSMauro Carvalho Chehab } 1116*b285192aSMauro Carvalho Chehab 1117*b285192aSMauro Carvalho Chehab /* The osd buffer size depends on the number of video buffers allocated 1118*b285192aSMauro Carvalho Chehab on the PVR350 itself. For now we'll hardcode the smallest osd buffer 1119*b285192aSMauro Carvalho Chehab size to prevent any overlap. */ 1120*b285192aSMauro Carvalho Chehab oi->video_buffer_size = 1704960; 1121*b285192aSMauro Carvalho Chehab 1122*b285192aSMauro Carvalho Chehab oi->video_pbase = itv->base_addr + IVTV_DECODER_OFFSET + oi->video_rbase; 1123*b285192aSMauro Carvalho Chehab oi->video_vbase = itv->dec_mem + oi->video_rbase; 1124*b285192aSMauro Carvalho Chehab 1125*b285192aSMauro Carvalho Chehab if (!oi->video_vbase) { 1126*b285192aSMauro Carvalho Chehab IVTVFB_ERR("abort, video memory 0x%x @ 0x%lx isn't mapped!\n", 1127*b285192aSMauro Carvalho Chehab oi->video_buffer_size, oi->video_pbase); 1128*b285192aSMauro Carvalho Chehab return -EIO; 1129*b285192aSMauro Carvalho Chehab } 1130*b285192aSMauro Carvalho Chehab 1131*b285192aSMauro Carvalho Chehab IVTVFB_INFO("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n", 1132*b285192aSMauro Carvalho Chehab oi->video_pbase, oi->video_vbase, 1133*b285192aSMauro Carvalho Chehab oi->video_buffer_size / 1024); 1134*b285192aSMauro Carvalho Chehab 1135*b285192aSMauro Carvalho Chehab #ifdef CONFIG_MTRR 1136*b285192aSMauro Carvalho Chehab { 1137*b285192aSMauro Carvalho Chehab /* Find the largest power of two that maps the whole buffer */ 1138*b285192aSMauro Carvalho Chehab int size_shift = 31; 1139*b285192aSMauro Carvalho Chehab 1140*b285192aSMauro Carvalho Chehab while (!(oi->video_buffer_size & (1 << size_shift))) { 1141*b285192aSMauro Carvalho Chehab size_shift--; 1142*b285192aSMauro Carvalho Chehab } 1143*b285192aSMauro Carvalho Chehab size_shift++; 1144*b285192aSMauro Carvalho Chehab oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1); 1145*b285192aSMauro Carvalho Chehab oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size; 1146*b285192aSMauro Carvalho Chehab oi->fb_end_aligned_physaddr += (1 << size_shift) - 1; 1147*b285192aSMauro Carvalho Chehab oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1); 1148*b285192aSMauro Carvalho Chehab if (mtrr_add(oi->fb_start_aligned_physaddr, 1149*b285192aSMauro Carvalho Chehab oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr, 1150*b285192aSMauro Carvalho Chehab MTRR_TYPE_WRCOMB, 1) < 0) { 1151*b285192aSMauro Carvalho Chehab IVTVFB_INFO("disabled mttr\n"); 1152*b285192aSMauro Carvalho Chehab oi->fb_start_aligned_physaddr = 0; 1153*b285192aSMauro Carvalho Chehab oi->fb_end_aligned_physaddr = 0; 1154*b285192aSMauro Carvalho Chehab } 1155*b285192aSMauro Carvalho Chehab } 1156*b285192aSMauro Carvalho Chehab #endif 1157*b285192aSMauro Carvalho Chehab 1158*b285192aSMauro Carvalho Chehab /* Blank the entire osd. */ 1159*b285192aSMauro Carvalho Chehab memset_io(oi->video_vbase, 0, oi->video_buffer_size); 1160*b285192aSMauro Carvalho Chehab 1161*b285192aSMauro Carvalho Chehab return 0; 1162*b285192aSMauro Carvalho Chehab } 1163*b285192aSMauro Carvalho Chehab 1164*b285192aSMauro Carvalho Chehab /* Release any memory we've grabbed & remove mtrr entry */ 1165*b285192aSMauro Carvalho Chehab static void ivtvfb_release_buffers (struct ivtv *itv) 1166*b285192aSMauro Carvalho Chehab { 1167*b285192aSMauro Carvalho Chehab struct osd_info *oi = itv->osd_info; 1168*b285192aSMauro Carvalho Chehab 1169*b285192aSMauro Carvalho Chehab /* Release cmap */ 1170*b285192aSMauro Carvalho Chehab if (oi->ivtvfb_info.cmap.len) 1171*b285192aSMauro Carvalho Chehab fb_dealloc_cmap(&oi->ivtvfb_info.cmap); 1172*b285192aSMauro Carvalho Chehab 1173*b285192aSMauro Carvalho Chehab /* Release pseudo palette */ 1174*b285192aSMauro Carvalho Chehab if (oi->ivtvfb_info.pseudo_palette) 1175*b285192aSMauro Carvalho Chehab kfree(oi->ivtvfb_info.pseudo_palette); 1176*b285192aSMauro Carvalho Chehab 1177*b285192aSMauro Carvalho Chehab #ifdef CONFIG_MTRR 1178*b285192aSMauro Carvalho Chehab if (oi->fb_end_aligned_physaddr) { 1179*b285192aSMauro Carvalho Chehab mtrr_del(-1, oi->fb_start_aligned_physaddr, 1180*b285192aSMauro Carvalho Chehab oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr); 1181*b285192aSMauro Carvalho Chehab } 1182*b285192aSMauro Carvalho Chehab #endif 1183*b285192aSMauro Carvalho Chehab 1184*b285192aSMauro Carvalho Chehab kfree(oi); 1185*b285192aSMauro Carvalho Chehab itv->osd_info = NULL; 1186*b285192aSMauro Carvalho Chehab } 1187*b285192aSMauro Carvalho Chehab 1188*b285192aSMauro Carvalho Chehab /* Initialize the specified card */ 1189*b285192aSMauro Carvalho Chehab 1190*b285192aSMauro Carvalho Chehab static int ivtvfb_init_card(struct ivtv *itv) 1191*b285192aSMauro Carvalho Chehab { 1192*b285192aSMauro Carvalho Chehab int rc; 1193*b285192aSMauro Carvalho Chehab 1194*b285192aSMauro Carvalho Chehab if (itv->osd_info) { 1195*b285192aSMauro Carvalho Chehab IVTVFB_ERR("Card %d already initialised\n", ivtvfb_card_id); 1196*b285192aSMauro Carvalho Chehab return -EBUSY; 1197*b285192aSMauro Carvalho Chehab } 1198*b285192aSMauro Carvalho Chehab 1199*b285192aSMauro Carvalho Chehab itv->osd_info = kzalloc(sizeof(struct osd_info), 1200*b285192aSMauro Carvalho Chehab GFP_ATOMIC|__GFP_NOWARN); 1201*b285192aSMauro Carvalho Chehab if (itv->osd_info == NULL) { 1202*b285192aSMauro Carvalho Chehab IVTVFB_ERR("Failed to allocate memory for osd_info\n"); 1203*b285192aSMauro Carvalho Chehab return -ENOMEM; 1204*b285192aSMauro Carvalho Chehab } 1205*b285192aSMauro Carvalho Chehab 1206*b285192aSMauro Carvalho Chehab /* Find & setup the OSD buffer */ 1207*b285192aSMauro Carvalho Chehab rc = ivtvfb_init_io(itv); 1208*b285192aSMauro Carvalho Chehab if (rc) { 1209*b285192aSMauro Carvalho Chehab ivtvfb_release_buffers(itv); 1210*b285192aSMauro Carvalho Chehab return rc; 1211*b285192aSMauro Carvalho Chehab } 1212*b285192aSMauro Carvalho Chehab 1213*b285192aSMauro Carvalho Chehab /* Set the startup video mode information */ 1214*b285192aSMauro Carvalho Chehab if ((rc = ivtvfb_init_vidmode(itv))) { 1215*b285192aSMauro Carvalho Chehab ivtvfb_release_buffers(itv); 1216*b285192aSMauro Carvalho Chehab return rc; 1217*b285192aSMauro Carvalho Chehab } 1218*b285192aSMauro Carvalho Chehab 1219*b285192aSMauro Carvalho Chehab /* Register the framebuffer */ 1220*b285192aSMauro Carvalho Chehab if (register_framebuffer(&itv->osd_info->ivtvfb_info) < 0) { 1221*b285192aSMauro Carvalho Chehab ivtvfb_release_buffers(itv); 1222*b285192aSMauro Carvalho Chehab return -EINVAL; 1223*b285192aSMauro Carvalho Chehab } 1224*b285192aSMauro Carvalho Chehab 1225*b285192aSMauro Carvalho Chehab itv->osd_video_pbase = itv->osd_info->video_pbase; 1226*b285192aSMauro Carvalho Chehab 1227*b285192aSMauro Carvalho Chehab /* Set the card to the requested mode */ 1228*b285192aSMauro Carvalho Chehab ivtvfb_set_par(&itv->osd_info->ivtvfb_info); 1229*b285192aSMauro Carvalho Chehab 1230*b285192aSMauro Carvalho Chehab /* Set color 0 to black */ 1231*b285192aSMauro Carvalho Chehab write_reg(0, 0x02a30); 1232*b285192aSMauro Carvalho Chehab write_reg(0, 0x02a34); 1233*b285192aSMauro Carvalho Chehab 1234*b285192aSMauro Carvalho Chehab /* Enable the osd */ 1235*b285192aSMauro Carvalho Chehab ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info); 1236*b285192aSMauro Carvalho Chehab 1237*b285192aSMauro Carvalho Chehab /* Enable restart */ 1238*b285192aSMauro Carvalho Chehab itv->ivtvfb_restore = ivtvfb_restore; 1239*b285192aSMauro Carvalho Chehab 1240*b285192aSMauro Carvalho Chehab /* Allocate DMA */ 1241*b285192aSMauro Carvalho Chehab ivtv_udma_alloc(itv); 1242*b285192aSMauro Carvalho Chehab return 0; 1243*b285192aSMauro Carvalho Chehab 1244*b285192aSMauro Carvalho Chehab } 1245*b285192aSMauro Carvalho Chehab 1246*b285192aSMauro Carvalho Chehab static int __init ivtvfb_callback_init(struct device *dev, void *p) 1247*b285192aSMauro Carvalho Chehab { 1248*b285192aSMauro Carvalho Chehab struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); 1249*b285192aSMauro Carvalho Chehab struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev); 1250*b285192aSMauro Carvalho Chehab 1251*b285192aSMauro Carvalho Chehab if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { 1252*b285192aSMauro Carvalho Chehab if (ivtvfb_init_card(itv) == 0) { 1253*b285192aSMauro Carvalho Chehab IVTVFB_INFO("Framebuffer registered on %s\n", 1254*b285192aSMauro Carvalho Chehab itv->v4l2_dev.name); 1255*b285192aSMauro Carvalho Chehab (*(int *)p)++; 1256*b285192aSMauro Carvalho Chehab } 1257*b285192aSMauro Carvalho Chehab } 1258*b285192aSMauro Carvalho Chehab return 0; 1259*b285192aSMauro Carvalho Chehab } 1260*b285192aSMauro Carvalho Chehab 1261*b285192aSMauro Carvalho Chehab static int ivtvfb_callback_cleanup(struct device *dev, void *p) 1262*b285192aSMauro Carvalho Chehab { 1263*b285192aSMauro Carvalho Chehab struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); 1264*b285192aSMauro Carvalho Chehab struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev); 1265*b285192aSMauro Carvalho Chehab struct osd_info *oi = itv->osd_info; 1266*b285192aSMauro Carvalho Chehab 1267*b285192aSMauro Carvalho Chehab if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { 1268*b285192aSMauro Carvalho Chehab if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) { 1269*b285192aSMauro Carvalho Chehab IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n", 1270*b285192aSMauro Carvalho Chehab itv->instance); 1271*b285192aSMauro Carvalho Chehab return 0; 1272*b285192aSMauro Carvalho Chehab } 1273*b285192aSMauro Carvalho Chehab IVTVFB_INFO("Unregister framebuffer %d\n", itv->instance); 1274*b285192aSMauro Carvalho Chehab itv->ivtvfb_restore = NULL; 1275*b285192aSMauro Carvalho Chehab ivtvfb_blank(FB_BLANK_VSYNC_SUSPEND, &oi->ivtvfb_info); 1276*b285192aSMauro Carvalho Chehab ivtvfb_release_buffers(itv); 1277*b285192aSMauro Carvalho Chehab itv->osd_video_pbase = 0; 1278*b285192aSMauro Carvalho Chehab } 1279*b285192aSMauro Carvalho Chehab return 0; 1280*b285192aSMauro Carvalho Chehab } 1281*b285192aSMauro Carvalho Chehab 1282*b285192aSMauro Carvalho Chehab static int __init ivtvfb_init(void) 1283*b285192aSMauro Carvalho Chehab { 1284*b285192aSMauro Carvalho Chehab struct device_driver *drv; 1285*b285192aSMauro Carvalho Chehab int registered = 0; 1286*b285192aSMauro Carvalho Chehab int err; 1287*b285192aSMauro Carvalho Chehab 1288*b285192aSMauro Carvalho Chehab if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) { 1289*b285192aSMauro Carvalho Chehab printk(KERN_ERR "ivtvfb: ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n", 1290*b285192aSMauro Carvalho Chehab IVTV_MAX_CARDS - 1); 1291*b285192aSMauro Carvalho Chehab return -EINVAL; 1292*b285192aSMauro Carvalho Chehab } 1293*b285192aSMauro Carvalho Chehab 1294*b285192aSMauro Carvalho Chehab drv = driver_find("ivtv", &pci_bus_type); 1295*b285192aSMauro Carvalho Chehab err = driver_for_each_device(drv, NULL, ®istered, ivtvfb_callback_init); 1296*b285192aSMauro Carvalho Chehab (void)err; /* suppress compiler warning */ 1297*b285192aSMauro Carvalho Chehab if (!registered) { 1298*b285192aSMauro Carvalho Chehab printk(KERN_ERR "ivtvfb: no cards found\n"); 1299*b285192aSMauro Carvalho Chehab return -ENODEV; 1300*b285192aSMauro Carvalho Chehab } 1301*b285192aSMauro Carvalho Chehab return 0; 1302*b285192aSMauro Carvalho Chehab } 1303*b285192aSMauro Carvalho Chehab 1304*b285192aSMauro Carvalho Chehab static void ivtvfb_cleanup(void) 1305*b285192aSMauro Carvalho Chehab { 1306*b285192aSMauro Carvalho Chehab struct device_driver *drv; 1307*b285192aSMauro Carvalho Chehab int err; 1308*b285192aSMauro Carvalho Chehab 1309*b285192aSMauro Carvalho Chehab printk(KERN_INFO "ivtvfb: Unloading framebuffer module\n"); 1310*b285192aSMauro Carvalho Chehab 1311*b285192aSMauro Carvalho Chehab drv = driver_find("ivtv", &pci_bus_type); 1312*b285192aSMauro Carvalho Chehab err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup); 1313*b285192aSMauro Carvalho Chehab (void)err; /* suppress compiler warning */ 1314*b285192aSMauro Carvalho Chehab } 1315*b285192aSMauro Carvalho Chehab 1316*b285192aSMauro Carvalho Chehab module_init(ivtvfb_init); 1317*b285192aSMauro Carvalho Chehab module_exit(ivtvfb_cleanup); 1318