diff options
Diffstat (limited to 'drivers/media/platform/vimc/vimc-capture.c')
| -rw-r--r-- | drivers/media/platform/vimc/vimc-capture.c | 321 | 
1 files changed, 182 insertions, 139 deletions
| diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index 9adb06d7e13d..14cb32e21130 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -15,15 +15,21 @@   *   */ +#include <linux/component.h> +#include <linux/module.h> +#include <linux/platform_device.h>  #include <media/v4l2-ioctl.h>  #include <media/videobuf2-core.h>  #include <media/videobuf2-vmalloc.h> -#include "vimc-capture.h" +#include "vimc-common.h" + +#define VIMC_CAP_DRV_NAME "vimc-capture"  struct vimc_cap_device {  	struct vimc_ent_device ved;  	struct video_device vdev; +	struct device *dev;  	struct v4l2_pix_format format;  	struct vb2_queue queue;  	struct list_head buf_list; @@ -40,6 +46,14 @@ struct vimc_cap_device {  	struct media_pipeline pipe;  }; +static const struct v4l2_pix_format fmt_default = { +	.width = 640, +	.height = 480, +	.pixelformat = V4L2_PIX_FMT_RGB24, +	.field = V4L2_FIELD_NONE, +	.colorspace = V4L2_COLORSPACE_DEFAULT, +}; +  struct vimc_cap_buffer {  	/*  	 * struct vb2_v4l2_buffer must be the first element @@ -64,7 +78,16 @@ static int vimc_cap_querycap(struct file *file, void *priv,  	return 0;  } -static int vimc_cap_fmt_vid_cap(struct file *file, void *priv, +static void vimc_cap_get_format(struct vimc_ent_device *ved, +				struct v4l2_pix_format *fmt) +{ +	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device, +						    ved); + +	*fmt = vcap->format; +} + +static int vimc_cap_g_fmt_vid_cap(struct file *file, void *priv,  				  struct v4l2_format *f)  {  	struct vimc_cap_device *vcap = video_drvdata(file); @@ -74,16 +97,98 @@ static int vimc_cap_fmt_vid_cap(struct file *file, void *priv,  	return 0;  } +static int vimc_cap_try_fmt_vid_cap(struct file *file, void *priv, +				    struct v4l2_format *f) +{ +	struct v4l2_pix_format *format = &f->fmt.pix; +	const struct vimc_pix_map *vpix; + +	format->width = clamp_t(u32, format->width, VIMC_FRAME_MIN_WIDTH, +				VIMC_FRAME_MAX_WIDTH) & ~1; +	format->height = clamp_t(u32, format->height, VIMC_FRAME_MIN_HEIGHT, +				 VIMC_FRAME_MAX_HEIGHT) & ~1; + +	/* Don't accept a pixelformat that is not on the table */ +	vpix = vimc_pix_map_by_pixelformat(format->pixelformat); +	if (!vpix) { +		format->pixelformat = fmt_default.pixelformat; +		vpix = vimc_pix_map_by_pixelformat(format->pixelformat); +	} +	/* TODO: Add support for custom bytesperline values */ +	format->bytesperline = format->width * vpix->bpp; +	format->sizeimage = format->bytesperline * format->height; + +	if (format->field == V4L2_FIELD_ANY) +		format->field = fmt_default.field; + +	vimc_colorimetry_clamp(format); + +	return 0; +} + +static int vimc_cap_s_fmt_vid_cap(struct file *file, void *priv, +				  struct v4l2_format *f) +{ +	struct vimc_cap_device *vcap = video_drvdata(file); + +	/* Do not change the format while stream is on */ +	if (vb2_is_busy(&vcap->queue)) +		return -EBUSY; + +	vimc_cap_try_fmt_vid_cap(file, priv, f); + +	dev_dbg(vcap->dev, "%s: format update: " +		"old:%dx%d (0x%x, %d, %d, %d, %d) " +		"new:%dx%d (0x%x, %d, %d, %d, %d)\n", vcap->vdev.name, +		/* old */ +		vcap->format.width, vcap->format.height, +		vcap->format.pixelformat, vcap->format.colorspace, +		vcap->format.quantization, vcap->format.xfer_func, +		vcap->format.ycbcr_enc, +		/* new */ +		f->fmt.pix.width, f->fmt.pix.height, +		f->fmt.pix.pixelformat,	f->fmt.pix.colorspace, +		f->fmt.pix.quantization, f->fmt.pix.xfer_func, +		f->fmt.pix.ycbcr_enc); + +	vcap->format = f->fmt.pix; + +	return 0; +} +  static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv,  				     struct v4l2_fmtdesc *f)  { -	struct vimc_cap_device *vcap = video_drvdata(file); +	const struct vimc_pix_map *vpix = vimc_pix_map_by_index(f->index); + +	if (!vpix) +		return -EINVAL; + +	f->pixelformat = vpix->pixelformat; + +	return 0; +} + +static int vimc_cap_enum_framesizes(struct file *file, void *fh, +				    struct v4l2_frmsizeenum *fsize) +{ +	const struct vimc_pix_map *vpix; + +	if (fsize->index) +		return -EINVAL; -	if (f->index > 0) +	/* Only accept code in the pix map table */ +	vpix = vimc_pix_map_by_code(fsize->pixel_format); +	if (!vpix)  		return -EINVAL; -	/* We only support one format for now */ -	f->pixelformat = vcap->format.pixelformat; +	fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; +	fsize->stepwise.min_width = VIMC_FRAME_MIN_WIDTH; +	fsize->stepwise.max_width = VIMC_FRAME_MAX_WIDTH; +	fsize->stepwise.min_height = VIMC_FRAME_MIN_HEIGHT; +	fsize->stepwise.max_height = VIMC_FRAME_MAX_HEIGHT; +	fsize->stepwise.step_width = 2; +	fsize->stepwise.step_height = 2;  	return 0;  } @@ -101,10 +206,11 @@ static const struct v4l2_file_operations vimc_cap_fops = {  static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {  	.vidioc_querycap = vimc_cap_querycap, -	.vidioc_g_fmt_vid_cap = vimc_cap_fmt_vid_cap, -	.vidioc_s_fmt_vid_cap = vimc_cap_fmt_vid_cap, -	.vidioc_try_fmt_vid_cap = vimc_cap_fmt_vid_cap, +	.vidioc_g_fmt_vid_cap = vimc_cap_g_fmt_vid_cap, +	.vidioc_s_fmt_vid_cap = vimc_cap_s_fmt_vid_cap, +	.vidioc_try_fmt_vid_cap = vimc_cap_try_fmt_vid_cap,  	.vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap, +	.vidioc_enum_framesizes = vimc_cap_enum_framesizes,  	.vidioc_reqbufs = vb2_ioctl_reqbufs,  	.vidioc_create_bufs = vb2_ioctl_create_bufs, @@ -132,31 +238,6 @@ static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,  	spin_unlock(&vcap->qlock);  } -static int vimc_cap_pipeline_s_stream(struct vimc_cap_device *vcap, int enable) -{ -	struct v4l2_subdev *sd; -	struct media_pad *pad; -	int ret; - -	/* Start the stream in the subdevice direct connected */ -	pad = media_entity_remote_pad(&vcap->vdev.entity.pads[0]); - -	/* -	 * if it is a raw node from vimc-core, there is nothing to activate -	 * TODO: remove this when there are no more raw nodes in the -	 * core and return error instead -	 */ -	if (pad->entity->obj_type == MEDIA_ENTITY_TYPE_BASE) -		return 0; - -	sd = media_entity_to_v4l2_subdev(pad->entity); -	ret = v4l2_subdev_call(sd, video, s_stream, enable); -	if (ret && ret != -ENOIOCTLCMD) -		return ret; - -	return 0; -} -  static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)  {  	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq); @@ -173,7 +254,7 @@ static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)  	}  	/* Enable streaming from the pipe */ -	ret = vimc_cap_pipeline_s_stream(vcap, 1); +	ret = vimc_pipeline_s_stream(&vcap->vdev.entity, 1);  	if (ret) {  		media_pipeline_stop(entity);  		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED); @@ -192,7 +273,7 @@ static void vimc_cap_stop_streaming(struct vb2_queue *vq)  	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);  	/* Disable streaming from the pipe */ -	vimc_cap_pipeline_s_stream(vcap, 0); +	vimc_pipeline_s_stream(&vcap->vdev.entity, 0);  	/* Stop the media pipeline */  	media_pipeline_stop(&vcap->vdev.entity); @@ -234,8 +315,7 @@ static int vimc_cap_buffer_prepare(struct vb2_buffer *vb)  	unsigned long size = vcap->format.sizeimage;  	if (vb2_plane_size(vb, 0) < size) { -		dev_err(vcap->vdev.v4l2_dev->dev, -			"%s: buffer too small (%lu < %lu)\n", +		dev_err(vcap->dev, "%s: buffer too small (%lu < %lu)\n",  			vcap->vdev.name, vb2_plane_size(vb, 0), size);  		return -EINVAL;  	} @@ -256,78 +336,14 @@ static const struct vb2_ops vimc_cap_qops = {  	.wait_finish		= vb2_ops_wait_finish,  }; -/* - * NOTE: this function is a copy of v4l2_subdev_link_validate_get_format - * maybe the v4l2 function should be public - */ -static int vimc_cap_v4l2_subdev_link_validate_get_format(struct media_pad *pad, -						struct v4l2_subdev_format *fmt) -{ -	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity); - -	fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; -	fmt->pad = pad->index; - -	return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt); -} - -static int vimc_cap_link_validate(struct media_link *link) -{ -	struct v4l2_subdev_format source_fmt; -	const struct vimc_pix_map *vpix; -	struct vimc_cap_device *vcap = container_of(link->sink->entity, -						    struct vimc_cap_device, -						    vdev.entity); -	struct v4l2_pix_format *sink_fmt = &vcap->format; -	int ret; - -	/* -	 * if it is a raw node from vimc-core, ignore the link for now -	 * TODO: remove this when there are no more raw nodes in the -	 * core and return error instead -	 */ -	if (link->source->entity->obj_type == MEDIA_ENTITY_TYPE_BASE) -		return 0; - -	/* Get the the format of the subdev */ -	ret = vimc_cap_v4l2_subdev_link_validate_get_format(link->source, -							    &source_fmt); -	if (ret) -		return ret; - -	dev_dbg(vcap->vdev.v4l2_dev->dev, -		"%s: link validate formats src:%dx%d %d sink:%dx%d %d\n", -		vcap->vdev.name, -		source_fmt.format.width, source_fmt.format.height, -		source_fmt.format.code, -		sink_fmt->width, sink_fmt->height, -		sink_fmt->pixelformat); - -	/* The width, height and code must match. */ -	vpix = vimc_pix_map_by_pixelformat(sink_fmt->pixelformat); -	if (source_fmt.format.width != sink_fmt->width -	    || source_fmt.format.height != sink_fmt->height -	    || vpix->code != source_fmt.format.code) -		return -EPIPE; - -	/* -	 * The field order must match, or the sink field order must be NONE -	 * to support interlaced hardware connected to bridges that support -	 * progressive formats only. -	 */ -	if (source_fmt.format.field != sink_fmt->field && -	    sink_fmt->field != V4L2_FIELD_NONE) -		return -EPIPE; - -	return 0; -} -  static const struct media_entity_operations vimc_cap_mops = { -	.link_validate		= vimc_cap_link_validate, +	.link_validate		= vimc_link_validate,  }; -static void vimc_cap_destroy(struct vimc_ent_device *ved) +static void vimc_cap_comp_unbind(struct device *comp, struct device *master, +				 void *master_data)  { +	struct vimc_ent_device *ved = dev_get_drvdata(comp);  	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,  						    ved); @@ -376,42 +392,35 @@ static void vimc_cap_process_frame(struct vimc_ent_device *ved,  	vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);  } -struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev, -					const char *const name, -					u16 num_pads, -					const unsigned long *pads_flag) +static int vimc_cap_comp_bind(struct device *comp, struct device *master, +			      void *master_data)  { +	struct v4l2_device *v4l2_dev = master_data; +	struct vimc_platform_data *pdata = comp->platform_data;  	const struct vimc_pix_map *vpix;  	struct vimc_cap_device *vcap;  	struct video_device *vdev;  	struct vb2_queue *q;  	int ret; -	/* -	 * Check entity configuration params -	 * NOTE: we only support a single sink pad -	 */ -	if (!name || num_pads != 1 || !pads_flag || -	    !(pads_flag[0] & MEDIA_PAD_FL_SINK)) -		return ERR_PTR(-EINVAL); -  	/* Allocate the vimc_cap_device struct */  	vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);  	if (!vcap) -		return ERR_PTR(-ENOMEM); +		return -ENOMEM;  	/* Allocate the pads */ -	vcap->ved.pads = vimc_pads_init(num_pads, pads_flag); +	vcap->ved.pads = +		vimc_pads_init(1, (const unsigned long[1]) {MEDIA_PAD_FL_SINK});  	if (IS_ERR(vcap->ved.pads)) {  		ret = PTR_ERR(vcap->ved.pads);  		goto err_free_vcap;  	}  	/* Initialize the media entity */ -	vcap->vdev.entity.name = name; +	vcap->vdev.entity.name = pdata->entity_name;  	vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;  	ret = media_entity_pads_init(&vcap->vdev.entity, -				     num_pads, vcap->ved.pads); +				     1, vcap->ved.pads);  	if (ret)  		goto err_clean_pads; @@ -432,9 +441,8 @@ struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,  	ret = vb2_queue_init(q);  	if (ret) { -		dev_err(vcap->vdev.v4l2_dev->dev, -			"%s: vb2 queue init failed (err=%d)\n", -			vcap->vdev.name, ret); +		dev_err(comp, "%s: vb2 queue init failed (err=%d)\n", +			pdata->entity_name, ret);  		goto err_clean_m_ent;  	} @@ -442,23 +450,19 @@ struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,  	INIT_LIST_HEAD(&vcap->buf_list);  	spin_lock_init(&vcap->qlock); -	/* Set the frame format (this is hardcoded for now) */ -	vcap->format.width = 640; -	vcap->format.height = 480; -	vcap->format.pixelformat = V4L2_PIX_FMT_RGB24; -	vcap->format.field = V4L2_FIELD_NONE; -	vcap->format.colorspace = V4L2_COLORSPACE_SRGB; - +	/* Set default frame format */ +	vcap->format = fmt_default;  	vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat); -  	vcap->format.bytesperline = vcap->format.width * vpix->bpp;  	vcap->format.sizeimage = vcap->format.bytesperline *  				 vcap->format.height;  	/* Fill the vimc_ent_device struct */ -	vcap->ved.destroy = vimc_cap_destroy;  	vcap->ved.ent = &vcap->vdev.entity;  	vcap->ved.process_frame = vimc_cap_process_frame; +	vcap->ved.vdev_get_format = vimc_cap_get_format; +	dev_set_drvdata(comp, &vcap->ved); +	vcap->dev = comp;  	/* Initialize the video_device struct */  	vdev = &vcap->vdev; @@ -471,19 +475,18 @@ struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,  	vdev->queue = q;  	vdev->v4l2_dev = v4l2_dev;  	vdev->vfl_dir = VFL_DIR_RX; -	strlcpy(vdev->name, name, sizeof(vdev->name)); +	strlcpy(vdev->name, pdata->entity_name, sizeof(vdev->name));  	video_set_drvdata(vdev, &vcap->ved);  	/* Register the video_device with the v4l2 and the media framework */  	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);  	if (ret) { -		dev_err(vcap->vdev.v4l2_dev->dev, -			"%s: video register failed (err=%d)\n", +		dev_err(comp, "%s: video register failed (err=%d)\n",  			vcap->vdev.name, ret);  		goto err_release_queue;  	} -	return &vcap->ved; +	return 0;  err_release_queue:  	vb2_queue_release(q); @@ -494,5 +497,45 @@ err_clean_pads:  err_free_vcap:  	kfree(vcap); -	return ERR_PTR(ret); +	return ret; +} + +static const struct component_ops vimc_cap_comp_ops = { +	.bind = vimc_cap_comp_bind, +	.unbind = vimc_cap_comp_unbind, +}; + +static int vimc_cap_probe(struct platform_device *pdev) +{ +	return component_add(&pdev->dev, &vimc_cap_comp_ops);  } + +static int vimc_cap_remove(struct platform_device *pdev) +{ +	component_del(&pdev->dev, &vimc_cap_comp_ops); + +	return 0; +} + +static struct platform_driver vimc_cap_pdrv = { +	.probe		= vimc_cap_probe, +	.remove		= vimc_cap_remove, +	.driver		= { +		.name	= VIMC_CAP_DRV_NAME, +	}, +}; + +static const struct platform_device_id vimc_cap_driver_ids[] = { +	{ +		.name           = VIMC_CAP_DRV_NAME, +	}, +	{ } +}; + +module_platform_driver(vimc_cap_pdrv); + +MODULE_DEVICE_TABLE(platform, vimc_cap_driver_ids); + +MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Capture"); +MODULE_AUTHOR("Helen Mae Koike Fornazier <[email protected]>"); +MODULE_LICENSE("GPL"); |