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