blob: 52ffd154a3d80bbb21eb0514c7603c4368040c3d [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
370static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
371{
372 DEFINE_WAIT(wait);
373 struct ivtv *itv = (struct ivtv *)info->par;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300374 int rc = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300375
376 switch (cmd) {
Hans Verkuil32db7752007-07-20 09:29:43 -0300377 case FBIOGET_VBLANK: {
378 struct fb_vblank vblank;
379 u32 trace;
380
381 vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT |
382 FB_VBLANK_HAVE_VSYNC;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300383 trace = read_reg(0x028c0) >> 16;
Hans Verkuil32db7752007-07-20 09:29:43 -0300384 if (itv->is_50hz && trace > 312) trace -= 312;
385 else if (itv->is_60hz && trace > 262) trace -= 262;
386 if (trace == 1) vblank.flags |= FB_VBLANK_VSYNCING;
Hans Verkuila158f352007-08-23 11:31:57 -0300387 vblank.count = itv->last_vsync_field;
Hans Verkuil32db7752007-07-20 09:29:43 -0300388 vblank.vcount = trace;
389 vblank.hcount = 0;
390 if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank)))
391 return -EFAULT;
392 return 0;
393 }
394
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300395 case FBIO_WAITFORVSYNC:
Hans Verkuil32db7752007-07-20 09:29:43 -0300396 prepare_to_wait(&itv->vsync_waitq, &wait, TASK_INTERRUPTIBLE);
Hans Verkuil84149a02007-07-20 18:17:18 -0300397 if (!schedule_timeout(msecs_to_jiffies(50))) rc = -ETIMEDOUT;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300398 finish_wait(&itv->vsync_waitq, &wait);
Hans Verkuil32db7752007-07-20 09:29:43 -0300399 return rc;
Hans Verkuil32db7752007-07-20 09:29:43 -0300400
Hans Verkuild715e762007-07-20 10:30:32 -0300401 case IVTVFB_IOC_DMA_FRAME: {
402 struct ivtvfb_dma_frame args;
Hans Verkuil32db7752007-07-20 09:29:43 -0300403
Hans Verkuil641ed492007-08-28 03:24:31 -0300404 IVTVFB_DEBUG_INFO("IVTVFB_IOC_DMA_FRAME\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300405 if (copy_from_user(&args, (void __user *)arg, sizeof(args)))
406 return -EFAULT;
407
Hans Verkuil641ed492007-08-28 03:24:31 -0300408 return ivtvfb_prep_frame(itv, cmd, args.source, args.dest_offset, args.count);
Hans Verkuil32db7752007-07-20 09:29:43 -0300409 }
410
411 default:
Hans Verkuil641ed492007-08-28 03:24:31 -0300412 IVTVFB_DEBUG_INFO("Unknown ioctl %08x\n", cmd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300413 return -EINVAL;
414 }
415 return 0;
416}
417
418/* Framebuffer device handling */
419
420static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var)
421{
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300422 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300423 struct ivtv_osd_coords ivtv_osd;
424 struct v4l2_rect ivtv_window;
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300425 int osd_mode = -1;
Hans Verkuil32db7752007-07-20 09:29:43 -0300426
Hans Verkuil641ed492007-08-28 03:24:31 -0300427 IVTVFB_DEBUG_INFO("ivtvfb_set_var\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300428
429 /* Select color space */
430 if (var->nonstd) /* YUV */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300431 write_reg(read_reg(0x02a00) | 0x0002000, 0x02a00);
Hans Verkuil32db7752007-07-20 09:29:43 -0300432 else /* RGB */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300433 write_reg(read_reg(0x02a00) & ~0x0002000, 0x02a00);
Hans Verkuil32db7752007-07-20 09:29:43 -0300434
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300435 /* Set the color mode */
Hans Verkuil32db7752007-07-20 09:29:43 -0300436 switch (var->bits_per_pixel) {
437 case 8:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300438 osd_mode = IVTV_OSD_BPP_8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300439 break;
440 case 32:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300441 osd_mode = IVTV_OSD_BPP_32;
Hans Verkuil32db7752007-07-20 09:29:43 -0300442 break;
443 case 16:
444 switch (var->green.length) {
445 case 4:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300446 osd_mode = IVTV_OSD_BPP_16_444;
Hans Verkuil32db7752007-07-20 09:29:43 -0300447 break;
448 case 5:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300449 osd_mode = IVTV_OSD_BPP_16_555;
Hans Verkuil32db7752007-07-20 09:29:43 -0300450 break;
451 case 6:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300452 osd_mode = IVTV_OSD_BPP_16_565;
Hans Verkuil32db7752007-07-20 09:29:43 -0300453 break;
454 default:
Hans Verkuil641ed492007-08-28 03:24:31 -0300455 IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300456 }
457 break;
458 default:
Hans Verkuil641ed492007-08-28 03:24:31 -0300459 IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300460 }
461
Ian Armstrong6659e3e2007-10-12 08:15:41 -0300462 /* Set video mode. Although rare, the display can become scrambled even
463 if we don't change mode. Always 'bounce' to osd_mode via mode 0 */
464 if (osd_mode != -1) {
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300465 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0);
466 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, osd_mode);
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300467 }
468
469 oi->bits_per_pixel = var->bits_per_pixel;
470 oi->bytes_per_pixel = var->bits_per_pixel / 8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300471
472 /* Set the flicker filter */
473 switch (var->vmode & FB_VMODE_MASK) {
474 case FB_VMODE_NONINTERLACED: /* Filter on */
475 ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 1);
476 break;
477 case FB_VMODE_INTERLACED: /* Filter off */
478 ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 0);
479 break;
480 default:
Hans Verkuil641ed492007-08-28 03:24:31 -0300481 IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid video mode\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300482 }
483
484 /* Read the current osd info */
Hans Verkuil641ed492007-08-28 03:24:31 -0300485 ivtvfb_get_osd_coords(itv, &ivtv_osd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300486
487 /* Now set the OSD to the size we want */
488 ivtv_osd.pixel_stride = var->xres_virtual;
489 ivtv_osd.lines = var->yres_virtual;
490 ivtv_osd.x = 0;
491 ivtv_osd.y = 0;
Hans Verkuil641ed492007-08-28 03:24:31 -0300492 ivtvfb_set_osd_coords(itv, &ivtv_osd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300493
494 /* Can't seem to find the right API combo for this.
495 Use another function which does what we need through direct register access. */
496 ivtv_window.width = var->xres;
497 ivtv_window.height = var->yres;
498
499 /* Minimum margin cannot be 0, as X won't allow such a mode */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300500 if (!var->upper_margin) var->upper_margin++;
501 if (!var->left_margin) var->left_margin++;
Hans Verkuil32db7752007-07-20 09:29:43 -0300502 ivtv_window.top = var->upper_margin - 1;
503 ivtv_window.left = var->left_margin - 1;
504
Hans Verkuil641ed492007-08-28 03:24:31 -0300505 ivtvfb_set_display_window(itv, &ivtv_window);
Hans Verkuil32db7752007-07-20 09:29:43 -0300506
507 /* Force update of yuv registers */
508 itv->yuv_info.yuv_forced_update = 1;
509
Hans Verkuil641ed492007-08-28 03:24:31 -0300510 IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300511 var->xres, var->yres,
512 var->xres_virtual, var->yres_virtual,
513 var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300514
Hans Verkuil641ed492007-08-28 03:24:31 -0300515 IVTVFB_DEBUG_INFO("Display position: %d, %d\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300516 var->left_margin, var->upper_margin);
Hans Verkuil32db7752007-07-20 09:29:43 -0300517
Hans Verkuil641ed492007-08-28 03:24:31 -0300518 IVTVFB_DEBUG_INFO("Display filter: %s\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300519 (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
Hans Verkuil641ed492007-08-28 03:24:31 -0300520 IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
Hans Verkuil32db7752007-07-20 09:29:43 -0300521
522 return 0;
523}
524
525static int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix)
526{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300527 struct osd_info *oi = itv->osd_info;
528
Hans Verkuil641ed492007-08-28 03:24:31 -0300529 IVTVFB_DEBUG_INFO("ivtvfb_get_fix\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300530 memset(fix, 0, sizeof(struct fb_fix_screeninfo));
531 strcpy(fix->id, "cx23415 TV out");
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300532 fix->smem_start = oi->video_pbase;
533 fix->smem_len = oi->video_buffer_size;
Hans Verkuil32db7752007-07-20 09:29:43 -0300534 fix->type = FB_TYPE_PACKED_PIXELS;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300535 fix->visual = (oi->bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
Hans Verkuil32db7752007-07-20 09:29:43 -0300536 fix->xpanstep = 1;
537 fix->ypanstep = 1;
538 fix->ywrapstep = 0;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300539 fix->line_length = oi->display_byte_stride;
Hans Verkuil32db7752007-07-20 09:29:43 -0300540 fix->accel = FB_ACCEL_NONE;
541 return 0;
542}
543
544/* Check the requested display mode, returning -EINVAL if we can't
545 handle it. */
546
547static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv)
548{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300549 struct osd_info *oi = itv->osd_info;
Ian Armstrong68a341a2007-08-03 09:51:58 -0300550 int osd_height_limit;
551 u32 pixclock, hlimit, vlimit;
Hans Verkuil32db7752007-07-20 09:29:43 -0300552
Hans Verkuil641ed492007-08-28 03:24:31 -0300553 IVTVFB_DEBUG_INFO("ivtvfb_check_var\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300554
Ian Armstrong68a341a2007-08-03 09:51:58 -0300555 /* Set base references for mode calcs. */
556 if (itv->is_50hz) {
557 pixclock = 84316;
558 hlimit = 776;
559 vlimit = 591;
560 osd_height_limit = 576;
561 }
562 else {
563 pixclock = 83926;
564 hlimit = 776;
565 vlimit = 495;
566 osd_height_limit = 480;
567 }
568
Hans Verkuil32db7752007-07-20 09:29:43 -0300569 if (var->bits_per_pixel == 8 || var->bits_per_pixel == 32) {
570 var->transp.offset = 24;
571 var->transp.length = 8;
572 var->red.offset = 16;
573 var->red.length = 8;
574 var->green.offset = 8;
575 var->green.length = 8;
576 var->blue.offset = 0;
577 var->blue.length = 8;
578 }
579 else if (var->bits_per_pixel == 16) {
580 /* To find out the true mode, check green length */
581 switch (var->green.length) {
582 case 4:
Hans Verkuil32db7752007-07-20 09:29:43 -0300583 var->red.offset = 8;
584 var->red.length = 4;
585 var->green.offset = 4;
586 var->green.length = 4;
587 var->blue.offset = 0;
588 var->blue.length = 4;
Hans Verkuil459a52f2007-08-22 08:58:47 -0300589 var->transp.offset = 12;
590 var->transp.length = 1;
Hans Verkuil32db7752007-07-20 09:29:43 -0300591 break;
592 case 5:
Hans Verkuil32db7752007-07-20 09:29:43 -0300593 var->red.offset = 10;
594 var->red.length = 5;
595 var->green.offset = 5;
596 var->green.length = 5;
597 var->blue.offset = 0;
598 var->blue.length = 5;
Hans Verkuil459a52f2007-08-22 08:58:47 -0300599 var->transp.offset = 15;
600 var->transp.length = 1;
Hans Verkuil32db7752007-07-20 09:29:43 -0300601 break;
602 default:
Hans Verkuil32db7752007-07-20 09:29:43 -0300603 var->red.offset = 11;
604 var->red.length = 5;
605 var->green.offset = 5;
606 var->green.length = 6;
607 var->blue.offset = 0;
608 var->blue.length = 5;
Hans Verkuil459a52f2007-08-22 08:58:47 -0300609 var->transp.offset = 0;
610 var->transp.length = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300611 break;
612 }
613 }
614 else {
Hans Verkuil641ed492007-08-28 03:24:31 -0300615 IVTVFB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300616 return -EINVAL;
617 }
618
619 /* Check the resolution */
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300620 if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit) {
621 IVTVFB_DEBUG_WARN("Invalid resolution: %dx%d\n",
622 var->xres, var->yres);
623 return -EINVAL;
Hans Verkuil32db7752007-07-20 09:29:43 -0300624 }
Hans Verkuil32db7752007-07-20 09:29:43 -0300625
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300626 /* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */
627 if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) ||
628 var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8) > oi->video_buffer_size ||
629 var->xres_virtual < var->xres ||
630 var->yres_virtual < var->yres) {
631 IVTVFB_DEBUG_WARN("Invalid virtual resolution: %dx%d\n",
632 var->xres_virtual, var->yres_virtual);
633 return -EINVAL;
Hans Verkuil32db7752007-07-20 09:29:43 -0300634 }
635
636 /* Some extra checks if in 8 bit mode */
637 if (var->bits_per_pixel == 8) {
638 /* Width must be a multiple of 4 */
639 if (var->xres & 3) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300640 IVTVFB_DEBUG_WARN("Invalid resolution for 8bpp: %d\n", var->xres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300641 return -EINVAL;
642 }
643 if (var->xres_virtual & 3) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300644 IVTVFB_DEBUG_WARN("Invalid virtual resolution for 8bpp: %d)\n", var->xres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300645 return -EINVAL;
646 }
647 }
648 else if (var->bits_per_pixel == 16) {
649 /* Width must be a multiple of 2 */
650 if (var->xres & 1) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300651 IVTVFB_DEBUG_WARN("Invalid resolution for 16bpp: %d\n", var->xres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300652 return -EINVAL;
653 }
654 if (var->xres_virtual & 1) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300655 IVTVFB_DEBUG_WARN("Invalid virtual resolution for 16bpp: %d)\n", var->xres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300656 return -EINVAL;
657 }
658 }
659
660 /* Now check the offsets */
661 if (var->xoffset >= var->xres_virtual || var->yoffset >= var->yres_virtual) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300662 IVTVFB_DEBUG_WARN("Invalid offset: %d (%d) %d (%d)\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300663 var->xoffset, var->xres_virtual, var->yoffset, var->yres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300664 return -EINVAL;
665 }
666
667 /* Check pixel format */
668 if (var->nonstd > 1) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300669 IVTVFB_DEBUG_WARN("Invalid nonstd % d\n", var->nonstd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300670 return -EINVAL;
671 }
672
673 /* Check video mode */
674 if (((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) &&
675 ((var->vmode & FB_VMODE_MASK) != FB_VMODE_INTERLACED)) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300676 IVTVFB_DEBUG_WARN("Invalid video mode: %d\n", var->vmode & FB_VMODE_MASK);
Hans Verkuil32db7752007-07-20 09:29:43 -0300677 return -EINVAL;
678 }
679
680 /* Check the left & upper margins
681 If the margins are too large, just center the screen
682 (enforcing margins causes too many problems) */
683
684 if (var->left_margin + var->xres > IVTV_OSD_MAX_WIDTH + 1) {
685 var->left_margin = 1 + ((IVTV_OSD_MAX_WIDTH - var->xres) / 2);
686 }
687 if (var->upper_margin + var->yres > (itv->is_50hz ? 577 : 481)) {
688 var->upper_margin = 1 + (((itv->is_50hz ? 576 : 480) - var->yres) / 2);
689 }
690
691 /* Maintain overall 'size' for a constant refresh rate */
Ian Armstrong68a341a2007-08-03 09:51:58 -0300692 var->right_margin = hlimit - var->left_margin - var->xres;
693 var->lower_margin = vlimit - var->upper_margin - var->yres;
Hans Verkuil32db7752007-07-20 09:29:43 -0300694
695 /* Fixed sync times */
696 var->hsync_len = 24;
697 var->vsync_len = 2;
698
699 /* Non-interlaced / interlaced mode is used to switch the OSD filter
700 on or off. Adjust the clock timings to maintain a constant
701 vertical refresh rate. */
Hans Verkuil32db7752007-07-20 09:29:43 -0300702 if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED)
Ian Armstrong68a341a2007-08-03 09:51:58 -0300703 var->pixclock = pixclock / 2;
704 else
705 var->pixclock = pixclock;
Hans Verkuil32db7752007-07-20 09:29:43 -0300706
Hans Verkuil641ed492007-08-28 03:24:31 -0300707 IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300708 var->xres, var->yres,
709 var->xres_virtual, var->yres_virtual,
Hans Verkuil32db7752007-07-20 09:29:43 -0300710 var->bits_per_pixel);
711
Hans Verkuil641ed492007-08-28 03:24:31 -0300712 IVTVFB_DEBUG_INFO("Display position: %d, %d\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300713 var->left_margin, var->upper_margin);
Hans Verkuil32db7752007-07-20 09:29:43 -0300714
Hans Verkuil641ed492007-08-28 03:24:31 -0300715 IVTVFB_DEBUG_INFO("Display filter: %s\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300716 (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
Hans Verkuil641ed492007-08-28 03:24:31 -0300717 IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
Hans Verkuil32db7752007-07-20 09:29:43 -0300718 return 0;
719}
720
721static int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
722{
723 struct ivtv *itv = (struct ivtv *) info->par;
Hans Verkuil641ed492007-08-28 03:24:31 -0300724 IVTVFB_DEBUG_INFO("ivtvfb_check_var\n");
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300725 return _ivtvfb_check_var(var, itv);
Hans Verkuil32db7752007-07-20 09:29:43 -0300726}
727
728static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
729{
730 u32 osd_pan_index;
731 struct ivtv *itv = (struct ivtv *) info->par;
732
733 osd_pan_index = (var->xoffset + (var->yoffset * var->xres_virtual))*var->bits_per_pixel/8;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300734 write_reg(osd_pan_index, 0x02A0C);
Hans Verkuil32db7752007-07-20 09:29:43 -0300735
736 /* Pass this info back the yuv handler */
737 itv->yuv_info.osd_x_pan = var->xoffset;
738 itv->yuv_info.osd_y_pan = var->yoffset;
739 /* Force update of yuv registers */
740 itv->yuv_info.yuv_forced_update = 1;
741 return 0;
742}
743
744static int ivtvfb_set_par(struct fb_info *info)
745{
746 int rc = 0;
747 struct ivtv *itv = (struct ivtv *) info->par;
748
Hans Verkuil641ed492007-08-28 03:24:31 -0300749 IVTVFB_DEBUG_INFO("ivtvfb_set_par\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300750
751 rc = ivtvfb_set_var(itv, &info->var);
752 ivtvfb_pan_display(&info->var, info);
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300753 ivtvfb_get_fix(itv, &info->fix);
Hans Verkuil32db7752007-07-20 09:29:43 -0300754 return rc;
755}
756
757static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green,
758 unsigned blue, unsigned transp,
759 struct fb_info *info)
760{
761 u32 color, *palette;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300762 struct ivtv *itv = (struct ivtv *)info->par;
Hans Verkuil32db7752007-07-20 09:29:43 -0300763
764 if (regno >= info->cmap.len)
765 return -EINVAL;
766
767 color = ((transp & 0xFF00) << 16) |((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8);
768 if (info->var.bits_per_pixel <= 8) {
769 write_reg(regno, 0x02a30);
770 write_reg(color, 0x02a34);
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300771 return 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300772 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300773 if (regno >= 16)
774 return -EINVAL;
Hans Verkuil32db7752007-07-20 09:29:43 -0300775
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300776 palette = info->pseudo_palette;
777 if (info->var.bits_per_pixel == 16) {
778 switch (info->var.green.length) {
779 case 4:
780 color = ((red & 0xf000) >> 4) |
781 ((green & 0xf000) >> 8) |
782 ((blue & 0xf000) >> 12);
783 break;
784 case 5:
785 color = ((red & 0xf800) >> 1) |
786 ((green & 0xf800) >> 6) |
787 ((blue & 0xf800) >> 11);
788 break;
789 case 6:
790 color = (red & 0xf800 ) |
791 ((green & 0xfc00) >> 5) |
792 ((blue & 0xf800) >> 11);
793 break;
Hans Verkuil32db7752007-07-20 09:29:43 -0300794 }
Hans Verkuil32db7752007-07-20 09:29:43 -0300795 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300796 palette[regno] = color;
Hans Verkuil32db7752007-07-20 09:29:43 -0300797 return 0;
798}
799
800/* We don't really support blanking. All this does is enable or
801 disable the OSD. */
802static int ivtvfb_blank(int blank_mode, struct fb_info *info)
803{
804 struct ivtv *itv = (struct ivtv *)info->par;
805
Hans Verkuil641ed492007-08-28 03:24:31 -0300806 IVTVFB_DEBUG_INFO("Set blanking mode : %d\n", blank_mode);
Hans Verkuil32db7752007-07-20 09:29:43 -0300807 switch (blank_mode) {
808 case FB_BLANK_UNBLANK:
809 ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1);
810 break;
811 case FB_BLANK_NORMAL:
812 case FB_BLANK_HSYNC_SUSPEND:
813 case FB_BLANK_VSYNC_SUSPEND:
814 case FB_BLANK_POWERDOWN:
815 ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
816 break;
817 }
818 return 0;
819}
820
821static struct fb_ops ivtvfb_ops = {
822 .owner = THIS_MODULE,
823 .fb_check_var = ivtvfb_check_var,
824 .fb_set_par = ivtvfb_set_par,
825 .fb_setcolreg = ivtvfb_setcolreg,
826 .fb_fillrect = cfb_fillrect,
827 .fb_copyarea = cfb_copyarea,
828 .fb_imageblit = cfb_imageblit,
829 .fb_cursor = NULL,
830 .fb_ioctl = ivtvfb_ioctl,
831 .fb_pan_display = ivtvfb_pan_display,
832 .fb_blank = ivtvfb_blank,
833};
834
835/* Initialization */
836
837
838/* Setup our initial video mode */
839static int ivtvfb_init_vidmode(struct ivtv *itv)
840{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300841 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300842 struct v4l2_rect start_window;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300843 int max_height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300844
Hans Verkuil32db7752007-07-20 09:29:43 -0300845 /* Color mode */
846
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300847 if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32)
848 osd_depth = 8;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300849 oi->bits_per_pixel = osd_depth;
850 oi->bytes_per_pixel = oi->bits_per_pixel / 8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300851
852 /* Horizontal size & position */
853
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300854 if (osd_xres > 720)
855 osd_xres = 720;
Hans Verkuil32db7752007-07-20 09:29:43 -0300856
857 /* Must be a multiple of 4 for 8bpp & 2 for 16bpp */
858 if (osd_depth == 8)
859 osd_xres &= ~3;
860 else if (osd_depth == 16)
861 osd_xres &= ~1;
862
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300863 start_window.width = osd_xres ? osd_xres : 640;
Hans Verkuil32db7752007-07-20 09:29:43 -0300864
865 /* Check horizontal start (osd_left). */
866 if (osd_left && osd_left + start_window.width > 721) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300867 IVTVFB_ERR("Invalid osd_left - assuming default\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300868 osd_left = 0;
869 }
870
871 /* Hardware coords start at 0, user coords start at 1. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300872 osd_left--;
Hans Verkuil32db7752007-07-20 09:29:43 -0300873
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300874 start_window.left = osd_left >= 0 ? osd_left : ((IVTV_OSD_MAX_WIDTH - start_window.width) / 2);
Hans Verkuil32db7752007-07-20 09:29:43 -0300875
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300876 oi->display_byte_stride =
877 start_window.width * oi->bytes_per_pixel;
Hans Verkuil32db7752007-07-20 09:29:43 -0300878
879 /* Vertical size & position */
880
881 max_height = itv->is_50hz ? 576 : 480;
882
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300883 if (osd_yres > max_height)
884 osd_yres = max_height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300885
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300886 start_window.height = osd_yres ? osd_yres : itv->is_50hz ? 480 : 400;
Hans Verkuil32db7752007-07-20 09:29:43 -0300887
888 /* Check vertical start (osd_upper). */
889 if (osd_upper + start_window.height > max_height + 1) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300890 IVTVFB_ERR("Invalid osd_upper - assuming default\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300891 osd_upper = 0;
892 }
893
894 /* Hardware coords start at 0, user coords start at 1. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300895 osd_upper--;
Hans Verkuil32db7752007-07-20 09:29:43 -0300896
897 start_window.top = osd_upper >= 0 ? osd_upper : ((max_height - start_window.height) / 2);
898
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300899 oi->display_width = start_window.width;
900 oi->display_height = start_window.height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300901
902 /* Generate a valid fb_var_screeninfo */
903
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300904 oi->ivtvfb_defined.xres = oi->display_width;
905 oi->ivtvfb_defined.yres = oi->display_height;
906 oi->ivtvfb_defined.xres_virtual = oi->display_width;
907 oi->ivtvfb_defined.yres_virtual = oi->display_height;
908 oi->ivtvfb_defined.bits_per_pixel = oi->bits_per_pixel;
909 oi->ivtvfb_defined.vmode = (osd_laced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED);
910 oi->ivtvfb_defined.left_margin = start_window.left + 1;
911 oi->ivtvfb_defined.upper_margin = start_window.top + 1;
912 oi->ivtvfb_defined.accel_flags = FB_ACCEL_NONE;
913 oi->ivtvfb_defined.nonstd = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300914
915 /* We've filled in the most data, let the usual mode check
916 routine fill in the rest. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300917 _ivtvfb_check_var(&oi->ivtvfb_defined, itv);
Hans Verkuil32db7752007-07-20 09:29:43 -0300918
919 /* Generate valid fb_fix_screeninfo */
920
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300921 ivtvfb_get_fix(itv, &oi->ivtvfb_fix);
Hans Verkuil32db7752007-07-20 09:29:43 -0300922
923 /* Generate valid fb_info */
924
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300925 oi->ivtvfb_info.node = -1;
926 oi->ivtvfb_info.flags = FBINFO_FLAG_DEFAULT;
927 oi->ivtvfb_info.fbops = &ivtvfb_ops;
928 oi->ivtvfb_info.par = itv;
929 oi->ivtvfb_info.var = oi->ivtvfb_defined;
930 oi->ivtvfb_info.fix = oi->ivtvfb_fix;
931 oi->ivtvfb_info.screen_base = (u8 __iomem *)oi->video_vbase;
932 oi->ivtvfb_info.fbops = &ivtvfb_ops;
Hans Verkuil32db7752007-07-20 09:29:43 -0300933
934 /* Supply some monitor specs. Bogus values will do for now */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300935 oi->ivtvfb_info.monspecs.hfmin = 8000;
936 oi->ivtvfb_info.monspecs.hfmax = 70000;
937 oi->ivtvfb_info.monspecs.vfmin = 10;
938 oi->ivtvfb_info.monspecs.vfmax = 100;
Hans Verkuil32db7752007-07-20 09:29:43 -0300939
940 /* Allocate color map */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300941 if (fb_alloc_cmap(&oi->ivtvfb_info.cmap, 256, 1)) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300942 IVTVFB_ERR("abort, unable to alloc cmap\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300943 return -ENOMEM;
944 }
945
946 /* Allocate the pseudo palette */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300947 oi->ivtvfb_info.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
Hans Verkuil32db7752007-07-20 09:29:43 -0300948
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300949 if (!oi->ivtvfb_info.pseudo_palette) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300950 IVTVFB_ERR("abort, unable to alloc pseudo pallete\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300951 return -ENOMEM;
952 }
953
954 return 0;
955}
956
957/* Find OSD buffer base & size. Add to mtrr. Zero osd buffer. */
958
959static int ivtvfb_init_io(struct ivtv *itv)
960{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300961 struct osd_info *oi = itv->osd_info;
962
Hans Verkuil26e9d592007-08-25 05:41:52 -0300963 mutex_lock(&itv->serialize_lock);
Hans Verkuil6e5eb592007-07-25 12:55:52 -0300964 if (ivtv_init_on_first_open(itv)) {
Hans Verkuil26e9d592007-08-25 05:41:52 -0300965 mutex_unlock(&itv->serialize_lock);
Hans Verkuil641ed492007-08-28 03:24:31 -0300966 IVTVFB_ERR("Failed to initialize ivtv\n");
Hans Verkuil6e5eb592007-07-25 12:55:52 -0300967 return -ENXIO;
968 }
Hans Verkuil26e9d592007-08-25 05:41:52 -0300969 mutex_unlock(&itv->serialize_lock);
Hans Verkuil6e5eb592007-07-25 12:55:52 -0300970
Hans Verkuil641ed492007-08-28 03:24:31 -0300971 ivtvfb_get_framebuffer(itv, &oi->video_rbase, &oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -0300972
973 /* The osd buffer size depends on the number of video buffers allocated
974 on the PVR350 itself. For now we'll hardcode the smallest osd buffer
975 size to prevent any overlap. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300976 oi->video_buffer_size = 1704960;
Hans Verkuil32db7752007-07-20 09:29:43 -0300977
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300978 oi->video_pbase = itv->base_addr + IVTV_DECODER_OFFSET + oi->video_rbase;
979 oi->video_vbase = itv->dec_mem + oi->video_rbase;
Hans Verkuil32db7752007-07-20 09:29:43 -0300980
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300981 if (!oi->video_vbase) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300982 IVTVFB_ERR("abort, video memory 0x%x @ 0x%lx isn't mapped!\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300983 oi->video_buffer_size, oi->video_pbase);
Hans Verkuil32db7752007-07-20 09:29:43 -0300984 return -EIO;
985 }
986
Hans Verkuil641ed492007-08-28 03:24:31 -0300987 IVTVFB_INFO("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300988 oi->video_pbase, oi->video_vbase,
989 oi->video_buffer_size / 1024);
Hans Verkuil32db7752007-07-20 09:29:43 -0300990
991#ifdef CONFIG_MTRR
992 {
993 /* Find the largest power of two that maps the whole buffer */
994 int size_shift = 31;
995
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300996 while (!(oi->video_buffer_size & (1 << size_shift))) {
Hans Verkuil32db7752007-07-20 09:29:43 -0300997 size_shift--;
998 }
999 size_shift++;
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001000 oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1);
1001 oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size;
1002 oi->fb_end_aligned_physaddr += (1 << size_shift) - 1;
1003 oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1);
1004 if (mtrr_add(oi->fb_start_aligned_physaddr,
1005 oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr,
Hans Verkuil32db7752007-07-20 09:29:43 -03001006 MTRR_TYPE_WRCOMB, 1) < 0) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001007 IVTVFB_INFO("disabled mttr\n");
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001008 oi->fb_start_aligned_physaddr = 0;
1009 oi->fb_end_aligned_physaddr = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -03001010 }
1011 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001012#endif
Hans Verkuil32db7752007-07-20 09:29:43 -03001013
1014 /* Blank the entire osd. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001015 memset_io(oi->video_vbase, 0, oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -03001016
1017 return 0;
1018}
1019
1020/* Release any memory we've grabbed & remove mtrr entry */
1021static void ivtvfb_release_buffers (struct ivtv *itv)
1022{
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001023 struct osd_info *oi = itv->osd_info;
1024
Hans Verkuil32db7752007-07-20 09:29:43 -03001025 /* Release cmap */
Adrian Bunk13628032007-08-28 03:28:04 -03001026 if (oi->ivtvfb_info.cmap.len)
1027 fb_dealloc_cmap(&oi->ivtvfb_info.cmap);
Hans Verkuil32db7752007-07-20 09:29:43 -03001028
1029 /* Release pseudo palette */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001030 if (oi->ivtvfb_info.pseudo_palette)
1031 kfree(oi->ivtvfb_info.pseudo_palette);
Hans Verkuil32db7752007-07-20 09:29:43 -03001032
1033#ifdef CONFIG_MTRR
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001034 if (oi->fb_end_aligned_physaddr) {
1035 mtrr_del(-1, oi->fb_start_aligned_physaddr,
1036 oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr);
1037 }
1038#endif
Hans Verkuil32db7752007-07-20 09:29:43 -03001039
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001040 kfree(oi);
Hans Verkuil32db7752007-07-20 09:29:43 -03001041 itv->osd_info = NULL;
1042}
1043
1044/* Initialize the specified card */
1045
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001046static int ivtvfb_init_card(struct ivtv *itv)
Hans Verkuil32db7752007-07-20 09:29:43 -03001047{
1048 int rc;
1049
1050 if (itv->osd_info) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001051 IVTVFB_ERR("Card %d already initialised\n", ivtvfb_card_id);
Hans Verkuil32db7752007-07-20 09:29:43 -03001052 return -EBUSY;
1053 }
1054
1055 itv->osd_info = kzalloc(sizeof(struct osd_info), GFP_ATOMIC);
1056 if (itv->osd_info == 0) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001057 IVTVFB_ERR("Failed to allocate memory for osd_info\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001058 return -ENOMEM;
1059 }
1060
1061 /* Find & setup the OSD buffer */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001062 if ((rc = ivtvfb_init_io(itv)))
Hans Verkuil32db7752007-07-20 09:29:43 -03001063 return rc;
1064
1065 /* Set the startup video mode information */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001066 if ((rc = ivtvfb_init_vidmode(itv))) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001067 ivtvfb_release_buffers(itv);
1068 return rc;
1069 }
1070
1071 /* Register the framebuffer */
1072 if (register_framebuffer(&itv->osd_info->ivtvfb_info) < 0) {
1073 ivtvfb_release_buffers(itv);
1074 return -EINVAL;
1075 }
1076
1077 itv->osd_video_pbase = itv->osd_info->video_pbase;
1078
1079 /* Set the card to the requested mode */
1080 ivtvfb_set_par(&itv->osd_info->ivtvfb_info);
1081
1082 /* Set color 0 to black */
1083 write_reg(0, 0x02a30);
1084 write_reg(0, 0x02a34);
1085
1086 /* Enable the osd */
1087 ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info);
1088
Hans Verkuil32db7752007-07-20 09:29:43 -03001089 /* Allocate DMA */
1090 ivtv_udma_alloc(itv);
1091 return 0;
1092
1093}
1094
1095static int __init ivtvfb_init(void)
1096{
1097 struct ivtv *itv;
1098 int i, registered = 0;
1099
Hans Verkuil641ed492007-08-28 03:24:31 -03001100 if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) {
1101 printk(KERN_ERR "ivtvfb: ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n",
Hans Verkuil32db7752007-07-20 09:29:43 -03001102 IVTV_MAX_CARDS - 1);
1103 return -EINVAL;
1104 }
1105
1106 /* Locate & initialise all cards supporting an OSD. */
1107 for (i = 0; i < ivtv_cards_active; i++) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001108 if (ivtvfb_card_id != -1 && i != ivtvfb_card_id)
Hans Verkuil32db7752007-07-20 09:29:43 -03001109 continue;
1110 itv = ivtv_cards[i];
1111 if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
1112 if (ivtvfb_init_card(itv) == 0) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001113 IVTVFB_INFO("Framebuffer registered on ivtv card id %d\n", i);
Hans Verkuil32db7752007-07-20 09:29:43 -03001114 registered++;
1115 }
1116 }
1117 }
1118 if (!registered) {
Hans Verkuil7b3a0d42007-08-26 06:11:07 -03001119 printk(KERN_ERR "ivtvfb: no cards found");
Hans Verkuil32db7752007-07-20 09:29:43 -03001120 return -ENODEV;
1121 }
1122 return 0;
1123}
1124
1125static void ivtvfb_cleanup(void)
1126{
1127 struct ivtv *itv;
1128 int i;
1129
Hans Verkuil7b3a0d42007-08-26 06:11:07 -03001130 printk(KERN_INFO "ivtvfb: Unloading framebuffer module\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001131
1132 for (i = 0; i < ivtv_cards_active; i++) {
1133 itv = ivtv_cards[i];
1134 if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) && itv->osd_info) {
Hans Verkuild343d7f2007-10-14 11:17:14 -03001135 if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) {
1136 IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n", i);
1137 return;
1138 }
Hans Verkuil641ed492007-08-28 03:24:31 -03001139 IVTVFB_DEBUG_INFO("Unregister framebuffer %d\n", i);
Hans Verkuil32db7752007-07-20 09:29:43 -03001140 ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info);
Hans Verkuil32db7752007-07-20 09:29:43 -03001141 ivtvfb_release_buffers(itv);
1142 itv->osd_video_pbase = 0;
1143 }
1144 }
1145}
1146
1147module_init(ivtvfb_init);
1148module_exit(ivtvfb_cleanup);