summaryrefslogtreecommitdiff
path: root/firmware/target/mips/ingenic_x1000/fiiom3k
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/fiiom3k')
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/adc-target.h0
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c81
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c88
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/backlight-target.h37
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c503
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/button-target.h54
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h37
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c195
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c192
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c53
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h42
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c97
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/powermgmt-target.h0
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
32void 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
51void audiohw_postinit(void)
52{
53}
54
55void audiohw_close(void)
56{
57 ak4376_close();
58}
59
60void ak4376_set_pdn_pin(int level)
61{
62 gpio_config(GPIO_A, 1 << 16, GPIO_OUTPUT(level ? 1 : 0));
63}
64
65int 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
33static 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
38bool 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
50void backlight_hw_on(void)
51{
52 pwm_enable(BL_LCD_CHN);
53#ifdef HAVE_LCD_ENABLE
54 lcd_enable(true);
55#endif
56}
57
58void backlight_hw_off(void)
59{
60 pwm_disable(BL_LCD_CHN);
61#ifdef HAVE_LCD_ENABLE
62 lcd_enable(false);
63#endif
64}
65
66void 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
72void buttonlight_hw_on(void)
73{
74 pwm_enable(BL_BTN_CHN);
75}
76
77void buttonlight_hw_off(void)
78{
79 pwm_disable(BL_BTN_CHN);
80}
81
82void 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
27extern bool backlight_hw_init(void);
28
29extern void backlight_hw_on(void);
30extern void backlight_hw_off(void);
31extern void backlight_hw_brightness(int brightness);
32
33extern void buttonlight_hw_on(void);
34extern void buttonlight_hw_off(void);
35extern 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
71static 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
82static 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
112static 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
141static 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
167static 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
176static 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
185static 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
199static 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
314static 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
337void 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
344static 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
383void touchpad_set_sensitivity(int level)
384{
385 int pixels = 40;
386 pixels -= level;
387 ftd.scroll_thresh_sqr = pixels * pixels;
388}
389
390void 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 */
397static uint8_t hp_detect_reg = 0x00;
398
399/* Interval to poll the register */
400#define HPD_POLL_TIME (HZ/2)
401
402static 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
409static 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 */
440void 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
453int 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
471bool headphones_inserted()
472{
473 return hp_detect_reg & 0x40 ? true : false;
474}
475
476#ifndef BOOTLOADER
477static 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
486bool 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
51extern void touchpad_set_sensitivity(int level);
52extern 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
41static 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
73static 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
90int 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 */
132int 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
167const 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
31static 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
133static 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
142static 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
151static const uint8_t __attribute__((aligned(64)))
152 fiio_lcd_dma_wr_cmd[] = {0x00, 0x00, 0x00, 0x2c};
153
154const 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
169void 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
186void 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 */
33static 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
50const 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
30const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
31{
32 3470
33};
34
35/* the OF shuts down at this voltage */
36const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
37{
38 3400
39};
40
41/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
42const 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 */
48const 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
56void 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
78void adc_init(void)
79{
80}
81
82void 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
89bool charging_state(void)
90{
91 return axp173_battery_status() == AXP173_BATT_CHARGING;
92}
93
94int _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