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