summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmaury Pouly <amaury.pouly@gmail.com>2017-01-11 16:58:30 +0100
committerGerrit Rockbox <gerrit@rockbox.org>2017-01-16 20:08:13 +0100
commitb23b7088cbba364bd37a7ec5e6572f1ecf234e7a (patch)
tree38fd3014f7f667e0893245d3069e4ee41a9f18a4
parent759a78e5dff134f2632875f61aae60815eea6f5b (diff)
downloadrockbox-b23b7088cbba364bd37a7ec5e6572f1ecf234e7a.tar.gz
rockbox-b23b7088cbba364bd37a7ec5e6572f1ecf234e7a.zip
imx233: add small framework for LED
It handles GPIO and PWM based LEDs, possibly with several channels (red-green LED for example). The debug allows one to play with the setting. Currently the code supports the ZEN, ZEN X-Fi, and ZEN Mozaic. Change-Id: I8c3b66e6ba21778acdb123daabb724280a7d1a4f
-rw-r--r--firmware/SOURCES1
-rw-r--r--firmware/target/arm/imx233/debug-imx233.c134
-rw-r--r--firmware/target/arm/imx233/led-imx233.c100
-rw-r--r--firmware/target/arm/imx233/led-imx233.h69
-rw-r--r--firmware/target/arm/imx233/pwm-imx233.c11
-rw-r--r--firmware/target/arm/imx233/system-imx233.c2
6 files changed, 312 insertions, 5 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index ce445f35c9..d00e56f1a6 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -587,6 +587,7 @@ target/arm/imx233/debug-imx233.c
587#endif 587#endif
588#if !defined(BOOTLOADER) || defined(HAVE_BOOTLOADER_USB_MODE) 588#if !defined(BOOTLOADER) || defined(HAVE_BOOTLOADER_USB_MODE)
589target/arm/imx233/usb-imx233.c 589target/arm/imx233/usb-imx233.c
590target/arm/imx233/led-imx233.c
590#endif 591#endif
591#ifndef BOOTLOADER 592#ifndef BOOTLOADER
592#ifdef HAVE_IMX233_CODEC 593#ifdef HAVE_IMX233_CODEC
diff --git a/firmware/target/arm/imx233/debug-imx233.c b/firmware/target/arm/imx233/debug-imx233.c
index 808716916c..c5646a1c91 100644
--- a/firmware/target/arm/imx233/debug-imx233.c
+++ b/firmware/target/arm/imx233/debug-imx233.c
@@ -45,6 +45,7 @@
45#include "button.h" 45#include "button.h"
46#include "button-imx233.h" 46#include "button-imx233.h"
47#include "sdmmc-imx233.h" 47#include "sdmmc-imx233.h"
48#include "led-imx233.h"
48#include "storage.h" 49#include "storage.h"
49 50
50#include "regs/usbphy.h" 51#include "regs/usbphy.h"
@@ -792,6 +793,13 @@ bool dbg_hw_info_ocotp(void)
792 } 793 }
793} 794}
794 795
796static void get_pwm_freq_duty(int chan, int *freq, int *duty)
797{
798 struct imx233_pwm_info_t info = imx233_pwm_get_info(chan);
799 *freq = imx233_clkctrl_get_freq(CLK_XTAL) * 1000 / info.cdiv / info.period;
800 *duty = (info.inactive - info.active) * 100 / info.period;
801}
802
795bool dbg_hw_info_pwm(void) 803bool dbg_hw_info_pwm(void)
796{ 804{
797 lcd_setfont(FONT_SYSFIXED); 805 lcd_setfont(FONT_SYSFIXED);
@@ -833,14 +841,14 @@ bool dbg_hw_info_pwm(void)
833 } 841 }
834 else 842 else
835 { 843 {
836 char *prefix = ""; 844 int freq, duty;
837 int freq = imx233_clkctrl_get_freq(CLK_XTAL) * 1000 / info.cdiv / info.period; 845 get_pwm_freq_duty(i, &freq, &duty);
846 const char *prefix = "";
838 if(freq > 1000) 847 if(freq > 1000)
839 { 848 {
840 prefix = "K"; 849 prefix = "K";
841 freq /= 1000; 850 freq /= 1000;
842 } 851 }
843 int duty = (info.inactive - info.active) * 100 / info.period;
844 lcd_putsf(0, line++, "%d @%d %sHz, %d%% %c/%c", i, freq, prefix, 852 lcd_putsf(0, line++, "%d @%d %sHz, %d%% %c/%c", i, freq, prefix,
845 duty, info.active_state, info.inactive_state); 853 duty, info.active_state, info.inactive_state);
846 } 854 }
@@ -1261,6 +1269,125 @@ bool dbg_hw_info_sdmmc(void)
1261 } 1269 }
1262} 1270}
1263 1271
1272static const char *get_led_col(enum imx233_led_color_t col)
1273{
1274 switch(col)
1275 {
1276 case LED_RED: return "red";
1277 case LED_GREEN: return "green";
1278 case LED_BLUE: return "blue";
1279 default: return "unknown";
1280 }
1281}
1282
1283bool dbg_hw_info_led(void)
1284{
1285 lcd_setfont(FONT_SYSFIXED);
1286 int cur_led = 0, cur_chan = 0;
1287 bool nr_leds = imx233_led_get_count();
1288 struct imx233_led_t *leds = imx233_led_get_info();
1289 bool prev_pending = false;
1290 bool next_pending = false;
1291 bool editing = false;
1292
1293 while(1)
1294 {
1295 int button = my_get_action(HZ / 10);
1296 switch(button)
1297 {
1298 case ACT_NEXT:
1299 if(nr_leds > 0 && !editing)
1300 {
1301 cur_chan++;
1302 if(cur_chan == leds[cur_led].nr_chan)
1303 {
1304 cur_chan = 0;
1305 cur_led = (cur_led + 1) % nr_leds;
1306 }
1307 }
1308 else
1309 next_pending = true;
1310 break;
1311 case ACT_PREV:
1312 if(nr_leds > 0 && !editing)
1313 {
1314 cur_chan--;
1315 if(cur_chan == -1)
1316 {
1317 cur_led = (cur_led + nr_leds - 1) % nr_leds;
1318 cur_chan = leds[cur_led].nr_chan - 1;
1319 }
1320 }
1321 else
1322 prev_pending = true;
1323 break;
1324 case ACT_OK:
1325 editing = !editing;
1326 break;
1327 case ACT_CANCEL:
1328 lcd_setfont(FONT_UI);
1329 return false;
1330 }
1331
1332 lcd_clear_display();
1333 int line = 0;
1334 if(nr_leds == 0)
1335 lcd_putsf(0, line++, "This device has no LED!");
1336 for(int led = 0; led < imx233_led_get_count(); led++)
1337 {
1338 lcd_putsf(0, line++, "LED %d:", led);
1339 for(int chan = 0; chan < leds[led].nr_chan; chan++)
1340 {
1341 /* read current configuration */
1342 char buffer[64];
1343 bool use_pwm = false;
1344 int duty = 0, freq = 1;
1345 bool on = false;
1346 if(leds[led].chan[chan].has_pwm &&
1347 imx233_pwm_is_enabled(leds[led].chan[chan].pwm_chan))
1348 {
1349 get_pwm_freq_duty(leds[led].chan[chan].pwm_chan, &freq, &duty);
1350 /* assume active is high and inactive is low */
1351 snprintf(buffer, sizeof(buffer), "%d Hz, %d%%", freq, duty);
1352 use_pwm = true;
1353 }
1354 else
1355 {
1356 on = imx233_pinctrl_get_gpio(leds[led].chan[chan].gpio_bank,
1357 leds[led].chan[chan].gpio_pin);
1358 snprintf(buffer, sizeof(buffer), "%s", on ? "on" : "off");
1359 }
1360 if(cur_led == led && cur_chan == chan)
1361 lcd_set_foreground(editing ? LCD_RGBPACK(255, 0, 0) : LCD_RGBPACK(0, 0, 255));
1362 lcd_putsf(0, line++, " %s: %s",
1363 get_led_col(leds[led].chan[chan].color), buffer);
1364 lcd_set_foreground(LCD_WHITE);
1365 /* do edit */
1366 if(cur_led != led || cur_chan != chan || !editing)
1367 continue;
1368 if(!next_pending && !prev_pending)
1369 continue;
1370 bool inc = next_pending;
1371 next_pending = false;
1372 prev_pending = false;
1373 if(use_pwm)
1374 {
1375 duty += inc ? 10 : -10;
1376 if(duty < 0)
1377 duty = 0;
1378 if(duty > 100)
1379 duty = 100;
1380 imx233_led_set_pwm(cur_led, cur_chan, freq, duty);
1381 }
1382 else
1383 imx233_led_set(cur_led, cur_chan, !on);
1384 }
1385 }
1386 lcd_update();
1387 yield();
1388 }
1389}
1390
1264#ifdef HAVE_DUALBOOT_STUB 1391#ifdef HAVE_DUALBOOT_STUB
1265bool dbg_hw_info_dualboot(void) 1392bool dbg_hw_info_dualboot(void)
1266{ 1393{
@@ -1341,6 +1468,7 @@ static struct
1341 {"timrot", dbg_hw_info_timrot}, 1468 {"timrot", dbg_hw_info_timrot},
1342 {"button", dbg_hw_info_button}, 1469 {"button", dbg_hw_info_button},
1343 {"sdmmc", dbg_hw_info_sdmmc}, 1470 {"sdmmc", dbg_hw_info_sdmmc},
1471 {"led", dbg_hw_info_led},
1344#ifdef HAVE_DUALBOOT_STUB 1472#ifdef HAVE_DUALBOOT_STUB
1345 {"dualboot", dbg_hw_info_dualboot}, 1473 {"dualboot", dbg_hw_info_dualboot},
1346#endif 1474#endif
diff --git a/firmware/target/arm/imx233/led-imx233.c b/firmware/target/arm/imx233/led-imx233.c
new file mode 100644
index 0000000000..88c2b7e054
--- /dev/null
+++ b/firmware/target/arm/imx233/led-imx233.c
@@ -0,0 +1,100 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (c) 2017 by Amaury Pouly
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#include "led-imx233.h"
22#include "pwm-imx233.h"
23#include "pinctrl-imx233.h"
24
25/** Target specific tables */
26#if defined(CREATIVE_ZENXFI) || defined(CREATIVE_ZENMOZAIC)
27/* ZEN X-Fi/Mozaic have a Red-Green LED */
28static struct imx233_led_chan_t zenxfi_led_chans[] =
29{
30 { true, LED_RED, 2, 2, 2 }, /* red channel on B20P2 (pwm 2) */
31 { true, LED_GREEN, 2, 4, 4 }, /* green channel on B20P4 (pwm 4) */
32};
33static struct imx233_led_t leds[] = {{2, zenxfi_led_chans}};
34#elif defined(CREATIVE_ZEN)
35/* ZEN has a blue LED */
36static struct imx233_led_chan_t zen_led_chans[] =
37{
38 { true, LED_BLUE, 2, 3, 3 }, /* blue channel on B20P3 (pwm 3) */
39};
40static struct imx233_led_t leds[] = {{1, zen_led_chans}};
41#else
42#define NO_LEDS
43#endif
44
45#ifndef NO_LEDS
46int imx233_led_get_count(void)
47{
48 return sizeof(leds) / sizeof(leds[0]);
49}
50
51struct imx233_led_t *imx233_led_get_info(void)
52{
53 return leds;
54}
55#else
56int imx233_led_get_count(void)
57{
58 return 0;
59}
60
61struct imx233_led_t * imx233_led_get_info(void)
62{
63 return NULL;
64}
65#endif
66
67void imx233_led_init(void)
68{
69 struct imx233_led_t *leds = imx233_led_get_info();
70 /* turn off all channels */
71 for(int led = 0; led < imx233_led_get_count(); led++)
72 for(int chan = 0; chan < leds[led].nr_chan; chan++)
73 imx233_led_set(led, chan, false);
74}
75
76void imx233_led_set(int led, int chan, bool on)
77{
78 struct imx233_led_chan_t *c = &imx233_led_get_info()[led].chan[chan];
79 /* if LED has a PWM, handle it using the PWM */
80 if(c->has_pwm)
81 {
82 /* toogle at 1KHz */
83 return imx233_led_set_pwm(led, chan, 1000, on ? 100 : 0);
84 }
85 /* make sure pin is configured as a GPIO */
86 imx233_pinctrl_setup_vpin(
87 VPIN_PACK(c->gpio_bank, c->gpio_pin, GPIO), "led",
88 PINCTRL_DRIVE_4mA, false);
89 imx233_pinctrl_enable_gpio(c->gpio_bank, c->gpio_pin, true);
90 imx233_pinctrl_set_gpio(c->gpio_bank, c->gpio_pin, on);
91}
92
93void imx233_led_set_pwm(int led, int chan, int freq, int duty)
94{
95 struct imx233_led_chan_t *c = &imx233_led_get_info()[led].chan[chan];
96 if(!c->has_pwm)
97 panicf("led %d chan %d cannot do pwm", led, chan);
98 imx233_pwm_setup_simple(c->pwm_chan, freq, duty);
99 imx233_pwm_enable(c->pwm_chan, true);
100}
diff --git a/firmware/target/arm/imx233/led-imx233.h b/firmware/target/arm/imx233/led-imx233.h
new file mode 100644
index 0000000000..658135c94a
--- /dev/null
+++ b/firmware/target/arm/imx233/led-imx233.h
@@ -0,0 +1,69 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (c) 2017 by Amaury Pouly
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#ifndef __LED_IMX233_H__
22#define __LED_IMX233_H__
23
24#include "system.h"
25
26/** LED API
27 *
28 * The following assumes each single LED is controllable by one
29 * or more channels. Each channel is either controlled by a GPIO (on/off) or a
30 * PWM. Each channel also has a color, so it is possible to describe
31 * a red-green LED for example. */
32
33/* LED channel color */
34enum imx233_led_color_t
35{
36 LED_RED,
37 LED_GREEN,
38 LED_BLUE,
39};
40
41/* LED channel */
42struct imx233_led_chan_t
43{
44 bool has_pwm; /* GPIO or PWM */
45 enum imx233_led_color_t color; /* color */
46 int gpio_bank, gpio_pin; /* GPIO bank and pin (also valid for PWM) */
47 int pwm_chan; /* for PWM: harware channel*/
48};
49
50/* LED */
51struct imx233_led_t
52{
53 int nr_chan; /* number of channels */
54 struct imx233_led_chan_t *chan; /* array of channels */
55};
56
57/* init: all LEDs are turned off on init */
58void imx233_led_init(void);
59/* get number of LEDs */
60int imx233_led_get_count(void);
61/* return an array of LEDs */
62struct imx233_led_t *imx233_led_get_info(void);
63/* set LED channel on/off, it also works for PWM channel by setting the PWM to
64 * constantly off or constantly on */
65void imx233_led_set(int led, int chan, bool on);
66/* set LED PWM: control the frequency and the duty cycle percentage (0-100) */
67void imx233_led_set_pwm(int led, int chan, int freq, int duty);
68
69#endif /* __LED_IMX233_H__ */
diff --git a/firmware/target/arm/imx233/pwm-imx233.c b/firmware/target/arm/imx233/pwm-imx233.c
index 5e1cc1daa5..3704b60ff2 100644
--- a/firmware/target/arm/imx233/pwm-imx233.c
+++ b/firmware/target/arm/imx233/pwm-imx233.c
@@ -54,9 +54,18 @@ bool imx233_pwm_is_enabled(int channel)
54void imx233_pwm_enable(int channel, bool enable) 54void imx233_pwm_enable(int channel, bool enable)
55{ 55{
56 if(enable) 56 if(enable)
57 {
58 /* claim pin */
59 imx233_pinctrl_setup_vpin(VPIN_PWM(channel), "pwm", PINCTRL_DRIVE_4mA, false);
57 BF_SET(PWM_CTRL, PWMx_ENABLE(channel)); 60 BF_SET(PWM_CTRL, PWMx_ENABLE(channel));
61 }
58 else 62 else
63 {
59 BF_CLR(PWM_CTRL, PWMx_ENABLE(channel)); 64 BF_CLR(PWM_CTRL, PWMx_ENABLE(channel));
65 /* stop claiming the pin */
66 imx233_pinctrl_release(VPIN_UNPACK_BANK(VPIN_PWM(channel)),
67 VPIN_UNPACK_PIN(VPIN_PWM(channel)), "pwm");
68 }
60} 69}
61 70
62void imx233_pwm_setup(int channel, int period, int cdiv, int active, 71void imx233_pwm_setup(int channel, int period, int cdiv, int active,
@@ -66,8 +75,6 @@ void imx233_pwm_setup(int channel, int period, int cdiv, int active,
66 bool enable = imx233_pwm_is_enabled(channel); 75 bool enable = imx233_pwm_is_enabled(channel);
67 if(enable) 76 if(enable)
68 imx233_pwm_enable(channel, false); 77 imx233_pwm_enable(channel, false);
69 /* setup pin */
70 imx233_pinctrl_setup_vpin(VPIN_PWM(channel), "pwm", PINCTRL_DRIVE_4mA, false);
71 /* watch the order ! active THEN period 78 /* watch the order ! active THEN period
72 * NOTE: the register value is period-1 */ 79 * NOTE: the register value is period-1 */
73 BF_WR_ALL(PWM_ACTIVEn(channel), ACTIVE(active), INACTIVE(inactive)); 80 BF_WR_ALL(PWM_ACTIVEn(channel), ACTIVE(active), INACTIVE(inactive));
diff --git a/firmware/target/arm/imx233/system-imx233.c b/firmware/target/arm/imx233/system-imx233.c
index c6f974b108..860039b85f 100644
--- a/firmware/target/arm/imx233/system-imx233.c
+++ b/firmware/target/arm/imx233/system-imx233.c
@@ -44,6 +44,7 @@
44#include "button.h" 44#include "button.h"
45#include "fmradio_i2c.h" 45#include "fmradio_i2c.h"
46#include "powermgmt-imx233.h" 46#include "powermgmt-imx233.h"
47#include "led-imx233.h"
47 48
48#include "regs/digctl.h" 49#include "regs/digctl.h"
49#include "regs/usbphy.h" 50#include "regs/usbphy.h"
@@ -202,6 +203,7 @@ void system_init(void)
202 imx233_power_init(); 203 imx233_power_init();
203 imx233_i2c_init(); 204 imx233_i2c_init();
204 imx233_powermgmt_init(); 205 imx233_powermgmt_init();
206 imx233_led_init();
205 /* setup watchdog */ 207 /* setup watchdog */
206 watchdog_init(); 208 watchdog_init();
207 209