18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) STMicroelectronics SA 2014 48c2ecf20Sopenharmony_ci * Authors: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/errno.h> 88c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 168c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "bdisp.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define BDISP_MAX_CTRL_NUM 10 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define BDISP_WORK_TIMEOUT ((100 * HZ) / 1000) 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* User configuration change */ 258c2ecf20Sopenharmony_ci#define BDISP_PARAMS BIT(0) /* Config updated */ 268c2ecf20Sopenharmony_ci#define BDISP_SRC_FMT BIT(1) /* Source set */ 278c2ecf20Sopenharmony_ci#define BDISP_DST_FMT BIT(2) /* Destination set */ 288c2ecf20Sopenharmony_ci#define BDISP_CTX_STOP_REQ BIT(3) /* Stop request */ 298c2ecf20Sopenharmony_ci#define BDISP_CTX_ABORT BIT(4) /* Abort while device run */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define BDISP_MIN_W 1 328c2ecf20Sopenharmony_ci#define BDISP_MAX_W 8191 338c2ecf20Sopenharmony_ci#define BDISP_MIN_H 1 348c2ecf20Sopenharmony_ci#define BDISP_MAX_H 8191 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define fh_to_ctx(__fh) container_of(__fh, struct bdisp_ctx, fh) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cienum bdisp_dev_flags { 398c2ecf20Sopenharmony_ci ST_M2M_OPEN, /* Driver opened */ 408c2ecf20Sopenharmony_ci ST_M2M_RUNNING, /* HW device running */ 418c2ecf20Sopenharmony_ci ST_M2M_SUSPENDED, /* Driver suspended */ 428c2ecf20Sopenharmony_ci ST_M2M_SUSPENDING, /* Driver being suspended */ 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic const struct bdisp_fmt bdisp_formats[] = { 468c2ecf20Sopenharmony_ci /* ARGB888. [31:0] A:R:G:B 8:8:8:8 little endian */ 478c2ecf20Sopenharmony_ci { 488c2ecf20Sopenharmony_ci .pixelformat = V4L2_PIX_FMT_ABGR32, /* is actually ARGB */ 498c2ecf20Sopenharmony_ci .nb_planes = 1, 508c2ecf20Sopenharmony_ci .bpp = 32, 518c2ecf20Sopenharmony_ci .bpp_plane0 = 32, 528c2ecf20Sopenharmony_ci .w_align = 1, 538c2ecf20Sopenharmony_ci .h_align = 1 548c2ecf20Sopenharmony_ci }, 558c2ecf20Sopenharmony_ci /* XRGB888. [31:0] x:R:G:B 8:8:8:8 little endian */ 568c2ecf20Sopenharmony_ci { 578c2ecf20Sopenharmony_ci .pixelformat = V4L2_PIX_FMT_XBGR32, /* is actually xRGB */ 588c2ecf20Sopenharmony_ci .nb_planes = 1, 598c2ecf20Sopenharmony_ci .bpp = 32, 608c2ecf20Sopenharmony_ci .bpp_plane0 = 32, 618c2ecf20Sopenharmony_ci .w_align = 1, 628c2ecf20Sopenharmony_ci .h_align = 1 638c2ecf20Sopenharmony_ci }, 648c2ecf20Sopenharmony_ci /* RGB565. [15:0] R:G:B 5:6:5 little endian */ 658c2ecf20Sopenharmony_ci { 668c2ecf20Sopenharmony_ci .pixelformat = V4L2_PIX_FMT_RGB565, 678c2ecf20Sopenharmony_ci .nb_planes = 1, 688c2ecf20Sopenharmony_ci .bpp = 16, 698c2ecf20Sopenharmony_ci .bpp_plane0 = 16, 708c2ecf20Sopenharmony_ci .w_align = 1, 718c2ecf20Sopenharmony_ci .h_align = 1 728c2ecf20Sopenharmony_ci }, 738c2ecf20Sopenharmony_ci /* NV12. YUV420SP - 1 plane for Y + 1 plane for (CbCr) */ 748c2ecf20Sopenharmony_ci { 758c2ecf20Sopenharmony_ci .pixelformat = V4L2_PIX_FMT_NV12, 768c2ecf20Sopenharmony_ci .nb_planes = 2, 778c2ecf20Sopenharmony_ci .bpp = 12, 788c2ecf20Sopenharmony_ci .bpp_plane0 = 8, 798c2ecf20Sopenharmony_ci .w_align = 2, 808c2ecf20Sopenharmony_ci .h_align = 2 818c2ecf20Sopenharmony_ci }, 828c2ecf20Sopenharmony_ci /* RGB888. [23:0] B:G:R 8:8:8 little endian */ 838c2ecf20Sopenharmony_ci { 848c2ecf20Sopenharmony_ci .pixelformat = V4L2_PIX_FMT_RGB24, 858c2ecf20Sopenharmony_ci .nb_planes = 1, 868c2ecf20Sopenharmony_ci .bpp = 24, 878c2ecf20Sopenharmony_ci .bpp_plane0 = 24, 888c2ecf20Sopenharmony_ci .w_align = 1, 898c2ecf20Sopenharmony_ci .h_align = 1 908c2ecf20Sopenharmony_ci }, 918c2ecf20Sopenharmony_ci /* YU12. YUV420P - 1 plane for Y + 1 plane for Cb + 1 plane for Cr 928c2ecf20Sopenharmony_ci * To keep as the LAST element of this table (no support on capture) 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ci { 958c2ecf20Sopenharmony_ci .pixelformat = V4L2_PIX_FMT_YUV420, 968c2ecf20Sopenharmony_ci .nb_planes = 3, 978c2ecf20Sopenharmony_ci .bpp = 12, 988c2ecf20Sopenharmony_ci .bpp_plane0 = 8, 998c2ecf20Sopenharmony_ci .w_align = 2, 1008c2ecf20Sopenharmony_ci .h_align = 2 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* Default format : HD ARGB32*/ 1058c2ecf20Sopenharmony_ci#define BDISP_DEF_WIDTH 1920 1068c2ecf20Sopenharmony_ci#define BDISP_DEF_HEIGHT 1080 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic const struct bdisp_frame bdisp_dflt_fmt = { 1098c2ecf20Sopenharmony_ci .width = BDISP_DEF_WIDTH, 1108c2ecf20Sopenharmony_ci .height = BDISP_DEF_HEIGHT, 1118c2ecf20Sopenharmony_ci .fmt = &bdisp_formats[0], 1128c2ecf20Sopenharmony_ci .field = V4L2_FIELD_NONE, 1138c2ecf20Sopenharmony_ci .bytesperline = BDISP_DEF_WIDTH * 4, 1148c2ecf20Sopenharmony_ci .sizeimage = BDISP_DEF_WIDTH * BDISP_DEF_HEIGHT * 4, 1158c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_REC709, 1168c2ecf20Sopenharmony_ci .crop = {0, 0, BDISP_DEF_WIDTH, BDISP_DEF_HEIGHT}, 1178c2ecf20Sopenharmony_ci .paddr = {0, 0, 0, 0} 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic inline void bdisp_ctx_state_lock_set(u32 state, struct bdisp_ctx *ctx) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci unsigned long flags; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctx->bdisp_dev->slock, flags); 1258c2ecf20Sopenharmony_ci ctx->state |= state; 1268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctx->bdisp_dev->slock, flags); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic inline void bdisp_ctx_state_lock_clear(u32 state, struct bdisp_ctx *ctx) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci unsigned long flags; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctx->bdisp_dev->slock, flags); 1348c2ecf20Sopenharmony_ci ctx->state &= ~state; 1358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctx->bdisp_dev->slock, flags); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic inline bool bdisp_ctx_state_is_set(u32 mask, struct bdisp_ctx *ctx) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci unsigned long flags; 1418c2ecf20Sopenharmony_ci bool ret; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctx->bdisp_dev->slock, flags); 1448c2ecf20Sopenharmony_ci ret = (ctx->state & mask) == mask; 1458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctx->bdisp_dev->slock, flags); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return ret; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic const struct bdisp_fmt *bdisp_find_fmt(u32 pixelformat) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci const struct bdisp_fmt *fmt; 1538c2ecf20Sopenharmony_ci unsigned int i; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(bdisp_formats); i++) { 1568c2ecf20Sopenharmony_ci fmt = &bdisp_formats[i]; 1578c2ecf20Sopenharmony_ci if (fmt->pixelformat == pixelformat) 1588c2ecf20Sopenharmony_ci return fmt; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return NULL; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic struct bdisp_frame *ctx_get_frame(struct bdisp_ctx *ctx, 1658c2ecf20Sopenharmony_ci enum v4l2_buf_type type) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci switch (type) { 1688c2ecf20Sopenharmony_ci case V4L2_BUF_TYPE_VIDEO_OUTPUT: 1698c2ecf20Sopenharmony_ci return &ctx->src; 1708c2ecf20Sopenharmony_ci case V4L2_BUF_TYPE_VIDEO_CAPTURE: 1718c2ecf20Sopenharmony_ci return &ctx->dst; 1728c2ecf20Sopenharmony_ci default: 1738c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, 1748c2ecf20Sopenharmony_ci "Wrong buffer/video queue type (%d)\n", type); 1758c2ecf20Sopenharmony_ci break; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic void bdisp_job_finish(struct bdisp_ctx *ctx, int vb_state) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *src_vb, *dst_vb; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (WARN(!ctx || !ctx->fh.m2m_ctx, "Null hardware context\n")) 1868c2ecf20Sopenharmony_ci return; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci dev_dbg(ctx->bdisp_dev->dev, "%s\n", __func__); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 1918c2ecf20Sopenharmony_ci dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (src_vb && dst_vb) { 1948c2ecf20Sopenharmony_ci dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp; 1958c2ecf20Sopenharmony_ci dst_vb->timecode = src_vb->timecode; 1968c2ecf20Sopenharmony_ci dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; 1978c2ecf20Sopenharmony_ci dst_vb->flags |= src_vb->flags & 1988c2ecf20Sopenharmony_ci V4L2_BUF_FLAG_TSTAMP_SRC_MASK; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci v4l2_m2m_buf_done(src_vb, vb_state); 2018c2ecf20Sopenharmony_ci v4l2_m2m_buf_done(dst_vb, vb_state); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci v4l2_m2m_job_finish(ctx->bdisp_dev->m2m.m2m_dev, 2048c2ecf20Sopenharmony_ci ctx->fh.m2m_ctx); 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int bdisp_ctx_stop_req(struct bdisp_ctx *ctx) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct bdisp_ctx *curr_ctx; 2118c2ecf20Sopenharmony_ci struct bdisp_dev *bdisp = ctx->bdisp_dev; 2128c2ecf20Sopenharmony_ci int ret; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci dev_dbg(ctx->bdisp_dev->dev, "%s\n", __func__); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci cancel_delayed_work(&bdisp->timeout_work); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci curr_ctx = v4l2_m2m_get_curr_priv(bdisp->m2m.m2m_dev); 2198c2ecf20Sopenharmony_ci if (!test_bit(ST_M2M_RUNNING, &bdisp->state) || (curr_ctx != ctx)) 2208c2ecf20Sopenharmony_ci return 0; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci bdisp_ctx_state_lock_set(BDISP_CTX_STOP_REQ, ctx); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci ret = wait_event_timeout(bdisp->irq_queue, 2258c2ecf20Sopenharmony_ci !bdisp_ctx_state_is_set(BDISP_CTX_STOP_REQ, ctx), 2268c2ecf20Sopenharmony_ci BDISP_WORK_TIMEOUT); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (!ret) { 2298c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, "%s IRQ timeout\n", __func__); 2308c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic void __bdisp_job_abort(struct bdisp_ctx *ctx) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci int ret; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci ret = bdisp_ctx_stop_req(ctx); 2418c2ecf20Sopenharmony_ci if ((ret == -ETIMEDOUT) || (ctx->state & BDISP_CTX_ABORT)) { 2428c2ecf20Sopenharmony_ci bdisp_ctx_state_lock_clear(BDISP_CTX_STOP_REQ | BDISP_CTX_ABORT, 2438c2ecf20Sopenharmony_ci ctx); 2448c2ecf20Sopenharmony_ci bdisp_job_finish(ctx, VB2_BUF_STATE_ERROR); 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic void bdisp_job_abort(void *priv) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci __bdisp_job_abort((struct bdisp_ctx *)priv); 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int bdisp_get_addr(struct bdisp_ctx *ctx, struct vb2_buffer *vb, 2548c2ecf20Sopenharmony_ci struct bdisp_frame *frame, dma_addr_t *paddr) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci if (!vb || !frame) 2578c2ecf20Sopenharmony_ci return -EINVAL; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci paddr[0] = vb2_dma_contig_plane_dma_addr(vb, 0); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (frame->fmt->nb_planes > 1) 2628c2ecf20Sopenharmony_ci /* UV (NV12) or U (420P) */ 2638c2ecf20Sopenharmony_ci paddr[1] = (dma_addr_t)(paddr[0] + 2648c2ecf20Sopenharmony_ci frame->bytesperline * frame->height); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (frame->fmt->nb_planes > 2) 2678c2ecf20Sopenharmony_ci /* V (420P) */ 2688c2ecf20Sopenharmony_ci paddr[2] = (dma_addr_t)(paddr[1] + 2698c2ecf20Sopenharmony_ci (frame->bytesperline * frame->height) / 4); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (frame->fmt->nb_planes > 3) 2728c2ecf20Sopenharmony_ci dev_dbg(ctx->bdisp_dev->dev, "ignoring some planes\n"); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci dev_dbg(ctx->bdisp_dev->dev, 2758c2ecf20Sopenharmony_ci "%s plane[0]=%pad plane[1]=%pad plane[2]=%pad\n", 2768c2ecf20Sopenharmony_ci __func__, &paddr[0], &paddr[1], &paddr[2]); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return 0; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic int bdisp_get_bufs(struct bdisp_ctx *ctx) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct bdisp_frame *src, *dst; 2848c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *src_vb, *dst_vb; 2858c2ecf20Sopenharmony_ci int ret; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci src = &ctx->src; 2888c2ecf20Sopenharmony_ci dst = &ctx->dst; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci src_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 2918c2ecf20Sopenharmony_ci ret = bdisp_get_addr(ctx, &src_vb->vb2_buf, src, src->paddr); 2928c2ecf20Sopenharmony_ci if (ret) 2938c2ecf20Sopenharmony_ci return ret; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci dst_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); 2968c2ecf20Sopenharmony_ci ret = bdisp_get_addr(ctx, &dst_vb->vb2_buf, dst, dst->paddr); 2978c2ecf20Sopenharmony_ci if (ret) 2988c2ecf20Sopenharmony_ci return ret; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci return 0; 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic void bdisp_device_run(void *priv) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx = priv; 3088c2ecf20Sopenharmony_ci struct bdisp_dev *bdisp; 3098c2ecf20Sopenharmony_ci unsigned long flags; 3108c2ecf20Sopenharmony_ci int err = 0; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (WARN(!ctx, "Null hardware context\n")) 3138c2ecf20Sopenharmony_ci return; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci bdisp = ctx->bdisp_dev; 3168c2ecf20Sopenharmony_ci dev_dbg(bdisp->dev, "%s\n", __func__); 3178c2ecf20Sopenharmony_ci spin_lock_irqsave(&bdisp->slock, flags); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (bdisp->m2m.ctx != ctx) { 3208c2ecf20Sopenharmony_ci dev_dbg(bdisp->dev, "ctx updated: %p -> %p\n", 3218c2ecf20Sopenharmony_ci bdisp->m2m.ctx, ctx); 3228c2ecf20Sopenharmony_ci ctx->state |= BDISP_PARAMS; 3238c2ecf20Sopenharmony_ci bdisp->m2m.ctx = ctx; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (ctx->state & BDISP_CTX_STOP_REQ) { 3278c2ecf20Sopenharmony_ci ctx->state &= ~BDISP_CTX_STOP_REQ; 3288c2ecf20Sopenharmony_ci ctx->state |= BDISP_CTX_ABORT; 3298c2ecf20Sopenharmony_ci wake_up(&bdisp->irq_queue); 3308c2ecf20Sopenharmony_ci goto out; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci err = bdisp_get_bufs(ctx); 3348c2ecf20Sopenharmony_ci if (err) { 3358c2ecf20Sopenharmony_ci dev_err(bdisp->dev, "cannot get address\n"); 3368c2ecf20Sopenharmony_ci goto out; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci bdisp_dbg_perf_begin(bdisp); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci err = bdisp_hw_reset(bdisp); 3428c2ecf20Sopenharmony_ci if (err) { 3438c2ecf20Sopenharmony_ci dev_err(bdisp->dev, "could not get HW ready\n"); 3448c2ecf20Sopenharmony_ci goto out; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci err = bdisp_hw_update(ctx); 3488c2ecf20Sopenharmony_ci if (err) { 3498c2ecf20Sopenharmony_ci dev_err(bdisp->dev, "could not send HW request\n"); 3508c2ecf20Sopenharmony_ci goto out; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci queue_delayed_work(bdisp->work_queue, &bdisp->timeout_work, 3548c2ecf20Sopenharmony_ci BDISP_WORK_TIMEOUT); 3558c2ecf20Sopenharmony_ci set_bit(ST_M2M_RUNNING, &bdisp->state); 3568c2ecf20Sopenharmony_ciout: 3578c2ecf20Sopenharmony_ci ctx->state &= ~BDISP_PARAMS; 3588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bdisp->slock, flags); 3598c2ecf20Sopenharmony_ci if (err) 3608c2ecf20Sopenharmony_ci bdisp_job_finish(ctx, VB2_BUF_STATE_ERROR); 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic const struct v4l2_m2m_ops bdisp_m2m_ops = { 3648c2ecf20Sopenharmony_ci .device_run = bdisp_device_run, 3658c2ecf20Sopenharmony_ci .job_abort = bdisp_job_abort, 3668c2ecf20Sopenharmony_ci}; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic int __bdisp_s_ctrl(struct bdisp_ctx *ctx, struct v4l2_ctrl *ctrl) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) 3718c2ecf20Sopenharmony_ci return 0; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci switch (ctrl->id) { 3748c2ecf20Sopenharmony_ci case V4L2_CID_HFLIP: 3758c2ecf20Sopenharmony_ci ctx->hflip = ctrl->val; 3768c2ecf20Sopenharmony_ci break; 3778c2ecf20Sopenharmony_ci case V4L2_CID_VFLIP: 3788c2ecf20Sopenharmony_ci ctx->vflip = ctrl->val; 3798c2ecf20Sopenharmony_ci break; 3808c2ecf20Sopenharmony_ci default: 3818c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, "unknown control %d\n", ctrl->id); 3828c2ecf20Sopenharmony_ci return -EINVAL; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci ctx->state |= BDISP_PARAMS; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic int bdisp_s_ctrl(struct v4l2_ctrl *ctrl) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx = container_of(ctrl->handler, struct bdisp_ctx, 3938c2ecf20Sopenharmony_ci ctrl_handler); 3948c2ecf20Sopenharmony_ci unsigned long flags; 3958c2ecf20Sopenharmony_ci int ret; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctx->bdisp_dev->slock, flags); 3988c2ecf20Sopenharmony_ci ret = __bdisp_s_ctrl(ctx, ctrl); 3998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctx->bdisp_dev->slock, flags); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return ret; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops bdisp_c_ops = { 4058c2ecf20Sopenharmony_ci .s_ctrl = bdisp_s_ctrl, 4068c2ecf20Sopenharmony_ci}; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic int bdisp_ctrls_create(struct bdisp_ctx *ctx) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci if (ctx->ctrls_rdy) 4118c2ecf20Sopenharmony_ci return 0; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&ctx->ctrl_handler, BDISP_MAX_CTRL_NUM); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci ctx->bdisp_ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, 4168c2ecf20Sopenharmony_ci &bdisp_c_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); 4178c2ecf20Sopenharmony_ci ctx->bdisp_ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, 4188c2ecf20Sopenharmony_ci &bdisp_c_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (ctx->ctrl_handler.error) { 4218c2ecf20Sopenharmony_ci int err = ctx->ctrl_handler.error; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&ctx->ctrl_handler); 4248c2ecf20Sopenharmony_ci return err; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci ctx->ctrls_rdy = true; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic void bdisp_ctrls_delete(struct bdisp_ctx *ctx) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci if (ctx->ctrls_rdy) { 4358c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&ctx->ctrl_handler); 4368c2ecf20Sopenharmony_ci ctx->ctrls_rdy = false; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic int bdisp_queue_setup(struct vb2_queue *vq, 4418c2ecf20Sopenharmony_ci unsigned int *nb_buf, unsigned int *nb_planes, 4428c2ecf20Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx = vb2_get_drv_priv(vq); 4458c2ecf20Sopenharmony_ci struct bdisp_frame *frame = ctx_get_frame(ctx, vq->type); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (IS_ERR(frame)) { 4488c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame); 4498c2ecf20Sopenharmony_ci return PTR_ERR(frame); 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (!frame->fmt) { 4538c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, "Invalid format\n"); 4548c2ecf20Sopenharmony_ci return -EINVAL; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (*nb_planes) 4588c2ecf20Sopenharmony_ci return sizes[0] < frame->sizeimage ? -EINVAL : 0; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci *nb_planes = 1; 4618c2ecf20Sopenharmony_ci sizes[0] = frame->sizeimage; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci return 0; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic int bdisp_buf_prepare(struct vb2_buffer *vb) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 4698c2ecf20Sopenharmony_ci struct bdisp_frame *frame = ctx_get_frame(ctx, vb->vb2_queue->type); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (IS_ERR(frame)) { 4728c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame); 4738c2ecf20Sopenharmony_ci return PTR_ERR(frame); 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 4778c2ecf20Sopenharmony_ci vb2_set_plane_payload(vb, 0, frame->sizeimage); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci return 0; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic void bdisp_buf_queue(struct vb2_buffer *vb) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 4858c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* return to V4L2 any 0-size buffer so it can be dequeued by user */ 4888c2ecf20Sopenharmony_ci if (!vb2_get_plane_payload(vb, 0)) { 4898c2ecf20Sopenharmony_ci dev_dbg(ctx->bdisp_dev->dev, "0 data buffer, skip it\n"); 4908c2ecf20Sopenharmony_ci vb2_buffer_done(vb, VB2_BUF_STATE_DONE); 4918c2ecf20Sopenharmony_ci return; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (ctx->fh.m2m_ctx) 4958c2ecf20Sopenharmony_ci v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic int bdisp_start_streaming(struct vb2_queue *q, unsigned int count) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx = q->drv_priv; 5018c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *buf; 5028c2ecf20Sopenharmony_ci int ret = pm_runtime_resume_and_get(ctx->bdisp_dev->dev); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (ret < 0) { 5058c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, "failed to set runtime PM\n"); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { 5088c2ecf20Sopenharmony_ci while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) 5098c2ecf20Sopenharmony_ci v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); 5108c2ecf20Sopenharmony_ci } else { 5118c2ecf20Sopenharmony_ci while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) 5128c2ecf20Sopenharmony_ci v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci return ret; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return 0; 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic void bdisp_stop_streaming(struct vb2_queue *q) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx = q->drv_priv; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci __bdisp_job_abort(ctx); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci pm_runtime_put(ctx->bdisp_dev->dev); 5288c2ecf20Sopenharmony_ci} 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cistatic const struct vb2_ops bdisp_qops = { 5318c2ecf20Sopenharmony_ci .queue_setup = bdisp_queue_setup, 5328c2ecf20Sopenharmony_ci .buf_prepare = bdisp_buf_prepare, 5338c2ecf20Sopenharmony_ci .buf_queue = bdisp_buf_queue, 5348c2ecf20Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 5358c2ecf20Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 5368c2ecf20Sopenharmony_ci .stop_streaming = bdisp_stop_streaming, 5378c2ecf20Sopenharmony_ci .start_streaming = bdisp_start_streaming, 5388c2ecf20Sopenharmony_ci}; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic int queue_init(void *priv, 5418c2ecf20Sopenharmony_ci struct vb2_queue *src_vq, struct vb2_queue *dst_vq) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx = priv; 5448c2ecf20Sopenharmony_ci int ret; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci memset(src_vq, 0, sizeof(*src_vq)); 5478c2ecf20Sopenharmony_ci src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 5488c2ecf20Sopenharmony_ci src_vq->io_modes = VB2_MMAP | VB2_DMABUF; 5498c2ecf20Sopenharmony_ci src_vq->drv_priv = ctx; 5508c2ecf20Sopenharmony_ci src_vq->ops = &bdisp_qops; 5518c2ecf20Sopenharmony_ci src_vq->mem_ops = &vb2_dma_contig_memops; 5528c2ecf20Sopenharmony_ci src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 5538c2ecf20Sopenharmony_ci src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 5548c2ecf20Sopenharmony_ci src_vq->lock = &ctx->bdisp_dev->lock; 5558c2ecf20Sopenharmony_ci src_vq->dev = ctx->bdisp_dev->v4l2_dev.dev; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci ret = vb2_queue_init(src_vq); 5588c2ecf20Sopenharmony_ci if (ret) 5598c2ecf20Sopenharmony_ci return ret; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci memset(dst_vq, 0, sizeof(*dst_vq)); 5628c2ecf20Sopenharmony_ci dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 5638c2ecf20Sopenharmony_ci dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; 5648c2ecf20Sopenharmony_ci dst_vq->drv_priv = ctx; 5658c2ecf20Sopenharmony_ci dst_vq->ops = &bdisp_qops; 5668c2ecf20Sopenharmony_ci dst_vq->mem_ops = &vb2_dma_contig_memops; 5678c2ecf20Sopenharmony_ci dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 5688c2ecf20Sopenharmony_ci dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 5698c2ecf20Sopenharmony_ci dst_vq->lock = &ctx->bdisp_dev->lock; 5708c2ecf20Sopenharmony_ci dst_vq->dev = ctx->bdisp_dev->v4l2_dev.dev; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci return vb2_queue_init(dst_vq); 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic int bdisp_open(struct file *file) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci struct bdisp_dev *bdisp = video_drvdata(file); 5788c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx = NULL; 5798c2ecf20Sopenharmony_ci int ret; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&bdisp->lock)) 5828c2ecf20Sopenharmony_ci return -ERESTARTSYS; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* Allocate memory for both context and node */ 5858c2ecf20Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 5868c2ecf20Sopenharmony_ci if (!ctx) { 5878c2ecf20Sopenharmony_ci ret = -ENOMEM; 5888c2ecf20Sopenharmony_ci goto unlock; 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci ctx->bdisp_dev = bdisp; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci if (bdisp_hw_alloc_nodes(ctx)) { 5938c2ecf20Sopenharmony_ci dev_err(bdisp->dev, "no memory for nodes\n"); 5948c2ecf20Sopenharmony_ci ret = -ENOMEM; 5958c2ecf20Sopenharmony_ci goto mem_ctx; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci v4l2_fh_init(&ctx->fh, bdisp->m2m.vdev); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci ret = bdisp_ctrls_create(ctx); 6018c2ecf20Sopenharmony_ci if (ret) { 6028c2ecf20Sopenharmony_ci dev_err(bdisp->dev, "Failed to create control\n"); 6038c2ecf20Sopenharmony_ci goto error_fh; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci /* Use separate control handler per file handle */ 6078c2ecf20Sopenharmony_ci ctx->fh.ctrl_handler = &ctx->ctrl_handler; 6088c2ecf20Sopenharmony_ci file->private_data = &ctx->fh; 6098c2ecf20Sopenharmony_ci v4l2_fh_add(&ctx->fh); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* Default format */ 6128c2ecf20Sopenharmony_ci ctx->src = bdisp_dflt_fmt; 6138c2ecf20Sopenharmony_ci ctx->dst = bdisp_dflt_fmt; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci /* Setup the device context for mem2mem mode. */ 6168c2ecf20Sopenharmony_ci ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(bdisp->m2m.m2m_dev, ctx, 6178c2ecf20Sopenharmony_ci queue_init); 6188c2ecf20Sopenharmony_ci if (IS_ERR(ctx->fh.m2m_ctx)) { 6198c2ecf20Sopenharmony_ci dev_err(bdisp->dev, "Failed to initialize m2m context\n"); 6208c2ecf20Sopenharmony_ci ret = PTR_ERR(ctx->fh.m2m_ctx); 6218c2ecf20Sopenharmony_ci goto error_ctrls; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci bdisp->m2m.refcnt++; 6258c2ecf20Sopenharmony_ci set_bit(ST_M2M_OPEN, &bdisp->state); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci dev_dbg(bdisp->dev, "driver opened, ctx = 0x%p\n", ctx); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci mutex_unlock(&bdisp->lock); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci return 0; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cierror_ctrls: 6348c2ecf20Sopenharmony_ci bdisp_ctrls_delete(ctx); 6358c2ecf20Sopenharmony_ci v4l2_fh_del(&ctx->fh); 6368c2ecf20Sopenharmony_cierror_fh: 6378c2ecf20Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 6388c2ecf20Sopenharmony_ci bdisp_hw_free_nodes(ctx); 6398c2ecf20Sopenharmony_cimem_ctx: 6408c2ecf20Sopenharmony_ci kfree(ctx); 6418c2ecf20Sopenharmony_ciunlock: 6428c2ecf20Sopenharmony_ci mutex_unlock(&bdisp->lock); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci return ret; 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic int bdisp_release(struct file *file) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx = fh_to_ctx(file->private_data); 6508c2ecf20Sopenharmony_ci struct bdisp_dev *bdisp = ctx->bdisp_dev; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci dev_dbg(bdisp->dev, "%s\n", __func__); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci mutex_lock(&bdisp->lock); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci bdisp_ctrls_delete(ctx); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci v4l2_fh_del(&ctx->fh); 6618c2ecf20Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (--bdisp->m2m.refcnt <= 0) 6648c2ecf20Sopenharmony_ci clear_bit(ST_M2M_OPEN, &bdisp->state); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci bdisp_hw_free_nodes(ctx); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci kfree(ctx); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci mutex_unlock(&bdisp->lock); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci return 0; 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations bdisp_fops = { 6768c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6778c2ecf20Sopenharmony_ci .open = bdisp_open, 6788c2ecf20Sopenharmony_ci .release = bdisp_release, 6798c2ecf20Sopenharmony_ci .poll = v4l2_m2m_fop_poll, 6808c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 6818c2ecf20Sopenharmony_ci .mmap = v4l2_m2m_fop_mmap, 6828c2ecf20Sopenharmony_ci}; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_cistatic int bdisp_querycap(struct file *file, void *fh, 6858c2ecf20Sopenharmony_ci struct v4l2_capability *cap) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx = fh_to_ctx(fh); 6888c2ecf20Sopenharmony_ci struct bdisp_dev *bdisp = ctx->bdisp_dev; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci strscpy(cap->driver, bdisp->pdev->name, sizeof(cap->driver)); 6918c2ecf20Sopenharmony_ci strscpy(cap->card, bdisp->pdev->name, sizeof(cap->card)); 6928c2ecf20Sopenharmony_ci snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s%d", 6938c2ecf20Sopenharmony_ci BDISP_NAME, bdisp->id); 6948c2ecf20Sopenharmony_ci return 0; 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_cistatic int bdisp_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) 6988c2ecf20Sopenharmony_ci{ 6998c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx = fh_to_ctx(fh); 7008c2ecf20Sopenharmony_ci const struct bdisp_fmt *fmt; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci if (f->index >= ARRAY_SIZE(bdisp_formats)) 7038c2ecf20Sopenharmony_ci return -EINVAL; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci fmt = &bdisp_formats[f->index]; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if ((fmt->pixelformat == V4L2_PIX_FMT_YUV420) && 7088c2ecf20Sopenharmony_ci (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)) { 7098c2ecf20Sopenharmony_ci dev_dbg(ctx->bdisp_dev->dev, "No YU12 on capture\n"); 7108c2ecf20Sopenharmony_ci return -EINVAL; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci f->pixelformat = fmt->pixelformat; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci return 0; 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_cistatic int bdisp_g_fmt(struct file *file, void *fh, struct v4l2_format *f) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx = fh_to_ctx(fh); 7208c2ecf20Sopenharmony_ci struct v4l2_pix_format *pix; 7218c2ecf20Sopenharmony_ci struct bdisp_frame *frame = ctx_get_frame(ctx, f->type); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (IS_ERR(frame)) { 7248c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame); 7258c2ecf20Sopenharmony_ci return PTR_ERR(frame); 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci pix = &f->fmt.pix; 7298c2ecf20Sopenharmony_ci pix->width = frame->width; 7308c2ecf20Sopenharmony_ci pix->height = frame->height; 7318c2ecf20Sopenharmony_ci pix->pixelformat = frame->fmt->pixelformat; 7328c2ecf20Sopenharmony_ci pix->field = frame->field; 7338c2ecf20Sopenharmony_ci pix->bytesperline = frame->bytesperline; 7348c2ecf20Sopenharmony_ci pix->sizeimage = frame->sizeimage; 7358c2ecf20Sopenharmony_ci pix->colorspace = (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ? 7368c2ecf20Sopenharmony_ci frame->colorspace : bdisp_dflt_fmt.colorspace; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci return 0; 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic int bdisp_try_fmt(struct file *file, void *fh, struct v4l2_format *f) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx = fh_to_ctx(fh); 7448c2ecf20Sopenharmony_ci struct v4l2_pix_format *pix = &f->fmt.pix; 7458c2ecf20Sopenharmony_ci const struct bdisp_fmt *format; 7468c2ecf20Sopenharmony_ci u32 in_w, in_h; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci format = bdisp_find_fmt(pix->pixelformat); 7498c2ecf20Sopenharmony_ci if (!format) { 7508c2ecf20Sopenharmony_ci dev_dbg(ctx->bdisp_dev->dev, "Unknown format 0x%x\n", 7518c2ecf20Sopenharmony_ci pix->pixelformat); 7528c2ecf20Sopenharmony_ci return -EINVAL; 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci /* YUV420P only supported for VIDEO_OUTPUT */ 7568c2ecf20Sopenharmony_ci if ((format->pixelformat == V4L2_PIX_FMT_YUV420) && 7578c2ecf20Sopenharmony_ci (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)) { 7588c2ecf20Sopenharmony_ci dev_dbg(ctx->bdisp_dev->dev, "No YU12 on capture\n"); 7598c2ecf20Sopenharmony_ci return -EINVAL; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci /* Field (interlaced only supported on OUTPUT) */ 7638c2ecf20Sopenharmony_ci if ((f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) || 7648c2ecf20Sopenharmony_ci (pix->field != V4L2_FIELD_INTERLACED)) 7658c2ecf20Sopenharmony_ci pix->field = V4L2_FIELD_NONE; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci /* Adjust width & height */ 7688c2ecf20Sopenharmony_ci in_w = pix->width; 7698c2ecf20Sopenharmony_ci in_h = pix->height; 7708c2ecf20Sopenharmony_ci v4l_bound_align_image(&pix->width, 7718c2ecf20Sopenharmony_ci BDISP_MIN_W, BDISP_MAX_W, 7728c2ecf20Sopenharmony_ci ffs(format->w_align) - 1, 7738c2ecf20Sopenharmony_ci &pix->height, 7748c2ecf20Sopenharmony_ci BDISP_MIN_H, BDISP_MAX_H, 7758c2ecf20Sopenharmony_ci ffs(format->h_align) - 1, 7768c2ecf20Sopenharmony_ci 0); 7778c2ecf20Sopenharmony_ci if ((pix->width != in_w) || (pix->height != in_h)) 7788c2ecf20Sopenharmony_ci dev_dbg(ctx->bdisp_dev->dev, 7798c2ecf20Sopenharmony_ci "%s size updated: %dx%d -> %dx%d\n", __func__, 7808c2ecf20Sopenharmony_ci in_w, in_h, pix->width, pix->height); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci pix->bytesperline = (pix->width * format->bpp_plane0) / 8; 7838c2ecf20Sopenharmony_ci pix->sizeimage = (pix->width * pix->height * format->bpp) / 8; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 7868c2ecf20Sopenharmony_ci pix->colorspace = bdisp_dflt_fmt.colorspace; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci return 0; 7898c2ecf20Sopenharmony_ci} 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_cistatic int bdisp_s_fmt(struct file *file, void *fh, struct v4l2_format *f) 7928c2ecf20Sopenharmony_ci{ 7938c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx = fh_to_ctx(fh); 7948c2ecf20Sopenharmony_ci struct vb2_queue *vq; 7958c2ecf20Sopenharmony_ci struct bdisp_frame *frame; 7968c2ecf20Sopenharmony_ci struct v4l2_pix_format *pix; 7978c2ecf20Sopenharmony_ci int ret; 7988c2ecf20Sopenharmony_ci u32 state; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci ret = bdisp_try_fmt(file, fh, f); 8018c2ecf20Sopenharmony_ci if (ret) { 8028c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, "Cannot set format\n"); 8038c2ecf20Sopenharmony_ci return ret; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 8078c2ecf20Sopenharmony_ci if (vb2_is_streaming(vq)) { 8088c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, "queue (%d) busy\n", f->type); 8098c2ecf20Sopenharmony_ci return -EBUSY; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci frame = (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ? 8138c2ecf20Sopenharmony_ci &ctx->src : &ctx->dst; 8148c2ecf20Sopenharmony_ci pix = &f->fmt.pix; 8158c2ecf20Sopenharmony_ci frame->fmt = bdisp_find_fmt(pix->pixelformat); 8168c2ecf20Sopenharmony_ci if (!frame->fmt) { 8178c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, "Unknown format 0x%x\n", 8188c2ecf20Sopenharmony_ci pix->pixelformat); 8198c2ecf20Sopenharmony_ci return -EINVAL; 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci frame->width = pix->width; 8238c2ecf20Sopenharmony_ci frame->height = pix->height; 8248c2ecf20Sopenharmony_ci frame->bytesperline = pix->bytesperline; 8258c2ecf20Sopenharmony_ci frame->sizeimage = pix->sizeimage; 8268c2ecf20Sopenharmony_ci frame->field = pix->field; 8278c2ecf20Sopenharmony_ci if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 8288c2ecf20Sopenharmony_ci frame->colorspace = pix->colorspace; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci frame->crop.width = frame->width; 8318c2ecf20Sopenharmony_ci frame->crop.height = frame->height; 8328c2ecf20Sopenharmony_ci frame->crop.left = 0; 8338c2ecf20Sopenharmony_ci frame->crop.top = 0; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci state = BDISP_PARAMS; 8368c2ecf20Sopenharmony_ci state |= (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ? 8378c2ecf20Sopenharmony_ci BDISP_DST_FMT : BDISP_SRC_FMT; 8388c2ecf20Sopenharmony_ci bdisp_ctx_state_lock_set(state, ctx); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci return 0; 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_cistatic int bdisp_g_selection(struct file *file, void *fh, 8448c2ecf20Sopenharmony_ci struct v4l2_selection *s) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci struct bdisp_frame *frame; 8478c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx = fh_to_ctx(fh); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci frame = ctx_get_frame(ctx, s->type); 8508c2ecf20Sopenharmony_ci if (IS_ERR(frame)) { 8518c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame); 8528c2ecf20Sopenharmony_ci return PTR_ERR(frame); 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci switch (s->type) { 8568c2ecf20Sopenharmony_ci case V4L2_BUF_TYPE_VIDEO_OUTPUT: 8578c2ecf20Sopenharmony_ci switch (s->target) { 8588c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP: 8598c2ecf20Sopenharmony_ci /* cropped frame */ 8608c2ecf20Sopenharmony_ci s->r = frame->crop; 8618c2ecf20Sopenharmony_ci break; 8628c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 8638c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 8648c2ecf20Sopenharmony_ci /* complete frame */ 8658c2ecf20Sopenharmony_ci s->r.left = 0; 8668c2ecf20Sopenharmony_ci s->r.top = 0; 8678c2ecf20Sopenharmony_ci s->r.width = frame->width; 8688c2ecf20Sopenharmony_ci s->r.height = frame->height; 8698c2ecf20Sopenharmony_ci break; 8708c2ecf20Sopenharmony_ci default: 8718c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, "Invalid target\n"); 8728c2ecf20Sopenharmony_ci return -EINVAL; 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci break; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci case V4L2_BUF_TYPE_VIDEO_CAPTURE: 8778c2ecf20Sopenharmony_ci switch (s->target) { 8788c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 8798c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_PADDED: 8808c2ecf20Sopenharmony_ci /* composed (cropped) frame */ 8818c2ecf20Sopenharmony_ci s->r = frame->crop; 8828c2ecf20Sopenharmony_ci break; 8838c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 8848c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 8858c2ecf20Sopenharmony_ci /* complete frame */ 8868c2ecf20Sopenharmony_ci s->r.left = 0; 8878c2ecf20Sopenharmony_ci s->r.top = 0; 8888c2ecf20Sopenharmony_ci s->r.width = frame->width; 8898c2ecf20Sopenharmony_ci s->r.height = frame->height; 8908c2ecf20Sopenharmony_ci break; 8918c2ecf20Sopenharmony_ci default: 8928c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, "Invalid target\n"); 8938c2ecf20Sopenharmony_ci return -EINVAL; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci break; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci default: 8988c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, "Invalid type\n"); 8998c2ecf20Sopenharmony_ci return -EINVAL; 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci return 0; 9038c2ecf20Sopenharmony_ci} 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_cistatic int is_rect_enclosed(struct v4l2_rect *a, struct v4l2_rect *b) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci /* Return 1 if a is enclosed in b, or 0 otherwise. */ 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci if (a->left < b->left || a->top < b->top) 9108c2ecf20Sopenharmony_ci return 0; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci if (a->left + a->width > b->left + b->width) 9138c2ecf20Sopenharmony_ci return 0; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci if (a->top + a->height > b->top + b->height) 9168c2ecf20Sopenharmony_ci return 0; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci return 1; 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_cistatic int bdisp_s_selection(struct file *file, void *fh, 9228c2ecf20Sopenharmony_ci struct v4l2_selection *s) 9238c2ecf20Sopenharmony_ci{ 9248c2ecf20Sopenharmony_ci struct bdisp_frame *frame; 9258c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx = fh_to_ctx(fh); 9268c2ecf20Sopenharmony_ci struct v4l2_rect *in, out; 9278c2ecf20Sopenharmony_ci bool valid = false; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci if ((s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) && 9308c2ecf20Sopenharmony_ci (s->target == V4L2_SEL_TGT_CROP)) 9318c2ecf20Sopenharmony_ci valid = true; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci if ((s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) && 9348c2ecf20Sopenharmony_ci (s->target == V4L2_SEL_TGT_COMPOSE)) 9358c2ecf20Sopenharmony_ci valid = true; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (!valid) { 9388c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, "Invalid type / target\n"); 9398c2ecf20Sopenharmony_ci return -EINVAL; 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci frame = ctx_get_frame(ctx, s->type); 9438c2ecf20Sopenharmony_ci if (IS_ERR(frame)) { 9448c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame); 9458c2ecf20Sopenharmony_ci return PTR_ERR(frame); 9468c2ecf20Sopenharmony_ci } 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci in = &s->r; 9498c2ecf20Sopenharmony_ci out = *in; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci /* Align and check origin */ 9528c2ecf20Sopenharmony_ci out.left = ALIGN(in->left, frame->fmt->w_align); 9538c2ecf20Sopenharmony_ci out.top = ALIGN(in->top, frame->fmt->h_align); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci if ((out.left < 0) || (out.left >= frame->width) || 9568c2ecf20Sopenharmony_ci (out.top < 0) || (out.top >= frame->height)) { 9578c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, 9588c2ecf20Sopenharmony_ci "Invalid crop: %dx%d@(%d,%d) vs frame: %dx%d\n", 9598c2ecf20Sopenharmony_ci out.width, out.height, out.left, out.top, 9608c2ecf20Sopenharmony_ci frame->width, frame->height); 9618c2ecf20Sopenharmony_ci return -EINVAL; 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci /* Align and check size */ 9658c2ecf20Sopenharmony_ci out.width = ALIGN(in->width, frame->fmt->w_align); 9668c2ecf20Sopenharmony_ci out.height = ALIGN(in->height, frame->fmt->w_align); 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci if (((out.left + out.width) > frame->width) || 9698c2ecf20Sopenharmony_ci ((out.top + out.height) > frame->height)) { 9708c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, 9718c2ecf20Sopenharmony_ci "Invalid crop: %dx%d@(%d,%d) vs frame: %dx%d\n", 9728c2ecf20Sopenharmony_ci out.width, out.height, out.left, out.top, 9738c2ecf20Sopenharmony_ci frame->width, frame->height); 9748c2ecf20Sopenharmony_ci return -EINVAL; 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci /* Checks adjust constraints flags */ 9788c2ecf20Sopenharmony_ci if (s->flags & V4L2_SEL_FLAG_LE && !is_rect_enclosed(&out, in)) 9798c2ecf20Sopenharmony_ci return -ERANGE; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci if (s->flags & V4L2_SEL_FLAG_GE && !is_rect_enclosed(in, &out)) 9828c2ecf20Sopenharmony_ci return -ERANGE; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci if ((out.left != in->left) || (out.top != in->top) || 9858c2ecf20Sopenharmony_ci (out.width != in->width) || (out.height != in->height)) { 9868c2ecf20Sopenharmony_ci dev_dbg(ctx->bdisp_dev->dev, 9878c2ecf20Sopenharmony_ci "%s crop updated: %dx%d@(%d,%d) -> %dx%d@(%d,%d)\n", 9888c2ecf20Sopenharmony_ci __func__, in->width, in->height, in->left, in->top, 9898c2ecf20Sopenharmony_ci out.width, out.height, out.left, out.top); 9908c2ecf20Sopenharmony_ci *in = out; 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci frame->crop = out; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci bdisp_ctx_state_lock_set(BDISP_PARAMS, ctx); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci return 0; 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_cistatic int bdisp_streamon(struct file *file, void *fh, enum v4l2_buf_type type) 10018c2ecf20Sopenharmony_ci{ 10028c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx = fh_to_ctx(fh); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci if ((type == V4L2_BUF_TYPE_VIDEO_OUTPUT) && 10058c2ecf20Sopenharmony_ci !bdisp_ctx_state_is_set(BDISP_SRC_FMT, ctx)) { 10068c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, "src not defined\n"); 10078c2ecf20Sopenharmony_ci return -EINVAL; 10088c2ecf20Sopenharmony_ci } 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci if ((type == V4L2_BUF_TYPE_VIDEO_CAPTURE) && 10118c2ecf20Sopenharmony_ci !bdisp_ctx_state_is_set(BDISP_DST_FMT, ctx)) { 10128c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, "dst not defined\n"); 10138c2ecf20Sopenharmony_ci return -EINVAL; 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci return v4l2_m2m_streamon(file, ctx->fh.m2m_ctx, type); 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops bdisp_ioctl_ops = { 10208c2ecf20Sopenharmony_ci .vidioc_querycap = bdisp_querycap, 10218c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_cap = bdisp_enum_fmt, 10228c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_out = bdisp_enum_fmt, 10238c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_cap = bdisp_g_fmt, 10248c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_out = bdisp_g_fmt, 10258c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_cap = bdisp_try_fmt, 10268c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_out = bdisp_try_fmt, 10278c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_cap = bdisp_s_fmt, 10288c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_out = bdisp_s_fmt, 10298c2ecf20Sopenharmony_ci .vidioc_g_selection = bdisp_g_selection, 10308c2ecf20Sopenharmony_ci .vidioc_s_selection = bdisp_s_selection, 10318c2ecf20Sopenharmony_ci .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, 10328c2ecf20Sopenharmony_ci .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, 10338c2ecf20Sopenharmony_ci .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, 10348c2ecf20Sopenharmony_ci .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, 10358c2ecf20Sopenharmony_ci .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, 10368c2ecf20Sopenharmony_ci .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, 10378c2ecf20Sopenharmony_ci .vidioc_streamon = bdisp_streamon, 10388c2ecf20Sopenharmony_ci .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, 10398c2ecf20Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 10408c2ecf20Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 10418c2ecf20Sopenharmony_ci}; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_cistatic int bdisp_register_device(struct bdisp_dev *bdisp) 10448c2ecf20Sopenharmony_ci{ 10458c2ecf20Sopenharmony_ci int ret; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci if (!bdisp) 10488c2ecf20Sopenharmony_ci return -ENODEV; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci bdisp->vdev.fops = &bdisp_fops; 10518c2ecf20Sopenharmony_ci bdisp->vdev.ioctl_ops = &bdisp_ioctl_ops; 10528c2ecf20Sopenharmony_ci bdisp->vdev.release = video_device_release_empty; 10538c2ecf20Sopenharmony_ci bdisp->vdev.lock = &bdisp->lock; 10548c2ecf20Sopenharmony_ci bdisp->vdev.vfl_dir = VFL_DIR_M2M; 10558c2ecf20Sopenharmony_ci bdisp->vdev.v4l2_dev = &bdisp->v4l2_dev; 10568c2ecf20Sopenharmony_ci bdisp->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M; 10578c2ecf20Sopenharmony_ci snprintf(bdisp->vdev.name, sizeof(bdisp->vdev.name), "%s.%d", 10588c2ecf20Sopenharmony_ci BDISP_NAME, bdisp->id); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci video_set_drvdata(&bdisp->vdev, bdisp); 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci bdisp->m2m.vdev = &bdisp->vdev; 10638c2ecf20Sopenharmony_ci bdisp->m2m.m2m_dev = v4l2_m2m_init(&bdisp_m2m_ops); 10648c2ecf20Sopenharmony_ci if (IS_ERR(bdisp->m2m.m2m_dev)) { 10658c2ecf20Sopenharmony_ci dev_err(bdisp->dev, "failed to initialize v4l2-m2m device\n"); 10668c2ecf20Sopenharmony_ci return PTR_ERR(bdisp->m2m.m2m_dev); 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci ret = video_register_device(&bdisp->vdev, VFL_TYPE_VIDEO, -1); 10708c2ecf20Sopenharmony_ci if (ret) { 10718c2ecf20Sopenharmony_ci dev_err(bdisp->dev, 10728c2ecf20Sopenharmony_ci "%s(): failed to register video device\n", __func__); 10738c2ecf20Sopenharmony_ci v4l2_m2m_release(bdisp->m2m.m2m_dev); 10748c2ecf20Sopenharmony_ci return ret; 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci return 0; 10788c2ecf20Sopenharmony_ci} 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_cistatic void bdisp_unregister_device(struct bdisp_dev *bdisp) 10818c2ecf20Sopenharmony_ci{ 10828c2ecf20Sopenharmony_ci if (!bdisp) 10838c2ecf20Sopenharmony_ci return; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci if (bdisp->m2m.m2m_dev) 10868c2ecf20Sopenharmony_ci v4l2_m2m_release(bdisp->m2m.m2m_dev); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci video_unregister_device(bdisp->m2m.vdev); 10898c2ecf20Sopenharmony_ci} 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_cistatic irqreturn_t bdisp_irq_thread(int irq, void *priv) 10928c2ecf20Sopenharmony_ci{ 10938c2ecf20Sopenharmony_ci struct bdisp_dev *bdisp = priv; 10948c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci spin_lock(&bdisp->slock); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci bdisp_dbg_perf_end(bdisp); 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci cancel_delayed_work(&bdisp->timeout_work); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci if (!test_and_clear_bit(ST_M2M_RUNNING, &bdisp->state)) 11038c2ecf20Sopenharmony_ci goto isr_unlock; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci if (test_and_clear_bit(ST_M2M_SUSPENDING, &bdisp->state)) { 11068c2ecf20Sopenharmony_ci set_bit(ST_M2M_SUSPENDED, &bdisp->state); 11078c2ecf20Sopenharmony_ci wake_up(&bdisp->irq_queue); 11088c2ecf20Sopenharmony_ci goto isr_unlock; 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci ctx = v4l2_m2m_get_curr_priv(bdisp->m2m.m2m_dev); 11128c2ecf20Sopenharmony_ci if (!ctx || !ctx->fh.m2m_ctx) 11138c2ecf20Sopenharmony_ci goto isr_unlock; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci spin_unlock(&bdisp->slock); 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci bdisp_job_finish(ctx, VB2_BUF_STATE_DONE); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci if (bdisp_ctx_state_is_set(BDISP_CTX_STOP_REQ, ctx)) { 11208c2ecf20Sopenharmony_ci bdisp_ctx_state_lock_clear(BDISP_CTX_STOP_REQ, ctx); 11218c2ecf20Sopenharmony_ci wake_up(&bdisp->irq_queue); 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci return IRQ_HANDLED; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ciisr_unlock: 11278c2ecf20Sopenharmony_ci spin_unlock(&bdisp->slock); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci return IRQ_HANDLED; 11308c2ecf20Sopenharmony_ci} 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_cistatic irqreturn_t bdisp_irq_handler(int irq, void *priv) 11338c2ecf20Sopenharmony_ci{ 11348c2ecf20Sopenharmony_ci if (bdisp_hw_get_and_clear_irq((struct bdisp_dev *)priv)) 11358c2ecf20Sopenharmony_ci return IRQ_NONE; 11368c2ecf20Sopenharmony_ci else 11378c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 11388c2ecf20Sopenharmony_ci} 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_cistatic void bdisp_irq_timeout(struct work_struct *ptr) 11418c2ecf20Sopenharmony_ci{ 11428c2ecf20Sopenharmony_ci struct delayed_work *twork = to_delayed_work(ptr); 11438c2ecf20Sopenharmony_ci struct bdisp_dev *bdisp = container_of(twork, struct bdisp_dev, 11448c2ecf20Sopenharmony_ci timeout_work); 11458c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci ctx = v4l2_m2m_get_curr_priv(bdisp->m2m.m2m_dev); 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci dev_err(ctx->bdisp_dev->dev, "Device work timeout\n"); 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci spin_lock(&bdisp->slock); 11528c2ecf20Sopenharmony_ci clear_bit(ST_M2M_RUNNING, &bdisp->state); 11538c2ecf20Sopenharmony_ci spin_unlock(&bdisp->slock); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci bdisp_hw_reset(bdisp); 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci bdisp_job_finish(ctx, VB2_BUF_STATE_ERROR); 11588c2ecf20Sopenharmony_ci} 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_cistatic int bdisp_m2m_suspend(struct bdisp_dev *bdisp) 11618c2ecf20Sopenharmony_ci{ 11628c2ecf20Sopenharmony_ci unsigned long flags; 11638c2ecf20Sopenharmony_ci int timeout; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci spin_lock_irqsave(&bdisp->slock, flags); 11668c2ecf20Sopenharmony_ci if (!test_bit(ST_M2M_RUNNING, &bdisp->state)) { 11678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bdisp->slock, flags); 11688c2ecf20Sopenharmony_ci return 0; 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci clear_bit(ST_M2M_SUSPENDED, &bdisp->state); 11718c2ecf20Sopenharmony_ci set_bit(ST_M2M_SUSPENDING, &bdisp->state); 11728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bdisp->slock, flags); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci timeout = wait_event_timeout(bdisp->irq_queue, 11758c2ecf20Sopenharmony_ci test_bit(ST_M2M_SUSPENDED, &bdisp->state), 11768c2ecf20Sopenharmony_ci BDISP_WORK_TIMEOUT); 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci clear_bit(ST_M2M_SUSPENDING, &bdisp->state); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci if (!timeout) { 11818c2ecf20Sopenharmony_ci dev_err(bdisp->dev, "%s IRQ timeout\n", __func__); 11828c2ecf20Sopenharmony_ci return -EAGAIN; 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci return 0; 11868c2ecf20Sopenharmony_ci} 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_cistatic int bdisp_m2m_resume(struct bdisp_dev *bdisp) 11898c2ecf20Sopenharmony_ci{ 11908c2ecf20Sopenharmony_ci struct bdisp_ctx *ctx; 11918c2ecf20Sopenharmony_ci unsigned long flags; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci spin_lock_irqsave(&bdisp->slock, flags); 11948c2ecf20Sopenharmony_ci ctx = bdisp->m2m.ctx; 11958c2ecf20Sopenharmony_ci bdisp->m2m.ctx = NULL; 11968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bdisp->slock, flags); 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci if (test_and_clear_bit(ST_M2M_SUSPENDED, &bdisp->state)) 11998c2ecf20Sopenharmony_ci bdisp_job_finish(ctx, VB2_BUF_STATE_ERROR); 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci return 0; 12028c2ecf20Sopenharmony_ci} 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_cistatic int bdisp_runtime_resume(struct device *dev) 12058c2ecf20Sopenharmony_ci{ 12068c2ecf20Sopenharmony_ci struct bdisp_dev *bdisp = dev_get_drvdata(dev); 12078c2ecf20Sopenharmony_ci int ret = clk_enable(bdisp->clock); 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci if (ret) 12108c2ecf20Sopenharmony_ci return ret; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci return bdisp_m2m_resume(bdisp); 12138c2ecf20Sopenharmony_ci} 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_cistatic int bdisp_runtime_suspend(struct device *dev) 12168c2ecf20Sopenharmony_ci{ 12178c2ecf20Sopenharmony_ci struct bdisp_dev *bdisp = dev_get_drvdata(dev); 12188c2ecf20Sopenharmony_ci int ret = bdisp_m2m_suspend(bdisp); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci if (!ret) 12218c2ecf20Sopenharmony_ci clk_disable(bdisp->clock); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci return ret; 12248c2ecf20Sopenharmony_ci} 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_cistatic int bdisp_resume(struct device *dev) 12278c2ecf20Sopenharmony_ci{ 12288c2ecf20Sopenharmony_ci struct bdisp_dev *bdisp = dev_get_drvdata(dev); 12298c2ecf20Sopenharmony_ci unsigned long flags; 12308c2ecf20Sopenharmony_ci int opened; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci spin_lock_irqsave(&bdisp->slock, flags); 12338c2ecf20Sopenharmony_ci opened = test_bit(ST_M2M_OPEN, &bdisp->state); 12348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bdisp->slock, flags); 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci if (!opened) 12378c2ecf20Sopenharmony_ci return 0; 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci if (!pm_runtime_suspended(dev)) 12408c2ecf20Sopenharmony_ci return bdisp_runtime_resume(dev); 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci return 0; 12438c2ecf20Sopenharmony_ci} 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_cistatic int bdisp_suspend(struct device *dev) 12468c2ecf20Sopenharmony_ci{ 12478c2ecf20Sopenharmony_ci if (!pm_runtime_suspended(dev)) 12488c2ecf20Sopenharmony_ci return bdisp_runtime_suspend(dev); 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci return 0; 12518c2ecf20Sopenharmony_ci} 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_cistatic const struct dev_pm_ops bdisp_pm_ops = { 12548c2ecf20Sopenharmony_ci .suspend = bdisp_suspend, 12558c2ecf20Sopenharmony_ci .resume = bdisp_resume, 12568c2ecf20Sopenharmony_ci .runtime_suspend = bdisp_runtime_suspend, 12578c2ecf20Sopenharmony_ci .runtime_resume = bdisp_runtime_resume, 12588c2ecf20Sopenharmony_ci}; 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_cistatic int bdisp_remove(struct platform_device *pdev) 12618c2ecf20Sopenharmony_ci{ 12628c2ecf20Sopenharmony_ci struct bdisp_dev *bdisp = platform_get_drvdata(pdev); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci bdisp_unregister_device(bdisp); 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci bdisp_hw_free_filters(bdisp->dev); 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci bdisp_debugfs_remove(bdisp); 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci v4l2_device_unregister(&bdisp->v4l2_dev); 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci if (!IS_ERR(bdisp->clock)) 12758c2ecf20Sopenharmony_ci clk_unprepare(bdisp->clock); 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci destroy_workqueue(bdisp->work_queue); 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci return 0; 12828c2ecf20Sopenharmony_ci} 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_cistatic int bdisp_probe(struct platform_device *pdev) 12858c2ecf20Sopenharmony_ci{ 12868c2ecf20Sopenharmony_ci struct bdisp_dev *bdisp; 12878c2ecf20Sopenharmony_ci struct resource *res; 12888c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 12898c2ecf20Sopenharmony_ci int ret; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci dev_dbg(dev, "%s\n", __func__); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci bdisp = devm_kzalloc(dev, sizeof(struct bdisp_dev), GFP_KERNEL); 12948c2ecf20Sopenharmony_ci if (!bdisp) 12958c2ecf20Sopenharmony_ci return -ENOMEM; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); 12988c2ecf20Sopenharmony_ci if (ret) 12998c2ecf20Sopenharmony_ci return ret; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci bdisp->pdev = pdev; 13028c2ecf20Sopenharmony_ci bdisp->dev = dev; 13038c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, bdisp); 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci if (dev->of_node) 13068c2ecf20Sopenharmony_ci bdisp->id = of_alias_get_id(pdev->dev.of_node, BDISP_NAME); 13078c2ecf20Sopenharmony_ci else 13088c2ecf20Sopenharmony_ci bdisp->id = pdev->id; 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci init_waitqueue_head(&bdisp->irq_queue); 13118c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&bdisp->timeout_work, bdisp_irq_timeout); 13128c2ecf20Sopenharmony_ci bdisp->work_queue = create_workqueue(BDISP_NAME); 13138c2ecf20Sopenharmony_ci if (!bdisp->work_queue) 13148c2ecf20Sopenharmony_ci return -ENOMEM; 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci spin_lock_init(&bdisp->slock); 13178c2ecf20Sopenharmony_ci mutex_init(&bdisp->lock); 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci /* get resources */ 13208c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 13218c2ecf20Sopenharmony_ci bdisp->regs = devm_ioremap_resource(dev, res); 13228c2ecf20Sopenharmony_ci if (IS_ERR(bdisp->regs)) { 13238c2ecf20Sopenharmony_ci dev_err(dev, "failed to get regs\n"); 13248c2ecf20Sopenharmony_ci ret = PTR_ERR(bdisp->regs); 13258c2ecf20Sopenharmony_ci goto err_wq; 13268c2ecf20Sopenharmony_ci } 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci bdisp->clock = devm_clk_get(dev, BDISP_NAME); 13298c2ecf20Sopenharmony_ci if (IS_ERR(bdisp->clock)) { 13308c2ecf20Sopenharmony_ci dev_err(dev, "failed to get clock\n"); 13318c2ecf20Sopenharmony_ci ret = PTR_ERR(bdisp->clock); 13328c2ecf20Sopenharmony_ci goto err_wq; 13338c2ecf20Sopenharmony_ci } 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci ret = clk_prepare(bdisp->clock); 13368c2ecf20Sopenharmony_ci if (ret < 0) { 13378c2ecf20Sopenharmony_ci dev_err(dev, "clock prepare failed\n"); 13388c2ecf20Sopenharmony_ci bdisp->clock = ERR_PTR(-EINVAL); 13398c2ecf20Sopenharmony_ci goto err_wq; 13408c2ecf20Sopenharmony_ci } 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 13438c2ecf20Sopenharmony_ci if (!res) { 13448c2ecf20Sopenharmony_ci dev_err(dev, "failed to get IRQ resource\n"); 13458c2ecf20Sopenharmony_ci ret = -EINVAL; 13468c2ecf20Sopenharmony_ci goto err_clk; 13478c2ecf20Sopenharmony_ci } 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(dev, res->start, bdisp_irq_handler, 13508c2ecf20Sopenharmony_ci bdisp_irq_thread, IRQF_ONESHOT, 13518c2ecf20Sopenharmony_ci pdev->name, bdisp); 13528c2ecf20Sopenharmony_ci if (ret) { 13538c2ecf20Sopenharmony_ci dev_err(dev, "failed to install irq\n"); 13548c2ecf20Sopenharmony_ci goto err_clk; 13558c2ecf20Sopenharmony_ci } 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci /* v4l2 register */ 13588c2ecf20Sopenharmony_ci ret = v4l2_device_register(dev, &bdisp->v4l2_dev); 13598c2ecf20Sopenharmony_ci if (ret) { 13608c2ecf20Sopenharmony_ci dev_err(dev, "failed to register\n"); 13618c2ecf20Sopenharmony_ci goto err_clk; 13628c2ecf20Sopenharmony_ci } 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci /* Debug */ 13658c2ecf20Sopenharmony_ci bdisp_debugfs_create(bdisp); 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci /* Power management */ 13688c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 13698c2ecf20Sopenharmony_ci ret = pm_runtime_resume_and_get(dev); 13708c2ecf20Sopenharmony_ci if (ret < 0) { 13718c2ecf20Sopenharmony_ci dev_err(dev, "failed to set PM\n"); 13728c2ecf20Sopenharmony_ci goto err_remove; 13738c2ecf20Sopenharmony_ci } 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci /* Filters */ 13768c2ecf20Sopenharmony_ci if (bdisp_hw_alloc_filters(bdisp->dev)) { 13778c2ecf20Sopenharmony_ci dev_err(bdisp->dev, "no memory for filters\n"); 13788c2ecf20Sopenharmony_ci ret = -ENOMEM; 13798c2ecf20Sopenharmony_ci goto err_pm; 13808c2ecf20Sopenharmony_ci } 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci /* Register */ 13838c2ecf20Sopenharmony_ci ret = bdisp_register_device(bdisp); 13848c2ecf20Sopenharmony_ci if (ret) { 13858c2ecf20Sopenharmony_ci dev_err(dev, "failed to register\n"); 13868c2ecf20Sopenharmony_ci goto err_filter; 13878c2ecf20Sopenharmony_ci } 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci dev_info(dev, "%s%d registered as /dev/video%d\n", BDISP_NAME, 13908c2ecf20Sopenharmony_ci bdisp->id, bdisp->vdev.num); 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci pm_runtime_put(dev); 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci return 0; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_cierr_filter: 13978c2ecf20Sopenharmony_ci bdisp_hw_free_filters(bdisp->dev); 13988c2ecf20Sopenharmony_cierr_pm: 13998c2ecf20Sopenharmony_ci pm_runtime_put(dev); 14008c2ecf20Sopenharmony_cierr_remove: 14018c2ecf20Sopenharmony_ci bdisp_debugfs_remove(bdisp); 14028c2ecf20Sopenharmony_ci v4l2_device_unregister(&bdisp->v4l2_dev); 14038c2ecf20Sopenharmony_cierr_clk: 14048c2ecf20Sopenharmony_ci if (!IS_ERR(bdisp->clock)) 14058c2ecf20Sopenharmony_ci clk_unprepare(bdisp->clock); 14068c2ecf20Sopenharmony_cierr_wq: 14078c2ecf20Sopenharmony_ci destroy_workqueue(bdisp->work_queue); 14088c2ecf20Sopenharmony_ci return ret; 14098c2ecf20Sopenharmony_ci} 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_cistatic const struct of_device_id bdisp_match_types[] = { 14128c2ecf20Sopenharmony_ci { 14138c2ecf20Sopenharmony_ci .compatible = "st,stih407-bdisp", 14148c2ecf20Sopenharmony_ci }, 14158c2ecf20Sopenharmony_ci { /* end node */ } 14168c2ecf20Sopenharmony_ci}; 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, bdisp_match_types); 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_cistatic struct platform_driver bdisp_driver = { 14218c2ecf20Sopenharmony_ci .probe = bdisp_probe, 14228c2ecf20Sopenharmony_ci .remove = bdisp_remove, 14238c2ecf20Sopenharmony_ci .driver = { 14248c2ecf20Sopenharmony_ci .name = BDISP_NAME, 14258c2ecf20Sopenharmony_ci .of_match_table = bdisp_match_types, 14268c2ecf20Sopenharmony_ci .pm = &bdisp_pm_ops, 14278c2ecf20Sopenharmony_ci }, 14288c2ecf20Sopenharmony_ci}; 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_cimodule_platform_driver(bdisp_driver); 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("2D blitter for STMicroelectronics SoC"); 14338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>"); 14348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1435