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