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