| #include <media/v4l2-device.h> |
| #include <media/v4l2-ioctl.h> |
| #include <media/videobuf2-vmalloc.h> |
| #include <media/v4l2-ctrls.h> |
| |
| #include "ambxc.h" |
| #include "ambxc_ctrls.h" |
| |
| #define IS_AMBXC_PRIV(x) ((V4L2_CTRL_ID2CLASS(x) == V4L2_CTRL_CLASS_MPEG) \ |
| && V4L2_CTRL_DRIVER_PRIV(x)) |
| |
| struct ambxc_control { |
| __u32 id; |
| enum v4l2_ctrl_type type; |
| __u8 name[32]; /* Whatever */ |
| __s32 minimum; /* Note signedness */ |
| __s32 maximum; |
| __s32 step; |
| __u32 menu_skip_mask; |
| __s32 default_value; |
| __u32 flags; |
| __u32 reserved[2]; |
| __u8 is_volatile; |
| }; |
| |
| static struct ambxc_control controls[] = { |
| { |
| .id = V4L2_CID_MPEG_VIDEO_GOP_SIZE, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .minimum = 0, |
| .maximum = (1 << 16) - 1, |
| .step = 1, |
| .default_value = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, |
| .type = V4L2_CTRL_TYPE_MENU, |
| .minimum = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE, |
| .maximum = V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, |
| .default_value = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE, |
| .menu_skip_mask = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .minimum = 1, |
| .maximum = (1 << 16) - 1, |
| .step = 1, |
| .default_value = 1, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .minimum = 1900, |
| .maximum = (1 << 30) - 1, |
| .step = 1, |
| .default_value = 1900, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .minimum = 0, |
| .maximum = (1 << 16) - 1, |
| .step = 1, |
| .default_value = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, |
| .type = V4L2_CTRL_TYPE_BOOLEAN, |
| .minimum = 0, |
| .maximum = 1, |
| .step = 1, |
| .default_value = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_BITRATE, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .minimum = 1, |
| .maximum = (1 << 30) - 1, |
| .step = 1, |
| .default_value = 1, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_VBV_SIZE, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .minimum = 0, |
| .maximum = (1 << 16) - 1, |
| .step = 1, |
| .default_value = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .minimum = 0, |
| .maximum = (1 << 16) - 1, |
| .step = 1, |
| .default_value = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_HEADER_MODE, |
| .type = V4L2_CTRL_TYPE_MENU, |
| .minimum = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE, |
| .maximum = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, |
| .default_value = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE, |
| .menu_skip_mask = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_B_FRAMES, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .minimum = 0, |
| .maximum = 2, |
| .step = 1, |
| .default_value = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, |
| .type = V4L2_CTRL_TYPE_MENU, |
| .minimum = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, |
| .maximum = V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH, |
| .default_value = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, |
| .menu_skip_mask = ~( |
| (1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | |
| (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | |
| (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) |
| ), |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL, |
| .type = V4L2_CTRL_TYPE_MENU, |
| .minimum = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, |
| .maximum = V4L2_MPEG_VIDEO_H264_LEVEL_4_0, |
| .default_value = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, |
| .type = V4L2_CTRL_TYPE_MENU, |
| .minimum = V4L2_MPEG_VIDEO_MPEG4_LEVEL_0, |
| .maximum = V4L2_MPEG_VIDEO_MPEG4_LEVEL_5, |
| .default_value = V4L2_MPEG_VIDEO_MPEG4_LEVEL_0, |
| .menu_skip_mask = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, |
| .type = V4L2_CTRL_TYPE_MENU, |
| .minimum = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED, |
| .maximum = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, |
| .default_value = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED, |
| .menu_skip_mask = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .minimum = -6, |
| .maximum = 6, |
| .step = 1, |
| .default_value = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .minimum = -6, |
| .maximum = 6, |
| .step = 1, |
| .default_value = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE, |
| .type = V4L2_CTRL_TYPE_MENU, |
| .minimum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC, |
| .maximum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, |
| .default_value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC, |
| .menu_skip_mask = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, |
| .type = V4L2_CTRL_TYPE_BOOLEAN, |
| .minimum = 0, |
| .maximum = 1, |
| .step = 1, |
| .default_value = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE, |
| .type = V4L2_CTRL_TYPE_BOOLEAN, |
| .minimum = 0, |
| .maximum = 1, |
| .step = 1, |
| .default_value = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .minimum = 0, |
| .maximum = 51, |
| .step = 1, |
| .default_value = 1, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .minimum = 0, |
| .maximum = 51, |
| .step = 1, |
| .default_value = 1, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .minimum = 0, |
| .maximum = 51, |
| .step = 1, |
| .default_value = 1, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .minimum = 0, |
| .maximum = 51, |
| .step = 1, |
| .default_value = 1, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .minimum = 0, |
| .maximum = 51, |
| .step = 1, |
| .default_value = 1, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .name = "MPEG4 I-Frame QP value", |
| .minimum = 1, |
| .maximum = 31, |
| .step = 1, |
| .default_value = 1, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .name = "MPEG4 Minimum QP value", |
| .minimum = 1, |
| .maximum = 31, |
| .step = 1, |
| .default_value = 1, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .name = "MPEG4 Maximum QP value", |
| .minimum = 0, |
| .maximum = 51, |
| .step = 1, |
| .default_value = 1, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .name = "MPEG4 P frame QP value", |
| .minimum = 1, |
| .maximum = 31, |
| .step = 1, |
| .default_value = 1, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .name = "MPEG4 B frame QP value", |
| .minimum = 1, |
| .maximum = 31, |
| .step = 1, |
| .default_value = 1, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE, |
| .type = V4L2_CTRL_TYPE_BOOLEAN, |
| .minimum = 0, |
| .maximum = 1, |
| .step = 1, |
| .default_value = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC, |
| .type = V4L2_CTRL_TYPE_MENU, |
| .minimum = V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED, |
| .maximum = V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED, |
| .default_value = V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED, |
| .menu_skip_mask = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .minimum = 0, |
| .maximum = (1 << 16) - 1, |
| .step = 1, |
| .default_value = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .minimum = 0, |
| .maximum = (1 << 16) - 1, |
| .step = 1, |
| .default_value = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, |
| .type = V4L2_CTRL_TYPE_BOOLEAN, |
| .minimum = 0, |
| .maximum = 1, |
| .step = 1, |
| .default_value = 1, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, |
| .type = V4L2_CTRL_TYPE_INTEGER, |
| .minimum = 0, |
| .maximum = (1 << 16) - 1, |
| .step = 1, |
| .default_value = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, |
| .type = V4L2_CTRL_TYPE_MENU, |
| .minimum = V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE, |
| .maximum = V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE, |
| .default_value = V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE, |
| .menu_skip_mask = 0, |
| }, |
| { |
| .id = V4L2_CID_MPEG_VIDEO_MPEG4_QPEL, |
| .type = V4L2_CTRL_TYPE_BOOLEAN, |
| .minimum = 0, |
| .maximum = 1, |
| .step = 1, |
| .default_value = 0, |
| }, |
| }; |
| |
| #define NUM_CTRLS ARRAY_SIZE(controls) |
| |
| /* Set controls - v4l2 control framework */ |
| static int ambxc_s_ctrl(struct v4l2_ctrl *ctrl) |
| { |
| struct ambxc_ctx *ctx; |
| struct ambxc_dev *dev; |
| int ret = 0; |
| |
| ctx = container_of(ctrl->handler, struct ambxc_ctx, hdl); |
| dev = ctx->dev; |
| |
| switch (ctrl->id) { |
| case V4L2_CID_MPEG_VIDEO_GOP_SIZE: |
| break; |
| case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: |
| break; |
| case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: |
| break; |
| case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: |
| break; |
| case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: |
| break; |
| case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: |
| break; |
| case V4L2_CID_MPEG_VIDEO_BITRATE: |
| break; |
| case V4L2_CID_MPEG_VIDEO_VBV_SIZE: |
| break; |
| case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: |
| break; |
| case V4L2_CID_MPEG_VIDEO_HEADER_MODE: |
| break; |
| case V4L2_CID_MPEG_VIDEO_B_FRAMES: |
| break; |
| case V4L2_CID_MPEG_VIDEO_H264_PROFILE: |
| switch (ctrl->val) { |
| case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: |
| break; |
| case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: |
| break; |
| case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: |
| break; |
| case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: |
| break; |
| default: |
| ret = -EINVAL; |
| } |
| break; |
| case V4L2_CID_MPEG_VIDEO_H264_LEVEL: |
| break; |
| case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: |
| break; |
| case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: |
| break; |
| case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: |
| break; |
| case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: |
| break; |
| case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: |
| break; |
| case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: |
| break; |
| case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: |
| break; |
| case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: |
| break; |
| case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: |
| break; |
| case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: |
| break; |
| case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: |
| break; |
| case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: |
| break; |
| case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: |
| case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP: |
| break; |
| case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP: |
| case V4L2_CID_MPEG_VIDEO_H263_MIN_QP: |
| break; |
| case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP: |
| case V4L2_CID_MPEG_VIDEO_H263_MAX_QP: |
| break; |
| case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: |
| case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP: |
| break; |
| case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP: |
| case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP: |
| break; |
| case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE: |
| break; |
| case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: |
| break; |
| case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH: |
| break; |
| case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT: |
| break; |
| case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: |
| break; |
| case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: |
| break; |
| case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: |
| switch (ctrl->val) { |
| case V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE: |
| break; |
| case V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE: |
| break; |
| default: |
| ret = -EINVAL; |
| } |
| break; |
| case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL: |
| break; |
| default: |
| v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n", |
| ctrl->id, ctrl->val); |
| ret = -EINVAL; |
| } |
| return ret; |
| } |
| |
| static int ambxc_g_v_ctrl(struct v4l2_ctrl *ctrl) |
| { |
| struct ambxc_ctx *ctx; |
| struct ambxc_dev *dev; |
| |
| ctx = container_of(ctrl->handler, struct ambxc_ctx, hdl); |
| dev = ctx->dev; |
| |
| switch (ctrl->id) { |
| case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static const struct v4l2_ctrl_ops ambxc_ctrl_ops = { |
| .s_ctrl = ambxc_s_ctrl, |
| .g_volatile_ctrl = ambxc_g_v_ctrl, |
| }; |
| |
| int ambxc_init_ctrls(struct ambxc_ctx *ctx) |
| { |
| struct ambxc_dev *dev = ctx->dev; |
| struct v4l2_ctrl_config cfg; |
| struct ambxc_control *ctrl; |
| int i; |
| |
| v4l2_ctrl_handler_init(&ctx->hdl, NUM_CTRLS); |
| if (ctx->hdl.error) { |
| dprintk(dev, "v4l2_ctrl_handler_init failed\n"); |
| return ctx->hdl.error; |
| } |
| |
| for (i = 0; i < NUM_CTRLS; i++) { |
| |
| ctrl = &controls[i]; |
| |
| if (IS_AMBXC_PRIV(ctrl->id)) { |
| |
| memset(&cfg, 0, sizeof(struct v4l2_ctrl_config)); |
| cfg.ops = &ambxc_ctrl_ops; |
| cfg.id = ctrl->id; |
| cfg.min = ctrl->minimum; |
| cfg.max = ctrl->maximum; |
| cfg.def = ctrl->default_value; |
| cfg.name = ctrl->name; |
| cfg.type = ctrl->type; |
| |
| cfg.step = ctrl->step; |
| cfg.menu_skip_mask = 0; |
| |
| ctx->ctrls[i] = v4l2_ctrl_new_custom(&ctx->hdl, |
| &cfg, NULL); |
| } |
| else { |
| if (ctrl->type == V4L2_CTRL_TYPE_MENU) { |
| ctx->ctrls[i] = v4l2_ctrl_new_std_menu( |
| &ctx->hdl, |
| &ambxc_ctrl_ops, ctrl->id, |
| ctrl->maximum, 0, |
| ctrl->default_value); |
| } |
| else { |
| ctx->ctrls[i] = v4l2_ctrl_new_std( |
| &ctx->hdl, |
| &ambxc_ctrl_ops, ctrl->id, |
| ctrl->minimum, |
| ctrl->maximum, ctrl->step, |
| ctrl->default_value); |
| } |
| } |
| if (ctx->hdl.error) { |
| dprintk(dev, "Adding control (%d) failed\n", i); |
| return ctx->hdl.error; |
| } |
| if (ctrl->is_volatile && ctx->ctrls[i]) |
| ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_VOLATILE; |
| } |
| |
| v4l2_ctrl_handler_setup(&ctx->hdl); |
| |
| return 0; |
| } |
| |
| void ambxc_release_ctrls(struct ambxc_ctx *ctx) |
| { |
| v4l2_ctrl_handler_free(&ctx->hdl); |
| } |