Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2c11d1bd authored by Philipp Zabel's avatar Philipp Zabel Committed by Mauro Carvalho Chehab
Browse files

[media] coda: add coda_video_device descriptors



Each video device descriptor determines the name, callback ops, and input and
output formats on the corresponding video device. This simplifies coda_enum_fmt
and coda_try_fmt a bit and will simplify adding separate video devices for JPEG
codecs due to the slightly different behavior in the CodaDx6/CODA7542 case and
a separate hardware unit on CODA960.

Signed-off-by: default avatarPhilipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: default avatarKamil Debski <k.debski@samsung.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@osg.samsung.com>
parent b2f91ae3
Loading
Loading
Loading
Loading
+198 −152
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@
#define CODA_NAME		"coda"

#define CODADX6_MAX_INSTANCES	4
#define CODA_MAX_FORMATS	4

#define CODA_PARA_BUF_SIZE	(10 * 1024)
#define CODA_ISRAM_SIZE	(2048 * 2)
@@ -169,6 +170,58 @@ static const struct coda_codec coda9_codecs[] = {
	CODA_CODEC(CODA9_MODE_DECODE_MP4,  V4L2_PIX_FMT_MPEG4,  V4L2_PIX_FMT_YUV420, 1920, 1088),
};

struct coda_video_device {
	const char *name;
	enum coda_inst_type type;
	const struct coda_context_ops *ops;
	u32 src_formats[CODA_MAX_FORMATS];
	u32 dst_formats[CODA_MAX_FORMATS];
};

static const struct coda_video_device coda_bit_encoder = {
	.name = "coda-encoder",
	.type = CODA_INST_ENCODER,
	.ops = &coda_bit_encode_ops,
	.src_formats = {
		V4L2_PIX_FMT_YUV420,
		V4L2_PIX_FMT_YVU420,
		V4L2_PIX_FMT_NV12,
	},
	.dst_formats = {
		V4L2_PIX_FMT_H264,
		V4L2_PIX_FMT_MPEG4,
	},
};

static const struct coda_video_device coda_bit_decoder = {
	.name = "coda-decoder",
	.type = CODA_INST_DECODER,
	.ops = &coda_bit_decode_ops,
	.src_formats = {
		V4L2_PIX_FMT_H264,
		V4L2_PIX_FMT_MPEG4,
	},
	.dst_formats = {
		V4L2_PIX_FMT_YUV420,
		V4L2_PIX_FMT_YVU420,
		V4L2_PIX_FMT_NV12,
	},
};

static const struct coda_video_device *codadx6_video_devices[] = {
	&coda_bit_encoder,
};

static const struct coda_video_device *coda7_video_devices[] = {
	&coda_bit_encoder,
	&coda_bit_decoder,
};

static const struct coda_video_device *coda9_video_devices[] = {
	&coda_bit_encoder,
	&coda_bit_decoder,
};

static bool coda_format_is_yuv(u32 fourcc)
{
	switch (fourcc) {
@@ -182,6 +235,18 @@ static bool coda_format_is_yuv(u32 fourcc)
	}
}

static const char *coda_format_name(u32 fourcc)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(coda_formats); i++) {
		if (coda_formats[i].fourcc == fourcc)
			return coda_formats[i].name;
	}

	return NULL;
}

/*
 * Normalize all supported YUV 4:2:0 formats to the value used in the codec
 * tables.
@@ -240,6 +305,17 @@ static void coda_get_max_dimensions(struct coda_dev *dev,
		*max_h = h;
}

const struct coda_video_device *to_coda_video_device(struct video_device *vdev)
{
	struct coda_dev *dev = video_get_drvdata(vdev);
	unsigned int i = vdev - dev->vfd;

	if (i >= dev->devtype->num_vdevs)
		return NULL;

	return dev->devtype->vdevs[i];
}

const char *coda_product_name(int product)
{
	static char buf[9];
@@ -278,58 +354,28 @@ static int coda_querycap(struct file *file, void *priv,
static int coda_enum_fmt(struct file *file, void *priv,
			 struct v4l2_fmtdesc *f)
{
	struct coda_ctx *ctx = fh_to_ctx(priv);
	const struct coda_codec *codecs = ctx->dev->devtype->codecs;
	const struct coda_fmt *formats = coda_formats;
	const struct coda_fmt *fmt;
	int num_codecs = ctx->dev->devtype->num_codecs;
	int num_formats = ARRAY_SIZE(coda_formats);
	int i, k, num = 0;
	bool yuv;

	if (ctx->inst_type == CODA_INST_ENCODER)
		yuv = (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT);
	struct video_device *vdev = video_devdata(file);
	const struct coda_video_device *cvd = to_coda_video_device(vdev);
	const u32 *formats;
	const char *name;

	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
		formats = cvd->src_formats;
	else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
		formats = cvd->dst_formats;
	else
		yuv = (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE);

	for (i = 0; i < num_formats; i++) {
		/* Skip either raw or compressed formats */
		if (yuv != coda_format_is_yuv(formats[i].fourcc))
			continue;
		/* All uncompressed formats are always supported */
		if (yuv) {
			if (num == f->index)
				break;
			++num;
			continue;
		}
		/* Compressed formats may be supported, check the codec list */
		for (k = 0; k < num_codecs; k++) {
			if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
			    formats[i].fourcc == codecs[k].dst_fourcc)
				break;
			if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
			    formats[i].fourcc == codecs[k].src_fourcc)
				break;
		}
		if (k < num_codecs) {
			if (num == f->index)
				break;
			++num;
		}
	}
		return -EINVAL;

	if (f->index >= CODA_MAX_FORMATS || formats[f->index] == 0)
		return -EINVAL;

	if (i < num_formats) {
		fmt = &formats[i];
		strlcpy(f->description, fmt->name, sizeof(f->description));
		f->pixelformat = fmt->fourcc;
		if (!yuv)
	name = coda_format_name(formats[f->index]);
	strlcpy(f->description, name, sizeof(f->description));
	f->pixelformat = formats[f->index];
	if (!coda_format_is_yuv(formats[f->index]))
		f->flags |= V4L2_FMT_FLAG_COMPRESSED;
		return 0;
	}

	/* Format not found */
	return -EINVAL;
	return 0;
}

static int coda_g_fmt(struct file *file, void *priv,
@@ -354,11 +400,37 @@ static int coda_g_fmt(struct file *file, void *priv,
	return 0;
}

static int coda_try_pixelformat(struct coda_ctx *ctx, struct v4l2_format *f)
{
	struct coda_q_data *q_data;
	const u32 *formats;
	int i;

	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
		formats = ctx->cvd->src_formats;
	else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
		formats = ctx->cvd->dst_formats;
	else
		return -EINVAL;

	for (i = 0; i < CODA_MAX_FORMATS; i++) {
		if (formats[i] == f->fmt.pix.pixelformat) {
			f->fmt.pix.pixelformat = formats[i];
			return 0;
		}
	}

	/* Fall back to currently set pixelformat */
	q_data = get_q_data(ctx, f->type);
	f->fmt.pix.pixelformat = q_data->fourcc;

	return 0;
}

static int coda_try_fmt(struct coda_ctx *ctx, const struct coda_codec *codec,
			struct v4l2_format *f)
{
	struct coda_dev *dev = ctx->dev;
	struct coda_q_data *q_data;
	unsigned int max_w, max_h;
	enum v4l2_field field;

@@ -377,21 +449,6 @@ static int coda_try_fmt(struct coda_ctx *ctx, const struct coda_codec *codec,
			      &f->fmt.pix.height, MIN_H, max_h, H_ALIGN,
			      S_ALIGN);

	switch (f->fmt.pix.pixelformat) {
	case V4L2_PIX_FMT_YUV420:
	case V4L2_PIX_FMT_YVU420:
	case V4L2_PIX_FMT_NV12:
	case V4L2_PIX_FMT_H264:
	case V4L2_PIX_FMT_MPEG4:
	case V4L2_PIX_FMT_JPEG:
		break;
	default:
		q_data = get_q_data(ctx, f->type);
		if (!q_data)
			return -EINVAL;
		f->fmt.pix.pixelformat = q_data->fourcc;
	}

	switch (f->fmt.pix.pixelformat) {
	case V4L2_PIX_FMT_YUV420:
	case V4L2_PIX_FMT_YVU420:
@@ -423,17 +480,28 @@ static int coda_try_fmt_vid_cap(struct file *file, void *priv,
				struct v4l2_format *f)
{
	struct coda_ctx *ctx = fh_to_ctx(priv);
	const struct coda_codec *codec = NULL;
	const struct coda_q_data *q_data_src;
	const struct coda_codec *codec;
	struct vb2_queue *src_vq;
	int ret;

	ret = coda_try_pixelformat(ctx, f);
	if (ret < 0)
		return ret;

	q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);

	/*
	 * If the source format is already fixed, try to find a codec that
	 * converts to the given destination format
	 * If the source format is already fixed, only allow the same output
	 * resolution
	 */
	src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
	if (vb2_is_streaming(src_vq)) {
		struct coda_q_data *q_data_src;
		f->fmt.pix.width = q_data_src->width;
		f->fmt.pix.height = q_data_src->height;
	}

	f->fmt.pix.colorspace = ctx->colorspace;

	q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
	codec = coda_find_codec(ctx->dev, q_data_src->fourcc,
@@ -441,16 +509,6 @@ static int coda_try_fmt_vid_cap(struct file *file, void *priv,
	if (!codec)
		return -EINVAL;

		f->fmt.pix.width = q_data_src->width;
		f->fmt.pix.height = q_data_src->height;
	} else {
		/* Otherwise determine codec by encoded format, if possible */
		codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420,
					f->fmt.pix.pixelformat);
	}

	f->fmt.pix.colorspace = ctx->colorspace;

	ret = coda_try_fmt(ctx, codec, f);
	if (ret < 0)
		return ret;
@@ -471,22 +529,21 @@ static int coda_try_fmt_vid_out(struct file *file, void *priv,
				struct v4l2_format *f)
{
	struct coda_ctx *ctx = fh_to_ctx(priv);
	const struct coda_codec *codec = NULL;
	struct coda_dev *dev = ctx->dev;
	const struct coda_q_data *q_data_dst;
	const struct coda_codec *codec;
	int ret;

	/* Determine codec by encoded format, returns NULL if raw or invalid */
	if (ctx->inst_type == CODA_INST_DECODER) {
		codec = coda_find_codec(ctx->dev, f->fmt.pix.pixelformat,
					V4L2_PIX_FMT_YUV420);
		if (!codec)
			codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_H264,
						V4L2_PIX_FMT_YUV420);
		if (!codec)
			return -EINVAL;
	}
	ret = coda_try_pixelformat(ctx, f);
	if (ret < 0)
		return ret;

	if (!f->fmt.pix.colorspace)
		f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;

	q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
	codec = coda_find_codec(dev, f->fmt.pix.pixelformat, q_data_dst->fourcc);

	return coda_try_fmt(ctx, codec, f);
}

@@ -907,18 +964,10 @@ static void coda_set_tiled_map_type(struct coda_ctx *ctx, int tiled_map_type)

static void set_default_params(struct coda_ctx *ctx)
{
	u32 src_fourcc, dst_fourcc;
	int max_w;
	int max_h;
	int max_w, max_h;

	if (ctx->inst_type == CODA_INST_ENCODER) {
		src_fourcc = V4L2_PIX_FMT_YUV420;
		dst_fourcc = V4L2_PIX_FMT_H264;
	} else {
		src_fourcc = V4L2_PIX_FMT_H264;
		dst_fourcc = V4L2_PIX_FMT_YUV420;
	}
	ctx->codec = coda_find_codec(ctx->dev, src_fourcc, dst_fourcc);
	ctx->codec = coda_find_codec(ctx->dev, ctx->cvd->src_formats[0],
				     ctx->cvd->dst_formats[0]);
	max_w = ctx->codec->max_w;
	max_h = ctx->codec->max_h;

@@ -1409,10 +1458,14 @@ static int coda_next_free_instance(struct coda_dev *dev)
	return idx;
}

static int coda_open(struct file *file, enum coda_inst_type inst_type,
		     const struct coda_context_ops *ctx_ops)
/*
 * File operations
 */

static int coda_open(struct file *file)
{
	struct coda_dev *dev = video_drvdata(file);
	struct video_device *vdev = video_devdata(file);
	struct coda_dev *dev = video_get_drvdata(vdev);
	struct coda_ctx *ctx = NULL;
	char *name;
	int ret;
@@ -1433,8 +1486,9 @@ static int coda_open(struct file *file, enum coda_inst_type inst_type,
	ctx->debugfs_entry = debugfs_create_dir(name, dev->debugfs_root);
	kfree(name);

	ctx->inst_type = inst_type;
	ctx->ops = ctx_ops;
	ctx->cvd = to_coda_video_device(vdev);
	ctx->inst_type = ctx->cvd->type;
	ctx->ops = ctx->cvd->ops;
	init_completion(&ctx->completion);
	INIT_WORK(&ctx->pic_run_work, coda_pic_run_work);
	INIT_WORK(&ctx->seq_end_work, ctx->ops->seq_end_work);
@@ -1542,16 +1596,6 @@ static int coda_open(struct file *file, enum coda_inst_type inst_type,
	return ret;
}

static int coda_encoder_open(struct file *file)
{
	return coda_open(file, CODA_INST_ENCODER, &coda_bit_encode_ops);
}

static int coda_decoder_open(struct file *file)
{
	return coda_open(file, CODA_INST_DECODER, &coda_bit_decode_ops);
}

static int coda_release(struct file *file)
{
	struct coda_dev *dev = video_drvdata(file);
@@ -1595,18 +1639,9 @@ static int coda_release(struct file *file)
	return 0;
}

static const struct v4l2_file_operations coda_encoder_fops = {
static const struct v4l2_file_operations coda_fops = {
	.owner		= THIS_MODULE,
	.open		= coda_encoder_open,
	.release	= coda_release,
	.poll		= v4l2_m2m_fop_poll,
	.unlocked_ioctl	= video_ioctl2,
	.mmap		= v4l2_m2m_fop_mmap,
};

static const struct v4l2_file_operations coda_decoder_fops = {
	.owner		= THIS_MODULE,
	.open		= coda_decoder_open,
	.open		= coda_open,
	.release	= coda_release,
	.poll		= v4l2_m2m_fop_poll,
	.unlocked_ioctl	= video_ioctl2,
@@ -1711,8 +1746,16 @@ static int coda_hw_init(struct coda_dev *dev)
	return ret;
}

static int coda_register_device(struct coda_dev *dev, struct video_device *vfd)
static int coda_register_device(struct coda_dev *dev, int i)
{
	struct video_device *vfd = &dev->vfd[i];

	if (i > ARRAY_SIZE(dev->vfd))
		return -EINVAL;

	snprintf(vfd->name, sizeof(vfd->name), dev->devtype->vdevs[i]->name);
	vfd->fops	= &coda_fops;
	vfd->ioctl_ops	= &coda_ioctl_ops;
	vfd->release	= video_device_release_empty,
	vfd->lock	= &dev->dev_mutex;
	vfd->v4l2_dev	= &dev->v4l2_dev;
@@ -1731,7 +1774,7 @@ static void coda_fw_callback(const struct firmware *fw, void *context)
{
	struct coda_dev *dev = context;
	struct platform_device *pdev = dev->plat_dev;
	int ret;
	int i, ret;

	if (!fw) {
		v4l2_err(&dev->v4l2_dev, "firmware request failed\n");
@@ -1772,33 +1815,25 @@ static void coda_fw_callback(const struct firmware *fw, void *context)
		goto rel_ctx;
	}

	dev->vfd[0].fops      = &coda_encoder_fops,
	dev->vfd[0].ioctl_ops = &coda_ioctl_ops;
	snprintf(dev->vfd[0].name, sizeof(dev->vfd[0].name), "coda-encoder");
	ret = coda_register_device(dev, &dev->vfd[0]);
	for (i = 0; i < dev->devtype->num_vdevs; i++) {
		ret = coda_register_device(dev, i);
		if (ret) {
			v4l2_err(&dev->v4l2_dev,
			 "Failed to register encoder video device\n");
		goto rel_m2m;
				 "Failed to register %s video device: %d\n",
				 dev->devtype->vdevs[i]->name, ret);
			goto rel_vfd;
		}

	dev->vfd[1].fops      = &coda_decoder_fops,
	dev->vfd[1].ioctl_ops = &coda_ioctl_ops;
	snprintf(dev->vfd[1].name, sizeof(dev->vfd[1].name), "coda-decoder");
	ret = coda_register_device(dev, &dev->vfd[1]);
	if (ret) {
		v4l2_err(&dev->v4l2_dev,
			 "Failed to register decoder video device\n");
		goto rel_m2m;
	}

	v4l2_info(&dev->v4l2_dev, "codec registered as /dev/video[%d-%d]\n",
		  dev->vfd[0].num, dev->vfd[1].num);
		  dev->vfd[0].num, dev->vfd[i - 1].num);

	pm_runtime_put_sync(&pdev->dev);
	return;

rel_m2m:
rel_vfd:
	while (--i >= 0)
		video_unregister_device(&dev->vfd[i]);
	v4l2_m2m_release(dev->m2m_dev);
rel_ctx:
	vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
@@ -1830,6 +1865,8 @@ static const struct coda_devtype coda_devdata[] = {
		.product      = CODA_DX6,
		.codecs       = codadx6_codecs,
		.num_codecs   = ARRAY_SIZE(codadx6_codecs),
		.vdevs        = codadx6_video_devices,
		.num_vdevs    = ARRAY_SIZE(codadx6_video_devices),
		.workbuf_size = 288 * 1024 + FMO_SLICE_SAVE_BUF_SIZE * 8 * 1024,
		.iram_size    = 0xb000,
	},
@@ -1838,6 +1875,8 @@ static const struct coda_devtype coda_devdata[] = {
		.product      = CODA_7541,
		.codecs       = coda7_codecs,
		.num_codecs   = ARRAY_SIZE(coda7_codecs),
		.vdevs        = coda7_video_devices,
		.num_vdevs    = ARRAY_SIZE(coda7_video_devices),
		.workbuf_size = 128 * 1024,
		.tempbuf_size = 304 * 1024,
		.iram_size    = 0x14000,
@@ -1847,6 +1886,8 @@ static const struct coda_devtype coda_devdata[] = {
		.product      = CODA_960,
		.codecs       = coda9_codecs,
		.num_codecs   = ARRAY_SIZE(coda9_codecs),
		.vdevs        = coda9_video_devices,
		.num_vdevs    = ARRAY_SIZE(coda9_video_devices),
		.workbuf_size = 80 * 1024,
		.tempbuf_size = 204 * 1024,
		.iram_size    = 0x21000,
@@ -1856,6 +1897,8 @@ static const struct coda_devtype coda_devdata[] = {
		.product      = CODA_960,
		.codecs       = coda9_codecs,
		.num_codecs   = ARRAY_SIZE(coda9_codecs),
		.vdevs        = coda9_video_devices,
		.num_vdevs    = ARRAY_SIZE(coda9_video_devices),
		.workbuf_size = 80 * 1024,
		.tempbuf_size = 204 * 1024,
		.iram_size    = 0x20000,
@@ -2035,9 +2078,12 @@ static int coda_probe(struct platform_device *pdev)
static int coda_remove(struct platform_device *pdev)
{
	struct coda_dev *dev = platform_get_drvdata(pdev);
	int i;

	video_unregister_device(&dev->vfd[0]);
	video_unregister_device(&dev->vfd[1]);
	for (i = 0; i < ARRAY_SIZE(dev->vfd); i++) {
		if (video_get_drvdata(&dev->vfd[i]))
			video_unregister_device(&dev->vfd[i]);
	}
	if (dev->m2m_dev)
		v4l2_m2m_release(dev->m2m_dev);
	pm_runtime_disable(&pdev->dev);
+6 −1
Original line number Diff line number Diff line
@@ -45,11 +45,15 @@ enum coda_product {
	CODA_960 = 0xf020,
};

struct coda_video_device;

struct coda_devtype {
	char			*firmware;
	enum coda_product	product;
	const struct coda_codec	*codecs;
	unsigned int		num_codecs;
	const struct coda_video_device **vdevs;
	unsigned int		num_vdevs;
	size_t			workbuf_size;
	size_t			tempbuf_size;
	size_t			iram_size;
@@ -65,7 +69,7 @@ struct coda_aux_buf {

struct coda_dev {
	struct v4l2_device	v4l2_dev;
	struct video_device	vfd[2];
	struct video_device	vfd[3];
	struct platform_device	*plat_dev;
	const struct coda_devtype *devtype;

@@ -183,6 +187,7 @@ struct coda_ctx {
	struct work_struct		pic_run_work;
	struct work_struct		seq_end_work;
	struct completion		completion;
	const struct coda_video_device	*cvd;
	const struct coda_context_ops	*ops;
	int				aborting;
	int				initialized;