1 /*
2  * Copyright (c) 2025 Croxel
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #include "als31300.h"
7 
8 #include <zephyr/device.h>
9 #include <zephyr/drivers/sensor.h>
10 #include <zephyr/logging/log.h>
11 #include <zephyr/dsp/types.h>
12 
13 LOG_MODULE_DECLARE(als31300, CONFIG_SENSOR_LOG_LEVEL);
14 
15 /**
16  * @brief Encode channel flags for the given sensor channel
17  */
als31300_encode_channel(enum sensor_channel chan)18 static uint8_t als31300_encode_channel(enum sensor_channel chan)
19 {
20 	uint8_t encode_bmask = 0;
21 
22 	switch (chan) {
23 	case SENSOR_CHAN_MAGN_X:
24 		encode_bmask |= BIT(0);
25 		break;
26 	case SENSOR_CHAN_MAGN_Y:
27 		encode_bmask |= BIT(1);
28 		break;
29 	case SENSOR_CHAN_MAGN_Z:
30 		encode_bmask |= BIT(2);
31 		break;
32 	case SENSOR_CHAN_MAGN_XYZ:
33 		encode_bmask |= BIT(0) | BIT(1) | BIT(2);
34 		break;
35 	case SENSOR_CHAN_AMBIENT_TEMP:
36 		encode_bmask |= BIT(3);
37 		break;
38 	case SENSOR_CHAN_ALL:
39 		encode_bmask |= BIT(0) | BIT(1) | BIT(2) | BIT(3);
40 		break;
41 	default:
42 		break;
43 	}
44 
45 	return encode_bmask;
46 }
47 
48 /**
49  * @brief Convert raw magnetic field value to Q31 format
50  * @param raw_value Signed 12-bit magnetic field value
51  * @param q31_out Pointer to store Q31 value
52  */
als31300_convert_raw_to_q31_magn(int16_t raw_value,q31_t * q31_out)53 static void als31300_convert_raw_to_q31_magn(int16_t raw_value, q31_t *q31_out)
54 {
55 	/* Convert to microgauss using integer arithmetic */
56 	int32_t microgauss = als31300_convert_to_gauss(raw_value);
57 
58 	/* Convert to Q31 format: Q31 = (value * 2^shift) / 1000000
59 	 * For magnetic field, we use shift=16, so the full scale is ±2^(31-16) = ±32768 gauss
60 	 * This gives us good resolution for the ±500G range of the ALS31300
61 	 * microgauss * 2^16 / 1000000 = microgauss * 65536 / 1000000
62 	 */
63 	*q31_out = (q31_t)(((int64_t)microgauss << ALS31300_MAGN_SHIFT) / 1000000);
64 }
65 
66 /**
67  * @brief Convert raw temperature value to Q31 format
68  * @param raw_temp 12-bit raw temperature value
69  * @param q31_out Pointer to store Q31 value
70  */
als31300_convert_temp_to_q31(uint16_t raw_temp,q31_t * q31_out)71 static void als31300_convert_temp_to_q31(uint16_t raw_temp, q31_t *q31_out)
72 {
73 	/* Convert to microcelsius using integer arithmetic */
74 	int32_t microcelsius = als31300_convert_temperature(raw_temp);
75 
76 	/* Convert to Q31 format: Q31 = (value * 2^shift) / 1000000
77 	 * For temperature, we use shift=16, so the full scale is ±2^(31-16) = ±32768°C
78 	 * This gives us good resolution for typical temperature ranges (-40°C to +125°C)
79 	 * microcelsius * 2^16 / 1000000 = microcelsius * 65536 / 1000000
80 	 */
81 	*q31_out = (q31_t)(((int64_t)microcelsius << ALS31300_TEMP_SHIFT) / 1000000);
82 }
83 
84 /**
85  * @brief Get frame count for decoder
86  */
als31300_decoder_get_frame_count(const uint8_t * buffer,struct sensor_chan_spec chan_spec,uint16_t * frame_count)87 static int als31300_decoder_get_frame_count(const uint8_t *buffer,
88 					    struct sensor_chan_spec chan_spec,
89 					    uint16_t *frame_count)
90 {
91 	const struct als31300_encoded_data *edata = (const struct als31300_encoded_data *)buffer;
92 
93 	if (chan_spec.chan_idx != 0) {
94 		return -ENOTSUP;
95 	}
96 
97 	uint8_t channel_request = als31300_encode_channel(chan_spec.chan_type);
98 
99 	/* Filter unknown channels and having no data */
100 	if ((edata->header.channels & channel_request) != channel_request) {
101 		return -ENODATA;
102 	}
103 
104 	*frame_count = 1;
105 	return 0;
106 }
107 
108 /**
109  * @brief Get size info for decoder
110  */
als31300_decoder_get_size_info(struct sensor_chan_spec chan_spec,size_t * base_size,size_t * frame_size)111 static int als31300_decoder_get_size_info(struct sensor_chan_spec chan_spec, size_t *base_size,
112 					  size_t *frame_size)
113 {
114 	switch (chan_spec.chan_type) {
115 	case SENSOR_CHAN_MAGN_X:
116 	case SENSOR_CHAN_MAGN_Y:
117 	case SENSOR_CHAN_MAGN_Z:
118 	case SENSOR_CHAN_MAGN_XYZ:
119 		*base_size = sizeof(struct sensor_three_axis_data);
120 		*frame_size = sizeof(struct sensor_three_axis_sample_data);
121 		return 0;
122 	case SENSOR_CHAN_AMBIENT_TEMP:
123 		*base_size = sizeof(struct sensor_q31_data);
124 		*frame_size = sizeof(struct sensor_q31_sample_data);
125 		return 0;
126 	default:
127 		return -ENOTSUP;
128 	}
129 }
130 
131 /**
132  * @brief Decode function for RTIO
133  */
als31300_decoder_decode(const uint8_t * buffer,struct sensor_chan_spec chan_spec,uint32_t * fit,uint16_t max_count,void * data_out)134 static int als31300_decoder_decode(const uint8_t *buffer, struct sensor_chan_spec chan_spec,
135 				   uint32_t *fit, uint16_t max_count, void *data_out)
136 {
137 	const struct als31300_encoded_data *edata = (const struct als31300_encoded_data *)buffer;
138 
139 	if (*fit != 0) {
140 		return 0;
141 	}
142 
143 	/* Parse raw payload data using common helper */
144 	struct als31300_readings readings;
145 
146 	als31300_parse_registers(edata->payload, &readings);
147 
148 	switch (chan_spec.chan_type) {
149 	case SENSOR_CHAN_MAGN_X:
150 	case SENSOR_CHAN_MAGN_Y:
151 	case SENSOR_CHAN_MAGN_Z:
152 	case SENSOR_CHAN_MAGN_XYZ: {
153 		struct sensor_three_axis_data *out = data_out;
154 
155 		out->header.base_timestamp_ns = edata->header.timestamp;
156 		out->header.reading_count = 1;
157 		out->shift = ALS31300_MAGN_SHIFT;
158 
159 		/* Convert raw readings to Q31 format */
160 		als31300_convert_raw_to_q31_magn(readings.x, &out->readings[0].x);
161 		als31300_convert_raw_to_q31_magn(readings.y, &out->readings[0].y);
162 		als31300_convert_raw_to_q31_magn(readings.z, &out->readings[0].z);
163 		*fit = 1;
164 		return 1;
165 	}
166 	case SENSOR_CHAN_AMBIENT_TEMP: {
167 		struct sensor_q31_data *out = data_out;
168 
169 		out->header.base_timestamp_ns = edata->header.timestamp;
170 		out->header.reading_count = 1;
171 		out->shift = ALS31300_TEMP_SHIFT;
172 
173 		als31300_convert_temp_to_q31(readings.temp, &out->readings[0].temperature);
174 		*fit = 1;
175 		return 1;
176 	}
177 	default:
178 		return -ENOTSUP;
179 	}
180 }
181 
182 SENSOR_DECODER_API_DT_DEFINE() = {
183 	.get_frame_count = als31300_decoder_get_frame_count,
184 	.get_size_info = als31300_decoder_get_size_info,
185 	.decode = als31300_decoder_decode,
186 };
187 
als31300_get_decoder(const struct device * dev,const struct sensor_decoder_api ** decoder)188 int als31300_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder)
189 {
190 	ARG_UNUSED(dev);
191 	*decoder = &SENSOR_DECODER_NAME();
192 	return 0;
193 }
194