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