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