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