blob: d2cc0317274953c8c4b44dec9a0e869639590c0d [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 {
Hans Verkuil32db7752007-07-20 09:29:43 -0300167 /* Physical base address */
168 unsigned long video_pbase;
169 /* Relative base address (relative to start of decoder memory) */
170 u32 video_rbase;
171 /* Mapped base address */
172 volatile char __iomem *video_vbase;
173 /* Buffer size */
174 u32 video_buffer_size;
175
176#ifdef CONFIG_MTRR
177 /* video_base rounded down as required by hardware MTRRs */
178 unsigned long fb_start_aligned_physaddr;
179 /* video_base rounded up as required by hardware MTRRs */
180 unsigned long fb_end_aligned_physaddr;
181#endif
182
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300183 /* Current osd mode */
184 int osd_mode;
185
Hans Verkuil32db7752007-07-20 09:29:43 -0300186 /* Store the buffer offset */
187 int set_osd_coords_x;
188 int set_osd_coords_y;
189
190 /* Current dimensions (NOT VISIBLE SIZE!) */
191 int display_width;
192 int display_height;
193 int display_byte_stride;
194
195 /* Current bits per pixel */
196 int bits_per_pixel;
197 int bytes_per_pixel;
198
199 /* Frame buffer stuff */
200 struct fb_info ivtvfb_info;
201 struct fb_var_screeninfo ivtvfb_defined;
202 struct fb_fix_screeninfo ivtvfb_fix;
203};
204
205struct ivtv_osd_coords {
206 unsigned long offset;
207 unsigned long max_offset;
208 int pixel_stride;
209 int lines;
210 int x;
211 int y;
212};
213
214/* --------------------------------------------------------------------- */
215
216/* ivtv API calls for framebuffer related support */
217
218static int ivtv_fb_get_framebuffer(struct ivtv *itv, u32 *fbbase,
219 u32 *fblength)
220{
221 u32 data[CX2341X_MBOX_MAX_DATA];
222 int rc;
223
224 rc = ivtv_vapi_result(itv, data, CX2341X_OSD_GET_FRAMEBUFFER, 0);
225 *fbbase = data[0];
226 *fblength = data[1];
227 return rc;
228}
229
230static int ivtv_fb_get_osd_coords(struct ivtv *itv,
231 struct ivtv_osd_coords *osd)
232{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300233 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300234 u32 data[CX2341X_MBOX_MAX_DATA];
235
236 ivtv_vapi_result(itv, data, CX2341X_OSD_GET_OSD_COORDS, 0);
237
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300238 osd->offset = data[0] - oi->video_rbase;
239 osd->max_offset = oi->display_width * oi->display_height * 4;
Hans Verkuil32db7752007-07-20 09:29:43 -0300240 osd->pixel_stride = data[1];
241 osd->lines = data[2];
242 osd->x = data[3];
243 osd->y = data[4];
244 return 0;
245}
246
247static int ivtv_fb_set_osd_coords(struct ivtv *itv, const struct ivtv_osd_coords *osd)
248{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300249 struct osd_info *oi = itv->osd_info;
250
251 oi->display_width = osd->pixel_stride;
252 oi->display_byte_stride = osd->pixel_stride * oi->bytes_per_pixel;
253 oi->set_osd_coords_x += osd->x;
254 oi->set_osd_coords_y = osd->y;
Hans Verkuil32db7752007-07-20 09:29:43 -0300255
256 return ivtv_vapi(itv, CX2341X_OSD_SET_OSD_COORDS, 5,
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300257 osd->offset + oi->video_rbase,
Hans Verkuil32db7752007-07-20 09:29:43 -0300258 osd->pixel_stride,
259 osd->lines, osd->x, osd->y);
260}
261
262static int ivtv_fb_set_display_window(struct ivtv *itv, struct v4l2_rect *ivtv_window)
263{
Hans Verkuil32db7752007-07-20 09:29:43 -0300264 int osd_height_limit = itv->is_50hz ? 576 : 480;
265
266 /* Only fail if resolution too high, otherwise fudge the start coords. */
267 if ((ivtv_window->height > osd_height_limit) || (ivtv_window->width > IVTV_OSD_MAX_WIDTH))
268 return -EINVAL;
269
270 /* Ensure we don't exceed display limits */
271 if (ivtv_window->top + ivtv_window->height > osd_height_limit) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300272 IVTV_FB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid height setting (%d, %d)\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300273 ivtv_window->top, ivtv_window->height);
274 ivtv_window->top = osd_height_limit - ivtv_window->height;
275 }
276
277 if (ivtv_window->left + ivtv_window->width > IVTV_OSD_MAX_WIDTH) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300278 IVTV_FB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid width setting (%d, %d)\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300279 ivtv_window->left, ivtv_window->width);
280 ivtv_window->left = IVTV_OSD_MAX_WIDTH - ivtv_window->width;
281 }
282
283 /* Set the OSD origin */
284 write_reg((ivtv_window->top << 16) | ivtv_window->left, 0x02a04);
285
286 /* How much to display */
287 write_reg(((ivtv_window->top+ivtv_window->height) << 16) | (ivtv_window->left+ivtv_window->width), 0x02a08);
288
289 /* Pass this info back the yuv handler */
290 itv->yuv_info.osd_vis_w = ivtv_window->width;
291 itv->yuv_info.osd_vis_h = ivtv_window->height;
292 itv->yuv_info.osd_x_offset = ivtv_window->left;
293 itv->yuv_info.osd_y_offset = ivtv_window->top;
294
295 return 0;
296}
297
298static int ivtv_fb_prep_dec_dma_to_device(struct ivtv *itv,
299 unsigned long ivtv_dest_addr, void __user *userbuf,
300 int size_in_bytes)
301{
302 DEFINE_WAIT(wait);
303 int ret = 0;
304 int got_sig = 0;
305
306 mutex_lock(&itv->udma.lock);
307 /* Map User DMA */
308 if (ivtv_udma_setup(itv, ivtv_dest_addr, userbuf, size_in_bytes) <= 0) {
309 mutex_unlock(&itv->udma.lock);
310 IVTV_FB_WARN("ivtvfb_prep_dec_dma_to_device, "
311 "Error with get_user_pages: %d bytes, %d pages returned\n",
312 size_in_bytes, itv->udma.page_count);
313
314 /* get_user_pages must have failed completely */
315 return -EIO;
316 }
317
318 IVTV_FB_DEBUG_INFO("ivtvfb_prep_dec_dma_to_device, %d bytes, %d pages\n",
319 size_in_bytes, itv->udma.page_count);
320
321 ivtv_udma_prepare(itv);
322 prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
323 /* if no UDMA is pending and no UDMA is in progress, then the DMA
324 is finished */
325 while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) {
326 /* don't interrupt if the DMA is in progress but break off
327 a still pending DMA. */
328 got_sig = signal_pending(current);
329 if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))
330 break;
331 got_sig = 0;
332 schedule();
333 }
334 finish_wait(&itv->dma_waitq, &wait);
335
336 /* Unmap Last DMA Xfer */
337 ivtv_udma_unmap(itv);
338 mutex_unlock(&itv->udma.lock);
339 if (got_sig) {
340 IVTV_DEBUG_INFO("User stopped OSD\n");
341 return -EINTR;
342 }
343
344 return ret;
345}
346
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300347static int ivtv_fb_prep_frame(struct ivtv *itv, int cmd, void __user *source,
348 unsigned long dest_offset, int count)
Hans Verkuil32db7752007-07-20 09:29:43 -0300349{
350 DEFINE_WAIT(wait);
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300351 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300352
353 /* Nothing to do */
354 if (count == 0) {
355 IVTV_FB_DEBUG_WARN("ivtv_fb_prep_frame: Nothing to do. count = 0\n");
356 return -EINVAL;
357 }
358
359 /* Check Total FB Size */
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300360 if ((dest_offset + count) > oi->video_buffer_size) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300361 IVTV_FB_WARN("ivtv_fb_prep_frame: Overflowing the framebuffer %ld, only %d available\n",
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300362 dest_offset + count, oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -0300363 return -E2BIG;
364 }
365
366 /* Not fatal, but will have undesirable results */
367 if ((unsigned long)source & 3)
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300368 IVTV_FB_WARN("ivtv_fb_prep_frame: Source address not 32 bit aligned (0x%08lx)\n",
369 (unsigned long)source);
Hans Verkuil32db7752007-07-20 09:29:43 -0300370
371 if (dest_offset & 3)
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300372 IVTV_FB_WARN("ivtv_fb_prep_frame: Dest offset not 32 bit aligned (%ld)\n", dest_offset);
Hans Verkuil32db7752007-07-20 09:29:43 -0300373
374 if (count & 3)
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300375 IVTV_FB_WARN("ivtv_fb_prep_frame: Count not a multiple of 4 (%d)\n", count);
Hans Verkuil32db7752007-07-20 09:29:43 -0300376
377 /* Check Source */
378 if (!access_ok(VERIFY_READ, source + dest_offset, count)) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300379 IVTV_FB_WARN("Invalid userspace pointer 0x%08lx\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300380 (unsigned long)source);
381
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300382 IVTV_FB_DEBUG_WARN("access_ok() failed for offset 0x%08lx source 0x%08lx count %d\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300383 dest_offset, (unsigned long)source,
384 count);
385 return -EINVAL;
386 }
387
388 /* OSD Address to send DMA to */
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300389 dest_offset += IVTV_DEC_MEM_START + oi->video_rbase;
Hans Verkuil32db7752007-07-20 09:29:43 -0300390
391 /* Fill Buffers */
392 return ivtv_fb_prep_dec_dma_to_device(itv, dest_offset, source, count);
393}
394
395static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
396{
397 DEFINE_WAIT(wait);
398 struct ivtv *itv = (struct ivtv *)info->par;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300399 int rc = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300400
401 switch (cmd) {
Hans Verkuil32db7752007-07-20 09:29:43 -0300402 case FBIOGET_VBLANK: {
403 struct fb_vblank vblank;
404 u32 trace;
405
406 vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT |
407 FB_VBLANK_HAVE_VSYNC;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300408 trace = read_reg(0x028c0) >> 16;
Hans Verkuil32db7752007-07-20 09:29:43 -0300409 if (itv->is_50hz && trace > 312) trace -= 312;
410 else if (itv->is_60hz && trace > 262) trace -= 262;
411 if (trace == 1) vblank.flags |= FB_VBLANK_VSYNCING;
412 vblank.count = itv->lastVsyncFrame;
413 vblank.vcount = trace;
414 vblank.hcount = 0;
415 if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank)))
416 return -EFAULT;
417 return 0;
418 }
419
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300420 case FBIO_WAITFORVSYNC:
Hans Verkuil32db7752007-07-20 09:29:43 -0300421 prepare_to_wait(&itv->vsync_waitq, &wait, TASK_INTERRUPTIBLE);
Hans Verkuil84149a02007-07-20 18:17:18 -0300422 if (!schedule_timeout(msecs_to_jiffies(50))) rc = -ETIMEDOUT;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300423 finish_wait(&itv->vsync_waitq, &wait);
Hans Verkuil32db7752007-07-20 09:29:43 -0300424 return rc;
Hans Verkuil32db7752007-07-20 09:29:43 -0300425
Hans Verkuild715e762007-07-20 10:30:32 -0300426 case IVTVFB_IOC_DMA_FRAME: {
427 struct ivtvfb_dma_frame args;
Hans Verkuil32db7752007-07-20 09:29:43 -0300428
Hans Verkuild715e762007-07-20 10:30:32 -0300429 IVTV_FB_DEBUG_INFO("IVTVFB_IOC_DMA_FRAME\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300430 if (copy_from_user(&args, (void __user *)arg, sizeof(args)))
431 return -EFAULT;
432
433 return ivtv_fb_prep_frame(itv, cmd, args.source, args.dest_offset, args.count);
434 }
435
436 default:
Hans Verkuilc6f95d12007-07-22 15:44:41 -0300437 IVTV_FB_DEBUG_INFO("Unknown ioctl %08x\n", cmd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300438 return -EINVAL;
439 }
440 return 0;
441}
442
443/* Framebuffer device handling */
444
445static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var)
446{
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300447 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300448 struct ivtv_osd_coords ivtv_osd;
449 struct v4l2_rect ivtv_window;
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300450 int osd_mode = -1;
Hans Verkuil32db7752007-07-20 09:29:43 -0300451
452 IVTV_FB_DEBUG_INFO("ivtvfb_set_var\n");
453
454 /* Select color space */
455 if (var->nonstd) /* YUV */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300456 write_reg(read_reg(0x02a00) | 0x0002000, 0x02a00);
Hans Verkuil32db7752007-07-20 09:29:43 -0300457 else /* RGB */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300458 write_reg(read_reg(0x02a00) & ~0x0002000, 0x02a00);
Hans Verkuil32db7752007-07-20 09:29:43 -0300459
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300460 /* Set the color mode */
Hans Verkuil32db7752007-07-20 09:29:43 -0300461 switch (var->bits_per_pixel) {
462 case 8:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300463 osd_mode = IVTV_OSD_BPP_8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300464 break;
465 case 32:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300466 osd_mode = IVTV_OSD_BPP_32;
Hans Verkuil32db7752007-07-20 09:29:43 -0300467 break;
468 case 16:
469 switch (var->green.length) {
470 case 4:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300471 osd_mode = IVTV_OSD_BPP_16_444;
Hans Verkuil32db7752007-07-20 09:29:43 -0300472 break;
473 case 5:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300474 osd_mode = IVTV_OSD_BPP_16_555;
Hans Verkuil32db7752007-07-20 09:29:43 -0300475 break;
476 case 6:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300477 osd_mode = IVTV_OSD_BPP_16_565;
Hans Verkuil32db7752007-07-20 09:29:43 -0300478 break;
479 default:
480 IVTV_FB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
481 }
482 break;
483 default:
484 IVTV_FB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
485 }
486
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300487 /* Change osd mode if needed.
488 Although rare, things can go wrong. The extra mode
489 change seems to help... */
490 if (osd_mode != -1 && osd_mode != oi->osd_mode) {
491 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0);
492 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, osd_mode);
493 oi->osd_mode = osd_mode;
494 }
495
496 oi->bits_per_pixel = var->bits_per_pixel;
497 oi->bytes_per_pixel = var->bits_per_pixel / 8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300498
499 /* Set the flicker filter */
500 switch (var->vmode & FB_VMODE_MASK) {
501 case FB_VMODE_NONINTERLACED: /* Filter on */
502 ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 1);
503 break;
504 case FB_VMODE_INTERLACED: /* Filter off */
505 ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 0);
506 break;
507 default:
508 IVTV_FB_DEBUG_WARN("ivtvfb_set_var - Invalid video mode\n");
509 }
510
511 /* Read the current osd info */
512 ivtv_fb_get_osd_coords(itv, &ivtv_osd);
513
514 /* Now set the OSD to the size we want */
515 ivtv_osd.pixel_stride = var->xres_virtual;
516 ivtv_osd.lines = var->yres_virtual;
517 ivtv_osd.x = 0;
518 ivtv_osd.y = 0;
519 ivtv_fb_set_osd_coords(itv, &ivtv_osd);
520
521 /* Can't seem to find the right API combo for this.
522 Use another function which does what we need through direct register access. */
523 ivtv_window.width = var->xres;
524 ivtv_window.height = var->yres;
525
526 /* Minimum margin cannot be 0, as X won't allow such a mode */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300527 if (!var->upper_margin) var->upper_margin++;
528 if (!var->left_margin) var->left_margin++;
Hans Verkuil32db7752007-07-20 09:29:43 -0300529 ivtv_window.top = var->upper_margin - 1;
530 ivtv_window.left = var->left_margin - 1;
531
532 ivtv_fb_set_display_window(itv, &ivtv_window);
533
534 /* Force update of yuv registers */
535 itv->yuv_info.yuv_forced_update = 1;
536
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300537 IVTV_FB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
538 var->xres, var->yres,
539 var->xres_virtual, var->yres_virtual,
540 var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300541
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300542 IVTV_FB_DEBUG_INFO("Display position: %d, %d\n",
543 var->left_margin, var->upper_margin);
Hans Verkuil32db7752007-07-20 09:29:43 -0300544
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300545 IVTV_FB_DEBUG_INFO("Display filter: %s\n",
546 (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
547 IVTV_FB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
Hans Verkuil32db7752007-07-20 09:29:43 -0300548
549 return 0;
550}
551
552static int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix)
553{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300554 struct osd_info *oi = itv->osd_info;
555
556 IVTV_FB_DEBUG_INFO("ivtvfb_get_fix\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300557 memset(fix, 0, sizeof(struct fb_fix_screeninfo));
558 strcpy(fix->id, "cx23415 TV out");
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300559 fix->smem_start = oi->video_pbase;
560 fix->smem_len = oi->video_buffer_size;
Hans Verkuil32db7752007-07-20 09:29:43 -0300561 fix->type = FB_TYPE_PACKED_PIXELS;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300562 fix->visual = (oi->bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
Hans Verkuil32db7752007-07-20 09:29:43 -0300563 fix->xpanstep = 1;
564 fix->ypanstep = 1;
565 fix->ywrapstep = 0;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300566 fix->line_length = oi->display_byte_stride;
Hans Verkuil32db7752007-07-20 09:29:43 -0300567 fix->accel = FB_ACCEL_NONE;
568 return 0;
569}
570
571/* Check the requested display mode, returning -EINVAL if we can't
572 handle it. */
573
574static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv)
575{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300576 struct osd_info *oi = itv->osd_info;
Ian Armstrong68a341a2007-08-03 09:51:58 -0300577 int osd_height_limit;
578 u32 pixclock, hlimit, vlimit;
Hans Verkuil32db7752007-07-20 09:29:43 -0300579
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300580 IVTV_FB_DEBUG_INFO("ivtvfb_check_var\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300581
Ian Armstrong68a341a2007-08-03 09:51:58 -0300582 /* Set base references for mode calcs. */
583 if (itv->is_50hz) {
584 pixclock = 84316;
585 hlimit = 776;
586 vlimit = 591;
587 osd_height_limit = 576;
588 }
589 else {
590 pixclock = 83926;
591 hlimit = 776;
592 vlimit = 495;
593 osd_height_limit = 480;
594 }
595
Hans Verkuil32db7752007-07-20 09:29:43 -0300596 /* Check the bits per pixel */
597 if (osd_compat) {
598 if (var->bits_per_pixel != 32) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300599 IVTV_FB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300600 return -EINVAL;
601 }
602 }
603
604 if (var->bits_per_pixel == 8 || var->bits_per_pixel == 32) {
605 var->transp.offset = 24;
606 var->transp.length = 8;
607 var->red.offset = 16;
608 var->red.length = 8;
609 var->green.offset = 8;
610 var->green.length = 8;
611 var->blue.offset = 0;
612 var->blue.length = 8;
613 }
614 else if (var->bits_per_pixel == 16) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300615 var->transp.offset = 0;
616 var->transp.length = 0;
617
Hans Verkuil32db7752007-07-20 09:29:43 -0300618 /* To find out the true mode, check green length */
619 switch (var->green.length) {
620 case 4:
Hans Verkuil32db7752007-07-20 09:29:43 -0300621 var->red.offset = 8;
622 var->red.length = 4;
623 var->green.offset = 4;
624 var->green.length = 4;
625 var->blue.offset = 0;
626 var->blue.length = 4;
627 break;
628 case 5:
Hans Verkuil32db7752007-07-20 09:29:43 -0300629 var->red.offset = 10;
630 var->red.length = 5;
631 var->green.offset = 5;
632 var->green.length = 5;
633 var->blue.offset = 0;
634 var->blue.length = 5;
635 break;
636 default:
Hans Verkuil32db7752007-07-20 09:29:43 -0300637 var->red.offset = 11;
638 var->red.length = 5;
639 var->green.offset = 5;
640 var->green.length = 6;
641 var->blue.offset = 0;
642 var->blue.length = 5;
643 break;
644 }
645 }
646 else {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300647 IVTV_FB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300648 return -EINVAL;
649 }
650
651 /* Check the resolution */
652 if (osd_compat) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300653 if (var->xres != oi->ivtvfb_defined.xres ||
654 var->yres != oi->ivtvfb_defined.yres ||
655 var->xres_virtual != oi->ivtvfb_defined.xres_virtual ||
656 var->yres_virtual != oi->ivtvfb_defined.yres_virtual) {
657 IVTV_FB_DEBUG_WARN("Invalid resolution: %dx%d (virtual %dx%d)\n",
658 var->xres, var->yres, var->xres_virtual, var->yres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300659 return -EINVAL;
660 }
661 }
662 else {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300663 if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit) {
664 IVTV_FB_DEBUG_WARN("Invalid resolution: %dx%d\n",
665 var->xres, var->yres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300666 return -EINVAL;
667 }
668
669 /* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */
670 if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) ||
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300671 var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8) > oi->video_buffer_size ||
Hans Verkuil32db7752007-07-20 09:29:43 -0300672 var->xres_virtual < var->xres ||
673 var->yres_virtual < var->yres) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300674 IVTV_FB_DEBUG_WARN("Invalid virtual resolution: %dx%d\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300675 var->xres_virtual, var->yres_virtual);
676 return -EINVAL;
677 }
678 }
679
680 /* Some extra checks if in 8 bit mode */
681 if (var->bits_per_pixel == 8) {
682 /* Width must be a multiple of 4 */
683 if (var->xres & 3) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300684 IVTV_FB_DEBUG_WARN("Invalid resolution for 8bpp: %d\n", var->xres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300685 return -EINVAL;
686 }
687 if (var->xres_virtual & 3) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300688 IVTV_FB_DEBUG_WARN("Invalid virtual resolution for 8bpp: %d)\n", var->xres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300689 return -EINVAL;
690 }
691 }
692 else if (var->bits_per_pixel == 16) {
693 /* Width must be a multiple of 2 */
694 if (var->xres & 1) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300695 IVTV_FB_DEBUG_WARN("Invalid resolution for 16bpp: %d\n", var->xres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300696 return -EINVAL;
697 }
698 if (var->xres_virtual & 1) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300699 IVTV_FB_DEBUG_WARN("Invalid virtual resolution for 16bpp: %d)\n", var->xres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300700 return -EINVAL;
701 }
702 }
703
704 /* Now check the offsets */
705 if (var->xoffset >= var->xres_virtual || var->yoffset >= var->yres_virtual) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300706 IVTV_FB_DEBUG_WARN("Invalid offset: %d (%d) %d (%d)\n",
707 var->xoffset, var->xres_virtual, var->yoffset, var->yres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300708 return -EINVAL;
709 }
710
711 /* Check pixel format */
712 if (var->nonstd > 1) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300713 IVTV_FB_DEBUG_WARN("Invalid nonstd % d\n", var->nonstd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300714 return -EINVAL;
715 }
716
717 /* Check video mode */
718 if (((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) &&
719 ((var->vmode & FB_VMODE_MASK) != FB_VMODE_INTERLACED)) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300720 IVTV_FB_DEBUG_WARN("Invalid video mode: %d\n", var->vmode & FB_VMODE_MASK);
Hans Verkuil32db7752007-07-20 09:29:43 -0300721 return -EINVAL;
722 }
723
724 /* Check the left & upper margins
725 If the margins are too large, just center the screen
726 (enforcing margins causes too many problems) */
727
728 if (var->left_margin + var->xres > IVTV_OSD_MAX_WIDTH + 1) {
729 var->left_margin = 1 + ((IVTV_OSD_MAX_WIDTH - var->xres) / 2);
730 }
731 if (var->upper_margin + var->yres > (itv->is_50hz ? 577 : 481)) {
732 var->upper_margin = 1 + (((itv->is_50hz ? 576 : 480) - var->yres) / 2);
733 }
734
735 /* Maintain overall 'size' for a constant refresh rate */
Ian Armstrong68a341a2007-08-03 09:51:58 -0300736 var->right_margin = hlimit - var->left_margin - var->xres;
737 var->lower_margin = vlimit - var->upper_margin - var->yres;
Hans Verkuil32db7752007-07-20 09:29:43 -0300738
739 /* Fixed sync times */
740 var->hsync_len = 24;
741 var->vsync_len = 2;
742
743 /* Non-interlaced / interlaced mode is used to switch the OSD filter
744 on or off. Adjust the clock timings to maintain a constant
745 vertical refresh rate. */
Hans Verkuil32db7752007-07-20 09:29:43 -0300746 if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED)
Ian Armstrong68a341a2007-08-03 09:51:58 -0300747 var->pixclock = pixclock / 2;
748 else
749 var->pixclock = pixclock;
Hans Verkuil32db7752007-07-20 09:29:43 -0300750
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300751 IVTV_FB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
752 var->xres, var->yres,
753 var->xres_virtual, var->yres_virtual,
Hans Verkuil32db7752007-07-20 09:29:43 -0300754 var->bits_per_pixel);
755
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300756 IVTV_FB_DEBUG_INFO("Display position: %d, %d\n",
757 var->left_margin, var->upper_margin);
Hans Verkuil32db7752007-07-20 09:29:43 -0300758
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300759 IVTV_FB_DEBUG_INFO("Display filter: %s\n",
760 (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
761 IVTV_FB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
Hans Verkuil32db7752007-07-20 09:29:43 -0300762 return 0;
763}
764
765static int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
766{
767 struct ivtv *itv = (struct ivtv *) info->par;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300768 IVTV_FB_DEBUG_INFO("ivtvfb_check_var\n");
769 return _ivtvfb_check_var(var, itv);
Hans Verkuil32db7752007-07-20 09:29:43 -0300770}
771
772static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
773{
774 u32 osd_pan_index;
775 struct ivtv *itv = (struct ivtv *) info->par;
776
777 osd_pan_index = (var->xoffset + (var->yoffset * var->xres_virtual))*var->bits_per_pixel/8;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300778 write_reg(osd_pan_index, 0x02A0C);
Hans Verkuil32db7752007-07-20 09:29:43 -0300779
780 /* Pass this info back the yuv handler */
781 itv->yuv_info.osd_x_pan = var->xoffset;
782 itv->yuv_info.osd_y_pan = var->yoffset;
783 /* Force update of yuv registers */
784 itv->yuv_info.yuv_forced_update = 1;
785 return 0;
786}
787
788static int ivtvfb_set_par(struct fb_info *info)
789{
790 int rc = 0;
791 struct ivtv *itv = (struct ivtv *) info->par;
792
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300793 IVTV_FB_DEBUG_INFO("ivtvfb_set_par\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300794
795 rc = ivtvfb_set_var(itv, &info->var);
796 ivtvfb_pan_display(&info->var, info);
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300797 ivtvfb_get_fix(itv, &info->fix);
Hans Verkuil32db7752007-07-20 09:29:43 -0300798 return rc;
799}
800
801static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green,
802 unsigned blue, unsigned transp,
803 struct fb_info *info)
804{
805 u32 color, *palette;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300806 struct ivtv *itv = (struct ivtv *)info->par;
Hans Verkuil32db7752007-07-20 09:29:43 -0300807
808 if (regno >= info->cmap.len)
809 return -EINVAL;
810
811 color = ((transp & 0xFF00) << 16) |((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8);
812 if (info->var.bits_per_pixel <= 8) {
813 write_reg(regno, 0x02a30);
814 write_reg(color, 0x02a34);
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300815 return 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300816 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300817 if (regno >= 16)
818 return -EINVAL;
Hans Verkuil32db7752007-07-20 09:29:43 -0300819
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300820 palette = info->pseudo_palette;
821 if (info->var.bits_per_pixel == 16) {
822 switch (info->var.green.length) {
823 case 4:
824 color = ((red & 0xf000) >> 4) |
825 ((green & 0xf000) >> 8) |
826 ((blue & 0xf000) >> 12);
827 break;
828 case 5:
829 color = ((red & 0xf800) >> 1) |
830 ((green & 0xf800) >> 6) |
831 ((blue & 0xf800) >> 11);
832 break;
833 case 6:
834 color = (red & 0xf800 ) |
835 ((green & 0xfc00) >> 5) |
836 ((blue & 0xf800) >> 11);
837 break;
Hans Verkuil32db7752007-07-20 09:29:43 -0300838 }
Hans Verkuil32db7752007-07-20 09:29:43 -0300839 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300840 palette[regno] = color;
Hans Verkuil32db7752007-07-20 09:29:43 -0300841 return 0;
842}
843
844/* We don't really support blanking. All this does is enable or
845 disable the OSD. */
846static int ivtvfb_blank(int blank_mode, struct fb_info *info)
847{
848 struct ivtv *itv = (struct ivtv *)info->par;
849
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300850 IVTV_FB_DEBUG_INFO("Set blanking mode : %d\n", blank_mode);
Hans Verkuil32db7752007-07-20 09:29:43 -0300851 switch (blank_mode) {
852 case FB_BLANK_UNBLANK:
853 ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1);
854 break;
855 case FB_BLANK_NORMAL:
856 case FB_BLANK_HSYNC_SUSPEND:
857 case FB_BLANK_VSYNC_SUSPEND:
858 case FB_BLANK_POWERDOWN:
859 ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
860 break;
861 }
862 return 0;
863}
864
865static struct fb_ops ivtvfb_ops = {
866 .owner = THIS_MODULE,
867 .fb_check_var = ivtvfb_check_var,
868 .fb_set_par = ivtvfb_set_par,
869 .fb_setcolreg = ivtvfb_setcolreg,
870 .fb_fillrect = cfb_fillrect,
871 .fb_copyarea = cfb_copyarea,
872 .fb_imageblit = cfb_imageblit,
873 .fb_cursor = NULL,
874 .fb_ioctl = ivtvfb_ioctl,
875 .fb_pan_display = ivtvfb_pan_display,
876 .fb_blank = ivtvfb_blank,
877};
878
879/* Initialization */
880
881
882/* Setup our initial video mode */
883static int ivtvfb_init_vidmode(struct ivtv *itv)
884{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300885 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300886 struct v4l2_rect start_window;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300887 int max_height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300888
Hans Verkuil32db7752007-07-20 09:29:43 -0300889 /* Color mode */
890
891 if (osd_compat) osd_depth = 32;
892 if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32) osd_depth = 8;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300893 oi->bits_per_pixel = osd_depth;
894 oi->bytes_per_pixel = oi->bits_per_pixel / 8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300895
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300896 /* Invalidate current osd mode to force a mode switch later */
897 oi->osd_mode = -1;
898
Hans Verkuil32db7752007-07-20 09:29:43 -0300899 /* Horizontal size & position */
900
901 if (osd_xres > 720) osd_xres = 720;
902
903 /* Must be a multiple of 4 for 8bpp & 2 for 16bpp */
904 if (osd_depth == 8)
905 osd_xres &= ~3;
906 else if (osd_depth == 16)
907 osd_xres &= ~1;
908
909 if (osd_xres)
910 start_window.width = osd_xres;
911 else
912 start_window.width = osd_compat ? 720: 640;
913
914 /* Check horizontal start (osd_left). */
915 if (osd_left && osd_left + start_window.width > 721) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300916 IVTV_FB_ERR("Invalid osd_left - assuming default\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300917 osd_left = 0;
918 }
919
920 /* Hardware coords start at 0, user coords start at 1. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300921 osd_left--;
Hans Verkuil32db7752007-07-20 09:29:43 -0300922
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300923 start_window.left = osd_left >= 0 ? osd_left : ((IVTV_OSD_MAX_WIDTH - start_window.width) / 2);
Hans Verkuil32db7752007-07-20 09:29:43 -0300924
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300925 oi->display_byte_stride =
926 start_window.width * oi->bytes_per_pixel;
Hans Verkuil32db7752007-07-20 09:29:43 -0300927
928 /* Vertical size & position */
929
930 max_height = itv->is_50hz ? 576 : 480;
931
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300932 if (osd_yres > max_height)
933 osd_yres = max_height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300934
935 if (osd_yres)
936 start_window.height = osd_yres;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300937 else
938 start_window.height = osd_compat ? max_height : (itv->is_50hz ? 480 : 400);
Hans Verkuil32db7752007-07-20 09:29:43 -0300939
940 /* Check vertical start (osd_upper). */
941 if (osd_upper + start_window.height > max_height + 1) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300942 IVTV_FB_ERR("Invalid osd_upper - assuming default\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300943 osd_upper = 0;
944 }
945
946 /* Hardware coords start at 0, user coords start at 1. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300947 osd_upper--;
Hans Verkuil32db7752007-07-20 09:29:43 -0300948
949 start_window.top = osd_upper >= 0 ? osd_upper : ((max_height - start_window.height) / 2);
950
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300951 oi->display_width = start_window.width;
952 oi->display_height = start_window.height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300953
954 /* Generate a valid fb_var_screeninfo */
955
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300956 oi->ivtvfb_defined.xres = oi->display_width;
957 oi->ivtvfb_defined.yres = oi->display_height;
958 oi->ivtvfb_defined.xres_virtual = oi->display_width;
959 oi->ivtvfb_defined.yres_virtual = oi->display_height;
960 oi->ivtvfb_defined.bits_per_pixel = oi->bits_per_pixel;
961 oi->ivtvfb_defined.vmode = (osd_laced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED);
962 oi->ivtvfb_defined.left_margin = start_window.left + 1;
963 oi->ivtvfb_defined.upper_margin = start_window.top + 1;
964 oi->ivtvfb_defined.accel_flags = FB_ACCEL_NONE;
965 oi->ivtvfb_defined.nonstd = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300966
967 /* We've filled in the most data, let the usual mode check
968 routine fill in the rest. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300969 _ivtvfb_check_var(&oi->ivtvfb_defined, itv);
Hans Verkuil32db7752007-07-20 09:29:43 -0300970
971 /* Generate valid fb_fix_screeninfo */
972
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300973 ivtvfb_get_fix(itv, &oi->ivtvfb_fix);
Hans Verkuil32db7752007-07-20 09:29:43 -0300974
975 /* Generate valid fb_info */
976
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300977 oi->ivtvfb_info.node = -1;
978 oi->ivtvfb_info.flags = FBINFO_FLAG_DEFAULT;
979 oi->ivtvfb_info.fbops = &ivtvfb_ops;
980 oi->ivtvfb_info.par = itv;
981 oi->ivtvfb_info.var = oi->ivtvfb_defined;
982 oi->ivtvfb_info.fix = oi->ivtvfb_fix;
983 oi->ivtvfb_info.screen_base = (u8 __iomem *)oi->video_vbase;
984 oi->ivtvfb_info.fbops = &ivtvfb_ops;
Hans Verkuil32db7752007-07-20 09:29:43 -0300985
986 /* Supply some monitor specs. Bogus values will do for now */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300987 oi->ivtvfb_info.monspecs.hfmin = 8000;
988 oi->ivtvfb_info.monspecs.hfmax = 70000;
989 oi->ivtvfb_info.monspecs.vfmin = 10;
990 oi->ivtvfb_info.monspecs.vfmax = 100;
Hans Verkuil32db7752007-07-20 09:29:43 -0300991
992 /* Allocate color map */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300993 if (fb_alloc_cmap(&oi->ivtvfb_info.cmap, 256, 1)) {
994 IVTV_FB_ERR("abort, unable to alloc cmap\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300995 return -ENOMEM;
996 }
997
998 /* Allocate the pseudo palette */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300999 oi->ivtvfb_info.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
Hans Verkuil32db7752007-07-20 09:29:43 -03001000
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001001 if (!oi->ivtvfb_info.pseudo_palette) {
1002 IVTV_FB_ERR("abort, unable to alloc pseudo pallete\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001003 return -ENOMEM;
1004 }
1005
1006 return 0;
1007}
1008
1009/* Find OSD buffer base & size. Add to mtrr. Zero osd buffer. */
1010
1011static int ivtvfb_init_io(struct ivtv *itv)
1012{
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001013 struct osd_info *oi = itv->osd_info;
1014
Hans Verkuil6e5eb592007-07-25 12:55:52 -03001015 if (ivtv_init_on_first_open(itv)) {
1016 IVTV_FB_ERR("Failed to initialize ivtv\n");
1017 return -ENXIO;
1018 }
1019
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001020 ivtv_fb_get_framebuffer(itv, &oi->video_rbase, &oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -03001021
1022 /* The osd buffer size depends on the number of video buffers allocated
1023 on the PVR350 itself. For now we'll hardcode the smallest osd buffer
1024 size to prevent any overlap. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001025 oi->video_buffer_size = 1704960;
Hans Verkuil32db7752007-07-20 09:29:43 -03001026
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001027 oi->video_pbase = itv->base_addr + IVTV_DECODER_OFFSET + oi->video_rbase;
1028 oi->video_vbase = itv->dec_mem + oi->video_rbase;
Hans Verkuil32db7752007-07-20 09:29:43 -03001029
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001030 if (!oi->video_vbase) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001031 IVTV_FB_ERR("abort, video memory 0x%x @ 0x%lx isn't mapped!\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001032 oi->video_buffer_size, oi->video_pbase);
Hans Verkuil32db7752007-07-20 09:29:43 -03001033 return -EIO;
1034 }
1035
1036 IVTV_FB_INFO("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001037 oi->video_pbase, oi->video_vbase,
1038 oi->video_buffer_size / 1024);
Hans Verkuil32db7752007-07-20 09:29:43 -03001039
1040#ifdef CONFIG_MTRR
1041 {
1042 /* Find the largest power of two that maps the whole buffer */
1043 int size_shift = 31;
1044
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001045 while (!(oi->video_buffer_size & (1 << size_shift))) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001046 size_shift--;
1047 }
1048 size_shift++;
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001049 oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1);
1050 oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size;
1051 oi->fb_end_aligned_physaddr += (1 << size_shift) - 1;
1052 oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1);
1053 if (mtrr_add(oi->fb_start_aligned_physaddr,
1054 oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr,
Hans Verkuil32db7752007-07-20 09:29:43 -03001055 MTRR_TYPE_WRCOMB, 1) < 0) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001056 IVTV_FB_WARN("cannot use mttr\n");
1057 oi->fb_start_aligned_physaddr = 0;
1058 oi->fb_end_aligned_physaddr = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -03001059 }
1060 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001061#endif
Hans Verkuil32db7752007-07-20 09:29:43 -03001062
1063 /* Blank the entire osd. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001064 memset_io(oi->video_vbase, 0, oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -03001065
1066 return 0;
1067}
1068
1069/* Release any memory we've grabbed & remove mtrr entry */
1070static void ivtvfb_release_buffers (struct ivtv *itv)
1071{
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001072 struct osd_info *oi = itv->osd_info;
1073
Hans Verkuil32db7752007-07-20 09:29:43 -03001074 /* Release cmap */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001075 if (oi->ivtvfb_info.cmap.len);
1076 fb_dealloc_cmap(&oi->ivtvfb_info.cmap);
Hans Verkuil32db7752007-07-20 09:29:43 -03001077
1078 /* Release pseudo palette */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001079 if (oi->ivtvfb_info.pseudo_palette)
1080 kfree(oi->ivtvfb_info.pseudo_palette);
Hans Verkuil32db7752007-07-20 09:29:43 -03001081
1082#ifdef CONFIG_MTRR
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001083 if (oi->fb_end_aligned_physaddr) {
1084 mtrr_del(-1, oi->fb_start_aligned_physaddr,
1085 oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr);
1086 }
1087#endif
Hans Verkuil32db7752007-07-20 09:29:43 -03001088
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001089 kfree(oi);
Hans Verkuil32db7752007-07-20 09:29:43 -03001090 itv->osd_info = NULL;
1091}
1092
1093/* Initialize the specified card */
1094
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001095static int ivtvfb_init_card(struct ivtv *itv)
Hans Verkuil32db7752007-07-20 09:29:43 -03001096{
1097 int rc;
1098
1099 if (itv->osd_info) {
1100 IVTV_FB_ERR("Card %d already initialised\n", ivtv_fb_card_id);
1101 return -EBUSY;
1102 }
1103
1104 itv->osd_info = kzalloc(sizeof(struct osd_info), GFP_ATOMIC);
1105 if (itv->osd_info == 0) {
1106 IVTV_FB_ERR("Failed to allocate memory for osd_info\n");
1107 return -ENOMEM;
1108 }
1109
1110 /* Find & setup the OSD buffer */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001111 if ((rc = ivtvfb_init_io(itv)))
Hans Verkuil32db7752007-07-20 09:29:43 -03001112 return rc;
1113
1114 /* Set the startup video mode information */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001115 if ((rc = ivtvfb_init_vidmode(itv))) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001116 ivtvfb_release_buffers(itv);
1117 return rc;
1118 }
1119
1120 /* Register the framebuffer */
1121 if (register_framebuffer(&itv->osd_info->ivtvfb_info) < 0) {
1122 ivtvfb_release_buffers(itv);
1123 return -EINVAL;
1124 }
1125
1126 itv->osd_video_pbase = itv->osd_info->video_pbase;
1127
1128 /* Set the card to the requested mode */
1129 ivtvfb_set_par(&itv->osd_info->ivtvfb_info);
1130
1131 /* Set color 0 to black */
1132 write_reg(0, 0x02a30);
1133 write_reg(0, 0x02a34);
1134
1135 /* Enable the osd */
1136 ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info);
1137
1138 /* Note if we're running in compatibility mode */
1139 if (osd_compat)
1140 IVTV_FB_INFO("Running in compatibility mode. Display resize & mode change disabled\n");
1141
1142 /* Allocate DMA */
1143 ivtv_udma_alloc(itv);
1144 return 0;
1145
1146}
1147
1148static int __init ivtvfb_init(void)
1149{
1150 struct ivtv *itv;
1151 int i, registered = 0;
1152
1153 if (ivtv_fb_card_id < -1 || ivtv_fb_card_id >= IVTV_MAX_CARDS) {
1154 printk(KERN_ERR "ivtv-fb: ivtv_fb_card_id parameter is out of range (valid range: -1 - %d)\n",
1155 IVTV_MAX_CARDS - 1);
1156 return -EINVAL;
1157 }
1158
1159 /* Locate & initialise all cards supporting an OSD. */
1160 for (i = 0; i < ivtv_cards_active; i++) {
1161 if (ivtv_fb_card_id != -1 && i != ivtv_fb_card_id)
1162 continue;
1163 itv = ivtv_cards[i];
1164 if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
1165 if (ivtvfb_init_card(itv) == 0) {
1166 IVTV_FB_INFO("Framebuffer registered on ivtv card id %d\n", i);
1167 registered++;
1168 }
1169 }
1170 }
1171 if (!registered) {
1172 printk(KERN_ERR "ivtv-fb: no cards found");
1173 return -ENODEV;
1174 }
1175 return 0;
1176}
1177
1178static void ivtvfb_cleanup(void)
1179{
1180 struct ivtv *itv;
1181 int i;
1182
1183 printk(KERN_INFO "ivtv-fb: Unloading framebuffer module\n");
1184
1185 for (i = 0; i < ivtv_cards_active; i++) {
1186 itv = ivtv_cards[i];
1187 if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) && itv->osd_info) {
1188 IVTV_FB_DEBUG_INFO("Unregister framebuffer %d\n", i);
1189 ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info);
1190 unregister_framebuffer(&itv->osd_info->ivtvfb_info);
1191 ivtvfb_release_buffers(itv);
1192 itv->osd_video_pbase = 0;
1193 }
1194 }
1195}
1196
1197module_init(ivtvfb_init);
1198module_exit(ivtvfb_cleanup);