blob: 921e281876f8181168fe60b8fdb3d7823e51a0bf [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>
Hans Verkuil32db7752007-07-20 09:29:43 -030043#include <linux/fb.h>
Hans Verkuil0f45b8c2007-08-26 06:04:10 -030044#include <linux/ivtvfb.h>
Hans Verkuil32db7752007-07-20 09:29:43 -030045
46#ifdef CONFIG_MTRR
47#include <asm/mtrr.h>
48#endif
49
50#include "ivtv-driver.h"
Ian Armstrong4e7ca402008-10-19 18:58:26 -030051#include "ivtv-i2c.h"
Hans Verkuil32db7752007-07-20 09:29:43 -030052#include "ivtv-udma.h"
Hans Verkuil32db7752007-07-20 09:29:43 -030053#include "ivtv-mailbox.h"
Hans Verkuil32db7752007-07-20 09:29:43 -030054
55/* card parameters */
Hans Verkuil641ed492007-08-28 03:24:31 -030056static int ivtvfb_card_id = -1;
57static int ivtvfb_debug = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -030058static int osd_laced;
Hans Verkuil32db7752007-07-20 09:29:43 -030059static int osd_depth;
60static int osd_upper;
61static int osd_left;
62static int osd_yres;
63static int osd_xres;
64
Hans Verkuil641ed492007-08-28 03:24:31 -030065module_param(ivtvfb_card_id, int, 0444);
66module_param_named(debug,ivtvfb_debug, int, 0644);
Hans Verkuil32db7752007-07-20 09:29:43 -030067module_param(osd_laced, bool, 0444);
Hans Verkuil32db7752007-07-20 09:29:43 -030068module_param(osd_depth, int, 0444);
69module_param(osd_upper, int, 0444);
70module_param(osd_left, int, 0444);
71module_param(osd_yres, int, 0444);
72module_param(osd_xres, int, 0444);
73
Hans Verkuil641ed492007-08-28 03:24:31 -030074MODULE_PARM_DESC(ivtvfb_card_id,
Hans Verkuil32db7752007-07-20 09:29:43 -030075 "Only use framebuffer of the specified ivtv card (0-31)\n"
76 "\t\t\tdefault -1: initialize all available framebuffers");
77
78MODULE_PARM_DESC(debug,
79 "Debug level (bitmask). Default: errors only\n"
80 "\t\t\t(debug = 3 gives full debugging)");
81
Hans Verkuil32db7752007-07-20 09:29:43 -030082/* Why upper, left, xres, yres, depth, laced ? To match terminology used
83 by fbset.
84 Why start at 1 for left & upper coordinate ? Because X doesn't allow 0 */
85
86MODULE_PARM_DESC(osd_laced,
87 "Interlaced mode\n"
88 "\t\t\t0=off\n"
89 "\t\t\t1=on\n"
90 "\t\t\tdefault off");
91
92MODULE_PARM_DESC(osd_depth,
Hans Verkuilbe383bd2007-07-20 10:16:03 -030093 "Bits per pixel - 8, 16, 32\n"
Hans Verkuil32db7752007-07-20 09:29:43 -030094 "\t\t\tdefault 8");
95
96MODULE_PARM_DESC(osd_upper,
97 "Vertical start position\n"
98 "\t\t\tdefault 0 (Centered)");
99
100MODULE_PARM_DESC(osd_left,
101 "Horizontal start position\n"
102 "\t\t\tdefault 0 (Centered)");
103
104MODULE_PARM_DESC(osd_yres,
105 "Display height\n"
106 "\t\t\tdefault 480 (PAL)\n"
107 "\t\t\t 400 (NTSC)");
108
109MODULE_PARM_DESC(osd_xres,
110 "Display width\n"
111 "\t\t\tdefault 640");
112
113MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil, John Harvey, Ian Armstrong");
114MODULE_LICENSE("GPL");
115
116/* --------------------------------------------------------------------- */
117
Hans Verkuil641ed492007-08-28 03:24:31 -0300118#define IVTVFB_DBGFLG_WARN (1 << 0)
119#define IVTVFB_DBGFLG_INFO (1 << 1)
Hans Verkuil32db7752007-07-20 09:29:43 -0300120
Hans Verkuil641ed492007-08-28 03:24:31 -0300121#define IVTVFB_DEBUG(x, type, fmt, args...) \
Hans Verkuil32db7752007-07-20 09:29:43 -0300122 do { \
Hans Verkuil641ed492007-08-28 03:24:31 -0300123 if ((x) & ivtvfb_debug) \
Hans Verkuil7b3a0d42007-08-26 06:11:07 -0300124 printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->num , ## args); \
Hans Verkuil32db7752007-07-20 09:29:43 -0300125 } while (0)
Hans Verkuil641ed492007-08-28 03:24:31 -0300126#define IVTVFB_DEBUG_WARN(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_WARN, "warning", fmt , ## args)
127#define IVTVFB_DEBUG_INFO(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_INFO, "info", fmt , ## args)
Hans Verkuil32db7752007-07-20 09:29:43 -0300128
129/* Standard kernel messages */
Hans Verkuil641ed492007-08-28 03:24:31 -0300130#define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->num , ## args)
131#define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->num , ## args)
132#define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->num , ## args)
Hans Verkuil32db7752007-07-20 09:29:43 -0300133
134/* --------------------------------------------------------------------- */
135
136#define IVTV_OSD_MAX_WIDTH 720
137#define IVTV_OSD_MAX_HEIGHT 576
138
139#define IVTV_OSD_BPP_8 0x00
140#define IVTV_OSD_BPP_16_444 0x03
141#define IVTV_OSD_BPP_16_555 0x02
142#define IVTV_OSD_BPP_16_565 0x01
143#define IVTV_OSD_BPP_32 0x04
144
145struct osd_info {
Hans Verkuil32db7752007-07-20 09:29:43 -0300146 /* Physical base address */
147 unsigned long video_pbase;
148 /* Relative base address (relative to start of decoder memory) */
149 u32 video_rbase;
150 /* Mapped base address */
151 volatile char __iomem *video_vbase;
152 /* Buffer size */
153 u32 video_buffer_size;
154
155#ifdef CONFIG_MTRR
156 /* video_base rounded down as required by hardware MTRRs */
157 unsigned long fb_start_aligned_physaddr;
158 /* video_base rounded up as required by hardware MTRRs */
159 unsigned long fb_end_aligned_physaddr;
160#endif
161
162 /* Store the buffer offset */
163 int set_osd_coords_x;
164 int set_osd_coords_y;
165
166 /* Current dimensions (NOT VISIBLE SIZE!) */
167 int display_width;
168 int display_height;
169 int display_byte_stride;
170
171 /* Current bits per pixel */
172 int bits_per_pixel;
173 int bytes_per_pixel;
174
175 /* Frame buffer stuff */
176 struct fb_info ivtvfb_info;
177 struct fb_var_screeninfo ivtvfb_defined;
178 struct fb_fix_screeninfo ivtvfb_fix;
179};
180
181struct ivtv_osd_coords {
182 unsigned long offset;
183 unsigned long max_offset;
184 int pixel_stride;
185 int lines;
186 int x;
187 int y;
188};
189
190/* --------------------------------------------------------------------- */
191
192/* ivtv API calls for framebuffer related support */
193
Hans Verkuil641ed492007-08-28 03:24:31 -0300194static int ivtvfb_get_framebuffer(struct ivtv *itv, u32 *fbbase,
Hans Verkuil32db7752007-07-20 09:29:43 -0300195 u32 *fblength)
196{
197 u32 data[CX2341X_MBOX_MAX_DATA];
198 int rc;
199
200 rc = ivtv_vapi_result(itv, data, CX2341X_OSD_GET_FRAMEBUFFER, 0);
201 *fbbase = data[0];
202 *fblength = data[1];
203 return rc;
204}
205
Hans Verkuil641ed492007-08-28 03:24:31 -0300206static int ivtvfb_get_osd_coords(struct ivtv *itv,
Hans Verkuil32db7752007-07-20 09:29:43 -0300207 struct ivtv_osd_coords *osd)
208{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300209 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300210 u32 data[CX2341X_MBOX_MAX_DATA];
211
212 ivtv_vapi_result(itv, data, CX2341X_OSD_GET_OSD_COORDS, 0);
213
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300214 osd->offset = data[0] - oi->video_rbase;
215 osd->max_offset = oi->display_width * oi->display_height * 4;
Hans Verkuil32db7752007-07-20 09:29:43 -0300216 osd->pixel_stride = data[1];
217 osd->lines = data[2];
218 osd->x = data[3];
219 osd->y = data[4];
220 return 0;
221}
222
Hans Verkuil641ed492007-08-28 03:24:31 -0300223static int ivtvfb_set_osd_coords(struct ivtv *itv, const struct ivtv_osd_coords *osd)
Hans Verkuil32db7752007-07-20 09:29:43 -0300224{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300225 struct osd_info *oi = itv->osd_info;
226
227 oi->display_width = osd->pixel_stride;
228 oi->display_byte_stride = osd->pixel_stride * oi->bytes_per_pixel;
229 oi->set_osd_coords_x += osd->x;
230 oi->set_osd_coords_y = osd->y;
Hans Verkuil32db7752007-07-20 09:29:43 -0300231
232 return ivtv_vapi(itv, CX2341X_OSD_SET_OSD_COORDS, 5,
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300233 osd->offset + oi->video_rbase,
Hans Verkuil32db7752007-07-20 09:29:43 -0300234 osd->pixel_stride,
235 osd->lines, osd->x, osd->y);
236}
237
Hans Verkuil641ed492007-08-28 03:24:31 -0300238static int ivtvfb_set_display_window(struct ivtv *itv, struct v4l2_rect *ivtv_window)
Hans Verkuil32db7752007-07-20 09:29:43 -0300239{
Hans Verkuil32db7752007-07-20 09:29:43 -0300240 int osd_height_limit = itv->is_50hz ? 576 : 480;
241
242 /* Only fail if resolution too high, otherwise fudge the start coords. */
243 if ((ivtv_window->height > osd_height_limit) || (ivtv_window->width > IVTV_OSD_MAX_WIDTH))
244 return -EINVAL;
245
246 /* Ensure we don't exceed display limits */
247 if (ivtv_window->top + ivtv_window->height > osd_height_limit) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300248 IVTVFB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid height setting (%d, %d)\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300249 ivtv_window->top, ivtv_window->height);
250 ivtv_window->top = osd_height_limit - ivtv_window->height;
251 }
252
253 if (ivtv_window->left + ivtv_window->width > IVTV_OSD_MAX_WIDTH) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300254 IVTVFB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid width setting (%d, %d)\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300255 ivtv_window->left, ivtv_window->width);
256 ivtv_window->left = IVTV_OSD_MAX_WIDTH - ivtv_window->width;
257 }
258
259 /* Set the OSD origin */
260 write_reg((ivtv_window->top << 16) | ivtv_window->left, 0x02a04);
261
262 /* How much to display */
263 write_reg(((ivtv_window->top+ivtv_window->height) << 16) | (ivtv_window->left+ivtv_window->width), 0x02a08);
264
265 /* Pass this info back the yuv handler */
266 itv->yuv_info.osd_vis_w = ivtv_window->width;
267 itv->yuv_info.osd_vis_h = ivtv_window->height;
268 itv->yuv_info.osd_x_offset = ivtv_window->left;
269 itv->yuv_info.osd_y_offset = ivtv_window->top;
270
271 return 0;
272}
273
Hans Verkuil641ed492007-08-28 03:24:31 -0300274static int ivtvfb_prep_dec_dma_to_device(struct ivtv *itv,
Hans Verkuil32db7752007-07-20 09:29:43 -0300275 unsigned long ivtv_dest_addr, void __user *userbuf,
276 int size_in_bytes)
277{
278 DEFINE_WAIT(wait);
Hans Verkuil32db7752007-07-20 09:29:43 -0300279 int got_sig = 0;
280
281 mutex_lock(&itv->udma.lock);
282 /* Map User DMA */
283 if (ivtv_udma_setup(itv, ivtv_dest_addr, userbuf, size_in_bytes) <= 0) {
284 mutex_unlock(&itv->udma.lock);
Hans Verkuil641ed492007-08-28 03:24:31 -0300285 IVTVFB_WARN("ivtvfb_prep_dec_dma_to_device, "
Hans Verkuil32db7752007-07-20 09:29:43 -0300286 "Error with get_user_pages: %d bytes, %d pages returned\n",
287 size_in_bytes, itv->udma.page_count);
288
289 /* get_user_pages must have failed completely */
290 return -EIO;
291 }
292
Hans Verkuil641ed492007-08-28 03:24:31 -0300293 IVTVFB_DEBUG_INFO("ivtvfb_prep_dec_dma_to_device, %d bytes, %d pages\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300294 size_in_bytes, itv->udma.page_count);
295
296 ivtv_udma_prepare(itv);
297 prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
298 /* if no UDMA is pending and no UDMA is in progress, then the DMA
299 is finished */
300 while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) {
301 /* don't interrupt if the DMA is in progress but break off
302 a still pending DMA. */
303 got_sig = signal_pending(current);
304 if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))
305 break;
306 got_sig = 0;
307 schedule();
308 }
309 finish_wait(&itv->dma_waitq, &wait);
310
311 /* Unmap Last DMA Xfer */
312 ivtv_udma_unmap(itv);
313 mutex_unlock(&itv->udma.lock);
314 if (got_sig) {
315 IVTV_DEBUG_INFO("User stopped OSD\n");
316 return -EINTR;
317 }
318
Ian Armstrongc7775492008-10-04 10:28:24 -0300319 return 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300320}
321
Hans Verkuil641ed492007-08-28 03:24:31 -0300322static int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source,
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300323 unsigned long dest_offset, int count)
Hans Verkuil32db7752007-07-20 09:29:43 -0300324{
325 DEFINE_WAIT(wait);
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300326 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300327
328 /* Nothing to do */
329 if (count == 0) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300330 IVTVFB_DEBUG_WARN("ivtvfb_prep_frame: Nothing to do. count = 0\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300331 return -EINVAL;
332 }
333
334 /* Check Total FB Size */
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300335 if ((dest_offset + count) > oi->video_buffer_size) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300336 IVTVFB_WARN("ivtvfb_prep_frame: Overflowing the framebuffer %ld, only %d available\n",
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300337 dest_offset + count, oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -0300338 return -E2BIG;
339 }
340
341 /* Not fatal, but will have undesirable results */
342 if ((unsigned long)source & 3)
Hans Verkuil641ed492007-08-28 03:24:31 -0300343 IVTVFB_WARN("ivtvfb_prep_frame: Source address not 32 bit aligned (0x%08lx)\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300344 (unsigned long)source);
Hans Verkuil32db7752007-07-20 09:29:43 -0300345
346 if (dest_offset & 3)
Hans Verkuil641ed492007-08-28 03:24:31 -0300347 IVTVFB_WARN("ivtvfb_prep_frame: Dest offset not 32 bit aligned (%ld)\n", dest_offset);
Hans Verkuil32db7752007-07-20 09:29:43 -0300348
349 if (count & 3)
Hans Verkuil641ed492007-08-28 03:24:31 -0300350 IVTVFB_WARN("ivtvfb_prep_frame: Count not a multiple of 4 (%d)\n", count);
Hans Verkuil32db7752007-07-20 09:29:43 -0300351
352 /* Check Source */
353 if (!access_ok(VERIFY_READ, source + dest_offset, count)) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300354 IVTVFB_WARN("Invalid userspace pointer 0x%08lx\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300355 (unsigned long)source);
356
Hans Verkuil641ed492007-08-28 03:24:31 -0300357 IVTVFB_DEBUG_WARN("access_ok() failed for offset 0x%08lx source 0x%08lx count %d\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300358 dest_offset, (unsigned long)source,
359 count);
360 return -EINVAL;
361 }
362
363 /* OSD Address to send DMA to */
Hans Verkuil33c0fca2007-08-23 06:32:46 -0300364 dest_offset += IVTV_DECODER_OFFSET + oi->video_rbase;
Hans Verkuil32db7752007-07-20 09:29:43 -0300365
366 /* Fill Buffers */
Hans Verkuil641ed492007-08-28 03:24:31 -0300367 return ivtvfb_prep_dec_dma_to_device(itv, dest_offset, source, count);
Hans Verkuil32db7752007-07-20 09:29:43 -0300368}
369
Ian Armstrong4cbeb3712008-05-12 11:53:10 -0300370static ssize_t ivtvfb_write(struct fb_info *info, const char __user *buf,
Ian Armstrong4ee0e422008-10-06 03:03:18 -0300371 size_t count, loff_t *ppos)
Ian Armstrong4cbeb3712008-05-12 11:53:10 -0300372{
373 unsigned long p = *ppos;
374 void *dst;
375 int err = 0;
Ian Armstrongc7775492008-10-04 10:28:24 -0300376 int dma_err;
Ian Armstrong4cbeb3712008-05-12 11:53:10 -0300377 unsigned long total_size;
378 struct ivtv *itv = (struct ivtv *) info->par;
379 unsigned long dma_offset =
380 IVTV_DECODER_OFFSET + itv->osd_info->video_rbase;
381 unsigned long dma_size;
382 u16 lead = 0, tail = 0;
383
384 if (info->state != FBINFO_STATE_RUNNING)
385 return -EPERM;
386
387 total_size = info->screen_size;
388
389 if (total_size == 0)
390 total_size = info->fix.smem_len;
391
392 if (p > total_size)
393 return -EFBIG;
394
395 if (count > total_size) {
396 err = -EFBIG;
397 count = total_size;
398 }
399
400 if (count + p > total_size) {
401 if (!err)
402 err = -ENOSPC;
Ian Armstrong4cbeb3712008-05-12 11:53:10 -0300403 count = total_size - p;
404 }
405
406 dst = (void __force *) (info->screen_base + p);
407
408 if (info->fbops->fb_sync)
409 info->fbops->fb_sync(info);
410
Ian Armstrongc7775492008-10-04 10:28:24 -0300411 /* If transfer size > threshold and both src/dst
412 addresses are aligned, use DMA */
413 if (count >= 4096 &&
414 ((unsigned long)buf & 3) == ((unsigned long)dst & 3)) {
415 /* Odd address = can't DMA. Align */
416 if ((unsigned long)dst & 3) {
417 lead = 4 - ((unsigned long)dst & 3);
418 if (copy_from_user(dst, buf, lead))
419 return -EFAULT;
420 buf += lead;
421 dst += lead;
Ian Armstrong4cbeb3712008-05-12 11:53:10 -0300422 }
Ian Armstrongc7775492008-10-04 10:28:24 -0300423 /* DMA resolution is 32 bits */
424 if ((count - lead) & 3)
425 tail = (count - lead) & 3;
426 /* DMA the data */
427 dma_size = count - lead - tail;
428 dma_err = ivtvfb_prep_dec_dma_to_device(itv,
429 p + lead + dma_offset, (void __user *)buf, dma_size);
430 if (dma_err)
431 return dma_err;
432 dst += dma_size;
433 buf += dma_size;
434 /* Copy any leftover data */
435 if (tail && copy_from_user(dst, buf, tail))
436 return -EFAULT;
437 } else if (copy_from_user(dst, buf, count)) {
438 return -EFAULT;
Ian Armstrong4cbeb3712008-05-12 11:53:10 -0300439 }
440
441 if (!err)
442 *ppos += count;
443
444 return (err) ? err : count;
445}
446
Hans Verkuil32db7752007-07-20 09:29:43 -0300447static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
448{
449 DEFINE_WAIT(wait);
450 struct ivtv *itv = (struct ivtv *)info->par;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300451 int rc = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300452
453 switch (cmd) {
Hans Verkuil32db7752007-07-20 09:29:43 -0300454 case FBIOGET_VBLANK: {
455 struct fb_vblank vblank;
456 u32 trace;
457
458 vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT |
459 FB_VBLANK_HAVE_VSYNC;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300460 trace = read_reg(0x028c0) >> 16;
Ian Armstrongc7775492008-10-04 10:28:24 -0300461 if (itv->is_50hz && trace > 312)
462 trace -= 312;
463 else if (itv->is_60hz && trace > 262)
464 trace -= 262;
465 if (trace == 1)
466 vblank.flags |= FB_VBLANK_VSYNCING;
Hans Verkuila158f352007-08-23 11:31:57 -0300467 vblank.count = itv->last_vsync_field;
Hans Verkuil32db7752007-07-20 09:29:43 -0300468 vblank.vcount = trace;
469 vblank.hcount = 0;
470 if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank)))
471 return -EFAULT;
472 return 0;
473 }
474
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300475 case FBIO_WAITFORVSYNC:
Hans Verkuil32db7752007-07-20 09:29:43 -0300476 prepare_to_wait(&itv->vsync_waitq, &wait, TASK_INTERRUPTIBLE);
Ian Armstrongc7775492008-10-04 10:28:24 -0300477 if (!schedule_timeout(msecs_to_jiffies(50)))
478 rc = -ETIMEDOUT;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300479 finish_wait(&itv->vsync_waitq, &wait);
Hans Verkuil32db7752007-07-20 09:29:43 -0300480 return rc;
Hans Verkuil32db7752007-07-20 09:29:43 -0300481
Hans Verkuild715e762007-07-20 10:30:32 -0300482 case IVTVFB_IOC_DMA_FRAME: {
483 struct ivtvfb_dma_frame args;
Hans Verkuil32db7752007-07-20 09:29:43 -0300484
Hans Verkuil641ed492007-08-28 03:24:31 -0300485 IVTVFB_DEBUG_INFO("IVTVFB_IOC_DMA_FRAME\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300486 if (copy_from_user(&args, (void __user *)arg, sizeof(args)))
487 return -EFAULT;
488
Hans Verkuil641ed492007-08-28 03:24:31 -0300489 return ivtvfb_prep_frame(itv, cmd, args.source, args.dest_offset, args.count);
Hans Verkuil32db7752007-07-20 09:29:43 -0300490 }
491
492 default:
Hans Verkuil641ed492007-08-28 03:24:31 -0300493 IVTVFB_DEBUG_INFO("Unknown ioctl %08x\n", cmd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300494 return -EINVAL;
495 }
496 return 0;
497}
498
499/* Framebuffer device handling */
500
501static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var)
502{
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300503 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300504 struct ivtv_osd_coords ivtv_osd;
505 struct v4l2_rect ivtv_window;
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300506 int osd_mode = -1;
Hans Verkuil32db7752007-07-20 09:29:43 -0300507
Hans Verkuil641ed492007-08-28 03:24:31 -0300508 IVTVFB_DEBUG_INFO("ivtvfb_set_var\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300509
510 /* Select color space */
511 if (var->nonstd) /* YUV */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300512 write_reg(read_reg(0x02a00) | 0x0002000, 0x02a00);
Hans Verkuil32db7752007-07-20 09:29:43 -0300513 else /* RGB */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300514 write_reg(read_reg(0x02a00) & ~0x0002000, 0x02a00);
Hans Verkuil32db7752007-07-20 09:29:43 -0300515
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300516 /* Set the color mode */
Hans Verkuil32db7752007-07-20 09:29:43 -0300517 switch (var->bits_per_pixel) {
518 case 8:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300519 osd_mode = IVTV_OSD_BPP_8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300520 break;
521 case 32:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300522 osd_mode = IVTV_OSD_BPP_32;
Hans Verkuil32db7752007-07-20 09:29:43 -0300523 break;
524 case 16:
525 switch (var->green.length) {
526 case 4:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300527 osd_mode = IVTV_OSD_BPP_16_444;
Hans Verkuil32db7752007-07-20 09:29:43 -0300528 break;
529 case 5:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300530 osd_mode = IVTV_OSD_BPP_16_555;
Hans Verkuil32db7752007-07-20 09:29:43 -0300531 break;
532 case 6:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300533 osd_mode = IVTV_OSD_BPP_16_565;
Hans Verkuil32db7752007-07-20 09:29:43 -0300534 break;
535 default:
Hans Verkuil641ed492007-08-28 03:24:31 -0300536 IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300537 }
538 break;
539 default:
Hans Verkuil641ed492007-08-28 03:24:31 -0300540 IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300541 }
542
Ian Armstrong6659e3e2007-10-12 08:15:41 -0300543 /* Set video mode. Although rare, the display can become scrambled even
544 if we don't change mode. Always 'bounce' to osd_mode via mode 0 */
545 if (osd_mode != -1) {
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300546 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0);
547 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, osd_mode);
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300548 }
549
550 oi->bits_per_pixel = var->bits_per_pixel;
551 oi->bytes_per_pixel = var->bits_per_pixel / 8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300552
553 /* Set the flicker filter */
554 switch (var->vmode & FB_VMODE_MASK) {
555 case FB_VMODE_NONINTERLACED: /* Filter on */
556 ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 1);
557 break;
558 case FB_VMODE_INTERLACED: /* Filter off */
559 ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 0);
560 break;
561 default:
Hans Verkuil641ed492007-08-28 03:24:31 -0300562 IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid video mode\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300563 }
564
565 /* Read the current osd info */
Hans Verkuil641ed492007-08-28 03:24:31 -0300566 ivtvfb_get_osd_coords(itv, &ivtv_osd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300567
568 /* Now set the OSD to the size we want */
569 ivtv_osd.pixel_stride = var->xres_virtual;
570 ivtv_osd.lines = var->yres_virtual;
571 ivtv_osd.x = 0;
572 ivtv_osd.y = 0;
Hans Verkuil641ed492007-08-28 03:24:31 -0300573 ivtvfb_set_osd_coords(itv, &ivtv_osd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300574
575 /* Can't seem to find the right API combo for this.
576 Use another function which does what we need through direct register access. */
577 ivtv_window.width = var->xres;
578 ivtv_window.height = var->yres;
579
580 /* Minimum margin cannot be 0, as X won't allow such a mode */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300581 if (!var->upper_margin) var->upper_margin++;
582 if (!var->left_margin) var->left_margin++;
Hans Verkuil32db7752007-07-20 09:29:43 -0300583 ivtv_window.top = var->upper_margin - 1;
584 ivtv_window.left = var->left_margin - 1;
585
Hans Verkuil641ed492007-08-28 03:24:31 -0300586 ivtvfb_set_display_window(itv, &ivtv_window);
Hans Verkuil32db7752007-07-20 09:29:43 -0300587
Ian Armstrong77aded62007-11-05 14:27:09 -0300588 /* Pass screen size back to yuv handler */
589 itv->yuv_info.osd_full_w = ivtv_osd.pixel_stride;
590 itv->yuv_info.osd_full_h = ivtv_osd.lines;
591
Hans Verkuil32db7752007-07-20 09:29:43 -0300592 /* Force update of yuv registers */
593 itv->yuv_info.yuv_forced_update = 1;
594
Hans Verkuil641ed492007-08-28 03:24:31 -0300595 IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300596 var->xres, var->yres,
597 var->xres_virtual, var->yres_virtual,
598 var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300599
Hans Verkuil641ed492007-08-28 03:24:31 -0300600 IVTVFB_DEBUG_INFO("Display position: %d, %d\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300601 var->left_margin, var->upper_margin);
Hans Verkuil32db7752007-07-20 09:29:43 -0300602
Hans Verkuil641ed492007-08-28 03:24:31 -0300603 IVTVFB_DEBUG_INFO("Display filter: %s\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300604 (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
Hans Verkuil641ed492007-08-28 03:24:31 -0300605 IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
Hans Verkuil32db7752007-07-20 09:29:43 -0300606
607 return 0;
608}
609
610static int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix)
611{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300612 struct osd_info *oi = itv->osd_info;
613
Hans Verkuil641ed492007-08-28 03:24:31 -0300614 IVTVFB_DEBUG_INFO("ivtvfb_get_fix\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300615 memset(fix, 0, sizeof(struct fb_fix_screeninfo));
Hans Verkuilcebfadf2008-04-26 08:51:51 -0300616 strlcpy(fix->id, "cx23415 TV out", sizeof(fix->id));
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300617 fix->smem_start = oi->video_pbase;
618 fix->smem_len = oi->video_buffer_size;
Hans Verkuil32db7752007-07-20 09:29:43 -0300619 fix->type = FB_TYPE_PACKED_PIXELS;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300620 fix->visual = (oi->bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
Hans Verkuil32db7752007-07-20 09:29:43 -0300621 fix->xpanstep = 1;
622 fix->ypanstep = 1;
623 fix->ywrapstep = 0;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300624 fix->line_length = oi->display_byte_stride;
Hans Verkuil32db7752007-07-20 09:29:43 -0300625 fix->accel = FB_ACCEL_NONE;
626 return 0;
627}
628
629/* Check the requested display mode, returning -EINVAL if we can't
630 handle it. */
631
632static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv)
633{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300634 struct osd_info *oi = itv->osd_info;
Ian Armstrong68a341a2007-08-03 09:51:58 -0300635 int osd_height_limit;
636 u32 pixclock, hlimit, vlimit;
Hans Verkuil32db7752007-07-20 09:29:43 -0300637
Hans Verkuil641ed492007-08-28 03:24:31 -0300638 IVTVFB_DEBUG_INFO("ivtvfb_check_var\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300639
Ian Armstrong68a341a2007-08-03 09:51:58 -0300640 /* Set base references for mode calcs. */
641 if (itv->is_50hz) {
642 pixclock = 84316;
643 hlimit = 776;
644 vlimit = 591;
645 osd_height_limit = 576;
646 }
647 else {
648 pixclock = 83926;
649 hlimit = 776;
650 vlimit = 495;
651 osd_height_limit = 480;
652 }
653
Hans Verkuil32db7752007-07-20 09:29:43 -0300654 if (var->bits_per_pixel == 8 || var->bits_per_pixel == 32) {
655 var->transp.offset = 24;
656 var->transp.length = 8;
657 var->red.offset = 16;
658 var->red.length = 8;
659 var->green.offset = 8;
660 var->green.length = 8;
661 var->blue.offset = 0;
662 var->blue.length = 8;
663 }
664 else if (var->bits_per_pixel == 16) {
665 /* To find out the true mode, check green length */
666 switch (var->green.length) {
667 case 4:
Hans Verkuil32db7752007-07-20 09:29:43 -0300668 var->red.offset = 8;
669 var->red.length = 4;
670 var->green.offset = 4;
671 var->green.length = 4;
672 var->blue.offset = 0;
673 var->blue.length = 4;
Hans Verkuil459a52f2007-08-22 08:58:47 -0300674 var->transp.offset = 12;
675 var->transp.length = 1;
Hans Verkuil32db7752007-07-20 09:29:43 -0300676 break;
677 case 5:
Hans Verkuil32db7752007-07-20 09:29:43 -0300678 var->red.offset = 10;
679 var->red.length = 5;
680 var->green.offset = 5;
681 var->green.length = 5;
682 var->blue.offset = 0;
683 var->blue.length = 5;
Hans Verkuil459a52f2007-08-22 08:58:47 -0300684 var->transp.offset = 15;
685 var->transp.length = 1;
Hans Verkuil32db7752007-07-20 09:29:43 -0300686 break;
687 default:
Hans Verkuil32db7752007-07-20 09:29:43 -0300688 var->red.offset = 11;
689 var->red.length = 5;
690 var->green.offset = 5;
691 var->green.length = 6;
692 var->blue.offset = 0;
693 var->blue.length = 5;
Hans Verkuil459a52f2007-08-22 08:58:47 -0300694 var->transp.offset = 0;
695 var->transp.length = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300696 break;
697 }
698 }
699 else {
Hans Verkuil641ed492007-08-28 03:24:31 -0300700 IVTVFB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300701 return -EINVAL;
702 }
703
704 /* Check the resolution */
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300705 if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit) {
706 IVTVFB_DEBUG_WARN("Invalid resolution: %dx%d\n",
707 var->xres, var->yres);
708 return -EINVAL;
Hans Verkuil32db7752007-07-20 09:29:43 -0300709 }
Hans Verkuil32db7752007-07-20 09:29:43 -0300710
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300711 /* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */
712 if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) ||
713 var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8) > oi->video_buffer_size ||
714 var->xres_virtual < var->xres ||
715 var->yres_virtual < var->yres) {
716 IVTVFB_DEBUG_WARN("Invalid virtual resolution: %dx%d\n",
717 var->xres_virtual, var->yres_virtual);
718 return -EINVAL;
Hans Verkuil32db7752007-07-20 09:29:43 -0300719 }
720
721 /* Some extra checks if in 8 bit mode */
722 if (var->bits_per_pixel == 8) {
723 /* Width must be a multiple of 4 */
724 if (var->xres & 3) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300725 IVTVFB_DEBUG_WARN("Invalid resolution for 8bpp: %d\n", var->xres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300726 return -EINVAL;
727 }
728 if (var->xres_virtual & 3) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300729 IVTVFB_DEBUG_WARN("Invalid virtual resolution for 8bpp: %d)\n", var->xres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300730 return -EINVAL;
731 }
732 }
733 else if (var->bits_per_pixel == 16) {
734 /* Width must be a multiple of 2 */
735 if (var->xres & 1) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300736 IVTVFB_DEBUG_WARN("Invalid resolution for 16bpp: %d\n", var->xres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300737 return -EINVAL;
738 }
739 if (var->xres_virtual & 1) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300740 IVTVFB_DEBUG_WARN("Invalid virtual resolution for 16bpp: %d)\n", var->xres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300741 return -EINVAL;
742 }
743 }
744
745 /* Now check the offsets */
746 if (var->xoffset >= var->xres_virtual || var->yoffset >= var->yres_virtual) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300747 IVTVFB_DEBUG_WARN("Invalid offset: %d (%d) %d (%d)\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300748 var->xoffset, var->xres_virtual, var->yoffset, var->yres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300749 return -EINVAL;
750 }
751
752 /* Check pixel format */
753 if (var->nonstd > 1) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300754 IVTVFB_DEBUG_WARN("Invalid nonstd % d\n", var->nonstd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300755 return -EINVAL;
756 }
757
758 /* Check video mode */
759 if (((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) &&
760 ((var->vmode & FB_VMODE_MASK) != FB_VMODE_INTERLACED)) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300761 IVTVFB_DEBUG_WARN("Invalid video mode: %d\n", var->vmode & FB_VMODE_MASK);
Hans Verkuil32db7752007-07-20 09:29:43 -0300762 return -EINVAL;
763 }
764
765 /* Check the left & upper margins
766 If the margins are too large, just center the screen
767 (enforcing margins causes too many problems) */
768
769 if (var->left_margin + var->xres > IVTV_OSD_MAX_WIDTH + 1) {
770 var->left_margin = 1 + ((IVTV_OSD_MAX_WIDTH - var->xres) / 2);
771 }
772 if (var->upper_margin + var->yres > (itv->is_50hz ? 577 : 481)) {
773 var->upper_margin = 1 + (((itv->is_50hz ? 576 : 480) - var->yres) / 2);
774 }
775
776 /* Maintain overall 'size' for a constant refresh rate */
Ian Armstrong68a341a2007-08-03 09:51:58 -0300777 var->right_margin = hlimit - var->left_margin - var->xres;
778 var->lower_margin = vlimit - var->upper_margin - var->yres;
Hans Verkuil32db7752007-07-20 09:29:43 -0300779
780 /* Fixed sync times */
781 var->hsync_len = 24;
782 var->vsync_len = 2;
783
784 /* Non-interlaced / interlaced mode is used to switch the OSD filter
785 on or off. Adjust the clock timings to maintain a constant
786 vertical refresh rate. */
Hans Verkuil32db7752007-07-20 09:29:43 -0300787 if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED)
Ian Armstrong68a341a2007-08-03 09:51:58 -0300788 var->pixclock = pixclock / 2;
789 else
790 var->pixclock = pixclock;
Hans Verkuil32db7752007-07-20 09:29:43 -0300791
Hans Verkuil37f89f92008-06-22 11:57:31 -0300792 itv->osd_rect.width = var->xres;
793 itv->osd_rect.height = var->yres;
794
Hans Verkuil641ed492007-08-28 03:24:31 -0300795 IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300796 var->xres, var->yres,
797 var->xres_virtual, var->yres_virtual,
Hans Verkuil32db7752007-07-20 09:29:43 -0300798 var->bits_per_pixel);
799
Hans Verkuil641ed492007-08-28 03:24:31 -0300800 IVTVFB_DEBUG_INFO("Display position: %d, %d\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300801 var->left_margin, var->upper_margin);
Hans Verkuil32db7752007-07-20 09:29:43 -0300802
Hans Verkuil641ed492007-08-28 03:24:31 -0300803 IVTVFB_DEBUG_INFO("Display filter: %s\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300804 (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
Hans Verkuil641ed492007-08-28 03:24:31 -0300805 IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
Hans Verkuil32db7752007-07-20 09:29:43 -0300806 return 0;
807}
808
809static int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
810{
811 struct ivtv *itv = (struct ivtv *) info->par;
Hans Verkuil641ed492007-08-28 03:24:31 -0300812 IVTVFB_DEBUG_INFO("ivtvfb_check_var\n");
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300813 return _ivtvfb_check_var(var, itv);
Hans Verkuil32db7752007-07-20 09:29:43 -0300814}
815
816static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
817{
818 u32 osd_pan_index;
819 struct ivtv *itv = (struct ivtv *) info->par;
820
821 osd_pan_index = (var->xoffset + (var->yoffset * var->xres_virtual))*var->bits_per_pixel/8;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300822 write_reg(osd_pan_index, 0x02A0C);
Hans Verkuil32db7752007-07-20 09:29:43 -0300823
824 /* Pass this info back the yuv handler */
825 itv->yuv_info.osd_x_pan = var->xoffset;
826 itv->yuv_info.osd_y_pan = var->yoffset;
827 /* Force update of yuv registers */
828 itv->yuv_info.yuv_forced_update = 1;
829 return 0;
830}
831
832static int ivtvfb_set_par(struct fb_info *info)
833{
834 int rc = 0;
835 struct ivtv *itv = (struct ivtv *) info->par;
836
Hans Verkuil641ed492007-08-28 03:24:31 -0300837 IVTVFB_DEBUG_INFO("ivtvfb_set_par\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300838
839 rc = ivtvfb_set_var(itv, &info->var);
840 ivtvfb_pan_display(&info->var, info);
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300841 ivtvfb_get_fix(itv, &info->fix);
Hans Verkuil32db7752007-07-20 09:29:43 -0300842 return rc;
843}
844
845static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green,
846 unsigned blue, unsigned transp,
847 struct fb_info *info)
848{
849 u32 color, *palette;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300850 struct ivtv *itv = (struct ivtv *)info->par;
Hans Verkuil32db7752007-07-20 09:29:43 -0300851
852 if (regno >= info->cmap.len)
853 return -EINVAL;
854
855 color = ((transp & 0xFF00) << 16) |((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8);
856 if (info->var.bits_per_pixel <= 8) {
857 write_reg(regno, 0x02a30);
858 write_reg(color, 0x02a34);
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300859 return 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300860 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300861 if (regno >= 16)
862 return -EINVAL;
Hans Verkuil32db7752007-07-20 09:29:43 -0300863
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300864 palette = info->pseudo_palette;
865 if (info->var.bits_per_pixel == 16) {
866 switch (info->var.green.length) {
867 case 4:
868 color = ((red & 0xf000) >> 4) |
869 ((green & 0xf000) >> 8) |
870 ((blue & 0xf000) >> 12);
871 break;
872 case 5:
873 color = ((red & 0xf800) >> 1) |
874 ((green & 0xf800) >> 6) |
875 ((blue & 0xf800) >> 11);
876 break;
877 case 6:
878 color = (red & 0xf800 ) |
879 ((green & 0xfc00) >> 5) |
880 ((blue & 0xf800) >> 11);
881 break;
Hans Verkuil32db7752007-07-20 09:29:43 -0300882 }
Hans Verkuil32db7752007-07-20 09:29:43 -0300883 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300884 palette[regno] = color;
Hans Verkuil32db7752007-07-20 09:29:43 -0300885 return 0;
886}
887
888/* We don't really support blanking. All this does is enable or
889 disable the OSD. */
890static int ivtvfb_blank(int blank_mode, struct fb_info *info)
891{
892 struct ivtv *itv = (struct ivtv *)info->par;
893
Hans Verkuil641ed492007-08-28 03:24:31 -0300894 IVTVFB_DEBUG_INFO("Set blanking mode : %d\n", blank_mode);
Hans Verkuil32db7752007-07-20 09:29:43 -0300895 switch (blank_mode) {
896 case FB_BLANK_UNBLANK:
897 ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1);
Ian Armstrong4e7ca402008-10-19 18:58:26 -0300898 ivtv_saa7127(itv, VIDIOC_STREAMON, NULL);
Hans Verkuil32db7752007-07-20 09:29:43 -0300899 break;
900 case FB_BLANK_NORMAL:
901 case FB_BLANK_HSYNC_SUSPEND:
902 case FB_BLANK_VSYNC_SUSPEND:
Ian Armstrong4e7ca402008-10-19 18:58:26 -0300903 ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
904 ivtv_saa7127(itv, VIDIOC_STREAMON, NULL);
905 break;
Hans Verkuil32db7752007-07-20 09:29:43 -0300906 case FB_BLANK_POWERDOWN:
Ian Armstrong4e7ca402008-10-19 18:58:26 -0300907 ivtv_saa7127(itv, VIDIOC_STREAMOFF, NULL);
Hans Verkuil32db7752007-07-20 09:29:43 -0300908 ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
909 break;
910 }
911 return 0;
912}
913
914static struct fb_ops ivtvfb_ops = {
915 .owner = THIS_MODULE,
Ian Armstrong4cbeb3712008-05-12 11:53:10 -0300916 .fb_write = ivtvfb_write,
Hans Verkuil32db7752007-07-20 09:29:43 -0300917 .fb_check_var = ivtvfb_check_var,
918 .fb_set_par = ivtvfb_set_par,
919 .fb_setcolreg = ivtvfb_setcolreg,
920 .fb_fillrect = cfb_fillrect,
921 .fb_copyarea = cfb_copyarea,
922 .fb_imageblit = cfb_imageblit,
923 .fb_cursor = NULL,
924 .fb_ioctl = ivtvfb_ioctl,
925 .fb_pan_display = ivtvfb_pan_display,
926 .fb_blank = ivtvfb_blank,
927};
928
929/* Initialization */
930
931
932/* Setup our initial video mode */
933static int ivtvfb_init_vidmode(struct ivtv *itv)
934{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300935 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300936 struct v4l2_rect start_window;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300937 int max_height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300938
Hans Verkuil32db7752007-07-20 09:29:43 -0300939 /* Color mode */
940
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300941 if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32)
942 osd_depth = 8;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300943 oi->bits_per_pixel = osd_depth;
944 oi->bytes_per_pixel = oi->bits_per_pixel / 8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300945
946 /* Horizontal size & position */
947
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300948 if (osd_xres > 720)
949 osd_xres = 720;
Hans Verkuil32db7752007-07-20 09:29:43 -0300950
951 /* Must be a multiple of 4 for 8bpp & 2 for 16bpp */
952 if (osd_depth == 8)
953 osd_xres &= ~3;
954 else if (osd_depth == 16)
955 osd_xres &= ~1;
956
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300957 start_window.width = osd_xres ? osd_xres : 640;
Hans Verkuil32db7752007-07-20 09:29:43 -0300958
959 /* Check horizontal start (osd_left). */
960 if (osd_left && osd_left + start_window.width > 721) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300961 IVTVFB_ERR("Invalid osd_left - assuming default\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300962 osd_left = 0;
963 }
964
965 /* Hardware coords start at 0, user coords start at 1. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300966 osd_left--;
Hans Verkuil32db7752007-07-20 09:29:43 -0300967
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300968 start_window.left = osd_left >= 0 ? osd_left : ((IVTV_OSD_MAX_WIDTH - start_window.width) / 2);
Hans Verkuil32db7752007-07-20 09:29:43 -0300969
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300970 oi->display_byte_stride =
971 start_window.width * oi->bytes_per_pixel;
Hans Verkuil32db7752007-07-20 09:29:43 -0300972
973 /* Vertical size & position */
974
975 max_height = itv->is_50hz ? 576 : 480;
976
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300977 if (osd_yres > max_height)
978 osd_yres = max_height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300979
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300980 start_window.height = osd_yres ? osd_yres : itv->is_50hz ? 480 : 400;
Hans Verkuil32db7752007-07-20 09:29:43 -0300981
982 /* Check vertical start (osd_upper). */
983 if (osd_upper + start_window.height > max_height + 1) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300984 IVTVFB_ERR("Invalid osd_upper - assuming default\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300985 osd_upper = 0;
986 }
987
988 /* Hardware coords start at 0, user coords start at 1. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300989 osd_upper--;
Hans Verkuil32db7752007-07-20 09:29:43 -0300990
991 start_window.top = osd_upper >= 0 ? osd_upper : ((max_height - start_window.height) / 2);
992
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300993 oi->display_width = start_window.width;
994 oi->display_height = start_window.height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300995
996 /* Generate a valid fb_var_screeninfo */
997
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300998 oi->ivtvfb_defined.xres = oi->display_width;
999 oi->ivtvfb_defined.yres = oi->display_height;
1000 oi->ivtvfb_defined.xres_virtual = oi->display_width;
1001 oi->ivtvfb_defined.yres_virtual = oi->display_height;
1002 oi->ivtvfb_defined.bits_per_pixel = oi->bits_per_pixel;
1003 oi->ivtvfb_defined.vmode = (osd_laced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED);
1004 oi->ivtvfb_defined.left_margin = start_window.left + 1;
1005 oi->ivtvfb_defined.upper_margin = start_window.top + 1;
1006 oi->ivtvfb_defined.accel_flags = FB_ACCEL_NONE;
1007 oi->ivtvfb_defined.nonstd = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -03001008
1009 /* We've filled in the most data, let the usual mode check
1010 routine fill in the rest. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001011 _ivtvfb_check_var(&oi->ivtvfb_defined, itv);
Hans Verkuil32db7752007-07-20 09:29:43 -03001012
1013 /* Generate valid fb_fix_screeninfo */
1014
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001015 ivtvfb_get_fix(itv, &oi->ivtvfb_fix);
Hans Verkuil32db7752007-07-20 09:29:43 -03001016
1017 /* Generate valid fb_info */
1018
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001019 oi->ivtvfb_info.node = -1;
1020 oi->ivtvfb_info.flags = FBINFO_FLAG_DEFAULT;
1021 oi->ivtvfb_info.fbops = &ivtvfb_ops;
1022 oi->ivtvfb_info.par = itv;
1023 oi->ivtvfb_info.var = oi->ivtvfb_defined;
1024 oi->ivtvfb_info.fix = oi->ivtvfb_fix;
1025 oi->ivtvfb_info.screen_base = (u8 __iomem *)oi->video_vbase;
1026 oi->ivtvfb_info.fbops = &ivtvfb_ops;
Hans Verkuil32db7752007-07-20 09:29:43 -03001027
1028 /* Supply some monitor specs. Bogus values will do for now */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001029 oi->ivtvfb_info.monspecs.hfmin = 8000;
1030 oi->ivtvfb_info.monspecs.hfmax = 70000;
1031 oi->ivtvfb_info.monspecs.vfmin = 10;
1032 oi->ivtvfb_info.monspecs.vfmax = 100;
Hans Verkuil32db7752007-07-20 09:29:43 -03001033
1034 /* Allocate color map */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001035 if (fb_alloc_cmap(&oi->ivtvfb_info.cmap, 256, 1)) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001036 IVTVFB_ERR("abort, unable to alloc cmap\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001037 return -ENOMEM;
1038 }
1039
1040 /* Allocate the pseudo palette */
Hans Verkuil3f983872008-05-01 10:31:12 -03001041 oi->ivtvfb_info.pseudo_palette =
1042 kmalloc(sizeof(u32) * 16, GFP_KERNEL|__GFP_NOWARN);
Hans Verkuil32db7752007-07-20 09:29:43 -03001043
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001044 if (!oi->ivtvfb_info.pseudo_palette) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001045 IVTVFB_ERR("abort, unable to alloc pseudo pallete\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001046 return -ENOMEM;
1047 }
1048
1049 return 0;
1050}
1051
1052/* Find OSD buffer base & size. Add to mtrr. Zero osd buffer. */
1053
1054static int ivtvfb_init_io(struct ivtv *itv)
1055{
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001056 struct osd_info *oi = itv->osd_info;
1057
Hans Verkuil26e9d592007-08-25 05:41:52 -03001058 mutex_lock(&itv->serialize_lock);
Hans Verkuil6e5eb592007-07-25 12:55:52 -03001059 if (ivtv_init_on_first_open(itv)) {
Hans Verkuil26e9d592007-08-25 05:41:52 -03001060 mutex_unlock(&itv->serialize_lock);
Hans Verkuil641ed492007-08-28 03:24:31 -03001061 IVTVFB_ERR("Failed to initialize ivtv\n");
Hans Verkuil6e5eb592007-07-25 12:55:52 -03001062 return -ENXIO;
1063 }
Hans Verkuil26e9d592007-08-25 05:41:52 -03001064 mutex_unlock(&itv->serialize_lock);
Hans Verkuil6e5eb592007-07-25 12:55:52 -03001065
Hans Verkuil641ed492007-08-28 03:24:31 -03001066 ivtvfb_get_framebuffer(itv, &oi->video_rbase, &oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -03001067
1068 /* The osd buffer size depends on the number of video buffers allocated
1069 on the PVR350 itself. For now we'll hardcode the smallest osd buffer
1070 size to prevent any overlap. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001071 oi->video_buffer_size = 1704960;
Hans Verkuil32db7752007-07-20 09:29:43 -03001072
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001073 oi->video_pbase = itv->base_addr + IVTV_DECODER_OFFSET + oi->video_rbase;
1074 oi->video_vbase = itv->dec_mem + oi->video_rbase;
Hans Verkuil32db7752007-07-20 09:29:43 -03001075
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001076 if (!oi->video_vbase) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001077 IVTVFB_ERR("abort, video memory 0x%x @ 0x%lx isn't mapped!\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001078 oi->video_buffer_size, oi->video_pbase);
Hans Verkuil32db7752007-07-20 09:29:43 -03001079 return -EIO;
1080 }
1081
Hans Verkuil641ed492007-08-28 03:24:31 -03001082 IVTVFB_INFO("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001083 oi->video_pbase, oi->video_vbase,
1084 oi->video_buffer_size / 1024);
Hans Verkuil32db7752007-07-20 09:29:43 -03001085
1086#ifdef CONFIG_MTRR
1087 {
1088 /* Find the largest power of two that maps the whole buffer */
1089 int size_shift = 31;
1090
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001091 while (!(oi->video_buffer_size & (1 << size_shift))) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001092 size_shift--;
1093 }
1094 size_shift++;
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001095 oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1);
1096 oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size;
1097 oi->fb_end_aligned_physaddr += (1 << size_shift) - 1;
1098 oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1);
1099 if (mtrr_add(oi->fb_start_aligned_physaddr,
1100 oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr,
Hans Verkuil32db7752007-07-20 09:29:43 -03001101 MTRR_TYPE_WRCOMB, 1) < 0) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001102 IVTVFB_INFO("disabled mttr\n");
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001103 oi->fb_start_aligned_physaddr = 0;
1104 oi->fb_end_aligned_physaddr = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -03001105 }
1106 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001107#endif
Hans Verkuil32db7752007-07-20 09:29:43 -03001108
1109 /* Blank the entire osd. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001110 memset_io(oi->video_vbase, 0, oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -03001111
1112 return 0;
1113}
1114
1115/* Release any memory we've grabbed & remove mtrr entry */
1116static void ivtvfb_release_buffers (struct ivtv *itv)
1117{
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001118 struct osd_info *oi = itv->osd_info;
1119
Hans Verkuil32db7752007-07-20 09:29:43 -03001120 /* Release cmap */
Adrian Bunk13628032007-08-28 03:28:04 -03001121 if (oi->ivtvfb_info.cmap.len)
1122 fb_dealloc_cmap(&oi->ivtvfb_info.cmap);
Hans Verkuil32db7752007-07-20 09:29:43 -03001123
1124 /* Release pseudo palette */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001125 if (oi->ivtvfb_info.pseudo_palette)
1126 kfree(oi->ivtvfb_info.pseudo_palette);
Hans Verkuil32db7752007-07-20 09:29:43 -03001127
1128#ifdef CONFIG_MTRR
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001129 if (oi->fb_end_aligned_physaddr) {
1130 mtrr_del(-1, oi->fb_start_aligned_physaddr,
1131 oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr);
1132 }
1133#endif
Hans Verkuil32db7752007-07-20 09:29:43 -03001134
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001135 kfree(oi);
Hans Verkuil32db7752007-07-20 09:29:43 -03001136 itv->osd_info = NULL;
1137}
1138
1139/* Initialize the specified card */
1140
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001141static int ivtvfb_init_card(struct ivtv *itv)
Hans Verkuil32db7752007-07-20 09:29:43 -03001142{
1143 int rc;
1144
1145 if (itv->osd_info) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001146 IVTVFB_ERR("Card %d already initialised\n", ivtvfb_card_id);
Hans Verkuil32db7752007-07-20 09:29:43 -03001147 return -EBUSY;
1148 }
1149
Hans Verkuil3f983872008-05-01 10:31:12 -03001150 itv->osd_info = kzalloc(sizeof(struct osd_info),
1151 GFP_ATOMIC|__GFP_NOWARN);
Richard Knutsson14d5deb2007-12-08 10:35:06 -03001152 if (itv->osd_info == NULL) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001153 IVTVFB_ERR("Failed to allocate memory for osd_info\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001154 return -ENOMEM;
1155 }
1156
1157 /* Find & setup the OSD buffer */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001158 if ((rc = ivtvfb_init_io(itv)))
Hans Verkuil32db7752007-07-20 09:29:43 -03001159 return rc;
1160
1161 /* Set the startup video mode information */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001162 if ((rc = ivtvfb_init_vidmode(itv))) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001163 ivtvfb_release_buffers(itv);
1164 return rc;
1165 }
1166
1167 /* Register the framebuffer */
1168 if (register_framebuffer(&itv->osd_info->ivtvfb_info) < 0) {
1169 ivtvfb_release_buffers(itv);
1170 return -EINVAL;
1171 }
1172
1173 itv->osd_video_pbase = itv->osd_info->video_pbase;
1174
1175 /* Set the card to the requested mode */
1176 ivtvfb_set_par(&itv->osd_info->ivtvfb_info);
1177
1178 /* Set color 0 to black */
1179 write_reg(0, 0x02a30);
1180 write_reg(0, 0x02a34);
1181
1182 /* Enable the osd */
1183 ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info);
1184
Hans Verkuil32db7752007-07-20 09:29:43 -03001185 /* Allocate DMA */
1186 ivtv_udma_alloc(itv);
1187 return 0;
1188
1189}
1190
1191static int __init ivtvfb_init(void)
1192{
1193 struct ivtv *itv;
1194 int i, registered = 0;
1195
Hans Verkuil641ed492007-08-28 03:24:31 -03001196 if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) {
1197 printk(KERN_ERR "ivtvfb: ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n",
Hans Verkuil32db7752007-07-20 09:29:43 -03001198 IVTV_MAX_CARDS - 1);
1199 return -EINVAL;
1200 }
1201
1202 /* Locate & initialise all cards supporting an OSD. */
1203 for (i = 0; i < ivtv_cards_active; i++) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001204 if (ivtvfb_card_id != -1 && i != ivtvfb_card_id)
Hans Verkuil32db7752007-07-20 09:29:43 -03001205 continue;
1206 itv = ivtv_cards[i];
1207 if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
1208 if (ivtvfb_init_card(itv) == 0) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001209 IVTVFB_INFO("Framebuffer registered on ivtv card id %d\n", i);
Hans Verkuil32db7752007-07-20 09:29:43 -03001210 registered++;
1211 }
1212 }
1213 }
1214 if (!registered) {
Hans Verkuil7b3a0d42007-08-26 06:11:07 -03001215 printk(KERN_ERR "ivtvfb: no cards found");
Hans Verkuil32db7752007-07-20 09:29:43 -03001216 return -ENODEV;
1217 }
1218 return 0;
1219}
1220
1221static void ivtvfb_cleanup(void)
1222{
1223 struct ivtv *itv;
1224 int i;
1225
Hans Verkuil7b3a0d42007-08-26 06:11:07 -03001226 printk(KERN_INFO "ivtvfb: Unloading framebuffer module\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001227
1228 for (i = 0; i < ivtv_cards_active; i++) {
1229 itv = ivtv_cards[i];
1230 if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) && itv->osd_info) {
Hans Verkuild343d7f2007-10-14 11:17:14 -03001231 if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) {
1232 IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n", i);
1233 return;
1234 }
Hans Verkuil641ed492007-08-28 03:24:31 -03001235 IVTVFB_DEBUG_INFO("Unregister framebuffer %d\n", i);
Hans Verkuil32db7752007-07-20 09:29:43 -03001236 ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info);
Hans Verkuil32db7752007-07-20 09:29:43 -03001237 ivtvfb_release_buffers(itv);
1238 itv->osd_video_pbase = 0;
1239 }
1240 }
1241}
1242
1243module_init(ivtvfb_init);
1244module_exit(ivtvfb_cleanup);