supporting caanoo, line doublers, refactoring
[libpicofe.git] / gp2x / soc_pollux.c
CommitLineData
f27b1cea 1/*
2 * <random_info=mem_map>
3 * 00000000-029fffff linux (42MB)
4 * 02a00000-02dfffff fb (4MB, 153600B really used)
5 * 02e00000-02ffffff sound dma (2MB)
6 * 03000000-03ffffff MPEGDEC (?, 16MB)
7 * </random_info>
8 */
fa8d1331 9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <math.h>
13#include <sys/types.h>
14#include <sys/stat.h>
15#include <fcntl.h>
16#include <sys/mman.h>
17#include <unistd.h>
18#include <sys/ioctl.h>
19#include <linux/fb.h>
20
21#include "soc.h"
22#include "plat_gp2x.h"
23#include "../common/emu.h"
b6072c17 24#include "../common/plat.h"
fa8d1331 25#include "../common/arm_utils.h"
26#include "pollux_set.h"
27
28static volatile unsigned short *memregs;
b6072c17 29static volatile unsigned int *memregl;
fa8d1331 30static int memdev = -1;
053bef76 31static int battdev = -1;
fa8d1331 32
33extern void *gp2x_screens[4];
34
35#define fb_buf_count 4
36static unsigned int fb_paddr[fb_buf_count];
37static int fb_work_buf;
38static int fbdev = -1;
39
71769e27 40static char cpuclk_was_changed = 0;
41static unsigned short memtimex_old[2];
42static unsigned int pllsetreg0;
b7911801 43static int last_pal_setting = 0;
71769e27 44
fa8d1331 45
f71361b5 46/* misc */
47static void pollux_set_fromenv(const char *env_var)
48{
49 const char *set_string;
50 set_string = getenv(env_var);
51 if (set_string)
52 pollux_set(memregs, set_string);
53 else
54 printf("env var %s not defined.\n", env_var);
55}
56
fa8d1331 57/* video stuff */
58static void pollux_video_flip(int buf_count)
59{
60 memregl[0x406C>>2] = fb_paddr[fb_work_buf];
61 memregl[0x4058>>2] |= 0x10;
62 fb_work_buf++;
63 if (fb_work_buf >= buf_count)
64 fb_work_buf = 0;
65 g_screen_ptr = gp2x_screens[fb_work_buf];
66}
67
68static void gp2x_video_flip_(void)
69{
70 pollux_video_flip(fb_buf_count);
71}
72
73/* doulblebuffered flip */
74static void gp2x_video_flip2_(void)
75{
76 pollux_video_flip(2);
77}
78
79static void gp2x_video_changemode_ll_(int bpp)
80{
b7911801 81 static int prev_bpp = 0;
fa8d1331 82 int code = 0, bytes = 2;
b7911801 83 int rot_cmd[2] = { 0, 0 };
fa8d1331 84 unsigned int r;
f71361b5 85 char buff[32];
b7911801 86 int ret;
87
88 if (bpp == prev_bpp)
89 return;
90 prev_bpp = bpp;
91
92 printf("changemode: %dbpp rot=%d\n", abs(bpp), bpp < 0);
93
94 /* negative bpp means rotated mode */
95 rot_cmd[0] = (bpp < 0) ? 6 : 5;
96 ret = ioctl(fbdev, _IOW('D', 90, int[2]), rot_cmd);
97 if (ret < 0)
98 perror("rot ioctl failed");
99 memregl[0x4004>>2] = (bpp < 0) ? 0x013f00ef : 0x00ef013f;
100 memregl[0x4000>>2] |= 1 << 3;
101
102 /* the above ioctl resets LCD timings, so set them here */
f71361b5 103 snprintf(buff, sizeof(buff), "POLLUX_LCD_TIMINGS_%s", last_pal_setting ? "PAL" : "NTSC");
104 pollux_set_fromenv(buff);
b7911801 105
106 switch (abs(bpp))
fa8d1331 107 {
108 case 8:
109 code = 0x443a;
110 bytes = 1;
111 break;
112
113 case 15:
114 case 16:
115 code = 0x4432;
116 bytes = 2;
117 break;
118
119 default:
b7911801 120 printf("unhandled bpp request: %d\n", abs(bpp));
fa8d1331 121 return;
122 }
123
124 memregl[0x405c>>2] = bytes;
b7911801 125 memregl[0x4060>>2] = bytes * (bpp < 0 ? 240 : 320);
fa8d1331 126
127 r = memregl[0x4058>>2];
128 r = (r & 0xffff) | (code << 16) | 0x10;
129 memregl[0x4058>>2] = r;
130}
131
132static void gp2x_video_setpalette_(int *pal, int len)
133{
134 /* pollux palette is 16bpp only.. */
135 int i;
136 for (i = 0; i < len; i++)
137 {
138 int c = pal[i];
139 c = ((c >> 8) & 0xf800) | ((c >> 5) & 0x07c0) | ((c >> 3) & 0x001f);
140 memregl[0x4070>>2] = (i << 24) | c;
141 }
142}
143
144static void gp2x_video_RGB_setscaling_(int ln_offs, int W, int H)
145{
146 /* maybe a job for 3d hardware? */
147}
148
149static void gp2x_video_wait_vsync_(void)
150{
151 while (!(memregl[0x308c>>2] & (1 << 10)));
152 spend_cycles(128);
153 memregl[0x308c>>2] |= 1 << 10;
154}
155
156/* CPU clock */
157static void gp2x_set_cpuclk_(unsigned int mhz)
158{
159 char buff[24];
160 snprintf(buff, sizeof(buff), "cpuclk=%u", mhz);
161 pollux_set(memregs, buff);
71769e27 162
163 cpuclk_was_changed = 1;
fa8d1331 164}
165
fa8d1331 166/* RAM timings */
fa8d1331 167static void set_ram_timings_(void)
168{
169 pollux_set_fromenv("POLLUX_RAM_TIMINGS");
170}
171
172static void unset_ram_timings_(void)
173{
174 int i;
175
71769e27 176 memregs[0x14802>>1] = memtimex_old[0];
177 memregs[0x14804>>1] = memtimex_old[1] | 0x8000;
fa8d1331 178
179 for (i = 0; i < 0x100000; i++)
180 if (!(memregs[0x14804>>1] & 0x8000))
181 break;
182
183 printf("RAM timings reset to startup values.\n");
184}
185
186/* LCD refresh */
187static void set_lcd_custom_rate_(int is_pal)
188{
f71361b5 189 /* just remember PAL/NTSC. We always set timings in _changemode_ll() */
b7911801 190 last_pal_setting = is_pal;
fa8d1331 191}
192
193static void unset_lcd_custom_rate_(void)
194{
195}
196
197static void set_lcd_gamma_(int g100, int A_SNs_curve)
198{
199 /* hm, the LCD possibly can do it (but not POLLUX) */
200}
d572cbad 201
053bef76 202static int gp2x_read_battery_(void)
203{
204 unsigned short magic_val = 0;
205
206 if (battdev < 0)
207 return -1;
208 if (read(battdev, &magic_val, sizeof(magic_val)) != sizeof(magic_val))
209 return -1;
210 switch (magic_val) {
211 default:
212 case 1: return 100;
213 case 2: return 66;
214 case 3: return 40;
215 case 4: return 0;
216 }
217}
218
b5bfb864 219#define TIMER_BASE3 0x1980
220#define TIMER_REG(x) memregl[(TIMER_BASE3 + x) >> 2]
221
222unsigned int gp2x_get_ticks_us_(void)
223{
224 TIMER_REG(0x08) = 0x4b; /* run timer, latch value */
225 return TIMER_REG(0);
226}
227
228unsigned int gp2x_get_ticks_ms_(void)
229{
8af2da72 230 /* approximate /= 1000 */
b5bfb864 231 unsigned long long v64;
8af2da72 232 v64 = (unsigned long long)gp2x_get_ticks_us_() * 4294968;
233 return v64 >> 32;
b5bfb864 234}
235
236static void timer_cleanup(void)
237{
238 TIMER_REG(0x40) = 0x0c; /* be sure clocks are on */
239 TIMER_REG(0x08) = 0x23; /* stop the timer, clear irq in case it's pending */
240 TIMER_REG(0x00) = 0; /* clear counter */
241 TIMER_REG(0x40) = 0; /* clocks off */
242 TIMER_REG(0x44) = 0; /* dividers back to default */
243}
244
b6072c17 245/* note: both PLLs are programmed the same way,
246 * the databook incorrectly states that PLL1 differs */
247static int decode_pll(unsigned int reg)
248{
249 long long v;
250 int p, m, s;
251
252 p = (reg >> 18) & 0x3f;
253 m = (reg >> 8) & 0x3ff;
254 s = reg & 0xff;
255
256 if (p == 0)
257 p = 1;
258
259 v = 27000000; // master clock
260 v = v * m / (p << s);
261 return v;
262}
263
d572cbad 264void pollux_init(void)
265{
fa8d1331 266 struct fb_fix_screeninfo fbfix;
b6072c17 267 int i, ret, rate;
fa8d1331 268
269 memdev = open("/dev/mem", O_RDWR);
270 if (memdev == -1) {
271 perror("open(/dev/mem) failed");
272 exit(1);
273 }
274
275 memregs = mmap(0, 0x20000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0xc0000000);
276 if (memregs == MAP_FAILED) {
277 perror("mmap(memregs) failed");
278 exit(1);
279 }
280 memregl = (volatile void *)memregs;
281
282 fbdev = open("/dev/fb0", O_RDWR);
283 if (fbdev < 0) {
284 perror("can't open fbdev");
285 exit(1);
286 }
287
288 ret = ioctl(fbdev, FBIOGET_FSCREENINFO, &fbfix);
289 if (ret == -1) {
290 perror("ioctl(fbdev) failed");
291 exit(1);
292 }
293
294 printf("framebuffer: \"%s\" @ %08lx\n", fbfix.id, fbfix.smem_start);
295 fb_paddr[0] = fbfix.smem_start;
296
297 gp2x_screens[0] = mmap(0, 320*240*2*fb_buf_count, PROT_READ|PROT_WRITE,
298 MAP_SHARED, memdev, fb_paddr[0]);
299 if (gp2x_screens[0] == MAP_FAILED)
300 {
301 perror("mmap(gp2x_screens) failed");
302 exit(1);
303 }
304 memset(gp2x_screens[0], 0, 320*240*2*fb_buf_count);
305
306 printf(" %p -> %08x\n", gp2x_screens[0], fb_paddr[0]);
307 for (i = 1; i < fb_buf_count; i++)
308 {
309 fb_paddr[i] = fb_paddr[i-1] + 320*240*2;
310 gp2x_screens[i] = (char *)gp2x_screens[i-1] + 320*240*2;
311 printf(" %p -> %08x\n", gp2x_screens[i], fb_paddr[i]);
312 }
313 fb_work_buf = 0;
314 g_screen_ptr = gp2x_screens[0];
315
053bef76 316 battdev = open("/dev/pollux_batt", O_RDONLY);
317 if (battdev < 0)
318 perror("Warning: could't open pollux_batt");
319
b6072c17 320 /* find what PLL1 runs at, for the timer */
321 rate = decode_pll(memregl[0xf008>>2]);
322 printf("PLL1 @ %dHz\n", rate);
323 rate /= 1000000;
324
b5bfb864 325 /* setup timer */
b6072c17 326 if (1 <= rate && rate <= 256) {
327 if (TIMER_REG(0x08) & 8) {
328 fprintf(stderr, "warning: timer in use, overriding!\n");
329 timer_cleanup();
330 }
331
332 TIMER_REG(0x44) = ((rate - 1) << 4) | 2; /* using PLL1, divide by it's rate */
333 TIMER_REG(0x40) = 0x0c; /* clocks on */
334 TIMER_REG(0x08) = 0x6b; /* run timer, clear irq, latch value */
335
336 gp2x_get_ticks_ms = gp2x_get_ticks_ms_;
337 gp2x_get_ticks_us = gp2x_get_ticks_us_;
338 }
339 else {
340 fprintf(stderr, "warning: could not make use of timer\n");
b5bfb864 341
b6072c17 342 // those functions are actually not good at all on Wiz kernel
343 gp2x_get_ticks_ms = plat_get_ticks_ms_good;
344 gp2x_get_ticks_us = plat_get_ticks_us_good;
345 }
b5bfb864 346
053bef76 347 pllsetreg0 = memregl[0xf004>>2];
71769e27 348 memtimex_old[0] = memregs[0x14802>>1];
349 memtimex_old[1] = memregs[0x14804>>1];
fa8d1331 350
351 gp2x_video_flip = gp2x_video_flip_;
352 gp2x_video_flip2 = gp2x_video_flip2_;
353 gp2x_video_changemode_ll = gp2x_video_changemode_ll_;
354 gp2x_video_setpalette = gp2x_video_setpalette_;
355 gp2x_video_RGB_setscaling = gp2x_video_RGB_setscaling_;
356 gp2x_video_wait_vsync = gp2x_video_wait_vsync_;
357
b6072c17 358 /* some firmwares have sys clk on PLL0, we can't adjust CPU clock
359 * by reprogramming the PLL0 then, as it overclocks system bus */
360 if ((memregl[0xf000>>2] & 0x03000030) == 0x01000000)
361 gp2x_set_cpuclk = gp2x_set_cpuclk_;
362 else {
363 fprintf(stderr, "unexpected PLL config (%08x), overclocking disabled\n",
364 memregl[0xf000>>2]);
365 gp2x_set_cpuclk = NULL;
366 }
fa8d1331 367
368 set_lcd_custom_rate = set_lcd_custom_rate_;
369 unset_lcd_custom_rate = unset_lcd_custom_rate_;
370 set_lcd_gamma = set_lcd_gamma_;
371
372 set_ram_timings = set_ram_timings_;
373 unset_ram_timings = unset_ram_timings_;
053bef76 374 gp2x_read_battery = gp2x_read_battery_;
d572cbad 375}
376
377void pollux_finish(void)
378{
fa8d1331 379 /* switch to default fb mem, turn portrait off */
380 memregl[0x406C>>2] = fb_paddr[0];
381 memregl[0x4058>>2] |= 0x10;
fa8d1331 382 close(fbdev);
383
71769e27 384 gp2x_video_changemode_ll_(16);
385 unset_ram_timings_();
386 if (cpuclk_was_changed) {
387 memregl[0xf004>>2] = pllsetreg0;
388 memregl[0xf07c>>2] |= 0x8000;
389 }
390
fa8d1331 391 munmap((void *)memregs, 0x20000);
392 close(memdev);
053bef76 393 if (battdev >= 0)
394 close(battdev);
d572cbad 395}
396