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, &registered, 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