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_code[0];寻找与申请差距最小的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;
}
相关推荐
FreakStudio6 小时前
和做工厂系统的印尼老哥,复刻了一套属于 MicroPython 的包管理系统
python·单片机·嵌入式·大学生·面向对象·并行计算·电子diy·电子计算机
7yewh13 小时前
电阻应变片的深度探究
嵌入式
CinzWS14 小时前
A53低功耗验证:状态机验证与唤醒时序检查——芯片的“睡眠科学“
嵌入式·芯片验证·原型验证·a53
keibin98817 小时前
UEFI规范到底规定了什么?五分钟读懂核心概念
嵌入式
CinzWS20 小时前
DMA控制器 - 沉默的加速器:驾驭通道仲裁、传输握手与内存一致性的双刃剑
arm开发·dma·嵌入式
CinzWS21 小时前
GIPC(处理器间通信) - 多核的桥梁:剖析硬件队列、门铃中断与共享内存的数据一致性困局
arm开发·嵌入式·gipc
阿源-1 天前
嵌入式-常见简单通信协议介绍
嵌入式
SSONICX2 天前
OpenWrt:安装网卡驱动
嵌入式
lularible2 天前
PTP协议精讲(3.7):传输层实现——PTP报文的“高速公路“
网络·网络协议·开源·嵌入式·ptp
济6172 天前
FreeRTOS 控制任务设计 (2)--- 运动学逆解 + PID 闭环 + PWM 驱动全流程实现
stm32·单片机·嵌入式·freertos