summaryrefslogtreecommitdiffstats
path: root/include/linux/nvs_light.h
blob: 8b177593856027ffa4407cfe507642d122c66485 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/* Copyright (c) 2014-2016, NVIDIA CORPORATION.  All rights reserved.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */


#ifndef _NVS_LIGHT_H_
#define _NVS_LIGHT_H_

#include <linux/of.h>
#include <linux/nvs.h>

#define RET_POLL_NEXT			(-1)
#define RET_NO_CHANGE			(0)
#define RET_HW_UPDATE			(1)

#define NVS_LIGHT_STRING		"light"

/* nvs_light_threshold_calibrate_ data uses upper byte for multiple purposes if
 * nld_thr is enabled.
 * bit 31:30 reserved.
 * bit 29 selects which nld_thresh threshold (0 = lo/hi, 1 = i_lo/i_hi).
 * bit 28:24 selects which nld_tbl index entry the threshold is for.
 */
#define NVS_LIGHT_THRESH_CMD_SHIFT	(24)
#define NVS_LIGHT_THRESH_CMD_NDX_MSK	(0x1F)
#define NVS_LIGHT_THRESH_CMD_SEL_MSK	(0x20)

/**
 * struct nvs_light_dynamic - the structure that allows the NVS
 * light module to control dynamic resolution/range.
 * @resolution_ival: resolution.
 * @max_range_ival: max_range.
 * @milliamp: milliamp.
 * @delay_min_ms: minimum delay time in ms.
 * @driver_data: private data for driver.
 *
 * Driver includes a pointer to this table to enable dynamic
 * resolution/max_range.
 * The data must be from low to high max_range.
 */
struct nvs_light_dynamic {
	struct nvs_float resolution;
	struct nvs_float max_range;
	struct nvs_float milliamp;
	unsigned int delay_min_ms;	/* minimum ms delay time */
	unsigned int driver_data;	/* private data for driver */
};

/**
 * struct nld_thresh - nvs_light_dynamic table thresholds.  Each
 * entry in the nld table has its own low/high thresholds.
 * @lo: low threshold within a table entry.
 * @hi: high threshold within a table entry.
 * @i_lo: low threshold to switch to another table entry.
 * @i_hi: high threshold to switch to another table entry.
 *
 * Driver includes a pointer to this array to enable this
 * WAR feature.  The array is populated when nvs_light_of_dt is
 * called and the standard sensor_cfg thresh_lo/hi is not used
 * (as normally global thresholds). The feature is disabled if
 * there isn't a device tree entry for at least one of the
 * thresholds.
 *
 * Consider a scenario where an ALS sensor has been compromised
 * in its design, e.g. putting tint over the sensor so that the
 * HW value read from the sensor never reads its full value or
 * the HW value read by the sensor never goes to 0 in a
 * lightless environment, etc.  For whatever reason where the
 * normal algorithm of placing the global thresholds around the
 * current HW value and if near the "edge" (HW value near 0 or
 * the hw_mask) of the resolution/max_range of a nld entry,
 * switching to the next entry, doesn't work, then this feature
 * allows more specific thresholds to work around the issue(s).
 * The lo/hi thresholds are used to set the "window" around the
 * current HW value and the regular algorithms apply using these
 * specific thresholds for the specific nld table entry.
 * The i_lo/i_hi thresholds are absolute HW values only used for
 * the specific nld table entry to determine when to switch to
 * the next nld table entry.  They can be thought of as
 * thresholds around the entire HW range for an nld table entry.
 * For example, let's say table entry 1 will only get a HW
 * reading from 4 to 1000 but the integration time is 3 seconds
 * and we would like to improve on the response (integration)
 * time by using the next entry in the table as much as possible
 * while tolerating the extra delay for more accuracy in low
 * light conditions. By setting the i_lo to 5 and i_hi to 999
 * the next corresponding table entry will be switched to when
 * the HW reads values < 5 or > 999 (hw values of 0 and hw_mask
 * will also trigger the switch).
 *
 * Note: lo/hi are relative thresholds based on the current HW
 *       value and the normal threshold algorithms apply.
 *       i_lo/i_hi are absolute threshold HW values and force
 *       switching to the next nld table entry when reached.
 *       Both work together as individual mechanisms.
 *
 * Note: nvs_light_of_dt will use the string
 *       %s_nld_thr_lo_%u and %s_nld_thr_hi_%u to search the DT
 *       for the lo/hi values where %s is typically "light" and
 *       %u is the index number into the nvs_light_dynamic
 *       table.
 *       For the i_lo/i_hi, the following string is used:
 *       %s_nld_thr_i_lo_%u and s_nld_thr_i_hi_%u
 *       If a device tree entry is not found the corresponding
 *       sensor_cfg thresh_lo/hi value is used instead.  This
 *       way only the table entry/ies with "issues" need the
 *       specific threshold(s) override.
 *       This goes for the i_lo/i_hi absolute values as well; if
 *       no DT entry is found the absolute values will be
 *       calculated based on sensor_cfg thresh_lo/hi values as
 *       would normally be calculated.
 *       If there isn't any DT thresholds for this feature then
 *       nvs_light_of_dt will disable this feature if
 *       nld_tbl_n > 0, by setting nld_thr to NULL.
 *
 * Note: If the driver supports it, the nld table thresholds can
 *       be changed at runtime by writing to the sysfs threshold
 *       nodes where bit 29 selects the threshold (0 = lo/hi and
 *       1 = i_lo/i_hi) and 28:24 of the value specifies the
 *       table index.
 */
struct nld_thresh {
	unsigned int lo;		/* low threshold */
	unsigned int hi;		/* high threshold */
	unsigned int i_lo;		/* index absolute low threshold */
	unsigned int i_hi;		/* index absolute high threshold */
};

/**
 * struct nvs_light - the common structure between the ALS
 * driver and the NVS common module for light.
 * @timestamp: Driver writes the timestamp HW was read.
 * @timestamp_report: NVS writes the timestamp used to report.
 * @lux: NVS writes the lux value that is reported.
 * @lux_max: The maximum lux value allowed.
 * Note: This is a WAR for compromised sensor implementations as
 *       described in struct nld_thresh.
 *       When this is 0 this WAR feature is disabled which is
 *       the default. The device tree string is %s_lux_maximum
 *       where %s is typically "light" but may be the part name.
 * @hw: Driver writes the value read from HW.
 * @hw_mask: Driver writes the HW value mask (maximum value).
 * @hw_thresh_lo: NVS writes the low threshold value needed.
 * @hw_thresh_hi: NVS writes the high threshold value needed.
 * @hw_limit_lo: NVS determines if HW at low limit.  Driver can
 *               use this for dynamic resolution if disabled in
 *               NVS.  See nld_tbl.
 * @hw_limit_hi: NVS determines if HW at high limit.  Driver can
 *               use this for dynamic resolution if disabled in
 *               NVS.  See nld_tbl.
 * @thresh_valid_lo: NVS determines if cfg.thresh_lo valid.
 * @thresh_valid_hi: NVS determines if cfg.thresh_hi valid.
 * @thresholds_valid: NVS determines if both thresholds valid.
 * @nld_i_change: NVS determined dynamic resolution changed.
 *                Driver needs to update HW.
 * @calibration_en: NVS determines if calibration is enabled.
 * @dynamic_resolution_dis: Driver can set this to true if the
 *                          resolution is static.
 * Note: dynamic resolution allows floating point to be
 *       calculated in the kernel by shifting up to integer the
 *       floating point significant amount. This allows
 *       real-time resolution changes without the NVS HAL having
 *       to synchronize to the actual resolution per data value.
 *       The scale.fval must be a 10 base value, e.g. 0.1, 0.01,
 *       ... 0.000001, etc. as the significant amount.  The NVS
 *       HAL will then convert the integer float-data to a float
 *       value by multiplying it with scale.
 * @poll_delay_ms: NVS writes the poll time needed if polling.
 * @delay_us: Driver writes the requested sample time.
 * @report: NVS writes the report count.
 * @nld_i: NVS writes index to nvs_light_dynamic table.
 * @nld_i_lo: Driver writes low index limit to nvs_light_dynamic
 *            table.
 *            Note: nvs_light_of_dt can be used.
 * @nld_i_hi: Driver writes high index limit to
 *            nvs_light_dynamic table.
 *            Note: nvs_light_of_dt can be used.
 * @nld_tbl_n: Driver writes ARRAY_SIZE of the nvs_light_dynamic
 *             table.  This allows runtime changes to nld_i_lo
 *             and nld_i_hi using resolution and max_range as
 *             well as nld_thr DT and runtime changes.
 *             Note: If this value is 0 then these features are
 *                   disabled - typically to lock values.
 * @nld_thr: See struct nld_thresh above.
 * @nld_tbl: Driver writes pointer to nvs_light_dynamic table.
 *           If this is NULL then dynamic resolution is disabled
 *           in NVS.
 * @cfg: Driver writes the sensor_cfg structure pointer.
 * @nvs_data: Driver writes the private pointer for handler.
 * @handler: Driver writes the handler pointer.
 */
struct nvs_light {
	s64 timestamp;			/* sample timestamp */
	s64 timestamp_report;		/* last reported timestamp */
	u32 lux;			/* calculated lux */
	u32 lux_max;			/* lux max limit WAR */
	u32 hw;				/* HW light value */
	u32 hw_mask;			/* HW light mask */
	u32 hw_thresh_lo;		/* HW low threshold value */
	u32 hw_thresh_hi;		/* HW high threshold value */
	bool hw_limit_lo;		/* hw < hw_thresh_lo or hw = 0 */
	bool hw_limit_hi;		/* hw > hw_thresh_hi or hw = hw_mask */
	bool thresh_valid_lo;		/* valid cfg.thresh_lo */
	bool thresh_valid_hi;		/* valid cfg.thresh_hi */
	bool thresholds_valid;		/* both thresholds valid */
	bool nld_i_change;		/* flag that dynamic index changed */
	bool calibration_en;		/* if calibration enabled */
	bool dynamic_resolution_dis;	/* disable float significance */
	unsigned int poll_delay_ms;	/* HW polling delay (ms) */
	unsigned int delay_us;		/* OS requested sample delay */
	unsigned int report;		/* report count */
	unsigned int nld_i;		/* index to light dynamic table */
	unsigned int nld_i_lo;		/* low index limit to dynamic table */
	unsigned int nld_i_hi;		/* high index limit to dynamic table */
	unsigned int nld_tbl_n;		/* nvs_light_dynamic tbl array size */
	struct nld_thresh *nld_thr;	/* ptr to nld_tbl thresholds */
	struct nvs_light_dynamic *nld_tbl; /* ptr to nvs_light_dynamic table */
	struct sensor_cfg *cfg;		/* pointer to sensor configuration */
	void *nvs_st;			/* NVS state data for NVS handler */
	int (*handler)(void *handle, void *buffer, s64 ts);
};

int nvs_light_read(struct nvs_light *nl);
int nvs_light_enable(struct nvs_light *nl);
int nvs_light_of_dt(struct nvs_light *nl, const struct device_node *np,
		    const char *dev_name);
int nvs_light_resolution(struct nvs_light *nl, int resolution);
int nvs_light_max_range(struct nvs_light *nl, int max_range);
int nvs_light_threshold_calibrate_lo(struct nvs_light *nl, int lo);
int nvs_light_threshold_calibrate_hi(struct nvs_light *nl, int hi);
ssize_t nvs_light_dbg(struct nvs_light *nl, char *buf);

#endif /* _NVS_LIGHT_H_ */