blob: b8ad249a0b171f855cc9b5413ad415b072526e3f [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
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300188 /* Current osd mode */
189 int osd_mode;
190
Hans Verkuil32db7752007-07-20 09:29:43 -0300191 /* Store the buffer offset */
192 int set_osd_coords_x;
193 int set_osd_coords_y;
194
195 /* Current dimensions (NOT VISIBLE SIZE!) */
196 int display_width;
197 int display_height;
198 int display_byte_stride;
199
200 /* Current bits per pixel */
201 int bits_per_pixel;
202 int bytes_per_pixel;
203
204 /* Frame buffer stuff */
205 struct fb_info ivtvfb_info;
206 struct fb_var_screeninfo ivtvfb_defined;
207 struct fb_fix_screeninfo ivtvfb_fix;
208};
209
210struct ivtv_osd_coords {
211 unsigned long offset;
212 unsigned long max_offset;
213 int pixel_stride;
214 int lines;
215 int x;
216 int y;
217};
218
219/* --------------------------------------------------------------------- */
220
221/* ivtv API calls for framebuffer related support */
222
223static int ivtv_fb_get_framebuffer(struct ivtv *itv, u32 *fbbase,
224 u32 *fblength)
225{
226 u32 data[CX2341X_MBOX_MAX_DATA];
227 int rc;
228
229 rc = ivtv_vapi_result(itv, data, CX2341X_OSD_GET_FRAMEBUFFER, 0);
230 *fbbase = data[0];
231 *fblength = data[1];
232 return rc;
233}
234
235static int ivtv_fb_get_osd_coords(struct ivtv *itv,
236 struct ivtv_osd_coords *osd)
237{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300238 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300239 u32 data[CX2341X_MBOX_MAX_DATA];
240
241 ivtv_vapi_result(itv, data, CX2341X_OSD_GET_OSD_COORDS, 0);
242
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300243 osd->offset = data[0] - oi->video_rbase;
244 osd->max_offset = oi->display_width * oi->display_height * 4;
Hans Verkuil32db7752007-07-20 09:29:43 -0300245 osd->pixel_stride = data[1];
246 osd->lines = data[2];
247 osd->x = data[3];
248 osd->y = data[4];
249 return 0;
250}
251
252static int ivtv_fb_set_osd_coords(struct ivtv *itv, const struct ivtv_osd_coords *osd)
253{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300254 struct osd_info *oi = itv->osd_info;
255
256 oi->display_width = osd->pixel_stride;
257 oi->display_byte_stride = osd->pixel_stride * oi->bytes_per_pixel;
258 oi->set_osd_coords_x += osd->x;
259 oi->set_osd_coords_y = osd->y;
Hans Verkuil32db7752007-07-20 09:29:43 -0300260
261 return ivtv_vapi(itv, CX2341X_OSD_SET_OSD_COORDS, 5,
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300262 osd->offset + oi->video_rbase,
Hans Verkuil32db7752007-07-20 09:29:43 -0300263 osd->pixel_stride,
264 osd->lines, osd->x, osd->y);
265}
266
267static int ivtv_fb_set_display_window(struct ivtv *itv, struct v4l2_rect *ivtv_window)
268{
Hans Verkuil32db7752007-07-20 09:29:43 -0300269 int osd_height_limit = itv->is_50hz ? 576 : 480;
270
271 /* Only fail if resolution too high, otherwise fudge the start coords. */
272 if ((ivtv_window->height > osd_height_limit) || (ivtv_window->width > IVTV_OSD_MAX_WIDTH))
273 return -EINVAL;
274
275 /* Ensure we don't exceed display limits */
276 if (ivtv_window->top + ivtv_window->height > osd_height_limit) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300277 IVTV_FB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid height setting (%d, %d)\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300278 ivtv_window->top, ivtv_window->height);
279 ivtv_window->top = osd_height_limit - ivtv_window->height;
280 }
281
282 if (ivtv_window->left + ivtv_window->width > IVTV_OSD_MAX_WIDTH) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300283 IVTV_FB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid width setting (%d, %d)\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300284 ivtv_window->left, ivtv_window->width);
285 ivtv_window->left = IVTV_OSD_MAX_WIDTH - ivtv_window->width;
286 }
287
288 /* Set the OSD origin */
289 write_reg((ivtv_window->top << 16) | ivtv_window->left, 0x02a04);
290
291 /* How much to display */
292 write_reg(((ivtv_window->top+ivtv_window->height) << 16) | (ivtv_window->left+ivtv_window->width), 0x02a08);
293
294 /* Pass this info back the yuv handler */
295 itv->yuv_info.osd_vis_w = ivtv_window->width;
296 itv->yuv_info.osd_vis_h = ivtv_window->height;
297 itv->yuv_info.osd_x_offset = ivtv_window->left;
298 itv->yuv_info.osd_y_offset = ivtv_window->top;
299
300 return 0;
301}
302
303static int ivtv_fb_prep_dec_dma_to_device(struct ivtv *itv,
304 unsigned long ivtv_dest_addr, void __user *userbuf,
305 int size_in_bytes)
306{
307 DEFINE_WAIT(wait);
308 int ret = 0;
309 int got_sig = 0;
310
311 mutex_lock(&itv->udma.lock);
312 /* Map User DMA */
313 if (ivtv_udma_setup(itv, ivtv_dest_addr, userbuf, size_in_bytes) <= 0) {
314 mutex_unlock(&itv->udma.lock);
315 IVTV_FB_WARN("ivtvfb_prep_dec_dma_to_device, "
316 "Error with get_user_pages: %d bytes, %d pages returned\n",
317 size_in_bytes, itv->udma.page_count);
318
319 /* get_user_pages must have failed completely */
320 return -EIO;
321 }
322
323 IVTV_FB_DEBUG_INFO("ivtvfb_prep_dec_dma_to_device, %d bytes, %d pages\n",
324 size_in_bytes, itv->udma.page_count);
325
326 ivtv_udma_prepare(itv);
327 prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
328 /* if no UDMA is pending and no UDMA is in progress, then the DMA
329 is finished */
330 while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) {
331 /* don't interrupt if the DMA is in progress but break off
332 a still pending DMA. */
333 got_sig = signal_pending(current);
334 if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))
335 break;
336 got_sig = 0;
337 schedule();
338 }
339 finish_wait(&itv->dma_waitq, &wait);
340
341 /* Unmap Last DMA Xfer */
342 ivtv_udma_unmap(itv);
343 mutex_unlock(&itv->udma.lock);
344 if (got_sig) {
345 IVTV_DEBUG_INFO("User stopped OSD\n");
346 return -EINTR;
347 }
348
349 return ret;
350}
351
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300352static int ivtv_fb_prep_frame(struct ivtv *itv, int cmd, void __user *source,
353 unsigned long dest_offset, int count)
Hans Verkuil32db7752007-07-20 09:29:43 -0300354{
355 DEFINE_WAIT(wait);
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300356 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300357
358 /* Nothing to do */
359 if (count == 0) {
360 IVTV_FB_DEBUG_WARN("ivtv_fb_prep_frame: Nothing to do. count = 0\n");
361 return -EINVAL;
362 }
363
364 /* Check Total FB Size */
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300365 if ((dest_offset + count) > oi->video_buffer_size) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300366 IVTV_FB_WARN("ivtv_fb_prep_frame: Overflowing the framebuffer %ld, only %d available\n",
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300367 dest_offset + count, oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -0300368 return -E2BIG;
369 }
370
371 /* Not fatal, but will have undesirable results */
372 if ((unsigned long)source & 3)
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300373 IVTV_FB_WARN("ivtv_fb_prep_frame: Source address not 32 bit aligned (0x%08lx)\n",
374 (unsigned long)source);
Hans Verkuil32db7752007-07-20 09:29:43 -0300375
376 if (dest_offset & 3)
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300377 IVTV_FB_WARN("ivtv_fb_prep_frame: Dest offset not 32 bit aligned (%ld)\n", dest_offset);
Hans Verkuil32db7752007-07-20 09:29:43 -0300378
379 if (count & 3)
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300380 IVTV_FB_WARN("ivtv_fb_prep_frame: Count not a multiple of 4 (%d)\n", count);
Hans Verkuil32db7752007-07-20 09:29:43 -0300381
382 /* Check Source */
383 if (!access_ok(VERIFY_READ, source + dest_offset, count)) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300384 IVTV_FB_WARN("Invalid userspace pointer 0x%08lx\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300385 (unsigned long)source);
386
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300387 IVTV_FB_DEBUG_WARN("access_ok() failed for offset 0x%08lx source 0x%08lx count %d\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300388 dest_offset, (unsigned long)source,
389 count);
390 return -EINVAL;
391 }
392
393 /* OSD Address to send DMA to */
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300394 dest_offset += IVTV_DEC_MEM_START + oi->video_rbase;
Hans Verkuil32db7752007-07-20 09:29:43 -0300395
396 /* Fill Buffers */
397 return ivtv_fb_prep_dec_dma_to_device(itv, dest_offset, source, count);
398}
399
400static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
401{
402 DEFINE_WAIT(wait);
403 struct ivtv *itv = (struct ivtv *)info->par;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300404 int rc = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300405
406 switch (cmd) {
Hans Verkuil32db7752007-07-20 09:29:43 -0300407 case FBIOGET_VBLANK: {
408 struct fb_vblank vblank;
409 u32 trace;
410
411 vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT |
412 FB_VBLANK_HAVE_VSYNC;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300413 trace = read_reg(0x028c0) >> 16;
Hans Verkuil32db7752007-07-20 09:29:43 -0300414 if (itv->is_50hz && trace > 312) trace -= 312;
415 else if (itv->is_60hz && trace > 262) trace -= 262;
416 if (trace == 1) vblank.flags |= FB_VBLANK_VSYNCING;
417 vblank.count = itv->lastVsyncFrame;
418 vblank.vcount = trace;
419 vblank.hcount = 0;
420 if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank)))
421 return -EFAULT;
422 return 0;
423 }
424
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300425 case FBIO_WAITFORVSYNC:
Hans Verkuil32db7752007-07-20 09:29:43 -0300426 prepare_to_wait(&itv->vsync_waitq, &wait, TASK_INTERRUPTIBLE);
Hans Verkuil84149a02007-07-20 18:17:18 -0300427 if (!schedule_timeout(msecs_to_jiffies(50))) rc = -ETIMEDOUT;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300428 finish_wait(&itv->vsync_waitq, &wait);
Hans Verkuil32db7752007-07-20 09:29:43 -0300429 return rc;
Hans Verkuil32db7752007-07-20 09:29:43 -0300430
Hans Verkuild715e762007-07-20 10:30:32 -0300431 case IVTVFB_IOC_DMA_FRAME: {
432 struct ivtvfb_dma_frame args;
Hans Verkuil32db7752007-07-20 09:29:43 -0300433
Hans Verkuild715e762007-07-20 10:30:32 -0300434 IVTV_FB_DEBUG_INFO("IVTVFB_IOC_DMA_FRAME\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300435 if (copy_from_user(&args, (void __user *)arg, sizeof(args)))
436 return -EFAULT;
437
438 return ivtv_fb_prep_frame(itv, cmd, args.source, args.dest_offset, args.count);
439 }
440
441 default:
Hans Verkuilc6f95d12007-07-22 15:44:41 -0300442 IVTV_FB_DEBUG_INFO("Unknown ioctl %08x\n", cmd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300443 return -EINVAL;
444 }
445 return 0;
446}
447
448/* Framebuffer device handling */
449
450static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var)
451{
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300452 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300453 struct ivtv_osd_coords ivtv_osd;
454 struct v4l2_rect ivtv_window;
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300455 int osd_mode = -1;
Hans Verkuil32db7752007-07-20 09:29:43 -0300456
457 IVTV_FB_DEBUG_INFO("ivtvfb_set_var\n");
458
459 /* Select color space */
460 if (var->nonstd) /* YUV */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300461 write_reg(read_reg(0x02a00) | 0x0002000, 0x02a00);
Hans Verkuil32db7752007-07-20 09:29:43 -0300462 else /* RGB */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300463 write_reg(read_reg(0x02a00) & ~0x0002000, 0x02a00);
Hans Verkuil32db7752007-07-20 09:29:43 -0300464
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300465 /* Set the color mode */
Hans Verkuil32db7752007-07-20 09:29:43 -0300466 switch (var->bits_per_pixel) {
467 case 8:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300468 osd_mode = IVTV_OSD_BPP_8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300469 break;
470 case 32:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300471 osd_mode = IVTV_OSD_BPP_32;
Hans Verkuil32db7752007-07-20 09:29:43 -0300472 break;
473 case 16:
474 switch (var->green.length) {
475 case 4:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300476 osd_mode = IVTV_OSD_BPP_16_444;
Hans Verkuil32db7752007-07-20 09:29:43 -0300477 break;
478 case 5:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300479 osd_mode = IVTV_OSD_BPP_16_555;
Hans Verkuil32db7752007-07-20 09:29:43 -0300480 break;
481 case 6:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300482 osd_mode = IVTV_OSD_BPP_16_565;
Hans Verkuil32db7752007-07-20 09:29:43 -0300483 break;
484 default:
485 IVTV_FB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
486 }
487 break;
488 default:
489 IVTV_FB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
490 }
491
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300492 /* Change osd mode if needed.
493 Although rare, things can go wrong. The extra mode
494 change seems to help... */
495 if (osd_mode != -1 && osd_mode != oi->osd_mode) {
496 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0);
497 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, osd_mode);
498 oi->osd_mode = osd_mode;
499 }
500
501 oi->bits_per_pixel = var->bits_per_pixel;
502 oi->bytes_per_pixel = var->bits_per_pixel / 8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300503
504 /* Set the flicker filter */
505 switch (var->vmode & FB_VMODE_MASK) {
506 case FB_VMODE_NONINTERLACED: /* Filter on */
507 ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 1);
508 break;
509 case FB_VMODE_INTERLACED: /* Filter off */
510 ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 0);
511 break;
512 default:
513 IVTV_FB_DEBUG_WARN("ivtvfb_set_var - Invalid video mode\n");
514 }
515
516 /* Read the current osd info */
517 ivtv_fb_get_osd_coords(itv, &ivtv_osd);
518
519 /* Now set the OSD to the size we want */
520 ivtv_osd.pixel_stride = var->xres_virtual;
521 ivtv_osd.lines = var->yres_virtual;
522 ivtv_osd.x = 0;
523 ivtv_osd.y = 0;
524 ivtv_fb_set_osd_coords(itv, &ivtv_osd);
525
526 /* Can't seem to find the right API combo for this.
527 Use another function which does what we need through direct register access. */
528 ivtv_window.width = var->xres;
529 ivtv_window.height = var->yres;
530
531 /* Minimum margin cannot be 0, as X won't allow such a mode */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300532 if (!var->upper_margin) var->upper_margin++;
533 if (!var->left_margin) var->left_margin++;
Hans Verkuil32db7752007-07-20 09:29:43 -0300534 ivtv_window.top = var->upper_margin - 1;
535 ivtv_window.left = var->left_margin - 1;
536
537 ivtv_fb_set_display_window(itv, &ivtv_window);
538
539 /* Force update of yuv registers */
540 itv->yuv_info.yuv_forced_update = 1;
541
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300542 IVTV_FB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
543 var->xres, var->yres,
544 var->xres_virtual, var->yres_virtual,
545 var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300546
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300547 IVTV_FB_DEBUG_INFO("Display position: %d, %d\n",
548 var->left_margin, var->upper_margin);
Hans Verkuil32db7752007-07-20 09:29:43 -0300549
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300550 IVTV_FB_DEBUG_INFO("Display filter: %s\n",
551 (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
552 IVTV_FB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
Hans Verkuil32db7752007-07-20 09:29:43 -0300553
554 return 0;
555}
556
557static int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix)
558{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300559 struct osd_info *oi = itv->osd_info;
560
561 IVTV_FB_DEBUG_INFO("ivtvfb_get_fix\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300562 memset(fix, 0, sizeof(struct fb_fix_screeninfo));
563 strcpy(fix->id, "cx23415 TV out");
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300564 fix->smem_start = oi->video_pbase;
565 fix->smem_len = oi->video_buffer_size;
Hans Verkuil32db7752007-07-20 09:29:43 -0300566 fix->type = FB_TYPE_PACKED_PIXELS;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300567 fix->visual = (oi->bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
Hans Verkuil32db7752007-07-20 09:29:43 -0300568 fix->xpanstep = 1;
569 fix->ypanstep = 1;
570 fix->ywrapstep = 0;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300571 fix->line_length = oi->display_byte_stride;
Hans Verkuil32db7752007-07-20 09:29:43 -0300572 fix->accel = FB_ACCEL_NONE;
573 return 0;
574}
575
576/* Check the requested display mode, returning -EINVAL if we can't
577 handle it. */
578
579static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv)
580{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300581 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300582 int osd_height_limit = itv->is_50hz ? 576 : 480;
583
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300584 IVTV_FB_DEBUG_INFO("ivtvfb_check_var\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300585
586 /* Check the bits per pixel */
587 if (osd_compat) {
588 if (var->bits_per_pixel != 32) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300589 IVTV_FB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300590 return -EINVAL;
591 }
592 }
593
594 if (var->bits_per_pixel == 8 || var->bits_per_pixel == 32) {
595 var->transp.offset = 24;
596 var->transp.length = 8;
597 var->red.offset = 16;
598 var->red.length = 8;
599 var->green.offset = 8;
600 var->green.length = 8;
601 var->blue.offset = 0;
602 var->blue.length = 8;
603 }
604 else if (var->bits_per_pixel == 16) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300605 var->transp.offset = 0;
606 var->transp.length = 0;
607
Hans Verkuil32db7752007-07-20 09:29:43 -0300608 /* To find out the true mode, check green length */
609 switch (var->green.length) {
610 case 4:
Hans Verkuil32db7752007-07-20 09:29:43 -0300611 var->red.offset = 8;
612 var->red.length = 4;
613 var->green.offset = 4;
614 var->green.length = 4;
615 var->blue.offset = 0;
616 var->blue.length = 4;
617 break;
618 case 5:
Hans Verkuil32db7752007-07-20 09:29:43 -0300619 var->red.offset = 10;
620 var->red.length = 5;
621 var->green.offset = 5;
622 var->green.length = 5;
623 var->blue.offset = 0;
624 var->blue.length = 5;
625 break;
626 default:
Hans Verkuil32db7752007-07-20 09:29:43 -0300627 var->red.offset = 11;
628 var->red.length = 5;
629 var->green.offset = 5;
630 var->green.length = 6;
631 var->blue.offset = 0;
632 var->blue.length = 5;
633 break;
634 }
635 }
636 else {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300637 IVTV_FB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300638 return -EINVAL;
639 }
640
641 /* Check the resolution */
642 if (osd_compat) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300643 if (var->xres != oi->ivtvfb_defined.xres ||
644 var->yres != oi->ivtvfb_defined.yres ||
645 var->xres_virtual != oi->ivtvfb_defined.xres_virtual ||
646 var->yres_virtual != oi->ivtvfb_defined.yres_virtual) {
647 IVTV_FB_DEBUG_WARN("Invalid resolution: %dx%d (virtual %dx%d)\n",
648 var->xres, var->yres, var->xres_virtual, var->yres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300649 return -EINVAL;
650 }
651 }
652 else {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300653 if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit) {
654 IVTV_FB_DEBUG_WARN("Invalid resolution: %dx%d\n",
655 var->xres, var->yres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300656 return -EINVAL;
657 }
658
659 /* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */
660 if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) ||
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300661 var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8) > oi->video_buffer_size ||
Hans Verkuil32db7752007-07-20 09:29:43 -0300662 var->xres_virtual < var->xres ||
663 var->yres_virtual < var->yres) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300664 IVTV_FB_DEBUG_WARN("Invalid virtual resolution: %dx%d\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300665 var->xres_virtual, var->yres_virtual);
666 return -EINVAL;
667 }
668 }
669
670 /* Some extra checks if in 8 bit mode */
671 if (var->bits_per_pixel == 8) {
672 /* Width must be a multiple of 4 */
673 if (var->xres & 3) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300674 IVTV_FB_DEBUG_WARN("Invalid resolution for 8bpp: %d\n", var->xres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300675 return -EINVAL;
676 }
677 if (var->xres_virtual & 3) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300678 IVTV_FB_DEBUG_WARN("Invalid virtual resolution for 8bpp: %d)\n", var->xres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300679 return -EINVAL;
680 }
681 }
682 else if (var->bits_per_pixel == 16) {
683 /* Width must be a multiple of 2 */
684 if (var->xres & 1) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300685 IVTV_FB_DEBUG_WARN("Invalid resolution for 16bpp: %d\n", var->xres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300686 return -EINVAL;
687 }
688 if (var->xres_virtual & 1) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300689 IVTV_FB_DEBUG_WARN("Invalid virtual resolution for 16bpp: %d)\n", var->xres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300690 return -EINVAL;
691 }
692 }
693
694 /* Now check the offsets */
695 if (var->xoffset >= var->xres_virtual || var->yoffset >= var->yres_virtual) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300696 IVTV_FB_DEBUG_WARN("Invalid offset: %d (%d) %d (%d)\n",
697 var->xoffset, var->xres_virtual, var->yoffset, var->yres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300698 return -EINVAL;
699 }
700
701 /* Check pixel format */
702 if (var->nonstd > 1) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300703 IVTV_FB_DEBUG_WARN("Invalid nonstd % d\n", var->nonstd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300704 return -EINVAL;
705 }
706
707 /* Check video mode */
708 if (((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) &&
709 ((var->vmode & FB_VMODE_MASK) != FB_VMODE_INTERLACED)) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300710 IVTV_FB_DEBUG_WARN("Invalid video mode: %d\n", var->vmode & FB_VMODE_MASK);
Hans Verkuil32db7752007-07-20 09:29:43 -0300711 return -EINVAL;
712 }
713
714 /* Check the left & upper margins
715 If the margins are too large, just center the screen
716 (enforcing margins causes too many problems) */
717
718 if (var->left_margin + var->xres > IVTV_OSD_MAX_WIDTH + 1) {
719 var->left_margin = 1 + ((IVTV_OSD_MAX_WIDTH - var->xres) / 2);
720 }
721 if (var->upper_margin + var->yres > (itv->is_50hz ? 577 : 481)) {
722 var->upper_margin = 1 + (((itv->is_50hz ? 576 : 480) - var->yres) / 2);
723 }
724
725 /* Maintain overall 'size' for a constant refresh rate */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300726 var->right_margin = oi->hlimit - var->left_margin - var->xres;
727 var->lower_margin = oi->vlimit - var->upper_margin - var->yres;
Hans Verkuil32db7752007-07-20 09:29:43 -0300728
729 /* Fixed sync times */
730 var->hsync_len = 24;
731 var->vsync_len = 2;
732
733 /* Non-interlaced / interlaced mode is used to switch the OSD filter
734 on or off. Adjust the clock timings to maintain a constant
735 vertical refresh rate. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300736 var->pixclock = oi->pixclock;
Hans Verkuil32db7752007-07-20 09:29:43 -0300737 if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED)
738 var->pixclock /= 2;
739
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300740 IVTV_FB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
741 var->xres, var->yres,
742 var->xres_virtual, var->yres_virtual,
Hans Verkuil32db7752007-07-20 09:29:43 -0300743 var->bits_per_pixel);
744
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300745 IVTV_FB_DEBUG_INFO("Display position: %d, %d\n",
746 var->left_margin, var->upper_margin);
Hans Verkuil32db7752007-07-20 09:29:43 -0300747
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300748 IVTV_FB_DEBUG_INFO("Display filter: %s\n",
749 (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
750 IVTV_FB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
Hans Verkuil32db7752007-07-20 09:29:43 -0300751 return 0;
752}
753
754static int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
755{
756 struct ivtv *itv = (struct ivtv *) info->par;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300757 IVTV_FB_DEBUG_INFO("ivtvfb_check_var\n");
758 return _ivtvfb_check_var(var, itv);
Hans Verkuil32db7752007-07-20 09:29:43 -0300759}
760
761static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
762{
763 u32 osd_pan_index;
764 struct ivtv *itv = (struct ivtv *) info->par;
765
766 osd_pan_index = (var->xoffset + (var->yoffset * var->xres_virtual))*var->bits_per_pixel/8;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300767 write_reg(osd_pan_index, 0x02A0C);
Hans Verkuil32db7752007-07-20 09:29:43 -0300768
769 /* Pass this info back the yuv handler */
770 itv->yuv_info.osd_x_pan = var->xoffset;
771 itv->yuv_info.osd_y_pan = var->yoffset;
772 /* Force update of yuv registers */
773 itv->yuv_info.yuv_forced_update = 1;
774 return 0;
775}
776
777static int ivtvfb_set_par(struct fb_info *info)
778{
779 int rc = 0;
780 struct ivtv *itv = (struct ivtv *) info->par;
781
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300782 IVTV_FB_DEBUG_INFO("ivtvfb_set_par\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300783
784 rc = ivtvfb_set_var(itv, &info->var);
785 ivtvfb_pan_display(&info->var, info);
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300786 ivtvfb_get_fix(itv, &info->fix);
Hans Verkuil32db7752007-07-20 09:29:43 -0300787 return rc;
788}
789
790static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green,
791 unsigned blue, unsigned transp,
792 struct fb_info *info)
793{
794 u32 color, *palette;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300795 struct ivtv *itv = (struct ivtv *)info->par;
Hans Verkuil32db7752007-07-20 09:29:43 -0300796
797 if (regno >= info->cmap.len)
798 return -EINVAL;
799
800 color = ((transp & 0xFF00) << 16) |((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8);
801 if (info->var.bits_per_pixel <= 8) {
802 write_reg(regno, 0x02a30);
803 write_reg(color, 0x02a34);
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300804 return 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300805 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300806 if (regno >= 16)
807 return -EINVAL;
Hans Verkuil32db7752007-07-20 09:29:43 -0300808
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300809 palette = info->pseudo_palette;
810 if (info->var.bits_per_pixel == 16) {
811 switch (info->var.green.length) {
812 case 4:
813 color = ((red & 0xf000) >> 4) |
814 ((green & 0xf000) >> 8) |
815 ((blue & 0xf000) >> 12);
816 break;
817 case 5:
818 color = ((red & 0xf800) >> 1) |
819 ((green & 0xf800) >> 6) |
820 ((blue & 0xf800) >> 11);
821 break;
822 case 6:
823 color = (red & 0xf800 ) |
824 ((green & 0xfc00) >> 5) |
825 ((blue & 0xf800) >> 11);
826 break;
Hans Verkuil32db7752007-07-20 09:29:43 -0300827 }
Hans Verkuil32db7752007-07-20 09:29:43 -0300828 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300829 palette[regno] = color;
Hans Verkuil32db7752007-07-20 09:29:43 -0300830 return 0;
831}
832
833/* We don't really support blanking. All this does is enable or
834 disable the OSD. */
835static int ivtvfb_blank(int blank_mode, struct fb_info *info)
836{
837 struct ivtv *itv = (struct ivtv *)info->par;
838
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300839 IVTV_FB_DEBUG_INFO("Set blanking mode : %d\n", blank_mode);
Hans Verkuil32db7752007-07-20 09:29:43 -0300840 switch (blank_mode) {
841 case FB_BLANK_UNBLANK:
842 ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1);
843 break;
844 case FB_BLANK_NORMAL:
845 case FB_BLANK_HSYNC_SUSPEND:
846 case FB_BLANK_VSYNC_SUSPEND:
847 case FB_BLANK_POWERDOWN:
848 ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
849 break;
850 }
851 return 0;
852}
853
854static struct fb_ops ivtvfb_ops = {
855 .owner = THIS_MODULE,
856 .fb_check_var = ivtvfb_check_var,
857 .fb_set_par = ivtvfb_set_par,
858 .fb_setcolreg = ivtvfb_setcolreg,
859 .fb_fillrect = cfb_fillrect,
860 .fb_copyarea = cfb_copyarea,
861 .fb_imageblit = cfb_imageblit,
862 .fb_cursor = NULL,
863 .fb_ioctl = ivtvfb_ioctl,
864 .fb_pan_display = ivtvfb_pan_display,
865 .fb_blank = ivtvfb_blank,
866};
867
868/* Initialization */
869
870
871/* Setup our initial video mode */
872static int ivtvfb_init_vidmode(struct ivtv *itv)
873{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300874 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300875 struct v4l2_rect start_window;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300876 int max_height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300877
878 /* Set base references for mode calcs. */
879 if (itv->is_50hz) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300880 oi->pixclock = 84316;
881 oi->hlimit = 776;
882 oi->vlimit = 591;
Hans Verkuil32db7752007-07-20 09:29:43 -0300883 }
884 else {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300885 oi->pixclock = 83926;
886 oi->hlimit = 776;
887 oi->vlimit = 495;
Hans Verkuil32db7752007-07-20 09:29:43 -0300888 }
889
890 /* Color mode */
891
892 if (osd_compat) osd_depth = 32;
893 if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32) osd_depth = 8;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300894 oi->bits_per_pixel = osd_depth;
895 oi->bytes_per_pixel = oi->bits_per_pixel / 8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300896
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300897 /* Invalidate current osd mode to force a mode switch later */
898 oi->osd_mode = -1;
899
Hans Verkuil32db7752007-07-20 09:29:43 -0300900 /* Horizontal size & position */
901
902 if (osd_xres > 720) osd_xres = 720;
903
904 /* Must be a multiple of 4 for 8bpp & 2 for 16bpp */
905 if (osd_depth == 8)
906 osd_xres &= ~3;
907 else if (osd_depth == 16)
908 osd_xres &= ~1;
909
910 if (osd_xres)
911 start_window.width = osd_xres;
912 else
913 start_window.width = osd_compat ? 720: 640;
914
915 /* Check horizontal start (osd_left). */
916 if (osd_left && osd_left + start_window.width > 721) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300917 IVTV_FB_ERR("Invalid osd_left - assuming default\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300918 osd_left = 0;
919 }
920
921 /* Hardware coords start at 0, user coords start at 1. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300922 osd_left--;
Hans Verkuil32db7752007-07-20 09:29:43 -0300923
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300924 start_window.left = osd_left >= 0 ? osd_left : ((IVTV_OSD_MAX_WIDTH - start_window.width) / 2);
Hans Verkuil32db7752007-07-20 09:29:43 -0300925
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300926 oi->display_byte_stride =
927 start_window.width * oi->bytes_per_pixel;
Hans Verkuil32db7752007-07-20 09:29:43 -0300928
929 /* Vertical size & position */
930
931 max_height = itv->is_50hz ? 576 : 480;
932
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300933 if (osd_yres > max_height)
934 osd_yres = max_height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300935
936 if (osd_yres)
937 start_window.height = osd_yres;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300938 else
939 start_window.height = osd_compat ? max_height : (itv->is_50hz ? 480 : 400);
Hans Verkuil32db7752007-07-20 09:29:43 -0300940
941 /* Check vertical start (osd_upper). */
942 if (osd_upper + start_window.height > max_height + 1) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300943 IVTV_FB_ERR("Invalid osd_upper - assuming default\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300944 osd_upper = 0;
945 }
946
947 /* Hardware coords start at 0, user coords start at 1. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300948 osd_upper--;
Hans Verkuil32db7752007-07-20 09:29:43 -0300949
950 start_window.top = osd_upper >= 0 ? osd_upper : ((max_height - start_window.height) / 2);
951
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300952 oi->display_width = start_window.width;
953 oi->display_height = start_window.height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300954
955 /* Generate a valid fb_var_screeninfo */
956
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300957 oi->ivtvfb_defined.xres = oi->display_width;
958 oi->ivtvfb_defined.yres = oi->display_height;
959 oi->ivtvfb_defined.xres_virtual = oi->display_width;
960 oi->ivtvfb_defined.yres_virtual = oi->display_height;
961 oi->ivtvfb_defined.bits_per_pixel = oi->bits_per_pixel;
962 oi->ivtvfb_defined.vmode = (osd_laced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED);
963 oi->ivtvfb_defined.left_margin = start_window.left + 1;
964 oi->ivtvfb_defined.upper_margin = start_window.top + 1;
965 oi->ivtvfb_defined.accel_flags = FB_ACCEL_NONE;
966 oi->ivtvfb_defined.nonstd = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300967
968 /* We've filled in the most data, let the usual mode check
969 routine fill in the rest. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300970 _ivtvfb_check_var(&oi->ivtvfb_defined, itv);
Hans Verkuil32db7752007-07-20 09:29:43 -0300971
972 /* Generate valid fb_fix_screeninfo */
973
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300974 ivtvfb_get_fix(itv, &oi->ivtvfb_fix);
Hans Verkuil32db7752007-07-20 09:29:43 -0300975
976 /* Generate valid fb_info */
977
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300978 oi->ivtvfb_info.node = -1;
979 oi->ivtvfb_info.flags = FBINFO_FLAG_DEFAULT;
980 oi->ivtvfb_info.fbops = &ivtvfb_ops;
981 oi->ivtvfb_info.par = itv;
982 oi->ivtvfb_info.var = oi->ivtvfb_defined;
983 oi->ivtvfb_info.fix = oi->ivtvfb_fix;
984 oi->ivtvfb_info.screen_base = (u8 __iomem *)oi->video_vbase;
985 oi->ivtvfb_info.fbops = &ivtvfb_ops;
Hans Verkuil32db7752007-07-20 09:29:43 -0300986
987 /* Supply some monitor specs. Bogus values will do for now */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300988 oi->ivtvfb_info.monspecs.hfmin = 8000;
989 oi->ivtvfb_info.monspecs.hfmax = 70000;
990 oi->ivtvfb_info.monspecs.vfmin = 10;
991 oi->ivtvfb_info.monspecs.vfmax = 100;
Hans Verkuil32db7752007-07-20 09:29:43 -0300992
993 /* Allocate color map */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300994 if (fb_alloc_cmap(&oi->ivtvfb_info.cmap, 256, 1)) {
995 IVTV_FB_ERR("abort, unable to alloc cmap\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300996 return -ENOMEM;
997 }
998
999 /* Allocate the pseudo palette */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001000 oi->ivtvfb_info.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
Hans Verkuil32db7752007-07-20 09:29:43 -03001001
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001002 if (!oi->ivtvfb_info.pseudo_palette) {
1003 IVTV_FB_ERR("abort, unable to alloc pseudo pallete\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001004 return -ENOMEM;
1005 }
1006
1007 return 0;
1008}
1009
1010/* Find OSD buffer base & size. Add to mtrr. Zero osd buffer. */
1011
1012static int ivtvfb_init_io(struct ivtv *itv)
1013{
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001014 struct osd_info *oi = itv->osd_info;
1015
1016 ivtv_fb_get_framebuffer(itv, &oi->video_rbase, &oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -03001017
1018 /* The osd buffer size depends on the number of video buffers allocated
1019 on the PVR350 itself. For now we'll hardcode the smallest osd buffer
1020 size to prevent any overlap. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001021 oi->video_buffer_size = 1704960;
Hans Verkuil32db7752007-07-20 09:29:43 -03001022
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001023 oi->video_pbase = itv->base_addr + IVTV_DECODER_OFFSET + oi->video_rbase;
1024 oi->video_vbase = itv->dec_mem + oi->video_rbase;
Hans Verkuil32db7752007-07-20 09:29:43 -03001025
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001026 if (!oi->video_vbase) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001027 IVTV_FB_ERR("abort, video memory 0x%x @ 0x%lx isn't mapped!\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001028 oi->video_buffer_size, oi->video_pbase);
Hans Verkuil32db7752007-07-20 09:29:43 -03001029 return -EIO;
1030 }
1031
1032 IVTV_FB_INFO("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001033 oi->video_pbase, oi->video_vbase,
1034 oi->video_buffer_size / 1024);
Hans Verkuil32db7752007-07-20 09:29:43 -03001035
1036#ifdef CONFIG_MTRR
1037 {
1038 /* Find the largest power of two that maps the whole buffer */
1039 int size_shift = 31;
1040
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001041 while (!(oi->video_buffer_size & (1 << size_shift))) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001042 size_shift--;
1043 }
1044 size_shift++;
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001045 oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1);
1046 oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size;
1047 oi->fb_end_aligned_physaddr += (1 << size_shift) - 1;
1048 oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1);
1049 if (mtrr_add(oi->fb_start_aligned_physaddr,
1050 oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr,
Hans Verkuil32db7752007-07-20 09:29:43 -03001051 MTRR_TYPE_WRCOMB, 1) < 0) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001052 IVTV_FB_WARN("cannot use mttr\n");
1053 oi->fb_start_aligned_physaddr = 0;
1054 oi->fb_end_aligned_physaddr = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -03001055 }
1056 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001057#endif
Hans Verkuil32db7752007-07-20 09:29:43 -03001058
1059 /* Blank the entire osd. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001060 memset_io(oi->video_vbase, 0, oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -03001061
1062 return 0;
1063}
1064
1065/* Release any memory we've grabbed & remove mtrr entry */
1066static void ivtvfb_release_buffers (struct ivtv *itv)
1067{
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001068 struct osd_info *oi = itv->osd_info;
1069
Hans Verkuil32db7752007-07-20 09:29:43 -03001070 /* Release cmap */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001071 if (oi->ivtvfb_info.cmap.len);
1072 fb_dealloc_cmap(&oi->ivtvfb_info.cmap);
Hans Verkuil32db7752007-07-20 09:29:43 -03001073
1074 /* Release pseudo palette */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001075 if (oi->ivtvfb_info.pseudo_palette)
1076 kfree(oi->ivtvfb_info.pseudo_palette);
Hans Verkuil32db7752007-07-20 09:29:43 -03001077
1078#ifdef CONFIG_MTRR
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001079 if (oi->fb_end_aligned_physaddr) {
1080 mtrr_del(-1, oi->fb_start_aligned_physaddr,
1081 oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr);
1082 }
1083#endif
Hans Verkuil32db7752007-07-20 09:29:43 -03001084
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001085 kfree(oi);
Hans Verkuil32db7752007-07-20 09:29:43 -03001086 itv->osd_info = NULL;
1087}
1088
1089/* Initialize the specified card */
1090
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001091static int ivtvfb_init_card(struct ivtv *itv)
Hans Verkuil32db7752007-07-20 09:29:43 -03001092{
1093 int rc;
1094
1095 if (itv->osd_info) {
1096 IVTV_FB_ERR("Card %d already initialised\n", ivtv_fb_card_id);
1097 return -EBUSY;
1098 }
1099
1100 itv->osd_info = kzalloc(sizeof(struct osd_info), GFP_ATOMIC);
1101 if (itv->osd_info == 0) {
1102 IVTV_FB_ERR("Failed to allocate memory for osd_info\n");
1103 return -ENOMEM;
1104 }
1105
1106 /* Find & setup the OSD buffer */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001107 if ((rc = ivtvfb_init_io(itv)))
Hans Verkuil32db7752007-07-20 09:29:43 -03001108 return rc;
1109
1110 /* Set the startup video mode information */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001111 if ((rc = ivtvfb_init_vidmode(itv))) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001112 ivtvfb_release_buffers(itv);
1113 return rc;
1114 }
1115
1116 /* Register the framebuffer */
1117 if (register_framebuffer(&itv->osd_info->ivtvfb_info) < 0) {
1118 ivtvfb_release_buffers(itv);
1119 return -EINVAL;
1120 }
1121
1122 itv->osd_video_pbase = itv->osd_info->video_pbase;
1123
1124 /* Set the card to the requested mode */
1125 ivtvfb_set_par(&itv->osd_info->ivtvfb_info);
1126
1127 /* Set color 0 to black */
1128 write_reg(0, 0x02a30);
1129 write_reg(0, 0x02a34);
1130
1131 /* Enable the osd */
1132 ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info);
1133
1134 /* Note if we're running in compatibility mode */
1135 if (osd_compat)
1136 IVTV_FB_INFO("Running in compatibility mode. Display resize & mode change disabled\n");
1137
1138 /* Allocate DMA */
1139 ivtv_udma_alloc(itv);
1140 return 0;
1141
1142}
1143
1144static int __init ivtvfb_init(void)
1145{
1146 struct ivtv *itv;
1147 int i, registered = 0;
1148
1149 if (ivtv_fb_card_id < -1 || ivtv_fb_card_id >= IVTV_MAX_CARDS) {
1150 printk(KERN_ERR "ivtv-fb: ivtv_fb_card_id parameter is out of range (valid range: -1 - %d)\n",
1151 IVTV_MAX_CARDS - 1);
1152 return -EINVAL;
1153 }
1154
1155 /* Locate & initialise all cards supporting an OSD. */
1156 for (i = 0; i < ivtv_cards_active; i++) {
1157 if (ivtv_fb_card_id != -1 && i != ivtv_fb_card_id)
1158 continue;
1159 itv = ivtv_cards[i];
1160 if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
1161 if (ivtvfb_init_card(itv) == 0) {
1162 IVTV_FB_INFO("Framebuffer registered on ivtv card id %d\n", i);
1163 registered++;
1164 }
1165 }
1166 }
1167 if (!registered) {
1168 printk(KERN_ERR "ivtv-fb: no cards found");
1169 return -ENODEV;
1170 }
1171 return 0;
1172}
1173
1174static void ivtvfb_cleanup(void)
1175{
1176 struct ivtv *itv;
1177 int i;
1178
1179 printk(KERN_INFO "ivtv-fb: Unloading framebuffer module\n");
1180
1181 for (i = 0; i < ivtv_cards_active; i++) {
1182 itv = ivtv_cards[i];
1183 if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) && itv->osd_info) {
1184 IVTV_FB_DEBUG_INFO("Unregister framebuffer %d\n", i);
1185 ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info);
1186 unregister_framebuffer(&itv->osd_info->ivtvfb_info);
1187 ivtvfb_release_buffers(itv);
1188 itv->osd_video_pbase = 0;
1189 }
1190 }
1191}
1192
1193module_init(ivtvfb_init);
1194module_exit(ivtvfb_cleanup);