diff options
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/fiiom3k')
13 files changed, 1379 insertions, 0 deletions
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/adc-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/adc-target.h new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/adc-target.h | |||
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c new file mode 100644 index 0000000000..2f43809523 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c | |||
@@ -0,0 +1,81 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "audiohw.h" | ||
23 | #include "system.h" | ||
24 | #include "pcm_sampr.h" | ||
25 | #include "logf.h" | ||
26 | #include "aic-x1000.h" | ||
27 | #include "i2c-x1000.h" | ||
28 | #include "gpio-x1000.h" | ||
29 | #include "x1000/aic.h" | ||
30 | #include "x1000/cpm.h" | ||
31 | |||
32 | void audiohw_init(void) | ||
33 | { | ||
34 | /* Configure AIC for I2S operation */ | ||
35 | jz_writef(CPM_CLKGR, AIC(0)); | ||
36 | gpio_config(GPIO_B, 0x1f, GPIO_DEVICE(1)); | ||
37 | jz_writef(AIC_I2SCR, STPBK(1)); | ||
38 | |||
39 | /* Operate as I2S master, use external codec */ | ||
40 | jz_writef(AIC_CFG, AUSEL(1), ICDC(0), BCKD(1), SYNCD(1), LSMP(1)); | ||
41 | jz_writef(AIC_I2SCR, ESCLK(1), AMSL(0)); | ||
42 | |||
43 | /* Stereo audio, packed 16 bit samples */ | ||
44 | jz_writef(AIC_CCR, PACK16(1), CHANNEL(1), OSS(1)); | ||
45 | |||
46 | /* Initialize DAC */ | ||
47 | i2c_x1000_set_freq(AK4376_BUS, I2C_FREQ_400K); | ||
48 | ak4376_init(); | ||
49 | } | ||
50 | |||
51 | void audiohw_postinit(void) | ||
52 | { | ||
53 | } | ||
54 | |||
55 | void audiohw_close(void) | ||
56 | { | ||
57 | ak4376_close(); | ||
58 | } | ||
59 | |||
60 | void ak4376_set_pdn_pin(int level) | ||
61 | { | ||
62 | gpio_config(GPIO_A, 1 << 16, GPIO_OUTPUT(level ? 1 : 0)); | ||
63 | } | ||
64 | |||
65 | int ak4376_set_mclk_freq(int hw_freq, bool enabled) | ||
66 | { | ||
67 | /* Get the multiplier */ | ||
68 | int freq = hw_freq_sampr[hw_freq]; | ||
69 | int mult = freq >= SAMPR_176 ? 128 : 256; | ||
70 | |||
71 | if(enabled) { | ||
72 | /* Set the new frequency; clock is enabled afterward */ | ||
73 | if(aic_i2s_set_mclk(X1000_CLK_SCLK_A, freq, mult)) | ||
74 | logf("WARNING: unachievable audio rate %d x %d!?", freq, mult); | ||
75 | } else { | ||
76 | /* Shut off the clock */ | ||
77 | jz_writef(AIC_I2SCR, STPBK(1)); | ||
78 | } | ||
79 | |||
80 | return mult; | ||
81 | } | ||
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c new file mode 100644 index 0000000000..f02fcaaee8 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c | |||
@@ -0,0 +1,88 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "backlight.h" | ||
23 | #include "backlight-target.h" | ||
24 | #include "lcd.h" | ||
25 | #include "pwm-x1000.h" | ||
26 | |||
27 | #define BL_LCD_CHN 0 | ||
28 | #define BL_LCD_PERIOD 33000 | ||
29 | |||
30 | #define BL_BTN_CHN 4 | ||
31 | #define BL_BTN_PERIOD 100000 | ||
32 | |||
33 | static int backlight_calc_duty(int period, int min_duty, int brightness) | ||
34 | { | ||
35 | return min_duty + (period - min_duty) * brightness / MAX_BRIGHTNESS_SETTING; | ||
36 | } | ||
37 | |||
38 | bool backlight_hw_init(void) | ||
39 | { | ||
40 | pwm_init(BL_LCD_CHN); | ||
41 | pwm_init(BL_BTN_CHN); | ||
42 | pwm_enable(BL_LCD_CHN); | ||
43 | pwm_enable(BL_BTN_CHN); | ||
44 | backlight_hw_brightness(MAX_BRIGHTNESS_SETTING); | ||
45 | buttonlight_hw_brightness(MAX_BRIGHTNESS_SETTING); | ||
46 | /* TODO: avoid buttonlight flicker when powering up the machine */ | ||
47 | return true; | ||
48 | } | ||
49 | |||
50 | void backlight_hw_on(void) | ||
51 | { | ||
52 | pwm_enable(BL_LCD_CHN); | ||
53 | #ifdef HAVE_LCD_ENABLE | ||
54 | lcd_enable(true); | ||
55 | #endif | ||
56 | } | ||
57 | |||
58 | void backlight_hw_off(void) | ||
59 | { | ||
60 | pwm_disable(BL_LCD_CHN); | ||
61 | #ifdef HAVE_LCD_ENABLE | ||
62 | lcd_enable(false); | ||
63 | #endif | ||
64 | } | ||
65 | |||
66 | void backlight_hw_brightness(int brightness) | ||
67 | { | ||
68 | int duty_ns = backlight_calc_duty(BL_LCD_PERIOD, 0, brightness); | ||
69 | pwm_set_period(BL_LCD_CHN, BL_LCD_PERIOD, duty_ns); | ||
70 | } | ||
71 | |||
72 | void buttonlight_hw_on(void) | ||
73 | { | ||
74 | pwm_enable(BL_BTN_CHN); | ||
75 | } | ||
76 | |||
77 | void buttonlight_hw_off(void) | ||
78 | { | ||
79 | pwm_disable(BL_BTN_CHN); | ||
80 | } | ||
81 | |||
82 | void buttonlight_hw_brightness(int brightness) | ||
83 | { | ||
84 | /* Duty cycle below 11% seems to turn the buttonlight off entirely, | ||
85 | * so we need to rescale the range */ | ||
86 | int duty_ns = backlight_calc_duty(BL_BTN_PERIOD, BL_BTN_PERIOD*11/100, brightness); | ||
87 | pwm_set_period(BL_BTN_CHN, BL_BTN_PERIOD, duty_ns); | ||
88 | } | ||
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-target.h new file mode 100644 index 0000000000..791a013c32 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-target.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef __BACKLIGHT_TARGET_H__ | ||
23 | #define __BACKLIGHT_TARGET_H__ | ||
24 | |||
25 | #include <stdbool.h> | ||
26 | |||
27 | extern bool backlight_hw_init(void); | ||
28 | |||
29 | extern void backlight_hw_on(void); | ||
30 | extern void backlight_hw_off(void); | ||
31 | extern void backlight_hw_brightness(int brightness); | ||
32 | |||
33 | extern void buttonlight_hw_on(void); | ||
34 | extern void buttonlight_hw_off(void); | ||
35 | extern void buttonlight_hw_brightness(int brightness); | ||
36 | |||
37 | #endif /* __BACKLIGHT_TARGET_H__ */ | ||
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c new file mode 100644 index 0000000000..db5ece10b0 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c | |||
@@ -0,0 +1,503 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "button.h" | ||
23 | #include "kernel.h" | ||
24 | #include "backlight.h" | ||
25 | #include "panic.h" | ||
26 | #include "lcd.h" | ||
27 | #include "gpio-x1000.h" | ||
28 | #include "i2c-x1000.h" | ||
29 | #include <string.h> | ||
30 | #include <stdbool.h> | ||
31 | |||
32 | #ifndef BOOTLOADER | ||
33 | # include "font.h" | ||
34 | #endif | ||
35 | |||
36 | #define FT_RST_PIN (1 << 15) | ||
37 | #define FT_INT_PIN (1 << 12) | ||
38 | #define ft_interrupt GPIOB12 | ||
39 | |||
40 | /* Touch event types */ | ||
41 | #define EVENT_NONE (-1) | ||
42 | #define EVENT_PRESS 0 | ||
43 | #define EVENT_RELEASE 1 | ||
44 | #define EVENT_CONTACT 2 | ||
45 | |||
46 | /* FSM states */ | ||
47 | #define STATE_IDLE 0 | ||
48 | #define STATE_PRESS 1 | ||
49 | #define STATE_REPORT 2 | ||
50 | #define STATE_SCROLL_PRESS 3 | ||
51 | #define STATE_SCROLLING 4 | ||
52 | |||
53 | /* Assume there's no active touch if no event is reported in this time */ | ||
54 | #define AUTORELEASE_TIME (10000 * OST_TICKS_PER_US) | ||
55 | |||
56 | /* If there's no significant motion on the scrollbar for this time, | ||
57 | * then report it as a button press instead */ | ||
58 | #define SCROLL_PRESS_TIME (100000 * OST_TICKS_PER_US) | ||
59 | |||
60 | /* If a press on the scrollbar moves more than this during SCROLL_PRESS_TIME, | ||
61 | * then we enter scrolling mode. */ | ||
62 | #define MIN_SCROLL_THRESH 15 | ||
63 | |||
64 | /* If OST tick a is after OST tick b, then returns the number of ticks | ||
65 | * in the interval between a and b; otherwise undefined. */ | ||
66 | #define TICKS_SINCE(a, b) ((a) - (b)) | ||
67 | |||
68 | /* Number of touch samples to smooth before reading */ | ||
69 | #define TOUCH_SAMPLES 3 | ||
70 | |||
71 | static struct ft_driver { | ||
72 | int i2c_cookie; | ||
73 | i2c_descriptor i2c_desc; | ||
74 | uint8_t raw_data[6]; | ||
75 | bool active; | ||
76 | |||
77 | /* Number of pixels squared which must be moved before | ||
78 | * a scrollbar pulse is generated */ | ||
79 | int scroll_thresh_sqr; | ||
80 | } ftd; | ||
81 | |||
82 | static struct ft_state_machine { | ||
83 | /* Current button state, used by button_read_device() */ | ||
84 | int buttons; | ||
85 | |||
86 | /* FSM state */ | ||
87 | int state; | ||
88 | |||
89 | /* Time of the last touch event, as 32-bit OST timestamp. The kernel | ||
90 | * tick is simply too low-resolution to work reliably, especially as | ||
91 | * we handle touchpad events asynchronously. */ | ||
92 | uint32_t last_event_t; | ||
93 | |||
94 | /* Time of entering the SCROLL_PRESS state, used to differentiate | ||
95 | * between a press, hold, or scrolling motion */ | ||
96 | uint32_t scroll_press_t; | ||
97 | |||
98 | /* Number of CONTACT events sampled in the PRESS state. | ||
99 | * Must reach TOUCH_SAMPLES before we move forward. */ | ||
100 | int samples; | ||
101 | |||
102 | /* Filter for smoothing touch points */ | ||
103 | int sum_x, sum_y; | ||
104 | |||
105 | /* Position of the original touch */ | ||
106 | int orig_x, orig_y; | ||
107 | |||
108 | /* Current touch position */ | ||
109 | int cur_x, cur_y; | ||
110 | } fsm; | ||
111 | |||
112 | static int touch_to_button(int x, int y) | ||
113 | { | ||
114 | if(x == 900) { | ||
115 | /* Right strip */ | ||
116 | if(y == 80) | ||
117 | return BUTTON_BACK; | ||
118 | else if(y == 240) | ||
119 | return BUTTON_RIGHT; | ||
120 | else | ||
121 | return 0; | ||
122 | } else if(x < 80) { | ||
123 | /* Left strip */ | ||
124 | if(y < 80) | ||
125 | return BUTTON_MENU; | ||
126 | else if(y > 190) | ||
127 | return BUTTON_LEFT; | ||
128 | else | ||
129 | return 0; | ||
130 | } else { | ||
131 | /* Middle strip */ | ||
132 | if(y < 100) | ||
133 | return BUTTON_UP; | ||
134 | else if(y > 220) | ||
135 | return BUTTON_DOWN; | ||
136 | else | ||
137 | return BUTTON_SELECT; | ||
138 | } | ||
139 | } | ||
140 | |||
141 | static bool ft_accum_touch(uint32_t t, int tx, int ty) | ||
142 | { | ||
143 | /* Record event time */ | ||
144 | fsm.last_event_t = t; | ||
145 | |||
146 | if(fsm.samples < TOUCH_SAMPLES) { | ||
147 | /* Continue "priming" the filter */ | ||
148 | fsm.sum_x += tx; | ||
149 | fsm.sum_y += ty; | ||
150 | fsm.samples += 1; | ||
151 | |||
152 | /* Return if filter is not ready */ | ||
153 | if(fsm.samples < TOUCH_SAMPLES) | ||
154 | return false; | ||
155 | } else { | ||
156 | /* Update filter */ | ||
157 | fsm.sum_x += tx - fsm.sum_x / TOUCH_SAMPLES; | ||
158 | fsm.sum_y += ty - fsm.sum_y / TOUCH_SAMPLES; | ||
159 | } | ||
160 | |||
161 | /* Filter is ready, so read the point */ | ||
162 | fsm.cur_x = fsm.sum_x / TOUCH_SAMPLES; | ||
163 | fsm.cur_y = fsm.sum_y / TOUCH_SAMPLES; | ||
164 | return true; | ||
165 | } | ||
166 | |||
167 | static void ft_go_idle(void) | ||
168 | { | ||
169 | /* Null out the touch state */ | ||
170 | fsm.buttons = 0; | ||
171 | fsm.samples = 0; | ||
172 | fsm.sum_x = fsm.sum_y = 0; | ||
173 | fsm.state = STATE_IDLE; | ||
174 | } | ||
175 | |||
176 | static void ft_start_report(void) | ||
177 | { | ||
178 | /* Report the button bit */ | ||
179 | fsm.buttons = touch_to_button(fsm.cur_x, fsm.cur_y); | ||
180 | fsm.orig_x = fsm.cur_x; | ||
181 | fsm.orig_y = fsm.cur_y; | ||
182 | fsm.state = STATE_REPORT; | ||
183 | } | ||
184 | |||
185 | static void ft_start_report_or_scroll(void) | ||
186 | { | ||
187 | ft_start_report(); | ||
188 | |||
189 | /* If the press occurs on the scrollbar, then we need to | ||
190 | * wait an additional delay before reporting it in case | ||
191 | * this is the beginning of a scrolling motion */ | ||
192 | if(fsm.buttons & (BUTTON_UP|BUTTON_DOWN|BUTTON_SELECT)) { | ||
193 | fsm.buttons = 0; | ||
194 | fsm.scroll_press_t = __ost_read32(); | ||
195 | fsm.state = STATE_SCROLL_PRESS; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | static void ft_step_state(uint32_t t, int evt, int tx, int ty) | ||
200 | { | ||
201 | /* Generate a release event automatically in case we missed it */ | ||
202 | if(evt == EVENT_NONE) { | ||
203 | if(TICKS_SINCE(t, fsm.last_event_t) >= AUTORELEASE_TIME) { | ||
204 | evt = EVENT_RELEASE; | ||
205 | tx = fsm.cur_x; | ||
206 | ty = fsm.cur_y; | ||
207 | } | ||
208 | } | ||
209 | |||
210 | switch(fsm.state) { | ||
211 | case STATE_IDLE: { | ||
212 | if(evt == EVENT_PRESS || evt == EVENT_CONTACT) { | ||
213 | /* Move to REPORT or PRESS state */ | ||
214 | if(ft_accum_touch(t, tx, ty)) | ||
215 | ft_start_report_or_scroll(); | ||
216 | else | ||
217 | fsm.state = STATE_PRESS; | ||
218 | } | ||
219 | } break; | ||
220 | |||
221 | case STATE_PRESS: { | ||
222 | if(evt == EVENT_RELEASE) { | ||
223 | /* Ignore if the number of samples is too low */ | ||
224 | ft_go_idle(); | ||
225 | } else if(evt == EVENT_PRESS || evt == EVENT_CONTACT) { | ||
226 | /* Accumulate the touch position in the filter */ | ||
227 | if(ft_accum_touch(t, tx, ty)) | ||
228 | ft_start_report_or_scroll(); | ||
229 | } | ||
230 | } break; | ||
231 | |||
232 | case STATE_REPORT: { | ||
233 | if(evt == EVENT_RELEASE) | ||
234 | ft_go_idle(); | ||
235 | else if(evt == EVENT_PRESS || evt == EVENT_CONTACT) | ||
236 | ft_accum_touch(t, tx, ty); | ||
237 | } break; | ||
238 | |||
239 | case STATE_SCROLL_PRESS: { | ||
240 | if(evt == EVENT_RELEASE) { | ||
241 | /* This _should_ synthesize a button press. | ||
242 | * | ||
243 | * - ft_start_report() will set the button bit based on the | ||
244 | * current touch position and enter the REPORT state, which | ||
245 | * will automatically hold the bit high | ||
246 | * | ||
247 | * - The next button_read_device() will see the button bit | ||
248 | * and report it back to Rockbox, then step the FSM with | ||
249 | * EVENT_NONE. | ||
250 | * | ||
251 | * - The EVENT_NONE stepping will eventually autogenerate a | ||
252 | * RELEASE event and restore the button state back to 0 | ||
253 | * | ||
254 | * - There's a small logic hole in the REPORT state which | ||
255 | * could cause it to miss an immediately repeated PRESS | ||
256 | * that occurs before the autorelease timeout kicks in. | ||
257 | * FIXME: We might want to special-case that. | ||
258 | */ | ||
259 | ft_start_report(); | ||
260 | break; | ||
261 | } | ||
262 | |||
263 | if(evt == EVENT_PRESS || evt == EVENT_CONTACT) | ||
264 | ft_accum_touch(t, tx, ty); | ||
265 | |||
266 | int dx = fsm.cur_x - fsm.orig_x; | ||
267 | int dy = fsm.cur_y - fsm.orig_y; | ||
268 | int dp = (dx*dx) + (dy*dy); | ||
269 | if(dp >= MIN_SCROLL_THRESH*MIN_SCROLL_THRESH) { | ||
270 | /* Significant motion: enter SCROLLING state */ | ||
271 | fsm.state = STATE_SCROLLING; | ||
272 | } else if(TICKS_SINCE(t, fsm.scroll_press_t) >= SCROLL_PRESS_TIME) { | ||
273 | /* No significant motion: report it as a press */ | ||
274 | fsm.cur_x = fsm.orig_x; | ||
275 | fsm.cur_y = fsm.orig_y; | ||
276 | ft_start_report(); | ||
277 | } | ||
278 | } break; | ||
279 | |||
280 | case STATE_SCROLLING: { | ||
281 | if(evt == EVENT_RELEASE) { | ||
282 | ft_go_idle(); | ||
283 | break; | ||
284 | } | ||
285 | |||
286 | if(evt == EVENT_PRESS || evt == EVENT_CONTACT) | ||
287 | ft_accum_touch(t, tx, ty); | ||
288 | |||
289 | int dx = fsm.cur_x - fsm.orig_x; | ||
290 | int dy = fsm.cur_y - fsm.orig_y; | ||
291 | int dp = (dx*dx) + (dy*dy); | ||
292 | if(dp >= ftd.scroll_thresh_sqr) { | ||
293 | if(dy < 0) { | ||
294 | queue_post(&button_queue, BUTTON_SCROLL_BACK, 0); | ||
295 | } else { | ||
296 | queue_post(&button_queue, BUTTON_SCROLL_FWD, 0); | ||
297 | } | ||
298 | |||
299 | /* Poke the backlight */ | ||
300 | backlight_on(); | ||
301 | buttonlight_on(); | ||
302 | |||
303 | fsm.orig_x = fsm.cur_x; | ||
304 | fsm.orig_y = fsm.cur_y; | ||
305 | } | ||
306 | } break; | ||
307 | |||
308 | default: | ||
309 | panicf("ft6x06: unhandled state"); | ||
310 | break; | ||
311 | } | ||
312 | } | ||
313 | |||
314 | static void ft_i2c_callback(int status, i2c_descriptor* desc) | ||
315 | { | ||
316 | (void)desc; | ||
317 | if(status != I2C_STATUS_OK) | ||
318 | return; | ||
319 | |||
320 | /* The panel is oriented such that its X axis is vertical, | ||
321 | * so swap the axes for reporting */ | ||
322 | int evt = ftd.raw_data[1] >> 6; | ||
323 | int ty = ftd.raw_data[2] | ((ftd.raw_data[1] & 0xf) << 8); | ||
324 | int tx = ftd.raw_data[4] | ((ftd.raw_data[3] & 0xf) << 8); | ||
325 | |||
326 | /* TODO: convert the touch positions to linear positions. | ||
327 | * | ||
328 | * Points reported by the touch controller are distorted and non-linear, | ||
329 | * ideally we'd like to correct these values. There's more precision in | ||
330 | * the middle of the touchpad than on the edges, so scrolling feels slow | ||
331 | * in the middle and faster near the edge. | ||
332 | */ | ||
333 | |||
334 | ft_step_state(__ost_read32(), evt, tx, ty); | ||
335 | } | ||
336 | |||
337 | void ft_interrupt(void) | ||
338 | { | ||
339 | /* We don't care if this fails */ | ||
340 | i2c_async_queue(FT6x06_BUS, TIMEOUT_NOBLOCK, I2C_Q_ONCE, | ||
341 | ftd.i2c_cookie, &ftd.i2c_desc); | ||
342 | } | ||
343 | |||
344 | static void ft_init(void) | ||
345 | { | ||
346 | /* Initialize the driver state */ | ||
347 | ftd.i2c_cookie = i2c_async_reserve_cookies(FT6x06_BUS, 1); | ||
348 | ftd.i2c_desc.slave_addr = FT6x06_ADDR; | ||
349 | ftd.i2c_desc.bus_cond = I2C_START | I2C_STOP; | ||
350 | ftd.i2c_desc.tran_mode = I2C_READ; | ||
351 | ftd.i2c_desc.buffer[0] = &ftd.raw_data[5]; | ||
352 | ftd.i2c_desc.count[0] = 1; | ||
353 | ftd.i2c_desc.buffer[1] = &ftd.raw_data[0]; | ||
354 | ftd.i2c_desc.count[1] = 5; | ||
355 | ftd.i2c_desc.callback = ft_i2c_callback; | ||
356 | ftd.i2c_desc.arg = 0; | ||
357 | ftd.i2c_desc.next = NULL; | ||
358 | ftd.raw_data[5] = 0x02; | ||
359 | ftd.active = true; | ||
360 | touchpad_set_sensitivity(DEFAULT_TOUCHPAD_SENSITIVITY_SETTING); | ||
361 | |||
362 | /* Initialize the state machine */ | ||
363 | fsm.buttons = 0; | ||
364 | fsm.state = STATE_IDLE; | ||
365 | fsm.last_event_t = 0; | ||
366 | fsm.scroll_press_t = 0; | ||
367 | fsm.samples = 0; | ||
368 | fsm.sum_x = fsm.sum_y = 0; | ||
369 | fsm.orig_x = fsm.orig_y = 0; | ||
370 | fsm.cur_x = fsm.cur_y = 0; | ||
371 | |||
372 | /* Bring up I2C bus */ | ||
373 | i2c_x1000_set_freq(FT6x06_BUS, I2C_FREQ_400K); | ||
374 | |||
375 | /* Reset chip */ | ||
376 | gpio_config(GPIO_B, FT_RST_PIN|FT_INT_PIN, GPIO_OUTPUT(0)); | ||
377 | mdelay(5); | ||
378 | gpio_out_level(GPIO_B, FT_RST_PIN, 1); | ||
379 | gpio_config(GPIO_B, FT_INT_PIN, GPIO_IRQ_EDGE(0)); | ||
380 | gpio_enable_irq(GPIO_B, FT_INT_PIN); | ||
381 | } | ||
382 | |||
383 | void touchpad_set_sensitivity(int level) | ||
384 | { | ||
385 | int pixels = 40; | ||
386 | pixels -= level; | ||
387 | ftd.scroll_thresh_sqr = pixels * pixels; | ||
388 | } | ||
389 | |||
390 | void touchpad_enable_device(bool en) | ||
391 | { | ||
392 | i2c_reg_write1(FT6x06_BUS, FT6x06_ADDR, 0xa5, en ? 0 : 3); | ||
393 | ftd.active = en; | ||
394 | } | ||
395 | |||
396 | /* Value of headphone detect register */ | ||
397 | static uint8_t hp_detect_reg = 0x00; | ||
398 | |||
399 | /* Interval to poll the register */ | ||
400 | #define HPD_POLL_TIME (HZ/2) | ||
401 | |||
402 | static int hp_detect_tmo_cb(struct timeout* tmo) | ||
403 | { | ||
404 | i2c_descriptor* d = (i2c_descriptor*)tmo->data; | ||
405 | i2c_async_queue(AXP173_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, d); | ||
406 | return HPD_POLL_TIME; | ||
407 | } | ||
408 | |||
409 | static void hp_detect_init(void) | ||
410 | { | ||
411 | static struct timeout tmo; | ||
412 | static const uint8_t gpio_reg = 0x94; | ||
413 | static i2c_descriptor desc = { | ||
414 | .slave_addr = AXP173_ADDR, | ||
415 | .bus_cond = I2C_START | I2C_STOP, | ||
416 | .tran_mode = I2C_READ, | ||
417 | .buffer[0] = (void*)&gpio_reg, | ||
418 | .count[0] = 1, | ||
419 | .buffer[1] = &hp_detect_reg, | ||
420 | .count[1] = 1, | ||
421 | .callback = NULL, | ||
422 | .arg = 0, | ||
423 | .next = NULL, | ||
424 | }; | ||
425 | |||
426 | /* Headphone detect is wired to an undocumented GPIO on the AXP173. | ||
427 | * This sets it to input mode so we can see the pin state. */ | ||
428 | i2c_reg_write1(AXP173_BUS, AXP173_ADDR, 0x93, 0x01); | ||
429 | |||
430 | /* Get an initial reading before startup */ | ||
431 | int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, gpio_reg); | ||
432 | if(r >= 0) | ||
433 | hp_detect_reg = r; | ||
434 | |||
435 | /* Poll the register every second */ | ||
436 | timeout_register(&tmo, &hp_detect_tmo_cb, HPD_POLL_TIME, (intptr_t)&desc); | ||
437 | } | ||
438 | |||
439 | /* Rockbox interface */ | ||
440 | void button_init_device(void) | ||
441 | { | ||
442 | /* Configure physical button GPIOs */ | ||
443 | gpio_config(GPIO_A, (1 << 17) | (1 << 19), GPIO_INPUT); | ||
444 | gpio_config(GPIO_B, (1 << 28) | (1 << 31), GPIO_INPUT); | ||
445 | |||
446 | /* Initialize touchpad */ | ||
447 | ft_init(); | ||
448 | |||
449 | /* Set up headphone detect polling */ | ||
450 | hp_detect_init(); | ||
451 | } | ||
452 | |||
453 | int button_read_device(void) | ||
454 | { | ||
455 | int r = fsm.buttons; | ||
456 | ft_step_state(__ost_read32(), EVENT_NONE, 0, 0); | ||
457 | |||
458 | /* Read GPIOs for physical buttons */ | ||
459 | uint32_t a = REG_GPIO_PIN(GPIO_A); | ||
460 | uint32_t b = REG_GPIO_PIN(GPIO_B); | ||
461 | |||
462 | /* All buttons are active low */ | ||
463 | if((a & (1 << 17)) == 0) r |= BUTTON_PLAY; | ||
464 | if((a & (1 << 19)) == 0) r |= BUTTON_VOL_UP; | ||
465 | if((b & (1 << 28)) == 0) r |= BUTTON_VOL_DOWN; | ||
466 | if((b & (1 << 31)) == 0) r |= BUTTON_POWER; | ||
467 | |||
468 | return r; | ||
469 | } | ||
470 | |||
471 | bool headphones_inserted() | ||
472 | { | ||
473 | return hp_detect_reg & 0x40 ? true : false; | ||
474 | } | ||
475 | |||
476 | #ifndef BOOTLOADER | ||
477 | static int getbtn(void) | ||
478 | { | ||
479 | int btn; | ||
480 | do { | ||
481 | btn = button_get_w_tmo(1); | ||
482 | } while(btn & (BUTTON_REL|BUTTON_REPEAT)); | ||
483 | return btn; | ||
484 | } | ||
485 | |||
486 | bool dbg_fiiom3k_touchpad(void) | ||
487 | { | ||
488 | static const char* fsm_statenames[] = { | ||
489 | "IDLE", "PRESS", "REPORT", "SCROLL_PRESS", "SCROLLING" | ||
490 | }; | ||
491 | |||
492 | do { | ||
493 | int line = 0; | ||
494 | lcd_clear_display(); | ||
495 | lcd_putsf(0, line++, "state: %s", fsm_statenames[fsm.state]); | ||
496 | lcd_putsf(0, line++, "button: %08x", fsm.buttons); | ||
497 | lcd_putsf(0, line++, "pos x: %4d orig x: %4d", fsm.cur_x, fsm.orig_x); | ||
498 | lcd_putsf(0, line++, "pos y: %4d orig y: %4d", fsm.cur_y, fsm.orig_y); | ||
499 | lcd_update(); | ||
500 | } while(getbtn() != BUTTON_POWER); | ||
501 | return false; | ||
502 | } | ||
503 | #endif | ||
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/button-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/button-target.h new file mode 100644 index 0000000000..f75a43242d --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/button-target.h | |||
@@ -0,0 +1,54 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef __BUTTON_TARGET_H__ | ||
23 | #define __BUTTON_TARGET_H__ | ||
24 | |||
25 | #include <stdbool.h> | ||
26 | |||
27 | #define BUTTON_POWER 0x00000001 | ||
28 | #define BUTTON_PLAY 0x00000002 | ||
29 | #define BUTTON_VOL_UP 0x00000004 | ||
30 | #define BUTTON_VOL_DOWN 0x00000008 | ||
31 | #define BUTTON_UP 0x00000010 | ||
32 | #define BUTTON_DOWN 0x00000020 | ||
33 | #define BUTTON_LEFT 0x00000040 | ||
34 | #define BUTTON_RIGHT 0x00000080 | ||
35 | #define BUTTON_SELECT 0x00000100 | ||
36 | #define BUTTON_BACK 0x00000200 | ||
37 | #define BUTTON_MENU 0x00000400 | ||
38 | #define BUTTON_SCROLL_FWD 0x00000800 | ||
39 | #define BUTTON_SCROLL_BACK 0x00001000 | ||
40 | |||
41 | #define BUTTON_MAIN (BUTTON_POWER|BUTTON_VOL_UP|BUTTON_VOL_DOWN|\ | ||
42 | BUTTON_PLAY|BUTTON_TOUCHPAD) | ||
43 | |||
44 | #define BUTTON_TOUCHPAD (BUTTON_UP|BUTTON_DOWN|BUTTON_LEFT|BUTTON_RIGHT|\ | ||
45 | BUTTON_SELECT|BUTTON_BACK|BUTTON_MENU|\ | ||
46 | BUTTON_SCROLL_FWD|BUTTON_SCROLL_BACK) | ||
47 | |||
48 | #define POWEROFF_BUTTON BUTTON_POWER | ||
49 | #define POWEROFF_COUNT 30 | ||
50 | |||
51 | extern void touchpad_set_sensitivity(int level); | ||
52 | extern void touchpad_enable_device(bool en); | ||
53 | |||
54 | #endif /* __BUTTON_TARGET_H__ */ | ||
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h new file mode 100644 index 0000000000..a389d2af42 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef __I2C_TARGET_H__ | ||
23 | #define __I2C_TARGET_H__ | ||
24 | |||
25 | #define I2C_ASYNC_BUS_COUNT 3 | ||
26 | #define I2C_ASYNC_QUEUE_SIZE 4 | ||
27 | |||
28 | #define AK4376_BUS 0 | ||
29 | #define AK4376_ADDR 0x10 | ||
30 | |||
31 | #define FT6x06_BUS 1 | ||
32 | #define FT6x06_ADDR 0x38 | ||
33 | |||
34 | #define AXP173_BUS 2 | ||
35 | #define AXP173_ADDR 0x34 | ||
36 | |||
37 | #endif /* __I2C_TARGET_H__ */ | ||
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c new file mode 100644 index 0000000000..c794da4000 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c | |||
@@ -0,0 +1,195 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "installer.h" | ||
23 | #include "nand-x1000.h" | ||
24 | #include "core_alloc.h" | ||
25 | #include "file.h" | ||
26 | |||
27 | #define INSTALL_SUCCESS 0 | ||
28 | #define ERR_FLASH_OPEN_FAILED (-1) | ||
29 | #define ERR_FLASH_ENABLE_WP_FAILED (-2) | ||
30 | #define ERR_FLASH_DISABLE_WP_FAILED (-3) | ||
31 | #define ERR_FLASH_ERASE_FAILED (-4) | ||
32 | #define ERR_FLASH_WRITE_FAILED (-5) | ||
33 | #define ERR_FLASH_READ_FAILED (-6) | ||
34 | #define ERR_OUT_OF_MEMORY (-7) | ||
35 | #define ERR_CANNOT_READ_FILE (-8) | ||
36 | #define ERR_CANNOT_WRITE_FILE (-9) | ||
37 | #define ERR_WRONG_SIZE (-10) | ||
38 | |||
39 | #define BOOT_IMAGE_SIZE (128 * 1024) | ||
40 | |||
41 | static int install_from_buffer(const void* buf) | ||
42 | { | ||
43 | if(nand_open()) | ||
44 | return ERR_FLASH_OPEN_FAILED; | ||
45 | |||
46 | int status = INSTALL_SUCCESS; | ||
47 | |||
48 | if(nand_enable_writes(true)) { | ||
49 | status = ERR_FLASH_DISABLE_WP_FAILED; | ||
50 | goto _exit; | ||
51 | } | ||
52 | |||
53 | if(nand_erase_block(0)) { | ||
54 | status = ERR_FLASH_ERASE_FAILED; | ||
55 | goto _exit; | ||
56 | } | ||
57 | |||
58 | if(nand_write_bytes(0, BOOT_IMAGE_SIZE, buf)) { | ||
59 | status = ERR_FLASH_WRITE_FAILED; | ||
60 | goto _exit; | ||
61 | } | ||
62 | |||
63 | if(nand_enable_writes(false)) { | ||
64 | status = ERR_FLASH_ENABLE_WP_FAILED; | ||
65 | goto _exit; | ||
66 | } | ||
67 | |||
68 | _exit: | ||
69 | nand_close(); | ||
70 | return status; | ||
71 | } | ||
72 | |||
73 | static int dump_to_buffer(void* buf) | ||
74 | { | ||
75 | if(nand_open()) | ||
76 | return ERR_FLASH_OPEN_FAILED; | ||
77 | |||
78 | int status = INSTALL_SUCCESS; | ||
79 | |||
80 | if(nand_read_bytes(0, BOOT_IMAGE_SIZE, buf)) { | ||
81 | status = ERR_FLASH_READ_FAILED; | ||
82 | goto _exit; | ||
83 | } | ||
84 | |||
85 | _exit: | ||
86 | nand_close(); | ||
87 | return status; | ||
88 | } | ||
89 | |||
90 | int install_bootloader(const char* path) | ||
91 | { | ||
92 | /* Allocate memory to hold image */ | ||
93 | int handle = core_alloc("boot_image", BOOT_IMAGE_SIZE); | ||
94 | if(handle < 0) | ||
95 | return ERR_OUT_OF_MEMORY; | ||
96 | |||
97 | int status = INSTALL_SUCCESS; | ||
98 | void* buffer = core_get_data(handle); | ||
99 | |||
100 | /* Open the boot image */ | ||
101 | int fd = open(path, O_RDONLY); | ||
102 | if(fd < 0) { | ||
103 | status = ERR_CANNOT_READ_FILE; | ||
104 | goto _exit; | ||
105 | } | ||
106 | |||
107 | /* Check file size */ | ||
108 | off_t fsize = filesize(fd); | ||
109 | if(fsize != BOOT_IMAGE_SIZE) { | ||
110 | status = ERR_WRONG_SIZE; | ||
111 | goto _exit; | ||
112 | } | ||
113 | |||
114 | /* Read the file into the buffer */ | ||
115 | ssize_t cnt = read(fd, buffer, BOOT_IMAGE_SIZE); | ||
116 | if(cnt != BOOT_IMAGE_SIZE) { | ||
117 | status = ERR_CANNOT_READ_FILE; | ||
118 | goto _exit; | ||
119 | } | ||
120 | |||
121 | /* Perform the installation */ | ||
122 | status = install_from_buffer(buffer); | ||
123 | |||
124 | _exit: | ||
125 | if(fd >= 0) | ||
126 | close(fd); | ||
127 | core_free(handle); | ||
128 | return status; | ||
129 | } | ||
130 | |||
131 | /* Dump the current bootloader to a file */ | ||
132 | int dump_bootloader(const char* path) | ||
133 | { | ||
134 | /* Allocate memory to hold image */ | ||
135 | int handle = core_alloc("boot_image", BOOT_IMAGE_SIZE); | ||
136 | if(handle < 0) | ||
137 | return -1; | ||
138 | |||
139 | /* Read data from flash */ | ||
140 | int fd = -1; | ||
141 | void* buffer = core_get_data(handle); | ||
142 | int status = dump_to_buffer(buffer); | ||
143 | if(status) | ||
144 | goto _exit; | ||
145 | |||
146 | /* Open file */ | ||
147 | fd = open(path, O_CREAT|O_TRUNC|O_WRONLY); | ||
148 | if(fd < 0) { | ||
149 | status = ERR_CANNOT_WRITE_FILE; | ||
150 | goto _exit; | ||
151 | } | ||
152 | |||
153 | /* Write data to file */ | ||
154 | ssize_t cnt = write(fd, buffer, BOOT_IMAGE_SIZE); | ||
155 | if(cnt != BOOT_IMAGE_SIZE) { | ||
156 | status = ERR_CANNOT_WRITE_FILE; | ||
157 | goto _exit; | ||
158 | } | ||
159 | |||
160 | _exit: | ||
161 | if(fd >= 0) | ||
162 | close(fd); | ||
163 | core_free(handle); | ||
164 | return status; | ||
165 | } | ||
166 | |||
167 | const char* installer_strerror(int rc) | ||
168 | { | ||
169 | switch(rc) { | ||
170 | case INSTALL_SUCCESS: | ||
171 | return "Success"; | ||
172 | case ERR_FLASH_OPEN_FAILED: | ||
173 | return "Can't open flash device"; | ||
174 | case ERR_FLASH_ENABLE_WP_FAILED: | ||
175 | return "Couldn't re-enable write protect"; | ||
176 | case ERR_FLASH_DISABLE_WP_FAILED: | ||
177 | return "Can't disable write protect"; | ||
178 | case ERR_FLASH_ERASE_FAILED: | ||
179 | return "Flash erase failed"; | ||
180 | case ERR_FLASH_WRITE_FAILED: | ||
181 | return "Flash write error"; | ||
182 | case ERR_FLASH_READ_FAILED: | ||
183 | return "Flash read error"; | ||
184 | case ERR_OUT_OF_MEMORY: | ||
185 | return "Out of memory"; | ||
186 | case ERR_CANNOT_READ_FILE: | ||
187 | return "Error reading file"; | ||
188 | case ERR_CANNOT_WRITE_FILE: | ||
189 | return "Error writing file"; | ||
190 | case ERR_WRONG_SIZE: | ||
191 | return "Wrong file size"; | ||
192 | default: | ||
193 | return "Unknown error"; | ||
194 | } | ||
195 | } | ||
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c new file mode 100644 index 0000000000..96f794d7df --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c | |||
@@ -0,0 +1,192 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "lcd.h" | ||
23 | #include "kernel.h" | ||
24 | #include "lcd-x1000.h" | ||
25 | #include "gpio-x1000.h" | ||
26 | #include "system.h" | ||
27 | |||
28 | #define CS_PIN (1 << 18) | ||
29 | #define RD_PIN (1 << 16) | ||
30 | |||
31 | static const uint32_t fiio_lcd_cmd_enable[] = { | ||
32 | /* Software reset */ | ||
33 | LCD_INSTR_CMD, 0x01, | ||
34 | LCD_INSTR_UDELAY, 120000, | ||
35 | /* Sleep out */ | ||
36 | LCD_INSTR_CMD, 0x11, | ||
37 | LCD_INSTR_UDELAY, 5000, | ||
38 | /* Memory access order */ | ||
39 | LCD_INSTR_CMD, 0x36, | ||
40 | LCD_INSTR_DAT, 0x00, | ||
41 | /* Row and column address set */ | ||
42 | LCD_INSTR_CMD, 0x2a, | ||
43 | LCD_INSTR_DAT, 0x00, | ||
44 | LCD_INSTR_DAT, 0x00, | ||
45 | LCD_INSTR_DAT, (LCD_WIDTH >> 8) & 0xff, | ||
46 | LCD_INSTR_DAT, (LCD_WIDTH & 0xff), | ||
47 | LCD_INSTR_CMD, 0x2b, | ||
48 | LCD_INSTR_DAT, 0x00, | ||
49 | LCD_INSTR_DAT, 0x00, | ||
50 | LCD_INSTR_DAT, (LCD_HEIGHT >> 8) & 0xff, | ||
51 | LCD_INSTR_DAT, (LCD_HEIGHT & 0xff), | ||
52 | /* Interface pixel format */ | ||
53 | LCD_INSTR_CMD, 0x3a, | ||
54 | LCD_INSTR_DAT, 0x05, | ||
55 | /* Enable display inversion */ | ||
56 | LCD_INSTR_CMD, 0x21, | ||
57 | /* Porch setting */ | ||
58 | LCD_INSTR_CMD, 0xb2, | ||
59 | LCD_INSTR_DAT, 0x0c, | ||
60 | LCD_INSTR_DAT, 0x0c, | ||
61 | LCD_INSTR_DAT, 0x00, | ||
62 | LCD_INSTR_DAT, 0x33, | ||
63 | LCD_INSTR_DAT, 0x33, | ||
64 | /* Gate control */ | ||
65 | LCD_INSTR_CMD, 0xb7, | ||
66 | LCD_INSTR_DAT, 0x35, | ||
67 | /* VCOM setting */ | ||
68 | LCD_INSTR_CMD, 0xbb, | ||
69 | LCD_INSTR_DAT, 0x1f, | ||
70 | /* Backlight control 5 */ | ||
71 | LCD_INSTR_CMD, 0xbc, | ||
72 | LCD_INSTR_DAT, 0xec, | ||
73 | /* Backlight control 6 */ | ||
74 | LCD_INSTR_CMD, 0xbd, | ||
75 | LCD_INSTR_DAT, 0xfe, | ||
76 | /* Voltage settings */ | ||
77 | LCD_INSTR_CMD, 0xc2, | ||
78 | LCD_INSTR_DAT, 0x01, | ||
79 | LCD_INSTR_CMD, 0xc3, | ||
80 | LCD_INSTR_DAT, 0x19, | ||
81 | LCD_INSTR_CMD, 0xc4, | ||
82 | LCD_INSTR_DAT, 0x20, | ||
83 | /* Frame rate control */ | ||
84 | LCD_INSTR_CMD, 0xc6, | ||
85 | LCD_INSTR_DAT, 0x0f, /* = 60 fps */ | ||
86 | /* Power control 1 */ | ||
87 | LCD_INSTR_CMD, 0xd0, | ||
88 | LCD_INSTR_DAT, 0xa4, | ||
89 | LCD_INSTR_DAT, 0xa1, | ||
90 | /* d6 Unknown */ | ||
91 | LCD_INSTR_CMD, 0xd6, | ||
92 | LCD_INSTR_DAT, 0xa1, | ||
93 | /* Positive gamma correction */ | ||
94 | LCD_INSTR_CMD, 0xe0, | ||
95 | LCD_INSTR_DAT, 0xd0, | ||
96 | LCD_INSTR_DAT, 0x06, | ||
97 | LCD_INSTR_DAT, 0x0c, | ||
98 | LCD_INSTR_DAT, 0x0a, | ||
99 | LCD_INSTR_DAT, 0x09, | ||
100 | LCD_INSTR_DAT, 0x0a, | ||
101 | LCD_INSTR_DAT, 0x32, | ||
102 | LCD_INSTR_DAT, 0x33, | ||
103 | LCD_INSTR_DAT, 0x49, | ||
104 | LCD_INSTR_DAT, 0x19, | ||
105 | LCD_INSTR_DAT, 0x14, | ||
106 | LCD_INSTR_DAT, 0x15, | ||
107 | LCD_INSTR_DAT, 0x2b, | ||
108 | LCD_INSTR_DAT, 0x34, | ||
109 | /* Negative gamma correction */ | ||
110 | LCD_INSTR_CMD, 0xe1, | ||
111 | LCD_INSTR_DAT, 0xd0, | ||
112 | LCD_INSTR_DAT, 0x06, | ||
113 | LCD_INSTR_DAT, 0x0c, | ||
114 | LCD_INSTR_DAT, 0x0a, | ||
115 | LCD_INSTR_DAT, 0x09, | ||
116 | LCD_INSTR_DAT, 0x11, | ||
117 | LCD_INSTR_DAT, 0x37, | ||
118 | LCD_INSTR_DAT, 0x33, | ||
119 | LCD_INSTR_DAT, 0x49, | ||
120 | LCD_INSTR_DAT, 0x19, | ||
121 | LCD_INSTR_DAT, 0x14, | ||
122 | LCD_INSTR_DAT, 0x15, | ||
123 | LCD_INSTR_DAT, 0x2d, | ||
124 | LCD_INSTR_DAT, 0x34, | ||
125 | /* Tearing effect line ON, mode=0 (vsync signal) */ | ||
126 | LCD_INSTR_CMD, 0x35, | ||
127 | LCD_INSTR_DAT, 0x00, | ||
128 | /* Display ON */ | ||
129 | LCD_INSTR_CMD, 0x29, | ||
130 | LCD_INSTR_END, | ||
131 | }; | ||
132 | |||
133 | static const uint32_t fiio_lcd_cmd_sleep[] = { | ||
134 | /* Display OFF */ | ||
135 | LCD_INSTR_CMD, 0x28, | ||
136 | /* Sleep IN */ | ||
137 | LCD_INSTR_CMD, 0x10, | ||
138 | LCD_INSTR_UDELAY, 5000, | ||
139 | LCD_INSTR_END, | ||
140 | }; | ||
141 | |||
142 | static const uint32_t fiio_lcd_cmd_wake[] = { | ||
143 | /* Sleep OUT */ | ||
144 | LCD_INSTR_CMD, 0x11, | ||
145 | LCD_INSTR_UDELAY, 5000, | ||
146 | /* Display ON */ | ||
147 | LCD_INSTR_CMD, 0x29, | ||
148 | LCD_INSTR_END, | ||
149 | }; | ||
150 | |||
151 | static const uint8_t __attribute__((aligned(64))) | ||
152 | fiio_lcd_dma_wr_cmd[] = {0x00, 0x00, 0x00, 0x2c}; | ||
153 | |||
154 | const struct lcd_tgt_config lcd_tgt_config = { | ||
155 | .bus_width = 16, | ||
156 | .cmd_width = 8, | ||
157 | .use_6800_mode = 0, | ||
158 | .use_serial = 0, | ||
159 | .clk_polarity = 0, | ||
160 | .dc_polarity = 0, | ||
161 | .wr_polarity = 1, | ||
162 | .te_enable = 1, | ||
163 | .te_polarity = 1, | ||
164 | .te_narrow = 0, | ||
165 | .dma_wr_cmd_buf = &fiio_lcd_dma_wr_cmd, | ||
166 | .dma_wr_cmd_size = sizeof(fiio_lcd_dma_wr_cmd), | ||
167 | }; | ||
168 | |||
169 | void lcd_tgt_enable(bool enable) | ||
170 | { | ||
171 | if(enable) { | ||
172 | gpio_config(GPIO_A, 0xffff, GPIO_DEVICE(1)); | ||
173 | gpio_config(GPIO_B, 0x1f << 16, GPIO_DEVICE(1)); | ||
174 | gpio_config(GPIO_B, CS_PIN|RD_PIN, GPIO_OUTPUT(1)); | ||
175 | mdelay(5); | ||
176 | gpio_out_level(GPIO_B, CS_PIN, 0); | ||
177 | lcd_set_clock(X1000_CLK_SCLK_A, 30000000); | ||
178 | lcd_exec_commands(&fiio_lcd_cmd_enable[0]); | ||
179 | } else { | ||
180 | lcd_exec_commands(&fiio_lcd_cmd_sleep[0]); | ||
181 | mdelay(115); /* ensure we wait a total of 120ms before power off */ | ||
182 | gpio_config(GPIO_B, CS_PIN|RD_PIN, 0); | ||
183 | } | ||
184 | } | ||
185 | |||
186 | void lcd_tgt_sleep(bool sleep) | ||
187 | { | ||
188 | if(sleep) | ||
189 | lcd_exec_commands(&fiio_lcd_cmd_sleep[0]); | ||
190 | else | ||
191 | lcd_exec_commands(&fiio_lcd_cmd_wake[0]); | ||
192 | } | ||
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c new file mode 100644 index 0000000000..7c8a306bae --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c | |||
@@ -0,0 +1,53 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "nand-x1000.h" | ||
23 | #include "nand-target.h" | ||
24 | #include "sfc-x1000.h" | ||
25 | |||
26 | /* Unbelievably FiiO has completely disabled the use of ECC for this chip | ||
27 | * in their Linux kernel, even though it has perfectly good spare areas. | ||
28 | * There's no internal ECC either. | ||
29 | * | ||
30 | * Using nanddump to read the spare areas reveals they're filled with 0xff, | ||
31 | * and the publicly released Linux source has the ecc_strength set to 0. | ||
32 | */ | ||
33 | static const nand_chip_data ato25d1ga = { | ||
34 | .name = "ATO25D1GA", | ||
35 | .mf_id = 0x9b, | ||
36 | .dev_id = 0x12, | ||
37 | .dev_conf = NAND_INIT_SFC_DEV_CONF, | ||
38 | /* XXX: datasheet says 104 MHz but FiiO seems to run this at 150 MHz. | ||
39 | * Didn't find any issues doing this so might as well keep the behavior. | ||
40 | */ | ||
41 | .clock_freq = NAND_INIT_CLOCK_SPEED, | ||
42 | .block_size = 64, | ||
43 | .page_size = 2048, | ||
44 | .spare_size = 64, | ||
45 | .rowaddr_width = 3, | ||
46 | .coladdr_width = 2, | ||
47 | .flags = NANDCHIP_FLAG_QUAD, | ||
48 | }; | ||
49 | |||
50 | const nand_chip_desc target_nand_chip_descs[] = { | ||
51 | {&ato25d1ga, &nand_chip_ops_std}, | ||
52 | {NULL, NULL}, | ||
53 | }; | ||
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h new file mode 100644 index 0000000000..26a8b840c9 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h | |||
@@ -0,0 +1,42 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef __NAND_TARGET_H__ | ||
23 | #define __NAND_TARGET_H__ | ||
24 | |||
25 | /* The max page size (main + spare) of all NAND chips used by this target */ | ||
26 | #define NAND_MAX_PAGE_SIZE (2048 + 64) | ||
27 | |||
28 | /* The clock source to use for the SFC controller. Note the SPL has special | ||
29 | * handling which ignores this choice, so it only applies to bootloader & app. | ||
30 | */ | ||
31 | #define NAND_CLOCK_SOURCE X1000_CLK_SCLK_A | ||
32 | |||
33 | /* The clock speed to use for the SFC controller during chip identification */ | ||
34 | #define NAND_INIT_CLOCK_SPEED 150000000 | ||
35 | |||
36 | /* Initial value to program SFC_DEV_CONF register with */ | ||
37 | #define NAND_INIT_SFC_DEV_CONF \ | ||
38 | jz_orf(SFC_DEV_CONF, CE_DL(1), HOLD_DL(1), WP_DL(1), \ | ||
39 | CPHA(0), CPOL(0), TSH(7), TSETUP(0), THOLD(0), \ | ||
40 | STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS), SMP_DELAY(1)) | ||
41 | |||
42 | #endif /* __NAND_TARGET_H__ */ | ||
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c new file mode 100644 index 0000000000..3eb3146d97 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c | |||
@@ -0,0 +1,97 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "power.h" | ||
23 | #include "adc.h" | ||
24 | #include "system.h" | ||
25 | #include "kernel.h" | ||
26 | #include "axp173.h" | ||
27 | #include "i2c-x1000.h" | ||
28 | #include "gpio-x1000.h" | ||
29 | |||
30 | const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = | ||
31 | { | ||
32 | 3470 | ||
33 | }; | ||
34 | |||
35 | /* the OF shuts down at this voltage */ | ||
36 | const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] = | ||
37 | { | ||
38 | 3400 | ||
39 | }; | ||
40 | |||
41 | /* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */ | ||
42 | const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] = | ||
43 | { | ||
44 | { 3400, 3639, 3697, 3723, 3757, 3786, 3836, 3906, 3980, 4050, 4159 } | ||
45 | }; | ||
46 | |||
47 | /* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */ | ||
48 | const unsigned short const percent_to_volt_charge[11] = | ||
49 | { | ||
50 | 3485, 3780, 3836, 3857, 3890, 3930, 3986, 4062, 4158, 4185, 4196 | ||
51 | }; | ||
52 | |||
53 | #define AXP173_IRQ_PORT GPIO_B | ||
54 | #define AXP173_IRQ_PIN (1 << 10) | ||
55 | |||
56 | void power_init(void) | ||
57 | { | ||
58 | /* Initialize driver */ | ||
59 | i2c_x1000_set_freq(2, I2C_FREQ_400K); | ||
60 | axp173_init(); | ||
61 | |||
62 | /* Set lowest sample rate */ | ||
63 | axp173_adc_set_rate(AXP173_ADC_RATE_25HZ); | ||
64 | |||
65 | /* Ensure battery voltage ADC is enabled */ | ||
66 | int bits = axp173_adc_get_enabled(); | ||
67 | bits |= (1 << ADC_BATTERY_VOLTAGE); | ||
68 | axp173_adc_set_enabled(bits); | ||
69 | |||
70 | /* Turn on all power outputs */ | ||
71 | i2c_reg_modify1(AXP173_BUS, AXP173_ADDR, 0x12, 0, 0x5f, NULL); | ||
72 | i2c_reg_modify1(AXP173_BUS, AXP173_ADDR, 0x80, 0, 0xc0, NULL); | ||
73 | |||
74 | /* Short delay to give power outputs time to stabilize */ | ||
75 | mdelay(5); | ||
76 | } | ||
77 | |||
78 | void adc_init(void) | ||
79 | { | ||
80 | } | ||
81 | |||
82 | void power_off(void) | ||
83 | { | ||
84 | /* Set the shutdown bit */ | ||
85 | i2c_reg_setbit1(AXP173_BUS, AXP173_ADDR, 0x32, 7, 1, NULL); | ||
86 | while(1); | ||
87 | } | ||
88 | |||
89 | bool charging_state(void) | ||
90 | { | ||
91 | return axp173_battery_status() == AXP173_BATT_CHARGING; | ||
92 | } | ||
93 | |||
94 | int _battery_voltage(void) | ||
95 | { | ||
96 | return axp173_adc_read(ADC_BATTERY_VOLTAGE); | ||
97 | } | ||
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/powermgmt-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/powermgmt-target.h new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/powermgmt-target.h | |||