diff --git a/drivers/video/CMakeLists.txt b/drivers/video/CMakeLists.txt index 7a03156165ce0..b952aed19e1ae 100644 --- a/drivers/video/CMakeLists.txt +++ b/drivers/video/CMakeLists.txt @@ -26,5 +26,6 @@ zephyr_library_sources_ifdef(CONFIG_VIDEO_IMX335 imx335.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_ST_MIPID02 video_st_mipid02.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_STM32_DCMIPP video_stm32_dcmipp.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_RENESAS_RA_CEU video_renesas_ra_ceu.c) +zephyr_library_sources_ifdef(CONFIG_VIDEO_STM32_JPEG video_stm32_jpeg.c) zephyr_linker_sources(DATA_SECTIONS video.ld) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 7b1f57a83c487..8b07ba7250dbe 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -98,4 +98,6 @@ source "drivers/video/Kconfig.stm32_dcmipp" source "drivers/video/Kconfig.renesas_ra_ceu" +source "drivers/video/Kconfig.stm32_jpeg" + endif # VIDEO diff --git a/drivers/video/Kconfig.stm32_jpeg b/drivers/video/Kconfig.stm32_jpeg new file mode 100644 index 0000000000000..369486b5a9c39 --- /dev/null +++ b/drivers/video/Kconfig.stm32_jpeg @@ -0,0 +1,16 @@ +# STM32 JPEG codec driver configuration options + +# Copyright (c) 2025 STMicroelectronics. +# SPDX-License-Identifier: Apache-2.0 + +config VIDEO_STM32_JPEG + bool "STM32 JPEG codec driver" + default y + depends on DT_HAS_ST_STM32_JPEG_ENABLED + select RESET + select USE_STM32_HAL_JPEG + select USE_STM32_HAL_DMA + select USE_STM32_HAL_DMA_EX + select USE_STM32_HAL_RIF if SOC_SERIES_STM32N6X + help + Enable driver for STM32 JPEG Codec peripheral diff --git a/drivers/video/video_stm32_jpeg.c b/drivers/video/video_stm32_jpeg.c new file mode 100644 index 0000000000000..ac9e1d5606818 --- /dev/null +++ b/drivers/video/video_stm32_jpeg.c @@ -0,0 +1,589 @@ +/* + * Copyright (c) 2025 STMicroelectronics. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "video_ctrls.h" +#include "video_device.h" + +#define DT_DRV_COMPAT st_stm32_jpeg + +#define STM32_JPEG_DEFAULT_WIDTH 640 +#define STM32_JPEG_DEFAULT_HEIGHT 480 +#define STM32_JPEG_DEFAULT_IN_FMT VIDEO_PIX_FMT_NV12 +#define STM32_JPEG_DEFAULT_OUT_FMT VIDEO_PIX_FMT_JPEG + +/* FIXME - this depends on the JPEG quality */ +#define JPEG_ESTIMATED_COMPRESSION_RATIO 2 + +LOG_MODULE_REGISTER(stm32_jpeg, CONFIG_VIDEO_LOG_LEVEL); + +typedef void (*irq_config_func_t)(const struct device *dev); + +enum stm32_jpeg_state { + STM32_JPEG_STOPPED = 0, + STM32_JPEG_WAIT_FOR_BUFFER, + STM32_JPEG_RUNNING, +}; + +struct video_common_header { + struct video_format fmt; + struct k_fifo fifo_in; + struct k_fifo fifo_out; + bool is_streaming; +}; + +struct video_m2m_common { + struct video_common_header in; + struct video_common_header out; +}; + +#define YCBCR_420_MCU_BLOCK_SIZE 384 /* 4 8x8 Y, 1 8x8 Cb, 1 8x8 Cr */ +struct stm32_jpeg_data { + const struct device *dev; + struct video_m2m_common m2m; + JPEG_HandleTypeDef hjpeg; + struct k_mutex lock; + enum stm32_jpeg_state state; + struct video_buffer *current_in; + struct video_buffer *current_out; + + struct video_ctrl jpeg_quality; + + uint32_t current_x_mcu; + uint32_t current_y_mcu; + uint8_t mcu_ycbcr[YCBCR_420_MCU_BLOCK_SIZE]; +}; + +struct stm32_jpeg_config { + const struct stm32_pclken jpeg_hclken[1]; + irq_config_func_t irq_config; + const struct reset_dt_spec reset_jpeg; +}; + +struct stm32_jpeg_fmt_conf { + uint32_t pixelformat; + uint32_t subsampling; + uint8_t hmcu_div; + uint8_t vmcu_div; +}; + +static struct stm32_jpeg_fmt_conf stm32_jpeg_confs[] = { + /* JPEG */ + { + .pixelformat = VIDEO_PIX_FMT_JPEG, + /* Meaningless but set to 1 to make set_fmt check working */ + .hmcu_div = 1, + .vmcu_div = 1, + }, + /* YCrCb 4:2:0 */ + { + .pixelformat = VIDEO_PIX_FMT_NV12, + .subsampling = JPEG_420_SUBSAMPLING, + .hmcu_div = 16, + .vmcu_div = 16, + }, + /* TODO: YCrCb 4:2:2 to be added */ + /* TODO: YCrCb 4:4:4 to be added */ +}; + +#define MCU_WIDTH 16 +#define MCU_HEIGHT 16 +#define MCU_BLOCK_SZ 8 +static void stm32_jpeg_nv12_to_ycbcr_mcu(const uint8_t mcu_x, const uint8_t mcu_y, + const uint8_t *in_y, const uint8_t *in_uv, + uint8_t *out, uint32_t width) +{ + int mcu_idx = 0; + + /* Copy the 4 8x8 Y */ + for (int by = 0; by < 2; ++by) { + for (int bx = 0; bx < 2; ++bx) { + for (int y = 0; y < MCU_BLOCK_SZ; ++y) { + int src_y = mcu_y * MCU_HEIGHT + by * MCU_BLOCK_SZ + y; + int src_x = mcu_x * MCU_WIDTH + bx * MCU_BLOCK_SZ; + const uint8_t *src = in_y + src_y * width + src_x; + uint8_t *dst = out + mcu_idx; + + memcpy(dst, src, MCU_BLOCK_SZ); + mcu_idx += MCU_BLOCK_SZ; + } + } + } + + /* Copy 1 8x8 Cb block */ + for (int y = 0; y < MCU_BLOCK_SZ; ++y) { + int src_y = (mcu_y * MCU_HEIGHT) / 2 + y; + int src_x = (mcu_x * MCU_WIDTH) / 2; + const uint8_t *src = in_uv + (src_y * width) + (src_x * 2); + uint8_t *dst = out + mcu_idx; + + for (int x = 0; x < MCU_BLOCK_SZ; ++x) { + dst[x] = src[x * 2]; + } + mcu_idx += MCU_BLOCK_SZ; + } + + /* Copy 1 8x8 Cr block */ + for (int y = 0; y < MCU_BLOCK_SZ; ++y) { + int src_y = (mcu_y * MCU_HEIGHT) / 2 + y; + int src_x = (mcu_x * MCU_WIDTH) / 2; + const uint8_t *src = in_uv + (src_y * width) + (src_x * 2); + uint8_t *dst = out + mcu_idx; + + for (int x = 0; x < MCU_BLOCK_SZ; ++x) { + dst[x] = src[x * 2 + 1]; + } + mcu_idx += MCU_BLOCK_SZ; + } +} + +static struct stm32_jpeg_fmt_conf *stm32_jpeg_get_conf(uint32_t pixelformat) +{ + for (int i = 0; i < ARRAY_SIZE(stm32_jpeg_confs); i++) { + if (stm32_jpeg_confs[i].pixelformat == pixelformat) { + return &stm32_jpeg_confs[i]; + } + } + + return NULL; +} + +static void stm32_jpeg_convert_next_mcu(struct stm32_jpeg_data *data) +{ + stm32_jpeg_nv12_to_ycbcr_mcu(data->current_x_mcu++, data->current_y_mcu, + data->current_in->buffer, data->current_in->buffer + + data->m2m.in.fmt.width * data->m2m.in.fmt.height, + data->mcu_ycbcr, data->m2m.in.fmt.width); + if (data->current_x_mcu >= data->m2m.in.fmt.width / MCU_WIDTH) { + data->current_x_mcu = 0; + data->current_y_mcu++; + } +} + +static int stm32_jpeg_start_codec(const struct device *dev) +{ + struct stm32_jpeg_data *data = dev->data; + struct k_fifo *in_fifo_in = &data->m2m.in.fifo_in; + struct k_fifo *out_fifo_in = &data->m2m.out.fifo_in; + int ret; + + if (k_fifo_is_empty(in_fifo_in) || k_fifo_is_empty(out_fifo_in)) { + /* Nothing to do */ + data->state = STM32_JPEG_WAIT_FOR_BUFFER; + return 0; + } + + data->current_in = k_fifo_get(in_fifo_in, K_NO_WAIT); + data->current_out = k_fifo_get(out_fifo_in, K_NO_WAIT); + + if (data->m2m.in.fmt.pixelformat != VIDEO_PIX_FMT_JPEG) { + struct stm32_jpeg_fmt_conf *conf = + stm32_jpeg_get_conf(data->m2m.in.fmt.pixelformat); + JPEG_ConfTypeDef Conf = {0}; + HAL_StatusTypeDef hret; + + /* Reset value of current MCU and output buffer offset */ + data->current_x_mcu = 0; + data->current_y_mcu = 0; + + /* JPEG Encoding */ + Conf.ColorSpace = JPEG_YCBCR_COLORSPACE; + Conf.ChromaSubsampling = conf->subsampling; + Conf.ImageWidth = data->m2m.in.fmt.width; + Conf.ImageHeight = data->m2m.in.fmt.height; + Conf.ImageQuality = data->jpeg_quality.val; + + hret = HAL_JPEG_ConfigEncoding(&data->hjpeg, &Conf); + if (hret != HAL_OK) { + LOG_ERR("Failed to configure codec for encoding"); + ret = -EIO; + goto error; + } + + data->state = STM32_JPEG_RUNNING; + + /* Convert the first MCU (and store it into mcu_ycbcr) */ + stm32_jpeg_convert_next_mcu(data); + + hret = HAL_JPEG_Encode_IT(&data->hjpeg, data->mcu_ycbcr, YCBCR_420_MCU_BLOCK_SIZE, + data->current_out->buffer, data->current_out->size); + if (hret != HAL_OK) { + LOG_ERR("Failed to request encoding"); + ret = -EIO; + goto error; + } + } else { + LOG_ERR("Decoder not yet implemented"); + ret = -EINVAL; + goto error; + } + + return 0; + +error: + data->state = STM32_JPEG_WAIT_FOR_BUFFER; + return ret; +} + +/* Function called when the data have been generated by the JPEG block */ +void HAL_JPEG_DataReadyCallback(JPEG_HandleTypeDef *hjpeg, uint8_t *pDataOut, + uint32_t OutDataLength) +{ + struct stm32_jpeg_data *data = + CONTAINER_OF(hjpeg, struct stm32_jpeg_data, hjpeg); + + k_mutex_lock(&data->lock, K_FOREVER); + + /* Store the output data size and timestamp */ + data->current_out->bytesused = OutDataLength; + data->current_out->timestamp = k_uptime_get_32(); + + k_mutex_unlock(&data->lock); +} + +/* + * Function called when all processing is finished, at that moment we can be + * sure that buffers won't be used anymore + */ +void HAL_JPEG_EncodeCpltCallback(JPEG_HandleTypeDef *hjpeg) +{ + struct stm32_jpeg_data *data = CONTAINER_OF(hjpeg, struct stm32_jpeg_data, hjpeg); + int ret = 0; + + k_mutex_lock(&data->lock, K_FOREVER); + + /* Give back the buffers to the application */ + k_fifo_put(&data->m2m.in.fifo_out, data->current_in); + k_fifo_put(&data->m2m.out.fifo_out, data->current_out); + + /* Try to restart the next processing if needed */ + ret = stm32_jpeg_start_codec(data->dev); + if (ret) { + LOG_ERR("Failed to start codec, err: %d", ret); + goto out; + } + +out: + k_mutex_unlock(&data->lock); +} + +void HAL_JPEG_ErrorCallback(JPEG_HandleTypeDef *hjpeg) +{ + __ASSERT(false, "Got %s", __func__); +} + +/* + * This function is called whenever new input data (MCU) must be given in + * order to proceed the frame + */ +void HAL_JPEG_GetDataCallback(JPEG_HandleTypeDef *hjpeg, uint32_t NbEncodedData) +{ + struct stm32_jpeg_data *data = + CONTAINER_OF(hjpeg, struct stm32_jpeg_data, hjpeg); + + /* Convert the next MCU */ + stm32_jpeg_convert_next_mcu(data); + + HAL_JPEG_ConfigInputBuffer(hjpeg, data->mcu_ycbcr, YCBCR_420_MCU_BLOCK_SIZE); +} + +static int stm32_jpeg_get_fmt(const struct device *dev, struct video_format *fmt) +{ + struct stm32_jpeg_data *data = dev->data; + + *fmt = fmt->type == VIDEO_BUF_TYPE_INPUT ? data->m2m.in.fmt : data->m2m.out.fmt; + + return 0; +} + +static int stm32_jpeg_set_fmt(const struct device *dev, struct video_format *fmt) +{ + struct stm32_jpeg_data *data = dev->data; + struct video_common_header *common = + fmt->type == VIDEO_BUF_TYPE_INPUT ? &data->m2m.in : &data->m2m.out; + struct stm32_jpeg_fmt_conf *conf; + int ret = 0; + + /* Validate the settings */ + conf = stm32_jpeg_get_conf(fmt->pixelformat); + if (conf == NULL) { + return -EINVAL; + } + if (fmt->width % conf->hmcu_div || fmt->height % conf->vmcu_div) { + LOG_ERR("Format %s: %d pixels width / %d pixels height multiple required", + VIDEO_FOURCC_TO_STR(fmt->pixelformat), conf->hmcu_div, conf->vmcu_div); + return -EINVAL; + } + + k_mutex_lock(&data->lock, K_FOREVER); + + if (common->is_streaming) { + ret = -EBUSY; + goto out; + } + + /* FIXME - we need to approximate the JPEG compression ratio depending on quality */ + fmt->sizeimage = fmt->width * fmt->height / JPEG_ESTIMATED_COMPRESSION_RATIO; + + common->fmt = *fmt; + +out: + k_mutex_unlock(&data->lock); + + return ret; +} + +static int stm32_jpeg_set_stream(const struct device *dev, bool enable, enum video_buf_type type) +{ + struct stm32_jpeg_data *data = dev->data; + struct video_common_header *common = + type == VIDEO_BUF_TYPE_INPUT ? &data->m2m.in : &data->m2m.out; + uint32_t in_fmt = data->m2m.in.fmt.pixelformat; + uint32_t out_fmt = data->m2m.out.fmt.pixelformat; + int ret = 0; + + k_mutex_lock(&data->lock, K_FOREVER); + + /* Check that input / output formats are correct */ + if (!((in_fmt == VIDEO_PIX_FMT_JPEG && out_fmt != VIDEO_PIX_FMT_JPEG) || + (in_fmt != VIDEO_PIX_FMT_JPEG && out_fmt == VIDEO_PIX_FMT_JPEG))) { + LOG_ERR("Exactly one of input/output format must be JPEG"); + ret = -EINVAL; + goto out; + } + + /* FIXME - temporary until the decoder support get added */ + if (data->m2m.in.fmt.pixelformat == VIDEO_PIX_FMT_JPEG) { + LOG_ERR("Decoder not yet implemented"); + ret = -EIO; + goto out; + } + + if ((enable && common->is_streaming) || (!enable && !common->is_streaming)) { + ret = -EALREADY; + goto out; + } + + common->is_streaming = enable; + + data->state = STM32_JPEG_WAIT_FOR_BUFFER; + +out: + k_mutex_unlock(&data->lock); + + return ret; +} + +static int stm32_jpeg_enqueue(const struct device *dev, struct video_buffer *vbuf) +{ + struct stm32_jpeg_data *data = dev->data; + struct video_common_header *common = + vbuf->type == VIDEO_BUF_TYPE_INPUT ? &data->m2m.in : &data->m2m.out; + int ret = 0; + + /* TODO - need to check for buffer size here */ + + k_mutex_lock(&data->lock, K_FOREVER); + + k_fifo_put(&common->fifo_in, vbuf); + + /* Try to start codec if necessary */ + if (data->state == STM32_JPEG_WAIT_FOR_BUFFER) { + ret = stm32_jpeg_start_codec(dev); + if (ret) { + LOG_ERR("Failed to start codec, err: %d", ret); + goto out; + } + } + +out: + k_mutex_unlock(&data->lock); + + return ret; +} + +static int stm32_jpeg_dequeue(const struct device *dev, struct video_buffer **vbuf, + k_timeout_t timeout) +{ + struct stm32_jpeg_data *data = dev->data; + struct video_common_header *common = + (*vbuf)->type == VIDEO_BUF_TYPE_INPUT ? &data->m2m.in : &data->m2m.out; + + *vbuf = k_fifo_get(&common->fifo_out, timeout); + if (*vbuf == NULL) { + return -EAGAIN; + } + + return 0; +} + +static const struct video_format_cap stm32_jpeg_fmts[] = { + { + .pixelformat = VIDEO_PIX_FMT_JPEG, + .width_min = 1, + .width_max = 65535, + .height_min = 1, + .height_max = 65535, + .width_step = 1, + .height_step = 1, + }, + { + .pixelformat = VIDEO_PIX_FMT_NV12, + .width_min = 16, + .width_max = 65520, + .height_min = 16, + .height_max = 65520, + .width_step = 16, + .height_step = 16, + }, + {0} +}; + +static int stm32_jpeg_get_caps(const struct device *dev, struct video_caps *caps) +{ + caps->format_caps = stm32_jpeg_fmts; + + caps->min_vbuf_count = 1; + caps->min_line_count = LINE_COUNT_HEIGHT; + caps->max_line_count = LINE_COUNT_HEIGHT; + + return 0; +} + +static DEVICE_API(video, stm32_jpeg_driver_api) = { + .set_format = stm32_jpeg_set_fmt, + .get_format = stm32_jpeg_get_fmt, + .set_stream = stm32_jpeg_set_stream, + .enqueue = stm32_jpeg_enqueue, + .dequeue = stm32_jpeg_dequeue, + .get_caps = stm32_jpeg_get_caps, +}; + +static int stm32_jpeg_enable_clock(const struct device *dev) +{ + const struct stm32_jpeg_config *config = dev->config; + const struct device *cc_node = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE); + + if (!device_is_ready(cc_node)) { + LOG_ERR("clock control device not ready"); + return -ENODEV; + } + + /* Turn on JPEG peripheral clock */ + return clock_control_on(cc_node, (clock_control_subsys_t)&config->jpeg_hclken); +} + +static int stm32_jpeg_init(const struct device *dev) +{ + const struct stm32_jpeg_config *cfg = dev->config; + struct stm32_jpeg_data *data = dev->data; + HAL_StatusTypeDef hret; + int ret; + + data->dev = dev; + + ret = stm32_jpeg_enable_clock(dev); + if (ret < 0) { + LOG_ERR("Clock enabling failed."); + return ret; + } + + if (!device_is_ready(cfg->reset_jpeg.dev)) { + LOG_ERR("reset controller not ready"); + return -ENODEV; + } + reset_line_toggle_dt(&cfg->reset_jpeg); + + /* Run IRQ init */ + cfg->irq_config(dev); + +#if defined(CONFIG_SOC_SERIES_STM32N6X) + HAL_RIF_RISC_SetSlaveSecureAttributes(RIF_RISC_PERIPH_INDEX_JPEG, + RIF_ATTRIBUTE_PRIV | RIF_ATTRIBUTE_SEC); +#endif + + /* Initialise default input / output formats */ + k_mutex_init(&data->lock); + k_fifo_init(&data->m2m.in.fifo_in); + k_fifo_init(&data->m2m.in.fifo_out); + k_fifo_init(&data->m2m.out.fifo_in); + k_fifo_init(&data->m2m.out.fifo_out); + + /* Initialize default formats */ + data->m2m.in.fmt.type = VIDEO_BUF_TYPE_INPUT; + data->m2m.in.fmt.width = STM32_JPEG_DEFAULT_WIDTH; + data->m2m.in.fmt.height = STM32_JPEG_DEFAULT_HEIGHT; + data->m2m.in.fmt.pixelformat = STM32_JPEG_DEFAULT_IN_FMT; + data->m2m.out.fmt.type = VIDEO_BUF_TYPE_OUTPUT; + data->m2m.out.fmt.width = STM32_JPEG_DEFAULT_WIDTH; + data->m2m.out.fmt.height = STM32_JPEG_DEFAULT_HEIGHT; + data->m2m.out.fmt.pixelformat = STM32_JPEG_DEFAULT_OUT_FMT; + + ret = video_init_ctrl(&data->jpeg_quality, dev, VIDEO_CID_JPEG_COMPRESSION_QUALITY, + (struct video_ctrl_range) {.min = 5, .max = 100, + .step = 1, .def = 50}); + if (ret < 0) { + return ret; + } + + /* Initialize JPEG peripheral */ + hret = HAL_JPEG_Init(&data->hjpeg); + if (hret != HAL_OK) { + LOG_ERR("JPEG initialization failed."); + return -EIO; + } + + LOG_DBG("%s initialized", dev->name); + + return 0; +} + +static void stm32_jpeg_isr(const struct device *dev) +{ + struct stm32_jpeg_data *jpeg = dev->data; + + HAL_JPEG_IRQHandler(&jpeg->hjpeg); +} + +#define STM32_JPEG_INIT(n) \ + static void stm32_jpeg_irq_config_##n(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \ + stm32_jpeg_isr, DEVICE_DT_INST_GET(n), 0); \ + irq_enable(DT_INST_IRQN(n)); \ + } \ + \ + static struct stm32_jpeg_data stm32_jpeg_data_##n = { \ + .hjpeg = { \ + .Instance = (JPEG_TypeDef *)DT_INST_REG_ADDR(n), \ + }, \ + }; \ + \ + static const struct stm32_jpeg_config stm32_jpeg_config_##n = { \ + .jpeg_hclken = STM32_DT_INST_CLOCKS(n), \ + .irq_config = stm32_jpeg_irq_config_##n, \ + .reset_jpeg = RESET_DT_SPEC_INST_GET_BY_IDX(n, 0), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, &stm32_jpeg_init, \ + NULL, &stm32_jpeg_data_##n, \ + &stm32_jpeg_config_##n, \ + POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, \ + &stm32_jpeg_driver_api); \ + \ + VIDEO_DEVICE_DEFINE(jpeg_##n, DEVICE_DT_INST_GET(n), NULL); + +DT_INST_FOREACH_STATUS_OKAY(STM32_JPEG_INIT) diff --git a/dts/arm/st/n6/stm32n6.dtsi b/dts/arm/st/n6/stm32n6.dtsi index c06a499b4cfce..bcaeefe6bf6d5 100644 --- a/dts/arm/st/n6/stm32n6.dtsi +++ b/dts/arm/st/n6/stm32n6.dtsi @@ -1273,6 +1273,15 @@ resets = <&rctl STM32_RESET(AHB5, 31)>; status = "disabled"; }; + + jpeg: codec@58023000 { + compatible = "st,stm32-jpeg"; + reg = <0x58023000 0x1000>; + interrupts = <61 0>; + clocks = <&rcc STM32_CLOCK(AHB5, 3)>; + resets = <&rctl STM32_RESET(AHB5, 3)>; + status = "disabled"; + }; }; }; diff --git a/dts/bindings/video/st,stm32-jpeg.yaml b/dts/bindings/video/st,stm32-jpeg.yaml new file mode 100644 index 0000000000000..b3cdf9d5cceb4 --- /dev/null +++ b/dts/bindings/video/st,stm32-jpeg.yaml @@ -0,0 +1,27 @@ +# +# Copyright (c) 2025 STMicroelectronics. +# +# SPDX-License-Identifier: Apache-2.0 +# + +description: | + STM32 JPEG Codec. The STM32 JPEG codec allows JPEG encoding and + decoding from / to MCU blocks which can be generated from NV12 + format. + +compatible: "st,stm32-jpeg" + +include: [base.yaml, reset-device.yaml] + +properties: + reg: + required: true + + clocks: + required: true + + interrupts: + required: true + + resets: + required: true diff --git a/snippets/stm32/video-stm32-jpegenc/README.rst b/snippets/stm32/video-stm32-jpegenc/README.rst new file mode 100644 index 0000000000000..cf563e80e0898 --- /dev/null +++ b/snippets/stm32/video-stm32-jpegenc/README.rst @@ -0,0 +1,19 @@ +.. _snippet-video-stm32-jpegenc: + +STM32 Video JPEG encoder Snippet (video-stm32-jpegenc) +###################################################### + +.. code-block:: console + + west build -S video-stm32-jpegenc [...] + +Overview +******** + +This snippet instantiate the STM32 JPEG encoder and set it +as ``zephyr,videoenc`` :ref:`devicetree` chosen node. + +Requirements +************ + +The board must have the hardware support for the JPEG encoder. diff --git a/snippets/stm32/video-stm32-jpegenc/snippet.yml b/snippets/stm32/video-stm32-jpegenc/snippet.yml new file mode 100644 index 0000000000000..161aff2953c53 --- /dev/null +++ b/snippets/stm32/video-stm32-jpegenc/snippet.yml @@ -0,0 +1,4 @@ +name: video-stm32-jpegenc +append: + EXTRA_DTC_OVERLAY_FILE: video-stm32-jpegenc.overlay + EXTRA_CONF_FILE: video-stm32-jpegenc.conf diff --git a/snippets/stm32/video-stm32-jpegenc/video-stm32-jpegenc.conf b/snippets/stm32/video-stm32-jpegenc/video-stm32-jpegenc.conf new file mode 100644 index 0000000000000..941b43cf4b04b --- /dev/null +++ b/snippets/stm32/video-stm32-jpegenc/video-stm32-jpegenc.conf @@ -0,0 +1,9 @@ +# JPEG encoder output +CONFIG_VIDEO_ENCODED_PIXEL_FORMAT="JPEG" + +# Default frame size +CONFIG_VIDEO_FRAME_WIDTH=320 +CONFIG_VIDEO_FRAME_HEIGHT=240 + +# JPEG encoder input +CONFIG_VIDEO_PIXEL_FORMAT="NV12" diff --git a/snippets/stm32/video-stm32-jpegenc/video-stm32-jpegenc.overlay b/snippets/stm32/video-stm32-jpegenc/video-stm32-jpegenc.overlay new file mode 100644 index 0000000000000..186938aa0469f --- /dev/null +++ b/snippets/stm32/video-stm32-jpegenc/video-stm32-jpegenc.overlay @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2025 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + chosen { + zephyr,videoenc = &jpeg; + }; +}; + +&jpeg { + status = "okay"; +};