diff options
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/shanlingq1')
12 files changed, 1296 insertions, 0 deletions
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/adc-target.h b/firmware/target/mips/ingenic_x1000/shanlingq1/adc-target.h new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/adc-target.h | |||
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c new file mode 100644 index 0000000000..7314f20412 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c | |||
@@ -0,0 +1,191 @@ | |||
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 "aic-x1000.h" | ||
26 | #include "i2c-x1000.h" | ||
27 | #include "gpio-x1000.h" | ||
28 | #include "x1000/aic.h" | ||
29 | #include "x1000/cpm.h" | ||
30 | |||
31 | /* Codec has an dedicated oscillator connected, so it can operate | ||
32 | * as i2s master or slave. I can't distinguish any difference in | ||
33 | * terms of audio quality or power consumption. Code is left here | ||
34 | * for reference in case it proves useful to change it. */ | ||
35 | #define CODEC_MASTER_MODE 0 | ||
36 | |||
37 | static int cur_fsel = HW_FREQ_48; | ||
38 | static int cur_vol_l = 0, cur_vol_r = 0; | ||
39 | static int cur_filter = 0; | ||
40 | static enum es9218_amp_mode cur_amp_mode = ES9218_AMP_MODE_1VRMS; | ||
41 | |||
42 | static void codec_start(void) | ||
43 | { | ||
44 | es9218_open(); | ||
45 | es9218_mute(true); | ||
46 | es9218_set_iface_role(CODEC_MASTER_MODE ? ES9218_IFACE_ROLE_MASTER | ||
47 | : ES9218_IFACE_ROLE_SLAVE); | ||
48 | es9218_set_iface_format(ES9218_IFACE_FORMAT_I2S, ES9218_IFACE_BITS_32); | ||
49 | es9218_set_dpll_bandwidth(10); | ||
50 | es9218_set_thd_compensation(true); | ||
51 | es9218_set_thd_coeffs(0, 0); | ||
52 | audiohw_set_filter_roll_off(cur_filter); | ||
53 | audiohw_set_frequency(cur_fsel); | ||
54 | audiohw_set_volume(cur_vol_l, cur_vol_r); | ||
55 | es9218_set_amp_mode(cur_amp_mode); | ||
56 | } | ||
57 | |||
58 | static void codec_stop(void) | ||
59 | { | ||
60 | es9218_mute(true); | ||
61 | es9218_close(); | ||
62 | mdelay(1); | ||
63 | } | ||
64 | |||
65 | void audiohw_init(void) | ||
66 | { | ||
67 | /* Configure AIC */ | ||
68 | aic_set_external_codec(true); | ||
69 | aic_set_i2s_mode(CODEC_MASTER_MODE ? AIC_I2S_SLAVE_MODE | ||
70 | : AIC_I2S_MASTER_MODE); | ||
71 | aic_enable_i2s_bit_clock(true); | ||
72 | |||
73 | /* Open DAC driver */ | ||
74 | i2c_x1000_set_freq(1, I2C_FREQ_400K); | ||
75 | codec_start(); | ||
76 | } | ||
77 | |||
78 | void audiohw_postinit(void) | ||
79 | { | ||
80 | es9218_mute(false); | ||
81 | } | ||
82 | |||
83 | void audiohw_close(void) | ||
84 | { | ||
85 | codec_stop(); | ||
86 | } | ||
87 | |||
88 | void audiohw_set_frequency(int fsel) | ||
89 | { | ||
90 | int sampr = hw_freq_sampr[fsel]; | ||
91 | |||
92 | /* choose clock gear setting, in line with the OF */ | ||
93 | enum es9218_clock_gear clkgear; | ||
94 | if(sampr <= 48000) | ||
95 | clkgear = ES9218_CLK_GEAR_4; | ||
96 | else if(sampr <= 96000) | ||
97 | clkgear = ES9218_CLK_GEAR_2; | ||
98 | else | ||
99 | clkgear = ES9218_CLK_GEAR_1; | ||
100 | |||
101 | aic_enable_i2s_bit_clock(false); | ||
102 | es9218_set_clock_gear(clkgear); | ||
103 | |||
104 | if(CODEC_MASTER_MODE) | ||
105 | es9218_set_nco_frequency(sampr); | ||
106 | else | ||
107 | aic_set_i2s_clock(X1000_CLK_SCLK_A, sampr, 64); | ||
108 | |||
109 | aic_enable_i2s_bit_clock(true); | ||
110 | |||
111 | /* save frequency selection */ | ||
112 | cur_fsel = fsel; | ||
113 | } | ||
114 | |||
115 | static int round_step_up(int x, int step) | ||
116 | { | ||
117 | int rem = x % step; | ||
118 | if(rem > 0) | ||
119 | rem -= step; | ||
120 | return x - rem; | ||
121 | } | ||
122 | |||
123 | void audiohw_set_volume(int vol_l, int vol_r) | ||
124 | { | ||
125 | /* save volume */ | ||
126 | cur_vol_l = vol_l; | ||
127 | cur_vol_r = vol_r; | ||
128 | |||
129 | /* adjust the amp setting first */ | ||
130 | int amp = round_step_up(MAX(vol_l, vol_r), ES9218_AMP_VOLUME_STEP); | ||
131 | amp = MIN(amp, ES9218_AMP_VOLUME_MAX); | ||
132 | amp = MAX(amp, ES9218_AMP_VOLUME_MIN); | ||
133 | |||
134 | /* adjust digital volumes */ | ||
135 | vol_l -= amp; | ||
136 | vol_l = MIN(vol_l, ES9218_DIG_VOLUME_MAX); | ||
137 | vol_l = MAX(vol_l, ES9218_DIG_VOLUME_MIN); | ||
138 | |||
139 | vol_r -= amp; | ||
140 | vol_r = MIN(vol_r, ES9218_DIG_VOLUME_MAX); | ||
141 | vol_r = MAX(vol_r, ES9218_DIG_VOLUME_MIN); | ||
142 | |||
143 | /* program DAC */ | ||
144 | es9218_set_amp_volume(amp); | ||
145 | es9218_set_dig_volume(vol_l, vol_r); | ||
146 | } | ||
147 | |||
148 | void audiohw_set_filter_roll_off(int value) | ||
149 | { | ||
150 | cur_filter = value; | ||
151 | es9218_set_filter(value); | ||
152 | } | ||
153 | |||
154 | void audiohw_set_power_mode(int mode) | ||
155 | { | ||
156 | enum es9218_amp_mode new_amp_mode; | ||
157 | if(mode == 0) | ||
158 | new_amp_mode = ES9218_AMP_MODE_2VRMS; | ||
159 | else | ||
160 | new_amp_mode = ES9218_AMP_MODE_1VRMS; | ||
161 | |||
162 | if(new_amp_mode != cur_amp_mode) { | ||
163 | codec_stop(); | ||
164 | cur_amp_mode = new_amp_mode; | ||
165 | codec_start(); | ||
166 | es9218_mute(false); | ||
167 | } | ||
168 | } | ||
169 | |||
170 | void es9218_set_power_pin(int level) | ||
171 | { | ||
172 | gpio_set_level(GPIO_ES9218_POWER, level ? 1 : 0); | ||
173 | } | ||
174 | |||
175 | void es9218_set_reset_pin(int level) | ||
176 | { | ||
177 | gpio_set_level(GPIO_ES9218_RESET, level ? 1 : 0); | ||
178 | } | ||
179 | |||
180 | uint32_t es9218_get_mclk(void) | ||
181 | { | ||
182 | /* Measured by running the DAC in asynchronous I2S slave mode, | ||
183 | * and reading back the DPLL number from regs 0x42-0x45 while | ||
184 | * playing back 44.1 KHz audio. | ||
185 | * | ||
186 | * CLK = (44_100 * 2**32) / 0x4b46e5 | ||
187 | * = 38_393_403.29532737 | ||
188 | * ~ 38.4 Mhz | ||
189 | */ | ||
190 | return 38400000; | ||
191 | } | ||
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/backlight-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/backlight-shanlingq1.c new file mode 100644 index 0000000000..32c1b902aa --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/backlight-shanlingq1.c | |||
@@ -0,0 +1,63 @@ | |||
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 10000 | ||
29 | |||
30 | static int backlight_calc_duty(int period, int min_duty, int brightness) | ||
31 | { | ||
32 | return min_duty + (period - min_duty) * brightness / MAX_BRIGHTNESS_SETTING; | ||
33 | } | ||
34 | |||
35 | bool backlight_hw_init(void) | ||
36 | { | ||
37 | pwm_init(BL_LCD_CHN); | ||
38 | pwm_enable(BL_LCD_CHN); | ||
39 | backlight_hw_brightness(MAX_BRIGHTNESS_SETTING); | ||
40 | return true; | ||
41 | } | ||
42 | |||
43 | void backlight_hw_on(void) | ||
44 | { | ||
45 | pwm_enable(BL_LCD_CHN); | ||
46 | #ifdef HAVE_LCD_ENABLE | ||
47 | lcd_enable(true); | ||
48 | #endif | ||
49 | } | ||
50 | |||
51 | void backlight_hw_off(void) | ||
52 | { | ||
53 | pwm_disable(BL_LCD_CHN); | ||
54 | #ifdef HAVE_LCD_ENABLE | ||
55 | lcd_enable(false); | ||
56 | #endif | ||
57 | } | ||
58 | |||
59 | void backlight_hw_brightness(int brightness) | ||
60 | { | ||
61 | int duty_ns = backlight_calc_duty(BL_LCD_PERIOD, 0, brightness); | ||
62 | pwm_set_period(BL_LCD_CHN, BL_LCD_PERIOD, duty_ns); | ||
63 | } | ||
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/backlight-target.h b/firmware/target/mips/ingenic_x1000/shanlingq1/backlight-target.h new file mode 100644 index 0000000000..7298c1c06a --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/backlight-target.h | |||
@@ -0,0 +1,33 @@ | |||
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 | #endif /* __BACKLIGHT_TARGET_H__ */ | ||
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/boot.make b/firmware/target/mips/ingenic_x1000/shanlingq1/boot.make new file mode 100644 index 0000000000..639f570ea3 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/boot.make | |||
@@ -0,0 +1,31 @@ | |||
1 | # __________ __ ___. | ||
2 | # Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
3 | # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
4 | # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
5 | # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
6 | # \/ \/ \/ \/ \/ | ||
7 | # $Id$ | ||
8 | # | ||
9 | |||
10 | include $(ROOTDIR)/lib/microtar/microtar.make | ||
11 | |||
12 | .SECONDEXPANSION: | ||
13 | |||
14 | # FIXME(q1): verify NAND parameters | ||
15 | $(BUILDDIR)/spl.q1: $(BUILDDIR)/spl.bin | ||
16 | $(call PRINTS,MKSPL $(@F))$(TOOLSDIR)/mkspl-x1000 -type=nand -ppb=2 -bpp=2 $< $@ | ||
17 | |||
18 | $(BUILDDIR)/bootloader.ucl: $(BUILDDIR)/bootloader.bin | ||
19 | $(call PRINTS,UCLPACK $(@F))$(TOOLSDIR)/uclpack --nrv2e -9 $< $@ >/dev/null | ||
20 | |||
21 | .PHONY: $(BUILDDIR)/bootloader-info.txt | ||
22 | $(BUILDDIR)/bootloader-info.txt: | ||
23 | $(call PRINTS,GEN $(@F))echo $(SVNVERSION) > $@ | ||
24 | |||
25 | $(BUILDDIR)/$(BINARY): $(BUILDDIR)/spl.q1 \ | ||
26 | $(BUILDDIR)/bootloader.ucl \ | ||
27 | $(BUILDDIR)/bootloader-info.txt | ||
28 | $(call PRINTS,TAR $(@F))tar -C $(BUILDDIR) \ | ||
29 | --numeric-owner --no-acls --no-xattrs --no-selinux \ | ||
30 | --mode=0644 --owner=0 --group=0 \ | ||
31 | -cf $@ $(call full_path_subst,$(BUILDDIR)/%,%,$^) | ||
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c new file mode 100644 index 0000000000..27c49a7bd7 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.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 | * Copyright (C) 2021 Dana Conrad | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | |||
23 | #include "button.h" | ||
24 | #include "touchscreen.h" | ||
25 | #include "ft6x06.h" | ||
26 | #include "axp-pmu.h" | ||
27 | #include "kernel.h" | ||
28 | #include "backlight.h" | ||
29 | #include "powermgmt.h" | ||
30 | #include "gpio-x1000.h" | ||
31 | #include "irq-x1000.h" | ||
32 | #include "i2c-x1000.h" | ||
33 | #include <stdbool.h> | ||
34 | |||
35 | /* Volume wheel rotation */ | ||
36 | static volatile int wheel_pos = 0; | ||
37 | |||
38 | /* Value of headphone detect register */ | ||
39 | static uint8_t hp_detect_reg = 0x00; | ||
40 | |||
41 | /* Interval to poll the register */ | ||
42 | #define HPD_POLL_TIME (HZ/2) | ||
43 | |||
44 | static int hp_detect_tmo_cb(struct timeout* tmo) | ||
45 | { | ||
46 | i2c_descriptor* d = (i2c_descriptor*)tmo->data; | ||
47 | i2c_async_queue(AXP_PMU_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, d); | ||
48 | return HPD_POLL_TIME; | ||
49 | } | ||
50 | |||
51 | static void hp_detect_init(void) | ||
52 | { | ||
53 | /* TODO: replace this copy paste cruft with an API in axp-pmu */ | ||
54 | static struct timeout tmo; | ||
55 | static const uint8_t gpio_reg = AXP192_REG_GPIOSTATE1; | ||
56 | static i2c_descriptor desc = { | ||
57 | .slave_addr = AXP_PMU_ADDR, | ||
58 | .bus_cond = I2C_START | I2C_STOP, | ||
59 | .tran_mode = I2C_READ, | ||
60 | .buffer[0] = (void*)&gpio_reg, | ||
61 | .count[0] = 1, | ||
62 | .buffer[1] = &hp_detect_reg, | ||
63 | .count[1] = 1, | ||
64 | .callback = NULL, | ||
65 | .arg = 0, | ||
66 | .next = NULL, | ||
67 | }; | ||
68 | |||
69 | /* Headphone detect is wired to AXP192 GPIO: set it to input state */ | ||
70 | i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO1FUNCTION, 0x01); | ||
71 | |||
72 | /* Get an initial reading before startup */ | ||
73 | int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, gpio_reg); | ||
74 | if(r >= 0) | ||
75 | hp_detect_reg = r; | ||
76 | |||
77 | /* Poll the register every second */ | ||
78 | timeout_register(&tmo, &hp_detect_tmo_cb, HPD_POLL_TIME, (intptr_t)&desc); | ||
79 | } | ||
80 | |||
81 | void button_init_device(void) | ||
82 | { | ||
83 | /* Setup interrupts for the volume wheel */ | ||
84 | gpio_set_function(GPIO_WHEEL1, GPIOF_IRQ_EDGE(0)); | ||
85 | gpio_set_function(GPIO_WHEEL2, GPIOF_IRQ_EDGE(0)); | ||
86 | gpio_flip_edge_irq(GPIO_WHEEL1); | ||
87 | gpio_flip_edge_irq(GPIO_WHEEL2); | ||
88 | gpio_enable_irq(GPIO_WHEEL1); | ||
89 | gpio_enable_irq(GPIO_WHEEL2); | ||
90 | |||
91 | /* Init touchscreen driver */ | ||
92 | i2c_x1000_set_freq(FT6x06_BUS, I2C_FREQ_400K); | ||
93 | ft6x06_init(); | ||
94 | |||
95 | /* Reset touch controller */ | ||
96 | gpio_set_level(GPIO_FT6x06_POWER, 1); | ||
97 | gpio_set_level(GPIO_FT6x06_RESET, 0); | ||
98 | mdelay(5); | ||
99 | gpio_set_level(GPIO_FT6x06_RESET, 1); | ||
100 | |||
101 | /* Enable ft6x06 interrupt */ | ||
102 | system_set_irq_handler(GPIO_TO_IRQ(GPIO_FT6x06_INTERRUPT), ft6x06_irq_handler); | ||
103 | gpio_set_function(GPIO_FT6x06_INTERRUPT, GPIOF_IRQ_EDGE(0)); | ||
104 | gpio_enable_irq(GPIO_FT6x06_INTERRUPT); | ||
105 | |||
106 | /* Headphone detection */ | ||
107 | hp_detect_init(); | ||
108 | } | ||
109 | |||
110 | int button_read_device(int* data) | ||
111 | { | ||
112 | int r = 0; | ||
113 | |||
114 | /* Read GPIO buttons, these are all active low */ | ||
115 | uint32_t b = REG_GPIO_PIN(GPIO_B); | ||
116 | if((b & (1 << 21)) == 0) r |= BUTTON_PREV; | ||
117 | if((b & (1 << 22)) == 0) r |= BUTTON_NEXT; | ||
118 | if((b & (1 << 28)) == 0) r |= BUTTON_PLAY; | ||
119 | if((b & (1 << 31)) == 0) r |= BUTTON_POWER; | ||
120 | |||
121 | /* Check the wheel */ | ||
122 | int wheel_btn = 0; | ||
123 | int whpos = wheel_pos; | ||
124 | if(whpos > 3) | ||
125 | wheel_btn = BUTTON_VOL_DOWN; | ||
126 | else if(whpos < -3) | ||
127 | wheel_btn = BUTTON_VOL_UP; | ||
128 | |||
129 | if(wheel_btn) { | ||
130 | wheel_pos = 0; | ||
131 | |||
132 | /* Post the event (rapid motion is more reliable this way) */ | ||
133 | queue_post(&button_queue, wheel_btn, 0); | ||
134 | queue_post(&button_queue, wheel_btn|BUTTON_REL, 0); | ||
135 | |||
136 | /* Poke the backlight */ | ||
137 | backlight_on(); | ||
138 | reset_poweroff_timer(); | ||
139 | } | ||
140 | |||
141 | /* Handle touchscreen | ||
142 | * | ||
143 | * TODO: Support 2-point multitouch (useful for 3x3 grid mode) | ||
144 | * TODO: Support simple gestures by converting them to fake buttons | ||
145 | */ | ||
146 | int t = touchscreen_to_pixels(ft6x06_state.pos_x, ft6x06_state.pos_y, data); | ||
147 | if(ft6x06_state.event == FT6x06_EVT_PRESS || | ||
148 | ft6x06_state.event == FT6x06_EVT_CONTACT) { | ||
149 | /* Only set the button bit if the screen is being touched. */ | ||
150 | r |= t; | ||
151 | } | ||
152 | |||
153 | return r; | ||
154 | } | ||
155 | |||
156 | void touchscreen_enable_device(bool en) | ||
157 | { | ||
158 | ft6x06_enable(en); | ||
159 | /* TODO: check if it's worth shutting off the power pin */ | ||
160 | } | ||
161 | |||
162 | bool headphones_inserted(void) | ||
163 | { | ||
164 | /* TODO: Also check if the headset button is detectable via an ADC. | ||
165 | * The AXP driver should probably get proper interrupt handling, | ||
166 | * that would be useful for more things than just GPIO polling. */ | ||
167 | return hp_detect_reg & 0x20 ? true : false; | ||
168 | } | ||
169 | |||
170 | static void handle_wheel_irq(void) | ||
171 | { | ||
172 | /* Wheel stuff adapted from button-erosqnative.c */ | ||
173 | static const int delta[16] = { 0, -1, 1, 0, | ||
174 | 1, 0, 0, -1, | ||
175 | -1, 0, 0, 1, | ||
176 | 0, 1, -1, 0 }; | ||
177 | static uint32_t state = 0; | ||
178 | state <<= 2; | ||
179 | state |= (REG_GPIO_PIN(GPIO_D) >> 2) & 3; | ||
180 | state &= 0xf; | ||
181 | |||
182 | wheel_pos += delta[state]; | ||
183 | } | ||
184 | |||
185 | void GPIOD02(void) | ||
186 | { | ||
187 | handle_wheel_irq(); | ||
188 | gpio_flip_edge_irq(GPIO_WHEEL1); | ||
189 | } | ||
190 | |||
191 | void GPIOD03(void) | ||
192 | { | ||
193 | handle_wheel_irq(); | ||
194 | gpio_flip_edge_irq(GPIO_WHEEL2); | ||
195 | } | ||
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/button-target.h b/firmware/target/mips/ingenic_x1000/shanlingq1/button-target.h new file mode 100644 index 0000000000..905d148afa --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/button-target.h | |||
@@ -0,0 +1,56 @@ | |||
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 | /* physical buttons */ | ||
28 | #define BUTTON_POWER 0x00000001 | ||
29 | #define BUTTON_VOL_UP 0x00000002 /* up = wheel clockwise */ | ||
30 | #define BUTTON_VOL_DOWN 0x00000004 | ||
31 | #define BUTTON_PLAY 0x00000008 /* circle */ | ||
32 | #define BUTTON_NEXT 0x00000010 /* down */ | ||
33 | #define BUTTON_PREV 0x00000020 /* up */ | ||
34 | |||
35 | /* compatibility hacks */ | ||
36 | #define BUTTON_LEFT BUTTON_MIDLEFT | ||
37 | #define BUTTON_RIGHT BUTTON_MIDRIGHT | ||
38 | |||
39 | /* touchscreen "buttons" */ | ||
40 | #define BUTTON_TOPLEFT 0x00000040 | ||
41 | #define BUTTON_TOPMIDDLE 0x00000080 | ||
42 | #define BUTTON_TOPRIGHT 0x00000100 | ||
43 | #define BUTTON_MIDLEFT 0x00000200 | ||
44 | #define BUTTON_CENTER 0x00000400 | ||
45 | #define BUTTON_MIDRIGHT 0x00000800 | ||
46 | #define BUTTON_BOTTOMLEFT 0x00001000 | ||
47 | #define BUTTON_BOTTOMMIDDLE 0x00002000 | ||
48 | #define BUTTON_BOTTOMRIGHT 0x00004000 | ||
49 | |||
50 | #define BUTTON_MAIN (BUTTON_POWER|BUTTON_VOL_UP|BUTTON_VOL_DOWN|\ | ||
51 | BUTTON_PLAY|BUTTON_NEXT|BUTTON_PREV) | ||
52 | |||
53 | #define POWEROFF_BUTTON BUTTON_POWER | ||
54 | #define POWEROFF_COUNT 30 | ||
55 | |||
56 | #endif /* __BUTTON_TARGET_H__ */ | ||
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/gpio-target.h b/firmware/target/mips/ingenic_x1000/shanlingq1/gpio-target.h new file mode 100644 index 0000000000..7c71d12888 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/gpio-target.h | |||
@@ -0,0 +1,32 @@ | |||
1 | /* Name Port Pins Function */ | ||
2 | DEFINE_PINGROUP(LCD_DATA, GPIO_A, 0xffff << 0, GPIOF_DEVICE(1)) | ||
3 | DEFINE_PINGROUP(LCD_CONTROL, GPIO_B, 0x1a << 16, GPIOF_DEVICE(1)) | ||
4 | DEFINE_PINGROUP(MSC0, GPIO_A, 0x3f << 20, GPIOF_DEVICE(1)) | ||
5 | DEFINE_PINGROUP(SFC, GPIO_A, 0x3f << 26, GPIOF_DEVICE(1)) | ||
6 | DEFINE_PINGROUP(I2S, GPIO_B, 0x1f << 0, GPIOF_DEVICE(1)) | ||
7 | DEFINE_PINGROUP(I2C0, GPIO_B, 3 << 23, GPIOF_DEVICE(0)) | ||
8 | DEFINE_PINGROUP(I2C1, GPIO_C, 3 << 26, GPIOF_DEVICE(0)) | ||
9 | DEFINE_PINGROUP(I2C2, GPIO_D, 3 << 0, GPIOF_DEVICE(1)) | ||
10 | |||
11 | /* Name Pin Function */ | ||
12 | DEFINE_GPIO(FT6x06_INTERRUPT, GPIO_PA(16), GPIOF_INPUT) | ||
13 | DEFINE_GPIO(USB_DETECT, GPIO_PA(17), GPIOF_INPUT) | ||
14 | DEFINE_GPIO(FT6x06_RESET, GPIO_PA(19), GPIOF_OUTPUT(0)) | ||
15 | DEFINE_GPIO(LCD_PWR, GPIO_PB(6), GPIOF_OUTPUT(1)) | ||
16 | DEFINE_GPIO(FT6x06_POWER, GPIO_PB(8), GPIOF_OUTPUT(0)) | ||
17 | DEFINE_GPIO(MSC0_CD, GPIO_PB(9), GPIOF_INPUT) | ||
18 | DEFINE_GPIO(ES9218_POWER, GPIO_PB(13), GPIOF_OUTPUT(0)) | ||
19 | DEFINE_GPIO(LCD_RST, GPIO_PB(15), GPIOF_OUTPUT(1)) | ||
20 | DEFINE_GPIO(LCD_RD, GPIO_PB(16), GPIOF_OUTPUT(1)) | ||
21 | DEFINE_GPIO(LCD_CE, GPIO_PB(18), GPIOF_OUTPUT(1)) | ||
22 | DEFINE_GPIO(BTN_PREV, GPIO_PB(21), GPIOF_INPUT) | ||
23 | DEFINE_GPIO(BTN_NEXT, GPIO_PB(22), GPIOF_INPUT) | ||
24 | DEFINE_GPIO(USB_DRVVBUS, GPIO_PB(25), GPIOF_OUTPUT(0)) | ||
25 | DEFINE_GPIO(BTN_PLAY, GPIO_PB(28), GPIOF_INPUT) | ||
26 | DEFINE_GPIO(BTN_POWER, GPIO_PB(31), GPIOF_INPUT) | ||
27 | DEFINE_GPIO(AXP_IRQ, GPIO_PC(21), GPIOF_INPUT) | ||
28 | DEFINE_GPIO(USB_ID, GPIO_PC(23), GPIOF_INPUT) | ||
29 | DEFINE_GPIO(WHEEL1, GPIO_PD(2), GPIOF_INPUT) | ||
30 | DEFINE_GPIO(WHEEL2, GPIO_PD(3), GPIOF_INPUT) | ||
31 | DEFINE_GPIO(ES9218_GPIO2, GPIO_PD(4), GPIOF_OUTPUT(0)) | ||
32 | DEFINE_GPIO(ES9218_RESET, GPIO_PD(5), GPIOF_OUTPUT(0)) | ||
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/i2c-target.h b/firmware/target/mips/ingenic_x1000/shanlingq1/i2c-target.h new file mode 100644 index 0000000000..af19aeb28c --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/i2c-target.h | |||
@@ -0,0 +1,40 @@ | |||
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 FT6x06_BUS 0 | ||
29 | #define FT6x06_ADDR 0x38 | ||
30 | |||
31 | #define ES9218_BUS 1 | ||
32 | #define ES9218_ADDR 0x48 | ||
33 | |||
34 | #define AXP_PMU_BUS 2 | ||
35 | #define AXP_PMU_ADDR 0x34 | ||
36 | |||
37 | #define CW2015_BUS 2 | ||
38 | #define CW2015_ADDR 0x62 | ||
39 | |||
40 | #endif /* __I2C_TARGET_H__ */ | ||
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/lcd-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/lcd-shanlingq1.c new file mode 100644 index 0000000000..532a149185 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/lcd-shanlingq1.c | |||
@@ -0,0 +1,399 @@ | |||
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 "system.h" | ||
24 | #include "lcd-x1000.h" | ||
25 | #include "gpio-x1000.h" | ||
26 | |||
27 | /* LCD controller is probably an RM68090. | ||
28 | */ | ||
29 | |||
30 | static const uint32_t q1_lcd_cmd_enable[] = { | ||
31 | LCD_INSTR_CMD, 0x00, | ||
32 | LCD_INSTR_CMD, 0xbe, | ||
33 | LCD_INSTR_DAT, 0xc3, | ||
34 | LCD_INSTR_DAT, 0x29, | ||
35 | |||
36 | LCD_INSTR_CMD, 0x00, | ||
37 | LCD_INSTR_CMD, 0x01, | ||
38 | LCD_INSTR_DAT, 0x01, | ||
39 | LCD_INSTR_DAT, 0x04, | ||
40 | |||
41 | LCD_INSTR_CMD, 0x00, | ||
42 | LCD_INSTR_CMD, 0x02, | ||
43 | LCD_INSTR_DAT, 0x01, | ||
44 | LCD_INSTR_DAT, 0x00, | ||
45 | |||
46 | LCD_INSTR_CMD, 0x00, | ||
47 | LCD_INSTR_CMD, 0x03, | ||
48 | LCD_INSTR_DAT, 0x00, | ||
49 | LCD_INSTR_DAT, 0x10, | ||
50 | |||
51 | LCD_INSTR_CMD, 0x00, | ||
52 | LCD_INSTR_CMD, 0x05, | ||
53 | LCD_INSTR_DAT, 0x00, | ||
54 | LCD_INSTR_DAT, 0x00, | ||
55 | |||
56 | LCD_INSTR_CMD, 0x00, | ||
57 | LCD_INSTR_CMD, 0x06, | ||
58 | LCD_INSTR_DAT, 0x00, | ||
59 | LCD_INSTR_DAT, 0x00, | ||
60 | |||
61 | LCD_INSTR_CMD, 0x00, | ||
62 | LCD_INSTR_CMD, 0x07, | ||
63 | LCD_INSTR_DAT, 0x01, | ||
64 | LCD_INSTR_DAT, 0x03, | ||
65 | |||
66 | LCD_INSTR_CMD, 0x00, | ||
67 | LCD_INSTR_CMD, 0x08, | ||
68 | LCD_INSTR_DAT, 0x03, | ||
69 | LCD_INSTR_DAT, 0x03, | ||
70 | |||
71 | LCD_INSTR_CMD, 0x00, | ||
72 | LCD_INSTR_CMD, 0x0d, | ||
73 | LCD_INSTR_DAT, 0x00, | ||
74 | LCD_INSTR_DAT, 0x00, | ||
75 | |||
76 | LCD_INSTR_CMD, 0x00, | ||
77 | LCD_INSTR_CMD, 0x10, | ||
78 | LCD_INSTR_DAT, 0x00, | ||
79 | LCD_INSTR_DAT, 0xc1, | ||
80 | |||
81 | LCD_INSTR_CMD, 0x00, | ||
82 | LCD_INSTR_CMD, 0x11, | ||
83 | LCD_INSTR_DAT, 0xb1, | ||
84 | LCD_INSTR_DAT, 0x08, | ||
85 | |||
86 | LCD_INSTR_CMD, 0x00, | ||
87 | LCD_INSTR_CMD, 0x12, | ||
88 | LCD_INSTR_DAT, 0xb1, | ||
89 | LCD_INSTR_DAT, 0x08, | ||
90 | |||
91 | LCD_INSTR_CMD, 0x00, | ||
92 | LCD_INSTR_CMD, 0x13, | ||
93 | LCD_INSTR_DAT, 0x00, | ||
94 | LCD_INSTR_DAT, 0x0f, | ||
95 | |||
96 | LCD_INSTR_CMD, 0x00, | ||
97 | LCD_INSTR_CMD, 0x14, | ||
98 | LCD_INSTR_DAT, 0x00, | ||
99 | LCD_INSTR_DAT, 0x14, | ||
100 | |||
101 | LCD_INSTR_CMD, 0x00, | ||
102 | LCD_INSTR_CMD, 0x15, | ||
103 | LCD_INSTR_DAT, 0x00, | ||
104 | LCD_INSTR_DAT, 0x04, | ||
105 | |||
106 | LCD_INSTR_CMD, 0x00, | ||
107 | LCD_INSTR_CMD, 0x16, | ||
108 | LCD_INSTR_DAT, 0x00, | ||
109 | LCD_INSTR_DAT, 0x00, | ||
110 | |||
111 | LCD_INSTR_CMD, 0x00, | ||
112 | LCD_INSTR_CMD, 0x22, | ||
113 | LCD_INSTR_DAT, 0x00, | ||
114 | LCD_INSTR_DAT, 0x00, | ||
115 | |||
116 | LCD_INSTR_CMD, 0x00, | ||
117 | LCD_INSTR_CMD, 0x23, | ||
118 | LCD_INSTR_DAT, 0x00, | ||
119 | LCD_INSTR_DAT, 0x00, | ||
120 | |||
121 | LCD_INSTR_CMD, 0x00, | ||
122 | LCD_INSTR_CMD, 0x30, | ||
123 | LCD_INSTR_DAT, 0x7c, | ||
124 | LCD_INSTR_DAT, 0x3f, | ||
125 | |||
126 | LCD_INSTR_CMD, 0x00, | ||
127 | LCD_INSTR_CMD, 0x32, | ||
128 | LCD_INSTR_DAT, 0x00, | ||
129 | LCD_INSTR_DAT, 0x00, | ||
130 | |||
131 | LCD_INSTR_CMD, 0x00, | ||
132 | LCD_INSTR_CMD, 0x70, | ||
133 | LCD_INSTR_DAT, 0x00, | ||
134 | LCD_INSTR_DAT, 0x01, | ||
135 | |||
136 | LCD_INSTR_CMD, 0x00, | ||
137 | LCD_INSTR_CMD, 0x91, | ||
138 | LCD_INSTR_DAT, 0x01, | ||
139 | LCD_INSTR_DAT, 0x00, | ||
140 | |||
141 | LCD_INSTR_CMD, 0x00, | ||
142 | LCD_INSTR_CMD, 0xe0, | ||
143 | LCD_INSTR_DAT, 0x00, | ||
144 | LCD_INSTR_DAT, 0x01, | ||
145 | |||
146 | LCD_INSTR_CMD, 0x00, | ||
147 | LCD_INSTR_CMD, 0xe1, | ||
148 | LCD_INSTR_DAT, 0x00, | ||
149 | LCD_INSTR_DAT, 0x61, | ||
150 | |||
151 | LCD_INSTR_CMD, 0x01, | ||
152 | LCD_INSTR_CMD, 0x00, | ||
153 | LCD_INSTR_DAT, 0x10, | ||
154 | LCD_INSTR_DAT, 0x30, | ||
155 | |||
156 | LCD_INSTR_CMD, 0x01, | ||
157 | LCD_INSTR_CMD, 0x01, | ||
158 | LCD_INSTR_DAT, 0xf6, | ||
159 | LCD_INSTR_DAT, 0x3f, | ||
160 | |||
161 | LCD_INSTR_CMD, 0x01, | ||
162 | LCD_INSTR_CMD, 0x02, | ||
163 | LCD_INSTR_DAT, 0x50, | ||
164 | LCD_INSTR_DAT, 0x1f, | ||
165 | |||
166 | LCD_INSTR_CMD, 0x01, | ||
167 | LCD_INSTR_CMD, 0x03, | ||
168 | LCD_INSTR_DAT, 0x00, | ||
169 | LCD_INSTR_DAT, 0x30, | ||
170 | |||
171 | LCD_INSTR_CMD, 0x01, | ||
172 | LCD_INSTR_CMD, 0x08, | ||
173 | LCD_INSTR_DAT, 0x03, | ||
174 | LCD_INSTR_DAT, 0x00, | ||
175 | |||
176 | LCD_INSTR_CMD, 0x01, | ||
177 | LCD_INSTR_CMD, 0x11, | ||
178 | LCD_INSTR_DAT, 0x00, | ||
179 | LCD_INSTR_DAT, 0x01, | ||
180 | |||
181 | LCD_INSTR_CMD, 0x01, | ||
182 | LCD_INSTR_CMD, 0x35, | ||
183 | LCD_INSTR_DAT, 0x76, | ||
184 | LCD_INSTR_DAT, 0x66, | ||
185 | |||
186 | LCD_INSTR_CMD, 0x01, | ||
187 | LCD_INSTR_CMD, 0x39, | ||
188 | LCD_INSTR_DAT, 0x00, | ||
189 | LCD_INSTR_DAT, 0x26, | ||
190 | |||
191 | LCD_INSTR_CMD, 0x04, | ||
192 | LCD_INSTR_CMD, 0x00, | ||
193 | LCD_INSTR_DAT, 0x00, | ||
194 | LCD_INSTR_DAT, 0xc7, | ||
195 | |||
196 | LCD_INSTR_CMD, 0x04, | ||
197 | LCD_INSTR_CMD, 0x01, | ||
198 | LCD_INSTR_DAT, 0x00, | ||
199 | LCD_INSTR_DAT, 0x00, | ||
200 | |||
201 | LCD_INSTR_CMD, 0x06, | ||
202 | LCD_INSTR_CMD, 0x06, | ||
203 | LCD_INSTR_DAT, 0x00, | ||
204 | LCD_INSTR_DAT, 0x00, | ||
205 | |||
206 | LCD_INSTR_CMD, 0x03, | ||
207 | LCD_INSTR_CMD, 0x00, | ||
208 | LCD_INSTR_DAT, 0x0d, | ||
209 | LCD_INSTR_DAT, 0x0e, | ||
210 | |||
211 | LCD_INSTR_CMD, 0x03, | ||
212 | LCD_INSTR_CMD, 0x01, | ||
213 | LCD_INSTR_DAT, 0x00, | ||
214 | LCD_INSTR_DAT, 0x03, | ||
215 | |||
216 | LCD_INSTR_CMD, 0x03, | ||
217 | LCD_INSTR_CMD, 0x02, | ||
218 | LCD_INSTR_DAT, 0x08, | ||
219 | LCD_INSTR_DAT, 0x08, | ||
220 | |||
221 | LCD_INSTR_CMD, 0x03, | ||
222 | LCD_INSTR_CMD, 0x03, | ||
223 | LCD_INSTR_DAT, 0x02, | ||
224 | LCD_INSTR_DAT, 0x01, | ||
225 | |||
226 | LCD_INSTR_CMD, 0x03, | ||
227 | LCD_INSTR_CMD, 0x04, | ||
228 | LCD_INSTR_DAT, 0x03, | ||
229 | LCD_INSTR_DAT, 0x01, | ||
230 | |||
231 | LCD_INSTR_CMD, 0x03, | ||
232 | LCD_INSTR_CMD, 0x05, | ||
233 | LCD_INSTR_DAT, 0x00, | ||
234 | LCD_INSTR_DAT, 0x04, | ||
235 | |||
236 | LCD_INSTR_CMD, 0x03, | ||
237 | LCD_INSTR_CMD, 0x06, | ||
238 | LCD_INSTR_DAT, 0x1b, | ||
239 | LCD_INSTR_DAT, 0x21, | ||
240 | |||
241 | LCD_INSTR_CMD, 0x03, | ||
242 | LCD_INSTR_CMD, 0x07, | ||
243 | LCD_INSTR_DAT, 0x0f, | ||
244 | LCD_INSTR_DAT, 0x0e, | ||
245 | |||
246 | LCD_INSTR_CMD, 0x03, | ||
247 | LCD_INSTR_CMD, 0x08, | ||
248 | LCD_INSTR_DAT, 0x01, | ||
249 | LCD_INSTR_DAT, 0x04, | ||
250 | |||
251 | LCD_INSTR_CMD, 0x03, | ||
252 | LCD_INSTR_CMD, 0x09, | ||
253 | LCD_INSTR_DAT, 0x08, | ||
254 | LCD_INSTR_DAT, 0x08, | ||
255 | |||
256 | LCD_INSTR_CMD, 0x03, | ||
257 | LCD_INSTR_CMD, 0x0a, | ||
258 | LCD_INSTR_DAT, 0x02, | ||
259 | LCD_INSTR_DAT, 0x01, | ||
260 | |||
261 | LCD_INSTR_CMD, 0x03, | ||
262 | LCD_INSTR_CMD, 0x0b, | ||
263 | LCD_INSTR_DAT, 0x03, | ||
264 | LCD_INSTR_DAT, 0x01, | ||
265 | |||
266 | LCD_INSTR_CMD, 0x03, | ||
267 | LCD_INSTR_CMD, 0x0c, | ||
268 | LCD_INSTR_DAT, 0x00, | ||
269 | LCD_INSTR_DAT, 0x03, | ||
270 | |||
271 | LCD_INSTR_CMD, 0x03, | ||
272 | LCD_INSTR_CMD, 0x0d, | ||
273 | LCD_INSTR_DAT, 0x31, | ||
274 | LCD_INSTR_DAT, 0x34, | ||
275 | |||
276 | /* X start */ | ||
277 | LCD_INSTR_CMD, 0x02, | ||
278 | LCD_INSTR_CMD, 0x10, | ||
279 | LCD_INSTR_DAT, 0x00, | ||
280 | LCD_INSTR_DAT, 0x1e, /* 30 */ | ||
281 | |||
282 | /* X end */ | ||
283 | LCD_INSTR_CMD, 0x02, | ||
284 | LCD_INSTR_CMD, 0x11, | ||
285 | LCD_INSTR_DAT, 0x01, | ||
286 | LCD_INSTR_DAT, 0x85, /* 389 */ | ||
287 | |||
288 | /* Y start */ | ||
289 | LCD_INSTR_CMD, 0x02, | ||
290 | LCD_INSTR_CMD, 0x12, | ||
291 | LCD_INSTR_DAT, 0x00, | ||
292 | LCD_INSTR_DAT, 0x00, /* 0 */ | ||
293 | |||
294 | /* Y end */ | ||
295 | LCD_INSTR_CMD, 0x02, | ||
296 | LCD_INSTR_CMD, 0x13, | ||
297 | LCD_INSTR_DAT, 0x01, | ||
298 | LCD_INSTR_DAT, 0x8f, /* 399 */ | ||
299 | |||
300 | /* RAM write start X? */ | ||
301 | LCD_INSTR_CMD, 0x02, | ||
302 | LCD_INSTR_CMD, 0x00, | ||
303 | LCD_INSTR_DAT, 0x00, | ||
304 | LCD_INSTR_DAT, 0x1e, | ||
305 | |||
306 | /* RAM write start Y? */ | ||
307 | LCD_INSTR_CMD, 0x02, | ||
308 | LCD_INSTR_CMD, 0x01, | ||
309 | LCD_INSTR_DAT, 0x00, | ||
310 | LCD_INSTR_DAT, 0x00, | ||
311 | |||
312 | LCD_INSTR_CMD, 0x00, | ||
313 | LCD_INSTR_CMD, 0x03, | ||
314 | LCD_INSTR_DAT, 0x00, | ||
315 | LCD_INSTR_DAT, 0x30, | ||
316 | |||
317 | LCD_INSTR_CMD, 0x02, | ||
318 | LCD_INSTR_CMD, 0x02, | ||
319 | LCD_INSTR_END, | ||
320 | }; | ||
321 | |||
322 | /* NOTE this sleep mode may not be saving power, but it gets rid of the | ||
323 | * ghost image that would otherwise remain on the display */ | ||
324 | static const uint32_t q1_lcd_cmd_sleep[] = { | ||
325 | LCD_INSTR_CMD, 0x00, | ||
326 | LCD_INSTR_CMD, 0x10, | ||
327 | LCD_INSTR_DAT, 0x00, | ||
328 | LCD_INSTR_DAT, 0x03, | ||
329 | |||
330 | LCD_INSTR_CMD, 0x00, | ||
331 | LCD_INSTR_CMD, 0x07, | ||
332 | LCD_INSTR_DAT, 0x01, | ||
333 | LCD_INSTR_DAT, 0x01, | ||
334 | |||
335 | LCD_INSTR_END, | ||
336 | }; | ||
337 | |||
338 | static const uint32_t q1_lcd_cmd_wake[] = { | ||
339 | LCD_INSTR_CMD, 0x00, | ||
340 | LCD_INSTR_CMD, 0x07, | ||
341 | LCD_INSTR_DAT, 0x01, | ||
342 | LCD_INSTR_DAT, 0x03, | ||
343 | |||
344 | LCD_INSTR_CMD, 0x00, | ||
345 | LCD_INSTR_CMD, 0x10, | ||
346 | LCD_INSTR_DAT, 0x00, | ||
347 | LCD_INSTR_DAT, 0xc1, | ||
348 | |||
349 | LCD_INSTR_END, | ||
350 | }; | ||
351 | |||
352 | static const uint8_t __attribute__((aligned(64))) | ||
353 | q1_lcd_dma_wr_cmd[] = {0x02, 0x02, 0x02, 0x02}; | ||
354 | |||
355 | const struct lcd_tgt_config lcd_tgt_config = { | ||
356 | .bus_width = 8, | ||
357 | .cmd_width = 8, | ||
358 | .use_6800_mode = 0, | ||
359 | .use_serial = 0, | ||
360 | .clk_polarity = 0, | ||
361 | .dc_polarity = 0, | ||
362 | .wr_polarity = 1, | ||
363 | .te_enable = 0, | ||
364 | .big_endian = 1, | ||
365 | .dma_wr_cmd_buf = &q1_lcd_dma_wr_cmd, | ||
366 | .dma_wr_cmd_size = sizeof(q1_lcd_dma_wr_cmd), | ||
367 | }; | ||
368 | |||
369 | void lcd_tgt_enable(bool enable) | ||
370 | { | ||
371 | if(enable) { | ||
372 | /* power on the panel */ | ||
373 | gpio_set_level(GPIO_LCD_PWR, 1); | ||
374 | gpio_set_level(GPIO_LCD_RST, 1); | ||
375 | gpio_set_level(GPIO_LCD_CE, 1); | ||
376 | gpio_set_level(GPIO_LCD_RD, 1); | ||
377 | mdelay(50); | ||
378 | gpio_set_level(GPIO_LCD_RST, 0); | ||
379 | mdelay(100); | ||
380 | gpio_set_level(GPIO_LCD_RST, 1); | ||
381 | mdelay(50); | ||
382 | gpio_set_level(GPIO_LCD_CE, 0); | ||
383 | |||
384 | /* Start the controller */ | ||
385 | lcd_set_clock(X1000_CLK_MPLL, 50000000); | ||
386 | lcd_exec_commands(q1_lcd_cmd_enable); | ||
387 | } else { | ||
388 | /* FIXME: Shanling Q1 LCD power down sequence | ||
389 | * not important because we don't use it but it'd be nice to know */ | ||
390 | } | ||
391 | } | ||
392 | |||
393 | void lcd_tgt_sleep(bool sleep) | ||
394 | { | ||
395 | if(sleep) | ||
396 | lcd_exec_commands(q1_lcd_cmd_sleep); | ||
397 | else | ||
398 | lcd_exec_commands(q1_lcd_cmd_wake); | ||
399 | } | ||
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c new file mode 100644 index 0000000000..17fbe1cede --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c | |||
@@ -0,0 +1,140 @@ | |||
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 "axp-pmu.h" | ||
26 | #ifdef HAVE_CW2015 | ||
27 | # include "cw2015.h" | ||
28 | #endif | ||
29 | #ifdef HAVE_USB_CHARGING_ENABLE | ||
30 | # include "usb_core.h" | ||
31 | #endif | ||
32 | |||
33 | #include "i2c-x1000.h" | ||
34 | |||
35 | /* TODO: Better(?) battery reporting for Q1 using CW2015 driver | ||
36 | * | ||
37 | * The CW2015 has its own quirks so the driver has to be more complicated | ||
38 | * than "read stuff from I2C," unfortunately. Without fixing the quirks it | ||
39 | * is probably worse than the simple voltage-based method. | ||
40 | * | ||
41 | * A bigger problem is that it shares an I2C bus with the AXP192, but when | ||
42 | * we attempt to communicate with both chips, they start returning bogus | ||
43 | * data intermittently. Ususally, reads will return 0 but sometimes they | ||
44 | * can return other nonzero bogus data. It could be that one or the other is | ||
45 | * pulling the bus line down inappropriately, or maybe the hardware does not | ||
46 | * respect the bus free time between start/stop conditions and one of the | ||
47 | * devices is getting confused. | ||
48 | */ | ||
49 | |||
50 | const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = | ||
51 | { | ||
52 | 3470 | ||
53 | }; | ||
54 | |||
55 | /* the OF shuts down at this voltage */ | ||
56 | const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] = | ||
57 | { | ||
58 | 3400 | ||
59 | }; | ||
60 | |||
61 | /* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */ | ||
62 | const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] = | ||
63 | { | ||
64 | { 3400, 3639, 3697, 3723, 3757, 3786, 3836, 3906, 3980, 4050, 4159 } | ||
65 | }; | ||
66 | |||
67 | /* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */ | ||
68 | const unsigned short percent_to_volt_charge[11] = | ||
69 | { | ||
70 | 3485, 3780, 3836, 3857, 3890, 3930, 3986, 4062, 4158, 4185, 4196 | ||
71 | }; | ||
72 | |||
73 | void power_init(void) | ||
74 | { | ||
75 | i2c_x1000_set_freq(AXP_PMU_BUS, I2C_FREQ_400K); | ||
76 | axp_init(); | ||
77 | #ifdef HAVE_CW2015 | ||
78 | cw2015_init(); | ||
79 | #endif | ||
80 | |||
81 | /* Change supply voltage from the default of 1250 mV to 1200 mV, | ||
82 | * this matches the original firmware's settings. Didn't observe | ||
83 | * any obviously bad behavior at 1250 mV, but better to be safe. */ | ||
84 | axp_supply_set_voltage(AXP_SUPPLY_DCDC2, 1200); | ||
85 | |||
86 | /* For now, just turn everything on... definitely the touchscreen | ||
87 | * is powered by one of the outputs */ | ||
88 | i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, | ||
89 | AXP_REG_PWROUTPUTCTRL1, 0, 0x05, NULL); | ||
90 | i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, | ||
91 | AXP_REG_PWROUTPUTCTRL2, 0, 0x0f, NULL); | ||
92 | i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, | ||
93 | AXP_REG_DCDCWORKINGMODE, 0, 0xc0, NULL); | ||
94 | |||
95 | /* Delay to give power output time to stabilize */ | ||
96 | mdelay(20); | ||
97 | } | ||
98 | |||
99 | #ifdef HAVE_USB_CHARGING_ENABLE | ||
100 | void usb_charging_maxcurrent_change(int maxcurrent) | ||
101 | { | ||
102 | axp_set_charge_current(maxcurrent); | ||
103 | } | ||
104 | #endif | ||
105 | |||
106 | void power_off(void) | ||
107 | { | ||
108 | axp_power_off(); | ||
109 | while(1); | ||
110 | } | ||
111 | |||
112 | bool charging_state(void) | ||
113 | { | ||
114 | return axp_battery_status() == AXP_BATT_CHARGING; | ||
115 | } | ||
116 | |||
117 | int _battery_voltage(void) | ||
118 | { | ||
119 | /* CW2015 can also read battery voltage, but the AXP consistently | ||
120 | * reads ~20-30 mV higher so I suspect it's the "real" voltage. */ | ||
121 | return axp_adc_read(ADC_BATTERY_VOLTAGE); | ||
122 | } | ||
123 | |||
124 | #if defined(HAVE_CW2015) && (CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE) != 0 | ||
125 | int _battery_level(void) | ||
126 | { | ||
127 | return cw2015_get_soc(); | ||
128 | } | ||
129 | #endif | ||
130 | |||
131 | #if defined(HAVE_CW2015) && (CONFIG_BATTERY_MEASURE & TIME_MEASURE) != 0 | ||
132 | int _battery_time(void) | ||
133 | { | ||
134 | return cw2015_get_rrt(); | ||
135 | } | ||
136 | #endif | ||
137 | |||
138 | void adc_init(void) | ||
139 | { | ||
140 | } | ||
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/spl-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/spl-shanlingq1.c new file mode 100644 index 0000000000..33303c5e6b --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/spl-shanlingq1.c | |||
@@ -0,0 +1,116 @@ | |||
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 "system.h" | ||
23 | #include "clk-x1000.h" | ||
24 | #include "spl-x1000.h" | ||
25 | #include "gpio-x1000.h" | ||
26 | |||
27 | #define CMDLINE_COMMON \ | ||
28 | "mem=64M@0x0 no_console_suspend console=ttyS2,115200n8 lpj=5009408 ip=off" | ||
29 | #define CMDLINE_NORMAL \ | ||
30 | " init=/linuxrc ubi.mtd=5 root=ubi0:rootfs ubi.mtd=6 rootfstype=ubifs rw" | ||
31 | |||
32 | static int dualboot_setup(void) | ||
33 | { | ||
34 | spl_dualboot_init_clocktree(); | ||
35 | spl_dualboot_init_uart2(); | ||
36 | |||
37 | /* load PDMA MCU firmware */ | ||
38 | jz_writef(CPM_CLKGR, PDMA(0)); | ||
39 | return spl_storage_read(0x4000, 0x2000, (void*)0xb3422000); | ||
40 | } | ||
41 | |||
42 | const struct spl_boot_option spl_boot_options[] = { | ||
43 | [BOOT_OPTION_ROCKBOX] = { | ||
44 | .storage_addr = 0x6800, | ||
45 | .storage_size = 102 * 1024, | ||
46 | .load_addr = X1000_DRAM_BASE, | ||
47 | .exec_addr = X1000_DRAM_BASE, | ||
48 | .flags = BOOTFLAG_UCLPACK, | ||
49 | }, | ||
50 | [BOOT_OPTION_OFW_PLAYER] = { | ||
51 | .storage_addr = 0x140000, | ||
52 | .storage_size = 8 * 1024 * 1024, | ||
53 | .load_addr = 0x80efffc0, | ||
54 | .exec_addr = 0x80f00000, | ||
55 | .cmdline = CMDLINE_COMMON CMDLINE_NORMAL, | ||
56 | .cmdline_addr = 0x80004000, | ||
57 | .setup = dualboot_setup, | ||
58 | }, | ||
59 | [BOOT_OPTION_OFW_RECOVERY] = { | ||
60 | .storage_addr = 0x940000, | ||
61 | .storage_size = 10 * 1024 * 1024, | ||
62 | .load_addr = 0x80efffc0, | ||
63 | .exec_addr = 0x80f00000, | ||
64 | .cmdline = CMDLINE_COMMON, | ||
65 | .cmdline_addr = 0x80004000, | ||
66 | .setup = dualboot_setup, | ||
67 | }, | ||
68 | }; | ||
69 | |||
70 | int spl_get_boot_option(void) | ||
71 | { | ||
72 | /* Button debounce time in OST clock cycles */ | ||
73 | const uint32_t btn_stable_time = 100 * (X1000_EXCLK_FREQ / 4000); | ||
74 | |||
75 | /* Buttons to poll */ | ||
76 | const unsigned port = GPIO_B; | ||
77 | const uint32_t recov_pin = (1 << 22); /* Next */ | ||
78 | const uint32_t orig_fw_pin = (1 << 21); /* Prev */ | ||
79 | |||
80 | uint32_t pin = -1, lastpin = 0; | ||
81 | uint32_t deadline = 0; | ||
82 | int iter_count = 30; /* to avoid an infinite loop */ | ||
83 | |||
84 | /* set GPIOs to input state */ | ||
85 | gpioz_configure(port, recov_pin|orig_fw_pin, GPIOF_INPUT); | ||
86 | |||
87 | /* Poll until we get a stable reading */ | ||
88 | do { | ||
89 | lastpin = pin; | ||
90 | pin = ~REG_GPIO_PIN(port) & (recov_pin|orig_fw_pin); | ||
91 | if(pin != lastpin) { | ||
92 | deadline = __ost_read32() + btn_stable_time; | ||
93 | iter_count -= 1; | ||
94 | } | ||
95 | } while(iter_count > 0 && __ost_read32() < deadline); | ||
96 | |||
97 | if(iter_count >= 0 && (pin & orig_fw_pin)) { | ||
98 | if(pin & recov_pin) | ||
99 | return BOOT_OPTION_OFW_RECOVERY; | ||
100 | else | ||
101 | return BOOT_OPTION_OFW_PLAYER; | ||
102 | } | ||
103 | |||
104 | return BOOT_OPTION_ROCKBOX; | ||
105 | } | ||
106 | |||
107 | void spl_error(void) | ||
108 | { | ||
109 | /* Flash the backlight */ | ||
110 | int level = 0; | ||
111 | while(1) { | ||
112 | gpio_set_function(GPIO_PC(25), GPIOF_OUTPUT(level)); | ||
113 | mdelay(100); | ||
114 | level = 1 - level; | ||
115 | } | ||
116 | } | ||