blob: 4f26977dcce10f4691b34fe3e5ce6570eef43e5e [file] [log] [blame]
/*
* Copyright (c) 2025 STMicroelectronics.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT st_stm32_venc
#include <errno.h>
#include <stdlib.h>
#include <zephyr/drivers/video.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
#include <zephyr/drivers/reset.h>
#include <zephyr/irq.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/multi_heap/shared_multi_heap.h>
#include <ewl.h>
#include <h264encapi.h>
#include <reg_offset_v7.h>
LOG_MODULE_REGISTER(stm32_venc, CONFIG_VIDEO_LOG_LEVEL);
#define VENC_DEFAULT_WIDTH 320
#define VENC_DEFAULT_HEIGHT 240
#define VENC_DEFAULT_IN_FMT VIDEO_PIX_FMT_NV12
#define VENC_DEFAULT_OUT_FMT VIDEO_PIX_FMT_H264
#define VENC_DEFAULT_FRAMERATE 30
#define VENC_DEFAULT_LEVEL H264ENC_LEVEL_4
#define VENC_DEFAULT_QP 25
#define VENC_ESTIMATED_COMPRESSION_RATIO 10
#define ALIGNMENT_INCR 8UL
#define EWL_HEAP_ALIGNED_ALLOC(size)\
shared_multi_heap_aligned_alloc(CONFIG_VIDEO_BUFFER_SMH_ATTRIBUTE, ALIGNMENT_INCR, (size))
#define EWL_HEAP_ALIGNED_FREE(block) shared_multi_heap_free(block)
#define EWL_TIMEOUT 100UL
#define MEM_CHUNKS 32
#define NUM_SLICES_READY_MASK GENMASK(23, 16)
#define LOW_LATENCY_HW_ITF_EN 29
typedef void (*irq_config_func_t)(const struct device *dev);
struct stm32_venc_config {
mm_reg_t reg;
const struct stm32_pclken pclken;
const struct reset_dt_spec reset;
irq_config_func_t irq_config;
};
struct stm32_venc_ewl {
uint32_t client_type;
uint32_t *chunks[MEM_CHUNKS];
uint32_t *aligned_chunks[MEM_CHUNKS];
uint32_t total_chunks;
const struct stm32_venc_config *config;
struct k_sem complete;
uint32_t irq_status;
uint32_t irq_cnt;
uint32_t mem_cnt;
};
static struct stm32_venc_ewl ewl_instance;
struct stm32_venc_data {
const struct device *dev;
struct k_mutex lock;
struct video_format in_fmt;
struct video_format out_fmt;
struct k_fifo in_fifo_in;
struct k_fifo in_fifo_out;
struct k_fifo out_fifo_in;
struct k_fifo out_fifo_out;
struct video_buffer *vbuf;
H264EncInst encoder;
uint32_t frame_nb;
bool resync;
};
static H264EncPictureType to_h264pixfmt(uint32_t pixelformat)
{
switch (pixelformat) {
case VIDEO_PIX_FMT_NV12:
return H264ENC_YUV420_SEMIPLANAR;
case VIDEO_PIX_FMT_RGB565:
return H264ENC_RGB565;
default:
__ASSERT_NO_MSG(false);
return H264ENC_YUV420_SEMIPLANAR;
}
}
u32 EWLReadAsicID(void)
{
const struct stm32_venc_config *config = ewl_instance.config;
return sys_read32(config->reg + BASE_HEncASIC);
}
EWLHwConfig_t EWLReadAsicConfig(void)
{
const struct stm32_venc_config *config = ewl_instance.config;
EWLHwConfig_t cfg_info;
uint32_t cfgval, cfgval2;
cfgval = sys_read32(config->reg + BASE_HEncSynth);
cfgval2 = sys_read32(config->reg + BASE_HEncSynth1);
cfg_info = EWLBuildHwConfig(cfgval, cfgval2);
LOG_DBG("maxEncodedWidth = %d", cfg_info.maxEncodedWidth);
LOG_DBG("h264Enabled = %d", cfg_info.h264Enabled);
LOG_DBG("jpegEnabled = %d", cfg_info.jpegEnabled);
LOG_DBG("vp8Enabled = %d", cfg_info.vp8Enabled);
LOG_DBG("vsEnabled = %d", cfg_info.vsEnabled);
LOG_DBG("rgbEnabled = %d", cfg_info.rgbEnabled);
LOG_DBG("searchAreaSmall = %d", cfg_info.searchAreaSmall);
LOG_DBG("scalingEnabled = %d", cfg_info.scalingEnabled);
LOG_DBG("address64bits = %d", cfg_info.addr64Support);
LOG_DBG("denoiseEnabled = %d", cfg_info.dnfSupport);
LOG_DBG("rfcEnabled = %d", cfg_info.rfcSupport);
LOG_DBG("instanctEnabled = %d", cfg_info.instantSupport);
LOG_DBG("busType = %d", cfg_info.busType);
LOG_DBG("synthesisLanguage = %d", cfg_info.synthesisLanguage);
LOG_DBG("busWidth = %d", cfg_info.busWidth * 32);
return cfg_info;
}
const void *EWLInit(EWLInitParam_t *param)
{
__ASSERT_NO_MSG(param != NULL);
__ASSERT_NO_MSG(param->clientType == EWL_CLIENT_TYPE_H264_ENC);
/* sync */
k_sem_init(&ewl_instance.complete, 0, 1);
/* set client type */
ewl_instance.client_type = param->clientType;
ewl_instance.irq_cnt = 0;
return (void *)&ewl_instance;
}
i32 EWLRelease(const void *inst)
{
ARG_UNUSED(inst);
return EWL_OK;
}
void EWLWriteReg(const void *instance, uint32_t offset, uint32_t val)
{
struct stm32_venc_ewl *inst = (struct stm32_venc_ewl *)instance;
const struct stm32_venc_config *config = inst->config;
sys_write32(val, config->reg + offset);
}
void EWLEnableHW(const void *instance, uint32_t offset, uint32_t val)
{
struct stm32_venc_ewl *inst = (struct stm32_venc_ewl *)instance;
const struct stm32_venc_config *config = inst->config;
sys_write32(val, config->reg + offset);
}
void EWLDisableHW(const void *instance, uint32_t offset, uint32_t val)
{
struct stm32_venc_ewl *inst = (struct stm32_venc_ewl *)instance;
const struct stm32_venc_config *config = inst->config;
sys_write32(val, config->reg + offset);
}
uint32_t EWLReadReg(const void *instance, uint32_t offset)
{
struct stm32_venc_ewl *inst = (struct stm32_venc_ewl *)instance;
const struct stm32_venc_config *config = inst->config;
return sys_read32(config->reg + offset);
}
i32 EWLMallocRefFrm(const void *instance, uint32_t size, EWLLinearMem_t *info)
{
return EWLMallocLinear(instance, size, info);
}
void EWLFreeRefFrm(const void *instance, EWLLinearMem_t *info)
{
EWLFreeLinear(instance, info);
}
i32 EWLMallocLinear(const void *instance, uint32_t size, EWLLinearMem_t *info)
{
struct stm32_venc_ewl *inst = (struct stm32_venc_ewl *)instance;
__ASSERT_NO_MSG(inst != NULL);
__ASSERT_NO_MSG(info != NULL);
/* align size */
uint32_t size_aligned = ROUND_UP(size, ALIGNMENT_INCR);
info->size = size_aligned;
/* allocate */
inst->chunks[inst->total_chunks] = (uint32_t *)EWL_HEAP_ALIGNED_ALLOC(size_aligned);
if (inst->chunks[inst->total_chunks] == NULL) {
LOG_DBG("unable to allocate %8d bytes", size_aligned);
return EWL_ERROR;
}
/* align given allocated buffer */
inst->aligned_chunks[inst->total_chunks] =
(uint32_t *)ROUND_UP((uint32_t)inst->chunks[inst->total_chunks], ALIGNMENT_INCR);
/* put the aligned pointer in the return structure */
info->virtualAddress = inst->aligned_chunks[inst->total_chunks];
if (info->virtualAddress == NULL) {
LOG_DBG("unable to get chunk for %8d bytes", size_aligned);
EWL_HEAP_ALIGNED_FREE(inst->chunks[inst->total_chunks]);
return EWL_ERROR;
}
inst->total_chunks++;
/* bus address is the same as virtual address because no MMU */
info->busAddress = (ptr_t)info->virtualAddress;
inst->mem_cnt += size;
LOG_DBG("allocated %8d bytes --> %p / 0x%x. Total : %d", size_aligned,
(void *)info->virtualAddress, info->busAddress, inst->mem_cnt);
return EWL_OK;
}
void EWLFreeLinear(const void *instance, EWLLinearMem_t *info)
{
struct stm32_venc_ewl *inst = (struct stm32_venc_ewl *)instance;
__ASSERT_NO_MSG(inst != NULL);
__ASSERT_NO_MSG(info != NULL);
/* find the pointer corresponding to the aligned buffer */
for (uint32_t i = 0; i < inst->total_chunks; i++) {
if (inst->aligned_chunks[i] == info->virtualAddress) {
if (inst->chunks[i] != NULL) {
EWL_HEAP_ALIGNED_FREE(inst->chunks[i]);
}
break;
}
}
info->virtualAddress = NULL;
info->busAddress = 0;
info->size = 0;
}
i32 EWLReserveHw(const void *inst)
{
__ASSERT_NO_MSG(inst != NULL);
return EWL_OK;
}
void EWLReleaseHw(const void *inst)
{
__ASSERT_NO_MSG(inst != NULL);
}
void *EWLmalloc(uint32_t n)
{
struct stm32_venc_ewl *inst = &ewl_instance;
void *p = NULL;
p = EWL_HEAP_ALIGNED_ALLOC(n);
if (p == NULL) {
LOG_ERR("alloc failed for size=%d", n);
return NULL;
}
inst->mem_cnt += n;
LOG_DBG("%8d bytes --> %p, total : %d", n, p, inst->mem_cnt);
return p;
}
void EWLfree(void *p)
{
if (p != NULL) {
EWL_HEAP_ALIGNED_FREE(p);
}
}
void *EWLcalloc(uint32_t n, uint32_t s)
{
void *p = EWLmalloc(n * s);
if (p != NULL) {
EWLmemset(p, 0, n * s);
}
return p;
}
void *EWLmemcpy(void *d, const void *s, uint32_t n)
{
return memcpy(d, s, (size_t)n);
}
void *EWLmemset(void *d, i32 c, uint32_t n)
{
return memset(d, c, (size_t)n);
}
int EWLmemcmp(const void *s1, const void *s2, uint32_t n)
{
return memcmp(s1, s2, n);
}
i32 EWLWaitHwRdy(const void *instance, uint32_t *slicesReady)
{
struct stm32_venc_ewl *inst = (struct stm32_venc_ewl *)instance;
const struct stm32_venc_config *config = inst->config;
uint32_t ret = EWL_HW_WAIT_TIMEOUT;
volatile uint32_t irq_stats;
uint32_t prevSlicesReady = 0;
k_timepoint_t timeout = sys_timepoint_calc(K_MSEC(EWL_TIMEOUT));
uint32_t start = sys_clock_tick_get_32();
__ASSERT_NO_MSG(inst != NULL);
/* check how to clear IRQ flags for VENC */
uint32_t clrByWrite1 = EWLReadReg(inst, BASE_HWFuse2) & HWCFGIrqClearSupport;
do {
irq_stats = sys_read32(config->reg + BASE_HEncIRQ);
/* get the number of completed slices from ASIC registers. */
if (slicesReady != NULL && *slicesReady > prevSlicesReady) {
*slicesReady = FIELD_GET(NUM_SLICES_READY_MASK,
sys_read32(config->reg + BASE_HEncControl7));
}
LOG_DBG("IRQ stat = %08x", irq_stats);
uint32_t hw_handshake_status = IS_BIT_SET(
sys_read32(config->reg + BASE_HEncInstantInput), LOW_LATENCY_HW_ITF_EN);
/* ignore the irq status of input line buffer in hw handshake mode */
if ((irq_stats == ASIC_STATUS_LINE_BUFFER_DONE) && (hw_handshake_status != 0UL)) {
sys_write32(ASIC_STATUS_FUSE, config->reg + BASE_HEncIRQ);
continue;
}
if ((irq_stats & ASIC_STATUS_ALL) != 0UL) {
/* clear IRQ and slice ready status */
uint32_t clr_stats;
irq_stats &= ~(ASIC_STATUS_SLICE_READY | ASIC_IRQ_LINE);
if (clrByWrite1 != 0UL) {
clr_stats = ASIC_STATUS_SLICE_READY | ASIC_IRQ_LINE;
} else {
clr_stats = irq_stats;
}
sys_write32(clr_stats, config->reg + BASE_HEncIRQ);
ret = EWL_OK;
break;
}
if (slicesReady != NULL && *slicesReady > prevSlicesReady) {
ret = EWL_OK;
break;
}
} while (!sys_timepoint_expired(timeout));
LOG_DBG("encoding = %d ms", k_ticks_to_ms_ceil32(sys_clock_tick_get_32() - start));
if (slicesReady != NULL) {
LOG_DBG("slicesReady = %d", *slicesReady);
}
return ret;
}
void EWLassert(bool expr, const char *str_expr, const char *file, unsigned int line)
{
__ASSERT(expr, "ASSERTION FAIL [%s] @ %s:%d", str_expr, file, line);
}
/* Set CONFIG_VC8000NANOE_LOG_LEVEL_DBG to enable library tracing */
void EWLtrace(const char *s)
{
printk("%s\n", s);
}
void EWLtraceparam(const char *fmt, const char *param, unsigned int val)
{
printk(fmt, param, val);
}
static int stm32_venc_enable_clock(const struct device *dev)
{
const struct stm32_venc_config *config = dev->config;
const struct device *clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
if (!device_is_ready(clk)) {
LOG_ERR("clock control device not ready");
return -ENODEV;
}
if (clock_control_on(clk, (clock_control_subsys_t)&config->pclken) != 0) {
return -EIO;
}
return 0;
}
static int stm32_venc_set_fmt(const struct device *dev, struct video_format *fmt)
{
struct stm32_venc_data *data = dev->data;
if (fmt->type == VIDEO_BUF_TYPE_INPUT) {
if ((fmt->pixelformat != VIDEO_PIX_FMT_NV12) &&
(fmt->pixelformat != VIDEO_PIX_FMT_RGB565)) {
LOG_ERR("invalid input pixel format");
return -EINVAL;
}
fmt->pitch = fmt->width * video_bits_per_pixel(fmt->pixelformat) / BITS_PER_BYTE;
data->in_fmt = *fmt;
} else {
if (fmt->pixelformat != VIDEO_PIX_FMT_H264) {
LOG_ERR("invalid output pixel format");
return -EINVAL;
}
fmt->size = ROUND_UP(fmt->width * fmt->height / VENC_ESTIMATED_COMPRESSION_RATIO,
ALIGNMENT_INCR);
data->out_fmt = *fmt;
}
return 0;
}
static int stm32_venc_get_fmt(const struct device *dev, struct video_format *fmt)
{
struct stm32_venc_data *data = dev->data;
if (fmt->type == VIDEO_BUF_TYPE_INPUT) {
*fmt = data->in_fmt;
} else {
*fmt = data->out_fmt;
}
return 0;
}
static int encoder_prepare(struct stm32_venc_data *data)
{
H264EncRet ret;
H264EncConfig cfg = {0};
H264EncPreProcessingCfg preproc_cfg = {0};
H264EncRateCtrl ratectrl_cfg = {0};
H264EncCodingCtrl codingctrl_cfg = {0};
data->frame_nb = 0;
/* set config to 1 reference frame */
cfg.refFrameAmount = 1;
/* frame rate */
cfg.frameRateDenom = 1;
cfg.frameRateNum = VENC_DEFAULT_FRAMERATE;
/* image resolution */
cfg.width = data->out_fmt.width;
cfg.height = data->out_fmt.height;
/* stream type */
cfg.streamType = H264ENC_BYTE_STREAM;
/* encoding level*/
cfg.level = VENC_DEFAULT_LEVEL;
cfg.svctLevel = 0;
cfg.viewMode = H264ENC_BASE_VIEW_SINGLE_BUFFER;
ret = H264EncInit(&cfg, &data->encoder);
if (ret != H264ENC_OK) {
LOG_ERR("H264EncInit error=%d", ret);
return -EIO;
}
/* set format conversion for preprocessing */
ret = H264EncGetPreProcessing(data->encoder, &preproc_cfg);
if (ret != H264ENC_OK) {
LOG_ERR("H264EncGetPreProcessing error=%d", ret);
return -EIO;
}
preproc_cfg.inputType = to_h264pixfmt(data->in_fmt.pixelformat);
ret = H264EncSetPreProcessing(data->encoder, &preproc_cfg);
if (ret != H264ENC_OK) {
LOG_ERR("H264EncSetPreProcessing error=%d", ret);
return -EIO;
}
/* setup coding ctrl */
ret = H264EncGetCodingCtrl(data->encoder, &codingctrl_cfg);
if (ret != H264ENC_OK) {
LOG_ERR("H264EncGetCodingCtrl error=%d", ret);
return -EIO;
}
ret = H264EncSetCodingCtrl(data->encoder, &codingctrl_cfg);
if (ret != H264ENC_OK) {
LOG_ERR("H264EncSetCodingCtrl error=%d", ret);
return -EIO;
}
/* set bit rate configuration */
ret = H264EncGetRateCtrl(data->encoder, &ratectrl_cfg);
if (ret != H264ENC_OK) {
LOG_ERR("H264EncGetRateCtrl error=%d", ret);
return -EIO;
}
/* Constant bitrate */
ratectrl_cfg.pictureRc = 0;
ratectrl_cfg.mbRc = 0;
ratectrl_cfg.pictureSkip = 0;
ratectrl_cfg.hrd = 0;
ratectrl_cfg.qpHdr = VENC_DEFAULT_QP;
ratectrl_cfg.qpMin = ratectrl_cfg.qpHdr;
ratectrl_cfg.qpMax = ratectrl_cfg.qpHdr;
ret = H264EncSetRateCtrl(data->encoder, &ratectrl_cfg);
if (ret != H264ENC_OK) {
LOG_ERR("H264EncSetRateCtrl error=%d", ret);
return -EIO;
}
return 0;
}
static int encoder_start(struct stm32_venc_data *data, struct video_buffer *output)
{
H264EncRet ret;
H264EncIn encIn = {0};
H264EncOut encOut = {0};
encIn.pOutBuf = (uint32_t *)output->buffer;
encIn.busOutBuf = (uint32_t)encIn.pOutBuf;
encIn.outBufSize = output->size;
/* create stream */
ret = H264EncStrmStart(data->encoder, &encIn, &encOut);
if (ret != H264ENC_OK) {
LOG_ERR("H264EncStrmStart error=%d", ret);
return -EIO;
}
output->bytesused = encOut.streamSize;
LOG_DBG("SPS/PPS generated, size= %d", output->bytesused);
data->resync = true;
return 0;
}
static int encoder_end(struct stm32_venc_data *data)
{
H264EncIn encIn = {0};
H264EncOut encOut = {0};
if (data->encoder != NULL) {
H264EncStrmEnd(data->encoder, &encIn, &encOut);
data->encoder = NULL;
}
return 0;
}
static int encode_frame(struct stm32_venc_data *data)
{
int ret = H264ENC_FRAME_READY;
struct video_buffer *input;
struct video_buffer *output;
H264EncIn encIn = {0};
H264EncOut encOut = {0};
if (k_fifo_is_empty(&data->in_fifo_in) || k_fifo_is_empty(&data->out_fifo_in)) {
/* Encoding deferred to next buffer queueing */
return 0;
}
input = k_fifo_get(&data->in_fifo_in, K_NO_WAIT);
output = k_fifo_get(&data->out_fifo_in, K_NO_WAIT);
if (data->encoder == NULL) {
ret = encoder_prepare(data);
if (ret) {
goto out;
}
ret = encoder_start(data, output);
/*
* Output buffer is now filled with SPS/PPS header, return it to application.
* Input buffer is dropped to not introduce latency.
*/
goto out;
}
/* one key frame every seconds */
if ((data->frame_nb % VENC_DEFAULT_FRAMERATE) == 0 || data->resync) {
/* if frame is the first or resync needed: set as intra coded */
encIn.codingType = H264ENC_INTRA_FRAME;
} else {
/* if there was a frame previously, set as predicted */
encIn.timeIncrement = 1;
encIn.codingType = H264ENC_PREDICTED_FRAME;
}
encIn.ipf = H264ENC_REFERENCE_AND_REFRESH;
encIn.ltrf = H264ENC_REFERENCE;
/* set input buffers to structures */
encIn.busLuma = (ptr_t)input->buffer;
encIn.busChromaU = (ptr_t)encIn.busLuma + data->in_fmt.width * data->in_fmt.height;
encIn.pOutBuf = (uint32_t *)output->buffer;
encIn.busOutBuf = (uint32_t)encIn.pOutBuf;
encIn.outBufSize = output->size;
encOut.streamSize = 0;
ret = H264EncStrmEncode(data->encoder, &encIn, &encOut, NULL, NULL, NULL);
output->bytesused = encOut.streamSize;
LOG_DBG("output=%p, encOut.streamSize=%d", output, encOut.streamSize);
switch (ret) {
case H264ENC_FRAME_READY:
/* save stream */
if (encOut.streamSize == 0) {
/* Nothing encoded */
data->resync = true;
goto out;
}
output->bytesused = encOut.streamSize;
break;
case H264ENC_FUSE_ERROR:
LOG_ERR("H264EncStrmEncode error=%d", ret);
LOG_ERR("DCMIPP and VENC desync at frame %d, restart the video", data->frame_nb);
encoder_end(data);
ret = encoder_start(data, output);
if (ret) {
goto out;
}
break;
default:
LOG_ERR("H264EncStrmEncode error=%d", ret);
LOG_ERR("error encoding frame %d", data->frame_nb);
encoder_end(data);
ret = encoder_start(data, output);
if (ret) {
goto out;
}
data->resync = true;
ret = -EIO;
goto out;
}
data->frame_nb++;
out:
k_fifo_put(&data->in_fifo_out, input);
k_fifo_put(&data->out_fifo_out, output);
return ret;
}
static int stm32_venc_set_stream(const struct device *dev, bool enable, enum video_buf_type type)
{
struct stm32_venc_data *data = dev->data;
ARG_UNUSED(type);
if (!enable) {
/* Stop VENC */
encoder_end(data);
}
return 0;
}
static int stm32_venc_enqueue(const struct device *dev, struct video_buffer *vbuf)
{
struct stm32_venc_data *data = dev->data;
int ret = 0;
k_mutex_lock(&data->lock, K_FOREVER);
if (vbuf->type == VIDEO_BUF_TYPE_INPUT) {
k_fifo_put(&data->in_fifo_in, vbuf);
} else {
k_fifo_put(&data->out_fifo_in, vbuf);
}
ret = encode_frame(data);
k_mutex_unlock(&data->lock);
return ret;
}
static int stm32_venc_dequeue(const struct device *dev, struct video_buffer **vbuf,
k_timeout_t timeout)
{
struct stm32_venc_data *data = dev->data;
int ret = 0;
k_mutex_lock(&data->lock, K_FOREVER);
if ((*vbuf)->type == VIDEO_BUF_TYPE_INPUT) {
*vbuf = k_fifo_get(&data->in_fifo_out, timeout);
} else {
*vbuf = k_fifo_get(&data->out_fifo_out, timeout);
}
if (*vbuf == NULL) {
ret = -EAGAIN;
goto out;
}
out:
k_mutex_unlock(&data->lock);
return ret;
}
ISR_DIRECT_DECLARE(stm32_venc_isr)
{
struct stm32_venc_ewl *inst = &ewl_instance;
const struct stm32_venc_config *config = inst->config;
uint32_t hw_handshake_status =
IS_BIT_SET(sys_read32(config->reg + BASE_HEncInstantInput), LOW_LATENCY_HW_ITF_EN);
uint32_t irq_status = sys_read32(config->reg + BASE_HEncIRQ);
inst->irq_status = irq_status;
inst->irq_cnt++;
if (hw_handshake_status == 0 && (irq_status & ASIC_STATUS_FUSE) != 0) {
sys_write32(ASIC_STATUS_FUSE | ASIC_IRQ_LINE, config->reg + BASE_HEncIRQ);
/* read back the IRQ status to update its value */
irq_status = sys_read32(config->reg + BASE_HEncIRQ);
}
if (irq_status != 0U) {
/* status flag is raised,
* clear the ones that the IRQ needs to clear
* and signal to EWLWaitHwRdy
*/
sys_write32(ASIC_STATUS_SLICE_READY | ASIC_IRQ_LINE, config->reg + BASE_HEncIRQ);
}
k_sem_give(&inst->complete);
return 0;
}
#define VENC_FORMAT_CAP(pixfmt) \
{ \
.pixelformat = pixfmt, \
.width_min = 48, \
.width_max = 1920, \
.height_min = 48, \
.height_max = 1088, \
.width_step = 16, \
.height_step = 16, \
}
static const struct video_format_cap in_fmts[] = {
VENC_FORMAT_CAP(VIDEO_PIX_FMT_NV12),
VENC_FORMAT_CAP(VIDEO_PIX_FMT_RGB565),
{0},
};
static const struct video_format_cap out_fmts[] = {
VENC_FORMAT_CAP(VIDEO_PIX_FMT_H264),
{0},
};
static int stm32_venc_get_caps(const struct device *dev, struct video_caps *caps)
{
if (caps->type == VIDEO_BUF_TYPE_INPUT) {
caps->format_caps = in_fmts;
} else {
caps->format_caps = out_fmts;
}
/* VENC produces full frames */
caps->min_line_count = caps->max_line_count = LINE_COUNT_HEIGHT;
caps->min_vbuf_count = 1;
return 0;
}
static DEVICE_API(video, stm32_venc_driver_api) = {
.set_format = stm32_venc_set_fmt,
.get_format = stm32_venc_get_fmt,
.set_stream = stm32_venc_set_stream,
.enqueue = stm32_venc_enqueue,
.dequeue = stm32_venc_dequeue,
.get_caps = stm32_venc_get_caps,
};
static void stm32_venc_irq_config_func(const struct device *dev)
{
IRQ_DIRECT_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), stm32_venc_isr, 0);
irq_enable(DT_INST_IRQN(0));
}
static struct stm32_venc_data stm32_venc_data_0 = {};
static const struct stm32_venc_config stm32_venc_config_0 = {
.reg = DT_INST_REG_ADDR(0),
.pclken = {.bus = DT_INST_CLOCKS_CELL(0, bus), .enr = DT_INST_CLOCKS_CELL(0, bits)},
.reset = RESET_DT_SPEC_INST_GET_BY_IDX(0, 0),
.irq_config = stm32_venc_irq_config_func,
};
static void RISAF_Config(void)
{
/* Define and initialize the master configuration structure */
RIMC_MasterConfig_t RIMC_master = {0};
/* Enable the clock for the RIFSC (RIF Security Controller) */
__HAL_RCC_RIFSC_CLK_ENABLE();
RIMC_master.MasterCID = RIF_CID_1;
RIMC_master.SecPriv = RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV;
/* Configure the master attributes for the video encoder peripheral (VENC) */
HAL_RIF_RIMC_ConfigMasterAttributes(RIF_MASTER_INDEX_VENC, &RIMC_master);
/* Set the secure and privileged attributes for the VENC as a slave */
HAL_RIF_RISC_SetSlaveSecureAttributes(RIF_RISC_PERIPH_INDEX_VENC,
RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV);
}
static int stm32_venc_init(const struct device *dev)
{
const struct stm32_venc_config *config = dev->config;
struct stm32_venc_data *data = dev->data;
int err;
/* Enable VENC clock */
err = stm32_venc_enable_clock(dev);
if (err < 0) {
LOG_ERR("clock enabling failed.");
return err;
}
/* Reset VENC */
if (!device_is_ready(config->reset.dev)) {
LOG_ERR("reset controller not ready");
return -ENODEV;
}
reset_line_toggle_dt(&config->reset);
data->dev = dev;
k_mutex_init(&data->lock);
k_fifo_init(&data->in_fifo_in);
k_fifo_init(&data->in_fifo_out);
k_fifo_init(&data->out_fifo_in);
k_fifo_init(&data->out_fifo_out);
/* Run IRQ init */
config->irq_config(dev);
RISAF_Config();
LOG_DBG("CPU frequency : %d", HAL_RCC_GetCpuClockFreq() / 1000000);
LOG_DBG("sysclk frequency : %d", HAL_RCC_GetSysClockFreq() / 1000000);
LOG_DBG("pclk5 frequency : %d", HAL_RCC_GetPCLK5Freq() / 1000000);
/* default input */
data->in_fmt.width = VENC_DEFAULT_WIDTH;
data->in_fmt.height = VENC_DEFAULT_HEIGHT;
data->in_fmt.pixelformat = VENC_DEFAULT_IN_FMT;
data->in_fmt.pitch = data->in_fmt.width;
/* default output */
data->out_fmt.width = VENC_DEFAULT_WIDTH;
data->out_fmt.height = VENC_DEFAULT_HEIGHT;
data->out_fmt.pixelformat = VENC_DEFAULT_OUT_FMT;
/* store config for register accesses */
ewl_instance.config = config;
LOG_DBG("%s inited", dev->name);
return 0;
}
DEVICE_DT_INST_DEFINE(0, &stm32_venc_init, NULL, &stm32_venc_data_0, &stm32_venc_config_0,
POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, &stm32_venc_driver_api);