18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Samsung S5P G2D - 2D Graphics Accelerator Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2011 Samsung Electronics Co., Ltd. 68c2ecf20Sopenharmony_ci * Kamil Debski, <k.debski@samsung.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/fs.h> 118c2ecf20Sopenharmony_ci#include <linux/timer.h> 128c2ecf20Sopenharmony_ci#include <linux/sched.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/clk.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci#include <media/v4l2-mem2mem.h> 208c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 218c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 228c2ecf20Sopenharmony_ci#include <media/videobuf2-v4l2.h> 238c2ecf20Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "g2d.h" 268c2ecf20Sopenharmony_ci#include "g2d-regs.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define fh2ctx(__fh) container_of(__fh, struct g2d_ctx, fh) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic struct g2d_fmt formats[] = { 318c2ecf20Sopenharmony_ci { 328c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB32, 338c2ecf20Sopenharmony_ci .depth = 32, 348c2ecf20Sopenharmony_ci .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_8888), 358c2ecf20Sopenharmony_ci }, 368c2ecf20Sopenharmony_ci { 378c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB565X, 388c2ecf20Sopenharmony_ci .depth = 16, 398c2ecf20Sopenharmony_ci .hw = COLOR_MODE(ORDER_XRGB, MODE_RGB_565), 408c2ecf20Sopenharmony_ci }, 418c2ecf20Sopenharmony_ci { 428c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB555X, 438c2ecf20Sopenharmony_ci .depth = 16, 448c2ecf20Sopenharmony_ci .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_1555), 458c2ecf20Sopenharmony_ci }, 468c2ecf20Sopenharmony_ci { 478c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB444, 488c2ecf20Sopenharmony_ci .depth = 16, 498c2ecf20Sopenharmony_ci .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_4444), 508c2ecf20Sopenharmony_ci }, 518c2ecf20Sopenharmony_ci { 528c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB24, 538c2ecf20Sopenharmony_ci .depth = 24, 548c2ecf20Sopenharmony_ci .hw = COLOR_MODE(ORDER_XRGB, MODE_PACKED_RGB_888), 558c2ecf20Sopenharmony_ci }, 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci#define NUM_FORMATS ARRAY_SIZE(formats) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic struct g2d_frame def_frame = { 608c2ecf20Sopenharmony_ci .width = DEFAULT_WIDTH, 618c2ecf20Sopenharmony_ci .height = DEFAULT_HEIGHT, 628c2ecf20Sopenharmony_ci .c_width = DEFAULT_WIDTH, 638c2ecf20Sopenharmony_ci .c_height = DEFAULT_HEIGHT, 648c2ecf20Sopenharmony_ci .o_width = 0, 658c2ecf20Sopenharmony_ci .o_height = 0, 668c2ecf20Sopenharmony_ci .fmt = &formats[0], 678c2ecf20Sopenharmony_ci .right = DEFAULT_WIDTH, 688c2ecf20Sopenharmony_ci .bottom = DEFAULT_HEIGHT, 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic struct g2d_fmt *find_fmt(struct v4l2_format *f) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci unsigned int i; 748c2ecf20Sopenharmony_ci for (i = 0; i < NUM_FORMATS; i++) { 758c2ecf20Sopenharmony_ci if (formats[i].fourcc == f->fmt.pix.pixelformat) 768c2ecf20Sopenharmony_ci return &formats[i]; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci return NULL; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic struct g2d_frame *get_frame(struct g2d_ctx *ctx, 838c2ecf20Sopenharmony_ci enum v4l2_buf_type type) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci switch (type) { 868c2ecf20Sopenharmony_ci case V4L2_BUF_TYPE_VIDEO_OUTPUT: 878c2ecf20Sopenharmony_ci return &ctx->in; 888c2ecf20Sopenharmony_ci case V4L2_BUF_TYPE_VIDEO_CAPTURE: 898c2ecf20Sopenharmony_ci return &ctx->out; 908c2ecf20Sopenharmony_ci default: 918c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic int g2d_queue_setup(struct vb2_queue *vq, 968c2ecf20Sopenharmony_ci unsigned int *nbuffers, unsigned int *nplanes, 978c2ecf20Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct g2d_ctx *ctx = vb2_get_drv_priv(vq); 1008c2ecf20Sopenharmony_ci struct g2d_frame *f = get_frame(ctx, vq->type); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (IS_ERR(f)) 1038c2ecf20Sopenharmony_ci return PTR_ERR(f); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci sizes[0] = f->size; 1068c2ecf20Sopenharmony_ci *nplanes = 1; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (*nbuffers == 0) 1098c2ecf20Sopenharmony_ci *nbuffers = 1; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return 0; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int g2d_buf_prepare(struct vb2_buffer *vb) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct g2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 1178c2ecf20Sopenharmony_ci struct g2d_frame *f = get_frame(ctx, vb->vb2_queue->type); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (IS_ERR(f)) 1208c2ecf20Sopenharmony_ci return PTR_ERR(f); 1218c2ecf20Sopenharmony_ci vb2_set_plane_payload(vb, 0, f->size); 1228c2ecf20Sopenharmony_ci return 0; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void g2d_buf_queue(struct vb2_buffer *vb) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 1288c2ecf20Sopenharmony_ci struct g2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 1298c2ecf20Sopenharmony_ci v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic const struct vb2_ops g2d_qops = { 1338c2ecf20Sopenharmony_ci .queue_setup = g2d_queue_setup, 1348c2ecf20Sopenharmony_ci .buf_prepare = g2d_buf_prepare, 1358c2ecf20Sopenharmony_ci .buf_queue = g2d_buf_queue, 1368c2ecf20Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 1378c2ecf20Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int queue_init(void *priv, struct vb2_queue *src_vq, 1418c2ecf20Sopenharmony_ci struct vb2_queue *dst_vq) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct g2d_ctx *ctx = priv; 1448c2ecf20Sopenharmony_ci int ret; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 1478c2ecf20Sopenharmony_ci src_vq->io_modes = VB2_MMAP | VB2_USERPTR; 1488c2ecf20Sopenharmony_ci src_vq->drv_priv = ctx; 1498c2ecf20Sopenharmony_ci src_vq->ops = &g2d_qops; 1508c2ecf20Sopenharmony_ci src_vq->mem_ops = &vb2_dma_contig_memops; 1518c2ecf20Sopenharmony_ci src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 1528c2ecf20Sopenharmony_ci src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 1538c2ecf20Sopenharmony_ci src_vq->lock = &ctx->dev->mutex; 1548c2ecf20Sopenharmony_ci src_vq->dev = ctx->dev->v4l2_dev.dev; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ret = vb2_queue_init(src_vq); 1578c2ecf20Sopenharmony_ci if (ret) 1588c2ecf20Sopenharmony_ci return ret; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 1618c2ecf20Sopenharmony_ci dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; 1628c2ecf20Sopenharmony_ci dst_vq->drv_priv = ctx; 1638c2ecf20Sopenharmony_ci dst_vq->ops = &g2d_qops; 1648c2ecf20Sopenharmony_ci dst_vq->mem_ops = &vb2_dma_contig_memops; 1658c2ecf20Sopenharmony_ci dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 1668c2ecf20Sopenharmony_ci dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 1678c2ecf20Sopenharmony_ci dst_vq->lock = &ctx->dev->mutex; 1688c2ecf20Sopenharmony_ci dst_vq->dev = ctx->dev->v4l2_dev.dev; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return vb2_queue_init(dst_vq); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int g2d_s_ctrl(struct v4l2_ctrl *ctrl) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct g2d_ctx *ctx = container_of(ctrl->handler, struct g2d_ctx, 1768c2ecf20Sopenharmony_ci ctrl_handler); 1778c2ecf20Sopenharmony_ci unsigned long flags; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctx->dev->ctrl_lock, flags); 1808c2ecf20Sopenharmony_ci switch (ctrl->id) { 1818c2ecf20Sopenharmony_ci case V4L2_CID_COLORFX: 1828c2ecf20Sopenharmony_ci if (ctrl->val == V4L2_COLORFX_NEGATIVE) 1838c2ecf20Sopenharmony_ci ctx->rop = ROP4_INVERT; 1848c2ecf20Sopenharmony_ci else 1858c2ecf20Sopenharmony_ci ctx->rop = ROP4_COPY; 1868c2ecf20Sopenharmony_ci break; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci case V4L2_CID_HFLIP: 1898c2ecf20Sopenharmony_ci ctx->flip = ctx->ctrl_hflip->val | (ctx->ctrl_vflip->val << 1); 1908c2ecf20Sopenharmony_ci break; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctx->dev->ctrl_lock, flags); 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops g2d_ctrl_ops = { 1988c2ecf20Sopenharmony_ci .s_ctrl = g2d_s_ctrl, 1998c2ecf20Sopenharmony_ci}; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic int g2d_setup_ctrls(struct g2d_ctx *ctx) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct g2d_dev *dev = ctx->dev; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci ctx->ctrl_hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops, 2088c2ecf20Sopenharmony_ci V4L2_CID_HFLIP, 0, 1, 1, 0); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci ctx->ctrl_vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops, 2118c2ecf20Sopenharmony_ci V4L2_CID_VFLIP, 0, 1, 1, 0); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci v4l2_ctrl_new_std_menu( 2148c2ecf20Sopenharmony_ci &ctx->ctrl_handler, 2158c2ecf20Sopenharmony_ci &g2d_ctrl_ops, 2168c2ecf20Sopenharmony_ci V4L2_CID_COLORFX, 2178c2ecf20Sopenharmony_ci V4L2_COLORFX_NEGATIVE, 2188c2ecf20Sopenharmony_ci ~((1 << V4L2_COLORFX_NONE) | (1 << V4L2_COLORFX_NEGATIVE)), 2198c2ecf20Sopenharmony_ci V4L2_COLORFX_NONE); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (ctx->ctrl_handler.error) { 2228c2ecf20Sopenharmony_ci int err = ctx->ctrl_handler.error; 2238c2ecf20Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "g2d_setup_ctrls failed\n"); 2248c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&ctx->ctrl_handler); 2258c2ecf20Sopenharmony_ci return err; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci v4l2_ctrl_cluster(2, &ctx->ctrl_hflip); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic int g2d_open(struct file *file) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct g2d_dev *dev = video_drvdata(file); 2368c2ecf20Sopenharmony_ci struct g2d_ctx *ctx = NULL; 2378c2ecf20Sopenharmony_ci int ret = 0; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 2408c2ecf20Sopenharmony_ci if (!ctx) 2418c2ecf20Sopenharmony_ci return -ENOMEM; 2428c2ecf20Sopenharmony_ci ctx->dev = dev; 2438c2ecf20Sopenharmony_ci /* Set default formats */ 2448c2ecf20Sopenharmony_ci ctx->in = def_frame; 2458c2ecf20Sopenharmony_ci ctx->out = def_frame; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&dev->mutex)) { 2488c2ecf20Sopenharmony_ci kfree(ctx); 2498c2ecf20Sopenharmony_ci return -ERESTARTSYS; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); 2528c2ecf20Sopenharmony_ci if (IS_ERR(ctx->fh.m2m_ctx)) { 2538c2ecf20Sopenharmony_ci ret = PTR_ERR(ctx->fh.m2m_ctx); 2548c2ecf20Sopenharmony_ci mutex_unlock(&dev->mutex); 2558c2ecf20Sopenharmony_ci kfree(ctx); 2568c2ecf20Sopenharmony_ci return ret; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci v4l2_fh_init(&ctx->fh, video_devdata(file)); 2598c2ecf20Sopenharmony_ci file->private_data = &ctx->fh; 2608c2ecf20Sopenharmony_ci v4l2_fh_add(&ctx->fh); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci g2d_setup_ctrls(ctx); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* Write the default values to the ctx struct */ 2658c2ecf20Sopenharmony_ci v4l2_ctrl_handler_setup(&ctx->ctrl_handler); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci ctx->fh.ctrl_handler = &ctx->ctrl_handler; 2688c2ecf20Sopenharmony_ci mutex_unlock(&dev->mutex); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci v4l2_info(&dev->v4l2_dev, "instance opened\n"); 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic int g2d_release(struct file *file) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct g2d_dev *dev = video_drvdata(file); 2778c2ecf20Sopenharmony_ci struct g2d_ctx *ctx = fh2ctx(file->private_data); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci mutex_lock(&dev->mutex); 2808c2ecf20Sopenharmony_ci v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 2818c2ecf20Sopenharmony_ci mutex_unlock(&dev->mutex); 2828c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&ctx->ctrl_handler); 2838c2ecf20Sopenharmony_ci v4l2_fh_del(&ctx->fh); 2848c2ecf20Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 2858c2ecf20Sopenharmony_ci kfree(ctx); 2868c2ecf20Sopenharmony_ci v4l2_info(&dev->v4l2_dev, "instance closed\n"); 2878c2ecf20Sopenharmony_ci return 0; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int vidioc_querycap(struct file *file, void *priv, 2928c2ecf20Sopenharmony_ci struct v4l2_capability *cap) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci strscpy(cap->driver, G2D_NAME, sizeof(cap->driver)); 2958c2ecf20Sopenharmony_ci strscpy(cap->card, G2D_NAME, sizeof(cap->card)); 2968c2ecf20Sopenharmony_ci cap->bus_info[0] = 0; 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic int vidioc_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci if (f->index >= NUM_FORMATS) 3038c2ecf20Sopenharmony_ci return -EINVAL; 3048c2ecf20Sopenharmony_ci f->pixelformat = formats[f->index].fourcc; 3058c2ecf20Sopenharmony_ci return 0; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci struct g2d_ctx *ctx = prv; 3118c2ecf20Sopenharmony_ci struct vb2_queue *vq; 3128c2ecf20Sopenharmony_ci struct g2d_frame *frm; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 3158c2ecf20Sopenharmony_ci if (!vq) 3168c2ecf20Sopenharmony_ci return -EINVAL; 3178c2ecf20Sopenharmony_ci frm = get_frame(ctx, f->type); 3188c2ecf20Sopenharmony_ci if (IS_ERR(frm)) 3198c2ecf20Sopenharmony_ci return PTR_ERR(frm); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci f->fmt.pix.width = frm->width; 3228c2ecf20Sopenharmony_ci f->fmt.pix.height = frm->height; 3238c2ecf20Sopenharmony_ci f->fmt.pix.field = V4L2_FIELD_NONE; 3248c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat = frm->fmt->fourcc; 3258c2ecf20Sopenharmony_ci f->fmt.pix.bytesperline = (frm->width * frm->fmt->depth) >> 3; 3268c2ecf20Sopenharmony_ci f->fmt.pix.sizeimage = frm->size; 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic int vidioc_try_fmt(struct file *file, void *prv, struct v4l2_format *f) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct g2d_fmt *fmt; 3338c2ecf20Sopenharmony_ci enum v4l2_field *field; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci fmt = find_fmt(f); 3368c2ecf20Sopenharmony_ci if (!fmt) 3378c2ecf20Sopenharmony_ci return -EINVAL; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci field = &f->fmt.pix.field; 3408c2ecf20Sopenharmony_ci if (*field == V4L2_FIELD_ANY) 3418c2ecf20Sopenharmony_ci *field = V4L2_FIELD_NONE; 3428c2ecf20Sopenharmony_ci else if (*field != V4L2_FIELD_NONE) 3438c2ecf20Sopenharmony_ci return -EINVAL; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (f->fmt.pix.width > MAX_WIDTH) 3468c2ecf20Sopenharmony_ci f->fmt.pix.width = MAX_WIDTH; 3478c2ecf20Sopenharmony_ci if (f->fmt.pix.height > MAX_HEIGHT) 3488c2ecf20Sopenharmony_ci f->fmt.pix.height = MAX_HEIGHT; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (f->fmt.pix.width < 1) 3518c2ecf20Sopenharmony_ci f->fmt.pix.width = 1; 3528c2ecf20Sopenharmony_ci if (f->fmt.pix.height < 1) 3538c2ecf20Sopenharmony_ci f->fmt.pix.height = 1; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; 3568c2ecf20Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct g2d_ctx *ctx = prv; 3638c2ecf20Sopenharmony_ci struct g2d_dev *dev = ctx->dev; 3648c2ecf20Sopenharmony_ci struct vb2_queue *vq; 3658c2ecf20Sopenharmony_ci struct g2d_frame *frm; 3668c2ecf20Sopenharmony_ci struct g2d_fmt *fmt; 3678c2ecf20Sopenharmony_ci int ret = 0; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* Adjust all values accordingly to the hardware capabilities 3708c2ecf20Sopenharmony_ci * and chosen format. */ 3718c2ecf20Sopenharmony_ci ret = vidioc_try_fmt(file, prv, f); 3728c2ecf20Sopenharmony_ci if (ret) 3738c2ecf20Sopenharmony_ci return ret; 3748c2ecf20Sopenharmony_ci vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 3758c2ecf20Sopenharmony_ci if (vb2_is_busy(vq)) { 3768c2ecf20Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "queue (%d) bust\n", f->type); 3778c2ecf20Sopenharmony_ci return -EBUSY; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci frm = get_frame(ctx, f->type); 3808c2ecf20Sopenharmony_ci if (IS_ERR(frm)) 3818c2ecf20Sopenharmony_ci return PTR_ERR(frm); 3828c2ecf20Sopenharmony_ci fmt = find_fmt(f); 3838c2ecf20Sopenharmony_ci if (!fmt) 3848c2ecf20Sopenharmony_ci return -EINVAL; 3858c2ecf20Sopenharmony_ci frm->width = f->fmt.pix.width; 3868c2ecf20Sopenharmony_ci frm->height = f->fmt.pix.height; 3878c2ecf20Sopenharmony_ci frm->size = f->fmt.pix.sizeimage; 3888c2ecf20Sopenharmony_ci /* Reset crop settings */ 3898c2ecf20Sopenharmony_ci frm->o_width = 0; 3908c2ecf20Sopenharmony_ci frm->o_height = 0; 3918c2ecf20Sopenharmony_ci frm->c_width = frm->width; 3928c2ecf20Sopenharmony_ci frm->c_height = frm->height; 3938c2ecf20Sopenharmony_ci frm->right = frm->width; 3948c2ecf20Sopenharmony_ci frm->bottom = frm->height; 3958c2ecf20Sopenharmony_ci frm->fmt = fmt; 3968c2ecf20Sopenharmony_ci frm->stride = f->fmt.pix.bytesperline; 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic int vidioc_g_selection(struct file *file, void *prv, 4018c2ecf20Sopenharmony_ci struct v4l2_selection *s) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct g2d_ctx *ctx = prv; 4048c2ecf20Sopenharmony_ci struct g2d_frame *f; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci f = get_frame(ctx, s->type); 4078c2ecf20Sopenharmony_ci if (IS_ERR(f)) 4088c2ecf20Sopenharmony_ci return PTR_ERR(f); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci switch (s->target) { 4118c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP: 4128c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 4138c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 4148c2ecf20Sopenharmony_ci if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) 4158c2ecf20Sopenharmony_ci return -EINVAL; 4168c2ecf20Sopenharmony_ci break; 4178c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 4188c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 4198c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 4208c2ecf20Sopenharmony_ci if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 4218c2ecf20Sopenharmony_ci return -EINVAL; 4228c2ecf20Sopenharmony_ci break; 4238c2ecf20Sopenharmony_ci default: 4248c2ecf20Sopenharmony_ci return -EINVAL; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci switch (s->target) { 4288c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP: 4298c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 4308c2ecf20Sopenharmony_ci s->r.left = f->o_height; 4318c2ecf20Sopenharmony_ci s->r.top = f->o_width; 4328c2ecf20Sopenharmony_ci s->r.width = f->c_width; 4338c2ecf20Sopenharmony_ci s->r.height = f->c_height; 4348c2ecf20Sopenharmony_ci break; 4358c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 4368c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 4378c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 4388c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 4398c2ecf20Sopenharmony_ci s->r.left = 0; 4408c2ecf20Sopenharmony_ci s->r.top = 0; 4418c2ecf20Sopenharmony_ci s->r.width = f->width; 4428c2ecf20Sopenharmony_ci s->r.height = f->height; 4438c2ecf20Sopenharmony_ci break; 4448c2ecf20Sopenharmony_ci default: 4458c2ecf20Sopenharmony_ci return -EINVAL; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic int vidioc_try_selection(struct file *file, void *prv, 4518c2ecf20Sopenharmony_ci const struct v4l2_selection *s) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct g2d_ctx *ctx = prv; 4548c2ecf20Sopenharmony_ci struct g2d_dev *dev = ctx->dev; 4558c2ecf20Sopenharmony_ci struct g2d_frame *f; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci f = get_frame(ctx, s->type); 4588c2ecf20Sopenharmony_ci if (IS_ERR(f)) 4598c2ecf20Sopenharmony_ci return PTR_ERR(f); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { 4628c2ecf20Sopenharmony_ci if (s->target != V4L2_SEL_TGT_COMPOSE) 4638c2ecf20Sopenharmony_ci return -EINVAL; 4648c2ecf20Sopenharmony_ci } else if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { 4658c2ecf20Sopenharmony_ci if (s->target != V4L2_SEL_TGT_CROP) 4668c2ecf20Sopenharmony_ci return -EINVAL; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (s->r.top < 0 || s->r.left < 0) { 4708c2ecf20Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 4718c2ecf20Sopenharmony_ci "doesn't support negative values for top & left\n"); 4728c2ecf20Sopenharmony_ci return -EINVAL; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci return 0; 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_cistatic int vidioc_s_selection(struct file *file, void *prv, 4798c2ecf20Sopenharmony_ci struct v4l2_selection *s) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct g2d_ctx *ctx = prv; 4828c2ecf20Sopenharmony_ci struct g2d_frame *f; 4838c2ecf20Sopenharmony_ci int ret; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci ret = vidioc_try_selection(file, prv, s); 4868c2ecf20Sopenharmony_ci if (ret) 4878c2ecf20Sopenharmony_ci return ret; 4888c2ecf20Sopenharmony_ci f = get_frame(ctx, s->type); 4898c2ecf20Sopenharmony_ci if (IS_ERR(f)) 4908c2ecf20Sopenharmony_ci return PTR_ERR(f); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci f->c_width = s->r.width; 4938c2ecf20Sopenharmony_ci f->c_height = s->r.height; 4948c2ecf20Sopenharmony_ci f->o_width = s->r.left; 4958c2ecf20Sopenharmony_ci f->o_height = s->r.top; 4968c2ecf20Sopenharmony_ci f->bottom = f->o_height + f->c_height; 4978c2ecf20Sopenharmony_ci f->right = f->o_width + f->c_width; 4988c2ecf20Sopenharmony_ci return 0; 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic void device_run(void *prv) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci struct g2d_ctx *ctx = prv; 5048c2ecf20Sopenharmony_ci struct g2d_dev *dev = ctx->dev; 5058c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *src, *dst; 5068c2ecf20Sopenharmony_ci unsigned long flags; 5078c2ecf20Sopenharmony_ci u32 cmd = 0; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci dev->curr = ctx; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 5128c2ecf20Sopenharmony_ci dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci clk_enable(dev->gate); 5158c2ecf20Sopenharmony_ci g2d_reset(dev); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->ctrl_lock, flags); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci g2d_set_src_size(dev, &ctx->in); 5208c2ecf20Sopenharmony_ci g2d_set_src_addr(dev, vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0)); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci g2d_set_dst_size(dev, &ctx->out); 5238c2ecf20Sopenharmony_ci g2d_set_dst_addr(dev, vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0)); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci g2d_set_rop4(dev, ctx->rop); 5268c2ecf20Sopenharmony_ci g2d_set_flip(dev, ctx->flip); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (ctx->in.c_width != ctx->out.c_width || 5298c2ecf20Sopenharmony_ci ctx->in.c_height != ctx->out.c_height) { 5308c2ecf20Sopenharmony_ci if (dev->variant->hw_rev == TYPE_G2D_3X) 5318c2ecf20Sopenharmony_ci cmd |= CMD_V3_ENABLE_STRETCH; 5328c2ecf20Sopenharmony_ci else 5338c2ecf20Sopenharmony_ci g2d_set_v41_stretch(dev, &ctx->in, &ctx->out); 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci g2d_set_cmd(dev, cmd); 5378c2ecf20Sopenharmony_ci g2d_start(dev); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->ctrl_lock, flags); 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic irqreturn_t g2d_isr(int irq, void *prv) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci struct g2d_dev *dev = prv; 5458c2ecf20Sopenharmony_ci struct g2d_ctx *ctx = dev->curr; 5468c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *src, *dst; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci g2d_clear_int(dev); 5498c2ecf20Sopenharmony_ci clk_disable(dev->gate); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci BUG_ON(ctx == NULL); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 5548c2ecf20Sopenharmony_ci dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci BUG_ON(src == NULL); 5578c2ecf20Sopenharmony_ci BUG_ON(dst == NULL); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci dst->timecode = src->timecode; 5608c2ecf20Sopenharmony_ci dst->vb2_buf.timestamp = src->vb2_buf.timestamp; 5618c2ecf20Sopenharmony_ci dst->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; 5628c2ecf20Sopenharmony_ci dst->flags |= 5638c2ecf20Sopenharmony_ci src->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); 5668c2ecf20Sopenharmony_ci v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); 5678c2ecf20Sopenharmony_ci v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci dev->curr = NULL; 5708c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations g2d_fops = { 5748c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5758c2ecf20Sopenharmony_ci .open = g2d_open, 5768c2ecf20Sopenharmony_ci .release = g2d_release, 5778c2ecf20Sopenharmony_ci .poll = v4l2_m2m_fop_poll, 5788c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 5798c2ecf20Sopenharmony_ci .mmap = v4l2_m2m_fop_mmap, 5808c2ecf20Sopenharmony_ci}; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops g2d_ioctl_ops = { 5838c2ecf20Sopenharmony_ci .vidioc_querycap = vidioc_querycap, 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt, 5868c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_cap = vidioc_g_fmt, 5878c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_cap = vidioc_try_fmt, 5888c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_cap = vidioc_s_fmt, 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_out = vidioc_enum_fmt, 5918c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_out = vidioc_g_fmt, 5928c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_out = vidioc_try_fmt, 5938c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_out = vidioc_s_fmt, 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, 5968c2ecf20Sopenharmony_ci .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, 5978c2ecf20Sopenharmony_ci .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, 5988c2ecf20Sopenharmony_ci .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci .vidioc_streamon = v4l2_m2m_ioctl_streamon, 6018c2ecf20Sopenharmony_ci .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci .vidioc_g_selection = vidioc_g_selection, 6048c2ecf20Sopenharmony_ci .vidioc_s_selection = vidioc_s_selection, 6058c2ecf20Sopenharmony_ci}; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic const struct video_device g2d_videodev = { 6088c2ecf20Sopenharmony_ci .name = G2D_NAME, 6098c2ecf20Sopenharmony_ci .fops = &g2d_fops, 6108c2ecf20Sopenharmony_ci .ioctl_ops = &g2d_ioctl_ops, 6118c2ecf20Sopenharmony_ci .minor = -1, 6128c2ecf20Sopenharmony_ci .release = video_device_release, 6138c2ecf20Sopenharmony_ci .vfl_dir = VFL_DIR_M2M, 6148c2ecf20Sopenharmony_ci}; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_cistatic const struct v4l2_m2m_ops g2d_m2m_ops = { 6178c2ecf20Sopenharmony_ci .device_run = device_run, 6188c2ecf20Sopenharmony_ci}; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cistatic const struct of_device_id exynos_g2d_match[]; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic int g2d_probe(struct platform_device *pdev) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci struct g2d_dev *dev; 6258c2ecf20Sopenharmony_ci struct video_device *vfd; 6268c2ecf20Sopenharmony_ci struct resource *res; 6278c2ecf20Sopenharmony_ci const struct of_device_id *of_id; 6288c2ecf20Sopenharmony_ci int ret = 0; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 6318c2ecf20Sopenharmony_ci if (!dev) 6328c2ecf20Sopenharmony_ci return -ENOMEM; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci spin_lock_init(&dev->ctrl_lock); 6358c2ecf20Sopenharmony_ci mutex_init(&dev->mutex); 6368c2ecf20Sopenharmony_ci atomic_set(&dev->num_inst, 0); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci dev->regs = devm_ioremap_resource(&pdev->dev, res); 6418c2ecf20Sopenharmony_ci if (IS_ERR(dev->regs)) 6428c2ecf20Sopenharmony_ci return PTR_ERR(dev->regs); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci dev->clk = clk_get(&pdev->dev, "sclk_fimg2d"); 6458c2ecf20Sopenharmony_ci if (IS_ERR(dev->clk)) { 6468c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get g2d clock\n"); 6478c2ecf20Sopenharmony_ci return -ENXIO; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci ret = clk_prepare(dev->clk); 6518c2ecf20Sopenharmony_ci if (ret) { 6528c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to prepare g2d clock\n"); 6538c2ecf20Sopenharmony_ci goto put_clk; 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci dev->gate = clk_get(&pdev->dev, "fimg2d"); 6578c2ecf20Sopenharmony_ci if (IS_ERR(dev->gate)) { 6588c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get g2d clock gate\n"); 6598c2ecf20Sopenharmony_ci ret = -ENXIO; 6608c2ecf20Sopenharmony_ci goto unprep_clk; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci ret = clk_prepare(dev->gate); 6648c2ecf20Sopenharmony_ci if (ret) { 6658c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to prepare g2d clock gate\n"); 6668c2ecf20Sopenharmony_ci goto put_clk_gate; 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 6708c2ecf20Sopenharmony_ci if (!res) { 6718c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to find IRQ\n"); 6728c2ecf20Sopenharmony_ci ret = -ENXIO; 6738c2ecf20Sopenharmony_ci goto unprep_clk_gate; 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci dev->irq = res->start; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, dev->irq, g2d_isr, 6798c2ecf20Sopenharmony_ci 0, pdev->name, dev); 6808c2ecf20Sopenharmony_ci if (ret) { 6818c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to install IRQ\n"); 6828c2ecf20Sopenharmony_ci goto unprep_clk_gate; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); 6888c2ecf20Sopenharmony_ci if (ret) 6898c2ecf20Sopenharmony_ci goto unprep_clk_gate; 6908c2ecf20Sopenharmony_ci vfd = video_device_alloc(); 6918c2ecf20Sopenharmony_ci if (!vfd) { 6928c2ecf20Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n"); 6938c2ecf20Sopenharmony_ci ret = -ENOMEM; 6948c2ecf20Sopenharmony_ci goto unreg_v4l2_dev; 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci *vfd = g2d_videodev; 6978c2ecf20Sopenharmony_ci set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags); 6988c2ecf20Sopenharmony_ci vfd->lock = &dev->mutex; 6998c2ecf20Sopenharmony_ci vfd->v4l2_dev = &dev->v4l2_dev; 7008c2ecf20Sopenharmony_ci vfd->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, dev); 7038c2ecf20Sopenharmony_ci dev->m2m_dev = v4l2_m2m_init(&g2d_m2m_ops); 7048c2ecf20Sopenharmony_ci if (IS_ERR(dev->m2m_dev)) { 7058c2ecf20Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); 7068c2ecf20Sopenharmony_ci ret = PTR_ERR(dev->m2m_dev); 7078c2ecf20Sopenharmony_ci goto rel_vdev; 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci of_id = of_match_node(exynos_g2d_match, pdev->dev.of_node); 7138c2ecf20Sopenharmony_ci if (!of_id) { 7148c2ecf20Sopenharmony_ci ret = -ENODEV; 7158c2ecf20Sopenharmony_ci goto free_m2m; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci dev->variant = (struct g2d_variant *)of_id->data; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); 7208c2ecf20Sopenharmony_ci if (ret) { 7218c2ecf20Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); 7228c2ecf20Sopenharmony_ci goto free_m2m; 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci video_set_drvdata(vfd, dev); 7258c2ecf20Sopenharmony_ci dev->vfd = vfd; 7268c2ecf20Sopenharmony_ci v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n", 7278c2ecf20Sopenharmony_ci vfd->num); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci return 0; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cifree_m2m: 7328c2ecf20Sopenharmony_ci v4l2_m2m_release(dev->m2m_dev); 7338c2ecf20Sopenharmony_cirel_vdev: 7348c2ecf20Sopenharmony_ci video_device_release(vfd); 7358c2ecf20Sopenharmony_ciunreg_v4l2_dev: 7368c2ecf20Sopenharmony_ci v4l2_device_unregister(&dev->v4l2_dev); 7378c2ecf20Sopenharmony_ciunprep_clk_gate: 7388c2ecf20Sopenharmony_ci clk_unprepare(dev->gate); 7398c2ecf20Sopenharmony_ciput_clk_gate: 7408c2ecf20Sopenharmony_ci clk_put(dev->gate); 7418c2ecf20Sopenharmony_ciunprep_clk: 7428c2ecf20Sopenharmony_ci clk_unprepare(dev->clk); 7438c2ecf20Sopenharmony_ciput_clk: 7448c2ecf20Sopenharmony_ci clk_put(dev->clk); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci return ret; 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_cistatic int g2d_remove(struct platform_device *pdev) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci struct g2d_dev *dev = platform_get_drvdata(pdev); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci v4l2_info(&dev->v4l2_dev, "Removing " G2D_NAME); 7548c2ecf20Sopenharmony_ci v4l2_m2m_release(dev->m2m_dev); 7558c2ecf20Sopenharmony_ci video_unregister_device(dev->vfd); 7568c2ecf20Sopenharmony_ci v4l2_device_unregister(&dev->v4l2_dev); 7578c2ecf20Sopenharmony_ci vb2_dma_contig_clear_max_seg_size(&pdev->dev); 7588c2ecf20Sopenharmony_ci clk_unprepare(dev->gate); 7598c2ecf20Sopenharmony_ci clk_put(dev->gate); 7608c2ecf20Sopenharmony_ci clk_unprepare(dev->clk); 7618c2ecf20Sopenharmony_ci clk_put(dev->clk); 7628c2ecf20Sopenharmony_ci return 0; 7638c2ecf20Sopenharmony_ci} 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_cistatic struct g2d_variant g2d_drvdata_v3x = { 7668c2ecf20Sopenharmony_ci .hw_rev = TYPE_G2D_3X, /* Revision 3.0 for S5PV210 and Exynos4210 */ 7678c2ecf20Sopenharmony_ci}; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic struct g2d_variant g2d_drvdata_v4x = { 7708c2ecf20Sopenharmony_ci .hw_rev = TYPE_G2D_4X, /* Revision 4.1 for Exynos4X12 and Exynos5 */ 7718c2ecf20Sopenharmony_ci}; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_cistatic const struct of_device_id exynos_g2d_match[] = { 7748c2ecf20Sopenharmony_ci { 7758c2ecf20Sopenharmony_ci .compatible = "samsung,s5pv210-g2d", 7768c2ecf20Sopenharmony_ci .data = &g2d_drvdata_v3x, 7778c2ecf20Sopenharmony_ci }, { 7788c2ecf20Sopenharmony_ci .compatible = "samsung,exynos4212-g2d", 7798c2ecf20Sopenharmony_ci .data = &g2d_drvdata_v4x, 7808c2ecf20Sopenharmony_ci }, 7818c2ecf20Sopenharmony_ci {}, 7828c2ecf20Sopenharmony_ci}; 7838c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, exynos_g2d_match); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_cistatic struct platform_driver g2d_pdrv = { 7868c2ecf20Sopenharmony_ci .probe = g2d_probe, 7878c2ecf20Sopenharmony_ci .remove = g2d_remove, 7888c2ecf20Sopenharmony_ci .driver = { 7898c2ecf20Sopenharmony_ci .name = G2D_NAME, 7908c2ecf20Sopenharmony_ci .of_match_table = exynos_g2d_match, 7918c2ecf20Sopenharmony_ci }, 7928c2ecf20Sopenharmony_ci}; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_cimodule_platform_driver(g2d_pdrv); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>"); 7978c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("S5P G2D 2d graphics accelerator driver"); 7988c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 799