xref: /aosp_15_r20/external/libwebsockets/lib/drivers/led/led-seq.c (revision 1c60b9aca93fdbc9b5f19b2d2194c91294b22281)
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