blob: 14f93341f061ee0bb446ee502d6ec247bb8a6ff8 [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"
Hans Verkuil32db7752007-07-20 09:29:43 -030051#include "ivtv-udma.h"
Hans Verkuil32db7752007-07-20 09:29:43 -030052#include "ivtv-mailbox.h"
Hans Verkuil32db7752007-07-20 09:29:43 -030053
54/* card parameters */
Hans Verkuil641ed492007-08-28 03:24:31 -030055static int ivtvfb_card_id = -1;
56static int ivtvfb_debug = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -030057static int osd_laced;
Hans Verkuil32db7752007-07-20 09:29:43 -030058static int osd_depth;
59static int osd_upper;
60static int osd_left;
61static int osd_yres;
62static int osd_xres;
63
Hans Verkuil641ed492007-08-28 03:24:31 -030064module_param(ivtvfb_card_id, int, 0444);
65module_param_named(debug,ivtvfb_debug, int, 0644);
Hans Verkuil32db7752007-07-20 09:29:43 -030066module_param(osd_laced, bool, 0444);
Hans Verkuil32db7752007-07-20 09:29:43 -030067module_param(osd_depth, int, 0444);
68module_param(osd_upper, int, 0444);
69module_param(osd_left, int, 0444);
70module_param(osd_yres, int, 0444);
71module_param(osd_xres, int, 0444);
72
Hans Verkuil641ed492007-08-28 03:24:31 -030073MODULE_PARM_DESC(ivtvfb_card_id,
Hans Verkuil32db7752007-07-20 09:29:43 -030074 "Only use framebuffer of the specified ivtv card (0-31)\n"
75 "\t\t\tdefault -1: initialize all available framebuffers");
76
77MODULE_PARM_DESC(debug,
78 "Debug level (bitmask). Default: errors only\n"
79 "\t\t\t(debug = 3 gives full debugging)");
80
Hans Verkuil32db7752007-07-20 09:29:43 -030081/* Why upper, left, xres, yres, depth, laced ? To match terminology used
82 by fbset.
83 Why start at 1 for left & upper coordinate ? Because X doesn't allow 0 */
84
85MODULE_PARM_DESC(osd_laced,
86 "Interlaced mode\n"
87 "\t\t\t0=off\n"
88 "\t\t\t1=on\n"
89 "\t\t\tdefault off");
90
91MODULE_PARM_DESC(osd_depth,
Hans Verkuilbe383bd2007-07-20 10:16:03 -030092 "Bits per pixel - 8, 16, 32\n"
Hans Verkuil32db7752007-07-20 09:29:43 -030093 "\t\t\tdefault 8");
94
95MODULE_PARM_DESC(osd_upper,
96 "Vertical start position\n"
97 "\t\t\tdefault 0 (Centered)");
98
99MODULE_PARM_DESC(osd_left,
100 "Horizontal start position\n"
101 "\t\t\tdefault 0 (Centered)");
102
103MODULE_PARM_DESC(osd_yres,
104 "Display height\n"
105 "\t\t\tdefault 480 (PAL)\n"
106 "\t\t\t 400 (NTSC)");
107
108MODULE_PARM_DESC(osd_xres,
109 "Display width\n"
110 "\t\t\tdefault 640");
111
112MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil, John Harvey, Ian Armstrong");
113MODULE_LICENSE("GPL");
114
115/* --------------------------------------------------------------------- */
116
Hans Verkuil641ed492007-08-28 03:24:31 -0300117#define IVTVFB_DBGFLG_WARN (1 << 0)
118#define IVTVFB_DBGFLG_INFO (1 << 1)
Hans Verkuil32db7752007-07-20 09:29:43 -0300119
Hans Verkuil641ed492007-08-28 03:24:31 -0300120#define IVTVFB_DEBUG(x, type, fmt, args...) \
Hans Verkuil32db7752007-07-20 09:29:43 -0300121 do { \
Hans Verkuil641ed492007-08-28 03:24:31 -0300122 if ((x) & ivtvfb_debug) \
Hans Verkuil7b3a0d42007-08-26 06:11:07 -0300123 printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->num , ## args); \
Hans Verkuil32db7752007-07-20 09:29:43 -0300124 } while (0)
Hans Verkuil641ed492007-08-28 03:24:31 -0300125#define IVTVFB_DEBUG_WARN(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_WARN, "warning", fmt , ## args)
126#define IVTVFB_DEBUG_INFO(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_INFO, "info", fmt , ## args)
Hans Verkuil32db7752007-07-20 09:29:43 -0300127
128/* Standard kernel messages */
Hans Verkuil641ed492007-08-28 03:24:31 -0300129#define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->num , ## args)
130#define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->num , ## args)
131#define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->num , ## args)
Hans Verkuil32db7752007-07-20 09:29:43 -0300132
133/* --------------------------------------------------------------------- */
134
135#define IVTV_OSD_MAX_WIDTH 720
136#define IVTV_OSD_MAX_HEIGHT 576
137
138#define IVTV_OSD_BPP_8 0x00
139#define IVTV_OSD_BPP_16_444 0x03
140#define IVTV_OSD_BPP_16_555 0x02
141#define IVTV_OSD_BPP_16_565 0x01
142#define IVTV_OSD_BPP_32 0x04
143
144struct osd_info {
Hans Verkuil32db7752007-07-20 09:29:43 -0300145 /* Physical base address */
146 unsigned long video_pbase;
147 /* Relative base address (relative to start of decoder memory) */
148 u32 video_rbase;
149 /* Mapped base address */
150 volatile char __iomem *video_vbase;
151 /* Buffer size */
152 u32 video_buffer_size;
153
154#ifdef CONFIG_MTRR
155 /* video_base rounded down as required by hardware MTRRs */
156 unsigned long fb_start_aligned_physaddr;
157 /* video_base rounded up as required by hardware MTRRs */
158 unsigned long fb_end_aligned_physaddr;
159#endif
160
161 /* Store the buffer offset */
162 int set_osd_coords_x;
163 int set_osd_coords_y;
164
165 /* Current dimensions (NOT VISIBLE SIZE!) */
166 int display_width;
167 int display_height;
168 int display_byte_stride;
169
170 /* Current bits per pixel */
171 int bits_per_pixel;
172 int bytes_per_pixel;
173
174 /* Frame buffer stuff */
175 struct fb_info ivtvfb_info;
176 struct fb_var_screeninfo ivtvfb_defined;
177 struct fb_fix_screeninfo ivtvfb_fix;
178};
179
180struct ivtv_osd_coords {
181 unsigned long offset;
182 unsigned long max_offset;
183 int pixel_stride;
184 int lines;
185 int x;
186 int y;
187};
188
189/* --------------------------------------------------------------------- */
190
191/* ivtv API calls for framebuffer related support */
192
Hans Verkuil641ed492007-08-28 03:24:31 -0300193static int ivtvfb_get_framebuffer(struct ivtv *itv, u32 *fbbase,
Hans Verkuil32db7752007-07-20 09:29:43 -0300194 u32 *fblength)
195{
196 u32 data[CX2341X_MBOX_MAX_DATA];
197 int rc;
198
199 rc = ivtv_vapi_result(itv, data, CX2341X_OSD_GET_FRAMEBUFFER, 0);
200 *fbbase = data[0];
201 *fblength = data[1];
202 return rc;
203}
204
Hans Verkuil641ed492007-08-28 03:24:31 -0300205static int ivtvfb_get_osd_coords(struct ivtv *itv,
Hans Verkuil32db7752007-07-20 09:29:43 -0300206 struct ivtv_osd_coords *osd)
207{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300208 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300209 u32 data[CX2341X_MBOX_MAX_DATA];
210
211 ivtv_vapi_result(itv, data, CX2341X_OSD_GET_OSD_COORDS, 0);
212
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300213 osd->offset = data[0] - oi->video_rbase;
214 osd->max_offset = oi->display_width * oi->display_height * 4;
Hans Verkuil32db7752007-07-20 09:29:43 -0300215 osd->pixel_stride = data[1];
216 osd->lines = data[2];
217 osd->x = data[3];
218 osd->y = data[4];
219 return 0;
220}
221
Hans Verkuil641ed492007-08-28 03:24:31 -0300222static int ivtvfb_set_osd_coords(struct ivtv *itv, const struct ivtv_osd_coords *osd)
Hans Verkuil32db7752007-07-20 09:29:43 -0300223{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300224 struct osd_info *oi = itv->osd_info;
225
226 oi->display_width = osd->pixel_stride;
227 oi->display_byte_stride = osd->pixel_stride * oi->bytes_per_pixel;
228 oi->set_osd_coords_x += osd->x;
229 oi->set_osd_coords_y = osd->y;
Hans Verkuil32db7752007-07-20 09:29:43 -0300230
231 return ivtv_vapi(itv, CX2341X_OSD_SET_OSD_COORDS, 5,
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300232 osd->offset + oi->video_rbase,
Hans Verkuil32db7752007-07-20 09:29:43 -0300233 osd->pixel_stride,
234 osd->lines, osd->x, osd->y);
235}
236
Hans Verkuil641ed492007-08-28 03:24:31 -0300237static int ivtvfb_set_display_window(struct ivtv *itv, struct v4l2_rect *ivtv_window)
Hans Verkuil32db7752007-07-20 09:29:43 -0300238{
Hans Verkuil32db7752007-07-20 09:29:43 -0300239 int osd_height_limit = itv->is_50hz ? 576 : 480;
240
241 /* Only fail if resolution too high, otherwise fudge the start coords. */
242 if ((ivtv_window->height > osd_height_limit) || (ivtv_window->width > IVTV_OSD_MAX_WIDTH))
243 return -EINVAL;
244
245 /* Ensure we don't exceed display limits */
246 if (ivtv_window->top + ivtv_window->height > osd_height_limit) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300247 IVTVFB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid height setting (%d, %d)\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300248 ivtv_window->top, ivtv_window->height);
249 ivtv_window->top = osd_height_limit - ivtv_window->height;
250 }
251
252 if (ivtv_window->left + ivtv_window->width > IVTV_OSD_MAX_WIDTH) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300253 IVTVFB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid width setting (%d, %d)\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300254 ivtv_window->left, ivtv_window->width);
255 ivtv_window->left = IVTV_OSD_MAX_WIDTH - ivtv_window->width;
256 }
257
258 /* Set the OSD origin */
259 write_reg((ivtv_window->top << 16) | ivtv_window->left, 0x02a04);
260
261 /* How much to display */
262 write_reg(((ivtv_window->top+ivtv_window->height) << 16) | (ivtv_window->left+ivtv_window->width), 0x02a08);
263
264 /* Pass this info back the yuv handler */
265 itv->yuv_info.osd_vis_w = ivtv_window->width;
266 itv->yuv_info.osd_vis_h = ivtv_window->height;
267 itv->yuv_info.osd_x_offset = ivtv_window->left;
268 itv->yuv_info.osd_y_offset = ivtv_window->top;
269
270 return 0;
271}
272
Hans Verkuil641ed492007-08-28 03:24:31 -0300273static int ivtvfb_prep_dec_dma_to_device(struct ivtv *itv,
Hans Verkuil32db7752007-07-20 09:29:43 -0300274 unsigned long ivtv_dest_addr, void __user *userbuf,
275 int size_in_bytes)
276{
277 DEFINE_WAIT(wait);
278 int ret = 0;
279 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
319 return ret;
320}
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,
371 size_t count, loff_t *ppos)
372{
373 unsigned long p = *ppos;
374 void *dst;
375 int err = 0;
376 unsigned long total_size;
377 struct ivtv *itv = (struct ivtv *) info->par;
378 unsigned long dma_offset =
379 IVTV_DECODER_OFFSET + itv->osd_info->video_rbase;
380 unsigned long dma_size;
381 u16 lead = 0, tail = 0;
382
383 if (info->state != FBINFO_STATE_RUNNING)
384 return -EPERM;
385
386 total_size = info->screen_size;
387
388 if (total_size == 0)
389 total_size = info->fix.smem_len;
390
391 if (p > total_size)
392 return -EFBIG;
393
394 if (count > total_size) {
395 err = -EFBIG;
396 count = total_size;
397 }
398
399 if (count + p > total_size) {
400 if (!err)
401 err = -ENOSPC;
402
403 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
411 if (!access_ok(VERIFY_READ, buf, count)) {
412 IVTVFB_WARN("Invalid userspace pointer 0x%08lx\n",
413 (unsigned long)buf);
414 err = -EFAULT;
415 }
416
417 if (!err) {
418 /* If transfer size > threshold and both src/dst
419 addresses are aligned, use DMA */
420 if (count >= 4096 && ((u32)buf & 3) == ((u32)dst & 3)) {
421 /* Odd address = can't DMA. Align */
422 if ((u32)dst & 3) {
423 lead = 4 - ((u32)dst & 3);
424 memcpy(dst, buf, lead);
425 buf += lead;
426 dst += lead;
427 }
428 /* DMA resolution is 32 bits */
429 if ((count - lead) & 3)
430 tail = (count - lead) & 3;
431 /* DMA the data */
432 dma_size = count - lead - tail;
433 err = ivtvfb_prep_dec_dma_to_device(itv,
434 p + lead + dma_offset, (void *)buf, dma_size);
435 dst += dma_size;
436 buf += dma_size;
437 /* Copy any leftover data */
438 if (tail)
439 memcpy(dst, buf, tail);
440 } else {
441 memcpy(dst, buf, count);
442 }
443 }
444
445 if (!err)
446 *ppos += count;
447
448 return (err) ? err : count;
449}
450
Hans Verkuil32db7752007-07-20 09:29:43 -0300451static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
452{
453 DEFINE_WAIT(wait);
454 struct ivtv *itv = (struct ivtv *)info->par;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300455 int rc = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300456
457 switch (cmd) {
Hans Verkuil32db7752007-07-20 09:29:43 -0300458 case FBIOGET_VBLANK: {
459 struct fb_vblank vblank;
460 u32 trace;
461
462 vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT |
463 FB_VBLANK_HAVE_VSYNC;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300464 trace = read_reg(0x028c0) >> 16;
Hans Verkuil32db7752007-07-20 09:29:43 -0300465 if (itv->is_50hz && trace > 312) trace -= 312;
466 else if (itv->is_60hz && trace > 262) trace -= 262;
467 if (trace == 1) vblank.flags |= FB_VBLANK_VSYNCING;
Hans Verkuila158f352007-08-23 11:31:57 -0300468 vblank.count = itv->last_vsync_field;
Hans Verkuil32db7752007-07-20 09:29:43 -0300469 vblank.vcount = trace;
470 vblank.hcount = 0;
471 if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank)))
472 return -EFAULT;
473 return 0;
474 }
475
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300476 case FBIO_WAITFORVSYNC:
Hans Verkuil32db7752007-07-20 09:29:43 -0300477 prepare_to_wait(&itv->vsync_waitq, &wait, TASK_INTERRUPTIBLE);
Hans Verkuil84149a02007-07-20 18:17:18 -0300478 if (!schedule_timeout(msecs_to_jiffies(50))) 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);
898 break;
899 case FB_BLANK_NORMAL:
900 case FB_BLANK_HSYNC_SUSPEND:
901 case FB_BLANK_VSYNC_SUSPEND:
902 case FB_BLANK_POWERDOWN:
903 ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
904 break;
905 }
906 return 0;
907}
908
909static struct fb_ops ivtvfb_ops = {
910 .owner = THIS_MODULE,
Ian Armstrong4cbeb3712008-05-12 11:53:10 -0300911 .fb_write = ivtvfb_write,
Hans Verkuil32db7752007-07-20 09:29:43 -0300912 .fb_check_var = ivtvfb_check_var,
913 .fb_set_par = ivtvfb_set_par,
914 .fb_setcolreg = ivtvfb_setcolreg,
915 .fb_fillrect = cfb_fillrect,
916 .fb_copyarea = cfb_copyarea,
917 .fb_imageblit = cfb_imageblit,
918 .fb_cursor = NULL,
919 .fb_ioctl = ivtvfb_ioctl,
920 .fb_pan_display = ivtvfb_pan_display,
921 .fb_blank = ivtvfb_blank,
922};
923
924/* Initialization */
925
926
927/* Setup our initial video mode */
928static int ivtvfb_init_vidmode(struct ivtv *itv)
929{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300930 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300931 struct v4l2_rect start_window;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300932 int max_height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300933
Hans Verkuil32db7752007-07-20 09:29:43 -0300934 /* Color mode */
935
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300936 if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32)
937 osd_depth = 8;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300938 oi->bits_per_pixel = osd_depth;
939 oi->bytes_per_pixel = oi->bits_per_pixel / 8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300940
941 /* Horizontal size & position */
942
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300943 if (osd_xres > 720)
944 osd_xres = 720;
Hans Verkuil32db7752007-07-20 09:29:43 -0300945
946 /* Must be a multiple of 4 for 8bpp & 2 for 16bpp */
947 if (osd_depth == 8)
948 osd_xres &= ~3;
949 else if (osd_depth == 16)
950 osd_xres &= ~1;
951
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300952 start_window.width = osd_xres ? osd_xres : 640;
Hans Verkuil32db7752007-07-20 09:29:43 -0300953
954 /* Check horizontal start (osd_left). */
955 if (osd_left && osd_left + start_window.width > 721) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300956 IVTVFB_ERR("Invalid osd_left - assuming default\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300957 osd_left = 0;
958 }
959
960 /* Hardware coords start at 0, user coords start at 1. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300961 osd_left--;
Hans Verkuil32db7752007-07-20 09:29:43 -0300962
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300963 start_window.left = osd_left >= 0 ? osd_left : ((IVTV_OSD_MAX_WIDTH - start_window.width) / 2);
Hans Verkuil32db7752007-07-20 09:29:43 -0300964
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300965 oi->display_byte_stride =
966 start_window.width * oi->bytes_per_pixel;
Hans Verkuil32db7752007-07-20 09:29:43 -0300967
968 /* Vertical size & position */
969
970 max_height = itv->is_50hz ? 576 : 480;
971
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300972 if (osd_yres > max_height)
973 osd_yres = max_height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300974
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300975 start_window.height = osd_yres ? osd_yres : itv->is_50hz ? 480 : 400;
Hans Verkuil32db7752007-07-20 09:29:43 -0300976
977 /* Check vertical start (osd_upper). */
978 if (osd_upper + start_window.height > max_height + 1) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300979 IVTVFB_ERR("Invalid osd_upper - assuming default\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300980 osd_upper = 0;
981 }
982
983 /* Hardware coords start at 0, user coords start at 1. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300984 osd_upper--;
Hans Verkuil32db7752007-07-20 09:29:43 -0300985
986 start_window.top = osd_upper >= 0 ? osd_upper : ((max_height - start_window.height) / 2);
987
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300988 oi->display_width = start_window.width;
989 oi->display_height = start_window.height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300990
991 /* Generate a valid fb_var_screeninfo */
992
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300993 oi->ivtvfb_defined.xres = oi->display_width;
994 oi->ivtvfb_defined.yres = oi->display_height;
995 oi->ivtvfb_defined.xres_virtual = oi->display_width;
996 oi->ivtvfb_defined.yres_virtual = oi->display_height;
997 oi->ivtvfb_defined.bits_per_pixel = oi->bits_per_pixel;
998 oi->ivtvfb_defined.vmode = (osd_laced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED);
999 oi->ivtvfb_defined.left_margin = start_window.left + 1;
1000 oi->ivtvfb_defined.upper_margin = start_window.top + 1;
1001 oi->ivtvfb_defined.accel_flags = FB_ACCEL_NONE;
1002 oi->ivtvfb_defined.nonstd = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -03001003
1004 /* We've filled in the most data, let the usual mode check
1005 routine fill in the rest. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001006 _ivtvfb_check_var(&oi->ivtvfb_defined, itv);
Hans Verkuil32db7752007-07-20 09:29:43 -03001007
1008 /* Generate valid fb_fix_screeninfo */
1009
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001010 ivtvfb_get_fix(itv, &oi->ivtvfb_fix);
Hans Verkuil32db7752007-07-20 09:29:43 -03001011
1012 /* Generate valid fb_info */
1013
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001014 oi->ivtvfb_info.node = -1;
1015 oi->ivtvfb_info.flags = FBINFO_FLAG_DEFAULT;
1016 oi->ivtvfb_info.fbops = &ivtvfb_ops;
1017 oi->ivtvfb_info.par = itv;
1018 oi->ivtvfb_info.var = oi->ivtvfb_defined;
1019 oi->ivtvfb_info.fix = oi->ivtvfb_fix;
1020 oi->ivtvfb_info.screen_base = (u8 __iomem *)oi->video_vbase;
1021 oi->ivtvfb_info.fbops = &ivtvfb_ops;
Hans Verkuil32db7752007-07-20 09:29:43 -03001022
1023 /* Supply some monitor specs. Bogus values will do for now */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001024 oi->ivtvfb_info.monspecs.hfmin = 8000;
1025 oi->ivtvfb_info.monspecs.hfmax = 70000;
1026 oi->ivtvfb_info.monspecs.vfmin = 10;
1027 oi->ivtvfb_info.monspecs.vfmax = 100;
Hans Verkuil32db7752007-07-20 09:29:43 -03001028
1029 /* Allocate color map */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001030 if (fb_alloc_cmap(&oi->ivtvfb_info.cmap, 256, 1)) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001031 IVTVFB_ERR("abort, unable to alloc cmap\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001032 return -ENOMEM;
1033 }
1034
1035 /* Allocate the pseudo palette */
Hans Verkuil3f983872008-05-01 10:31:12 -03001036 oi->ivtvfb_info.pseudo_palette =
1037 kmalloc(sizeof(u32) * 16, GFP_KERNEL|__GFP_NOWARN);
Hans Verkuil32db7752007-07-20 09:29:43 -03001038
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001039 if (!oi->ivtvfb_info.pseudo_palette) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001040 IVTVFB_ERR("abort, unable to alloc pseudo pallete\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001041 return -ENOMEM;
1042 }
1043
1044 return 0;
1045}
1046
1047/* Find OSD buffer base & size. Add to mtrr. Zero osd buffer. */
1048
1049static int ivtvfb_init_io(struct ivtv *itv)
1050{
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001051 struct osd_info *oi = itv->osd_info;
1052
Hans Verkuil26e9d592007-08-25 05:41:52 -03001053 mutex_lock(&itv->serialize_lock);
Hans Verkuil6e5eb592007-07-25 12:55:52 -03001054 if (ivtv_init_on_first_open(itv)) {
Hans Verkuil26e9d592007-08-25 05:41:52 -03001055 mutex_unlock(&itv->serialize_lock);
Hans Verkuil641ed492007-08-28 03:24:31 -03001056 IVTVFB_ERR("Failed to initialize ivtv\n");
Hans Verkuil6e5eb592007-07-25 12:55:52 -03001057 return -ENXIO;
1058 }
Hans Verkuil26e9d592007-08-25 05:41:52 -03001059 mutex_unlock(&itv->serialize_lock);
Hans Verkuil6e5eb592007-07-25 12:55:52 -03001060
Hans Verkuil641ed492007-08-28 03:24:31 -03001061 ivtvfb_get_framebuffer(itv, &oi->video_rbase, &oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -03001062
1063 /* The osd buffer size depends on the number of video buffers allocated
1064 on the PVR350 itself. For now we'll hardcode the smallest osd buffer
1065 size to prevent any overlap. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001066 oi->video_buffer_size = 1704960;
Hans Verkuil32db7752007-07-20 09:29:43 -03001067
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001068 oi->video_pbase = itv->base_addr + IVTV_DECODER_OFFSET + oi->video_rbase;
1069 oi->video_vbase = itv->dec_mem + oi->video_rbase;
Hans Verkuil32db7752007-07-20 09:29:43 -03001070
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001071 if (!oi->video_vbase) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001072 IVTVFB_ERR("abort, video memory 0x%x @ 0x%lx isn't mapped!\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001073 oi->video_buffer_size, oi->video_pbase);
Hans Verkuil32db7752007-07-20 09:29:43 -03001074 return -EIO;
1075 }
1076
Hans Verkuil641ed492007-08-28 03:24:31 -03001077 IVTVFB_INFO("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001078 oi->video_pbase, oi->video_vbase,
1079 oi->video_buffer_size / 1024);
Hans Verkuil32db7752007-07-20 09:29:43 -03001080
1081#ifdef CONFIG_MTRR
1082 {
1083 /* Find the largest power of two that maps the whole buffer */
1084 int size_shift = 31;
1085
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001086 while (!(oi->video_buffer_size & (1 << size_shift))) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001087 size_shift--;
1088 }
1089 size_shift++;
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001090 oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1);
1091 oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size;
1092 oi->fb_end_aligned_physaddr += (1 << size_shift) - 1;
1093 oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1);
1094 if (mtrr_add(oi->fb_start_aligned_physaddr,
1095 oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr,
Hans Verkuil32db7752007-07-20 09:29:43 -03001096 MTRR_TYPE_WRCOMB, 1) < 0) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001097 IVTVFB_INFO("disabled mttr\n");
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001098 oi->fb_start_aligned_physaddr = 0;
1099 oi->fb_end_aligned_physaddr = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -03001100 }
1101 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001102#endif
Hans Verkuil32db7752007-07-20 09:29:43 -03001103
1104 /* Blank the entire osd. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001105 memset_io(oi->video_vbase, 0, oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -03001106
1107 return 0;
1108}
1109
1110/* Release any memory we've grabbed & remove mtrr entry */
1111static void ivtvfb_release_buffers (struct ivtv *itv)
1112{
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001113 struct osd_info *oi = itv->osd_info;
1114
Hans Verkuil32db7752007-07-20 09:29:43 -03001115 /* Release cmap */
Adrian Bunk13628032007-08-28 03:28:04 -03001116 if (oi->ivtvfb_info.cmap.len)
1117 fb_dealloc_cmap(&oi->ivtvfb_info.cmap);
Hans Verkuil32db7752007-07-20 09:29:43 -03001118
1119 /* Release pseudo palette */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001120 if (oi->ivtvfb_info.pseudo_palette)
1121 kfree(oi->ivtvfb_info.pseudo_palette);
Hans Verkuil32db7752007-07-20 09:29:43 -03001122
1123#ifdef CONFIG_MTRR
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001124 if (oi->fb_end_aligned_physaddr) {
1125 mtrr_del(-1, oi->fb_start_aligned_physaddr,
1126 oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr);
1127 }
1128#endif
Hans Verkuil32db7752007-07-20 09:29:43 -03001129
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001130 kfree(oi);
Hans Verkuil32db7752007-07-20 09:29:43 -03001131 itv->osd_info = NULL;
1132}
1133
1134/* Initialize the specified card */
1135
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001136static int ivtvfb_init_card(struct ivtv *itv)
Hans Verkuil32db7752007-07-20 09:29:43 -03001137{
1138 int rc;
1139
1140 if (itv->osd_info) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001141 IVTVFB_ERR("Card %d already initialised\n", ivtvfb_card_id);
Hans Verkuil32db7752007-07-20 09:29:43 -03001142 return -EBUSY;
1143 }
1144
Hans Verkuil3f983872008-05-01 10:31:12 -03001145 itv->osd_info = kzalloc(sizeof(struct osd_info),
1146 GFP_ATOMIC|__GFP_NOWARN);
Richard Knutsson14d5deb2007-12-08 10:35:06 -03001147 if (itv->osd_info == NULL) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001148 IVTVFB_ERR("Failed to allocate memory for osd_info\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001149 return -ENOMEM;
1150 }
1151
1152 /* Find & setup the OSD buffer */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001153 if ((rc = ivtvfb_init_io(itv)))
Hans Verkuil32db7752007-07-20 09:29:43 -03001154 return rc;
1155
1156 /* Set the startup video mode information */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001157 if ((rc = ivtvfb_init_vidmode(itv))) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001158 ivtvfb_release_buffers(itv);
1159 return rc;
1160 }
1161
1162 /* Register the framebuffer */
1163 if (register_framebuffer(&itv->osd_info->ivtvfb_info) < 0) {
1164 ivtvfb_release_buffers(itv);
1165 return -EINVAL;
1166 }
1167
1168 itv->osd_video_pbase = itv->osd_info->video_pbase;
1169
1170 /* Set the card to the requested mode */
1171 ivtvfb_set_par(&itv->osd_info->ivtvfb_info);
1172
1173 /* Set color 0 to black */
1174 write_reg(0, 0x02a30);
1175 write_reg(0, 0x02a34);
1176
1177 /* Enable the osd */
1178 ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info);
1179
Hans Verkuil32db7752007-07-20 09:29:43 -03001180 /* Allocate DMA */
1181 ivtv_udma_alloc(itv);
1182 return 0;
1183
1184}
1185
1186static int __init ivtvfb_init(void)
1187{
1188 struct ivtv *itv;
1189 int i, registered = 0;
1190
Hans Verkuil641ed492007-08-28 03:24:31 -03001191 if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) {
1192 printk(KERN_ERR "ivtvfb: ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n",
Hans Verkuil32db7752007-07-20 09:29:43 -03001193 IVTV_MAX_CARDS - 1);
1194 return -EINVAL;
1195 }
1196
1197 /* Locate & initialise all cards supporting an OSD. */
1198 for (i = 0; i < ivtv_cards_active; i++) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001199 if (ivtvfb_card_id != -1 && i != ivtvfb_card_id)
Hans Verkuil32db7752007-07-20 09:29:43 -03001200 continue;
1201 itv = ivtv_cards[i];
1202 if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
1203 if (ivtvfb_init_card(itv) == 0) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001204 IVTVFB_INFO("Framebuffer registered on ivtv card id %d\n", i);
Hans Verkuil32db7752007-07-20 09:29:43 -03001205 registered++;
1206 }
1207 }
1208 }
1209 if (!registered) {
Hans Verkuil7b3a0d42007-08-26 06:11:07 -03001210 printk(KERN_ERR "ivtvfb: no cards found");
Hans Verkuil32db7752007-07-20 09:29:43 -03001211 return -ENODEV;
1212 }
1213 return 0;
1214}
1215
1216static void ivtvfb_cleanup(void)
1217{
1218 struct ivtv *itv;
1219 int i;
1220
Hans Verkuil7b3a0d42007-08-26 06:11:07 -03001221 printk(KERN_INFO "ivtvfb: Unloading framebuffer module\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001222
1223 for (i = 0; i < ivtv_cards_active; i++) {
1224 itv = ivtv_cards[i];
1225 if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) && itv->osd_info) {
Hans Verkuild343d7f2007-10-14 11:17:14 -03001226 if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) {
1227 IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n", i);
1228 return;
1229 }
Hans Verkuil641ed492007-08-28 03:24:31 -03001230 IVTVFB_DEBUG_INFO("Unregister framebuffer %d\n", i);
Hans Verkuil32db7752007-07-20 09:29:43 -03001231 ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info);
Hans Verkuil32db7752007-07-20 09:29:43 -03001232 ivtvfb_release_buffers(itv);
1233 itv->osd_video_pbase = 0;
1234 }
1235 }
1236}
1237
1238module_init(ivtvfb_init);
1239module_exit(ivtvfb_cleanup);