diff options
Diffstat (limited to 'drivers/media/v4l2-core/v4l2-subdev.c')
-rw-r--r-- | drivers/media/v4l2-core/v4l2-subdev.c | 150 |
1 files changed, 143 insertions, 7 deletions
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 730c31eaa35e..a3e412c73715 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -168,8 +168,22 @@ static inline int check_pad(struct v4l2_subdev *sd, u32 pad) return 0; } -static int check_state_pads(u32 which, struct v4l2_subdev_state *state) +static int check_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, + u32 which, u32 pad, u32 stream) { + if (sd->flags & V4L2_SUBDEV_FL_STREAMS) { +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) + if (!v4l2_subdev_state_get_stream_format(state, pad, stream)) + return -EINVAL; + return 0; +#else + return -EINVAL; +#endif + } + + if (stream != 0) + return -EINVAL; + if (which == V4L2_SUBDEV_FORMAT_TRY && (!state || !state->pads)) return -EINVAL; @@ -184,7 +198,7 @@ static inline int check_format(struct v4l2_subdev *sd, return -EINVAL; return check_which(format->which) ? : check_pad(sd, format->pad) ? : - check_state_pads(format->which, state); + check_state(sd, state, format->which, format->pad, format->stream); } static int call_get_fmt(struct v4l2_subdev *sd, @@ -211,7 +225,7 @@ static int call_enum_mbus_code(struct v4l2_subdev *sd, return -EINVAL; return check_which(code->which) ? : check_pad(sd, code->pad) ? : - check_state_pads(code->which, state) ? : + check_state(sd, state, code->which, code->pad, code->stream) ? : sd->ops->pad->enum_mbus_code(sd, state, code); } @@ -223,7 +237,7 @@ static int call_enum_frame_size(struct v4l2_subdev *sd, return -EINVAL; return check_which(fse->which) ? : check_pad(sd, fse->pad) ? : - check_state_pads(fse->which, state) ? : + check_state(sd, state, fse->which, fse->pad, fse->stream) ? : sd->ops->pad->enum_frame_size(sd, state, fse); } @@ -258,7 +272,7 @@ static int call_enum_frame_interval(struct v4l2_subdev *sd, return -EINVAL; return check_which(fie->which) ? : check_pad(sd, fie->pad) ? : - check_state_pads(fie->which, state) ? : + check_state(sd, state, fie->which, fie->pad, fie->stream) ? : sd->ops->pad->enum_frame_interval(sd, state, fie); } @@ -270,7 +284,7 @@ static inline int check_selection(struct v4l2_subdev *sd, return -EINVAL; return check_which(sel->which) ? : check_pad(sd, sel->pad) ? : - check_state_pads(sel->which, state); + check_state(sd, state, sel->which, sel->pad, sel->stream); } static int call_get_selection(struct v4l2_subdev *sd, @@ -1122,7 +1136,8 @@ __v4l2_subdev_state_alloc(struct v4l2_subdev *sd, const char *lock_name, else state->lock = &state->_lock; - if (sd->entity.num_pads) { + /* Drivers that support streams do not need the legacy pad config */ + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS) && sd->entity.num_pads) { state->pads = kvcalloc(sd->entity.num_pads, sizeof(*state->pads), GFP_KERNEL); if (!state->pads) { @@ -1162,6 +1177,7 @@ void __v4l2_subdev_state_free(struct v4l2_subdev_state *state) mutex_destroy(&state->_lock); kfree(state->routing.routes); + kvfree(state->stream_configs.configs); kvfree(state->pads); kfree(state); } @@ -1191,6 +1207,55 @@ EXPORT_SYMBOL_GPL(v4l2_subdev_cleanup); #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) +static int +v4l2_subdev_init_stream_configs(struct v4l2_subdev_stream_configs *stream_configs, + const struct v4l2_subdev_krouting *routing) +{ + struct v4l2_subdev_stream_configs new_configs = { 0 }; + struct v4l2_subdev_route *route; + u32 idx; + + /* Count number of formats needed */ + for_each_active_route(routing, route) { + /* + * Each route needs a format on both ends of the route. + */ + new_configs.num_configs += 2; + } + + if (new_configs.num_configs) { + new_configs.configs = kvcalloc(new_configs.num_configs, + sizeof(*new_configs.configs), + GFP_KERNEL); + + if (!new_configs.configs) + return -ENOMEM; + } + + /* + * Fill in the 'pad' and stream' value for each item in the array from + * the routing table + */ + idx = 0; + + for_each_active_route(routing, route) { + new_configs.configs[idx].pad = route->sink_pad; + new_configs.configs[idx].stream = route->sink_stream; + + idx++; + + new_configs.configs[idx].pad = route->source_pad; + new_configs.configs[idx].stream = route->source_stream; + + idx++; + } + + kvfree(stream_configs->configs); + *stream_configs = new_configs; + + return 0; +} + int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { @@ -1217,6 +1282,7 @@ int v4l2_subdev_set_routing(struct v4l2_subdev *sd, const struct v4l2_subdev_krouting *src = routing; struct v4l2_subdev_krouting new_routing = { 0 }; size_t bytes; + int r; if (unlikely(check_mul_overflow((size_t)src->num_routes, sizeof(*src->routes), &bytes))) @@ -1232,6 +1298,13 @@ int v4l2_subdev_set_routing(struct v4l2_subdev *sd, new_routing.num_routes = src->num_routes; + r = v4l2_subdev_init_stream_configs(&state->stream_configs, + &new_routing); + if (r) { + kfree(new_routing.routes); + return r; + } + kfree(dst->routes); *dst = new_routing; @@ -1259,6 +1332,69 @@ __v4l2_subdev_next_active_route(const struct v4l2_subdev_krouting *routing, } EXPORT_SYMBOL_GPL(__v4l2_subdev_next_active_route); +struct v4l2_mbus_framefmt * +v4l2_subdev_state_get_stream_format(struct v4l2_subdev_state *state, + unsigned int pad, u32 stream) +{ + struct v4l2_subdev_stream_configs *stream_configs; + unsigned int i; + + lockdep_assert_held(state->lock); + + stream_configs = &state->stream_configs; + + for (i = 0; i < stream_configs->num_configs; ++i) { + if (stream_configs->configs[i].pad == pad && + stream_configs->configs[i].stream == stream) + return &stream_configs->configs[i].fmt; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_format); + +struct v4l2_rect * +v4l2_subdev_state_get_stream_crop(struct v4l2_subdev_state *state, + unsigned int pad, u32 stream) +{ + struct v4l2_subdev_stream_configs *stream_configs; + unsigned int i; + + lockdep_assert_held(state->lock); + + stream_configs = &state->stream_configs; + + for (i = 0; i < stream_configs->num_configs; ++i) { + if (stream_configs->configs[i].pad == pad && + stream_configs->configs[i].stream == stream) + return &stream_configs->configs[i].crop; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_crop); + +struct v4l2_rect * +v4l2_subdev_state_get_stream_compose(struct v4l2_subdev_state *state, + unsigned int pad, u32 stream) +{ + struct v4l2_subdev_stream_configs *stream_configs; + unsigned int i; + + lockdep_assert_held(state->lock); + + stream_configs = &state->stream_configs; + + for (i = 0; i < stream_configs->num_configs; ++i) { + if (stream_configs->configs[i].pad == pad && + stream_configs->configs[i].stream == stream) + return &stream_configs->configs[i].compose; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_state_get_stream_compose); + #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ #endif /* CONFIG_MEDIA_CONTROLLER */ |