2148 lines · 58.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Copyright 1998-2009 VIA Technologies, Inc. All Rights Reserved. * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved.  */ #include <linux/compiler.h>#include <linux/module.h>#include <linux/seq_file.h>#include <linux/slab.h>#include <linux/stat.h>#include <linux/via-core.h>#include <linux/via_i2c.h> #define _MASTER_FILE#include "global.h" static u32 pseudo_pal[17]; /* video mode */static char *viafb_mode;static char *viafb_mode1;static int viafb_bpp = 32;static int viafb_bpp1 = 32; static unsigned int viafb_second_offset;static int viafb_second_size; static int viafb_accel = 1; /* Added for specifying active devices.*/static char *viafb_active_dev; /*Added for specify lcd output port*/static char *viafb_lcd_port = "";static char *viafb_dvi_port = ""; static void retrieve_device_setting(struct viafb_ioctl_setting	*setting_info);static int viafb_pan_display(struct fb_var_screeninfo *var,	struct fb_info *info); static struct fb_ops viafb_ops; /* supported output devices on each IGP * only CX700, VX800, VX855, VX900 were documented * VIA_CRT should be everywhere * VIA_6C can be onle pre-CX700 (probably only on CLE266) as 6C is used for PLL * source selection on CX700 and later * K400 seems to support VIA_96, VIA_DVP1, VIA_LVDS{1,2} as in viamode.c */static const u32 supported_odev_map[] = {	[UNICHROME_CLE266]	= VIA_CRT | VIA_LDVP0 | VIA_LDVP1,	[UNICHROME_K400]	= VIA_CRT | VIA_DVP0 | VIA_DVP1 | VIA_LVDS1				| VIA_LVDS2,	[UNICHROME_K800]	= VIA_CRT | VIA_DVP0 | VIA_DVP1 | VIA_LVDS1				| VIA_LVDS2,	[UNICHROME_PM800]	= VIA_CRT | VIA_DVP0 | VIA_DVP1 | VIA_LVDS1				| VIA_LVDS2,	[UNICHROME_CN700]	= VIA_CRT | VIA_DVP0 | VIA_DVP1 | VIA_LVDS1				| VIA_LVDS2,	[UNICHROME_CX700]	= VIA_CRT | VIA_DVP1 | VIA_LVDS1 | VIA_LVDS2,	[UNICHROME_CN750]	= VIA_CRT | VIA_DVP1 | VIA_LVDS1 | VIA_LVDS2,	[UNICHROME_K8M890]	= VIA_CRT | VIA_DVP1 | VIA_LVDS1 | VIA_LVDS2,	[UNICHROME_P4M890]	= VIA_CRT | VIA_DVP1 | VIA_LVDS1 | VIA_LVDS2,	[UNICHROME_P4M900]	= VIA_CRT | VIA_DVP1 | VIA_LVDS1 | VIA_LVDS2,	[UNICHROME_VX800]	= VIA_CRT | VIA_DVP1 | VIA_LVDS1 | VIA_LVDS2,	[UNICHROME_VX855]	= VIA_CRT | VIA_DVP1 | VIA_LVDS1 | VIA_LVDS2,	[UNICHROME_VX900]	= VIA_CRT | VIA_DVP1 | VIA_LVDS1 | VIA_LVDS2,}; static void viafb_fill_var_color_info(struct fb_var_screeninfo *var, u8 depth){	var->grayscale = 0;	var->red.msb_right = 0;	var->green.msb_right = 0;	var->blue.msb_right = 0;	var->transp.offset = 0;	var->transp.length = 0;	var->transp.msb_right = 0;	var->nonstd = 0;	switch (depth) {	case 8:		var->bits_per_pixel = 8;		var->red.offset = 0;		var->green.offset = 0;		var->blue.offset = 0;		var->red.length = 8;		var->green.length = 8;		var->blue.length = 8;		break;	case 15:		var->bits_per_pixel = 16;		var->red.offset = 10;		var->green.offset = 5;		var->blue.offset = 0;		var->red.length = 5;		var->green.length = 5;		var->blue.length = 5;		break;	case 16:		var->bits_per_pixel = 16;		var->red.offset = 11;		var->green.offset = 5;		var->blue.offset = 0;		var->red.length = 5;		var->green.length = 6;		var->blue.length = 5;		break;	case 24:		var->bits_per_pixel = 32;		var->red.offset = 16;		var->green.offset = 8;		var->blue.offset = 0;		var->red.length = 8;		var->green.length = 8;		var->blue.length = 8;		break;	case 30:		var->bits_per_pixel = 32;		var->red.offset = 20;		var->green.offset = 10;		var->blue.offset = 0;		var->red.length = 10;		var->green.length = 10;		var->blue.length = 10;		break;	}} static void viafb_update_fix(struct fb_info *info){	u32 bpp = info->var.bits_per_pixel; 	info->fix.visual =		bpp == 8 ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;	info->fix.line_length = ALIGN(info->var.xres_virtual * bpp / 8,		VIA_PITCH_SIZE);} static void viafb_setup_fixinfo(struct fb_fix_screeninfo *fix,	struct viafb_par *viaparinfo){	memset(fix, 0, sizeof(struct fb_fix_screeninfo));	strcpy(fix->id, "Via"); 	fix->smem_start = viaparinfo->fbmem;	fix->smem_len = viaparinfo->fbmem_free; 	fix->type = FB_TYPE_PACKED_PIXELS;	fix->type_aux = 0;	fix->visual = FB_VISUAL_TRUECOLOR; 	fix->xpanstep = fix->ywrapstep = 0;	fix->ypanstep = 1; 	/* Just tell the accel name */	viafbinfo->fix.accel = FB_ACCEL_VIA_UNICHROME;}static int viafb_open(struct fb_info *info, int user){	DEBUG_MSG(KERN_INFO "viafb_open!\n");	return 0;} static int viafb_release(struct fb_info *info, int user){	DEBUG_MSG(KERN_INFO "viafb_release!\n");	return 0;} static inline int get_var_refresh(struct fb_var_screeninfo *var){	u32 htotal, vtotal; 	htotal = var->left_margin + var->xres + var->right_margin		+ var->hsync_len;	vtotal = var->upper_margin + var->yres + var->lower_margin		+ var->vsync_len;	return PICOS2KHZ(var->pixclock) * 1000 / (htotal * vtotal);} static int viafb_check_var(struct fb_var_screeninfo *var,	struct fb_info *info){	int depth, refresh;	struct viafb_par *ppar = info->par;	u32 line; 	DEBUG_MSG(KERN_INFO "viafb_check_var!\n");	/* Sanity check */	/* HW neither support interlacte nor double-scaned mode */	if (var->vmode & FB_VMODE_INTERLACED || var->vmode & FB_VMODE_DOUBLE)		return -EINVAL; 	/* the refresh rate is not important here, as we only want to know	 * whether the resolution exists	 */	if (!viafb_get_best_mode(var->xres, var->yres, 60)) {		DEBUG_MSG(KERN_INFO			  "viafb: Mode %dx%dx%d not supported!!\n",			  var->xres, var->yres, var->bits_per_pixel);		return -EINVAL;	} 	depth = fb_get_color_depth(var, &info->fix);	if (!depth)		depth = var->bits_per_pixel; 	if (depth < 0 || depth > 32)		return -EINVAL;	else if (!depth)		depth = 24;	else if (depth == 15 && viafb_dual_fb && ppar->iga_path == IGA1)		depth = 15;	else if (depth == 30)		depth = 30;	else if (depth <= 8)		depth = 8;	else if (depth <= 16)		depth = 16;	else		depth = 24; 	viafb_fill_var_color_info(var, depth);	if (var->xres_virtual < var->xres)		var->xres_virtual = var->xres; 	line = ALIGN(var->xres_virtual * var->bits_per_pixel / 8,		VIA_PITCH_SIZE);	if (line > VIA_PITCH_MAX || line * var->yres_virtual > ppar->memsize)		return -EINVAL; 	/* Based on var passed in to calculate the refresh,	 * because our driver use some modes special.	 */	refresh = viafb_get_refresh(var->xres, var->yres,		get_var_refresh(var)); 	/* Adjust var according to our driver's own table */	viafb_fill_var_timing_info(var,		viafb_get_best_mode(var->xres, var->yres, refresh));	if (var->accel_flags & FB_ACCELF_TEXT &&		!ppar->shared->vdev->engine_mmio)		var->accel_flags = 0; 	return 0;} static int viafb_set_par(struct fb_info *info){	struct viafb_par *viapar = info->par;	int refresh;	DEBUG_MSG(KERN_INFO "viafb_set_par!\n"); 	viafb_update_fix(info);	viapar->depth = fb_get_color_depth(&info->var, &info->fix);	viafb_update_device_setting(viafbinfo->var.xres, viafbinfo->var.yres,		viafbinfo->var.bits_per_pixel, 0); 	if (viafb_dual_fb) {		viafb_update_device_setting(viafbinfo1->var.xres,			viafbinfo1->var.yres, viafbinfo1->var.bits_per_pixel,			1);	} else if (viafb_SAMM_ON == 1) {		DEBUG_MSG(KERN_INFO		"viafb_second_xres = %d, viafb_second_yres = %d, bpp = %d\n",			  viafb_second_xres, viafb_second_yres, viafb_bpp1); 		viafb_update_device_setting(viafb_second_xres,			viafb_second_yres, viafb_bpp1, 1);	} 	refresh = get_var_refresh(&info->var);	if (viafb_dual_fb && viapar->iga_path == IGA2) {		viafb_bpp1 = info->var.bits_per_pixel;		viafb_refresh1 = refresh;	} else {		viafb_bpp = info->var.bits_per_pixel;		viafb_refresh = refresh;	} 	if (info->var.accel_flags & FB_ACCELF_TEXT)		info->flags &= ~FBINFO_HWACCEL_DISABLED;	else		info->flags |= FBINFO_HWACCEL_DISABLED;	viafb_setmode();	viafb_pan_display(&info->var, info); 	return 0;} /* Set one color register */static int viafb_setcolreg(unsigned regno, unsigned red, unsigned green,unsigned blue, unsigned transp, struct fb_info *info){	struct viafb_par *viapar = info->par;	u32 r, g, b; 	if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR) {		if (regno > 255)			return -EINVAL; 		if (!viafb_dual_fb || viapar->iga_path == IGA1)			viafb_set_primary_color_register(regno, red >> 8,				green >> 8, blue >> 8); 		if (!viafb_dual_fb || viapar->iga_path == IGA2)			viafb_set_secondary_color_register(regno, red >> 8,				green >> 8, blue >> 8);	} else {		if (regno > 15)			return -EINVAL; 		r = (red >> (16 - info->var.red.length))			<< info->var.red.offset;		b = (blue >> (16 - info->var.blue.length))			<< info->var.blue.offset;		g = (green >> (16 - info->var.green.length))			<< info->var.green.offset;		((u32 *) info->pseudo_palette)[regno] = r | g | b;	} 	return 0;} static int viafb_pan_display(struct fb_var_screeninfo *var,	struct fb_info *info){	struct viafb_par *viapar = info->par;	u32 vram_addr = viapar->vram_addr		+ var->yoffset * info->fix.line_length		+ var->xoffset * info->var.bits_per_pixel / 8; 	DEBUG_MSG(KERN_DEBUG "viafb_pan_display, address = %d\n", vram_addr);	if (!viafb_dual_fb) {		via_set_primary_address(vram_addr);		via_set_secondary_address(vram_addr);	} else if (viapar->iga_path == IGA1)		via_set_primary_address(vram_addr);	else		via_set_secondary_address(vram_addr); 	return 0;} static int viafb_blank(int blank_mode, struct fb_info *info){	DEBUG_MSG(KERN_INFO "viafb_blank!\n");	/* clear DPMS setting */ 	switch (blank_mode) {	case FB_BLANK_UNBLANK:		/* Screen: On, HSync: On, VSync: On */		/* control CRT monitor power management */		via_set_state(VIA_CRT, VIA_STATE_ON);		break;	case FB_BLANK_HSYNC_SUSPEND:		/* Screen: Off, HSync: Off, VSync: On */		/* control CRT monitor power management */		via_set_state(VIA_CRT, VIA_STATE_STANDBY);		break;	case FB_BLANK_VSYNC_SUSPEND:		/* Screen: Off, HSync: On, VSync: Off */		/* control CRT monitor power management */		via_set_state(VIA_CRT, VIA_STATE_SUSPEND);		break;	case FB_BLANK_POWERDOWN:		/* Screen: Off, HSync: Off, VSync: Off */		/* control CRT monitor power management */		via_set_state(VIA_CRT, VIA_STATE_OFF);		break;	} 	return 0;} static int viafb_ioctl(struct fb_info *info, u_int cmd, u_long arg){	union {		struct viafb_ioctl_mode viamode;		struct viafb_ioctl_samm viasamm;		struct viafb_driver_version driver_version;		struct fb_var_screeninfo sec_var;		struct _panel_size_pos_info panel_pos_size_para;		struct viafb_ioctl_setting viafb_setting;		struct device_t active_dev;	} u;	u32 state_info = 0;	u32 *viafb_gamma_table;	char driver_name[] = "viafb"; 	u32 __user *argp = (u32 __user *) arg;	u32 gpu32; 	DEBUG_MSG(KERN_INFO "viafb_ioctl: 0x%X !!\n", cmd);	printk(KERN_WARNING "viafb_ioctl: Please avoid this interface as it is unstable and might change or vanish at any time!\n");	memset(&u, 0, sizeof(u)); 	switch (cmd) {	case VIAFB_GET_CHIP_INFO:		if (copy_to_user(argp, viaparinfo->chip_info,				sizeof(struct chip_information)))			return -EFAULT;		break;	case VIAFB_GET_INFO_SIZE:		return put_user((u32)sizeof(struct viafb_ioctl_info), argp);	case VIAFB_GET_INFO:		return viafb_ioctl_get_viafb_info(arg);	case VIAFB_HOTPLUG:		return put_user(viafb_ioctl_hotplug(info->var.xres,					      info->var.yres,					      info->var.bits_per_pixel), argp);	case VIAFB_SET_HOTPLUG_FLAG:		if (copy_from_user(&gpu32, argp, sizeof(gpu32)))			return -EFAULT;		viafb_hotplug = (gpu32) ? 1 : 0;		break;	case VIAFB_GET_RESOLUTION:		u.viamode.xres = (u32) viafb_hotplug_Xres;		u.viamode.yres = (u32) viafb_hotplug_Yres;		u.viamode.refresh = (u32) viafb_hotplug_refresh;		u.viamode.bpp = (u32) viafb_hotplug_bpp;		if (viafb_SAMM_ON == 1) {			u.viamode.xres_sec = viafb_second_xres;			u.viamode.yres_sec = viafb_second_yres;			u.viamode.virtual_xres_sec = viafb_dual_fb ? viafbinfo1->var.xres_virtual : viafbinfo->var.xres_virtual;			u.viamode.virtual_yres_sec = viafb_dual_fb ? viafbinfo1->var.yres_virtual : viafbinfo->var.yres_virtual;			u.viamode.refresh_sec = viafb_refresh1;			u.viamode.bpp_sec = viafb_bpp1;		} else {			u.viamode.xres_sec = 0;			u.viamode.yres_sec = 0;			u.viamode.virtual_xres_sec = 0;			u.viamode.virtual_yres_sec = 0;			u.viamode.refresh_sec = 0;			u.viamode.bpp_sec = 0;		}		if (copy_to_user(argp, &u.viamode, sizeof(u.viamode)))			return -EFAULT;		break;	case VIAFB_GET_SAMM_INFO:		u.viasamm.samm_status = viafb_SAMM_ON; 		if (viafb_SAMM_ON == 1) {			if (viafb_dual_fb) {				u.viasamm.size_prim = viaparinfo->fbmem_free;				u.viasamm.size_sec = viaparinfo1->fbmem_free;			} else {				if (viafb_second_size) {					u.viasamm.size_prim =					    viaparinfo->fbmem_free -					    viafb_second_size * 1024 * 1024;					u.viasamm.size_sec =					    viafb_second_size * 1024 * 1024;				} else {					u.viasamm.size_prim =					    viaparinfo->fbmem_free >> 1;					u.viasamm.size_sec =					    (viaparinfo->fbmem_free >> 1);				}			}			u.viasamm.mem_base = viaparinfo->fbmem;			u.viasamm.offset_sec = viafb_second_offset;		} else {			u.viasamm.size_prim =			    viaparinfo->memsize - viaparinfo->fbmem_used;			u.viasamm.size_sec = 0;			u.viasamm.mem_base = viaparinfo->fbmem;			u.viasamm.offset_sec = 0;		} 		if (copy_to_user(argp, &u.viasamm, sizeof(u.viasamm)))			return -EFAULT; 		break;	case VIAFB_TURN_ON_OUTPUT_DEVICE:		if (copy_from_user(&gpu32, argp, sizeof(gpu32)))			return -EFAULT;		if (gpu32 & CRT_Device)			via_set_state(VIA_CRT, VIA_STATE_ON);		if (gpu32 & DVI_Device)			viafb_dvi_enable();		if (gpu32 & LCD_Device)			viafb_lcd_enable();		break;	case VIAFB_TURN_OFF_OUTPUT_DEVICE:		if (copy_from_user(&gpu32, argp, sizeof(gpu32)))			return -EFAULT;		if (gpu32 & CRT_Device)			via_set_state(VIA_CRT, VIA_STATE_OFF);		if (gpu32 & DVI_Device)			viafb_dvi_disable();		if (gpu32 & LCD_Device)			viafb_lcd_disable();		break;	case VIAFB_GET_DEVICE:		u.active_dev.crt = viafb_CRT_ON;		u.active_dev.dvi = viafb_DVI_ON;

Lines 1–500 of 2148.