Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.9.4.
   1/*
   2 * Silicon Motion SM7XX frame buffer device
   3 *
   4 * Copyright (C) 2006 Silicon Motion Technology Corp.
   5 * Authors: Ge Wang, gewang@siliconmotion.com
   6 *	    Boyod boyod.yang@siliconmotion.com.cn
   7 *
   8 * Copyright (C) 2009 Lemote, Inc.
   9 * Author: Wu Zhangjin, wuzhangjin@gmail.com
  10 *
  11 * Copyright (C) 2011 Igalia, S.L.
  12 * Author: Javier M. Mellid <jmunhoz@igalia.com>
  13 *
  14 *  This file is subject to the terms and conditions of the GNU General Public
  15 *  License. See the file COPYING in the main directory of this archive for
  16 *  more details.
  17 *
  18 * Version 0.10.26192.21.01
  19 *	- Add PowerPC/Big endian support
  20 *	- Verified on 2.6.19.2
  21 *	Boyod.yang <boyod.yang@siliconmotion.com.cn>
  22 *
  23 * Version 0.09.2621.00.01
  24 *	- Only support Linux Kernel's version 2.6.21
  25 *	Boyod.yang <boyod.yang@siliconmotion.com.cn>
  26 *
  27 * Version 0.09
  28 *	- Only support Linux Kernel's version 2.6.12
  29 *	Boyod.yang <boyod.yang@siliconmotion.com.cn>
  30 */
  31
  32#include <linux/io.h>
  33#include <linux/fb.h>
  34#include <linux/pci.h>
  35#include <linux/init.h>
  36#include <linux/slab.h>
  37#include <linux/uaccess.h>
  38#include <linux/console.h>
  39#include <linux/screen_info.h>
  40
  41#ifdef CONFIG_PM
  42#include <linux/pm.h>
  43#endif
  44
  45#include "smtcfb.h"
  46
  47#ifdef DEBUG
  48#define smdbg(format, arg...) printk(KERN_DEBUG format , ## arg)
  49#else
  50#define smdbg(format, arg...)
  51#endif
  52
  53struct screen_info smtc_screen_info;
  54
  55/*
  56* Private structure
  57*/
  58struct smtcfb_info {
  59	/*
  60	 * The following is a pointer to be passed into the
  61	 * functions below.  The modules outside the main
  62	 * voyager.c driver have no knowledge as to what
  63	 * is within this structure.
  64	 */
  65	struct fb_info fb;
  66	struct display_switch *dispsw;
  67	struct pci_dev *dev;
  68	signed int currcon;
  69
  70	struct {
  71		u8 red, green, blue;
  72	} palette[NR_RGB];
  73
  74	u_int palette_size;
  75};
  76
  77struct par_info {
  78	/*
  79	 * Hardware
  80	 */
  81	u16 chipID;
  82	unsigned char __iomem *m_pMMIO;
  83	char __iomem *m_pLFB;
  84	char *m_pDPR;
  85	char *m_pVPR;
  86	char *m_pCPR;
  87
  88	u_int width;
  89	u_int height;
  90	u_int hz;
  91	u_long BaseAddressInVRAM;
  92	u8 chipRevID;
  93};
  94
  95struct vesa_mode_table	{
  96	char mode_index[6];
  97	u16 lfb_width;
  98	u16 lfb_height;
  99	u16 lfb_depth;
 100};
 101
 102static struct vesa_mode_table vesa_mode[] = {
 103	{"0x301", 640,  480,  8},
 104	{"0x303", 800,  600,  8},
 105	{"0x305", 1024, 768,  8},
 106	{"0x307", 1280, 1024, 8},
 107
 108	{"0x311", 640,  480,  16},
 109	{"0x314", 800,  600,  16},
 110	{"0x317", 1024, 768,  16},
 111	{"0x31A", 1280, 1024, 16},
 112
 113	{"0x312", 640,  480,  24},
 114	{"0x315", 800,  600,  24},
 115	{"0x318", 1024, 768,  24},
 116	{"0x31B", 1280, 1024, 24},
 117};
 118
 119char __iomem *smtc_RegBaseAddress;	/* Memory Map IO starting address */
 120char __iomem *smtc_VRAMBaseAddress;	/* video memory starting address */
 121
 122static u32 colreg[17];
 123static struct par_info hw;	/* hardware information */
 124
 125u16 smtc_ChipIDs[] = {
 126	0x710,
 127	0x712,
 128	0x720
 129};
 130
 131#define numSMTCchipIDs ARRAY_SIZE(smtc_ChipIDs)
 132
 133static struct fb_var_screeninfo smtcfb_var = {
 134	.xres           = 1024,
 135	.yres           = 600,
 136	.xres_virtual   = 1024,
 137	.yres_virtual   = 600,
 138	.bits_per_pixel = 16,
 139	.red            = {16, 8, 0},
 140	.green          = {8, 8, 0},
 141	.blue           = {0, 8, 0},
 142	.activate       = FB_ACTIVATE_NOW,
 143	.height         = -1,
 144	.width          = -1,
 145	.vmode          = FB_VMODE_NONINTERLACED,
 146};
 147
 148static struct fb_fix_screeninfo smtcfb_fix = {
 149	.id             = "sm712fb",
 150	.type           = FB_TYPE_PACKED_PIXELS,
 151	.visual         = FB_VISUAL_TRUECOLOR,
 152	.line_length    = 800 * 3,
 153	.accel          = FB_ACCEL_SMI_LYNX,
 154};
 155
 156static void sm712_set_timing(struct smtcfb_info *sfb,
 157			     struct par_info *ppar_info)
 158{
 159	int i = 0, j = 0;
 160	u32 m_nScreenStride;
 161
 162	smdbg("\nppar_info->width = %d ppar_info->height = %d"
 163			"sfb->fb.var.bits_per_pixel = %d ppar_info->hz = %d\n",
 164			ppar_info->width, ppar_info->height,
 165			sfb->fb.var.bits_per_pixel, ppar_info->hz);
 166
 167	for (j = 0; j < numVGAModes; j++) {
 168		if (VGAMode[j].mmSizeX == ppar_info->width &&
 169		    VGAMode[j].mmSizeY == ppar_info->height &&
 170		    VGAMode[j].bpp == sfb->fb.var.bits_per_pixel &&
 171		    VGAMode[j].hz == ppar_info->hz) {
 172
 173			smdbg("\nVGAMode[j].mmSizeX  = %d VGAMode[j].mmSizeY ="
 174					"%d VGAMode[j].bpp = %d"
 175					"VGAMode[j].hz=%d\n",
 176					VGAMode[j].mmSizeX, VGAMode[j].mmSizeY,
 177					VGAMode[j].bpp, VGAMode[j].hz);
 178
 179			smdbg("VGAMode index=%d\n", j);
 180
 181			smtc_mmiowb(0x0, 0x3c6);
 182
 183			smtc_seqw(0, 0x1);
 184
 185			smtc_mmiowb(VGAMode[j].Init_MISC, 0x3c2);
 186
 187			/* init SEQ register SR00 - SR04 */
 188			for (i = 0; i < SIZE_SR00_SR04; i++)
 189				smtc_seqw(i, VGAMode[j].Init_SR00_SR04[i]);
 190
 191			/* init SEQ register SR10 - SR24 */
 192			for (i = 0; i < SIZE_SR10_SR24; i++)
 193				smtc_seqw(i + 0x10,
 194					  VGAMode[j].Init_SR10_SR24[i]);
 195
 196			/* init SEQ register SR30 - SR75 */
 197			for (i = 0; i < SIZE_SR30_SR75; i++)
 198				if (((i + 0x30) != 0x62) \
 199					&& ((i + 0x30) != 0x6a) \
 200					&& ((i + 0x30) != 0x6b))
 201					smtc_seqw(i + 0x30,
 202						VGAMode[j].Init_SR30_SR75[i]);
 203
 204			/* init SEQ register SR80 - SR93 */
 205			for (i = 0; i < SIZE_SR80_SR93; i++)
 206				smtc_seqw(i + 0x80,
 207					  VGAMode[j].Init_SR80_SR93[i]);
 208
 209			/* init SEQ register SRA0 - SRAF */
 210			for (i = 0; i < SIZE_SRA0_SRAF; i++)
 211				smtc_seqw(i + 0xa0,
 212					  VGAMode[j].Init_SRA0_SRAF[i]);
 213
 214			/* init Graphic register GR00 - GR08 */
 215			for (i = 0; i < SIZE_GR00_GR08; i++)
 216				smtc_grphw(i, VGAMode[j].Init_GR00_GR08[i]);
 217
 218			/* init Attribute register AR00 - AR14 */
 219			for (i = 0; i < SIZE_AR00_AR14; i++)
 220				smtc_attrw(i, VGAMode[j].Init_AR00_AR14[i]);
 221
 222			/* init CRTC register CR00 - CR18 */
 223			for (i = 0; i < SIZE_CR00_CR18; i++)
 224				smtc_crtcw(i, VGAMode[j].Init_CR00_CR18[i]);
 225
 226			/* init CRTC register CR30 - CR4D */
 227			for (i = 0; i < SIZE_CR30_CR4D; i++)
 228				smtc_crtcw(i + 0x30,
 229					   VGAMode[j].Init_CR30_CR4D[i]);
 230
 231			/* init CRTC register CR90 - CRA7 */
 232			for (i = 0; i < SIZE_CR90_CRA7; i++)
 233				smtc_crtcw(i + 0x90,
 234					   VGAMode[j].Init_CR90_CRA7[i]);
 235		}
 236	}
 237	smtc_mmiowb(0x67, 0x3c2);
 238
 239	/* set VPR registers */
 240	writel(0x0, ppar_info->m_pVPR + 0x0C);
 241	writel(0x0, ppar_info->m_pVPR + 0x40);
 242
 243	/* set data width */
 244	m_nScreenStride =
 245		(ppar_info->width * sfb->fb.var.bits_per_pixel) / 64;
 246	switch (sfb->fb.var.bits_per_pixel) {
 247	case 8:
 248		writel(0x0, ppar_info->m_pVPR + 0x0);
 249		break;
 250	case 16:
 251		writel(0x00020000, ppar_info->m_pVPR + 0x0);
 252		break;
 253	case 24:
 254		writel(0x00040000, ppar_info->m_pVPR + 0x0);
 255		break;
 256	case 32:
 257		writel(0x00030000, ppar_info->m_pVPR + 0x0);
 258		break;
 259	}
 260	writel((u32) (((m_nScreenStride + 2) << 16) | m_nScreenStride),
 261	       ppar_info->m_pVPR + 0x10);
 262
 263}
 264
 265static void sm712_setpalette(int regno, unsigned red, unsigned green,
 266			     unsigned blue, struct fb_info *info)
 267{
 268	struct par_info *cur_par = (struct par_info *)info->par;
 269
 270	if (cur_par->BaseAddressInVRAM)
 271		/*
 272		 * second display palette for dual head. Enable CRT RAM, 6-bit
 273		 * RAM
 274		 */
 275		smtc_seqw(0x66, (smtc_seqr(0x66) & 0xC3) | 0x20);
 276	else
 277		/* primary display palette. Enable LCD RAM only, 6-bit RAM */
 278		smtc_seqw(0x66, (smtc_seqr(0x66) & 0xC3) | 0x10);
 279	smtc_mmiowb(regno, dac_reg);
 280	smtc_mmiowb(red >> 10, dac_val);
 281	smtc_mmiowb(green >> 10, dac_val);
 282	smtc_mmiowb(blue >> 10, dac_val);
 283}
 284
 285static void smtc_set_timing(struct smtcfb_info *sfb, struct par_info
 286		*ppar_info)
 287{
 288	switch (ppar_info->chipID) {
 289	case 0x710:
 290	case 0x712:
 291	case 0x720:
 292		sm712_set_timing(sfb, ppar_info);
 293		break;
 294	}
 295}
 296
 297/* chan_to_field
 298 *
 299 * convert a colour value into a field position
 300 *
 301 * from pxafb.c
 302 */
 303
 304static inline unsigned int chan_to_field(unsigned int chan,
 305					 struct fb_bitfield *bf)
 306{
 307	chan &= 0xffff;
 308	chan >>= 16 - bf->length;
 309	return chan << bf->offset;
 310}
 311
 312static int cfb_blank(int blank_mode, struct fb_info *info)
 313{
 314	/* clear DPMS setting */
 315	switch (blank_mode) {
 316	case FB_BLANK_UNBLANK:
 317		/* Screen On: HSync: On, VSync : On */
 318		smtc_seqw(0x01, (smtc_seqr(0x01) & (~0x20)));
 319		smtc_seqw(0x6a, 0x16);
 320		smtc_seqw(0x6b, 0x02);
 321		smtc_seqw(0x21, (smtc_seqr(0x21) & 0x77));
 322		smtc_seqw(0x22, (smtc_seqr(0x22) & (~0x30)));
 323		smtc_seqw(0x23, (smtc_seqr(0x23) & (~0xc0)));
 324		smtc_seqw(0x24, (smtc_seqr(0x24) | 0x01));
 325		smtc_seqw(0x31, (smtc_seqr(0x31) | 0x03));
 326		break;
 327	case FB_BLANK_NORMAL:
 328		/* Screen Off: HSync: On, VSync : On   Soft blank */
 329		smtc_seqw(0x01, (smtc_seqr(0x01) & (~0x20)));
 330		smtc_seqw(0x6a, 0x16);
 331		smtc_seqw(0x6b, 0x02);
 332		smtc_seqw(0x22, (smtc_seqr(0x22) & (~0x30)));
 333		smtc_seqw(0x23, (smtc_seqr(0x23) & (~0xc0)));
 334		smtc_seqw(0x24, (smtc_seqr(0x24) | 0x01));
 335		smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
 336		break;
 337	case FB_BLANK_VSYNC_SUSPEND:
 338		/* Screen On: HSync: On, VSync : Off */
 339		smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20));
 340		smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0)));
 341		smtc_seqw(0x6a, 0x0c);
 342		smtc_seqw(0x6b, 0x02);
 343		smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88));
 344		smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x20));
 345		smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0x20));
 346		smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01)));
 347		smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
 348		smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80));
 349		break;
 350	case FB_BLANK_HSYNC_SUSPEND:
 351		/* Screen On: HSync: Off, VSync : On */
 352		smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20));
 353		smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0)));
 354		smtc_seqw(0x6a, 0x0c);
 355		smtc_seqw(0x6b, 0x02);
 356		smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88));
 357		smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x10));
 358		smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0xD8));
 359		smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01)));
 360		smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
 361		smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80));
 362		break;
 363	case FB_BLANK_POWERDOWN:
 364		/* Screen On: HSync: Off, VSync : Off */
 365		smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20));
 366		smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0)));
 367		smtc_seqw(0x6a, 0x0c);
 368		smtc_seqw(0x6b, 0x02);
 369		smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88));
 370		smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x30));
 371		smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0xD8));
 372		smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01)));
 373		smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
 374		smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80));
 375		break;
 376	default:
 377		return -EINVAL;
 378	}
 379
 380	return 0;
 381}
 382
 383static int smtc_setcolreg(unsigned regno, unsigned red, unsigned green,
 384			  unsigned blue, unsigned trans, struct fb_info *info)
 385{
 386	struct smtcfb_info *sfb = (struct smtcfb_info *)info;
 387	u32 val;
 388
 389	if (regno > 255)
 390		return 1;
 391
 392	switch (sfb->fb.fix.visual) {
 393	case FB_VISUAL_DIRECTCOLOR:
 394	case FB_VISUAL_TRUECOLOR:
 395		/*
 396		 * 16/32 bit true-colour, use pseuo-palette for 16 base color
 397		 */
 398		if (regno < 16) {
 399			if (sfb->fb.var.bits_per_pixel == 16) {
 400				u32 *pal = sfb->fb.pseudo_palette;
 401				val = chan_to_field(red, &sfb->fb.var.red);
 402				val |= chan_to_field(green, \
 403						&sfb->fb.var.green);
 404				val |= chan_to_field(blue, &sfb->fb.var.blue);
 405#ifdef __BIG_ENDIAN
 406				pal[regno] =
 407				    ((red & 0xf800) >> 8) |
 408				    ((green & 0xe000) >> 13) |
 409				    ((green & 0x1c00) << 3) |
 410				    ((blue & 0xf800) >> 3);
 411#else
 412				pal[regno] = val;
 413#endif
 414			} else {
 415				u32 *pal = sfb->fb.pseudo_palette;
 416				val = chan_to_field(red, &sfb->fb.var.red);
 417				val |= chan_to_field(green, \
 418						&sfb->fb.var.green);
 419				val |= chan_to_field(blue, &sfb->fb.var.blue);
 420#ifdef __BIG_ENDIAN
 421				val =
 422				    (val & 0xff00ff00 >> 8) |
 423				    (val & 0x00ff00ff << 8);
 424#endif
 425				pal[regno] = val;
 426			}
 427		}
 428		break;
 429
 430	case FB_VISUAL_PSEUDOCOLOR:
 431		/* color depth 8 bit */
 432		sm712_setpalette(regno, red, green, blue, info);
 433		break;
 434
 435	default:
 436		return 1;	/* unknown type */
 437	}
 438
 439	return 0;
 440
 441}
 442
 443#ifdef __BIG_ENDIAN
 444static ssize_t smtcfb_read(struct fb_info *info, char __user * buf, size_t
 445				count, loff_t *ppos)
 446{
 447	unsigned long p = *ppos;
 448
 449	u32 *buffer, *dst;
 450	u32 __iomem *src;
 451	int c, i, cnt = 0, err = 0;
 452	unsigned long total_size;
 453
 454	if (!info || !info->screen_base)
 455		return -ENODEV;
 456
 457	if (info->state != FBINFO_STATE_RUNNING)
 458		return -EPERM;
 459
 460	total_size = info->screen_size;
 461
 462	if (total_size == 0)
 463		total_size = info->fix.smem_len;
 464
 465	if (p >= total_size)
 466		return 0;
 467
 468	if (count >= total_size)
 469		count = total_size;
 470
 471	if (count + p > total_size)
 472		count = total_size - p;
 473
 474	buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, GFP_KERNEL);
 475	if (!buffer)
 476		return -ENOMEM;
 477
 478	src = (u32 __iomem *) (info->screen_base + p);
 479
 480	if (info->fbops->fb_sync)
 481		info->fbops->fb_sync(info);
 482
 483	while (count) {
 484		c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
 485		dst = buffer;
 486		for (i = c >> 2; i--;) {
 487			*dst = fb_readl(src++);
 488			*dst =
 489			    (*dst & 0xff00ff00 >> 8) |
 490			    (*dst & 0x00ff00ff << 8);
 491			dst++;
 492		}
 493		if (c & 3) {
 494			u8 *dst8 = (u8 *) dst;
 495			u8 __iomem *src8 = (u8 __iomem *) src;
 496
 497			for (i = c & 3; i--;) {
 498				if (i & 1) {
 499					*dst8++ = fb_readb(++src8);
 500				} else {
 501					*dst8++ = fb_readb(--src8);
 502					src8 += 2;
 503				}
 504			}
 505			src = (u32 __iomem *) src8;
 506		}
 507
 508		if (copy_to_user(buf, buffer, c)) {
 509			err = -EFAULT;
 510			break;
 511		}
 512		*ppos += c;
 513		buf += c;
 514		cnt += c;
 515		count -= c;
 516	}
 517
 518	kfree(buffer);
 519
 520	return (err) ? err : cnt;
 521}
 522
 523static ssize_t
 524smtcfb_write(struct fb_info *info, const char __user *buf, size_t count,
 525	     loff_t *ppos)
 526{
 527	unsigned long p = *ppos;
 528
 529	u32 *buffer, *src;
 530	u32 __iomem *dst;
 531	int c, i, cnt = 0, err = 0;
 532	unsigned long total_size;
 533
 534	if (!info || !info->screen_base)
 535		return -ENODEV;
 536
 537	if (info->state != FBINFO_STATE_RUNNING)
 538		return -EPERM;
 539
 540	total_size = info->screen_size;
 541
 542	if (total_size == 0)
 543		total_size = info->fix.smem_len;
 544
 545	if (p > total_size)
 546		return -EFBIG;
 547
 548	if (count > total_size) {
 549		err = -EFBIG;
 550		count = total_size;
 551	}
 552
 553	if (count + p > total_size) {
 554		if (!err)
 555			err = -ENOSPC;
 556
 557		count = total_size - p;
 558	}
 559
 560	buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, GFP_KERNEL);
 561	if (!buffer)
 562		return -ENOMEM;
 563
 564	dst = (u32 __iomem *) (info->screen_base + p);
 565
 566	if (info->fbops->fb_sync)
 567		info->fbops->fb_sync(info);
 568
 569	while (count) {
 570		c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
 571		src = buffer;
 572
 573		if (copy_from_user(src, buf, c)) {
 574			err = -EFAULT;
 575			break;
 576		}
 577
 578		for (i = c >> 2; i--;) {
 579			fb_writel((*src & 0xff00ff00 >> 8) |
 580				  (*src & 0x00ff00ff << 8), dst++);
 581			src++;
 582		}
 583		if (c & 3) {
 584			u8 *src8 = (u8 *) src;
 585			u8 __iomem *dst8 = (u8 __iomem *) dst;
 586
 587			for (i = c & 3; i--;) {
 588				if (i & 1) {
 589					fb_writeb(*src8++, ++dst8);
 590				} else {
 591					fb_writeb(*src8++, --dst8);
 592					dst8 += 2;
 593				}
 594			}
 595			dst = (u32 __iomem *) dst8;
 596		}
 597
 598		*ppos += c;
 599		buf += c;
 600		cnt += c;
 601		count -= c;
 602	}
 603
 604	kfree(buffer);
 605
 606	return (cnt) ? cnt : err;
 607}
 608#endif	/* ! __BIG_ENDIAN */
 609
 610void smtcfb_setmode(struct smtcfb_info *sfb)
 611{
 612	switch (sfb->fb.var.bits_per_pixel) {
 613	case 32:
 614		sfb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
 615		sfb->fb.fix.line_length = sfb->fb.var.xres * 4;
 616		sfb->fb.var.red.length = 8;
 617		sfb->fb.var.green.length = 8;
 618		sfb->fb.var.blue.length = 8;
 619		sfb->fb.var.red.offset = 16;
 620		sfb->fb.var.green.offset = 8;
 621		sfb->fb.var.blue.offset = 0;
 622
 623		break;
 624	case 8:
 625		sfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
 626		sfb->fb.fix.line_length = sfb->fb.var.xres;
 627		sfb->fb.var.red.offset = 5;
 628		sfb->fb.var.red.length = 3;
 629		sfb->fb.var.green.offset = 2;
 630		sfb->fb.var.green.length = 3;
 631		sfb->fb.var.blue.offset = 0;
 632		sfb->fb.var.blue.length = 2;
 633		break;
 634	case 24:
 635		sfb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
 636		sfb->fb.fix.line_length = sfb->fb.var.xres * 3;
 637		sfb->fb.var.red.length = 8;
 638		sfb->fb.var.green.length = 8;
 639		sfb->fb.var.blue.length = 8;
 640
 641		sfb->fb.var.red.offset = 16;
 642		sfb->fb.var.green.offset = 8;
 643		sfb->fb.var.blue.offset = 0;
 644
 645		break;
 646	case 16:
 647	default:
 648		sfb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
 649		sfb->fb.fix.line_length = sfb->fb.var.xres * 2;
 650
 651		sfb->fb.var.red.length = 5;
 652		sfb->fb.var.green.length = 6;
 653		sfb->fb.var.blue.length = 5;
 654
 655		sfb->fb.var.red.offset = 11;
 656		sfb->fb.var.green.offset = 5;
 657		sfb->fb.var.blue.offset = 0;
 658
 659		break;
 660	}
 661
 662	hw.width = sfb->fb.var.xres;
 663	hw.height = sfb->fb.var.yres;
 664	hw.hz = 60;
 665	smtc_set_timing(sfb, &hw);
 666}
 667
 668static int smtc_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
 669{
 670	/* sanity checks */
 671	if (var->xres_virtual < var->xres)
 672		var->xres_virtual = var->xres;
 673
 674	if (var->yres_virtual < var->yres)
 675		var->yres_virtual = var->yres;
 676
 677	/* set valid default bpp */
 678	if ((var->bits_per_pixel != 8)  && (var->bits_per_pixel != 16) &&
 679	    (var->bits_per_pixel != 24) && (var->bits_per_pixel != 32))
 680		var->bits_per_pixel = 16;
 681
 682	return 0;
 683}
 684
 685static int smtc_set_par(struct fb_info *info)
 686{
 687	struct smtcfb_info *sfb = (struct smtcfb_info *)info;
 688
 689	smtcfb_setmode(sfb);
 690
 691	return 0;
 692}
 693
 694static struct fb_ops smtcfb_ops = {
 695	.owner        = THIS_MODULE,
 696	.fb_check_var = smtc_check_var,
 697	.fb_set_par   = smtc_set_par,
 698	.fb_setcolreg = smtc_setcolreg,
 699	.fb_blank     = cfb_blank,
 700	.fb_fillrect  = cfb_fillrect,
 701	.fb_imageblit = cfb_imageblit,
 702	.fb_copyarea  = cfb_copyarea,
 703#ifdef __BIG_ENDIAN
 704	.fb_read      = smtcfb_read,
 705	.fb_write     = smtcfb_write,
 706#endif
 707};
 708
 709/*
 710 * Alloc struct smtcfb_info and assign the default value
 711 */
 712static struct smtcfb_info *smtc_alloc_fb_info(struct pci_dev *dev,
 713							char *name)
 714{
 715	struct smtcfb_info *sfb;
 716
 717	sfb = kzalloc(sizeof(*sfb), GFP_KERNEL);
 718
 719	if (!sfb)
 720		return NULL;
 721
 722	sfb->currcon = -1;
 723	sfb->dev = dev;
 724
 725	/*** Init sfb->fb with default value ***/
 726	sfb->fb.flags = FBINFO_FLAG_DEFAULT;
 727	sfb->fb.fbops = &smtcfb_ops;
 728	sfb->fb.var = smtcfb_var;
 729	sfb->fb.fix = smtcfb_fix;
 730
 731	strcpy(sfb->fb.fix.id, name);
 732
 733	sfb->fb.fix.type = FB_TYPE_PACKED_PIXELS;
 734	sfb->fb.fix.type_aux = 0;
 735	sfb->fb.fix.xpanstep = 0;
 736	sfb->fb.fix.ypanstep = 0;
 737	sfb->fb.fix.ywrapstep = 0;
 738	sfb->fb.fix.accel = FB_ACCEL_SMI_LYNX;
 739
 740	sfb->fb.var.nonstd = 0;
 741	sfb->fb.var.activate = FB_ACTIVATE_NOW;
 742	sfb->fb.var.height = -1;
 743	sfb->fb.var.width = -1;
 744	/* text mode acceleration */
 745	sfb->fb.var.accel_flags = FB_ACCELF_TEXT;
 746	sfb->fb.var.vmode = FB_VMODE_NONINTERLACED;
 747	sfb->fb.par = &hw;
 748	sfb->fb.pseudo_palette = colreg;
 749
 750	return sfb;
 751}
 752
 753/*
 754 * Unmap in the memory mapped IO registers
 755 */
 756
 757static void smtc_unmap_mmio(struct smtcfb_info *sfb)
 758{
 759	if (sfb && smtc_RegBaseAddress)
 760		smtc_RegBaseAddress = NULL;
 761}
 762
 763/*
 764 * Map in the screen memory
 765 */
 766
 767static int smtc_map_smem(struct smtcfb_info *sfb,
 768		struct pci_dev *dev, u_long smem_len)
 769{
 770	if (sfb->fb.var.bits_per_pixel == 32) {
 771#ifdef __BIG_ENDIAN
 772		sfb->fb.fix.smem_start = pci_resource_start(dev, 0)
 773			+ 0x800000;
 774#else
 775		sfb->fb.fix.smem_start = pci_resource_start(dev, 0);
 776#endif
 777	} else {
 778		sfb->fb.fix.smem_start = pci_resource_start(dev, 0);
 779	}
 780
 781	sfb->fb.fix.smem_len = smem_len;
 782
 783	sfb->fb.screen_base = smtc_VRAMBaseAddress;
 784
 785	if (!sfb->fb.screen_base) {
 786		printk(KERN_ERR "%s: unable to map screen memory\n",
 787				sfb->fb.fix.id);
 788		return -ENOMEM;
 789	}
 790
 791	return 0;
 792}
 793
 794/*
 795 * Unmap in the screen memory
 796 *
 797 */
 798static void smtc_unmap_smem(struct smtcfb_info *sfb)
 799{
 800	if (sfb && sfb->fb.screen_base) {
 801		iounmap(sfb->fb.screen_base);
 802		sfb->fb.screen_base = NULL;
 803	}
 804}
 805
 806/*
 807 * We need to wake up the LynxEM+, and make sure its in linear memory mode.
 808 */
 809static inline void sm7xx_init_hw(void)
 810{
 811	outb_p(0x18, 0x3c4);
 812	outb_p(0x11, 0x3c5);
 813}
 814
 815static void smtc_free_fb_info(struct smtcfb_info *sfb)
 816{
 817	if (sfb) {
 818		fb_alloc_cmap(&sfb->fb.cmap, 0, 0);
 819		kfree(sfb);
 820	}
 821}
 822
 823/*
 824 *	sm712vga_setup - process command line options, get vga parameter
 825 *	@options: string of options
 826 *	Returns zero.
 827 *
 828 */
 829static int __init sm712vga_setup(char *options)
 830{
 831	int index;
 832
 833	if (!options || !*options) {
 834		smdbg("\n No vga parameter\n");
 835		return -EINVAL;
 836	}
 837
 838	smtc_screen_info.lfb_width = 0;
 839	smtc_screen_info.lfb_height = 0;
 840	smtc_screen_info.lfb_depth = 0;
 841
 842	smdbg("\nsm712vga_setup = %s\n", options);
 843
 844	for (index = 0;
 845	     index < ARRAY_SIZE(vesa_mode);
 846	     index++) {
 847		if (strstr(options, vesa_mode[index].mode_index)) {
 848			smtc_screen_info.lfb_width = vesa_mode[index].lfb_width;
 849			smtc_screen_info.lfb_height =
 850					vesa_mode[index].lfb_height;
 851			smtc_screen_info.lfb_depth = vesa_mode[index].lfb_depth;
 852			return 0;
 853		}
 854	}
 855
 856	return -1;
 857}
 858__setup("vga=", sm712vga_setup);
 859
 860/* Jason (08/13/2009)
 861 * Original init function changed to probe method to be used by pci_drv
 862 * process used to detect chips replaced with kernel process in pci_drv
 863 */
 864static int __devinit smtcfb_pci_probe(struct pci_dev *pdev,
 865				   const struct pci_device_id *ent)
 866{
 867	struct smtcfb_info *sfb;
 868	u_long smem_size = 0x00800000;	/* default 8MB */
 869	char name[16];
 870	int err;
 871	unsigned long pFramebufferPhysical;
 872
 873	printk(KERN_INFO
 874		"Silicon Motion display driver " SMTC_LINUX_FB_VERSION "\n");
 875
 876	err = pci_enable_device(pdev);	/* enable SMTC chip */
 877	if (err)
 878		return err;
 879
 880	hw.chipID = ent->device;
 881	sprintf(name, "sm%Xfb", hw.chipID);
 882
 883	sfb = smtc_alloc_fb_info(pdev, name);
 884
 885	if (!sfb)
 886		goto failed_free;
 887	/* Jason (08/13/2009)
 888	 * Store fb_info to be further used when suspending and resuming
 889	 */
 890	pci_set_drvdata(pdev, sfb);
 891
 892	sm7xx_init_hw();
 893
 894	/*get mode parameter from smtc_screen_info */
 895	if (smtc_screen_info.lfb_width != 0) {
 896		sfb->fb.var.xres = smtc_screen_info.lfb_width;
 897		sfb->fb.var.yres = smtc_screen_info.lfb_height;
 898		sfb->fb.var.bits_per_pixel = smtc_screen_info.lfb_depth;
 899	} else {
 900		/* default resolution 1024x600 16bit mode */
 901		sfb->fb.var.xres = SCREEN_X_RES;
 902		sfb->fb.var.yres = SCREEN_Y_RES;
 903		sfb->fb.var.bits_per_pixel = SCREEN_BPP;
 904	}
 905
 906#ifdef __BIG_ENDIAN
 907	if (sfb->fb.var.bits_per_pixel == 24)
 908		sfb->fb.var.bits_per_pixel = (smtc_screen_info.lfb_depth = 32);
 909#endif
 910	/* Map address and memory detection */
 911	pFramebufferPhysical = pci_resource_start(pdev, 0);
 912	pci_read_config_byte(pdev, PCI_REVISION_ID, &hw.chipRevID);
 913
 914	switch (hw.chipID) {
 915	case 0x710:
 916	case 0x712:
 917		sfb->fb.fix.mmio_start = pFramebufferPhysical + 0x00400000;
 918		sfb->fb.fix.mmio_len = 0x00400000;
 919		smem_size = SM712_VIDEOMEMORYSIZE;
 920#ifdef __BIG_ENDIAN
 921		hw.m_pLFB = (smtc_VRAMBaseAddress =
 922		    ioremap(pFramebufferPhysical, 0x00c00000));
 923#else
 924		hw.m_pLFB = (smtc_VRAMBaseAddress =
 925		    ioremap(pFramebufferPhysical, 0x00800000));
 926#endif
 927		hw.m_pMMIO = (smtc_RegBaseAddress =
 928		    smtc_VRAMBaseAddress + 0x00700000);
 929		hw.m_pDPR = smtc_VRAMBaseAddress + 0x00408000;
 930		hw.m_pVPR = hw.m_pLFB + 0x0040c000;
 931#ifdef __BIG_ENDIAN
 932		if (sfb->fb.var.bits_per_pixel == 32) {
 933			smtc_VRAMBaseAddress += 0x800000;
 934			hw.m_pLFB += 0x800000;
 935			printk(KERN_INFO
 936				"\nsmtc_VRAMBaseAddress=%p hw.m_pLFB=%p\n",
 937					smtc_VRAMBaseAddress, hw.m_pLFB);
 938		}
 939#endif
 940		if (!smtc_RegBaseAddress) {
 941			printk(KERN_ERR
 942				"%s: unable to map memory mapped IO\n",
 943				sfb->fb.fix.id);
 944			err = -ENOMEM;
 945			goto failed_fb;
 946		}
 947
 948		/* set MCLK = 14.31818 * (0x16 / 0x2) */
 949		smtc_seqw(0x6a, 0x16);
 950		smtc_seqw(0x6b, 0x02);
 951		smtc_seqw(0x62, 0x3e);
 952		/* enable PCI burst */
 953		smtc_seqw(0x17, 0x20);
 954		/* enable word swap */
 955#ifdef __BIG_ENDIAN
 956		if (sfb->fb.var.bits_per_pixel == 32)
 957			smtc_seqw(0x17, 0x30);
 958#endif
 959		break;
 960	case 0x720:
 961		sfb->fb.fix.mmio_start = pFramebufferPhysical;
 962		sfb->fb.fix.mmio_len = 0x00200000;
 963		smem_size = SM722_VIDEOMEMORYSIZE;
 964		hw.m_pDPR = ioremap(pFramebufferPhysical, 0x00a00000);
 965		hw.m_pLFB = (smtc_VRAMBaseAddress =
 966		    hw.m_pDPR + 0x00200000);
 967		hw.m_pMMIO = (smtc_RegBaseAddress =
 968		    hw.m_pDPR + 0x000c0000);
 969		hw.m_pVPR = hw.m_pDPR + 0x800;
 970
 971		smtc_seqw(0x62, 0xff);
 972		smtc_seqw(0x6a, 0x0d);
 973		smtc_seqw(0x6b, 0x02);
 974		break;
 975	default:
 976		printk(KERN_ERR
 977		"No valid Silicon Motion display chip was detected!\n");
 978
 979		goto failed_fb;
 980	}
 981
 982	/* can support 32 bpp */
 983	if (15 == sfb->fb.var.bits_per_pixel)
 984		sfb->fb.var.bits_per_pixel = 16;
 985
 986	sfb->fb.var.xres_virtual = sfb->fb.var.xres;
 987	sfb->fb.var.yres_virtual = sfb->fb.var.yres;
 988	err = smtc_map_smem(sfb, pdev, smem_size);
 989	if (err)
 990		goto failed;
 991
 992	smtcfb_setmode(sfb);
 993	/* Primary display starting from 0 position */
 994	hw.BaseAddressInVRAM = 0;
 995	sfb->fb.par = &hw;
 996
 997	err = register_framebuffer(&sfb->fb);
 998	if (err < 0)
 999		goto failed;
1000
1001	printk(KERN_INFO "Silicon Motion SM%X Rev%X primary display mode"
1002			"%dx%d-%d Init Complete.\n", hw.chipID, hw.chipRevID,
1003			sfb->fb.var.xres, sfb->fb.var.yres,
1004			sfb->fb.var.bits_per_pixel);
1005
1006	return 0;
1007
1008failed:
1009	printk(KERN_ERR "Silicon Motion, Inc.  primary display init fail\n");
1010
1011	smtc_unmap_smem(sfb);
1012	smtc_unmap_mmio(sfb);
1013failed_fb:
1014	smtc_free_fb_info(sfb);
1015
1016failed_free:
1017	pci_disable_device(pdev);
1018
1019	return err;
1020}
1021
1022
1023/* Jason (08/11/2009) PCI_DRV wrapper essential structs */
1024static DEFINE_PCI_DEVICE_TABLE(smtcfb_pci_table) = {
1025	{0x126f, 0x710, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1026	{0x126f, 0x712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1027	{0x126f, 0x720, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1028	{0,}
1029};
1030
1031
1032/* Jason (08/14/2009)
1033 * do some clean up when the driver module is removed
1034 */
1035static void __devexit smtcfb_pci_remove(struct pci_dev *pdev)
1036{
1037	struct smtcfb_info *sfb;
1038
1039	sfb = pci_get_drvdata(pdev);
1040	pci_set_drvdata(pdev, NULL);
1041	smtc_unmap_smem(sfb);
1042	smtc_unmap_mmio(sfb);
1043	unregister_framebuffer(&sfb->fb);
1044	smtc_free_fb_info(sfb);
1045}
1046
1047#ifdef CONFIG_PM
1048static int smtcfb_pci_suspend(struct device *device)
1049{
1050	struct pci_dev *pdev = to_pci_dev(device);
1051	struct smtcfb_info *sfb;
1052
1053	sfb = pci_get_drvdata(pdev);
1054
1055	/* set the hw in sleep mode use externel clock and self memory refresh
1056	 * so that we can turn off internal PLLs later on
1057	 */
1058	smtc_seqw(0x20, (smtc_seqr(0x20) | 0xc0));
1059	smtc_seqw(0x69, (smtc_seqr(0x69) & 0xf7));
1060
1061	console_lock();
1062	fb_set_suspend(&sfb->fb, 1);
1063	console_unlock();
1064
1065	/* additionally turn off all function blocks including internal PLLs */
1066	smtc_seqw(0x21, 0xff);
1067
1068	return 0;
1069}
1070
1071static int smtcfb_pci_resume(struct device *device)
1072{
1073	struct pci_dev *pdev = to_pci_dev(device);
1074	struct smtcfb_info *sfb;
1075
1076	sfb = pci_get_drvdata(pdev);
1077
1078	/* reinit hardware */
1079	sm7xx_init_hw();
1080	switch (hw.chipID) {
1081	case 0x710:
1082	case 0x712:
1083		/* set MCLK = 14.31818 *  (0x16 / 0x2) */
1084		smtc_seqw(0x6a, 0x16);
1085		smtc_seqw(0x6b, 0x02);
1086		smtc_seqw(0x62, 0x3e);
1087		/* enable PCI burst */
1088		smtc_seqw(0x17, 0x20);
1089#ifdef __BIG_ENDIAN
1090		if (sfb->fb.var.bits_per_pixel == 32)
1091			smtc_seqw(0x17, 0x30);
1092#endif
1093		break;
1094	case 0x720:
1095		smtc_seqw(0x62, 0xff);
1096		smtc_seqw(0x6a, 0x0d);
1097		smtc_seqw(0x6b, 0x02);
1098		break;
1099	}
1100
1101	smtc_seqw(0x34, (smtc_seqr(0x34) | 0xc0));
1102	smtc_seqw(0x33, ((smtc_seqr(0x33) | 0x08) & 0xfb));
1103
1104	smtcfb_setmode(sfb);
1105
1106	console_lock();
1107	fb_set_suspend(&sfb->fb, 0);
1108	console_unlock();
1109
1110	return 0;
1111}
1112
1113static const struct dev_pm_ops sm7xx_pm_ops = {
1114	.suspend = smtcfb_pci_suspend,
1115	.resume = smtcfb_pci_resume,
1116	.freeze = smtcfb_pci_suspend,
1117	.thaw = smtcfb_pci_resume,
1118	.poweroff = smtcfb_pci_suspend,
1119	.restore = smtcfb_pci_resume,
1120};
1121
1122#define SM7XX_PM_OPS (&sm7xx_pm_ops)
1123
1124#else  /* !CONFIG_PM */
1125
1126#define SM7XX_PM_OPS NULL
1127
1128#endif /* !CONFIG_PM */
1129
1130static struct pci_driver smtcfb_driver = {
1131	.name = "smtcfb",
1132	.id_table = smtcfb_pci_table,
1133	.probe = smtcfb_pci_probe,
1134	.remove = __devexit_p(smtcfb_pci_remove),
1135	.driver.pm  = SM7XX_PM_OPS,
1136};
1137
1138static int __init smtcfb_init(void)
1139{
1140	return pci_register_driver(&smtcfb_driver);
1141}
1142
1143static void __exit smtcfb_exit(void)
1144{
1145	pci_unregister_driver(&smtcfb_driver);
1146}
1147
1148module_init(smtcfb_init);
1149module_exit(smtcfb_exit);
1150
1151MODULE_AUTHOR("Siliconmotion ");
1152MODULE_DESCRIPTION("Framebuffer driver for SMI Graphic Cards");
1153MODULE_LICENSE("GPL");