1 /*
2 * Copyright (c) 2025 Croxel Inc.
3 * Copyright (c) 2025 CogniPilot Foundation
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #define DT_DRV_COMPAT pixart_paa3905
9
10 #include <zephyr/drivers/sensor.h>
11 #include <zephyr/drivers/spi.h>
12 #include <zephyr/rtio/rtio.h>
13
14 #include "paa3905.h"
15 #include "paa3905_bus.h"
16 #include "paa3905_stream.h"
17 #include "paa3905_decoder.h"
18
19 #include <zephyr/logging/log.h>
20 LOG_MODULE_REGISTER(PAA3905, CONFIG_SENSOR_LOG_LEVEL);
21
22 /* Helper struct used to set reg-val sequences for op-modes */
23 struct reg_val_pair {
24 uint8_t reg;
25 uint8_t val;
26 };
27
paa3905_complete_result(struct rtio * ctx,const struct rtio_sqe * sqe,void * arg)28 static void paa3905_complete_result(struct rtio *ctx,
29 const struct rtio_sqe *sqe,
30 void *arg)
31 {
32 struct rtio_iodev_sqe *iodev_sqe = (struct rtio_iodev_sqe *)sqe->userdata;
33 struct rtio_cqe *cqe;
34 int err = 0;
35
36 do {
37 cqe = rtio_cqe_consume(ctx);
38 if (cqe != NULL) {
39 err = cqe->result;
40 rtio_cqe_release(ctx, cqe);
41 }
42 } while (cqe != NULL);
43
44 if (err) {
45 rtio_iodev_sqe_err(iodev_sqe, err);
46 } else {
47 rtio_iodev_sqe_ok(iodev_sqe, 0);
48 }
49
50 LOG_DBG("One-shot fetch completed");
51 }
52
paa3905_submit_one_shot(const struct device * dev,struct rtio_iodev_sqe * iodev_sqe)53 static void paa3905_submit_one_shot(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
54 {
55 const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data;
56 const struct sensor_chan_spec *const channels = cfg->channels;
57 struct paa3905_data *data = dev->data;
58 const size_t num_channels = cfg->count;
59 uint32_t min_buf_len = sizeof(struct paa3905_encoded_data);
60 int err;
61 uint8_t *buf;
62 uint32_t buf_len;
63 struct paa3905_encoded_data *edata;
64
65 err = rtio_sqe_rx_buf(iodev_sqe, min_buf_len, min_buf_len, &buf, &buf_len);
66 if (err) {
67 LOG_ERR("Failed to get a read buffer of size %u bytes", min_buf_len);
68 rtio_iodev_sqe_err(iodev_sqe, err);
69 return;
70 }
71
72 edata = (struct paa3905_encoded_data *)buf;
73
74 err = paa3905_encode(dev, channels, num_channels, buf);
75 if (err != 0) {
76 LOG_ERR("Failed to encode sensor data");
77 rtio_iodev_sqe_err(iodev_sqe, err);
78 return;
79 }
80
81 struct rtio_sqe *write_sqe = rtio_sqe_acquire(data->rtio.ctx);
82 struct rtio_sqe *read_sqe = rtio_sqe_acquire(data->rtio.ctx);
83 struct rtio_sqe *complete_sqe = rtio_sqe_acquire(data->rtio.ctx);
84
85 if (!write_sqe || !read_sqe | !complete_sqe) {
86 LOG_ERR("Failed to acquire RTIO SQEs");
87 rtio_iodev_sqe_err(iodev_sqe, -ENOMEM);
88 return;
89 }
90
91 uint8_t val = REG_BURST_READ | REG_SPI_READ_BIT;
92
93 rtio_sqe_prep_tiny_write(write_sqe,
94 data->rtio.iodev,
95 RTIO_PRIO_HIGH,
96 &val,
97 1,
98 NULL);
99 write_sqe->flags |= RTIO_SQE_TRANSACTION;
100
101 rtio_sqe_prep_read(read_sqe,
102 data->rtio.iodev,
103 RTIO_PRIO_HIGH,
104 edata->buf,
105 sizeof(edata->buf),
106 NULL);
107 read_sqe->flags |= RTIO_SQE_CHAINED;
108
109 rtio_sqe_prep_callback_no_cqe(complete_sqe,
110 paa3905_complete_result,
111 (void *)dev,
112 iodev_sqe);
113
114 rtio_submit(data->rtio.ctx, 0);
115 }
116
paa3905_submit(const struct device * dev,struct rtio_iodev_sqe * iodev_sqe)117 static void paa3905_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
118 {
119 const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data;
120
121 if (!cfg->is_streaming) {
122 paa3905_submit_one_shot(dev, iodev_sqe);
123 } else if (IS_ENABLED(CONFIG_PAA3905_STREAM)) {
124 paa3905_stream_submit(dev, iodev_sqe);
125 } else {
126 LOG_ERR("Streaming not supported");
127 rtio_iodev_sqe_err(iodev_sqe, -ENOTSUP);
128 }
129 }
130
131 static DEVICE_API(sensor, paa3905_driver_api) = {
132 .submit = paa3905_submit,
133 .get_decoder = paa3905_get_decoder,
134 };
135
detection_mode_standard(const struct device * dev)136 static int detection_mode_standard(const struct device *dev)
137 {
138 int err;
139 /** As per datasheet, set of values configure the sensor in Standard mode. */
140 const static struct reg_val_pair paa3905_detection_mode_std[] = {
141 {.reg = 0x7F, .val = 0x00}, {.reg = 0x51, .val = 0xFF}, {.reg = 0x4E, .val = 0x2A},
142 {.reg = 0x66, .val = 0x3E}, {.reg = 0x7F, .val = 0x14}, {.reg = 0x7E, .val = 0x71},
143 {.reg = 0x55, .val = 0x00}, {.reg = 0x59, .val = 0x00}, {.reg = 0x6F, .val = 0x2C},
144 {.reg = 0x7F, .val = 0x05}, {.reg = 0x4D, .val = 0xAC}, {.reg = 0x4E, .val = 0x32},
145 {.reg = 0x7F, .val = 0x09}, {.reg = 0x5C, .val = 0xAF}, {.reg = 0x5F, .val = 0xAF},
146 {.reg = 0x70, .val = 0x08}, {.reg = 0x71, .val = 0x04}, {.reg = 0x72, .val = 0x06},
147 {.reg = 0x74, .val = 0x3C}, {.reg = 0x75, .val = 0x28}, {.reg = 0x76, .val = 0x20},
148 {.reg = 0x4E, .val = 0xBF}, {.reg = 0x7F, .val = 0x03}, {.reg = 0x64, .val = 0x14},
149 {.reg = 0x65, .val = 0x0A}, {.reg = 0x66, .val = 0x10}, {.reg = 0x55, .val = 0x3C},
150 {.reg = 0x56, .val = 0x28}, {.reg = 0x57, .val = 0x20}, {.reg = 0x4A, .val = 0x2D},
151 {.reg = 0x4B, .val = 0x2D}, {.reg = 0x4E, .val = 0x4B}, {.reg = 0x69, .val = 0xFA},
152 {.reg = 0x7F, .val = 0x05}, {.reg = 0x69, .val = 0x1F}, {.reg = 0x47, .val = 0x1F},
153 {.reg = 0x48, .val = 0x0C}, {.reg = 0x5A, .val = 0x20}, {.reg = 0x75, .val = 0x0F},
154 {.reg = 0x4A, .val = 0x0F}, {.reg = 0x42, .val = 0x02}, {.reg = 0x45, .val = 0x03},
155 {.reg = 0x65, .val = 0x00}, {.reg = 0x67, .val = 0x76}, {.reg = 0x68, .val = 0x76},
156 {.reg = 0x6A, .val = 0xC5}, {.reg = 0x43, .val = 0x00}, {.reg = 0x7F, .val = 0x06},
157 {.reg = 0x4A, .val = 0x18}, {.reg = 0x4B, .val = 0x0C}, {.reg = 0x4C, .val = 0x0C},
158 {.reg = 0x4D, .val = 0x0C}, {.reg = 0x46, .val = 0x0A}, {.reg = 0x59, .val = 0xCD},
159 {.reg = 0x7F, .val = 0x0A}, {.reg = 0x4A, .val = 0x2A}, {.reg = 0x48, .val = 0x96},
160 {.reg = 0x52, .val = 0xB4}, {.reg = 0x7F, .val = 0x00}, {.reg = 0x5B, .val = 0xA0},
161 };
162
163 for (size_t i = 0 ; i < ARRAY_SIZE(paa3905_detection_mode_std) ; i++) {
164 err = paa3905_bus_write(dev,
165 paa3905_detection_mode_std[i].reg,
166 &paa3905_detection_mode_std[i].val,
167 1);
168 if (err) {
169 LOG_ERR("Failed to write detection mode standard");
170 return err;
171 }
172 }
173
174 return 0;
175 }
176
paa3905_configure(const struct device * dev)177 static int paa3905_configure(const struct device *dev)
178 {
179 const struct paa3905_config *cfg = dev->config;
180 uint8_t val;
181 int err;
182
183 /* Configure registers for Standard detection mode */
184 err = detection_mode_standard(dev);
185 if (err) {
186 LOG_ERR("Failed to configure detection mode");
187 return err;
188 }
189
190 val = cfg->resolution;
191 err = paa3905_bus_write(dev, REG_RESOLUTION, &val, 1);
192 if (err) {
193 LOG_ERR("Failed to configure resolution");
194 return err;
195 }
196
197 if (cfg->led_control) {
198 struct reg_val_pair led_control_regs[] = {
199 {.reg = 0x7F, .val = 0x14},
200 {.reg = 0x6F, .val = 0x0C},
201 {.reg = 0x7F, .val = 0x00},
202 };
203
204 for (size_t i = 0 ; i < ARRAY_SIZE(led_control_regs) ; i++) {
205 err = paa3905_bus_write(dev,
206 led_control_regs[i].reg,
207 &led_control_regs[i].val,
208 1);
209 if (err) {
210 LOG_ERR("Failed to write LED control reg");
211 return err;
212 }
213 }
214 }
215
216 return 0;
217 }
218
paa3905_recover(const struct device * dev)219 int paa3905_recover(const struct device *dev)
220 {
221 int err;
222 uint8_t val;
223
224 /* Write 0x5A to Power up reset reg */
225 val = POWER_UP_RESET_VAL;
226 err = paa3905_bus_write(dev, REG_POWER_UP_RESET, &val, 1);
227 if (err) {
228 LOG_ERR("Failed to write Power up reset reg");
229 return err;
230 }
231 /* As per datasheet, writing power-up reset requires 1-ms afterwards. */
232 k_sleep(K_MSEC(1));
233
234 /* Configure registers for Standard detection mode */
235 err = paa3905_configure(dev);
236 if (err) {
237 LOG_ERR("Failed to configure");
238 return err;
239 }
240
241 return err;
242 }
243
paa3905_init(const struct device * dev)244 static int paa3905_init(const struct device *dev)
245 {
246 int err;
247 uint8_t val;
248 uint8_t motion_data[6];
249
250 /* Power-up sequence delay */
251 k_sleep(K_MSEC(140));
252
253 /* Read Product ID */
254 err = paa3905_bus_read(dev, REG_PRODUCT_ID, &val, 1);
255 if (err) {
256 LOG_ERR("Failed to read Product ID");
257 return err;
258 } else if (val != PRODUCT_ID) {
259 LOG_ERR("Invalid Product ID: 0x%02X", val);
260 return -EIO;
261 }
262
263 /* Write 0x5A to Power up reset reg */
264 val = POWER_UP_RESET_VAL;
265 err = paa3905_bus_write(dev, REG_POWER_UP_RESET, &val, 1);
266 if (err) {
267 LOG_ERR("Failed to write Power up reset reg");
268 return err;
269 }
270 /* As per datasheet, writing power-up reset requires 1-ms afterwards. */
271 k_sleep(K_MSEC(1));
272
273 /* Read reg's 0x02-0x06 to clear motion data. */
274 err = paa3905_bus_read(dev, REG_MOTION, motion_data, sizeof(motion_data));
275 if (err) {
276 LOG_ERR("Failed to read motion data");
277 return err;
278 }
279
280 if (IS_ENABLED(CONFIG_PAA3905_STREAM)) {
281 err = paa3905_stream_init(dev);
282 if (err) {
283 LOG_ERR("Failed to initialize streaming");
284 return err;
285 }
286 }
287
288 err = paa3905_configure(dev);
289 if (err) {
290 LOG_ERR("Failed to configure");
291 return err;
292 }
293
294 return 0;
295 }
296
297 #define PAA3905_INIT(inst) \
298 \
299 BUILD_ASSERT(DT_PROP(DT_DRV_INST(inst), resolution) > 0 && \
300 DT_PROP(DT_DRV_INST(inst), resolution) <= 0xFF, \
301 "Resolution must be in range 1-255"); \
302 \
303 RTIO_DEFINE(paa3905_rtio_ctx_##inst, 8, 8); \
304 SPI_DT_IODEV_DEFINE(paa3905_bus_##inst, \
305 DT_DRV_INST(inst), \
306 SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_TRANSFER_MSB, \
307 0U); \
308 \
309 static const struct paa3905_config paa3905_cfg_##inst = { \
310 .int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0}), \
311 .backup_timer_period = DT_PROP(DT_DRV_INST(inst), backup_timer_ms), \
312 .resolution = DT_PROP(DT_DRV_INST(inst), resolution), \
313 .led_control = DT_PROP_OR(DT_DRV_INST(inst), led_control, false), \
314 }; \
315 \
316 static struct paa3905_data paa3905_data_##inst = { \
317 .rtio = { \
318 .iodev = &paa3905_bus_##inst, \
319 .ctx = &paa3905_rtio_ctx_##inst, \
320 }, \
321 }; \
322 \
323 SENSOR_DEVICE_DT_INST_DEFINE(inst, \
324 paa3905_init, \
325 NULL, \
326 &paa3905_data_##inst, \
327 &paa3905_cfg_##inst, \
328 POST_KERNEL, \
329 CONFIG_SENSOR_INIT_PRIORITY, \
330 &paa3905_driver_api);
331
332 DT_INST_FOREACH_STATUS_OKAY(PAA3905_INIT)
333