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