/* * quick tool to set various timings for Wiz * * Copyright (c) GraÅžvydas "notaz" Ignotas, 2009 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the organization nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * HTOTAL: X VTOTAL: 341 * HSWIDTH: 1 VSWIDTH: 0 * HASTART: 37 VASTART: 17 * HAEND: 277 VAEND: 337 * * 120Hz * pcd 8, 447: + 594us * pcd 9, 397: + 36us * pcd 10, 357: - 523us * pcd 11, 325: +1153us * * 'lcd_timings=397,1,37,277,341,0,17,337;dpc_clkdiv0=9' * 'ram_timings=2,9,4,1,1,1,1' */ #include #include #include #include "pollux_set.h" /* parse stuff */ static int parse_lcd_timings(const char *str, void *data) { int *lcd_timings = (int *)data; const char *p = str; int ret, c; ret = sscanf(str, "%d,%d,%d,%d,%d,%d,%d,%d", &lcd_timings[0], &lcd_timings[1], &lcd_timings[2], &lcd_timings[3], &lcd_timings[4], &lcd_timings[5], &lcd_timings[6], &lcd_timings[7]); if (ret != 8) return -1; /* skip seven commas */ for (c = 0; c < 7 && *p != 0; p++) if (*p == ',') c++; if (c != 7) return -1; /* skip last number */ while ('0' <= *p && *p <= '9') p++; return p - str; } static int parse_ram_timings(const char *str, void *data) { int *ram_timings = (int *)data; const char *p = str; int ret, c; float cas; ret = sscanf(p, "%f,%d,%d,%d,%d,%d,%d", &cas, &ram_timings[1], &ram_timings[2], &ram_timings[3], &ram_timings[4], &ram_timings[5], &ram_timings[6]); if (ret != 7) return -1; if (cas == 2) ram_timings[0] = 1; else if (cas == 2.5) ram_timings[0] = 2; else if (cas == 3) ram_timings[0] = 3; else return -1; for (c = 0; c < 6 && *p != 0; p++) if (*p == ',') c++; if (c != 6) return -1; while ('0' <= *p && *p <= '9') p++; return p - str; } static int parse_decimal(const char *str, void *data) { char *ep; *(int *)data = strtoul(str, &ep, 10); if (ep == str) return -1; return ep - str; } /* validate and apply stuff */ static int apply_lcd_timings(volatile unsigned short *memregs, void *data) { int *lcd_timings = (int *)data; int i; for (i = 0; i < 8; i++) { if (lcd_timings[i] & ~0xffff) { fprintf(stderr, "pollux_set: invalid lcd timing %d: %d\n", i, lcd_timings[i]); return -1; } } for (i = 0; i < 8; i++) memregs[(0x307c>>1) + i] = lcd_timings[i]; return 0; } static const struct { signed char adj; /* how to adjust value passed by user */ signed short min; /* range of */ signed short max; /* allowed values (inclusive) */ } ram_ranges[] = { { 0, 1, 3 }, /* cas (cl) */ { -2, 0, 15 }, /* trc */ { -2, 0, 15 }, /* tras */ { 0, 0, 15 }, /* twr */ { 0, 0, 15 }, /* tmrd */ { 0, 0, 15 }, /* trp */ { 0, 0, 15 }, /* trcd */ }; static int apply_ram_timings(volatile unsigned short *memregs, void *data) { int *ram_timings = (int *)data; int i, val; for (i = 0; i < 7; i++) { ram_timings[i] += ram_ranges[i].adj; if (ram_timings[i] < ram_ranges[i].min || ram_timings[i] > ram_ranges[i].max) { fprintf(stderr, "pollux_set: invalid RAM timing %d\n", i); return -1; } } val = memregs[0x14802>>1] & 0x0f00; val |= (ram_timings[4] << 12) | (ram_timings[5] << 4) | ram_timings[6]; memregs[0x14802>>1] = val; val = memregs[0x14804>>1] & 0x4000; val |= (ram_timings[0] << 12) | (ram_timings[1] << 8) | (ram_timings[2] << 4) | ram_timings[3]; val |= 0x8000; memregs[0x14804>>1] = val; for (i = 0; i < 0x100000 && (memregs[0x14804>>1] & 0x8000); i++) ; return 0; } static int apply_dpc_clkdiv0(volatile unsigned short *memregs, void *data) { int pcd = *(int *)data; int tmp; if ((pcd - 1) & ~0x3f) { fprintf(stderr, "pollux_set: invalid lcd clkdiv0: %d\n", pcd); return -1; } pcd = (pcd - 1) & 0x3f; tmp = memregs[0x31c4>>1]; memregs[0x31c4>>1] = (tmp & ~0x3f0) | (pcd << 4); return 0; } static int apply_cpuclk(volatile unsigned short *memregs, void *data) { volatile unsigned int *memregl = (volatile unsigned int *)memregs; int mhz = *(int *)data; int mdiv, pdiv, sdiv = 0; int i, v; // m = MDIV, p = PDIV, s = SDIV #define SYS_CLK_FREQ 27 pdiv = 9; mdiv = (mhz * pdiv) / SYS_CLK_FREQ; if (mdiv & ~0x3ff) return -1; v = (pdiv<<18) | (mdiv<<8) | sdiv; memregl[0xf004>>2] = v; memregl[0xf07c>>2] |= 0x8000; for (i = 0; (memregl[0xf07c>>2] & 0x8000) && i < 0x100000; i++) ; return 0; } static int lcd_timings[8]; static int ram_timings[7]; static int dpc_clkdiv0; static int cpuclk; static const char lcd_t_help[] = "htotal,hswidth,hastart,haend,vtotal,vswidth,vastart,vaend"; static const char ram_t_help[] = "CAS,tRC,tRAS,tWR,tMRD,tRP,tRCD"; static const struct { const char *name; const char *help; int (*parse)(const char *str, void *data); int (*apply)(volatile unsigned short *memregs, void *data); void *data; } all_params[] = { { "lcd_timings", lcd_t_help, parse_lcd_timings, apply_lcd_timings, lcd_timings }, { "ram_timings", ram_t_help, parse_ram_timings, apply_ram_timings, ram_timings }, { "dpc_clkdiv0", "divider", parse_decimal, apply_dpc_clkdiv0, &dpc_clkdiv0 }, { "clkdiv0", "divider", parse_decimal, apply_dpc_clkdiv0, &dpc_clkdiv0 }, /* alias */ { "cpuclk", "MHZ", parse_decimal, apply_cpuclk, &cpuclk }, }; #define ALL_PARAM_COUNT (sizeof(all_params) / sizeof(all_params[0])) /* * set timings based on preformated string (see usage() below for params). * returns 0 on success. */ int pollux_set(volatile unsigned short *memregs, const char *str) { int parsed_params[ALL_PARAM_COUNT]; int applied_params[ALL_PARAM_COUNT]; int applied_something = 0; const char *p, *po; int i, ret; if (str == NULL) return -1; memset(parsed_params, 0, sizeof(parsed_params)); memset(applied_params, 0, sizeof(applied_params)); p = str; while (1) { again: while (*p == ';' || *p == ' ') p++; if (*p == 0) break; for (i = 0; i < ALL_PARAM_COUNT; i++) { int param_len = strlen(all_params[i].name); if (strncmp(p, all_params[i].name, param_len) == 0 && p[param_len] == '=') { p += param_len + 1; ret = all_params[i].parse(p, all_params[i].data); if (ret < 0) { fprintf(stderr, "pollux_set parser: error at %-10s\n", p); fprintf(stderr, " valid format is: <%s>\n", all_params[i].help); return -1; } parsed_params[i] = 1; p += ret; goto again; } } /* Unknown param. Attempt to be forward compatible and ignore it. */ for (po = p; *p != 0 && *p != ';'; p++) ; fprintf(stderr, "unhandled param: "); fwrite(po, 1, p - po, stderr); fprintf(stderr, "\n"); } /* validate and apply */ for (i = 0; i < ALL_PARAM_COUNT; i++) { if (!parsed_params[i]) continue; ret = all_params[i].apply(memregs, all_params[i].data); if (ret < 0) { fprintf(stderr, "pollux_set: failed to apply %s (bad value?)\n", all_params[i].name); continue; } applied_something = 1; applied_params[i] = 1; } if (applied_something) { int c; printf("applied: "); for (i = c = 0; i < ALL_PARAM_COUNT; i++) { if (!applied_params[i]) continue; if (c != 0) printf(", "); printf("%s", all_params[i].name); c++; } printf("\n"); } return 0; } #ifdef BINARY #include #include #include #include #include static void usage(const char *binary) { int i; printf("usage:\n%s \n" "set_str:\n", binary); for (i = 0; i < ALL_PARAM_COUNT; i++) printf(" %s=<%s>\n", all_params[i].name, all_params[i].help); } int main(int argc, char *argv[]) { volatile unsigned short *memregs; int ret, memdev; if (argc != 2) { usage(argv[0]); return 1; } memdev = open("/dev/mem", O_RDWR); if (memdev == -1) { perror("open(/dev/mem) failed"); return 1; } memregs = mmap(0, 0x20000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0xc0000000); if (memregs == MAP_FAILED) { perror("mmap(memregs) failed"); close(memdev); return 1; } ret = pollux_set(memregs, argv[1]); munmap((void *)memregs, 0x20000); close(memdev); return ret; } #endif