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