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