18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci On Screen Display cx23415 Framebuffer driver 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci This module presents the cx23415 OSD (onscreen display) framebuffer memory 68c2ecf20Sopenharmony_ci as a standard Linux /dev/fb style framebuffer device. The framebuffer has 78c2ecf20Sopenharmony_ci support for 8, 16 & 32 bpp packed pixel formats with alpha channel. In 16bpp 88c2ecf20Sopenharmony_ci mode, there is a choice of a three color depths (12, 15 or 16 bits), but no 98c2ecf20Sopenharmony_ci local alpha. The colorspace is selectable between rgb & yuv. 108c2ecf20Sopenharmony_ci Depending on the TV standard configured in the ivtv module at load time, 118c2ecf20Sopenharmony_ci the initial resolution is either 640x400 (NTSC) or 640x480 (PAL) at 8bpp. 128c2ecf20Sopenharmony_ci Video timings are locked to ensure a vertical refresh rate of 50Hz (PAL) 138c2ecf20Sopenharmony_ci or 59.94 (NTSC) 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci Copyright (c) 2003 Matt T. Yourst <yourst@yourst.com> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci Derived from drivers/video/vesafb.c 188c2ecf20Sopenharmony_ci Portions (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci 2.6 kernel port: 218c2ecf20Sopenharmony_ci Copyright (C) 2004 Matthias Badaire 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci Copyright (C) 2004 Chris Kennedy <c@groovy.org> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci Copyright (C) 2006 Ian Armstrong <ian@iarmst.demon.co.uk> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "ivtv-driver.h" 308c2ecf20Sopenharmony_ci#include "ivtv-cards.h" 318c2ecf20Sopenharmony_ci#include "ivtv-i2c.h" 328c2ecf20Sopenharmony_ci#include "ivtv-udma.h" 338c2ecf20Sopenharmony_ci#include "ivtv-mailbox.h" 348c2ecf20Sopenharmony_ci#include "ivtv-firmware.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include <linux/fb.h> 378c2ecf20Sopenharmony_ci#include <linux/ivtvfb.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_64 408c2ecf20Sopenharmony_ci#include <asm/memtype.h> 418c2ecf20Sopenharmony_ci#endif 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* card parameters */ 448c2ecf20Sopenharmony_cistatic int ivtvfb_card_id = -1; 458c2ecf20Sopenharmony_cistatic int ivtvfb_debug = 0; 468c2ecf20Sopenharmony_cistatic bool ivtvfb_force_pat = IS_ENABLED(CONFIG_VIDEO_FB_IVTV_FORCE_PAT); 478c2ecf20Sopenharmony_cistatic bool osd_laced; 488c2ecf20Sopenharmony_cistatic int osd_depth; 498c2ecf20Sopenharmony_cistatic int osd_upper; 508c2ecf20Sopenharmony_cistatic int osd_left; 518c2ecf20Sopenharmony_cistatic int osd_yres; 528c2ecf20Sopenharmony_cistatic int osd_xres; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cimodule_param(ivtvfb_card_id, int, 0444); 558c2ecf20Sopenharmony_cimodule_param_named(debug,ivtvfb_debug, int, 0644); 568c2ecf20Sopenharmony_cimodule_param_named(force_pat, ivtvfb_force_pat, bool, 0644); 578c2ecf20Sopenharmony_cimodule_param(osd_laced, bool, 0444); 588c2ecf20Sopenharmony_cimodule_param(osd_depth, int, 0444); 598c2ecf20Sopenharmony_cimodule_param(osd_upper, int, 0444); 608c2ecf20Sopenharmony_cimodule_param(osd_left, int, 0444); 618c2ecf20Sopenharmony_cimodule_param(osd_yres, int, 0444); 628c2ecf20Sopenharmony_cimodule_param(osd_xres, int, 0444); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ivtvfb_card_id, 658c2ecf20Sopenharmony_ci "Only use framebuffer of the specified ivtv card (0-31)\n" 668c2ecf20Sopenharmony_ci "\t\t\tdefault -1: initialize all available framebuffers"); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, 698c2ecf20Sopenharmony_ci "Debug level (bitmask). Default: errors only\n" 708c2ecf20Sopenharmony_ci "\t\t\t(debug = 3 gives full debugging)"); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_pat, 738c2ecf20Sopenharmony_ci "Force initialization on x86 PAT-enabled systems (bool).\n"); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* Why upper, left, xres, yres, depth, laced ? To match terminology used 768c2ecf20Sopenharmony_ci by fbset. 778c2ecf20Sopenharmony_ci Why start at 1 for left & upper coordinate ? Because X doesn't allow 0 */ 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ciMODULE_PARM_DESC(osd_laced, 808c2ecf20Sopenharmony_ci "Interlaced mode\n" 818c2ecf20Sopenharmony_ci "\t\t\t0=off\n" 828c2ecf20Sopenharmony_ci "\t\t\t1=on\n" 838c2ecf20Sopenharmony_ci "\t\t\tdefault off"); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ciMODULE_PARM_DESC(osd_depth, 868c2ecf20Sopenharmony_ci "Bits per pixel - 8, 16, 32\n" 878c2ecf20Sopenharmony_ci "\t\t\tdefault 8"); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ciMODULE_PARM_DESC(osd_upper, 908c2ecf20Sopenharmony_ci "Vertical start position\n" 918c2ecf20Sopenharmony_ci "\t\t\tdefault 0 (Centered)"); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ciMODULE_PARM_DESC(osd_left, 948c2ecf20Sopenharmony_ci "Horizontal start position\n" 958c2ecf20Sopenharmony_ci "\t\t\tdefault 0 (Centered)"); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ciMODULE_PARM_DESC(osd_yres, 988c2ecf20Sopenharmony_ci "Display height\n" 998c2ecf20Sopenharmony_ci "\t\t\tdefault 480 (PAL)\n" 1008c2ecf20Sopenharmony_ci "\t\t\t 400 (NTSC)"); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ciMODULE_PARM_DESC(osd_xres, 1038c2ecf20Sopenharmony_ci "Display width\n" 1048c2ecf20Sopenharmony_ci "\t\t\tdefault 640"); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil, John Harvey, Ian Armstrong"); 1078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- */ 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#define IVTVFB_DBGFLG_WARN (1 << 0) 1128c2ecf20Sopenharmony_ci#define IVTVFB_DBGFLG_INFO (1 << 1) 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci#define IVTVFB_DEBUG(x, type, fmt, args...) \ 1158c2ecf20Sopenharmony_ci do { \ 1168c2ecf20Sopenharmony_ci if ((x) & ivtvfb_debug) \ 1178c2ecf20Sopenharmony_ci printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->instance , ## args); \ 1188c2ecf20Sopenharmony_ci } while (0) 1198c2ecf20Sopenharmony_ci#define IVTVFB_DEBUG_WARN(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_WARN, "warning", fmt , ## args) 1208c2ecf20Sopenharmony_ci#define IVTVFB_DEBUG_INFO(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_INFO, "info", fmt , ## args) 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* Standard kernel messages */ 1238c2ecf20Sopenharmony_ci#define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->instance , ## args) 1248c2ecf20Sopenharmony_ci#define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->instance , ## args) 1258c2ecf20Sopenharmony_ci#define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->instance , ## args) 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- */ 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci#define IVTV_OSD_MAX_WIDTH 720 1308c2ecf20Sopenharmony_ci#define IVTV_OSD_MAX_HEIGHT 576 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci#define IVTV_OSD_BPP_8 0x00 1338c2ecf20Sopenharmony_ci#define IVTV_OSD_BPP_16_444 0x03 1348c2ecf20Sopenharmony_ci#define IVTV_OSD_BPP_16_555 0x02 1358c2ecf20Sopenharmony_ci#define IVTV_OSD_BPP_16_565 0x01 1368c2ecf20Sopenharmony_ci#define IVTV_OSD_BPP_32 0x04 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistruct osd_info { 1398c2ecf20Sopenharmony_ci /* Physical base address */ 1408c2ecf20Sopenharmony_ci unsigned long video_pbase; 1418c2ecf20Sopenharmony_ci /* Relative base address (relative to start of decoder memory) */ 1428c2ecf20Sopenharmony_ci u32 video_rbase; 1438c2ecf20Sopenharmony_ci /* Mapped base address */ 1448c2ecf20Sopenharmony_ci volatile char __iomem *video_vbase; 1458c2ecf20Sopenharmony_ci /* Buffer size */ 1468c2ecf20Sopenharmony_ci u32 video_buffer_size; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* video_base rounded down as required by hardware MTRRs */ 1498c2ecf20Sopenharmony_ci unsigned long fb_start_aligned_physaddr; 1508c2ecf20Sopenharmony_ci /* video_base rounded up as required by hardware MTRRs */ 1518c2ecf20Sopenharmony_ci unsigned long fb_end_aligned_physaddr; 1528c2ecf20Sopenharmony_ci int wc_cookie; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* Store the buffer offset */ 1558c2ecf20Sopenharmony_ci int set_osd_coords_x; 1568c2ecf20Sopenharmony_ci int set_osd_coords_y; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* Current dimensions (NOT VISIBLE SIZE!) */ 1598c2ecf20Sopenharmony_ci int display_width; 1608c2ecf20Sopenharmony_ci int display_height; 1618c2ecf20Sopenharmony_ci int display_byte_stride; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* Current bits per pixel */ 1648c2ecf20Sopenharmony_ci int bits_per_pixel; 1658c2ecf20Sopenharmony_ci int bytes_per_pixel; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* Frame buffer stuff */ 1688c2ecf20Sopenharmony_ci struct fb_info ivtvfb_info; 1698c2ecf20Sopenharmony_ci struct fb_var_screeninfo ivtvfb_defined; 1708c2ecf20Sopenharmony_ci struct fb_fix_screeninfo ivtvfb_fix; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* Used for a warm start */ 1738c2ecf20Sopenharmony_ci struct fb_var_screeninfo fbvar_cur; 1748c2ecf20Sopenharmony_ci int blank_cur; 1758c2ecf20Sopenharmony_ci u32 palette_cur[256]; 1768c2ecf20Sopenharmony_ci u32 pan_cur; 1778c2ecf20Sopenharmony_ci}; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistruct ivtv_osd_coords { 1808c2ecf20Sopenharmony_ci unsigned long offset; 1818c2ecf20Sopenharmony_ci unsigned long max_offset; 1828c2ecf20Sopenharmony_ci int pixel_stride; 1838c2ecf20Sopenharmony_ci int lines; 1848c2ecf20Sopenharmony_ci int x; 1858c2ecf20Sopenharmony_ci int y; 1868c2ecf20Sopenharmony_ci}; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- */ 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci/* ivtv API calls for framebuffer related support */ 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int ivtvfb_get_framebuffer(struct ivtv *itv, u32 *fbbase, 1938c2ecf20Sopenharmony_ci u32 *fblength) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci u32 data[CX2341X_MBOX_MAX_DATA]; 1968c2ecf20Sopenharmony_ci int rc; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci ivtv_firmware_check(itv, "ivtvfb_get_framebuffer"); 1998c2ecf20Sopenharmony_ci rc = ivtv_vapi_result(itv, data, CX2341X_OSD_GET_FRAMEBUFFER, 0); 2008c2ecf20Sopenharmony_ci *fbbase = data[0]; 2018c2ecf20Sopenharmony_ci *fblength = data[1]; 2028c2ecf20Sopenharmony_ci return rc; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int ivtvfb_get_osd_coords(struct ivtv *itv, 2068c2ecf20Sopenharmony_ci struct ivtv_osd_coords *osd) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct osd_info *oi = itv->osd_info; 2098c2ecf20Sopenharmony_ci u32 data[CX2341X_MBOX_MAX_DATA]; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci ivtv_vapi_result(itv, data, CX2341X_OSD_GET_OSD_COORDS, 0); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci osd->offset = data[0] - oi->video_rbase; 2148c2ecf20Sopenharmony_ci osd->max_offset = oi->display_width * oi->display_height * 4; 2158c2ecf20Sopenharmony_ci osd->pixel_stride = data[1]; 2168c2ecf20Sopenharmony_ci osd->lines = data[2]; 2178c2ecf20Sopenharmony_ci osd->x = data[3]; 2188c2ecf20Sopenharmony_ci osd->y = data[4]; 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic int ivtvfb_set_osd_coords(struct ivtv *itv, const struct ivtv_osd_coords *osd) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct osd_info *oi = itv->osd_info; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci oi->display_width = osd->pixel_stride; 2278c2ecf20Sopenharmony_ci oi->display_byte_stride = osd->pixel_stride * oi->bytes_per_pixel; 2288c2ecf20Sopenharmony_ci oi->set_osd_coords_x += osd->x; 2298c2ecf20Sopenharmony_ci oi->set_osd_coords_y = osd->y; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return ivtv_vapi(itv, CX2341X_OSD_SET_OSD_COORDS, 5, 2328c2ecf20Sopenharmony_ci osd->offset + oi->video_rbase, 2338c2ecf20Sopenharmony_ci osd->pixel_stride, 2348c2ecf20Sopenharmony_ci osd->lines, osd->x, osd->y); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int ivtvfb_set_display_window(struct ivtv *itv, struct v4l2_rect *ivtv_window) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci int osd_height_limit = itv->is_out_50hz ? 576 : 480; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* Only fail if resolution too high, otherwise fudge the start coords. */ 2428c2ecf20Sopenharmony_ci if ((ivtv_window->height > osd_height_limit) || (ivtv_window->width > IVTV_OSD_MAX_WIDTH)) 2438c2ecf20Sopenharmony_ci return -EINVAL; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* Ensure we don't exceed display limits */ 2468c2ecf20Sopenharmony_ci if (ivtv_window->top + ivtv_window->height > osd_height_limit) { 2478c2ecf20Sopenharmony_ci IVTVFB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid height setting (%d, %d)\n", 2488c2ecf20Sopenharmony_ci ivtv_window->top, ivtv_window->height); 2498c2ecf20Sopenharmony_ci ivtv_window->top = osd_height_limit - ivtv_window->height; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (ivtv_window->left + ivtv_window->width > IVTV_OSD_MAX_WIDTH) { 2538c2ecf20Sopenharmony_ci IVTVFB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid width setting (%d, %d)\n", 2548c2ecf20Sopenharmony_ci ivtv_window->left, ivtv_window->width); 2558c2ecf20Sopenharmony_ci ivtv_window->left = IVTV_OSD_MAX_WIDTH - ivtv_window->width; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* Set the OSD origin */ 2598c2ecf20Sopenharmony_ci write_reg((ivtv_window->top << 16) | ivtv_window->left, 0x02a04); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* How much to display */ 2628c2ecf20Sopenharmony_ci write_reg(((ivtv_window->top+ivtv_window->height) << 16) | (ivtv_window->left+ivtv_window->width), 0x02a08); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* Pass this info back the yuv handler */ 2658c2ecf20Sopenharmony_ci itv->yuv_info.osd_vis_w = ivtv_window->width; 2668c2ecf20Sopenharmony_ci itv->yuv_info.osd_vis_h = ivtv_window->height; 2678c2ecf20Sopenharmony_ci itv->yuv_info.osd_x_offset = ivtv_window->left; 2688c2ecf20Sopenharmony_ci itv->yuv_info.osd_y_offset = ivtv_window->top; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic int ivtvfb_prep_dec_dma_to_device(struct ivtv *itv, 2748c2ecf20Sopenharmony_ci unsigned long ivtv_dest_addr, void __user *userbuf, 2758c2ecf20Sopenharmony_ci int size_in_bytes) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci DEFINE_WAIT(wait); 2788c2ecf20Sopenharmony_ci int got_sig = 0; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci mutex_lock(&itv->udma.lock); 2818c2ecf20Sopenharmony_ci /* Map User DMA */ 2828c2ecf20Sopenharmony_ci if (ivtv_udma_setup(itv, ivtv_dest_addr, userbuf, size_in_bytes) <= 0) { 2838c2ecf20Sopenharmony_ci mutex_unlock(&itv->udma.lock); 2848c2ecf20Sopenharmony_ci IVTVFB_WARN("ivtvfb_prep_dec_dma_to_device, Error with pin_user_pages: %d bytes, %d pages returned\n", 2858c2ecf20Sopenharmony_ci size_in_bytes, itv->udma.page_count); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* pin_user_pages must have failed completely */ 2888c2ecf20Sopenharmony_ci return -EIO; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci IVTVFB_DEBUG_INFO("ivtvfb_prep_dec_dma_to_device, %d bytes, %d pages\n", 2928c2ecf20Sopenharmony_ci size_in_bytes, itv->udma.page_count); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci ivtv_udma_prepare(itv); 2958c2ecf20Sopenharmony_ci prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); 2968c2ecf20Sopenharmony_ci /* if no UDMA is pending and no UDMA is in progress, then the DMA 2978c2ecf20Sopenharmony_ci is finished */ 2988c2ecf20Sopenharmony_ci while (test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags) || 2998c2ecf20Sopenharmony_ci test_bit(IVTV_F_I_UDMA, &itv->i_flags)) { 3008c2ecf20Sopenharmony_ci /* don't interrupt if the DMA is in progress but break off 3018c2ecf20Sopenharmony_ci a still pending DMA. */ 3028c2ecf20Sopenharmony_ci got_sig = signal_pending(current); 3038c2ecf20Sopenharmony_ci if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) 3048c2ecf20Sopenharmony_ci break; 3058c2ecf20Sopenharmony_ci got_sig = 0; 3068c2ecf20Sopenharmony_ci schedule(); 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci finish_wait(&itv->dma_waitq, &wait); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* Unmap Last DMA Xfer */ 3118c2ecf20Sopenharmony_ci ivtv_udma_unmap(itv); 3128c2ecf20Sopenharmony_ci mutex_unlock(&itv->udma.lock); 3138c2ecf20Sopenharmony_ci if (got_sig) { 3148c2ecf20Sopenharmony_ci IVTV_DEBUG_INFO("User stopped OSD\n"); 3158c2ecf20Sopenharmony_ci return -EINTR; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return 0; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source, 3228c2ecf20Sopenharmony_ci unsigned long dest_offset, int count) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci DEFINE_WAIT(wait); 3258c2ecf20Sopenharmony_ci struct osd_info *oi = itv->osd_info; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* Nothing to do */ 3288c2ecf20Sopenharmony_ci if (count == 0) { 3298c2ecf20Sopenharmony_ci IVTVFB_DEBUG_WARN("ivtvfb_prep_frame: Nothing to do. count = 0\n"); 3308c2ecf20Sopenharmony_ci return -EINVAL; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci /* Check Total FB Size */ 3348c2ecf20Sopenharmony_ci if ((dest_offset + count) > oi->video_buffer_size) { 3358c2ecf20Sopenharmony_ci IVTVFB_WARN("ivtvfb_prep_frame: Overflowing the framebuffer %ld, only %d available\n", 3368c2ecf20Sopenharmony_ci dest_offset + count, oi->video_buffer_size); 3378c2ecf20Sopenharmony_ci return -E2BIG; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* Not fatal, but will have undesirable results */ 3418c2ecf20Sopenharmony_ci if ((unsigned long)source & 3) 3428c2ecf20Sopenharmony_ci IVTVFB_WARN("ivtvfb_prep_frame: Source address not 32 bit aligned (%p)\n", 3438c2ecf20Sopenharmony_ci source); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (dest_offset & 3) 3468c2ecf20Sopenharmony_ci IVTVFB_WARN("ivtvfb_prep_frame: Dest offset not 32 bit aligned (%ld)\n", dest_offset); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (count & 3) 3498c2ecf20Sopenharmony_ci IVTVFB_WARN("ivtvfb_prep_frame: Count not a multiple of 4 (%d)\n", count); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* Check Source */ 3528c2ecf20Sopenharmony_ci if (!access_ok(source + dest_offset, count)) { 3538c2ecf20Sopenharmony_ci IVTVFB_WARN("Invalid userspace pointer %p\n", source); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci IVTVFB_DEBUG_WARN("access_ok() failed for offset 0x%08lx source %p count %d\n", 3568c2ecf20Sopenharmony_ci dest_offset, source, count); 3578c2ecf20Sopenharmony_ci return -EINVAL; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* OSD Address to send DMA to */ 3618c2ecf20Sopenharmony_ci dest_offset += IVTV_DECODER_OFFSET + oi->video_rbase; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* Fill Buffers */ 3648c2ecf20Sopenharmony_ci return ivtvfb_prep_dec_dma_to_device(itv, dest_offset, source, count); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic ssize_t ivtvfb_write(struct fb_info *info, const char __user *buf, 3688c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci unsigned long p = *ppos; 3718c2ecf20Sopenharmony_ci void *dst; 3728c2ecf20Sopenharmony_ci int err = 0; 3738c2ecf20Sopenharmony_ci int dma_err; 3748c2ecf20Sopenharmony_ci unsigned long total_size; 3758c2ecf20Sopenharmony_ci struct ivtv *itv = (struct ivtv *) info->par; 3768c2ecf20Sopenharmony_ci unsigned long dma_offset = 3778c2ecf20Sopenharmony_ci IVTV_DECODER_OFFSET + itv->osd_info->video_rbase; 3788c2ecf20Sopenharmony_ci unsigned long dma_size; 3798c2ecf20Sopenharmony_ci u16 lead = 0, tail = 0; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (info->state != FBINFO_STATE_RUNNING) 3828c2ecf20Sopenharmony_ci return -EPERM; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci total_size = info->screen_size; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (total_size == 0) 3878c2ecf20Sopenharmony_ci total_size = info->fix.smem_len; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (p > total_size) 3908c2ecf20Sopenharmony_ci return -EFBIG; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (count > total_size) { 3938c2ecf20Sopenharmony_ci err = -EFBIG; 3948c2ecf20Sopenharmony_ci count = total_size; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (count + p > total_size) { 3988c2ecf20Sopenharmony_ci if (!err) 3998c2ecf20Sopenharmony_ci err = -ENOSPC; 4008c2ecf20Sopenharmony_ci count = total_size - p; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci dst = (void __force *) (info->screen_base + p); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (info->fbops->fb_sync) 4068c2ecf20Sopenharmony_ci info->fbops->fb_sync(info); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* If transfer size > threshold and both src/dst 4098c2ecf20Sopenharmony_ci addresses are aligned, use DMA */ 4108c2ecf20Sopenharmony_ci if (count >= 4096 && 4118c2ecf20Sopenharmony_ci ((unsigned long)buf & 3) == ((unsigned long)dst & 3)) { 4128c2ecf20Sopenharmony_ci /* Odd address = can't DMA. Align */ 4138c2ecf20Sopenharmony_ci if ((unsigned long)dst & 3) { 4148c2ecf20Sopenharmony_ci lead = 4 - ((unsigned long)dst & 3); 4158c2ecf20Sopenharmony_ci if (copy_from_user(dst, buf, lead)) 4168c2ecf20Sopenharmony_ci return -EFAULT; 4178c2ecf20Sopenharmony_ci buf += lead; 4188c2ecf20Sopenharmony_ci dst += lead; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci /* DMA resolution is 32 bits */ 4218c2ecf20Sopenharmony_ci if ((count - lead) & 3) 4228c2ecf20Sopenharmony_ci tail = (count - lead) & 3; 4238c2ecf20Sopenharmony_ci /* DMA the data */ 4248c2ecf20Sopenharmony_ci dma_size = count - lead - tail; 4258c2ecf20Sopenharmony_ci dma_err = ivtvfb_prep_dec_dma_to_device(itv, 4268c2ecf20Sopenharmony_ci p + lead + dma_offset, (void __user *)buf, dma_size); 4278c2ecf20Sopenharmony_ci if (dma_err) 4288c2ecf20Sopenharmony_ci return dma_err; 4298c2ecf20Sopenharmony_ci dst += dma_size; 4308c2ecf20Sopenharmony_ci buf += dma_size; 4318c2ecf20Sopenharmony_ci /* Copy any leftover data */ 4328c2ecf20Sopenharmony_ci if (tail && copy_from_user(dst, buf, tail)) 4338c2ecf20Sopenharmony_ci return -EFAULT; 4348c2ecf20Sopenharmony_ci } else if (copy_from_user(dst, buf, count)) { 4358c2ecf20Sopenharmony_ci return -EFAULT; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (!err) 4398c2ecf20Sopenharmony_ci *ppos += count; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci return (err) ? err : count; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci DEFINE_WAIT(wait); 4478c2ecf20Sopenharmony_ci struct ivtv *itv = (struct ivtv *)info->par; 4488c2ecf20Sopenharmony_ci int rc = 0; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci switch (cmd) { 4518c2ecf20Sopenharmony_ci case FBIOGET_VBLANK: { 4528c2ecf20Sopenharmony_ci struct fb_vblank vblank; 4538c2ecf20Sopenharmony_ci u32 trace; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci memset(&vblank, 0, sizeof(struct fb_vblank)); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT | 4588c2ecf20Sopenharmony_ci FB_VBLANK_HAVE_VSYNC; 4598c2ecf20Sopenharmony_ci trace = read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16; 4608c2ecf20Sopenharmony_ci if (itv->is_out_50hz && trace > 312) 4618c2ecf20Sopenharmony_ci trace -= 312; 4628c2ecf20Sopenharmony_ci else if (itv->is_out_60hz && trace > 262) 4638c2ecf20Sopenharmony_ci trace -= 262; 4648c2ecf20Sopenharmony_ci if (trace == 1) 4658c2ecf20Sopenharmony_ci vblank.flags |= FB_VBLANK_VSYNCING; 4668c2ecf20Sopenharmony_ci vblank.count = itv->last_vsync_field; 4678c2ecf20Sopenharmony_ci vblank.vcount = trace; 4688c2ecf20Sopenharmony_ci vblank.hcount = 0; 4698c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank))) 4708c2ecf20Sopenharmony_ci return -EFAULT; 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci case FBIO_WAITFORVSYNC: 4758c2ecf20Sopenharmony_ci prepare_to_wait(&itv->vsync_waitq, &wait, TASK_INTERRUPTIBLE); 4768c2ecf20Sopenharmony_ci if (!schedule_timeout(msecs_to_jiffies(50))) 4778c2ecf20Sopenharmony_ci rc = -ETIMEDOUT; 4788c2ecf20Sopenharmony_ci finish_wait(&itv->vsync_waitq, &wait); 4798c2ecf20Sopenharmony_ci return rc; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci case IVTVFB_IOC_DMA_FRAME: { 4828c2ecf20Sopenharmony_ci struct ivtvfb_dma_frame args; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci IVTVFB_DEBUG_INFO("IVTVFB_IOC_DMA_FRAME\n"); 4858c2ecf20Sopenharmony_ci if (copy_from_user(&args, (void __user *)arg, sizeof(args))) 4868c2ecf20Sopenharmony_ci return -EFAULT; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return ivtvfb_prep_frame(itv, cmd, args.source, args.dest_offset, args.count); 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci default: 4928c2ecf20Sopenharmony_ci IVTVFB_DEBUG_INFO("Unknown ioctl %08x\n", cmd); 4938c2ecf20Sopenharmony_ci return -EINVAL; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci return 0; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci/* Framebuffer device handling */ 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci struct osd_info *oi = itv->osd_info; 5038c2ecf20Sopenharmony_ci struct ivtv_osd_coords ivtv_osd; 5048c2ecf20Sopenharmony_ci struct v4l2_rect ivtv_window; 5058c2ecf20Sopenharmony_ci int osd_mode = -1; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci IVTVFB_DEBUG_INFO("ivtvfb_set_var\n"); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* Select color space */ 5108c2ecf20Sopenharmony_ci if (var->nonstd) /* YUV */ 5118c2ecf20Sopenharmony_ci write_reg(read_reg(0x02a00) | 0x0002000, 0x02a00); 5128c2ecf20Sopenharmony_ci else /* RGB */ 5138c2ecf20Sopenharmony_ci write_reg(read_reg(0x02a00) & ~0x0002000, 0x02a00); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* Set the color mode */ 5168c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 5178c2ecf20Sopenharmony_ci case 8: 5188c2ecf20Sopenharmony_ci osd_mode = IVTV_OSD_BPP_8; 5198c2ecf20Sopenharmony_ci break; 5208c2ecf20Sopenharmony_ci case 32: 5218c2ecf20Sopenharmony_ci osd_mode = IVTV_OSD_BPP_32; 5228c2ecf20Sopenharmony_ci break; 5238c2ecf20Sopenharmony_ci case 16: 5248c2ecf20Sopenharmony_ci switch (var->green.length) { 5258c2ecf20Sopenharmony_ci case 4: 5268c2ecf20Sopenharmony_ci osd_mode = IVTV_OSD_BPP_16_444; 5278c2ecf20Sopenharmony_ci break; 5288c2ecf20Sopenharmony_ci case 5: 5298c2ecf20Sopenharmony_ci osd_mode = IVTV_OSD_BPP_16_555; 5308c2ecf20Sopenharmony_ci break; 5318c2ecf20Sopenharmony_ci case 6: 5328c2ecf20Sopenharmony_ci osd_mode = IVTV_OSD_BPP_16_565; 5338c2ecf20Sopenharmony_ci break; 5348c2ecf20Sopenharmony_ci default: 5358c2ecf20Sopenharmony_ci IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n"); 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci break; 5388c2ecf20Sopenharmony_ci default: 5398c2ecf20Sopenharmony_ci IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n"); 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci /* Set video mode. Although rare, the display can become scrambled even 5438c2ecf20Sopenharmony_ci if we don't change mode. Always 'bounce' to osd_mode via mode 0 */ 5448c2ecf20Sopenharmony_ci if (osd_mode != -1) { 5458c2ecf20Sopenharmony_ci ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0); 5468c2ecf20Sopenharmony_ci ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, osd_mode); 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci oi->bits_per_pixel = var->bits_per_pixel; 5508c2ecf20Sopenharmony_ci oi->bytes_per_pixel = var->bits_per_pixel / 8; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* Set the flicker filter */ 5538c2ecf20Sopenharmony_ci switch (var->vmode & FB_VMODE_MASK) { 5548c2ecf20Sopenharmony_ci case FB_VMODE_NONINTERLACED: /* Filter on */ 5558c2ecf20Sopenharmony_ci ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 1); 5568c2ecf20Sopenharmony_ci break; 5578c2ecf20Sopenharmony_ci case FB_VMODE_INTERLACED: /* Filter off */ 5588c2ecf20Sopenharmony_ci ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 0); 5598c2ecf20Sopenharmony_ci break; 5608c2ecf20Sopenharmony_ci default: 5618c2ecf20Sopenharmony_ci IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid video mode\n"); 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci /* Read the current osd info */ 5658c2ecf20Sopenharmony_ci ivtvfb_get_osd_coords(itv, &ivtv_osd); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* Now set the OSD to the size we want */ 5688c2ecf20Sopenharmony_ci ivtv_osd.pixel_stride = var->xres_virtual; 5698c2ecf20Sopenharmony_ci ivtv_osd.lines = var->yres_virtual; 5708c2ecf20Sopenharmony_ci ivtv_osd.x = 0; 5718c2ecf20Sopenharmony_ci ivtv_osd.y = 0; 5728c2ecf20Sopenharmony_ci ivtvfb_set_osd_coords(itv, &ivtv_osd); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* Can't seem to find the right API combo for this. 5758c2ecf20Sopenharmony_ci Use another function which does what we need through direct register access. */ 5768c2ecf20Sopenharmony_ci ivtv_window.width = var->xres; 5778c2ecf20Sopenharmony_ci ivtv_window.height = var->yres; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* Minimum margin cannot be 0, as X won't allow such a mode */ 5808c2ecf20Sopenharmony_ci if (!var->upper_margin) 5818c2ecf20Sopenharmony_ci var->upper_margin++; 5828c2ecf20Sopenharmony_ci if (!var->left_margin) 5838c2ecf20Sopenharmony_ci var->left_margin++; 5848c2ecf20Sopenharmony_ci ivtv_window.top = var->upper_margin - 1; 5858c2ecf20Sopenharmony_ci ivtv_window.left = var->left_margin - 1; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci ivtvfb_set_display_window(itv, &ivtv_window); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* Pass screen size back to yuv handler */ 5908c2ecf20Sopenharmony_ci itv->yuv_info.osd_full_w = ivtv_osd.pixel_stride; 5918c2ecf20Sopenharmony_ci itv->yuv_info.osd_full_h = ivtv_osd.lines; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci /* Force update of yuv registers */ 5948c2ecf20Sopenharmony_ci itv->yuv_info.yuv_forced_update = 1; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci /* Keep a copy of these settings */ 5978c2ecf20Sopenharmony_ci memcpy(&oi->fbvar_cur, var, sizeof(oi->fbvar_cur)); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n", 6008c2ecf20Sopenharmony_ci var->xres, var->yres, 6018c2ecf20Sopenharmony_ci var->xres_virtual, var->yres_virtual, 6028c2ecf20Sopenharmony_ci var->bits_per_pixel); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci IVTVFB_DEBUG_INFO("Display position: %d, %d\n", 6058c2ecf20Sopenharmony_ci var->left_margin, var->upper_margin); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci IVTVFB_DEBUG_INFO("Display filter: %s\n", 6088c2ecf20Sopenharmony_ci (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off"); 6098c2ecf20Sopenharmony_ci IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB"); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci return 0; 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci struct osd_info *oi = itv->osd_info; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci IVTVFB_DEBUG_INFO("ivtvfb_get_fix\n"); 6198c2ecf20Sopenharmony_ci memset(fix, 0, sizeof(struct fb_fix_screeninfo)); 6208c2ecf20Sopenharmony_ci strscpy(fix->id, "cx23415 TV out", sizeof(fix->id)); 6218c2ecf20Sopenharmony_ci fix->smem_start = oi->video_pbase; 6228c2ecf20Sopenharmony_ci fix->smem_len = oi->video_buffer_size; 6238c2ecf20Sopenharmony_ci fix->type = FB_TYPE_PACKED_PIXELS; 6248c2ecf20Sopenharmony_ci fix->visual = (oi->bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; 6258c2ecf20Sopenharmony_ci fix->xpanstep = 1; 6268c2ecf20Sopenharmony_ci fix->ypanstep = 1; 6278c2ecf20Sopenharmony_ci fix->ywrapstep = 0; 6288c2ecf20Sopenharmony_ci fix->line_length = oi->display_byte_stride; 6298c2ecf20Sopenharmony_ci fix->accel = FB_ACCEL_NONE; 6308c2ecf20Sopenharmony_ci return 0; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci/* Check the requested display mode, returning -EINVAL if we can't 6348c2ecf20Sopenharmony_ci handle it. */ 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cistatic int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci struct osd_info *oi = itv->osd_info; 6398c2ecf20Sopenharmony_ci int osd_height_limit; 6408c2ecf20Sopenharmony_ci u32 pixclock, hlimit, vlimit; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci IVTVFB_DEBUG_INFO("ivtvfb_check_var\n"); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci /* Set base references for mode calcs. */ 6458c2ecf20Sopenharmony_ci if (itv->is_out_50hz) { 6468c2ecf20Sopenharmony_ci pixclock = 84316; 6478c2ecf20Sopenharmony_ci hlimit = 776; 6488c2ecf20Sopenharmony_ci vlimit = 591; 6498c2ecf20Sopenharmony_ci osd_height_limit = 576; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci else { 6528c2ecf20Sopenharmony_ci pixclock = 83926; 6538c2ecf20Sopenharmony_ci hlimit = 776; 6548c2ecf20Sopenharmony_ci vlimit = 495; 6558c2ecf20Sopenharmony_ci osd_height_limit = 480; 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if (var->bits_per_pixel == 8 || var->bits_per_pixel == 32) { 6598c2ecf20Sopenharmony_ci var->transp.offset = 24; 6608c2ecf20Sopenharmony_ci var->transp.length = 8; 6618c2ecf20Sopenharmony_ci var->red.offset = 16; 6628c2ecf20Sopenharmony_ci var->red.length = 8; 6638c2ecf20Sopenharmony_ci var->green.offset = 8; 6648c2ecf20Sopenharmony_ci var->green.length = 8; 6658c2ecf20Sopenharmony_ci var->blue.offset = 0; 6668c2ecf20Sopenharmony_ci var->blue.length = 8; 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci else if (var->bits_per_pixel == 16) { 6698c2ecf20Sopenharmony_ci /* To find out the true mode, check green length */ 6708c2ecf20Sopenharmony_ci switch (var->green.length) { 6718c2ecf20Sopenharmony_ci case 4: 6728c2ecf20Sopenharmony_ci var->red.offset = 8; 6738c2ecf20Sopenharmony_ci var->red.length = 4; 6748c2ecf20Sopenharmony_ci var->green.offset = 4; 6758c2ecf20Sopenharmony_ci var->green.length = 4; 6768c2ecf20Sopenharmony_ci var->blue.offset = 0; 6778c2ecf20Sopenharmony_ci var->blue.length = 4; 6788c2ecf20Sopenharmony_ci var->transp.offset = 12; 6798c2ecf20Sopenharmony_ci var->transp.length = 1; 6808c2ecf20Sopenharmony_ci break; 6818c2ecf20Sopenharmony_ci case 5: 6828c2ecf20Sopenharmony_ci var->red.offset = 10; 6838c2ecf20Sopenharmony_ci var->red.length = 5; 6848c2ecf20Sopenharmony_ci var->green.offset = 5; 6858c2ecf20Sopenharmony_ci var->green.length = 5; 6868c2ecf20Sopenharmony_ci var->blue.offset = 0; 6878c2ecf20Sopenharmony_ci var->blue.length = 5; 6888c2ecf20Sopenharmony_ci var->transp.offset = 15; 6898c2ecf20Sopenharmony_ci var->transp.length = 1; 6908c2ecf20Sopenharmony_ci break; 6918c2ecf20Sopenharmony_ci default: 6928c2ecf20Sopenharmony_ci var->red.offset = 11; 6938c2ecf20Sopenharmony_ci var->red.length = 5; 6948c2ecf20Sopenharmony_ci var->green.offset = 5; 6958c2ecf20Sopenharmony_ci var->green.length = 6; 6968c2ecf20Sopenharmony_ci var->blue.offset = 0; 6978c2ecf20Sopenharmony_ci var->blue.length = 5; 6988c2ecf20Sopenharmony_ci var->transp.offset = 0; 6998c2ecf20Sopenharmony_ci var->transp.length = 0; 7008c2ecf20Sopenharmony_ci break; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci else { 7048c2ecf20Sopenharmony_ci IVTVFB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel); 7058c2ecf20Sopenharmony_ci return -EINVAL; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci /* Check the resolution */ 7098c2ecf20Sopenharmony_ci if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit) { 7108c2ecf20Sopenharmony_ci IVTVFB_DEBUG_WARN("Invalid resolution: %dx%d\n", 7118c2ecf20Sopenharmony_ci var->xres, var->yres); 7128c2ecf20Sopenharmony_ci return -EINVAL; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci /* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */ 7168c2ecf20Sopenharmony_ci if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) || 7178c2ecf20Sopenharmony_ci var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8) > oi->video_buffer_size || 7188c2ecf20Sopenharmony_ci var->xres_virtual < var->xres || 7198c2ecf20Sopenharmony_ci var->yres_virtual < var->yres) { 7208c2ecf20Sopenharmony_ci IVTVFB_DEBUG_WARN("Invalid virtual resolution: %dx%d\n", 7218c2ecf20Sopenharmony_ci var->xres_virtual, var->yres_virtual); 7228c2ecf20Sopenharmony_ci return -EINVAL; 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci /* Some extra checks if in 8 bit mode */ 7268c2ecf20Sopenharmony_ci if (var->bits_per_pixel == 8) { 7278c2ecf20Sopenharmony_ci /* Width must be a multiple of 4 */ 7288c2ecf20Sopenharmony_ci if (var->xres & 3) { 7298c2ecf20Sopenharmony_ci IVTVFB_DEBUG_WARN("Invalid resolution for 8bpp: %d\n", var->xres); 7308c2ecf20Sopenharmony_ci return -EINVAL; 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci if (var->xres_virtual & 3) { 7338c2ecf20Sopenharmony_ci IVTVFB_DEBUG_WARN("Invalid virtual resolution for 8bpp: %d)\n", var->xres_virtual); 7348c2ecf20Sopenharmony_ci return -EINVAL; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci else if (var->bits_per_pixel == 16) { 7388c2ecf20Sopenharmony_ci /* Width must be a multiple of 2 */ 7398c2ecf20Sopenharmony_ci if (var->xres & 1) { 7408c2ecf20Sopenharmony_ci IVTVFB_DEBUG_WARN("Invalid resolution for 16bpp: %d\n", var->xres); 7418c2ecf20Sopenharmony_ci return -EINVAL; 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci if (var->xres_virtual & 1) { 7448c2ecf20Sopenharmony_ci IVTVFB_DEBUG_WARN("Invalid virtual resolution for 16bpp: %d)\n", var->xres_virtual); 7458c2ecf20Sopenharmony_ci return -EINVAL; 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci /* Now check the offsets */ 7508c2ecf20Sopenharmony_ci if (var->xoffset >= var->xres_virtual || var->yoffset >= var->yres_virtual) { 7518c2ecf20Sopenharmony_ci IVTVFB_DEBUG_WARN("Invalid offset: %d (%d) %d (%d)\n", 7528c2ecf20Sopenharmony_ci var->xoffset, var->xres_virtual, var->yoffset, var->yres_virtual); 7538c2ecf20Sopenharmony_ci return -EINVAL; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* Check pixel format */ 7578c2ecf20Sopenharmony_ci if (var->nonstd > 1) { 7588c2ecf20Sopenharmony_ci IVTVFB_DEBUG_WARN("Invalid nonstd % d\n", var->nonstd); 7598c2ecf20Sopenharmony_ci return -EINVAL; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci /* Check video mode */ 7638c2ecf20Sopenharmony_ci if (((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) && 7648c2ecf20Sopenharmony_ci ((var->vmode & FB_VMODE_MASK) != FB_VMODE_INTERLACED)) { 7658c2ecf20Sopenharmony_ci IVTVFB_DEBUG_WARN("Invalid video mode: %d\n", var->vmode & FB_VMODE_MASK); 7668c2ecf20Sopenharmony_ci return -EINVAL; 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci /* Check the left & upper margins 7708c2ecf20Sopenharmony_ci If the margins are too large, just center the screen 7718c2ecf20Sopenharmony_ci (enforcing margins causes too many problems) */ 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci if (var->left_margin + var->xres > IVTV_OSD_MAX_WIDTH + 1) 7748c2ecf20Sopenharmony_ci var->left_margin = 1 + ((IVTV_OSD_MAX_WIDTH - var->xres) / 2); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci if (var->upper_margin + var->yres > (itv->is_out_50hz ? 577 : 481)) 7778c2ecf20Sopenharmony_ci var->upper_margin = 1 + (((itv->is_out_50hz ? 576 : 480) - 7788c2ecf20Sopenharmony_ci var->yres) / 2); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci /* Maintain overall 'size' for a constant refresh rate */ 7818c2ecf20Sopenharmony_ci var->right_margin = hlimit - var->left_margin - var->xres; 7828c2ecf20Sopenharmony_ci var->lower_margin = vlimit - var->upper_margin - var->yres; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci /* Fixed sync times */ 7858c2ecf20Sopenharmony_ci var->hsync_len = 24; 7868c2ecf20Sopenharmony_ci var->vsync_len = 2; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci /* Non-interlaced / interlaced mode is used to switch the OSD filter 7898c2ecf20Sopenharmony_ci on or off. Adjust the clock timings to maintain a constant 7908c2ecf20Sopenharmony_ci vertical refresh rate. */ 7918c2ecf20Sopenharmony_ci if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED) 7928c2ecf20Sopenharmony_ci var->pixclock = pixclock / 2; 7938c2ecf20Sopenharmony_ci else 7948c2ecf20Sopenharmony_ci var->pixclock = pixclock; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci itv->osd_rect.width = var->xres; 7978c2ecf20Sopenharmony_ci itv->osd_rect.height = var->yres; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n", 8008c2ecf20Sopenharmony_ci var->xres, var->yres, 8018c2ecf20Sopenharmony_ci var->xres_virtual, var->yres_virtual, 8028c2ecf20Sopenharmony_ci var->bits_per_pixel); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci IVTVFB_DEBUG_INFO("Display position: %d, %d\n", 8058c2ecf20Sopenharmony_ci var->left_margin, var->upper_margin); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci IVTVFB_DEBUG_INFO("Display filter: %s\n", 8088c2ecf20Sopenharmony_ci (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off"); 8098c2ecf20Sopenharmony_ci IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB"); 8108c2ecf20Sopenharmony_ci return 0; 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_cistatic int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci struct ivtv *itv = (struct ivtv *) info->par; 8168c2ecf20Sopenharmony_ci IVTVFB_DEBUG_INFO("ivtvfb_check_var\n"); 8178c2ecf20Sopenharmony_ci return _ivtvfb_check_var(var, itv); 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_cistatic int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) 8218c2ecf20Sopenharmony_ci{ 8228c2ecf20Sopenharmony_ci u32 osd_pan_index; 8238c2ecf20Sopenharmony_ci struct ivtv *itv = (struct ivtv *) info->par; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci if (var->yoffset + info->var.yres > info->var.yres_virtual || 8268c2ecf20Sopenharmony_ci var->xoffset + info->var.xres > info->var.xres_virtual) 8278c2ecf20Sopenharmony_ci return -EINVAL; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci osd_pan_index = var->yoffset * info->fix.line_length 8308c2ecf20Sopenharmony_ci + var->xoffset * info->var.bits_per_pixel / 8; 8318c2ecf20Sopenharmony_ci write_reg(osd_pan_index, 0x02A0C); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci /* Pass this info back the yuv handler */ 8348c2ecf20Sopenharmony_ci itv->yuv_info.osd_x_pan = var->xoffset; 8358c2ecf20Sopenharmony_ci itv->yuv_info.osd_y_pan = var->yoffset; 8368c2ecf20Sopenharmony_ci /* Force update of yuv registers */ 8378c2ecf20Sopenharmony_ci itv->yuv_info.yuv_forced_update = 1; 8388c2ecf20Sopenharmony_ci /* Remember this value */ 8398c2ecf20Sopenharmony_ci itv->osd_info->pan_cur = osd_pan_index; 8408c2ecf20Sopenharmony_ci return 0; 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_cistatic int ivtvfb_set_par(struct fb_info *info) 8448c2ecf20Sopenharmony_ci{ 8458c2ecf20Sopenharmony_ci int rc = 0; 8468c2ecf20Sopenharmony_ci struct ivtv *itv = (struct ivtv *) info->par; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci IVTVFB_DEBUG_INFO("ivtvfb_set_par\n"); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci rc = ivtvfb_set_var(itv, &info->var); 8518c2ecf20Sopenharmony_ci ivtvfb_pan_display(&info->var, info); 8528c2ecf20Sopenharmony_ci ivtvfb_get_fix(itv, &info->fix); 8538c2ecf20Sopenharmony_ci ivtv_firmware_check(itv, "ivtvfb_set_par"); 8548c2ecf20Sopenharmony_ci return rc; 8558c2ecf20Sopenharmony_ci} 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_cistatic int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green, 8588c2ecf20Sopenharmony_ci unsigned blue, unsigned transp, 8598c2ecf20Sopenharmony_ci struct fb_info *info) 8608c2ecf20Sopenharmony_ci{ 8618c2ecf20Sopenharmony_ci u32 color, *palette; 8628c2ecf20Sopenharmony_ci struct ivtv *itv = (struct ivtv *)info->par; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci if (regno >= info->cmap.len) 8658c2ecf20Sopenharmony_ci return -EINVAL; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci color = ((transp & 0xFF00) << 16) |((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8); 8688c2ecf20Sopenharmony_ci if (info->var.bits_per_pixel <= 8) { 8698c2ecf20Sopenharmony_ci write_reg(regno, 0x02a30); 8708c2ecf20Sopenharmony_ci write_reg(color, 0x02a34); 8718c2ecf20Sopenharmony_ci itv->osd_info->palette_cur[regno] = color; 8728c2ecf20Sopenharmony_ci return 0; 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci if (regno >= 16) 8758c2ecf20Sopenharmony_ci return -EINVAL; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci palette = info->pseudo_palette; 8788c2ecf20Sopenharmony_ci if (info->var.bits_per_pixel == 16) { 8798c2ecf20Sopenharmony_ci switch (info->var.green.length) { 8808c2ecf20Sopenharmony_ci case 4: 8818c2ecf20Sopenharmony_ci color = ((red & 0xf000) >> 4) | 8828c2ecf20Sopenharmony_ci ((green & 0xf000) >> 8) | 8838c2ecf20Sopenharmony_ci ((blue & 0xf000) >> 12); 8848c2ecf20Sopenharmony_ci break; 8858c2ecf20Sopenharmony_ci case 5: 8868c2ecf20Sopenharmony_ci color = ((red & 0xf800) >> 1) | 8878c2ecf20Sopenharmony_ci ((green & 0xf800) >> 6) | 8888c2ecf20Sopenharmony_ci ((blue & 0xf800) >> 11); 8898c2ecf20Sopenharmony_ci break; 8908c2ecf20Sopenharmony_ci case 6: 8918c2ecf20Sopenharmony_ci color = (red & 0xf800 ) | 8928c2ecf20Sopenharmony_ci ((green & 0xfc00) >> 5) | 8938c2ecf20Sopenharmony_ci ((blue & 0xf800) >> 11); 8948c2ecf20Sopenharmony_ci break; 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci } 8978c2ecf20Sopenharmony_ci palette[regno] = color; 8988c2ecf20Sopenharmony_ci return 0; 8998c2ecf20Sopenharmony_ci} 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci/* We don't really support blanking. All this does is enable or 9028c2ecf20Sopenharmony_ci disable the OSD. */ 9038c2ecf20Sopenharmony_cistatic int ivtvfb_blank(int blank_mode, struct fb_info *info) 9048c2ecf20Sopenharmony_ci{ 9058c2ecf20Sopenharmony_ci struct ivtv *itv = (struct ivtv *)info->par; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci IVTVFB_DEBUG_INFO("Set blanking mode : %d\n", blank_mode); 9088c2ecf20Sopenharmony_ci switch (blank_mode) { 9098c2ecf20Sopenharmony_ci case FB_BLANK_UNBLANK: 9108c2ecf20Sopenharmony_ci ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1); 9118c2ecf20Sopenharmony_ci ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1); 9128c2ecf20Sopenharmony_ci break; 9138c2ecf20Sopenharmony_ci case FB_BLANK_NORMAL: 9148c2ecf20Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 9158c2ecf20Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 9168c2ecf20Sopenharmony_ci ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0); 9178c2ecf20Sopenharmony_ci ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1); 9188c2ecf20Sopenharmony_ci break; 9198c2ecf20Sopenharmony_ci case FB_BLANK_POWERDOWN: 9208c2ecf20Sopenharmony_ci ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0); 9218c2ecf20Sopenharmony_ci ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0); 9228c2ecf20Sopenharmony_ci break; 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci itv->osd_info->blank_cur = blank_mode; 9258c2ecf20Sopenharmony_ci return 0; 9268c2ecf20Sopenharmony_ci} 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_cistatic const struct fb_ops ivtvfb_ops = { 9298c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 9308c2ecf20Sopenharmony_ci .fb_write = ivtvfb_write, 9318c2ecf20Sopenharmony_ci .fb_check_var = ivtvfb_check_var, 9328c2ecf20Sopenharmony_ci .fb_set_par = ivtvfb_set_par, 9338c2ecf20Sopenharmony_ci .fb_setcolreg = ivtvfb_setcolreg, 9348c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 9358c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 9368c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 9378c2ecf20Sopenharmony_ci .fb_cursor = NULL, 9388c2ecf20Sopenharmony_ci .fb_ioctl = ivtvfb_ioctl, 9398c2ecf20Sopenharmony_ci .fb_pan_display = ivtvfb_pan_display, 9408c2ecf20Sopenharmony_ci .fb_blank = ivtvfb_blank, 9418c2ecf20Sopenharmony_ci}; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci/* Restore hardware after firmware restart */ 9448c2ecf20Sopenharmony_cistatic void ivtvfb_restore(struct ivtv *itv) 9458c2ecf20Sopenharmony_ci{ 9468c2ecf20Sopenharmony_ci struct osd_info *oi = itv->osd_info; 9478c2ecf20Sopenharmony_ci int i; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci ivtvfb_set_var(itv, &oi->fbvar_cur); 9508c2ecf20Sopenharmony_ci ivtvfb_blank(oi->blank_cur, &oi->ivtvfb_info); 9518c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) { 9528c2ecf20Sopenharmony_ci write_reg(i, 0x02a30); 9538c2ecf20Sopenharmony_ci write_reg(oi->palette_cur[i], 0x02a34); 9548c2ecf20Sopenharmony_ci } 9558c2ecf20Sopenharmony_ci write_reg(oi->pan_cur, 0x02a0c); 9568c2ecf20Sopenharmony_ci} 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci/* Initialization */ 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci/* Setup our initial video mode */ 9628c2ecf20Sopenharmony_cistatic int ivtvfb_init_vidmode(struct ivtv *itv) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci struct osd_info *oi = itv->osd_info; 9658c2ecf20Sopenharmony_ci struct v4l2_rect start_window; 9668c2ecf20Sopenharmony_ci int max_height; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci /* Color mode */ 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32) 9718c2ecf20Sopenharmony_ci osd_depth = 8; 9728c2ecf20Sopenharmony_ci oi->bits_per_pixel = osd_depth; 9738c2ecf20Sopenharmony_ci oi->bytes_per_pixel = oi->bits_per_pixel / 8; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci /* Horizontal size & position */ 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci if (osd_xres > 720) 9788c2ecf20Sopenharmony_ci osd_xres = 720; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci /* Must be a multiple of 4 for 8bpp & 2 for 16bpp */ 9818c2ecf20Sopenharmony_ci if (osd_depth == 8) 9828c2ecf20Sopenharmony_ci osd_xres &= ~3; 9838c2ecf20Sopenharmony_ci else if (osd_depth == 16) 9848c2ecf20Sopenharmony_ci osd_xres &= ~1; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci start_window.width = osd_xres ? osd_xres : 640; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci /* Check horizontal start (osd_left). */ 9898c2ecf20Sopenharmony_ci if (osd_left && osd_left + start_window.width > 721) { 9908c2ecf20Sopenharmony_ci IVTVFB_ERR("Invalid osd_left - assuming default\n"); 9918c2ecf20Sopenharmony_ci osd_left = 0; 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci /* Hardware coords start at 0, user coords start at 1. */ 9958c2ecf20Sopenharmony_ci osd_left--; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci start_window.left = osd_left >= 0 ? 9988c2ecf20Sopenharmony_ci osd_left : ((IVTV_OSD_MAX_WIDTH - start_window.width) / 2); 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci oi->display_byte_stride = 10018c2ecf20Sopenharmony_ci start_window.width * oi->bytes_per_pixel; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci /* Vertical size & position */ 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci max_height = itv->is_out_50hz ? 576 : 480; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci if (osd_yres > max_height) 10088c2ecf20Sopenharmony_ci osd_yres = max_height; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci start_window.height = osd_yres ? 10118c2ecf20Sopenharmony_ci osd_yres : itv->is_out_50hz ? 480 : 400; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci /* Check vertical start (osd_upper). */ 10148c2ecf20Sopenharmony_ci if (osd_upper + start_window.height > max_height + 1) { 10158c2ecf20Sopenharmony_ci IVTVFB_ERR("Invalid osd_upper - assuming default\n"); 10168c2ecf20Sopenharmony_ci osd_upper = 0; 10178c2ecf20Sopenharmony_ci } 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci /* Hardware coords start at 0, user coords start at 1. */ 10208c2ecf20Sopenharmony_ci osd_upper--; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci start_window.top = osd_upper >= 0 ? osd_upper : ((max_height - start_window.height) / 2); 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci oi->display_width = start_window.width; 10258c2ecf20Sopenharmony_ci oi->display_height = start_window.height; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci /* Generate a valid fb_var_screeninfo */ 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci oi->ivtvfb_defined.xres = oi->display_width; 10308c2ecf20Sopenharmony_ci oi->ivtvfb_defined.yres = oi->display_height; 10318c2ecf20Sopenharmony_ci oi->ivtvfb_defined.xres_virtual = oi->display_width; 10328c2ecf20Sopenharmony_ci oi->ivtvfb_defined.yres_virtual = oi->display_height; 10338c2ecf20Sopenharmony_ci oi->ivtvfb_defined.bits_per_pixel = oi->bits_per_pixel; 10348c2ecf20Sopenharmony_ci oi->ivtvfb_defined.vmode = (osd_laced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED); 10358c2ecf20Sopenharmony_ci oi->ivtvfb_defined.left_margin = start_window.left + 1; 10368c2ecf20Sopenharmony_ci oi->ivtvfb_defined.upper_margin = start_window.top + 1; 10378c2ecf20Sopenharmony_ci oi->ivtvfb_defined.accel_flags = FB_ACCEL_NONE; 10388c2ecf20Sopenharmony_ci oi->ivtvfb_defined.nonstd = 0; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci /* We've filled in the most data, let the usual mode check 10418c2ecf20Sopenharmony_ci routine fill in the rest. */ 10428c2ecf20Sopenharmony_ci _ivtvfb_check_var(&oi->ivtvfb_defined, itv); 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci /* Generate valid fb_fix_screeninfo */ 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci ivtvfb_get_fix(itv, &oi->ivtvfb_fix); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci /* Generate valid fb_info */ 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci oi->ivtvfb_info.node = -1; 10518c2ecf20Sopenharmony_ci oi->ivtvfb_info.flags = FBINFO_FLAG_DEFAULT; 10528c2ecf20Sopenharmony_ci oi->ivtvfb_info.par = itv; 10538c2ecf20Sopenharmony_ci oi->ivtvfb_info.var = oi->ivtvfb_defined; 10548c2ecf20Sopenharmony_ci oi->ivtvfb_info.fix = oi->ivtvfb_fix; 10558c2ecf20Sopenharmony_ci oi->ivtvfb_info.screen_base = (u8 __iomem *)oi->video_vbase; 10568c2ecf20Sopenharmony_ci oi->ivtvfb_info.fbops = &ivtvfb_ops; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci /* Supply some monitor specs. Bogus values will do for now */ 10598c2ecf20Sopenharmony_ci oi->ivtvfb_info.monspecs.hfmin = 8000; 10608c2ecf20Sopenharmony_ci oi->ivtvfb_info.monspecs.hfmax = 70000; 10618c2ecf20Sopenharmony_ci oi->ivtvfb_info.monspecs.vfmin = 10; 10628c2ecf20Sopenharmony_ci oi->ivtvfb_info.monspecs.vfmax = 100; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci /* Allocate color map */ 10658c2ecf20Sopenharmony_ci if (fb_alloc_cmap(&oi->ivtvfb_info.cmap, 256, 1)) { 10668c2ecf20Sopenharmony_ci IVTVFB_ERR("abort, unable to alloc cmap\n"); 10678c2ecf20Sopenharmony_ci return -ENOMEM; 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci /* Allocate the pseudo palette */ 10718c2ecf20Sopenharmony_ci oi->ivtvfb_info.pseudo_palette = 10728c2ecf20Sopenharmony_ci kmalloc_array(16, sizeof(u32), GFP_KERNEL|__GFP_NOWARN); 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci if (!oi->ivtvfb_info.pseudo_palette) { 10758c2ecf20Sopenharmony_ci IVTVFB_ERR("abort, unable to alloc pseudo palette\n"); 10768c2ecf20Sopenharmony_ci return -ENOMEM; 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci return 0; 10808c2ecf20Sopenharmony_ci} 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci/* Find OSD buffer base & size. Add to mtrr. Zero osd buffer. */ 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_cistatic int ivtvfb_init_io(struct ivtv *itv) 10858c2ecf20Sopenharmony_ci{ 10868c2ecf20Sopenharmony_ci struct osd_info *oi = itv->osd_info; 10878c2ecf20Sopenharmony_ci /* Find the largest power of two that maps the whole buffer */ 10888c2ecf20Sopenharmony_ci int size_shift = 31; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci mutex_lock(&itv->serialize_lock); 10918c2ecf20Sopenharmony_ci if (ivtv_init_on_first_open(itv)) { 10928c2ecf20Sopenharmony_ci mutex_unlock(&itv->serialize_lock); 10938c2ecf20Sopenharmony_ci IVTVFB_ERR("Failed to initialize ivtv\n"); 10948c2ecf20Sopenharmony_ci return -ENXIO; 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci mutex_unlock(&itv->serialize_lock); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci if (ivtvfb_get_framebuffer(itv, &oi->video_rbase, 10998c2ecf20Sopenharmony_ci &oi->video_buffer_size) < 0) { 11008c2ecf20Sopenharmony_ci IVTVFB_ERR("Firmware failed to respond\n"); 11018c2ecf20Sopenharmony_ci return -EIO; 11028c2ecf20Sopenharmony_ci } 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci /* The osd buffer size depends on the number of video buffers allocated 11058c2ecf20Sopenharmony_ci on the PVR350 itself. For now we'll hardcode the smallest osd buffer 11068c2ecf20Sopenharmony_ci size to prevent any overlap. */ 11078c2ecf20Sopenharmony_ci oi->video_buffer_size = 1704960; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci oi->video_pbase = itv->base_addr + IVTV_DECODER_OFFSET + oi->video_rbase; 11108c2ecf20Sopenharmony_ci oi->video_vbase = itv->dec_mem + oi->video_rbase; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci if (!oi->video_vbase) { 11138c2ecf20Sopenharmony_ci IVTVFB_ERR("abort, video memory 0x%x @ 0x%lx isn't mapped!\n", 11148c2ecf20Sopenharmony_ci oi->video_buffer_size, oi->video_pbase); 11158c2ecf20Sopenharmony_ci return -EIO; 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci IVTVFB_INFO("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n", 11198c2ecf20Sopenharmony_ci oi->video_pbase, oi->video_vbase, 11208c2ecf20Sopenharmony_ci oi->video_buffer_size / 1024); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci while (!(oi->video_buffer_size & (1 << size_shift))) 11238c2ecf20Sopenharmony_ci size_shift--; 11248c2ecf20Sopenharmony_ci size_shift++; 11258c2ecf20Sopenharmony_ci oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1); 11268c2ecf20Sopenharmony_ci oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size; 11278c2ecf20Sopenharmony_ci oi->fb_end_aligned_physaddr += (1 << size_shift) - 1; 11288c2ecf20Sopenharmony_ci oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1); 11298c2ecf20Sopenharmony_ci oi->wc_cookie = arch_phys_wc_add(oi->fb_start_aligned_physaddr, 11308c2ecf20Sopenharmony_ci oi->fb_end_aligned_physaddr - 11318c2ecf20Sopenharmony_ci oi->fb_start_aligned_physaddr); 11328c2ecf20Sopenharmony_ci /* Blank the entire osd. */ 11338c2ecf20Sopenharmony_ci memset_io(oi->video_vbase, 0, oi->video_buffer_size); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci return 0; 11368c2ecf20Sopenharmony_ci} 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci/* Release any memory we've grabbed & remove mtrr entry */ 11398c2ecf20Sopenharmony_cistatic void ivtvfb_release_buffers (struct ivtv *itv) 11408c2ecf20Sopenharmony_ci{ 11418c2ecf20Sopenharmony_ci struct osd_info *oi = itv->osd_info; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci /* Release cmap */ 11448c2ecf20Sopenharmony_ci if (oi->ivtvfb_info.cmap.len) 11458c2ecf20Sopenharmony_ci fb_dealloc_cmap(&oi->ivtvfb_info.cmap); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci /* Release pseudo palette */ 11488c2ecf20Sopenharmony_ci kfree(oi->ivtvfb_info.pseudo_palette); 11498c2ecf20Sopenharmony_ci arch_phys_wc_del(oi->wc_cookie); 11508c2ecf20Sopenharmony_ci kfree(oi); 11518c2ecf20Sopenharmony_ci itv->osd_info = NULL; 11528c2ecf20Sopenharmony_ci} 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci/* Initialize the specified card */ 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_cistatic int ivtvfb_init_card(struct ivtv *itv) 11578c2ecf20Sopenharmony_ci{ 11588c2ecf20Sopenharmony_ci int rc; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_64 11618c2ecf20Sopenharmony_ci if (pat_enabled()) { 11628c2ecf20Sopenharmony_ci if (ivtvfb_force_pat) { 11638c2ecf20Sopenharmony_ci pr_info("PAT is enabled. Write-combined framebuffer caching will be disabled.\n"); 11648c2ecf20Sopenharmony_ci pr_info("To enable caching, boot with nopat kernel parameter\n"); 11658c2ecf20Sopenharmony_ci } else { 11668c2ecf20Sopenharmony_ci pr_warn("ivtvfb needs PAT disabled for write-combined framebuffer caching.\n"); 11678c2ecf20Sopenharmony_ci pr_warn("Boot with nopat kernel parameter to use caching, or use the\n"); 11688c2ecf20Sopenharmony_ci pr_warn("force_pat module parameter to run with caching disabled\n"); 11698c2ecf20Sopenharmony_ci return -ENODEV; 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci } 11728c2ecf20Sopenharmony_ci#endif 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci if (itv->osd_info) { 11758c2ecf20Sopenharmony_ci IVTVFB_ERR("Card %d already initialised\n", ivtvfb_card_id); 11768c2ecf20Sopenharmony_ci return -EBUSY; 11778c2ecf20Sopenharmony_ci } 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci itv->osd_info = kzalloc(sizeof(struct osd_info), 11808c2ecf20Sopenharmony_ci GFP_KERNEL|__GFP_NOWARN); 11818c2ecf20Sopenharmony_ci if (itv->osd_info == NULL) { 11828c2ecf20Sopenharmony_ci IVTVFB_ERR("Failed to allocate memory for osd_info\n"); 11838c2ecf20Sopenharmony_ci return -ENOMEM; 11848c2ecf20Sopenharmony_ci } 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci /* Find & setup the OSD buffer */ 11878c2ecf20Sopenharmony_ci rc = ivtvfb_init_io(itv); 11888c2ecf20Sopenharmony_ci if (rc) { 11898c2ecf20Sopenharmony_ci ivtvfb_release_buffers(itv); 11908c2ecf20Sopenharmony_ci return rc; 11918c2ecf20Sopenharmony_ci } 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci /* Set the startup video mode information */ 11948c2ecf20Sopenharmony_ci if ((rc = ivtvfb_init_vidmode(itv))) { 11958c2ecf20Sopenharmony_ci ivtvfb_release_buffers(itv); 11968c2ecf20Sopenharmony_ci return rc; 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci /* Register the framebuffer */ 12008c2ecf20Sopenharmony_ci if (register_framebuffer(&itv->osd_info->ivtvfb_info) < 0) { 12018c2ecf20Sopenharmony_ci ivtvfb_release_buffers(itv); 12028c2ecf20Sopenharmony_ci return -EINVAL; 12038c2ecf20Sopenharmony_ci } 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci itv->osd_video_pbase = itv->osd_info->video_pbase; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci /* Set the card to the requested mode */ 12088c2ecf20Sopenharmony_ci ivtvfb_set_par(&itv->osd_info->ivtvfb_info); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci /* Set color 0 to black */ 12118c2ecf20Sopenharmony_ci write_reg(0, 0x02a30); 12128c2ecf20Sopenharmony_ci write_reg(0, 0x02a34); 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci /* Enable the osd */ 12158c2ecf20Sopenharmony_ci ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info); 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci /* Enable restart */ 12188c2ecf20Sopenharmony_ci itv->ivtvfb_restore = ivtvfb_restore; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci /* Allocate DMA */ 12218c2ecf20Sopenharmony_ci ivtv_udma_alloc(itv); 12228c2ecf20Sopenharmony_ci itv->streams[IVTV_DEC_STREAM_TYPE_YUV].vdev.device_caps |= 12238c2ecf20Sopenharmony_ci V4L2_CAP_VIDEO_OUTPUT_OVERLAY; 12248c2ecf20Sopenharmony_ci itv->streams[IVTV_DEC_STREAM_TYPE_MPG].vdev.device_caps |= 12258c2ecf20Sopenharmony_ci V4L2_CAP_VIDEO_OUTPUT_OVERLAY; 12268c2ecf20Sopenharmony_ci itv->v4l2_cap |= V4L2_CAP_VIDEO_OUTPUT_OVERLAY; 12278c2ecf20Sopenharmony_ci return 0; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci} 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_cistatic int __init ivtvfb_callback_init(struct device *dev, void *p) 12328c2ecf20Sopenharmony_ci{ 12338c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); 12348c2ecf20Sopenharmony_ci struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev); 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { 12378c2ecf20Sopenharmony_ci if (ivtvfb_init_card(itv) == 0) { 12388c2ecf20Sopenharmony_ci IVTVFB_INFO("Framebuffer registered on %s\n", 12398c2ecf20Sopenharmony_ci itv->v4l2_dev.name); 12408c2ecf20Sopenharmony_ci (*(int *)p)++; 12418c2ecf20Sopenharmony_ci } 12428c2ecf20Sopenharmony_ci } 12438c2ecf20Sopenharmony_ci return 0; 12448c2ecf20Sopenharmony_ci} 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_cistatic int ivtvfb_callback_cleanup(struct device *dev, void *p) 12478c2ecf20Sopenharmony_ci{ 12488c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); 12498c2ecf20Sopenharmony_ci struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev); 12508c2ecf20Sopenharmony_ci struct osd_info *oi = itv->osd_info; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { 12538c2ecf20Sopenharmony_ci itv->streams[IVTV_DEC_STREAM_TYPE_YUV].vdev.device_caps &= 12548c2ecf20Sopenharmony_ci ~V4L2_CAP_VIDEO_OUTPUT_OVERLAY; 12558c2ecf20Sopenharmony_ci itv->streams[IVTV_DEC_STREAM_TYPE_MPG].vdev.device_caps &= 12568c2ecf20Sopenharmony_ci ~V4L2_CAP_VIDEO_OUTPUT_OVERLAY; 12578c2ecf20Sopenharmony_ci itv->v4l2_cap &= ~V4L2_CAP_VIDEO_OUTPUT_OVERLAY; 12588c2ecf20Sopenharmony_ci unregister_framebuffer(&itv->osd_info->ivtvfb_info); 12598c2ecf20Sopenharmony_ci IVTVFB_INFO("Unregister framebuffer %d\n", itv->instance); 12608c2ecf20Sopenharmony_ci itv->ivtvfb_restore = NULL; 12618c2ecf20Sopenharmony_ci ivtvfb_blank(FB_BLANK_VSYNC_SUSPEND, &oi->ivtvfb_info); 12628c2ecf20Sopenharmony_ci ivtvfb_release_buffers(itv); 12638c2ecf20Sopenharmony_ci itv->osd_video_pbase = 0; 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci return 0; 12668c2ecf20Sopenharmony_ci} 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_cistatic int __init ivtvfb_init(void) 12698c2ecf20Sopenharmony_ci{ 12708c2ecf20Sopenharmony_ci struct device_driver *drv; 12718c2ecf20Sopenharmony_ci int registered = 0; 12728c2ecf20Sopenharmony_ci int err; 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) { 12768c2ecf20Sopenharmony_ci pr_err("ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n", 12778c2ecf20Sopenharmony_ci IVTV_MAX_CARDS - 1); 12788c2ecf20Sopenharmony_ci return -EINVAL; 12798c2ecf20Sopenharmony_ci } 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci drv = driver_find("ivtv", &pci_bus_type); 12828c2ecf20Sopenharmony_ci err = driver_for_each_device(drv, NULL, ®istered, ivtvfb_callback_init); 12838c2ecf20Sopenharmony_ci (void)err; /* suppress compiler warning */ 12848c2ecf20Sopenharmony_ci if (!registered) { 12858c2ecf20Sopenharmony_ci pr_err("no cards found\n"); 12868c2ecf20Sopenharmony_ci return -ENODEV; 12878c2ecf20Sopenharmony_ci } 12888c2ecf20Sopenharmony_ci return 0; 12898c2ecf20Sopenharmony_ci} 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_cistatic void ivtvfb_cleanup(void) 12928c2ecf20Sopenharmony_ci{ 12938c2ecf20Sopenharmony_ci struct device_driver *drv; 12948c2ecf20Sopenharmony_ci int err; 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci pr_info("Unloading framebuffer module\n"); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci drv = driver_find("ivtv", &pci_bus_type); 12998c2ecf20Sopenharmony_ci err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup); 13008c2ecf20Sopenharmony_ci (void)err; /* suppress compiler warning */ 13018c2ecf20Sopenharmony_ci} 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_cimodule_init(ivtvfb_init); 13048c2ecf20Sopenharmony_cimodule_exit(ivtvfb_cleanup); 1305