162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Samsung S5P G2D - 2D Graphics Accelerator Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2011 Samsung Electronics Co., Ltd. 662306a36Sopenharmony_ci * Kamil Debski, <k.debski@samsung.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/fs.h> 1162306a36Sopenharmony_ci#include <linux/timer.h> 1262306a36Sopenharmony_ci#include <linux/sched.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/clk.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/platform_device.h> 1962306a36Sopenharmony_ci#include <media/v4l2-mem2mem.h> 2062306a36Sopenharmony_ci#include <media/v4l2-device.h> 2162306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 2262306a36Sopenharmony_ci#include <media/videobuf2-v4l2.h> 2362306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "g2d.h" 2662306a36Sopenharmony_ci#include "g2d-regs.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define fh2ctx(__fh) container_of(__fh, struct g2d_ctx, fh) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic struct g2d_fmt formats[] = { 3162306a36Sopenharmony_ci { 3262306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB32, 3362306a36Sopenharmony_ci .depth = 32, 3462306a36Sopenharmony_ci .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_8888), 3562306a36Sopenharmony_ci }, 3662306a36Sopenharmony_ci { 3762306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB565X, 3862306a36Sopenharmony_ci .depth = 16, 3962306a36Sopenharmony_ci .hw = COLOR_MODE(ORDER_XRGB, MODE_RGB_565), 4062306a36Sopenharmony_ci }, 4162306a36Sopenharmony_ci { 4262306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB555X, 4362306a36Sopenharmony_ci .depth = 16, 4462306a36Sopenharmony_ci .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_1555), 4562306a36Sopenharmony_ci }, 4662306a36Sopenharmony_ci { 4762306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB444, 4862306a36Sopenharmony_ci .depth = 16, 4962306a36Sopenharmony_ci .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_4444), 5062306a36Sopenharmony_ci }, 5162306a36Sopenharmony_ci { 5262306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB24, 5362306a36Sopenharmony_ci .depth = 24, 5462306a36Sopenharmony_ci .hw = COLOR_MODE(ORDER_XRGB, MODE_PACKED_RGB_888), 5562306a36Sopenharmony_ci }, 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci#define NUM_FORMATS ARRAY_SIZE(formats) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic struct g2d_frame def_frame = { 6062306a36Sopenharmony_ci .width = DEFAULT_WIDTH, 6162306a36Sopenharmony_ci .height = DEFAULT_HEIGHT, 6262306a36Sopenharmony_ci .c_width = DEFAULT_WIDTH, 6362306a36Sopenharmony_ci .c_height = DEFAULT_HEIGHT, 6462306a36Sopenharmony_ci .o_width = 0, 6562306a36Sopenharmony_ci .o_height = 0, 6662306a36Sopenharmony_ci .fmt = &formats[0], 6762306a36Sopenharmony_ci .right = DEFAULT_WIDTH, 6862306a36Sopenharmony_ci .bottom = DEFAULT_HEIGHT, 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic struct g2d_fmt *find_fmt(struct v4l2_format *f) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci unsigned int i; 7462306a36Sopenharmony_ci for (i = 0; i < NUM_FORMATS; i++) { 7562306a36Sopenharmony_ci if (formats[i].fourcc == f->fmt.pix.pixelformat) 7662306a36Sopenharmony_ci return &formats[i]; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci return NULL; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic struct g2d_frame *get_frame(struct g2d_ctx *ctx, 8362306a36Sopenharmony_ci enum v4l2_buf_type type) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci switch (type) { 8662306a36Sopenharmony_ci case V4L2_BUF_TYPE_VIDEO_OUTPUT: 8762306a36Sopenharmony_ci return &ctx->in; 8862306a36Sopenharmony_ci case V4L2_BUF_TYPE_VIDEO_CAPTURE: 8962306a36Sopenharmony_ci return &ctx->out; 9062306a36Sopenharmony_ci default: 9162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic int g2d_queue_setup(struct vb2_queue *vq, 9662306a36Sopenharmony_ci unsigned int *nbuffers, unsigned int *nplanes, 9762306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct g2d_ctx *ctx = vb2_get_drv_priv(vq); 10062306a36Sopenharmony_ci struct g2d_frame *f = get_frame(ctx, vq->type); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (IS_ERR(f)) 10362306a36Sopenharmony_ci return PTR_ERR(f); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci sizes[0] = f->size; 10662306a36Sopenharmony_ci *nplanes = 1; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (*nbuffers == 0) 10962306a36Sopenharmony_ci *nbuffers = 1; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic int g2d_buf_prepare(struct vb2_buffer *vb) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci struct g2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 11762306a36Sopenharmony_ci struct g2d_frame *f = get_frame(ctx, vb->vb2_queue->type); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (IS_ERR(f)) 12062306a36Sopenharmony_ci return PTR_ERR(f); 12162306a36Sopenharmony_ci vb2_set_plane_payload(vb, 0, f->size); 12262306a36Sopenharmony_ci return 0; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic void g2d_buf_queue(struct vb2_buffer *vb) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 12862306a36Sopenharmony_ci struct g2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 12962306a36Sopenharmony_ci v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic const struct vb2_ops g2d_qops = { 13362306a36Sopenharmony_ci .queue_setup = g2d_queue_setup, 13462306a36Sopenharmony_ci .buf_prepare = g2d_buf_prepare, 13562306a36Sopenharmony_ci .buf_queue = g2d_buf_queue, 13662306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 13762306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 13862306a36Sopenharmony_ci}; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int queue_init(void *priv, struct vb2_queue *src_vq, 14162306a36Sopenharmony_ci struct vb2_queue *dst_vq) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct g2d_ctx *ctx = priv; 14462306a36Sopenharmony_ci int ret; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 14762306a36Sopenharmony_ci src_vq->io_modes = VB2_MMAP | VB2_USERPTR; 14862306a36Sopenharmony_ci src_vq->drv_priv = ctx; 14962306a36Sopenharmony_ci src_vq->ops = &g2d_qops; 15062306a36Sopenharmony_ci src_vq->mem_ops = &vb2_dma_contig_memops; 15162306a36Sopenharmony_ci src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 15262306a36Sopenharmony_ci src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 15362306a36Sopenharmony_ci src_vq->lock = &ctx->dev->mutex; 15462306a36Sopenharmony_ci src_vq->dev = ctx->dev->v4l2_dev.dev; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci ret = vb2_queue_init(src_vq); 15762306a36Sopenharmony_ci if (ret) 15862306a36Sopenharmony_ci return ret; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 16162306a36Sopenharmony_ci dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; 16262306a36Sopenharmony_ci dst_vq->drv_priv = ctx; 16362306a36Sopenharmony_ci dst_vq->ops = &g2d_qops; 16462306a36Sopenharmony_ci dst_vq->mem_ops = &vb2_dma_contig_memops; 16562306a36Sopenharmony_ci dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 16662306a36Sopenharmony_ci dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 16762306a36Sopenharmony_ci dst_vq->lock = &ctx->dev->mutex; 16862306a36Sopenharmony_ci dst_vq->dev = ctx->dev->v4l2_dev.dev; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return vb2_queue_init(dst_vq); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int g2d_s_ctrl(struct v4l2_ctrl *ctrl) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct g2d_ctx *ctx = container_of(ctrl->handler, struct g2d_ctx, 17662306a36Sopenharmony_ci ctrl_handler); 17762306a36Sopenharmony_ci unsigned long flags; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci spin_lock_irqsave(&ctx->dev->ctrl_lock, flags); 18062306a36Sopenharmony_ci switch (ctrl->id) { 18162306a36Sopenharmony_ci case V4L2_CID_COLORFX: 18262306a36Sopenharmony_ci if (ctrl->val == V4L2_COLORFX_NEGATIVE) 18362306a36Sopenharmony_ci ctx->rop = ROP4_INVERT; 18462306a36Sopenharmony_ci else 18562306a36Sopenharmony_ci ctx->rop = ROP4_COPY; 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci case V4L2_CID_HFLIP: 18962306a36Sopenharmony_ci ctx->flip = ctx->ctrl_hflip->val | (ctx->ctrl_vflip->val << 1); 19062306a36Sopenharmony_ci break; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci spin_unlock_irqrestore(&ctx->dev->ctrl_lock, flags); 19462306a36Sopenharmony_ci return 0; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops g2d_ctrl_ops = { 19862306a36Sopenharmony_ci .s_ctrl = g2d_s_ctrl, 19962306a36Sopenharmony_ci}; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic int g2d_setup_ctrls(struct g2d_ctx *ctx) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci struct g2d_dev *dev = ctx->dev; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci ctx->ctrl_hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops, 20862306a36Sopenharmony_ci V4L2_CID_HFLIP, 0, 1, 1, 0); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci ctx->ctrl_vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops, 21162306a36Sopenharmony_ci V4L2_CID_VFLIP, 0, 1, 1, 0); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci v4l2_ctrl_new_std_menu( 21462306a36Sopenharmony_ci &ctx->ctrl_handler, 21562306a36Sopenharmony_ci &g2d_ctrl_ops, 21662306a36Sopenharmony_ci V4L2_CID_COLORFX, 21762306a36Sopenharmony_ci V4L2_COLORFX_NEGATIVE, 21862306a36Sopenharmony_ci ~((1 << V4L2_COLORFX_NONE) | (1 << V4L2_COLORFX_NEGATIVE)), 21962306a36Sopenharmony_ci V4L2_COLORFX_NONE); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (ctx->ctrl_handler.error) { 22262306a36Sopenharmony_ci int err = ctx->ctrl_handler.error; 22362306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "g2d_setup_ctrls failed\n"); 22462306a36Sopenharmony_ci v4l2_ctrl_handler_free(&ctx->ctrl_handler); 22562306a36Sopenharmony_ci return err; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci v4l2_ctrl_cluster(2, &ctx->ctrl_hflip); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return 0; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic int g2d_open(struct file *file) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct g2d_dev *dev = video_drvdata(file); 23662306a36Sopenharmony_ci struct g2d_ctx *ctx = NULL; 23762306a36Sopenharmony_ci int ret = 0; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 24062306a36Sopenharmony_ci if (!ctx) 24162306a36Sopenharmony_ci return -ENOMEM; 24262306a36Sopenharmony_ci ctx->dev = dev; 24362306a36Sopenharmony_ci /* Set default formats */ 24462306a36Sopenharmony_ci ctx->in = def_frame; 24562306a36Sopenharmony_ci ctx->out = def_frame; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (mutex_lock_interruptible(&dev->mutex)) { 24862306a36Sopenharmony_ci kfree(ctx); 24962306a36Sopenharmony_ci return -ERESTARTSYS; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); 25262306a36Sopenharmony_ci if (IS_ERR(ctx->fh.m2m_ctx)) { 25362306a36Sopenharmony_ci ret = PTR_ERR(ctx->fh.m2m_ctx); 25462306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 25562306a36Sopenharmony_ci kfree(ctx); 25662306a36Sopenharmony_ci return ret; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci v4l2_fh_init(&ctx->fh, video_devdata(file)); 25962306a36Sopenharmony_ci file->private_data = &ctx->fh; 26062306a36Sopenharmony_ci v4l2_fh_add(&ctx->fh); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci g2d_setup_ctrls(ctx); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* Write the default values to the ctx struct */ 26562306a36Sopenharmony_ci v4l2_ctrl_handler_setup(&ctx->ctrl_handler); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci ctx->fh.ctrl_handler = &ctx->ctrl_handler; 26862306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci v4l2_info(&dev->v4l2_dev, "instance opened\n"); 27162306a36Sopenharmony_ci return 0; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic int g2d_release(struct file *file) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct g2d_dev *dev = video_drvdata(file); 27762306a36Sopenharmony_ci struct g2d_ctx *ctx = fh2ctx(file->private_data); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci mutex_lock(&dev->mutex); 28062306a36Sopenharmony_ci v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 28162306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 28262306a36Sopenharmony_ci v4l2_ctrl_handler_free(&ctx->ctrl_handler); 28362306a36Sopenharmony_ci v4l2_fh_del(&ctx->fh); 28462306a36Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 28562306a36Sopenharmony_ci kfree(ctx); 28662306a36Sopenharmony_ci v4l2_info(&dev->v4l2_dev, "instance closed\n"); 28762306a36Sopenharmony_ci return 0; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic int vidioc_querycap(struct file *file, void *priv, 29262306a36Sopenharmony_ci struct v4l2_capability *cap) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci strscpy(cap->driver, G2D_NAME, sizeof(cap->driver)); 29562306a36Sopenharmony_ci strscpy(cap->card, G2D_NAME, sizeof(cap->card)); 29662306a36Sopenharmony_ci cap->bus_info[0] = 0; 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic int vidioc_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci if (f->index >= NUM_FORMATS) 30362306a36Sopenharmony_ci return -EINVAL; 30462306a36Sopenharmony_ci f->pixelformat = formats[f->index].fourcc; 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct g2d_ctx *ctx = prv; 31162306a36Sopenharmony_ci struct vb2_queue *vq; 31262306a36Sopenharmony_ci struct g2d_frame *frm; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 31562306a36Sopenharmony_ci if (!vq) 31662306a36Sopenharmony_ci return -EINVAL; 31762306a36Sopenharmony_ci frm = get_frame(ctx, f->type); 31862306a36Sopenharmony_ci if (IS_ERR(frm)) 31962306a36Sopenharmony_ci return PTR_ERR(frm); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci f->fmt.pix.width = frm->width; 32262306a36Sopenharmony_ci f->fmt.pix.height = frm->height; 32362306a36Sopenharmony_ci f->fmt.pix.field = V4L2_FIELD_NONE; 32462306a36Sopenharmony_ci f->fmt.pix.pixelformat = frm->fmt->fourcc; 32562306a36Sopenharmony_ci f->fmt.pix.bytesperline = (frm->width * frm->fmt->depth) >> 3; 32662306a36Sopenharmony_ci f->fmt.pix.sizeimage = frm->size; 32762306a36Sopenharmony_ci return 0; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic int vidioc_try_fmt(struct file *file, void *prv, struct v4l2_format *f) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct g2d_fmt *fmt; 33362306a36Sopenharmony_ci enum v4l2_field *field; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci fmt = find_fmt(f); 33662306a36Sopenharmony_ci if (!fmt) 33762306a36Sopenharmony_ci return -EINVAL; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci field = &f->fmt.pix.field; 34062306a36Sopenharmony_ci if (*field == V4L2_FIELD_ANY) 34162306a36Sopenharmony_ci *field = V4L2_FIELD_NONE; 34262306a36Sopenharmony_ci else if (*field != V4L2_FIELD_NONE) 34362306a36Sopenharmony_ci return -EINVAL; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (f->fmt.pix.width > MAX_WIDTH) 34662306a36Sopenharmony_ci f->fmt.pix.width = MAX_WIDTH; 34762306a36Sopenharmony_ci if (f->fmt.pix.height > MAX_HEIGHT) 34862306a36Sopenharmony_ci f->fmt.pix.height = MAX_HEIGHT; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (f->fmt.pix.width < 1) 35162306a36Sopenharmony_ci f->fmt.pix.width = 1; 35262306a36Sopenharmony_ci if (f->fmt.pix.height < 1) 35362306a36Sopenharmony_ci f->fmt.pix.height = 1; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; 35662306a36Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci struct g2d_ctx *ctx = prv; 36362306a36Sopenharmony_ci struct g2d_dev *dev = ctx->dev; 36462306a36Sopenharmony_ci struct vb2_queue *vq; 36562306a36Sopenharmony_ci struct g2d_frame *frm; 36662306a36Sopenharmony_ci struct g2d_fmt *fmt; 36762306a36Sopenharmony_ci int ret = 0; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* Adjust all values accordingly to the hardware capabilities 37062306a36Sopenharmony_ci * and chosen format. */ 37162306a36Sopenharmony_ci ret = vidioc_try_fmt(file, prv, f); 37262306a36Sopenharmony_ci if (ret) 37362306a36Sopenharmony_ci return ret; 37462306a36Sopenharmony_ci vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 37562306a36Sopenharmony_ci if (vb2_is_busy(vq)) { 37662306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "queue (%d) bust\n", f->type); 37762306a36Sopenharmony_ci return -EBUSY; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci frm = get_frame(ctx, f->type); 38062306a36Sopenharmony_ci if (IS_ERR(frm)) 38162306a36Sopenharmony_ci return PTR_ERR(frm); 38262306a36Sopenharmony_ci fmt = find_fmt(f); 38362306a36Sopenharmony_ci if (!fmt) 38462306a36Sopenharmony_ci return -EINVAL; 38562306a36Sopenharmony_ci frm->width = f->fmt.pix.width; 38662306a36Sopenharmony_ci frm->height = f->fmt.pix.height; 38762306a36Sopenharmony_ci frm->size = f->fmt.pix.sizeimage; 38862306a36Sopenharmony_ci /* Reset crop settings */ 38962306a36Sopenharmony_ci frm->o_width = 0; 39062306a36Sopenharmony_ci frm->o_height = 0; 39162306a36Sopenharmony_ci frm->c_width = frm->width; 39262306a36Sopenharmony_ci frm->c_height = frm->height; 39362306a36Sopenharmony_ci frm->right = frm->width; 39462306a36Sopenharmony_ci frm->bottom = frm->height; 39562306a36Sopenharmony_ci frm->fmt = fmt; 39662306a36Sopenharmony_ci frm->stride = f->fmt.pix.bytesperline; 39762306a36Sopenharmony_ci return 0; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic int vidioc_g_selection(struct file *file, void *prv, 40162306a36Sopenharmony_ci struct v4l2_selection *s) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct g2d_ctx *ctx = prv; 40462306a36Sopenharmony_ci struct g2d_frame *f; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci f = get_frame(ctx, s->type); 40762306a36Sopenharmony_ci if (IS_ERR(f)) 40862306a36Sopenharmony_ci return PTR_ERR(f); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci switch (s->target) { 41162306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 41262306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 41362306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 41462306a36Sopenharmony_ci if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) 41562306a36Sopenharmony_ci return -EINVAL; 41662306a36Sopenharmony_ci break; 41762306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 41862306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 41962306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 42062306a36Sopenharmony_ci if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 42162306a36Sopenharmony_ci return -EINVAL; 42262306a36Sopenharmony_ci break; 42362306a36Sopenharmony_ci default: 42462306a36Sopenharmony_ci return -EINVAL; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci switch (s->target) { 42862306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 42962306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 43062306a36Sopenharmony_ci s->r.left = f->o_height; 43162306a36Sopenharmony_ci s->r.top = f->o_width; 43262306a36Sopenharmony_ci s->r.width = f->c_width; 43362306a36Sopenharmony_ci s->r.height = f->c_height; 43462306a36Sopenharmony_ci break; 43562306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 43662306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 43762306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 43862306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 43962306a36Sopenharmony_ci s->r.left = 0; 44062306a36Sopenharmony_ci s->r.top = 0; 44162306a36Sopenharmony_ci s->r.width = f->width; 44262306a36Sopenharmony_ci s->r.height = f->height; 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci default: 44562306a36Sopenharmony_ci return -EINVAL; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic int vidioc_try_selection(struct file *file, void *prv, 45162306a36Sopenharmony_ci const struct v4l2_selection *s) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct g2d_ctx *ctx = prv; 45462306a36Sopenharmony_ci struct g2d_dev *dev = ctx->dev; 45562306a36Sopenharmony_ci struct g2d_frame *f; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci f = get_frame(ctx, s->type); 45862306a36Sopenharmony_ci if (IS_ERR(f)) 45962306a36Sopenharmony_ci return PTR_ERR(f); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { 46262306a36Sopenharmony_ci if (s->target != V4L2_SEL_TGT_COMPOSE) 46362306a36Sopenharmony_ci return -EINVAL; 46462306a36Sopenharmony_ci } else if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { 46562306a36Sopenharmony_ci if (s->target != V4L2_SEL_TGT_CROP) 46662306a36Sopenharmony_ci return -EINVAL; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (s->r.top < 0 || s->r.left < 0) { 47062306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, 47162306a36Sopenharmony_ci "doesn't support negative values for top & left\n"); 47262306a36Sopenharmony_ci return -EINVAL; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return 0; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic int vidioc_s_selection(struct file *file, void *prv, 47962306a36Sopenharmony_ci struct v4l2_selection *s) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct g2d_ctx *ctx = prv; 48262306a36Sopenharmony_ci struct g2d_frame *f; 48362306a36Sopenharmony_ci int ret; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci ret = vidioc_try_selection(file, prv, s); 48662306a36Sopenharmony_ci if (ret) 48762306a36Sopenharmony_ci return ret; 48862306a36Sopenharmony_ci f = get_frame(ctx, s->type); 48962306a36Sopenharmony_ci if (IS_ERR(f)) 49062306a36Sopenharmony_ci return PTR_ERR(f); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci f->c_width = s->r.width; 49362306a36Sopenharmony_ci f->c_height = s->r.height; 49462306a36Sopenharmony_ci f->o_width = s->r.left; 49562306a36Sopenharmony_ci f->o_height = s->r.top; 49662306a36Sopenharmony_ci f->bottom = f->o_height + f->c_height; 49762306a36Sopenharmony_ci f->right = f->o_width + f->c_width; 49862306a36Sopenharmony_ci return 0; 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic void device_run(void *prv) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci struct g2d_ctx *ctx = prv; 50462306a36Sopenharmony_ci struct g2d_dev *dev = ctx->dev; 50562306a36Sopenharmony_ci struct vb2_v4l2_buffer *src, *dst; 50662306a36Sopenharmony_ci unsigned long flags; 50762306a36Sopenharmony_ci u32 cmd = 0; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci dev->curr = ctx; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 51262306a36Sopenharmony_ci dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci clk_enable(dev->gate); 51562306a36Sopenharmony_ci g2d_reset(dev); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci spin_lock_irqsave(&dev->ctrl_lock, flags); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci g2d_set_src_size(dev, &ctx->in); 52062306a36Sopenharmony_ci g2d_set_src_addr(dev, vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0)); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci g2d_set_dst_size(dev, &ctx->out); 52362306a36Sopenharmony_ci g2d_set_dst_addr(dev, vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0)); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci g2d_set_rop4(dev, ctx->rop); 52662306a36Sopenharmony_ci g2d_set_flip(dev, ctx->flip); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (ctx->in.c_width != ctx->out.c_width || 52962306a36Sopenharmony_ci ctx->in.c_height != ctx->out.c_height) { 53062306a36Sopenharmony_ci if (dev->variant->hw_rev == TYPE_G2D_3X) 53162306a36Sopenharmony_ci cmd |= CMD_V3_ENABLE_STRETCH; 53262306a36Sopenharmony_ci else 53362306a36Sopenharmony_ci g2d_set_v41_stretch(dev, &ctx->in, &ctx->out); 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci g2d_set_cmd(dev, cmd); 53762306a36Sopenharmony_ci g2d_start(dev); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->ctrl_lock, flags); 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic irqreturn_t g2d_isr(int irq, void *prv) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct g2d_dev *dev = prv; 54562306a36Sopenharmony_ci struct g2d_ctx *ctx = dev->curr; 54662306a36Sopenharmony_ci struct vb2_v4l2_buffer *src, *dst; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci g2d_clear_int(dev); 54962306a36Sopenharmony_ci clk_disable(dev->gate); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci BUG_ON(ctx == NULL); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 55462306a36Sopenharmony_ci dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci BUG_ON(src == NULL); 55762306a36Sopenharmony_ci BUG_ON(dst == NULL); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci dst->timecode = src->timecode; 56062306a36Sopenharmony_ci dst->vb2_buf.timestamp = src->vb2_buf.timestamp; 56162306a36Sopenharmony_ci dst->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; 56262306a36Sopenharmony_ci dst->flags |= 56362306a36Sopenharmony_ci src->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); 56662306a36Sopenharmony_ci v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); 56762306a36Sopenharmony_ci v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci dev->curr = NULL; 57062306a36Sopenharmony_ci return IRQ_HANDLED; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic const struct v4l2_file_operations g2d_fops = { 57462306a36Sopenharmony_ci .owner = THIS_MODULE, 57562306a36Sopenharmony_ci .open = g2d_open, 57662306a36Sopenharmony_ci .release = g2d_release, 57762306a36Sopenharmony_ci .poll = v4l2_m2m_fop_poll, 57862306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 57962306a36Sopenharmony_ci .mmap = v4l2_m2m_fop_mmap, 58062306a36Sopenharmony_ci}; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops g2d_ioctl_ops = { 58362306a36Sopenharmony_ci .vidioc_querycap = vidioc_querycap, 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt, 58662306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap = vidioc_g_fmt, 58762306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap = vidioc_try_fmt, 58862306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap = vidioc_s_fmt, 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci .vidioc_enum_fmt_vid_out = vidioc_enum_fmt, 59162306a36Sopenharmony_ci .vidioc_g_fmt_vid_out = vidioc_g_fmt, 59262306a36Sopenharmony_ci .vidioc_try_fmt_vid_out = vidioc_try_fmt, 59362306a36Sopenharmony_ci .vidioc_s_fmt_vid_out = vidioc_s_fmt, 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, 59662306a36Sopenharmony_ci .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, 59762306a36Sopenharmony_ci .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, 59862306a36Sopenharmony_ci .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci .vidioc_streamon = v4l2_m2m_ioctl_streamon, 60162306a36Sopenharmony_ci .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci .vidioc_g_selection = vidioc_g_selection, 60462306a36Sopenharmony_ci .vidioc_s_selection = vidioc_s_selection, 60562306a36Sopenharmony_ci}; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic const struct video_device g2d_videodev = { 60862306a36Sopenharmony_ci .name = G2D_NAME, 60962306a36Sopenharmony_ci .fops = &g2d_fops, 61062306a36Sopenharmony_ci .ioctl_ops = &g2d_ioctl_ops, 61162306a36Sopenharmony_ci .minor = -1, 61262306a36Sopenharmony_ci .release = video_device_release, 61362306a36Sopenharmony_ci .vfl_dir = VFL_DIR_M2M, 61462306a36Sopenharmony_ci}; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic const struct v4l2_m2m_ops g2d_m2m_ops = { 61762306a36Sopenharmony_ci .device_run = device_run, 61862306a36Sopenharmony_ci}; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic const struct of_device_id exynos_g2d_match[]; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic int g2d_probe(struct platform_device *pdev) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci struct g2d_dev *dev; 62562306a36Sopenharmony_ci struct video_device *vfd; 62662306a36Sopenharmony_ci const struct of_device_id *of_id; 62762306a36Sopenharmony_ci int ret = 0; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 63062306a36Sopenharmony_ci if (!dev) 63162306a36Sopenharmony_ci return -ENOMEM; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci spin_lock_init(&dev->ctrl_lock); 63462306a36Sopenharmony_ci mutex_init(&dev->mutex); 63562306a36Sopenharmony_ci atomic_set(&dev->num_inst, 0); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci dev->regs = devm_platform_ioremap_resource(pdev, 0); 63862306a36Sopenharmony_ci if (IS_ERR(dev->regs)) 63962306a36Sopenharmony_ci return PTR_ERR(dev->regs); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci dev->clk = clk_get(&pdev->dev, "sclk_fimg2d"); 64262306a36Sopenharmony_ci if (IS_ERR(dev->clk)) { 64362306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get g2d clock\n"); 64462306a36Sopenharmony_ci return -ENXIO; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci ret = clk_prepare(dev->clk); 64862306a36Sopenharmony_ci if (ret) { 64962306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to prepare g2d clock\n"); 65062306a36Sopenharmony_ci goto put_clk; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci dev->gate = clk_get(&pdev->dev, "fimg2d"); 65462306a36Sopenharmony_ci if (IS_ERR(dev->gate)) { 65562306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get g2d clock gate\n"); 65662306a36Sopenharmony_ci ret = -ENXIO; 65762306a36Sopenharmony_ci goto unprep_clk; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci ret = clk_prepare(dev->gate); 66162306a36Sopenharmony_ci if (ret) { 66262306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to prepare g2d clock gate\n"); 66362306a36Sopenharmony_ci goto put_clk_gate; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci ret = platform_get_irq(pdev, 0); 66762306a36Sopenharmony_ci if (ret < 0) 66862306a36Sopenharmony_ci goto unprep_clk_gate; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci dev->irq = ret; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, dev->irq, g2d_isr, 67362306a36Sopenharmony_ci 0, pdev->name, dev); 67462306a36Sopenharmony_ci if (ret) { 67562306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to install IRQ\n"); 67662306a36Sopenharmony_ci goto unprep_clk_gate; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); 68262306a36Sopenharmony_ci if (ret) 68362306a36Sopenharmony_ci goto unprep_clk_gate; 68462306a36Sopenharmony_ci vfd = video_device_alloc(); 68562306a36Sopenharmony_ci if (!vfd) { 68662306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n"); 68762306a36Sopenharmony_ci ret = -ENOMEM; 68862306a36Sopenharmony_ci goto unreg_v4l2_dev; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci *vfd = g2d_videodev; 69162306a36Sopenharmony_ci set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags); 69262306a36Sopenharmony_ci vfd->lock = &dev->mutex; 69362306a36Sopenharmony_ci vfd->v4l2_dev = &dev->v4l2_dev; 69462306a36Sopenharmony_ci vfd->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci platform_set_drvdata(pdev, dev); 69762306a36Sopenharmony_ci dev->m2m_dev = v4l2_m2m_init(&g2d_m2m_ops); 69862306a36Sopenharmony_ci if (IS_ERR(dev->m2m_dev)) { 69962306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); 70062306a36Sopenharmony_ci ret = PTR_ERR(dev->m2m_dev); 70162306a36Sopenharmony_ci goto rel_vdev; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci of_id = of_match_node(exynos_g2d_match, pdev->dev.of_node); 70762306a36Sopenharmony_ci if (!of_id) { 70862306a36Sopenharmony_ci ret = -ENODEV; 70962306a36Sopenharmony_ci goto free_m2m; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci dev->variant = (struct g2d_variant *)of_id->data; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); 71462306a36Sopenharmony_ci if (ret) { 71562306a36Sopenharmony_ci v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); 71662306a36Sopenharmony_ci goto free_m2m; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci video_set_drvdata(vfd, dev); 71962306a36Sopenharmony_ci dev->vfd = vfd; 72062306a36Sopenharmony_ci v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n", 72162306a36Sopenharmony_ci vfd->num); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci return 0; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cifree_m2m: 72662306a36Sopenharmony_ci v4l2_m2m_release(dev->m2m_dev); 72762306a36Sopenharmony_cirel_vdev: 72862306a36Sopenharmony_ci video_device_release(vfd); 72962306a36Sopenharmony_ciunreg_v4l2_dev: 73062306a36Sopenharmony_ci v4l2_device_unregister(&dev->v4l2_dev); 73162306a36Sopenharmony_ciunprep_clk_gate: 73262306a36Sopenharmony_ci clk_unprepare(dev->gate); 73362306a36Sopenharmony_ciput_clk_gate: 73462306a36Sopenharmony_ci clk_put(dev->gate); 73562306a36Sopenharmony_ciunprep_clk: 73662306a36Sopenharmony_ci clk_unprepare(dev->clk); 73762306a36Sopenharmony_ciput_clk: 73862306a36Sopenharmony_ci clk_put(dev->clk); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci return ret; 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_cistatic void g2d_remove(struct platform_device *pdev) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci struct g2d_dev *dev = platform_get_drvdata(pdev); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci v4l2_info(&dev->v4l2_dev, "Removing " G2D_NAME); 74862306a36Sopenharmony_ci v4l2_m2m_release(dev->m2m_dev); 74962306a36Sopenharmony_ci video_unregister_device(dev->vfd); 75062306a36Sopenharmony_ci v4l2_device_unregister(&dev->v4l2_dev); 75162306a36Sopenharmony_ci vb2_dma_contig_clear_max_seg_size(&pdev->dev); 75262306a36Sopenharmony_ci clk_unprepare(dev->gate); 75362306a36Sopenharmony_ci clk_put(dev->gate); 75462306a36Sopenharmony_ci clk_unprepare(dev->clk); 75562306a36Sopenharmony_ci clk_put(dev->clk); 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic struct g2d_variant g2d_drvdata_v3x = { 75962306a36Sopenharmony_ci .hw_rev = TYPE_G2D_3X, /* Revision 3.0 for S5PV210 and Exynos4210 */ 76062306a36Sopenharmony_ci}; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic struct g2d_variant g2d_drvdata_v4x = { 76362306a36Sopenharmony_ci .hw_rev = TYPE_G2D_4X, /* Revision 4.1 for Exynos4X12 and Exynos5 */ 76462306a36Sopenharmony_ci}; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_cistatic const struct of_device_id exynos_g2d_match[] = { 76762306a36Sopenharmony_ci { 76862306a36Sopenharmony_ci .compatible = "samsung,s5pv210-g2d", 76962306a36Sopenharmony_ci .data = &g2d_drvdata_v3x, 77062306a36Sopenharmony_ci }, { 77162306a36Sopenharmony_ci .compatible = "samsung,exynos4212-g2d", 77262306a36Sopenharmony_ci .data = &g2d_drvdata_v4x, 77362306a36Sopenharmony_ci }, 77462306a36Sopenharmony_ci {}, 77562306a36Sopenharmony_ci}; 77662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, exynos_g2d_match); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cistatic struct platform_driver g2d_pdrv = { 77962306a36Sopenharmony_ci .probe = g2d_probe, 78062306a36Sopenharmony_ci .remove_new = g2d_remove, 78162306a36Sopenharmony_ci .driver = { 78262306a36Sopenharmony_ci .name = G2D_NAME, 78362306a36Sopenharmony_ci .of_match_table = exynos_g2d_match, 78462306a36Sopenharmony_ci }, 78562306a36Sopenharmony_ci}; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cimodule_platform_driver(g2d_pdrv); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ciMODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>"); 79062306a36Sopenharmony_ciMODULE_DESCRIPTION("S5P G2D 2d graphics accelerator driver"); 79162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 792