linux_dsm_epyc7002/drivers/video/fbdev/sm712fb.c

1794 lines
48 KiB
C
Raw Normal View History

/*
* Silicon Motion SM7XX frame buffer device
*
* Copyright (C) 2006 Silicon Motion Technology Corp.
* Authors: Ge Wang, gewang@siliconmotion.com
* Boyod boyod.yang@siliconmotion.com.cn
*
* Copyright (C) 2009 Lemote, Inc.
* Author: Wu Zhangjin, wuzhangjin@gmail.com
*
* Copyright (C) 2011 Igalia, S.L.
* Author: Javier M. Mellid <jmunhoz@igalia.com>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*
* Framebuffer driver for Silicon Motion SM710, SM712, SM721 and SM722 chips
*/
#include <linux/io.h>
#include <linux/fb.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/console.h>
#include <linux/screen_info.h>
#include <linux/pm.h>
#include "sm712.h"
/*
* Private structure
*/
struct smtcfb_info {
struct pci_dev *pdev;
struct fb_info *fb;
u16 chip_id;
u8 chip_rev_id;
void __iomem *lfb; /* linear frame buffer */
void __iomem *dp_regs; /* drawing processor control regs */
void __iomem *vp_regs; /* video processor control regs */
void __iomem *cp_regs; /* capture processor control regs */
void __iomem *mmio; /* memory map IO port */
u_int width;
u_int height;
u_int hz;
u32 colreg[17];
};
void __iomem *smtc_regbaseaddress; /* Memory Map IO starting address */
static const struct fb_var_screeninfo smtcfb_var = {
.xres = 1024,
.yres = 600,
.xres_virtual = 1024,
.yres_virtual = 600,
.bits_per_pixel = 16,
.red = {16, 8, 0},
.green = {8, 8, 0},
.blue = {0, 8, 0},
.activate = FB_ACTIVATE_NOW,
.height = -1,
.width = -1,
.vmode = FB_VMODE_NONINTERLACED,
.nonstd = 0,
.accel_flags = FB_ACCELF_TEXT,
};
static struct fb_fix_screeninfo smtcfb_fix = {
.id = "smXXXfb",
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_TRUECOLOR,
.line_length = 800 * 3,
.accel = FB_ACCEL_SMI_LYNX,
.type_aux = 0,
.xpanstep = 0,
.ypanstep = 0,
.ywrapstep = 0,
};
struct vesa_mode {
char index[6];
u16 lfb_width;
u16 lfb_height;
u16 lfb_depth;
};
static const struct vesa_mode vesa_mode_table[] = {
{"0x301", 640, 480, 8},
{"0x303", 800, 600, 8},
{"0x305", 1024, 768, 8},
{"0x307", 1280, 1024, 8},
{"0x311", 640, 480, 16},
{"0x314", 800, 600, 16},
{"0x317", 1024, 768, 16},
{"0x31A", 1280, 1024, 16},
{"0x312", 640, 480, 24},
{"0x315", 800, 600, 24},
{"0x318", 1024, 768, 24},
{"0x31B", 1280, 1024, 24},
};
/**********************************************************************
SM712 Mode table.
**********************************************************************/
static const struct modeinit vgamode[] = {
{
/* mode#0: 640 x 480 16Bpp 60Hz */
640, 480, 16, 60,
/* Init_MISC */
0xE3,
{ /* Init_SR0_SR4 */
0x03, 0x01, 0x0F, 0x00, 0x0E,
},
{ /* Init_SR10_SR24 */
0xFF, 0xBE, 0xEF, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC4, 0x30, 0x02, 0x01, 0x01,
},
{ /* Init_SR30_SR75 */
0x32, 0x03, 0xA0, 0x09, 0xC0, 0x32, 0x32, 0x32,
0x32, 0x32, 0x32, 0x32, 0x00, 0x00, 0x03, 0xFF,
0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
0x20, 0x0C, 0x44, 0x20, 0x00, 0x32, 0x32, 0x32,
0x04, 0x24, 0x63, 0x4F, 0x52, 0x0B, 0xDF, 0xEA,
0x04, 0x50, 0x19, 0x32, 0x32, 0x00, 0x00, 0x32,
0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
0x50, 0x03, 0x74, 0x14, 0x07, 0x82, 0x07, 0x04,
0x00, 0x45, 0x30, 0x30, 0x40, 0x30,
},
{ /* Init_SR80_SR93 */
0xFF, 0x07, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x32,
0xF7, 0x00, 0x00, 0x00, 0xEF, 0xFF, 0x32, 0x32,
0x00, 0x00, 0x00, 0x00,
},
{ /* Init_SRA0_SRAF */
0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xFF, 0xDF,
},
{ /* Init_GR00_GR08 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
0xFF,
},
{ /* Init_AR00_AR14 */
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x41, 0x00, 0x0F, 0x00, 0x00,
},
{ /* Init_CR00_CR18 */
0x5F, 0x4F, 0x4F, 0x00, 0x53, 0x1F, 0x0B, 0x3E,
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xEA, 0x0C, 0xDF, 0x50, 0x40, 0xDF, 0x00, 0xE3,
0xFF,
},
{ /* Init_CR30_CR4D */
0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x20,
0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xFF, 0xFD,
0x5F, 0x4F, 0x00, 0x54, 0x00, 0x0B, 0xDF, 0x00,
0xEA, 0x0C, 0x2E, 0x00, 0x4F, 0xDF,
},
{ /* Init_CR90_CRA7 */
0x56, 0xDD, 0x5E, 0xEA, 0x87, 0x44, 0x8F, 0x55,
0x0A, 0x8F, 0x55, 0x0A, 0x00, 0x00, 0x18, 0x00,
0x11, 0x10, 0x0B, 0x0A, 0x0A, 0x0A, 0x0A, 0x00,
},
},
{
/* mode#1: 640 x 480 24Bpp 60Hz */
640, 480, 24, 60,
/* Init_MISC */
0xE3,
{ /* Init_SR0_SR4 */
0x03, 0x01, 0x0F, 0x00, 0x0E,
},
{ /* Init_SR10_SR24 */
0xFF, 0xBE, 0xEF, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC4, 0x30, 0x02, 0x01, 0x01,
},
{ /* Init_SR30_SR75 */
0x32, 0x03, 0xA0, 0x09, 0xC0, 0x32, 0x32, 0x32,
0x32, 0x32, 0x32, 0x32, 0x00, 0x00, 0x03, 0xFF,
0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
0x20, 0x0C, 0x44, 0x20, 0x00, 0x32, 0x32, 0x32,
0x04, 0x24, 0x63, 0x4F, 0x52, 0x0B, 0xDF, 0xEA,
0x04, 0x50, 0x19, 0x32, 0x32, 0x00, 0x00, 0x32,
0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
0x50, 0x03, 0x74, 0x14, 0x07, 0x82, 0x07, 0x04,
0x00, 0x45, 0x30, 0x30, 0x40, 0x30,
},
{ /* Init_SR80_SR93 */
0xFF, 0x07, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x32,
0xF7, 0x00, 0x00, 0x00, 0xEF, 0xFF, 0x32, 0x32,
0x00, 0x00, 0x00, 0x00,
},
{ /* Init_SRA0_SRAF */
0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xFF, 0xDF,
},
{ /* Init_GR00_GR08 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
0xFF,
},
{ /* Init_AR00_AR14 */
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x41, 0x00, 0x0F, 0x00, 0x00,
},
{ /* Init_CR00_CR18 */
0x5F, 0x4F, 0x4F, 0x00, 0x53, 0x1F, 0x0B, 0x3E,
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xEA, 0x0C, 0xDF, 0x50, 0x40, 0xDF, 0x00, 0xE3,
0xFF,
},
{ /* Init_CR30_CR4D */
0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x20,
0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xFF, 0xFD,
0x5F, 0x4F, 0x00, 0x54, 0x00, 0x0B, 0xDF, 0x00,
0xEA, 0x0C, 0x2E, 0x00, 0x4F, 0xDF,
},
{ /* Init_CR90_CRA7 */
0x56, 0xDD, 0x5E, 0xEA, 0x87, 0x44, 0x8F, 0x55,
0x0A, 0x8F, 0x55, 0x0A, 0x00, 0x00, 0x18, 0x00,
0x11, 0x10, 0x0B, 0x0A, 0x0A, 0x0A, 0x0A, 0x00,
},
},
{
/* mode#0: 640 x 480 32Bpp 60Hz */
640, 480, 32, 60,
/* Init_MISC */
0xE3,
{ /* Init_SR0_SR4 */
0x03, 0x01, 0x0F, 0x00, 0x0E,
},
{ /* Init_SR10_SR24 */
0xFF, 0xBE, 0xEF, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC4, 0x30, 0x02, 0x01, 0x01,
},
{ /* Init_SR30_SR75 */
0x32, 0x03, 0xA0, 0x09, 0xC0, 0x32, 0x32, 0x32,
0x32, 0x32, 0x32, 0x32, 0x00, 0x00, 0x03, 0xFF,
0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
0x20, 0x0C, 0x44, 0x20, 0x00, 0x32, 0x32, 0x32,
0x04, 0x24, 0x63, 0x4F, 0x52, 0x0B, 0xDF, 0xEA,
0x04, 0x50, 0x19, 0x32, 0x32, 0x00, 0x00, 0x32,
0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
0x50, 0x03, 0x74, 0x14, 0x07, 0x82, 0x07, 0x04,
0x00, 0x45, 0x30, 0x30, 0x40, 0x30,
},
{ /* Init_SR80_SR93 */
0xFF, 0x07, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x32,
0xF7, 0x00, 0x00, 0x00, 0xEF, 0xFF, 0x32, 0x32,
0x00, 0x00, 0x00, 0x00,
},
{ /* Init_SRA0_SRAF */
0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xFF, 0xDF,
},
{ /* Init_GR00_GR08 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
0xFF,
},
{ /* Init_AR00_AR14 */
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x41, 0x00, 0x0F, 0x00, 0x00,
},
{ /* Init_CR00_CR18 */
0x5F, 0x4F, 0x4F, 0x00, 0x53, 0x1F, 0x0B, 0x3E,
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xEA, 0x0C, 0xDF, 0x50, 0x40, 0xDF, 0x00, 0xE3,
0xFF,
},
{ /* Init_CR30_CR4D */
0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x20,
0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xFF, 0xFD,
0x5F, 0x4F, 0x00, 0x54, 0x00, 0x0B, 0xDF, 0x00,
0xEA, 0x0C, 0x2E, 0x00, 0x4F, 0xDF,
},
{ /* Init_CR90_CRA7 */
0x56, 0xDD, 0x5E, 0xEA, 0x87, 0x44, 0x8F, 0x55,
0x0A, 0x8F, 0x55, 0x0A, 0x00, 0x00, 0x18, 0x00,
0x11, 0x10, 0x0B, 0x0A, 0x0A, 0x0A, 0x0A, 0x00,
},
},
{ /* mode#2: 800 x 600 16Bpp 60Hz */
800, 600, 16, 60,
/* Init_MISC */
0x2B,
{ /* Init_SR0_SR4 */
0x03, 0x01, 0x0F, 0x03, 0x0E,
},
{ /* Init_SR10_SR24 */
0xFF, 0xBE, 0xEE, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
0x99, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC4, 0x30, 0x02, 0x01, 0x01,
},
{ /* Init_SR30_SR75 */
0x34, 0x03, 0x20, 0x09, 0xC0, 0x24, 0x24, 0x24,
0x24, 0x24, 0x24, 0x24, 0x00, 0x00, 0x03, 0xFF,
0x00, 0xFC, 0x00, 0x00, 0x20, 0x38, 0x00, 0xFC,
0x20, 0x0C, 0x44, 0x20, 0x00, 0x24, 0x24, 0x24,
0x04, 0x48, 0x83, 0x63, 0x68, 0x72, 0x57, 0x58,
0x04, 0x55, 0x59, 0x24, 0x24, 0x00, 0x00, 0x24,
0x01, 0x80, 0x7A, 0x1A, 0x1A, 0x00, 0x00, 0x00,
0x50, 0x03, 0x74, 0x14, 0x1C, 0x85, 0x35, 0x13,
0x02, 0x45, 0x30, 0x35, 0x40, 0x20,
},
{ /* Init_SR80_SR93 */
0x00, 0x00, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x24,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x24, 0x24,
0x00, 0x00, 0x00, 0x00,
},
{ /* Init_SRA0_SRAF */
0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xBF, 0xDF,
},
{ /* Init_GR00_GR08 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
0xFF,
},
{ /* Init_AR00_AR14 */
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x41, 0x00, 0x0F, 0x00, 0x00,
},
{ /* Init_CR00_CR18 */
0x7F, 0x63, 0x63, 0x00, 0x68, 0x18, 0x72, 0xF0,
0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x58, 0x0C, 0x57, 0x64, 0x40, 0x57, 0x00, 0xE3,
0xFF,
},
{ /* Init_CR30_CR4D */
0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x03, 0x20,
0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xBF, 0xFD,
0x7F, 0x63, 0x00, 0x69, 0x18, 0x72, 0x57, 0x00,
0x58, 0x0C, 0xE0, 0x20, 0x63, 0x57,
},
{ /* Init_CR90_CRA7 */
0x56, 0x4B, 0x5E, 0x55, 0x86, 0x9D, 0x8E, 0xAA,
0xDB, 0x2A, 0xDF, 0x33, 0x00, 0x00, 0x18, 0x00,
0x20, 0x1F, 0x1A, 0x19, 0x0F, 0x0F, 0x0F, 0x00,
},
},
{ /* mode#3: 800 x 600 24Bpp 60Hz */
800, 600, 24, 60,
0x2B,
{ /* Init_SR0_SR4 */
0x03, 0x01, 0x0F, 0x03, 0x0E,
},
{ /* Init_SR10_SR24 */
0xFF, 0xBE, 0xEE, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC4, 0x30, 0x02, 0x01, 0x01,
},
{ /* Init_SR30_SR75 */
0x36, 0x03, 0x20, 0x09, 0xC0, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x03, 0xFF,
0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
0x20, 0x0C, 0x44, 0x20, 0x00, 0x36, 0x36, 0x36,
0x04, 0x48, 0x83, 0x63, 0x68, 0x72, 0x57, 0x58,
0x04, 0x55, 0x59, 0x36, 0x36, 0x00, 0x00, 0x36,
0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
0x50, 0x03, 0x74, 0x14, 0x1C, 0x85, 0x35, 0x13,
0x02, 0x45, 0x30, 0x30, 0x40, 0x20,
},
{ /* Init_SR80_SR93 */
0xFF, 0x07, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x36,
0xF7, 0x00, 0x00, 0x00, 0xEF, 0xFF, 0x36, 0x36,
0x00, 0x00, 0x00, 0x00,
},
{ /* Init_SRA0_SRAF */
0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xBF, 0xDF,
},
{ /* Init_GR00_GR08 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
0xFF,
},
{ /* Init_AR00_AR14 */
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x41, 0x00, 0x0F, 0x00, 0x00,
},
{ /* Init_CR00_CR18 */
0x7F, 0x63, 0x63, 0x00, 0x68, 0x18, 0x72, 0xF0,
0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x58, 0x0C, 0x57, 0x64, 0x40, 0x57, 0x00, 0xE3,
0xFF,
},
{ /* Init_CR30_CR4D */
0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x03, 0x20,
0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xBF, 0xFD,
0x7F, 0x63, 0x00, 0x69, 0x18, 0x72, 0x57, 0x00,
0x58, 0x0C, 0xE0, 0x20, 0x63, 0x57,
},
{ /* Init_CR90_CRA7 */
0x56, 0x4B, 0x5E, 0x55, 0x86, 0x9D, 0x8E, 0xAA,
0xDB, 0x2A, 0xDF, 0x33, 0x00, 0x00, 0x18, 0x00,
0x20, 0x1F, 0x1A, 0x19, 0x0F, 0x0F, 0x0F, 0x00,
},
},
{ /* mode#7: 800 x 600 32Bpp 60Hz */
800, 600, 32, 60,
/* Init_MISC */
0x2B,
{ /* Init_SR0_SR4 */
0x03, 0x01, 0x0F, 0x03, 0x0E,
},
{ /* Init_SR10_SR24 */
0xFF, 0xBE, 0xEE, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
0x99, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC4, 0x30, 0x02, 0x01, 0x01,
},
{ /* Init_SR30_SR75 */
0x34, 0x03, 0x20, 0x09, 0xC0, 0x24, 0x24, 0x24,
0x24, 0x24, 0x24, 0x24, 0x00, 0x00, 0x03, 0xFF,
0x00, 0xFC, 0x00, 0x00, 0x20, 0x38, 0x00, 0xFC,
0x20, 0x0C, 0x44, 0x20, 0x00, 0x24, 0x24, 0x24,
0x04, 0x48, 0x83, 0x63, 0x68, 0x72, 0x57, 0x58,
0x04, 0x55, 0x59, 0x24, 0x24, 0x00, 0x00, 0x24,
0x01, 0x80, 0x7A, 0x1A, 0x1A, 0x00, 0x00, 0x00,
0x50, 0x03, 0x74, 0x14, 0x1C, 0x85, 0x35, 0x13,
0x02, 0x45, 0x30, 0x35, 0x40, 0x20,
},
{ /* Init_SR80_SR93 */
0x00, 0x00, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x24,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x24, 0x24,
0x00, 0x00, 0x00, 0x00,
},
{ /* Init_SRA0_SRAF */
0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xBF, 0xDF,
},
{ /* Init_GR00_GR08 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
0xFF,
},
{ /* Init_AR00_AR14 */
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x41, 0x00, 0x0F, 0x00, 0x00,
},
{ /* Init_CR00_CR18 */
0x7F, 0x63, 0x63, 0x00, 0x68, 0x18, 0x72, 0xF0,
0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x58, 0x0C, 0x57, 0x64, 0x40, 0x57, 0x00, 0xE3,
0xFF,
},
{ /* Init_CR30_CR4D */
0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x03, 0x20,
0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xBF, 0xFD,
0x7F, 0x63, 0x00, 0x69, 0x18, 0x72, 0x57, 0x00,
0x58, 0x0C, 0xE0, 0x20, 0x63, 0x57,
},
{ /* Init_CR90_CRA7 */
0x56, 0x4B, 0x5E, 0x55, 0x86, 0x9D, 0x8E, 0xAA,
0xDB, 0x2A, 0xDF, 0x33, 0x00, 0x00, 0x18, 0x00,
0x20, 0x1F, 0x1A, 0x19, 0x0F, 0x0F, 0x0F, 0x00,
},
},
/* We use 1024x768 table to light 1024x600 panel for lemote */
{ /* mode#4: 1024 x 600 16Bpp 60Hz */
1024, 600, 16, 60,
/* Init_MISC */
0xEB,
{ /* Init_SR0_SR4 */
0x03, 0x01, 0x0F, 0x00, 0x0E,
},
{ /* Init_SR10_SR24 */
0xC8, 0x40, 0x14, 0x60, 0x00, 0x0A, 0x17, 0x20,
0x51, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC4, 0x30, 0x02, 0x00, 0x01,
},
{ /* Init_SR30_SR75 */
0x22, 0x03, 0x24, 0x09, 0xC0, 0x22, 0x22, 0x22,
0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x03, 0xFF,
0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
0x20, 0x0C, 0x44, 0x20, 0x00, 0x22, 0x22, 0x22,
0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
0x00, 0x60, 0x59, 0x22, 0x22, 0x00, 0x00, 0x22,
0x01, 0x80, 0x7A, 0x1A, 0x1A, 0x00, 0x00, 0x00,
0x50, 0x03, 0x16, 0x02, 0x0D, 0x82, 0x09, 0x02,
0x04, 0x45, 0x3F, 0x30, 0x40, 0x20,
},
{ /* Init_SR80_SR93 */
0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
0x00, 0x00, 0x00, 0x00,
},
{ /* Init_SRA0_SRAF */
0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
},
{ /* Init_GR00_GR08 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
0xFF,
},
{ /* Init_AR00_AR14 */
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x41, 0x00, 0x0F, 0x00, 0x00,
},
{ /* Init_CR00_CR18 */
0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
0xFF,
},
{ /* Init_CR30_CR4D */
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
0x00, 0x00, 0x00, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
0xA3, 0x7F, 0x00, 0x82, 0x0b, 0x6f, 0x57, 0x00,
0x5c, 0x0f, 0xE0, 0xe0, 0x7F, 0x57,
},
{ /* Init_CR90_CRA7 */
0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
},
},
{ /* 1024 x 768 16Bpp 60Hz */
1024, 768, 16, 60,
/* Init_MISC */
0xEB,
{ /* Init_SR0_SR4 */
0x03, 0x01, 0x0F, 0x03, 0x0E,
},
{ /* Init_SR10_SR24 */
0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC4, 0x30, 0x02, 0x01, 0x01,
},
{ /* Init_SR30_SR75 */
0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
0x0F, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
0x50, 0x03, 0x74, 0x14, 0x3B, 0x0D, 0x09, 0x02,
0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
},
{ /* Init_SR80_SR93 */
0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
0x00, 0x00, 0x00, 0x00,
},
{ /* Init_SRA0_SRAF */
0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
},
{ /* Init_GR00_GR08 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
0xFF,
},
{ /* Init_AR00_AR14 */
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x41, 0x00, 0x0F, 0x00, 0x00,
},
{ /* Init_CR00_CR18 */
0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
0xFF,
},
{ /* Init_CR30_CR4D */
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
0x00, 0x00, 0x00, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
0xA3, 0x7F, 0x00, 0x86, 0x15, 0x24, 0xFF, 0x00,
0x01, 0x07, 0xE5, 0x20, 0x7F, 0xFF,
},
{ /* Init_CR90_CRA7 */
0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
},
},
{ /* mode#5: 1024 x 768 24Bpp 60Hz */
1024, 768, 24, 60,
/* Init_MISC */
0xEB,
{ /* Init_SR0_SR4 */
0x03, 0x01, 0x0F, 0x03, 0x0E,
},
{ /* Init_SR10_SR24 */
0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC4, 0x30, 0x02, 0x01, 0x01,
},
{ /* Init_SR30_SR75 */
0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
0x00, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
0x50, 0x03, 0x74, 0x14, 0x3B, 0x0D, 0x09, 0x02,
0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
},
{ /* Init_SR80_SR93 */
0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
0x00, 0x00, 0x00, 0x00,
},
{ /* Init_SRA0_SRAF */
0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
},
{ /* Init_GR00_GR08 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
0xFF,
},
{ /* Init_AR00_AR14 */
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x41, 0x00, 0x0F, 0x00, 0x00,
},
{ /* Init_CR00_CR18 */
0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
0xFF,
},
{ /* Init_CR30_CR4D */
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
0x00, 0x00, 0x00, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
0xA3, 0x7F, 0x00, 0x86, 0x15, 0x24, 0xFF, 0x00,
0x01, 0x07, 0xE5, 0x20, 0x7F, 0xFF,
},
{ /* Init_CR90_CRA7 */
0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
},
},
{ /* mode#4: 1024 x 768 32Bpp 60Hz */
1024, 768, 32, 60,
/* Init_MISC */
0xEB,
{ /* Init_SR0_SR4 */
0x03, 0x01, 0x0F, 0x03, 0x0E,
},
{ /* Init_SR10_SR24 */
0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC4, 0x32, 0x02, 0x01, 0x01,
},
{ /* Init_SR30_SR75 */
0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
0x00, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
0x50, 0x03, 0x74, 0x14, 0x3B, 0x0D, 0x09, 0x02,
0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
},
{ /* Init_SR80_SR93 */
0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
0x00, 0x00, 0x00, 0x00,
},
{ /* Init_SRA0_SRAF */
0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
},
{ /* Init_GR00_GR08 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
0xFF,
},
{ /* Init_AR00_AR14 */
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x41, 0x00, 0x0F, 0x00, 0x00,
},
{ /* Init_CR00_CR18 */
0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
0xFF,
},
{ /* Init_CR30_CR4D */
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
0x00, 0x00, 0x00, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
0xA3, 0x7F, 0x00, 0x86, 0x15, 0x24, 0xFF, 0x00,
0x01, 0x07, 0xE5, 0x20, 0x7F, 0xFF,
},
{ /* Init_CR90_CRA7 */
0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
},
},
{ /* mode#6: 320 x 240 16Bpp 60Hz */
320, 240, 16, 60,
/* Init_MISC */
0xEB,
{ /* Init_SR0_SR4 */
0x03, 0x01, 0x0F, 0x03, 0x0E,
},
{ /* Init_SR10_SR24 */
0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC4, 0x32, 0x02, 0x01, 0x01,
},
{ /* Init_SR30_SR75 */
0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
0x00, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
0x50, 0x03, 0x74, 0x14, 0x08, 0x43, 0x08, 0x43,
0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
},
{ /* Init_SR80_SR93 */
0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
0x00, 0x00, 0x00, 0x00,
},
{ /* Init_SRA0_SRAF */
0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
},
{ /* Init_GR00_GR08 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
0xFF,
},
{ /* Init_AR00_AR14 */
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x41, 0x00, 0x0F, 0x00, 0x00,
},
{ /* Init_CR00_CR18 */
0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
0xFF,
},
{ /* Init_CR30_CR4D */
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
0x00, 0x00, 0x30, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
0x2E, 0x27, 0x00, 0x2b, 0x0c, 0x0F, 0xEF, 0x00,
0xFe, 0x0f, 0x01, 0xC0, 0x27, 0xEF,
},
{ /* Init_CR90_CRA7 */
0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
},
},
{ /* mode#8: 320 x 240 32Bpp 60Hz */
320, 240, 32, 60,
/* Init_MISC */
0xEB,
{ /* Init_SR0_SR4 */
0x03, 0x01, 0x0F, 0x03, 0x0E,
},
{ /* Init_SR10_SR24 */
0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC4, 0x32, 0x02, 0x01, 0x01,
},
{ /* Init_SR30_SR75 */
0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
0x00, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
0x50, 0x03, 0x74, 0x14, 0x08, 0x43, 0x08, 0x43,
0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
},
{ /* Init_SR80_SR93 */
0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
0x00, 0x00, 0x00, 0x00,
},
{ /* Init_SRA0_SRAF */
0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
},
{ /* Init_GR00_GR08 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
0xFF,
},
{ /* Init_AR00_AR14 */
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x41, 0x00, 0x0F, 0x00, 0x00,
},
{ /* Init_CR00_CR18 */
0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
0xFF,
},
{ /* Init_CR30_CR4D */
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
0x00, 0x00, 0x30, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
0x2E, 0x27, 0x00, 0x2b, 0x0c, 0x0F, 0xEF, 0x00,
0xFe, 0x0f, 0x01, 0xC0, 0x27, 0xEF,
},
{ /* Init_CR90_CRA7 */
0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
},
},
};
static struct screen_info smtc_scr_info;
static char *mode_option;
/* process command line options, get vga parameter */
static void __init sm7xx_vga_setup(char *options)
{
int i;
if (!options || !*options)
return;
smtc_scr_info.lfb_width = 0;
smtc_scr_info.lfb_height = 0;
smtc_scr_info.lfb_depth = 0;
pr_debug("%s = %s\n", __func__, options);
for (i = 0; i < ARRAY_SIZE(vesa_mode_table); i++) {
if (strstr(options, vesa_mode_table[i].index)) {
smtc_scr_info.lfb_width = vesa_mode_table[i].lfb_width;
smtc_scr_info.lfb_height =
vesa_mode_table[i].lfb_height;
smtc_scr_info.lfb_depth = vesa_mode_table[i].lfb_depth;
return;
}
}
}
static void sm712_setpalette(int regno, unsigned int red, unsigned int green,
unsigned int blue, struct fb_info *info)
{
/* set bit 5:4 = 01 (write LCD RAM only) */
smtc_seqw(0x66, (smtc_seqr(0x66) & 0xC3) | 0x10);
smtc_mmiowb(regno, dac_reg);
smtc_mmiowb(red >> 10, dac_val);
smtc_mmiowb(green >> 10, dac_val);
smtc_mmiowb(blue >> 10, dac_val);
}
/* chan_to_field
*
* convert a colour value into a field position
*
* from pxafb.c
*/
static inline unsigned int chan_to_field(unsigned int chan,
struct fb_bitfield *bf)
{
chan &= 0xffff;
chan >>= 16 - bf->length;
return chan << bf->offset;
}
static int smtc_blank(int blank_mode, struct fb_info *info)
{
struct smtcfb_info *sfb = info->par;
/* clear DPMS setting */
switch (blank_mode) {
case FB_BLANK_UNBLANK:
/* Screen On: HSync: On, VSync : On */
switch (sfb->chip_id) {
case 0x710:
case 0x712:
smtc_seqw(0x6a, 0x16);
smtc_seqw(0x6b, 0x02);
break;
case 0x720:
smtc_seqw(0x6a, 0x0d);
smtc_seqw(0x6b, 0x02);
break;
}
smtc_seqw(0x23, (smtc_seqr(0x23) & (~0xc0)));
smtc_seqw(0x01, (smtc_seqr(0x01) & (~0x20)));
smtc_seqw(0x21, (smtc_seqr(0x21) & 0x77));
smtc_seqw(0x22, (smtc_seqr(0x22) & (~0x30)));
smtc_seqw(0x31, (smtc_seqr(0x31) | 0x03));
smtc_seqw(0x24, (smtc_seqr(0x24) | 0x01));
break;
case FB_BLANK_NORMAL:
/* Screen Off: HSync: On, VSync : On Soft blank */
smtc_seqw(0x24, (smtc_seqr(0x24) | 0x01));
smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
smtc_seqw(0x23, (smtc_seqr(0x23) & (~0xc0)));
smtc_seqw(0x01, (smtc_seqr(0x01) & (~0x20)));
smtc_seqw(0x22, (smtc_seqr(0x22) & (~0x30)));
smtc_seqw(0x6a, 0x16);
smtc_seqw(0x6b, 0x02);
break;
case FB_BLANK_VSYNC_SUSPEND:
/* Screen On: HSync: On, VSync : Off */
smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01)));
smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0x20));
smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20));
smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88));
smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0)));
smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x20));
smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80));
smtc_seqw(0x6a, 0x0c);
smtc_seqw(0x6b, 0x02);
break;
case FB_BLANK_HSYNC_SUSPEND:
/* Screen On: HSync: Off, VSync : On */
smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01)));
smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0xD8));
smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20));
smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88));
smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0)));
smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x10));
smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80));
smtc_seqw(0x6a, 0x0c);
smtc_seqw(0x6b, 0x02);
break;
case FB_BLANK_POWERDOWN:
/* Screen On: HSync: Off, VSync : Off */
smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01)));
smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0xD8));
smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20));
smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88));
smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0)));
smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x30));
smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80));
smtc_seqw(0x6a, 0x0c);
smtc_seqw(0x6b, 0x02);
break;
default:
return -EINVAL;
}
return 0;
}
static int smtc_setcolreg(unsigned int regno, unsigned int red,
unsigned int green, unsigned int blue,
unsigned int trans, struct fb_info *info)
{
struct smtcfb_info *sfb;
u32 val;
sfb = info->par;
if (regno > 255)
return 1;
switch (sfb->fb->fix.visual) {
case FB_VISUAL_DIRECTCOLOR:
case FB_VISUAL_TRUECOLOR:
/*
* 16/32 bit true-colour, use pseudo-palette for 16 base color
*/
if (regno >= 16)
break;
if (sfb->fb->var.bits_per_pixel == 16) {
u32 *pal = sfb->fb->pseudo_palette;
val = chan_to_field(red, &sfb->fb->var.red);
val |= chan_to_field(green, &sfb->fb->var.green);
val |= chan_to_field(blue, &sfb->fb->var.blue);
pal[regno] = pal_rgb(red, green, blue, val);
} else {
u32 *pal = sfb->fb->pseudo_palette;
val = chan_to_field(red, &sfb->fb->var.red);
val |= chan_to_field(green, &sfb->fb->var.green);
val |= chan_to_field(blue, &sfb->fb->var.blue);
pal[regno] = big_swap(val);
}
break;
case FB_VISUAL_PSEUDOCOLOR:
/* color depth 8 bit */
sm712_setpalette(regno, red, green, blue, info);
break;
default:
return 1; /* unknown type */
}
return 0;
}
static ssize_t smtcfb_read(struct fb_info *info, char __user *buf,
size_t count, loff_t *ppos)
{
unsigned long p = *ppos;
u32 *buffer, *dst;
u32 __iomem *src;
int c, i, cnt = 0, err = 0;
unsigned long total_size;
if (!info || !info->screen_base)
return -ENODEV;
if (info->state != FBINFO_STATE_RUNNING)
return -EPERM;
total_size = info->screen_size;
if (total_size == 0)
total_size = info->fix.smem_len;
if (p >= total_size)
return 0;
if (count >= total_size)
count = total_size;
if (count + p > total_size)
count = total_size - p;
buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
src = (u32 __iomem *)(info->screen_base + p);
if (info->fbops->fb_sync)
info->fbops->fb_sync(info);
while (count) {
c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
dst = buffer;
for (i = c >> 2; i--;) {
*dst = fb_readl(src++);
*dst = big_swap(*dst);
dst++;
}
if (c & 3) {
u8 *dst8 = (u8 *)dst;
u8 __iomem *src8 = (u8 __iomem *)src;
for (i = c & 3; i--;) {
if (i & 1) {
*dst8++ = fb_readb(++src8);
} else {
*dst8++ = fb_readb(--src8);
src8 += 2;
}
}
src = (u32 __iomem *)src8;
}
if (copy_to_user(buf, buffer, c)) {
err = -EFAULT;
break;
}
*ppos += c;
buf += c;
cnt += c;
count -= c;
}
kfree(buffer);
return (err) ? err : cnt;
}
static ssize_t smtcfb_write(struct fb_info *info, const char __user *buf,
size_t count, loff_t *ppos)
{
unsigned long p = *ppos;
u32 *buffer, *src;
u32 __iomem *dst;
int c, i, cnt = 0, err = 0;
unsigned long total_size;
if (!info || !info->screen_base)
return -ENODEV;
if (info->state != FBINFO_STATE_RUNNING)
return -EPERM;
total_size = info->screen_size;
if (total_size == 0)
total_size = info->fix.smem_len;
if (p > total_size)
return -EFBIG;
if (count > total_size) {
err = -EFBIG;
count = total_size;
}
if (count + p > total_size) {
if (!err)
err = -ENOSPC;
count = total_size - p;
}
buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
dst = (u32 __iomem *)(info->screen_base + p);
if (info->fbops->fb_sync)
info->fbops->fb_sync(info);
while (count) {
c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
src = buffer;
if (copy_from_user(src, buf, c)) {
err = -EFAULT;
break;
}
for (i = c >> 2; i--;) {
fb_writel(big_swap(*src), dst++);
src++;
}
if (c & 3) {
u8 *src8 = (u8 *)src;
u8 __iomem *dst8 = (u8 __iomem *)dst;
for (i = c & 3; i--;) {
if (i & 1) {
fb_writeb(*src8++, ++dst8);
} else {
fb_writeb(*src8++, --dst8);
dst8 += 2;
}
}
dst = (u32 __iomem *)dst8;
}
*ppos += c;
buf += c;
cnt += c;
count -= c;
}
kfree(buffer);
return (cnt) ? cnt : err;
}
static void sm7xx_set_timing(struct smtcfb_info *sfb)
{
int i = 0, j = 0;
u32 m_nscreenstride;
dev_dbg(&sfb->pdev->dev,
"sfb->width=%d sfb->height=%d sfb->fb->var.bits_per_pixel=%d sfb->hz=%d\n",
sfb->width, sfb->height, sfb->fb->var.bits_per_pixel, sfb->hz);
for (j = 0; j < ARRAY_SIZE(vgamode); j++) {
if (vgamode[j].mmsizex != sfb->width ||
vgamode[j].mmsizey != sfb->height ||
vgamode[j].bpp != sfb->fb->var.bits_per_pixel ||
vgamode[j].hz != sfb->hz)
continue;
dev_dbg(&sfb->pdev->dev,
"vgamode[j].mmsizex=%d vgamode[j].mmSizeY=%d vgamode[j].bpp=%d vgamode[j].hz=%d\n",
vgamode[j].mmsizex, vgamode[j].mmsizey,
vgamode[j].bpp, vgamode[j].hz);
dev_dbg(&sfb->pdev->dev, "vgamode index=%d\n", j);
smtc_mmiowb(0x0, 0x3c6);
smtc_seqw(0, 0x1);
smtc_mmiowb(vgamode[j].init_misc, 0x3c2);
/* init SEQ register SR00 - SR04 */
for (i = 0; i < SIZE_SR00_SR04; i++)
smtc_seqw(i, vgamode[j].init_sr00_sr04[i]);
/* init SEQ register SR10 - SR24 */
for (i = 0; i < SIZE_SR10_SR24; i++)
smtc_seqw(i + 0x10, vgamode[j].init_sr10_sr24[i]);
/* init SEQ register SR30 - SR75 */
for (i = 0; i < SIZE_SR30_SR75; i++)
if ((i + 0x30) != 0x30 && (i + 0x30) != 0x62 &&
(i + 0x30) != 0x6a && (i + 0x30) != 0x6b &&
(i + 0x30) != 0x70 && (i + 0x30) != 0x71 &&
(i + 0x30) != 0x74 && (i + 0x30) != 0x75)
smtc_seqw(i + 0x30,
vgamode[j].init_sr30_sr75[i]);
/* init SEQ register SR80 - SR93 */
for (i = 0; i < SIZE_SR80_SR93; i++)
smtc_seqw(i + 0x80, vgamode[j].init_sr80_sr93[i]);
/* init SEQ register SRA0 - SRAF */
for (i = 0; i < SIZE_SRA0_SRAF; i++)
smtc_seqw(i + 0xa0, vgamode[j].init_sra0_sraf[i]);
/* init Graphic register GR00 - GR08 */
for (i = 0; i < SIZE_GR00_GR08; i++)
smtc_grphw(i, vgamode[j].init_gr00_gr08[i]);
/* init Attribute register AR00 - AR14 */
for (i = 0; i < SIZE_AR00_AR14; i++)
smtc_attrw(i, vgamode[j].init_ar00_ar14[i]);
/* init CRTC register CR00 - CR18 */
for (i = 0; i < SIZE_CR00_CR18; i++)
smtc_crtcw(i, vgamode[j].init_cr00_cr18[i]);
/* init CRTC register CR30 - CR4D */
for (i = 0; i < SIZE_CR30_CR4D; i++) {
if ((i + 0x30) >= 0x3B && (i + 0x30) <= 0x3F)
/* side-effect, don't write to CR3B-CR3F */
continue;
smtc_crtcw(i + 0x30, vgamode[j].init_cr30_cr4d[i]);
}
/* init CRTC register CR90 - CRA7 */
for (i = 0; i < SIZE_CR90_CRA7; i++)
smtc_crtcw(i + 0x90, vgamode[j].init_cr90_cra7[i]);
}
smtc_mmiowb(0x67, 0x3c2);
/* set VPR registers */
writel(0x0, sfb->vp_regs + 0x0C);
writel(0x0, sfb->vp_regs + 0x40);
/* set data width */
m_nscreenstride = (sfb->width * sfb->fb->var.bits_per_pixel) / 64;
switch (sfb->fb->var.bits_per_pixel) {
case 8:
writel(0x0, sfb->vp_regs + 0x0);
break;
case 16:
writel(0x00020000, sfb->vp_regs + 0x0);
break;
case 24:
writel(0x00040000, sfb->vp_regs + 0x0);
break;
case 32:
writel(0x00030000, sfb->vp_regs + 0x0);
break;
}
writel((u32)(((m_nscreenstride + 2) << 16) | m_nscreenstride),
sfb->vp_regs + 0x10);
}
static void smtc_set_timing(struct smtcfb_info *sfb)
{
switch (sfb->chip_id) {
case 0x710:
case 0x712:
case 0x720:
sm7xx_set_timing(sfb);
break;
}
}
static void smtcfb_setmode(struct smtcfb_info *sfb)
{
switch (sfb->fb->var.bits_per_pixel) {
case 32:
sfb->fb->fix.visual = FB_VISUAL_TRUECOLOR;
sfb->fb->fix.line_length = sfb->fb->var.xres * 4;
sfb->fb->var.red.length = 8;
sfb->fb->var.green.length = 8;
sfb->fb->var.blue.length = 8;
sfb->fb->var.red.offset = 16;
sfb->fb->var.green.offset = 8;
sfb->fb->var.blue.offset = 0;
break;
case 24:
sfb->fb->fix.visual = FB_VISUAL_TRUECOLOR;
sfb->fb->fix.line_length = sfb->fb->var.xres * 3;
sfb->fb->var.red.length = 8;
sfb->fb->var.green.length = 8;
sfb->fb->var.blue.length = 8;
sfb->fb->var.red.offset = 16;
sfb->fb->var.green.offset = 8;
sfb->fb->var.blue.offset = 0;
break;
case 8:
sfb->fb->fix.visual = FB_VISUAL_PSEUDOCOLOR;
sfb->fb->fix.line_length = sfb->fb->var.xres;
sfb->fb->var.red.length = 3;
sfb->fb->var.green.length = 3;
sfb->fb->var.blue.length = 2;
sfb->fb->var.red.offset = 5;
sfb->fb->var.green.offset = 2;
sfb->fb->var.blue.offset = 0;
break;
case 16:
default:
sfb->fb->fix.visual = FB_VISUAL_TRUECOLOR;
sfb->fb->fix.line_length = sfb->fb->var.xres * 2;
sfb->fb->var.red.length = 5;
sfb->fb->var.green.length = 6;
sfb->fb->var.blue.length = 5;
sfb->fb->var.red.offset = 11;
sfb->fb->var.green.offset = 5;
sfb->fb->var.blue.offset = 0;
break;
}
sfb->width = sfb->fb->var.xres;
sfb->height = sfb->fb->var.yres;
sfb->hz = 60;
smtc_set_timing(sfb);
}
static int smtc_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
/* sanity checks */
if (var->xres_virtual < var->xres)
var->xres_virtual = var->xres;
if (var->yres_virtual < var->yres)
var->yres_virtual = var->yres;
/* set valid default bpp */
if ((var->bits_per_pixel != 8) && (var->bits_per_pixel != 16) &&
(var->bits_per_pixel != 24) && (var->bits_per_pixel != 32))
var->bits_per_pixel = 16;
return 0;
}
static int smtc_set_par(struct fb_info *info)
{
smtcfb_setmode(info->par);
return 0;
}
static struct fb_ops smtcfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = smtc_check_var,
.fb_set_par = smtc_set_par,
.fb_setcolreg = smtc_setcolreg,
.fb_blank = smtc_blank,
.fb_fillrect = cfb_fillrect,
.fb_imageblit = cfb_imageblit,
.fb_copyarea = cfb_copyarea,
.fb_read = smtcfb_read,
.fb_write = smtcfb_write,
};
/*
* Unmap in the memory mapped IO registers
*/
static void smtc_unmap_mmio(struct smtcfb_info *sfb)
{
if (sfb && smtc_regbaseaddress)
smtc_regbaseaddress = NULL;
}
/*
* Map in the screen memory
*/
static int smtc_map_smem(struct smtcfb_info *sfb,
struct pci_dev *pdev, u_long smem_len)
{
sfb->fb->fix.smem_start = pci_resource_start(pdev, 0);
fbdev: sm712fb: fix crashes during framebuffer writes by correctly mapping VRAM On a Thinkpad s30 (Pentium III / i440MX, Lynx3DM), running fbtest or X will crash the machine instantly, because the VRAM/framebuffer is not mapped correctly. On SM712, the framebuffer starts at the beginning of address space, but SM720's framebuffer starts at the 1 MiB offset from the beginning. However, sm712fb fails to take this into account, as a result, writing to the framebuffer will destroy all the registers and kill the system immediately. Another problem is the driver assumes 8 MiB of VRAM for SM720, but some SM720 system, such as this IBM Thinkpad, only has 4 MiB of VRAM. Fix this problem by removing the hardcoded VRAM size, adding a function to query the amount of VRAM from register MCR76 on SM720, and adding proper framebuffer offset. Please note that the memory map may have additional problems on Big-Endian system, which is not available for testing by myself. But I highly suspect that the original code is also broken on Big-Endian machines for SM720, so at least we are not making the problem worse. More, the driver also assumed SM710/SM712 has 4 MiB of VRAM, but it has a 2 MiB version as well, and used in earlier laptops, such as IBM Thinkpad 240X, the driver would probably crash on them. I've never seen one of those machines and cannot fix it, but I have documented these problems in the comments. Signed-off-by: Yifeng Li <tomli@tomli.me> Tested-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com> Cc: Teddy Wang <teddy.wang@siliconmotion.com> Cc: <stable@vger.kernel.org> # v4.4+ Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
2019-04-01 22:46:59 +07:00
if (sfb->chip_id == 0x720)
/* on SM720, the framebuffer starts at the 1 MB offset */
sfb->fb->fix.smem_start += 0x00200000;
/* XXX: is it safe for SM720 on Big-Endian? */
if (sfb->fb->var.bits_per_pixel == 32)
sfb->fb->fix.smem_start += big_addr;
sfb->fb->fix.smem_len = smem_len;
sfb->fb->screen_base = sfb->lfb;
if (!sfb->fb->screen_base) {
dev_err(&pdev->dev,
"%s: unable to map screen memory\n", sfb->fb->fix.id);
return -ENOMEM;
}
return 0;
}
/*
* Unmap in the screen memory
*
*/
static void smtc_unmap_smem(struct smtcfb_info *sfb)
{
if (sfb && sfb->fb->screen_base) {
iounmap(sfb->fb->screen_base);
sfb->fb->screen_base = NULL;
}
}
/*
* We need to wake up the device and make sure its in linear memory mode.
*/
static inline void sm7xx_init_hw(void)
{
outb_p(0x18, 0x3c4);
outb_p(0x11, 0x3c5);
}
fbdev: sm712fb: fix crashes during framebuffer writes by correctly mapping VRAM On a Thinkpad s30 (Pentium III / i440MX, Lynx3DM), running fbtest or X will crash the machine instantly, because the VRAM/framebuffer is not mapped correctly. On SM712, the framebuffer starts at the beginning of address space, but SM720's framebuffer starts at the 1 MiB offset from the beginning. However, sm712fb fails to take this into account, as a result, writing to the framebuffer will destroy all the registers and kill the system immediately. Another problem is the driver assumes 8 MiB of VRAM for SM720, but some SM720 system, such as this IBM Thinkpad, only has 4 MiB of VRAM. Fix this problem by removing the hardcoded VRAM size, adding a function to query the amount of VRAM from register MCR76 on SM720, and adding proper framebuffer offset. Please note that the memory map may have additional problems on Big-Endian system, which is not available for testing by myself. But I highly suspect that the original code is also broken on Big-Endian machines for SM720, so at least we are not making the problem worse. More, the driver also assumed SM710/SM712 has 4 MiB of VRAM, but it has a 2 MiB version as well, and used in earlier laptops, such as IBM Thinkpad 240X, the driver would probably crash on them. I've never seen one of those machines and cannot fix it, but I have documented these problems in the comments. Signed-off-by: Yifeng Li <tomli@tomli.me> Tested-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com> Cc: Teddy Wang <teddy.wang@siliconmotion.com> Cc: <stable@vger.kernel.org> # v4.4+ Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
2019-04-01 22:46:59 +07:00
static u_long sm7xx_vram_probe(struct smtcfb_info *sfb)
{
u8 vram;
switch (sfb->chip_id) {
case 0x710:
case 0x712:
/*
* Assume SM712 graphics chip has 4MB VRAM.
*
* FIXME: SM712 can have 2MB VRAM, which is used on earlier
* laptops, such as IBM Thinkpad 240X. This driver would
* probably crash on those machines. If anyone gets one of
* those and is willing to help, run "git blame" and send me
* an E-mail.
*/
return 0x00400000;
case 0x720:
outb_p(0x76, 0x3c4);
vram = inb_p(0x3c5) >> 6;
if (vram == 0x00)
return 0x00800000; /* 8 MB */
else if (vram == 0x01)
return 0x01000000; /* 16 MB */
else if (vram == 0x02)
return 0x00400000; /* illegal, fallback to 4 MB */
else if (vram == 0x03)
return 0x00400000; /* 4 MB */
}
return 0; /* unknown hardware */
}
static void sm7xx_resolution_probe(struct smtcfb_info *sfb)
{
/* get mode parameter from smtc_scr_info */
if (smtc_scr_info.lfb_width != 0) {
sfb->fb->var.xres = smtc_scr_info.lfb_width;
sfb->fb->var.yres = smtc_scr_info.lfb_height;
sfb->fb->var.bits_per_pixel = smtc_scr_info.lfb_depth;
goto final;
}
/*
* No parameter, default resolution is 1024x768-16.
*
* FIXME: earlier laptops, such as IBM Thinkpad 240X, has a 800x600
* panel, also see the comments about Thinkpad 240X above.
*/
sfb->fb->var.xres = SCREEN_X_RES;
sfb->fb->var.yres = SCREEN_Y_RES_PC;
sfb->fb->var.bits_per_pixel = SCREEN_BPP;
#ifdef CONFIG_MIPS
/*
* Loongson MIPS netbooks use 1024x600 LCD panels, which is the original
* target platform of this driver, but nearly all old x86 laptops have
* 1024x768. Lighting 768 panels using 600's timings would partially
* garble the display, so we don't want that. But it's not possible to
* distinguish them reliably.
*
* So we change the default to 768, but keep 600 as-is on MIPS.
*/
sfb->fb->var.yres = SCREEN_Y_RES_NETBOOK;
#endif
final:
big_pixel_depth(sfb->fb->var.bits_per_pixel, smtc_scr_info.lfb_depth);
}
static int smtcfb_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct smtcfb_info *sfb;
struct fb_info *info;
fbdev: sm712fb: fix crashes during framebuffer writes by correctly mapping VRAM On a Thinkpad s30 (Pentium III / i440MX, Lynx3DM), running fbtest or X will crash the machine instantly, because the VRAM/framebuffer is not mapped correctly. On SM712, the framebuffer starts at the beginning of address space, but SM720's framebuffer starts at the 1 MiB offset from the beginning. However, sm712fb fails to take this into account, as a result, writing to the framebuffer will destroy all the registers and kill the system immediately. Another problem is the driver assumes 8 MiB of VRAM for SM720, but some SM720 system, such as this IBM Thinkpad, only has 4 MiB of VRAM. Fix this problem by removing the hardcoded VRAM size, adding a function to query the amount of VRAM from register MCR76 on SM720, and adding proper framebuffer offset. Please note that the memory map may have additional problems on Big-Endian system, which is not available for testing by myself. But I highly suspect that the original code is also broken on Big-Endian machines for SM720, so at least we are not making the problem worse. More, the driver also assumed SM710/SM712 has 4 MiB of VRAM, but it has a 2 MiB version as well, and used in earlier laptops, such as IBM Thinkpad 240X, the driver would probably crash on them. I've never seen one of those machines and cannot fix it, but I have documented these problems in the comments. Signed-off-by: Yifeng Li <tomli@tomli.me> Tested-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com> Cc: Teddy Wang <teddy.wang@siliconmotion.com> Cc: <stable@vger.kernel.org> # v4.4+ Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
2019-04-01 22:46:59 +07:00
u_long smem_size;
int err;
unsigned long mmio_base;
dev_info(&pdev->dev, "Silicon Motion display driver.\n");
err = pci_enable_device(pdev); /* enable SMTC chip */
if (err)
return err;
err = pci_request_region(pdev, 0, "sm7xxfb");
if (err < 0) {
dev_err(&pdev->dev, "cannot reserve framebuffer region\n");
goto failed_regions;
}
sprintf(smtcfb_fix.id, "sm%Xfb", ent->device);
info = framebuffer_alloc(sizeof(*sfb), &pdev->dev);
if (!info) {
dev_err(&pdev->dev, "framebuffer_alloc failed\n");
err = -ENOMEM;
goto failed_free;
}
sfb = info->par;
sfb->fb = info;
sfb->chip_id = ent->device;
sfb->pdev = pdev;
info->flags = FBINFO_FLAG_DEFAULT;
info->fbops = &smtcfb_ops;
info->fix = smtcfb_fix;
info->var = smtcfb_var;
info->pseudo_palette = sfb->colreg;
info->par = sfb;
pci_set_drvdata(pdev, sfb);
sm7xx_init_hw();
/* Map address and memory detection */
mmio_base = pci_resource_start(pdev, 0);
pci_read_config_byte(pdev, PCI_REVISION_ID, &sfb->chip_rev_id);
fbdev: sm712fb: fix crashes during framebuffer writes by correctly mapping VRAM On a Thinkpad s30 (Pentium III / i440MX, Lynx3DM), running fbtest or X will crash the machine instantly, because the VRAM/framebuffer is not mapped correctly. On SM712, the framebuffer starts at the beginning of address space, but SM720's framebuffer starts at the 1 MiB offset from the beginning. However, sm712fb fails to take this into account, as a result, writing to the framebuffer will destroy all the registers and kill the system immediately. Another problem is the driver assumes 8 MiB of VRAM for SM720, but some SM720 system, such as this IBM Thinkpad, only has 4 MiB of VRAM. Fix this problem by removing the hardcoded VRAM size, adding a function to query the amount of VRAM from register MCR76 on SM720, and adding proper framebuffer offset. Please note that the memory map may have additional problems on Big-Endian system, which is not available for testing by myself. But I highly suspect that the original code is also broken on Big-Endian machines for SM720, so at least we are not making the problem worse. More, the driver also assumed SM710/SM712 has 4 MiB of VRAM, but it has a 2 MiB version as well, and used in earlier laptops, such as IBM Thinkpad 240X, the driver would probably crash on them. I've never seen one of those machines and cannot fix it, but I have documented these problems in the comments. Signed-off-by: Yifeng Li <tomli@tomli.me> Tested-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com> Cc: Teddy Wang <teddy.wang@siliconmotion.com> Cc: <stable@vger.kernel.org> # v4.4+ Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
2019-04-01 22:46:59 +07:00
smem_size = sm7xx_vram_probe(sfb);
dev_info(&pdev->dev, "%lu MiB of VRAM detected.\n",
smem_size / 1048576);
switch (sfb->chip_id) {
case 0x710:
case 0x712:
sfb->fb->fix.mmio_start = mmio_base + 0x00400000;
sfb->fb->fix.mmio_len = 0x00400000;
sfb->lfb = ioremap(mmio_base, mmio_addr);
if (!sfb->lfb) {
dev_err(&pdev->dev,
"%s: unable to map memory mapped IO!\n",
sfb->fb->fix.id);
err = -ENOMEM;
goto failed_fb;
}
sfb->mmio = (smtc_regbaseaddress =
sfb->lfb + 0x00700000);
sfb->dp_regs = sfb->lfb + 0x00408000;
sfb->vp_regs = sfb->lfb + 0x0040c000;
if (sfb->fb->var.bits_per_pixel == 32) {
sfb->lfb += big_addr;
dev_info(&pdev->dev, "sfb->lfb=%p\n", sfb->lfb);
}
/* set MCLK = 14.31818 * (0x16 / 0x2) */
smtc_seqw(0x6a, 0x16);
smtc_seqw(0x6b, 0x02);
smtc_seqw(0x62, 0x3e);
/* enable PCI burst */
smtc_seqw(0x17, 0x20);
/* enable word swap */
if (sfb->fb->var.bits_per_pixel == 32)
seqw17();
break;
case 0x720:
sfb->fb->fix.mmio_start = mmio_base;
sfb->fb->fix.mmio_len = 0x00200000;
fbdev: sm712fb: fix crashes during framebuffer writes by correctly mapping VRAM On a Thinkpad s30 (Pentium III / i440MX, Lynx3DM), running fbtest or X will crash the machine instantly, because the VRAM/framebuffer is not mapped correctly. On SM712, the framebuffer starts at the beginning of address space, but SM720's framebuffer starts at the 1 MiB offset from the beginning. However, sm712fb fails to take this into account, as a result, writing to the framebuffer will destroy all the registers and kill the system immediately. Another problem is the driver assumes 8 MiB of VRAM for SM720, but some SM720 system, such as this IBM Thinkpad, only has 4 MiB of VRAM. Fix this problem by removing the hardcoded VRAM size, adding a function to query the amount of VRAM from register MCR76 on SM720, and adding proper framebuffer offset. Please note that the memory map may have additional problems on Big-Endian system, which is not available for testing by myself. But I highly suspect that the original code is also broken on Big-Endian machines for SM720, so at least we are not making the problem worse. More, the driver also assumed SM710/SM712 has 4 MiB of VRAM, but it has a 2 MiB version as well, and used in earlier laptops, such as IBM Thinkpad 240X, the driver would probably crash on them. I've never seen one of those machines and cannot fix it, but I have documented these problems in the comments. Signed-off-by: Yifeng Li <tomli@tomli.me> Tested-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com> Cc: Teddy Wang <teddy.wang@siliconmotion.com> Cc: <stable@vger.kernel.org> # v4.4+ Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
2019-04-01 22:46:59 +07:00
sfb->dp_regs = ioremap(mmio_base, 0x00200000 + smem_size);
sfb->lfb = sfb->dp_regs + 0x00200000;
sfb->mmio = (smtc_regbaseaddress =
sfb->dp_regs + 0x000c0000);
sfb->vp_regs = sfb->dp_regs + 0x800;
smtc_seqw(0x62, 0xff);
smtc_seqw(0x6a, 0x0d);
smtc_seqw(0x6b, 0x02);
break;
default:
dev_err(&pdev->dev,
"No valid Silicon Motion display chip was detected!\n");
goto failed_fb;
}
/* probe and decide resolution */
sm7xx_resolution_probe(sfb);
/* can support 32 bpp */
if (sfb->fb->var.bits_per_pixel == 15)
sfb->fb->var.bits_per_pixel = 16;
sfb->fb->var.xres_virtual = sfb->fb->var.xres;
sfb->fb->var.yres_virtual = sfb->fb->var.yres;
err = smtc_map_smem(sfb, pdev, smem_size);
if (err)
goto failed;
/*
* The screen would be temporarily garbled when sm712fb takes over
* vesafb or VGA text mode. Zero the framebuffer.
*/
memset_io(sfb->lfb, 0, sfb->fb->fix.smem_len);
err = register_framebuffer(info);
if (err < 0)
goto failed;
dev_info(&pdev->dev,
"Silicon Motion SM%X Rev%X primary display mode %dx%d-%d Init Complete.\n",
sfb->chip_id, sfb->chip_rev_id, sfb->fb->var.xres,
sfb->fb->var.yres, sfb->fb->var.bits_per_pixel);
return 0;
failed:
dev_err(&pdev->dev, "Silicon Motion, Inc. primary display init fail.\n");
smtc_unmap_smem(sfb);
smtc_unmap_mmio(sfb);
failed_fb:
framebuffer_release(info);
failed_free:
pci_release_region(pdev, 0);
failed_regions:
pci_disable_device(pdev);
return err;
}
/*
* 0x710 (LynxEM)
* 0x712 (LynxEM+)
* 0x720 (Lynx3DM, Lynx3DM+)
*/
static const struct pci_device_id smtcfb_pci_table[] = {
{ PCI_DEVICE(0x126f, 0x710), },
{ PCI_DEVICE(0x126f, 0x712), },
{ PCI_DEVICE(0x126f, 0x720), },
{0,}
};
MODULE_DEVICE_TABLE(pci, smtcfb_pci_table);
static void smtcfb_pci_remove(struct pci_dev *pdev)
{
struct smtcfb_info *sfb;
sfb = pci_get_drvdata(pdev);
smtc_unmap_smem(sfb);
smtc_unmap_mmio(sfb);
unregister_framebuffer(sfb->fb);
framebuffer_release(sfb->fb);
pci_release_region(pdev, 0);
pci_disable_device(pdev);
}
static int __maybe_unused smtcfb_pci_suspend(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct smtcfb_info *sfb;
sfb = pci_get_drvdata(pdev);
/* set the hw in sleep mode use external clock and self memory refresh
* so that we can turn off internal PLLs later on
*/
smtc_seqw(0x20, (smtc_seqr(0x20) | 0xc0));
smtc_seqw(0x69, (smtc_seqr(0x69) & 0xf7));
console_lock();
fb_set_suspend(sfb->fb, 1);
console_unlock();
/* additionally turn off all function blocks including internal PLLs */
smtc_seqw(0x21, 0xff);
return 0;
}
static int __maybe_unused smtcfb_pci_resume(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct smtcfb_info *sfb;
sfb = pci_get_drvdata(pdev);
/* reinit hardware */
sm7xx_init_hw();
switch (sfb->chip_id) {
case 0x710:
case 0x712:
/* set MCLK = 14.31818 * (0x16 / 0x2) */
smtc_seqw(0x6a, 0x16);
smtc_seqw(0x6b, 0x02);
smtc_seqw(0x62, 0x3e);
/* enable PCI burst */
smtc_seqw(0x17, 0x20);
if (sfb->fb->var.bits_per_pixel == 32)
seqw17();
break;
case 0x720:
smtc_seqw(0x62, 0xff);
smtc_seqw(0x6a, 0x0d);
smtc_seqw(0x6b, 0x02);
break;
}
smtc_seqw(0x34, (smtc_seqr(0x34) | 0xc0));
smtc_seqw(0x33, ((smtc_seqr(0x33) | 0x08) & 0xfb));
smtcfb_setmode(sfb);
console_lock();
fb_set_suspend(sfb->fb, 0);
console_unlock();
return 0;
}
static SIMPLE_DEV_PM_OPS(sm7xx_pm_ops, smtcfb_pci_suspend, smtcfb_pci_resume);
static struct pci_driver smtcfb_driver = {
.name = "smtcfb",
.id_table = smtcfb_pci_table,
.probe = smtcfb_pci_probe,
.remove = smtcfb_pci_remove,
.driver.pm = &sm7xx_pm_ops,
};
static int __init sm712fb_init(void)
{
char *option = NULL;
if (fb_get_options("sm712fb", &option))
return -ENODEV;
if (option && *option)
mode_option = option;
sm7xx_vga_setup(mode_option);
return pci_register_driver(&smtcfb_driver);
}
module_init(sm712fb_init);
static void __exit sm712fb_exit(void)
{
pci_unregister_driver(&smtcfb_driver);
}
module_exit(sm712fb_exit);
MODULE_AUTHOR("Siliconmotion ");
MODULE_DESCRIPTION("Framebuffer driver for SMI Graphic Cards");
MODULE_LICENSE("GPL");