| 1 | /* cpuctrl for GP2X |
| 2 | Copyright (C) 2005 Hermes/PS2Reality |
| 3 | the gamma-routine was provided by theoddbot |
| 4 | parts (c) Rlyehs Work & (C) 2006 god_at_hell |
| 5 | |
| 6 | This program is free software; you can redistribute it and/or modify |
| 7 | it under the terms of the GNU General Public License as published by |
| 8 | the Free Software Foundation; either version 2 of the License, or |
| 9 | (at your option) any later version. |
| 10 | |
| 11 | This program is distributed in the hope that it will be useful, |
| 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | GNU General Public License for more details. |
| 15 | |
| 16 | You should have received a copy of the GNU General Public License |
| 17 | along with this program; if not, write to the Free Software |
| 18 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 19 | |
| 20 | */ |
| 21 | |
| 22 | |
| 23 | #include <stdio.h> |
| 24 | #include <math.h> |
| 25 | #include "cpuctrl.h" |
| 26 | |
| 27 | |
| 28 | /* system registers */ |
| 29 | static struct |
| 30 | { |
| 31 | unsigned short SYSCLKENREG,SYSCSETREG,UPLLVSETREG,FPLLVSETREG, |
| 32 | DUALINT920,DUALINT940,DUALCTRL940,MEMTIMEX0,MEMTIMEX1,DISPCSETREG, |
| 33 | DPC_HS_WIDTH,DPC_HS_STR,DPC_HS_END,DPC_VS_END,DPC_DE; |
| 34 | } |
| 35 | system_reg; |
| 36 | |
| 37 | static volatile unsigned short *MEM_REG; |
| 38 | |
| 39 | #define SYS_CLK_FREQ 7372800 |
| 40 | |
| 41 | // Fout = (m * Fin) / (p * 2^s) |
| 42 | // m = MDIV+8, p = PDIV+2, s = SDIV |
| 43 | |
| 44 | // m = (Fout * p * 2^s) / Fin |
| 45 | |
| 46 | void cpuctrl_init(void) |
| 47 | { |
| 48 | extern volatile unsigned short *gp2x_memregs; /* from minimal library rlyeh */ |
| 49 | MEM_REG=&gp2x_memregs[0]; |
| 50 | system_reg.DISPCSETREG=MEM_REG[0x924>>1]; |
| 51 | system_reg.UPLLVSETREG=MEM_REG[0x916>>1]; |
| 52 | system_reg.FPLLVSETREG=MEM_REG[0x912>>1]; |
| 53 | system_reg.SYSCSETREG=MEM_REG[0x91c>>1]; |
| 54 | system_reg.SYSCLKENREG=MEM_REG[0x904>>1]; |
| 55 | system_reg.DUALINT920=MEM_REG[0x3B40>>1]; |
| 56 | system_reg.DUALINT940=MEM_REG[0x3B42>>1]; |
| 57 | system_reg.DUALCTRL940=MEM_REG[0x3B48>>1]; |
| 58 | system_reg.MEMTIMEX0=MEM_REG[0x3802>>1]; |
| 59 | system_reg.MEMTIMEX1=MEM_REG[0x3804>>1]; |
| 60 | system_reg.DPC_HS_WIDTH=MEM_REG[0x281A>>1]; |
| 61 | system_reg.DPC_HS_STR=MEM_REG[0x281C>>1]; |
| 62 | system_reg.DPC_HS_END=MEM_REG[0x281E>>1]; |
| 63 | system_reg.DPC_VS_END=MEM_REG[0x2822>>1]; |
| 64 | system_reg.DPC_DE=MEM_REG[0x2826>>1]; |
| 65 | } |
| 66 | |
| 67 | |
| 68 | void cpuctrl_deinit(void) |
| 69 | { |
| 70 | MEM_REG[0x910>>1]=system_reg.FPLLVSETREG; |
| 71 | MEM_REG[0x91c>>1]=system_reg.SYSCSETREG; |
| 72 | MEM_REG[0x3B40>>1]=system_reg.DUALINT920; |
| 73 | MEM_REG[0x3B42>>1]=system_reg.DUALINT940; |
| 74 | MEM_REG[0x3B48>>1]=system_reg.DUALCTRL940; |
| 75 | MEM_REG[0x904>>1]=system_reg.SYSCLKENREG; |
| 76 | MEM_REG[0x3802>>1]=system_reg.MEMTIMEX0; |
| 77 | MEM_REG[0x3804>>1]=system_reg.MEMTIMEX1 /*| 0x9000*/; |
| 78 | unset_LCD_custom_rate(); |
| 79 | } |
| 80 | |
| 81 | |
| 82 | void set_display_clock_div(unsigned div) |
| 83 | { |
| 84 | div=((div & 63) | 64)<<8; |
| 85 | MEM_REG[0x924>>1]=(MEM_REG[0x924>>1] & ~(255<<8)) | div; |
| 86 | } |
| 87 | |
| 88 | |
| 89 | void set_FCLK(unsigned MHZ) |
| 90 | { |
| 91 | unsigned v; |
| 92 | unsigned mdiv,pdiv=3,scale=0; |
| 93 | MHZ*=1000000; |
| 94 | mdiv=(MHZ*pdiv)/SYS_CLK_FREQ; |
| 95 | mdiv=((mdiv-8)<<8) & 0xff00; |
| 96 | pdiv=((pdiv-2)<<2) & 0xfc; |
| 97 | scale&=3; |
| 98 | v=mdiv | pdiv | scale; |
| 99 | MEM_REG[0x910>>1]=v; |
| 100 | } |
| 101 | |
| 102 | |
| 103 | void set_920_Div(unsigned short div) |
| 104 | { |
| 105 | unsigned short v; |
| 106 | v = MEM_REG[0x91c>>1] & (~0x3); |
| 107 | MEM_REG[0x91c>>1] = (div & 0x7) | v; |
| 108 | } |
| 109 | |
| 110 | |
| 111 | void set_DCLK_Div( unsigned short div ) |
| 112 | { |
| 113 | unsigned short v; |
| 114 | v = (unsigned short)( MEM_REG[0x91c>>1] & (~(0x7 << 6)) ); |
| 115 | MEM_REG[0x91c>>1] = ((div & 0x7) << 6) | v; |
| 116 | } |
| 117 | |
| 118 | /* |
| 119 | void Disable_940(void) |
| 120 | { |
| 121 | MEM_REG[0x3B42>>1]; |
| 122 | MEM_REG[0x3B42>>1]=0; |
| 123 | MEM_REG[0x3B46>>1]=0xffff; |
| 124 | MEM_REG[0x3B48>>1]|= (1 << 7); |
| 125 | MEM_REG[0x904>>1]&=0xfffe; |
| 126 | } |
| 127 | */ |
| 128 | |
| 129 | |
| 130 | typedef struct |
| 131 | { |
| 132 | unsigned short reg, valmask, val; |
| 133 | } |
| 134 | reg_setting; |
| 135 | |
| 136 | // ~59.998, couldn't figure closer values |
| 137 | static reg_setting rate_almost60[] = |
| 138 | { |
| 139 | { 0x0914, 0xffff, (212<<8)|(2<<2)|1 }, /* UPLLSETVREG */ |
| 140 | { 0x0924, 0xff00, (2<<14)|(36<<8) }, /* DISPCSETREG */ |
| 141 | { 0x281A, 0x00ff, 1 }, /* .HSWID(T2) */ |
| 142 | { 0x281C, 0x00ff, 0 }, /* .HSSTR(T8) */ |
| 143 | { 0x281E, 0x00ff, 2 }, /* .HSEND(T7) */ |
| 144 | { 0x2822, 0x01ff, 12 }, /* .VSEND (T9) */ |
| 145 | { 0x2826, 0x0ff0, 34<<4 }, /* .DESTR(T3) */ |
| 146 | { 0, 0, 0 } |
| 147 | }; |
| 148 | |
| 149 | // perfect 50Hz? |
| 150 | static reg_setting rate_50[] = |
| 151 | { |
| 152 | { 0x0914, 0xffff, (39<<8)|(0<<2)|2 }, /* UPLLSETVREG */ |
| 153 | { 0x0924, 0xff00, (2<<14)|(7<<8) }, /* DISPCSETREG */ |
| 154 | { 0x281A, 0x00ff, 31 }, /* .HSWID(T2) */ |
| 155 | { 0x281C, 0x00ff, 16 }, /* .HSSTR(T8) */ |
| 156 | { 0x281E, 0x00ff, 15 }, /* .HSEND(T7) */ |
| 157 | { 0x2822, 0x01ff, 15 }, /* .VSEND (T9) */ |
| 158 | { 0x2826, 0x0ff0, 37<<4 }, /* .DESTR(T3) */ |
| 159 | { 0, 0, 0 } |
| 160 | }; |
| 161 | |
| 162 | // 16639/2 ~120.20 |
| 163 | static reg_setting rate_120_20[] = |
| 164 | { |
| 165 | { 0x0914, 0xffff, (96<<8)|(0<<2)|2 }, /* UPLLSETVREG */ |
| 166 | { 0x0924, 0xff00, (2<<14)|(7<<8) }, /* DISPCSETREG */ |
| 167 | { 0x281A, 0x00ff, 19 }, /* .HSWID(T2) */ |
| 168 | { 0x281C, 0x00ff, 7 }, /* .HSSTR(T8) */ |
| 169 | { 0x281E, 0x00ff, 7 }, /* .HSEND(T7) */ |
| 170 | { 0x2822, 0x01ff, 12 }, /* .VSEND (T9) */ |
| 171 | { 0x2826, 0x0ff0, 37<<4 }, /* .DESTR(T3) */ |
| 172 | { 0, 0, 0 } |
| 173 | }; |
| 174 | |
| 175 | // 19997/2 ~100.02 |
| 176 | static reg_setting rate_100_02[] = |
| 177 | { |
| 178 | { 0x0914, 0xffff, (98<<8)|(0<<2)|2 }, /* UPLLSETVREG */ |
| 179 | { 0x0924, 0xff00, (2<<14)|(8<<8) }, /* DISPCSETREG */ |
| 180 | { 0x281A, 0x00ff, 26 }, /* .HSWID(T2) */ |
| 181 | { 0x281C, 0x00ff, 6 }, /* .HSSTR(T8) */ |
| 182 | { 0x281E, 0x00ff, 6 }, /* .HSEND(T7) */ |
| 183 | { 0x2822, 0x01ff, 31 }, /* .VSEND (T9) */ |
| 184 | { 0x2826, 0x0ff0, 37<<4 }, /* .DESTR(T3) */ |
| 185 | { 0, 0, 0 } |
| 186 | }; |
| 187 | |
| 188 | // 120.00 97/0/2/7|25/ 7/ 7/11/37 |
| 189 | static reg_setting rate_120[] = |
| 190 | { |
| 191 | { 0x0914, 0xffff, (97<<8)|(0<<2)|2 }, /* UPLLSETVREG */ |
| 192 | { 0x0924, 0xff00, (2<<14)|(7<<8) }, /* DISPCSETREG */ |
| 193 | { 0x281A, 0x00ff, 25 }, /* .HSWID(T2) */ |
| 194 | { 0x281C, 0x00ff, 7 }, /* .HSSTR(T8) */ |
| 195 | { 0x281E, 0x00ff, 7 }, /* .HSEND(T7) */ |
| 196 | { 0x2822, 0x01ff, 11 }, /* .VSEND (T9) */ |
| 197 | { 0x2826, 0x0ff0, 37<<4 }, /* .DESTR(T3) */ |
| 198 | { 0, 0, 0 } |
| 199 | }; |
| 200 | |
| 201 | // 100.00 96/0/2/7|29/25/53/15/37 |
| 202 | static reg_setting rate_100[] = |
| 203 | { |
| 204 | { 0x0914, 0xffff, (96<<8)|(0<<2)|2 }, /* UPLLSETVREG */ |
| 205 | { 0x0924, 0xff00, (2<<14)|(7<<8) }, /* DISPCSETREG */ |
| 206 | { 0x281A, 0x00ff, 29 }, /* .HSWID(T2) */ |
| 207 | { 0x281C, 0x00ff, 25 }, /* .HSSTR(T8) */ |
| 208 | { 0x281E, 0x00ff, 53 }, /* .HSEND(T7) */ |
| 209 | { 0x2822, 0x01ff, 15 }, /* .VSEND (T9) */ |
| 210 | { 0x2826, 0x0ff0, 37<<4 }, /* .DESTR(T3) */ |
| 211 | { 0, 0, 0 } |
| 212 | }; |
| 213 | |
| 214 | |
| 215 | |
| 216 | static reg_setting *possible_rates[] = { rate_almost60, rate_50, rate_120_20, rate_100_02, rate_120, rate_100 }; |
| 217 | |
| 218 | void set_LCD_custom_rate(lcd_rate_t rate) |
| 219 | { |
| 220 | reg_setting *set; |
| 221 | |
| 222 | if (MEM_REG[0x2800>>1] & 0x100) // tv-out |
| 223 | { |
| 224 | return; |
| 225 | } |
| 226 | |
| 227 | printf("setting custom LCD refresh, mode=%i... ", rate); fflush(stdout); |
| 228 | for (set = possible_rates[rate]; set->reg; set++) |
| 229 | { |
| 230 | unsigned short val = MEM_REG[set->reg >> 1]; |
| 231 | val &= ~set->valmask; |
| 232 | val |= set->val; |
| 233 | MEM_REG[set->reg >> 1] = val; |
| 234 | } |
| 235 | printf("done.\n"); |
| 236 | } |
| 237 | |
| 238 | void unset_LCD_custom_rate(void) |
| 239 | { |
| 240 | printf("reset to prev LCD refresh.\n"); |
| 241 | MEM_REG[0x914>>1]=system_reg.UPLLVSETREG; |
| 242 | MEM_REG[0x924>>1]=system_reg.DISPCSETREG; |
| 243 | MEM_REG[0x281A>>1]=system_reg.DPC_HS_WIDTH; |
| 244 | MEM_REG[0x281C>>1]=system_reg.DPC_HS_STR; |
| 245 | MEM_REG[0x281E>>1]=system_reg.DPC_HS_END; |
| 246 | MEM_REG[0x2822>>1]=system_reg.DPC_VS_END; |
| 247 | MEM_REG[0x2826>>1]=system_reg.DPC_DE; |
| 248 | } |
| 249 | |
| 250 | void set_RAM_Timings(int tRC, int tRAS, int tWR, int tMRD, int tRFC, int tRP, int tRCD) |
| 251 | { |
| 252 | tRC -= 1; tRAS -= 1; tWR -= 1; tMRD -= 1; tRFC -= 1; tRP -= 1; tRCD -= 1; // ??? |
| 253 | MEM_REG[0x3802>>1] = ((tMRD & 0xF) << 12) | ((tRFC & 0xF) << 8) | ((tRP & 0xF) << 4) | (tRCD & 0xF); |
| 254 | MEM_REG[0x3804>>1] = /*0x9000 |*/ ((tRC & 0xF) << 8) | ((tRAS & 0xF) << 4) | (tWR & 0xF); |
| 255 | } |
| 256 | |
| 257 | |
| 258 | void set_gamma(int g100, int A_SNs_curve) |
| 259 | { |
| 260 | float gamma = (float) g100 / 100; |
| 261 | int i; |
| 262 | gamma = 1/gamma; |
| 263 | |
| 264 | //enable gamma |
| 265 | MEM_REG[0x2880>>1]&=~(1<<12); |
| 266 | |
| 267 | MEM_REG[0x295C>>1]=0; |
| 268 | for(i=0; i<256; i++) |
| 269 | { |
| 270 | unsigned char g; |
| 271 | unsigned short s; |
| 272 | const unsigned short grey50=143, grey75=177, grey25=97; |
| 273 | float blah; |
| 274 | |
| 275 | if (A_SNs_curve) |
| 276 | { |
| 277 | // The next formula is all about gaussian interpolation |
| 278 | blah = (( -128 * exp(-powf((float) i/64.0f + 2.0f , 2.0f))) + |
| 279 | ( -64 * exp(-powf((float) i/64.0f + 1.0f , 2.0f))) + |
| 280 | (grey25 * exp(-powf((float) i/64.0f - 1.0f , 2.0f))) + |
| 281 | (grey50 * exp(-powf((float) i/64.0f - 2.0f , 2.0f))) + |
| 282 | (grey75 * exp(-powf((float) i/64.0f - 3.0f , 2.0f))) + |
| 283 | ( 256 * exp(-powf((float) i/64.0f - 4.0f , 2.0f))) + |
| 284 | ( 320 * exp(-powf((float) i/64.0f - 5.0f , 2.0f))) + |
| 285 | ( 384 * exp(-powf((float) i/64.0f - 6.0f , 2.0f)))) / (1.772637f); |
| 286 | blah += 0.5f; |
| 287 | } |
| 288 | else |
| 289 | { |
| 290 | blah = i; |
| 291 | } |
| 292 | |
| 293 | g = (unsigned char)(255.0*pow(blah/255.0,gamma)); |
| 294 | //printf("%d : %d\n", i, g); |
| 295 | s = (g<<8) | g; |
| 296 | MEM_REG[0x295E>>1]= s; |
| 297 | MEM_REG[0x295E>>1]= g; |
| 298 | } |
| 299 | } |
| 300 | |