162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci On Screen Display cx23415 Framebuffer driver 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci This module presents the cx23415 OSD (onscreen display) framebuffer memory 662306a36Sopenharmony_ci as a standard Linux /dev/fb style framebuffer device. The framebuffer has 762306a36Sopenharmony_ci support for 8, 16 & 32 bpp packed pixel formats with alpha channel. In 16bpp 862306a36Sopenharmony_ci mode, there is a choice of a three color depths (12, 15 or 16 bits), but no 962306a36Sopenharmony_ci local alpha. The colorspace is selectable between rgb & yuv. 1062306a36Sopenharmony_ci Depending on the TV standard configured in the ivtv module at load time, 1162306a36Sopenharmony_ci the initial resolution is either 640x400 (NTSC) or 640x480 (PAL) at 8bpp. 1262306a36Sopenharmony_ci Video timings are locked to ensure a vertical refresh rate of 50Hz (PAL) 1362306a36Sopenharmony_ci or 59.94 (NTSC) 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci Copyright (c) 2003 Matt T. Yourst <yourst@yourst.com> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci Derived from drivers/video/vesafb.c 1862306a36Sopenharmony_ci Portions (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci 2.6 kernel port: 2162306a36Sopenharmony_ci Copyright (C) 2004 Matthias Badaire 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci Copyright (C) 2004 Chris Kennedy <c@groovy.org> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci Copyright (C) 2006 Ian Armstrong <ian@iarmst.demon.co.uk> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "ivtv-driver.h" 3062306a36Sopenharmony_ci#include "ivtv-cards.h" 3162306a36Sopenharmony_ci#include "ivtv-i2c.h" 3262306a36Sopenharmony_ci#include "ivtv-udma.h" 3362306a36Sopenharmony_ci#include "ivtv-mailbox.h" 3462306a36Sopenharmony_ci#include "ivtv-firmware.h" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include <linux/fb.h> 3762306a36Sopenharmony_ci#include <linux/ivtvfb.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#if defined(CONFIG_X86_64) && !defined(CONFIG_UML) 4062306a36Sopenharmony_ci#include <asm/memtype.h> 4162306a36Sopenharmony_ci#endif 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* card parameters */ 4462306a36Sopenharmony_cistatic int ivtvfb_card_id = -1; 4562306a36Sopenharmony_cistatic int ivtvfb_debug; 4662306a36Sopenharmony_cistatic bool ivtvfb_force_pat = IS_ENABLED(CONFIG_VIDEO_FB_IVTV_FORCE_PAT); 4762306a36Sopenharmony_cistatic bool osd_laced; 4862306a36Sopenharmony_cistatic int osd_depth; 4962306a36Sopenharmony_cistatic int osd_upper; 5062306a36Sopenharmony_cistatic int osd_left; 5162306a36Sopenharmony_cistatic unsigned int osd_yres; 5262306a36Sopenharmony_cistatic unsigned int osd_xres; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cimodule_param(ivtvfb_card_id, int, 0444); 5562306a36Sopenharmony_cimodule_param_named(debug,ivtvfb_debug, int, 0644); 5662306a36Sopenharmony_cimodule_param_named(force_pat, ivtvfb_force_pat, bool, 0644); 5762306a36Sopenharmony_cimodule_param(osd_laced, bool, 0444); 5862306a36Sopenharmony_cimodule_param(osd_depth, int, 0444); 5962306a36Sopenharmony_cimodule_param(osd_upper, int, 0444); 6062306a36Sopenharmony_cimodule_param(osd_left, int, 0444); 6162306a36Sopenharmony_cimodule_param(osd_yres, uint, 0444); 6262306a36Sopenharmony_cimodule_param(osd_xres, uint, 0444); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ciMODULE_PARM_DESC(ivtvfb_card_id, 6562306a36Sopenharmony_ci "Only use framebuffer of the specified ivtv card (0-31)\n" 6662306a36Sopenharmony_ci "\t\t\tdefault -1: initialize all available framebuffers"); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ciMODULE_PARM_DESC(debug, 6962306a36Sopenharmony_ci "Debug level (bitmask). Default: errors only\n" 7062306a36Sopenharmony_ci "\t\t\t(debug = 3 gives full debugging)"); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ciMODULE_PARM_DESC(force_pat, 7362306a36Sopenharmony_ci "Force initialization on x86 PAT-enabled systems (bool).\n"); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* Why upper, left, xres, yres, depth, laced ? To match terminology used 7662306a36Sopenharmony_ci by fbset. 7762306a36Sopenharmony_ci Why start at 1 for left & upper coordinate ? Because X doesn't allow 0 */ 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ciMODULE_PARM_DESC(osd_laced, 8062306a36Sopenharmony_ci "Interlaced mode\n" 8162306a36Sopenharmony_ci "\t\t\t0=off\n" 8262306a36Sopenharmony_ci "\t\t\t1=on\n" 8362306a36Sopenharmony_ci "\t\t\tdefault off"); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ciMODULE_PARM_DESC(osd_depth, 8662306a36Sopenharmony_ci "Bits per pixel - 8, 16, 32\n" 8762306a36Sopenharmony_ci "\t\t\tdefault 8"); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ciMODULE_PARM_DESC(osd_upper, 9062306a36Sopenharmony_ci "Vertical start position\n" 9162306a36Sopenharmony_ci "\t\t\tdefault 0 (Centered)"); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ciMODULE_PARM_DESC(osd_left, 9462306a36Sopenharmony_ci "Horizontal start position\n" 9562306a36Sopenharmony_ci "\t\t\tdefault 0 (Centered)"); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ciMODULE_PARM_DESC(osd_yres, 9862306a36Sopenharmony_ci "Display height\n" 9962306a36Sopenharmony_ci "\t\t\tdefault 480 (PAL)\n" 10062306a36Sopenharmony_ci "\t\t\t 400 (NTSC)"); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ciMODULE_PARM_DESC(osd_xres, 10362306a36Sopenharmony_ci "Display width\n" 10462306a36Sopenharmony_ci "\t\t\tdefault 640"); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ciMODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil, John Harvey, Ian Armstrong"); 10762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* --------------------------------------------------------------------- */ 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci#define IVTVFB_DBGFLG_WARN (1 << 0) 11262306a36Sopenharmony_ci#define IVTVFB_DBGFLG_INFO (1 << 1) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define IVTVFB_DEBUG(x, type, fmt, args...) \ 11562306a36Sopenharmony_ci do { \ 11662306a36Sopenharmony_ci if ((x) & ivtvfb_debug) \ 11762306a36Sopenharmony_ci printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->instance , ## args); \ 11862306a36Sopenharmony_ci } while (0) 11962306a36Sopenharmony_ci#define IVTVFB_DEBUG_WARN(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_WARN, "warning", fmt , ## args) 12062306a36Sopenharmony_ci#define IVTVFB_DEBUG_INFO(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_INFO, "info", fmt , ## args) 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* Standard kernel messages */ 12362306a36Sopenharmony_ci#define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->instance , ## args) 12462306a36Sopenharmony_ci#define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->instance , ## args) 12562306a36Sopenharmony_ci#define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->instance , ## args) 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* --------------------------------------------------------------------- */ 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci#define IVTV_OSD_MAX_WIDTH 720 13062306a36Sopenharmony_ci#define IVTV_OSD_MAX_HEIGHT 576 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci#define IVTV_OSD_BPP_8 0x00 13362306a36Sopenharmony_ci#define IVTV_OSD_BPP_16_444 0x03 13462306a36Sopenharmony_ci#define IVTV_OSD_BPP_16_555 0x02 13562306a36Sopenharmony_ci#define IVTV_OSD_BPP_16_565 0x01 13662306a36Sopenharmony_ci#define IVTV_OSD_BPP_32 0x04 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistruct osd_info { 13962306a36Sopenharmony_ci /* Physical base address */ 14062306a36Sopenharmony_ci unsigned long video_pbase; 14162306a36Sopenharmony_ci /* Relative base address (relative to start of decoder memory) */ 14262306a36Sopenharmony_ci u32 video_rbase; 14362306a36Sopenharmony_ci /* Mapped base address */ 14462306a36Sopenharmony_ci volatile char __iomem *video_vbase; 14562306a36Sopenharmony_ci /* Buffer size */ 14662306a36Sopenharmony_ci u32 video_buffer_size; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* video_base rounded down as required by hardware MTRRs */ 14962306a36Sopenharmony_ci unsigned long fb_start_aligned_physaddr; 15062306a36Sopenharmony_ci /* video_base rounded up as required by hardware MTRRs */ 15162306a36Sopenharmony_ci unsigned long fb_end_aligned_physaddr; 15262306a36Sopenharmony_ci int wc_cookie; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* Store the buffer offset */ 15562306a36Sopenharmony_ci int set_osd_coords_x; 15662306a36Sopenharmony_ci int set_osd_coords_y; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* Current dimensions (NOT VISIBLE SIZE!) */ 15962306a36Sopenharmony_ci int display_width; 16062306a36Sopenharmony_ci int display_height; 16162306a36Sopenharmony_ci int display_byte_stride; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* Current bits per pixel */ 16462306a36Sopenharmony_ci int bits_per_pixel; 16562306a36Sopenharmony_ci int bytes_per_pixel; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* Frame buffer stuff */ 16862306a36Sopenharmony_ci struct fb_info ivtvfb_info; 16962306a36Sopenharmony_ci struct fb_var_screeninfo ivtvfb_defined; 17062306a36Sopenharmony_ci struct fb_fix_screeninfo ivtvfb_fix; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* Used for a warm start */ 17362306a36Sopenharmony_ci struct fb_var_screeninfo fbvar_cur; 17462306a36Sopenharmony_ci int blank_cur; 17562306a36Sopenharmony_ci u32 palette_cur[256]; 17662306a36Sopenharmony_ci u32 pan_cur; 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistruct ivtv_osd_coords { 18062306a36Sopenharmony_ci unsigned long offset; 18162306a36Sopenharmony_ci unsigned long max_offset; 18262306a36Sopenharmony_ci int pixel_stride; 18362306a36Sopenharmony_ci int lines; 18462306a36Sopenharmony_ci int x; 18562306a36Sopenharmony_ci int y; 18662306a36Sopenharmony_ci}; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/* --------------------------------------------------------------------- */ 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* ivtv API calls for framebuffer related support */ 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic int ivtvfb_get_framebuffer(struct ivtv *itv, u32 *fbbase, 19362306a36Sopenharmony_ci u32 *fblength) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci u32 data[CX2341X_MBOX_MAX_DATA]; 19662306a36Sopenharmony_ci int rc; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci ivtv_firmware_check(itv, "ivtvfb_get_framebuffer"); 19962306a36Sopenharmony_ci rc = ivtv_vapi_result(itv, data, CX2341X_OSD_GET_FRAMEBUFFER, 0); 20062306a36Sopenharmony_ci *fbbase = data[0]; 20162306a36Sopenharmony_ci *fblength = data[1]; 20262306a36Sopenharmony_ci return rc; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic int ivtvfb_get_osd_coords(struct ivtv *itv, 20662306a36Sopenharmony_ci struct ivtv_osd_coords *osd) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct osd_info *oi = itv->osd_info; 20962306a36Sopenharmony_ci u32 data[CX2341X_MBOX_MAX_DATA]; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci ivtv_vapi_result(itv, data, CX2341X_OSD_GET_OSD_COORDS, 0); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci osd->offset = data[0] - oi->video_rbase; 21462306a36Sopenharmony_ci osd->max_offset = oi->display_width * oi->display_height * 4; 21562306a36Sopenharmony_ci osd->pixel_stride = data[1]; 21662306a36Sopenharmony_ci osd->lines = data[2]; 21762306a36Sopenharmony_ci osd->x = data[3]; 21862306a36Sopenharmony_ci osd->y = data[4]; 21962306a36Sopenharmony_ci return 0; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic int ivtvfb_set_osd_coords(struct ivtv *itv, const struct ivtv_osd_coords *osd) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct osd_info *oi = itv->osd_info; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci oi->display_width = osd->pixel_stride; 22762306a36Sopenharmony_ci oi->display_byte_stride = osd->pixel_stride * oi->bytes_per_pixel; 22862306a36Sopenharmony_ci oi->set_osd_coords_x += osd->x; 22962306a36Sopenharmony_ci oi->set_osd_coords_y = osd->y; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return ivtv_vapi(itv, CX2341X_OSD_SET_OSD_COORDS, 5, 23262306a36Sopenharmony_ci osd->offset + oi->video_rbase, 23362306a36Sopenharmony_ci osd->pixel_stride, 23462306a36Sopenharmony_ci osd->lines, osd->x, osd->y); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic int ivtvfb_set_display_window(struct ivtv *itv, struct v4l2_rect *ivtv_window) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci int osd_height_limit = itv->is_out_50hz ? 576 : 480; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* Only fail if resolution too high, otherwise fudge the start coords. */ 24262306a36Sopenharmony_ci if ((ivtv_window->height > osd_height_limit) || (ivtv_window->width > IVTV_OSD_MAX_WIDTH)) 24362306a36Sopenharmony_ci return -EINVAL; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* Ensure we don't exceed display limits */ 24662306a36Sopenharmony_ci if (ivtv_window->top + ivtv_window->height > osd_height_limit) { 24762306a36Sopenharmony_ci IVTVFB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid height setting (%d, %d)\n", 24862306a36Sopenharmony_ci ivtv_window->top, ivtv_window->height); 24962306a36Sopenharmony_ci ivtv_window->top = osd_height_limit - ivtv_window->height; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (ivtv_window->left + ivtv_window->width > IVTV_OSD_MAX_WIDTH) { 25362306a36Sopenharmony_ci IVTVFB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid width setting (%d, %d)\n", 25462306a36Sopenharmony_ci ivtv_window->left, ivtv_window->width); 25562306a36Sopenharmony_ci ivtv_window->left = IVTV_OSD_MAX_WIDTH - ivtv_window->width; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Set the OSD origin */ 25962306a36Sopenharmony_ci write_reg((ivtv_window->top << 16) | ivtv_window->left, 0x02a04); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* How much to display */ 26262306a36Sopenharmony_ci write_reg(((ivtv_window->top+ivtv_window->height) << 16) | (ivtv_window->left+ivtv_window->width), 0x02a08); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* Pass this info back the yuv handler */ 26562306a36Sopenharmony_ci itv->yuv_info.osd_vis_w = ivtv_window->width; 26662306a36Sopenharmony_ci itv->yuv_info.osd_vis_h = ivtv_window->height; 26762306a36Sopenharmony_ci itv->yuv_info.osd_x_offset = ivtv_window->left; 26862306a36Sopenharmony_ci itv->yuv_info.osd_y_offset = ivtv_window->top; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic int ivtvfb_prep_dec_dma_to_device(struct ivtv *itv, 27462306a36Sopenharmony_ci unsigned long ivtv_dest_addr, void __user *userbuf, 27562306a36Sopenharmony_ci int size_in_bytes) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci DEFINE_WAIT(wait); 27862306a36Sopenharmony_ci int got_sig = 0; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci mutex_lock(&itv->udma.lock); 28162306a36Sopenharmony_ci /* Map User DMA */ 28262306a36Sopenharmony_ci if (ivtv_udma_setup(itv, ivtv_dest_addr, userbuf, size_in_bytes) <= 0) { 28362306a36Sopenharmony_ci mutex_unlock(&itv->udma.lock); 28462306a36Sopenharmony_ci IVTVFB_WARN("ivtvfb_prep_dec_dma_to_device, Error with pin_user_pages: %d bytes, %d pages returned\n", 28562306a36Sopenharmony_ci size_in_bytes, itv->udma.page_count); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* pin_user_pages must have failed completely */ 28862306a36Sopenharmony_ci return -EIO; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci IVTVFB_DEBUG_INFO("ivtvfb_prep_dec_dma_to_device, %d bytes, %d pages\n", 29262306a36Sopenharmony_ci size_in_bytes, itv->udma.page_count); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci ivtv_udma_prepare(itv); 29562306a36Sopenharmony_ci prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); 29662306a36Sopenharmony_ci /* if no UDMA is pending and no UDMA is in progress, then the DMA 29762306a36Sopenharmony_ci is finished */ 29862306a36Sopenharmony_ci while (test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags) || 29962306a36Sopenharmony_ci test_bit(IVTV_F_I_UDMA, &itv->i_flags)) { 30062306a36Sopenharmony_ci /* don't interrupt if the DMA is in progress but break off 30162306a36Sopenharmony_ci a still pending DMA. */ 30262306a36Sopenharmony_ci got_sig = signal_pending(current); 30362306a36Sopenharmony_ci if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci got_sig = 0; 30662306a36Sopenharmony_ci schedule(); 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci finish_wait(&itv->dma_waitq, &wait); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* Unmap Last DMA Xfer */ 31162306a36Sopenharmony_ci ivtv_udma_unmap(itv); 31262306a36Sopenharmony_ci mutex_unlock(&itv->udma.lock); 31362306a36Sopenharmony_ci if (got_sig) { 31462306a36Sopenharmony_ci IVTV_DEBUG_INFO("User stopped OSD\n"); 31562306a36Sopenharmony_ci return -EINTR; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return 0; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source, 32262306a36Sopenharmony_ci unsigned long dest_offset, int count) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci DEFINE_WAIT(wait); 32562306a36Sopenharmony_ci struct osd_info *oi = itv->osd_info; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* Nothing to do */ 32862306a36Sopenharmony_ci if (count == 0) { 32962306a36Sopenharmony_ci IVTVFB_DEBUG_WARN("ivtvfb_prep_frame: Nothing to do. count = 0\n"); 33062306a36Sopenharmony_ci return -EINVAL; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* Check Total FB Size */ 33462306a36Sopenharmony_ci if ((dest_offset + count) > oi->video_buffer_size) { 33562306a36Sopenharmony_ci IVTVFB_WARN("ivtvfb_prep_frame: Overflowing the framebuffer %ld, only %d available\n", 33662306a36Sopenharmony_ci dest_offset + count, oi->video_buffer_size); 33762306a36Sopenharmony_ci return -E2BIG; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* Not fatal, but will have undesirable results */ 34162306a36Sopenharmony_ci if ((unsigned long)source & 3) 34262306a36Sopenharmony_ci IVTVFB_WARN("ivtvfb_prep_frame: Source address not 32 bit aligned (%p)\n", 34362306a36Sopenharmony_ci source); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (dest_offset & 3) 34662306a36Sopenharmony_ci IVTVFB_WARN("ivtvfb_prep_frame: Dest offset not 32 bit aligned (%ld)\n", dest_offset); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (count & 3) 34962306a36Sopenharmony_ci IVTVFB_WARN("ivtvfb_prep_frame: Count not a multiple of 4 (%d)\n", count); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* Check Source */ 35262306a36Sopenharmony_ci if (!access_ok(source + dest_offset, count)) { 35362306a36Sopenharmony_ci IVTVFB_WARN("Invalid userspace pointer %p\n", source); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci IVTVFB_DEBUG_WARN("access_ok() failed for offset 0x%08lx source %p count %d\n", 35662306a36Sopenharmony_ci dest_offset, source, count); 35762306a36Sopenharmony_ci return -EINVAL; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* OSD Address to send DMA to */ 36162306a36Sopenharmony_ci dest_offset += IVTV_DECODER_OFFSET + oi->video_rbase; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* Fill Buffers */ 36462306a36Sopenharmony_ci return ivtvfb_prep_dec_dma_to_device(itv, dest_offset, source, count); 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic ssize_t ivtvfb_write(struct fb_info *info, const char __user *buf, 36862306a36Sopenharmony_ci size_t count, loff_t *ppos) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci unsigned long p = *ppos; 37162306a36Sopenharmony_ci void *dst; 37262306a36Sopenharmony_ci int err = 0; 37362306a36Sopenharmony_ci int dma_err; 37462306a36Sopenharmony_ci unsigned long total_size; 37562306a36Sopenharmony_ci struct ivtv *itv = (struct ivtv *) info->par; 37662306a36Sopenharmony_ci unsigned long dma_offset = 37762306a36Sopenharmony_ci IVTV_DECODER_OFFSET + itv->osd_info->video_rbase; 37862306a36Sopenharmony_ci unsigned long dma_size; 37962306a36Sopenharmony_ci u16 lead = 0, tail = 0; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (!info->screen_base) 38262306a36Sopenharmony_ci return -ENODEV; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci total_size = info->screen_size; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (total_size == 0) 38762306a36Sopenharmony_ci total_size = info->fix.smem_len; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (p > total_size) 39062306a36Sopenharmony_ci return -EFBIG; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (count > total_size) { 39362306a36Sopenharmony_ci err = -EFBIG; 39462306a36Sopenharmony_ci count = total_size; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (count + p > total_size) { 39862306a36Sopenharmony_ci if (!err) 39962306a36Sopenharmony_ci err = -ENOSPC; 40062306a36Sopenharmony_ci count = total_size - p; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci dst = (void __force *) (info->screen_base + p); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (info->fbops->fb_sync) 40662306a36Sopenharmony_ci info->fbops->fb_sync(info); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* If transfer size > threshold and both src/dst 40962306a36Sopenharmony_ci addresses are aligned, use DMA */ 41062306a36Sopenharmony_ci if (count >= 4096 && 41162306a36Sopenharmony_ci ((unsigned long)buf & 3) == ((unsigned long)dst & 3)) { 41262306a36Sopenharmony_ci /* Odd address = can't DMA. Align */ 41362306a36Sopenharmony_ci if ((unsigned long)dst & 3) { 41462306a36Sopenharmony_ci lead = 4 - ((unsigned long)dst & 3); 41562306a36Sopenharmony_ci if (copy_from_user(dst, buf, lead)) 41662306a36Sopenharmony_ci return -EFAULT; 41762306a36Sopenharmony_ci buf += lead; 41862306a36Sopenharmony_ci dst += lead; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci /* DMA resolution is 32 bits */ 42162306a36Sopenharmony_ci if ((count - lead) & 3) 42262306a36Sopenharmony_ci tail = (count - lead) & 3; 42362306a36Sopenharmony_ci /* DMA the data */ 42462306a36Sopenharmony_ci dma_size = count - lead - tail; 42562306a36Sopenharmony_ci dma_err = ivtvfb_prep_dec_dma_to_device(itv, 42662306a36Sopenharmony_ci p + lead + dma_offset, (void __user *)buf, dma_size); 42762306a36Sopenharmony_ci if (dma_err) 42862306a36Sopenharmony_ci return dma_err; 42962306a36Sopenharmony_ci dst += dma_size; 43062306a36Sopenharmony_ci buf += dma_size; 43162306a36Sopenharmony_ci /* Copy any leftover data */ 43262306a36Sopenharmony_ci if (tail && copy_from_user(dst, buf, tail)) 43362306a36Sopenharmony_ci return -EFAULT; 43462306a36Sopenharmony_ci } else if (copy_from_user(dst, buf, count)) { 43562306a36Sopenharmony_ci return -EFAULT; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (!err) 43962306a36Sopenharmony_ci *ppos += count; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return (err) ? err : count; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci DEFINE_WAIT(wait); 44762306a36Sopenharmony_ci struct ivtv *itv = (struct ivtv *)info->par; 44862306a36Sopenharmony_ci int rc = 0; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci switch (cmd) { 45162306a36Sopenharmony_ci case FBIOGET_VBLANK: { 45262306a36Sopenharmony_ci struct fb_vblank vblank; 45362306a36Sopenharmony_ci u32 trace; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci memset(&vblank, 0, sizeof(struct fb_vblank)); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT | 45862306a36Sopenharmony_ci FB_VBLANK_HAVE_VSYNC; 45962306a36Sopenharmony_ci trace = read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16; 46062306a36Sopenharmony_ci if (itv->is_out_50hz && trace > 312) 46162306a36Sopenharmony_ci trace -= 312; 46262306a36Sopenharmony_ci else if (itv->is_out_60hz && trace > 262) 46362306a36Sopenharmony_ci trace -= 262; 46462306a36Sopenharmony_ci if (trace == 1) 46562306a36Sopenharmony_ci vblank.flags |= FB_VBLANK_VSYNCING; 46662306a36Sopenharmony_ci vblank.count = itv->last_vsync_field; 46762306a36Sopenharmony_ci vblank.vcount = trace; 46862306a36Sopenharmony_ci vblank.hcount = 0; 46962306a36Sopenharmony_ci if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank))) 47062306a36Sopenharmony_ci return -EFAULT; 47162306a36Sopenharmony_ci return 0; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci case FBIO_WAITFORVSYNC: 47562306a36Sopenharmony_ci prepare_to_wait(&itv->vsync_waitq, &wait, TASK_INTERRUPTIBLE); 47662306a36Sopenharmony_ci if (!schedule_timeout(msecs_to_jiffies(50))) 47762306a36Sopenharmony_ci rc = -ETIMEDOUT; 47862306a36Sopenharmony_ci finish_wait(&itv->vsync_waitq, &wait); 47962306a36Sopenharmony_ci return rc; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci case IVTVFB_IOC_DMA_FRAME: { 48262306a36Sopenharmony_ci struct ivtvfb_dma_frame args; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci IVTVFB_DEBUG_INFO("IVTVFB_IOC_DMA_FRAME\n"); 48562306a36Sopenharmony_ci if (copy_from_user(&args, (void __user *)arg, sizeof(args))) 48662306a36Sopenharmony_ci return -EFAULT; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return ivtvfb_prep_frame(itv, cmd, args.source, args.dest_offset, args.count); 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci default: 49262306a36Sopenharmony_ci IVTVFB_DEBUG_INFO("Unknown ioctl %08x\n", cmd); 49362306a36Sopenharmony_ci return -EINVAL; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci return 0; 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci/* Framebuffer device handling */ 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct osd_info *oi = itv->osd_info; 50362306a36Sopenharmony_ci struct ivtv_osd_coords ivtv_osd; 50462306a36Sopenharmony_ci struct v4l2_rect ivtv_window; 50562306a36Sopenharmony_ci int osd_mode = -1; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci IVTVFB_DEBUG_INFO("ivtvfb_set_var\n"); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* Select color space */ 51062306a36Sopenharmony_ci if (var->nonstd) /* YUV */ 51162306a36Sopenharmony_ci write_reg(read_reg(0x02a00) | 0x0002000, 0x02a00); 51262306a36Sopenharmony_ci else /* RGB */ 51362306a36Sopenharmony_ci write_reg(read_reg(0x02a00) & ~0x0002000, 0x02a00); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* Set the color mode */ 51662306a36Sopenharmony_ci switch (var->bits_per_pixel) { 51762306a36Sopenharmony_ci case 8: 51862306a36Sopenharmony_ci osd_mode = IVTV_OSD_BPP_8; 51962306a36Sopenharmony_ci break; 52062306a36Sopenharmony_ci case 32: 52162306a36Sopenharmony_ci osd_mode = IVTV_OSD_BPP_32; 52262306a36Sopenharmony_ci break; 52362306a36Sopenharmony_ci case 16: 52462306a36Sopenharmony_ci switch (var->green.length) { 52562306a36Sopenharmony_ci case 4: 52662306a36Sopenharmony_ci osd_mode = IVTV_OSD_BPP_16_444; 52762306a36Sopenharmony_ci break; 52862306a36Sopenharmony_ci case 5: 52962306a36Sopenharmony_ci osd_mode = IVTV_OSD_BPP_16_555; 53062306a36Sopenharmony_ci break; 53162306a36Sopenharmony_ci case 6: 53262306a36Sopenharmony_ci osd_mode = IVTV_OSD_BPP_16_565; 53362306a36Sopenharmony_ci break; 53462306a36Sopenharmony_ci default: 53562306a36Sopenharmony_ci IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n"); 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci break; 53862306a36Sopenharmony_ci default: 53962306a36Sopenharmony_ci IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n"); 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* Set video mode. Although rare, the display can become scrambled even 54362306a36Sopenharmony_ci if we don't change mode. Always 'bounce' to osd_mode via mode 0 */ 54462306a36Sopenharmony_ci if (osd_mode != -1) { 54562306a36Sopenharmony_ci ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0); 54662306a36Sopenharmony_ci ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, osd_mode); 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci oi->bits_per_pixel = var->bits_per_pixel; 55062306a36Sopenharmony_ci oi->bytes_per_pixel = var->bits_per_pixel / 8; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* Set the flicker filter */ 55362306a36Sopenharmony_ci switch (var->vmode & FB_VMODE_MASK) { 55462306a36Sopenharmony_ci case FB_VMODE_NONINTERLACED: /* Filter on */ 55562306a36Sopenharmony_ci ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 1); 55662306a36Sopenharmony_ci break; 55762306a36Sopenharmony_ci case FB_VMODE_INTERLACED: /* Filter off */ 55862306a36Sopenharmony_ci ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 0); 55962306a36Sopenharmony_ci break; 56062306a36Sopenharmony_ci default: 56162306a36Sopenharmony_ci IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid video mode\n"); 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* Read the current osd info */ 56562306a36Sopenharmony_ci ivtvfb_get_osd_coords(itv, &ivtv_osd); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* Now set the OSD to the size we want */ 56862306a36Sopenharmony_ci ivtv_osd.pixel_stride = var->xres_virtual; 56962306a36Sopenharmony_ci ivtv_osd.lines = var->yres_virtual; 57062306a36Sopenharmony_ci ivtv_osd.x = 0; 57162306a36Sopenharmony_ci ivtv_osd.y = 0; 57262306a36Sopenharmony_ci ivtvfb_set_osd_coords(itv, &ivtv_osd); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* Can't seem to find the right API combo for this. 57562306a36Sopenharmony_ci Use another function which does what we need through direct register access. */ 57662306a36Sopenharmony_ci ivtv_window.width = var->xres; 57762306a36Sopenharmony_ci ivtv_window.height = var->yres; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* Minimum margin cannot be 0, as X won't allow such a mode */ 58062306a36Sopenharmony_ci if (!var->upper_margin) 58162306a36Sopenharmony_ci var->upper_margin++; 58262306a36Sopenharmony_ci if (!var->left_margin) 58362306a36Sopenharmony_ci var->left_margin++; 58462306a36Sopenharmony_ci ivtv_window.top = var->upper_margin - 1; 58562306a36Sopenharmony_ci ivtv_window.left = var->left_margin - 1; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci ivtvfb_set_display_window(itv, &ivtv_window); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* Pass screen size back to yuv handler */ 59062306a36Sopenharmony_ci itv->yuv_info.osd_full_w = ivtv_osd.pixel_stride; 59162306a36Sopenharmony_ci itv->yuv_info.osd_full_h = ivtv_osd.lines; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* Force update of yuv registers */ 59462306a36Sopenharmony_ci itv->yuv_info.yuv_forced_update = 1; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci /* Keep a copy of these settings */ 59762306a36Sopenharmony_ci memcpy(&oi->fbvar_cur, var, sizeof(oi->fbvar_cur)); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n", 60062306a36Sopenharmony_ci var->xres, var->yres, 60162306a36Sopenharmony_ci var->xres_virtual, var->yres_virtual, 60262306a36Sopenharmony_ci var->bits_per_pixel); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci IVTVFB_DEBUG_INFO("Display position: %d, %d\n", 60562306a36Sopenharmony_ci var->left_margin, var->upper_margin); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci IVTVFB_DEBUG_INFO("Display filter: %s\n", 60862306a36Sopenharmony_ci (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off"); 60962306a36Sopenharmony_ci IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB"); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci return 0; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct osd_info *oi = itv->osd_info; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci IVTVFB_DEBUG_INFO("ivtvfb_get_fix\n"); 61962306a36Sopenharmony_ci memset(fix, 0, sizeof(struct fb_fix_screeninfo)); 62062306a36Sopenharmony_ci strscpy(fix->id, "cx23415 TV out", sizeof(fix->id)); 62162306a36Sopenharmony_ci fix->smem_start = oi->video_pbase; 62262306a36Sopenharmony_ci fix->smem_len = oi->video_buffer_size; 62362306a36Sopenharmony_ci fix->type = FB_TYPE_PACKED_PIXELS; 62462306a36Sopenharmony_ci fix->visual = (oi->bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; 62562306a36Sopenharmony_ci fix->xpanstep = 1; 62662306a36Sopenharmony_ci fix->ypanstep = 1; 62762306a36Sopenharmony_ci fix->ywrapstep = 0; 62862306a36Sopenharmony_ci fix->line_length = oi->display_byte_stride; 62962306a36Sopenharmony_ci fix->accel = FB_ACCEL_NONE; 63062306a36Sopenharmony_ci return 0; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci/* Check the requested display mode, returning -EINVAL if we can't 63462306a36Sopenharmony_ci handle it. */ 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct osd_info *oi = itv->osd_info; 63962306a36Sopenharmony_ci int osd_height_limit; 64062306a36Sopenharmony_ci u32 pixclock, hlimit, vlimit; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci IVTVFB_DEBUG_INFO("ivtvfb_check_var\n"); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci /* Set base references for mode calcs. */ 64562306a36Sopenharmony_ci if (itv->is_out_50hz) { 64662306a36Sopenharmony_ci pixclock = 84316; 64762306a36Sopenharmony_ci hlimit = 776; 64862306a36Sopenharmony_ci vlimit = 591; 64962306a36Sopenharmony_ci osd_height_limit = 576; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci else { 65262306a36Sopenharmony_ci pixclock = 83926; 65362306a36Sopenharmony_ci hlimit = 776; 65462306a36Sopenharmony_ci vlimit = 495; 65562306a36Sopenharmony_ci osd_height_limit = 480; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci if (var->bits_per_pixel == 8 || var->bits_per_pixel == 32) { 65962306a36Sopenharmony_ci var->transp.offset = 24; 66062306a36Sopenharmony_ci var->transp.length = 8; 66162306a36Sopenharmony_ci var->red.offset = 16; 66262306a36Sopenharmony_ci var->red.length = 8; 66362306a36Sopenharmony_ci var->green.offset = 8; 66462306a36Sopenharmony_ci var->green.length = 8; 66562306a36Sopenharmony_ci var->blue.offset = 0; 66662306a36Sopenharmony_ci var->blue.length = 8; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci else if (var->bits_per_pixel == 16) { 66962306a36Sopenharmony_ci /* To find out the true mode, check green length */ 67062306a36Sopenharmony_ci switch (var->green.length) { 67162306a36Sopenharmony_ci case 4: 67262306a36Sopenharmony_ci var->red.offset = 8; 67362306a36Sopenharmony_ci var->red.length = 4; 67462306a36Sopenharmony_ci var->green.offset = 4; 67562306a36Sopenharmony_ci var->green.length = 4; 67662306a36Sopenharmony_ci var->blue.offset = 0; 67762306a36Sopenharmony_ci var->blue.length = 4; 67862306a36Sopenharmony_ci var->transp.offset = 12; 67962306a36Sopenharmony_ci var->transp.length = 1; 68062306a36Sopenharmony_ci break; 68162306a36Sopenharmony_ci case 5: 68262306a36Sopenharmony_ci var->red.offset = 10; 68362306a36Sopenharmony_ci var->red.length = 5; 68462306a36Sopenharmony_ci var->green.offset = 5; 68562306a36Sopenharmony_ci var->green.length = 5; 68662306a36Sopenharmony_ci var->blue.offset = 0; 68762306a36Sopenharmony_ci var->blue.length = 5; 68862306a36Sopenharmony_ci var->transp.offset = 15; 68962306a36Sopenharmony_ci var->transp.length = 1; 69062306a36Sopenharmony_ci break; 69162306a36Sopenharmony_ci default: 69262306a36Sopenharmony_ci var->red.offset = 11; 69362306a36Sopenharmony_ci var->red.length = 5; 69462306a36Sopenharmony_ci var->green.offset = 5; 69562306a36Sopenharmony_ci var->green.length = 6; 69662306a36Sopenharmony_ci var->blue.offset = 0; 69762306a36Sopenharmony_ci var->blue.length = 5; 69862306a36Sopenharmony_ci var->transp.offset = 0; 69962306a36Sopenharmony_ci var->transp.length = 0; 70062306a36Sopenharmony_ci break; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci else { 70462306a36Sopenharmony_ci IVTVFB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel); 70562306a36Sopenharmony_ci return -EINVAL; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* Check the resolution */ 70962306a36Sopenharmony_ci if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit) { 71062306a36Sopenharmony_ci IVTVFB_DEBUG_WARN("Invalid resolution: %dx%d\n", 71162306a36Sopenharmony_ci var->xres, var->yres); 71262306a36Sopenharmony_ci return -EINVAL; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci /* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */ 71662306a36Sopenharmony_ci if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) || 71762306a36Sopenharmony_ci var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8) > oi->video_buffer_size || 71862306a36Sopenharmony_ci var->xres_virtual < var->xres || 71962306a36Sopenharmony_ci var->yres_virtual < var->yres) { 72062306a36Sopenharmony_ci IVTVFB_DEBUG_WARN("Invalid virtual resolution: %dx%d\n", 72162306a36Sopenharmony_ci var->xres_virtual, var->yres_virtual); 72262306a36Sopenharmony_ci return -EINVAL; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci /* Some extra checks if in 8 bit mode */ 72662306a36Sopenharmony_ci if (var->bits_per_pixel == 8) { 72762306a36Sopenharmony_ci /* Width must be a multiple of 4 */ 72862306a36Sopenharmony_ci if (var->xres & 3) { 72962306a36Sopenharmony_ci IVTVFB_DEBUG_WARN("Invalid resolution for 8bpp: %d\n", var->xres); 73062306a36Sopenharmony_ci return -EINVAL; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci if (var->xres_virtual & 3) { 73362306a36Sopenharmony_ci IVTVFB_DEBUG_WARN("Invalid virtual resolution for 8bpp: %d)\n", var->xres_virtual); 73462306a36Sopenharmony_ci return -EINVAL; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci else if (var->bits_per_pixel == 16) { 73862306a36Sopenharmony_ci /* Width must be a multiple of 2 */ 73962306a36Sopenharmony_ci if (var->xres & 1) { 74062306a36Sopenharmony_ci IVTVFB_DEBUG_WARN("Invalid resolution for 16bpp: %d\n", var->xres); 74162306a36Sopenharmony_ci return -EINVAL; 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci if (var->xres_virtual & 1) { 74462306a36Sopenharmony_ci IVTVFB_DEBUG_WARN("Invalid virtual resolution for 16bpp: %d)\n", var->xres_virtual); 74562306a36Sopenharmony_ci return -EINVAL; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci /* Now check the offsets */ 75062306a36Sopenharmony_ci if (var->xoffset >= var->xres_virtual || var->yoffset >= var->yres_virtual) { 75162306a36Sopenharmony_ci IVTVFB_DEBUG_WARN("Invalid offset: %d (%d) %d (%d)\n", 75262306a36Sopenharmony_ci var->xoffset, var->xres_virtual, var->yoffset, var->yres_virtual); 75362306a36Sopenharmony_ci return -EINVAL; 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci /* Check pixel format */ 75762306a36Sopenharmony_ci if (var->nonstd > 1) { 75862306a36Sopenharmony_ci IVTVFB_DEBUG_WARN("Invalid nonstd % d\n", var->nonstd); 75962306a36Sopenharmony_ci return -EINVAL; 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci /* Check video mode */ 76362306a36Sopenharmony_ci if (((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) && 76462306a36Sopenharmony_ci ((var->vmode & FB_VMODE_MASK) != FB_VMODE_INTERLACED)) { 76562306a36Sopenharmony_ci IVTVFB_DEBUG_WARN("Invalid video mode: %d\n", var->vmode & FB_VMODE_MASK); 76662306a36Sopenharmony_ci return -EINVAL; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* Check the left & upper margins 77062306a36Sopenharmony_ci If the margins are too large, just center the screen 77162306a36Sopenharmony_ci (enforcing margins causes too many problems) */ 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci if (var->left_margin + var->xres > IVTV_OSD_MAX_WIDTH + 1) 77462306a36Sopenharmony_ci var->left_margin = 1 + ((IVTV_OSD_MAX_WIDTH - var->xres) / 2); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (var->upper_margin + var->yres > (itv->is_out_50hz ? 577 : 481)) 77762306a36Sopenharmony_ci var->upper_margin = 1 + (((itv->is_out_50hz ? 576 : 480) - 77862306a36Sopenharmony_ci var->yres) / 2); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci /* Maintain overall 'size' for a constant refresh rate */ 78162306a36Sopenharmony_ci var->right_margin = hlimit - var->left_margin - var->xres; 78262306a36Sopenharmony_ci var->lower_margin = vlimit - var->upper_margin - var->yres; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci /* Fixed sync times */ 78562306a36Sopenharmony_ci var->hsync_len = 24; 78662306a36Sopenharmony_ci var->vsync_len = 2; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci /* Non-interlaced / interlaced mode is used to switch the OSD filter 78962306a36Sopenharmony_ci on or off. Adjust the clock timings to maintain a constant 79062306a36Sopenharmony_ci vertical refresh rate. */ 79162306a36Sopenharmony_ci if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED) 79262306a36Sopenharmony_ci var->pixclock = pixclock / 2; 79362306a36Sopenharmony_ci else 79462306a36Sopenharmony_ci var->pixclock = pixclock; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci itv->osd_rect.width = var->xres; 79762306a36Sopenharmony_ci itv->osd_rect.height = var->yres; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n", 80062306a36Sopenharmony_ci var->xres, var->yres, 80162306a36Sopenharmony_ci var->xres_virtual, var->yres_virtual, 80262306a36Sopenharmony_ci var->bits_per_pixel); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci IVTVFB_DEBUG_INFO("Display position: %d, %d\n", 80562306a36Sopenharmony_ci var->left_margin, var->upper_margin); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci IVTVFB_DEBUG_INFO("Display filter: %s\n", 80862306a36Sopenharmony_ci (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off"); 80962306a36Sopenharmony_ci IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB"); 81062306a36Sopenharmony_ci return 0; 81162306a36Sopenharmony_ci} 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_cistatic int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci struct ivtv *itv = (struct ivtv *) info->par; 81662306a36Sopenharmony_ci IVTVFB_DEBUG_INFO("ivtvfb_check_var\n"); 81762306a36Sopenharmony_ci return _ivtvfb_check_var(var, itv); 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) 82162306a36Sopenharmony_ci{ 82262306a36Sopenharmony_ci u32 osd_pan_index; 82362306a36Sopenharmony_ci struct ivtv *itv = (struct ivtv *) info->par; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci if (var->yoffset + info->var.yres > info->var.yres_virtual || 82662306a36Sopenharmony_ci var->xoffset + info->var.xres > info->var.xres_virtual) 82762306a36Sopenharmony_ci return -EINVAL; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci osd_pan_index = var->yoffset * info->fix.line_length 83062306a36Sopenharmony_ci + var->xoffset * info->var.bits_per_pixel / 8; 83162306a36Sopenharmony_ci write_reg(osd_pan_index, 0x02A0C); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci /* Pass this info back the yuv handler */ 83462306a36Sopenharmony_ci itv->yuv_info.osd_x_pan = var->xoffset; 83562306a36Sopenharmony_ci itv->yuv_info.osd_y_pan = var->yoffset; 83662306a36Sopenharmony_ci /* Force update of yuv registers */ 83762306a36Sopenharmony_ci itv->yuv_info.yuv_forced_update = 1; 83862306a36Sopenharmony_ci /* Remember this value */ 83962306a36Sopenharmony_ci itv->osd_info->pan_cur = osd_pan_index; 84062306a36Sopenharmony_ci return 0; 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic int ivtvfb_set_par(struct fb_info *info) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci int rc = 0; 84662306a36Sopenharmony_ci struct ivtv *itv = (struct ivtv *) info->par; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci IVTVFB_DEBUG_INFO("ivtvfb_set_par\n"); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci rc = ivtvfb_set_var(itv, &info->var); 85162306a36Sopenharmony_ci ivtvfb_pan_display(&info->var, info); 85262306a36Sopenharmony_ci ivtvfb_get_fix(itv, &info->fix); 85362306a36Sopenharmony_ci ivtv_firmware_check(itv, "ivtvfb_set_par"); 85462306a36Sopenharmony_ci return rc; 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cistatic int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green, 85862306a36Sopenharmony_ci unsigned blue, unsigned transp, 85962306a36Sopenharmony_ci struct fb_info *info) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci u32 color, *palette; 86262306a36Sopenharmony_ci struct ivtv *itv = (struct ivtv *)info->par; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (regno >= info->cmap.len) 86562306a36Sopenharmony_ci return -EINVAL; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci color = ((transp & 0xFF00) << 16) |((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8); 86862306a36Sopenharmony_ci if (info->var.bits_per_pixel <= 8) { 86962306a36Sopenharmony_ci write_reg(regno, 0x02a30); 87062306a36Sopenharmony_ci write_reg(color, 0x02a34); 87162306a36Sopenharmony_ci itv->osd_info->palette_cur[regno] = color; 87262306a36Sopenharmony_ci return 0; 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci if (regno >= 16) 87562306a36Sopenharmony_ci return -EINVAL; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci palette = info->pseudo_palette; 87862306a36Sopenharmony_ci if (info->var.bits_per_pixel == 16) { 87962306a36Sopenharmony_ci switch (info->var.green.length) { 88062306a36Sopenharmony_ci case 4: 88162306a36Sopenharmony_ci color = ((red & 0xf000) >> 4) | 88262306a36Sopenharmony_ci ((green & 0xf000) >> 8) | 88362306a36Sopenharmony_ci ((blue & 0xf000) >> 12); 88462306a36Sopenharmony_ci break; 88562306a36Sopenharmony_ci case 5: 88662306a36Sopenharmony_ci color = ((red & 0xf800) >> 1) | 88762306a36Sopenharmony_ci ((green & 0xf800) >> 6) | 88862306a36Sopenharmony_ci ((blue & 0xf800) >> 11); 88962306a36Sopenharmony_ci break; 89062306a36Sopenharmony_ci case 6: 89162306a36Sopenharmony_ci color = (red & 0xf800 ) | 89262306a36Sopenharmony_ci ((green & 0xfc00) >> 5) | 89362306a36Sopenharmony_ci ((blue & 0xf800) >> 11); 89462306a36Sopenharmony_ci break; 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci palette[regno] = color; 89862306a36Sopenharmony_ci return 0; 89962306a36Sopenharmony_ci} 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci/* We don't really support blanking. All this does is enable or 90262306a36Sopenharmony_ci disable the OSD. */ 90362306a36Sopenharmony_cistatic int ivtvfb_blank(int blank_mode, struct fb_info *info) 90462306a36Sopenharmony_ci{ 90562306a36Sopenharmony_ci struct ivtv *itv = (struct ivtv *)info->par; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci IVTVFB_DEBUG_INFO("Set blanking mode : %d\n", blank_mode); 90862306a36Sopenharmony_ci switch (blank_mode) { 90962306a36Sopenharmony_ci case FB_BLANK_UNBLANK: 91062306a36Sopenharmony_ci ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1); 91162306a36Sopenharmony_ci ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1); 91262306a36Sopenharmony_ci break; 91362306a36Sopenharmony_ci case FB_BLANK_NORMAL: 91462306a36Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 91562306a36Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 91662306a36Sopenharmony_ci ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0); 91762306a36Sopenharmony_ci ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1); 91862306a36Sopenharmony_ci break; 91962306a36Sopenharmony_ci case FB_BLANK_POWERDOWN: 92062306a36Sopenharmony_ci ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0); 92162306a36Sopenharmony_ci ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0); 92262306a36Sopenharmony_ci break; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci itv->osd_info->blank_cur = blank_mode; 92562306a36Sopenharmony_ci return 0; 92662306a36Sopenharmony_ci} 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_cistatic const struct fb_ops ivtvfb_ops = { 92962306a36Sopenharmony_ci .owner = THIS_MODULE, 93062306a36Sopenharmony_ci .fb_write = ivtvfb_write, 93162306a36Sopenharmony_ci .fb_check_var = ivtvfb_check_var, 93262306a36Sopenharmony_ci .fb_set_par = ivtvfb_set_par, 93362306a36Sopenharmony_ci .fb_setcolreg = ivtvfb_setcolreg, 93462306a36Sopenharmony_ci .fb_fillrect = cfb_fillrect, 93562306a36Sopenharmony_ci .fb_copyarea = cfb_copyarea, 93662306a36Sopenharmony_ci .fb_imageblit = cfb_imageblit, 93762306a36Sopenharmony_ci .fb_cursor = NULL, 93862306a36Sopenharmony_ci .fb_ioctl = ivtvfb_ioctl, 93962306a36Sopenharmony_ci .fb_pan_display = ivtvfb_pan_display, 94062306a36Sopenharmony_ci .fb_blank = ivtvfb_blank, 94162306a36Sopenharmony_ci}; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci/* Restore hardware after firmware restart */ 94462306a36Sopenharmony_cistatic void ivtvfb_restore(struct ivtv *itv) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci struct osd_info *oi = itv->osd_info; 94762306a36Sopenharmony_ci int i; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci ivtvfb_set_var(itv, &oi->fbvar_cur); 95062306a36Sopenharmony_ci ivtvfb_blank(oi->blank_cur, &oi->ivtvfb_info); 95162306a36Sopenharmony_ci for (i = 0; i < 256; i++) { 95262306a36Sopenharmony_ci write_reg(i, 0x02a30); 95362306a36Sopenharmony_ci write_reg(oi->palette_cur[i], 0x02a34); 95462306a36Sopenharmony_ci } 95562306a36Sopenharmony_ci write_reg(oi->pan_cur, 0x02a0c); 95662306a36Sopenharmony_ci} 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci/* Initialization */ 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci/* Setup our initial video mode */ 96262306a36Sopenharmony_cistatic int ivtvfb_init_vidmode(struct ivtv *itv) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci struct osd_info *oi = itv->osd_info; 96562306a36Sopenharmony_ci struct v4l2_rect start_window; 96662306a36Sopenharmony_ci int max_height; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci /* Color mode */ 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32) 97162306a36Sopenharmony_ci osd_depth = 8; 97262306a36Sopenharmony_ci oi->bits_per_pixel = osd_depth; 97362306a36Sopenharmony_ci oi->bytes_per_pixel = oi->bits_per_pixel / 8; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci /* Horizontal size & position */ 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci if (osd_xres > 720) 97862306a36Sopenharmony_ci osd_xres = 720; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci /* Must be a multiple of 4 for 8bpp & 2 for 16bpp */ 98162306a36Sopenharmony_ci if (osd_depth == 8) 98262306a36Sopenharmony_ci osd_xres &= ~3; 98362306a36Sopenharmony_ci else if (osd_depth == 16) 98462306a36Sopenharmony_ci osd_xres &= ~1; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci start_window.width = osd_xres ? osd_xres : 640; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci /* Check horizontal start (osd_left). */ 98962306a36Sopenharmony_ci if (osd_left && osd_left + start_window.width > 721) { 99062306a36Sopenharmony_ci IVTVFB_ERR("Invalid osd_left - assuming default\n"); 99162306a36Sopenharmony_ci osd_left = 0; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci /* Hardware coords start at 0, user coords start at 1. */ 99562306a36Sopenharmony_ci osd_left--; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci start_window.left = osd_left >= 0 ? 99862306a36Sopenharmony_ci osd_left : ((IVTV_OSD_MAX_WIDTH - start_window.width) / 2); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci oi->display_byte_stride = 100162306a36Sopenharmony_ci start_window.width * oi->bytes_per_pixel; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci /* Vertical size & position */ 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci max_height = itv->is_out_50hz ? 576 : 480; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci if (osd_yres > max_height) 100862306a36Sopenharmony_ci osd_yres = max_height; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci start_window.height = osd_yres ? 101162306a36Sopenharmony_ci osd_yres : itv->is_out_50hz ? 480 : 400; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci /* Check vertical start (osd_upper). */ 101462306a36Sopenharmony_ci if (osd_upper + start_window.height > max_height + 1) { 101562306a36Sopenharmony_ci IVTVFB_ERR("Invalid osd_upper - assuming default\n"); 101662306a36Sopenharmony_ci osd_upper = 0; 101762306a36Sopenharmony_ci } 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci /* Hardware coords start at 0, user coords start at 1. */ 102062306a36Sopenharmony_ci osd_upper--; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci start_window.top = osd_upper >= 0 ? osd_upper : ((max_height - start_window.height) / 2); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci oi->display_width = start_window.width; 102562306a36Sopenharmony_ci oi->display_height = start_window.height; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci /* Generate a valid fb_var_screeninfo */ 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci oi->ivtvfb_defined.xres = oi->display_width; 103062306a36Sopenharmony_ci oi->ivtvfb_defined.yres = oi->display_height; 103162306a36Sopenharmony_ci oi->ivtvfb_defined.xres_virtual = oi->display_width; 103262306a36Sopenharmony_ci oi->ivtvfb_defined.yres_virtual = oi->display_height; 103362306a36Sopenharmony_ci oi->ivtvfb_defined.bits_per_pixel = oi->bits_per_pixel; 103462306a36Sopenharmony_ci oi->ivtvfb_defined.vmode = (osd_laced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED); 103562306a36Sopenharmony_ci oi->ivtvfb_defined.left_margin = start_window.left + 1; 103662306a36Sopenharmony_ci oi->ivtvfb_defined.upper_margin = start_window.top + 1; 103762306a36Sopenharmony_ci oi->ivtvfb_defined.accel_flags = FB_ACCEL_NONE; 103862306a36Sopenharmony_ci oi->ivtvfb_defined.nonstd = 0; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci /* We've filled in the most data, let the usual mode check 104162306a36Sopenharmony_ci routine fill in the rest. */ 104262306a36Sopenharmony_ci _ivtvfb_check_var(&oi->ivtvfb_defined, itv); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci /* Generate valid fb_fix_screeninfo */ 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci ivtvfb_get_fix(itv, &oi->ivtvfb_fix); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci /* Generate valid fb_info */ 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci oi->ivtvfb_info.node = -1; 105162306a36Sopenharmony_ci oi->ivtvfb_info.par = itv; 105262306a36Sopenharmony_ci oi->ivtvfb_info.var = oi->ivtvfb_defined; 105362306a36Sopenharmony_ci oi->ivtvfb_info.fix = oi->ivtvfb_fix; 105462306a36Sopenharmony_ci oi->ivtvfb_info.screen_base = (u8 __iomem *)oi->video_vbase; 105562306a36Sopenharmony_ci oi->ivtvfb_info.fbops = &ivtvfb_ops; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci /* Supply some monitor specs. Bogus values will do for now */ 105862306a36Sopenharmony_ci oi->ivtvfb_info.monspecs.hfmin = 8000; 105962306a36Sopenharmony_ci oi->ivtvfb_info.monspecs.hfmax = 70000; 106062306a36Sopenharmony_ci oi->ivtvfb_info.monspecs.vfmin = 10; 106162306a36Sopenharmony_ci oi->ivtvfb_info.monspecs.vfmax = 100; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci /* Allocate color map */ 106462306a36Sopenharmony_ci if (fb_alloc_cmap(&oi->ivtvfb_info.cmap, 256, 1)) { 106562306a36Sopenharmony_ci IVTVFB_ERR("abort, unable to alloc cmap\n"); 106662306a36Sopenharmony_ci return -ENOMEM; 106762306a36Sopenharmony_ci } 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci /* Allocate the pseudo palette */ 107062306a36Sopenharmony_ci oi->ivtvfb_info.pseudo_palette = 107162306a36Sopenharmony_ci kmalloc_array(16, sizeof(u32), GFP_KERNEL|__GFP_NOWARN); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci if (!oi->ivtvfb_info.pseudo_palette) { 107462306a36Sopenharmony_ci IVTVFB_ERR("abort, unable to alloc pseudo palette\n"); 107562306a36Sopenharmony_ci return -ENOMEM; 107662306a36Sopenharmony_ci } 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci return 0; 107962306a36Sopenharmony_ci} 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci/* Find OSD buffer base & size. Add to mtrr. Zero osd buffer. */ 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_cistatic int ivtvfb_init_io(struct ivtv *itv) 108462306a36Sopenharmony_ci{ 108562306a36Sopenharmony_ci struct osd_info *oi = itv->osd_info; 108662306a36Sopenharmony_ci /* Find the largest power of two that maps the whole buffer */ 108762306a36Sopenharmony_ci int size_shift = 31; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci mutex_lock(&itv->serialize_lock); 109062306a36Sopenharmony_ci if (ivtv_init_on_first_open(itv)) { 109162306a36Sopenharmony_ci mutex_unlock(&itv->serialize_lock); 109262306a36Sopenharmony_ci IVTVFB_ERR("Failed to initialize ivtv\n"); 109362306a36Sopenharmony_ci return -ENXIO; 109462306a36Sopenharmony_ci } 109562306a36Sopenharmony_ci mutex_unlock(&itv->serialize_lock); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci if (ivtvfb_get_framebuffer(itv, &oi->video_rbase, 109862306a36Sopenharmony_ci &oi->video_buffer_size) < 0) { 109962306a36Sopenharmony_ci IVTVFB_ERR("Firmware failed to respond\n"); 110062306a36Sopenharmony_ci return -EIO; 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci /* The osd buffer size depends on the number of video buffers allocated 110462306a36Sopenharmony_ci on the PVR350 itself. For now we'll hardcode the smallest osd buffer 110562306a36Sopenharmony_ci size to prevent any overlap. */ 110662306a36Sopenharmony_ci oi->video_buffer_size = 1704960; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci oi->video_pbase = itv->base_addr + IVTV_DECODER_OFFSET + oi->video_rbase; 110962306a36Sopenharmony_ci oi->video_vbase = itv->dec_mem + oi->video_rbase; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci if (!oi->video_vbase) { 111262306a36Sopenharmony_ci IVTVFB_ERR("abort, video memory 0x%x @ 0x%lx isn't mapped!\n", 111362306a36Sopenharmony_ci oi->video_buffer_size, oi->video_pbase); 111462306a36Sopenharmony_ci return -EIO; 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci IVTVFB_INFO("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n", 111862306a36Sopenharmony_ci oi->video_pbase, oi->video_vbase, 111962306a36Sopenharmony_ci oi->video_buffer_size / 1024); 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci while (!(oi->video_buffer_size & (1 << size_shift))) 112262306a36Sopenharmony_ci size_shift--; 112362306a36Sopenharmony_ci size_shift++; 112462306a36Sopenharmony_ci oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1); 112562306a36Sopenharmony_ci oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size; 112662306a36Sopenharmony_ci oi->fb_end_aligned_physaddr += (1 << size_shift) - 1; 112762306a36Sopenharmony_ci oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1); 112862306a36Sopenharmony_ci oi->wc_cookie = arch_phys_wc_add(oi->fb_start_aligned_physaddr, 112962306a36Sopenharmony_ci oi->fb_end_aligned_physaddr - 113062306a36Sopenharmony_ci oi->fb_start_aligned_physaddr); 113162306a36Sopenharmony_ci /* Blank the entire osd. */ 113262306a36Sopenharmony_ci memset_io(oi->video_vbase, 0, oi->video_buffer_size); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci return 0; 113562306a36Sopenharmony_ci} 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci/* Release any memory we've grabbed & remove mtrr entry */ 113862306a36Sopenharmony_cistatic void ivtvfb_release_buffers (struct ivtv *itv) 113962306a36Sopenharmony_ci{ 114062306a36Sopenharmony_ci struct osd_info *oi = itv->osd_info; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci /* Release cmap */ 114362306a36Sopenharmony_ci if (oi->ivtvfb_info.cmap.len) 114462306a36Sopenharmony_ci fb_dealloc_cmap(&oi->ivtvfb_info.cmap); 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci /* Release pseudo palette */ 114762306a36Sopenharmony_ci kfree(oi->ivtvfb_info.pseudo_palette); 114862306a36Sopenharmony_ci arch_phys_wc_del(oi->wc_cookie); 114962306a36Sopenharmony_ci kfree(oi); 115062306a36Sopenharmony_ci itv->osd_info = NULL; 115162306a36Sopenharmony_ci} 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci/* Initialize the specified card */ 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_cistatic int ivtvfb_init_card(struct ivtv *itv) 115662306a36Sopenharmony_ci{ 115762306a36Sopenharmony_ci int rc; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci#if defined(CONFIG_X86_64) && !defined(CONFIG_UML) 116062306a36Sopenharmony_ci if (pat_enabled()) { 116162306a36Sopenharmony_ci if (ivtvfb_force_pat) { 116262306a36Sopenharmony_ci pr_info("PAT is enabled. Write-combined framebuffer caching will be disabled.\n"); 116362306a36Sopenharmony_ci pr_info("To enable caching, boot with nopat kernel parameter\n"); 116462306a36Sopenharmony_ci } else { 116562306a36Sopenharmony_ci pr_warn("ivtvfb needs PAT disabled for write-combined framebuffer caching.\n"); 116662306a36Sopenharmony_ci pr_warn("Boot with nopat kernel parameter to use caching, or use the\n"); 116762306a36Sopenharmony_ci pr_warn("force_pat module parameter to run with caching disabled\n"); 116862306a36Sopenharmony_ci return -ENODEV; 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci#endif 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci if (itv->osd_info) { 117462306a36Sopenharmony_ci IVTVFB_ERR("Card %d already initialised\n", ivtvfb_card_id); 117562306a36Sopenharmony_ci return -EBUSY; 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci itv->osd_info = kzalloc(sizeof(struct osd_info), 117962306a36Sopenharmony_ci GFP_KERNEL|__GFP_NOWARN); 118062306a36Sopenharmony_ci if (itv->osd_info == NULL) { 118162306a36Sopenharmony_ci IVTVFB_ERR("Failed to allocate memory for osd_info\n"); 118262306a36Sopenharmony_ci return -ENOMEM; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci /* Find & setup the OSD buffer */ 118662306a36Sopenharmony_ci rc = ivtvfb_init_io(itv); 118762306a36Sopenharmony_ci if (rc) { 118862306a36Sopenharmony_ci ivtvfb_release_buffers(itv); 118962306a36Sopenharmony_ci return rc; 119062306a36Sopenharmony_ci } 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci /* Set the startup video mode information */ 119362306a36Sopenharmony_ci if ((rc = ivtvfb_init_vidmode(itv))) { 119462306a36Sopenharmony_ci ivtvfb_release_buffers(itv); 119562306a36Sopenharmony_ci return rc; 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci /* Register the framebuffer */ 119962306a36Sopenharmony_ci if (register_framebuffer(&itv->osd_info->ivtvfb_info) < 0) { 120062306a36Sopenharmony_ci ivtvfb_release_buffers(itv); 120162306a36Sopenharmony_ci return -EINVAL; 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci itv->osd_video_pbase = itv->osd_info->video_pbase; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci /* Set the card to the requested mode */ 120762306a36Sopenharmony_ci ivtvfb_set_par(&itv->osd_info->ivtvfb_info); 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci /* Set color 0 to black */ 121062306a36Sopenharmony_ci write_reg(0, 0x02a30); 121162306a36Sopenharmony_ci write_reg(0, 0x02a34); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci /* Enable the osd */ 121462306a36Sopenharmony_ci ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci /* Enable restart */ 121762306a36Sopenharmony_ci itv->ivtvfb_restore = ivtvfb_restore; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci /* Allocate DMA */ 122062306a36Sopenharmony_ci ivtv_udma_alloc(itv); 122162306a36Sopenharmony_ci itv->streams[IVTV_DEC_STREAM_TYPE_YUV].vdev.device_caps |= 122262306a36Sopenharmony_ci V4L2_CAP_VIDEO_OUTPUT_OVERLAY; 122362306a36Sopenharmony_ci itv->streams[IVTV_DEC_STREAM_TYPE_MPG].vdev.device_caps |= 122462306a36Sopenharmony_ci V4L2_CAP_VIDEO_OUTPUT_OVERLAY; 122562306a36Sopenharmony_ci itv->v4l2_cap |= V4L2_CAP_VIDEO_OUTPUT_OVERLAY; 122662306a36Sopenharmony_ci return 0; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_cistatic int __init ivtvfb_callback_init(struct device *dev, void *p) 123162306a36Sopenharmony_ci{ 123262306a36Sopenharmony_ci struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); 123362306a36Sopenharmony_ci struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { 123662306a36Sopenharmony_ci if (ivtvfb_init_card(itv) == 0) { 123762306a36Sopenharmony_ci IVTVFB_INFO("Framebuffer registered on %s\n", 123862306a36Sopenharmony_ci itv->v4l2_dev.name); 123962306a36Sopenharmony_ci (*(int *)p)++; 124062306a36Sopenharmony_ci } 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci return 0; 124362306a36Sopenharmony_ci} 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_cistatic int ivtvfb_callback_cleanup(struct device *dev, void *p) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); 124862306a36Sopenharmony_ci struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev); 124962306a36Sopenharmony_ci struct osd_info *oi = itv->osd_info; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { 125262306a36Sopenharmony_ci itv->streams[IVTV_DEC_STREAM_TYPE_YUV].vdev.device_caps &= 125362306a36Sopenharmony_ci ~V4L2_CAP_VIDEO_OUTPUT_OVERLAY; 125462306a36Sopenharmony_ci itv->streams[IVTV_DEC_STREAM_TYPE_MPG].vdev.device_caps &= 125562306a36Sopenharmony_ci ~V4L2_CAP_VIDEO_OUTPUT_OVERLAY; 125662306a36Sopenharmony_ci itv->v4l2_cap &= ~V4L2_CAP_VIDEO_OUTPUT_OVERLAY; 125762306a36Sopenharmony_ci unregister_framebuffer(&itv->osd_info->ivtvfb_info); 125862306a36Sopenharmony_ci IVTVFB_INFO("Unregister framebuffer %d\n", itv->instance); 125962306a36Sopenharmony_ci itv->ivtvfb_restore = NULL; 126062306a36Sopenharmony_ci ivtvfb_blank(FB_BLANK_VSYNC_SUSPEND, &oi->ivtvfb_info); 126162306a36Sopenharmony_ci ivtvfb_release_buffers(itv); 126262306a36Sopenharmony_ci itv->osd_video_pbase = 0; 126362306a36Sopenharmony_ci } 126462306a36Sopenharmony_ci return 0; 126562306a36Sopenharmony_ci} 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_cistatic int __init ivtvfb_init(void) 126862306a36Sopenharmony_ci{ 126962306a36Sopenharmony_ci struct device_driver *drv; 127062306a36Sopenharmony_ci int registered = 0; 127162306a36Sopenharmony_ci int err; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) { 127562306a36Sopenharmony_ci pr_err("ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n", 127662306a36Sopenharmony_ci IVTV_MAX_CARDS - 1); 127762306a36Sopenharmony_ci return -EINVAL; 127862306a36Sopenharmony_ci } 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci drv = driver_find("ivtv", &pci_bus_type); 128162306a36Sopenharmony_ci err = driver_for_each_device(drv, NULL, ®istered, ivtvfb_callback_init); 128262306a36Sopenharmony_ci (void)err; /* suppress compiler warning */ 128362306a36Sopenharmony_ci if (!registered) { 128462306a36Sopenharmony_ci pr_err("no cards found\n"); 128562306a36Sopenharmony_ci return -ENODEV; 128662306a36Sopenharmony_ci } 128762306a36Sopenharmony_ci return 0; 128862306a36Sopenharmony_ci} 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_cistatic void ivtvfb_cleanup(void) 129162306a36Sopenharmony_ci{ 129262306a36Sopenharmony_ci struct device_driver *drv; 129362306a36Sopenharmony_ci int err; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci pr_info("Unloading framebuffer module\n"); 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci drv = driver_find("ivtv", &pci_bus_type); 129862306a36Sopenharmony_ci err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup); 129962306a36Sopenharmony_ci (void)err; /* suppress compiler warning */ 130062306a36Sopenharmony_ci} 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_cimodule_init(ivtvfb_init); 130362306a36Sopenharmony_cimodule_exit(ivtvfb_cleanup); 1304