Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / staging / sm750fb / sm750_hw.c
1 #include <linux/version.h>
2 #include<linux/module.h>
3 #include<linux/kernel.h>
4 #include<linux/errno.h>
5 #include<linux/string.h>
6 #include<linux/mm.h>
7 #include<linux/slab.h>
8 #include<linux/delay.h>
9 #include<linux/fb.h>
10 #include<linux/ioport.h>
11 #include<linux/init.h>
12 #include<linux/pci.h>
13 #include<linux/vmalloc.h>
14 #include<linux/pagemap.h>
15 #include <linux/console.h>
16 #ifdef CONFIG_MTRR
17 #include <asm/mtrr.h>
18 #endif
19 #include<linux/platform_device.h>
20 #include<linux/screen_info.h>
21
22 #include "sm750.h"
23 #include "sm750_hw.h"
24 #include "ddk750.h"
25 #include "sm750_accel.h"
26
27 int hw_sm750_map(struct lynx_share* share, struct pci_dev* pdev)
28 {
29         int ret;
30         struct sm750_share * spec_share;
31         
32
33         spec_share = container_of(share, struct sm750_share,share);
34         ret = 0;
35
36         share->vidreg_start  = pci_resource_start(pdev, 1);
37         share->vidreg_size = MB(2);
38
39         pr_info("mmio phyAddr = %lx\n", share->vidreg_start);
40
41         /* reserve the vidreg space of smi adaptor
42          * if you do this, u need to add release region code
43          * in lynxfb_remove, or memory will not be mapped again
44          * successfully
45          * */
46
47         if((ret = pci_request_region(pdev, 1, "sm750fb")))
48         {
49                 pr_err("Can not request PCI regions.\n");
50                 goto exit;
51         }
52
53         /* now map mmio and vidmem*/
54         share->pvReg = ioremap_nocache(share->vidreg_start, share->vidreg_size);
55         if(!share->pvReg){
56                 pr_err("mmio failed\n");
57                 ret = -EFAULT;
58                 goto exit;
59         }else{
60                 pr_info("mmio virtual addr = %p\n", share->pvReg);
61         }
62
63         
64         share->accel.dprBase = share->pvReg + DE_BASE_ADDR_TYPE1;
65         share->accel.dpPortBase = share->pvReg + DE_PORT_ADDR_TYPE1;
66
67         ddk750_set_mmio(share->pvReg,share->devid, share->revid);
68
69         share->vidmem_start = pci_resource_start(pdev, 0);
70         /* don't use pdev_resource[x].end - resource[x].start to
71          * calculate the resource size,its only the maximum available
72          * size but not the actual size,use
73          * @hw_sm750_getVMSize function can be safe.
74          * */
75         share->vidmem_size = hw_sm750_getVMSize(share);
76         pr_info("video memory phyAddr = %lx, size = %u bytes\n",
77         share->vidmem_start, share->vidmem_size);
78
79         /* reserve the vidmem space of smi adaptor */
80 #if 0
81         if((ret = pci_request_region(pdev,0,_moduleName_)))
82         {
83                 pr_err("Can not request PCI regions.\n");
84                 goto exit;
85         }
86 #endif
87
88         share->pvMem = ioremap(share->vidmem_start,
89                                                         share->vidmem_size);
90
91         if(!share->pvMem){
92                 pr_err("Map video memory failed\n");
93                 ret = -EFAULT;
94                 goto exit;
95         }else{
96                 pr_info("video memory vaddr = %p\n", share->pvMem);
97         }
98 exit:
99         return ret;
100 }
101
102
103
104 int hw_sm750_inithw(struct lynx_share* share, struct pci_dev * pdev)
105 {
106         struct sm750_share * spec_share;
107         struct init_status * parm;
108         
109         spec_share = container_of(share, struct sm750_share,share);
110         parm = &spec_share->state.initParm;
111         if(parm->chip_clk == 0)
112                 parm->chip_clk = (getChipType() == SM750LE)?
113                                                 DEFAULT_SM750LE_CHIP_CLOCK :
114                                                 DEFAULT_SM750_CHIP_CLOCK;
115
116         if(parm->mem_clk == 0)
117                 parm->mem_clk = parm->chip_clk;
118         if(parm->master_clk == 0)
119                 parm->master_clk = parm->chip_clk/3;
120
121         ddk750_initHw((initchip_param_t *)&spec_share->state.initParm);
122         /* for sm718,open pci burst */
123         if(share->devid == 0x718){
124                 POKE32(SYSTEM_CTRL,
125                                 FIELD_SET(PEEK32(SYSTEM_CTRL), SYSTEM_CTRL, PCI_BURST, ON));
126         }
127
128         /* sm750 use sii164, it can be setup with default value
129          * by on power, so initDVIDisp can be skipped */
130 #if 0
131         ddk750_initDVIDisp();
132 #endif
133
134         if(getChipType() != SM750LE)
135         {
136                 /* does user need CRT ?*/
137                 if(spec_share->state.nocrt){
138                         POKE32(MISC_CTRL,
139                                         FIELD_SET(PEEK32(MISC_CTRL),
140                                         MISC_CTRL,
141                                         DAC_POWER, OFF));
142                         /* shut off dpms */
143                         POKE32(SYSTEM_CTRL,
144                                         FIELD_SET(PEEK32(SYSTEM_CTRL),
145                                         SYSTEM_CTRL,
146                                         DPMS, VNHN));
147                 }else{
148                         POKE32(MISC_CTRL,
149                                         FIELD_SET(PEEK32(MISC_CTRL),
150                                         MISC_CTRL,
151                                         DAC_POWER, ON));
152                         /* turn on dpms */
153                         POKE32(SYSTEM_CTRL,
154                                         FIELD_SET(PEEK32(SYSTEM_CTRL),
155                                         SYSTEM_CTRL,
156                                         DPMS, VPHP));
157                 }
158
159                 switch (spec_share->state.pnltype){
160                         case sm750_doubleTFT:
161                         case sm750_24TFT:
162                         case sm750_dualTFT:
163                         POKE32(PANEL_DISPLAY_CTRL,
164                                 FIELD_VALUE(PEEK32(PANEL_DISPLAY_CTRL),
165                                                         PANEL_DISPLAY_CTRL,
166                                                         TFT_DISP,
167                                                         spec_share->state.pnltype));
168                         break;
169                 }
170         }else{
171                 /* for 750LE ,no DVI chip initilization makes Monitor no signal */
172                 /* Set up GPIO for software I2C to program DVI chip in the
173                    Xilinx SP605 board, in order to have video signal.
174                  */
175         swI2CInit(0,1);
176
177
178         /* Customer may NOT use CH7301 DVI chip, which has to be
179            initialized differently.
180          */
181         if (swI2CReadReg(0xec, 0x4a) == 0x95)
182         {
183             /* The following register values for CH7301 are from
184                Chrontel app note and our experiment.
185              */
186                         pr_info("yes,CH7301 DVI chip found\n");
187             swI2CWriteReg(0xec, 0x1d, 0x16);
188             swI2CWriteReg(0xec, 0x21, 0x9);
189             swI2CWriteReg(0xec, 0x49, 0xC0);
190                         pr_info("okay,CH7301 DVI chip setup done\n");
191         }
192         }
193
194         /* init 2d engine */
195         if(!share->accel_off){
196                 hw_sm750_initAccel(share);
197 //              share->accel.de_wait = hw_sm750_deWait;
198         }
199
200         return 0;
201 }
202
203
204 resource_size_t hw_sm750_getVMSize(struct lynx_share * share)
205 {
206         resource_size_t ret;
207         
208         ret = ddk750_getVMSize();
209         return ret;
210 }
211
212
213
214 int hw_sm750_output_checkMode(struct lynxfb_output* output, struct fb_var_screeninfo* var)
215 {
216         
217         return 0;
218 }
219
220
221 int hw_sm750_output_setMode(struct lynxfb_output* output,
222                                                                         struct fb_var_screeninfo* var, struct fb_fix_screeninfo* fix)
223 {
224         int ret;
225         disp_output_t dispSet;
226         int channel;
227         
228         ret = 0;
229         dispSet = 0;
230         channel = *output->channel;
231
232
233         if(getChipType() != SM750LE){
234                 if(channel == sm750_primary){
235                         pr_info("primary channel\n");
236                         if(output->paths & sm750_panel)
237                                 dispSet |= do_LCD1_PRI;
238                         if(output->paths & sm750_crt)
239                                 dispSet |= do_CRT_PRI;
240
241                 }else{
242                         pr_info("secondary channel\n");
243                         if(output->paths & sm750_panel)
244                                 dispSet |= do_LCD1_SEC;
245                         if(output->paths & sm750_crt)
246                                 dispSet |= do_CRT_SEC;
247
248                 }
249                 ddk750_setLogicalDispOut(dispSet);
250         }else{
251                 /* just open DISPLAY_CONTROL_750LE register bit 3:0*/
252                 u32 reg;
253                 reg = PEEK32(DISPLAY_CONTROL_750LE);
254                 reg |= 0xf;
255                 POKE32(DISPLAY_CONTROL_750LE, reg);
256         }
257
258         pr_info("ddk setlogicdispout done \n");
259         return ret;
260 }
261
262 void hw_sm750_output_clear(struct lynxfb_output* output)
263 {
264         
265         return;
266 }
267
268 int hw_sm750_crtc_checkMode(struct lynxfb_crtc* crtc, struct fb_var_screeninfo* var)
269 {
270         struct lynx_share * share;
271         
272
273         share = container_of(crtc, struct lynxfb_par,crtc)->share;
274
275         switch (var->bits_per_pixel){
276                 case 8:
277                 case 16:
278                         break;
279                 case 32:
280                         if (share->revid == SM750LE_REVISION_ID) {
281                                 pr_debug("750le do not support 32bpp\n");
282                                 return -EINVAL;
283                         }
284                         break;
285                 default:
286                         return -EINVAL;
287
288         }
289
290         return 0;
291 }
292
293
294 /*
295         set the controller's mode for @crtc charged with @var and @fix parameters
296 */
297 int hw_sm750_crtc_setMode(struct lynxfb_crtc* crtc,
298                                                                 struct fb_var_screeninfo* var,
299                                                                 struct fb_fix_screeninfo* fix)
300 {
301         int ret,fmt;
302         u32 reg;
303         mode_parameter_t modparm;
304         clock_type_t clock;
305         struct lynx_share * share;
306         struct lynxfb_par * par;
307
308         
309         ret = 0;
310         par = container_of(crtc, struct lynxfb_par, crtc);
311         share = par->share;
312 #if 1
313         if(!share->accel_off){
314                 /* set 2d engine pixel format according to mode bpp */
315                 switch(var->bits_per_pixel){
316                         case 8:
317                                 fmt = 0;
318                                 break;
319                         case 16:
320                                 fmt = 1;
321                                 break;
322                         case 32:
323                         default:
324                                 fmt = 2;
325                                 break;
326                 }
327                 hw_set2dformat(&share->accel, fmt);
328         }
329 #endif
330
331         /* set timing */
332 //      modparm.pixel_clock = PS_TO_HZ(var->pixclock);
333         modparm.pixel_clock = ps_to_hz(var->pixclock);
334         modparm.vertical_sync_polarity = (var->sync & FB_SYNC_HOR_HIGH_ACT) ? POS:NEG;
335         modparm.horizontal_sync_polarity = (var->sync & FB_SYNC_VERT_HIGH_ACT) ? POS:NEG;
336         modparm.clock_phase_polarity = (var->sync& FB_SYNC_COMP_HIGH_ACT) ? POS:NEG;
337         modparm.horizontal_display_end = var->xres;
338         modparm.horizontal_sync_width = var->hsync_len;
339         modparm.horizontal_sync_start = var->xres + var->right_margin;
340         modparm.horizontal_total = var->xres + var->left_margin + var->right_margin + var->hsync_len;
341         modparm.vertical_display_end = var->yres;
342         modparm.vertical_sync_height = var->vsync_len;
343         modparm.vertical_sync_start = var->yres + var->lower_margin;
344         modparm.vertical_total = var->yres + var->upper_margin + var->lower_margin + var->vsync_len;
345
346         /* choose pll */
347         if(crtc->channel != sm750_secondary)
348                 clock = PRIMARY_PLL;
349         else
350                 clock = SECONDARY_PLL;
351
352         pr_debug("Request pixel clock = %lu\n", modparm.pixel_clock);
353         ret = ddk750_setModeTiming(&modparm, clock);
354         if(ret){
355                 pr_err("Set mode timing failed\n");
356                 goto exit;
357         }
358
359         if(crtc->channel != sm750_secondary){
360                 /* set pitch, offset ,width,start address ,etc... */
361                 POKE32(PANEL_FB_ADDRESS,
362                         FIELD_SET(0, PANEL_FB_ADDRESS, STATUS, CURRENT)|
363                         FIELD_SET(0, PANEL_FB_ADDRESS, EXT, LOCAL)|
364                         FIELD_VALUE(0, PANEL_FB_ADDRESS, ADDRESS, crtc->oScreen));
365
366                 reg = var->xres * (var->bits_per_pixel >> 3);
367                 /* crtc->channel is not equal to par->index on numeric,be aware of that */
368                 reg = PADDING(crtc->line_pad,reg);
369
370                 POKE32(PANEL_FB_WIDTH,
371                         FIELD_VALUE(0, PANEL_FB_WIDTH, WIDTH, reg)|
372                         FIELD_VALUE(0, PANEL_FB_WIDTH, OFFSET, fix->line_length));
373
374                 POKE32(PANEL_WINDOW_WIDTH,
375                         FIELD_VALUE(0, PANEL_WINDOW_WIDTH, WIDTH, var->xres -1)|
376                         FIELD_VALUE(0, PANEL_WINDOW_WIDTH, X, var->xoffset));
377
378                 POKE32(PANEL_WINDOW_HEIGHT,
379                         FIELD_VALUE(0, PANEL_WINDOW_HEIGHT, HEIGHT, var->yres_virtual - 1)|
380                         FIELD_VALUE(0, PANEL_WINDOW_HEIGHT, Y, var->yoffset));
381
382                 POKE32(PANEL_PLANE_TL, 0);
383
384                 POKE32(PANEL_PLANE_BR,
385                         FIELD_VALUE(0, PANEL_PLANE_BR, BOTTOM, var->yres - 1)|
386                         FIELD_VALUE(0, PANEL_PLANE_BR,RIGHT, var->xres - 1));
387
388                 /* set pixel format */
389                 reg = PEEK32(PANEL_DISPLAY_CTRL);
390                 POKE32(PANEL_DISPLAY_CTRL,
391                         FIELD_VALUE(reg,
392                         PANEL_DISPLAY_CTRL, FORMAT,
393                         (var->bits_per_pixel >> 4)
394                         ));
395         }else{
396                 /* not implemented now */
397                 POKE32(CRT_FB_ADDRESS, crtc->oScreen);
398                 reg = var->xres * (var->bits_per_pixel >> 3);
399                 /* crtc->channel is not equal to par->index on numeric,be aware of that */
400                 reg = PADDING(crtc->line_pad, reg);
401
402                 POKE32(CRT_FB_WIDTH,
403                         FIELD_VALUE(0, CRT_FB_WIDTH, WIDTH, reg)|
404                         FIELD_VALUE(0, CRT_FB_WIDTH, OFFSET, fix->line_length));
405
406                 /* SET PIXEL FORMAT */
407                 reg = PEEK32(CRT_DISPLAY_CTRL);
408                 reg = FIELD_VALUE(reg, CRT_DISPLAY_CTRL, FORMAT, var->bits_per_pixel >> 4);
409                 POKE32(CRT_DISPLAY_CTRL, reg);
410
411         }
412
413
414 exit:
415         return ret;
416 }
417
418 void hw_sm750_crtc_clear(struct lynxfb_crtc* crtc)
419 {
420         
421         return;
422 }
423
424 int hw_sm750_setColReg(struct lynxfb_crtc* crtc, ushort index,
425                                                                 ushort red, ushort green, ushort blue)
426 {
427         static unsigned int add[]={PANEL_PALETTE_RAM,CRT_PALETTE_RAM};
428         POKE32(add[crtc->channel] + index*4, (red<<16)|(green<<8)|blue);
429         return 0;
430 }
431
432 int hw_sm750le_setBLANK(struct lynxfb_output * output, int blank){
433         int dpms,crtdb;
434         
435         switch(blank)
436         {
437 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
438                 case FB_BLANK_UNBLANK:
439 #else
440                 case VESA_NO_BLANKING:
441 #endif
442                         dpms = CRT_DISPLAY_CTRL_DPMS_0;
443                         crtdb = CRT_DISPLAY_CTRL_BLANK_OFF;
444                         break;
445 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
446                 case FB_BLANK_NORMAL:
447                         dpms = CRT_DISPLAY_CTRL_DPMS_0;
448                         crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
449                         break;
450 #endif
451 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
452                 case FB_BLANK_VSYNC_SUSPEND:
453 #else
454                 case VESA_VSYNC_SUSPEND:
455 #endif
456                         dpms = CRT_DISPLAY_CTRL_DPMS_2;
457                         crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
458                         break;
459 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
460                 case FB_BLANK_HSYNC_SUSPEND:
461 #else
462                 case VESA_HSYNC_SUSPEND:
463 #endif
464                         dpms = CRT_DISPLAY_CTRL_DPMS_1;
465                         crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
466                         break;
467 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
468                 case FB_BLANK_POWERDOWN:
469 #else
470                 case VESA_POWERDOWN:
471 #endif
472                         dpms = CRT_DISPLAY_CTRL_DPMS_3;
473                         crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
474                         break;
475                 default:
476                         return -EINVAL;
477         }
478
479         if(output->paths & sm750_crt){
480                 POKE32(CRT_DISPLAY_CTRL, FIELD_VALUE(PEEK32(CRT_DISPLAY_CTRL), CRT_DISPLAY_CTRL, DPMS, dpms));
481                 POKE32(CRT_DISPLAY_CTRL, FIELD_VALUE(PEEK32(CRT_DISPLAY_CTRL), CRT_DISPLAY_CTRL, BLANK, crtdb));
482         }
483         return 0;
484 }
485
486 int hw_sm750_setBLANK(struct lynxfb_output* output,int blank)
487 {
488         unsigned int dpms, pps, crtdb;
489         
490         dpms = pps = crtdb = 0;
491
492         switch (blank)
493         {
494 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
495                 case FB_BLANK_UNBLANK:
496 #else
497                 case VESA_NO_BLANKING:
498 #endif
499                         pr_info("flag = FB_BLANK_UNBLANK \n");
500                         dpms = SYSTEM_CTRL_DPMS_VPHP;
501                         pps = PANEL_DISPLAY_CTRL_DATA_ENABLE;
502                         crtdb = CRT_DISPLAY_CTRL_BLANK_OFF;
503                         break;
504 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
505                 case FB_BLANK_NORMAL:
506                         pr_info("flag = FB_BLANK_NORMAL \n");
507                         dpms = SYSTEM_CTRL_DPMS_VPHP;
508                         pps = PANEL_DISPLAY_CTRL_DATA_DISABLE;
509                         crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
510                         break;
511 #endif
512 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
513                 case FB_BLANK_VSYNC_SUSPEND:
514 #else
515                 case VESA_VSYNC_SUSPEND:
516 #endif
517                         dpms = SYSTEM_CTRL_DPMS_VNHP;
518                         pps = PANEL_DISPLAY_CTRL_DATA_DISABLE;
519                         crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
520                         break;
521 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
522                 case FB_BLANK_HSYNC_SUSPEND:
523 #else
524                 case VESA_HSYNC_SUSPEND:
525 #endif
526                         dpms = SYSTEM_CTRL_DPMS_VPHN;
527                         pps = PANEL_DISPLAY_CTRL_DATA_DISABLE;
528                         crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
529                         break;
530 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
531                 case FB_BLANK_POWERDOWN:
532 #else
533                 case VESA_POWERDOWN:
534 #endif
535                         dpms = SYSTEM_CTRL_DPMS_VNHN;
536                         pps = PANEL_DISPLAY_CTRL_DATA_DISABLE;
537                         crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
538                         break;
539         }
540
541         if(output->paths & sm750_crt){
542
543                 POKE32(SYSTEM_CTRL,FIELD_VALUE(PEEK32(SYSTEM_CTRL), SYSTEM_CTRL, DPMS, dpms));
544                 POKE32(CRT_DISPLAY_CTRL,FIELD_VALUE(PEEK32(CRT_DISPLAY_CTRL), CRT_DISPLAY_CTRL,BLANK, crtdb));
545         }
546
547         if(output->paths & sm750_panel){
548                 POKE32(PANEL_DISPLAY_CTRL, FIELD_VALUE(PEEK32(PANEL_DISPLAY_CTRL), PANEL_DISPLAY_CTRL, DATA, pps));
549         }
550
551         return 0;
552 }
553
554
555 void hw_sm750_initAccel(struct lynx_share * share)
556 {
557         u32 reg;
558         enable2DEngine(1);
559
560         if(getChipType() == SM750LE){
561                 reg = PEEK32(DE_STATE1);
562                 reg = FIELD_SET(reg, DE_STATE1, DE_ABORT,ON);
563                 POKE32(DE_STATE1,reg);
564
565                 reg = PEEK32(DE_STATE1);
566                 reg = FIELD_SET(reg, DE_STATE1, DE_ABORT,OFF);
567                 POKE32(DE_STATE1, reg);
568
569         }else{
570                 /* engine reset */
571                 reg = PEEK32(SYSTEM_CTRL);
572             reg = FIELD_SET(reg, SYSTEM_CTRL, DE_ABORT,ON);
573                 POKE32(SYSTEM_CTRL, reg);
574
575                 reg = PEEK32(SYSTEM_CTRL);
576                 reg = FIELD_SET(reg, SYSTEM_CTRL, DE_ABORT,OFF);
577                 POKE32(SYSTEM_CTRL, reg);
578         }
579
580         /* call 2d init */
581         share->accel.de_init(&share->accel);
582 }
583
584 int hw_sm750le_deWait(void)
585 {
586         int i=0x10000000;
587         while(i--){
588                 unsigned int dwVal = PEEK32(DE_STATE2);
589                 if((FIELD_GET(dwVal, DE_STATE2, DE_STATUS) == DE_STATE2_DE_STATUS_IDLE) &&
590                         (FIELD_GET(dwVal, DE_STATE2, DE_FIFO)  == DE_STATE2_DE_FIFO_EMPTY) &&
591                         (FIELD_GET(dwVal, DE_STATE2, DE_MEM_FIFO) == DE_STATE2_DE_MEM_FIFO_EMPTY))
592                 {
593                         return 0;
594                 }
595         }
596         /* timeout error */
597         return -1;
598 }
599
600
601 int hw_sm750_deWait(void)
602 {
603         int i=0x10000000;
604         while(i--){
605                 unsigned int dwVal = PEEK32(SYSTEM_CTRL);
606                 if((FIELD_GET(dwVal,SYSTEM_CTRL,DE_STATUS) == SYSTEM_CTRL_DE_STATUS_IDLE) &&
607                         (FIELD_GET(dwVal,SYSTEM_CTRL,DE_FIFO)  == SYSTEM_CTRL_DE_FIFO_EMPTY) &&
608                         (FIELD_GET(dwVal,SYSTEM_CTRL,DE_MEM_FIFO) == SYSTEM_CTRL_DE_MEM_FIFO_EMPTY))
609                 {
610                         return 0;
611                 }
612         }
613         /* timeout error */
614         return -1;
615 }
616
617 int hw_sm750_pan_display(struct lynxfb_crtc *crtc,
618         const struct fb_var_screeninfo *var,
619         const struct fb_info *info)
620 {
621     uint32_t total;
622     //check params
623     if ((var->xoffset + var->xres > var->xres_virtual) ||
624             (var->yoffset + var->yres > var->yres_virtual)) {
625         return -EINVAL;
626     }
627
628     total = var->yoffset * info->fix.line_length +
629         ((var->xoffset * var->bits_per_pixel) >> 3);
630     total += crtc->oScreen;
631     if (crtc->channel == sm750_primary) {
632         POKE32(PANEL_FB_ADDRESS,
633                 FIELD_VALUE(PEEK32(PANEL_FB_ADDRESS),
634                     PANEL_FB_ADDRESS, ADDRESS, total));
635     } else {
636         POKE32(CRT_FB_ADDRESS,
637                 FIELD_VALUE(PEEK32(CRT_FB_ADDRESS),
638                     CRT_FB_ADDRESS, ADDRESS, total));
639     }
640     return 0;
641 }
642