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