blob: e8dbee4439460ecb9c0ca2714b06e78f04220f72 [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 Verkuil641ed492007-08-28 03:24:31 -0300792 IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300793 var->xres, var->yres,
794 var->xres_virtual, var->yres_virtual,
Hans Verkuil32db7752007-07-20 09:29:43 -0300795 var->bits_per_pixel);
796
Hans Verkuil641ed492007-08-28 03:24:31 -0300797 IVTVFB_DEBUG_INFO("Display position: %d, %d\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300798 var->left_margin, var->upper_margin);
Hans Verkuil32db7752007-07-20 09:29:43 -0300799
Hans Verkuil641ed492007-08-28 03:24:31 -0300800 IVTVFB_DEBUG_INFO("Display filter: %s\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300801 (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
Hans Verkuil641ed492007-08-28 03:24:31 -0300802 IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
Hans Verkuil32db7752007-07-20 09:29:43 -0300803 return 0;
804}
805
806static int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
807{
808 struct ivtv *itv = (struct ivtv *) info->par;
Hans Verkuil641ed492007-08-28 03:24:31 -0300809 IVTVFB_DEBUG_INFO("ivtvfb_check_var\n");
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300810 return _ivtvfb_check_var(var, itv);
Hans Verkuil32db7752007-07-20 09:29:43 -0300811}
812
813static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
814{
815 u32 osd_pan_index;
816 struct ivtv *itv = (struct ivtv *) info->par;
817
818 osd_pan_index = (var->xoffset + (var->yoffset * var->xres_virtual))*var->bits_per_pixel/8;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300819 write_reg(osd_pan_index, 0x02A0C);
Hans Verkuil32db7752007-07-20 09:29:43 -0300820
821 /* Pass this info back the yuv handler */
822 itv->yuv_info.osd_x_pan = var->xoffset;
823 itv->yuv_info.osd_y_pan = var->yoffset;
824 /* Force update of yuv registers */
825 itv->yuv_info.yuv_forced_update = 1;
826 return 0;
827}
828
829static int ivtvfb_set_par(struct fb_info *info)
830{
831 int rc = 0;
832 struct ivtv *itv = (struct ivtv *) info->par;
833
Hans Verkuil641ed492007-08-28 03:24:31 -0300834 IVTVFB_DEBUG_INFO("ivtvfb_set_par\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300835
836 rc = ivtvfb_set_var(itv, &info->var);
837 ivtvfb_pan_display(&info->var, info);
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300838 ivtvfb_get_fix(itv, &info->fix);
Hans Verkuil32db7752007-07-20 09:29:43 -0300839 return rc;
840}
841
842static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green,
843 unsigned blue, unsigned transp,
844 struct fb_info *info)
845{
846 u32 color, *palette;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300847 struct ivtv *itv = (struct ivtv *)info->par;
Hans Verkuil32db7752007-07-20 09:29:43 -0300848
849 if (regno >= info->cmap.len)
850 return -EINVAL;
851
852 color = ((transp & 0xFF00) << 16) |((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8);
853 if (info->var.bits_per_pixel <= 8) {
854 write_reg(regno, 0x02a30);
855 write_reg(color, 0x02a34);
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300856 return 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300857 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300858 if (regno >= 16)
859 return -EINVAL;
Hans Verkuil32db7752007-07-20 09:29:43 -0300860
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300861 palette = info->pseudo_palette;
862 if (info->var.bits_per_pixel == 16) {
863 switch (info->var.green.length) {
864 case 4:
865 color = ((red & 0xf000) >> 4) |
866 ((green & 0xf000) >> 8) |
867 ((blue & 0xf000) >> 12);
868 break;
869 case 5:
870 color = ((red & 0xf800) >> 1) |
871 ((green & 0xf800) >> 6) |
872 ((blue & 0xf800) >> 11);
873 break;
874 case 6:
875 color = (red & 0xf800 ) |
876 ((green & 0xfc00) >> 5) |
877 ((blue & 0xf800) >> 11);
878 break;
Hans Verkuil32db7752007-07-20 09:29:43 -0300879 }
Hans Verkuil32db7752007-07-20 09:29:43 -0300880 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300881 palette[regno] = color;
Hans Verkuil32db7752007-07-20 09:29:43 -0300882 return 0;
883}
884
885/* We don't really support blanking. All this does is enable or
886 disable the OSD. */
887static int ivtvfb_blank(int blank_mode, struct fb_info *info)
888{
889 struct ivtv *itv = (struct ivtv *)info->par;
890
Hans Verkuil641ed492007-08-28 03:24:31 -0300891 IVTVFB_DEBUG_INFO("Set blanking mode : %d\n", blank_mode);
Hans Verkuil32db7752007-07-20 09:29:43 -0300892 switch (blank_mode) {
893 case FB_BLANK_UNBLANK:
894 ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1);
895 break;
896 case FB_BLANK_NORMAL:
897 case FB_BLANK_HSYNC_SUSPEND:
898 case FB_BLANK_VSYNC_SUSPEND:
899 case FB_BLANK_POWERDOWN:
900 ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
901 break;
902 }
903 return 0;
904}
905
906static struct fb_ops ivtvfb_ops = {
907 .owner = THIS_MODULE,
Ian Armstrong4cbeb3712008-05-12 11:53:10 -0300908 .fb_write = ivtvfb_write,
Hans Verkuil32db7752007-07-20 09:29:43 -0300909 .fb_check_var = ivtvfb_check_var,
910 .fb_set_par = ivtvfb_set_par,
911 .fb_setcolreg = ivtvfb_setcolreg,
912 .fb_fillrect = cfb_fillrect,
913 .fb_copyarea = cfb_copyarea,
914 .fb_imageblit = cfb_imageblit,
915 .fb_cursor = NULL,
916 .fb_ioctl = ivtvfb_ioctl,
917 .fb_pan_display = ivtvfb_pan_display,
918 .fb_blank = ivtvfb_blank,
919};
920
921/* Initialization */
922
923
924/* Setup our initial video mode */
925static int ivtvfb_init_vidmode(struct ivtv *itv)
926{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300927 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300928 struct v4l2_rect start_window;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300929 int max_height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300930
Hans Verkuil32db7752007-07-20 09:29:43 -0300931 /* Color mode */
932
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300933 if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32)
934 osd_depth = 8;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300935 oi->bits_per_pixel = osd_depth;
936 oi->bytes_per_pixel = oi->bits_per_pixel / 8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300937
938 /* Horizontal size & position */
939
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300940 if (osd_xres > 720)
941 osd_xres = 720;
Hans Verkuil32db7752007-07-20 09:29:43 -0300942
943 /* Must be a multiple of 4 for 8bpp & 2 for 16bpp */
944 if (osd_depth == 8)
945 osd_xres &= ~3;
946 else if (osd_depth == 16)
947 osd_xres &= ~1;
948
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300949 start_window.width = osd_xres ? osd_xres : 640;
Hans Verkuil32db7752007-07-20 09:29:43 -0300950
951 /* Check horizontal start (osd_left). */
952 if (osd_left && osd_left + start_window.width > 721) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300953 IVTVFB_ERR("Invalid osd_left - assuming default\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300954 osd_left = 0;
955 }
956
957 /* Hardware coords start at 0, user coords start at 1. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300958 osd_left--;
Hans Verkuil32db7752007-07-20 09:29:43 -0300959
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300960 start_window.left = osd_left >= 0 ? osd_left : ((IVTV_OSD_MAX_WIDTH - start_window.width) / 2);
Hans Verkuil32db7752007-07-20 09:29:43 -0300961
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300962 oi->display_byte_stride =
963 start_window.width * oi->bytes_per_pixel;
Hans Verkuil32db7752007-07-20 09:29:43 -0300964
965 /* Vertical size & position */
966
967 max_height = itv->is_50hz ? 576 : 480;
968
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300969 if (osd_yres > max_height)
970 osd_yres = max_height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300971
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300972 start_window.height = osd_yres ? osd_yres : itv->is_50hz ? 480 : 400;
Hans Verkuil32db7752007-07-20 09:29:43 -0300973
974 /* Check vertical start (osd_upper). */
975 if (osd_upper + start_window.height > max_height + 1) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300976 IVTVFB_ERR("Invalid osd_upper - assuming default\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300977 osd_upper = 0;
978 }
979
980 /* Hardware coords start at 0, user coords start at 1. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300981 osd_upper--;
Hans Verkuil32db7752007-07-20 09:29:43 -0300982
983 start_window.top = osd_upper >= 0 ? osd_upper : ((max_height - start_window.height) / 2);
984
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300985 oi->display_width = start_window.width;
986 oi->display_height = start_window.height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300987
988 /* Generate a valid fb_var_screeninfo */
989
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300990 oi->ivtvfb_defined.xres = oi->display_width;
991 oi->ivtvfb_defined.yres = oi->display_height;
992 oi->ivtvfb_defined.xres_virtual = oi->display_width;
993 oi->ivtvfb_defined.yres_virtual = oi->display_height;
994 oi->ivtvfb_defined.bits_per_pixel = oi->bits_per_pixel;
995 oi->ivtvfb_defined.vmode = (osd_laced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED);
996 oi->ivtvfb_defined.left_margin = start_window.left + 1;
997 oi->ivtvfb_defined.upper_margin = start_window.top + 1;
998 oi->ivtvfb_defined.accel_flags = FB_ACCEL_NONE;
999 oi->ivtvfb_defined.nonstd = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -03001000
1001 /* We've filled in the most data, let the usual mode check
1002 routine fill in the rest. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001003 _ivtvfb_check_var(&oi->ivtvfb_defined, itv);
Hans Verkuil32db7752007-07-20 09:29:43 -03001004
1005 /* Generate valid fb_fix_screeninfo */
1006
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001007 ivtvfb_get_fix(itv, &oi->ivtvfb_fix);
Hans Verkuil32db7752007-07-20 09:29:43 -03001008
1009 /* Generate valid fb_info */
1010
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001011 oi->ivtvfb_info.node = -1;
1012 oi->ivtvfb_info.flags = FBINFO_FLAG_DEFAULT;
1013 oi->ivtvfb_info.fbops = &ivtvfb_ops;
1014 oi->ivtvfb_info.par = itv;
1015 oi->ivtvfb_info.var = oi->ivtvfb_defined;
1016 oi->ivtvfb_info.fix = oi->ivtvfb_fix;
1017 oi->ivtvfb_info.screen_base = (u8 __iomem *)oi->video_vbase;
1018 oi->ivtvfb_info.fbops = &ivtvfb_ops;
Hans Verkuil32db7752007-07-20 09:29:43 -03001019
1020 /* Supply some monitor specs. Bogus values will do for now */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001021 oi->ivtvfb_info.monspecs.hfmin = 8000;
1022 oi->ivtvfb_info.monspecs.hfmax = 70000;
1023 oi->ivtvfb_info.monspecs.vfmin = 10;
1024 oi->ivtvfb_info.monspecs.vfmax = 100;
Hans Verkuil32db7752007-07-20 09:29:43 -03001025
1026 /* Allocate color map */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001027 if (fb_alloc_cmap(&oi->ivtvfb_info.cmap, 256, 1)) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001028 IVTVFB_ERR("abort, unable to alloc cmap\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001029 return -ENOMEM;
1030 }
1031
1032 /* Allocate the pseudo palette */
Hans Verkuil3f983872008-05-01 10:31:12 -03001033 oi->ivtvfb_info.pseudo_palette =
1034 kmalloc(sizeof(u32) * 16, GFP_KERNEL|__GFP_NOWARN);
Hans Verkuil32db7752007-07-20 09:29:43 -03001035
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001036 if (!oi->ivtvfb_info.pseudo_palette) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001037 IVTVFB_ERR("abort, unable to alloc pseudo pallete\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001038 return -ENOMEM;
1039 }
1040
1041 return 0;
1042}
1043
1044/* Find OSD buffer base & size. Add to mtrr. Zero osd buffer. */
1045
1046static int ivtvfb_init_io(struct ivtv *itv)
1047{
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001048 struct osd_info *oi = itv->osd_info;
1049
Hans Verkuil26e9d592007-08-25 05:41:52 -03001050 mutex_lock(&itv->serialize_lock);
Hans Verkuil6e5eb592007-07-25 12:55:52 -03001051 if (ivtv_init_on_first_open(itv)) {
Hans Verkuil26e9d592007-08-25 05:41:52 -03001052 mutex_unlock(&itv->serialize_lock);
Hans Verkuil641ed492007-08-28 03:24:31 -03001053 IVTVFB_ERR("Failed to initialize ivtv\n");
Hans Verkuil6e5eb592007-07-25 12:55:52 -03001054 return -ENXIO;
1055 }
Hans Verkuil26e9d592007-08-25 05:41:52 -03001056 mutex_unlock(&itv->serialize_lock);
Hans Verkuil6e5eb592007-07-25 12:55:52 -03001057
Hans Verkuil641ed492007-08-28 03:24:31 -03001058 ivtvfb_get_framebuffer(itv, &oi->video_rbase, &oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -03001059
1060 /* The osd buffer size depends on the number of video buffers allocated
1061 on the PVR350 itself. For now we'll hardcode the smallest osd buffer
1062 size to prevent any overlap. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001063 oi->video_buffer_size = 1704960;
Hans Verkuil32db7752007-07-20 09:29:43 -03001064
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001065 oi->video_pbase = itv->base_addr + IVTV_DECODER_OFFSET + oi->video_rbase;
1066 oi->video_vbase = itv->dec_mem + oi->video_rbase;
Hans Verkuil32db7752007-07-20 09:29:43 -03001067
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001068 if (!oi->video_vbase) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001069 IVTVFB_ERR("abort, video memory 0x%x @ 0x%lx isn't mapped!\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001070 oi->video_buffer_size, oi->video_pbase);
Hans Verkuil32db7752007-07-20 09:29:43 -03001071 return -EIO;
1072 }
1073
Hans Verkuil641ed492007-08-28 03:24:31 -03001074 IVTVFB_INFO("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001075 oi->video_pbase, oi->video_vbase,
1076 oi->video_buffer_size / 1024);
Hans Verkuil32db7752007-07-20 09:29:43 -03001077
1078#ifdef CONFIG_MTRR
1079 {
1080 /* Find the largest power of two that maps the whole buffer */
1081 int size_shift = 31;
1082
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001083 while (!(oi->video_buffer_size & (1 << size_shift))) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001084 size_shift--;
1085 }
1086 size_shift++;
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001087 oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1);
1088 oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size;
1089 oi->fb_end_aligned_physaddr += (1 << size_shift) - 1;
1090 oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1);
1091 if (mtrr_add(oi->fb_start_aligned_physaddr,
1092 oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr,
Hans Verkuil32db7752007-07-20 09:29:43 -03001093 MTRR_TYPE_WRCOMB, 1) < 0) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001094 IVTVFB_INFO("disabled mttr\n");
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001095 oi->fb_start_aligned_physaddr = 0;
1096 oi->fb_end_aligned_physaddr = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -03001097 }
1098 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001099#endif
Hans Verkuil32db7752007-07-20 09:29:43 -03001100
1101 /* Blank the entire osd. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001102 memset_io(oi->video_vbase, 0, oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -03001103
1104 return 0;
1105}
1106
1107/* Release any memory we've grabbed & remove mtrr entry */
1108static void ivtvfb_release_buffers (struct ivtv *itv)
1109{
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001110 struct osd_info *oi = itv->osd_info;
1111
Hans Verkuil32db7752007-07-20 09:29:43 -03001112 /* Release cmap */
Adrian Bunk13628032007-08-28 03:28:04 -03001113 if (oi->ivtvfb_info.cmap.len)
1114 fb_dealloc_cmap(&oi->ivtvfb_info.cmap);
Hans Verkuil32db7752007-07-20 09:29:43 -03001115
1116 /* Release pseudo palette */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001117 if (oi->ivtvfb_info.pseudo_palette)
1118 kfree(oi->ivtvfb_info.pseudo_palette);
Hans Verkuil32db7752007-07-20 09:29:43 -03001119
1120#ifdef CONFIG_MTRR
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001121 if (oi->fb_end_aligned_physaddr) {
1122 mtrr_del(-1, oi->fb_start_aligned_physaddr,
1123 oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr);
1124 }
1125#endif
Hans Verkuil32db7752007-07-20 09:29:43 -03001126
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001127 kfree(oi);
Hans Verkuil32db7752007-07-20 09:29:43 -03001128 itv->osd_info = NULL;
1129}
1130
1131/* Initialize the specified card */
1132
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001133static int ivtvfb_init_card(struct ivtv *itv)
Hans Verkuil32db7752007-07-20 09:29:43 -03001134{
1135 int rc;
1136
1137 if (itv->osd_info) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001138 IVTVFB_ERR("Card %d already initialised\n", ivtvfb_card_id);
Hans Verkuil32db7752007-07-20 09:29:43 -03001139 return -EBUSY;
1140 }
1141
Hans Verkuil3f983872008-05-01 10:31:12 -03001142 itv->osd_info = kzalloc(sizeof(struct osd_info),
1143 GFP_ATOMIC|__GFP_NOWARN);
Richard Knutsson14d5deb2007-12-08 10:35:06 -03001144 if (itv->osd_info == NULL) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001145 IVTVFB_ERR("Failed to allocate memory for osd_info\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001146 return -ENOMEM;
1147 }
1148
1149 /* Find & setup the OSD buffer */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001150 if ((rc = ivtvfb_init_io(itv)))
Hans Verkuil32db7752007-07-20 09:29:43 -03001151 return rc;
1152
1153 /* Set the startup video mode information */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001154 if ((rc = ivtvfb_init_vidmode(itv))) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001155 ivtvfb_release_buffers(itv);
1156 return rc;
1157 }
1158
1159 /* Register the framebuffer */
1160 if (register_framebuffer(&itv->osd_info->ivtvfb_info) < 0) {
1161 ivtvfb_release_buffers(itv);
1162 return -EINVAL;
1163 }
1164
1165 itv->osd_video_pbase = itv->osd_info->video_pbase;
1166
1167 /* Set the card to the requested mode */
1168 ivtvfb_set_par(&itv->osd_info->ivtvfb_info);
1169
1170 /* Set color 0 to black */
1171 write_reg(0, 0x02a30);
1172 write_reg(0, 0x02a34);
1173
1174 /* Enable the osd */
1175 ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info);
1176
Hans Verkuil32db7752007-07-20 09:29:43 -03001177 /* Allocate DMA */
1178 ivtv_udma_alloc(itv);
1179 return 0;
1180
1181}
1182
1183static int __init ivtvfb_init(void)
1184{
1185 struct ivtv *itv;
1186 int i, registered = 0;
1187
Hans Verkuil641ed492007-08-28 03:24:31 -03001188 if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) {
1189 printk(KERN_ERR "ivtvfb: ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n",
Hans Verkuil32db7752007-07-20 09:29:43 -03001190 IVTV_MAX_CARDS - 1);
1191 return -EINVAL;
1192 }
1193
1194 /* Locate & initialise all cards supporting an OSD. */
1195 for (i = 0; i < ivtv_cards_active; i++) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001196 if (ivtvfb_card_id != -1 && i != ivtvfb_card_id)
Hans Verkuil32db7752007-07-20 09:29:43 -03001197 continue;
1198 itv = ivtv_cards[i];
1199 if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
1200 if (ivtvfb_init_card(itv) == 0) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001201 IVTVFB_INFO("Framebuffer registered on ivtv card id %d\n", i);
Hans Verkuil32db7752007-07-20 09:29:43 -03001202 registered++;
1203 }
1204 }
1205 }
1206 if (!registered) {
Hans Verkuil7b3a0d42007-08-26 06:11:07 -03001207 printk(KERN_ERR "ivtvfb: no cards found");
Hans Verkuil32db7752007-07-20 09:29:43 -03001208 return -ENODEV;
1209 }
1210 return 0;
1211}
1212
1213static void ivtvfb_cleanup(void)
1214{
1215 struct ivtv *itv;
1216 int i;
1217
Hans Verkuil7b3a0d42007-08-26 06:11:07 -03001218 printk(KERN_INFO "ivtvfb: Unloading framebuffer module\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001219
1220 for (i = 0; i < ivtv_cards_active; i++) {
1221 itv = ivtv_cards[i];
1222 if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) && itv->osd_info) {
Hans Verkuild343d7f2007-10-14 11:17:14 -03001223 if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) {
1224 IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n", i);
1225 return;
1226 }
Hans Verkuil641ed492007-08-28 03:24:31 -03001227 IVTVFB_DEBUG_INFO("Unregister framebuffer %d\n", i);
Hans Verkuil32db7752007-07-20 09:29:43 -03001228 ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info);
Hans Verkuil32db7752007-07-20 09:29:43 -03001229 ivtvfb_release_buffers(itv);
1230 itv->osd_video_pbase = 0;
1231 }
1232 }
1233}
1234
1235module_init(ivtvfb_init);
1236module_exit(ivtvfb_cleanup);