2 * Silicon Motion SM7XX frame buffer device
4 * Copyright (C) 2006 Silicon Motion Technology Corp.
5 * Authors: Ge Wang, gewang@siliconmotion.com
6 * Boyod boyod.yang@siliconmotion.com.cn
8 * Copyright (C) 2009 Lemote, Inc.
9 * Author: Wu Zhangjin, wuzhangjin@gmail.com
11 * Copyright (C) 2011 Igalia, S.L.
12 * Author: Javier M. Mellid <jmunhoz@igalia.com>
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
18 * Framebuffer driver for Silicon Motion SM710, SM712, SM721 and SM722 chips
23 #include <linux/pci.h>
24 #include <linux/init.h>
25 #include <linux/slab.h>
26 #include <linux/uaccess.h>
27 #include <linux/module.h>
28 #include <linux/console.h>
29 #include <linux/screen_info.h>
44 void __iomem
*lfb
; /* linear frame buffer */
45 void __iomem
*dp_regs
; /* drawing processor control regs */
46 void __iomem
*vp_regs
; /* video processor control regs */
47 void __iomem
*cp_regs
; /* capture processor control regs */
48 void __iomem
*mmio
; /* memory map IO port */
57 void __iomem
*smtc_regbaseaddress
; /* Memory Map IO starting address */
59 static const struct fb_var_screeninfo smtcfb_var
= {
68 .activate
= FB_ACTIVATE_NOW
,
71 .vmode
= FB_VMODE_NONINTERLACED
,
73 .accel_flags
= FB_ACCELF_TEXT
,
76 static struct fb_fix_screeninfo smtcfb_fix
= {
78 .type
= FB_TYPE_PACKED_PIXELS
,
79 .visual
= FB_VISUAL_TRUECOLOR
,
80 .line_length
= 800 * 3,
81 .accel
= FB_ACCEL_SMI_LYNX
,
95 static const struct vesa_mode vesa_mode_table
[] = {
96 {"0x301", 640, 480, 8},
97 {"0x303", 800, 600, 8},
98 {"0x305", 1024, 768, 8},
99 {"0x307", 1280, 1024, 8},
101 {"0x311", 640, 480, 16},
102 {"0x314", 800, 600, 16},
103 {"0x317", 1024, 768, 16},
104 {"0x31A", 1280, 1024, 16},
106 {"0x312", 640, 480, 24},
107 {"0x315", 800, 600, 24},
108 {"0x318", 1024, 768, 24},
109 {"0x31B", 1280, 1024, 24},
112 /**********************************************************************
114 **********************************************************************/
115 static const struct modeinit vgamode
[] = {
117 /* mode#0: 640 x 480 16Bpp 60Hz */
122 0x03, 0x01, 0x0F, 0x00, 0x0E,
124 { /* Init_SR10_SR24 */
125 0xFF, 0xBE, 0xEF, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
126 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127 0xC4, 0x30, 0x02, 0x01, 0x01,
129 { /* Init_SR30_SR75 */
130 0x32, 0x03, 0xA0, 0x09, 0xC0, 0x32, 0x32, 0x32,
131 0x32, 0x32, 0x32, 0x32, 0x00, 0x00, 0x03, 0xFF,
132 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
133 0x20, 0x0C, 0x44, 0x20, 0x00, 0x32, 0x32, 0x32,
134 0x04, 0x24, 0x63, 0x4F, 0x52, 0x0B, 0xDF, 0xEA,
135 0x04, 0x50, 0x19, 0x32, 0x32, 0x00, 0x00, 0x32,
136 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
137 0x50, 0x03, 0x74, 0x14, 0x07, 0x82, 0x07, 0x04,
138 0x00, 0x45, 0x30, 0x30, 0x40, 0x30,
140 { /* Init_SR80_SR93 */
141 0xFF, 0x07, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x32,
142 0xF7, 0x00, 0x00, 0x00, 0xEF, 0xFF, 0x32, 0x32,
143 0x00, 0x00, 0x00, 0x00,
145 { /* Init_SRA0_SRAF */
146 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
147 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xFF, 0xDF,
149 { /* Init_GR00_GR08 */
150 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
153 { /* Init_AR00_AR14 */
154 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
155 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
156 0x41, 0x00, 0x0F, 0x00, 0x00,
158 { /* Init_CR00_CR18 */
159 0x5F, 0x4F, 0x4F, 0x00, 0x53, 0x1F, 0x0B, 0x3E,
160 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
161 0xEA, 0x0C, 0xDF, 0x50, 0x40, 0xDF, 0x00, 0xE3,
164 { /* Init_CR30_CR4D */
165 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x20,
166 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xFF, 0xFD,
167 0x5F, 0x4F, 0x00, 0x54, 0x00, 0x0B, 0xDF, 0x00,
168 0xEA, 0x0C, 0x2E, 0x00, 0x4F, 0xDF,
170 { /* Init_CR90_CRA7 */
171 0x56, 0xDD, 0x5E, 0xEA, 0x87, 0x44, 0x8F, 0x55,
172 0x0A, 0x8F, 0x55, 0x0A, 0x00, 0x00, 0x18, 0x00,
173 0x11, 0x10, 0x0B, 0x0A, 0x0A, 0x0A, 0x0A, 0x00,
177 /* mode#1: 640 x 480 24Bpp 60Hz */
182 0x03, 0x01, 0x0F, 0x00, 0x0E,
184 { /* Init_SR10_SR24 */
185 0xFF, 0xBE, 0xEF, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
186 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
187 0xC4, 0x30, 0x02, 0x01, 0x01,
189 { /* Init_SR30_SR75 */
190 0x32, 0x03, 0xA0, 0x09, 0xC0, 0x32, 0x32, 0x32,
191 0x32, 0x32, 0x32, 0x32, 0x00, 0x00, 0x03, 0xFF,
192 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
193 0x20, 0x0C, 0x44, 0x20, 0x00, 0x32, 0x32, 0x32,
194 0x04, 0x24, 0x63, 0x4F, 0x52, 0x0B, 0xDF, 0xEA,
195 0x04, 0x50, 0x19, 0x32, 0x32, 0x00, 0x00, 0x32,
196 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
197 0x50, 0x03, 0x74, 0x14, 0x07, 0x82, 0x07, 0x04,
198 0x00, 0x45, 0x30, 0x30, 0x40, 0x30,
200 { /* Init_SR80_SR93 */
201 0xFF, 0x07, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x32,
202 0xF7, 0x00, 0x00, 0x00, 0xEF, 0xFF, 0x32, 0x32,
203 0x00, 0x00, 0x00, 0x00,
205 { /* Init_SRA0_SRAF */
206 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
207 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xFF, 0xDF,
209 { /* Init_GR00_GR08 */
210 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
213 { /* Init_AR00_AR14 */
214 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
215 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
216 0x41, 0x00, 0x0F, 0x00, 0x00,
218 { /* Init_CR00_CR18 */
219 0x5F, 0x4F, 0x4F, 0x00, 0x53, 0x1F, 0x0B, 0x3E,
220 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
221 0xEA, 0x0C, 0xDF, 0x50, 0x40, 0xDF, 0x00, 0xE3,
224 { /* Init_CR30_CR4D */
225 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x20,
226 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xFF, 0xFD,
227 0x5F, 0x4F, 0x00, 0x54, 0x00, 0x0B, 0xDF, 0x00,
228 0xEA, 0x0C, 0x2E, 0x00, 0x4F, 0xDF,
230 { /* Init_CR90_CRA7 */
231 0x56, 0xDD, 0x5E, 0xEA, 0x87, 0x44, 0x8F, 0x55,
232 0x0A, 0x8F, 0x55, 0x0A, 0x00, 0x00, 0x18, 0x00,
233 0x11, 0x10, 0x0B, 0x0A, 0x0A, 0x0A, 0x0A, 0x00,
237 /* mode#0: 640 x 480 32Bpp 60Hz */
242 0x03, 0x01, 0x0F, 0x00, 0x0E,
244 { /* Init_SR10_SR24 */
245 0xFF, 0xBE, 0xEF, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
246 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
247 0xC4, 0x30, 0x02, 0x01, 0x01,
249 { /* Init_SR30_SR75 */
250 0x32, 0x03, 0xA0, 0x09, 0xC0, 0x32, 0x32, 0x32,
251 0x32, 0x32, 0x32, 0x32, 0x00, 0x00, 0x03, 0xFF,
252 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
253 0x20, 0x0C, 0x44, 0x20, 0x00, 0x32, 0x32, 0x32,
254 0x04, 0x24, 0x63, 0x4F, 0x52, 0x0B, 0xDF, 0xEA,
255 0x04, 0x50, 0x19, 0x32, 0x32, 0x00, 0x00, 0x32,
256 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
257 0x50, 0x03, 0x74, 0x14, 0x07, 0x82, 0x07, 0x04,
258 0x00, 0x45, 0x30, 0x30, 0x40, 0x30,
260 { /* Init_SR80_SR93 */
261 0xFF, 0x07, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x32,
262 0xF7, 0x00, 0x00, 0x00, 0xEF, 0xFF, 0x32, 0x32,
263 0x00, 0x00, 0x00, 0x00,
265 { /* Init_SRA0_SRAF */
266 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
267 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xFF, 0xDF,
269 { /* Init_GR00_GR08 */
270 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
273 { /* Init_AR00_AR14 */
274 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
275 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
276 0x41, 0x00, 0x0F, 0x00, 0x00,
278 { /* Init_CR00_CR18 */
279 0x5F, 0x4F, 0x4F, 0x00, 0x53, 0x1F, 0x0B, 0x3E,
280 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
281 0xEA, 0x0C, 0xDF, 0x50, 0x40, 0xDF, 0x00, 0xE3,
284 { /* Init_CR30_CR4D */
285 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x20,
286 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xFF, 0xFD,
287 0x5F, 0x4F, 0x00, 0x54, 0x00, 0x0B, 0xDF, 0x00,
288 0xEA, 0x0C, 0x2E, 0x00, 0x4F, 0xDF,
290 { /* Init_CR90_CRA7 */
291 0x56, 0xDD, 0x5E, 0xEA, 0x87, 0x44, 0x8F, 0x55,
292 0x0A, 0x8F, 0x55, 0x0A, 0x00, 0x00, 0x18, 0x00,
293 0x11, 0x10, 0x0B, 0x0A, 0x0A, 0x0A, 0x0A, 0x00,
297 { /* mode#2: 800 x 600 16Bpp 60Hz */
302 0x03, 0x01, 0x0F, 0x03, 0x0E,
304 { /* Init_SR10_SR24 */
305 0xFF, 0xBE, 0xEE, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
306 0x99, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
307 0xC4, 0x30, 0x02, 0x01, 0x01,
309 { /* Init_SR30_SR75 */
310 0x34, 0x03, 0x20, 0x09, 0xC0, 0x24, 0x24, 0x24,
311 0x24, 0x24, 0x24, 0x24, 0x00, 0x00, 0x03, 0xFF,
312 0x00, 0xFC, 0x00, 0x00, 0x20, 0x38, 0x00, 0xFC,
313 0x20, 0x0C, 0x44, 0x20, 0x00, 0x24, 0x24, 0x24,
314 0x04, 0x48, 0x83, 0x63, 0x68, 0x72, 0x57, 0x58,
315 0x04, 0x55, 0x59, 0x24, 0x24, 0x00, 0x00, 0x24,
316 0x01, 0x80, 0x7A, 0x1A, 0x1A, 0x00, 0x00, 0x00,
317 0x50, 0x03, 0x74, 0x14, 0x1C, 0x85, 0x35, 0x13,
318 0x02, 0x45, 0x30, 0x35, 0x40, 0x20,
320 { /* Init_SR80_SR93 */
321 0x00, 0x00, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x24,
322 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x24, 0x24,
323 0x00, 0x00, 0x00, 0x00,
325 { /* Init_SRA0_SRAF */
326 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
327 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xBF, 0xDF,
329 { /* Init_GR00_GR08 */
330 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
333 { /* Init_AR00_AR14 */
334 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
335 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
336 0x41, 0x00, 0x0F, 0x00, 0x00,
338 { /* Init_CR00_CR18 */
339 0x7F, 0x63, 0x63, 0x00, 0x68, 0x18, 0x72, 0xF0,
340 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
341 0x58, 0x0C, 0x57, 0x64, 0x40, 0x57, 0x00, 0xE3,
344 { /* Init_CR30_CR4D */
345 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x03, 0x20,
346 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xBF, 0xFD,
347 0x7F, 0x63, 0x00, 0x69, 0x18, 0x72, 0x57, 0x00,
348 0x58, 0x0C, 0xE0, 0x20, 0x63, 0x57,
350 { /* Init_CR90_CRA7 */
351 0x56, 0x4B, 0x5E, 0x55, 0x86, 0x9D, 0x8E, 0xAA,
352 0xDB, 0x2A, 0xDF, 0x33, 0x00, 0x00, 0x18, 0x00,
353 0x20, 0x1F, 0x1A, 0x19, 0x0F, 0x0F, 0x0F, 0x00,
356 { /* mode#3: 800 x 600 24Bpp 60Hz */
360 0x03, 0x01, 0x0F, 0x03, 0x0E,
362 { /* Init_SR10_SR24 */
363 0xFF, 0xBE, 0xEE, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
364 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
365 0xC4, 0x30, 0x02, 0x01, 0x01,
367 { /* Init_SR30_SR75 */
368 0x36, 0x03, 0x20, 0x09, 0xC0, 0x36, 0x36, 0x36,
369 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x03, 0xFF,
370 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
371 0x20, 0x0C, 0x44, 0x20, 0x00, 0x36, 0x36, 0x36,
372 0x04, 0x48, 0x83, 0x63, 0x68, 0x72, 0x57, 0x58,
373 0x04, 0x55, 0x59, 0x36, 0x36, 0x00, 0x00, 0x36,
374 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
375 0x50, 0x03, 0x74, 0x14, 0x1C, 0x85, 0x35, 0x13,
376 0x02, 0x45, 0x30, 0x30, 0x40, 0x20,
378 { /* Init_SR80_SR93 */
379 0xFF, 0x07, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x36,
380 0xF7, 0x00, 0x00, 0x00, 0xEF, 0xFF, 0x36, 0x36,
381 0x00, 0x00, 0x00, 0x00,
383 { /* Init_SRA0_SRAF */
384 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
385 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xBF, 0xDF,
387 { /* Init_GR00_GR08 */
388 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
391 { /* Init_AR00_AR14 */
392 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
393 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
394 0x41, 0x00, 0x0F, 0x00, 0x00,
396 { /* Init_CR00_CR18 */
397 0x7F, 0x63, 0x63, 0x00, 0x68, 0x18, 0x72, 0xF0,
398 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
399 0x58, 0x0C, 0x57, 0x64, 0x40, 0x57, 0x00, 0xE3,
402 { /* Init_CR30_CR4D */
403 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x03, 0x20,
404 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xBF, 0xFD,
405 0x7F, 0x63, 0x00, 0x69, 0x18, 0x72, 0x57, 0x00,
406 0x58, 0x0C, 0xE0, 0x20, 0x63, 0x57,
408 { /* Init_CR90_CRA7 */
409 0x56, 0x4B, 0x5E, 0x55, 0x86, 0x9D, 0x8E, 0xAA,
410 0xDB, 0x2A, 0xDF, 0x33, 0x00, 0x00, 0x18, 0x00,
411 0x20, 0x1F, 0x1A, 0x19, 0x0F, 0x0F, 0x0F, 0x00,
414 { /* mode#7: 800 x 600 32Bpp 60Hz */
419 0x03, 0x01, 0x0F, 0x03, 0x0E,
421 { /* Init_SR10_SR24 */
422 0xFF, 0xBE, 0xEE, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
423 0x99, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
424 0xC4, 0x30, 0x02, 0x01, 0x01,
426 { /* Init_SR30_SR75 */
427 0x34, 0x03, 0x20, 0x09, 0xC0, 0x24, 0x24, 0x24,
428 0x24, 0x24, 0x24, 0x24, 0x00, 0x00, 0x03, 0xFF,
429 0x00, 0xFC, 0x00, 0x00, 0x20, 0x38, 0x00, 0xFC,
430 0x20, 0x0C, 0x44, 0x20, 0x00, 0x24, 0x24, 0x24,
431 0x04, 0x48, 0x83, 0x63, 0x68, 0x72, 0x57, 0x58,
432 0x04, 0x55, 0x59, 0x24, 0x24, 0x00, 0x00, 0x24,
433 0x01, 0x80, 0x7A, 0x1A, 0x1A, 0x00, 0x00, 0x00,
434 0x50, 0x03, 0x74, 0x14, 0x1C, 0x85, 0x35, 0x13,
435 0x02, 0x45, 0x30, 0x35, 0x40, 0x20,
437 { /* Init_SR80_SR93 */
438 0x00, 0x00, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x24,
439 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x24, 0x24,
440 0x00, 0x00, 0x00, 0x00,
442 { /* Init_SRA0_SRAF */
443 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
444 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xBF, 0xDF,
446 { /* Init_GR00_GR08 */
447 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
450 { /* Init_AR00_AR14 */
451 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
452 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
453 0x41, 0x00, 0x0F, 0x00, 0x00,
455 { /* Init_CR00_CR18 */
456 0x7F, 0x63, 0x63, 0x00, 0x68, 0x18, 0x72, 0xF0,
457 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
458 0x58, 0x0C, 0x57, 0x64, 0x40, 0x57, 0x00, 0xE3,
461 { /* Init_CR30_CR4D */
462 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x03, 0x20,
463 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xBF, 0xFD,
464 0x7F, 0x63, 0x00, 0x69, 0x18, 0x72, 0x57, 0x00,
465 0x58, 0x0C, 0xE0, 0x20, 0x63, 0x57,
467 { /* Init_CR90_CRA7 */
468 0x56, 0x4B, 0x5E, 0x55, 0x86, 0x9D, 0x8E, 0xAA,
469 0xDB, 0x2A, 0xDF, 0x33, 0x00, 0x00, 0x18, 0x00,
470 0x20, 0x1F, 0x1A, 0x19, 0x0F, 0x0F, 0x0F, 0x00,
473 /* We use 1024x768 table to light 1024x600 panel for lemote */
474 { /* mode#4: 1024 x 600 16Bpp 60Hz */
479 0x03, 0x01, 0x0F, 0x00, 0x0E,
481 { /* Init_SR10_SR24 */
482 0xC8, 0x40, 0x14, 0x60, 0x00, 0x0A, 0x17, 0x20,
483 0x51, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
484 0xC4, 0x30, 0x02, 0x00, 0x01,
486 { /* Init_SR30_SR75 */
487 0x22, 0x03, 0x24, 0x09, 0xC0, 0x22, 0x22, 0x22,
488 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x03, 0xFF,
489 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
490 0x20, 0x0C, 0x44, 0x20, 0x00, 0x22, 0x22, 0x22,
491 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
492 0x00, 0x60, 0x59, 0x22, 0x22, 0x00, 0x00, 0x22,
493 0x01, 0x80, 0x7A, 0x1A, 0x1A, 0x00, 0x00, 0x00,
494 0x50, 0x03, 0x16, 0x02, 0x0D, 0x82, 0x09, 0x02,
495 0x04, 0x45, 0x3F, 0x30, 0x40, 0x20,
497 { /* Init_SR80_SR93 */
498 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
499 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
500 0x00, 0x00, 0x00, 0x00,
502 { /* Init_SRA0_SRAF */
503 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
504 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
506 { /* Init_GR00_GR08 */
507 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
510 { /* Init_AR00_AR14 */
511 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
512 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
513 0x41, 0x00, 0x0F, 0x00, 0x00,
515 { /* Init_CR00_CR18 */
516 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
517 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
518 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
521 { /* Init_CR30_CR4D */
522 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
523 0x00, 0x00, 0x00, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
524 0xA3, 0x7F, 0x00, 0x82, 0x0b, 0x6f, 0x57, 0x00,
525 0x5c, 0x0f, 0xE0, 0xe0, 0x7F, 0x57,
527 { /* Init_CR90_CRA7 */
528 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
529 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
530 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
533 { /* mode#5: 1024 x 768 24Bpp 60Hz */
538 0x03, 0x01, 0x0F, 0x03, 0x0E,
540 { /* Init_SR10_SR24 */
541 0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
542 0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
543 0xC4, 0x30, 0x02, 0x01, 0x01,
545 { /* Init_SR30_SR75 */
546 0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
547 0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
548 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
549 0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
550 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
551 0x00, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
552 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
553 0x50, 0x03, 0x74, 0x14, 0x3B, 0x0D, 0x09, 0x02,
554 0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
556 { /* Init_SR80_SR93 */
557 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
558 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
559 0x00, 0x00, 0x00, 0x00,
561 { /* Init_SRA0_SRAF */
562 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
563 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
565 { /* Init_GR00_GR08 */
566 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
569 { /* Init_AR00_AR14 */
570 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
571 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
572 0x41, 0x00, 0x0F, 0x00, 0x00,
574 { /* Init_CR00_CR18 */
575 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
576 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
577 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
580 { /* Init_CR30_CR4D */
581 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
582 0x00, 0x00, 0x00, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
583 0xA3, 0x7F, 0x00, 0x86, 0x15, 0x24, 0xFF, 0x00,
584 0x01, 0x07, 0xE5, 0x20, 0x7F, 0xFF,
586 { /* Init_CR90_CRA7 */
587 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
588 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
589 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
592 { /* mode#4: 1024 x 768 32Bpp 60Hz */
597 0x03, 0x01, 0x0F, 0x03, 0x0E,
599 { /* Init_SR10_SR24 */
600 0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
601 0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
602 0xC4, 0x32, 0x02, 0x01, 0x01,
604 { /* Init_SR30_SR75 */
605 0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
606 0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
607 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
608 0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
609 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
610 0x00, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
611 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
612 0x50, 0x03, 0x74, 0x14, 0x3B, 0x0D, 0x09, 0x02,
613 0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
615 { /* Init_SR80_SR93 */
616 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
617 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
618 0x00, 0x00, 0x00, 0x00,
620 { /* Init_SRA0_SRAF */
621 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
622 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
624 { /* Init_GR00_GR08 */
625 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
628 { /* Init_AR00_AR14 */
629 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
630 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
631 0x41, 0x00, 0x0F, 0x00, 0x00,
633 { /* Init_CR00_CR18 */
634 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
635 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
636 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
639 { /* Init_CR30_CR4D */
640 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
641 0x00, 0x00, 0x00, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
642 0xA3, 0x7F, 0x00, 0x86, 0x15, 0x24, 0xFF, 0x00,
643 0x01, 0x07, 0xE5, 0x20, 0x7F, 0xFF,
645 { /* Init_CR90_CRA7 */
646 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
647 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
648 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
651 { /* mode#6: 320 x 240 16Bpp 60Hz */
656 0x03, 0x01, 0x0F, 0x03, 0x0E,
658 { /* Init_SR10_SR24 */
659 0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
660 0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
661 0xC4, 0x32, 0x02, 0x01, 0x01,
663 { /* Init_SR30_SR75 */
664 0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
665 0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
666 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
667 0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
668 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
669 0x00, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
670 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
671 0x50, 0x03, 0x74, 0x14, 0x08, 0x43, 0x08, 0x43,
672 0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
674 { /* Init_SR80_SR93 */
675 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
676 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
677 0x00, 0x00, 0x00, 0x00,
679 { /* Init_SRA0_SRAF */
680 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
681 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
683 { /* Init_GR00_GR08 */
684 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
687 { /* Init_AR00_AR14 */
688 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
689 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
690 0x41, 0x00, 0x0F, 0x00, 0x00,
692 { /* Init_CR00_CR18 */
693 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
694 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
695 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
698 { /* Init_CR30_CR4D */
699 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
700 0x00, 0x00, 0x30, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
701 0x2E, 0x27, 0x00, 0x2b, 0x0c, 0x0F, 0xEF, 0x00,
702 0xFe, 0x0f, 0x01, 0xC0, 0x27, 0xEF,
704 { /* Init_CR90_CRA7 */
705 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
706 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
707 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
711 { /* mode#8: 320 x 240 32Bpp 60Hz */
716 0x03, 0x01, 0x0F, 0x03, 0x0E,
718 { /* Init_SR10_SR24 */
719 0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
720 0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
721 0xC4, 0x32, 0x02, 0x01, 0x01,
723 { /* Init_SR30_SR75 */
724 0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
725 0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
726 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
727 0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
728 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
729 0x00, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
730 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
731 0x50, 0x03, 0x74, 0x14, 0x08, 0x43, 0x08, 0x43,
732 0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
734 { /* Init_SR80_SR93 */
735 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
736 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
737 0x00, 0x00, 0x00, 0x00,
739 { /* Init_SRA0_SRAF */
740 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
741 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
743 { /* Init_GR00_GR08 */
744 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
747 { /* Init_AR00_AR14 */
748 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
749 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
750 0x41, 0x00, 0x0F, 0x00, 0x00,
752 { /* Init_CR00_CR18 */
753 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
754 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
755 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
758 { /* Init_CR30_CR4D */
759 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
760 0x00, 0x00, 0x30, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
761 0x2E, 0x27, 0x00, 0x2b, 0x0c, 0x0F, 0xEF, 0x00,
762 0xFe, 0x0f, 0x01, 0xC0, 0x27, 0xEF,
764 { /* Init_CR90_CRA7 */
765 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
766 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
767 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
772 static struct screen_info smtc_scr_info
;
774 static char *mode_option
;
776 /* process command line options, get vga parameter */
777 static void __init
sm7xx_vga_setup(char *options
)
781 if (!options
|| !*options
)
784 smtc_scr_info
.lfb_width
= 0;
785 smtc_scr_info
.lfb_height
= 0;
786 smtc_scr_info
.lfb_depth
= 0;
788 pr_debug("%s = %s\n", __func__
, options
);
790 for (i
= 0; i
< ARRAY_SIZE(vesa_mode_table
); i
++) {
791 if (strstr(options
, vesa_mode_table
[i
].index
)) {
792 smtc_scr_info
.lfb_width
= vesa_mode_table
[i
].lfb_width
;
793 smtc_scr_info
.lfb_height
=
794 vesa_mode_table
[i
].lfb_height
;
795 smtc_scr_info
.lfb_depth
= vesa_mode_table
[i
].lfb_depth
;
801 static void sm712_setpalette(int regno
, unsigned int red
, unsigned int green
,
802 unsigned int blue
, struct fb_info
*info
)
804 /* set bit 5:4 = 01 (write LCD RAM only) */
805 smtc_seqw(0x66, (smtc_seqr(0x66) & 0xC3) | 0x10);
807 smtc_mmiowb(regno
, dac_reg
);
808 smtc_mmiowb(red
>> 10, dac_val
);
809 smtc_mmiowb(green
>> 10, dac_val
);
810 smtc_mmiowb(blue
>> 10, dac_val
);
815 * convert a colour value into a field position
820 static inline unsigned int chan_to_field(unsigned int chan
,
821 struct fb_bitfield
*bf
)
824 chan
>>= 16 - bf
->length
;
825 return chan
<< bf
->offset
;
828 static int smtc_blank(int blank_mode
, struct fb_info
*info
)
830 /* clear DPMS setting */
831 switch (blank_mode
) {
832 case FB_BLANK_UNBLANK
:
833 /* Screen On: HSync: On, VSync : On */
834 smtc_seqw(0x01, (smtc_seqr(0x01) & (~0x20)));
835 smtc_seqw(0x6a, 0x16);
836 smtc_seqw(0x6b, 0x02);
837 smtc_seqw(0x21, (smtc_seqr(0x21) & 0x77));
838 smtc_seqw(0x22, (smtc_seqr(0x22) & (~0x30)));
839 smtc_seqw(0x23, (smtc_seqr(0x23) & (~0xc0)));
840 smtc_seqw(0x24, (smtc_seqr(0x24) | 0x01));
841 smtc_seqw(0x31, (smtc_seqr(0x31) | 0x03));
843 case FB_BLANK_NORMAL
:
844 /* Screen Off: HSync: On, VSync : On Soft blank */
845 smtc_seqw(0x01, (smtc_seqr(0x01) & (~0x20)));
846 smtc_seqw(0x6a, 0x16);
847 smtc_seqw(0x6b, 0x02);
848 smtc_seqw(0x22, (smtc_seqr(0x22) & (~0x30)));
849 smtc_seqw(0x23, (smtc_seqr(0x23) & (~0xc0)));
850 smtc_seqw(0x24, (smtc_seqr(0x24) | 0x01));
851 smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
853 case FB_BLANK_VSYNC_SUSPEND
:
854 /* Screen On: HSync: On, VSync : Off */
855 smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20));
856 smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0)));
857 smtc_seqw(0x6a, 0x0c);
858 smtc_seqw(0x6b, 0x02);
859 smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88));
860 smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x20));
861 smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0x20));
862 smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01)));
863 smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
864 smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80));
866 case FB_BLANK_HSYNC_SUSPEND
:
867 /* Screen On: HSync: Off, VSync : On */
868 smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20));
869 smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0)));
870 smtc_seqw(0x6a, 0x0c);
871 smtc_seqw(0x6b, 0x02);
872 smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88));
873 smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x10));
874 smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0xD8));
875 smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01)));
876 smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
877 smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80));
879 case FB_BLANK_POWERDOWN
:
880 /* Screen On: HSync: Off, VSync : Off */
881 smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20));
882 smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0)));
883 smtc_seqw(0x6a, 0x0c);
884 smtc_seqw(0x6b, 0x02);
885 smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88));
886 smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x30));
887 smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0xD8));
888 smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01)));
889 smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
890 smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80));
899 static int smtc_setcolreg(unsigned int regno
, unsigned int red
,
900 unsigned int green
, unsigned int blue
,
901 unsigned int trans
, struct fb_info
*info
)
903 struct smtcfb_info
*sfb
;
911 switch (sfb
->fb
->fix
.visual
) {
912 case FB_VISUAL_DIRECTCOLOR
:
913 case FB_VISUAL_TRUECOLOR
:
915 * 16/32 bit true-colour, use pseudo-palette for 16 base color
919 if (sfb
->fb
->var
.bits_per_pixel
== 16) {
920 u32
*pal
= sfb
->fb
->pseudo_palette
;
922 val
= chan_to_field(red
, &sfb
->fb
->var
.red
);
923 val
|= chan_to_field(green
, &sfb
->fb
->var
.green
);
924 val
|= chan_to_field(blue
, &sfb
->fb
->var
.blue
);
925 pal
[regno
] = pal_rgb(red
, green
, blue
, val
);
927 u32
*pal
= sfb
->fb
->pseudo_palette
;
929 val
= chan_to_field(red
, &sfb
->fb
->var
.red
);
930 val
|= chan_to_field(green
, &sfb
->fb
->var
.green
);
931 val
|= chan_to_field(blue
, &sfb
->fb
->var
.blue
);
932 pal
[regno
] = big_swap(val
);
936 case FB_VISUAL_PSEUDOCOLOR
:
937 /* color depth 8 bit */
938 sm712_setpalette(regno
, red
, green
, blue
, info
);
942 return 1; /* unknown type */
948 static ssize_t
smtcfb_read(struct fb_info
*info
, char __user
*buf
,
949 size_t count
, loff_t
*ppos
)
951 unsigned long p
= *ppos
;
955 int c
, i
, cnt
= 0, err
= 0;
956 unsigned long total_size
;
958 if (!info
|| !info
->screen_base
)
961 if (info
->state
!= FBINFO_STATE_RUNNING
)
964 total_size
= info
->screen_size
;
967 total_size
= info
->fix
.smem_len
;
972 if (count
>= total_size
)
975 if (count
+ p
> total_size
)
976 count
= total_size
- p
;
978 buffer
= kmalloc((count
> PAGE_SIZE
) ? PAGE_SIZE
: count
, GFP_KERNEL
);
982 src
= (u32 __iomem
*)(info
->screen_base
+ p
);
984 if (info
->fbops
->fb_sync
)
985 info
->fbops
->fb_sync(info
);
988 c
= (count
> PAGE_SIZE
) ? PAGE_SIZE
: count
;
990 for (i
= c
>> 2; i
--;) {
991 *dst
= fb_readl(src
++);
992 *dst
= big_swap(*dst
);
996 u8
*dst8
= (u8
*)dst
;
997 u8 __iomem
*src8
= (u8 __iomem
*)src
;
999 for (i
= c
& 3; i
--;) {
1001 *dst8
++ = fb_readb(++src8
);
1003 *dst8
++ = fb_readb(--src8
);
1007 src
= (u32 __iomem
*)src8
;
1010 if (copy_to_user(buf
, buffer
, c
)) {
1022 return (err
) ? err
: cnt
;
1025 static ssize_t
smtcfb_write(struct fb_info
*info
, const char __user
*buf
,
1026 size_t count
, loff_t
*ppos
)
1028 unsigned long p
= *ppos
;
1032 int c
, i
, cnt
= 0, err
= 0;
1033 unsigned long total_size
;
1035 if (!info
|| !info
->screen_base
)
1038 if (info
->state
!= FBINFO_STATE_RUNNING
)
1041 total_size
= info
->screen_size
;
1043 if (total_size
== 0)
1044 total_size
= info
->fix
.smem_len
;
1049 if (count
> total_size
) {
1054 if (count
+ p
> total_size
) {
1058 count
= total_size
- p
;
1061 buffer
= kmalloc((count
> PAGE_SIZE
) ? PAGE_SIZE
: count
, GFP_KERNEL
);
1065 dst
= (u32 __iomem
*)(info
->screen_base
+ p
);
1067 if (info
->fbops
->fb_sync
)
1068 info
->fbops
->fb_sync(info
);
1071 c
= (count
> PAGE_SIZE
) ? PAGE_SIZE
: count
;
1074 if (copy_from_user(src
, buf
, c
)) {
1079 for (i
= c
>> 2; i
--;) {
1080 fb_writel(big_swap(*src
), dst
++);
1084 u8
*src8
= (u8
*)src
;
1085 u8 __iomem
*dst8
= (u8 __iomem
*)dst
;
1087 for (i
= c
& 3; i
--;) {
1089 fb_writeb(*src8
++, ++dst8
);
1091 fb_writeb(*src8
++, --dst8
);
1095 dst
= (u32 __iomem
*)dst8
;
1106 return (cnt
) ? cnt
: err
;
1109 static void sm7xx_set_timing(struct smtcfb_info
*sfb
)
1112 u32 m_nscreenstride
;
1114 dev_dbg(&sfb
->pdev
->dev
,
1115 "sfb->width=%d sfb->height=%d sfb->fb->var.bits_per_pixel=%d sfb->hz=%d\n",
1116 sfb
->width
, sfb
->height
, sfb
->fb
->var
.bits_per_pixel
, sfb
->hz
);
1118 for (j
= 0; j
< ARRAY_SIZE(vgamode
); j
++) {
1119 if (vgamode
[j
].mmsizex
!= sfb
->width
||
1120 vgamode
[j
].mmsizey
!= sfb
->height
||
1121 vgamode
[j
].bpp
!= sfb
->fb
->var
.bits_per_pixel
||
1122 vgamode
[j
].hz
!= sfb
->hz
)
1125 dev_dbg(&sfb
->pdev
->dev
,
1126 "vgamode[j].mmsizex=%d vgamode[j].mmSizeY=%d vgamode[j].bpp=%d vgamode[j].hz=%d\n",
1127 vgamode
[j
].mmsizex
, vgamode
[j
].mmsizey
,
1128 vgamode
[j
].bpp
, vgamode
[j
].hz
);
1130 dev_dbg(&sfb
->pdev
->dev
, "vgamode index=%d\n", j
);
1132 smtc_mmiowb(0x0, 0x3c6);
1136 smtc_mmiowb(vgamode
[j
].init_misc
, 0x3c2);
1138 /* init SEQ register SR00 - SR04 */
1139 for (i
= 0; i
< SIZE_SR00_SR04
; i
++)
1140 smtc_seqw(i
, vgamode
[j
].init_sr00_sr04
[i
]);
1142 /* init SEQ register SR10 - SR24 */
1143 for (i
= 0; i
< SIZE_SR10_SR24
; i
++)
1144 smtc_seqw(i
+ 0x10, vgamode
[j
].init_sr10_sr24
[i
]);
1146 /* init SEQ register SR30 - SR75 */
1147 for (i
= 0; i
< SIZE_SR30_SR75
; i
++)
1148 if ((i
+ 0x30) != 0x62 && (i
+ 0x30) != 0x6a &&
1151 vgamode
[j
].init_sr30_sr75
[i
]);
1153 /* init SEQ register SR80 - SR93 */
1154 for (i
= 0; i
< SIZE_SR80_SR93
; i
++)
1155 smtc_seqw(i
+ 0x80, vgamode
[j
].init_sr80_sr93
[i
]);
1157 /* init SEQ register SRA0 - SRAF */
1158 for (i
= 0; i
< SIZE_SRA0_SRAF
; i
++)
1159 smtc_seqw(i
+ 0xa0, vgamode
[j
].init_sra0_sraf
[i
]);
1161 /* init Graphic register GR00 - GR08 */
1162 for (i
= 0; i
< SIZE_GR00_GR08
; i
++)
1163 smtc_grphw(i
, vgamode
[j
].init_gr00_gr08
[i
]);
1165 /* init Attribute register AR00 - AR14 */
1166 for (i
= 0; i
< SIZE_AR00_AR14
; i
++)
1167 smtc_attrw(i
, vgamode
[j
].init_ar00_ar14
[i
]);
1169 /* init CRTC register CR00 - CR18 */
1170 for (i
= 0; i
< SIZE_CR00_CR18
; i
++)
1171 smtc_crtcw(i
, vgamode
[j
].init_cr00_cr18
[i
]);
1173 /* init CRTC register CR30 - CR4D */
1174 for (i
= 0; i
< SIZE_CR30_CR4D
; i
++)
1175 smtc_crtcw(i
+ 0x30, vgamode
[j
].init_cr30_cr4d
[i
]);
1177 /* init CRTC register CR90 - CRA7 */
1178 for (i
= 0; i
< SIZE_CR90_CRA7
; i
++)
1179 smtc_crtcw(i
+ 0x90, vgamode
[j
].init_cr90_cra7
[i
]);
1181 smtc_mmiowb(0x67, 0x3c2);
1183 /* set VPR registers */
1184 writel(0x0, sfb
->vp_regs
+ 0x0C);
1185 writel(0x0, sfb
->vp_regs
+ 0x40);
1187 /* set data width */
1188 m_nscreenstride
= (sfb
->width
* sfb
->fb
->var
.bits_per_pixel
) / 64;
1189 switch (sfb
->fb
->var
.bits_per_pixel
) {
1191 writel(0x0, sfb
->vp_regs
+ 0x0);
1194 writel(0x00020000, sfb
->vp_regs
+ 0x0);
1197 writel(0x00040000, sfb
->vp_regs
+ 0x0);
1200 writel(0x00030000, sfb
->vp_regs
+ 0x0);
1203 writel((u32
)(((m_nscreenstride
+ 2) << 16) | m_nscreenstride
),
1204 sfb
->vp_regs
+ 0x10);
1207 static void smtc_set_timing(struct smtcfb_info
*sfb
)
1209 switch (sfb
->chip_id
) {
1213 sm7xx_set_timing(sfb
);
1218 static void smtcfb_setmode(struct smtcfb_info
*sfb
)
1220 switch (sfb
->fb
->var
.bits_per_pixel
) {
1222 sfb
->fb
->fix
.visual
= FB_VISUAL_TRUECOLOR
;
1223 sfb
->fb
->fix
.line_length
= sfb
->fb
->var
.xres
* 4;
1224 sfb
->fb
->var
.red
.length
= 8;
1225 sfb
->fb
->var
.green
.length
= 8;
1226 sfb
->fb
->var
.blue
.length
= 8;
1227 sfb
->fb
->var
.red
.offset
= 16;
1228 sfb
->fb
->var
.green
.offset
= 8;
1229 sfb
->fb
->var
.blue
.offset
= 0;
1232 sfb
->fb
->fix
.visual
= FB_VISUAL_TRUECOLOR
;
1233 sfb
->fb
->fix
.line_length
= sfb
->fb
->var
.xres
* 3;
1234 sfb
->fb
->var
.red
.length
= 8;
1235 sfb
->fb
->var
.green
.length
= 8;
1236 sfb
->fb
->var
.blue
.length
= 8;
1237 sfb
->fb
->var
.red
.offset
= 16;
1238 sfb
->fb
->var
.green
.offset
= 8;
1239 sfb
->fb
->var
.blue
.offset
= 0;
1242 sfb
->fb
->fix
.visual
= FB_VISUAL_PSEUDOCOLOR
;
1243 sfb
->fb
->fix
.line_length
= sfb
->fb
->var
.xres
;
1244 sfb
->fb
->var
.red
.length
= 3;
1245 sfb
->fb
->var
.green
.length
= 3;
1246 sfb
->fb
->var
.blue
.length
= 2;
1247 sfb
->fb
->var
.red
.offset
= 5;
1248 sfb
->fb
->var
.green
.offset
= 2;
1249 sfb
->fb
->var
.blue
.offset
= 0;
1253 sfb
->fb
->fix
.visual
= FB_VISUAL_TRUECOLOR
;
1254 sfb
->fb
->fix
.line_length
= sfb
->fb
->var
.xres
* 2;
1255 sfb
->fb
->var
.red
.length
= 5;
1256 sfb
->fb
->var
.green
.length
= 6;
1257 sfb
->fb
->var
.blue
.length
= 5;
1258 sfb
->fb
->var
.red
.offset
= 11;
1259 sfb
->fb
->var
.green
.offset
= 5;
1260 sfb
->fb
->var
.blue
.offset
= 0;
1264 sfb
->width
= sfb
->fb
->var
.xres
;
1265 sfb
->height
= sfb
->fb
->var
.yres
;
1267 smtc_set_timing(sfb
);
1270 static int smtc_check_var(struct fb_var_screeninfo
*var
, struct fb_info
*info
)
1273 if (var
->xres_virtual
< var
->xres
)
1274 var
->xres_virtual
= var
->xres
;
1276 if (var
->yres_virtual
< var
->yres
)
1277 var
->yres_virtual
= var
->yres
;
1279 /* set valid default bpp */
1280 if ((var
->bits_per_pixel
!= 8) && (var
->bits_per_pixel
!= 16) &&
1281 (var
->bits_per_pixel
!= 24) && (var
->bits_per_pixel
!= 32))
1282 var
->bits_per_pixel
= 16;
1287 static int smtc_set_par(struct fb_info
*info
)
1289 smtcfb_setmode(info
->par
);
1294 static struct fb_ops smtcfb_ops
= {
1295 .owner
= THIS_MODULE
,
1296 .fb_check_var
= smtc_check_var
,
1297 .fb_set_par
= smtc_set_par
,
1298 .fb_setcolreg
= smtc_setcolreg
,
1299 .fb_blank
= smtc_blank
,
1300 .fb_fillrect
= cfb_fillrect
,
1301 .fb_imageblit
= cfb_imageblit
,
1302 .fb_copyarea
= cfb_copyarea
,
1303 .fb_read
= smtcfb_read
,
1304 .fb_write
= smtcfb_write
,
1308 * Unmap in the memory mapped IO registers
1311 static void smtc_unmap_mmio(struct smtcfb_info
*sfb
)
1313 if (sfb
&& smtc_regbaseaddress
)
1314 smtc_regbaseaddress
= NULL
;
1318 * Map in the screen memory
1321 static int smtc_map_smem(struct smtcfb_info
*sfb
,
1322 struct pci_dev
*pdev
, u_long smem_len
)
1324 sfb
->fb
->fix
.smem_start
= pci_resource_start(pdev
, 0);
1326 if (sfb
->fb
->var
.bits_per_pixel
== 32)
1327 sfb
->fb
->fix
.smem_start
+= big_addr
;
1329 sfb
->fb
->fix
.smem_len
= smem_len
;
1331 sfb
->fb
->screen_base
= sfb
->lfb
;
1333 if (!sfb
->fb
->screen_base
) {
1335 "%s: unable to map screen memory\n", sfb
->fb
->fix
.id
);
1343 * Unmap in the screen memory
1346 static void smtc_unmap_smem(struct smtcfb_info
*sfb
)
1348 if (sfb
&& sfb
->fb
->screen_base
) {
1349 iounmap(sfb
->fb
->screen_base
);
1350 sfb
->fb
->screen_base
= NULL
;
1355 * We need to wake up the device and make sure its in linear memory mode.
1357 static inline void sm7xx_init_hw(void)
1359 outb_p(0x18, 0x3c4);
1360 outb_p(0x11, 0x3c5);
1363 static int smtcfb_pci_probe(struct pci_dev
*pdev
,
1364 const struct pci_device_id
*ent
)
1366 struct smtcfb_info
*sfb
;
1367 struct fb_info
*info
;
1368 u_long smem_size
= 0x00800000; /* default 8MB */
1370 unsigned long mmio_base
;
1372 dev_info(&pdev
->dev
, "Silicon Motion display driver.\n");
1374 err
= pci_enable_device(pdev
); /* enable SMTC chip */
1378 err
= pci_request_region(pdev
, 0, "sm7xxfb");
1380 dev_err(&pdev
->dev
, "cannot reserve framebuffer region\n");
1381 goto failed_regions
;
1384 sprintf(smtcfb_fix
.id
, "sm%Xfb", ent
->device
);
1386 info
= framebuffer_alloc(sizeof(*sfb
), &pdev
->dev
);
1388 dev_err(&pdev
->dev
, "framebuffer_alloc failed\n");
1395 sfb
->chip_id
= ent
->device
;
1397 info
->flags
= FBINFO_FLAG_DEFAULT
;
1398 info
->fbops
= &smtcfb_ops
;
1399 info
->fix
= smtcfb_fix
;
1400 info
->var
= smtcfb_var
;
1401 info
->pseudo_palette
= sfb
->colreg
;
1404 pci_set_drvdata(pdev
, sfb
);
1408 /* get mode parameter from smtc_scr_info */
1409 if (smtc_scr_info
.lfb_width
!= 0) {
1410 sfb
->fb
->var
.xres
= smtc_scr_info
.lfb_width
;
1411 sfb
->fb
->var
.yres
= smtc_scr_info
.lfb_height
;
1412 sfb
->fb
->var
.bits_per_pixel
= smtc_scr_info
.lfb_depth
;
1414 /* default resolution 1024x600 16bit mode */
1415 sfb
->fb
->var
.xres
= SCREEN_X_RES
;
1416 sfb
->fb
->var
.yres
= SCREEN_Y_RES
;
1417 sfb
->fb
->var
.bits_per_pixel
= SCREEN_BPP
;
1420 big_pixel_depth(sfb
->fb
->var
.bits_per_pixel
, smtc_scr_info
.lfb_depth
);
1421 /* Map address and memory detection */
1422 mmio_base
= pci_resource_start(pdev
, 0);
1423 pci_read_config_byte(pdev
, PCI_REVISION_ID
, &sfb
->chip_rev_id
);
1425 switch (sfb
->chip_id
) {
1428 sfb
->fb
->fix
.mmio_start
= mmio_base
+ 0x00400000;
1429 sfb
->fb
->fix
.mmio_len
= 0x00400000;
1430 smem_size
= SM712_VIDEOMEMORYSIZE
;
1431 sfb
->lfb
= ioremap(mmio_base
, mmio_addr
);
1434 "%s: unable to map memory mapped IO!\n",
1440 sfb
->mmio
= (smtc_regbaseaddress
=
1441 sfb
->lfb
+ 0x00700000);
1442 sfb
->dp_regs
= sfb
->lfb
+ 0x00408000;
1443 sfb
->vp_regs
= sfb
->lfb
+ 0x0040c000;
1444 if (sfb
->fb
->var
.bits_per_pixel
== 32) {
1445 sfb
->lfb
+= big_addr
;
1446 dev_info(&pdev
->dev
, "sfb->lfb=%p\n", sfb
->lfb
);
1449 /* set MCLK = 14.31818 * (0x16 / 0x2) */
1450 smtc_seqw(0x6a, 0x16);
1451 smtc_seqw(0x6b, 0x02);
1452 smtc_seqw(0x62, 0x3e);
1453 /* enable PCI burst */
1454 smtc_seqw(0x17, 0x20);
1455 /* enable word swap */
1456 if (sfb
->fb
->var
.bits_per_pixel
== 32)
1460 sfb
->fb
->fix
.mmio_start
= mmio_base
;
1461 sfb
->fb
->fix
.mmio_len
= 0x00200000;
1462 smem_size
= SM722_VIDEOMEMORYSIZE
;
1463 sfb
->dp_regs
= ioremap(mmio_base
, 0x00a00000);
1464 sfb
->lfb
= sfb
->dp_regs
+ 0x00200000;
1465 sfb
->mmio
= (smtc_regbaseaddress
=
1466 sfb
->dp_regs
+ 0x000c0000);
1467 sfb
->vp_regs
= sfb
->dp_regs
+ 0x800;
1469 smtc_seqw(0x62, 0xff);
1470 smtc_seqw(0x6a, 0x0d);
1471 smtc_seqw(0x6b, 0x02);
1475 "No valid Silicon Motion display chip was detected!\n");
1480 /* can support 32 bpp */
1481 if (sfb
->fb
->var
.bits_per_pixel
== 15)
1482 sfb
->fb
->var
.bits_per_pixel
= 16;
1484 sfb
->fb
->var
.xres_virtual
= sfb
->fb
->var
.xres
;
1485 sfb
->fb
->var
.yres_virtual
= sfb
->fb
->var
.yres
;
1486 err
= smtc_map_smem(sfb
, pdev
, smem_size
);
1490 smtcfb_setmode(sfb
);
1492 err
= register_framebuffer(info
);
1496 dev_info(&pdev
->dev
,
1497 "Silicon Motion SM%X Rev%X primary display mode %dx%d-%d Init Complete.\n",
1498 sfb
->chip_id
, sfb
->chip_rev_id
, sfb
->fb
->var
.xres
,
1499 sfb
->fb
->var
.yres
, sfb
->fb
->var
.bits_per_pixel
);
1504 dev_err(&pdev
->dev
, "Silicon Motion, Inc. primary display init fail.\n");
1506 smtc_unmap_smem(sfb
);
1507 smtc_unmap_mmio(sfb
);
1509 framebuffer_release(info
);
1512 pci_release_region(pdev
, 0);
1515 pci_disable_device(pdev
);
1523 * 0x720 (Lynx3DM, Lynx3DM+)
1525 static const struct pci_device_id smtcfb_pci_table
[] = {
1526 { PCI_DEVICE(0x126f, 0x710), },
1527 { PCI_DEVICE(0x126f, 0x712), },
1528 { PCI_DEVICE(0x126f, 0x720), },
1532 MODULE_DEVICE_TABLE(pci
, smtcfb_pci_table
);
1534 static void smtcfb_pci_remove(struct pci_dev
*pdev
)
1536 struct smtcfb_info
*sfb
;
1538 sfb
= pci_get_drvdata(pdev
);
1539 smtc_unmap_smem(sfb
);
1540 smtc_unmap_mmio(sfb
);
1541 unregister_framebuffer(sfb
->fb
);
1542 framebuffer_release(sfb
->fb
);
1543 pci_release_region(pdev
, 0);
1544 pci_disable_device(pdev
);
1547 static int __maybe_unused
smtcfb_pci_suspend(struct device
*device
)
1549 struct pci_dev
*pdev
= to_pci_dev(device
);
1550 struct smtcfb_info
*sfb
;
1552 sfb
= pci_get_drvdata(pdev
);
1554 /* set the hw in sleep mode use external clock and self memory refresh
1555 * so that we can turn off internal PLLs later on
1557 smtc_seqw(0x20, (smtc_seqr(0x20) | 0xc0));
1558 smtc_seqw(0x69, (smtc_seqr(0x69) & 0xf7));
1561 fb_set_suspend(sfb
->fb
, 1);
1564 /* additionally turn off all function blocks including internal PLLs */
1565 smtc_seqw(0x21, 0xff);
1570 static int __maybe_unused
smtcfb_pci_resume(struct device
*device
)
1572 struct pci_dev
*pdev
= to_pci_dev(device
);
1573 struct smtcfb_info
*sfb
;
1575 sfb
= pci_get_drvdata(pdev
);
1577 /* reinit hardware */
1579 switch (sfb
->chip_id
) {
1582 /* set MCLK = 14.31818 * (0x16 / 0x2) */
1583 smtc_seqw(0x6a, 0x16);
1584 smtc_seqw(0x6b, 0x02);
1585 smtc_seqw(0x62, 0x3e);
1586 /* enable PCI burst */
1587 smtc_seqw(0x17, 0x20);
1588 if (sfb
->fb
->var
.bits_per_pixel
== 32)
1592 smtc_seqw(0x62, 0xff);
1593 smtc_seqw(0x6a, 0x0d);
1594 smtc_seqw(0x6b, 0x02);
1598 smtc_seqw(0x34, (smtc_seqr(0x34) | 0xc0));
1599 smtc_seqw(0x33, ((smtc_seqr(0x33) | 0x08) & 0xfb));
1601 smtcfb_setmode(sfb
);
1604 fb_set_suspend(sfb
->fb
, 0);
1610 static SIMPLE_DEV_PM_OPS(sm7xx_pm_ops
, smtcfb_pci_suspend
, smtcfb_pci_resume
);
1612 static struct pci_driver smtcfb_driver
= {
1614 .id_table
= smtcfb_pci_table
,
1615 .probe
= smtcfb_pci_probe
,
1616 .remove
= smtcfb_pci_remove
,
1617 .driver
.pm
= &sm7xx_pm_ops
,
1620 static int __init
sm712fb_init(void)
1622 char *option
= NULL
;
1624 if (fb_get_options("sm712fb", &option
))
1626 if (option
&& *option
)
1627 mode_option
= option
;
1628 sm7xx_vga_setup(mode_option
);
1630 return pci_register_driver(&smtcfb_driver
);
1633 module_init(sm712fb_init
);
1635 static void __exit
sm712fb_exit(void)
1637 pci_unregister_driver(&smtcfb_driver
);
1640 module_exit(sm712fb_exit
);
1642 MODULE_AUTHOR("Siliconmotion ");
1643 MODULE_DESCRIPTION("Framebuffer driver for SMI Graphic Cards");
1644 MODULE_LICENSE("GPL");