blob: df789f683e63ce1402f34725f0fe16ccb11e824a [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
Ian Armstrong77aded62007-11-05 14:27:09 -0300507 /* Pass screen size back to yuv handler */
508 itv->yuv_info.osd_full_w = ivtv_osd.pixel_stride;
509 itv->yuv_info.osd_full_h = ivtv_osd.lines;
510
Hans Verkuil32db7752007-07-20 09:29:43 -0300511 /* Force update of yuv registers */
512 itv->yuv_info.yuv_forced_update = 1;
513
Hans Verkuil641ed492007-08-28 03:24:31 -0300514 IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300515 var->xres, var->yres,
516 var->xres_virtual, var->yres_virtual,
517 var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300518
Hans Verkuil641ed492007-08-28 03:24:31 -0300519 IVTVFB_DEBUG_INFO("Display position: %d, %d\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300520 var->left_margin, var->upper_margin);
Hans Verkuil32db7752007-07-20 09:29:43 -0300521
Hans Verkuil641ed492007-08-28 03:24:31 -0300522 IVTVFB_DEBUG_INFO("Display filter: %s\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300523 (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
Hans Verkuil641ed492007-08-28 03:24:31 -0300524 IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
Hans Verkuil32db7752007-07-20 09:29:43 -0300525
526 return 0;
527}
528
529static int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix)
530{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300531 struct osd_info *oi = itv->osd_info;
532
Hans Verkuil641ed492007-08-28 03:24:31 -0300533 IVTVFB_DEBUG_INFO("ivtvfb_get_fix\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300534 memset(fix, 0, sizeof(struct fb_fix_screeninfo));
Hans Verkuilcebfadf2008-04-26 08:51:51 -0300535 strlcpy(fix->id, "cx23415 TV out", sizeof(fix->id));
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300536 fix->smem_start = oi->video_pbase;
537 fix->smem_len = oi->video_buffer_size;
Hans Verkuil32db7752007-07-20 09:29:43 -0300538 fix->type = FB_TYPE_PACKED_PIXELS;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300539 fix->visual = (oi->bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
Hans Verkuil32db7752007-07-20 09:29:43 -0300540 fix->xpanstep = 1;
541 fix->ypanstep = 1;
542 fix->ywrapstep = 0;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300543 fix->line_length = oi->display_byte_stride;
Hans Verkuil32db7752007-07-20 09:29:43 -0300544 fix->accel = FB_ACCEL_NONE;
545 return 0;
546}
547
548/* Check the requested display mode, returning -EINVAL if we can't
549 handle it. */
550
551static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv)
552{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300553 struct osd_info *oi = itv->osd_info;
Ian Armstrong68a341a2007-08-03 09:51:58 -0300554 int osd_height_limit;
555 u32 pixclock, hlimit, vlimit;
Hans Verkuil32db7752007-07-20 09:29:43 -0300556
Hans Verkuil641ed492007-08-28 03:24:31 -0300557 IVTVFB_DEBUG_INFO("ivtvfb_check_var\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300558
Ian Armstrong68a341a2007-08-03 09:51:58 -0300559 /* Set base references for mode calcs. */
560 if (itv->is_50hz) {
561 pixclock = 84316;
562 hlimit = 776;
563 vlimit = 591;
564 osd_height_limit = 576;
565 }
566 else {
567 pixclock = 83926;
568 hlimit = 776;
569 vlimit = 495;
570 osd_height_limit = 480;
571 }
572
Hans Verkuil32db7752007-07-20 09:29:43 -0300573 if (var->bits_per_pixel == 8 || var->bits_per_pixel == 32) {
574 var->transp.offset = 24;
575 var->transp.length = 8;
576 var->red.offset = 16;
577 var->red.length = 8;
578 var->green.offset = 8;
579 var->green.length = 8;
580 var->blue.offset = 0;
581 var->blue.length = 8;
582 }
583 else if (var->bits_per_pixel == 16) {
584 /* To find out the true mode, check green length */
585 switch (var->green.length) {
586 case 4:
Hans Verkuil32db7752007-07-20 09:29:43 -0300587 var->red.offset = 8;
588 var->red.length = 4;
589 var->green.offset = 4;
590 var->green.length = 4;
591 var->blue.offset = 0;
592 var->blue.length = 4;
Hans Verkuil459a52f2007-08-22 08:58:47 -0300593 var->transp.offset = 12;
594 var->transp.length = 1;
Hans Verkuil32db7752007-07-20 09:29:43 -0300595 break;
596 case 5:
Hans Verkuil32db7752007-07-20 09:29:43 -0300597 var->red.offset = 10;
598 var->red.length = 5;
599 var->green.offset = 5;
600 var->green.length = 5;
601 var->blue.offset = 0;
602 var->blue.length = 5;
Hans Verkuil459a52f2007-08-22 08:58:47 -0300603 var->transp.offset = 15;
604 var->transp.length = 1;
Hans Verkuil32db7752007-07-20 09:29:43 -0300605 break;
606 default:
Hans Verkuil32db7752007-07-20 09:29:43 -0300607 var->red.offset = 11;
608 var->red.length = 5;
609 var->green.offset = 5;
610 var->green.length = 6;
611 var->blue.offset = 0;
612 var->blue.length = 5;
Hans Verkuil459a52f2007-08-22 08:58:47 -0300613 var->transp.offset = 0;
614 var->transp.length = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300615 break;
616 }
617 }
618 else {
Hans Verkuil641ed492007-08-28 03:24:31 -0300619 IVTVFB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300620 return -EINVAL;
621 }
622
623 /* Check the resolution */
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300624 if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit) {
625 IVTVFB_DEBUG_WARN("Invalid resolution: %dx%d\n",
626 var->xres, var->yres);
627 return -EINVAL;
Hans Verkuil32db7752007-07-20 09:29:43 -0300628 }
Hans Verkuil32db7752007-07-20 09:29:43 -0300629
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300630 /* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */
631 if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) ||
632 var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8) > oi->video_buffer_size ||
633 var->xres_virtual < var->xres ||
634 var->yres_virtual < var->yres) {
635 IVTVFB_DEBUG_WARN("Invalid virtual resolution: %dx%d\n",
636 var->xres_virtual, var->yres_virtual);
637 return -EINVAL;
Hans Verkuil32db7752007-07-20 09:29:43 -0300638 }
639
640 /* Some extra checks if in 8 bit mode */
641 if (var->bits_per_pixel == 8) {
642 /* Width must be a multiple of 4 */
643 if (var->xres & 3) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300644 IVTVFB_DEBUG_WARN("Invalid resolution for 8bpp: %d\n", var->xres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300645 return -EINVAL;
646 }
647 if (var->xres_virtual & 3) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300648 IVTVFB_DEBUG_WARN("Invalid virtual resolution for 8bpp: %d)\n", var->xres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300649 return -EINVAL;
650 }
651 }
652 else if (var->bits_per_pixel == 16) {
653 /* Width must be a multiple of 2 */
654 if (var->xres & 1) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300655 IVTVFB_DEBUG_WARN("Invalid resolution for 16bpp: %d\n", var->xres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300656 return -EINVAL;
657 }
658 if (var->xres_virtual & 1) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300659 IVTVFB_DEBUG_WARN("Invalid virtual resolution for 16bpp: %d)\n", var->xres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300660 return -EINVAL;
661 }
662 }
663
664 /* Now check the offsets */
665 if (var->xoffset >= var->xres_virtual || var->yoffset >= var->yres_virtual) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300666 IVTVFB_DEBUG_WARN("Invalid offset: %d (%d) %d (%d)\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300667 var->xoffset, var->xres_virtual, var->yoffset, var->yres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300668 return -EINVAL;
669 }
670
671 /* Check pixel format */
672 if (var->nonstd > 1) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300673 IVTVFB_DEBUG_WARN("Invalid nonstd % d\n", var->nonstd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300674 return -EINVAL;
675 }
676
677 /* Check video mode */
678 if (((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) &&
679 ((var->vmode & FB_VMODE_MASK) != FB_VMODE_INTERLACED)) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300680 IVTVFB_DEBUG_WARN("Invalid video mode: %d\n", var->vmode & FB_VMODE_MASK);
Hans Verkuil32db7752007-07-20 09:29:43 -0300681 return -EINVAL;
682 }
683
684 /* Check the left & upper margins
685 If the margins are too large, just center the screen
686 (enforcing margins causes too many problems) */
687
688 if (var->left_margin + var->xres > IVTV_OSD_MAX_WIDTH + 1) {
689 var->left_margin = 1 + ((IVTV_OSD_MAX_WIDTH - var->xres) / 2);
690 }
691 if (var->upper_margin + var->yres > (itv->is_50hz ? 577 : 481)) {
692 var->upper_margin = 1 + (((itv->is_50hz ? 576 : 480) - var->yres) / 2);
693 }
694
695 /* Maintain overall 'size' for a constant refresh rate */
Ian Armstrong68a341a2007-08-03 09:51:58 -0300696 var->right_margin = hlimit - var->left_margin - var->xres;
697 var->lower_margin = vlimit - var->upper_margin - var->yres;
Hans Verkuil32db7752007-07-20 09:29:43 -0300698
699 /* Fixed sync times */
700 var->hsync_len = 24;
701 var->vsync_len = 2;
702
703 /* Non-interlaced / interlaced mode is used to switch the OSD filter
704 on or off. Adjust the clock timings to maintain a constant
705 vertical refresh rate. */
Hans Verkuil32db7752007-07-20 09:29:43 -0300706 if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED)
Ian Armstrong68a341a2007-08-03 09:51:58 -0300707 var->pixclock = pixclock / 2;
708 else
709 var->pixclock = pixclock;
Hans Verkuil32db7752007-07-20 09:29:43 -0300710
Hans Verkuil641ed492007-08-28 03:24:31 -0300711 IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300712 var->xres, var->yres,
713 var->xres_virtual, var->yres_virtual,
Hans Verkuil32db7752007-07-20 09:29:43 -0300714 var->bits_per_pixel);
715
Hans Verkuil641ed492007-08-28 03:24:31 -0300716 IVTVFB_DEBUG_INFO("Display position: %d, %d\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300717 var->left_margin, var->upper_margin);
Hans Verkuil32db7752007-07-20 09:29:43 -0300718
Hans Verkuil641ed492007-08-28 03:24:31 -0300719 IVTVFB_DEBUG_INFO("Display filter: %s\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300720 (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
Hans Verkuil641ed492007-08-28 03:24:31 -0300721 IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
Hans Verkuil32db7752007-07-20 09:29:43 -0300722 return 0;
723}
724
725static int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
726{
727 struct ivtv *itv = (struct ivtv *) info->par;
Hans Verkuil641ed492007-08-28 03:24:31 -0300728 IVTVFB_DEBUG_INFO("ivtvfb_check_var\n");
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300729 return _ivtvfb_check_var(var, itv);
Hans Verkuil32db7752007-07-20 09:29:43 -0300730}
731
732static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
733{
734 u32 osd_pan_index;
735 struct ivtv *itv = (struct ivtv *) info->par;
736
737 osd_pan_index = (var->xoffset + (var->yoffset * var->xres_virtual))*var->bits_per_pixel/8;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300738 write_reg(osd_pan_index, 0x02A0C);
Hans Verkuil32db7752007-07-20 09:29:43 -0300739
740 /* Pass this info back the yuv handler */
741 itv->yuv_info.osd_x_pan = var->xoffset;
742 itv->yuv_info.osd_y_pan = var->yoffset;
743 /* Force update of yuv registers */
744 itv->yuv_info.yuv_forced_update = 1;
745 return 0;
746}
747
748static int ivtvfb_set_par(struct fb_info *info)
749{
750 int rc = 0;
751 struct ivtv *itv = (struct ivtv *) info->par;
752
Hans Verkuil641ed492007-08-28 03:24:31 -0300753 IVTVFB_DEBUG_INFO("ivtvfb_set_par\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300754
755 rc = ivtvfb_set_var(itv, &info->var);
756 ivtvfb_pan_display(&info->var, info);
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300757 ivtvfb_get_fix(itv, &info->fix);
Hans Verkuil32db7752007-07-20 09:29:43 -0300758 return rc;
759}
760
761static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green,
762 unsigned blue, unsigned transp,
763 struct fb_info *info)
764{
765 u32 color, *palette;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300766 struct ivtv *itv = (struct ivtv *)info->par;
Hans Verkuil32db7752007-07-20 09:29:43 -0300767
768 if (regno >= info->cmap.len)
769 return -EINVAL;
770
771 color = ((transp & 0xFF00) << 16) |((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8);
772 if (info->var.bits_per_pixel <= 8) {
773 write_reg(regno, 0x02a30);
774 write_reg(color, 0x02a34);
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300775 return 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300776 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300777 if (regno >= 16)
778 return -EINVAL;
Hans Verkuil32db7752007-07-20 09:29:43 -0300779
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300780 palette = info->pseudo_palette;
781 if (info->var.bits_per_pixel == 16) {
782 switch (info->var.green.length) {
783 case 4:
784 color = ((red & 0xf000) >> 4) |
785 ((green & 0xf000) >> 8) |
786 ((blue & 0xf000) >> 12);
787 break;
788 case 5:
789 color = ((red & 0xf800) >> 1) |
790 ((green & 0xf800) >> 6) |
791 ((blue & 0xf800) >> 11);
792 break;
793 case 6:
794 color = (red & 0xf800 ) |
795 ((green & 0xfc00) >> 5) |
796 ((blue & 0xf800) >> 11);
797 break;
Hans Verkuil32db7752007-07-20 09:29:43 -0300798 }
Hans Verkuil32db7752007-07-20 09:29:43 -0300799 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300800 palette[regno] = color;
Hans Verkuil32db7752007-07-20 09:29:43 -0300801 return 0;
802}
803
804/* We don't really support blanking. All this does is enable or
805 disable the OSD. */
806static int ivtvfb_blank(int blank_mode, struct fb_info *info)
807{
808 struct ivtv *itv = (struct ivtv *)info->par;
809
Hans Verkuil641ed492007-08-28 03:24:31 -0300810 IVTVFB_DEBUG_INFO("Set blanking mode : %d\n", blank_mode);
Hans Verkuil32db7752007-07-20 09:29:43 -0300811 switch (blank_mode) {
812 case FB_BLANK_UNBLANK:
813 ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1);
814 break;
815 case FB_BLANK_NORMAL:
816 case FB_BLANK_HSYNC_SUSPEND:
817 case FB_BLANK_VSYNC_SUSPEND:
818 case FB_BLANK_POWERDOWN:
819 ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
820 break;
821 }
822 return 0;
823}
824
825static struct fb_ops ivtvfb_ops = {
826 .owner = THIS_MODULE,
827 .fb_check_var = ivtvfb_check_var,
828 .fb_set_par = ivtvfb_set_par,
829 .fb_setcolreg = ivtvfb_setcolreg,
830 .fb_fillrect = cfb_fillrect,
831 .fb_copyarea = cfb_copyarea,
832 .fb_imageblit = cfb_imageblit,
833 .fb_cursor = NULL,
834 .fb_ioctl = ivtvfb_ioctl,
835 .fb_pan_display = ivtvfb_pan_display,
836 .fb_blank = ivtvfb_blank,
837};
838
839/* Initialization */
840
841
842/* Setup our initial video mode */
843static int ivtvfb_init_vidmode(struct ivtv *itv)
844{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300845 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300846 struct v4l2_rect start_window;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300847 int max_height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300848
Hans Verkuil32db7752007-07-20 09:29:43 -0300849 /* Color mode */
850
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300851 if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32)
852 osd_depth = 8;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300853 oi->bits_per_pixel = osd_depth;
854 oi->bytes_per_pixel = oi->bits_per_pixel / 8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300855
856 /* Horizontal size & position */
857
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300858 if (osd_xres > 720)
859 osd_xres = 720;
Hans Verkuil32db7752007-07-20 09:29:43 -0300860
861 /* Must be a multiple of 4 for 8bpp & 2 for 16bpp */
862 if (osd_depth == 8)
863 osd_xres &= ~3;
864 else if (osd_depth == 16)
865 osd_xres &= ~1;
866
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300867 start_window.width = osd_xres ? osd_xres : 640;
Hans Verkuil32db7752007-07-20 09:29:43 -0300868
869 /* Check horizontal start (osd_left). */
870 if (osd_left && osd_left + start_window.width > 721) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300871 IVTVFB_ERR("Invalid osd_left - assuming default\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300872 osd_left = 0;
873 }
874
875 /* Hardware coords start at 0, user coords start at 1. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300876 osd_left--;
Hans Verkuil32db7752007-07-20 09:29:43 -0300877
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300878 start_window.left = osd_left >= 0 ? osd_left : ((IVTV_OSD_MAX_WIDTH - start_window.width) / 2);
Hans Verkuil32db7752007-07-20 09:29:43 -0300879
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300880 oi->display_byte_stride =
881 start_window.width * oi->bytes_per_pixel;
Hans Verkuil32db7752007-07-20 09:29:43 -0300882
883 /* Vertical size & position */
884
885 max_height = itv->is_50hz ? 576 : 480;
886
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300887 if (osd_yres > max_height)
888 osd_yres = max_height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300889
Ian Armstrong6b1ec9d2007-10-17 15:09:56 -0300890 start_window.height = osd_yres ? osd_yres : itv->is_50hz ? 480 : 400;
Hans Verkuil32db7752007-07-20 09:29:43 -0300891
892 /* Check vertical start (osd_upper). */
893 if (osd_upper + start_window.height > max_height + 1) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300894 IVTVFB_ERR("Invalid osd_upper - assuming default\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300895 osd_upper = 0;
896 }
897
898 /* Hardware coords start at 0, user coords start at 1. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300899 osd_upper--;
Hans Verkuil32db7752007-07-20 09:29:43 -0300900
901 start_window.top = osd_upper >= 0 ? osd_upper : ((max_height - start_window.height) / 2);
902
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300903 oi->display_width = start_window.width;
904 oi->display_height = start_window.height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300905
906 /* Generate a valid fb_var_screeninfo */
907
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300908 oi->ivtvfb_defined.xres = oi->display_width;
909 oi->ivtvfb_defined.yres = oi->display_height;
910 oi->ivtvfb_defined.xres_virtual = oi->display_width;
911 oi->ivtvfb_defined.yres_virtual = oi->display_height;
912 oi->ivtvfb_defined.bits_per_pixel = oi->bits_per_pixel;
913 oi->ivtvfb_defined.vmode = (osd_laced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED);
914 oi->ivtvfb_defined.left_margin = start_window.left + 1;
915 oi->ivtvfb_defined.upper_margin = start_window.top + 1;
916 oi->ivtvfb_defined.accel_flags = FB_ACCEL_NONE;
917 oi->ivtvfb_defined.nonstd = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300918
919 /* We've filled in the most data, let the usual mode check
920 routine fill in the rest. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300921 _ivtvfb_check_var(&oi->ivtvfb_defined, itv);
Hans Verkuil32db7752007-07-20 09:29:43 -0300922
923 /* Generate valid fb_fix_screeninfo */
924
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300925 ivtvfb_get_fix(itv, &oi->ivtvfb_fix);
Hans Verkuil32db7752007-07-20 09:29:43 -0300926
927 /* Generate valid fb_info */
928
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300929 oi->ivtvfb_info.node = -1;
930 oi->ivtvfb_info.flags = FBINFO_FLAG_DEFAULT;
931 oi->ivtvfb_info.fbops = &ivtvfb_ops;
932 oi->ivtvfb_info.par = itv;
933 oi->ivtvfb_info.var = oi->ivtvfb_defined;
934 oi->ivtvfb_info.fix = oi->ivtvfb_fix;
935 oi->ivtvfb_info.screen_base = (u8 __iomem *)oi->video_vbase;
936 oi->ivtvfb_info.fbops = &ivtvfb_ops;
Hans Verkuil32db7752007-07-20 09:29:43 -0300937
938 /* Supply some monitor specs. Bogus values will do for now */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300939 oi->ivtvfb_info.monspecs.hfmin = 8000;
940 oi->ivtvfb_info.monspecs.hfmax = 70000;
941 oi->ivtvfb_info.monspecs.vfmin = 10;
942 oi->ivtvfb_info.monspecs.vfmax = 100;
Hans Verkuil32db7752007-07-20 09:29:43 -0300943
944 /* Allocate color map */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300945 if (fb_alloc_cmap(&oi->ivtvfb_info.cmap, 256, 1)) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300946 IVTVFB_ERR("abort, unable to alloc cmap\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300947 return -ENOMEM;
948 }
949
950 /* Allocate the pseudo palette */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300951 oi->ivtvfb_info.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
Hans Verkuil32db7752007-07-20 09:29:43 -0300952
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300953 if (!oi->ivtvfb_info.pseudo_palette) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300954 IVTVFB_ERR("abort, unable to alloc pseudo pallete\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300955 return -ENOMEM;
956 }
957
958 return 0;
959}
960
961/* Find OSD buffer base & size. Add to mtrr. Zero osd buffer. */
962
963static int ivtvfb_init_io(struct ivtv *itv)
964{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300965 struct osd_info *oi = itv->osd_info;
966
Hans Verkuil26e9d592007-08-25 05:41:52 -0300967 mutex_lock(&itv->serialize_lock);
Hans Verkuil6e5eb592007-07-25 12:55:52 -0300968 if (ivtv_init_on_first_open(itv)) {
Hans Verkuil26e9d592007-08-25 05:41:52 -0300969 mutex_unlock(&itv->serialize_lock);
Hans Verkuil641ed492007-08-28 03:24:31 -0300970 IVTVFB_ERR("Failed to initialize ivtv\n");
Hans Verkuil6e5eb592007-07-25 12:55:52 -0300971 return -ENXIO;
972 }
Hans Verkuil26e9d592007-08-25 05:41:52 -0300973 mutex_unlock(&itv->serialize_lock);
Hans Verkuil6e5eb592007-07-25 12:55:52 -0300974
Hans Verkuil641ed492007-08-28 03:24:31 -0300975 ivtvfb_get_framebuffer(itv, &oi->video_rbase, &oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -0300976
977 /* The osd buffer size depends on the number of video buffers allocated
978 on the PVR350 itself. For now we'll hardcode the smallest osd buffer
979 size to prevent any overlap. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300980 oi->video_buffer_size = 1704960;
Hans Verkuil32db7752007-07-20 09:29:43 -0300981
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300982 oi->video_pbase = itv->base_addr + IVTV_DECODER_OFFSET + oi->video_rbase;
983 oi->video_vbase = itv->dec_mem + oi->video_rbase;
Hans Verkuil32db7752007-07-20 09:29:43 -0300984
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300985 if (!oi->video_vbase) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300986 IVTVFB_ERR("abort, video memory 0x%x @ 0x%lx isn't mapped!\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300987 oi->video_buffer_size, oi->video_pbase);
Hans Verkuil32db7752007-07-20 09:29:43 -0300988 return -EIO;
989 }
990
Hans Verkuil641ed492007-08-28 03:24:31 -0300991 IVTVFB_INFO("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300992 oi->video_pbase, oi->video_vbase,
993 oi->video_buffer_size / 1024);
Hans Verkuil32db7752007-07-20 09:29:43 -0300994
995#ifdef CONFIG_MTRR
996 {
997 /* Find the largest power of two that maps the whole buffer */
998 int size_shift = 31;
999
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001000 while (!(oi->video_buffer_size & (1 << size_shift))) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001001 size_shift--;
1002 }
1003 size_shift++;
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001004 oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1);
1005 oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size;
1006 oi->fb_end_aligned_physaddr += (1 << size_shift) - 1;
1007 oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1);
1008 if (mtrr_add(oi->fb_start_aligned_physaddr,
1009 oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr,
Hans Verkuil32db7752007-07-20 09:29:43 -03001010 MTRR_TYPE_WRCOMB, 1) < 0) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001011 IVTVFB_INFO("disabled mttr\n");
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001012 oi->fb_start_aligned_physaddr = 0;
1013 oi->fb_end_aligned_physaddr = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -03001014 }
1015 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001016#endif
Hans Verkuil32db7752007-07-20 09:29:43 -03001017
1018 /* Blank the entire osd. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001019 memset_io(oi->video_vbase, 0, oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -03001020
1021 return 0;
1022}
1023
1024/* Release any memory we've grabbed & remove mtrr entry */
1025static void ivtvfb_release_buffers (struct ivtv *itv)
1026{
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001027 struct osd_info *oi = itv->osd_info;
1028
Hans Verkuil32db7752007-07-20 09:29:43 -03001029 /* Release cmap */
Adrian Bunk13628032007-08-28 03:28:04 -03001030 if (oi->ivtvfb_info.cmap.len)
1031 fb_dealloc_cmap(&oi->ivtvfb_info.cmap);
Hans Verkuil32db7752007-07-20 09:29:43 -03001032
1033 /* Release pseudo palette */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001034 if (oi->ivtvfb_info.pseudo_palette)
1035 kfree(oi->ivtvfb_info.pseudo_palette);
Hans Verkuil32db7752007-07-20 09:29:43 -03001036
1037#ifdef CONFIG_MTRR
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001038 if (oi->fb_end_aligned_physaddr) {
1039 mtrr_del(-1, oi->fb_start_aligned_physaddr,
1040 oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr);
1041 }
1042#endif
Hans Verkuil32db7752007-07-20 09:29:43 -03001043
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001044 kfree(oi);
Hans Verkuil32db7752007-07-20 09:29:43 -03001045 itv->osd_info = NULL;
1046}
1047
1048/* Initialize the specified card */
1049
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001050static int ivtvfb_init_card(struct ivtv *itv)
Hans Verkuil32db7752007-07-20 09:29:43 -03001051{
1052 int rc;
1053
1054 if (itv->osd_info) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001055 IVTVFB_ERR("Card %d already initialised\n", ivtvfb_card_id);
Hans Verkuil32db7752007-07-20 09:29:43 -03001056 return -EBUSY;
1057 }
1058
1059 itv->osd_info = kzalloc(sizeof(struct osd_info), GFP_ATOMIC);
Richard Knutsson14d5deb2007-12-08 10:35:06 -03001060 if (itv->osd_info == NULL) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001061 IVTVFB_ERR("Failed to allocate memory for osd_info\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001062 return -ENOMEM;
1063 }
1064
1065 /* Find & setup the OSD buffer */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001066 if ((rc = ivtvfb_init_io(itv)))
Hans Verkuil32db7752007-07-20 09:29:43 -03001067 return rc;
1068
1069 /* Set the startup video mode information */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001070 if ((rc = ivtvfb_init_vidmode(itv))) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001071 ivtvfb_release_buffers(itv);
1072 return rc;
1073 }
1074
1075 /* Register the framebuffer */
1076 if (register_framebuffer(&itv->osd_info->ivtvfb_info) < 0) {
1077 ivtvfb_release_buffers(itv);
1078 return -EINVAL;
1079 }
1080
1081 itv->osd_video_pbase = itv->osd_info->video_pbase;
1082
1083 /* Set the card to the requested mode */
1084 ivtvfb_set_par(&itv->osd_info->ivtvfb_info);
1085
1086 /* Set color 0 to black */
1087 write_reg(0, 0x02a30);
1088 write_reg(0, 0x02a34);
1089
1090 /* Enable the osd */
1091 ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info);
1092
Hans Verkuil32db7752007-07-20 09:29:43 -03001093 /* Allocate DMA */
1094 ivtv_udma_alloc(itv);
1095 return 0;
1096
1097}
1098
1099static int __init ivtvfb_init(void)
1100{
1101 struct ivtv *itv;
1102 int i, registered = 0;
1103
Hans Verkuil641ed492007-08-28 03:24:31 -03001104 if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) {
1105 printk(KERN_ERR "ivtvfb: ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n",
Hans Verkuil32db7752007-07-20 09:29:43 -03001106 IVTV_MAX_CARDS - 1);
1107 return -EINVAL;
1108 }
1109
1110 /* Locate & initialise all cards supporting an OSD. */
1111 for (i = 0; i < ivtv_cards_active; i++) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001112 if (ivtvfb_card_id != -1 && i != ivtvfb_card_id)
Hans Verkuil32db7752007-07-20 09:29:43 -03001113 continue;
1114 itv = ivtv_cards[i];
1115 if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
1116 if (ivtvfb_init_card(itv) == 0) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001117 IVTVFB_INFO("Framebuffer registered on ivtv card id %d\n", i);
Hans Verkuil32db7752007-07-20 09:29:43 -03001118 registered++;
1119 }
1120 }
1121 }
1122 if (!registered) {
Hans Verkuil7b3a0d42007-08-26 06:11:07 -03001123 printk(KERN_ERR "ivtvfb: no cards found");
Hans Verkuil32db7752007-07-20 09:29:43 -03001124 return -ENODEV;
1125 }
1126 return 0;
1127}
1128
1129static void ivtvfb_cleanup(void)
1130{
1131 struct ivtv *itv;
1132 int i;
1133
Hans Verkuil7b3a0d42007-08-26 06:11:07 -03001134 printk(KERN_INFO "ivtvfb: Unloading framebuffer module\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001135
1136 for (i = 0; i < ivtv_cards_active; i++) {
1137 itv = ivtv_cards[i];
1138 if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) && itv->osd_info) {
Hans Verkuild343d7f2007-10-14 11:17:14 -03001139 if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) {
1140 IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n", i);
1141 return;
1142 }
Hans Verkuil641ed492007-08-28 03:24:31 -03001143 IVTVFB_DEBUG_INFO("Unregister framebuffer %d\n", i);
Hans Verkuil32db7752007-07-20 09:29:43 -03001144 ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info);
Hans Verkuil32db7752007-07-20 09:29:43 -03001145 ivtvfb_release_buffers(itv);
1146 itv->osd_video_pbase = 0;
1147 }
1148 }
1149}
1150
1151module_init(ivtvfb_init);
1152module_exit(ivtvfb_cleanup);