video: stm32-dcmi: implement frame interval handling

Implement the video API frame interval handling in order
to control the framerate of capture.

This allow to remove the capture-rate DT property as well.

Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
This commit is contained in:
Alain Volmat 2025-05-08 16:42:10 +02:00 committed by Anas Nashif
commit c1e7bdaa75
2 changed files with 128 additions and 23 deletions

View file

@ -41,6 +41,7 @@ struct video_stm32_dcmi_data {
const struct device *dev;
DCMI_HandleTypeDef hdcmi;
struct video_format fmt;
int capture_rate;
struct k_fifo fifo_in;
struct k_fifo fifo_out;
struct video_buffer *vbuf;
@ -251,6 +252,12 @@ static int video_stm32_dcmi_get_fmt(const struct device *dev,
return 0;
}
#define STM32_DCMI_GET_CAPTURE_RATE(capture_rate) \
((capture_rate) == 1 ? DCMI_CR_ALL_FRAME : \
(capture_rate) == 2 ? DCMI_CR_ALTERNATE_2_FRAME : \
(capture_rate) == 4 ? DCMI_CR_ALTERNATE_4_FRAME : \
DCMI_CR_ALL_FRAME)
static int video_stm32_dcmi_set_stream(const struct device *dev, bool enable)
{
struct video_stm32_dcmi_data *data = dev->data;
@ -282,6 +289,10 @@ static int video_stm32_dcmi_set_stream(const struct device *dev, bool enable)
return -ENOMEM;
}
/* Set the frame control */
data->hdcmi.Instance->CR &= ~(DCMI_CR_FCRC_0 | DCMI_CR_FCRC_1);
data->hdcmi.Instance->CR |= STM32_DCMI_GET_CAPTURE_RATE(data->capture_rate);
err = HAL_DCMI_Start_DMA(&data->hdcmi, DCMI_MODE_CONTINUOUS,
(uint32_t)data->vbuf->buffer, data->vbuf->bytesused / 4);
if (err != HAL_OK) {
@ -351,6 +362,119 @@ static int video_stm32_dcmi_get_caps(const struct device *dev,
return video_get_caps(config->sensor_dev, ep, caps);
}
static int video_stm32_dcmi_enum_frmival(const struct device *dev, enum video_endpoint_id ep,
struct video_frmival_enum *fie)
{
const struct video_stm32_dcmi_config *config = dev->config;
int ret;
if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
return -EINVAL;
}
ret = video_stm32_dcmi_is_fmt_valid(fie->format->pixelformat, fie->format->pitch,
fie->format->height);
if (ret < 0) {
return ret;
}
ret = video_enum_frmival(config->sensor_dev, ep, fie);
if (ret < 0) {
return ret;
}
/* Adapt the interval in order to report the frame drop capabilities */
if (fie->type == VIDEO_FRMIVAL_TYPE_DISCRETE) {
struct video_frmival discrete = fie->discrete;
fie->type = VIDEO_FRMIVAL_TYPE_STEPWISE;
fie->stepwise.max = discrete;
fie->stepwise.min.denominator = discrete.denominator;
fie->stepwise.min.numerator = discrete.numerator * 4;
fie->stepwise.step.denominator = discrete.denominator;
fie->stepwise.step.numerator = discrete.numerator * 2;
} else {
fie->stepwise.min.numerator *= 4;
fie->stepwise.step.numerator *= 2;
}
return 0;
}
#define STM32_DCMI_MAX_FRAME_DROP 4
static int video_stm32_dcmi_set_frmival(const struct device *dev, enum video_endpoint_id ep,
struct video_frmival *frmival)
{
const struct video_stm32_dcmi_config *config = dev->config;
struct video_stm32_dcmi_data *data = dev->data;
struct video_frmival_enum fie = {
.format = &data->fmt,
};
struct video_frmival best_sensor_frmival;
uint64_t best_diff_nsec = INT32_MAX;
uint64_t diff_nsec = 0, a, b;
int best_capture_rate = 1;
if (ep != VIDEO_EP_OUT && ep != VIDEO_EP_ALL) {
return -EINVAL;
}
/*
* Try to figure out a frameinterval setting allow to reach as close as
* possible to the request. At first without relying on DCMI frame control,
* then enabling it
*/
for (int capture_rate = 1; capture_rate <= STM32_DCMI_MAX_FRAME_DROP; capture_rate *= 2) {
/*
* Take into consideration the drop done by the DCMI hence multiply
* denominator by the rate introduced by the DCMI
*/
fie.discrete.numerator = frmival->numerator;
fie.discrete.denominator = frmival->denominator * capture_rate;
a = video_frmival_nsec(&fie.discrete);
video_closest_frmival(config->sensor_dev, ep, &fie);
b = video_frmival_nsec(&fie.discrete);
diff_nsec = a > b ? a - b : b - a;
if (diff_nsec < best_diff_nsec) {
best_diff_nsec = diff_nsec;
best_sensor_frmival = fie.discrete;
best_capture_rate = capture_rate;
}
if (diff_nsec == 0) {
break;
}
}
/*
* Give back the achieved frame interval achieved, ensuring to take into
* consideration the DCMI frame control
*/
frmival->numerator = best_sensor_frmival.numerator * best_capture_rate;
frmival->denominator = best_sensor_frmival.denominator;
data->capture_rate = best_capture_rate;
return video_set_frmival(config->sensor_dev, ep, &best_sensor_frmival);
}
static int video_stm32_dcmi_get_frmival(const struct device *dev, enum video_endpoint_id ep,
struct video_frmival *frmival)
{
const struct video_stm32_dcmi_config *config = dev->config;
struct video_stm32_dcmi_data *data = dev->data;
int ret;
ret = video_get_frmival(config->sensor_dev, ep, frmival);
if (ret < 0) {
return ret;
}
frmival->numerator *= data->capture_rate;
return 0;
}
static DEVICE_API(video, video_stm32_dcmi_driver_api) = {
.set_format = video_stm32_dcmi_set_fmt,
.get_format = video_stm32_dcmi_get_fmt,
@ -358,6 +482,9 @@ static DEVICE_API(video, video_stm32_dcmi_driver_api) = {
.enqueue = video_stm32_dcmi_enqueue,
.dequeue = video_stm32_dcmi_dequeue,
.get_caps = video_stm32_dcmi_get_caps,
.enum_frmival = video_stm32_dcmi_enum_frmival,
.set_frmival = video_stm32_dcmi_set_frmival,
.get_frmival = video_stm32_dcmi_get_frmival,
};
static void video_stm32_dcmi_irq_config_func(const struct device *dev)
@ -389,12 +516,6 @@ static void video_stm32_dcmi_irq_config_func(const struct device *dev)
PINCTRL_DT_INST_DEFINE(0);
#define STM32_DCMI_GET_CAPTURE_RATE(capture_rate) \
((capture_rate) == 1 ? DCMI_CR_ALL_FRAME : \
(capture_rate) == 2 ? DCMI_CR_ALTERNATE_2_FRAME : \
(capture_rate) == 4 ? DCMI_CR_ALTERNATE_4_FRAME : \
DCMI_CR_ALL_FRAME)
#define STM32_DCMI_GET_BUS_WIDTH(bus_width) \
((bus_width) == 8 ? DCMI_EXTEND_DATA_8B : \
(bus_width) == 10 ? DCMI_EXTEND_DATA_10B : \
@ -424,9 +545,6 @@ static struct video_stm32_dcmi_data video_stm32_dcmi_data_0 = {
.VSPolarity = DT_PROP_OR(DT_INST_ENDPOINT_BY_ID(n, 0, 0),
vsync_active, 0) ?
DCMI_VSPOLARITY_HIGH : DCMI_VSPOLARITY_LOW,
.CaptureRate = STM32_DCMI_GET_CAPTURE_RATE(
DT_PROP_OR(DT_DRV_INST(inst), capture_rate,
1)),
.ExtendedDataMode = STM32_DCMI_GET_BUS_WIDTH(
DT_PROP_OR(DT_INST_ENDPOINT_BY_ID(n, 0, 0),
bus_width, 8)),
@ -482,6 +600,7 @@ static int video_stm32_dcmi_init(const struct device *dev)
data->dev = dev;
k_fifo_init(&data->fifo_in);
k_fifo_init(&data->fifo_out);
data->capture_rate = 1;
/* Run IRQ init */
config->irq_config(dev);

View file

@ -35,20 +35,6 @@ properties:
interrupts:
required: true
capture-rate:
type: int
enum:
- 1
- 2
- 4
default: 1
description: |
The DCMI can capture all frames or alternate frames. If it is not specified,
the default is all frames.
1 Capture all frames.
2 Capture alternate frames.
4 Capture one frame every 4 frames.
dmas:
required: true
description: |