blob: e80564aed6326ee564bd2f9df52f5f1679fa2ece [file] [log] [blame]
Hans Verkuil32db7752007-07-20 09:29:43 -03001/*
2 On Screen Display cx23415 Framebuffer driver
3
4 This module presents the cx23415 OSD (onscreen display) framebuffer memory
5 as a standard Linux /dev/fb style framebuffer device. The framebuffer has
Hans Verkuilbe383bd2007-07-20 10:16:03 -03006 support for 8, 16 & 32 bpp packed pixel formats with alpha channel. In 16bpp
Hans Verkuil32db7752007-07-20 09:29:43 -03007 mode, there is a choice of a three color depths (12, 15 or 16 bits), but no
8 local alpha. The colorspace is selectable between rgb & yuv.
9 Depending on the TV standard configured in the ivtv module at load time,
10 the initial resolution is either 640x400 (NTSC) or 640x480 (PAL) at 8bpp.
11 Video timings are locked to ensure a vertical refresh rate of 50Hz (PAL)
12 or 59.94 (NTSC)
13
14 Copyright (c) 2003 Matt T. Yourst <yourst@yourst.com>
15
16 Derived from drivers/video/vesafb.c
17 Portions (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
18
19 2.6 kernel port:
20 Copyright (C) 2004 Matthias Badaire
21
22 Copyright (C) 2004 Chris Kennedy <c@groovy.org>
23
24 Copyright (C) 2006 Ian Armstrong <ian@iarmst.demon.co.uk>
25
26 This program is free software; you can redistribute it and/or modify
27 it under the terms of the GNU General Public License as published by
28 the Free Software Foundation; either version 2 of the License, or
29 (at your option) any later version.
30
31 This program is distributed in the hope that it will be useful,
32 but WITHOUT ANY WARRANTY; without even the implied warranty of
33 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34 GNU General Public License for more details.
35
36 You should have received a copy of the GNU General Public License
37 along with this program; if not, write to the Free Software
38 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
39 */
40
41#include <linux/module.h>
42#include <linux/kernel.h>
43#include <linux/string.h>
44#include <linux/mm.h>
45#include <linux/tty.h>
46#include <linux/fb.h>
47#include <linux/console.h>
48#include <linux/bitops.h>
49#include <linux/pagemap.h>
50#include <linux/matroxfb.h>
51
52#include <asm/io.h>
53#include <asm/ioctl.h>
54
55#ifdef CONFIG_MTRR
56#include <asm/mtrr.h>
57#endif
58
59#include "ivtv-driver.h"
Hans Verkuil32db7752007-07-20 09:29:43 -030060#include "ivtv-udma.h"
Hans Verkuil32db7752007-07-20 09:29:43 -030061#include "ivtv-mailbox.h"
Hans Verkuil32db7752007-07-20 09:29:43 -030062#include <media/ivtv-fb.h>
63
64/* card parameters */
65static int ivtv_fb_card_id = -1;
66static int ivtv_fb_debug = 0;
67static int osd_laced;
68static int osd_compat;
69static int osd_depth;
70static int osd_upper;
71static int osd_left;
72static int osd_yres;
73static int osd_xres;
74
75module_param(ivtv_fb_card_id, int, 0444);
76module_param_named(debug,ivtv_fb_debug, int, 0644);
77module_param(osd_laced, bool, 0444);
78module_param(osd_compat, bool, 0444);
79module_param(osd_depth, int, 0444);
80module_param(osd_upper, int, 0444);
81module_param(osd_left, int, 0444);
82module_param(osd_yres, int, 0444);
83module_param(osd_xres, int, 0444);
84
85MODULE_PARM_DESC(ivtv_fb_card_id,
86 "Only use framebuffer of the specified ivtv card (0-31)\n"
87 "\t\t\tdefault -1: initialize all available framebuffers");
88
89MODULE_PARM_DESC(debug,
90 "Debug level (bitmask). Default: errors only\n"
91 "\t\t\t(debug = 3 gives full debugging)");
92
93MODULE_PARM_DESC(osd_compat,
94 "Compatibility mode - Display size is locked (use for old X drivers)\n"
95 "\t\t\t0=off\n"
96 "\t\t\t1=on\n"
97 "\t\t\tdefault off");
98
99/* Why upper, left, xres, yres, depth, laced ? To match terminology used
100 by fbset.
101 Why start at 1 for left & upper coordinate ? Because X doesn't allow 0 */
102
103MODULE_PARM_DESC(osd_laced,
104 "Interlaced mode\n"
105 "\t\t\t0=off\n"
106 "\t\t\t1=on\n"
107 "\t\t\tdefault off");
108
109MODULE_PARM_DESC(osd_depth,
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300110 "Bits per pixel - 8, 16, 32\n"
Hans Verkuil32db7752007-07-20 09:29:43 -0300111 "\t\t\tdefault 8");
112
113MODULE_PARM_DESC(osd_upper,
114 "Vertical start position\n"
115 "\t\t\tdefault 0 (Centered)");
116
117MODULE_PARM_DESC(osd_left,
118 "Horizontal start position\n"
119 "\t\t\tdefault 0 (Centered)");
120
121MODULE_PARM_DESC(osd_yres,
122 "Display height\n"
123 "\t\t\tdefault 480 (PAL)\n"
124 "\t\t\t 400 (NTSC)");
125
126MODULE_PARM_DESC(osd_xres,
127 "Display width\n"
128 "\t\t\tdefault 640");
129
130MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil, John Harvey, Ian Armstrong");
131MODULE_LICENSE("GPL");
132
133/* --------------------------------------------------------------------- */
134
135#define IVTV_FB_DBGFLG_WARN (1 << 0)
136#define IVTV_FB_DBGFLG_INFO (1 << 1)
137
138#define IVTV_FB_DEBUG(x, type, fmt, args...) \
139 do { \
140 if ((x) & ivtv_fb_debug) \
141 printk(KERN_INFO "ivtv-fb%d " type ": " fmt, itv->num , ## args); \
142 } while (0)
143#define IVTV_FB_DEBUG_WARN(fmt, args...) IVTV_FB_DEBUG(IVTV_FB_DBGFLG_WARN, "warning", fmt , ## args)
144#define IVTV_FB_DEBUG_INFO(fmt, args...) IVTV_FB_DEBUG(IVTV_FB_DBGFLG_INFO, "info", fmt , ## args)
145
146/* Standard kernel messages */
147#define IVTV_FB_ERR(fmt, args...) printk(KERN_ERR "ivtv-fb%d: " fmt, itv->num , ## args)
148#define IVTV_FB_WARN(fmt, args...) printk(KERN_WARNING "ivtv-fb%d: " fmt, itv->num , ## args)
149#define IVTV_FB_INFO(fmt, args...) printk(KERN_INFO "ivtv-fb%d: " fmt, itv->num , ## args)
150
151/* --------------------------------------------------------------------- */
152
153#define IVTV_OSD_MAX_WIDTH 720
154#define IVTV_OSD_MAX_HEIGHT 576
155
156#define IVTV_OSD_BPP_8 0x00
157#define IVTV_OSD_BPP_16_444 0x03
158#define IVTV_OSD_BPP_16_555 0x02
159#define IVTV_OSD_BPP_16_565 0x01
160#define IVTV_OSD_BPP_32 0x04
161
162struct osd_info {
Hans Verkuil32db7752007-07-20 09:29:43 -0300163 /* Physical base address */
164 unsigned long video_pbase;
165 /* Relative base address (relative to start of decoder memory) */
166 u32 video_rbase;
167 /* Mapped base address */
168 volatile char __iomem *video_vbase;
169 /* Buffer size */
170 u32 video_buffer_size;
171
172#ifdef CONFIG_MTRR
173 /* video_base rounded down as required by hardware MTRRs */
174 unsigned long fb_start_aligned_physaddr;
175 /* video_base rounded up as required by hardware MTRRs */
176 unsigned long fb_end_aligned_physaddr;
177#endif
178
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300179 /* Current osd mode */
180 int osd_mode;
181
Hans Verkuil32db7752007-07-20 09:29:43 -0300182 /* Store the buffer offset */
183 int set_osd_coords_x;
184 int set_osd_coords_y;
185
186 /* Current dimensions (NOT VISIBLE SIZE!) */
187 int display_width;
188 int display_height;
189 int display_byte_stride;
190
191 /* Current bits per pixel */
192 int bits_per_pixel;
193 int bytes_per_pixel;
194
195 /* Frame buffer stuff */
196 struct fb_info ivtvfb_info;
197 struct fb_var_screeninfo ivtvfb_defined;
198 struct fb_fix_screeninfo ivtvfb_fix;
199};
200
201struct ivtv_osd_coords {
202 unsigned long offset;
203 unsigned long max_offset;
204 int pixel_stride;
205 int lines;
206 int x;
207 int y;
208};
209
210/* --------------------------------------------------------------------- */
211
212/* ivtv API calls for framebuffer related support */
213
214static int ivtv_fb_get_framebuffer(struct ivtv *itv, u32 *fbbase,
215 u32 *fblength)
216{
217 u32 data[CX2341X_MBOX_MAX_DATA];
218 int rc;
219
220 rc = ivtv_vapi_result(itv, data, CX2341X_OSD_GET_FRAMEBUFFER, 0);
221 *fbbase = data[0];
222 *fblength = data[1];
223 return rc;
224}
225
226static int ivtv_fb_get_osd_coords(struct ivtv *itv,
227 struct ivtv_osd_coords *osd)
228{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300229 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300230 u32 data[CX2341X_MBOX_MAX_DATA];
231
232 ivtv_vapi_result(itv, data, CX2341X_OSD_GET_OSD_COORDS, 0);
233
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300234 osd->offset = data[0] - oi->video_rbase;
235 osd->max_offset = oi->display_width * oi->display_height * 4;
Hans Verkuil32db7752007-07-20 09:29:43 -0300236 osd->pixel_stride = data[1];
237 osd->lines = data[2];
238 osd->x = data[3];
239 osd->y = data[4];
240 return 0;
241}
242
243static int ivtv_fb_set_osd_coords(struct ivtv *itv, const struct ivtv_osd_coords *osd)
244{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300245 struct osd_info *oi = itv->osd_info;
246
247 oi->display_width = osd->pixel_stride;
248 oi->display_byte_stride = osd->pixel_stride * oi->bytes_per_pixel;
249 oi->set_osd_coords_x += osd->x;
250 oi->set_osd_coords_y = osd->y;
Hans Verkuil32db7752007-07-20 09:29:43 -0300251
252 return ivtv_vapi(itv, CX2341X_OSD_SET_OSD_COORDS, 5,
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300253 osd->offset + oi->video_rbase,
Hans Verkuil32db7752007-07-20 09:29:43 -0300254 osd->pixel_stride,
255 osd->lines, osd->x, osd->y);
256}
257
258static int ivtv_fb_set_display_window(struct ivtv *itv, struct v4l2_rect *ivtv_window)
259{
Hans Verkuil32db7752007-07-20 09:29:43 -0300260 int osd_height_limit = itv->is_50hz ? 576 : 480;
261
262 /* Only fail if resolution too high, otherwise fudge the start coords. */
263 if ((ivtv_window->height > osd_height_limit) || (ivtv_window->width > IVTV_OSD_MAX_WIDTH))
264 return -EINVAL;
265
266 /* Ensure we don't exceed display limits */
267 if (ivtv_window->top + ivtv_window->height > osd_height_limit) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300268 IVTV_FB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid height setting (%d, %d)\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300269 ivtv_window->top, ivtv_window->height);
270 ivtv_window->top = osd_height_limit - ivtv_window->height;
271 }
272
273 if (ivtv_window->left + ivtv_window->width > IVTV_OSD_MAX_WIDTH) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300274 IVTV_FB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid width setting (%d, %d)\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300275 ivtv_window->left, ivtv_window->width);
276 ivtv_window->left = IVTV_OSD_MAX_WIDTH - ivtv_window->width;
277 }
278
279 /* Set the OSD origin */
280 write_reg((ivtv_window->top << 16) | ivtv_window->left, 0x02a04);
281
282 /* How much to display */
283 write_reg(((ivtv_window->top+ivtv_window->height) << 16) | (ivtv_window->left+ivtv_window->width), 0x02a08);
284
285 /* Pass this info back the yuv handler */
286 itv->yuv_info.osd_vis_w = ivtv_window->width;
287 itv->yuv_info.osd_vis_h = ivtv_window->height;
288 itv->yuv_info.osd_x_offset = ivtv_window->left;
289 itv->yuv_info.osd_y_offset = ivtv_window->top;
290
291 return 0;
292}
293
294static int ivtv_fb_prep_dec_dma_to_device(struct ivtv *itv,
295 unsigned long ivtv_dest_addr, void __user *userbuf,
296 int size_in_bytes)
297{
298 DEFINE_WAIT(wait);
299 int ret = 0;
300 int got_sig = 0;
301
302 mutex_lock(&itv->udma.lock);
303 /* Map User DMA */
304 if (ivtv_udma_setup(itv, ivtv_dest_addr, userbuf, size_in_bytes) <= 0) {
305 mutex_unlock(&itv->udma.lock);
306 IVTV_FB_WARN("ivtvfb_prep_dec_dma_to_device, "
307 "Error with get_user_pages: %d bytes, %d pages returned\n",
308 size_in_bytes, itv->udma.page_count);
309
310 /* get_user_pages must have failed completely */
311 return -EIO;
312 }
313
314 IVTV_FB_DEBUG_INFO("ivtvfb_prep_dec_dma_to_device, %d bytes, %d pages\n",
315 size_in_bytes, itv->udma.page_count);
316
317 ivtv_udma_prepare(itv);
318 prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
319 /* if no UDMA is pending and no UDMA is in progress, then the DMA
320 is finished */
321 while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) {
322 /* don't interrupt if the DMA is in progress but break off
323 a still pending DMA. */
324 got_sig = signal_pending(current);
325 if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))
326 break;
327 got_sig = 0;
328 schedule();
329 }
330 finish_wait(&itv->dma_waitq, &wait);
331
332 /* Unmap Last DMA Xfer */
333 ivtv_udma_unmap(itv);
334 mutex_unlock(&itv->udma.lock);
335 if (got_sig) {
336 IVTV_DEBUG_INFO("User stopped OSD\n");
337 return -EINTR;
338 }
339
340 return ret;
341}
342
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300343static int ivtv_fb_prep_frame(struct ivtv *itv, int cmd, void __user *source,
344 unsigned long dest_offset, int count)
Hans Verkuil32db7752007-07-20 09:29:43 -0300345{
346 DEFINE_WAIT(wait);
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300347 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300348
349 /* Nothing to do */
350 if (count == 0) {
351 IVTV_FB_DEBUG_WARN("ivtv_fb_prep_frame: Nothing to do. count = 0\n");
352 return -EINVAL;
353 }
354
355 /* Check Total FB Size */
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300356 if ((dest_offset + count) > oi->video_buffer_size) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300357 IVTV_FB_WARN("ivtv_fb_prep_frame: Overflowing the framebuffer %ld, only %d available\n",
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300358 dest_offset + count, oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -0300359 return -E2BIG;
360 }
361
362 /* Not fatal, but will have undesirable results */
363 if ((unsigned long)source & 3)
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300364 IVTV_FB_WARN("ivtv_fb_prep_frame: Source address not 32 bit aligned (0x%08lx)\n",
365 (unsigned long)source);
Hans Verkuil32db7752007-07-20 09:29:43 -0300366
367 if (dest_offset & 3)
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300368 IVTV_FB_WARN("ivtv_fb_prep_frame: Dest offset not 32 bit aligned (%ld)\n", dest_offset);
Hans Verkuil32db7752007-07-20 09:29:43 -0300369
370 if (count & 3)
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300371 IVTV_FB_WARN("ivtv_fb_prep_frame: Count not a multiple of 4 (%d)\n", count);
Hans Verkuil32db7752007-07-20 09:29:43 -0300372
373 /* Check Source */
374 if (!access_ok(VERIFY_READ, source + dest_offset, count)) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300375 IVTV_FB_WARN("Invalid userspace pointer 0x%08lx\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300376 (unsigned long)source);
377
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300378 IVTV_FB_DEBUG_WARN("access_ok() failed for offset 0x%08lx source 0x%08lx count %d\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300379 dest_offset, (unsigned long)source,
380 count);
381 return -EINVAL;
382 }
383
384 /* OSD Address to send DMA to */
Hans Verkuil33c0fca2007-08-23 06:32:46 -0300385 dest_offset += IVTV_DECODER_OFFSET + oi->video_rbase;
Hans Verkuil32db7752007-07-20 09:29:43 -0300386
387 /* Fill Buffers */
388 return ivtv_fb_prep_dec_dma_to_device(itv, dest_offset, source, count);
389}
390
391static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
392{
393 DEFINE_WAIT(wait);
394 struct ivtv *itv = (struct ivtv *)info->par;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300395 int rc = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300396
397 switch (cmd) {
Hans Verkuil32db7752007-07-20 09:29:43 -0300398 case FBIOGET_VBLANK: {
399 struct fb_vblank vblank;
400 u32 trace;
401
402 vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT |
403 FB_VBLANK_HAVE_VSYNC;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300404 trace = read_reg(0x028c0) >> 16;
Hans Verkuil32db7752007-07-20 09:29:43 -0300405 if (itv->is_50hz && trace > 312) trace -= 312;
406 else if (itv->is_60hz && trace > 262) trace -= 262;
407 if (trace == 1) vblank.flags |= FB_VBLANK_VSYNCING;
Hans Verkuila158f352007-08-23 11:31:57 -0300408 vblank.count = itv->last_vsync_field;
Hans Verkuil32db7752007-07-20 09:29:43 -0300409 vblank.vcount = trace;
410 vblank.hcount = 0;
411 if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank)))
412 return -EFAULT;
413 return 0;
414 }
415
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300416 case FBIO_WAITFORVSYNC:
Hans Verkuil32db7752007-07-20 09:29:43 -0300417 prepare_to_wait(&itv->vsync_waitq, &wait, TASK_INTERRUPTIBLE);
Hans Verkuil84149a02007-07-20 18:17:18 -0300418 if (!schedule_timeout(msecs_to_jiffies(50))) rc = -ETIMEDOUT;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300419 finish_wait(&itv->vsync_waitq, &wait);
Hans Verkuil32db7752007-07-20 09:29:43 -0300420 return rc;
Hans Verkuil32db7752007-07-20 09:29:43 -0300421
Hans Verkuild715e762007-07-20 10:30:32 -0300422 case IVTVFB_IOC_DMA_FRAME: {
423 struct ivtvfb_dma_frame args;
Hans Verkuil32db7752007-07-20 09:29:43 -0300424
Hans Verkuild715e762007-07-20 10:30:32 -0300425 IVTV_FB_DEBUG_INFO("IVTVFB_IOC_DMA_FRAME\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300426 if (copy_from_user(&args, (void __user *)arg, sizeof(args)))
427 return -EFAULT;
428
429 return ivtv_fb_prep_frame(itv, cmd, args.source, args.dest_offset, args.count);
430 }
431
432 default:
Hans Verkuilc6f95d12007-07-22 15:44:41 -0300433 IVTV_FB_DEBUG_INFO("Unknown ioctl %08x\n", cmd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300434 return -EINVAL;
435 }
436 return 0;
437}
438
439/* Framebuffer device handling */
440
441static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var)
442{
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300443 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300444 struct ivtv_osd_coords ivtv_osd;
445 struct v4l2_rect ivtv_window;
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300446 int osd_mode = -1;
Hans Verkuil32db7752007-07-20 09:29:43 -0300447
448 IVTV_FB_DEBUG_INFO("ivtvfb_set_var\n");
449
450 /* Select color space */
451 if (var->nonstd) /* YUV */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300452 write_reg(read_reg(0x02a00) | 0x0002000, 0x02a00);
Hans Verkuil32db7752007-07-20 09:29:43 -0300453 else /* RGB */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300454 write_reg(read_reg(0x02a00) & ~0x0002000, 0x02a00);
Hans Verkuil32db7752007-07-20 09:29:43 -0300455
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300456 /* Set the color mode */
Hans Verkuil32db7752007-07-20 09:29:43 -0300457 switch (var->bits_per_pixel) {
458 case 8:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300459 osd_mode = IVTV_OSD_BPP_8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300460 break;
461 case 32:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300462 osd_mode = IVTV_OSD_BPP_32;
Hans Verkuil32db7752007-07-20 09:29:43 -0300463 break;
464 case 16:
465 switch (var->green.length) {
466 case 4:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300467 osd_mode = IVTV_OSD_BPP_16_444;
Hans Verkuil32db7752007-07-20 09:29:43 -0300468 break;
469 case 5:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300470 osd_mode = IVTV_OSD_BPP_16_555;
Hans Verkuil32db7752007-07-20 09:29:43 -0300471 break;
472 case 6:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300473 osd_mode = IVTV_OSD_BPP_16_565;
Hans Verkuil32db7752007-07-20 09:29:43 -0300474 break;
475 default:
476 IVTV_FB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
477 }
478 break;
479 default:
480 IVTV_FB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
481 }
482
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300483 /* Change osd mode if needed.
484 Although rare, things can go wrong. The extra mode
485 change seems to help... */
486 if (osd_mode != -1 && osd_mode != oi->osd_mode) {
487 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0);
488 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, osd_mode);
489 oi->osd_mode = osd_mode;
490 }
491
492 oi->bits_per_pixel = var->bits_per_pixel;
493 oi->bytes_per_pixel = var->bits_per_pixel / 8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300494
495 /* Set the flicker filter */
496 switch (var->vmode & FB_VMODE_MASK) {
497 case FB_VMODE_NONINTERLACED: /* Filter on */
498 ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 1);
499 break;
500 case FB_VMODE_INTERLACED: /* Filter off */
501 ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 0);
502 break;
503 default:
504 IVTV_FB_DEBUG_WARN("ivtvfb_set_var - Invalid video mode\n");
505 }
506
507 /* Read the current osd info */
508 ivtv_fb_get_osd_coords(itv, &ivtv_osd);
509
510 /* Now set the OSD to the size we want */
511 ivtv_osd.pixel_stride = var->xres_virtual;
512 ivtv_osd.lines = var->yres_virtual;
513 ivtv_osd.x = 0;
514 ivtv_osd.y = 0;
515 ivtv_fb_set_osd_coords(itv, &ivtv_osd);
516
517 /* Can't seem to find the right API combo for this.
518 Use another function which does what we need through direct register access. */
519 ivtv_window.width = var->xres;
520 ivtv_window.height = var->yres;
521
522 /* Minimum margin cannot be 0, as X won't allow such a mode */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300523 if (!var->upper_margin) var->upper_margin++;
524 if (!var->left_margin) var->left_margin++;
Hans Verkuil32db7752007-07-20 09:29:43 -0300525 ivtv_window.top = var->upper_margin - 1;
526 ivtv_window.left = var->left_margin - 1;
527
528 ivtv_fb_set_display_window(itv, &ivtv_window);
529
530 /* Force update of yuv registers */
531 itv->yuv_info.yuv_forced_update = 1;
532
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300533 IVTV_FB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
534 var->xres, var->yres,
535 var->xres_virtual, var->yres_virtual,
536 var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300537
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300538 IVTV_FB_DEBUG_INFO("Display position: %d, %d\n",
539 var->left_margin, var->upper_margin);
Hans Verkuil32db7752007-07-20 09:29:43 -0300540
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300541 IVTV_FB_DEBUG_INFO("Display filter: %s\n",
542 (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
543 IVTV_FB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
Hans Verkuil32db7752007-07-20 09:29:43 -0300544
545 return 0;
546}
547
548static int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix)
549{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300550 struct osd_info *oi = itv->osd_info;
551
552 IVTV_FB_DEBUG_INFO("ivtvfb_get_fix\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300553 memset(fix, 0, sizeof(struct fb_fix_screeninfo));
554 strcpy(fix->id, "cx23415 TV out");
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300555 fix->smem_start = oi->video_pbase;
556 fix->smem_len = oi->video_buffer_size;
Hans Verkuil32db7752007-07-20 09:29:43 -0300557 fix->type = FB_TYPE_PACKED_PIXELS;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300558 fix->visual = (oi->bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
Hans Verkuil32db7752007-07-20 09:29:43 -0300559 fix->xpanstep = 1;
560 fix->ypanstep = 1;
561 fix->ywrapstep = 0;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300562 fix->line_length = oi->display_byte_stride;
Hans Verkuil32db7752007-07-20 09:29:43 -0300563 fix->accel = FB_ACCEL_NONE;
564 return 0;
565}
566
567/* Check the requested display mode, returning -EINVAL if we can't
568 handle it. */
569
570static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv)
571{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300572 struct osd_info *oi = itv->osd_info;
Ian Armstrong68a341a2007-08-03 09:51:58 -0300573 int osd_height_limit;
574 u32 pixclock, hlimit, vlimit;
Hans Verkuil32db7752007-07-20 09:29:43 -0300575
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300576 IVTV_FB_DEBUG_INFO("ivtvfb_check_var\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300577
Ian Armstrong68a341a2007-08-03 09:51:58 -0300578 /* Set base references for mode calcs. */
579 if (itv->is_50hz) {
580 pixclock = 84316;
581 hlimit = 776;
582 vlimit = 591;
583 osd_height_limit = 576;
584 }
585 else {
586 pixclock = 83926;
587 hlimit = 776;
588 vlimit = 495;
589 osd_height_limit = 480;
590 }
591
Hans Verkuil32db7752007-07-20 09:29:43 -0300592 /* Check the bits per pixel */
593 if (osd_compat) {
594 if (var->bits_per_pixel != 32) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300595 IVTV_FB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300596 return -EINVAL;
597 }
598 }
599
600 if (var->bits_per_pixel == 8 || var->bits_per_pixel == 32) {
601 var->transp.offset = 24;
602 var->transp.length = 8;
603 var->red.offset = 16;
604 var->red.length = 8;
605 var->green.offset = 8;
606 var->green.length = 8;
607 var->blue.offset = 0;
608 var->blue.length = 8;
609 }
610 else if (var->bits_per_pixel == 16) {
611 /* To find out the true mode, check green length */
612 switch (var->green.length) {
613 case 4:
Hans Verkuil32db7752007-07-20 09:29:43 -0300614 var->red.offset = 8;
615 var->red.length = 4;
616 var->green.offset = 4;
617 var->green.length = 4;
618 var->blue.offset = 0;
619 var->blue.length = 4;
Hans Verkuil459a52f2007-08-22 08:58:47 -0300620 var->transp.offset = 12;
621 var->transp.length = 1;
Hans Verkuil32db7752007-07-20 09:29:43 -0300622 break;
623 case 5:
Hans Verkuil32db7752007-07-20 09:29:43 -0300624 var->red.offset = 10;
625 var->red.length = 5;
626 var->green.offset = 5;
627 var->green.length = 5;
628 var->blue.offset = 0;
629 var->blue.length = 5;
Hans Verkuil459a52f2007-08-22 08:58:47 -0300630 var->transp.offset = 15;
631 var->transp.length = 1;
Hans Verkuil32db7752007-07-20 09:29:43 -0300632 break;
633 default:
Hans Verkuil32db7752007-07-20 09:29:43 -0300634 var->red.offset = 11;
635 var->red.length = 5;
636 var->green.offset = 5;
637 var->green.length = 6;
638 var->blue.offset = 0;
639 var->blue.length = 5;
Hans Verkuil459a52f2007-08-22 08:58:47 -0300640 var->transp.offset = 0;
641 var->transp.length = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300642 break;
643 }
644 }
645 else {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300646 IVTV_FB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300647 return -EINVAL;
648 }
649
650 /* Check the resolution */
651 if (osd_compat) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300652 if (var->xres != oi->ivtvfb_defined.xres ||
653 var->yres != oi->ivtvfb_defined.yres ||
654 var->xres_virtual != oi->ivtvfb_defined.xres_virtual ||
655 var->yres_virtual != oi->ivtvfb_defined.yres_virtual) {
656 IVTV_FB_DEBUG_WARN("Invalid resolution: %dx%d (virtual %dx%d)\n",
657 var->xres, var->yres, var->xres_virtual, var->yres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300658 return -EINVAL;
659 }
660 }
661 else {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300662 if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit) {
663 IVTV_FB_DEBUG_WARN("Invalid resolution: %dx%d\n",
664 var->xres, var->yres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300665 return -EINVAL;
666 }
667
668 /* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */
669 if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) ||
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300670 var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8) > oi->video_buffer_size ||
Hans Verkuil32db7752007-07-20 09:29:43 -0300671 var->xres_virtual < var->xres ||
672 var->yres_virtual < var->yres) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300673 IVTV_FB_DEBUG_WARN("Invalid virtual resolution: %dx%d\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300674 var->xres_virtual, var->yres_virtual);
675 return -EINVAL;
676 }
677 }
678
679 /* Some extra checks if in 8 bit mode */
680 if (var->bits_per_pixel == 8) {
681 /* Width must be a multiple of 4 */
682 if (var->xres & 3) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300683 IVTV_FB_DEBUG_WARN("Invalid resolution for 8bpp: %d\n", var->xres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300684 return -EINVAL;
685 }
686 if (var->xres_virtual & 3) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300687 IVTV_FB_DEBUG_WARN("Invalid virtual resolution for 8bpp: %d)\n", var->xres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300688 return -EINVAL;
689 }
690 }
691 else if (var->bits_per_pixel == 16) {
692 /* Width must be a multiple of 2 */
693 if (var->xres & 1) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300694 IVTV_FB_DEBUG_WARN("Invalid resolution for 16bpp: %d\n", var->xres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300695 return -EINVAL;
696 }
697 if (var->xres_virtual & 1) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300698 IVTV_FB_DEBUG_WARN("Invalid virtual resolution for 16bpp: %d)\n", var->xres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300699 return -EINVAL;
700 }
701 }
702
703 /* Now check the offsets */
704 if (var->xoffset >= var->xres_virtual || var->yoffset >= var->yres_virtual) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300705 IVTV_FB_DEBUG_WARN("Invalid offset: %d (%d) %d (%d)\n",
706 var->xoffset, var->xres_virtual, var->yoffset, var->yres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300707 return -EINVAL;
708 }
709
710 /* Check pixel format */
711 if (var->nonstd > 1) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300712 IVTV_FB_DEBUG_WARN("Invalid nonstd % d\n", var->nonstd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300713 return -EINVAL;
714 }
715
716 /* Check video mode */
717 if (((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) &&
718 ((var->vmode & FB_VMODE_MASK) != FB_VMODE_INTERLACED)) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300719 IVTV_FB_DEBUG_WARN("Invalid video mode: %d\n", var->vmode & FB_VMODE_MASK);
Hans Verkuil32db7752007-07-20 09:29:43 -0300720 return -EINVAL;
721 }
722
723 /* Check the left & upper margins
724 If the margins are too large, just center the screen
725 (enforcing margins causes too many problems) */
726
727 if (var->left_margin + var->xres > IVTV_OSD_MAX_WIDTH + 1) {
728 var->left_margin = 1 + ((IVTV_OSD_MAX_WIDTH - var->xres) / 2);
729 }
730 if (var->upper_margin + var->yres > (itv->is_50hz ? 577 : 481)) {
731 var->upper_margin = 1 + (((itv->is_50hz ? 576 : 480) - var->yres) / 2);
732 }
733
734 /* Maintain overall 'size' for a constant refresh rate */
Ian Armstrong68a341a2007-08-03 09:51:58 -0300735 var->right_margin = hlimit - var->left_margin - var->xres;
736 var->lower_margin = vlimit - var->upper_margin - var->yres;
Hans Verkuil32db7752007-07-20 09:29:43 -0300737
738 /* Fixed sync times */
739 var->hsync_len = 24;
740 var->vsync_len = 2;
741
742 /* Non-interlaced / interlaced mode is used to switch the OSD filter
743 on or off. Adjust the clock timings to maintain a constant
744 vertical refresh rate. */
Hans Verkuil32db7752007-07-20 09:29:43 -0300745 if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED)
Ian Armstrong68a341a2007-08-03 09:51:58 -0300746 var->pixclock = pixclock / 2;
747 else
748 var->pixclock = pixclock;
Hans Verkuil32db7752007-07-20 09:29:43 -0300749
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300750 IVTV_FB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
751 var->xres, var->yres,
752 var->xres_virtual, var->yres_virtual,
Hans Verkuil32db7752007-07-20 09:29:43 -0300753 var->bits_per_pixel);
754
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300755 IVTV_FB_DEBUG_INFO("Display position: %d, %d\n",
756 var->left_margin, var->upper_margin);
Hans Verkuil32db7752007-07-20 09:29:43 -0300757
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300758 IVTV_FB_DEBUG_INFO("Display filter: %s\n",
759 (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
760 IVTV_FB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
Hans Verkuil32db7752007-07-20 09:29:43 -0300761 return 0;
762}
763
764static int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
765{
766 struct ivtv *itv = (struct ivtv *) info->par;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300767 IVTV_FB_DEBUG_INFO("ivtvfb_check_var\n");
768 return _ivtvfb_check_var(var, itv);
Hans Verkuil32db7752007-07-20 09:29:43 -0300769}
770
771static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
772{
773 u32 osd_pan_index;
774 struct ivtv *itv = (struct ivtv *) info->par;
775
776 osd_pan_index = (var->xoffset + (var->yoffset * var->xres_virtual))*var->bits_per_pixel/8;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300777 write_reg(osd_pan_index, 0x02A0C);
Hans Verkuil32db7752007-07-20 09:29:43 -0300778
779 /* Pass this info back the yuv handler */
780 itv->yuv_info.osd_x_pan = var->xoffset;
781 itv->yuv_info.osd_y_pan = var->yoffset;
782 /* Force update of yuv registers */
783 itv->yuv_info.yuv_forced_update = 1;
784 return 0;
785}
786
787static int ivtvfb_set_par(struct fb_info *info)
788{
789 int rc = 0;
790 struct ivtv *itv = (struct ivtv *) info->par;
791
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300792 IVTV_FB_DEBUG_INFO("ivtvfb_set_par\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300793
794 rc = ivtvfb_set_var(itv, &info->var);
795 ivtvfb_pan_display(&info->var, info);
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300796 ivtvfb_get_fix(itv, &info->fix);
Hans Verkuil32db7752007-07-20 09:29:43 -0300797 return rc;
798}
799
800static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green,
801 unsigned blue, unsigned transp,
802 struct fb_info *info)
803{
804 u32 color, *palette;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300805 struct ivtv *itv = (struct ivtv *)info->par;
Hans Verkuil32db7752007-07-20 09:29:43 -0300806
807 if (regno >= info->cmap.len)
808 return -EINVAL;
809
810 color = ((transp & 0xFF00) << 16) |((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8);
811 if (info->var.bits_per_pixel <= 8) {
812 write_reg(regno, 0x02a30);
813 write_reg(color, 0x02a34);
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300814 return 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300815 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300816 if (regno >= 16)
817 return -EINVAL;
Hans Verkuil32db7752007-07-20 09:29:43 -0300818
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300819 palette = info->pseudo_palette;
820 if (info->var.bits_per_pixel == 16) {
821 switch (info->var.green.length) {
822 case 4:
823 color = ((red & 0xf000) >> 4) |
824 ((green & 0xf000) >> 8) |
825 ((blue & 0xf000) >> 12);
826 break;
827 case 5:
828 color = ((red & 0xf800) >> 1) |
829 ((green & 0xf800) >> 6) |
830 ((blue & 0xf800) >> 11);
831 break;
832 case 6:
833 color = (red & 0xf800 ) |
834 ((green & 0xfc00) >> 5) |
835 ((blue & 0xf800) >> 11);
836 break;
Hans Verkuil32db7752007-07-20 09:29:43 -0300837 }
Hans Verkuil32db7752007-07-20 09:29:43 -0300838 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300839 palette[regno] = color;
Hans Verkuil32db7752007-07-20 09:29:43 -0300840 return 0;
841}
842
843/* We don't really support blanking. All this does is enable or
844 disable the OSD. */
845static int ivtvfb_blank(int blank_mode, struct fb_info *info)
846{
847 struct ivtv *itv = (struct ivtv *)info->par;
848
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300849 IVTV_FB_DEBUG_INFO("Set blanking mode : %d\n", blank_mode);
Hans Verkuil32db7752007-07-20 09:29:43 -0300850 switch (blank_mode) {
851 case FB_BLANK_UNBLANK:
852 ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1);
853 break;
854 case FB_BLANK_NORMAL:
855 case FB_BLANK_HSYNC_SUSPEND:
856 case FB_BLANK_VSYNC_SUSPEND:
857 case FB_BLANK_POWERDOWN:
858 ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
859 break;
860 }
861 return 0;
862}
863
864static struct fb_ops ivtvfb_ops = {
865 .owner = THIS_MODULE,
866 .fb_check_var = ivtvfb_check_var,
867 .fb_set_par = ivtvfb_set_par,
868 .fb_setcolreg = ivtvfb_setcolreg,
869 .fb_fillrect = cfb_fillrect,
870 .fb_copyarea = cfb_copyarea,
871 .fb_imageblit = cfb_imageblit,
872 .fb_cursor = NULL,
873 .fb_ioctl = ivtvfb_ioctl,
874 .fb_pan_display = ivtvfb_pan_display,
875 .fb_blank = ivtvfb_blank,
876};
877
878/* Initialization */
879
880
881/* Setup our initial video mode */
882static int ivtvfb_init_vidmode(struct ivtv *itv)
883{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300884 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300885 struct v4l2_rect start_window;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300886 int max_height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300887
Hans Verkuil32db7752007-07-20 09:29:43 -0300888 /* Color mode */
889
890 if (osd_compat) osd_depth = 32;
891 if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32) osd_depth = 8;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300892 oi->bits_per_pixel = osd_depth;
893 oi->bytes_per_pixel = oi->bits_per_pixel / 8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300894
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300895 /* Invalidate current osd mode to force a mode switch later */
896 oi->osd_mode = -1;
897
Hans Verkuil32db7752007-07-20 09:29:43 -0300898 /* Horizontal size & position */
899
900 if (osd_xres > 720) osd_xres = 720;
901
902 /* Must be a multiple of 4 for 8bpp & 2 for 16bpp */
903 if (osd_depth == 8)
904 osd_xres &= ~3;
905 else if (osd_depth == 16)
906 osd_xres &= ~1;
907
908 if (osd_xres)
909 start_window.width = osd_xres;
910 else
911 start_window.width = osd_compat ? 720: 640;
912
913 /* Check horizontal start (osd_left). */
914 if (osd_left && osd_left + start_window.width > 721) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300915 IVTV_FB_ERR("Invalid osd_left - assuming default\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300916 osd_left = 0;
917 }
918
919 /* Hardware coords start at 0, user coords start at 1. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300920 osd_left--;
Hans Verkuil32db7752007-07-20 09:29:43 -0300921
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300922 start_window.left = osd_left >= 0 ? osd_left : ((IVTV_OSD_MAX_WIDTH - start_window.width) / 2);
Hans Verkuil32db7752007-07-20 09:29:43 -0300923
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300924 oi->display_byte_stride =
925 start_window.width * oi->bytes_per_pixel;
Hans Verkuil32db7752007-07-20 09:29:43 -0300926
927 /* Vertical size & position */
928
929 max_height = itv->is_50hz ? 576 : 480;
930
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300931 if (osd_yres > max_height)
932 osd_yres = max_height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300933
934 if (osd_yres)
935 start_window.height = osd_yres;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300936 else
937 start_window.height = osd_compat ? max_height : (itv->is_50hz ? 480 : 400);
Hans Verkuil32db7752007-07-20 09:29:43 -0300938
939 /* Check vertical start (osd_upper). */
940 if (osd_upper + start_window.height > max_height + 1) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300941 IVTV_FB_ERR("Invalid osd_upper - assuming default\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300942 osd_upper = 0;
943 }
944
945 /* Hardware coords start at 0, user coords start at 1. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300946 osd_upper--;
Hans Verkuil32db7752007-07-20 09:29:43 -0300947
948 start_window.top = osd_upper >= 0 ? osd_upper : ((max_height - start_window.height) / 2);
949
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300950 oi->display_width = start_window.width;
951 oi->display_height = start_window.height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300952
953 /* Generate a valid fb_var_screeninfo */
954
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300955 oi->ivtvfb_defined.xres = oi->display_width;
956 oi->ivtvfb_defined.yres = oi->display_height;
957 oi->ivtvfb_defined.xres_virtual = oi->display_width;
958 oi->ivtvfb_defined.yres_virtual = oi->display_height;
959 oi->ivtvfb_defined.bits_per_pixel = oi->bits_per_pixel;
960 oi->ivtvfb_defined.vmode = (osd_laced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED);
961 oi->ivtvfb_defined.left_margin = start_window.left + 1;
962 oi->ivtvfb_defined.upper_margin = start_window.top + 1;
963 oi->ivtvfb_defined.accel_flags = FB_ACCEL_NONE;
964 oi->ivtvfb_defined.nonstd = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300965
966 /* We've filled in the most data, let the usual mode check
967 routine fill in the rest. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300968 _ivtvfb_check_var(&oi->ivtvfb_defined, itv);
Hans Verkuil32db7752007-07-20 09:29:43 -0300969
970 /* Generate valid fb_fix_screeninfo */
971
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300972 ivtvfb_get_fix(itv, &oi->ivtvfb_fix);
Hans Verkuil32db7752007-07-20 09:29:43 -0300973
974 /* Generate valid fb_info */
975
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300976 oi->ivtvfb_info.node = -1;
977 oi->ivtvfb_info.flags = FBINFO_FLAG_DEFAULT;
978 oi->ivtvfb_info.fbops = &ivtvfb_ops;
979 oi->ivtvfb_info.par = itv;
980 oi->ivtvfb_info.var = oi->ivtvfb_defined;
981 oi->ivtvfb_info.fix = oi->ivtvfb_fix;
982 oi->ivtvfb_info.screen_base = (u8 __iomem *)oi->video_vbase;
983 oi->ivtvfb_info.fbops = &ivtvfb_ops;
Hans Verkuil32db7752007-07-20 09:29:43 -0300984
985 /* Supply some monitor specs. Bogus values will do for now */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300986 oi->ivtvfb_info.monspecs.hfmin = 8000;
987 oi->ivtvfb_info.monspecs.hfmax = 70000;
988 oi->ivtvfb_info.monspecs.vfmin = 10;
989 oi->ivtvfb_info.monspecs.vfmax = 100;
Hans Verkuil32db7752007-07-20 09:29:43 -0300990
991 /* Allocate color map */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300992 if (fb_alloc_cmap(&oi->ivtvfb_info.cmap, 256, 1)) {
993 IVTV_FB_ERR("abort, unable to alloc cmap\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300994 return -ENOMEM;
995 }
996
997 /* Allocate the pseudo palette */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300998 oi->ivtvfb_info.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
Hans Verkuil32db7752007-07-20 09:29:43 -0300999
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001000 if (!oi->ivtvfb_info.pseudo_palette) {
1001 IVTV_FB_ERR("abort, unable to alloc pseudo pallete\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001002 return -ENOMEM;
1003 }
1004
1005 return 0;
1006}
1007
1008/* Find OSD buffer base & size. Add to mtrr. Zero osd buffer. */
1009
1010static int ivtvfb_init_io(struct ivtv *itv)
1011{
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001012 struct osd_info *oi = itv->osd_info;
1013
Hans Verkuil6e5eb592007-07-25 12:55:52 -03001014 if (ivtv_init_on_first_open(itv)) {
1015 IVTV_FB_ERR("Failed to initialize ivtv\n");
1016 return -ENXIO;
1017 }
1018
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001019 ivtv_fb_get_framebuffer(itv, &oi->video_rbase, &oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -03001020
1021 /* The osd buffer size depends on the number of video buffers allocated
1022 on the PVR350 itself. For now we'll hardcode the smallest osd buffer
1023 size to prevent any overlap. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001024 oi->video_buffer_size = 1704960;
Hans Verkuil32db7752007-07-20 09:29:43 -03001025
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001026 oi->video_pbase = itv->base_addr + IVTV_DECODER_OFFSET + oi->video_rbase;
1027 oi->video_vbase = itv->dec_mem + oi->video_rbase;
Hans Verkuil32db7752007-07-20 09:29:43 -03001028
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001029 if (!oi->video_vbase) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001030 IVTV_FB_ERR("abort, video memory 0x%x @ 0x%lx isn't mapped!\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001031 oi->video_buffer_size, oi->video_pbase);
Hans Verkuil32db7752007-07-20 09:29:43 -03001032 return -EIO;
1033 }
1034
1035 IVTV_FB_INFO("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001036 oi->video_pbase, oi->video_vbase,
1037 oi->video_buffer_size / 1024);
Hans Verkuil32db7752007-07-20 09:29:43 -03001038
1039#ifdef CONFIG_MTRR
1040 {
1041 /* Find the largest power of two that maps the whole buffer */
1042 int size_shift = 31;
1043
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001044 while (!(oi->video_buffer_size & (1 << size_shift))) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001045 size_shift--;
1046 }
1047 size_shift++;
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001048 oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1);
1049 oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size;
1050 oi->fb_end_aligned_physaddr += (1 << size_shift) - 1;
1051 oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1);
1052 if (mtrr_add(oi->fb_start_aligned_physaddr,
1053 oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr,
Hans Verkuil32db7752007-07-20 09:29:43 -03001054 MTRR_TYPE_WRCOMB, 1) < 0) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001055 IVTV_FB_WARN("cannot use mttr\n");
1056 oi->fb_start_aligned_physaddr = 0;
1057 oi->fb_end_aligned_physaddr = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -03001058 }
1059 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001060#endif
Hans Verkuil32db7752007-07-20 09:29:43 -03001061
1062 /* Blank the entire osd. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001063 memset_io(oi->video_vbase, 0, oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -03001064
1065 return 0;
1066}
1067
1068/* Release any memory we've grabbed & remove mtrr entry */
1069static void ivtvfb_release_buffers (struct ivtv *itv)
1070{
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001071 struct osd_info *oi = itv->osd_info;
1072
Hans Verkuil32db7752007-07-20 09:29:43 -03001073 /* Release cmap */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001074 if (oi->ivtvfb_info.cmap.len);
1075 fb_dealloc_cmap(&oi->ivtvfb_info.cmap);
Hans Verkuil32db7752007-07-20 09:29:43 -03001076
1077 /* Release pseudo palette */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001078 if (oi->ivtvfb_info.pseudo_palette)
1079 kfree(oi->ivtvfb_info.pseudo_palette);
Hans Verkuil32db7752007-07-20 09:29:43 -03001080
1081#ifdef CONFIG_MTRR
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001082 if (oi->fb_end_aligned_physaddr) {
1083 mtrr_del(-1, oi->fb_start_aligned_physaddr,
1084 oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr);
1085 }
1086#endif
Hans Verkuil32db7752007-07-20 09:29:43 -03001087
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001088 kfree(oi);
Hans Verkuil32db7752007-07-20 09:29:43 -03001089 itv->osd_info = NULL;
1090}
1091
1092/* Initialize the specified card */
1093
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001094static int ivtvfb_init_card(struct ivtv *itv)
Hans Verkuil32db7752007-07-20 09:29:43 -03001095{
1096 int rc;
1097
1098 if (itv->osd_info) {
1099 IVTV_FB_ERR("Card %d already initialised\n", ivtv_fb_card_id);
1100 return -EBUSY;
1101 }
1102
1103 itv->osd_info = kzalloc(sizeof(struct osd_info), GFP_ATOMIC);
1104 if (itv->osd_info == 0) {
1105 IVTV_FB_ERR("Failed to allocate memory for osd_info\n");
1106 return -ENOMEM;
1107 }
1108
1109 /* Find & setup the OSD buffer */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001110 if ((rc = ivtvfb_init_io(itv)))
Hans Verkuil32db7752007-07-20 09:29:43 -03001111 return rc;
1112
1113 /* Set the startup video mode information */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001114 if ((rc = ivtvfb_init_vidmode(itv))) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001115 ivtvfb_release_buffers(itv);
1116 return rc;
1117 }
1118
1119 /* Register the framebuffer */
1120 if (register_framebuffer(&itv->osd_info->ivtvfb_info) < 0) {
1121 ivtvfb_release_buffers(itv);
1122 return -EINVAL;
1123 }
1124
1125 itv->osd_video_pbase = itv->osd_info->video_pbase;
1126
1127 /* Set the card to the requested mode */
1128 ivtvfb_set_par(&itv->osd_info->ivtvfb_info);
1129
1130 /* Set color 0 to black */
1131 write_reg(0, 0x02a30);
1132 write_reg(0, 0x02a34);
1133
1134 /* Enable the osd */
1135 ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info);
1136
1137 /* Note if we're running in compatibility mode */
1138 if (osd_compat)
1139 IVTV_FB_INFO("Running in compatibility mode. Display resize & mode change disabled\n");
1140
1141 /* Allocate DMA */
1142 ivtv_udma_alloc(itv);
1143 return 0;
1144
1145}
1146
1147static int __init ivtvfb_init(void)
1148{
1149 struct ivtv *itv;
1150 int i, registered = 0;
1151
1152 if (ivtv_fb_card_id < -1 || ivtv_fb_card_id >= IVTV_MAX_CARDS) {
1153 printk(KERN_ERR "ivtv-fb: ivtv_fb_card_id parameter is out of range (valid range: -1 - %d)\n",
1154 IVTV_MAX_CARDS - 1);
1155 return -EINVAL;
1156 }
1157
1158 /* Locate & initialise all cards supporting an OSD. */
1159 for (i = 0; i < ivtv_cards_active; i++) {
1160 if (ivtv_fb_card_id != -1 && i != ivtv_fb_card_id)
1161 continue;
1162 itv = ivtv_cards[i];
1163 if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
1164 if (ivtvfb_init_card(itv) == 0) {
1165 IVTV_FB_INFO("Framebuffer registered on ivtv card id %d\n", i);
1166 registered++;
1167 }
1168 }
1169 }
1170 if (!registered) {
1171 printk(KERN_ERR "ivtv-fb: no cards found");
1172 return -ENODEV;
1173 }
1174 return 0;
1175}
1176
1177static void ivtvfb_cleanup(void)
1178{
1179 struct ivtv *itv;
1180 int i;
1181
1182 printk(KERN_INFO "ivtv-fb: Unloading framebuffer module\n");
1183
1184 for (i = 0; i < ivtv_cards_active; i++) {
1185 itv = ivtv_cards[i];
1186 if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) && itv->osd_info) {
1187 IVTV_FB_DEBUG_INFO("Unregister framebuffer %d\n", i);
1188 ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info);
1189 unregister_framebuffer(&itv->osd_info->ivtvfb_info);
1190 ivtvfb_release_buffers(itv);
1191 itv->osd_video_pbase = 0;
1192 }
1193 }
1194}
1195
1196module_init(ivtvfb_init);
1197module_exit(ivtvfb_cleanup);