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