1*1c60b9acSAndroid Build Coastguard Worker /*
2*1c60b9acSAndroid Build Coastguard Worker * Generic GPIO led
3*1c60b9acSAndroid Build Coastguard Worker *
4*1c60b9acSAndroid Build Coastguard Worker * Copyright (C) 2019 - 2020 Andy Green <[email protected]>
5*1c60b9acSAndroid Build Coastguard Worker *
6*1c60b9acSAndroid Build Coastguard Worker * Permission is hereby granted, free of charge, to any person obtaining a copy
7*1c60b9acSAndroid Build Coastguard Worker * of this software and associated documentation files (the "Software"), to
8*1c60b9acSAndroid Build Coastguard Worker * deal in the Software without restriction, including without limitation the
9*1c60b9acSAndroid Build Coastguard Worker * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10*1c60b9acSAndroid Build Coastguard Worker * sell copies of the Software, and to permit persons to whom the Software is
11*1c60b9acSAndroid Build Coastguard Worker * furnished to do so, subject to the following conditions:
12*1c60b9acSAndroid Build Coastguard Worker *
13*1c60b9acSAndroid Build Coastguard Worker * The above copyright notice and this permission notice shall be included in
14*1c60b9acSAndroid Build Coastguard Worker * all copies or substantial portions of the Software.
15*1c60b9acSAndroid Build Coastguard Worker *
16*1c60b9acSAndroid Build Coastguard Worker * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17*1c60b9acSAndroid Build Coastguard Worker * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18*1c60b9acSAndroid Build Coastguard Worker * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19*1c60b9acSAndroid Build Coastguard Worker * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20*1c60b9acSAndroid Build Coastguard Worker * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21*1c60b9acSAndroid Build Coastguard Worker * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22*1c60b9acSAndroid Build Coastguard Worker * IN THE SOFTWARE.
23*1c60b9acSAndroid Build Coastguard Worker */
24*1c60b9acSAndroid Build Coastguard Worker #include "private-lib-core.h"
25*1c60b9acSAndroid Build Coastguard Worker
26*1c60b9acSAndroid Build Coastguard Worker #include "drivers/led/private-lib-drivers-led.h"
27*1c60b9acSAndroid Build Coastguard Worker
28*1c60b9acSAndroid Build Coastguard Worker /*
29*1c60b9acSAndroid Build Coastguard Worker * 64 entry interpolated CIE correction
30*1c60b9acSAndroid Build Coastguard Worker * https://en.wikipedia.org/wiki/Lightness
31*1c60b9acSAndroid Build Coastguard Worker */
32*1c60b9acSAndroid Build Coastguard Worker
33*1c60b9acSAndroid Build Coastguard Worker uint16_t cie[] = {
34*1c60b9acSAndroid Build Coastguard Worker 0, 113, 227, 340, 454, 568, 688, 824, 976, 1146,
35*1c60b9acSAndroid Build Coastguard Worker 1335, 1543, 1772, 2023, 2296, 2592, 2914, 3260, 3633, 4034,
36*1c60b9acSAndroid Build Coastguard Worker 4463, 4921, 5409, 5929, 6482, 7067, 7687, 8341, 9032, 9761,
37*1c60b9acSAndroid Build Coastguard Worker 10527, 11332, 12178, 13064, 13993, 14964, 15980, 17040, 18146, 19299,
38*1c60b9acSAndroid Build Coastguard Worker 20500, 21750, 23049, 24400, 25802, 27256, 28765, 30328, 31946, 33622,
39*1c60b9acSAndroid Build Coastguard Worker 35354, 37146, 38996, 40908, 42881, 44916, 47014, 49177, 51406, 53700,
40*1c60b9acSAndroid Build Coastguard Worker 56062, 58492, 60992, 63561,
41*1c60b9acSAndroid Build Coastguard Worker 65535 /* for interpolation */
42*1c60b9acSAndroid Build Coastguard Worker };
43*1c60b9acSAndroid Build Coastguard Worker
44*1c60b9acSAndroid Build Coastguard Worker /*
45*1c60b9acSAndroid Build Coastguard Worker * This is the default intensity correction function, it can be overridden
46*1c60b9acSAndroid Build Coastguard Worker * per-led to eg, normalize intensity of different leds
47*1c60b9acSAndroid Build Coastguard Worker */
48*1c60b9acSAndroid Build Coastguard Worker
49*1c60b9acSAndroid Build Coastguard Worker static lws_led_intensity_t
cie_antilog(lws_led_intensity_t lin)50*1c60b9acSAndroid Build Coastguard Worker cie_antilog(lws_led_intensity_t lin)
51*1c60b9acSAndroid Build Coastguard Worker {
52*1c60b9acSAndroid Build Coastguard Worker return (cie[lin >> 10] * (0x3ff - (lin & 0x3ff)) +
53*1c60b9acSAndroid Build Coastguard Worker cie[(lin >> 10) + 1] * (lin & 0x3ff)) / 0x3ff;
54*1c60b9acSAndroid Build Coastguard Worker }
55*1c60b9acSAndroid Build Coastguard Worker
56*1c60b9acSAndroid Build Coastguard Worker static void
lws_seq_advance(lws_led_state_t * lcs,lws_led_state_ch_t * ch)57*1c60b9acSAndroid Build Coastguard Worker lws_seq_advance(lws_led_state_t *lcs, lws_led_state_ch_t *ch)
58*1c60b9acSAndroid Build Coastguard Worker {
59*1c60b9acSAndroid Build Coastguard Worker if (!ch->seq)
60*1c60b9acSAndroid Build Coastguard Worker return;
61*1c60b9acSAndroid Build Coastguard Worker
62*1c60b9acSAndroid Build Coastguard Worker if (ch->phase_budget != LWS_SEQ_LEDPHASE_TOTAL_ENDLESS &&
63*1c60b9acSAndroid Build Coastguard Worker (ch->phase_budget < ch->step || !ch->phase_budget)) {
64*1c60b9acSAndroid Build Coastguard Worker
65*1c60b9acSAndroid Build Coastguard Worker /* we are done */
66*1c60b9acSAndroid Build Coastguard Worker
67*1c60b9acSAndroid Build Coastguard Worker ch->seq = NULL;
68*1c60b9acSAndroid Build Coastguard Worker if (!(--lcs->timer_refcount)) {
69*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_STOP)
70*1c60b9acSAndroid Build Coastguard Worker LWS_PLAT_TIMER_STOP(lcs->timer);
71*1c60b9acSAndroid Build Coastguard Worker #endif
72*1c60b9acSAndroid Build Coastguard Worker }
73*1c60b9acSAndroid Build Coastguard Worker
74*1c60b9acSAndroid Build Coastguard Worker return;
75*1c60b9acSAndroid Build Coastguard Worker }
76*1c60b9acSAndroid Build Coastguard Worker
77*1c60b9acSAndroid Build Coastguard Worker ch->ph += ch->step;
78*1c60b9acSAndroid Build Coastguard Worker if (ch->phase_budget != LWS_SEQ_LEDPHASE_TOTAL_ENDLESS)
79*1c60b9acSAndroid Build Coastguard Worker ch->phase_budget -= ch->step;
80*1c60b9acSAndroid Build Coastguard Worker }
81*1c60b9acSAndroid Build Coastguard Worker
82*1c60b9acSAndroid Build Coastguard Worker static lws_led_intensity_t
lws_seq_sample(const lws_led_gpio_map_t * map,lws_led_state_chs_t * chs)83*1c60b9acSAndroid Build Coastguard Worker lws_seq_sample(const lws_led_gpio_map_t *map, lws_led_state_chs_t *chs)
84*1c60b9acSAndroid Build Coastguard Worker {
85*1c60b9acSAndroid Build Coastguard Worker unsigned int i;
86*1c60b9acSAndroid Build Coastguard Worker
87*1c60b9acSAndroid Build Coastguard Worker if (chs->seqs[LLSI_CURR].seq)
88*1c60b9acSAndroid Build Coastguard Worker chs->seqs[LLSI_CURR].last = chs->seqs[LLSI_CURR].seq->
89*1c60b9acSAndroid Build Coastguard Worker func(chs->seqs[LLSI_CURR].ph);
90*1c60b9acSAndroid Build Coastguard Worker
91*1c60b9acSAndroid Build Coastguard Worker if (chs->seqs[LLSI_TRANS].seq) {
92*1c60b9acSAndroid Build Coastguard Worker /*
93*1c60b9acSAndroid Build Coastguard Worker * If a transition is ongoing, we need to use the transition
94*1c60b9acSAndroid Build Coastguard Worker * intensity as the mixing factor between the still-live current
95*1c60b9acSAndroid Build Coastguard Worker * and newly-live next sequences
96*1c60b9acSAndroid Build Coastguard Worker */
97*1c60b9acSAndroid Build Coastguard Worker chs->seqs[LLSI_TRANS].last = chs->seqs[LLSI_TRANS].seq->
98*1c60b9acSAndroid Build Coastguard Worker func(chs->seqs[LLSI_TRANS].ph);
99*1c60b9acSAndroid Build Coastguard Worker
100*1c60b9acSAndroid Build Coastguard Worker if (chs->seqs[LLSI_NEXT].seq)
101*1c60b9acSAndroid Build Coastguard Worker chs->seqs[LLSI_NEXT].last = chs->seqs[LLSI_NEXT].seq->
102*1c60b9acSAndroid Build Coastguard Worker func(chs->seqs[LLSI_NEXT].ph);
103*1c60b9acSAndroid Build Coastguard Worker
104*1c60b9acSAndroid Build Coastguard Worker i = (lws_led_intensity_t)(((
105*1c60b9acSAndroid Build Coastguard Worker (unsigned int)chs->seqs[LLSI_CURR].last *
106*1c60b9acSAndroid Build Coastguard Worker (65535 - chs->seqs[LLSI_TRANS].last) >> 16) +
107*1c60b9acSAndroid Build Coastguard Worker (((unsigned int)chs->seqs[LLSI_NEXT].last *
108*1c60b9acSAndroid Build Coastguard Worker (unsigned int)chs->seqs[LLSI_TRANS].last) >> 16)));
109*1c60b9acSAndroid Build Coastguard Worker } else
110*1c60b9acSAndroid Build Coastguard Worker i = chs->seqs[LLSI_CURR].last;
111*1c60b9acSAndroid Build Coastguard Worker
112*1c60b9acSAndroid Build Coastguard Worker return map->intensity_correction ? map->intensity_correction(i) :
113*1c60b9acSAndroid Build Coastguard Worker cie_antilog((lws_led_intensity_t)i);
114*1c60b9acSAndroid Build Coastguard Worker }
115*1c60b9acSAndroid Build Coastguard Worker
116*1c60b9acSAndroid Build Coastguard Worker void
lws_seq_timer_handle(lws_led_state_t * lcs)117*1c60b9acSAndroid Build Coastguard Worker lws_seq_timer_handle(lws_led_state_t *lcs)
118*1c60b9acSAndroid Build Coastguard Worker {
119*1c60b9acSAndroid Build Coastguard Worker lws_led_gpio_controller_t *lgc = lcs->controller;
120*1c60b9acSAndroid Build Coastguard Worker lws_led_state_chs_t *chs = (lws_led_state_chs_t *)&lcs[1];
121*1c60b9acSAndroid Build Coastguard Worker const lws_led_gpio_map_t *map = &lgc->led_map[0];
122*1c60b9acSAndroid Build Coastguard Worker unsigned int n;
123*1c60b9acSAndroid Build Coastguard Worker
124*1c60b9acSAndroid Build Coastguard Worker for (n = 0; n < lgc->count_leds; n++) {
125*1c60b9acSAndroid Build Coastguard Worker
126*1c60b9acSAndroid Build Coastguard Worker lgc->led_ops.intensity(&lgc->led_ops, map->name,
127*1c60b9acSAndroid Build Coastguard Worker lws_seq_sample(map, chs));
128*1c60b9acSAndroid Build Coastguard Worker
129*1c60b9acSAndroid Build Coastguard Worker lws_seq_advance(lcs, &chs->seqs[LLSI_CURR]);
130*1c60b9acSAndroid Build Coastguard Worker
131*1c60b9acSAndroid Build Coastguard Worker if (chs->seqs[LLSI_TRANS].seq) {
132*1c60b9acSAndroid Build Coastguard Worker lws_seq_advance(lcs, &chs->seqs[LLSI_NEXT]);
133*1c60b9acSAndroid Build Coastguard Worker lws_seq_advance(lcs, &chs->seqs[LLSI_TRANS]);
134*1c60b9acSAndroid Build Coastguard Worker
135*1c60b9acSAndroid Build Coastguard Worker /*
136*1c60b9acSAndroid Build Coastguard Worker * When we finished the transition, we can make the
137*1c60b9acSAndroid Build Coastguard Worker * "next" sequence the current sequence and no need for
138*1c60b9acSAndroid Build Coastguard Worker * a "next" or a transition any more.
139*1c60b9acSAndroid Build Coastguard Worker */
140*1c60b9acSAndroid Build Coastguard Worker
141*1c60b9acSAndroid Build Coastguard Worker if (!chs->seqs[LLSI_TRANS].seq) {
142*1c60b9acSAndroid Build Coastguard Worker chs->seqs[LLSI_CURR] = chs->seqs[LLSI_NEXT];
143*1c60b9acSAndroid Build Coastguard Worker chs->seqs[LLSI_NEXT].seq = NULL;
144*1c60b9acSAndroid Build Coastguard Worker }
145*1c60b9acSAndroid Build Coastguard Worker }
146*1c60b9acSAndroid Build Coastguard Worker
147*1c60b9acSAndroid Build Coastguard Worker map++;
148*1c60b9acSAndroid Build Coastguard Worker chs++;
149*1c60b9acSAndroid Build Coastguard Worker }
150*1c60b9acSAndroid Build Coastguard Worker }
151*1c60b9acSAndroid Build Coastguard Worker
152*1c60b9acSAndroid Build Coastguard Worker static int
lws_led_set_chs_seq(struct lws_led_state * lcs,lws_led_state_ch_t * dest,const lws_led_sequence_def_t * def)153*1c60b9acSAndroid Build Coastguard Worker lws_led_set_chs_seq(struct lws_led_state *lcs, lws_led_state_ch_t *dest,
154*1c60b9acSAndroid Build Coastguard Worker const lws_led_sequence_def_t *def)
155*1c60b9acSAndroid Build Coastguard Worker {
156*1c60b9acSAndroid Build Coastguard Worker int steps;
157*1c60b9acSAndroid Build Coastguard Worker
158*1c60b9acSAndroid Build Coastguard Worker dest->seq = def;
159*1c60b9acSAndroid Build Coastguard Worker dest->ph = def->ledphase_offset;
160*1c60b9acSAndroid Build Coastguard Worker dest->phase_budget = def->ledphase_total;
161*1c60b9acSAndroid Build Coastguard Worker
162*1c60b9acSAndroid Build Coastguard Worker /*
163*1c60b9acSAndroid Build Coastguard Worker * We need to compute the incremental phase angle step to cover the
164*1c60b9acSAndroid Build Coastguard Worker * total number of phases in the indicated ms, incrementing at the
165*1c60b9acSAndroid Build Coastguard Worker * timer rate of LWS_LED_SEQUENCER_UPDATE_RATE_HZ. Eg,
166*1c60b9acSAndroid Build Coastguard Worker *
167*1c60b9acSAndroid Build Coastguard Worker * 65536 phase steps (one cycle) in 2000ms at 30Hz timer rate means we
168*1c60b9acSAndroid Build Coastguard Worker * will update 2000ms / 33ms = 60 times, so we must step at at
169*1c60b9acSAndroid Build Coastguard Worker * 65536 / 60 = 1092 phase angle resolution
170*1c60b9acSAndroid Build Coastguard Worker */
171*1c60b9acSAndroid Build Coastguard Worker
172*1c60b9acSAndroid Build Coastguard Worker steps = def->ms / LWS_LED_SEQUENCER_UPDATE_INTERVAL_MS;
173*1c60b9acSAndroid Build Coastguard Worker dest->step = (def->ledphase_total != LWS_SEQ_LEDPHASE_TOTAL_ENDLESS ?
174*1c60b9acSAndroid Build Coastguard Worker def->ledphase_total : LWS_LED_FUNC_PHASE) / (steps ? steps : 1);
175*1c60b9acSAndroid Build Coastguard Worker
176*1c60b9acSAndroid Build Coastguard Worker if (!lcs->timer_refcount++) {
177*1c60b9acSAndroid Build Coastguard Worker #if defined(LWS_PLAT_TIMER_START)
178*1c60b9acSAndroid Build Coastguard Worker LWS_PLAT_TIMER_START(lcs->timer);
179*1c60b9acSAndroid Build Coastguard Worker #endif
180*1c60b9acSAndroid Build Coastguard Worker }
181*1c60b9acSAndroid Build Coastguard Worker
182*1c60b9acSAndroid Build Coastguard Worker return steps;
183*1c60b9acSAndroid Build Coastguard Worker }
184*1c60b9acSAndroid Build Coastguard Worker
185*1c60b9acSAndroid Build Coastguard Worker int
lws_led_transition(struct lws_led_state * lcs,const char * name,const lws_led_sequence_def_t * next,const lws_led_sequence_def_t * trans)186*1c60b9acSAndroid Build Coastguard Worker lws_led_transition(struct lws_led_state *lcs, const char *name,
187*1c60b9acSAndroid Build Coastguard Worker const lws_led_sequence_def_t *next,
188*1c60b9acSAndroid Build Coastguard Worker const lws_led_sequence_def_t *trans)
189*1c60b9acSAndroid Build Coastguard Worker {
190*1c60b9acSAndroid Build Coastguard Worker lws_led_state_chs_t *chs = (lws_led_state_chs_t *)&lcs[1];
191*1c60b9acSAndroid Build Coastguard Worker int index = lws_led_gpio_lookup(&lcs->controller->led_ops, name);
192*1c60b9acSAndroid Build Coastguard Worker
193*1c60b9acSAndroid Build Coastguard Worker if (index < 0)
194*1c60b9acSAndroid Build Coastguard Worker return 1;
195*1c60b9acSAndroid Build Coastguard Worker
196*1c60b9acSAndroid Build Coastguard Worker lws_led_set_chs_seq(lcs, &chs[index].seqs[LLSI_TRANS], trans);
197*1c60b9acSAndroid Build Coastguard Worker lws_led_set_chs_seq(lcs, &chs[index].seqs[LLSI_NEXT], next);
198*1c60b9acSAndroid Build Coastguard Worker
199*1c60b9acSAndroid Build Coastguard Worker return 0;
200*1c60b9acSAndroid Build Coastguard Worker }
201