xref: /openbmc/linux/drivers/media/pci/ivtv/ivtvfb.c (revision b285192a43f0432d82c2c10974204e78af0da596)
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, &registered, 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