Загрузка данных


static void mxc_isi_video_free_discard_buffers(struct mxc_isi_video *video)
{
	unsigned int i;

	for (i = 0; i < video->pix.num_planes; i++) {
		struct mxc_isi_dma_buffer *buf = &video->discard_buffer[i];

		if (!buf->addr)
			continue;

		dma_free_coherent(video->pipe->isi->dev, buf->size, buf->addr,
				  buf->dma);
		buf->addr = NULL;
	}
}

static int mxc_isi_video_alloc_discard_buffers(struct mxc_isi_video *video)
{
	unsigned int i, j;

	/* Allocate memory for each plane. */
	for (i = 0; i < video->pix.num_planes; i++) {
		struct mxc_isi_dma_buffer *buf = &video->discard_buffer[i];

		buf->size = PAGE_ALIGN(video->pix.plane_fmt[i].sizeimage);
		buf->addr = dma_alloc_coherent(video->pipe->isi->dev, buf->size,
					       &buf->dma, GFP_DMA | GFP_KERNEL);
		if (!buf->addr) {
			mxc_isi_video_free_discard_buffers(video);
			return -ENOMEM;
		}

		dev_dbg(video->pipe->isi->dev,
			"discard buffer plane %u: %zu bytes @%pad (CPU address %p)\n",
			i, buf->size, &buf->dma, buf->addr);
	}

	/* Fill the DMA addresses in the discard buffers. */
	for (i = 0; i < ARRAY_SIZE(video->buf_discard); ++i) {
		struct mxc_isi_buffer *buf = &video->buf_discard[i];

		buf->discard = true;

		for (j = 0; j < video->pix.num_planes; ++j)
			buf->dma_addrs[j] = video->discard_buffer[j].dma;
	}

	return 0;
}

static int mxc_isi_video_validate_format(struct mxc_isi_video *video)
{
	const struct v4l2_mbus_framefmt *format;
	const struct mxc_isi_format_info *info;
	struct v4l2_subdev_state *state;
	struct v4l2_subdev *sd = &video->pipe->sd;
	int ret = 0;

	state = v4l2_subdev_lock_and_get_active_state(sd);

	info = mxc_isi_format_by_fourcc(video->pix.pixelformat,
					MXC_ISI_VIDEO_CAP);
	format = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE);
#ifdef MY_CODE
	if (format->code != info->mbus_code ||
	    format->width != video->pix.width ||
	    format->height != video->pix.height) {
		dev_info(video->pipe->isi->dev,
			"%s: configuration mismatch, 0x%04x/%ux%u != 0x%04x/%ux%u\n",
			__func__, format->code, format->width, format->height,
			info->mbus_code, video->pix.width, video->pix.height);
		ret = -EINVAL;
	}
#endif
	v4l2_subdev_unlock_state(state);

	return ret;
}

static void mxc_isi_video_return_buffers(struct mxc_isi_video *video,
					 enum vb2_buffer_state state)
{
	struct mxc_isi_buffer *buf;

	spin_lock_irq(&video->buf_lock);

	while (!list_empty(&video->out_active)) {
		buf = list_first_entry(&video->out_active,
				       struct mxc_isi_buffer, list);
		list_del_init(&buf->list);
		if (buf->discard)
			continue;

		vb2_buffer_done(&buf->v4l2_buf.vb2_buf, state);
	}

	while (!list_empty(&video->out_pending)) {
		buf = list_first_entry(&video->out_pending,
				       struct mxc_isi_buffer, list);
		list_del_init(&buf->list);
		vb2_buffer_done(&buf->v4l2_buf.vb2_buf, state);
	}

	while (!list_empty(&video->out_discard)) {
		buf = list_first_entry(&video->out_discard,
				       struct mxc_isi_buffer, list);
		list_del_init(&buf->list);
	}

	INIT_LIST_HEAD(&video->out_active);
	INIT_LIST_HEAD(&video->out_pending);
	INIT_LIST_HEAD(&video->out_discard);

	spin_unlock_irq(&video->buf_lock);
}

static void mxc_isi_video_queue_first_buffers(struct mxc_isi_video *video)
{
	const struct mxc_isi_plat_data *pdata = video->pipe->isi->pdata;
	unsigned int discard;
	unsigned int i;

	lockdep_assert_held(&video->buf_lock);

	/*
	 * Queue two ISI channel output buffers. We are not guaranteed to have
	 * any buffer in the pending list when this function is called from the
	 * system resume handler. Use pending buffers as much as possible, and
	 * use discard buffers to fill the remaining slots.
	 */

	/* How many discard buffers do we need to queue first ? */
	discard = list_empty(&video->out_pending) ? 2
		: list_is_singular(&video->out_pending) ? 1
		: 0;

	for (i = 0; i < 2; ++i) {
		enum mxc_isi_buf_id buf_id = i == 0 ? MXC_ISI_BUF1
					   : MXC_ISI_BUF2;
		struct mxc_isi_buffer *buf;
		struct list_head *list;

		list = i < discard ? &video->out_discard : &video->out_pending;
		buf = list_first_entry(list, struct mxc_isi_buffer, list);

		mxc_isi_channel_set_outbuf(video->pipe, buf->dma_addrs, buf_id);
		mxc_isi_channel_set_max_size(video->pipe, &video->pix, pdata->buf_max_size);
		buf->id = buf_id;
		list_move_tail(&buf->list, &video->out_active);
	}
}

static inline struct mxc_isi_buffer *to_isi_buffer(struct vb2_v4l2_buffer *v4l2_buf)
{
	return container_of(v4l2_buf, struct mxc_isi_buffer, v4l2_buf);
}

int mxc_isi_video_queue_setup(const struct v4l2_pix_format_mplane *format,
			      const struct mxc_isi_format_info *info,
			      unsigned int *num_buffers,
			      unsigned int *num_planes, unsigned int sizes[])
{
	unsigned int i;

	if (*num_planes) {
		if (*num_planes != info->mem_planes)
			return -EINVAL;

		for (i = 0; i < info->mem_planes; ++i) {
			if (sizes[i] < format->plane_fmt[i].sizeimage)
				return -EINVAL;
		}

		return 0;
	}

	*num_planes = info->mem_planes;

	for (i = 0; i < info->mem_planes; ++i)
		sizes[i] = format->plane_fmt[i].sizeimage;

	return 0;
}

void mxc_isi_video_buffer_init(struct vb2_buffer *vb2, dma_addr_t dma_addrs[3],
			       const struct mxc_isi_format_info *info,
			       const struct v4l2_pix_format_mplane *pix)
{
	unsigned int i;

	for (i = 0; i < info->mem_planes; ++i)
		dma_addrs[i] = vb2_dma_contig_plane_dma_addr(vb2, i);

	/*
	 * For single-planar pixel formats with multiple color planes, split
	 * the buffer into color planes.
	 */
	if (info->color_planes != info->mem_planes) {
		unsigned int size = pix->plane_fmt[0].bytesperline * pix->height;

		for (i = 1; i < info->color_planes; ++i) {
			unsigned int vsub = i > 1 ? info->vsub : 1;

			dma_addrs[i] = dma_addrs[i - 1] + size / vsub;
		}
	}
}

int mxc_isi_video_buffer_prepare(struct mxc_isi_dev *isi, struct vb2_buffer *vb2,
				 const struct mxc_isi_format_info *info,
				 const struct v4l2_pix_format_mplane *pix)
{
	struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb2);
	unsigned int i;

	for (i = 0; i < info->mem_planes; i++) {
		unsigned long size = pix->plane_fmt[i].sizeimage;

		if (vb2_plane_size(vb2, i) < size) {
			dev_err(isi->dev, "User buffer too small (%ld < %ld)\n",
				vb2_plane_size(vb2, i), size);
			return -EINVAL;
		}

		vb2_set_plane_payload(vb2, i, size);
	}

	v4l2_buf->field = pix->field;

	return 0;
}

static int mxc_isi_vb2_queue_setup(struct vb2_queue *q,
				   unsigned int *num_buffers,
				   unsigned int *num_planes,
				   unsigned int sizes[],
				   struct device *alloc_devs[])
{
	struct mxc_isi_video *video = vb2_get_drv_priv(q);

	return mxc_isi_video_queue_setup(&video->pix, video->fmtinfo,
					 num_buffers, num_planes, sizes);
}

static int mxc_isi_vb2_buffer_init(struct vb2_buffer *vb2)
{
	struct mxc_isi_buffer *buf = to_isi_buffer(to_vb2_v4l2_buffer(vb2));
	struct mxc_isi_video *video = vb2_get_drv_priv(vb2->vb2_queue);

	mxc_isi_video_buffer_init(vb2, buf->dma_addrs, video->fmtinfo,
				  &video->pix);

	return 0;
}

static int mxc_isi_vb2_buffer_prepare(struct vb2_buffer *vb2)
{
	struct mxc_isi_video *video = vb2_get_drv_priv(vb2->vb2_queue);

	return mxc_isi_video_buffer_prepare(video->pipe->isi, vb2,
					    video->fmtinfo, &video->pix);
}