blob: 55265bd5c2505e7ec75832bd846138177900ccea [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"
60#include "ivtv-queue.h"
61#include "ivtv-udma.h"
62#include "ivtv-irq.h"
63#include "ivtv-fileops.h"
64#include "ivtv-mailbox.h"
65#include "ivtv-cards.h"
66#include <media/ivtv-fb.h>
67
68/* card parameters */
69static int ivtv_fb_card_id = -1;
70static int ivtv_fb_debug = 0;
71static int osd_laced;
72static int osd_compat;
73static int osd_depth;
74static int osd_upper;
75static int osd_left;
76static int osd_yres;
77static int osd_xres;
78
79module_param(ivtv_fb_card_id, int, 0444);
80module_param_named(debug,ivtv_fb_debug, int, 0644);
81module_param(osd_laced, bool, 0444);
82module_param(osd_compat, bool, 0444);
83module_param(osd_depth, int, 0444);
84module_param(osd_upper, int, 0444);
85module_param(osd_left, int, 0444);
86module_param(osd_yres, int, 0444);
87module_param(osd_xres, int, 0444);
88
89MODULE_PARM_DESC(ivtv_fb_card_id,
90 "Only use framebuffer of the specified ivtv card (0-31)\n"
91 "\t\t\tdefault -1: initialize all available framebuffers");
92
93MODULE_PARM_DESC(debug,
94 "Debug level (bitmask). Default: errors only\n"
95 "\t\t\t(debug = 3 gives full debugging)");
96
97MODULE_PARM_DESC(osd_compat,
98 "Compatibility mode - Display size is locked (use for old X drivers)\n"
99 "\t\t\t0=off\n"
100 "\t\t\t1=on\n"
101 "\t\t\tdefault off");
102
103/* Why upper, left, xres, yres, depth, laced ? To match terminology used
104 by fbset.
105 Why start at 1 for left & upper coordinate ? Because X doesn't allow 0 */
106
107MODULE_PARM_DESC(osd_laced,
108 "Interlaced mode\n"
109 "\t\t\t0=off\n"
110 "\t\t\t1=on\n"
111 "\t\t\tdefault off");
112
113MODULE_PARM_DESC(osd_depth,
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300114 "Bits per pixel - 8, 16, 32\n"
Hans Verkuil32db7752007-07-20 09:29:43 -0300115 "\t\t\tdefault 8");
116
117MODULE_PARM_DESC(osd_upper,
118 "Vertical start position\n"
119 "\t\t\tdefault 0 (Centered)");
120
121MODULE_PARM_DESC(osd_left,
122 "Horizontal start position\n"
123 "\t\t\tdefault 0 (Centered)");
124
125MODULE_PARM_DESC(osd_yres,
126 "Display height\n"
127 "\t\t\tdefault 480 (PAL)\n"
128 "\t\t\t 400 (NTSC)");
129
130MODULE_PARM_DESC(osd_xres,
131 "Display width\n"
132 "\t\t\tdefault 640");
133
134MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil, John Harvey, Ian Armstrong");
135MODULE_LICENSE("GPL");
136
137/* --------------------------------------------------------------------- */
138
139#define IVTV_FB_DBGFLG_WARN (1 << 0)
140#define IVTV_FB_DBGFLG_INFO (1 << 1)
141
142#define IVTV_FB_DEBUG(x, type, fmt, args...) \
143 do { \
144 if ((x) & ivtv_fb_debug) \
145 printk(KERN_INFO "ivtv-fb%d " type ": " fmt, itv->num , ## args); \
146 } while (0)
147#define IVTV_FB_DEBUG_WARN(fmt, args...) IVTV_FB_DEBUG(IVTV_FB_DBGFLG_WARN, "warning", fmt , ## args)
148#define IVTV_FB_DEBUG_INFO(fmt, args...) IVTV_FB_DEBUG(IVTV_FB_DBGFLG_INFO, "info", fmt , ## args)
149
150/* Standard kernel messages */
151#define IVTV_FB_ERR(fmt, args...) printk(KERN_ERR "ivtv-fb%d: " fmt, itv->num , ## args)
152#define IVTV_FB_WARN(fmt, args...) printk(KERN_WARNING "ivtv-fb%d: " fmt, itv->num , ## args)
153#define IVTV_FB_INFO(fmt, args...) printk(KERN_INFO "ivtv-fb%d: " fmt, itv->num , ## args)
154
155/* --------------------------------------------------------------------- */
156
157#define IVTV_OSD_MAX_WIDTH 720
158#define IVTV_OSD_MAX_HEIGHT 576
159
160#define IVTV_OSD_BPP_8 0x00
161#define IVTV_OSD_BPP_16_444 0x03
162#define IVTV_OSD_BPP_16_555 0x02
163#define IVTV_OSD_BPP_16_565 0x01
164#define IVTV_OSD_BPP_32 0x04
165
166struct osd_info {
167 /* Timing info for modes */
168 u32 pixclock;
169 u32 hlimit;
170 u32 vlimit;
171
172 /* Physical base address */
173 unsigned long video_pbase;
174 /* Relative base address (relative to start of decoder memory) */
175 u32 video_rbase;
176 /* Mapped base address */
177 volatile char __iomem *video_vbase;
178 /* Buffer size */
179 u32 video_buffer_size;
180
181#ifdef CONFIG_MTRR
182 /* video_base rounded down as required by hardware MTRRs */
183 unsigned long fb_start_aligned_physaddr;
184 /* video_base rounded up as required by hardware MTRRs */
185 unsigned long fb_end_aligned_physaddr;
186#endif
187
188 /* Store the buffer offset */
189 int set_osd_coords_x;
190 int set_osd_coords_y;
191
192 /* Current dimensions (NOT VISIBLE SIZE!) */
193 int display_width;
194 int display_height;
195 int display_byte_stride;
196
197 /* Current bits per pixel */
198 int bits_per_pixel;
199 int bytes_per_pixel;
200
201 /* Frame buffer stuff */
202 struct fb_info ivtvfb_info;
203 struct fb_var_screeninfo ivtvfb_defined;
204 struct fb_fix_screeninfo ivtvfb_fix;
205};
206
207struct ivtv_osd_coords {
208 unsigned long offset;
209 unsigned long max_offset;
210 int pixel_stride;
211 int lines;
212 int x;
213 int y;
214};
215
216/* --------------------------------------------------------------------- */
217
218/* ivtv API calls for framebuffer related support */
219
220static int ivtv_fb_get_framebuffer(struct ivtv *itv, u32 *fbbase,
221 u32 *fblength)
222{
223 u32 data[CX2341X_MBOX_MAX_DATA];
224 int rc;
225
226 rc = ivtv_vapi_result(itv, data, CX2341X_OSD_GET_FRAMEBUFFER, 0);
227 *fbbase = data[0];
228 *fblength = data[1];
229 return rc;
230}
231
232static int ivtv_fb_get_osd_coords(struct ivtv *itv,
233 struct ivtv_osd_coords *osd)
234{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300235 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300236 u32 data[CX2341X_MBOX_MAX_DATA];
237
238 ivtv_vapi_result(itv, data, CX2341X_OSD_GET_OSD_COORDS, 0);
239
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300240 osd->offset = data[0] - oi->video_rbase;
241 osd->max_offset = oi->display_width * oi->display_height * 4;
Hans Verkuil32db7752007-07-20 09:29:43 -0300242 osd->pixel_stride = data[1];
243 osd->lines = data[2];
244 osd->x = data[3];
245 osd->y = data[4];
246 return 0;
247}
248
249static int ivtv_fb_set_osd_coords(struct ivtv *itv, const struct ivtv_osd_coords *osd)
250{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300251 struct osd_info *oi = itv->osd_info;
252
253 oi->display_width = osd->pixel_stride;
254 oi->display_byte_stride = osd->pixel_stride * oi->bytes_per_pixel;
255 oi->set_osd_coords_x += osd->x;
256 oi->set_osd_coords_y = osd->y;
Hans Verkuil32db7752007-07-20 09:29:43 -0300257
258 return ivtv_vapi(itv, CX2341X_OSD_SET_OSD_COORDS, 5,
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300259 osd->offset + oi->video_rbase,
Hans Verkuil32db7752007-07-20 09:29:43 -0300260 osd->pixel_stride,
261 osd->lines, osd->x, osd->y);
262}
263
264static int ivtv_fb_set_display_window(struct ivtv *itv, struct v4l2_rect *ivtv_window)
265{
Hans Verkuil32db7752007-07-20 09:29:43 -0300266 int osd_height_limit = itv->is_50hz ? 576 : 480;
267
268 /* Only fail if resolution too high, otherwise fudge the start coords. */
269 if ((ivtv_window->height > osd_height_limit) || (ivtv_window->width > IVTV_OSD_MAX_WIDTH))
270 return -EINVAL;
271
272 /* Ensure we don't exceed display limits */
273 if (ivtv_window->top + ivtv_window->height > osd_height_limit) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300274 IVTV_FB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid height setting (%d, %d)\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300275 ivtv_window->top, ivtv_window->height);
276 ivtv_window->top = osd_height_limit - ivtv_window->height;
277 }
278
279 if (ivtv_window->left + ivtv_window->width > IVTV_OSD_MAX_WIDTH) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300280 IVTV_FB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid width setting (%d, %d)\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300281 ivtv_window->left, ivtv_window->width);
282 ivtv_window->left = IVTV_OSD_MAX_WIDTH - ivtv_window->width;
283 }
284
285 /* Set the OSD origin */
286 write_reg((ivtv_window->top << 16) | ivtv_window->left, 0x02a04);
287
288 /* How much to display */
289 write_reg(((ivtv_window->top+ivtv_window->height) << 16) | (ivtv_window->left+ivtv_window->width), 0x02a08);
290
291 /* Pass this info back the yuv handler */
292 itv->yuv_info.osd_vis_w = ivtv_window->width;
293 itv->yuv_info.osd_vis_h = ivtv_window->height;
294 itv->yuv_info.osd_x_offset = ivtv_window->left;
295 itv->yuv_info.osd_y_offset = ivtv_window->top;
296
297 return 0;
298}
299
300static int ivtv_fb_prep_dec_dma_to_device(struct ivtv *itv,
301 unsigned long ivtv_dest_addr, void __user *userbuf,
302 int size_in_bytes)
303{
304 DEFINE_WAIT(wait);
305 int ret = 0;
306 int got_sig = 0;
307
308 mutex_lock(&itv->udma.lock);
309 /* Map User DMA */
310 if (ivtv_udma_setup(itv, ivtv_dest_addr, userbuf, size_in_bytes) <= 0) {
311 mutex_unlock(&itv->udma.lock);
312 IVTV_FB_WARN("ivtvfb_prep_dec_dma_to_device, "
313 "Error with get_user_pages: %d bytes, %d pages returned\n",
314 size_in_bytes, itv->udma.page_count);
315
316 /* get_user_pages must have failed completely */
317 return -EIO;
318 }
319
320 IVTV_FB_DEBUG_INFO("ivtvfb_prep_dec_dma_to_device, %d bytes, %d pages\n",
321 size_in_bytes, itv->udma.page_count);
322
323 ivtv_udma_prepare(itv);
324 prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
325 /* if no UDMA is pending and no UDMA is in progress, then the DMA
326 is finished */
327 while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) {
328 /* don't interrupt if the DMA is in progress but break off
329 a still pending DMA. */
330 got_sig = signal_pending(current);
331 if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))
332 break;
333 got_sig = 0;
334 schedule();
335 }
336 finish_wait(&itv->dma_waitq, &wait);
337
338 /* Unmap Last DMA Xfer */
339 ivtv_udma_unmap(itv);
340 mutex_unlock(&itv->udma.lock);
341 if (got_sig) {
342 IVTV_DEBUG_INFO("User stopped OSD\n");
343 return -EINTR;
344 }
345
346 return ret;
347}
348
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300349static int ivtv_fb_prep_frame(struct ivtv *itv, int cmd, void __user *source,
350 unsigned long dest_offset, int count)
Hans Verkuil32db7752007-07-20 09:29:43 -0300351{
352 DEFINE_WAIT(wait);
353
354 /* Nothing to do */
355 if (count == 0) {
356 IVTV_FB_DEBUG_WARN("ivtv_fb_prep_frame: Nothing to do. count = 0\n");
357 return -EINVAL;
358 }
359
360 /* Check Total FB Size */
361 if ((dest_offset + count) > itv->osd_info->video_buffer_size) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300362 IVTV_FB_WARN("ivtv_fb_prep_frame: Overflowing the framebuffer %ld, only %d available\n",
363 dest_offset + count, itv->osd_info->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -0300364 return -E2BIG;
365 }
366
367 /* Not fatal, but will have undesirable results */
368 if ((unsigned long)source & 3)
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300369 IVTV_FB_WARN("ivtv_fb_prep_frame: Source address not 32 bit aligned (0x%08lx)\n",
370 (unsigned long)source);
Hans Verkuil32db7752007-07-20 09:29:43 -0300371
372 if (dest_offset & 3)
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300373 IVTV_FB_WARN("ivtv_fb_prep_frame: Dest offset not 32 bit aligned (%ld)\n", dest_offset);
Hans Verkuil32db7752007-07-20 09:29:43 -0300374
375 if (count & 3)
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300376 IVTV_FB_WARN("ivtv_fb_prep_frame: Count not a multiple of 4 (%d)\n", count);
Hans Verkuil32db7752007-07-20 09:29:43 -0300377
378 /* Check Source */
379 if (!access_ok(VERIFY_READ, source + dest_offset, count)) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300380 IVTV_FB_WARN("Invalid userspace pointer 0x%08lx\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300381 (unsigned long)source);
382
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300383 IVTV_FB_DEBUG_WARN("access_ok() failed for offset 0x%08lx source 0x%08lx count %d\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300384 dest_offset, (unsigned long)source,
385 count);
386 return -EINVAL;
387 }
388
389 /* OSD Address to send DMA to */
390 dest_offset += IVTV_DEC_MEM_START + itv->osd_info->video_rbase;
391
392 /* Fill Buffers */
393 return ivtv_fb_prep_dec_dma_to_device(itv, dest_offset, source, count);
394}
395
396static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
397{
398 DEFINE_WAIT(wait);
399 struct ivtv *itv = (struct ivtv *)info->par;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300400 int rc = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300401
402 switch (cmd) {
Hans Verkuil32db7752007-07-20 09:29:43 -0300403 case FBIOGET_VBLANK: {
404 struct fb_vblank vblank;
405 u32 trace;
406
407 vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT |
408 FB_VBLANK_HAVE_VSYNC;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300409 trace = read_reg(0x028c0) >> 16;
Hans Verkuil32db7752007-07-20 09:29:43 -0300410 if (itv->is_50hz && trace > 312) trace -= 312;
411 else if (itv->is_60hz && trace > 262) trace -= 262;
412 if (trace == 1) vblank.flags |= FB_VBLANK_VSYNCING;
413 vblank.count = itv->lastVsyncFrame;
414 vblank.vcount = trace;
415 vblank.hcount = 0;
416 if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank)))
417 return -EFAULT;
418 return 0;
419 }
420
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300421 case FBIO_WAITFORVSYNC:
Hans Verkuil32db7752007-07-20 09:29:43 -0300422 prepare_to_wait(&itv->vsync_waitq, &wait, TASK_INTERRUPTIBLE);
423 if (!schedule_timeout(HZ/20)) rc = -ETIMEDOUT;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300424 finish_wait(&itv->vsync_waitq, &wait);
Hans Verkuil32db7752007-07-20 09:29:43 -0300425 return rc;
Hans Verkuil32db7752007-07-20 09:29:43 -0300426
Hans Verkuild715e762007-07-20 10:30:32 -0300427 case IVTVFB_IOC_DMA_FRAME: {
428 struct ivtvfb_dma_frame args;
Hans Verkuil32db7752007-07-20 09:29:43 -0300429
Hans Verkuild715e762007-07-20 10:30:32 -0300430 IVTV_FB_DEBUG_INFO("IVTVFB_IOC_DMA_FRAME\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300431 if (copy_from_user(&args, (void __user *)arg, sizeof(args)))
432 return -EFAULT;
433
434 return ivtv_fb_prep_frame(itv, cmd, args.source, args.dest_offset, args.count);
435 }
436
437 default:
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300438 IVTV_FB_ERR("Unknown IOCTL %d\n", cmd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300439 return -EINVAL;
440 }
441 return 0;
442}
443
444/* Framebuffer device handling */
445
446static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var)
447{
Hans Verkuil32db7752007-07-20 09:29:43 -0300448 struct ivtv_osd_coords ivtv_osd;
449 struct v4l2_rect ivtv_window;
450
451 IVTV_FB_DEBUG_INFO("ivtvfb_set_var\n");
452
453 /* Select color space */
454 if (var->nonstd) /* YUV */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300455 write_reg(read_reg(0x02a00) | 0x0002000, 0x02a00);
Hans Verkuil32db7752007-07-20 09:29:43 -0300456 else /* RGB */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300457 write_reg(read_reg(0x02a00) & ~0x0002000, 0x02a00);
Hans Verkuil32db7752007-07-20 09:29:43 -0300458
459 /* Set the color mode
460 Although rare, occasionally things go wrong. The extra mode
461 change seems to help... */
462
463 switch (var->bits_per_pixel) {
464 case 8:
465 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0);
466 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, IVTV_OSD_BPP_8);
467 break;
468 case 32:
469 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0);
470 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, IVTV_OSD_BPP_32);
471 break;
472 case 16:
473 switch (var->green.length) {
474 case 4:
475 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0);
476 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, IVTV_OSD_BPP_16_444);
477 break;
478 case 5:
479 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0);
480 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, IVTV_OSD_BPP_16_555);
481 break;
482 case 6:
483 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0);
484 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, IVTV_OSD_BPP_16_565);
485 break;
486 default:
487 IVTV_FB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
488 }
489 break;
490 default:
491 IVTV_FB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
492 }
493
494 itv->osd_info->bits_per_pixel = var->bits_per_pixel;
495 itv->osd_info->bytes_per_pixel = var->bits_per_pixel / 8;
496
497 /* Set the flicker filter */
498 switch (var->vmode & FB_VMODE_MASK) {
499 case FB_VMODE_NONINTERLACED: /* Filter on */
500 ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 1);
501 break;
502 case FB_VMODE_INTERLACED: /* Filter off */
503 ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 0);
504 break;
505 default:
506 IVTV_FB_DEBUG_WARN("ivtvfb_set_var - Invalid video mode\n");
507 }
508
509 /* Read the current osd info */
510 ivtv_fb_get_osd_coords(itv, &ivtv_osd);
511
512 /* Now set the OSD to the size we want */
513 ivtv_osd.pixel_stride = var->xres_virtual;
514 ivtv_osd.lines = var->yres_virtual;
515 ivtv_osd.x = 0;
516 ivtv_osd.y = 0;
517 ivtv_fb_set_osd_coords(itv, &ivtv_osd);
518
519 /* Can't seem to find the right API combo for this.
520 Use another function which does what we need through direct register access. */
521 ivtv_window.width = var->xres;
522 ivtv_window.height = var->yres;
523
524 /* Minimum margin cannot be 0, as X won't allow such a mode */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300525 if (!var->upper_margin) var->upper_margin++;
526 if (!var->left_margin) var->left_margin++;
Hans Verkuil32db7752007-07-20 09:29:43 -0300527 ivtv_window.top = var->upper_margin - 1;
528 ivtv_window.left = var->left_margin - 1;
529
530 ivtv_fb_set_display_window(itv, &ivtv_window);
531
532 /* Force update of yuv registers */
533 itv->yuv_info.yuv_forced_update = 1;
534
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300535 IVTV_FB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
536 var->xres, var->yres,
537 var->xres_virtual, var->yres_virtual,
538 var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300539
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300540 IVTV_FB_DEBUG_INFO("Display position: %d, %d\n",
541 var->left_margin, var->upper_margin);
Hans Verkuil32db7752007-07-20 09:29:43 -0300542
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300543 IVTV_FB_DEBUG_INFO("Display filter: %s\n",
544 (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
545 IVTV_FB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
Hans Verkuil32db7752007-07-20 09:29:43 -0300546
547 return 0;
548}
549
550static int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix)
551{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300552 struct osd_info *oi = itv->osd_info;
553
554 IVTV_FB_DEBUG_INFO("ivtvfb_get_fix\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300555 memset(fix, 0, sizeof(struct fb_fix_screeninfo));
556 strcpy(fix->id, "cx23415 TV out");
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300557 fix->smem_start = oi->video_pbase;
558 fix->smem_len = oi->video_buffer_size;
Hans Verkuil32db7752007-07-20 09:29:43 -0300559 fix->type = FB_TYPE_PACKED_PIXELS;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300560 fix->visual = (oi->bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
Hans Verkuil32db7752007-07-20 09:29:43 -0300561 fix->xpanstep = 1;
562 fix->ypanstep = 1;
563 fix->ywrapstep = 0;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300564 fix->line_length = oi->display_byte_stride;
Hans Verkuil32db7752007-07-20 09:29:43 -0300565 fix->accel = FB_ACCEL_NONE;
566 return 0;
567}
568
569/* Check the requested display mode, returning -EINVAL if we can't
570 handle it. */
571
572static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv)
573{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300574 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300575 int osd_height_limit = itv->is_50hz ? 576 : 480;
576
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300577 IVTV_FB_DEBUG_INFO("ivtvfb_check_var\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300578
579 /* Check the bits per pixel */
580 if (osd_compat) {
581 if (var->bits_per_pixel != 32) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300582 IVTV_FB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300583 return -EINVAL;
584 }
585 }
586
587 if (var->bits_per_pixel == 8 || var->bits_per_pixel == 32) {
588 var->transp.offset = 24;
589 var->transp.length = 8;
590 var->red.offset = 16;
591 var->red.length = 8;
592 var->green.offset = 8;
593 var->green.length = 8;
594 var->blue.offset = 0;
595 var->blue.length = 8;
596 }
597 else if (var->bits_per_pixel == 16) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300598 var->transp.offset = 0;
599 var->transp.length = 0;
600
Hans Verkuil32db7752007-07-20 09:29:43 -0300601 /* To find out the true mode, check green length */
602 switch (var->green.length) {
603 case 4:
Hans Verkuil32db7752007-07-20 09:29:43 -0300604 var->red.offset = 8;
605 var->red.length = 4;
606 var->green.offset = 4;
607 var->green.length = 4;
608 var->blue.offset = 0;
609 var->blue.length = 4;
610 break;
611 case 5:
Hans Verkuil32db7752007-07-20 09:29:43 -0300612 var->red.offset = 10;
613 var->red.length = 5;
614 var->green.offset = 5;
615 var->green.length = 5;
616 var->blue.offset = 0;
617 var->blue.length = 5;
618 break;
619 default:
Hans Verkuil32db7752007-07-20 09:29:43 -0300620 var->red.offset = 11;
621 var->red.length = 5;
622 var->green.offset = 5;
623 var->green.length = 6;
624 var->blue.offset = 0;
625 var->blue.length = 5;
626 break;
627 }
628 }
629 else {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300630 IVTV_FB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300631 return -EINVAL;
632 }
633
634 /* Check the resolution */
635 if (osd_compat) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300636 if (var->xres != oi->ivtvfb_defined.xres ||
637 var->yres != oi->ivtvfb_defined.yres ||
638 var->xres_virtual != oi->ivtvfb_defined.xres_virtual ||
639 var->yres_virtual != oi->ivtvfb_defined.yres_virtual) {
640 IVTV_FB_DEBUG_WARN("Invalid resolution: %dx%d (virtual %dx%d)\n",
641 var->xres, var->yres, var->xres_virtual, var->yres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300642 return -EINVAL;
643 }
644 }
645 else {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300646 if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit) {
647 IVTV_FB_DEBUG_WARN("Invalid resolution: %dx%d\n",
648 var->xres, var->yres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300649 return -EINVAL;
650 }
651
652 /* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */
653 if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) ||
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300654 var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8) > oi->video_buffer_size ||
Hans Verkuil32db7752007-07-20 09:29:43 -0300655 var->xres_virtual < var->xres ||
656 var->yres_virtual < var->yres) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300657 IVTV_FB_DEBUG_WARN("Invalid virtual resolution: %dx%d\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300658 var->xres_virtual, var->yres_virtual);
659 return -EINVAL;
660 }
661 }
662
663 /* Some extra checks if in 8 bit mode */
664 if (var->bits_per_pixel == 8) {
665 /* Width must be a multiple of 4 */
666 if (var->xres & 3) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300667 IVTV_FB_DEBUG_WARN("Invalid resolution for 8bpp: %d\n", var->xres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300668 return -EINVAL;
669 }
670 if (var->xres_virtual & 3) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300671 IVTV_FB_DEBUG_WARN("Invalid virtual resolution for 8bpp: %d)\n", var->xres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300672 return -EINVAL;
673 }
674 }
675 else if (var->bits_per_pixel == 16) {
676 /* Width must be a multiple of 2 */
677 if (var->xres & 1) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300678 IVTV_FB_DEBUG_WARN("Invalid resolution for 16bpp: %d\n", var->xres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300679 return -EINVAL;
680 }
681 if (var->xres_virtual & 1) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300682 IVTV_FB_DEBUG_WARN("Invalid virtual resolution for 16bpp: %d)\n", var->xres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300683 return -EINVAL;
684 }
685 }
686
687 /* Now check the offsets */
688 if (var->xoffset >= var->xres_virtual || var->yoffset >= var->yres_virtual) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300689 IVTV_FB_DEBUG_WARN("Invalid offset: %d (%d) %d (%d)\n",
690 var->xoffset, var->xres_virtual, var->yoffset, var->yres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300691 return -EINVAL;
692 }
693
694 /* Check pixel format */
695 if (var->nonstd > 1) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300696 IVTV_FB_DEBUG_WARN("Invalid nonstd % d\n", var->nonstd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300697 return -EINVAL;
698 }
699
700 /* Check video mode */
701 if (((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) &&
702 ((var->vmode & FB_VMODE_MASK) != FB_VMODE_INTERLACED)) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300703 IVTV_FB_DEBUG_WARN("Invalid video mode: %d\n", var->vmode & FB_VMODE_MASK);
Hans Verkuil32db7752007-07-20 09:29:43 -0300704 return -EINVAL;
705 }
706
707 /* Check the left & upper margins
708 If the margins are too large, just center the screen
709 (enforcing margins causes too many problems) */
710
711 if (var->left_margin + var->xres > IVTV_OSD_MAX_WIDTH + 1) {
712 var->left_margin = 1 + ((IVTV_OSD_MAX_WIDTH - var->xres) / 2);
713 }
714 if (var->upper_margin + var->yres > (itv->is_50hz ? 577 : 481)) {
715 var->upper_margin = 1 + (((itv->is_50hz ? 576 : 480) - var->yres) / 2);
716 }
717
718 /* Maintain overall 'size' for a constant refresh rate */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300719 var->right_margin = oi->hlimit - var->left_margin - var->xres;
720 var->lower_margin = oi->vlimit - var->upper_margin - var->yres;
Hans Verkuil32db7752007-07-20 09:29:43 -0300721
722 /* Fixed sync times */
723 var->hsync_len = 24;
724 var->vsync_len = 2;
725
726 /* Non-interlaced / interlaced mode is used to switch the OSD filter
727 on or off. Adjust the clock timings to maintain a constant
728 vertical refresh rate. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300729 var->pixclock = oi->pixclock;
Hans Verkuil32db7752007-07-20 09:29:43 -0300730 if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED)
731 var->pixclock /= 2;
732
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300733 IVTV_FB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
734 var->xres, var->yres,
735 var->xres_virtual, var->yres_virtual,
Hans Verkuil32db7752007-07-20 09:29:43 -0300736 var->bits_per_pixel);
737
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300738 IVTV_FB_DEBUG_INFO("Display position: %d, %d\n",
739 var->left_margin, var->upper_margin);
Hans Verkuil32db7752007-07-20 09:29:43 -0300740
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300741 IVTV_FB_DEBUG_INFO("Display filter: %s\n",
742 (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
743 IVTV_FB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
Hans Verkuil32db7752007-07-20 09:29:43 -0300744 return 0;
745}
746
747static int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
748{
749 struct ivtv *itv = (struct ivtv *) info->par;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300750 IVTV_FB_DEBUG_INFO("ivtvfb_check_var\n");
751 return _ivtvfb_check_var(var, itv);
Hans Verkuil32db7752007-07-20 09:29:43 -0300752}
753
754static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
755{
756 u32 osd_pan_index;
757 struct ivtv *itv = (struct ivtv *) info->par;
758
759 osd_pan_index = (var->xoffset + (var->yoffset * var->xres_virtual))*var->bits_per_pixel/8;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300760 write_reg(osd_pan_index, 0x02A0C);
Hans Verkuil32db7752007-07-20 09:29:43 -0300761
762 /* Pass this info back the yuv handler */
763 itv->yuv_info.osd_x_pan = var->xoffset;
764 itv->yuv_info.osd_y_pan = var->yoffset;
765 /* Force update of yuv registers */
766 itv->yuv_info.yuv_forced_update = 1;
767 return 0;
768}
769
770static int ivtvfb_set_par(struct fb_info *info)
771{
772 int rc = 0;
773 struct ivtv *itv = (struct ivtv *) info->par;
774
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300775 IVTV_FB_DEBUG_INFO("ivtvfb_set_par\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300776
777 rc = ivtvfb_set_var(itv, &info->var);
778 ivtvfb_pan_display(&info->var, info);
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300779 ivtvfb_get_fix(itv, &info->fix);
Hans Verkuil32db7752007-07-20 09:29:43 -0300780 return rc;
781}
782
783static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green,
784 unsigned blue, unsigned transp,
785 struct fb_info *info)
786{
787 u32 color, *palette;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300788 struct ivtv *itv = (struct ivtv *)info->par;
Hans Verkuil32db7752007-07-20 09:29:43 -0300789
790 if (regno >= info->cmap.len)
791 return -EINVAL;
792
793 color = ((transp & 0xFF00) << 16) |((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8);
794 if (info->var.bits_per_pixel <= 8) {
795 write_reg(regno, 0x02a30);
796 write_reg(color, 0x02a34);
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300797 return 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300798 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300799 if (regno >= 16)
800 return -EINVAL;
Hans Verkuil32db7752007-07-20 09:29:43 -0300801
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300802 palette = info->pseudo_palette;
803 if (info->var.bits_per_pixel == 16) {
804 switch (info->var.green.length) {
805 case 4:
806 color = ((red & 0xf000) >> 4) |
807 ((green & 0xf000) >> 8) |
808 ((blue & 0xf000) >> 12);
809 break;
810 case 5:
811 color = ((red & 0xf800) >> 1) |
812 ((green & 0xf800) >> 6) |
813 ((blue & 0xf800) >> 11);
814 break;
815 case 6:
816 color = (red & 0xf800 ) |
817 ((green & 0xfc00) >> 5) |
818 ((blue & 0xf800) >> 11);
819 break;
Hans Verkuil32db7752007-07-20 09:29:43 -0300820 }
Hans Verkuil32db7752007-07-20 09:29:43 -0300821 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300822 palette[regno] = color;
Hans Verkuil32db7752007-07-20 09:29:43 -0300823 return 0;
824}
825
826/* We don't really support blanking. All this does is enable or
827 disable the OSD. */
828static int ivtvfb_blank(int blank_mode, struct fb_info *info)
829{
830 struct ivtv *itv = (struct ivtv *)info->par;
831
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300832 IVTV_FB_DEBUG_INFO("Set blanking mode : %d\n", blank_mode);
Hans Verkuil32db7752007-07-20 09:29:43 -0300833 switch (blank_mode) {
834 case FB_BLANK_UNBLANK:
835 ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1);
836 break;
837 case FB_BLANK_NORMAL:
838 case FB_BLANK_HSYNC_SUSPEND:
839 case FB_BLANK_VSYNC_SUSPEND:
840 case FB_BLANK_POWERDOWN:
841 ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
842 break;
843 }
844 return 0;
845}
846
847static struct fb_ops ivtvfb_ops = {
848 .owner = THIS_MODULE,
849 .fb_check_var = ivtvfb_check_var,
850 .fb_set_par = ivtvfb_set_par,
851 .fb_setcolreg = ivtvfb_setcolreg,
852 .fb_fillrect = cfb_fillrect,
853 .fb_copyarea = cfb_copyarea,
854 .fb_imageblit = cfb_imageblit,
855 .fb_cursor = NULL,
856 .fb_ioctl = ivtvfb_ioctl,
857 .fb_pan_display = ivtvfb_pan_display,
858 .fb_blank = ivtvfb_blank,
859};
860
861/* Initialization */
862
863
864/* Setup our initial video mode */
865static int ivtvfb_init_vidmode(struct ivtv *itv)
866{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300867 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300868 struct v4l2_rect start_window;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300869 int max_height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300870
871 /* Set base references for mode calcs. */
872 if (itv->is_50hz) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300873 oi->pixclock = 84316;
874 oi->hlimit = 776;
875 oi->vlimit = 591;
Hans Verkuil32db7752007-07-20 09:29:43 -0300876 }
877 else {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300878 oi->pixclock = 83926;
879 oi->hlimit = 776;
880 oi->vlimit = 495;
Hans Verkuil32db7752007-07-20 09:29:43 -0300881 }
882
883 /* Color mode */
884
885 if (osd_compat) osd_depth = 32;
886 if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32) osd_depth = 8;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300887 oi->bits_per_pixel = osd_depth;
888 oi->bytes_per_pixel = oi->bits_per_pixel / 8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300889
890 /* Horizontal size & position */
891
892 if (osd_xres > 720) osd_xres = 720;
893
894 /* Must be a multiple of 4 for 8bpp & 2 for 16bpp */
895 if (osd_depth == 8)
896 osd_xres &= ~3;
897 else if (osd_depth == 16)
898 osd_xres &= ~1;
899
900 if (osd_xres)
901 start_window.width = osd_xres;
902 else
903 start_window.width = osd_compat ? 720: 640;
904
905 /* Check horizontal start (osd_left). */
906 if (osd_left && osd_left + start_window.width > 721) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300907 IVTV_FB_ERR("Invalid osd_left - assuming default\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300908 osd_left = 0;
909 }
910
911 /* Hardware coords start at 0, user coords start at 1. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300912 osd_left--;
Hans Verkuil32db7752007-07-20 09:29:43 -0300913
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300914 start_window.left = osd_left >= 0 ? osd_left : ((IVTV_OSD_MAX_WIDTH - start_window.width) / 2);
Hans Verkuil32db7752007-07-20 09:29:43 -0300915
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300916 oi->display_byte_stride =
917 start_window.width * oi->bytes_per_pixel;
Hans Verkuil32db7752007-07-20 09:29:43 -0300918
919 /* Vertical size & position */
920
921 max_height = itv->is_50hz ? 576 : 480;
922
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300923 if (osd_yres > max_height)
924 osd_yres = max_height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300925
926 if (osd_yres)
927 start_window.height = osd_yres;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300928 else
929 start_window.height = osd_compat ? max_height : (itv->is_50hz ? 480 : 400);
Hans Verkuil32db7752007-07-20 09:29:43 -0300930
931 /* Check vertical start (osd_upper). */
932 if (osd_upper + start_window.height > max_height + 1) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300933 IVTV_FB_ERR("Invalid osd_upper - assuming default\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300934 osd_upper = 0;
935 }
936
937 /* Hardware coords start at 0, user coords start at 1. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300938 osd_upper--;
Hans Verkuil32db7752007-07-20 09:29:43 -0300939
940 start_window.top = osd_upper >= 0 ? osd_upper : ((max_height - start_window.height) / 2);
941
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300942 oi->display_width = start_window.width;
943 oi->display_height = start_window.height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300944
945 /* Generate a valid fb_var_screeninfo */
946
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300947 oi->ivtvfb_defined.xres = oi->display_width;
948 oi->ivtvfb_defined.yres = oi->display_height;
949 oi->ivtvfb_defined.xres_virtual = oi->display_width;
950 oi->ivtvfb_defined.yres_virtual = oi->display_height;
951 oi->ivtvfb_defined.bits_per_pixel = oi->bits_per_pixel;
952 oi->ivtvfb_defined.vmode = (osd_laced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED);
953 oi->ivtvfb_defined.left_margin = start_window.left + 1;
954 oi->ivtvfb_defined.upper_margin = start_window.top + 1;
955 oi->ivtvfb_defined.accel_flags = FB_ACCEL_NONE;
956 oi->ivtvfb_defined.nonstd = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300957
958 /* We've filled in the most data, let the usual mode check
959 routine fill in the rest. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300960 _ivtvfb_check_var(&oi->ivtvfb_defined, itv);
Hans Verkuil32db7752007-07-20 09:29:43 -0300961
962 /* Generate valid fb_fix_screeninfo */
963
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300964 ivtvfb_get_fix(itv, &oi->ivtvfb_fix);
Hans Verkuil32db7752007-07-20 09:29:43 -0300965
966 /* Generate valid fb_info */
967
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300968 oi->ivtvfb_info.node = -1;
969 oi->ivtvfb_info.flags = FBINFO_FLAG_DEFAULT;
970 oi->ivtvfb_info.fbops = &ivtvfb_ops;
971 oi->ivtvfb_info.par = itv;
972 oi->ivtvfb_info.var = oi->ivtvfb_defined;
973 oi->ivtvfb_info.fix = oi->ivtvfb_fix;
974 oi->ivtvfb_info.screen_base = (u8 __iomem *)oi->video_vbase;
975 oi->ivtvfb_info.fbops = &ivtvfb_ops;
Hans Verkuil32db7752007-07-20 09:29:43 -0300976
977 /* Supply some monitor specs. Bogus values will do for now */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300978 oi->ivtvfb_info.monspecs.hfmin = 8000;
979 oi->ivtvfb_info.monspecs.hfmax = 70000;
980 oi->ivtvfb_info.monspecs.vfmin = 10;
981 oi->ivtvfb_info.monspecs.vfmax = 100;
Hans Verkuil32db7752007-07-20 09:29:43 -0300982
983 /* Allocate color map */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300984 if (fb_alloc_cmap(&oi->ivtvfb_info.cmap, 256, 1)) {
985 IVTV_FB_ERR("abort, unable to alloc cmap\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300986 return -ENOMEM;
987 }
988
989 /* Allocate the pseudo palette */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300990 oi->ivtvfb_info.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
Hans Verkuil32db7752007-07-20 09:29:43 -0300991
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300992 if (!oi->ivtvfb_info.pseudo_palette) {
993 IVTV_FB_ERR("abort, unable to alloc pseudo pallete\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300994 return -ENOMEM;
995 }
996
997 return 0;
998}
999
1000/* Find OSD buffer base & size. Add to mtrr. Zero osd buffer. */
1001
1002static int ivtvfb_init_io(struct ivtv *itv)
1003{
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001004 struct osd_info *oi = itv->osd_info;
1005
1006 ivtv_fb_get_framebuffer(itv, &oi->video_rbase, &oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -03001007
1008 /* The osd buffer size depends on the number of video buffers allocated
1009 on the PVR350 itself. For now we'll hardcode the smallest osd buffer
1010 size to prevent any overlap. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001011 oi->video_buffer_size = 1704960;
Hans Verkuil32db7752007-07-20 09:29:43 -03001012
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001013 oi->video_pbase = itv->base_addr + IVTV_DECODER_OFFSET + oi->video_rbase;
1014 oi->video_vbase = itv->dec_mem + oi->video_rbase;
Hans Verkuil32db7752007-07-20 09:29:43 -03001015
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001016 if (!oi->video_vbase) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001017 IVTV_FB_ERR("abort, video memory 0x%x @ 0x%lx isn't mapped!\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001018 oi->video_buffer_size, oi->video_pbase);
Hans Verkuil32db7752007-07-20 09:29:43 -03001019 return -EIO;
1020 }
1021
1022 IVTV_FB_INFO("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001023 oi->video_pbase, oi->video_vbase,
1024 oi->video_buffer_size / 1024);
Hans Verkuil32db7752007-07-20 09:29:43 -03001025
1026#ifdef CONFIG_MTRR
1027 {
1028 /* Find the largest power of two that maps the whole buffer */
1029 int size_shift = 31;
1030
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001031 while (!(oi->video_buffer_size & (1 << size_shift))) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001032 size_shift--;
1033 }
1034 size_shift++;
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001035 oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1);
1036 oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size;
1037 oi->fb_end_aligned_physaddr += (1 << size_shift) - 1;
1038 oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1);
1039 if (mtrr_add(oi->fb_start_aligned_physaddr,
1040 oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr,
Hans Verkuil32db7752007-07-20 09:29:43 -03001041 MTRR_TYPE_WRCOMB, 1) < 0) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001042 IVTV_FB_WARN("cannot use mttr\n");
1043 oi->fb_start_aligned_physaddr = 0;
1044 oi->fb_end_aligned_physaddr = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -03001045 }
1046 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001047#endif
Hans Verkuil32db7752007-07-20 09:29:43 -03001048
1049 /* Blank the entire osd. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001050 memset_io(oi->video_vbase, 0, oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -03001051
1052 return 0;
1053}
1054
1055/* Release any memory we've grabbed & remove mtrr entry */
1056static void ivtvfb_release_buffers (struct ivtv *itv)
1057{
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001058 struct osd_info *oi = itv->osd_info;
1059
Hans Verkuil32db7752007-07-20 09:29:43 -03001060 /* Release cmap */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001061 if (oi->ivtvfb_info.cmap.len);
1062 fb_dealloc_cmap(&oi->ivtvfb_info.cmap);
Hans Verkuil32db7752007-07-20 09:29:43 -03001063
1064 /* Release pseudo palette */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001065 if (oi->ivtvfb_info.pseudo_palette)
1066 kfree(oi->ivtvfb_info.pseudo_palette);
Hans Verkuil32db7752007-07-20 09:29:43 -03001067
1068#ifdef CONFIG_MTRR
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001069 if (oi->fb_end_aligned_physaddr) {
1070 mtrr_del(-1, oi->fb_start_aligned_physaddr,
1071 oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr);
1072 }
1073#endif
Hans Verkuil32db7752007-07-20 09:29:43 -03001074
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001075 kfree(oi);
Hans Verkuil32db7752007-07-20 09:29:43 -03001076 itv->osd_info = NULL;
1077}
1078
1079/* Initialize the specified card */
1080
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001081static int ivtvfb_init_card(struct ivtv *itv)
Hans Verkuil32db7752007-07-20 09:29:43 -03001082{
1083 int rc;
1084
1085 if (itv->osd_info) {
1086 IVTV_FB_ERR("Card %d already initialised\n", ivtv_fb_card_id);
1087 return -EBUSY;
1088 }
1089
1090 itv->osd_info = kzalloc(sizeof(struct osd_info), GFP_ATOMIC);
1091 if (itv->osd_info == 0) {
1092 IVTV_FB_ERR("Failed to allocate memory for osd_info\n");
1093 return -ENOMEM;
1094 }
1095
1096 /* Find & setup the OSD buffer */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001097 if ((rc = ivtvfb_init_io(itv)))
Hans Verkuil32db7752007-07-20 09:29:43 -03001098 return rc;
1099
1100 /* Set the startup video mode information */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001101 if ((rc = ivtvfb_init_vidmode(itv))) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001102 ivtvfb_release_buffers(itv);
1103 return rc;
1104 }
1105
1106 /* Register the framebuffer */
1107 if (register_framebuffer(&itv->osd_info->ivtvfb_info) < 0) {
1108 ivtvfb_release_buffers(itv);
1109 return -EINVAL;
1110 }
1111
1112 itv->osd_video_pbase = itv->osd_info->video_pbase;
1113
1114 /* Set the card to the requested mode */
1115 ivtvfb_set_par(&itv->osd_info->ivtvfb_info);
1116
1117 /* Set color 0 to black */
1118 write_reg(0, 0x02a30);
1119 write_reg(0, 0x02a34);
1120
1121 /* Enable the osd */
1122 ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info);
1123
1124 /* Note if we're running in compatibility mode */
1125 if (osd_compat)
1126 IVTV_FB_INFO("Running in compatibility mode. Display resize & mode change disabled\n");
1127
1128 /* Allocate DMA */
1129 ivtv_udma_alloc(itv);
1130 return 0;
1131
1132}
1133
1134static int __init ivtvfb_init(void)
1135{
1136 struct ivtv *itv;
1137 int i, registered = 0;
1138
1139 if (ivtv_fb_card_id < -1 || ivtv_fb_card_id >= IVTV_MAX_CARDS) {
1140 printk(KERN_ERR "ivtv-fb: ivtv_fb_card_id parameter is out of range (valid range: -1 - %d)\n",
1141 IVTV_MAX_CARDS - 1);
1142 return -EINVAL;
1143 }
1144
1145 /* Locate & initialise all cards supporting an OSD. */
1146 for (i = 0; i < ivtv_cards_active; i++) {
1147 if (ivtv_fb_card_id != -1 && i != ivtv_fb_card_id)
1148 continue;
1149 itv = ivtv_cards[i];
1150 if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
1151 if (ivtvfb_init_card(itv) == 0) {
1152 IVTV_FB_INFO("Framebuffer registered on ivtv card id %d\n", i);
1153 registered++;
1154 }
1155 }
1156 }
1157 if (!registered) {
1158 printk(KERN_ERR "ivtv-fb: no cards found");
1159 return -ENODEV;
1160 }
1161 return 0;
1162}
1163
1164static void ivtvfb_cleanup(void)
1165{
1166 struct ivtv *itv;
1167 int i;
1168
1169 printk(KERN_INFO "ivtv-fb: Unloading framebuffer module\n");
1170
1171 for (i = 0; i < ivtv_cards_active; i++) {
1172 itv = ivtv_cards[i];
1173 if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) && itv->osd_info) {
1174 IVTV_FB_DEBUG_INFO("Unregister framebuffer %d\n", i);
1175 ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info);
1176 unregister_framebuffer(&itv->osd_info->ivtvfb_info);
1177 ivtvfb_release_buffers(itv);
1178 itv->osd_video_pbase = 0;
1179 }
1180 }
1181}
1182
1183module_init(ivtvfb_init);
1184module_exit(ivtvfb_cleanup);