Skip to content

Commit

Permalink
Use the LM8502 LED engine for notification.
Browse files Browse the repository at this point in the history
This serves 2 purposes:
- Make notification LED work in standby
- Have a nice pulsing like in WebOS
  • Loading branch information
maniac103 authored and dalingrin committed Jan 16, 2012
1 parent 8692f8a commit fd43dec
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 16 deletions.
2 changes: 2 additions & 0 deletions init.tenderloin.rc
Expand Up @@ -57,8 +57,10 @@ on init
chown system system /dev/mpuirq
chown system system /dev/timerirq

chmod 0664 /dev/lm8502
chmod 0664 /sys/class/leds/core_navi_left/brightness
chmod 0664 /sys/class/leds/core_navi_right/brightness
chown system system /dev/lm8502
chown system system /sys/class/leds/core_navi_left/brightness
chown system system /sys/class/leds/core_navi_right/brightness

Expand Down
123 changes: 107 additions & 16 deletions liblights/lights.c
Expand Up @@ -35,11 +35,23 @@ static pthread_once_t g_init = PTHREAD_ONCE_INIT;
static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;

char const *const LCD_FILE = "/sys/class/leds/lcd-backlight/brightness";
char const *const LED_FILE_RIGHT =
"/sys/class/leds/core_navi_right/brightness";
char const *const LED_FILE_LEFT =
"/sys/class/leds/core_navi_left/brightness";

char const *const LED_FILE = "/dev/lm8502";

/* copied from kernel include/linux/i2c_lm8502_led.h */
#define LM8502_DOWNLOAD_MICROCODE 1
#define LM8502_START_ENGINE 9
#define LM8502_STOP_ENGINE 3
#define LM8502_WAIT_FOR_ENGINE_STOPPED 8

/* LED engine programs */
static const uint16_t notif_led_program_pulse[] = {
0x9c0f, 0x9c8f, 0xe004, 0x4000, 0x047f, 0x4c00, 0x057f, 0x4c00,
0x047f, 0x4c00, 0x057f, 0x7c00, 0xa30a, 0x0000, 0x0000, 0x0007,
0x9c1f, 0x9c9f, 0xe080, 0x03ff, 0xc800
};
static const uint16_t notif_led_program_reset[] = {
0x9c0f, 0x9c8f, 0x03ff, 0xc000
};

/** TS power stuff */
static int vdd_fd, xres_fd, wake_fd, i2c_fd, ts_state;
Expand Down Expand Up @@ -177,23 +189,100 @@ static int rgb_to_brightness(struct light_state_t const *state)
+ (150*((color>>8) & 0x00ff)) + (29*(color & 0x00ff))) >> 8;
}

static void init_notification_led(void)
{
uint16_t microcode[96];
int fd;

memset(microcode, 0, sizeof(microcode));

fd = open(LED_FILE, O_RDWR);
if (fd < 0) {
LOGE("Cannot open notification LED device - %d", errno);
return;
}

/* download microcode */
if (ioctl(fd, LM8502_DOWNLOAD_MICROCODE, microcode) < 0) {
LOGE("Cannot download LED microcode - %d", errno);
return;
}
if (ioctl(fd, LM8502_STOP_ENGINE, 1) < 0) {
LOGE("Cannot stop LED engine 1 - %d", errno);
}
if (ioctl(fd, LM8502_STOP_ENGINE, 2) < 0) {
LOGE("Cannot stop LED engine 2 - %d", errno);
}

close(fd);
}

static int set_light_notifications(struct light_device_t* dev,
struct light_state_t const* state)
{
int brightness = rgb_to_brightness(state);
int v = 0;
int ret = 0;
int on = state->color & 0x00ffffff;
uint16_t microcode[96];
int fd, ret;

LOGI("%sabling notification light", on ? "En" : "Dis");
memset(microcode, 0, sizeof(microcode));
if (on) {
memcpy(microcode, notif_led_program_pulse, sizeof(notif_led_program_pulse));
} else {
memcpy(microcode, notif_led_program_reset, sizeof(notif_led_program_reset));
}

pthread_mutex_lock(&g_lock);

if (brightness+state->color == 0 || brightness > 100) {
if (state->color & 0x00ffffff)
v = 100;
} else
v = 0;
fd = open(LED_FILE, O_RDWR);
if (fd < 0) {
LOGE("Opening %s failed - %d", LED_FILE, errno);
ret = -errno;
} else {
ret = 0;

/* download microcode */
if (ioctl(fd, LM8502_DOWNLOAD_MICROCODE, microcode) < 0) {
LOGE("Copying notification microcode failed - %d", errno);
ret = -errno;
}
if (ret == 0 && on) {
if (ioctl(fd, LM8502_START_ENGINE, 2) < 0) {
LOGE("Starting notification LED engine 2 failed - %d", errno);
ret = -errno;
}
}
if (ret == 0) {
if (ioctl(fd, LM8502_START_ENGINE, 1) < 0) {
LOGE("Starting notification LED engine 1 failed - %d", errno);
ret = -errno;
}
}
if (ret == 0 && !on) {
LOGI("stop engine 1");
if (ioctl(fd, LM8502_STOP_ENGINE, 1) < 0) {
LOGE("Stopping notification LED engine 1 failed - %d", errno);
ret = -errno;
}
if (ioctl(fd, LM8502_STOP_ENGINE, 2) < 0) {
LOGE("Stopping notification LED engine 2 failed - %d", errno);
ret = -errno;
}
}
if (ret == 0 && !on) {
LOGD("Waiting for notification LED engine to stop after reset");
int state;
/* make sure the reset is complete */
if (ioctl(fd, LM8502_WAIT_FOR_ENGINE_STOPPED, &state) < 0) {
LOGW("Waiting for notification LED reset failed - %d", errno);
ret = -errno;
}
LOGD("Notification LED reset finished with stop state %d", state);
}

close(fd);
}

LOGI("color %u fm %u status %u is lit %u brightness", state->color, state->flashMode, v, (state->color & 0x00ffffff), brightness);
ret = write_int(LED_FILE_RIGHT, v);
write_int(LED_FILE_LEFT, v);
pthread_mutex_unlock(&g_lock);
return ret;
}
Expand Down Expand Up @@ -271,6 +360,8 @@ static int open_lights(const struct hw_module_t *module, char const *name,
i2c_fd = open("/dev/i2c-5", O_RDWR);
LOGE_IF(i2c_fd < 0, "TScontrol: Cannot open i2c dev - %d", errno);

init_notification_led();

return 0;
}

Expand Down

0 comments on commit fd43dec

Please sign in to comment.