Re: [PATCH 2/8] media: vidc: adding core part and helper functions

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Stanimir,

Thanks for this patch series!

I have some review comments:

On 08/22/2016 03:13 PM, Stanimir Varbanov wrote:

<snip>

> diff --git a/drivers/media/platform/qcom/vidc/core.h b/drivers/media/platform/qcom/vidc/core.h
> new file mode 100644
> index 000000000000..5dc8e05f8c36
> --- /dev/null
> +++ b/drivers/media/platform/qcom/vidc/core.h
> @@ -0,0 +1,196 @@
> +/*
> + * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
> + * Copyright (C) 2016 Linaro Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef __VIDC_CORE_H_
> +#define __VIDC_CORE_H_
> +
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/videobuf2-core.h>
> +
> +#include "resources.h"
> +#include "hfi.h"
> +
> +#define VIDC_DRV_NAME		"vidc"
> +
> +struct vidc_list {
> +	struct list_head list;
> +	struct mutex lock;
> +};
> +
> +struct vidc_format {
> +	u32 pixfmt;
> +	int num_planes;
> +	u32 type;
> +};
> +
> +struct vidc_core {
> +	struct list_head list;
> +	void __iomem *base;
> +	int irq;
> +	struct clk *clks[VIDC_CLKS_NUM_MAX];
> +	struct mutex lock;
> +	struct hfi_core hfi;
> +	struct video_device vdev_dec;
> +	struct video_device vdev_enc;

I know that many drivers embed struct video_device, but this can cause subtle
refcounting problems. I recommend changing this to a pointer and using video_device_alloc().

I have plans to reorganize the way video_devices are allocated and registered in
the near future, and you might just as well prepare this driver for that by switching
to a pointer.

> +	struct v4l2_device v4l2_dev;
> +	struct list_head instances;
> +	const struct vidc_resources *res;
> +	struct rproc *rproc;
> +	bool rproc_booted;
> +	struct device *dev;
> +};
> +
> +struct vdec_controls {
> +	u32 post_loop_deb_mode;
> +	u32 profile;
> +	u32 level;
> +};
> +
> +struct venc_controls {
> +	u16 gop_size;
> +	u32 idr_period;
> +	u32 num_p_frames;
> +	u32 num_b_frames;
> +	u32 bitrate_mode;
> +	u32 bitrate;
> +	u32 bitrate_peak;
> +
> +	u32 h264_i_period;
> +	u32 h264_entropy_mode;
> +	u32 h264_i_qp;
> +	u32 h264_p_qp;
> +	u32 h264_b_qp;
> +	u32 h264_min_qp;
> +	u32 h264_max_qp;
> +	u32 h264_loop_filter_mode;
> +	u32 h264_loop_filter_alpha;
> +	u32 h264_loop_filter_beta;
> +
> +	u32 vp8_min_qp;
> +	u32 vp8_max_qp;
> +
> +	u32 multi_slice_mode;
> +	u32 multi_slice_max_bytes;
> +	u32 multi_slice_max_mb;
> +
> +	u32 header_mode;
> +
> +	u32 profile;
> +	u32 level;
> +};
> +
> +struct vidc_inst {
> +	struct list_head list;
> +	struct mutex lock;
> +	struct vidc_core *core;
> +
> +	struct vidc_list scratchbufs;
> +	struct vidc_list persistbufs;
> +	struct vidc_list registeredbufs;
> +
> +	struct list_head bufqueue;
> +	struct mutex bufqueue_lock;
> +
> +	int streamoff;
> +	int streamon;
> +	struct vb2_queue bufq_out;
> +	struct vb2_queue bufq_cap;
> +
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	union {
> +		struct vdec_controls dec;
> +		struct venc_controls enc;
> +	} controls;
> +	struct v4l2_fh fh;
> +
> +	struct hfi_inst *hfi_inst;
> +
> +	/* session fields */
> +	u32 session_type;
> +	u32 width;
> +	u32 height;
> +	u32 out_width;
> +	u32 out_height;
> +	u32 colorspace;
> +	u8 ycbcr_enc;
> +	u8 quantization;
> +	u8 xfer_func;
> +	u64 fps;
> +	struct v4l2_fract timeperframe;
> +	const struct vidc_format *fmt_out;
> +	const struct vidc_format *fmt_cap;
> +	unsigned int num_input_bufs;
> +	unsigned int num_output_bufs;
> +	bool in_reconfig;
> +	u32 reconfig_width;
> +	u32 reconfig_height;
> +	u64 sequence;
> +};
> +
> +#define ctrl_to_inst(ctrl)	\
> +	container_of(ctrl->handler, struct vidc_inst, ctrl_handler)
> +
> +struct vidc_ctrl {
> +	u32 id;
> +	enum v4l2_ctrl_type type;
> +	s32 min;
> +	s32 max;
> +	s32 def;
> +	u32 step;
> +	u64 menu_skip_mask;
> +	u32 flags;
> +	const char * const *qmenu;
> +};
> +
> +/*
> + * Offset base for buffers on the destination queue - used to distinguish
> + * between source and destination buffers when mmapping - they receive the same
> + * offsets but for different queues
> + */
> +#define DST_QUEUE_OFF_BASE	(1 << 30)
> +
> +extern const struct v4l2_file_operations vidc_fops;
> +
> +static inline void INIT_VIDC_LIST(struct vidc_list *mlist)
> +{
> +	mutex_init(&mlist->lock);
> +	INIT_LIST_HEAD(&mlist->list);
> +}
> +
> +static inline struct vidc_inst *to_inst(struct file *filp)
> +{
> +	return container_of(filp->private_data, struct vidc_inst, fh);
> +}
> +
> +static inline struct hfi_inst *to_hfi_inst(struct file *filp)
> +{
> +	return to_inst(filp)->hfi_inst;
> +}
> +
> +static inline struct vb2_queue *
> +vidc_to_vb2q(struct file *file, enum v4l2_buf_type type)
> +{
> +	struct vidc_inst *inst = to_inst(file);
> +
> +	if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> +		return &inst->bufq_cap;
> +	else if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +		return &inst->bufq_out;
> +
> +	return NULL;
> +}
> +
> +#endif
> diff --git a/drivers/media/platform/qcom/vidc/helpers.c b/drivers/media/platform/qcom/vidc/helpers.c
> new file mode 100644
> index 000000000000..81079f2b5ed1
> --- /dev/null
> +++ b/drivers/media/platform/qcom/vidc/helpers.c
> @@ -0,0 +1,394 @@
> +/*
> + * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
> + * Copyright (C) 2016 Linaro Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +#include <linux/list.h>
> +#include <linux/mutex.h>
> +#include <linux/pm_runtime.h>
> +#include <media/videobuf2-dma-sg.h>
> +
> +#include "helpers.h"
> +#include "int_bufs.h"
> +#include "load.h"
> +#include "hfi_helper.h"
> +
> +static int session_set_buf(struct vb2_buffer *vb)
> +{
> +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +	struct vb2_queue *q = vb->vb2_queue;
> +	struct vidc_inst *inst = vb2_get_drv_priv(q);
> +	struct vidc_core *core = inst->core;
> +	struct device *dev = core->dev;
> +	struct hfi_core *hfi = &core->hfi;
> +	struct vidc_buffer *buf = to_vidc_buffer(vbuf);
> +	struct hfi_frame_data fdata;
> +	int ret;
> +
> +	memset(&fdata, 0, sizeof(fdata));
> +
> +	fdata.alloc_len = vb2_plane_size(vb, 0);
> +	fdata.device_addr = buf->dma_addr;
> +	fdata.timestamp = vb->timestamp;
> +	fdata.flags = 0;
> +	fdata.clnt_data = buf->dma_addr;
> +
> +	if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +		fdata.buffer_type = HFI_BUFFER_INPUT;
> +		fdata.filled_len = vb2_get_plane_payload(vb, 0);
> +		fdata.offset = vb->planes[0].data_offset;
> +
> +		if (vbuf->flags & V4L2_BUF_FLAG_LAST || !fdata.filled_len)
> +			fdata.flags |= HFI_BUFFERFLAG_EOS;
> +
> +		ret = vidc_hfi_session_etb(hfi, inst->hfi_inst, &fdata);
> +	} else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
> +		fdata.buffer_type = HFI_BUFFER_OUTPUT;
> +		fdata.filled_len = 0;
> +		fdata.offset = 0;
> +
> +		ret = vidc_hfi_session_ftb(hfi, inst->hfi_inst, &fdata);
> +	} else {
> +		ret = -EINVAL;
> +	}
> +
> +	if (ret) {
> +		dev_err(dev, "failed to set session buffer (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int session_unregister_bufs(struct vidc_inst *inst)
> +{
> +	struct device *dev = inst->core->dev;
> +	struct hfi_core *hfi = &inst->core->hfi;
> +	struct hfi_buffer_desc *bd;
> +	struct vidc_buffer *buf, *tmp;
> +	int ret = 0;
> +
> +	mutex_lock(&inst->registeredbufs.lock);
> +	list_for_each_entry_safe(buf, tmp, &inst->registeredbufs.list,
> +				 hfi_list) {
> +		list_del(&buf->hfi_list);
> +		bd = &buf->bd;
> +		bd->response_required = 1;
> +		ret = vidc_hfi_session_unset_buffers(hfi, inst->hfi_inst, bd);
> +		if (ret) {
> +			dev_err(dev, "%s: session release buffers failed\n",
> +				__func__);
> +			break;
> +		}
> +	}
> +	mutex_unlock(&inst->registeredbufs.lock);
> +
> +	return ret;
> +}
> +
> +static int session_register_bufs(struct vidc_inst *inst)
> +{
> +	struct device *dev = inst->core->dev;
> +	struct hfi_core *hfi = &inst->core->hfi;
> +	struct hfi_buffer_desc *bd;
> +	struct vidc_buffer *buf, *tmp;
> +	int ret = 0;
> +
> +	mutex_lock(&inst->registeredbufs.lock);
> +	list_for_each_entry_safe(buf, tmp, &inst->registeredbufs.list,
> +				 hfi_list) {
> +		bd = &buf->bd;
> +		ret = vidc_hfi_session_set_buffers(hfi, inst->hfi_inst, bd);
> +		if (ret) {
> +			dev_err(dev, "%s: session: set buffer failed\n",
> +				__func__);
> +			break;
> +		}
> +	}
> +	mutex_unlock(&inst->registeredbufs.lock);
> +
> +	return ret;
> +}
> +
> +int vidc_buf_descs(struct vidc_inst *inst, u32 type,
> +		   struct hfi_buffer_requirements *out)
> +{
> +	struct hfi_core *hfi = &inst->core->hfi;
> +	u32 ptype = HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS;
> +	union hfi_get_property hprop;
> +	int ret, i;
> +
> +	if (out)
> +		memset(out, 0, sizeof(*out));
> +
> +	ret = vidc_hfi_session_get_property(hfi, inst->hfi_inst, ptype, &hprop);
> +	if (ret)
> +		return ret;
> +
> +	ret = -EINVAL;
> +
> +	for (i = 0; i < HFI_BUFFER_TYPE_MAX; i++) {
> +		if (hprop.bufreq[i].type != type)
> +			continue;
> +
> +		if (out)
> +			memcpy(out, &hprop.bufreq[i], sizeof(*out));
> +		ret = 0;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +int vidc_set_color_format(struct vidc_inst *inst, u32 type, u32 pixfmt)
> +{
> +	struct hfi_uncompressed_format_select fmt;
> +	struct hfi_core *hfi = &inst->core->hfi;
> +	u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT;
> +	int ret;
> +
> +	fmt.buffer_type = type;
> +
> +	switch (pixfmt) {
> +	case V4L2_PIX_FMT_NV12:
> +		fmt.format = HFI_COLOR_FORMAT_NV12;
> +		break;
> +	case V4L2_PIX_FMT_NV21:
> +		fmt.format = HFI_COLOR_FORMAT_NV21;
> +		break;
> +	default:
> +		return -ENOTSUPP;

I'm not really sure how this error code is used, but normally -EINVAL is returned
for invalid pixel formats. -ENOTSUPP is not used by V4L2.

> +	}
> +
> +	ret = vidc_hfi_session_set_property(hfi, inst->hfi_inst, ptype, &fmt);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +struct vb2_v4l2_buffer *
> +vidc_vb2_find_buf(struct vidc_inst *inst, dma_addr_t addr)
> +{
> +	struct vidc_buffer *buf;
> +	struct vb2_v4l2_buffer *vb = NULL;
> +
> +	mutex_lock(&inst->bufqueue_lock);
> +
> +	list_for_each_entry(buf, &inst->bufqueue, list) {
> +		if (buf->dma_addr == addr) {
> +			vb = &buf->vb;
> +			break;
> +		}
> +	}
> +
> +	if (vb)
> +		list_del(&buf->list);
> +
> +	mutex_unlock(&inst->bufqueue_lock);
> +
> +	return vb;
> +}
> +
> +int vidc_vb2_buf_init(struct vb2_buffer *vb)
> +{
> +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +	struct vb2_queue *q = vb->vb2_queue;
> +	struct vidc_inst *inst = vb2_get_drv_priv(q);
> +	struct vidc_buffer *buf = to_vidc_buffer(vbuf);
> +	struct hfi_buffer_desc *bd = &buf->bd;
> +	struct sg_table *sgt;
> +
> +	memset(bd, 0, sizeof(*bd));
> +
> +	if (q->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> +		return 0;
> +
> +	sgt = vb2_dma_sg_plane_desc(vb, 0);
> +	if (!sgt)
> +		return -EINVAL;
> +
> +	bd->buffer_size = vb2_plane_size(vb, 0);
> +	bd->buffer_type = HFI_BUFFER_OUTPUT;
> +	bd->num_buffers = 1;
> +	bd->device_addr = sg_dma_address(sgt->sgl);
> +
> +	mutex_lock(&inst->registeredbufs.lock);
> +	list_add_tail(&buf->hfi_list, &inst->registeredbufs.list);
> +	mutex_unlock(&inst->registeredbufs.lock);
> +
> +	return 0;
> +}
> +
> +int vidc_vb2_buf_prepare(struct vb2_buffer *vb)
> +{
> +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +	struct vidc_buffer *buf = to_vidc_buffer(vbuf);
> +	struct sg_table *sgt;
> +
> +	sgt = vb2_dma_sg_plane_desc(vb, 0);
> +	if (!sgt)
> +		return -EINVAL;
> +
> +	buf->dma_addr = sg_dma_address(sgt->sgl);
> +
> +	return 0;
> +}
> +
> +void vidc_vb2_buf_queue(struct vb2_buffer *vb)
> +{
> +	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> +	struct vidc_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
> +	struct vidc_core *core = inst->core;
> +	struct device *dev = core->dev;
> +	struct vidc_buffer *buf = to_vidc_buffer(vbuf);
> +	unsigned int state;
> +	int ret;
> +
> +	mutex_lock(&inst->hfi_inst->lock);
> +	state = inst->hfi_inst->state;
> +	mutex_unlock(&inst->hfi_inst->lock);
> +
> +	if (state == INST_INVALID || state >= INST_STOP) {
> +		vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
> +		dev_dbg(dev, "%s: type:%d, invalid instance state\n", __func__,
> +			vb->type);
> +		return;
> +	}
> +
> +	mutex_lock(&inst->bufqueue_lock);
> +	list_add_tail(&buf->list, &inst->bufqueue);
> +	mutex_unlock(&inst->bufqueue_lock);
> +
> +	if (!vb2_is_streaming(&inst->bufq_cap) ||
> +	    !vb2_is_streaming(&inst->bufq_out))
> +		return;
> +
> +	ret = session_set_buf(vb);
> +	if (ret)
> +		vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
> +}
> +
> +void vidc_vb2_stop_streaming(struct vb2_queue *q)
> +{
> +	struct vidc_inst *inst = vb2_get_drv_priv(q);
> +	struct hfi_inst *hfi_inst = inst->hfi_inst;
> +	struct vidc_core *core = inst->core;
> +	struct device *dev = core->dev;
> +	struct hfi_core *hfi = &core->hfi;
> +	int ret, streamoff;
> +
> +	mutex_lock(&inst->lock);
> +	streamoff = inst->streamoff;
> +	mutex_unlock(&inst->lock);
> +
> +	if (streamoff)
> +		return;
> +
> +	mutex_lock(&inst->lock);
> +	if (inst->streamon == 0) {
> +		mutex_unlock(&inst->lock);
> +		return;
> +	}
> +	mutex_unlock(&inst->lock);
> +
> +	ret = vidc_hfi_session_stop(hfi, inst->hfi_inst);
> +	if (ret) {
> +		dev_err(dev, "session: stop failed (%d)\n", ret);
> +		goto abort;
> +	}
> +
> +	ret = vidc_hfi_session_unload_res(hfi, inst->hfi_inst);
> +	if (ret) {
> +		dev_err(dev, "session: release resources failed (%d)\n", ret);
> +		goto abort;
> +	}
> +
> +	ret = session_unregister_bufs(inst);
> +	if (ret) {
> +		dev_err(dev, "failed to release capture buffers: %d\n", ret);
> +		goto abort;
> +	}
> +
> +	ret = internal_bufs_free(inst);
> +
> +	if (hfi_inst->state == INST_INVALID || hfi->state == CORE_INVALID) {
> +		ret = -EINVAL;
> +		goto abort;
> +	}
> +
> +abort:
> +	if (ret)
> +		vidc_hfi_session_abort(hfi, inst->hfi_inst);
> +
> +	vidc_scale_clocks(inst->core);
> +
> +	ret = vidc_hfi_session_deinit(hfi, inst->hfi_inst);
> +
> +	mutex_lock(&inst->lock);
> +	inst->streamoff = 1;
> +	mutex_unlock(&inst->lock);
> +
> +	if (ret)
> +		dev_err(dev, "stop streaming failed type: %d, ret: %d\n",
> +			q->type, ret);
> +
> +	ret = pm_runtime_put_sync(dev);
> +	if (ret < 0)
> +		dev_err(dev, "%s: pm_runtime_put_sync (%d)\n", __func__, ret);
> +}
> +
> +int vidc_vb2_start_streaming(struct vidc_inst *inst)
> +{
> +	struct device *dev = inst->core->dev;
> +	struct hfi_core *hfi = &inst->core->hfi;
> +	struct vidc_buffer *buf, *n;
> +	int ret;
> +
> +	ret = session_register_bufs(inst);
> +	if (ret)
> +		return ret;
> +
> +	ret = internal_bufs_alloc(inst);
> +	if (ret)
> +		return ret;
> +
> +	vidc_scale_clocks(inst->core);
> +
> +	ret = vidc_hfi_session_load_res(hfi, inst->hfi_inst);
> +	if (ret) {
> +		dev_err(dev, "session: load resources (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = vidc_hfi_session_start(hfi, inst->hfi_inst);
> +	if (ret) {
> +		dev_err(dev, "session: start failed (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	mutex_lock(&inst->bufqueue_lock);
> +	list_for_each_entry_safe(buf, n, &inst->bufqueue, list) {
> +		ret = session_set_buf(&buf->vb.vb2_buf);
> +		if (ret)
> +			break;
> +	}
> +	mutex_unlock(&inst->bufqueue_lock);
> +
> +	if (!ret) {
> +		mutex_lock(&inst->lock);
> +		inst->streamon = 1;
> +		mutex_unlock(&inst->lock);
> +	}
> +
> +	return ret;
> +}

Regards,

	Hans
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux