]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/media/video/ivtv/ivtvfb.c
V4L/DVB (8479): tveeprom/ivtv: fix usage of has_ir field
[linux-2.6-omap-h63xx.git] / drivers / media / video / ivtv / ivtvfb.c
index 9684048fe56c8b5e69a5015fe2c342e979a0e390..bdfda48e56bf55e0b5ea3af83404348053fd26b8 100644 (file)
@@ -55,7 +55,6 @@
 static int ivtvfb_card_id = -1;
 static int ivtvfb_debug = 0;
 static int osd_laced;
-static int osd_compat;
 static int osd_depth;
 static int osd_upper;
 static int osd_left;
@@ -65,7 +64,6 @@ static int osd_xres;
 module_param(ivtvfb_card_id, int, 0444);
 module_param_named(debug,ivtvfb_debug, int, 0644);
 module_param(osd_laced, bool, 0444);
-module_param(osd_compat, bool, 0444);
 module_param(osd_depth, int, 0444);
 module_param(osd_upper, int, 0444);
 module_param(osd_left, int, 0444);
@@ -80,12 +78,6 @@ MODULE_PARM_DESC(debug,
                 "Debug level (bitmask). Default: errors only\n"
                 "\t\t\t(debug = 3 gives full debugging)");
 
-MODULE_PARM_DESC(osd_compat,
-                "Compatibility mode - Display size is locked (use for old X drivers)\n"
-                "\t\t\t0=off\n"
-                "\t\t\t1=on\n"
-                "\t\t\tdefault off");
-
 /* Why upper, left, xres, yres, depth, laced ? To match terminology used
    by fbset.
    Why start at 1 for left & upper coordinate ? Because X doesn't allow 0 */
@@ -166,9 +158,6 @@ struct osd_info {
        unsigned long fb_end_aligned_physaddr;
 #endif
 
-       /* Current osd mode */
-       int osd_mode;
-
        /* Store the buffer offset */
        int set_osd_coords_x;
        int set_osd_coords_y;
@@ -378,6 +367,88 @@ static int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source,
        return ivtvfb_prep_dec_dma_to_device(itv, dest_offset, source, count);
 }
 
+static ssize_t ivtvfb_write(struct fb_info *info, const char __user *buf,
+                    size_t count, loff_t *ppos)
+{
+       unsigned long p = *ppos;
+       void *dst;
+       int err = 0;
+       unsigned long total_size;
+       struct ivtv *itv = (struct ivtv *) info->par;
+       unsigned long dma_offset =
+                       IVTV_DECODER_OFFSET + itv->osd_info->video_rbase;
+       unsigned long dma_size;
+       u16 lead = 0, tail = 0;
+
+       if (info->state != FBINFO_STATE_RUNNING)
+               return -EPERM;
+
+       total_size = info->screen_size;
+
+       if (total_size == 0)
+               total_size = info->fix.smem_len;
+
+       if (p > total_size)
+               return -EFBIG;
+
+       if (count > total_size) {
+               err = -EFBIG;
+               count = total_size;
+       }
+
+       if (count + p > total_size) {
+               if (!err)
+                       err = -ENOSPC;
+
+               count = total_size - p;
+       }
+
+       dst = (void __force *) (info->screen_base + p);
+
+       if (info->fbops->fb_sync)
+               info->fbops->fb_sync(info);
+
+       if (!access_ok(VERIFY_READ, buf, count)) {
+               IVTVFB_WARN("Invalid userspace pointer 0x%08lx\n",
+                       (unsigned long)buf);
+               err = -EFAULT;
+       }
+
+       if (!err) {
+               /* If transfer size > threshold and both src/dst
+               addresses are aligned, use DMA */
+               if (count >= 4096 &&
+                   ((unsigned long)buf & 3) == ((unsigned long)dst & 3)) {
+                       /* Odd address = can't DMA. Align */
+                       if ((unsigned long)dst & 3) {
+                               lead = 4 - ((unsigned long)dst & 3);
+                               memcpy(dst, buf, lead);
+                               buf += lead;
+                               dst += lead;
+                       }
+                       /* DMA resolution is 32 bits */
+                       if ((count - lead) & 3)
+                               tail = (count - lead) & 3;
+                       /* DMA the data */
+                       dma_size = count - lead - tail;
+                       err = ivtvfb_prep_dec_dma_to_device(itv,
+                              p + lead + dma_offset, (void *)buf, dma_size);
+                       dst += dma_size;
+                       buf += dma_size;
+                       /* Copy any leftover data */
+                       if (tail)
+                               memcpy(dst, buf, tail);
+               } else {
+                       memcpy(dst, buf, count);
+               }
+       }
+
+       if  (!err)
+               *ppos += count;
+
+       return (err) ? err : count;
+}
+
 static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
 {
        DEFINE_WAIT(wait);
@@ -470,13 +541,11 @@ static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var)
                        IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
        }
 
-       /* Change osd mode if needed.
-          Although rare, things can go wrong. The extra mode
-          change seems to help... */
-       if (osd_mode != -1 && osd_mode != oi->osd_mode) {
+       /* Set video mode. Although rare, the display can become scrambled even
+          if we don't change mode. Always 'bounce' to osd_mode via mode 0 */
+       if (osd_mode != -1) {
                ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0);
                ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, osd_mode);
-               oi->osd_mode = osd_mode;
        }
 
        oi->bits_per_pixel = var->bits_per_pixel;
@@ -517,6 +586,10 @@ static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var)
 
        ivtvfb_set_display_window(itv, &ivtv_window);
 
+       /* Pass screen size back to yuv handler */
+       itv->yuv_info.osd_full_w = ivtv_osd.pixel_stride;
+       itv->yuv_info.osd_full_h = ivtv_osd.lines;
+
        /* Force update of yuv registers */
        itv->yuv_info.yuv_forced_update = 1;
 
@@ -541,7 +614,7 @@ static int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix)
 
        IVTVFB_DEBUG_INFO("ivtvfb_get_fix\n");
        memset(fix, 0, sizeof(struct fb_fix_screeninfo));
-       strcpy(fix->id, "cx23415 TV out");
+       strlcpy(fix->id, "cx23415 TV out", sizeof(fix->id));
        fix->smem_start = oi->video_pbase;
        fix->smem_len = oi->video_buffer_size;
        fix->type = FB_TYPE_PACKED_PIXELS;
@@ -579,14 +652,6 @@ static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv)
                osd_height_limit = 480;
        }
 
-       /* Check the bits per pixel */
-       if (osd_compat) {
-               if (var->bits_per_pixel != 32) {
-                       IVTVFB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel);
-                       return -EINVAL;
-               }
-       }
-
        if (var->bits_per_pixel == 8 || var->bits_per_pixel == 32) {
                var->transp.offset = 24;
                var->transp.length = 8;
@@ -638,32 +703,20 @@ static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv)
        }
 
        /* Check the resolution */
-       if (osd_compat) {
-               if (var->xres != oi->ivtvfb_defined.xres ||
-                   var->yres != oi->ivtvfb_defined.yres ||
-                   var->xres_virtual != oi->ivtvfb_defined.xres_virtual ||
-                   var->yres_virtual != oi->ivtvfb_defined.yres_virtual) {
-                       IVTVFB_DEBUG_WARN("Invalid resolution: %dx%d (virtual %dx%d)\n",
-                               var->xres, var->yres, var->xres_virtual, var->yres_virtual);
-                       return -EINVAL;
-               }
+       if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit) {
+               IVTVFB_DEBUG_WARN("Invalid resolution: %dx%d\n",
+                               var->xres, var->yres);
+               return -EINVAL;
        }
-       else {
-               if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit) {
-                       IVTVFB_DEBUG_WARN("Invalid resolution: %dx%d\n",
-                                       var->xres, var->yres);
-                       return -EINVAL;
-               }
 
-               /* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */
-               if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) ||
-                   var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8) > oi->video_buffer_size ||
-                   var->xres_virtual < var->xres ||
-                   var->yres_virtual < var->yres) {
-                       IVTVFB_DEBUG_WARN("Invalid virtual resolution: %dx%d\n",
-                               var->xres_virtual, var->yres_virtual);
-                       return -EINVAL;
-               }
+       /* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */
+       if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) ||
+           var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8) > oi->video_buffer_size ||
+           var->xres_virtual < var->xres ||
+           var->yres_virtual < var->yres) {
+               IVTVFB_DEBUG_WARN("Invalid virtual resolution: %dx%d\n",
+                       var->xres_virtual, var->yres_virtual);
+               return -EINVAL;
        }
 
        /* Some extra checks if in 8 bit mode */
@@ -737,6 +790,9 @@ static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv)
        else
                var->pixclock = pixclock;
 
+       itv->osd_rect.width = var->xres;
+       itv->osd_rect.height = var->yres;
+
        IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
                      var->xres, var->yres,
                      var->xres_virtual, var->yres_virtual,
@@ -853,6 +909,7 @@ static int ivtvfb_blank(int blank_mode, struct fb_info *info)
 
 static struct fb_ops ivtvfb_ops = {
        .owner = THIS_MODULE,
+       .fb_write       = ivtvfb_write,
        .fb_check_var   = ivtvfb_check_var,
        .fb_set_par     = ivtvfb_set_par,
        .fb_setcolreg   = ivtvfb_setcolreg,
@@ -877,17 +934,15 @@ static int ivtvfb_init_vidmode(struct ivtv *itv)
 
        /* Color mode */
 
-       if (osd_compat) osd_depth = 32;
-       if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32) osd_depth = 8;
+       if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32)
+               osd_depth = 8;
        oi->bits_per_pixel = osd_depth;
        oi->bytes_per_pixel = oi->bits_per_pixel / 8;
 
-       /* Invalidate current osd mode to force a mode switch later */
-       oi->osd_mode = -1;
-
        /* Horizontal size & position */
 
-       if (osd_xres > 720) osd_xres = 720;
+       if (osd_xres > 720)
+               osd_xres = 720;
 
        /* Must be a multiple of 4 for 8bpp & 2 for 16bpp */
        if (osd_depth == 8)
@@ -895,10 +950,7 @@ static int ivtvfb_init_vidmode(struct ivtv *itv)
        else if (osd_depth == 16)
                osd_xres &= ~1;
 
-       if (osd_xres)
-               start_window.width = osd_xres;
-       else
-               start_window.width = osd_compat ? 720: 640;
+       start_window.width = osd_xres ? osd_xres : 640;
 
        /* Check horizontal start (osd_left). */
        if (osd_left && osd_left + start_window.width > 721) {
@@ -921,10 +973,7 @@ static int ivtvfb_init_vidmode(struct ivtv *itv)
        if (osd_yres > max_height)
                osd_yres = max_height;
 
-       if (osd_yres)
-               start_window.height = osd_yres;
-       else
-               start_window.height = osd_compat ? max_height : (itv->is_50hz ? 480 : 400);
+       start_window.height = osd_yres ? osd_yres : itv->is_50hz ? 480 : 400;
 
        /* Check vertical start (osd_upper). */
        if (osd_upper + start_window.height > max_height + 1) {
@@ -985,7 +1034,8 @@ static int ivtvfb_init_vidmode(struct ivtv *itv)
        }
 
        /* Allocate the pseudo palette */
-       oi->ivtvfb_info.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
+       oi->ivtvfb_info.pseudo_palette =
+               kmalloc(sizeof(u32) * 16, GFP_KERNEL|__GFP_NOWARN);
 
        if (!oi->ivtvfb_info.pseudo_palette) {
                IVTVFB_ERR("abort, unable to alloc pseudo pallete\n");
@@ -1093,8 +1143,9 @@ static int ivtvfb_init_card(struct ivtv *itv)
                return -EBUSY;
        }
 
-       itv->osd_info = kzalloc(sizeof(struct osd_info), GFP_ATOMIC);
-       if (itv->osd_info == 0) {
+       itv->osd_info = kzalloc(sizeof(struct osd_info),
+                                       GFP_ATOMIC|__GFP_NOWARN);
+       if (itv->osd_info == NULL) {
                IVTVFB_ERR("Failed to allocate memory for osd_info\n");
                return -ENOMEM;
        }
@@ -1127,10 +1178,6 @@ static int ivtvfb_init_card(struct ivtv *itv)
        /* Enable the osd */
        ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info);
 
-       /* Note if we're running in compatibility mode */
-       if (osd_compat)
-               IVTVFB_INFO("Running in compatibility mode. Display resize & mode change disabled\n");
-
        /* Allocate DMA */
        ivtv_udma_alloc(itv);
        return 0;
@@ -1177,9 +1224,12 @@ static void ivtvfb_cleanup(void)
        for (i = 0; i < ivtv_cards_active; i++) {
                itv = ivtv_cards[i];
                if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) && itv->osd_info) {
+                       if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) {
+                               IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n", i);
+                               return;
+                       }
                        IVTVFB_DEBUG_INFO("Unregister framebuffer %d\n", i);
                        ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info);
-                       unregister_framebuffer(&itv->osd_info->ivtvfb_info);
                        ivtvfb_release_buffers(itv);
                        itv->osd_video_pbase = 0;
                }