brintos

brintos / linux-shallow public Read only

0
0
Text · 34.2 KiB · c7ee6e1 Raw
1427 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) STMicroelectronics SA 20144 * Authors: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.5 */6 7#include <linux/errno.h>8#include <linux/interrupt.h>9#include <linux/kernel.h>10#include <linux/module.h>11#include <linux/of.h>12#include <linux/pm_runtime.h>13#include <linux/slab.h>14 15#include <media/v4l2-event.h>16#include <media/v4l2-ioctl.h>17 18#include "bdisp.h"19 20#define BDISP_MAX_CTRL_NUM      1021 22#define BDISP_WORK_TIMEOUT      ((100 * HZ) / 1000)23 24/* User configuration change */25#define BDISP_PARAMS            BIT(0) /* Config updated */26#define BDISP_SRC_FMT           BIT(1) /* Source set */27#define BDISP_DST_FMT           BIT(2) /* Destination set */28#define BDISP_CTX_STOP_REQ      BIT(3) /* Stop request */29#define BDISP_CTX_ABORT         BIT(4) /* Abort while device run */30 31#define BDISP_MIN_W             132#define BDISP_MAX_W             819133#define BDISP_MIN_H             134#define BDISP_MAX_H             819135 36#define fh_to_ctx(__fh) container_of(__fh, struct bdisp_ctx, fh)37 38enum bdisp_dev_flags {39	ST_M2M_OPEN,            /* Driver opened */40	ST_M2M_RUNNING,         /* HW device running */41	ST_M2M_SUSPENDED,       /* Driver suspended */42	ST_M2M_SUSPENDING,      /* Driver being suspended */43};44 45static const struct bdisp_fmt bdisp_formats[] = {46	/* ARGB888. [31:0] A:R:G:B 8:8:8:8 little endian */47	{48		.pixelformat    = V4L2_PIX_FMT_ABGR32, /* is actually ARGB */49		.nb_planes      = 1,50		.bpp            = 32,51		.bpp_plane0     = 32,52		.w_align        = 1,53		.h_align        = 154	},55	/* XRGB888. [31:0] x:R:G:B 8:8:8:8 little endian */56	{57		.pixelformat    = V4L2_PIX_FMT_XBGR32, /* is actually xRGB */58		.nb_planes      = 1,59		.bpp            = 32,60		.bpp_plane0     = 32,61		.w_align        = 1,62		.h_align        = 163	},64	/* RGB565. [15:0] R:G:B 5:6:5 little endian */65	{66		.pixelformat    = V4L2_PIX_FMT_RGB565,67		.nb_planes      = 1,68		.bpp            = 16,69		.bpp_plane0     = 16,70		.w_align        = 1,71		.h_align        = 172	},73	/* NV12. YUV420SP - 1 plane for Y + 1 plane for (CbCr) */74	{75		.pixelformat    = V4L2_PIX_FMT_NV12,76		.nb_planes      = 2,77		.bpp            = 12,78		.bpp_plane0     = 8,79		.w_align        = 2,80		.h_align        = 281	},82	/* RGB888. [23:0] B:G:R 8:8:8 little endian */83	{84		.pixelformat    = V4L2_PIX_FMT_RGB24,85		.nb_planes      = 1,86		.bpp            = 24,87		.bpp_plane0     = 24,88		.w_align        = 1,89		.h_align        = 190	},91	/* YU12. YUV420P - 1 plane for Y + 1 plane for Cb + 1 plane for Cr92	 * To keep as the LAST element of this table (no support on capture)93	 */94	{95		.pixelformat    = V4L2_PIX_FMT_YUV420,96		.nb_planes      = 3,97		.bpp            = 12,98		.bpp_plane0     = 8,99		.w_align        = 2,100		.h_align        = 2101	}102};103 104/* Default format : HD ARGB32*/105#define BDISP_DEF_WIDTH         1920106#define BDISP_DEF_HEIGHT        1080107 108static const struct bdisp_frame bdisp_dflt_fmt = {109		.width          = BDISP_DEF_WIDTH,110		.height         = BDISP_DEF_HEIGHT,111		.fmt            = &bdisp_formats[0],112		.field          = V4L2_FIELD_NONE,113		.bytesperline   = BDISP_DEF_WIDTH * 4,114		.sizeimage      = BDISP_DEF_WIDTH * BDISP_DEF_HEIGHT * 4,115		.colorspace     = V4L2_COLORSPACE_REC709,116		.crop           = {0, 0, BDISP_DEF_WIDTH, BDISP_DEF_HEIGHT},117		.paddr          = {0, 0, 0, 0}118};119 120static inline void bdisp_ctx_state_lock_set(u32 state, struct bdisp_ctx *ctx)121{122	unsigned long flags;123 124	spin_lock_irqsave(&ctx->bdisp_dev->slock, flags);125	ctx->state |= state;126	spin_unlock_irqrestore(&ctx->bdisp_dev->slock, flags);127}128 129static inline void bdisp_ctx_state_lock_clear(u32 state, struct bdisp_ctx *ctx)130{131	unsigned long flags;132 133	spin_lock_irqsave(&ctx->bdisp_dev->slock, flags);134	ctx->state &= ~state;135	spin_unlock_irqrestore(&ctx->bdisp_dev->slock, flags);136}137 138static inline bool bdisp_ctx_state_is_set(u32 mask, struct bdisp_ctx *ctx)139{140	unsigned long flags;141	bool ret;142 143	spin_lock_irqsave(&ctx->bdisp_dev->slock, flags);144	ret = (ctx->state & mask) == mask;145	spin_unlock_irqrestore(&ctx->bdisp_dev->slock, flags);146 147	return ret;148}149 150static const struct bdisp_fmt *bdisp_find_fmt(u32 pixelformat)151{152	const struct bdisp_fmt *fmt;153	unsigned int i;154 155	for (i = 0; i < ARRAY_SIZE(bdisp_formats); i++) {156		fmt = &bdisp_formats[i];157		if (fmt->pixelformat == pixelformat)158			return fmt;159	}160 161	return NULL;162}163 164static struct bdisp_frame *ctx_get_frame(struct bdisp_ctx *ctx,165					 enum v4l2_buf_type type)166{167	switch (type) {168	case V4L2_BUF_TYPE_VIDEO_OUTPUT:169		return &ctx->src;170	case V4L2_BUF_TYPE_VIDEO_CAPTURE:171		return &ctx->dst;172	default:173		dev_err(ctx->bdisp_dev->dev,174			"Wrong buffer/video queue type (%d)\n", type);175		break;176	}177 178	return ERR_PTR(-EINVAL);179}180 181static void bdisp_job_finish(struct bdisp_ctx *ctx, int vb_state)182{183	struct vb2_v4l2_buffer *src_vb, *dst_vb;184 185	if (WARN(!ctx || !ctx->fh.m2m_ctx, "Null hardware context\n"))186		return;187 188	dev_dbg(ctx->bdisp_dev->dev, "%s\n", __func__);189 190	src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);191	dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);192 193	if (src_vb && dst_vb) {194		dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;195		dst_vb->timecode = src_vb->timecode;196		dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;197		dst_vb->flags |= src_vb->flags &198					  V4L2_BUF_FLAG_TSTAMP_SRC_MASK;199 200		v4l2_m2m_buf_done(src_vb, vb_state);201		v4l2_m2m_buf_done(dst_vb, vb_state);202 203		v4l2_m2m_job_finish(ctx->bdisp_dev->m2m.m2m_dev,204				    ctx->fh.m2m_ctx);205	}206}207 208static int bdisp_ctx_stop_req(struct bdisp_ctx *ctx)209{210	struct bdisp_ctx *curr_ctx;211	struct bdisp_dev *bdisp = ctx->bdisp_dev;212	int ret;213 214	dev_dbg(ctx->bdisp_dev->dev, "%s\n", __func__);215 216	cancel_delayed_work(&bdisp->timeout_work);217 218	curr_ctx = v4l2_m2m_get_curr_priv(bdisp->m2m.m2m_dev);219	if (!test_bit(ST_M2M_RUNNING, &bdisp->state) || (curr_ctx != ctx))220		return 0;221 222	bdisp_ctx_state_lock_set(BDISP_CTX_STOP_REQ, ctx);223 224	ret = wait_event_timeout(bdisp->irq_queue,225			!bdisp_ctx_state_is_set(BDISP_CTX_STOP_REQ, ctx),226			BDISP_WORK_TIMEOUT);227 228	if (!ret) {229		dev_err(ctx->bdisp_dev->dev, "%s IRQ timeout\n", __func__);230		return -ETIMEDOUT;231	}232 233	return 0;234}235 236static void __bdisp_job_abort(struct bdisp_ctx *ctx)237{238	int ret;239 240	ret = bdisp_ctx_stop_req(ctx);241	if ((ret == -ETIMEDOUT) || (ctx->state & BDISP_CTX_ABORT)) {242		bdisp_ctx_state_lock_clear(BDISP_CTX_STOP_REQ | BDISP_CTX_ABORT,243					   ctx);244		bdisp_job_finish(ctx, VB2_BUF_STATE_ERROR);245	}246}247 248static void bdisp_job_abort(void *priv)249{250	__bdisp_job_abort((struct bdisp_ctx *)priv);251}252 253static int bdisp_get_addr(struct bdisp_ctx *ctx, struct vb2_buffer *vb,254			  struct bdisp_frame *frame, dma_addr_t *paddr)255{256	if (!vb || !frame)257		return -EINVAL;258 259	paddr[0] = vb2_dma_contig_plane_dma_addr(vb, 0);260 261	if (frame->fmt->nb_planes > 1)262		/* UV (NV12) or U (420P) */263		paddr[1] = (dma_addr_t)(paddr[0] +264				frame->bytesperline * frame->height);265 266	if (frame->fmt->nb_planes > 2)267		/* V (420P) */268		paddr[2] = (dma_addr_t)(paddr[1] +269				(frame->bytesperline * frame->height) / 4);270 271	if (frame->fmt->nb_planes > 3)272		dev_dbg(ctx->bdisp_dev->dev, "ignoring some planes\n");273 274	dev_dbg(ctx->bdisp_dev->dev,275		"%s plane[0]=%pad plane[1]=%pad plane[2]=%pad\n",276		__func__, &paddr[0], &paddr[1], &paddr[2]);277 278	return 0;279}280 281static int bdisp_get_bufs(struct bdisp_ctx *ctx)282{283	struct bdisp_frame *src, *dst;284	struct vb2_v4l2_buffer *src_vb, *dst_vb;285	int ret;286 287	src = &ctx->src;288	dst = &ctx->dst;289 290	src_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);291	ret = bdisp_get_addr(ctx, &src_vb->vb2_buf, src, src->paddr);292	if (ret)293		return ret;294 295	dst_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);296	ret = bdisp_get_addr(ctx, &dst_vb->vb2_buf, dst, dst->paddr);297	if (ret)298		return ret;299 300	dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;301 302	return 0;303}304 305static void bdisp_device_run(void *priv)306{307	struct bdisp_ctx *ctx = priv;308	struct bdisp_dev *bdisp;309	unsigned long flags;310	int err = 0;311 312	if (WARN(!ctx, "Null hardware context\n"))313		return;314 315	bdisp = ctx->bdisp_dev;316	dev_dbg(bdisp->dev, "%s\n", __func__);317	spin_lock_irqsave(&bdisp->slock, flags);318 319	if (bdisp->m2m.ctx != ctx) {320		dev_dbg(bdisp->dev, "ctx updated: %p -> %p\n",321			bdisp->m2m.ctx, ctx);322		ctx->state |= BDISP_PARAMS;323		bdisp->m2m.ctx = ctx;324	}325 326	if (ctx->state & BDISP_CTX_STOP_REQ) {327		ctx->state &= ~BDISP_CTX_STOP_REQ;328		ctx->state |= BDISP_CTX_ABORT;329		wake_up(&bdisp->irq_queue);330		goto out;331	}332 333	err = bdisp_get_bufs(ctx);334	if (err) {335		dev_err(bdisp->dev, "cannot get address\n");336		goto out;337	}338 339	bdisp_dbg_perf_begin(bdisp);340 341	err = bdisp_hw_reset(bdisp);342	if (err) {343		dev_err(bdisp->dev, "could not get HW ready\n");344		goto out;345	}346 347	err = bdisp_hw_update(ctx);348	if (err) {349		dev_err(bdisp->dev, "could not send HW request\n");350		goto out;351	}352 353	queue_delayed_work(bdisp->work_queue, &bdisp->timeout_work,354			   BDISP_WORK_TIMEOUT);355	set_bit(ST_M2M_RUNNING, &bdisp->state);356out:357	ctx->state &= ~BDISP_PARAMS;358	spin_unlock_irqrestore(&bdisp->slock, flags);359	if (err)360		bdisp_job_finish(ctx, VB2_BUF_STATE_ERROR);361}362 363static const struct v4l2_m2m_ops bdisp_m2m_ops = {364	.device_run     = bdisp_device_run,365	.job_abort      = bdisp_job_abort,366};367 368static int __bdisp_s_ctrl(struct bdisp_ctx *ctx, struct v4l2_ctrl *ctrl)369{370	if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)371		return 0;372 373	switch (ctrl->id) {374	case V4L2_CID_HFLIP:375		ctx->hflip = ctrl->val;376		break;377	case V4L2_CID_VFLIP:378		ctx->vflip = ctrl->val;379		break;380	default:381		dev_err(ctx->bdisp_dev->dev, "unknown control %d\n", ctrl->id);382		return -EINVAL;383	}384 385	ctx->state |= BDISP_PARAMS;386 387	return 0;388}389 390static int bdisp_s_ctrl(struct v4l2_ctrl *ctrl)391{392	struct bdisp_ctx *ctx = container_of(ctrl->handler, struct bdisp_ctx,393						ctrl_handler);394	unsigned long flags;395	int ret;396 397	spin_lock_irqsave(&ctx->bdisp_dev->slock, flags);398	ret = __bdisp_s_ctrl(ctx, ctrl);399	spin_unlock_irqrestore(&ctx->bdisp_dev->slock, flags);400 401	return ret;402}403 404static const struct v4l2_ctrl_ops bdisp_c_ops = {405	.s_ctrl = bdisp_s_ctrl,406};407 408static int bdisp_ctrls_create(struct bdisp_ctx *ctx)409{410	if (ctx->ctrls_rdy)411		return 0;412 413	v4l2_ctrl_handler_init(&ctx->ctrl_handler, BDISP_MAX_CTRL_NUM);414 415	ctx->bdisp_ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,416				&bdisp_c_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);417	ctx->bdisp_ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,418				&bdisp_c_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);419 420	if (ctx->ctrl_handler.error) {421		int err = ctx->ctrl_handler.error;422 423		v4l2_ctrl_handler_free(&ctx->ctrl_handler);424		return err;425	}426 427	ctx->ctrls_rdy = true;428 429	return 0;430}431 432static void bdisp_ctrls_delete(struct bdisp_ctx *ctx)433{434	if (ctx->ctrls_rdy) {435		v4l2_ctrl_handler_free(&ctx->ctrl_handler);436		ctx->ctrls_rdy = false;437	}438}439 440static int bdisp_queue_setup(struct vb2_queue *vq,441			     unsigned int *nb_buf, unsigned int *nb_planes,442			     unsigned int sizes[], struct device *alloc_devs[])443{444	struct bdisp_ctx *ctx = vb2_get_drv_priv(vq);445	struct bdisp_frame *frame = ctx_get_frame(ctx, vq->type);446 447	if (IS_ERR(frame)) {448		dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame);449		return PTR_ERR(frame);450	}451 452	if (!frame->fmt) {453		dev_err(ctx->bdisp_dev->dev, "Invalid format\n");454		return -EINVAL;455	}456 457	if (*nb_planes)458		return sizes[0] < frame->sizeimage ? -EINVAL : 0;459 460	*nb_planes = 1;461	sizes[0] = frame->sizeimage;462 463	return 0;464}465 466static int bdisp_buf_prepare(struct vb2_buffer *vb)467{468	struct bdisp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);469	struct bdisp_frame *frame = ctx_get_frame(ctx, vb->vb2_queue->type);470 471	if (IS_ERR(frame)) {472		dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame);473		return PTR_ERR(frame);474	}475 476	if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)477		vb2_set_plane_payload(vb, 0, frame->sizeimage);478 479	return 0;480}481 482static void bdisp_buf_queue(struct vb2_buffer *vb)483{484	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);485	struct bdisp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);486 487	/* return to V4L2 any 0-size buffer so it can be dequeued by user */488	if (!vb2_get_plane_payload(vb, 0)) {489		dev_dbg(ctx->bdisp_dev->dev, "0 data buffer, skip it\n");490		vb2_buffer_done(vb, VB2_BUF_STATE_DONE);491		return;492	}493 494	if (ctx->fh.m2m_ctx)495		v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);496}497 498static int bdisp_start_streaming(struct vb2_queue *q, unsigned int count)499{500	struct bdisp_ctx *ctx = q->drv_priv;501	struct vb2_v4l2_buffer *buf;502	int ret = pm_runtime_resume_and_get(ctx->bdisp_dev->dev);503 504	if (ret < 0) {505		dev_err(ctx->bdisp_dev->dev, "failed to set runtime PM\n");506 507		if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {508			while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))509				v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);510		} else {511			while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx)))512				v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);513		}514 515		return ret;516	}517 518	return 0;519}520 521static void bdisp_stop_streaming(struct vb2_queue *q)522{523	struct bdisp_ctx *ctx = q->drv_priv;524 525	__bdisp_job_abort(ctx);526 527	pm_runtime_put(ctx->bdisp_dev->dev);528}529 530static const struct vb2_ops bdisp_qops = {531	.queue_setup     = bdisp_queue_setup,532	.buf_prepare     = bdisp_buf_prepare,533	.buf_queue       = bdisp_buf_queue,534	.wait_prepare    = vb2_ops_wait_prepare,535	.wait_finish     = vb2_ops_wait_finish,536	.stop_streaming  = bdisp_stop_streaming,537	.start_streaming = bdisp_start_streaming,538};539 540static int queue_init(void *priv,541		      struct vb2_queue *src_vq, struct vb2_queue *dst_vq)542{543	struct bdisp_ctx *ctx = priv;544	int ret;545 546	memset(src_vq, 0, sizeof(*src_vq));547	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;548	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;549	src_vq->drv_priv = ctx;550	src_vq->ops = &bdisp_qops;551	src_vq->mem_ops = &vb2_dma_contig_memops;552	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);553	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;554	src_vq->lock = &ctx->bdisp_dev->lock;555	src_vq->dev = ctx->bdisp_dev->v4l2_dev.dev;556 557	ret = vb2_queue_init(src_vq);558	if (ret)559		return ret;560 561	memset(dst_vq, 0, sizeof(*dst_vq));562	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;563	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;564	dst_vq->drv_priv = ctx;565	dst_vq->ops = &bdisp_qops;566	dst_vq->mem_ops = &vb2_dma_contig_memops;567	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);568	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;569	dst_vq->lock = &ctx->bdisp_dev->lock;570	dst_vq->dev = ctx->bdisp_dev->v4l2_dev.dev;571 572	return vb2_queue_init(dst_vq);573}574 575static int bdisp_open(struct file *file)576{577	struct bdisp_dev *bdisp = video_drvdata(file);578	struct bdisp_ctx *ctx = NULL;579	int ret;580 581	if (mutex_lock_interruptible(&bdisp->lock))582		return -ERESTARTSYS;583 584	/* Allocate memory for both context and node */585	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);586	if (!ctx) {587		ret = -ENOMEM;588		goto unlock;589	}590	ctx->bdisp_dev = bdisp;591 592	if (bdisp_hw_alloc_nodes(ctx)) {593		dev_err(bdisp->dev, "no memory for nodes\n");594		ret = -ENOMEM;595		goto mem_ctx;596	}597 598	v4l2_fh_init(&ctx->fh, bdisp->m2m.vdev);599 600	ret = bdisp_ctrls_create(ctx);601	if (ret) {602		dev_err(bdisp->dev, "Failed to create control\n");603		goto error_fh;604	}605 606	/* Use separate control handler per file handle */607	ctx->fh.ctrl_handler = &ctx->ctrl_handler;608	file->private_data = &ctx->fh;609	v4l2_fh_add(&ctx->fh);610 611	/* Default format */612	ctx->src = bdisp_dflt_fmt;613	ctx->dst = bdisp_dflt_fmt;614 615	/* Setup the device context for mem2mem mode. */616	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(bdisp->m2m.m2m_dev, ctx,617					    queue_init);618	if (IS_ERR(ctx->fh.m2m_ctx)) {619		dev_err(bdisp->dev, "Failed to initialize m2m context\n");620		ret = PTR_ERR(ctx->fh.m2m_ctx);621		goto error_ctrls;622	}623 624	bdisp->m2m.refcnt++;625	set_bit(ST_M2M_OPEN, &bdisp->state);626 627	dev_dbg(bdisp->dev, "driver opened, ctx = 0x%p\n", ctx);628 629	mutex_unlock(&bdisp->lock);630 631	return 0;632 633error_ctrls:634	bdisp_ctrls_delete(ctx);635	v4l2_fh_del(&ctx->fh);636error_fh:637	v4l2_fh_exit(&ctx->fh);638	bdisp_hw_free_nodes(ctx);639mem_ctx:640	kfree(ctx);641unlock:642	mutex_unlock(&bdisp->lock);643 644	return ret;645}646 647static int bdisp_release(struct file *file)648{649	struct bdisp_ctx *ctx = fh_to_ctx(file->private_data);650	struct bdisp_dev *bdisp = ctx->bdisp_dev;651 652	dev_dbg(bdisp->dev, "%s\n", __func__);653 654	mutex_lock(&bdisp->lock);655 656	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);657 658	bdisp_ctrls_delete(ctx);659 660	v4l2_fh_del(&ctx->fh);661	v4l2_fh_exit(&ctx->fh);662 663	if (--bdisp->m2m.refcnt <= 0)664		clear_bit(ST_M2M_OPEN, &bdisp->state);665 666	bdisp_hw_free_nodes(ctx);667 668	kfree(ctx);669 670	mutex_unlock(&bdisp->lock);671 672	return 0;673}674 675static const struct v4l2_file_operations bdisp_fops = {676	.owner          = THIS_MODULE,677	.open           = bdisp_open,678	.release        = bdisp_release,679	.poll           = v4l2_m2m_fop_poll,680	.unlocked_ioctl = video_ioctl2,681	.mmap           = v4l2_m2m_fop_mmap,682};683 684static int bdisp_querycap(struct file *file, void *fh,685			  struct v4l2_capability *cap)686{687	struct bdisp_ctx *ctx = fh_to_ctx(fh);688	struct bdisp_dev *bdisp = ctx->bdisp_dev;689 690	strscpy(cap->driver, bdisp->pdev->name, sizeof(cap->driver));691	strscpy(cap->card, bdisp->pdev->name, sizeof(cap->card));692	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s%d",693		 BDISP_NAME, bdisp->id);694	return 0;695}696 697static int bdisp_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)698{699	struct bdisp_ctx *ctx = fh_to_ctx(fh);700	const struct bdisp_fmt *fmt;701 702	if (f->index >= ARRAY_SIZE(bdisp_formats))703		return -EINVAL;704 705	fmt = &bdisp_formats[f->index];706 707	if ((fmt->pixelformat == V4L2_PIX_FMT_YUV420) &&708	    (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)) {709		dev_dbg(ctx->bdisp_dev->dev, "No YU12 on capture\n");710		return -EINVAL;711	}712	f->pixelformat = fmt->pixelformat;713 714	return 0;715}716 717static int bdisp_g_fmt(struct file *file, void *fh, struct v4l2_format *f)718{719	struct bdisp_ctx *ctx = fh_to_ctx(fh);720	struct v4l2_pix_format *pix;721	struct bdisp_frame *frame  = ctx_get_frame(ctx, f->type);722 723	if (IS_ERR(frame)) {724		dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame);725		return PTR_ERR(frame);726	}727 728	pix = &f->fmt.pix;729	pix->width = frame->width;730	pix->height = frame->height;731	pix->pixelformat = frame->fmt->pixelformat;732	pix->field = frame->field;733	pix->bytesperline = frame->bytesperline;734	pix->sizeimage = frame->sizeimage;735	pix->colorspace = (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ?736				frame->colorspace : bdisp_dflt_fmt.colorspace;737 738	return 0;739}740 741static int bdisp_try_fmt(struct file *file, void *fh, struct v4l2_format *f)742{743	struct bdisp_ctx *ctx = fh_to_ctx(fh);744	struct v4l2_pix_format *pix = &f->fmt.pix;745	const struct bdisp_fmt *format;746	u32 in_w, in_h;747 748	format = bdisp_find_fmt(pix->pixelformat);749	if (!format) {750		dev_dbg(ctx->bdisp_dev->dev, "Unknown format 0x%x\n",751			pix->pixelformat);752		return -EINVAL;753	}754 755	/* YUV420P only supported for VIDEO_OUTPUT */756	if ((format->pixelformat == V4L2_PIX_FMT_YUV420) &&757	    (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)) {758		dev_dbg(ctx->bdisp_dev->dev, "No YU12 on capture\n");759		return -EINVAL;760	}761 762	/* Field (interlaced only supported on OUTPUT) */763	if ((f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ||764	    (pix->field != V4L2_FIELD_INTERLACED))765		pix->field = V4L2_FIELD_NONE;766 767	/* Adjust width & height */768	in_w = pix->width;769	in_h = pix->height;770	v4l_bound_align_image(&pix->width,771			      BDISP_MIN_W, BDISP_MAX_W,772			      ffs(format->w_align) - 1,773			      &pix->height,774			      BDISP_MIN_H, BDISP_MAX_H,775			      ffs(format->h_align) - 1,776			      0);777	if ((pix->width != in_w) || (pix->height != in_h))778		dev_dbg(ctx->bdisp_dev->dev,779			"%s size updated: %dx%d -> %dx%d\n", __func__,780			in_w, in_h, pix->width, pix->height);781 782	pix->bytesperline = (pix->width * format->bpp_plane0) / 8;783	pix->sizeimage = (pix->width * pix->height * format->bpp) / 8;784 785	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)786		pix->colorspace = bdisp_dflt_fmt.colorspace;787 788	return 0;789}790 791static int bdisp_s_fmt(struct file *file, void *fh, struct v4l2_format *f)792{793	struct bdisp_ctx *ctx = fh_to_ctx(fh);794	struct vb2_queue *vq;795	struct bdisp_frame *frame;796	struct v4l2_pix_format *pix;797	int ret;798	u32 state;799 800	ret = bdisp_try_fmt(file, fh, f);801	if (ret) {802		dev_err(ctx->bdisp_dev->dev, "Cannot set format\n");803		return ret;804	}805 806	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);807	if (vb2_is_streaming(vq)) {808		dev_err(ctx->bdisp_dev->dev, "queue (%d) busy\n", f->type);809		return -EBUSY;810	}811 812	frame = (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ?813			&ctx->src : &ctx->dst;814	pix = &f->fmt.pix;815	frame->fmt = bdisp_find_fmt(pix->pixelformat);816	if (!frame->fmt) {817		dev_err(ctx->bdisp_dev->dev, "Unknown format 0x%x\n",818			pix->pixelformat);819		return -EINVAL;820	}821 822	frame->width = pix->width;823	frame->height = pix->height;824	frame->bytesperline = pix->bytesperline;825	frame->sizeimage = pix->sizeimage;826	frame->field = pix->field;827	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)828		frame->colorspace = pix->colorspace;829 830	frame->crop.width = frame->width;831	frame->crop.height = frame->height;832	frame->crop.left = 0;833	frame->crop.top = 0;834 835	state = BDISP_PARAMS;836	state |= (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ?837			BDISP_DST_FMT : BDISP_SRC_FMT;838	bdisp_ctx_state_lock_set(state, ctx);839 840	return 0;841}842 843static int bdisp_g_selection(struct file *file, void *fh,844			     struct v4l2_selection *s)845{846	struct bdisp_frame *frame;847	struct bdisp_ctx *ctx = fh_to_ctx(fh);848 849	frame = ctx_get_frame(ctx, s->type);850	if (IS_ERR(frame)) {851		dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame);852		return PTR_ERR(frame);853	}854 855	switch (s->type) {856	case V4L2_BUF_TYPE_VIDEO_OUTPUT:857		switch (s->target) {858		case V4L2_SEL_TGT_CROP:859			/* cropped frame */860			s->r = frame->crop;861			break;862		case V4L2_SEL_TGT_CROP_DEFAULT:863		case V4L2_SEL_TGT_CROP_BOUNDS:864			/* complete frame */865			s->r.left = 0;866			s->r.top = 0;867			s->r.width = frame->width;868			s->r.height = frame->height;869			break;870		default:871			dev_err(ctx->bdisp_dev->dev, "Invalid target\n");872			return -EINVAL;873		}874		break;875 876	case V4L2_BUF_TYPE_VIDEO_CAPTURE:877		switch (s->target) {878		case V4L2_SEL_TGT_COMPOSE:879		case V4L2_SEL_TGT_COMPOSE_PADDED:880			/* composed (cropped) frame */881			s->r = frame->crop;882			break;883		case V4L2_SEL_TGT_COMPOSE_DEFAULT:884		case V4L2_SEL_TGT_COMPOSE_BOUNDS:885			/* complete frame */886			s->r.left = 0;887			s->r.top = 0;888			s->r.width = frame->width;889			s->r.height = frame->height;890			break;891		default:892			dev_err(ctx->bdisp_dev->dev, "Invalid target\n");893			return -EINVAL;894		}895		break;896 897	default:898		dev_err(ctx->bdisp_dev->dev, "Invalid type\n");899		return -EINVAL;900	}901 902	return 0;903}904 905static int is_rect_enclosed(struct v4l2_rect *a, struct v4l2_rect *b)906{907	/* Return 1 if a is enclosed in b, or 0 otherwise. */908 909	if (a->left < b->left || a->top < b->top)910		return 0;911 912	if (a->left + a->width > b->left + b->width)913		return 0;914 915	if (a->top + a->height > b->top + b->height)916		return 0;917 918	return 1;919}920 921static int bdisp_s_selection(struct file *file, void *fh,922			     struct v4l2_selection *s)923{924	struct bdisp_frame *frame;925	struct bdisp_ctx *ctx = fh_to_ctx(fh);926	struct v4l2_rect *in, out;927	bool valid = false;928 929	if ((s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) &&930	    (s->target == V4L2_SEL_TGT_CROP))931		valid = true;932 933	if ((s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) &&934	    (s->target == V4L2_SEL_TGT_COMPOSE))935		valid = true;936 937	if (!valid) {938		dev_err(ctx->bdisp_dev->dev, "Invalid type / target\n");939		return -EINVAL;940	}941 942	frame = ctx_get_frame(ctx, s->type);943	if (IS_ERR(frame)) {944		dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame);945		return PTR_ERR(frame);946	}947 948	in = &s->r;949	out = *in;950 951	/* Align and check origin */952	out.left = ALIGN(in->left, frame->fmt->w_align);953	out.top = ALIGN(in->top, frame->fmt->h_align);954 955	if ((out.left < 0) || (out.left >= frame->width) ||956	    (out.top < 0) || (out.top >= frame->height)) {957		dev_err(ctx->bdisp_dev->dev,958			"Invalid crop: %dx%d@(%d,%d) vs frame: %dx%d\n",959			out.width, out.height, out.left, out.top,960			frame->width, frame->height);961		return -EINVAL;962	}963 964	/* Align and check size */965	out.width = ALIGN(in->width, frame->fmt->w_align);966	out.height = ALIGN(in->height, frame->fmt->w_align);967 968	if (((out.left + out.width) > frame->width) ||969	    ((out.top + out.height) > frame->height)) {970		dev_err(ctx->bdisp_dev->dev,971			"Invalid crop: %dx%d@(%d,%d) vs frame: %dx%d\n",972			out.width, out.height, out.left, out.top,973			frame->width, frame->height);974		return -EINVAL;975	}976 977	/* Checks adjust constraints flags */978	if (s->flags & V4L2_SEL_FLAG_LE && !is_rect_enclosed(&out, in))979		return -ERANGE;980 981	if (s->flags & V4L2_SEL_FLAG_GE && !is_rect_enclosed(in, &out))982		return -ERANGE;983 984	if ((out.left != in->left) || (out.top != in->top) ||985	    (out.width != in->width) || (out.height != in->height)) {986		dev_dbg(ctx->bdisp_dev->dev,987			"%s crop updated: %dx%d@(%d,%d) -> %dx%d@(%d,%d)\n",988			__func__, in->width, in->height, in->left, in->top,989			out.width, out.height, out.left, out.top);990		*in = out;991	}992 993	frame->crop = out;994 995	bdisp_ctx_state_lock_set(BDISP_PARAMS, ctx);996 997	return 0;998}999 1000static int bdisp_streamon(struct file *file, void *fh, enum v4l2_buf_type type)1001{1002	struct bdisp_ctx *ctx = fh_to_ctx(fh);1003 1004	if ((type == V4L2_BUF_TYPE_VIDEO_OUTPUT) &&1005	    !bdisp_ctx_state_is_set(BDISP_SRC_FMT, ctx)) {1006		dev_err(ctx->bdisp_dev->dev, "src not defined\n");1007		return -EINVAL;1008	}1009 1010	if ((type == V4L2_BUF_TYPE_VIDEO_CAPTURE) &&1011	    !bdisp_ctx_state_is_set(BDISP_DST_FMT, ctx)) {1012		dev_err(ctx->bdisp_dev->dev, "dst not defined\n");1013		return -EINVAL;1014	}1015 1016	return v4l2_m2m_streamon(file, ctx->fh.m2m_ctx, type);1017}1018 1019static const struct v4l2_ioctl_ops bdisp_ioctl_ops = {1020	.vidioc_querycap                = bdisp_querycap,1021	.vidioc_enum_fmt_vid_cap        = bdisp_enum_fmt,1022	.vidioc_enum_fmt_vid_out        = bdisp_enum_fmt,1023	.vidioc_g_fmt_vid_cap           = bdisp_g_fmt,1024	.vidioc_g_fmt_vid_out           = bdisp_g_fmt,1025	.vidioc_try_fmt_vid_cap         = bdisp_try_fmt,1026	.vidioc_try_fmt_vid_out         = bdisp_try_fmt,1027	.vidioc_s_fmt_vid_cap           = bdisp_s_fmt,1028	.vidioc_s_fmt_vid_out           = bdisp_s_fmt,1029	.vidioc_g_selection		= bdisp_g_selection,1030	.vidioc_s_selection		= bdisp_s_selection,1031	.vidioc_reqbufs                 = v4l2_m2m_ioctl_reqbufs,1032	.vidioc_create_bufs             = v4l2_m2m_ioctl_create_bufs,1033	.vidioc_expbuf                  = v4l2_m2m_ioctl_expbuf,1034	.vidioc_querybuf                = v4l2_m2m_ioctl_querybuf,1035	.vidioc_qbuf                    = v4l2_m2m_ioctl_qbuf,1036	.vidioc_dqbuf                   = v4l2_m2m_ioctl_dqbuf,1037	.vidioc_streamon                = bdisp_streamon,1038	.vidioc_streamoff               = v4l2_m2m_ioctl_streamoff,1039	.vidioc_subscribe_event         = v4l2_ctrl_subscribe_event,1040	.vidioc_unsubscribe_event       = v4l2_event_unsubscribe,1041};1042 1043static int bdisp_register_device(struct bdisp_dev *bdisp)1044{1045	int ret;1046 1047	if (!bdisp)1048		return -ENODEV;1049 1050	bdisp->vdev.fops        = &bdisp_fops;1051	bdisp->vdev.ioctl_ops   = &bdisp_ioctl_ops;1052	bdisp->vdev.release     = video_device_release_empty;1053	bdisp->vdev.lock        = &bdisp->lock;1054	bdisp->vdev.vfl_dir     = VFL_DIR_M2M;1055	bdisp->vdev.v4l2_dev    = &bdisp->v4l2_dev;1056	bdisp->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M;1057	snprintf(bdisp->vdev.name, sizeof(bdisp->vdev.name), "%s.%d",1058		 BDISP_NAME, bdisp->id);1059 1060	video_set_drvdata(&bdisp->vdev, bdisp);1061 1062	bdisp->m2m.vdev = &bdisp->vdev;1063	bdisp->m2m.m2m_dev = v4l2_m2m_init(&bdisp_m2m_ops);1064	if (IS_ERR(bdisp->m2m.m2m_dev)) {1065		dev_err(bdisp->dev, "failed to initialize v4l2-m2m device\n");1066		return PTR_ERR(bdisp->m2m.m2m_dev);1067	}1068 1069	ret = video_register_device(&bdisp->vdev, VFL_TYPE_VIDEO, -1);1070	if (ret) {1071		dev_err(bdisp->dev,1072			"%s(): failed to register video device\n", __func__);1073		v4l2_m2m_release(bdisp->m2m.m2m_dev);1074		return ret;1075	}1076 1077	return 0;1078}1079 1080static void bdisp_unregister_device(struct bdisp_dev *bdisp)1081{1082	if (!bdisp)1083		return;1084 1085	if (bdisp->m2m.m2m_dev)1086		v4l2_m2m_release(bdisp->m2m.m2m_dev);1087 1088	video_unregister_device(bdisp->m2m.vdev);1089}1090 1091static irqreturn_t bdisp_irq_thread(int irq, void *priv)1092{1093	struct bdisp_dev *bdisp = priv;1094	struct bdisp_ctx *ctx;1095 1096	spin_lock(&bdisp->slock);1097 1098	bdisp_dbg_perf_end(bdisp);1099 1100	cancel_delayed_work(&bdisp->timeout_work);1101 1102	if (!test_and_clear_bit(ST_M2M_RUNNING, &bdisp->state))1103		goto isr_unlock;1104 1105	if (test_and_clear_bit(ST_M2M_SUSPENDING, &bdisp->state)) {1106		set_bit(ST_M2M_SUSPENDED, &bdisp->state);1107		wake_up(&bdisp->irq_queue);1108		goto isr_unlock;1109	}1110 1111	ctx = v4l2_m2m_get_curr_priv(bdisp->m2m.m2m_dev);1112	if (!ctx || !ctx->fh.m2m_ctx)1113		goto isr_unlock;1114 1115	spin_unlock(&bdisp->slock);1116 1117	bdisp_job_finish(ctx, VB2_BUF_STATE_DONE);1118 1119	if (bdisp_ctx_state_is_set(BDISP_CTX_STOP_REQ, ctx)) {1120		bdisp_ctx_state_lock_clear(BDISP_CTX_STOP_REQ, ctx);1121		wake_up(&bdisp->irq_queue);1122	}1123 1124	return IRQ_HANDLED;1125 1126isr_unlock:1127	spin_unlock(&bdisp->slock);1128 1129	return IRQ_HANDLED;1130}1131 1132static irqreturn_t bdisp_irq_handler(int irq, void *priv)1133{1134	if (bdisp_hw_get_and_clear_irq((struct bdisp_dev *)priv))1135		return IRQ_NONE;1136	else1137		return IRQ_WAKE_THREAD;1138}1139 1140static void bdisp_irq_timeout(struct work_struct *ptr)1141{1142	struct delayed_work *twork = to_delayed_work(ptr);1143	struct bdisp_dev *bdisp = container_of(twork, struct bdisp_dev,1144			timeout_work);1145	struct bdisp_ctx *ctx;1146 1147	ctx = v4l2_m2m_get_curr_priv(bdisp->m2m.m2m_dev);1148 1149	dev_err(ctx->bdisp_dev->dev, "Device work timeout\n");1150 1151	spin_lock(&bdisp->slock);1152	clear_bit(ST_M2M_RUNNING, &bdisp->state);1153	spin_unlock(&bdisp->slock);1154 1155	bdisp_hw_reset(bdisp);1156 1157	bdisp_job_finish(ctx, VB2_BUF_STATE_ERROR);1158}1159 1160static int bdisp_m2m_suspend(struct bdisp_dev *bdisp)1161{1162	unsigned long flags;1163	long time_left;1164 1165	spin_lock_irqsave(&bdisp->slock, flags);1166	if (!test_bit(ST_M2M_RUNNING, &bdisp->state)) {1167		spin_unlock_irqrestore(&bdisp->slock, flags);1168		return 0;1169	}1170	clear_bit(ST_M2M_SUSPENDED, &bdisp->state);1171	set_bit(ST_M2M_SUSPENDING, &bdisp->state);1172	spin_unlock_irqrestore(&bdisp->slock, flags);1173 1174	time_left = wait_event_timeout(bdisp->irq_queue,1175				       test_bit(ST_M2M_SUSPENDED, &bdisp->state),1176				       BDISP_WORK_TIMEOUT);1177 1178	clear_bit(ST_M2M_SUSPENDING, &bdisp->state);1179 1180	if (!time_left) {1181		dev_err(bdisp->dev, "%s IRQ timeout\n", __func__);1182		return -EAGAIN;1183	}1184 1185	return 0;1186}1187 1188static int bdisp_m2m_resume(struct bdisp_dev *bdisp)1189{1190	struct bdisp_ctx *ctx;1191	unsigned long flags;1192 1193	spin_lock_irqsave(&bdisp->slock, flags);1194	ctx = bdisp->m2m.ctx;1195	bdisp->m2m.ctx = NULL;1196	spin_unlock_irqrestore(&bdisp->slock, flags);1197 1198	if (test_and_clear_bit(ST_M2M_SUSPENDED, &bdisp->state))1199		bdisp_job_finish(ctx, VB2_BUF_STATE_ERROR);1200 1201	return 0;1202}1203 1204static int bdisp_runtime_resume(struct device *dev)1205{1206	struct bdisp_dev *bdisp = dev_get_drvdata(dev);1207	int ret = clk_enable(bdisp->clock);1208 1209	if (ret)1210		return ret;1211 1212	return bdisp_m2m_resume(bdisp);1213}1214 1215static int bdisp_runtime_suspend(struct device *dev)1216{1217	struct bdisp_dev *bdisp = dev_get_drvdata(dev);1218	int ret = bdisp_m2m_suspend(bdisp);1219 1220	if (!ret)1221		clk_disable(bdisp->clock);1222 1223	return ret;1224}1225 1226static int bdisp_resume(struct device *dev)1227{1228	struct bdisp_dev *bdisp = dev_get_drvdata(dev);1229	unsigned long flags;1230	int opened;1231 1232	spin_lock_irqsave(&bdisp->slock, flags);1233	opened = test_bit(ST_M2M_OPEN, &bdisp->state);1234	spin_unlock_irqrestore(&bdisp->slock, flags);1235 1236	if (!opened)1237		return 0;1238 1239	if (!pm_runtime_suspended(dev))1240		return bdisp_runtime_resume(dev);1241 1242	return 0;1243}1244 1245static int bdisp_suspend(struct device *dev)1246{1247	if (!pm_runtime_suspended(dev))1248		return bdisp_runtime_suspend(dev);1249 1250	return 0;1251}1252 1253static const struct dev_pm_ops bdisp_pm_ops = {1254	.suspend                = bdisp_suspend,1255	.resume                 = bdisp_resume,1256	.runtime_suspend        = bdisp_runtime_suspend,1257	.runtime_resume         = bdisp_runtime_resume,1258};1259 1260static void bdisp_remove(struct platform_device *pdev)1261{1262	struct bdisp_dev *bdisp = platform_get_drvdata(pdev);1263 1264	bdisp_unregister_device(bdisp);1265 1266	bdisp_hw_free_filters(bdisp->dev);1267 1268	pm_runtime_disable(&pdev->dev);1269 1270	bdisp_debugfs_remove(bdisp);1271 1272	v4l2_device_unregister(&bdisp->v4l2_dev);1273 1274	if (!IS_ERR(bdisp->clock))1275		clk_unprepare(bdisp->clock);1276 1277	destroy_workqueue(bdisp->work_queue);1278 1279	dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name);1280}1281 1282static int bdisp_probe(struct platform_device *pdev)1283{1284	struct bdisp_dev *bdisp;1285	struct device *dev = &pdev->dev;1286	int ret;1287 1288	dev_dbg(dev, "%s\n", __func__);1289 1290	bdisp = devm_kzalloc(dev, sizeof(struct bdisp_dev), GFP_KERNEL);1291	if (!bdisp)1292		return -ENOMEM;1293 1294	ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));1295	if (ret)1296		return ret;1297 1298	bdisp->pdev = pdev;1299	bdisp->dev = dev;1300	platform_set_drvdata(pdev, bdisp);1301 1302	if (dev->of_node)1303		bdisp->id = of_alias_get_id(pdev->dev.of_node, BDISP_NAME);1304	else1305		bdisp->id = pdev->id;1306 1307	init_waitqueue_head(&bdisp->irq_queue);1308	INIT_DELAYED_WORK(&bdisp->timeout_work, bdisp_irq_timeout);1309	bdisp->work_queue = create_workqueue(BDISP_NAME);1310	if (!bdisp->work_queue)1311		return -ENOMEM;1312 1313	spin_lock_init(&bdisp->slock);1314	mutex_init(&bdisp->lock);1315 1316	/* get resources */1317	bdisp->regs = devm_platform_ioremap_resource(pdev, 0);1318	if (IS_ERR(bdisp->regs)) {1319		ret = PTR_ERR(bdisp->regs);1320		goto err_wq;1321	}1322 1323	bdisp->clock = devm_clk_get(dev, BDISP_NAME);1324	if (IS_ERR(bdisp->clock)) {1325		dev_err(dev, "failed to get clock\n");1326		ret = PTR_ERR(bdisp->clock);1327		goto err_wq;1328	}1329 1330	ret = clk_prepare(bdisp->clock);1331	if (ret < 0) {1332		dev_err(dev, "clock prepare failed\n");1333		bdisp->clock = ERR_PTR(-EINVAL);1334		goto err_wq;1335	}1336 1337	ret = platform_get_irq(pdev, 0);1338	if (ret < 0)1339		goto err_clk;1340 1341	ret = devm_request_threaded_irq(dev, ret, bdisp_irq_handler,1342					bdisp_irq_thread, IRQF_ONESHOT,1343					pdev->name, bdisp);1344	if (ret) {1345		dev_err(dev, "failed to install irq\n");1346		goto err_clk;1347	}1348 1349	/* v4l2 register */1350	ret = v4l2_device_register(dev, &bdisp->v4l2_dev);1351	if (ret) {1352		dev_err(dev, "failed to register\n");1353		goto err_clk;1354	}1355 1356	/* Debug */1357	bdisp_debugfs_create(bdisp);1358 1359	/* Power management */1360	pm_runtime_enable(dev);1361	ret = pm_runtime_resume_and_get(dev);1362	if (ret < 0) {1363		dev_err(dev, "failed to set PM\n");1364		goto err_remove;1365	}1366 1367	/* Filters */1368	if (bdisp_hw_alloc_filters(bdisp->dev)) {1369		dev_err(bdisp->dev, "no memory for filters\n");1370		ret = -ENOMEM;1371		goto err_pm;1372	}1373 1374	/* Register */1375	ret = bdisp_register_device(bdisp);1376	if (ret) {1377		dev_err(dev, "failed to register\n");1378		goto err_filter;1379	}1380 1381	dev_info(dev, "%s%d registered as /dev/video%d\n", BDISP_NAME,1382		 bdisp->id, bdisp->vdev.num);1383 1384	pm_runtime_put(dev);1385 1386	return 0;1387 1388err_filter:1389	bdisp_hw_free_filters(bdisp->dev);1390err_pm:1391	pm_runtime_put(dev);1392err_remove:1393	pm_runtime_disable(dev);1394	bdisp_debugfs_remove(bdisp);1395	v4l2_device_unregister(&bdisp->v4l2_dev);1396err_clk:1397	clk_unprepare(bdisp->clock);1398err_wq:1399	destroy_workqueue(bdisp->work_queue);1400	return ret;1401}1402 1403static const struct of_device_id bdisp_match_types[] = {1404	{1405		.compatible = "st,stih407-bdisp",1406	},1407	{ /* end node */ }1408};1409 1410MODULE_DEVICE_TABLE(of, bdisp_match_types);1411 1412static struct platform_driver bdisp_driver = {1413	.probe          = bdisp_probe,1414	.remove_new     = bdisp_remove,1415	.driver         = {1416		.name           = BDISP_NAME,1417		.of_match_table = bdisp_match_types,1418		.pm             = &bdisp_pm_ops,1419	},1420};1421 1422module_platform_driver(bdisp_driver);1423 1424MODULE_DESCRIPTION("2D blitter for STMicroelectronics SoC");1425MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>");1426MODULE_LICENSE("GPL");1427