summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
#endif
#if !defined(BOOTLOADER) || defined(HAVE_BOOTLOADER_USB_MODE)
target/arm/imx233/usb-imx233.c
+target/arm/imx233/led-imx233.c
#endif
#ifndef BOOTLOADER
#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 @@
#include "button.h"
#include "button-imx233.h"
#include "sdmmc-imx233.h"
+#include "led-imx233.h"
#include "storage.h"
#include "regs/usbphy.h"
@@ -792,6 +793,13 @@ bool dbg_hw_info_ocotp(void)
}
}
+static void get_pwm_freq_duty(int chan, int *freq, int *duty)
+{
+ struct imx233_pwm_info_t info = imx233_pwm_get_info(chan);
+ *freq = imx233_clkctrl_get_freq(CLK_XTAL) * 1000 / info.cdiv / info.period;
+ *duty = (info.inactive - info.active) * 100 / info.period;
+}
+
bool dbg_hw_info_pwm(void)
{
lcd_setfont(FONT_SYSFIXED);
@@ -833,14 +841,14 @@ bool dbg_hw_info_pwm(void)
}
else
{
- char *prefix = "";
- int freq = imx233_clkctrl_get_freq(CLK_XTAL) * 1000 / info.cdiv / info.period;
+ int freq, duty;
+ get_pwm_freq_duty(i, &freq, &duty);
+ const char *prefix = "";
if(freq > 1000)
{
prefix = "K";
freq /= 1000;
}
- int duty = (info.inactive - info.active) * 100 / info.period;
lcd_putsf(0, line++, "%d @%d %sHz, %d%% %c/%c", i, freq, prefix,
duty, info.active_state, info.inactive_state);
}
@@ -1261,6 +1269,125 @@ bool dbg_hw_info_sdmmc(void)
}
}
+static const char *get_led_col(enum imx233_led_color_t col)
+{
+ switch(col)
+ {
+ case LED_RED: return "red";
+ case LED_GREEN: return "green";
+ case LED_BLUE: return "blue";
+ default: return "unknown";
+ }
+}
+
+bool dbg_hw_info_led(void)
+{
+ lcd_setfont(FONT_SYSFIXED);
+ int cur_led = 0, cur_chan = 0;
+ bool nr_leds = imx233_led_get_count();
+ struct imx233_led_t *leds = imx233_led_get_info();
+ bool prev_pending = false;
+ bool next_pending = false;
+ bool editing = false;
+
+ while(1)
+ {
+ int button = my_get_action(HZ / 10);
+ switch(button)
+ {
+ case ACT_NEXT:
+ if(nr_leds > 0 && !editing)
+ {
+ cur_chan++;
+ if(cur_chan == leds[cur_led].nr_chan)
+ {
+ cur_chan = 0;
+ cur_led = (cur_led + 1) % nr_leds;
+ }
+ }
+ else
+ next_pending = true;
+ break;
+ case ACT_PREV:
+ if(nr_leds > 0 && !editing)
+ {
+ cur_chan--;
+ if(cur_chan == -1)
+ {
+ cur_led = (cur_led + nr_leds - 1) % nr_leds;
+ cur_chan = leds[cur_led].nr_chan - 1;
+ }
+ }
+ else
+ prev_pending = true;
+ break;
+ case ACT_OK:
+ editing = !editing;
+ break;
+ case ACT_CANCEL:
+ lcd_setfont(FONT_UI);
+ return false;
+ }
+
+ lcd_clear_display();
+ int line = 0;
+ if(nr_leds == 0)
+ lcd_putsf(0, line++, "This device has no LED!");
+ for(int led = 0; led < imx233_led_get_count(); led++)
+ {
+ lcd_putsf(0, line++, "LED %d:", led);
+ for(int chan = 0; chan < leds[led].nr_chan; chan++)
+ {
+ /* read current configuration */
+ char buffer[64];
+ bool use_pwm = false;
+ int duty = 0, freq = 1;
+ bool on = false;
+ if(leds[led].chan[chan].has_pwm &&
+ imx233_pwm_is_enabled(leds[led].chan[chan].pwm_chan))
+ {
+ get_pwm_freq_duty(leds[led].chan[chan].pwm_chan, &freq, &duty);
+ /* assume active is high and inactive is low */
+ snprintf(buffer, sizeof(buffer), "%d Hz, %d%%", freq, duty);
+ use_pwm = true;
+ }
+ else
+ {
+ on = imx233_pinctrl_get_gpio(leds[led].chan[chan].gpio_bank,
+ leds[led].chan[chan].gpio_pin);
+ snprintf(buffer, sizeof(buffer), "%s", on ? "on" : "off");
+ }
+ if(cur_led == led && cur_chan == chan)
+ lcd_set_foreground(editing ? LCD_RGBPACK(255, 0, 0) : LCD_RGBPACK(0, 0, 255));
+ lcd_putsf(0, line++, " %s: %s",
+ get_led_col(leds[led].chan[chan].color), buffer);
+ lcd_set_foreground(LCD_WHITE);
+ /* do edit */
+ if(cur_led != led || cur_chan != chan || !editing)
+ continue;
+ if(!next_pending && !prev_pending)
+ continue;
+ bool inc = next_pending;
+ next_pending = false;
+ prev_pending = false;
+ if(use_pwm)
+ {
+ duty += inc ? 10 : -10;
+ if(duty < 0)
+ duty = 0;
+ if(duty > 100)
+ duty = 100;
+ imx233_led_set_pwm(cur_led, cur_chan, freq, duty);
+ }
+ else
+ imx233_led_set(cur_led, cur_chan, !on);
+ }
+ }
+ lcd_update();
+ yield();
+ }
+}
+
#ifdef HAVE_DUALBOOT_STUB
bool dbg_hw_info_dualboot(void)
{
@@ -1341,6 +1468,7 @@ static struct
{"timrot", dbg_hw_info_timrot},
{"button", dbg_hw_info_button},
{"sdmmc", dbg_hw_info_sdmmc},
+ {"led", dbg_hw_info_led},
#ifdef HAVE_DUALBOOT_STUB
{"dualboot", dbg_hw_info_dualboot},
#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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (c) 2017 by Amaury Pouly
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "led-imx233.h"
+#include "pwm-imx233.h"
+#include "pinctrl-imx233.h"
+
+/** Target specific tables */
+#if defined(CREATIVE_ZENXFI) || defined(CREATIVE_ZENMOZAIC)
+/* ZEN X-Fi/Mozaic have a Red-Green LED */
+static struct imx233_led_chan_t zenxfi_led_chans[] =
+{
+ { true, LED_RED, 2, 2, 2 }, /* red channel on B20P2 (pwm 2) */
+ { true, LED_GREEN, 2, 4, 4 }, /* green channel on B20P4 (pwm 4) */
+};
+static struct imx233_led_t leds[] = {{2, zenxfi_led_chans}};
+#elif defined(CREATIVE_ZEN)
+/* ZEN has a blue LED */
+static struct imx233_led_chan_t zen_led_chans[] =
+{
+ { true, LED_BLUE, 2, 3, 3 }, /* blue channel on B20P3 (pwm 3) */
+};
+static struct imx233_led_t leds[] = {{1, zen_led_chans}};
+#else
+#define NO_LEDS
+#endif
+
+#ifndef NO_LEDS
+int imx233_led_get_count(void)
+{
+ return sizeof(leds) / sizeof(leds[0]);
+}
+
+struct imx233_led_t *imx233_led_get_info(void)
+{
+ return leds;
+}
+#else
+int imx233_led_get_count(void)
+{
+ return 0;
+}
+
+struct imx233_led_t * imx233_led_get_info(void)
+{
+ return NULL;
+}
+#endif
+
+void imx233_led_init(void)
+{
+ struct imx233_led_t *leds = imx233_led_get_info();
+ /* turn off all channels */
+ for(int led = 0; led < imx233_led_get_count(); led++)
+ for(int chan = 0; chan < leds[led].nr_chan; chan++)
+ imx233_led_set(led, chan, false);
+}
+
+void imx233_led_set(int led, int chan, bool on)
+{
+ struct imx233_led_chan_t *c = &imx233_led_get_info()[led].chan[chan];
+ /* if LED has a PWM, handle it using the PWM */
+ if(c->has_pwm)
+ {
+ /* toogle at 1KHz */
+ return imx233_led_set_pwm(led, chan, 1000, on ? 100 : 0);
+ }
+ /* make sure pin is configured as a GPIO */
+ imx233_pinctrl_setup_vpin(
+ VPIN_PACK(c->gpio_bank, c->gpio_pin, GPIO), "led",
+ PINCTRL_DRIVE_4mA, false);
+ imx233_pinctrl_enable_gpio(c->gpio_bank, c->gpio_pin, true);
+ imx233_pinctrl_set_gpio(c->gpio_bank, c->gpio_pin, on);
+}
+
+void imx233_led_set_pwm(int led, int chan, int freq, int duty)
+{
+ struct imx233_led_chan_t *c = &imx233_led_get_info()[led].chan[chan];
+ if(!c->has_pwm)
+ panicf("led %d chan %d cannot do pwm", led, chan);
+ imx233_pwm_setup_simple(c->pwm_chan, freq, duty);
+ imx233_pwm_enable(c->pwm_chan, true);
+}
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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (c) 2017 by Amaury Pouly
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __LED_IMX233_H__
+#define __LED_IMX233_H__
+
+#include "system.h"
+
+/** LED API
+ *
+ * The following assumes each single LED is controllable by one
+ * or more channels. Each channel is either controlled by a GPIO (on/off) or a
+ * PWM. Each channel also has a color, so it is possible to describe
+ * a red-green LED for example. */
+
+/* LED channel color */
+enum imx233_led_color_t
+{
+ LED_RED,
+ LED_GREEN,
+ LED_BLUE,
+};
+
+/* LED channel */
+struct imx233_led_chan_t
+{
+ bool has_pwm; /* GPIO or PWM */
+ enum imx233_led_color_t color; /* color */
+ int gpio_bank, gpio_pin; /* GPIO bank and pin (also valid for PWM) */
+ int pwm_chan; /* for PWM: harware channel*/
+};
+
+/* LED */
+struct imx233_led_t
+{
+ int nr_chan; /* number of channels */
+ struct imx233_led_chan_t *chan; /* array of channels */
+};
+
+/* init: all LEDs are turned off on init */
+void imx233_led_init(void);
+/* get number of LEDs */
+int imx233_led_get_count(void);
+/* return an array of LEDs */
+struct imx233_led_t *imx233_led_get_info(void);
+/* set LED channel on/off, it also works for PWM channel by setting the PWM to
+ * constantly off or constantly on */
+void imx233_led_set(int led, int chan, bool on);
+/* set LED PWM: control the frequency and the duty cycle percentage (0-100) */
+void imx233_led_set_pwm(int led, int chan, int freq, int duty);
+
+#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)
void imx233_pwm_enable(int channel, bool enable)
{
if(enable)
+ {
+ /* claim pin */
+ imx233_pinctrl_setup_vpin(VPIN_PWM(channel), "pwm", PINCTRL_DRIVE_4mA, false);
BF_SET(PWM_CTRL, PWMx_ENABLE(channel));
+ }
else
+ {
BF_CLR(PWM_CTRL, PWMx_ENABLE(channel));
+ /* stop claiming the pin */
+ imx233_pinctrl_release(VPIN_UNPACK_BANK(VPIN_PWM(channel)),
+ VPIN_UNPACK_PIN(VPIN_PWM(channel)), "pwm");
+ }
}
void 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,
bool enable = imx233_pwm_is_enabled(channel);
if(enable)
imx233_pwm_enable(channel, false);
- /* setup pin */
- imx233_pinctrl_setup_vpin(VPIN_PWM(channel), "pwm", PINCTRL_DRIVE_4mA, false);
/* watch the order ! active THEN period
* NOTE: the register value is period-1 */
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 @@
#include "button.h"
#include "fmradio_i2c.h"
#include "powermgmt-imx233.h"
+#include "led-imx233.h"
#include "regs/digctl.h"
#include "regs/usbphy.h"
@@ -202,6 +203,7 @@ void system_init(void)
imx233_power_init();
imx233_i2c_init();
imx233_powermgmt_init();
+ imx233_led_init();
/* setup watchdog */
watchdog_init();