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 red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
542
543 if (var->bits_per_pixel == 8 &&
544 info->fix.visual == FB_VISUAL_PSEUDOCOLOR) {
545 red >>= 8;
546 green >>= 8;
547 blue >>= 8;
548 ret = hw_sm750_set_col_reg(crtc, regno, red, green, blue);
549 goto exit;
550 }
551
552 if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 256) {
553 u32 val;
554
555 if (var->bits_per_pixel == 16 ||
556 var->bits_per_pixel == 32 ||
557 var->bits_per_pixel == 24) {
558 val = chan_to_field(red, &var->red);
559 val |= chan_to_field(green, &var->green);
560 val |= chan_to_field(blue, &var->blue);
561 par->pseudo_palette[regno] = val;
562 goto exit;
563 }
564 }
565
566 ret = -EINVAL;
567
568 exit:
569 return ret;
570 }
571
lynxfb_ops_blank(int blank,struct fb_info * info)572 static int lynxfb_ops_blank(int blank, struct fb_info *info)
573 {
574 struct sm750_dev *sm750_dev;
575 struct lynxfb_par *par;
576 struct lynxfb_output *output;
577
578 pr_debug("blank = %d.\n", blank);
579 par = info->par;
580 output = &par->output;
581 sm750_dev = par->dev;
582
583 if (sm750_dev->revid == SM750LE_REVISION_ID)
584 return hw_sm750le_set_blank(output, blank);
585 else
586 return hw_sm750_set_blank(output, blank);
587 }
588
sm750fb_set_drv(struct lynxfb_par * par)589 static int sm750fb_set_drv(struct lynxfb_par *par)
590 {
591 int ret;
592 struct sm750_dev *sm750_dev;
593 struct lynxfb_output *output;
594 struct lynxfb_crtc *crtc;
595
596 ret = 0;
597
598 sm750_dev = par->dev;
599 output = &par->output;
600 crtc = &par->crtc;
601
602 crtc->vidmem_size = sm750_dev->vidmem_size;
603 if (sm750_dev->fb_count > 1)
604 crtc->vidmem_size >>= 1;
605
606 /* setup crtc and output member */
607 sm750_dev->hw_cursor = g_hwcursor;
608
609 crtc->line_pad = 16;
610 crtc->xpanstep = 8;
611 crtc->ypanstep = 1;
612 crtc->ywrapstep = 0;
613
614 /* chip specific phase */
615 sm750_dev->accel.de_wait = (sm750_dev->revid == SM750LE_REVISION_ID) ?
616 hw_sm750le_de_wait : hw_sm750_de_wait;
617 switch (sm750_dev->dataflow) {
618 case sm750_simul_pri:
619 output->paths = sm750_pnc;
620 crtc->channel = sm750_primary;
621 crtc->o_screen = 0;
622 crtc->v_screen = sm750_dev->pvMem;
623 pr_info("use simul primary mode\n");
624 break;
625 case sm750_simul_sec:
626 output->paths = sm750_pnc;
627 crtc->channel = sm750_secondary;
628 crtc->o_screen = 0;
629 crtc->v_screen = sm750_dev->pvMem;
630 break;
631 case sm750_dual_normal:
632 if (par->index == 0) {
633 output->paths = sm750_panel;
634 crtc->channel = sm750_primary;
635 crtc->o_screen = 0;
636 crtc->v_screen = sm750_dev->pvMem;
637 } else {
638 output->paths = sm750_crt;
639 crtc->channel = sm750_secondary;
640 /* not consider of padding stuffs for o_screen,need fix */
641 crtc->o_screen = sm750_dev->vidmem_size >> 1;
642 crtc->v_screen = sm750_dev->pvMem + crtc->o_screen;
643 }
644 break;
645 case sm750_dual_swap:
646 if (par->index == 0) {
647 output->paths = sm750_panel;
648 crtc->channel = sm750_secondary;
649 crtc->o_screen = 0;
650 crtc->v_screen = sm750_dev->pvMem;
651 } else {
652 output->paths = sm750_crt;
653 crtc->channel = sm750_primary;
654 /* not consider of padding stuffs for o_screen,
655 * need fix
656 */
657 crtc->o_screen = sm750_dev->vidmem_size >> 1;
658 crtc->v_screen = sm750_dev->pvMem + crtc->o_screen;
659 }
660 break;
661 default:
662 ret = -EINVAL;
663 }
664
665 return ret;
666 }
667
668 static const struct fb_ops lynxfb_ops = {
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 };
677
678 static const struct fb_ops lynxfb_ops_with_cursor = {
679 .owner = THIS_MODULE,
680 FB_DEFAULT_IOMEM_OPS,
681 .fb_check_var = lynxfb_ops_check_var,
682 .fb_set_par = lynxfb_ops_set_par,
683 .fb_setcolreg = lynxfb_ops_setcolreg,
684 .fb_blank = lynxfb_ops_blank,
685 .fb_pan_display = lynxfb_ops_pan_display,
686 .fb_cursor = lynxfb_ops_cursor,
687 };
688
689 static const struct fb_ops lynxfb_ops_accel = {
690 .owner = THIS_MODULE,
691 __FB_DEFAULT_IOMEM_OPS_RDWR,
692 .fb_check_var = lynxfb_ops_check_var,
693 .fb_set_par = lynxfb_ops_set_par,
694 .fb_setcolreg = lynxfb_ops_setcolreg,
695 .fb_blank = lynxfb_ops_blank,
696 .fb_pan_display = lynxfb_ops_pan_display,
697 .fb_fillrect = lynxfb_ops_fillrect,
698 .fb_copyarea = lynxfb_ops_copyarea,
699 .fb_imageblit = lynxfb_ops_imageblit,
700 __FB_DEFAULT_IOMEM_OPS_MMAP,
701 };
702
703 static const struct fb_ops lynxfb_ops_accel_with_cursor = {
704 .owner = THIS_MODULE,
705 __FB_DEFAULT_IOMEM_OPS_RDWR,
706 .fb_check_var = lynxfb_ops_check_var,
707 .fb_set_par = lynxfb_ops_set_par,
708 .fb_setcolreg = lynxfb_ops_setcolreg,
709 .fb_blank = lynxfb_ops_blank,
710 .fb_pan_display = lynxfb_ops_pan_display,
711 .fb_fillrect = lynxfb_ops_fillrect,
712 .fb_copyarea = lynxfb_ops_copyarea,
713 .fb_imageblit = lynxfb_ops_imageblit,
714 .fb_cursor = lynxfb_ops_cursor,
715 __FB_DEFAULT_IOMEM_OPS_MMAP,
716 };
717
lynxfb_set_fbinfo(struct fb_info * info,int index)718 static int lynxfb_set_fbinfo(struct fb_info *info, int index)
719 {
720 int i;
721 struct lynxfb_par *par;
722 struct sm750_dev *sm750_dev;
723 struct lynxfb_crtc *crtc;
724 struct lynxfb_output *output;
725 struct fb_var_screeninfo *var;
726 struct fb_fix_screeninfo *fix;
727
728 const struct fb_videomode *pdb[] = {
729 lynx750_ext, NULL, vesa_modes,
730 };
731 int cdb[] = {ARRAY_SIZE(lynx750_ext), 0, VESA_MODEDB_SIZE};
732 static const char * const mdb_desc[] = {
733 "driver prepared modes",
734 "kernel prepared default modedb",
735 "kernel HELPERS prepared vesa_modes",
736 };
737
738 static const char *fixId[2] = {
739 "sm750_fb1", "sm750_fb2",
740 };
741
742 int ret, line_length;
743
744 ret = 0;
745 par = (struct lynxfb_par *)info->par;
746 sm750_dev = par->dev;
747 crtc = &par->crtc;
748 output = &par->output;
749 var = &info->var;
750 fix = &info->fix;
751
752 /* set index */
753 par->index = index;
754 output->channel = &crtc->channel;
755 sm750fb_set_drv(par);
756
757 /*
758 * set current cursor variable and proc pointer,
759 * must be set after crtc member initialized
760 */
761 crtc->cursor.offset = crtc->o_screen + crtc->vidmem_size - 1024;
762 crtc->cursor.mmio = sm750_dev->pvReg +
763 0x800f0 + (int)crtc->channel * 0x140;
764
765 pr_info("crtc->cursor.mmio = %p\n", crtc->cursor.mmio);
766 crtc->cursor.max_h = 64;
767 crtc->cursor.max_w = 64;
768 crtc->cursor.size = crtc->cursor.max_h * crtc->cursor.max_w * 2 / 8;
769 crtc->cursor.vstart = sm750_dev->pvMem + crtc->cursor.offset;
770
771 memset_io(crtc->cursor.vstart, 0, crtc->cursor.size);
772 if (!g_hwcursor)
773 sm750_hw_cursor_disable(&crtc->cursor);
774
775 /* set info->fbops, must be set before fb_find_mode */
776 if (!sm750_dev->accel_off) {
777 /* use 2d acceleration */
778 if (!g_hwcursor)
779 info->fbops = &lynxfb_ops_accel;
780 else
781 info->fbops = &lynxfb_ops_accel_with_cursor;
782 } else {
783 if (!g_hwcursor)
784 info->fbops = &lynxfb_ops;
785 else
786 info->fbops = &lynxfb_ops_with_cursor;
787 }
788
789 if (!g_fbmode[index]) {
790 g_fbmode[index] = g_def_fbmode;
791 if (index)
792 g_fbmode[index] = g_fbmode[0];
793 }
794
795 for (i = 0; i < 3; i++) {
796 ret = fb_find_mode(var, info, g_fbmode[index],
797 pdb[i], cdb[i], NULL, 8);
798
799 if (ret == 1) {
800 pr_info("success! use specified mode:%s in %s\n",
801 g_fbmode[index],
802 mdb_desc[i]);
803 break;
804 } else if (ret == 2) {
805 pr_warn("use specified mode:%s in %s,with an ignored refresh rate\n",
806 g_fbmode[index],
807 mdb_desc[i]);
808 break;
809 } else if (ret == 3) {
810 pr_warn("wanna use default mode\n");
811 /*break;*/
812 } else if (ret == 4) {
813 pr_warn("fall back to any valid mode\n");
814 } else {
815 pr_warn("ret = %d,fb_find_mode failed,with %s\n",
816 ret,
817 mdb_desc[i]);
818 }
819 }
820
821 /* some member of info->var had been set by fb_find_mode */
822
823 pr_info("Member of info->var is :\n"
824 "xres=%d\n"
825 "yres=%d\n"
826 "xres_virtual=%d\n"
827 "yres_virtual=%d\n"
828 "xoffset=%d\n"
829 "yoffset=%d\n"
830 "bits_per_pixel=%d\n"
831 " ...\n",
832 var->xres,
833 var->yres,
834 var->xres_virtual,
835 var->yres_virtual,
836 var->xoffset,
837 var->yoffset,
838 var->bits_per_pixel);
839
840 /* set par */
841 par->info = info;
842
843 /* set info */
844 line_length = ALIGN((var->xres_virtual * var->bits_per_pixel / 8),
845 crtc->line_pad);
846
847 info->pseudo_palette = &par->pseudo_palette[0];
848 info->screen_base = crtc->v_screen;
849 pr_debug("screen_base vaddr = %p\n", info->screen_base);
850 info->screen_size = line_length * var->yres_virtual;
851
852 /* set info->fix */
853 fix->type = FB_TYPE_PACKED_PIXELS;
854 fix->type_aux = 0;
855 fix->xpanstep = crtc->xpanstep;
856 fix->ypanstep = crtc->ypanstep;
857 fix->ywrapstep = crtc->ywrapstep;
858 fix->accel = FB_ACCEL_SMI;
859
860 strscpy(fix->id, fixId[index], sizeof(fix->id));
861
862 fix->smem_start = crtc->o_screen + sm750_dev->vidmem_start;
863 pr_info("fix->smem_start = %lx\n", fix->smem_start);
864 /*
865 * according to mmap experiment from user space application,
866 * fix->mmio_len should not larger than virtual size
867 * (xres_virtual x yres_virtual x ByPP)
868 * Below line maybe buggy when user mmap fb dev node and write
869 * data into the bound over virtual size
870 */
871 fix->smem_len = crtc->vidmem_size;
872 pr_info("fix->smem_len = %x\n", fix->smem_len);
873 info->screen_size = fix->smem_len;
874 fix->line_length = line_length;
875 fix->mmio_start = sm750_dev->vidreg_start;
876 pr_info("fix->mmio_start = %lx\n", fix->mmio_start);
877 fix->mmio_len = sm750_dev->vidreg_size;
878 pr_info("fix->mmio_len = %x\n", fix->mmio_len);
879
880 lynxfb_set_visual_mode(info);
881
882 /* set var */
883 var->activate = FB_ACTIVATE_NOW;
884 var->accel_flags = 0;
885 var->vmode = FB_VMODE_NONINTERLACED;
886
887 pr_debug("#1 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
888 info->cmap.start, info->cmap.len,
889 info->cmap.red, info->cmap.green, info->cmap.blue,
890 info->cmap.transp);
891
892 ret = fb_alloc_cmap(&info->cmap, 256, 0);
893 if (ret < 0) {
894 pr_err("Could not allocate memory for cmap.\n");
895 goto exit;
896 }
897
898 pr_debug("#2 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
899 info->cmap.start, info->cmap.len,
900 info->cmap.red, info->cmap.green, info->cmap.blue,
901 info->cmap.transp);
902
903 exit:
904 lynxfb_ops_check_var(var, info);
905 return ret;
906 }
907
908 /* chip specific g_option configuration routine */
sm750fb_setup(struct sm750_dev * sm750_dev,char * src)909 static void sm750fb_setup(struct sm750_dev *sm750_dev, char *src)
910 {
911 char *opt;
912 int swap;
913
914 swap = 0;
915
916 sm750_dev->initParm.chip_clk = 0;
917 sm750_dev->initParm.mem_clk = 0;
918 sm750_dev->initParm.master_clk = 0;
919 sm750_dev->initParm.powerMode = 0;
920 sm750_dev->initParm.setAllEngOff = 0;
921 sm750_dev->initParm.resetMemory = 1;
922
923 /* defaultly turn g_hwcursor on for both view */
924 g_hwcursor = 3;
925
926 if (!src || !*src) {
927 dev_warn(&sm750_dev->pdev->dev, "no specific g_option.\n");
928 goto NO_PARAM;
929 }
930
931 while ((opt = strsep(&src, ":")) != NULL && *opt != 0) {
932 dev_info(&sm750_dev->pdev->dev, "opt=%s\n", opt);
933 dev_info(&sm750_dev->pdev->dev, "src=%s\n", src);
934
935 if (!strncmp(opt, "swap", strlen("swap"))) {
936 swap = 1;
937 } else if (!strncmp(opt, "nocrt", strlen("nocrt"))) {
938 sm750_dev->nocrt = 1;
939 } else if (!strncmp(opt, "36bit", strlen("36bit"))) {
940 sm750_dev->pnltype = sm750_doubleTFT;
941 } else if (!strncmp(opt, "18bit", strlen("18bit"))) {
942 sm750_dev->pnltype = sm750_dualTFT;
943 } else if (!strncmp(opt, "24bit", strlen("24bit"))) {
944 sm750_dev->pnltype = sm750_24TFT;
945 } else if (!strncmp(opt, "nohwc0", strlen("nohwc0"))) {
946 g_hwcursor &= ~0x1;
947 } else if (!strncmp(opt, "nohwc1", strlen("nohwc1"))) {
948 g_hwcursor &= ~0x2;
949 } else if (!strncmp(opt, "nohwc", strlen("nohwc"))) {
950 g_hwcursor = 0;
951 } else {
952 if (!g_fbmode[0]) {
953 g_fbmode[0] = opt;
954 dev_info(&sm750_dev->pdev->dev,
955 "find fbmode0 : %s\n", g_fbmode[0]);
956 } else if (!g_fbmode[1]) {
957 g_fbmode[1] = opt;
958 dev_info(&sm750_dev->pdev->dev,
959 "find fbmode1 : %s\n", g_fbmode[1]);
960 } else {
961 dev_warn(&sm750_dev->pdev->dev, "How many view you wann set?\n");
962 }
963 }
964 }
965
966 NO_PARAM:
967 if (sm750_dev->revid != SM750LE_REVISION_ID) {
968 if (sm750_dev->fb_count > 1) {
969 if (swap)
970 sm750_dev->dataflow = sm750_dual_swap;
971 else
972 sm750_dev->dataflow = sm750_dual_normal;
973 } else {
974 if (swap)
975 sm750_dev->dataflow = sm750_simul_sec;
976 else
977 sm750_dev->dataflow = sm750_simul_pri;
978 }
979 } else {
980 /* SM750LE only have one crt channel */
981 sm750_dev->dataflow = sm750_simul_sec;
982 /* sm750le do not have complex attributes */
983 sm750_dev->nocrt = 0;
984 }
985 }
986
sm750fb_framebuffer_release(struct sm750_dev * sm750_dev)987 static void sm750fb_framebuffer_release(struct sm750_dev *sm750_dev)
988 {
989 struct fb_info *fb_info;
990
991 while (sm750_dev->fb_count) {
992 fb_info = sm750_dev->fbinfo[sm750_dev->fb_count - 1];
993 unregister_framebuffer(fb_info);
994 framebuffer_release(fb_info);
995 sm750_dev->fb_count--;
996 }
997 }
998
sm750fb_framebuffer_alloc(struct sm750_dev * sm750_dev,int fbidx)999 static int sm750fb_framebuffer_alloc(struct sm750_dev *sm750_dev, int fbidx)
1000 {
1001 struct fb_info *fb_info;
1002 struct lynxfb_par *par;
1003 int err;
1004
1005 fb_info = framebuffer_alloc(sizeof(struct lynxfb_par),
1006 &sm750_dev->pdev->dev);
1007 if (!fb_info)
1008 return -ENOMEM;
1009
1010 sm750_dev->fbinfo[fbidx] = fb_info;
1011 par = fb_info->par;
1012 par->dev = sm750_dev;
1013
1014 err = lynxfb_set_fbinfo(fb_info, fbidx);
1015 if (err)
1016 goto release_fb;
1017
1018 err = register_framebuffer(fb_info);
1019 if (err < 0)
1020 goto release_fb;
1021
1022 sm750_dev->fb_count++;
1023
1024 return 0;
1025
1026 release_fb:
1027 framebuffer_release(fb_info);
1028 return err;
1029 }
1030
lynxfb_pci_probe(struct pci_dev * pdev,const struct pci_device_id * ent)1031 static int lynxfb_pci_probe(struct pci_dev *pdev,
1032 const struct pci_device_id *ent)
1033 {
1034 struct sm750_dev *sm750_dev = NULL;
1035 int max_fb;
1036 int fbidx;
1037 int err;
1038
1039 err = aperture_remove_conflicting_pci_devices(pdev, "sm750_fb1");
1040 if (err)
1041 return err;
1042
1043 /* enable device */
1044 err = pcim_enable_device(pdev);
1045 if (err)
1046 return err;
1047
1048 err = -ENOMEM;
1049 sm750_dev = devm_kzalloc(&pdev->dev, sizeof(*sm750_dev), GFP_KERNEL);
1050 if (!sm750_dev)
1051 return err;
1052
1053 sm750_dev->fbinfo[0] = NULL;
1054 sm750_dev->fbinfo[1] = NULL;
1055 sm750_dev->devid = pdev->device;
1056 sm750_dev->revid = pdev->revision;
1057 sm750_dev->pdev = pdev;
1058 sm750_dev->mtrr_off = g_nomtrr;
1059 sm750_dev->mtrr.vram = 0;
1060 sm750_dev->accel_off = g_noaccel;
1061 spin_lock_init(&sm750_dev->slock);
1062
1063 if (!sm750_dev->accel_off) {
1064 /*
1065 * hook deInit and 2d routines, notes that below hw_xxx
1066 * routine can work on most of lynx chips
1067 * if some chip need specific function,
1068 * please hook it in smXXX_set_drv routine
1069 */
1070 sm750_dev->accel.de_init = sm750_hw_de_init;
1071 sm750_dev->accel.de_fillrect = sm750_hw_fillrect;
1072 sm750_dev->accel.de_copyarea = sm750_hw_copyarea;
1073 sm750_dev->accel.de_imageblit = sm750_hw_imageblit;
1074 }
1075
1076 /* call chip specific setup routine */
1077 sm750fb_setup(sm750_dev, g_settings);
1078
1079 /* call chip specific mmap routine */
1080 err = hw_sm750_map(sm750_dev, pdev);
1081 if (err)
1082 return err;
1083
1084 if (!sm750_dev->mtrr_off)
1085 sm750_dev->mtrr.vram = arch_phys_wc_add(sm750_dev->vidmem_start,
1086 sm750_dev->vidmem_size);
1087
1088 memset_io(sm750_dev->pvMem, 0, sm750_dev->vidmem_size);
1089
1090 pci_set_drvdata(pdev, sm750_dev);
1091
1092 /* call chipInit routine */
1093 hw_sm750_inithw(sm750_dev, pdev);
1094
1095 /* allocate frame buffer info structures according to g_dualview */
1096 max_fb = g_dualview ? 2 : 1;
1097 for (fbidx = 0; fbidx < max_fb; fbidx++) {
1098 err = sm750fb_framebuffer_alloc(sm750_dev, fbidx);
1099 if (err)
1100 goto release_fb;
1101 }
1102
1103 return 0;
1104
1105 release_fb:
1106 sm750fb_framebuffer_release(sm750_dev);
1107 return err;
1108 }
1109
lynxfb_pci_remove(struct pci_dev * pdev)1110 static void lynxfb_pci_remove(struct pci_dev *pdev)
1111 {
1112 struct sm750_dev *sm750_dev;
1113
1114 sm750_dev = pci_get_drvdata(pdev);
1115
1116 sm750fb_framebuffer_release(sm750_dev);
1117 arch_phys_wc_del(sm750_dev->mtrr.vram);
1118
1119 iounmap(sm750_dev->pvReg);
1120 iounmap(sm750_dev->pvMem);
1121 kfree(g_settings);
1122 }
1123
lynxfb_setup(char * options)1124 static int __init lynxfb_setup(char *options)
1125 {
1126 int len;
1127 char *opt, *tmp;
1128
1129 if (!options || !*options) {
1130 pr_warn("no options.\n");
1131 return 0;
1132 }
1133
1134 pr_info("options:%s\n", options);
1135
1136 len = strlen(options) + 1;
1137 g_settings = kzalloc(len, GFP_KERNEL);
1138 if (!g_settings)
1139 return -ENOMEM;
1140
1141 tmp = g_settings;
1142
1143 /*
1144 * Notes:
1145 * char * strsep(char **s,const char * ct);
1146 * @s: the string to be searched
1147 * @ct :the characters to search for
1148 *
1149 * strsep() updates @options to pointer after the first found token
1150 * it also returns the pointer ahead the token.
1151 */
1152 while ((opt = strsep(&options, ":")) != NULL) {
1153 /* options that mean for any lynx chips are configured here */
1154 if (!strncmp(opt, "noaccel", strlen("noaccel"))) {
1155 g_noaccel = 1;
1156 } else if (!strncmp(opt, "nomtrr", strlen("nomtrr"))) {
1157 g_nomtrr = 1;
1158 } else if (!strncmp(opt, "dual", strlen("dual"))) {
1159 g_dualview = 1;
1160 } else {
1161 strcat(tmp, opt);
1162 tmp += strlen(opt);
1163 if (options)
1164 *tmp++ = ':';
1165 else
1166 *tmp++ = 0;
1167 }
1168 }
1169
1170 /* misc g_settings are transport to chip specific routines */
1171 pr_info("parameter left for chip specific analysis:%s\n", g_settings);
1172 return 0;
1173 }
1174
1175 static const struct pci_device_id smi_pci_table[] = {
1176 { PCI_DEVICE(0x126f, 0x0750), },
1177 {0,}
1178 };
1179
1180 MODULE_DEVICE_TABLE(pci, smi_pci_table);
1181
1182 static SIMPLE_DEV_PM_OPS(lynxfb_pm_ops, lynxfb_suspend, lynxfb_resume);
1183
1184 static struct pci_driver lynxfb_driver = {
1185 .name = "sm750fb",
1186 .id_table = smi_pci_table,
1187 .probe = lynxfb_pci_probe,
1188 .remove = lynxfb_pci_remove,
1189 .driver.pm = &lynxfb_pm_ops,
1190 };
1191
lynxfb_init(void)1192 static int __init lynxfb_init(void)
1193 {
1194 char *option;
1195
1196 if (fb_modesetting_disabled("sm750fb"))
1197 return -ENODEV;
1198
1199 #ifdef MODULE
1200 option = g_option;
1201 #else
1202 if (fb_get_options("sm750fb", &option))
1203 return -ENODEV;
1204 #endif
1205
1206 lynxfb_setup(option);
1207 return pci_register_driver(&lynxfb_driver);
1208 }
1209 module_init(lynxfb_init);
1210
lynxfb_exit(void)1211 static void __exit lynxfb_exit(void)
1212 {
1213 pci_unregister_driver(&lynxfb_driver);
1214 }
1215 module_exit(lynxfb_exit);
1216
1217 module_param(g_option, charp, 0444);
1218
1219 MODULE_PARM_DESC(g_option,
1220 "\n\t\tCommon options:\n"
1221 "\t\tnoaccel:disable 2d capabilities\n"
1222 "\t\tnomtrr:disable MTRR attribute for video memory\n"
1223 "\t\tdualview:dual frame buffer feature enabled\n"
1224 "\t\tnohwc:disable hardware cursor\n"
1225 "\t\tUsual example:\n"
1226 "\t\tinsmod ./sm750fb.ko g_option=\"noaccel,nohwc,1280x1024-8@60\"\n"
1227 );
1228
1229 MODULE_AUTHOR("monk liu <monk.liu@siliconmotion.com>");
1230 MODULE_AUTHOR("Sudip Mukherjee <sudip@vectorindia.org>");
1231 MODULE_DESCRIPTION("Frame buffer driver for SM750 chipset");
1232 MODULE_LICENSE("Dual BSD/GPL");
1233