diff options
| author | Dmitry Torokhov <[email protected]> | 2023-08-30 16:06:38 -0700 | 
|---|---|---|
| committer | Dmitry Torokhov <[email protected]> | 2023-08-30 16:06:38 -0700 | 
| commit | 1ac731c529cd4d6adbce134754b51ff7d822b145 (patch) | |
| tree | 143ab3f35ca5f3b69f583c84e6964b17139c2ec1 /drivers/media/v4l2-core/v4l2-subdev.c | |
| parent | 07b4c950f27bef0362dc6ad7ee713aab61d58149 (diff) | |
| parent | 54116d442e001e1b6bd482122043b1870998a1f3 (diff) | |
Merge branch 'next' into for-linus
Prepare input updates for 6.6 merge window.
Diffstat (limited to 'drivers/media/v4l2-core/v4l2-subdev.c')
| -rw-r--r-- | drivers/media/v4l2-core/v4l2-subdev.c | 200 | 
1 files changed, 166 insertions, 34 deletions
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index b10045c02f43..2ec179cd1264 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -510,8 +510,11 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,  	struct video_device *vdev = video_devdata(file);  	struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);  	struct v4l2_fh *vfh = file->private_data; +	struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);  	bool ro_subdev = test_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags);  	bool streams_subdev = sd->flags & V4L2_SUBDEV_FL_STREAMS; +	bool client_supports_streams = subdev_fh->client_caps & +				       V4L2_SUBDEV_CLIENT_CAP_STREAMS;  	int rval;  	switch (cmd) { @@ -636,6 +639,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,  	case VIDIOC_SUBDEV_G_FMT: {  		struct v4l2_subdev_format *format = arg; +		if (!client_supports_streams) +			format->stream = 0; +  		memset(format->reserved, 0, sizeof(format->reserved));  		memset(format->format.reserved, 0, sizeof(format->format.reserved));  		return v4l2_subdev_call(sd, pad, get_fmt, state, format); @@ -647,6 +653,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,  		if (format->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)  			return -EPERM; +		if (!client_supports_streams) +			format->stream = 0; +  		memset(format->reserved, 0, sizeof(format->reserved));  		memset(format->format.reserved, 0, sizeof(format->format.reserved));  		return v4l2_subdev_call(sd, pad, set_fmt, state, format); @@ -656,6 +665,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,  		struct v4l2_subdev_crop *crop = arg;  		struct v4l2_subdev_selection sel; +		if (!client_supports_streams) +			crop->stream = 0; +  		memset(crop->reserved, 0, sizeof(crop->reserved));  		memset(&sel, 0, sizeof(sel));  		sel.which = crop->which; @@ -677,6 +689,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,  		if (crop->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)  			return -EPERM; +		if (!client_supports_streams) +			crop->stream = 0; +  		memset(crop->reserved, 0, sizeof(crop->reserved));  		memset(&sel, 0, sizeof(sel));  		sel.which = crop->which; @@ -695,6 +710,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,  	case VIDIOC_SUBDEV_ENUM_MBUS_CODE: {  		struct v4l2_subdev_mbus_code_enum *code = arg; +		if (!client_supports_streams) +			code->stream = 0; +  		memset(code->reserved, 0, sizeof(code->reserved));  		return v4l2_subdev_call(sd, pad, enum_mbus_code, state,  					code); @@ -703,6 +721,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,  	case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: {  		struct v4l2_subdev_frame_size_enum *fse = arg; +		if (!client_supports_streams) +			fse->stream = 0; +  		memset(fse->reserved, 0, sizeof(fse->reserved));  		return v4l2_subdev_call(sd, pad, enum_frame_size, state,  					fse); @@ -711,6 +732,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,  	case VIDIOC_SUBDEV_G_FRAME_INTERVAL: {  		struct v4l2_subdev_frame_interval *fi = arg; +		if (!client_supports_streams) +			fi->stream = 0; +  		memset(fi->reserved, 0, sizeof(fi->reserved));  		return v4l2_subdev_call(sd, video, g_frame_interval, arg);  	} @@ -721,6 +745,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,  		if (ro_subdev)  			return -EPERM; +		if (!client_supports_streams) +			fi->stream = 0; +  		memset(fi->reserved, 0, sizeof(fi->reserved));  		return v4l2_subdev_call(sd, video, s_frame_interval, arg);  	} @@ -728,6 +755,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,  	case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: {  		struct v4l2_subdev_frame_interval_enum *fie = arg; +		if (!client_supports_streams) +			fie->stream = 0; +  		memset(fie->reserved, 0, sizeof(fie->reserved));  		return v4l2_subdev_call(sd, pad, enum_frame_interval, state,  					fie); @@ -736,6 +766,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,  	case VIDIOC_SUBDEV_G_SELECTION: {  		struct v4l2_subdev_selection *sel = arg; +		if (!client_supports_streams) +			sel->stream = 0; +  		memset(sel->reserved, 0, sizeof(sel->reserved));  		return v4l2_subdev_call(  			sd, pad, get_selection, state, sel); @@ -747,6 +780,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,  		if (sel->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)  			return -EPERM; +		if (!client_supports_streams) +			sel->stream = 0; +  		memset(sel->reserved, 0, sizeof(sel->reserved));  		return v4l2_subdev_call(  			sd, pad, set_selection, state, sel); @@ -888,6 +924,33 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,  					routing->which, &krouting);  	} +	case VIDIOC_SUBDEV_G_CLIENT_CAP: { +		struct v4l2_subdev_client_capability *client_cap = arg; + +		client_cap->capabilities = subdev_fh->client_caps; + +		return 0; +	} + +	case VIDIOC_SUBDEV_S_CLIENT_CAP: { +		struct v4l2_subdev_client_capability *client_cap = arg; + +		/* +		 * Clear V4L2_SUBDEV_CLIENT_CAP_STREAMS if streams API is not +		 * enabled. Remove this when streams API is no longer +		 * experimental. +		 */ +		if (!v4l2_subdev_enable_streams_api) +			client_cap->capabilities &= ~V4L2_SUBDEV_CLIENT_CAP_STREAMS; + +		/* Filter out unsupported capabilities */ +		client_cap->capabilities &= V4L2_SUBDEV_CLIENT_CAP_STREAMS; + +		subdev_fh->client_caps = client_cap->capabilities; + +		return 0; +	} +  	default:  		return v4l2_subdev_call(sd, core, ioctl, cmd, arg);  	} @@ -1069,32 +1132,45 @@ EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate_default);  static int  v4l2_subdev_link_validate_get_format(struct media_pad *pad, u32 stream, -				     struct v4l2_subdev_format *fmt) +				     struct v4l2_subdev_format *fmt, +				     bool states_locked)  { -	if (is_media_entity_v4l2_subdev(pad->entity)) { -		struct v4l2_subdev *sd = -			media_entity_to_v4l2_subdev(pad->entity); +	struct v4l2_subdev_state *state; +	struct v4l2_subdev *sd; +	int ret; -		fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; -		fmt->pad = pad->index; -		fmt->stream = stream; +	if (!is_media_entity_v4l2_subdev(pad->entity)) { +		WARN(pad->entity->function != MEDIA_ENT_F_IO_V4L, +		     "Driver bug! Wrong media entity type 0x%08x, entity %s\n", +		     pad->entity->function, pad->entity->name); -		return v4l2_subdev_call(sd, pad, get_fmt, -					v4l2_subdev_get_locked_active_state(sd), -					fmt); +		return -EINVAL;  	} -	WARN(pad->entity->function != MEDIA_ENT_F_IO_V4L, -	     "Driver bug! Wrong media entity type 0x%08x, entity %s\n", -	     pad->entity->function, pad->entity->name); +	sd = media_entity_to_v4l2_subdev(pad->entity); -	return -EINVAL; +	fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; +	fmt->pad = pad->index; +	fmt->stream = stream; + +	if (states_locked) +		state = v4l2_subdev_get_locked_active_state(sd); +	else +		state = v4l2_subdev_lock_and_get_active_state(sd); + +	ret = v4l2_subdev_call(sd, pad, get_fmt, state, fmt); + +	if (!states_locked && state) +		v4l2_subdev_unlock_state(state); + +	return ret;  }  #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)  static void __v4l2_link_validate_get_streams(struct media_pad *pad, -					     u64 *streams_mask) +					     u64 *streams_mask, +					     bool states_locked)  {  	struct v4l2_subdev_route *route;  	struct v4l2_subdev_state *state; @@ -1104,7 +1180,11 @@ static void __v4l2_link_validate_get_streams(struct media_pad *pad,  	*streams_mask = 0; -	state = v4l2_subdev_get_locked_active_state(subdev); +	if (states_locked) +		state = v4l2_subdev_get_locked_active_state(subdev); +	else +		state = v4l2_subdev_lock_and_get_active_state(subdev); +  	if (WARN_ON(!state))  		return; @@ -1125,12 +1205,16 @@ static void __v4l2_link_validate_get_streams(struct media_pad *pad,  		*streams_mask |= BIT_ULL(route_stream);  	} + +	if (!states_locked) +		v4l2_subdev_unlock_state(state);  }  #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */  static void v4l2_link_validate_get_streams(struct media_pad *pad, -					   u64 *streams_mask) +					   u64 *streams_mask, +					   bool states_locked)  {  	struct v4l2_subdev *subdev = media_entity_to_v4l2_subdev(pad->entity); @@ -1141,14 +1225,14 @@ static void v4l2_link_validate_get_streams(struct media_pad *pad,  	}  #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) -	__v4l2_link_validate_get_streams(pad, streams_mask); +	__v4l2_link_validate_get_streams(pad, streams_mask, states_locked);  #else  	/* This shouldn't happen */  	*streams_mask = 0;  #endif  } -static int v4l2_subdev_link_validate_locked(struct media_link *link) +static int v4l2_subdev_link_validate_locked(struct media_link *link, bool states_locked)  {  	struct v4l2_subdev *sink_subdev =  		media_entity_to_v4l2_subdev(link->sink->entity); @@ -1163,8 +1247,8 @@ static int v4l2_subdev_link_validate_locked(struct media_link *link)  		link->source->entity->name, link->source->index,  		link->sink->entity->name, link->sink->index); -	v4l2_link_validate_get_streams(link->source, &source_streams_mask); -	v4l2_link_validate_get_streams(link->sink, &sink_streams_mask); +	v4l2_link_validate_get_streams(link->source, &source_streams_mask, states_locked); +	v4l2_link_validate_get_streams(link->sink, &sink_streams_mask, states_locked);  	/*  	 * It is ok to have more source streams than sink streams as extra @@ -1192,7 +1276,7 @@ static int v4l2_subdev_link_validate_locked(struct media_link *link)  			link->sink->entity->name, link->sink->index, stream);  		ret = v4l2_subdev_link_validate_get_format(link->source, stream, -							   &source_fmt); +							   &source_fmt, states_locked);  		if (ret < 0) {  			dev_dbg(dev,  				"Failed to get format for \"%s\":%u:%u (but that's ok)\n", @@ -1202,7 +1286,7 @@ static int v4l2_subdev_link_validate_locked(struct media_link *link)  		}  		ret = v4l2_subdev_link_validate_get_format(link->sink, stream, -							   &sink_fmt); +							   &sink_fmt, states_locked);  		if (ret < 0) {  			dev_dbg(dev,  				"Failed to get format for \"%s\":%u:%u (but that's ok)\n", @@ -1234,27 +1318,38 @@ int v4l2_subdev_link_validate(struct media_link *link)  {  	struct v4l2_subdev *source_sd, *sink_sd;  	struct v4l2_subdev_state *source_state, *sink_state; +	bool states_locked;  	int ret; +	if (!is_media_entity_v4l2_subdev(link->sink->entity) || +	    !is_media_entity_v4l2_subdev(link->source->entity)) { +		pr_warn_once("%s of link '%s':%u->'%s':%u is not a V4L2 sub-device, driver bug!\n", +			     !is_media_entity_v4l2_subdev(link->sink->entity) ? +			     "sink" : "source", +			     link->source->entity->name, link->source->index, +			     link->sink->entity->name, link->sink->index); +		return 0; +	} +  	sink_sd = media_entity_to_v4l2_subdev(link->sink->entity);  	source_sd = media_entity_to_v4l2_subdev(link->source->entity);  	sink_state = v4l2_subdev_get_unlocked_active_state(sink_sd);  	source_state = v4l2_subdev_get_unlocked_active_state(source_sd); -	if (sink_state) -		v4l2_subdev_lock_state(sink_state); +	states_locked = sink_state && source_state; -	if (source_state) +	if (states_locked) { +		v4l2_subdev_lock_state(sink_state);  		v4l2_subdev_lock_state(source_state); +	} -	ret = v4l2_subdev_link_validate_locked(link); +	ret = v4l2_subdev_link_validate_locked(link, states_locked); -	if (sink_state) +	if (states_locked) {  		v4l2_subdev_unlock_state(sink_state); - -	if (source_state)  		v4l2_subdev_unlock_state(source_state); +	}  	return ret;  } @@ -1676,7 +1771,8 @@ int v4l2_subdev_routing_validate(struct v4l2_subdev *sd,  	unsigned int i, j;  	int ret = -EINVAL; -	if (disallow & V4L2_SUBDEV_ROUTING_NO_STREAM_MIX) { +	if (disallow & (V4L2_SUBDEV_ROUTING_NO_STREAM_MIX | +			V4L2_SUBDEV_ROUTING_NO_MULTIPLEXING)) {  		remote_pads = kcalloc(sd->entity.num_pads, sizeof(*remote_pads),  				      GFP_KERNEL);  		if (!remote_pads) @@ -1705,10 +1801,10 @@ int v4l2_subdev_routing_validate(struct v4l2_subdev *sd,  		}  		/* -		 * V4L2_SUBDEV_ROUTING_NO_STREAM_MIX: Streams on the same pad -		 * may not be routed to streams on different pads. +		 * V4L2_SUBDEV_ROUTING_NO_SINK_STREAM_MIX: all streams from a +		 * sink pad must be routed to a single source pad.  		 */ -		if (disallow & V4L2_SUBDEV_ROUTING_NO_STREAM_MIX) { +		if (disallow & V4L2_SUBDEV_ROUTING_NO_SINK_STREAM_MIX) {  			if (remote_pads[route->sink_pad] != U32_MAX &&  			    remote_pads[route->sink_pad] != route->source_pad) {  				dev_dbg(sd->dev, @@ -1716,7 +1812,13 @@ int v4l2_subdev_routing_validate(struct v4l2_subdev *sd,  					i, "sink");  				goto out;  			} +		} +		/* +		 * V4L2_SUBDEV_ROUTING_NO_SOURCE_STREAM_MIX: all streams on a +		 * source pad must originate from a single sink pad. +		 */ +		if (disallow & V4L2_SUBDEV_ROUTING_NO_SOURCE_STREAM_MIX) {  			if (remote_pads[route->source_pad] != U32_MAX &&  			    remote_pads[route->source_pad] != route->sink_pad) {  				dev_dbg(sd->dev, @@ -1724,7 +1826,37 @@ int v4l2_subdev_routing_validate(struct v4l2_subdev *sd,  					i, "source");  				goto out;  			} +		} + +		/* +		 * V4L2_SUBDEV_ROUTING_NO_SINK_MULTIPLEXING: Pads on the sink +		 * side can not do stream multiplexing, i.e. there can be only +		 * a single stream in a sink pad. +		 */ +		if (disallow & V4L2_SUBDEV_ROUTING_NO_SINK_MULTIPLEXING) { +			if (remote_pads[route->sink_pad] != U32_MAX) { +				dev_dbg(sd->dev, +					"route %u attempts to multiplex on %s pad %u\n", +					i, "sink", route->sink_pad); +				goto out; +			} +		} + +		/* +		 * V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING: Pads on the +		 * source side can not do stream multiplexing, i.e. there can +		 * be only a single stream in a source pad. +		 */ +		if (disallow & V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING) { +			if (remote_pads[route->source_pad] != U32_MAX) { +				dev_dbg(sd->dev, +					"route %u attempts to multiplex on %s pad %u\n", +					i, "source", route->source_pad); +				goto out; +			} +		} +		if (remote_pads) {  			remote_pads[route->sink_pad] = route->source_pad;  			remote_pads[route->source_pad] = route->sink_pad;  		}  |