SUBDEV驱动的set_fmt和get_fmt实现

开发环境:正点原子RV1126开发板

内核版本:4.19.111

用户API

c 复制代码
int ioctl(int fd, VIDIOC_SUBDEV_S_FMT, struct v4l2_subdev_foramt *argp);
int ioctl(int fd, VIDIOC_SUBDEV_G_FMT, struct v4l2_subdev_foramt *argp);

内核API

c 复制代码
/* 
1. 仅当format->which == V4L2_SUBDEV_FORMAT_TRY时,cfg有效,否则为NULL 
2. cfg有效时是一个数组,数组下标即pad_id
*/  
int set_fmt(struct v4l2_subdev *sd,  
    struct v4l2_subdev_pad_config* cfg,  
    struct v4l2_subdev_foramt *format);
int get_fmt(struct v4l2_subdev *sd,
    struct v4l2_subdev_pad_config *cfg,
    struct v4l2_subdev_format *fmt);
c 复制代码
/*
1. set_fmt实现只使用try_fmt成员
*/
struct v4l2_subdev_pad_config {  
    struct v4l2_mbus_framefmt try_fmt;
    struct v4l2_rect try_crop;  
    struct v4l2_rect try_compose;  
};
c 复制代码
/ *frame format on the media bus* /  
struct v4l2_mbus_framefmt {  
    __u32 width; //image width  
    __u32 height; //image height  
    __u32 code; //data format code (from enum v4l2_mbus_pixelcode)  
    __u32 field; //used interlacing type (from enum v4l2_field)  
    __u32 colorspace; //colorspace of the data (from enum v4l2_colorspace)  
    __u32 ycbcrenc; //YCbCr encoding of the data (from enum v4l2_ycbcr_encoding)  
    __u32 quantization; //quantization of the data (from enum v4l2_quantization)  
    __u32 xfer_func; //transfer function of the data (from enum v4l2_xfer_func)  
    __u16 reserved[11];  
};

/ *Pad-level media bus format* /  
struct v4l2_subdev_format {  
    __u32 which; //format type (from enum v4l2_subdev_format_whence)  
    __u32 pad; //pad number, as reported by the media API  
    struct v4l2_mbus_framefmt format; //media bus format (format code and frame size)  
    __u32 reserved[8];  
};

驱动实现

set_fmt实现

set_fmt的基本逻辑:

1.寻找与申请一致的mbus_code,否则使用supported_mbus_code0;寻找与申请差距最小的frame_size。

2.TRY模式向fmt->format和cfg更新协商后配置;ACTIVE模式向fmt->format更新协商后配置,以及硬件操作。

set_fmt的注意事项:

1.辅助函数__v4l2_find_nearest_size()前需检查cfg非NULL,如果是NULL不要报错,因为要兼容旧模式。 2.除了code、width、height成员,其他成员赋默认值,不要向应用层提供不确定值。

c 复制代码
struct imx415 {
    struct i2c_client *client;
    struct v4l2_subdev subdev;
    struct mutex mutex;
    struct v4l2_mbus_framefmt active_fmt;
};

static const u32 supported_mbus_code[] = {
    MEDIA_BUS_FMT_SGBRG10_1X10,
    MEDIA_BUS_FMT_SGBRG12_1X12,
};
static const struct v4l2_frmsize_discrete supported_frmsize[] = {
    {.width = 3864, .height = 2192,},
    {.width = 1944, .height = 1097,}
};
static const s64 supported_link_freq[] = {
    891000000,
    446000000,
    743000000,
    297000000,
};

static int imx415_set_fmt(struct v4l2_subdev *sd,
                struct v4l2_subdev_pad_config *cfg,
                struct v4l2_subdev_format *fmt)
{
    struct imx415 *imx415 = to_imx415(sd);
    u32 i = 0;
    u32 mbus_code_id = 0;
    u32 frmsize_id = 0;
    const struct v4l2_frmsize_discrete *best_frmsize;

    /* Pad validity check: sensor only has pad0 */
    if (fmt->pad != 0) {
        dev_err(sd->dev, "Invalid pad: %d\n", fmt->pad);
        return -EINVAL;
    }

    /* Check fmt->which validity: only support TRY / ACTIVE modes */
    if (fmt->which != V4L2_SUBDEV_FORMAT_TRY && fmt->which != V4L2_SUBDEV_FORMAT_ACTIVE) {
        dev_err(sd->dev, "Invalid format mode: %d\n", fmt->which);
        return -EINVAL;
    }

    /* Find matching mbus_code, use the first if not matched */
    for (i = 0; i < ARRAY_SIZE(supported_mbus_code); i++) {
        if (fmt->format.code == supported_mbus_code[i]) {
            mbus_code_id = i;
            break;
        }
    }
    if (i == ARRAY_SIZE(supported_mbus_code)) {
        mbus_code_id = 0;
        dev_warn(sd->dev, "Unsupported mbus format: 0x%x, use default 0x%x\n", fmt->format.code, supported_mbus_code[0]);
    }

    /* 寻找与申请差距最小的frame_size */
    best_frmsize = __v4l2_find_nearest_size(supported_frmsize, ARRAY_SIZE(supported_frmsize),
			 sizeof(struct v4l2_frmsize_discrete), offsetof(struct v4l2_frmsize_discrete, width),
			 offsetof(struct v4l2_frmsize_discrete, height), fmt->format.width, fmt->format.height);
    frmsize_id = best_frmsize - supported_frmsize;

    /* fmt->foramt赋值,除了width、height、code外赋默认值 */
    fmt->format.height = supported_frmsize[frmsize_id].height;
    fmt->format.width = supported_frmsize[frmsize_id].width;
    fmt->format.code = supported_mbus_code[mbus_code_id];
    fmt->format.field = V4L2_FIELD_NONE;
    fmt->format.colorspace = V4L2_COLORSPACE_SRGB;
    fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
    fmt->format.quantization = V4L2_QUANTIZATION_DEFAULT;
    fmt->format.xfer_func = V4L2_XFER_FUNC_DEFAULT;

    if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
    {
        if (cfg) {
            *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
        }
    } else {
        mutex_lock(&imx415->mutex);
        
        /* 这里可以添加对硬件寄存器的配置代码,设置传感器的输出格式和分辨率 */

        imx415->active_fmt = fmt->format;
        mutex_unlock(&imx415->mutex);
    }

    return 0;
}
get_fmt实现
c 复制代码
static int imx415_get_fmt(struct v4l2_subdev *sd,
		       struct v4l2_subdev_pad_config *cfg,
		       struct v4l2_subdev_format *fmt)
{
    struct imx415 *imx415 = to_imx415(sd);

    /* Pad validity check: sensor only has pad0 */
    if (fmt->pad != 0) {
        dev_err(sd->dev, "Invalid pad: %d\n", fmt->pad);
        return -EINVAL;
    }

    /* Check fmt->which validity: only support TRY / ACTIVE modes */
    if (fmt->which != V4L2_SUBDEV_FORMAT_TRY && fmt->which != V4L2_SUBDEV_FORMAT_ACTIVE) {
        dev_err(sd->dev, "Invalid format mode: %d\n", fmt->which);
        return -EINVAL;
    }

    if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
        if (cfg) {
            fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
        }
    } else {
        mutex_lock(&imx415->mutex);
        fmt->format = imx415->active_fmt;
        mutex_unlock(&imx415->mutex);
    }
    
    return 0;
}
相关推荐
bush415 小时前
嵌入式linux学习记录七,中断
linux·嵌入式
都在酒里1 天前
FreeRTOS 综合实战:串口命令控制 LED 闪烁模式与系统监控
stm32·单片机·嵌入式硬件·嵌入式·rtos·嵌入式软件
济6171 天前
ROS2 Humble 开发专栏Ubuntu22.04|基于OpenCV 实现机器视觉人脸检测实验指南
嵌入式硬件·嵌入式·ros2·机器人方向
济6171 天前
【ROS2 Humble 开发专栏】Ubuntu22.04 基于 OpenCV 实现颜色阈值分割与目标坐标定位|附完整工程源码
嵌入式硬件·嵌入式·ros2·机器人开发·机器人方向
2023自学中1 天前
Linux虚拟机,VSCode + GDB 调试配置:launch.json 模板
linux·vscode·嵌入式
2023自学中1 天前
Linux 解压命令速查表
linux·服务器·嵌入式·开发板
lularible2 天前
从沙子到车辙(5.2):实时操作系统
开源·嵌入式·汽车电子
2023自学中2 天前
Linux虚拟机 CMakeLists.txt:x86 与 ARM 双架构编译脚本
linux·c语言·c++·嵌入式
webmote332 天前
从零打造虚拟小智:用浏览器模拟 IoT 设备的实践之路
aigc·.net·嵌入式
FreakStudio2 天前
大话电容传感器和电容SOC芯片,看这一篇就够了
python·单片机·嵌入式·面向对象·并行计算·电子diy·电子计算机