xref: /openbmc/linux/drivers/media/pci/ivtv/ivtvfb.c (revision 526daee7301d8ec86d8de0698d4c9ada185c5470)
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 
41b285192aSMauro Carvalho Chehab #include "ivtv-driver.h"
42b285192aSMauro Carvalho Chehab #include "ivtv-cards.h"
43b285192aSMauro Carvalho Chehab #include "ivtv-i2c.h"
44b285192aSMauro Carvalho Chehab #include "ivtv-udma.h"
45b285192aSMauro Carvalho Chehab #include "ivtv-mailbox.h"
46b285192aSMauro Carvalho Chehab #include "ivtv-firmware.h"
47b285192aSMauro Carvalho Chehab 
48bbdba43fSMauro Carvalho Chehab #include <linux/fb.h>
49bbdba43fSMauro Carvalho Chehab #include <linux/ivtvfb.h>
50bbdba43fSMauro Carvalho Chehab 
51bbdba43fSMauro Carvalho Chehab #ifdef CONFIG_X86_64
52bbdba43fSMauro Carvalho Chehab #include <asm/pat.h>
53bbdba43fSMauro Carvalho Chehab #endif
54bbdba43fSMauro Carvalho Chehab 
55b285192aSMauro Carvalho Chehab /* card parameters */
56b285192aSMauro Carvalho Chehab static int ivtvfb_card_id = -1;
57b285192aSMauro Carvalho Chehab static int ivtvfb_debug = 0;
58*526daee7SFrench, Nicholas A static bool ivtvfb_force_pat = IS_ENABLED(CONFIG_VIDEO_FB_IVTV_FORCE_PAT);
59b285192aSMauro Carvalho Chehab static bool osd_laced;
60b285192aSMauro Carvalho Chehab static int osd_depth;
61b285192aSMauro Carvalho Chehab static int osd_upper;
62b285192aSMauro Carvalho Chehab static int osd_left;
63b285192aSMauro Carvalho Chehab static int osd_yres;
64b285192aSMauro Carvalho Chehab static int osd_xres;
65b285192aSMauro Carvalho Chehab 
66b285192aSMauro Carvalho Chehab module_param(ivtvfb_card_id, int, 0444);
67b285192aSMauro Carvalho Chehab module_param_named(debug,ivtvfb_debug, int, 0644);
68*526daee7SFrench, Nicholas A module_param_named(force_pat, ivtvfb_force_pat, bool, 0644);
69b285192aSMauro Carvalho Chehab module_param(osd_laced, bool, 0444);
70b285192aSMauro Carvalho Chehab module_param(osd_depth, int, 0444);
71b285192aSMauro Carvalho Chehab module_param(osd_upper, int, 0444);
72b285192aSMauro Carvalho Chehab module_param(osd_left, int, 0444);
73b285192aSMauro Carvalho Chehab module_param(osd_yres, int, 0444);
74b285192aSMauro Carvalho Chehab module_param(osd_xres, int, 0444);
75b285192aSMauro Carvalho Chehab 
76b285192aSMauro Carvalho Chehab MODULE_PARM_DESC(ivtvfb_card_id,
77b285192aSMauro Carvalho Chehab 		 "Only use framebuffer of the specified ivtv card (0-31)\n"
78b285192aSMauro Carvalho Chehab 		 "\t\t\tdefault -1: initialize all available framebuffers");
79b285192aSMauro Carvalho Chehab 
80b285192aSMauro Carvalho Chehab MODULE_PARM_DESC(debug,
81b285192aSMauro Carvalho Chehab 		 "Debug level (bitmask). Default: errors only\n"
82b285192aSMauro Carvalho Chehab 		 "\t\t\t(debug = 3 gives full debugging)");
83b285192aSMauro Carvalho Chehab 
84*526daee7SFrench, Nicholas A MODULE_PARM_DESC(force_pat,
85*526daee7SFrench, Nicholas A 		 "Force initialization on x86 PAT-enabled systems (bool).\n");
86*526daee7SFrench, Nicholas A 
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);
29667ccf860SMauro Carvalho Chehab 		IVTVFB_WARN("ivtvfb_prep_dec_dma_to_device, Error with get_user_pages: %d bytes, %d pages returned\n",
297b285192aSMauro Carvalho Chehab 			       size_in_bytes, itv->udma.page_count);
298b285192aSMauro Carvalho Chehab 
299b285192aSMauro Carvalho Chehab 		/* get_user_pages must have failed completely */
300b285192aSMauro Carvalho Chehab 		return -EIO;
301b285192aSMauro Carvalho Chehab 	}
302b285192aSMauro Carvalho Chehab 
303b285192aSMauro Carvalho Chehab 	IVTVFB_DEBUG_INFO("ivtvfb_prep_dec_dma_to_device, %d bytes, %d pages\n",
304b285192aSMauro Carvalho Chehab 		       size_in_bytes, itv->udma.page_count);
305b285192aSMauro Carvalho Chehab 
306b285192aSMauro Carvalho Chehab 	ivtv_udma_prepare(itv);
307b285192aSMauro Carvalho Chehab 	prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
308b285192aSMauro Carvalho Chehab 	/* if no UDMA is pending and no UDMA is in progress, then the DMA
309b285192aSMauro Carvalho Chehab 	   is finished */
310b285192aSMauro Carvalho Chehab 	while (test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags) ||
311b285192aSMauro Carvalho Chehab 	       test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
312b285192aSMauro Carvalho Chehab 		/* don't interrupt if the DMA is in progress but break off
313b285192aSMauro Carvalho Chehab 		   a still pending DMA. */
314b285192aSMauro Carvalho Chehab 		got_sig = signal_pending(current);
315b285192aSMauro Carvalho Chehab 		if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))
316b285192aSMauro Carvalho Chehab 			break;
317b285192aSMauro Carvalho Chehab 		got_sig = 0;
318b285192aSMauro Carvalho Chehab 		schedule();
319b285192aSMauro Carvalho Chehab 	}
320b285192aSMauro Carvalho Chehab 	finish_wait(&itv->dma_waitq, &wait);
321b285192aSMauro Carvalho Chehab 
322b285192aSMauro Carvalho Chehab 	/* Unmap Last DMA Xfer */
323b285192aSMauro Carvalho Chehab 	ivtv_udma_unmap(itv);
324b285192aSMauro Carvalho Chehab 	mutex_unlock(&itv->udma.lock);
325b285192aSMauro Carvalho Chehab 	if (got_sig) {
326b285192aSMauro Carvalho Chehab 		IVTV_DEBUG_INFO("User stopped OSD\n");
327b285192aSMauro Carvalho Chehab 		return -EINTR;
328b285192aSMauro Carvalho Chehab 	}
329b285192aSMauro Carvalho Chehab 
330b285192aSMauro Carvalho Chehab 	return 0;
331b285192aSMauro Carvalho Chehab }
332b285192aSMauro Carvalho Chehab 
333b285192aSMauro Carvalho Chehab static int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source,
334b285192aSMauro Carvalho Chehab 			      unsigned long dest_offset, int count)
335b285192aSMauro Carvalho Chehab {
336b285192aSMauro Carvalho Chehab 	DEFINE_WAIT(wait);
337b285192aSMauro Carvalho Chehab 	struct osd_info *oi = itv->osd_info;
338b285192aSMauro Carvalho Chehab 
339b285192aSMauro Carvalho Chehab 	/* Nothing to do */
340b285192aSMauro Carvalho Chehab 	if (count == 0) {
341b285192aSMauro Carvalho Chehab 		IVTVFB_DEBUG_WARN("ivtvfb_prep_frame: Nothing to do. count = 0\n");
342b285192aSMauro Carvalho Chehab 		return -EINVAL;
343b285192aSMauro Carvalho Chehab 	}
344b285192aSMauro Carvalho Chehab 
345b285192aSMauro Carvalho Chehab 	/* Check Total FB Size */
346b285192aSMauro Carvalho Chehab 	if ((dest_offset + count) > oi->video_buffer_size) {
347b285192aSMauro Carvalho Chehab 		IVTVFB_WARN("ivtvfb_prep_frame: Overflowing the framebuffer %ld, only %d available\n",
348b285192aSMauro Carvalho Chehab 			dest_offset + count, oi->video_buffer_size);
349b285192aSMauro Carvalho Chehab 		return -E2BIG;
350b285192aSMauro Carvalho Chehab 	}
351b285192aSMauro Carvalho Chehab 
352b285192aSMauro Carvalho Chehab 	/* Not fatal, but will have undesirable results */
353b285192aSMauro Carvalho Chehab 	if ((unsigned long)source & 3)
354057bad7bSMauro Carvalho Chehab 		IVTVFB_WARN("ivtvfb_prep_frame: Source address not 32 bit aligned (%p)\n",
355057bad7bSMauro Carvalho Chehab 			    source);
356b285192aSMauro Carvalho Chehab 
357b285192aSMauro Carvalho Chehab 	if (dest_offset & 3)
358b285192aSMauro Carvalho Chehab 		IVTVFB_WARN("ivtvfb_prep_frame: Dest offset not 32 bit aligned (%ld)\n", dest_offset);
359b285192aSMauro Carvalho Chehab 
360b285192aSMauro Carvalho Chehab 	if (count & 3)
361b285192aSMauro Carvalho Chehab 		IVTVFB_WARN("ivtvfb_prep_frame: Count not a multiple of 4 (%d)\n", count);
362b285192aSMauro Carvalho Chehab 
363b285192aSMauro Carvalho Chehab 	/* Check Source */
36496d4f267SLinus Torvalds 	if (!access_ok(source + dest_offset, count)) {
365057bad7bSMauro Carvalho Chehab 		IVTVFB_WARN("Invalid userspace pointer %p\n", source);
366b285192aSMauro Carvalho Chehab 
367057bad7bSMauro Carvalho Chehab 		IVTVFB_DEBUG_WARN("access_ok() failed for offset 0x%08lx source %p count %d\n",
368057bad7bSMauro Carvalho Chehab 				  dest_offset, source, count);
369b285192aSMauro Carvalho Chehab 		return -EINVAL;
370b285192aSMauro Carvalho Chehab 	}
371b285192aSMauro Carvalho Chehab 
372b285192aSMauro Carvalho Chehab 	/* OSD Address to send DMA to */
373b285192aSMauro Carvalho Chehab 	dest_offset += IVTV_DECODER_OFFSET + oi->video_rbase;
374b285192aSMauro Carvalho Chehab 
375b285192aSMauro Carvalho Chehab 	/* Fill Buffers */
376b285192aSMauro Carvalho Chehab 	return ivtvfb_prep_dec_dma_to_device(itv, dest_offset, source, count);
377b285192aSMauro Carvalho Chehab }
378b285192aSMauro Carvalho Chehab 
379b285192aSMauro Carvalho Chehab static ssize_t ivtvfb_write(struct fb_info *info, const char __user *buf,
380b285192aSMauro Carvalho Chehab 						size_t count, loff_t *ppos)
381b285192aSMauro Carvalho Chehab {
382b285192aSMauro Carvalho Chehab 	unsigned long p = *ppos;
383b285192aSMauro Carvalho Chehab 	void *dst;
384b285192aSMauro Carvalho Chehab 	int err = 0;
385b285192aSMauro Carvalho Chehab 	int dma_err;
386b285192aSMauro Carvalho Chehab 	unsigned long total_size;
387b285192aSMauro Carvalho Chehab 	struct ivtv *itv = (struct ivtv *) info->par;
388b285192aSMauro Carvalho Chehab 	unsigned long dma_offset =
389b285192aSMauro Carvalho Chehab 			IVTV_DECODER_OFFSET + itv->osd_info->video_rbase;
390b285192aSMauro Carvalho Chehab 	unsigned long dma_size;
391b285192aSMauro Carvalho Chehab 	u16 lead = 0, tail = 0;
392b285192aSMauro Carvalho Chehab 
393b285192aSMauro Carvalho Chehab 	if (info->state != FBINFO_STATE_RUNNING)
394b285192aSMauro Carvalho Chehab 		return -EPERM;
395b285192aSMauro Carvalho Chehab 
396b285192aSMauro Carvalho Chehab 	total_size = info->screen_size;
397b285192aSMauro Carvalho Chehab 
398b285192aSMauro Carvalho Chehab 	if (total_size == 0)
399b285192aSMauro Carvalho Chehab 		total_size = info->fix.smem_len;
400b285192aSMauro Carvalho Chehab 
401b285192aSMauro Carvalho Chehab 	if (p > total_size)
402b285192aSMauro Carvalho Chehab 		return -EFBIG;
403b285192aSMauro Carvalho Chehab 
404b285192aSMauro Carvalho Chehab 	if (count > total_size) {
405b285192aSMauro Carvalho Chehab 		err = -EFBIG;
406b285192aSMauro Carvalho Chehab 		count = total_size;
407b285192aSMauro Carvalho Chehab 	}
408b285192aSMauro Carvalho Chehab 
409b285192aSMauro Carvalho Chehab 	if (count + p > total_size) {
410b285192aSMauro Carvalho Chehab 		if (!err)
411b285192aSMauro Carvalho Chehab 			err = -ENOSPC;
412b285192aSMauro Carvalho Chehab 		count = total_size - p;
413b285192aSMauro Carvalho Chehab 	}
414b285192aSMauro Carvalho Chehab 
415b285192aSMauro Carvalho Chehab 	dst = (void __force *) (info->screen_base + p);
416b285192aSMauro Carvalho Chehab 
417b285192aSMauro Carvalho Chehab 	if (info->fbops->fb_sync)
418b285192aSMauro Carvalho Chehab 		info->fbops->fb_sync(info);
419b285192aSMauro Carvalho Chehab 
420b285192aSMauro Carvalho Chehab 	/* If transfer size > threshold and both src/dst
421b285192aSMauro Carvalho Chehab 	addresses are aligned, use DMA */
422b285192aSMauro Carvalho Chehab 	if (count >= 4096 &&
423b285192aSMauro Carvalho Chehab 	    ((unsigned long)buf & 3) == ((unsigned long)dst & 3)) {
424b285192aSMauro Carvalho Chehab 		/* Odd address = can't DMA. Align */
425b285192aSMauro Carvalho Chehab 		if ((unsigned long)dst & 3) {
426b285192aSMauro Carvalho Chehab 			lead = 4 - ((unsigned long)dst & 3);
427b285192aSMauro Carvalho Chehab 			if (copy_from_user(dst, buf, lead))
428b285192aSMauro Carvalho Chehab 				return -EFAULT;
429b285192aSMauro Carvalho Chehab 			buf += lead;
430b285192aSMauro Carvalho Chehab 			dst += lead;
431b285192aSMauro Carvalho Chehab 		}
432b285192aSMauro Carvalho Chehab 		/* DMA resolution is 32 bits */
433b285192aSMauro Carvalho Chehab 		if ((count - lead) & 3)
434b285192aSMauro Carvalho Chehab 			tail = (count - lead) & 3;
435b285192aSMauro Carvalho Chehab 		/* DMA the data */
436b285192aSMauro Carvalho Chehab 		dma_size = count - lead - tail;
437b285192aSMauro Carvalho Chehab 		dma_err = ivtvfb_prep_dec_dma_to_device(itv,
438b285192aSMauro Carvalho Chehab 		       p + lead + dma_offset, (void __user *)buf, dma_size);
439b285192aSMauro Carvalho Chehab 		if (dma_err)
440b285192aSMauro Carvalho Chehab 			return dma_err;
441b285192aSMauro Carvalho Chehab 		dst += dma_size;
442b285192aSMauro Carvalho Chehab 		buf += dma_size;
443b285192aSMauro Carvalho Chehab 		/* Copy any leftover data */
444b285192aSMauro Carvalho Chehab 		if (tail && copy_from_user(dst, buf, tail))
445b285192aSMauro Carvalho Chehab 			return -EFAULT;
446b285192aSMauro Carvalho Chehab 	} else if (copy_from_user(dst, buf, count)) {
447b285192aSMauro Carvalho Chehab 		return -EFAULT;
448b285192aSMauro Carvalho Chehab 	}
449b285192aSMauro Carvalho Chehab 
450b285192aSMauro Carvalho Chehab 	if  (!err)
451b285192aSMauro Carvalho Chehab 		*ppos += count;
452b285192aSMauro Carvalho Chehab 
453b285192aSMauro Carvalho Chehab 	return (err) ? err : count;
454b285192aSMauro Carvalho Chehab }
455b285192aSMauro Carvalho Chehab 
456b285192aSMauro Carvalho Chehab static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
457b285192aSMauro Carvalho Chehab {
458b285192aSMauro Carvalho Chehab 	DEFINE_WAIT(wait);
459b285192aSMauro Carvalho Chehab 	struct ivtv *itv = (struct ivtv *)info->par;
460b285192aSMauro Carvalho Chehab 	int rc = 0;
461b285192aSMauro Carvalho Chehab 
462b285192aSMauro Carvalho Chehab 	switch (cmd) {
463b285192aSMauro Carvalho Chehab 		case FBIOGET_VBLANK: {
464b285192aSMauro Carvalho Chehab 			struct fb_vblank vblank;
465b285192aSMauro Carvalho Chehab 			u32 trace;
466b285192aSMauro Carvalho Chehab 
467b285192aSMauro Carvalho Chehab 			memset(&vblank, 0, sizeof(struct fb_vblank));
468b285192aSMauro Carvalho Chehab 
469b285192aSMauro Carvalho Chehab 			vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT |
470b285192aSMauro Carvalho Chehab 					FB_VBLANK_HAVE_VSYNC;
471b285192aSMauro Carvalho Chehab 			trace = read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16;
472b285192aSMauro Carvalho Chehab 			if (itv->is_out_50hz && trace > 312)
473b285192aSMauro Carvalho Chehab 				trace -= 312;
474b285192aSMauro Carvalho Chehab 			else if (itv->is_out_60hz && trace > 262)
475b285192aSMauro Carvalho Chehab 				trace -= 262;
476b285192aSMauro Carvalho Chehab 			if (trace == 1)
477b285192aSMauro Carvalho Chehab 				vblank.flags |= FB_VBLANK_VSYNCING;
478b285192aSMauro Carvalho Chehab 			vblank.count = itv->last_vsync_field;
479b285192aSMauro Carvalho Chehab 			vblank.vcount = trace;
480b285192aSMauro Carvalho Chehab 			vblank.hcount = 0;
481b285192aSMauro Carvalho Chehab 			if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank)))
482b285192aSMauro Carvalho Chehab 				return -EFAULT;
483b285192aSMauro Carvalho Chehab 			return 0;
484b285192aSMauro Carvalho Chehab 		}
485b285192aSMauro Carvalho Chehab 
486b285192aSMauro Carvalho Chehab 		case FBIO_WAITFORVSYNC:
487b285192aSMauro Carvalho Chehab 			prepare_to_wait(&itv->vsync_waitq, &wait, TASK_INTERRUPTIBLE);
488b285192aSMauro Carvalho Chehab 			if (!schedule_timeout(msecs_to_jiffies(50)))
489b285192aSMauro Carvalho Chehab 				rc = -ETIMEDOUT;
490b285192aSMauro Carvalho Chehab 			finish_wait(&itv->vsync_waitq, &wait);
491b285192aSMauro Carvalho Chehab 			return rc;
492b285192aSMauro Carvalho Chehab 
493b285192aSMauro Carvalho Chehab 		case IVTVFB_IOC_DMA_FRAME: {
494b285192aSMauro Carvalho Chehab 			struct ivtvfb_dma_frame args;
495b285192aSMauro Carvalho Chehab 
496b285192aSMauro Carvalho Chehab 			IVTVFB_DEBUG_INFO("IVTVFB_IOC_DMA_FRAME\n");
497b285192aSMauro Carvalho Chehab 			if (copy_from_user(&args, (void __user *)arg, sizeof(args)))
498b285192aSMauro Carvalho Chehab 				return -EFAULT;
499b285192aSMauro Carvalho Chehab 
500b285192aSMauro Carvalho Chehab 			return ivtvfb_prep_frame(itv, cmd, args.source, args.dest_offset, args.count);
501b285192aSMauro Carvalho Chehab 		}
502b285192aSMauro Carvalho Chehab 
503b285192aSMauro Carvalho Chehab 		default:
504b285192aSMauro Carvalho Chehab 			IVTVFB_DEBUG_INFO("Unknown ioctl %08x\n", cmd);
505b285192aSMauro Carvalho Chehab 			return -EINVAL;
506b285192aSMauro Carvalho Chehab 	}
507b285192aSMauro Carvalho Chehab 	return 0;
508b285192aSMauro Carvalho Chehab }
509b285192aSMauro Carvalho Chehab 
510b285192aSMauro Carvalho Chehab /* Framebuffer device handling */
511b285192aSMauro Carvalho Chehab 
512b285192aSMauro Carvalho Chehab static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var)
513b285192aSMauro Carvalho Chehab {
514b285192aSMauro Carvalho Chehab 	struct osd_info *oi = itv->osd_info;
515b285192aSMauro Carvalho Chehab 	struct ivtv_osd_coords ivtv_osd;
516b285192aSMauro Carvalho Chehab 	struct v4l2_rect ivtv_window;
517b285192aSMauro Carvalho Chehab 	int osd_mode = -1;
518b285192aSMauro Carvalho Chehab 
519b285192aSMauro Carvalho Chehab 	IVTVFB_DEBUG_INFO("ivtvfb_set_var\n");
520b285192aSMauro Carvalho Chehab 
521b285192aSMauro Carvalho Chehab 	/* Select color space */
522b285192aSMauro Carvalho Chehab 	if (var->nonstd) /* YUV */
523b285192aSMauro Carvalho Chehab 		write_reg(read_reg(0x02a00) | 0x0002000, 0x02a00);
524b285192aSMauro Carvalho Chehab 	else /* RGB  */
525b285192aSMauro Carvalho Chehab 		write_reg(read_reg(0x02a00) & ~0x0002000, 0x02a00);
526b285192aSMauro Carvalho Chehab 
527b285192aSMauro Carvalho Chehab 	/* Set the color mode */
528b285192aSMauro Carvalho Chehab 	switch (var->bits_per_pixel) {
529b285192aSMauro Carvalho Chehab 		case 8:
530b285192aSMauro Carvalho Chehab 			osd_mode = IVTV_OSD_BPP_8;
531b285192aSMauro Carvalho Chehab 			break;
532b285192aSMauro Carvalho Chehab 		case 32:
533b285192aSMauro Carvalho Chehab 			osd_mode = IVTV_OSD_BPP_32;
534b285192aSMauro Carvalho Chehab 			break;
535b285192aSMauro Carvalho Chehab 		case 16:
536b285192aSMauro Carvalho Chehab 			switch (var->green.length) {
537b285192aSMauro Carvalho Chehab 			case 4:
538b285192aSMauro Carvalho Chehab 				osd_mode = IVTV_OSD_BPP_16_444;
539b285192aSMauro Carvalho Chehab 				break;
540b285192aSMauro Carvalho Chehab 			case 5:
541b285192aSMauro Carvalho Chehab 				osd_mode = IVTV_OSD_BPP_16_555;
542b285192aSMauro Carvalho Chehab 				break;
543b285192aSMauro Carvalho Chehab 			case 6:
544b285192aSMauro Carvalho Chehab 				osd_mode = IVTV_OSD_BPP_16_565;
545b285192aSMauro Carvalho Chehab 				break;
546b285192aSMauro Carvalho Chehab 			default:
547b285192aSMauro Carvalho Chehab 				IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
548b285192aSMauro Carvalho Chehab 			}
549b285192aSMauro Carvalho Chehab 			break;
550b285192aSMauro Carvalho Chehab 		default:
551b285192aSMauro Carvalho Chehab 			IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
552b285192aSMauro Carvalho Chehab 	}
553b285192aSMauro Carvalho Chehab 
554b285192aSMauro Carvalho Chehab 	/* Set video mode. Although rare, the display can become scrambled even
555b285192aSMauro Carvalho Chehab 	   if we don't change mode. Always 'bounce' to osd_mode via mode 0 */
556b285192aSMauro Carvalho Chehab 	if (osd_mode != -1) {
557b285192aSMauro Carvalho Chehab 		ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0);
558b285192aSMauro Carvalho Chehab 		ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, osd_mode);
559b285192aSMauro Carvalho Chehab 	}
560b285192aSMauro Carvalho Chehab 
561b285192aSMauro Carvalho Chehab 	oi->bits_per_pixel = var->bits_per_pixel;
562b285192aSMauro Carvalho Chehab 	oi->bytes_per_pixel = var->bits_per_pixel / 8;
563b285192aSMauro Carvalho Chehab 
564b285192aSMauro Carvalho Chehab 	/* Set the flicker filter */
565b285192aSMauro Carvalho Chehab 	switch (var->vmode & FB_VMODE_MASK) {
566b285192aSMauro Carvalho Chehab 		case FB_VMODE_NONINTERLACED: /* Filter on */
567b285192aSMauro Carvalho Chehab 			ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 1);
568b285192aSMauro Carvalho Chehab 			break;
569b285192aSMauro Carvalho Chehab 		case FB_VMODE_INTERLACED: /* Filter off */
570b285192aSMauro Carvalho Chehab 			ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 0);
571b285192aSMauro Carvalho Chehab 			break;
572b285192aSMauro Carvalho Chehab 		default:
573b285192aSMauro Carvalho Chehab 			IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid video mode\n");
574b285192aSMauro Carvalho Chehab 	}
575b285192aSMauro Carvalho Chehab 
576b285192aSMauro Carvalho Chehab 	/* Read the current osd info */
577b285192aSMauro Carvalho Chehab 	ivtvfb_get_osd_coords(itv, &ivtv_osd);
578b285192aSMauro Carvalho Chehab 
579b285192aSMauro Carvalho Chehab 	/* Now set the OSD to the size we want */
580b285192aSMauro Carvalho Chehab 	ivtv_osd.pixel_stride = var->xres_virtual;
581b285192aSMauro Carvalho Chehab 	ivtv_osd.lines = var->yres_virtual;
582b285192aSMauro Carvalho Chehab 	ivtv_osd.x = 0;
583b285192aSMauro Carvalho Chehab 	ivtv_osd.y = 0;
584b285192aSMauro Carvalho Chehab 	ivtvfb_set_osd_coords(itv, &ivtv_osd);
585b285192aSMauro Carvalho Chehab 
586b285192aSMauro Carvalho Chehab 	/* Can't seem to find the right API combo for this.
587b285192aSMauro Carvalho Chehab 	   Use another function which does what we need through direct register access. */
588b285192aSMauro Carvalho Chehab 	ivtv_window.width = var->xres;
589b285192aSMauro Carvalho Chehab 	ivtv_window.height = var->yres;
590b285192aSMauro Carvalho Chehab 
591b285192aSMauro Carvalho Chehab 	/* Minimum margin cannot be 0, as X won't allow such a mode */
592b285192aSMauro Carvalho Chehab 	if (!var->upper_margin)
593b285192aSMauro Carvalho Chehab 		var->upper_margin++;
594b285192aSMauro Carvalho Chehab 	if (!var->left_margin)
595b285192aSMauro Carvalho Chehab 		var->left_margin++;
596b285192aSMauro Carvalho Chehab 	ivtv_window.top = var->upper_margin - 1;
597b285192aSMauro Carvalho Chehab 	ivtv_window.left = var->left_margin - 1;
598b285192aSMauro Carvalho Chehab 
599b285192aSMauro Carvalho Chehab 	ivtvfb_set_display_window(itv, &ivtv_window);
600b285192aSMauro Carvalho Chehab 
601b285192aSMauro Carvalho Chehab 	/* Pass screen size back to yuv handler */
602b285192aSMauro Carvalho Chehab 	itv->yuv_info.osd_full_w = ivtv_osd.pixel_stride;
603b285192aSMauro Carvalho Chehab 	itv->yuv_info.osd_full_h = ivtv_osd.lines;
604b285192aSMauro Carvalho Chehab 
605b285192aSMauro Carvalho Chehab 	/* Force update of yuv registers */
606b285192aSMauro Carvalho Chehab 	itv->yuv_info.yuv_forced_update = 1;
607b285192aSMauro Carvalho Chehab 
608b285192aSMauro Carvalho Chehab 	/* Keep a copy of these settings */
609b285192aSMauro Carvalho Chehab 	memcpy(&oi->fbvar_cur, var, sizeof(oi->fbvar_cur));
610b285192aSMauro Carvalho Chehab 
611b285192aSMauro Carvalho Chehab 	IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
612b285192aSMauro Carvalho Chehab 		      var->xres, var->yres,
613b285192aSMauro Carvalho Chehab 		      var->xres_virtual, var->yres_virtual,
614b285192aSMauro Carvalho Chehab 		      var->bits_per_pixel);
615b285192aSMauro Carvalho Chehab 
616b285192aSMauro Carvalho Chehab 	IVTVFB_DEBUG_INFO("Display position: %d, %d\n",
617b285192aSMauro Carvalho Chehab 		      var->left_margin, var->upper_margin);
618b285192aSMauro Carvalho Chehab 
619b285192aSMauro Carvalho Chehab 	IVTVFB_DEBUG_INFO("Display filter: %s\n",
620b285192aSMauro Carvalho Chehab 			(var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
621b285192aSMauro Carvalho Chehab 	IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
622b285192aSMauro Carvalho Chehab 
623b285192aSMauro Carvalho Chehab 	return 0;
624b285192aSMauro Carvalho Chehab }
625b285192aSMauro Carvalho Chehab 
626b285192aSMauro Carvalho Chehab static int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix)
627b285192aSMauro Carvalho Chehab {
628b285192aSMauro Carvalho Chehab 	struct osd_info *oi = itv->osd_info;
629b285192aSMauro Carvalho Chehab 
630b285192aSMauro Carvalho Chehab 	IVTVFB_DEBUG_INFO("ivtvfb_get_fix\n");
631b285192aSMauro Carvalho Chehab 	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
632c0decac1SMauro Carvalho Chehab 	strscpy(fix->id, "cx23415 TV out", sizeof(fix->id));
633b285192aSMauro Carvalho Chehab 	fix->smem_start = oi->video_pbase;
634b285192aSMauro Carvalho Chehab 	fix->smem_len = oi->video_buffer_size;
635b285192aSMauro Carvalho Chehab 	fix->type = FB_TYPE_PACKED_PIXELS;
636b285192aSMauro Carvalho Chehab 	fix->visual = (oi->bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
637b285192aSMauro Carvalho Chehab 	fix->xpanstep = 1;
638b285192aSMauro Carvalho Chehab 	fix->ypanstep = 1;
639b285192aSMauro Carvalho Chehab 	fix->ywrapstep = 0;
640b285192aSMauro Carvalho Chehab 	fix->line_length = oi->display_byte_stride;
641b285192aSMauro Carvalho Chehab 	fix->accel = FB_ACCEL_NONE;
642b285192aSMauro Carvalho Chehab 	return 0;
643b285192aSMauro Carvalho Chehab }
644b285192aSMauro Carvalho Chehab 
645b285192aSMauro Carvalho Chehab /* Check the requested display mode, returning -EINVAL if we can't
646b285192aSMauro Carvalho Chehab    handle it. */
647b285192aSMauro Carvalho Chehab 
648b285192aSMauro Carvalho Chehab static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv)
649b285192aSMauro Carvalho Chehab {
650b285192aSMauro Carvalho Chehab 	struct osd_info *oi = itv->osd_info;
651b285192aSMauro Carvalho Chehab 	int osd_height_limit;
652b285192aSMauro Carvalho Chehab 	u32 pixclock, hlimit, vlimit;
653b285192aSMauro Carvalho Chehab 
654b285192aSMauro Carvalho Chehab 	IVTVFB_DEBUG_INFO("ivtvfb_check_var\n");
655b285192aSMauro Carvalho Chehab 
656b285192aSMauro Carvalho Chehab 	/* Set base references for mode calcs. */
657b285192aSMauro Carvalho Chehab 	if (itv->is_out_50hz) {
658b285192aSMauro Carvalho Chehab 		pixclock = 84316;
659b285192aSMauro Carvalho Chehab 		hlimit = 776;
660b285192aSMauro Carvalho Chehab 		vlimit = 591;
661b285192aSMauro Carvalho Chehab 		osd_height_limit = 576;
662b285192aSMauro Carvalho Chehab 	}
663b285192aSMauro Carvalho Chehab 	else {
664b285192aSMauro Carvalho Chehab 		pixclock = 83926;
665b285192aSMauro Carvalho Chehab 		hlimit = 776;
666b285192aSMauro Carvalho Chehab 		vlimit = 495;
667b285192aSMauro Carvalho Chehab 		osd_height_limit = 480;
668b285192aSMauro Carvalho Chehab 	}
669b285192aSMauro Carvalho Chehab 
670b285192aSMauro Carvalho Chehab 	if (var->bits_per_pixel == 8 || var->bits_per_pixel == 32) {
671b285192aSMauro Carvalho Chehab 		var->transp.offset = 24;
672b285192aSMauro Carvalho Chehab 		var->transp.length = 8;
673b285192aSMauro Carvalho Chehab 		var->red.offset = 16;
674b285192aSMauro Carvalho Chehab 		var->red.length = 8;
675b285192aSMauro Carvalho Chehab 		var->green.offset = 8;
676b285192aSMauro Carvalho Chehab 		var->green.length = 8;
677b285192aSMauro Carvalho Chehab 		var->blue.offset = 0;
678b285192aSMauro Carvalho Chehab 		var->blue.length = 8;
679b285192aSMauro Carvalho Chehab 	}
680b285192aSMauro Carvalho Chehab 	else if (var->bits_per_pixel == 16) {
681b285192aSMauro Carvalho Chehab 		/* To find out the true mode, check green length */
682b285192aSMauro Carvalho Chehab 		switch (var->green.length) {
683b285192aSMauro Carvalho Chehab 			case 4:
684b285192aSMauro Carvalho Chehab 				var->red.offset = 8;
685b285192aSMauro Carvalho Chehab 				var->red.length = 4;
686b285192aSMauro Carvalho Chehab 				var->green.offset = 4;
687b285192aSMauro Carvalho Chehab 				var->green.length = 4;
688b285192aSMauro Carvalho Chehab 				var->blue.offset = 0;
689b285192aSMauro Carvalho Chehab 				var->blue.length = 4;
690b285192aSMauro Carvalho Chehab 				var->transp.offset = 12;
691b285192aSMauro Carvalho Chehab 				var->transp.length = 1;
692b285192aSMauro Carvalho Chehab 				break;
693b285192aSMauro Carvalho Chehab 			case 5:
694b285192aSMauro Carvalho Chehab 				var->red.offset = 10;
695b285192aSMauro Carvalho Chehab 				var->red.length = 5;
696b285192aSMauro Carvalho Chehab 				var->green.offset = 5;
697b285192aSMauro Carvalho Chehab 				var->green.length = 5;
698b285192aSMauro Carvalho Chehab 				var->blue.offset = 0;
699b285192aSMauro Carvalho Chehab 				var->blue.length = 5;
700b285192aSMauro Carvalho Chehab 				var->transp.offset = 15;
701b285192aSMauro Carvalho Chehab 				var->transp.length = 1;
702b285192aSMauro Carvalho Chehab 				break;
703b285192aSMauro Carvalho Chehab 			default:
704b285192aSMauro Carvalho Chehab 				var->red.offset = 11;
705b285192aSMauro Carvalho Chehab 				var->red.length = 5;
706b285192aSMauro Carvalho Chehab 				var->green.offset = 5;
707b285192aSMauro Carvalho Chehab 				var->green.length = 6;
708b285192aSMauro Carvalho Chehab 				var->blue.offset = 0;
709b285192aSMauro Carvalho Chehab 				var->blue.length = 5;
710b285192aSMauro Carvalho Chehab 				var->transp.offset = 0;
711b285192aSMauro Carvalho Chehab 				var->transp.length = 0;
712b285192aSMauro Carvalho Chehab 				break;
713b285192aSMauro Carvalho Chehab 		}
714b285192aSMauro Carvalho Chehab 	}
715b285192aSMauro Carvalho Chehab 	else {
716b285192aSMauro Carvalho Chehab 		IVTVFB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel);
717b285192aSMauro Carvalho Chehab 		return -EINVAL;
718b285192aSMauro Carvalho Chehab 	}
719b285192aSMauro Carvalho Chehab 
720b285192aSMauro Carvalho Chehab 	/* Check the resolution */
721b285192aSMauro Carvalho Chehab 	if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit) {
722b285192aSMauro Carvalho Chehab 		IVTVFB_DEBUG_WARN("Invalid resolution: %dx%d\n",
723b285192aSMauro Carvalho Chehab 				var->xres, var->yres);
724b285192aSMauro Carvalho Chehab 		return -EINVAL;
725b285192aSMauro Carvalho Chehab 	}
726b285192aSMauro Carvalho Chehab 
727b285192aSMauro Carvalho Chehab 	/* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */
728b285192aSMauro Carvalho Chehab 	if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) ||
729b285192aSMauro Carvalho Chehab 	    var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8) > oi->video_buffer_size ||
730b285192aSMauro Carvalho Chehab 	    var->xres_virtual < var->xres ||
731b285192aSMauro Carvalho Chehab 	    var->yres_virtual < var->yres) {
732b285192aSMauro Carvalho Chehab 		IVTVFB_DEBUG_WARN("Invalid virtual resolution: %dx%d\n",
733b285192aSMauro Carvalho Chehab 			var->xres_virtual, var->yres_virtual);
734b285192aSMauro Carvalho Chehab 		return -EINVAL;
735b285192aSMauro Carvalho Chehab 	}
736b285192aSMauro Carvalho Chehab 
737b285192aSMauro Carvalho Chehab 	/* Some extra checks if in 8 bit mode */
738b285192aSMauro Carvalho Chehab 	if (var->bits_per_pixel == 8) {
739b285192aSMauro Carvalho Chehab 		/* Width must be a multiple of 4 */
740b285192aSMauro Carvalho Chehab 		if (var->xres & 3) {
741b285192aSMauro Carvalho Chehab 			IVTVFB_DEBUG_WARN("Invalid resolution for 8bpp: %d\n", var->xres);
742b285192aSMauro Carvalho Chehab 			return -EINVAL;
743b285192aSMauro Carvalho Chehab 		}
744b285192aSMauro Carvalho Chehab 		if (var->xres_virtual & 3) {
745b285192aSMauro Carvalho Chehab 			IVTVFB_DEBUG_WARN("Invalid virtual resolution for 8bpp: %d)\n", var->xres_virtual);
746b285192aSMauro Carvalho Chehab 			return -EINVAL;
747b285192aSMauro Carvalho Chehab 		}
748b285192aSMauro Carvalho Chehab 	}
749b285192aSMauro Carvalho Chehab 	else if (var->bits_per_pixel == 16) {
750b285192aSMauro Carvalho Chehab 		/* Width must be a multiple of 2 */
751b285192aSMauro Carvalho Chehab 		if (var->xres & 1) {
752b285192aSMauro Carvalho Chehab 			IVTVFB_DEBUG_WARN("Invalid resolution for 16bpp: %d\n", var->xres);
753b285192aSMauro Carvalho Chehab 			return -EINVAL;
754b285192aSMauro Carvalho Chehab 		}
755b285192aSMauro Carvalho Chehab 		if (var->xres_virtual & 1) {
756b285192aSMauro Carvalho Chehab 			IVTVFB_DEBUG_WARN("Invalid virtual resolution for 16bpp: %d)\n", var->xres_virtual);
757b285192aSMauro Carvalho Chehab 			return -EINVAL;
758b285192aSMauro Carvalho Chehab 		}
759b285192aSMauro Carvalho Chehab 	}
760b285192aSMauro Carvalho Chehab 
761b285192aSMauro Carvalho Chehab 	/* Now check the offsets */
762b285192aSMauro Carvalho Chehab 	if (var->xoffset >= var->xres_virtual || var->yoffset >= var->yres_virtual) {
763b285192aSMauro Carvalho Chehab 		IVTVFB_DEBUG_WARN("Invalid offset: %d (%d) %d (%d)\n",
764b285192aSMauro Carvalho Chehab 			var->xoffset, var->xres_virtual, var->yoffset, var->yres_virtual);
765b285192aSMauro Carvalho Chehab 		return -EINVAL;
766b285192aSMauro Carvalho Chehab 	}
767b285192aSMauro Carvalho Chehab 
768b285192aSMauro Carvalho Chehab 	/* Check pixel format */
769b285192aSMauro Carvalho Chehab 	if (var->nonstd > 1) {
770b285192aSMauro Carvalho Chehab 		IVTVFB_DEBUG_WARN("Invalid nonstd % d\n", var->nonstd);
771b285192aSMauro Carvalho Chehab 		return -EINVAL;
772b285192aSMauro Carvalho Chehab 	}
773b285192aSMauro Carvalho Chehab 
774b285192aSMauro Carvalho Chehab 	/* Check video mode */
775b285192aSMauro Carvalho Chehab 	if (((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) &&
776b285192aSMauro Carvalho Chehab 		((var->vmode & FB_VMODE_MASK) != FB_VMODE_INTERLACED)) {
777b285192aSMauro Carvalho Chehab 		IVTVFB_DEBUG_WARN("Invalid video mode: %d\n", var->vmode & FB_VMODE_MASK);
778b285192aSMauro Carvalho Chehab 		return -EINVAL;
779b285192aSMauro Carvalho Chehab 	}
780b285192aSMauro Carvalho Chehab 
781b285192aSMauro Carvalho Chehab 	/* Check the left & upper margins
782b285192aSMauro Carvalho Chehab 	   If the margins are too large, just center the screen
783b285192aSMauro Carvalho Chehab 	   (enforcing margins causes too many problems) */
784b285192aSMauro Carvalho Chehab 
785b285192aSMauro Carvalho Chehab 	if (var->left_margin + var->xres > IVTV_OSD_MAX_WIDTH + 1)
786b285192aSMauro Carvalho Chehab 		var->left_margin = 1 + ((IVTV_OSD_MAX_WIDTH - var->xres) / 2);
787b285192aSMauro Carvalho Chehab 
788b285192aSMauro Carvalho Chehab 	if (var->upper_margin + var->yres > (itv->is_out_50hz ? 577 : 481))
789b285192aSMauro Carvalho Chehab 		var->upper_margin = 1 + (((itv->is_out_50hz ? 576 : 480) -
790b285192aSMauro Carvalho Chehab 			var->yres) / 2);
791b285192aSMauro Carvalho Chehab 
792b285192aSMauro Carvalho Chehab 	/* Maintain overall 'size' for a constant refresh rate */
793b285192aSMauro Carvalho Chehab 	var->right_margin = hlimit - var->left_margin - var->xres;
794b285192aSMauro Carvalho Chehab 	var->lower_margin = vlimit - var->upper_margin - var->yres;
795b285192aSMauro Carvalho Chehab 
796b285192aSMauro Carvalho Chehab 	/* Fixed sync times */
797b285192aSMauro Carvalho Chehab 	var->hsync_len = 24;
798b285192aSMauro Carvalho Chehab 	var->vsync_len = 2;
799b285192aSMauro Carvalho Chehab 
800b285192aSMauro Carvalho Chehab 	/* Non-interlaced / interlaced mode is used to switch the OSD filter
801b285192aSMauro Carvalho Chehab 	   on or off. Adjust the clock timings to maintain a constant
802b285192aSMauro Carvalho Chehab 	   vertical refresh rate. */
803b285192aSMauro Carvalho Chehab 	if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED)
804b285192aSMauro Carvalho Chehab 		var->pixclock = pixclock / 2;
805b285192aSMauro Carvalho Chehab 	else
806b285192aSMauro Carvalho Chehab 		var->pixclock = pixclock;
807b285192aSMauro Carvalho Chehab 
808b285192aSMauro Carvalho Chehab 	itv->osd_rect.width = var->xres;
809b285192aSMauro Carvalho Chehab 	itv->osd_rect.height = var->yres;
810b285192aSMauro Carvalho Chehab 
811b285192aSMauro Carvalho Chehab 	IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
812b285192aSMauro Carvalho Chehab 		      var->xres, var->yres,
813b285192aSMauro Carvalho Chehab 		      var->xres_virtual, var->yres_virtual,
814b285192aSMauro Carvalho Chehab 		      var->bits_per_pixel);
815b285192aSMauro Carvalho Chehab 
816b285192aSMauro Carvalho Chehab 	IVTVFB_DEBUG_INFO("Display position: %d, %d\n",
817b285192aSMauro Carvalho Chehab 		      var->left_margin, var->upper_margin);
818b285192aSMauro Carvalho Chehab 
819b285192aSMauro Carvalho Chehab 	IVTVFB_DEBUG_INFO("Display filter: %s\n",
820b285192aSMauro Carvalho Chehab 			(var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
821b285192aSMauro Carvalho Chehab 	IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
822b285192aSMauro Carvalho Chehab 	return 0;
823b285192aSMauro Carvalho Chehab }
824b285192aSMauro Carvalho Chehab 
825b285192aSMauro Carvalho Chehab static int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
826b285192aSMauro Carvalho Chehab {
827b285192aSMauro Carvalho Chehab 	struct ivtv *itv = (struct ivtv *) info->par;
828b285192aSMauro Carvalho Chehab 	IVTVFB_DEBUG_INFO("ivtvfb_check_var\n");
829b285192aSMauro Carvalho Chehab 	return _ivtvfb_check_var(var, itv);
830b285192aSMauro Carvalho Chehab }
831b285192aSMauro Carvalho Chehab 
832b285192aSMauro Carvalho Chehab static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
833b285192aSMauro Carvalho Chehab {
834b285192aSMauro Carvalho Chehab 	u32 osd_pan_index;
835b285192aSMauro Carvalho Chehab 	struct ivtv *itv = (struct ivtv *) info->par;
836b285192aSMauro Carvalho Chehab 
837b285192aSMauro Carvalho Chehab 	if (var->yoffset + info->var.yres > info->var.yres_virtual ||
838b285192aSMauro Carvalho Chehab 	    var->xoffset + info->var.xres > info->var.xres_virtual)
839b285192aSMauro Carvalho Chehab 		return -EINVAL;
840b285192aSMauro Carvalho Chehab 
841b285192aSMauro Carvalho Chehab 	osd_pan_index = var->yoffset * info->fix.line_length
842b285192aSMauro Carvalho Chehab 		      + var->xoffset * info->var.bits_per_pixel / 8;
843b285192aSMauro Carvalho Chehab 	write_reg(osd_pan_index, 0x02A0C);
844b285192aSMauro Carvalho Chehab 
845b285192aSMauro Carvalho Chehab 	/* Pass this info back the yuv handler */
846b285192aSMauro Carvalho Chehab 	itv->yuv_info.osd_x_pan = var->xoffset;
847b285192aSMauro Carvalho Chehab 	itv->yuv_info.osd_y_pan = var->yoffset;
848b285192aSMauro Carvalho Chehab 	/* Force update of yuv registers */
849b285192aSMauro Carvalho Chehab 	itv->yuv_info.yuv_forced_update = 1;
850b285192aSMauro Carvalho Chehab 	/* Remember this value */
851b285192aSMauro Carvalho Chehab 	itv->osd_info->pan_cur = osd_pan_index;
852b285192aSMauro Carvalho Chehab 	return 0;
853b285192aSMauro Carvalho Chehab }
854b285192aSMauro Carvalho Chehab 
855b285192aSMauro Carvalho Chehab static int ivtvfb_set_par(struct fb_info *info)
856b285192aSMauro Carvalho Chehab {
857b285192aSMauro Carvalho Chehab 	int rc = 0;
858b285192aSMauro Carvalho Chehab 	struct ivtv *itv = (struct ivtv *) info->par;
859b285192aSMauro Carvalho Chehab 
860b285192aSMauro Carvalho Chehab 	IVTVFB_DEBUG_INFO("ivtvfb_set_par\n");
861b285192aSMauro Carvalho Chehab 
862b285192aSMauro Carvalho Chehab 	rc = ivtvfb_set_var(itv, &info->var);
863b285192aSMauro Carvalho Chehab 	ivtvfb_pan_display(&info->var, info);
864b285192aSMauro Carvalho Chehab 	ivtvfb_get_fix(itv, &info->fix);
865b285192aSMauro Carvalho Chehab 	ivtv_firmware_check(itv, "ivtvfb_set_par");
866b285192aSMauro Carvalho Chehab 	return rc;
867b285192aSMauro Carvalho Chehab }
868b285192aSMauro Carvalho Chehab 
869b285192aSMauro Carvalho Chehab static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green,
870b285192aSMauro Carvalho Chehab 				unsigned blue, unsigned transp,
871b285192aSMauro Carvalho Chehab 				struct fb_info *info)
872b285192aSMauro Carvalho Chehab {
873b285192aSMauro Carvalho Chehab 	u32 color, *palette;
874b285192aSMauro Carvalho Chehab 	struct ivtv *itv = (struct ivtv *)info->par;
875b285192aSMauro Carvalho Chehab 
876b285192aSMauro Carvalho Chehab 	if (regno >= info->cmap.len)
877b285192aSMauro Carvalho Chehab 		return -EINVAL;
878b285192aSMauro Carvalho Chehab 
879b285192aSMauro Carvalho Chehab 	color = ((transp & 0xFF00) << 16) |((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8);
880b285192aSMauro Carvalho Chehab 	if (info->var.bits_per_pixel <= 8) {
881b285192aSMauro Carvalho Chehab 		write_reg(regno, 0x02a30);
882b285192aSMauro Carvalho Chehab 		write_reg(color, 0x02a34);
883b285192aSMauro Carvalho Chehab 		itv->osd_info->palette_cur[regno] = color;
884b285192aSMauro Carvalho Chehab 		return 0;
885b285192aSMauro Carvalho Chehab 	}
886b285192aSMauro Carvalho Chehab 	if (regno >= 16)
887b285192aSMauro Carvalho Chehab 		return -EINVAL;
888b285192aSMauro Carvalho Chehab 
889b285192aSMauro Carvalho Chehab 	palette = info->pseudo_palette;
890b285192aSMauro Carvalho Chehab 	if (info->var.bits_per_pixel == 16) {
891b285192aSMauro Carvalho Chehab 		switch (info->var.green.length) {
892b285192aSMauro Carvalho Chehab 			case 4:
893b285192aSMauro Carvalho Chehab 				color = ((red & 0xf000) >> 4) |
894b285192aSMauro Carvalho Chehab 					((green & 0xf000) >> 8) |
895b285192aSMauro Carvalho Chehab 					((blue & 0xf000) >> 12);
896b285192aSMauro Carvalho Chehab 				break;
897b285192aSMauro Carvalho Chehab 			case 5:
898b285192aSMauro Carvalho Chehab 				color = ((red & 0xf800) >> 1) |
899b285192aSMauro Carvalho Chehab 					((green & 0xf800) >> 6) |
900b285192aSMauro Carvalho Chehab 					((blue & 0xf800) >> 11);
901b285192aSMauro Carvalho Chehab 				break;
902b285192aSMauro Carvalho Chehab 			case 6:
903b285192aSMauro Carvalho Chehab 				color = (red & 0xf800 ) |
904b285192aSMauro Carvalho Chehab 					((green & 0xfc00) >> 5) |
905b285192aSMauro Carvalho Chehab 					((blue & 0xf800) >> 11);
906b285192aSMauro Carvalho Chehab 				break;
907b285192aSMauro Carvalho Chehab 		}
908b285192aSMauro Carvalho Chehab 	}
909b285192aSMauro Carvalho Chehab 	palette[regno] = color;
910b285192aSMauro Carvalho Chehab 	return 0;
911b285192aSMauro Carvalho Chehab }
912b285192aSMauro Carvalho Chehab 
913b285192aSMauro Carvalho Chehab /* We don't really support blanking. All this does is enable or
914b285192aSMauro Carvalho Chehab    disable the OSD. */
915b285192aSMauro Carvalho Chehab static int ivtvfb_blank(int blank_mode, struct fb_info *info)
916b285192aSMauro Carvalho Chehab {
917b285192aSMauro Carvalho Chehab 	struct ivtv *itv = (struct ivtv *)info->par;
918b285192aSMauro Carvalho Chehab 
919b285192aSMauro Carvalho Chehab 	IVTVFB_DEBUG_INFO("Set blanking mode : %d\n", blank_mode);
920b285192aSMauro Carvalho Chehab 	switch (blank_mode) {
921b285192aSMauro Carvalho Chehab 	case FB_BLANK_UNBLANK:
922b285192aSMauro Carvalho Chehab 		ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1);
923b285192aSMauro Carvalho Chehab 		ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);
924b285192aSMauro Carvalho Chehab 		break;
925b285192aSMauro Carvalho Chehab 	case FB_BLANK_NORMAL:
926b285192aSMauro Carvalho Chehab 	case FB_BLANK_HSYNC_SUSPEND:
927b285192aSMauro Carvalho Chehab 	case FB_BLANK_VSYNC_SUSPEND:
928b285192aSMauro Carvalho Chehab 		ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
929b285192aSMauro Carvalho Chehab 		ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);
930b285192aSMauro Carvalho Chehab 		break;
931b285192aSMauro Carvalho Chehab 	case FB_BLANK_POWERDOWN:
932b285192aSMauro Carvalho Chehab 		ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);
933b285192aSMauro Carvalho Chehab 		ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
934b285192aSMauro Carvalho Chehab 		break;
935b285192aSMauro Carvalho Chehab 	}
936b285192aSMauro Carvalho Chehab 	itv->osd_info->blank_cur = blank_mode;
937b285192aSMauro Carvalho Chehab 	return 0;
938b285192aSMauro Carvalho Chehab }
939b285192aSMauro Carvalho Chehab 
940b285192aSMauro Carvalho Chehab static struct fb_ops ivtvfb_ops = {
941b285192aSMauro Carvalho Chehab 	.owner = THIS_MODULE,
942b285192aSMauro Carvalho Chehab 	.fb_write       = ivtvfb_write,
943b285192aSMauro Carvalho Chehab 	.fb_check_var   = ivtvfb_check_var,
944b285192aSMauro Carvalho Chehab 	.fb_set_par     = ivtvfb_set_par,
945b285192aSMauro Carvalho Chehab 	.fb_setcolreg   = ivtvfb_setcolreg,
946b285192aSMauro Carvalho Chehab 	.fb_fillrect    = cfb_fillrect,
947b285192aSMauro Carvalho Chehab 	.fb_copyarea    = cfb_copyarea,
948b285192aSMauro Carvalho Chehab 	.fb_imageblit   = cfb_imageblit,
949b285192aSMauro Carvalho Chehab 	.fb_cursor      = NULL,
950b285192aSMauro Carvalho Chehab 	.fb_ioctl       = ivtvfb_ioctl,
951b285192aSMauro Carvalho Chehab 	.fb_pan_display = ivtvfb_pan_display,
952b285192aSMauro Carvalho Chehab 	.fb_blank       = ivtvfb_blank,
953b285192aSMauro Carvalho Chehab };
954b285192aSMauro Carvalho Chehab 
955b285192aSMauro Carvalho Chehab /* Restore hardware after firmware restart */
956b285192aSMauro Carvalho Chehab static void ivtvfb_restore(struct ivtv *itv)
957b285192aSMauro Carvalho Chehab {
958b285192aSMauro Carvalho Chehab 	struct osd_info *oi = itv->osd_info;
959b285192aSMauro Carvalho Chehab 	int i;
960b285192aSMauro Carvalho Chehab 
961b285192aSMauro Carvalho Chehab 	ivtvfb_set_var(itv, &oi->fbvar_cur);
962b285192aSMauro Carvalho Chehab 	ivtvfb_blank(oi->blank_cur, &oi->ivtvfb_info);
963b285192aSMauro Carvalho Chehab 	for (i = 0; i < 256; i++) {
964b285192aSMauro Carvalho Chehab 		write_reg(i, 0x02a30);
965b285192aSMauro Carvalho Chehab 		write_reg(oi->palette_cur[i], 0x02a34);
966b285192aSMauro Carvalho Chehab 	}
967b285192aSMauro Carvalho Chehab 	write_reg(oi->pan_cur, 0x02a0c);
968b285192aSMauro Carvalho Chehab }
969b285192aSMauro Carvalho Chehab 
970b285192aSMauro Carvalho Chehab /* Initialization */
971b285192aSMauro Carvalho Chehab 
972b285192aSMauro Carvalho Chehab 
973b285192aSMauro Carvalho Chehab /* Setup our initial video mode */
974b285192aSMauro Carvalho Chehab static int ivtvfb_init_vidmode(struct ivtv *itv)
975b285192aSMauro Carvalho Chehab {
976b285192aSMauro Carvalho Chehab 	struct osd_info *oi = itv->osd_info;
977b285192aSMauro Carvalho Chehab 	struct v4l2_rect start_window;
978b285192aSMauro Carvalho Chehab 	int max_height;
979b285192aSMauro Carvalho Chehab 
980b285192aSMauro Carvalho Chehab 	/* Color mode */
981b285192aSMauro Carvalho Chehab 
982b285192aSMauro Carvalho Chehab 	if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32)
983b285192aSMauro Carvalho Chehab 		osd_depth = 8;
984b285192aSMauro Carvalho Chehab 	oi->bits_per_pixel = osd_depth;
985b285192aSMauro Carvalho Chehab 	oi->bytes_per_pixel = oi->bits_per_pixel / 8;
986b285192aSMauro Carvalho Chehab 
987b285192aSMauro Carvalho Chehab 	/* Horizontal size & position */
988b285192aSMauro Carvalho Chehab 
989b285192aSMauro Carvalho Chehab 	if (osd_xres > 720)
990b285192aSMauro Carvalho Chehab 		osd_xres = 720;
991b285192aSMauro Carvalho Chehab 
992b285192aSMauro Carvalho Chehab 	/* Must be a multiple of 4 for 8bpp & 2 for 16bpp */
993b285192aSMauro Carvalho Chehab 	if (osd_depth == 8)
994b285192aSMauro Carvalho Chehab 		osd_xres &= ~3;
995b285192aSMauro Carvalho Chehab 	else if (osd_depth == 16)
996b285192aSMauro Carvalho Chehab 		osd_xres &= ~1;
997b285192aSMauro Carvalho Chehab 
998b285192aSMauro Carvalho Chehab 	start_window.width = osd_xres ? osd_xres : 640;
999b285192aSMauro Carvalho Chehab 
1000b285192aSMauro Carvalho Chehab 	/* Check horizontal start (osd_left). */
1001b285192aSMauro Carvalho Chehab 	if (osd_left && osd_left + start_window.width > 721) {
1002b285192aSMauro Carvalho Chehab 		IVTVFB_ERR("Invalid osd_left - assuming default\n");
1003b285192aSMauro Carvalho Chehab 		osd_left = 0;
1004b285192aSMauro Carvalho Chehab 	}
1005b285192aSMauro Carvalho Chehab 
1006b285192aSMauro Carvalho Chehab 	/* Hardware coords start at 0, user coords start at 1. */
1007b285192aSMauro Carvalho Chehab 	osd_left--;
1008b285192aSMauro Carvalho Chehab 
1009b285192aSMauro Carvalho Chehab 	start_window.left = osd_left >= 0 ?
1010b285192aSMauro Carvalho Chehab 		 osd_left : ((IVTV_OSD_MAX_WIDTH - start_window.width) / 2);
1011b285192aSMauro Carvalho Chehab 
1012b285192aSMauro Carvalho Chehab 	oi->display_byte_stride =
1013b285192aSMauro Carvalho Chehab 			start_window.width * oi->bytes_per_pixel;
1014b285192aSMauro Carvalho Chehab 
1015b285192aSMauro Carvalho Chehab 	/* Vertical size & position */
1016b285192aSMauro Carvalho Chehab 
1017b285192aSMauro Carvalho Chehab 	max_height = itv->is_out_50hz ? 576 : 480;
1018b285192aSMauro Carvalho Chehab 
1019b285192aSMauro Carvalho Chehab 	if (osd_yres > max_height)
1020b285192aSMauro Carvalho Chehab 		osd_yres = max_height;
1021b285192aSMauro Carvalho Chehab 
1022b285192aSMauro Carvalho Chehab 	start_window.height = osd_yres ?
1023b285192aSMauro Carvalho Chehab 		osd_yres : itv->is_out_50hz ? 480 : 400;
1024b285192aSMauro Carvalho Chehab 
1025b285192aSMauro Carvalho Chehab 	/* Check vertical start (osd_upper). */
1026b285192aSMauro Carvalho Chehab 	if (osd_upper + start_window.height > max_height + 1) {
1027b285192aSMauro Carvalho Chehab 		IVTVFB_ERR("Invalid osd_upper - assuming default\n");
1028b285192aSMauro Carvalho Chehab 		osd_upper = 0;
1029b285192aSMauro Carvalho Chehab 	}
1030b285192aSMauro Carvalho Chehab 
1031b285192aSMauro Carvalho Chehab 	/* Hardware coords start at 0, user coords start at 1. */
1032b285192aSMauro Carvalho Chehab 	osd_upper--;
1033b285192aSMauro Carvalho Chehab 
1034b285192aSMauro Carvalho Chehab 	start_window.top = osd_upper >= 0 ? osd_upper : ((max_height - start_window.height) / 2);
1035b285192aSMauro Carvalho Chehab 
1036b285192aSMauro Carvalho Chehab 	oi->display_width = start_window.width;
1037b285192aSMauro Carvalho Chehab 	oi->display_height = start_window.height;
1038b285192aSMauro Carvalho Chehab 
1039b285192aSMauro Carvalho Chehab 	/* Generate a valid fb_var_screeninfo */
1040b285192aSMauro Carvalho Chehab 
1041b285192aSMauro Carvalho Chehab 	oi->ivtvfb_defined.xres = oi->display_width;
1042b285192aSMauro Carvalho Chehab 	oi->ivtvfb_defined.yres = oi->display_height;
1043b285192aSMauro Carvalho Chehab 	oi->ivtvfb_defined.xres_virtual = oi->display_width;
1044b285192aSMauro Carvalho Chehab 	oi->ivtvfb_defined.yres_virtual = oi->display_height;
1045b285192aSMauro Carvalho Chehab 	oi->ivtvfb_defined.bits_per_pixel = oi->bits_per_pixel;
1046b285192aSMauro Carvalho Chehab 	oi->ivtvfb_defined.vmode = (osd_laced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED);
1047b285192aSMauro Carvalho Chehab 	oi->ivtvfb_defined.left_margin = start_window.left + 1;
1048b285192aSMauro Carvalho Chehab 	oi->ivtvfb_defined.upper_margin = start_window.top + 1;
1049b285192aSMauro Carvalho Chehab 	oi->ivtvfb_defined.accel_flags = FB_ACCEL_NONE;
1050b285192aSMauro Carvalho Chehab 	oi->ivtvfb_defined.nonstd = 0;
1051b285192aSMauro Carvalho Chehab 
1052b285192aSMauro Carvalho Chehab 	/* We've filled in the most data, let the usual mode check
1053b285192aSMauro Carvalho Chehab 	   routine fill in the rest. */
1054b285192aSMauro Carvalho Chehab 	_ivtvfb_check_var(&oi->ivtvfb_defined, itv);
1055b285192aSMauro Carvalho Chehab 
1056b285192aSMauro Carvalho Chehab 	/* Generate valid fb_fix_screeninfo */
1057b285192aSMauro Carvalho Chehab 
1058b285192aSMauro Carvalho Chehab 	ivtvfb_get_fix(itv, &oi->ivtvfb_fix);
1059b285192aSMauro Carvalho Chehab 
1060b285192aSMauro Carvalho Chehab 	/* Generate valid fb_info */
1061b285192aSMauro Carvalho Chehab 
1062b285192aSMauro Carvalho Chehab 	oi->ivtvfb_info.node = -1;
1063b285192aSMauro Carvalho Chehab 	oi->ivtvfb_info.flags = FBINFO_FLAG_DEFAULT;
1064b285192aSMauro Carvalho Chehab 	oi->ivtvfb_info.fbops = &ivtvfb_ops;
1065b285192aSMauro Carvalho Chehab 	oi->ivtvfb_info.par = itv;
1066b285192aSMauro Carvalho Chehab 	oi->ivtvfb_info.var = oi->ivtvfb_defined;
1067b285192aSMauro Carvalho Chehab 	oi->ivtvfb_info.fix = oi->ivtvfb_fix;
1068b285192aSMauro Carvalho Chehab 	oi->ivtvfb_info.screen_base = (u8 __iomem *)oi->video_vbase;
1069b285192aSMauro Carvalho Chehab 	oi->ivtvfb_info.fbops = &ivtvfb_ops;
1070b285192aSMauro Carvalho Chehab 
1071b285192aSMauro Carvalho Chehab 	/* Supply some monitor specs. Bogus values will do for now */
1072b285192aSMauro Carvalho Chehab 	oi->ivtvfb_info.monspecs.hfmin = 8000;
1073b285192aSMauro Carvalho Chehab 	oi->ivtvfb_info.monspecs.hfmax = 70000;
1074b285192aSMauro Carvalho Chehab 	oi->ivtvfb_info.monspecs.vfmin = 10;
1075b285192aSMauro Carvalho Chehab 	oi->ivtvfb_info.monspecs.vfmax = 100;
1076b285192aSMauro Carvalho Chehab 
1077b285192aSMauro Carvalho Chehab 	/* Allocate color map */
1078b285192aSMauro Carvalho Chehab 	if (fb_alloc_cmap(&oi->ivtvfb_info.cmap, 256, 1)) {
1079b285192aSMauro Carvalho Chehab 		IVTVFB_ERR("abort, unable to alloc cmap\n");
1080b285192aSMauro Carvalho Chehab 		return -ENOMEM;
1081b285192aSMauro Carvalho Chehab 	}
1082b285192aSMauro Carvalho Chehab 
1083b285192aSMauro Carvalho Chehab 	/* Allocate the pseudo palette */
1084b285192aSMauro Carvalho Chehab 	oi->ivtvfb_info.pseudo_palette =
10856da2ec56SKees Cook 		kmalloc_array(16, sizeof(u32), GFP_KERNEL|__GFP_NOWARN);
1086b285192aSMauro Carvalho Chehab 
1087b285192aSMauro Carvalho Chehab 	if (!oi->ivtvfb_info.pseudo_palette) {
1088b285192aSMauro Carvalho Chehab 		IVTVFB_ERR("abort, unable to alloc pseudo palette\n");
1089b285192aSMauro Carvalho Chehab 		return -ENOMEM;
1090b285192aSMauro Carvalho Chehab 	}
1091b285192aSMauro Carvalho Chehab 
1092b285192aSMauro Carvalho Chehab 	return 0;
1093b285192aSMauro Carvalho Chehab }
1094b285192aSMauro Carvalho Chehab 
1095b285192aSMauro Carvalho Chehab /* Find OSD buffer base & size. Add to mtrr. Zero osd buffer. */
1096b285192aSMauro Carvalho Chehab 
1097b285192aSMauro Carvalho Chehab static int ivtvfb_init_io(struct ivtv *itv)
1098b285192aSMauro Carvalho Chehab {
1099b285192aSMauro Carvalho Chehab 	struct osd_info *oi = itv->osd_info;
11001bf1735bSLuis R. Rodriguez 	/* Find the largest power of two that maps the whole buffer */
11011bf1735bSLuis R. Rodriguez 	int size_shift = 31;
1102b285192aSMauro Carvalho Chehab 
1103b285192aSMauro Carvalho Chehab 	mutex_lock(&itv->serialize_lock);
1104b285192aSMauro Carvalho Chehab 	if (ivtv_init_on_first_open(itv)) {
1105b285192aSMauro Carvalho Chehab 		mutex_unlock(&itv->serialize_lock);
1106b285192aSMauro Carvalho Chehab 		IVTVFB_ERR("Failed to initialize ivtv\n");
1107b285192aSMauro Carvalho Chehab 		return -ENXIO;
1108b285192aSMauro Carvalho Chehab 	}
1109b285192aSMauro Carvalho Chehab 	mutex_unlock(&itv->serialize_lock);
1110b285192aSMauro Carvalho Chehab 
1111b285192aSMauro Carvalho Chehab 	if (ivtvfb_get_framebuffer(itv, &oi->video_rbase,
1112b285192aSMauro Carvalho Chehab 					&oi->video_buffer_size) < 0) {
1113b285192aSMauro Carvalho Chehab 		IVTVFB_ERR("Firmware failed to respond\n");
1114b285192aSMauro Carvalho Chehab 		return -EIO;
1115b285192aSMauro Carvalho Chehab 	}
1116b285192aSMauro Carvalho Chehab 
1117b285192aSMauro Carvalho Chehab 	/* The osd buffer size depends on the number of video buffers allocated
1118b285192aSMauro Carvalho Chehab 	   on the PVR350 itself. For now we'll hardcode the smallest osd buffer
1119b285192aSMauro Carvalho Chehab 	   size to prevent any overlap. */
1120b285192aSMauro Carvalho Chehab 	oi->video_buffer_size = 1704960;
1121b285192aSMauro Carvalho Chehab 
1122b285192aSMauro Carvalho Chehab 	oi->video_pbase = itv->base_addr + IVTV_DECODER_OFFSET + oi->video_rbase;
1123b285192aSMauro Carvalho Chehab 	oi->video_vbase = itv->dec_mem + oi->video_rbase;
1124b285192aSMauro Carvalho Chehab 
1125b285192aSMauro Carvalho Chehab 	if (!oi->video_vbase) {
1126b285192aSMauro Carvalho Chehab 		IVTVFB_ERR("abort, video memory 0x%x @ 0x%lx isn't mapped!\n",
1127b285192aSMauro Carvalho Chehab 		     oi->video_buffer_size, oi->video_pbase);
1128b285192aSMauro Carvalho Chehab 		return -EIO;
1129b285192aSMauro Carvalho Chehab 	}
1130b285192aSMauro Carvalho Chehab 
1131b285192aSMauro Carvalho Chehab 	IVTVFB_INFO("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n",
1132b285192aSMauro Carvalho Chehab 			oi->video_pbase, oi->video_vbase,
1133b285192aSMauro Carvalho Chehab 			oi->video_buffer_size / 1024);
1134b285192aSMauro Carvalho Chehab 
11351bf1735bSLuis R. Rodriguez 	while (!(oi->video_buffer_size & (1 << size_shift)))
1136b285192aSMauro Carvalho Chehab 		size_shift--;
1137b285192aSMauro Carvalho Chehab 	size_shift++;
1138b285192aSMauro Carvalho Chehab 	oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1);
1139b285192aSMauro Carvalho Chehab 	oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size;
1140b285192aSMauro Carvalho Chehab 	oi->fb_end_aligned_physaddr += (1 << size_shift) - 1;
1141b285192aSMauro Carvalho Chehab 	oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1);
11421bf1735bSLuis R. Rodriguez 	oi->wc_cookie = arch_phys_wc_add(oi->fb_start_aligned_physaddr,
11431bf1735bSLuis R. Rodriguez 					 oi->fb_end_aligned_physaddr -
11441bf1735bSLuis R. Rodriguez 					 oi->fb_start_aligned_physaddr);
1145b285192aSMauro Carvalho Chehab 	/* Blank the entire osd. */
1146b285192aSMauro Carvalho Chehab 	memset_io(oi->video_vbase, 0, oi->video_buffer_size);
1147b285192aSMauro Carvalho Chehab 
1148b285192aSMauro Carvalho Chehab 	return 0;
1149b285192aSMauro Carvalho Chehab }
1150b285192aSMauro Carvalho Chehab 
1151b285192aSMauro Carvalho Chehab /* Release any memory we've grabbed & remove mtrr entry */
1152b285192aSMauro Carvalho Chehab static void ivtvfb_release_buffers (struct ivtv *itv)
1153b285192aSMauro Carvalho Chehab {
1154b285192aSMauro Carvalho Chehab 	struct osd_info *oi = itv->osd_info;
1155b285192aSMauro Carvalho Chehab 
1156b285192aSMauro Carvalho Chehab 	/* Release cmap */
1157b285192aSMauro Carvalho Chehab 	if (oi->ivtvfb_info.cmap.len)
1158b285192aSMauro Carvalho Chehab 		fb_dealloc_cmap(&oi->ivtvfb_info.cmap);
1159b285192aSMauro Carvalho Chehab 
1160b285192aSMauro Carvalho Chehab 	/* Release pseudo palette */
1161b285192aSMauro Carvalho Chehab 	kfree(oi->ivtvfb_info.pseudo_palette);
11621bf1735bSLuis R. Rodriguez 	arch_phys_wc_del(oi->wc_cookie);
1163b285192aSMauro Carvalho Chehab 	kfree(oi);
1164b285192aSMauro Carvalho Chehab 	itv->osd_info = NULL;
1165b285192aSMauro Carvalho Chehab }
1166b285192aSMauro Carvalho Chehab 
1167b285192aSMauro Carvalho Chehab /* Initialize the specified card */
1168b285192aSMauro Carvalho Chehab 
1169b285192aSMauro Carvalho Chehab static int ivtvfb_init_card(struct ivtv *itv)
1170b285192aSMauro Carvalho Chehab {
1171b285192aSMauro Carvalho Chehab 	int rc;
1172b285192aSMauro Carvalho Chehab 
1173f5530d5aSLuis R. Rodriguez #ifdef CONFIG_X86_64
1174f5530d5aSLuis R. Rodriguez 	if (pat_enabled()) {
1175*526daee7SFrench, Nicholas A 		if (ivtvfb_force_pat) {
1176*526daee7SFrench, Nicholas A 			pr_info("PAT is enabled. Write-combined framebuffer caching will be disabled.\n");
1177*526daee7SFrench, Nicholas A 			pr_info("To enable caching, boot with nopat kernel parameter\n");
1178*526daee7SFrench, Nicholas A 		} else {
1179*526daee7SFrench, Nicholas A 			pr_warn("ivtvfb needs PAT disabled for write-combined framebuffer caching.\n");
1180*526daee7SFrench, Nicholas A 			pr_warn("Boot with nopat kernel parameter to use caching, or use the\n");
1181*526daee7SFrench, Nicholas A 			pr_warn("force_pat module parameter to run with caching disabled\n");
1182f5530d5aSLuis R. Rodriguez 			return -ENODEV;
1183f5530d5aSLuis R. Rodriguez 		}
1184*526daee7SFrench, Nicholas A 	}
1185f5530d5aSLuis R. Rodriguez #endif
1186f5530d5aSLuis R. Rodriguez 
1187b285192aSMauro Carvalho Chehab 	if (itv->osd_info) {
1188b285192aSMauro Carvalho Chehab 		IVTVFB_ERR("Card %d already initialised\n", ivtvfb_card_id);
1189b285192aSMauro Carvalho Chehab 		return -EBUSY;
1190b285192aSMauro Carvalho Chehab 	}
1191b285192aSMauro Carvalho Chehab 
1192b285192aSMauro Carvalho Chehab 	itv->osd_info = kzalloc(sizeof(struct osd_info),
11935bfffa0cSJia-Ju Bai 					GFP_KERNEL|__GFP_NOWARN);
1194b285192aSMauro Carvalho Chehab 	if (itv->osd_info == NULL) {
1195b285192aSMauro Carvalho Chehab 		IVTVFB_ERR("Failed to allocate memory for osd_info\n");
1196b285192aSMauro Carvalho Chehab 		return -ENOMEM;
1197b285192aSMauro Carvalho Chehab 	}
1198b285192aSMauro Carvalho Chehab 
1199b285192aSMauro Carvalho Chehab 	/* Find & setup the OSD buffer */
1200b285192aSMauro Carvalho Chehab 	rc = ivtvfb_init_io(itv);
1201b285192aSMauro Carvalho Chehab 	if (rc) {
1202b285192aSMauro Carvalho Chehab 		ivtvfb_release_buffers(itv);
1203b285192aSMauro Carvalho Chehab 		return rc;
1204b285192aSMauro Carvalho Chehab 	}
1205b285192aSMauro Carvalho Chehab 
1206b285192aSMauro Carvalho Chehab 	/* Set the startup video mode information */
1207b285192aSMauro Carvalho Chehab 	if ((rc = ivtvfb_init_vidmode(itv))) {
1208b285192aSMauro Carvalho Chehab 		ivtvfb_release_buffers(itv);
1209b285192aSMauro Carvalho Chehab 		return rc;
1210b285192aSMauro Carvalho Chehab 	}
1211b285192aSMauro Carvalho Chehab 
1212b285192aSMauro Carvalho Chehab 	/* Register the framebuffer */
1213b285192aSMauro Carvalho Chehab 	if (register_framebuffer(&itv->osd_info->ivtvfb_info) < 0) {
1214b285192aSMauro Carvalho Chehab 		ivtvfb_release_buffers(itv);
1215b285192aSMauro Carvalho Chehab 		return -EINVAL;
1216b285192aSMauro Carvalho Chehab 	}
1217b285192aSMauro Carvalho Chehab 
1218b285192aSMauro Carvalho Chehab 	itv->osd_video_pbase = itv->osd_info->video_pbase;
1219b285192aSMauro Carvalho Chehab 
1220b285192aSMauro Carvalho Chehab 	/* Set the card to the requested mode */
1221b285192aSMauro Carvalho Chehab 	ivtvfb_set_par(&itv->osd_info->ivtvfb_info);
1222b285192aSMauro Carvalho Chehab 
1223b285192aSMauro Carvalho Chehab 	/* Set color 0 to black */
1224b285192aSMauro Carvalho Chehab 	write_reg(0, 0x02a30);
1225b285192aSMauro Carvalho Chehab 	write_reg(0, 0x02a34);
1226b285192aSMauro Carvalho Chehab 
1227b285192aSMauro Carvalho Chehab 	/* Enable the osd */
1228b285192aSMauro Carvalho Chehab 	ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info);
1229b285192aSMauro Carvalho Chehab 
1230b285192aSMauro Carvalho Chehab 	/* Enable restart */
1231b285192aSMauro Carvalho Chehab 	itv->ivtvfb_restore = ivtvfb_restore;
1232b285192aSMauro Carvalho Chehab 
1233b285192aSMauro Carvalho Chehab 	/* Allocate DMA */
1234b285192aSMauro Carvalho Chehab 	ivtv_udma_alloc(itv);
1235b285192aSMauro Carvalho Chehab 	return 0;
1236b285192aSMauro Carvalho Chehab 
1237b285192aSMauro Carvalho Chehab }
1238b285192aSMauro Carvalho Chehab 
1239b285192aSMauro Carvalho Chehab static int __init ivtvfb_callback_init(struct device *dev, void *p)
1240b285192aSMauro Carvalho Chehab {
1241b285192aSMauro Carvalho Chehab 	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
1242b285192aSMauro Carvalho Chehab 	struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev);
1243b285192aSMauro Carvalho Chehab 
1244b285192aSMauro Carvalho Chehab 	if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
1245b285192aSMauro Carvalho Chehab 		if (ivtvfb_init_card(itv) == 0) {
1246b285192aSMauro Carvalho Chehab 			IVTVFB_INFO("Framebuffer registered on %s\n",
1247b285192aSMauro Carvalho Chehab 					itv->v4l2_dev.name);
1248b285192aSMauro Carvalho Chehab 			(*(int *)p)++;
1249b285192aSMauro Carvalho Chehab 		}
1250b285192aSMauro Carvalho Chehab 	}
1251b285192aSMauro Carvalho Chehab 	return 0;
1252b285192aSMauro Carvalho Chehab }
1253b285192aSMauro Carvalho Chehab 
1254b285192aSMauro Carvalho Chehab static int ivtvfb_callback_cleanup(struct device *dev, void *p)
1255b285192aSMauro Carvalho Chehab {
1256b285192aSMauro Carvalho Chehab 	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
1257b285192aSMauro Carvalho Chehab 	struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev);
1258b285192aSMauro Carvalho Chehab 	struct osd_info *oi = itv->osd_info;
1259b285192aSMauro Carvalho Chehab 
1260b285192aSMauro Carvalho Chehab 	if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
1261b285192aSMauro Carvalho Chehab 		if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) {
1262b285192aSMauro Carvalho Chehab 			IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n",
1263b285192aSMauro Carvalho Chehab 				       itv->instance);
1264b285192aSMauro Carvalho Chehab 			return 0;
1265b285192aSMauro Carvalho Chehab 		}
1266b285192aSMauro Carvalho Chehab 		IVTVFB_INFO("Unregister framebuffer %d\n", itv->instance);
1267b285192aSMauro Carvalho Chehab 		itv->ivtvfb_restore = NULL;
1268b285192aSMauro Carvalho Chehab 		ivtvfb_blank(FB_BLANK_VSYNC_SUSPEND, &oi->ivtvfb_info);
1269b285192aSMauro Carvalho Chehab 		ivtvfb_release_buffers(itv);
1270b285192aSMauro Carvalho Chehab 		itv->osd_video_pbase = 0;
1271b285192aSMauro Carvalho Chehab 	}
1272b285192aSMauro Carvalho Chehab 	return 0;
1273b285192aSMauro Carvalho Chehab }
1274b285192aSMauro Carvalho Chehab 
1275b285192aSMauro Carvalho Chehab static int __init ivtvfb_init(void)
1276b285192aSMauro Carvalho Chehab {
1277b285192aSMauro Carvalho Chehab 	struct device_driver *drv;
1278b285192aSMauro Carvalho Chehab 	int registered = 0;
1279b285192aSMauro Carvalho Chehab 	int err;
1280b285192aSMauro Carvalho Chehab 
12811bf1735bSLuis R. Rodriguez 
1282b285192aSMauro Carvalho Chehab 	if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) {
1283f57e9618SMauro Carvalho Chehab 		pr_err("ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n",
1284b285192aSMauro Carvalho Chehab 		     IVTV_MAX_CARDS - 1);
1285b285192aSMauro Carvalho Chehab 		return -EINVAL;
1286b285192aSMauro Carvalho Chehab 	}
1287b285192aSMauro Carvalho Chehab 
1288b285192aSMauro Carvalho Chehab 	drv = driver_find("ivtv", &pci_bus_type);
1289b285192aSMauro Carvalho Chehab 	err = driver_for_each_device(drv, NULL, &registered, ivtvfb_callback_init);
1290b285192aSMauro Carvalho Chehab 	(void)err;	/* suppress compiler warning */
1291b285192aSMauro Carvalho Chehab 	if (!registered) {
1292f57e9618SMauro Carvalho Chehab 		pr_err("no cards found\n");
1293b285192aSMauro Carvalho Chehab 		return -ENODEV;
1294b285192aSMauro Carvalho Chehab 	}
1295b285192aSMauro Carvalho Chehab 	return 0;
1296b285192aSMauro Carvalho Chehab }
1297b285192aSMauro Carvalho Chehab 
1298b285192aSMauro Carvalho Chehab static void ivtvfb_cleanup(void)
1299b285192aSMauro Carvalho Chehab {
1300b285192aSMauro Carvalho Chehab 	struct device_driver *drv;
1301b285192aSMauro Carvalho Chehab 	int err;
1302b285192aSMauro Carvalho Chehab 
1303f57e9618SMauro Carvalho Chehab 	pr_info("Unloading framebuffer module\n");
1304b285192aSMauro Carvalho Chehab 
1305b285192aSMauro Carvalho Chehab 	drv = driver_find("ivtv", &pci_bus_type);
1306b285192aSMauro Carvalho Chehab 	err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup);
1307b285192aSMauro Carvalho Chehab 	(void)err;	/* suppress compiler warning */
1308b285192aSMauro Carvalho Chehab }
1309b285192aSMauro Carvalho Chehab 
1310b285192aSMauro Carvalho Chehab module_init(ivtvfb_init);
1311b285192aSMauro Carvalho Chehab module_exit(ivtvfb_cleanup);
1312