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