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