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