1 /*
2 * Copyright (c) 2024 Charles Dias <charlesdias.cd@outlook.com>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT st_stm32_dcmi
8
9 #include <errno.h>
10
11 #include <zephyr/kernel.h>
12 #include <zephyr/irq.h>
13 #include <zephyr/logging/log.h>
14 #include <zephyr/drivers/video.h>
15 #include <zephyr/drivers/pinctrl.h>
16 #include <zephyr/drivers/clock_control/stm32_clock_control.h>
17 #include <zephyr/drivers/clock_control.h>
18 #include <zephyr/drivers/dma.h>
19 #include <zephyr/drivers/dma/dma_stm32.h>
20
21 #include <stm32_ll_dma.h>
22
23 LOG_MODULE_REGISTER(video_stm32_dcmi, CONFIG_VIDEO_LOG_LEVEL);
24
25 #if CONFIG_VIDEO_BUFFER_POOL_NUM_MAX < 2
26 #error "The minimum required number of buffers for video_stm32 is 2"
27 #endif
28
29 typedef void (*irq_config_func_t)(const struct device *dev);
30
31 struct stream {
32 DMA_TypeDef *reg;
33 const struct device *dma_dev;
34 uint32_t channel;
35 struct dma_config cfg;
36 };
37
38 struct video_stm32_dcmi_data {
39 const struct device *dev;
40 DCMI_HandleTypeDef hdcmi;
41 struct video_format fmt;
42 struct k_fifo fifo_in;
43 struct k_fifo fifo_out;
44 uint32_t pixel_format;
45 uint32_t height;
46 uint32_t width;
47 uint32_t pitch;
48 struct video_buffer *vbuf;
49 };
50
51 struct video_stm32_dcmi_config {
52 struct stm32_pclken pclken;
53 irq_config_func_t irq_config;
54 const struct pinctrl_dev_config *pctrl;
55 const struct device *sensor_dev;
56 const struct stream dma;
57 };
58
HAL_DCMI_ErrorCallback(DCMI_HandleTypeDef * hdcmi)59 void HAL_DCMI_ErrorCallback(DCMI_HandleTypeDef *hdcmi)
60 {
61 LOG_WRN("%s", __func__);
62 }
63
HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef * hdcmi)64 void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi)
65 {
66 struct video_stm32_dcmi_data *dev_data =
67 CONTAINER_OF(hdcmi, struct video_stm32_dcmi_data, hdcmi);
68 struct video_buffer *vbuf;
69
70 HAL_DCMI_Suspend(hdcmi);
71
72 vbuf = k_fifo_get(&dev_data->fifo_in, K_NO_WAIT);
73
74 if (vbuf == NULL) {
75 LOG_DBG("Failed to get buffer from fifo");
76 goto resume;
77 }
78
79 vbuf->timestamp = k_uptime_get_32();
80 memcpy(vbuf->buffer, dev_data->vbuf->buffer, vbuf->bytesused);
81
82 k_fifo_put(&dev_data->fifo_out, vbuf);
83
84 resume:
85 HAL_DCMI_Resume(hdcmi);
86 }
87
stm32_dcmi_isr(const struct device * dev)88 static void stm32_dcmi_isr(const struct device *dev)
89 {
90 struct video_stm32_dcmi_data *data = dev->data;
91
92 HAL_DCMI_IRQHandler(&data->hdcmi);
93 }
94
dmci_dma_callback(const struct device * dev,void * arg,uint32_t channel,int status)95 static void dmci_dma_callback(const struct device *dev, void *arg,
96 uint32_t channel, int status)
97 {
98 DMA_HandleTypeDef *hdma = arg;
99
100 ARG_UNUSED(dev);
101
102 if (status < 0) {
103 LOG_ERR("DMA callback error with channel %d.", channel);
104 }
105
106 HAL_DMA_IRQHandler(hdma);
107 }
108
HAL_DMA_ErrorCallback(DMA_HandleTypeDef * hdma)109 void HAL_DMA_ErrorCallback(DMA_HandleTypeDef *hdma)
110 {
111 LOG_WRN("%s", __func__);
112 }
113
stm32_dma_init(const struct device * dev)114 static int stm32_dma_init(const struct device *dev)
115 {
116 struct video_stm32_dcmi_data *data = dev->data;
117 const struct video_stm32_dcmi_config *config = dev->config;
118 int ret;
119
120 /* Check if the DMA device is ready */
121 if (!device_is_ready(config->dma.dma_dev)) {
122 LOG_ERR("%s DMA device not ready", config->dma.dma_dev->name);
123 return -ENODEV;
124 }
125
126 /*
127 * DMA configuration
128 * Due to use of QSPI HAL API in current driver,
129 * both HAL and Zephyr DMA drivers should be configured.
130 * The required configuration for Zephyr DMA driver should only provide
131 * the minimum information to inform the DMA slot will be in used and
132 * how to route callbacks.
133 */
134 struct dma_config dma_cfg = config->dma.cfg;
135 static DMA_HandleTypeDef hdma;
136
137 /* Proceed to the minimum Zephyr DMA driver init */
138 dma_cfg.user_data = &hdma;
139 /* HACK: This field is used to inform driver that it is overridden */
140 dma_cfg.linked_channel = STM32_DMA_HAL_OVERRIDE;
141 /* Because of the STREAM OFFSET, the DMA channel given here is from 1 - 8 */
142 ret = dma_config(config->dma.dma_dev,
143 config->dma.channel + STM32_DMA_STREAM_OFFSET, &dma_cfg);
144 if (ret != 0) {
145 LOG_ERR("Failed to configure DMA channel %d",
146 config->dma.channel + STM32_DMA_STREAM_OFFSET);
147 return ret;
148 }
149
150 /*** Configure the DMA ***/
151 /* Set the parameters to be configured */
152 hdma.Init.Request = DMA_REQUEST_DCMI;
153 hdma.Init.Direction = DMA_PERIPH_TO_MEMORY;
154 hdma.Init.PeriphInc = DMA_PINC_DISABLE;
155 hdma.Init.MemInc = DMA_MINC_ENABLE;
156 hdma.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
157 hdma.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
158 hdma.Init.Mode = DMA_CIRCULAR;
159 hdma.Init.Priority = DMA_PRIORITY_HIGH;
160 hdma.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
161
162 hdma.Instance = __LL_DMA_GET_STREAM_INSTANCE(config->dma.reg,
163 config->dma.channel);
164
165 /* Initialize DMA HAL */
166 __HAL_LINKDMA(&data->hdcmi, DMA_Handle, hdma);
167
168 if (HAL_DMA_Init(&hdma) != HAL_OK) {
169 LOG_ERR("DCMI DMA Init failed");
170 return -EIO;
171 }
172
173 return 0;
174 }
175
stm32_dcmi_enable_clock(const struct device * dev)176 static int stm32_dcmi_enable_clock(const struct device *dev)
177 {
178 const struct video_stm32_dcmi_config *config = dev->config;
179 const struct device *dcmi_clock = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
180 int err;
181
182 if (!device_is_ready(dcmi_clock)) {
183 LOG_ERR("clock control device not ready");
184 return -ENODEV;
185 }
186
187 /* Turn on DCMI peripheral clock */
188 err = clock_control_on(dcmi_clock, (clock_control_subsys_t *) &config->pclken);
189 if (err < 0) {
190 LOG_ERR("Failed to enable DCMI clock. Error %d", err);
191 return err;
192 }
193
194 return 0;
195 }
196
video_stm32_dcmi_set_fmt(const struct device * dev,enum video_endpoint_id ep,struct video_format * fmt)197 static int video_stm32_dcmi_set_fmt(const struct device *dev,
198 enum video_endpoint_id ep,
199 struct video_format *fmt)
200 {
201 const struct video_stm32_dcmi_config *config = dev->config;
202 struct video_stm32_dcmi_data *data = dev->data;
203 unsigned int bpp = video_bits_per_pixel(fmt->pixelformat) / BITS_PER_BYTE;
204
205 if (bpp == 0 || (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL)) {
206 return -EINVAL;
207 }
208
209 if ((fmt->pitch * fmt->height) > CONFIG_VIDEO_BUFFER_POOL_SZ_MAX) {
210 return -EINVAL;
211 }
212
213 data->pixel_format = fmt->pixelformat;
214 data->pitch = fmt->pitch;
215 data->height = fmt->height;
216 data->width = fmt->width;
217
218 if (video_set_format(config->sensor_dev, ep, fmt)) {
219 return -EIO;
220 }
221
222 return 0;
223 }
224
video_stm32_dcmi_get_fmt(const struct device * dev,enum video_endpoint_id ep,struct video_format * fmt)225 static int video_stm32_dcmi_get_fmt(const struct device *dev,
226 enum video_endpoint_id ep,
227 struct video_format *fmt)
228 {
229 struct video_stm32_dcmi_data *data = dev->data;
230 const struct video_stm32_dcmi_config *config = dev->config;
231
232 if (fmt == NULL || (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL)) {
233 return -EINVAL;
234 }
235
236 if (!video_get_format(config->sensor_dev, ep, fmt)) {
237 /* align DCMI with sensor fmt */
238 return video_stm32_dcmi_set_fmt(dev, ep, fmt);
239 }
240
241 fmt->pixelformat = data->pixel_format;
242 fmt->height = data->height;
243 fmt->width = data->width;
244 fmt->pitch = data->pitch;
245
246 return 0;
247 }
248
video_stm32_dcmi_set_stream(const struct device * dev,bool enable)249 static int video_stm32_dcmi_set_stream(const struct device *dev, bool enable)
250 {
251 int err;
252 struct video_stm32_dcmi_data *data = dev->data;
253 const struct video_stm32_dcmi_config *config = dev->config;
254
255 if (!enable) {
256 if (video_stream_stop(config->sensor_dev)) {
257 return -EIO;
258 }
259
260 err = HAL_DCMI_Stop(&data->hdcmi);
261 if (err != HAL_OK) {
262 LOG_ERR("Failed to stop DCMI");
263 return -EIO;
264 }
265
266 /* Release the video buffer allocated when start streaming */
267 k_fifo_put(&data->fifo_in, data->vbuf);
268
269 return 0;
270 }
271
272 data->vbuf = k_fifo_get(&data->fifo_in, K_NO_WAIT);
273
274 if (data->vbuf == NULL) {
275 LOG_ERR("Failed to dequeue a DCMI buffer.");
276 return -ENOMEM;
277 }
278
279 err = HAL_DCMI_Start_DMA(&data->hdcmi, DCMI_MODE_CONTINUOUS,
280 (uint32_t)data->vbuf->buffer, data->vbuf->bytesused / 4);
281 if (err != HAL_OK) {
282 LOG_ERR("Failed to start DCMI DMA");
283 return -EIO;
284 }
285
286 if (video_stream_start(config->sensor_dev)) {
287 return -EIO;
288 }
289
290 return 0;
291 }
292
video_stm32_dcmi_enqueue(const struct device * dev,enum video_endpoint_id ep,struct video_buffer * vbuf)293 static int video_stm32_dcmi_enqueue(const struct device *dev,
294 enum video_endpoint_id ep,
295 struct video_buffer *vbuf)
296 {
297 struct video_stm32_dcmi_data *data = dev->data;
298 const uint32_t buffer_size = data->pitch * data->height;
299
300 if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
301 return -EINVAL;
302 }
303
304 if (buffer_size > vbuf->size) {
305 return -EINVAL;
306 }
307
308 vbuf->bytesused = buffer_size;
309 vbuf->line_offset = 0;
310
311 k_fifo_put(&data->fifo_in, vbuf);
312
313 return 0;
314 }
315
video_stm32_dcmi_dequeue(const struct device * dev,enum video_endpoint_id ep,struct video_buffer ** vbuf,k_timeout_t timeout)316 static int video_stm32_dcmi_dequeue(const struct device *dev,
317 enum video_endpoint_id ep,
318 struct video_buffer **vbuf,
319 k_timeout_t timeout)
320 {
321 struct video_stm32_dcmi_data *data = dev->data;
322
323 if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
324 return -EINVAL;
325 }
326
327 *vbuf = k_fifo_get(&data->fifo_out, timeout);
328 if (*vbuf == NULL) {
329 return -EAGAIN;
330 }
331
332 return 0;
333 }
334
video_stm32_dcmi_get_caps(const struct device * dev,enum video_endpoint_id ep,struct video_caps * caps)335 static int video_stm32_dcmi_get_caps(const struct device *dev,
336 enum video_endpoint_id ep,
337 struct video_caps *caps)
338 {
339 const struct video_stm32_dcmi_config *config = dev->config;
340 int ret = -ENODEV;
341
342 if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
343 return -EINVAL;
344 }
345
346 /* DCMI produces full frames */
347 caps->min_line_count = caps->max_line_count = LINE_COUNT_HEIGHT;
348
349 /* Forward the message to the sensor device */
350 ret = video_get_caps(config->sensor_dev, ep, caps);
351
352 return ret;
353 }
354
video_stm32_dcmi_set_ctrl(const struct device * dev,unsigned int cid,void * value)355 static inline int video_stm32_dcmi_set_ctrl(const struct device *dev, unsigned int cid, void *value)
356 {
357 const struct video_stm32_dcmi_config *config = dev->config;
358 int ret;
359
360 /* Forward to source dev if any */
361 ret = video_set_ctrl(config->sensor_dev, cid, value);
362
363 return ret;
364 }
365
video_stm32_dcmi_get_ctrl(const struct device * dev,unsigned int cid,void * value)366 static inline int video_stm32_dcmi_get_ctrl(const struct device *dev, unsigned int cid, void *value)
367 {
368 const struct video_stm32_dcmi_config *config = dev->config;
369 int ret;
370
371 /* Forward to source dev if any */
372 ret = video_get_ctrl(config->sensor_dev, cid, value);
373
374 return ret;
375 }
376
377 static DEVICE_API(video, video_stm32_dcmi_driver_api) = {
378 .set_format = video_stm32_dcmi_set_fmt,
379 .get_format = video_stm32_dcmi_get_fmt,
380 .set_stream = video_stm32_dcmi_set_stream,
381 .enqueue = video_stm32_dcmi_enqueue,
382 .dequeue = video_stm32_dcmi_dequeue,
383 .get_caps = video_stm32_dcmi_get_caps,
384 .set_ctrl = video_stm32_dcmi_set_ctrl,
385 .get_ctrl = video_stm32_dcmi_get_ctrl,
386 };
387
video_stm32_dcmi_irq_config_func(const struct device * dev)388 static void video_stm32_dcmi_irq_config_func(const struct device *dev)
389 {
390 IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
391 stm32_dcmi_isr, DEVICE_DT_INST_GET(0), 0);
392 irq_enable(DT_INST_IRQN(0));
393 }
394
395 #define DCMI_DMA_CHANNEL_INIT(index, src_dev, dest_dev) \
396 .dma_dev = DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_IDX(index, 0)), \
397 .channel = DT_INST_DMAS_CELL_BY_IDX(index, 0, channel), \
398 .reg = (DMA_TypeDef *)DT_REG_ADDR( \
399 DT_PHANDLE_BY_IDX(DT_DRV_INST(0), dmas, 0)), \
400 .cfg = { \
401 .dma_slot = STM32_DMA_SLOT_BY_IDX(index, 0, slot), \
402 .channel_direction = STM32_DMA_CONFIG_DIRECTION( \
403 STM32_DMA_CHANNEL_CONFIG_BY_IDX(index, 0)), \
404 .source_data_size = STM32_DMA_CONFIG_##src_dev##_DATA_SIZE( \
405 STM32_DMA_CHANNEL_CONFIG_BY_IDX(index, 0)), \
406 .dest_data_size = STM32_DMA_CONFIG_##dest_dev##_DATA_SIZE( \
407 STM32_DMA_CHANNEL_CONFIG_BY_IDX(index, 0)), \
408 .source_burst_length = 1, /* SINGLE transfer */ \
409 .dest_burst_length = 1, /* SINGLE transfer */ \
410 .channel_priority = STM32_DMA_CONFIG_PRIORITY( \
411 STM32_DMA_CHANNEL_CONFIG_BY_IDX(index, 0)), \
412 .dma_callback = dmci_dma_callback, \
413 }, \
414
415 PINCTRL_DT_INST_DEFINE(0);
416
417 #define STM32_DCMI_GET_CAPTURE_RATE(capture_rate) \
418 ((capture_rate) == 1 ? DCMI_CR_ALL_FRAME : \
419 (capture_rate) == 2 ? DCMI_CR_ALTERNATE_2_FRAME : \
420 (capture_rate) == 4 ? DCMI_CR_ALTERNATE_4_FRAME : \
421 DCMI_CR_ALL_FRAME)
422
423 #define STM32_DCMI_GET_BUS_WIDTH(bus_width) \
424 ((bus_width) == 8 ? DCMI_EXTEND_DATA_8B : \
425 (bus_width) == 10 ? DCMI_EXTEND_DATA_10B : \
426 (bus_width) == 12 ? DCMI_EXTEND_DATA_12B : \
427 (bus_width) == 14 ? DCMI_EXTEND_DATA_14B : \
428 DCMI_EXTEND_DATA_8B)
429
430 #define DCMI_DMA_CHANNEL(id, src, dest) \
431 .dma = { \
432 COND_CODE_1(DT_INST_DMAS_HAS_IDX(id, 0), \
433 (DCMI_DMA_CHANNEL_INIT(id, src, dest)), \
434 (NULL)) \
435 },
436
437 static struct video_stm32_dcmi_data video_stm32_dcmi_data_0 = {
438 .hdcmi = {
439 .Instance = (DCMI_TypeDef *) DT_INST_REG_ADDR(0),
440 .Init = {
441 .SynchroMode = DCMI_SYNCHRO_HARDWARE,
442 .PCKPolarity = (DT_INST_PROP(0, pixelclk_active) ?
443 DCMI_PCKPOLARITY_RISING : DCMI_PCKPOLARITY_FALLING),
444 .HSPolarity = (DT_INST_PROP(0, hsync_active) ?
445 DCMI_HSPOLARITY_HIGH : DCMI_HSPOLARITY_LOW),
446 .VSPolarity = (DT_INST_PROP(0, vsync_active) ?
447 DCMI_VSPOLARITY_HIGH : DCMI_VSPOLARITY_LOW),
448 .CaptureRate = STM32_DCMI_GET_CAPTURE_RATE(
449 DT_INST_PROP(0, capture_rate)),
450 .ExtendedDataMode = STM32_DCMI_GET_BUS_WIDTH(
451 DT_INST_PROP(0, bus_width)),
452 .JPEGMode = DCMI_JPEG_DISABLE,
453 .ByteSelectMode = DCMI_BSM_ALL,
454 .ByteSelectStart = DCMI_OEBS_ODD,
455 .LineSelectMode = DCMI_LSM_ALL,
456 .LineSelectStart = DCMI_OELS_ODD,
457 },
458 },
459 };
460
461 static const struct video_stm32_dcmi_config video_stm32_dcmi_config_0 = {
462 .pclken = {
463 .enr = DT_INST_CLOCKS_CELL(0, bits),
464 .bus = DT_INST_CLOCKS_CELL(0, bus)
465 },
466 .irq_config = video_stm32_dcmi_irq_config_func,
467 .pctrl = PINCTRL_DT_INST_DEV_CONFIG_GET(0),
468 .sensor_dev = DEVICE_DT_GET(DT_INST_PHANDLE(0, sensor)),
469 DCMI_DMA_CHANNEL(0, PERIPHERAL, MEMORY)
470 };
471
video_stm32_dcmi_init(const struct device * dev)472 static int video_stm32_dcmi_init(const struct device *dev)
473 {
474 const struct video_stm32_dcmi_config *config = dev->config;
475 struct video_stm32_dcmi_data *data = dev->data;
476 int err;
477
478 /* Configure DT provided pins */
479 err = pinctrl_apply_state(config->pctrl, PINCTRL_STATE_DEFAULT);
480 if (err < 0) {
481 LOG_ERR("pinctrl setup failed. Error %d.", err);
482 return err;
483 }
484
485 /* Initialize DMA peripheral */
486 err = stm32_dma_init(dev);
487 if (err < 0) {
488 LOG_ERR("DMA initialization failed.");
489 return err;
490 }
491
492 /* Enable DCMI clock */
493 err = stm32_dcmi_enable_clock(dev);
494 if (err < 0) {
495 LOG_ERR("Clock enabling failed.");
496 return -EIO;
497 }
498
499 data->dev = dev;
500 k_fifo_init(&data->fifo_in);
501 k_fifo_init(&data->fifo_out);
502
503 /* Run IRQ init */
504 config->irq_config(dev);
505
506 /* Initialize DCMI peripheral */
507 err = HAL_DCMI_Init(&data->hdcmi);
508 if (err != HAL_OK) {
509 LOG_ERR("DCMI initialization failed.");
510 return -EIO;
511 }
512
513 k_sleep(K_MSEC(100));
514 LOG_DBG("%s inited", dev->name);
515
516 return 0;
517 }
518
519 DEVICE_DT_INST_DEFINE(0, &video_stm32_dcmi_init,
520 NULL, &video_stm32_dcmi_data_0,
521 &video_stm32_dcmi_config_0,
522 POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY,
523 &video_stm32_dcmi_driver_api);
524