blob: 4a7512d90bc92babbe37b2eeda36e7042a3db223 [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;
58static int osd_compat;
59static int osd_depth;
60static int osd_upper;
61static int osd_left;
62static int osd_yres;
63static int osd_xres;
64
Hans Verkuil641ed492007-08-28 03:24:31 -030065module_param(ivtvfb_card_id, int, 0444);
66module_param_named(debug,ivtvfb_debug, int, 0644);
Hans Verkuil32db7752007-07-20 09:29:43 -030067module_param(osd_laced, bool, 0444);
68module_param(osd_compat, bool, 0444);
69module_param(osd_depth, int, 0444);
70module_param(osd_upper, int, 0444);
71module_param(osd_left, int, 0444);
72module_param(osd_yres, int, 0444);
73module_param(osd_xres, int, 0444);
74
Hans Verkuil641ed492007-08-28 03:24:31 -030075MODULE_PARM_DESC(ivtvfb_card_id,
Hans Verkuil32db7752007-07-20 09:29:43 -030076 "Only use framebuffer of the specified ivtv card (0-31)\n"
77 "\t\t\tdefault -1: initialize all available framebuffers");
78
79MODULE_PARM_DESC(debug,
80 "Debug level (bitmask). Default: errors only\n"
81 "\t\t\t(debug = 3 gives full debugging)");
82
83MODULE_PARM_DESC(osd_compat,
84 "Compatibility mode - Display size is locked (use for old X drivers)\n"
85 "\t\t\t0=off\n"
86 "\t\t\t1=on\n"
87 "\t\t\tdefault off");
88
89/* Why upper, left, xres, yres, depth, laced ? To match terminology used
90 by fbset.
91 Why start at 1 for left & upper coordinate ? Because X doesn't allow 0 */
92
93MODULE_PARM_DESC(osd_laced,
94 "Interlaced mode\n"
95 "\t\t\t0=off\n"
96 "\t\t\t1=on\n"
97 "\t\t\tdefault off");
98
99MODULE_PARM_DESC(osd_depth,
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300100 "Bits per pixel - 8, 16, 32\n"
Hans Verkuil32db7752007-07-20 09:29:43 -0300101 "\t\t\tdefault 8");
102
103MODULE_PARM_DESC(osd_upper,
104 "Vertical start position\n"
105 "\t\t\tdefault 0 (Centered)");
106
107MODULE_PARM_DESC(osd_left,
108 "Horizontal start position\n"
109 "\t\t\tdefault 0 (Centered)");
110
111MODULE_PARM_DESC(osd_yres,
112 "Display height\n"
113 "\t\t\tdefault 480 (PAL)\n"
114 "\t\t\t 400 (NTSC)");
115
116MODULE_PARM_DESC(osd_xres,
117 "Display width\n"
118 "\t\t\tdefault 640");
119
120MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil, John Harvey, Ian Armstrong");
121MODULE_LICENSE("GPL");
122
123/* --------------------------------------------------------------------- */
124
Hans Verkuil641ed492007-08-28 03:24:31 -0300125#define IVTVFB_DBGFLG_WARN (1 << 0)
126#define IVTVFB_DBGFLG_INFO (1 << 1)
Hans Verkuil32db7752007-07-20 09:29:43 -0300127
Hans Verkuil641ed492007-08-28 03:24:31 -0300128#define IVTVFB_DEBUG(x, type, fmt, args...) \
Hans Verkuil32db7752007-07-20 09:29:43 -0300129 do { \
Hans Verkuil641ed492007-08-28 03:24:31 -0300130 if ((x) & ivtvfb_debug) \
Hans Verkuil7b3a0d42007-08-26 06:11:07 -0300131 printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->num , ## args); \
Hans Verkuil32db7752007-07-20 09:29:43 -0300132 } while (0)
Hans Verkuil641ed492007-08-28 03:24:31 -0300133#define IVTVFB_DEBUG_WARN(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_WARN, "warning", fmt , ## args)
134#define IVTVFB_DEBUG_INFO(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_INFO, "info", fmt , ## args)
Hans Verkuil32db7752007-07-20 09:29:43 -0300135
136/* Standard kernel messages */
Hans Verkuil641ed492007-08-28 03:24:31 -0300137#define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->num , ## args)
138#define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->num , ## args)
139#define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->num , ## args)
Hans Verkuil32db7752007-07-20 09:29:43 -0300140
141/* --------------------------------------------------------------------- */
142
143#define IVTV_OSD_MAX_WIDTH 720
144#define IVTV_OSD_MAX_HEIGHT 576
145
146#define IVTV_OSD_BPP_8 0x00
147#define IVTV_OSD_BPP_16_444 0x03
148#define IVTV_OSD_BPP_16_555 0x02
149#define IVTV_OSD_BPP_16_565 0x01
150#define IVTV_OSD_BPP_32 0x04
151
152struct osd_info {
Hans Verkuil32db7752007-07-20 09:29:43 -0300153 /* Physical base address */
154 unsigned long video_pbase;
155 /* Relative base address (relative to start of decoder memory) */
156 u32 video_rbase;
157 /* Mapped base address */
158 volatile char __iomem *video_vbase;
159 /* Buffer size */
160 u32 video_buffer_size;
161
162#ifdef CONFIG_MTRR
163 /* video_base rounded down as required by hardware MTRRs */
164 unsigned long fb_start_aligned_physaddr;
165 /* video_base rounded up as required by hardware MTRRs */
166 unsigned long fb_end_aligned_physaddr;
167#endif
168
169 /* Store the buffer offset */
170 int set_osd_coords_x;
171 int set_osd_coords_y;
172
173 /* Current dimensions (NOT VISIBLE SIZE!) */
174 int display_width;
175 int display_height;
176 int display_byte_stride;
177
178 /* Current bits per pixel */
179 int bits_per_pixel;
180 int bytes_per_pixel;
181
182 /* Frame buffer stuff */
183 struct fb_info ivtvfb_info;
184 struct fb_var_screeninfo ivtvfb_defined;
185 struct fb_fix_screeninfo ivtvfb_fix;
186};
187
188struct ivtv_osd_coords {
189 unsigned long offset;
190 unsigned long max_offset;
191 int pixel_stride;
192 int lines;
193 int x;
194 int y;
195};
196
197/* --------------------------------------------------------------------- */
198
199/* ivtv API calls for framebuffer related support */
200
Hans Verkuil641ed492007-08-28 03:24:31 -0300201static int ivtvfb_get_framebuffer(struct ivtv *itv, u32 *fbbase,
Hans Verkuil32db7752007-07-20 09:29:43 -0300202 u32 *fblength)
203{
204 u32 data[CX2341X_MBOX_MAX_DATA];
205 int rc;
206
207 rc = ivtv_vapi_result(itv, data, CX2341X_OSD_GET_FRAMEBUFFER, 0);
208 *fbbase = data[0];
209 *fblength = data[1];
210 return rc;
211}
212
Hans Verkuil641ed492007-08-28 03:24:31 -0300213static int ivtvfb_get_osd_coords(struct ivtv *itv,
Hans Verkuil32db7752007-07-20 09:29:43 -0300214 struct ivtv_osd_coords *osd)
215{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300216 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300217 u32 data[CX2341X_MBOX_MAX_DATA];
218
219 ivtv_vapi_result(itv, data, CX2341X_OSD_GET_OSD_COORDS, 0);
220
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300221 osd->offset = data[0] - oi->video_rbase;
222 osd->max_offset = oi->display_width * oi->display_height * 4;
Hans Verkuil32db7752007-07-20 09:29:43 -0300223 osd->pixel_stride = data[1];
224 osd->lines = data[2];
225 osd->x = data[3];
226 osd->y = data[4];
227 return 0;
228}
229
Hans Verkuil641ed492007-08-28 03:24:31 -0300230static int ivtvfb_set_osd_coords(struct ivtv *itv, const struct ivtv_osd_coords *osd)
Hans Verkuil32db7752007-07-20 09:29:43 -0300231{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300232 struct osd_info *oi = itv->osd_info;
233
234 oi->display_width = osd->pixel_stride;
235 oi->display_byte_stride = osd->pixel_stride * oi->bytes_per_pixel;
236 oi->set_osd_coords_x += osd->x;
237 oi->set_osd_coords_y = osd->y;
Hans Verkuil32db7752007-07-20 09:29:43 -0300238
239 return ivtv_vapi(itv, CX2341X_OSD_SET_OSD_COORDS, 5,
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300240 osd->offset + oi->video_rbase,
Hans Verkuil32db7752007-07-20 09:29:43 -0300241 osd->pixel_stride,
242 osd->lines, osd->x, osd->y);
243}
244
Hans Verkuil641ed492007-08-28 03:24:31 -0300245static int ivtvfb_set_display_window(struct ivtv *itv, struct v4l2_rect *ivtv_window)
Hans Verkuil32db7752007-07-20 09:29:43 -0300246{
Hans Verkuil32db7752007-07-20 09:29:43 -0300247 int osd_height_limit = itv->is_50hz ? 576 : 480;
248
249 /* Only fail if resolution too high, otherwise fudge the start coords. */
250 if ((ivtv_window->height > osd_height_limit) || (ivtv_window->width > IVTV_OSD_MAX_WIDTH))
251 return -EINVAL;
252
253 /* Ensure we don't exceed display limits */
254 if (ivtv_window->top + ivtv_window->height > osd_height_limit) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300255 IVTVFB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid height setting (%d, %d)\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300256 ivtv_window->top, ivtv_window->height);
257 ivtv_window->top = osd_height_limit - ivtv_window->height;
258 }
259
260 if (ivtv_window->left + ivtv_window->width > IVTV_OSD_MAX_WIDTH) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300261 IVTVFB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid width setting (%d, %d)\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300262 ivtv_window->left, ivtv_window->width);
263 ivtv_window->left = IVTV_OSD_MAX_WIDTH - ivtv_window->width;
264 }
265
266 /* Set the OSD origin */
267 write_reg((ivtv_window->top << 16) | ivtv_window->left, 0x02a04);
268
269 /* How much to display */
270 write_reg(((ivtv_window->top+ivtv_window->height) << 16) | (ivtv_window->left+ivtv_window->width), 0x02a08);
271
272 /* Pass this info back the yuv handler */
273 itv->yuv_info.osd_vis_w = ivtv_window->width;
274 itv->yuv_info.osd_vis_h = ivtv_window->height;
275 itv->yuv_info.osd_x_offset = ivtv_window->left;
276 itv->yuv_info.osd_y_offset = ivtv_window->top;
277
278 return 0;
279}
280
Hans Verkuil641ed492007-08-28 03:24:31 -0300281static int ivtvfb_prep_dec_dma_to_device(struct ivtv *itv,
Hans Verkuil32db7752007-07-20 09:29:43 -0300282 unsigned long ivtv_dest_addr, void __user *userbuf,
283 int size_in_bytes)
284{
285 DEFINE_WAIT(wait);
286 int ret = 0;
287 int got_sig = 0;
288
289 mutex_lock(&itv->udma.lock);
290 /* Map User DMA */
291 if (ivtv_udma_setup(itv, ivtv_dest_addr, userbuf, size_in_bytes) <= 0) {
292 mutex_unlock(&itv->udma.lock);
Hans Verkuil641ed492007-08-28 03:24:31 -0300293 IVTVFB_WARN("ivtvfb_prep_dec_dma_to_device, "
Hans Verkuil32db7752007-07-20 09:29:43 -0300294 "Error with get_user_pages: %d bytes, %d pages returned\n",
295 size_in_bytes, itv->udma.page_count);
296
297 /* get_user_pages must have failed completely */
298 return -EIO;
299 }
300
Hans Verkuil641ed492007-08-28 03:24:31 -0300301 IVTVFB_DEBUG_INFO("ivtvfb_prep_dec_dma_to_device, %d bytes, %d pages\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300302 size_in_bytes, itv->udma.page_count);
303
304 ivtv_udma_prepare(itv);
305 prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
306 /* if no UDMA is pending and no UDMA is in progress, then the DMA
307 is finished */
308 while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) {
309 /* don't interrupt if the DMA is in progress but break off
310 a still pending DMA. */
311 got_sig = signal_pending(current);
312 if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))
313 break;
314 got_sig = 0;
315 schedule();
316 }
317 finish_wait(&itv->dma_waitq, &wait);
318
319 /* Unmap Last DMA Xfer */
320 ivtv_udma_unmap(itv);
321 mutex_unlock(&itv->udma.lock);
322 if (got_sig) {
323 IVTV_DEBUG_INFO("User stopped OSD\n");
324 return -EINTR;
325 }
326
327 return ret;
328}
329
Hans Verkuil641ed492007-08-28 03:24:31 -0300330static int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source,
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300331 unsigned long dest_offset, int count)
Hans Verkuil32db7752007-07-20 09:29:43 -0300332{
333 DEFINE_WAIT(wait);
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300334 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300335
336 /* Nothing to do */
337 if (count == 0) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300338 IVTVFB_DEBUG_WARN("ivtvfb_prep_frame: Nothing to do. count = 0\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300339 return -EINVAL;
340 }
341
342 /* Check Total FB Size */
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300343 if ((dest_offset + count) > oi->video_buffer_size) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300344 IVTVFB_WARN("ivtvfb_prep_frame: Overflowing the framebuffer %ld, only %d available\n",
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300345 dest_offset + count, oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -0300346 return -E2BIG;
347 }
348
349 /* Not fatal, but will have undesirable results */
350 if ((unsigned long)source & 3)
Hans Verkuil641ed492007-08-28 03:24:31 -0300351 IVTVFB_WARN("ivtvfb_prep_frame: Source address not 32 bit aligned (0x%08lx)\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300352 (unsigned long)source);
Hans Verkuil32db7752007-07-20 09:29:43 -0300353
354 if (dest_offset & 3)
Hans Verkuil641ed492007-08-28 03:24:31 -0300355 IVTVFB_WARN("ivtvfb_prep_frame: Dest offset not 32 bit aligned (%ld)\n", dest_offset);
Hans Verkuil32db7752007-07-20 09:29:43 -0300356
357 if (count & 3)
Hans Verkuil641ed492007-08-28 03:24:31 -0300358 IVTVFB_WARN("ivtvfb_prep_frame: Count not a multiple of 4 (%d)\n", count);
Hans Verkuil32db7752007-07-20 09:29:43 -0300359
360 /* Check Source */
361 if (!access_ok(VERIFY_READ, source + dest_offset, count)) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300362 IVTVFB_WARN("Invalid userspace pointer 0x%08lx\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300363 (unsigned long)source);
364
Hans Verkuil641ed492007-08-28 03:24:31 -0300365 IVTVFB_DEBUG_WARN("access_ok() failed for offset 0x%08lx source 0x%08lx count %d\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300366 dest_offset, (unsigned long)source,
367 count);
368 return -EINVAL;
369 }
370
371 /* OSD Address to send DMA to */
Hans Verkuil33c0fca2007-08-23 06:32:46 -0300372 dest_offset += IVTV_DECODER_OFFSET + oi->video_rbase;
Hans Verkuil32db7752007-07-20 09:29:43 -0300373
374 /* Fill Buffers */
Hans Verkuil641ed492007-08-28 03:24:31 -0300375 return ivtvfb_prep_dec_dma_to_device(itv, dest_offset, source, count);
Hans Verkuil32db7752007-07-20 09:29:43 -0300376}
377
378static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
379{
380 DEFINE_WAIT(wait);
381 struct ivtv *itv = (struct ivtv *)info->par;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300382 int rc = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300383
384 switch (cmd) {
Hans Verkuil32db7752007-07-20 09:29:43 -0300385 case FBIOGET_VBLANK: {
386 struct fb_vblank vblank;
387 u32 trace;
388
389 vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT |
390 FB_VBLANK_HAVE_VSYNC;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300391 trace = read_reg(0x028c0) >> 16;
Hans Verkuil32db7752007-07-20 09:29:43 -0300392 if (itv->is_50hz && trace > 312) trace -= 312;
393 else if (itv->is_60hz && trace > 262) trace -= 262;
394 if (trace == 1) vblank.flags |= FB_VBLANK_VSYNCING;
Hans Verkuila158f352007-08-23 11:31:57 -0300395 vblank.count = itv->last_vsync_field;
Hans Verkuil32db7752007-07-20 09:29:43 -0300396 vblank.vcount = trace;
397 vblank.hcount = 0;
398 if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank)))
399 return -EFAULT;
400 return 0;
401 }
402
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300403 case FBIO_WAITFORVSYNC:
Hans Verkuil32db7752007-07-20 09:29:43 -0300404 prepare_to_wait(&itv->vsync_waitq, &wait, TASK_INTERRUPTIBLE);
Hans Verkuil84149a02007-07-20 18:17:18 -0300405 if (!schedule_timeout(msecs_to_jiffies(50))) rc = -ETIMEDOUT;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300406 finish_wait(&itv->vsync_waitq, &wait);
Hans Verkuil32db7752007-07-20 09:29:43 -0300407 return rc;
Hans Verkuil32db7752007-07-20 09:29:43 -0300408
Hans Verkuild715e762007-07-20 10:30:32 -0300409 case IVTVFB_IOC_DMA_FRAME: {
410 struct ivtvfb_dma_frame args;
Hans Verkuil32db7752007-07-20 09:29:43 -0300411
Hans Verkuil641ed492007-08-28 03:24:31 -0300412 IVTVFB_DEBUG_INFO("IVTVFB_IOC_DMA_FRAME\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300413 if (copy_from_user(&args, (void __user *)arg, sizeof(args)))
414 return -EFAULT;
415
Hans Verkuil641ed492007-08-28 03:24:31 -0300416 return ivtvfb_prep_frame(itv, cmd, args.source, args.dest_offset, args.count);
Hans Verkuil32db7752007-07-20 09:29:43 -0300417 }
418
419 default:
Hans Verkuil641ed492007-08-28 03:24:31 -0300420 IVTVFB_DEBUG_INFO("Unknown ioctl %08x\n", cmd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300421 return -EINVAL;
422 }
423 return 0;
424}
425
426/* Framebuffer device handling */
427
428static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var)
429{
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300430 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300431 struct ivtv_osd_coords ivtv_osd;
432 struct v4l2_rect ivtv_window;
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300433 int osd_mode = -1;
Hans Verkuil32db7752007-07-20 09:29:43 -0300434
Hans Verkuil641ed492007-08-28 03:24:31 -0300435 IVTVFB_DEBUG_INFO("ivtvfb_set_var\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300436
437 /* Select color space */
438 if (var->nonstd) /* YUV */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300439 write_reg(read_reg(0x02a00) | 0x0002000, 0x02a00);
Hans Verkuil32db7752007-07-20 09:29:43 -0300440 else /* RGB */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300441 write_reg(read_reg(0x02a00) & ~0x0002000, 0x02a00);
Hans Verkuil32db7752007-07-20 09:29:43 -0300442
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300443 /* Set the color mode */
Hans Verkuil32db7752007-07-20 09:29:43 -0300444 switch (var->bits_per_pixel) {
445 case 8:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300446 osd_mode = IVTV_OSD_BPP_8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300447 break;
448 case 32:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300449 osd_mode = IVTV_OSD_BPP_32;
Hans Verkuil32db7752007-07-20 09:29:43 -0300450 break;
451 case 16:
452 switch (var->green.length) {
453 case 4:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300454 osd_mode = IVTV_OSD_BPP_16_444;
Hans Verkuil32db7752007-07-20 09:29:43 -0300455 break;
456 case 5:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300457 osd_mode = IVTV_OSD_BPP_16_555;
Hans Verkuil32db7752007-07-20 09:29:43 -0300458 break;
459 case 6:
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300460 osd_mode = IVTV_OSD_BPP_16_565;
Hans Verkuil32db7752007-07-20 09:29:43 -0300461 break;
462 default:
Hans Verkuil641ed492007-08-28 03:24:31 -0300463 IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300464 }
465 break;
466 default:
Hans Verkuil641ed492007-08-28 03:24:31 -0300467 IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300468 }
469
Ian Armstrong6659e3e2007-10-12 08:15:41 -0300470 /* Set video mode. Although rare, the display can become scrambled even
471 if we don't change mode. Always 'bounce' to osd_mode via mode 0 */
472 if (osd_mode != -1) {
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300473 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0);
474 ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, osd_mode);
Ian Armstrongaaf9fa22007-07-21 16:43:36 -0300475 }
476
477 oi->bits_per_pixel = var->bits_per_pixel;
478 oi->bytes_per_pixel = var->bits_per_pixel / 8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300479
480 /* Set the flicker filter */
481 switch (var->vmode & FB_VMODE_MASK) {
482 case FB_VMODE_NONINTERLACED: /* Filter on */
483 ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 1);
484 break;
485 case FB_VMODE_INTERLACED: /* Filter off */
486 ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 0);
487 break;
488 default:
Hans Verkuil641ed492007-08-28 03:24:31 -0300489 IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid video mode\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300490 }
491
492 /* Read the current osd info */
Hans Verkuil641ed492007-08-28 03:24:31 -0300493 ivtvfb_get_osd_coords(itv, &ivtv_osd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300494
495 /* Now set the OSD to the size we want */
496 ivtv_osd.pixel_stride = var->xres_virtual;
497 ivtv_osd.lines = var->yres_virtual;
498 ivtv_osd.x = 0;
499 ivtv_osd.y = 0;
Hans Verkuil641ed492007-08-28 03:24:31 -0300500 ivtvfb_set_osd_coords(itv, &ivtv_osd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300501
502 /* Can't seem to find the right API combo for this.
503 Use another function which does what we need through direct register access. */
504 ivtv_window.width = var->xres;
505 ivtv_window.height = var->yres;
506
507 /* Minimum margin cannot be 0, as X won't allow such a mode */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300508 if (!var->upper_margin) var->upper_margin++;
509 if (!var->left_margin) var->left_margin++;
Hans Verkuil32db7752007-07-20 09:29:43 -0300510 ivtv_window.top = var->upper_margin - 1;
511 ivtv_window.left = var->left_margin - 1;
512
Hans Verkuil641ed492007-08-28 03:24:31 -0300513 ivtvfb_set_display_window(itv, &ivtv_window);
Hans Verkuil32db7752007-07-20 09:29:43 -0300514
515 /* Force update of yuv registers */
516 itv->yuv_info.yuv_forced_update = 1;
517
Hans Verkuil641ed492007-08-28 03:24:31 -0300518 IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300519 var->xres, var->yres,
520 var->xres_virtual, var->yres_virtual,
521 var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300522
Hans Verkuil641ed492007-08-28 03:24:31 -0300523 IVTVFB_DEBUG_INFO("Display position: %d, %d\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300524 var->left_margin, var->upper_margin);
Hans Verkuil32db7752007-07-20 09:29:43 -0300525
Hans Verkuil641ed492007-08-28 03:24:31 -0300526 IVTVFB_DEBUG_INFO("Display filter: %s\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300527 (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
Hans Verkuil641ed492007-08-28 03:24:31 -0300528 IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
Hans Verkuil32db7752007-07-20 09:29:43 -0300529
530 return 0;
531}
532
533static int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix)
534{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300535 struct osd_info *oi = itv->osd_info;
536
Hans Verkuil641ed492007-08-28 03:24:31 -0300537 IVTVFB_DEBUG_INFO("ivtvfb_get_fix\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300538 memset(fix, 0, sizeof(struct fb_fix_screeninfo));
539 strcpy(fix->id, "cx23415 TV out");
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300540 fix->smem_start = oi->video_pbase;
541 fix->smem_len = oi->video_buffer_size;
Hans Verkuil32db7752007-07-20 09:29:43 -0300542 fix->type = FB_TYPE_PACKED_PIXELS;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300543 fix->visual = (oi->bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
Hans Verkuil32db7752007-07-20 09:29:43 -0300544 fix->xpanstep = 1;
545 fix->ypanstep = 1;
546 fix->ywrapstep = 0;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300547 fix->line_length = oi->display_byte_stride;
Hans Verkuil32db7752007-07-20 09:29:43 -0300548 fix->accel = FB_ACCEL_NONE;
549 return 0;
550}
551
552/* Check the requested display mode, returning -EINVAL if we can't
553 handle it. */
554
555static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv)
556{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300557 struct osd_info *oi = itv->osd_info;
Ian Armstrong68a341a2007-08-03 09:51:58 -0300558 int osd_height_limit;
559 u32 pixclock, hlimit, vlimit;
Hans Verkuil32db7752007-07-20 09:29:43 -0300560
Hans Verkuil641ed492007-08-28 03:24:31 -0300561 IVTVFB_DEBUG_INFO("ivtvfb_check_var\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300562
Ian Armstrong68a341a2007-08-03 09:51:58 -0300563 /* Set base references for mode calcs. */
564 if (itv->is_50hz) {
565 pixclock = 84316;
566 hlimit = 776;
567 vlimit = 591;
568 osd_height_limit = 576;
569 }
570 else {
571 pixclock = 83926;
572 hlimit = 776;
573 vlimit = 495;
574 osd_height_limit = 480;
575 }
576
Hans Verkuil32db7752007-07-20 09:29:43 -0300577 /* Check the bits per pixel */
578 if (osd_compat) {
579 if (var->bits_per_pixel != 32) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300580 IVTVFB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300581 return -EINVAL;
582 }
583 }
584
585 if (var->bits_per_pixel == 8 || var->bits_per_pixel == 32) {
586 var->transp.offset = 24;
587 var->transp.length = 8;
588 var->red.offset = 16;
589 var->red.length = 8;
590 var->green.offset = 8;
591 var->green.length = 8;
592 var->blue.offset = 0;
593 var->blue.length = 8;
594 }
595 else if (var->bits_per_pixel == 16) {
596 /* To find out the true mode, check green length */
597 switch (var->green.length) {
598 case 4:
Hans Verkuil32db7752007-07-20 09:29:43 -0300599 var->red.offset = 8;
600 var->red.length = 4;
601 var->green.offset = 4;
602 var->green.length = 4;
603 var->blue.offset = 0;
604 var->blue.length = 4;
Hans Verkuil459a52f2007-08-22 08:58:47 -0300605 var->transp.offset = 12;
606 var->transp.length = 1;
Hans Verkuil32db7752007-07-20 09:29:43 -0300607 break;
608 case 5:
Hans Verkuil32db7752007-07-20 09:29:43 -0300609 var->red.offset = 10;
610 var->red.length = 5;
611 var->green.offset = 5;
612 var->green.length = 5;
613 var->blue.offset = 0;
614 var->blue.length = 5;
Hans Verkuil459a52f2007-08-22 08:58:47 -0300615 var->transp.offset = 15;
616 var->transp.length = 1;
Hans Verkuil32db7752007-07-20 09:29:43 -0300617 break;
618 default:
Hans Verkuil32db7752007-07-20 09:29:43 -0300619 var->red.offset = 11;
620 var->red.length = 5;
621 var->green.offset = 5;
622 var->green.length = 6;
623 var->blue.offset = 0;
624 var->blue.length = 5;
Hans Verkuil459a52f2007-08-22 08:58:47 -0300625 var->transp.offset = 0;
626 var->transp.length = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300627 break;
628 }
629 }
630 else {
Hans Verkuil641ed492007-08-28 03:24:31 -0300631 IVTVFB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel);
Hans Verkuil32db7752007-07-20 09:29:43 -0300632 return -EINVAL;
633 }
634
635 /* Check the resolution */
636 if (osd_compat) {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300637 if (var->xres != oi->ivtvfb_defined.xres ||
638 var->yres != oi->ivtvfb_defined.yres ||
639 var->xres_virtual != oi->ivtvfb_defined.xres_virtual ||
640 var->yres_virtual != oi->ivtvfb_defined.yres_virtual) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300641 IVTVFB_DEBUG_WARN("Invalid resolution: %dx%d (virtual %dx%d)\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300642 var->xres, var->yres, var->xres_virtual, var->yres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300643 return -EINVAL;
644 }
645 }
646 else {
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300647 if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300648 IVTVFB_DEBUG_WARN("Invalid resolution: %dx%d\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300649 var->xres, var->yres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300650 return -EINVAL;
651 }
652
653 /* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */
654 if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) ||
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300655 var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8) > oi->video_buffer_size ||
Hans Verkuil32db7752007-07-20 09:29:43 -0300656 var->xres_virtual < var->xres ||
657 var->yres_virtual < var->yres) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300658 IVTVFB_DEBUG_WARN("Invalid virtual resolution: %dx%d\n",
Hans Verkuil32db7752007-07-20 09:29:43 -0300659 var->xres_virtual, var->yres_virtual);
660 return -EINVAL;
661 }
662 }
663
664 /* Some extra checks if in 8 bit mode */
665 if (var->bits_per_pixel == 8) {
666 /* Width must be a multiple of 4 */
667 if (var->xres & 3) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300668 IVTVFB_DEBUG_WARN("Invalid resolution for 8bpp: %d\n", var->xres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300669 return -EINVAL;
670 }
671 if (var->xres_virtual & 3) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300672 IVTVFB_DEBUG_WARN("Invalid virtual resolution for 8bpp: %d)\n", var->xres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300673 return -EINVAL;
674 }
675 }
676 else if (var->bits_per_pixel == 16) {
677 /* Width must be a multiple of 2 */
678 if (var->xres & 1) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300679 IVTVFB_DEBUG_WARN("Invalid resolution for 16bpp: %d\n", var->xres);
Hans Verkuil32db7752007-07-20 09:29:43 -0300680 return -EINVAL;
681 }
682 if (var->xres_virtual & 1) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300683 IVTVFB_DEBUG_WARN("Invalid virtual resolution for 16bpp: %d)\n", var->xres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300684 return -EINVAL;
685 }
686 }
687
688 /* Now check the offsets */
689 if (var->xoffset >= var->xres_virtual || var->yoffset >= var->yres_virtual) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300690 IVTVFB_DEBUG_WARN("Invalid offset: %d (%d) %d (%d)\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300691 var->xoffset, var->xres_virtual, var->yoffset, var->yres_virtual);
Hans Verkuil32db7752007-07-20 09:29:43 -0300692 return -EINVAL;
693 }
694
695 /* Check pixel format */
696 if (var->nonstd > 1) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300697 IVTVFB_DEBUG_WARN("Invalid nonstd % d\n", var->nonstd);
Hans Verkuil32db7752007-07-20 09:29:43 -0300698 return -EINVAL;
699 }
700
701 /* Check video mode */
702 if (((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) &&
703 ((var->vmode & FB_VMODE_MASK) != FB_VMODE_INTERLACED)) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300704 IVTVFB_DEBUG_WARN("Invalid video mode: %d\n", var->vmode & FB_VMODE_MASK);
Hans Verkuil32db7752007-07-20 09:29:43 -0300705 return -EINVAL;
706 }
707
708 /* Check the left & upper margins
709 If the margins are too large, just center the screen
710 (enforcing margins causes too many problems) */
711
712 if (var->left_margin + var->xres > IVTV_OSD_MAX_WIDTH + 1) {
713 var->left_margin = 1 + ((IVTV_OSD_MAX_WIDTH - var->xres) / 2);
714 }
715 if (var->upper_margin + var->yres > (itv->is_50hz ? 577 : 481)) {
716 var->upper_margin = 1 + (((itv->is_50hz ? 576 : 480) - var->yres) / 2);
717 }
718
719 /* Maintain overall 'size' for a constant refresh rate */
Ian Armstrong68a341a2007-08-03 09:51:58 -0300720 var->right_margin = hlimit - var->left_margin - var->xres;
721 var->lower_margin = vlimit - var->upper_margin - var->yres;
Hans Verkuil32db7752007-07-20 09:29:43 -0300722
723 /* Fixed sync times */
724 var->hsync_len = 24;
725 var->vsync_len = 2;
726
727 /* Non-interlaced / interlaced mode is used to switch the OSD filter
728 on or off. Adjust the clock timings to maintain a constant
729 vertical refresh rate. */
Hans Verkuil32db7752007-07-20 09:29:43 -0300730 if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED)
Ian Armstrong68a341a2007-08-03 09:51:58 -0300731 var->pixclock = pixclock / 2;
732 else
733 var->pixclock = pixclock;
Hans Verkuil32db7752007-07-20 09:29:43 -0300734
Hans Verkuil641ed492007-08-28 03:24:31 -0300735 IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300736 var->xres, var->yres,
737 var->xres_virtual, var->yres_virtual,
Hans Verkuil32db7752007-07-20 09:29:43 -0300738 var->bits_per_pixel);
739
Hans Verkuil641ed492007-08-28 03:24:31 -0300740 IVTVFB_DEBUG_INFO("Display position: %d, %d\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300741 var->left_margin, var->upper_margin);
Hans Verkuil32db7752007-07-20 09:29:43 -0300742
Hans Verkuil641ed492007-08-28 03:24:31 -0300743 IVTVFB_DEBUG_INFO("Display filter: %s\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300744 (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
Hans Verkuil641ed492007-08-28 03:24:31 -0300745 IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
Hans Verkuil32db7752007-07-20 09:29:43 -0300746 return 0;
747}
748
749static int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
750{
751 struct ivtv *itv = (struct ivtv *) info->par;
Hans Verkuil641ed492007-08-28 03:24:31 -0300752 IVTVFB_DEBUG_INFO("ivtvfb_check_var\n");
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300753 return _ivtvfb_check_var(var, itv);
Hans Verkuil32db7752007-07-20 09:29:43 -0300754}
755
756static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
757{
758 u32 osd_pan_index;
759 struct ivtv *itv = (struct ivtv *) info->par;
760
761 osd_pan_index = (var->xoffset + (var->yoffset * var->xres_virtual))*var->bits_per_pixel/8;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300762 write_reg(osd_pan_index, 0x02A0C);
Hans Verkuil32db7752007-07-20 09:29:43 -0300763
764 /* Pass this info back the yuv handler */
765 itv->yuv_info.osd_x_pan = var->xoffset;
766 itv->yuv_info.osd_y_pan = var->yoffset;
767 /* Force update of yuv registers */
768 itv->yuv_info.yuv_forced_update = 1;
769 return 0;
770}
771
772static int ivtvfb_set_par(struct fb_info *info)
773{
774 int rc = 0;
775 struct ivtv *itv = (struct ivtv *) info->par;
776
Hans Verkuil641ed492007-08-28 03:24:31 -0300777 IVTVFB_DEBUG_INFO("ivtvfb_set_par\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300778
779 rc = ivtvfb_set_var(itv, &info->var);
780 ivtvfb_pan_display(&info->var, info);
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300781 ivtvfb_get_fix(itv, &info->fix);
Hans Verkuil32db7752007-07-20 09:29:43 -0300782 return rc;
783}
784
785static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green,
786 unsigned blue, unsigned transp,
787 struct fb_info *info)
788{
789 u32 color, *palette;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300790 struct ivtv *itv = (struct ivtv *)info->par;
Hans Verkuil32db7752007-07-20 09:29:43 -0300791
792 if (regno >= info->cmap.len)
793 return -EINVAL;
794
795 color = ((transp & 0xFF00) << 16) |((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8);
796 if (info->var.bits_per_pixel <= 8) {
797 write_reg(regno, 0x02a30);
798 write_reg(color, 0x02a34);
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300799 return 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300800 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300801 if (regno >= 16)
802 return -EINVAL;
Hans Verkuil32db7752007-07-20 09:29:43 -0300803
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300804 palette = info->pseudo_palette;
805 if (info->var.bits_per_pixel == 16) {
806 switch (info->var.green.length) {
807 case 4:
808 color = ((red & 0xf000) >> 4) |
809 ((green & 0xf000) >> 8) |
810 ((blue & 0xf000) >> 12);
811 break;
812 case 5:
813 color = ((red & 0xf800) >> 1) |
814 ((green & 0xf800) >> 6) |
815 ((blue & 0xf800) >> 11);
816 break;
817 case 6:
818 color = (red & 0xf800 ) |
819 ((green & 0xfc00) >> 5) |
820 ((blue & 0xf800) >> 11);
821 break;
Hans Verkuil32db7752007-07-20 09:29:43 -0300822 }
Hans Verkuil32db7752007-07-20 09:29:43 -0300823 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300824 palette[regno] = color;
Hans Verkuil32db7752007-07-20 09:29:43 -0300825 return 0;
826}
827
828/* We don't really support blanking. All this does is enable or
829 disable the OSD. */
830static int ivtvfb_blank(int blank_mode, struct fb_info *info)
831{
832 struct ivtv *itv = (struct ivtv *)info->par;
833
Hans Verkuil641ed492007-08-28 03:24:31 -0300834 IVTVFB_DEBUG_INFO("Set blanking mode : %d\n", blank_mode);
Hans Verkuil32db7752007-07-20 09:29:43 -0300835 switch (blank_mode) {
836 case FB_BLANK_UNBLANK:
837 ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1);
838 break;
839 case FB_BLANK_NORMAL:
840 case FB_BLANK_HSYNC_SUSPEND:
841 case FB_BLANK_VSYNC_SUSPEND:
842 case FB_BLANK_POWERDOWN:
843 ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
844 break;
845 }
846 return 0;
847}
848
849static struct fb_ops ivtvfb_ops = {
850 .owner = THIS_MODULE,
851 .fb_check_var = ivtvfb_check_var,
852 .fb_set_par = ivtvfb_set_par,
853 .fb_setcolreg = ivtvfb_setcolreg,
854 .fb_fillrect = cfb_fillrect,
855 .fb_copyarea = cfb_copyarea,
856 .fb_imageblit = cfb_imageblit,
857 .fb_cursor = NULL,
858 .fb_ioctl = ivtvfb_ioctl,
859 .fb_pan_display = ivtvfb_pan_display,
860 .fb_blank = ivtvfb_blank,
861};
862
863/* Initialization */
864
865
866/* Setup our initial video mode */
867static int ivtvfb_init_vidmode(struct ivtv *itv)
868{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300869 struct osd_info *oi = itv->osd_info;
Hans Verkuil32db7752007-07-20 09:29:43 -0300870 struct v4l2_rect start_window;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300871 int max_height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300872
Hans Verkuil32db7752007-07-20 09:29:43 -0300873 /* Color mode */
874
875 if (osd_compat) osd_depth = 32;
876 if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32) osd_depth = 8;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300877 oi->bits_per_pixel = osd_depth;
878 oi->bytes_per_pixel = oi->bits_per_pixel / 8;
Hans Verkuil32db7752007-07-20 09:29:43 -0300879
880 /* Horizontal size & position */
881
882 if (osd_xres > 720) osd_xres = 720;
883
884 /* Must be a multiple of 4 for 8bpp & 2 for 16bpp */
885 if (osd_depth == 8)
886 osd_xres &= ~3;
887 else if (osd_depth == 16)
888 osd_xres &= ~1;
889
890 if (osd_xres)
891 start_window.width = osd_xres;
892 else
893 start_window.width = osd_compat ? 720: 640;
894
895 /* Check horizontal start (osd_left). */
896 if (osd_left && osd_left + start_window.width > 721) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300897 IVTVFB_ERR("Invalid osd_left - assuming default\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300898 osd_left = 0;
899 }
900
901 /* Hardware coords start at 0, user coords start at 1. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300902 osd_left--;
Hans Verkuil32db7752007-07-20 09:29:43 -0300903
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300904 start_window.left = osd_left >= 0 ? osd_left : ((IVTV_OSD_MAX_WIDTH - start_window.width) / 2);
Hans Verkuil32db7752007-07-20 09:29:43 -0300905
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300906 oi->display_byte_stride =
907 start_window.width * oi->bytes_per_pixel;
Hans Verkuil32db7752007-07-20 09:29:43 -0300908
909 /* Vertical size & position */
910
911 max_height = itv->is_50hz ? 576 : 480;
912
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300913 if (osd_yres > max_height)
914 osd_yres = max_height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300915
916 if (osd_yres)
917 start_window.height = osd_yres;
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300918 else
919 start_window.height = osd_compat ? max_height : (itv->is_50hz ? 480 : 400);
Hans Verkuil32db7752007-07-20 09:29:43 -0300920
921 /* Check vertical start (osd_upper). */
922 if (osd_upper + start_window.height > max_height + 1) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300923 IVTVFB_ERR("Invalid osd_upper - assuming default\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300924 osd_upper = 0;
925 }
926
927 /* Hardware coords start at 0, user coords start at 1. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300928 osd_upper--;
Hans Verkuil32db7752007-07-20 09:29:43 -0300929
930 start_window.top = osd_upper >= 0 ? osd_upper : ((max_height - start_window.height) / 2);
931
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300932 oi->display_width = start_window.width;
933 oi->display_height = start_window.height;
Hans Verkuil32db7752007-07-20 09:29:43 -0300934
935 /* Generate a valid fb_var_screeninfo */
936
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300937 oi->ivtvfb_defined.xres = oi->display_width;
938 oi->ivtvfb_defined.yres = oi->display_height;
939 oi->ivtvfb_defined.xres_virtual = oi->display_width;
940 oi->ivtvfb_defined.yres_virtual = oi->display_height;
941 oi->ivtvfb_defined.bits_per_pixel = oi->bits_per_pixel;
942 oi->ivtvfb_defined.vmode = (osd_laced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED);
943 oi->ivtvfb_defined.left_margin = start_window.left + 1;
944 oi->ivtvfb_defined.upper_margin = start_window.top + 1;
945 oi->ivtvfb_defined.accel_flags = FB_ACCEL_NONE;
946 oi->ivtvfb_defined.nonstd = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -0300947
948 /* We've filled in the most data, let the usual mode check
949 routine fill in the rest. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300950 _ivtvfb_check_var(&oi->ivtvfb_defined, itv);
Hans Verkuil32db7752007-07-20 09:29:43 -0300951
952 /* Generate valid fb_fix_screeninfo */
953
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300954 ivtvfb_get_fix(itv, &oi->ivtvfb_fix);
Hans Verkuil32db7752007-07-20 09:29:43 -0300955
956 /* Generate valid fb_info */
957
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300958 oi->ivtvfb_info.node = -1;
959 oi->ivtvfb_info.flags = FBINFO_FLAG_DEFAULT;
960 oi->ivtvfb_info.fbops = &ivtvfb_ops;
961 oi->ivtvfb_info.par = itv;
962 oi->ivtvfb_info.var = oi->ivtvfb_defined;
963 oi->ivtvfb_info.fix = oi->ivtvfb_fix;
964 oi->ivtvfb_info.screen_base = (u8 __iomem *)oi->video_vbase;
965 oi->ivtvfb_info.fbops = &ivtvfb_ops;
Hans Verkuil32db7752007-07-20 09:29:43 -0300966
967 /* Supply some monitor specs. Bogus values will do for now */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300968 oi->ivtvfb_info.monspecs.hfmin = 8000;
969 oi->ivtvfb_info.monspecs.hfmax = 70000;
970 oi->ivtvfb_info.monspecs.vfmin = 10;
971 oi->ivtvfb_info.monspecs.vfmax = 100;
Hans Verkuil32db7752007-07-20 09:29:43 -0300972
973 /* Allocate color map */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300974 if (fb_alloc_cmap(&oi->ivtvfb_info.cmap, 256, 1)) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300975 IVTVFB_ERR("abort, unable to alloc cmap\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300976 return -ENOMEM;
977 }
978
979 /* Allocate the pseudo palette */
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300980 oi->ivtvfb_info.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
Hans Verkuil32db7752007-07-20 09:29:43 -0300981
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300982 if (!oi->ivtvfb_info.pseudo_palette) {
Hans Verkuil641ed492007-08-28 03:24:31 -0300983 IVTVFB_ERR("abort, unable to alloc pseudo pallete\n");
Hans Verkuil32db7752007-07-20 09:29:43 -0300984 return -ENOMEM;
985 }
986
987 return 0;
988}
989
990/* Find OSD buffer base & size. Add to mtrr. Zero osd buffer. */
991
992static int ivtvfb_init_io(struct ivtv *itv)
993{
Hans Verkuilbe383bd2007-07-20 10:16:03 -0300994 struct osd_info *oi = itv->osd_info;
995
Hans Verkuil26e9d592007-08-25 05:41:52 -0300996 mutex_lock(&itv->serialize_lock);
Hans Verkuil6e5eb592007-07-25 12:55:52 -0300997 if (ivtv_init_on_first_open(itv)) {
Hans Verkuil26e9d592007-08-25 05:41:52 -0300998 mutex_unlock(&itv->serialize_lock);
Hans Verkuil641ed492007-08-28 03:24:31 -0300999 IVTVFB_ERR("Failed to initialize ivtv\n");
Hans Verkuil6e5eb592007-07-25 12:55:52 -03001000 return -ENXIO;
1001 }
Hans Verkuil26e9d592007-08-25 05:41:52 -03001002 mutex_unlock(&itv->serialize_lock);
Hans Verkuil6e5eb592007-07-25 12:55:52 -03001003
Hans Verkuil641ed492007-08-28 03:24:31 -03001004 ivtvfb_get_framebuffer(itv, &oi->video_rbase, &oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -03001005
1006 /* The osd buffer size depends on the number of video buffers allocated
1007 on the PVR350 itself. For now we'll hardcode the smallest osd buffer
1008 size to prevent any overlap. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001009 oi->video_buffer_size = 1704960;
Hans Verkuil32db7752007-07-20 09:29:43 -03001010
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001011 oi->video_pbase = itv->base_addr + IVTV_DECODER_OFFSET + oi->video_rbase;
1012 oi->video_vbase = itv->dec_mem + oi->video_rbase;
Hans Verkuil32db7752007-07-20 09:29:43 -03001013
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001014 if (!oi->video_vbase) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001015 IVTVFB_ERR("abort, video memory 0x%x @ 0x%lx isn't mapped!\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001016 oi->video_buffer_size, oi->video_pbase);
Hans Verkuil32db7752007-07-20 09:29:43 -03001017 return -EIO;
1018 }
1019
Hans Verkuil641ed492007-08-28 03:24:31 -03001020 IVTVFB_INFO("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n",
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001021 oi->video_pbase, oi->video_vbase,
1022 oi->video_buffer_size / 1024);
Hans Verkuil32db7752007-07-20 09:29:43 -03001023
1024#ifdef CONFIG_MTRR
1025 {
1026 /* Find the largest power of two that maps the whole buffer */
1027 int size_shift = 31;
1028
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001029 while (!(oi->video_buffer_size & (1 << size_shift))) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001030 size_shift--;
1031 }
1032 size_shift++;
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001033 oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1);
1034 oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size;
1035 oi->fb_end_aligned_physaddr += (1 << size_shift) - 1;
1036 oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1);
1037 if (mtrr_add(oi->fb_start_aligned_physaddr,
1038 oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr,
Hans Verkuil32db7752007-07-20 09:29:43 -03001039 MTRR_TYPE_WRCOMB, 1) < 0) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001040 IVTVFB_INFO("disabled mttr\n");
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001041 oi->fb_start_aligned_physaddr = 0;
1042 oi->fb_end_aligned_physaddr = 0;
Hans Verkuil32db7752007-07-20 09:29:43 -03001043 }
1044 }
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001045#endif
Hans Verkuil32db7752007-07-20 09:29:43 -03001046
1047 /* Blank the entire osd. */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001048 memset_io(oi->video_vbase, 0, oi->video_buffer_size);
Hans Verkuil32db7752007-07-20 09:29:43 -03001049
1050 return 0;
1051}
1052
1053/* Release any memory we've grabbed & remove mtrr entry */
1054static void ivtvfb_release_buffers (struct ivtv *itv)
1055{
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001056 struct osd_info *oi = itv->osd_info;
1057
Hans Verkuil32db7752007-07-20 09:29:43 -03001058 /* Release cmap */
Adrian Bunk13628032007-08-28 03:28:04 -03001059 if (oi->ivtvfb_info.cmap.len)
1060 fb_dealloc_cmap(&oi->ivtvfb_info.cmap);
Hans Verkuil32db7752007-07-20 09:29:43 -03001061
1062 /* Release pseudo palette */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001063 if (oi->ivtvfb_info.pseudo_palette)
1064 kfree(oi->ivtvfb_info.pseudo_palette);
Hans Verkuil32db7752007-07-20 09:29:43 -03001065
1066#ifdef CONFIG_MTRR
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001067 if (oi->fb_end_aligned_physaddr) {
1068 mtrr_del(-1, oi->fb_start_aligned_physaddr,
1069 oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr);
1070 }
1071#endif
Hans Verkuil32db7752007-07-20 09:29:43 -03001072
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001073 kfree(oi);
Hans Verkuil32db7752007-07-20 09:29:43 -03001074 itv->osd_info = NULL;
1075}
1076
1077/* Initialize the specified card */
1078
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001079static int ivtvfb_init_card(struct ivtv *itv)
Hans Verkuil32db7752007-07-20 09:29:43 -03001080{
1081 int rc;
1082
1083 if (itv->osd_info) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001084 IVTVFB_ERR("Card %d already initialised\n", ivtvfb_card_id);
Hans Verkuil32db7752007-07-20 09:29:43 -03001085 return -EBUSY;
1086 }
1087
1088 itv->osd_info = kzalloc(sizeof(struct osd_info), GFP_ATOMIC);
1089 if (itv->osd_info == 0) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001090 IVTVFB_ERR("Failed to allocate memory for osd_info\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001091 return -ENOMEM;
1092 }
1093
1094 /* Find & setup the OSD buffer */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001095 if ((rc = ivtvfb_init_io(itv)))
Hans Verkuil32db7752007-07-20 09:29:43 -03001096 return rc;
1097
1098 /* Set the startup video mode information */
Hans Verkuilbe383bd2007-07-20 10:16:03 -03001099 if ((rc = ivtvfb_init_vidmode(itv))) {
Hans Verkuil32db7752007-07-20 09:29:43 -03001100 ivtvfb_release_buffers(itv);
1101 return rc;
1102 }
1103
1104 /* Register the framebuffer */
1105 if (register_framebuffer(&itv->osd_info->ivtvfb_info) < 0) {
1106 ivtvfb_release_buffers(itv);
1107 return -EINVAL;
1108 }
1109
1110 itv->osd_video_pbase = itv->osd_info->video_pbase;
1111
1112 /* Set the card to the requested mode */
1113 ivtvfb_set_par(&itv->osd_info->ivtvfb_info);
1114
1115 /* Set color 0 to black */
1116 write_reg(0, 0x02a30);
1117 write_reg(0, 0x02a34);
1118
1119 /* Enable the osd */
1120 ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info);
1121
1122 /* Note if we're running in compatibility mode */
1123 if (osd_compat)
Hans Verkuil641ed492007-08-28 03:24:31 -03001124 IVTVFB_INFO("Running in compatibility mode. Display resize & mode change disabled\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001125
1126 /* Allocate DMA */
1127 ivtv_udma_alloc(itv);
1128 return 0;
1129
1130}
1131
1132static int __init ivtvfb_init(void)
1133{
1134 struct ivtv *itv;
1135 int i, registered = 0;
1136
Hans Verkuil641ed492007-08-28 03:24:31 -03001137 if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) {
1138 printk(KERN_ERR "ivtvfb: ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n",
Hans Verkuil32db7752007-07-20 09:29:43 -03001139 IVTV_MAX_CARDS - 1);
1140 return -EINVAL;
1141 }
1142
1143 /* Locate & initialise all cards supporting an OSD. */
1144 for (i = 0; i < ivtv_cards_active; i++) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001145 if (ivtvfb_card_id != -1 && i != ivtvfb_card_id)
Hans Verkuil32db7752007-07-20 09:29:43 -03001146 continue;
1147 itv = ivtv_cards[i];
1148 if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
1149 if (ivtvfb_init_card(itv) == 0) {
Hans Verkuil641ed492007-08-28 03:24:31 -03001150 IVTVFB_INFO("Framebuffer registered on ivtv card id %d\n", i);
Hans Verkuil32db7752007-07-20 09:29:43 -03001151 registered++;
1152 }
1153 }
1154 }
1155 if (!registered) {
Hans Verkuil7b3a0d42007-08-26 06:11:07 -03001156 printk(KERN_ERR "ivtvfb: no cards found");
Hans Verkuil32db7752007-07-20 09:29:43 -03001157 return -ENODEV;
1158 }
1159 return 0;
1160}
1161
1162static void ivtvfb_cleanup(void)
1163{
1164 struct ivtv *itv;
1165 int i;
1166
Hans Verkuil7b3a0d42007-08-26 06:11:07 -03001167 printk(KERN_INFO "ivtvfb: Unloading framebuffer module\n");
Hans Verkuil32db7752007-07-20 09:29:43 -03001168
1169 for (i = 0; i < ivtv_cards_active; i++) {
1170 itv = ivtv_cards[i];
1171 if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) && itv->osd_info) {
Hans Verkuild343d7f2007-10-14 11:17:14 -03001172 if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) {
1173 IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n", i);
1174 return;
1175 }
Hans Verkuil641ed492007-08-28 03:24:31 -03001176 IVTVFB_DEBUG_INFO("Unregister framebuffer %d\n", i);
Hans Verkuil32db7752007-07-20 09:29:43 -03001177 ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info);
Hans Verkuil32db7752007-07-20 09:29:43 -03001178 ivtvfb_release_buffers(itv);
1179 itv->osd_video_pbase = 0;
1180 }
1181 }
1182}
1183
1184module_init(ivtvfb_init);
1185module_exit(ivtvfb_cleanup);