1#include <linux/kernel.h>
2#include <linux/module.h>
3#include <linux/errno.h>
4#include <linux/string.h>
5#include <linux/mm.h>
6#include <linux/slab.h>
7#include <linux/delay.h>
8#include <linux/fb.h>
9#include <linux/ioport.h>
10#include <linux/init.h>
11#include <linux/pci.h>
12#include <linux/mm_types.h>
13#include <linux/vmalloc.h>
14#include <linux/pagemap.h>
15#include <linux/screen_info.h>
16#include <linux/vmalloc.h>
17#include <linux/pagemap.h>
18#include <linux/console.h>
19#include <asm/fb.h>
20#include "sm750.h"
21#include "sm750_accel.h"
22#include "sm750_cursor.h"
23
24#include "modedb.h"
25
26/*
27 * #ifdef __BIG_ENDIAN
28 * ssize_t lynxfb_ops_write(struct fb_info *info, const char __user *buf,
29 * size_t count, loff_t *ppos);
30 * ssize_t lynxfb_ops_read(struct fb_info *info, char __user *buf,
31 * size_t count, loff_t *ppos);
32 * #endif
33 */
34
35/* common var for all device */
36static int g_hwcursor = 1;
37static int g_noaccel;
38static int g_nomtrr;
39static const char *g_fbmode[] = {NULL, NULL};
40static const char *g_def_fbmode = "800x600-16@60";
41static char *g_settings;
42static int g_dualview;
43static char *g_option;
44
45static const struct fb_videomode lynx750_ext[] = {
46	/*	1024x600-60 VESA	[1.71:1] */
47	{NULL,  60, 1024, 600, 20423, 144,  40, 18, 1, 104, 3,
48	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
49	 FB_VMODE_NONINTERLACED},
50
51	/*	1024x600-70 VESA */
52	{NULL,  70, 1024, 600, 17211, 152,  48, 21, 1, 104, 3,
53	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
54	 FB_VMODE_NONINTERLACED},
55
56	/*	1024x600-75 VESA */
57	{NULL,  75, 1024, 600, 15822, 160,  56, 23, 1, 104, 3,
58	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
59	 FB_VMODE_NONINTERLACED},
60
61	/*	1024x600-85 VESA */
62	{NULL,  85, 1024, 600, 13730, 168,  56, 26, 1, 112, 3,
63	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
64	 FB_VMODE_NONINTERLACED},
65
66	/*	720x480	*/
67	{NULL, 60,  720,  480,  37427, 88,   16, 13, 1,   72,  3,
68	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
69	 FB_VMODE_NONINTERLACED},
70
71	/*	1280x720		[1.78:1]	*/
72	{NULL, 60,  1280,  720,  13426, 162, 86, 22, 1,  136, 3,
73	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
74	 FB_VMODE_NONINTERLACED},
75
76	/*	1280x768@60 */
77	{NULL, 60, 1280, 768, 12579, 192, 64, 20, 3, 128, 7,
78	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
79	 FB_VMODE_NONINTERLACED},
80
81	/*	1360 x 768	[1.77083:1]	*/
82	{NULL,  60, 1360, 768, 11804, 208,  64, 23, 1, 144, 3,
83	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
84	 FB_VMODE_NONINTERLACED},
85
86	/*	1368 x 768      [1.78:1]	*/
87	{NULL, 60,  1368,  768,  11647, 216, 72, 23, 1,  144, 3,
88	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
89	 FB_VMODE_NONINTERLACED},
90
91	/*	1440 x 900		[16:10]	*/
92	{NULL, 60, 1440, 900, 9392, 232, 80, 28, 1, 152, 3,
93	 FB_SYNC_VERT_HIGH_ACT,
94	 FB_VMODE_NONINTERLACED},
95
96	/*	1440x960		[15:10]	*/
97	{NULL, 60, 1440, 960, 8733, 240, 88, 30, 1, 152, 3,
98	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
99	 FB_VMODE_NONINTERLACED},
100
101	/*	1920x1080	[16:9]	*/
102	{NULL, 60, 1920, 1080, 6734, 148, 88, 41, 1, 44, 3,
103	 FB_SYNC_VERT_HIGH_ACT,
104	 FB_VMODE_NONINTERLACED},
105};
106
107
108/* no hardware cursor supported under version 2.6.10, kernel bug */
109static int lynxfb_ops_cursor(struct fb_info *info, struct fb_cursor *fbcursor)
110{
111	struct lynxfb_par *par;
112	struct lynxfb_crtc *crtc;
113	struct lynx_cursor *cursor;
114
115	par = info->par;
116	crtc = &par->crtc;
117	cursor = &crtc->cursor;
118
119	if (fbcursor->image.width > cursor->maxW ||
120	   fbcursor->image.height > cursor->maxH ||
121	   fbcursor->image.depth > 1) {
122		return -ENXIO;
123	}
124
125	hw_cursor_disable(cursor);
126	if (fbcursor->set & FB_CUR_SETSIZE)
127		hw_cursor_setSize(cursor,
128				  fbcursor->image.width,
129				  fbcursor->image.height);
130
131	if (fbcursor->set & FB_CUR_SETPOS)
132		hw_cursor_setPos(cursor,
133				 fbcursor->image.dx - info->var.xoffset,
134				 fbcursor->image.dy - info->var.yoffset);
135
136	if (fbcursor->set & FB_CUR_SETCMAP) {
137		/* get the 16bit color of kernel means */
138		u16 fg, bg;
139
140		fg = ((info->cmap.red[fbcursor->image.fg_color] & 0xf800)) |
141		      ((info->cmap.green[fbcursor->image.fg_color] & 0xfc00) >> 5) |
142		      ((info->cmap.blue[fbcursor->image.fg_color] & 0xf800) >> 11);
143
144		bg = ((info->cmap.red[fbcursor->image.bg_color] & 0xf800)) |
145		      ((info->cmap.green[fbcursor->image.bg_color] & 0xfc00) >> 5) |
146		      ((info->cmap.blue[fbcursor->image.bg_color] & 0xf800) >> 11);
147
148		hw_cursor_setColor(cursor, fg, bg);
149	}
150
151	if (fbcursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) {
152		hw_cursor_setData(cursor,
153				  fbcursor->rop,
154				  fbcursor->image.data,
155				  fbcursor->mask);
156	}
157
158	if (fbcursor->enable)
159		hw_cursor_enable(cursor);
160
161	return 0;
162}
163
164static void lynxfb_ops_fillrect(struct fb_info *info,
165				const struct fb_fillrect *region)
166{
167	struct lynxfb_par *par;
168	struct sm750_dev *sm750_dev;
169	unsigned int base, pitch, Bpp, rop;
170	u32 color;
171
172	if (info->state != FBINFO_STATE_RUNNING)
173		return;
174
175	par = info->par;
176	sm750_dev = par->dev;
177
178	/*
179	 * each time 2d function begin to work,below three variable always need
180	 * be set, seems we can put them together in some place
181	 */
182	base = par->crtc.oScreen;
183	pitch = info->fix.line_length;
184	Bpp = info->var.bits_per_pixel >> 3;
185
186	color = (Bpp == 1) ? region->color :
187		((u32 *)info->pseudo_palette)[region->color];
188	rop = (region->rop != ROP_COPY) ? HW_ROP2_XOR : HW_ROP2_COPY;
189
190	/*
191	 * If not use spin_lock,system will die if user load driver
192	 * and immediately unload driver frequently (dual)
193	 */
194	if (sm750_dev->dual)
195		spin_lock(&sm750_dev->slock);
196
197	sm750_dev->accel.de_fillrect(&sm750_dev->accel,
198				     base, pitch, Bpp,
199				     region->dx, region->dy,
200				     region->width, region->height,
201				     color, rop);
202	if (sm750_dev->dual)
203		spin_unlock(&sm750_dev->slock);
204}
205
206static void lynxfb_ops_copyarea(struct fb_info *info,
207				const struct fb_copyarea *region)
208{
209	struct lynxfb_par *par;
210	struct sm750_dev *sm750_dev;
211	unsigned int base, pitch, Bpp;
212
213	par = info->par;
214	sm750_dev = par->dev;
215
216	/*
217	 * each time 2d function begin to work,below three variable always need
218	 * be set, seems we can put them together in some place
219	 */
220	base = par->crtc.oScreen;
221	pitch = info->fix.line_length;
222	Bpp = info->var.bits_per_pixel >> 3;
223
224	/*
225	 * If not use spin_lock, system will die if user load driver
226	 * and immediately unload driver frequently (dual)
227	 */
228	if (sm750_dev->dual)
229		spin_lock(&sm750_dev->slock);
230
231	sm750_dev->accel.de_copyarea(&sm750_dev->accel,
232				     base, pitch, region->sx, region->sy,
233				     base, pitch, Bpp, region->dx, region->dy,
234				     region->width, region->height,
235				     HW_ROP2_COPY);
236	if (sm750_dev->dual)
237		spin_unlock(&sm750_dev->slock);
238}
239
240static void lynxfb_ops_imageblit(struct fb_info *info,
241				 const struct fb_image *image)
242{
243	unsigned int base, pitch, Bpp;
244	unsigned int fgcol, bgcol;
245	struct lynxfb_par *par;
246	struct sm750_dev *sm750_dev;
247
248	par = info->par;
249	sm750_dev = par->dev;
250	/*
251	 * each time 2d function begin to work,below three variable always need
252	 * be set, seems we can put them together in some place
253	 */
254	base = par->crtc.oScreen;
255	pitch = info->fix.line_length;
256	Bpp = info->var.bits_per_pixel >> 3;
257
258	/* TODO: Implement hardware acceleration for image->depth > 1 */
259	if (image->depth != 1) {
260		cfb_imageblit(info, image);
261		return;
262	}
263
264	if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
265	    info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
266		fgcol = ((u32 *)info->pseudo_palette)[image->fg_color];
267		bgcol = ((u32 *)info->pseudo_palette)[image->bg_color];
268	} else {
269		fgcol = image->fg_color;
270		bgcol = image->bg_color;
271	}
272
273	/*
274	 * If not use spin_lock, system will die if user load driver
275	 * and immediately unload driver frequently (dual)
276	 */
277	if (sm750_dev->dual)
278		spin_lock(&sm750_dev->slock);
279
280	sm750_dev->accel.de_imageblit(&sm750_dev->accel,
281				      image->data, image->width >> 3, 0,
282				      base, pitch, Bpp,
283				      image->dx, image->dy,
284				      image->width, image->height,
285				      fgcol, bgcol, HW_ROP2_COPY);
286	if (sm750_dev->dual)
287		spin_unlock(&sm750_dev->slock);
288}
289
290static int lynxfb_ops_pan_display(struct fb_var_screeninfo *var,
291				  struct fb_info *info)
292{
293	struct lynxfb_par *par;
294	struct lynxfb_crtc *crtc;
295
296	if (!info)
297		return -EINVAL;
298
299	par = info->par;
300	crtc = &par->crtc;
301	return hw_sm750_pan_display(crtc, var, info);
302}
303
304static int lynxfb_ops_set_par(struct fb_info *info)
305{
306	struct lynxfb_par *par;
307	struct lynxfb_crtc *crtc;
308	struct lynxfb_output *output;
309	struct fb_var_screeninfo *var;
310	struct fb_fix_screeninfo *fix;
311	int ret;
312	unsigned int line_length;
313
314	if (!info)
315		return -EINVAL;
316
317	ret = 0;
318	par = info->par;
319	crtc = &par->crtc;
320	output = &par->output;
321	var = &info->var;
322	fix = &info->fix;
323
324	/* fix structur is not so FIX ... */
325	line_length = var->xres_virtual * var->bits_per_pixel / 8;
326	line_length = ALIGN(line_length, crtc->line_pad);
327	fix->line_length = line_length;
328	pr_info("fix->line_length = %d\n", fix->line_length);
329
330	/*
331	 * var->red,green,blue,transp are need to be set by driver
332	 * and these data should be set before setcolreg routine
333	 */
334
335	switch (var->bits_per_pixel) {
336	case 8:
337		fix->visual = FB_VISUAL_PSEUDOCOLOR;
338		var->red.offset = 0;
339		var->red.length = 8;
340		var->green.offset = 0;
341		var->green.length = 8;
342		var->blue.offset = 0;
343		var->blue.length = 8;
344		var->transp.length = 0;
345		var->transp.offset = 0;
346		break;
347	case 16:
348		var->red.offset = 11;
349		var->red.length = 5;
350		var->green.offset = 5;
351		var->green.length = 6;
352		var->blue.offset = 0;
353		var->blue.length = 5;
354		var->transp.length = 0;
355		var->transp.offset = 0;
356		fix->visual = FB_VISUAL_TRUECOLOR;
357		break;
358	case 24:
359	case 32:
360		var->red.offset = 16;
361		var->red.length = 8;
362		var->green.offset = 8;
363		var->green.length = 8;
364		var->blue.offset = 0;
365		var->blue.length = 8;
366		fix->visual = FB_VISUAL_TRUECOLOR;
367		break;
368	default:
369		ret = -EINVAL;
370		break;
371	}
372	var->height = var->width = -1;
373	var->accel_flags = 0;/*FB_ACCELF_TEXT;*/
374
375	if (ret) {
376		pr_err("pixel bpp format not satisfied\n.");
377		return ret;
378	}
379	ret = hw_sm750_crtc_setMode(crtc, var, fix);
380	if (!ret)
381		ret = hw_sm750_output_setMode(output, var, fix);
382	return ret;
383}
384
385static inline unsigned int chan_to_field(unsigned int chan,
386					 struct fb_bitfield *bf)
387{
388	chan &= 0xffff;
389	chan >>= 16 - bf->length;
390	return chan << bf->offset;
391}
392
393#ifdef CONFIG_PM
394static int lynxfb_suspend(struct pci_dev *pdev, pm_message_t mesg)
395{
396	struct fb_info *info;
397	struct sm750_dev *sm750_dev;
398	int ret;
399
400	if (mesg.event == pdev->dev.power.power_state.event)
401		return 0;
402
403	ret = 0;
404	sm750_dev = pci_get_drvdata(pdev);
405	switch (mesg.event) {
406	case PM_EVENT_FREEZE:
407	case PM_EVENT_PRETHAW:
408		pdev->dev.power.power_state = mesg;
409		return 0;
410	}
411
412	console_lock();
413	if (mesg.event & PM_EVENT_SLEEP) {
414		info = sm750_dev->fbinfo[0];
415		if (info)
416			/* 1 means do suspend */
417			fb_set_suspend(info, 1);
418		info = sm750_dev->fbinfo[1];
419		if (info)
420			/* 1 means do suspend */
421			fb_set_suspend(info, 1);
422
423		ret = pci_save_state(pdev);
424		if (ret) {
425			pr_err("error:%d occurred in pci_save_state\n", ret);
426			return ret;
427		}
428
429		pci_disable_device(pdev);
430		ret = pci_set_power_state(pdev, pci_choose_state(pdev, mesg));
431		if (ret) {
432			pr_err("error:%d occurred in pci_set_power_state\n", ret);
433			return ret;
434		}
435	}
436
437	pdev->dev.power.power_state = mesg;
438	console_unlock();
439	return ret;
440}
441
442static int lynxfb_resume(struct pci_dev *pdev)
443{
444	struct fb_info *info;
445	struct sm750_dev *sm750_dev;
446
447	struct lynxfb_par *par;
448	struct lynxfb_crtc *crtc;
449	struct lynx_cursor *cursor;
450
451	int ret;
452
453	ret = 0;
454	sm750_dev = pci_get_drvdata(pdev);
455
456	console_lock();
457
458	ret = pci_set_power_state(pdev, PCI_D0);
459	if (ret) {
460		pr_err("error:%d occurred in pci_set_power_state\n", ret);
461		return ret;
462	}
463
464	if (pdev->dev.power.power_state.event != PM_EVENT_FREEZE) {
465		pci_restore_state(pdev);
466		ret = pci_enable_device(pdev);
467		if (ret) {
468			pr_err("error:%d occurred in pci_enable_device\n", ret);
469			return ret;
470		}
471		pci_set_master(pdev);
472	}
473
474	hw_sm750_inithw(sm750_dev, pdev);
475
476	info = sm750_dev->fbinfo[0];
477
478	if (info) {
479		par = info->par;
480		crtc = &par->crtc;
481		cursor = &crtc->cursor;
482		memset_io(cursor->vstart, 0x0, cursor->size);
483		memset_io(crtc->vScreen, 0x0, crtc->vidmem_size);
484		lynxfb_ops_set_par(info);
485		fb_set_suspend(info, 0);
486	}
487
488	info = sm750_dev->fbinfo[1];
489
490	if (info) {
491		par = info->par;
492		crtc = &par->crtc;
493		cursor = &crtc->cursor;
494		memset_io(cursor->vstart, 0x0, cursor->size);
495		memset_io(crtc->vScreen, 0x0, crtc->vidmem_size);
496		lynxfb_ops_set_par(info);
497		fb_set_suspend(info, 0);
498	}
499
500	pdev->dev.power.power_state.event = PM_EVENT_RESUME;
501	console_unlock();
502	return ret;
503}
504#endif
505
506static int lynxfb_ops_check_var(struct fb_var_screeninfo *var,
507				struct fb_info *info)
508{
509	struct lynxfb_par *par;
510	struct lynxfb_crtc *crtc;
511	struct lynxfb_output *output;
512	resource_size_t request;
513
514	par = info->par;
515	crtc = &par->crtc;
516	output = &par->output;
517
518	pr_debug("check var:%dx%d-%d\n",
519		 var->xres,
520		 var->yres,
521		 var->bits_per_pixel);
522
523	switch (var->bits_per_pixel) {
524	case 8:
525		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
526		var->red.offset = 0;
527		var->red.length = 8;
528		var->green.offset = 0;
529		var->green.length = 8;
530		var->blue.offset = 0;
531		var->blue.length = 8;
532		var->transp.length = 0;
533		var->transp.offset = 0;
534		break;
535	case 16:
536		var->red.offset = 11;
537		var->red.length = 5;
538		var->green.offset = 5;
539		var->green.length = 6;
540		var->blue.offset = 0;
541		var->blue.length = 5;
542		var->transp.length = 0;
543		var->transp.offset = 0;
544		info->fix.visual = FB_VISUAL_TRUECOLOR;
545		break;
546	case 24:
547	case 32:
548		var->red.offset = 16;
549		var->red.length = 8;
550		var->green.offset = 8;
551		var->green.length = 8;
552		var->blue.offset = 0;
553		var->blue.length = 8;
554		info->fix.visual = FB_VISUAL_TRUECOLOR;
555		break;
556	default:
557		pr_err("bpp %d not supported\n", var->bits_per_pixel);
558		return -EINVAL;
559	}
560	var->height = var->width = -1;
561	var->accel_flags = 0;/* FB_ACCELF_TEXT; */
562
563	/* check if current fb's video memory big enought to hold the onscreen*/
564	request = var->xres_virtual * (var->bits_per_pixel >> 3);
565	/* defaulty crtc->channel go with par->index */
566
567	request = ALIGN(request, crtc->line_pad);
568	request = request * var->yres_virtual;
569	if (crtc->vidmem_size < request) {
570		pr_err("not enough video memory for mode\n");
571		return -ENOMEM;
572	}
573
574	return hw_sm750_crtc_checkMode(crtc, var);
575}
576
577static int lynxfb_ops_setcolreg(unsigned regno,
578				unsigned red,
579				unsigned green,
580				unsigned blue,
581				unsigned transp,
582				struct fb_info *info)
583{
584	struct lynxfb_par *par;
585	struct lynxfb_crtc *crtc;
586	struct fb_var_screeninfo *var;
587	int ret;
588
589	par = info->par;
590	crtc = &par->crtc;
591	var = &info->var;
592	ret = 0;
593
594	if (regno > 256) {
595		pr_err("regno = %d\n", regno);
596		return -EINVAL;
597	}
598
599	if (info->var.grayscale)
600		red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
601
602	if (var->bits_per_pixel == 8 &&
603	    info->fix.visual == FB_VISUAL_PSEUDOCOLOR) {
604		red >>= 8;
605		green >>= 8;
606		blue >>= 8;
607		ret = hw_sm750_setColReg(crtc, regno, red, green, blue);
608		goto exit;
609	}
610
611	if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 256) {
612		u32 val;
613
614		if (var->bits_per_pixel == 16 ||
615		    var->bits_per_pixel == 32 ||
616		    var->bits_per_pixel == 24) {
617			val = chan_to_field(red, &var->red);
618			val |= chan_to_field(green, &var->green);
619			val |= chan_to_field(blue, &var->blue);
620			par->pseudo_palette[regno] = val;
621			goto exit;
622		}
623	}
624
625	ret = -EINVAL;
626
627exit:
628	return ret;
629}
630
631static int lynxfb_ops_blank(int blank, struct fb_info *info)
632{
633	struct lynxfb_par *par;
634	struct lynxfb_output *output;
635
636	pr_debug("blank = %d.\n", blank);
637	par = info->par;
638	output = &par->output;
639	return output->proc_setBLANK(output, blank);
640}
641
642static int sm750fb_set_drv(struct lynxfb_par *par)
643{
644	int ret;
645	struct sm750_dev *sm750_dev;
646	struct lynxfb_output *output;
647	struct lynxfb_crtc *crtc;
648
649	ret = 0;
650
651	sm750_dev = par->dev;
652	output = &par->output;
653	crtc = &par->crtc;
654
655	crtc->vidmem_size = (sm750_dev->dual) ? sm750_dev->vidmem_size >> 1 :
656			     sm750_dev->vidmem_size;
657	/* setup crtc and output member */
658	sm750_dev->hwCursor = g_hwcursor;
659
660	crtc->line_pad = 16;
661	crtc->xpanstep = 8;
662	crtc->ypanstep = 1;
663	crtc->ywrapstep = 0;
664
665	output->proc_setBLANK = (sm750_dev->revid == SM750LE_REVISION_ID) ?
666				 hw_sm750le_setBLANK : hw_sm750_setBLANK;
667	/* chip specific phase */
668	sm750_dev->accel.de_wait = (sm750_dev->revid == SM750LE_REVISION_ID) ?
669				    hw_sm750le_deWait : hw_sm750_deWait;
670	switch (sm750_dev->dataflow) {
671	case sm750_simul_pri:
672		output->paths = sm750_pnc;
673		crtc->channel = sm750_primary;
674		crtc->oScreen = 0;
675		crtc->vScreen = sm750_dev->pvMem;
676		pr_info("use simul primary mode\n");
677		break;
678	case sm750_simul_sec:
679		output->paths = sm750_pnc;
680		crtc->channel = sm750_secondary;
681		crtc->oScreen = 0;
682		crtc->vScreen = sm750_dev->pvMem;
683		break;
684	case sm750_dual_normal:
685		if (par->index == 0) {
686			output->paths = sm750_panel;
687			crtc->channel = sm750_primary;
688			crtc->oScreen = 0;
689			crtc->vScreen = sm750_dev->pvMem;
690		} else {
691			output->paths = sm750_crt;
692			crtc->channel = sm750_secondary;
693			/* not consider of padding stuffs for oScreen,need fix */
694			crtc->oScreen = (sm750_dev->vidmem_size >> 1);
695			crtc->vScreen = sm750_dev->pvMem + crtc->oScreen;
696		}
697		break;
698	case sm750_dual_swap:
699		if (par->index == 0) {
700			output->paths = sm750_panel;
701			crtc->channel = sm750_secondary;
702			crtc->oScreen = 0;
703			crtc->vScreen = sm750_dev->pvMem;
704		} else {
705			output->paths = sm750_crt;
706			crtc->channel = sm750_primary;
707			/* not consider of padding stuffs for oScreen,need fix */
708			crtc->oScreen = (sm750_dev->vidmem_size >> 1);
709			crtc->vScreen = sm750_dev->pvMem + crtc->oScreen;
710		}
711		break;
712	default:
713		ret = -EINVAL;
714	}
715
716	return ret;
717}
718
719static struct fb_ops lynxfb_ops = {
720	.owner = THIS_MODULE,
721	.fb_check_var =  lynxfb_ops_check_var,
722	.fb_set_par = lynxfb_ops_set_par,
723	.fb_setcolreg = lynxfb_ops_setcolreg,
724	.fb_blank = lynxfb_ops_blank,
725	.fb_fillrect = cfb_fillrect,
726	.fb_imageblit = cfb_imageblit,
727	.fb_copyarea = cfb_copyarea,
728	/* cursor */
729	.fb_cursor = lynxfb_ops_cursor,
730};
731
732static int lynxfb_set_fbinfo(struct fb_info *info, int index)
733{
734	int i;
735	struct lynxfb_par *par;
736	struct sm750_dev *sm750_dev;
737	struct lynxfb_crtc *crtc;
738	struct lynxfb_output *output;
739	struct fb_var_screeninfo *var;
740	struct fb_fix_screeninfo *fix;
741
742	const struct fb_videomode *pdb[] = {
743		lynx750_ext, NULL, vesa_modes,
744	};
745	int cdb[] = {ARRAY_SIZE(lynx750_ext), 0, VESA_MODEDB_SIZE};
746	static const char *mdb_desc[] = {
747		"driver prepared modes",
748		"kernel prepared default modedb",
749		"kernel HELPERS prepared vesa_modes",
750	};
751
752	static const char *fixId[2] = {
753		"sm750_fb1", "sm750_fb2",
754	};
755
756	int ret, line_length;
757
758	ret = 0;
759	par = (struct lynxfb_par *)info->par;
760	sm750_dev = par->dev;
761	crtc = &par->crtc;
762	output = &par->output;
763	var = &info->var;
764	fix = &info->fix;
765
766	/* set index */
767	par->index = index;
768	output->channel = &crtc->channel;
769	sm750fb_set_drv(par);
770	lynxfb_ops.fb_pan_display = lynxfb_ops_pan_display;
771
772	/*
773	 * set current cursor variable and proc pointer,
774	 * must be set after crtc member initialized
775	 */
776	crtc->cursor.offset = crtc->oScreen + crtc->vidmem_size - 1024;
777	crtc->cursor.mmio = sm750_dev->pvReg +
778		0x800f0 + (int)crtc->channel * 0x140;
779
780	pr_info("crtc->cursor.mmio = %p\n", crtc->cursor.mmio);
781	crtc->cursor.maxH = crtc->cursor.maxW = 64;
782	crtc->cursor.size = crtc->cursor.maxH * crtc->cursor.maxW * 2 / 8;
783	crtc->cursor.vstart = sm750_dev->pvMem + crtc->cursor.offset;
784
785	memset_io(crtc->cursor.vstart, 0, crtc->cursor.size);
786	if (!g_hwcursor) {
787		lynxfb_ops.fb_cursor = NULL;
788		hw_cursor_disable(&crtc->cursor);
789	}
790
791	/* set info->fbops, must be set before fb_find_mode */
792	if (!sm750_dev->accel_off) {
793		/* use 2d acceleration */
794		lynxfb_ops.fb_fillrect = lynxfb_ops_fillrect;
795		lynxfb_ops.fb_copyarea = lynxfb_ops_copyarea;
796		lynxfb_ops.fb_imageblit = lynxfb_ops_imageblit;
797	}
798	info->fbops = &lynxfb_ops;
799
800	if (!g_fbmode[index]) {
801		g_fbmode[index] = g_def_fbmode;
802		if (index)
803			g_fbmode[index] = g_fbmode[0];
804	}
805
806	for (i = 0; i < 3; i++) {
807
808		ret = fb_find_mode(var, info, g_fbmode[index],
809				   pdb[i], cdb[i], NULL, 8);
810
811		if (ret == 1) {
812			pr_info("success! use specified mode:%s in %s\n",
813				g_fbmode[index],
814				mdb_desc[i]);
815			break;
816		} else if (ret == 2) {
817			pr_warn("use specified mode:%s in %s,with an ignored refresh rate\n",
818				g_fbmode[index],
819				mdb_desc[i]);
820			break;
821		} else if (ret == 3) {
822			pr_warn("wanna use default mode\n");
823			/*break;*/
824		} else if (ret == 4) {
825			pr_warn("fall back to any valid mode\n");
826		} else {
827			pr_warn("ret = %d,fb_find_mode failed,with %s\n",
828				ret,
829				mdb_desc[i]);
830		}
831	}
832
833	/* some member of info->var had been set by fb_find_mode */
834
835	pr_info("Member of info->var is :\n\
836		xres=%d\n\
837		yres=%d\n\
838		xres_virtual=%d\n\
839		yres_virtual=%d\n\
840		xoffset=%d\n\
841		yoffset=%d\n\
842		bits_per_pixel=%d\n \
843		...\n",
844		var->xres,
845		var->yres,
846		var->xres_virtual,
847		var->yres_virtual,
848		var->xoffset,
849		var->yoffset,
850		var->bits_per_pixel);
851
852	/* set par */
853	par->info = info;
854
855	/* set info */
856	line_length = ALIGN((var->xres_virtual * var->bits_per_pixel / 8),
857			    crtc->line_pad);
858
859	info->pseudo_palette = &par->pseudo_palette[0];
860	info->screen_base = crtc->vScreen;
861	pr_debug("screen_base vaddr = %p\n", info->screen_base);
862	info->screen_size = line_length * var->yres_virtual;
863	info->flags = FBINFO_FLAG_DEFAULT | 0;
864
865	/* set info->fix */
866	fix->type = FB_TYPE_PACKED_PIXELS;
867	fix->type_aux = 0;
868	fix->xpanstep = crtc->xpanstep;
869	fix->ypanstep = crtc->ypanstep;
870	fix->ywrapstep = crtc->ywrapstep;
871	fix->accel = FB_ACCEL_SMI;
872
873	strlcpy(fix->id, fixId[index], sizeof(fix->id));
874
875	fix->smem_start = crtc->oScreen + sm750_dev->vidmem_start;
876	pr_info("fix->smem_start = %lx\n", fix->smem_start);
877	/*
878	 * according to mmap experiment from user space application,
879	 * fix->mmio_len should not larger than virtual size
880	 * (xres_virtual x yres_virtual x ByPP)
881	 * Below line maybe buggy when user mmap fb dev node and write
882	 * data into the bound over virtual size
883	 */
884	fix->smem_len = crtc->vidmem_size;
885	pr_info("fix->smem_len = %x\n", fix->smem_len);
886	info->screen_size = fix->smem_len;
887	fix->line_length = line_length;
888	fix->mmio_start = sm750_dev->vidreg_start;
889	pr_info("fix->mmio_start = %lx\n", fix->mmio_start);
890	fix->mmio_len = sm750_dev->vidreg_size;
891	pr_info("fix->mmio_len = %x\n", fix->mmio_len);
892	switch (var->bits_per_pixel) {
893	case 8:
894		fix->visual = FB_VISUAL_PSEUDOCOLOR;
895		break;
896	case 16:
897	case 32:
898		fix->visual = FB_VISUAL_TRUECOLOR;
899		break;
900	}
901
902	/* set var */
903	var->activate = FB_ACTIVATE_NOW;
904	var->accel_flags = 0;
905	var->vmode = FB_VMODE_NONINTERLACED;
906
907	pr_debug("#1 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
908		 info->cmap.start, info->cmap.len,
909		 info->cmap.red, info->cmap.green, info->cmap.blue,
910		 info->cmap.transp);
911
912	ret = fb_alloc_cmap(&info->cmap, 256, 0);
913	if (ret < 0) {
914		pr_err("Could not allocate memory for cmap.\n");
915		goto exit;
916	}
917
918	pr_debug("#2 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
919		 info->cmap.start, info->cmap.len,
920		 info->cmap.red, info->cmap.green, info->cmap.blue,
921		 info->cmap.transp);
922
923exit:
924	lynxfb_ops_check_var(var, info);
925	return ret;
926}
927
928/*	chip specific g_option configuration routine */
929static void sm750fb_setup(struct sm750_dev *sm750_dev, char *src)
930{
931	char *opt;
932	int swap;
933
934	swap = 0;
935
936	sm750_dev->initParm.chip_clk = 0;
937	sm750_dev->initParm.mem_clk = 0;
938	sm750_dev->initParm.master_clk = 0;
939	sm750_dev->initParm.powerMode = 0;
940	sm750_dev->initParm.setAllEngOff = 0;
941	sm750_dev->initParm.resetMemory = 1;
942
943	/* defaultly turn g_hwcursor on for both view */
944	g_hwcursor = 3;
945
946	if (!src || !*src) {
947		pr_warn("no specific g_option.\n");
948		goto NO_PARAM;
949	}
950
951	while ((opt = strsep(&src, ":")) != NULL && *opt != 0) {
952		pr_info("opt=%s\n", opt);
953		pr_info("src=%s\n", src);
954
955		if (!strncmp(opt, "swap", strlen("swap")))
956			swap = 1;
957		else if (!strncmp(opt, "nocrt", strlen("nocrt")))
958			sm750_dev->nocrt = 1;
959		else if (!strncmp(opt, "36bit", strlen("36bit")))
960			sm750_dev->pnltype = sm750_doubleTFT;
961		else if (!strncmp(opt, "18bit", strlen("18bit")))
962			sm750_dev->pnltype = sm750_dualTFT;
963		else if (!strncmp(opt, "24bit", strlen("24bit")))
964			sm750_dev->pnltype = sm750_24TFT;
965		else if (!strncmp(opt, "nohwc0", strlen("nohwc0")))
966			g_hwcursor &= ~0x1;
967		else if (!strncmp(opt, "nohwc1", strlen("nohwc1")))
968			g_hwcursor &= ~0x2;
969		else if (!strncmp(opt, "nohwc", strlen("nohwc")))
970			g_hwcursor = 0;
971		else {
972			if (!g_fbmode[0]) {
973				g_fbmode[0] = opt;
974				pr_info("find fbmode0 : %s\n", g_fbmode[0]);
975			} else if (!g_fbmode[1]) {
976				g_fbmode[1] = opt;
977				pr_info("find fbmode1 : %s\n", g_fbmode[1]);
978			} else {
979				pr_warn("How many view you wann set?\n");
980			}
981		}
982	}
983
984NO_PARAM:
985	if (sm750_dev->revid != SM750LE_REVISION_ID) {
986		if (sm750_dev->dual) {
987			if (swap)
988				sm750_dev->dataflow = sm750_dual_swap;
989			else
990				sm750_dev->dataflow = sm750_dual_normal;
991		} else {
992			if (swap)
993				sm750_dev->dataflow = sm750_simul_sec;
994			else
995				sm750_dev->dataflow = sm750_simul_pri;
996		}
997	} else {
998		/* SM750LE only have one crt channel */
999		sm750_dev->dataflow = sm750_simul_sec;
1000		/* sm750le do not have complex attributes */
1001		sm750_dev->nocrt = 0;
1002	}
1003}
1004
1005static int lynxfb_pci_probe(struct pci_dev *pdev,
1006			    const struct pci_device_id *ent)
1007{
1008	struct fb_info *info[] = {NULL, NULL};
1009	struct sm750_dev *sm750_dev = NULL;
1010	int fbidx;
1011
1012	/* enable device */
1013	if (pci_enable_device(pdev)) {
1014		pr_err("can not enable device.\n");
1015		goto err_enable;
1016	}
1017
1018	sm750_dev = kzalloc(sizeof(*sm750_dev), GFP_KERNEL);
1019	if (!sm750_dev) {
1020		pr_err("Could not allocate memory for share.\n");
1021		goto err_share;
1022	}
1023
1024	sm750_dev->fbinfo[0] = sm750_dev->fbinfo[1] = NULL;
1025	sm750_dev->devid = pdev->device;
1026	sm750_dev->revid = pdev->revision;
1027
1028	pr_info("share->revid = %02x\n", sm750_dev->revid);
1029	sm750_dev->pdev = pdev;
1030	sm750_dev->mtrr_off = g_nomtrr;
1031	sm750_dev->mtrr.vram = 0;
1032	sm750_dev->accel_off = g_noaccel;
1033	sm750_dev->dual = g_dualview;
1034	spin_lock_init(&sm750_dev->slock);
1035
1036	if (!sm750_dev->accel_off) {
1037		/*
1038		 * hook deInit and 2d routines, notes that below hw_xxx
1039		 * routine can work on most of lynx chips
1040		 * if some chip need specific function,
1041		 * please hook it in smXXX_set_drv routine
1042		 */
1043		sm750_dev->accel.de_init = hw_de_init;
1044		sm750_dev->accel.de_fillrect = hw_fillrect;
1045		sm750_dev->accel.de_copyarea = hw_copyarea;
1046		sm750_dev->accel.de_imageblit = hw_imageblit;
1047		pr_info("enable 2d acceleration\n");
1048	} else {
1049		pr_info("disable 2d acceleration\n");
1050	}
1051
1052	/* call chip specific setup routine  */
1053	sm750fb_setup(sm750_dev, g_settings);
1054
1055	/* call chip specific mmap routine */
1056	if (hw_sm750_map(sm750_dev, pdev)) {
1057		pr_err("Memory map failed\n");
1058		goto err_map;
1059	}
1060
1061	if (!sm750_dev->mtrr_off)
1062		sm750_dev->mtrr.vram = arch_phys_wc_add(sm750_dev->vidmem_start,
1063							sm750_dev->vidmem_size);
1064
1065	memset_io(sm750_dev->pvMem, 0, sm750_dev->vidmem_size);
1066
1067	pr_info("sm%3x mmio address = %p\n", sm750_dev->devid,
1068		sm750_dev->pvReg);
1069
1070	pci_set_drvdata(pdev, sm750_dev);
1071
1072	/* call chipInit routine */
1073	hw_sm750_inithw(sm750_dev, pdev);
1074
1075	/* allocate frame buffer info structor according to g_dualview */
1076	fbidx = 0;
1077ALLOC_FB:
1078	info[fbidx] = framebuffer_alloc(sizeof(struct lynxfb_par), &pdev->dev);
1079	if (!info[fbidx]) {
1080		pr_err("Could not allocate framebuffer #%d.\n", fbidx);
1081		if (fbidx == 0)
1082			goto err_info0_alloc;
1083		else
1084			goto err_info1_alloc;
1085	} else {
1086		struct lynxfb_par *par;
1087		int errno;
1088
1089		pr_info("framebuffer #%d alloc okay\n", fbidx);
1090		sm750_dev->fbinfo[fbidx] = info[fbidx];
1091		par = info[fbidx]->par;
1092		par->dev = sm750_dev;
1093
1094		/* set fb_info structure */
1095		if (lynxfb_set_fbinfo(info[fbidx], fbidx)) {
1096			pr_err("Failed to initial fb_info #%d.\n", fbidx);
1097			if (fbidx == 0)
1098				goto err_info0_set;
1099			else
1100				goto err_info1_set;
1101		}
1102
1103		/* register frame buffer */
1104		pr_info("Ready to register framebuffer #%d.\n", fbidx);
1105		errno = register_framebuffer(info[fbidx]);
1106		if (errno < 0) {
1107			pr_err("Failed to register fb_info #%d. err %d\n",
1108			       fbidx,
1109			       errno);
1110			if (fbidx == 0)
1111				goto err_register0;
1112			else
1113				goto err_register1;
1114		}
1115		pr_info("Accomplished register framebuffer #%d.\n", fbidx);
1116	}
1117
1118	/* no dual view by far */
1119	fbidx++;
1120	if (sm750_dev->dual && fbidx < 2)
1121		goto ALLOC_FB;
1122
1123	return 0;
1124
1125err_register1:
1126err_info1_set:
1127	framebuffer_release(info[1]);
1128err_info1_alloc:
1129	unregister_framebuffer(info[0]);
1130err_register0:
1131err_info0_set:
1132	framebuffer_release(info[0]);
1133err_info0_alloc:
1134err_map:
1135	kfree(sm750_dev);
1136err_share:
1137err_enable:
1138	return -ENODEV;
1139}
1140
1141static void lynxfb_pci_remove(struct pci_dev *pdev)
1142{
1143	struct fb_info *info;
1144	struct sm750_dev *sm750_dev;
1145	struct lynxfb_par *par;
1146	int cnt;
1147
1148	cnt = 2;
1149	sm750_dev = pci_get_drvdata(pdev);
1150
1151	while (cnt-- > 0) {
1152		info = sm750_dev->fbinfo[cnt];
1153		if (!info)
1154			continue;
1155		par = info->par;
1156
1157		unregister_framebuffer(info);
1158		/* release frame buffer */
1159		framebuffer_release(info);
1160	}
1161	arch_phys_wc_del(sm750_dev->mtrr.vram);
1162
1163	iounmap(sm750_dev->pvReg);
1164	iounmap(sm750_dev->pvMem);
1165	kfree(g_settings);
1166	kfree(sm750_dev);
1167	pci_set_drvdata(pdev, NULL);
1168}
1169
1170static int __init lynxfb_setup(char *options)
1171{
1172	int len;
1173	char *opt, *tmp;
1174
1175	if (!options || !*options) {
1176		pr_warn("no options.\n");
1177		return 0;
1178	}
1179
1180	pr_info("options:%s\n", options);
1181
1182	len = strlen(options) + 1;
1183	g_settings = kzalloc(len, GFP_KERNEL);
1184	if (!g_settings)
1185		return -ENOMEM;
1186
1187	tmp = g_settings;
1188
1189	/*
1190	 * Notes:
1191	 * char * strsep(char **s,const char * ct);
1192	 * @s: the string to be searched
1193	 * @ct :the characters to search for
1194	 *
1195	 * strsep() updates @options to pointer after the first found token
1196	 * it also returns the pointer ahead the token.
1197	 */
1198	while ((opt = strsep(&options, ":")) != NULL) {
1199		/* options that mean for any lynx chips are configured here */
1200		if (!strncmp(opt, "noaccel", strlen("noaccel")))
1201			g_noaccel = 1;
1202		else if (!strncmp(opt, "nomtrr", strlen("nomtrr")))
1203			g_nomtrr = 1;
1204		else if (!strncmp(opt, "dual", strlen("dual")))
1205			g_dualview = 1;
1206		else {
1207			strcat(tmp, opt);
1208			tmp += strlen(opt);
1209			if (options != NULL)
1210				*tmp++ = ':';
1211			else
1212				*tmp++ = 0;
1213		}
1214	}
1215
1216	/* misc g_settings are transport to chip specific routines */
1217	pr_info("parameter left for chip specific analysis:%s\n", g_settings);
1218	return 0;
1219}
1220
1221static struct pci_device_id smi_pci_table[] = {
1222	{ PCI_DEVICE(0x126f, 0x0750), },
1223	{0,}
1224};
1225
1226MODULE_DEVICE_TABLE(pci, smi_pci_table);
1227
1228static struct pci_driver lynxfb_driver = {
1229	.name =		"sm750fb",
1230	.id_table =	smi_pci_table,
1231	.probe =	lynxfb_pci_probe,
1232	.remove =	lynxfb_pci_remove,
1233#ifdef CONFIG_PM
1234	.suspend = lynxfb_suspend,
1235	.resume = lynxfb_resume,
1236#endif
1237};
1238
1239static int __init lynxfb_init(void)
1240{
1241	char *option;
1242	int ret;
1243
1244#ifdef MODULE
1245	option = g_option;
1246#else
1247	if (fb_get_options("sm750fb", &option))
1248		return -ENODEV;
1249#endif
1250
1251	lynxfb_setup(option);
1252	ret = pci_register_driver(&lynxfb_driver);
1253	return ret;
1254}
1255module_init(lynxfb_init);
1256
1257static void __exit lynxfb_exit(void)
1258{
1259	pci_unregister_driver(&lynxfb_driver);
1260}
1261module_exit(lynxfb_exit);
1262
1263module_param(g_option, charp, S_IRUGO);
1264
1265MODULE_PARM_DESC(g_option,
1266		 "\n\t\tCommon options:\n"
1267		 "\t\tnoaccel:disable 2d capabilities\n"
1268		 "\t\tnomtrr:disable MTRR attribute for video memory\n"
1269		 "\t\tdualview:dual frame buffer feature enabled\n"
1270		 "\t\tnohwc:disable hardware cursor\n"
1271		 "\t\tUsual example:\n"
1272		 "\t\tinsmod ./sm750fb.ko g_option=\"noaccel,nohwc,1280x1024-8@60\"\n"
1273		 );
1274
1275MODULE_AUTHOR("monk liu <monk.liu@siliconmotion.com>");
1276MODULE_AUTHOR("Sudip Mukherjee <sudip@vectorindia.org>");
1277MODULE_DESCRIPTION("Frame buffer driver for SM750 chipset");
1278MODULE_LICENSE("GPL v2");
1279