1/* 2 * Samsung S5P G2D - 2D Graphics Accelerator Driver 3 * 4 * Copyright (c) 2011 Samsung Electronics Co., Ltd. 5 * Kamil Debski, <k.debski@samsung.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by the 9 * Free Software Foundation; either version 2 of the 10 * License, or (at your option) any later version 11 */ 12 13#include <linux/module.h> 14#include <linux/fs.h> 15#include <linux/timer.h> 16#include <linux/sched.h> 17#include <linux/slab.h> 18#include <linux/clk.h> 19#include <linux/interrupt.h> 20#include <linux/of.h> 21 22#include <linux/platform_device.h> 23#include <media/v4l2-mem2mem.h> 24#include <media/v4l2-device.h> 25#include <media/v4l2-ioctl.h> 26#include <media/videobuf2-v4l2.h> 27#include <media/videobuf2-dma-contig.h> 28 29#include "g2d.h" 30#include "g2d-regs.h" 31 32#define fh2ctx(__fh) container_of(__fh, struct g2d_ctx, fh) 33 34static struct g2d_fmt formats[] = { 35 { 36 .name = "XRGB_8888", 37 .fourcc = V4L2_PIX_FMT_RGB32, 38 .depth = 32, 39 .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_8888), 40 }, 41 { 42 .name = "RGB_565", 43 .fourcc = V4L2_PIX_FMT_RGB565X, 44 .depth = 16, 45 .hw = COLOR_MODE(ORDER_XRGB, MODE_RGB_565), 46 }, 47 { 48 .name = "XRGB_1555", 49 .fourcc = V4L2_PIX_FMT_RGB555X, 50 .depth = 16, 51 .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_1555), 52 }, 53 { 54 .name = "XRGB_4444", 55 .fourcc = V4L2_PIX_FMT_RGB444, 56 .depth = 16, 57 .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_4444), 58 }, 59 { 60 .name = "PACKED_RGB_888", 61 .fourcc = V4L2_PIX_FMT_RGB24, 62 .depth = 24, 63 .hw = COLOR_MODE(ORDER_XRGB, MODE_PACKED_RGB_888), 64 }, 65}; 66#define NUM_FORMATS ARRAY_SIZE(formats) 67 68static struct g2d_frame def_frame = { 69 .width = DEFAULT_WIDTH, 70 .height = DEFAULT_HEIGHT, 71 .c_width = DEFAULT_WIDTH, 72 .c_height = DEFAULT_HEIGHT, 73 .o_width = 0, 74 .o_height = 0, 75 .fmt = &formats[0], 76 .right = DEFAULT_WIDTH, 77 .bottom = DEFAULT_HEIGHT, 78}; 79 80static struct g2d_fmt *find_fmt(struct v4l2_format *f) 81{ 82 unsigned int i; 83 for (i = 0; i < NUM_FORMATS; i++) { 84 if (formats[i].fourcc == f->fmt.pix.pixelformat) 85 return &formats[i]; 86 } 87 return NULL; 88} 89 90 91static struct g2d_frame *get_frame(struct g2d_ctx *ctx, 92 enum v4l2_buf_type type) 93{ 94 switch (type) { 95 case V4L2_BUF_TYPE_VIDEO_OUTPUT: 96 return &ctx->in; 97 case V4L2_BUF_TYPE_VIDEO_CAPTURE: 98 return &ctx->out; 99 default: 100 return ERR_PTR(-EINVAL); 101 } 102} 103 104static int g2d_queue_setup(struct vb2_queue *vq, const void *parg, 105 unsigned int *nbuffers, unsigned int *nplanes, 106 unsigned int sizes[], void *alloc_ctxs[]) 107{ 108 struct g2d_ctx *ctx = vb2_get_drv_priv(vq); 109 struct g2d_frame *f = get_frame(ctx, vq->type); 110 111 if (IS_ERR(f)) 112 return PTR_ERR(f); 113 114 sizes[0] = f->size; 115 *nplanes = 1; 116 alloc_ctxs[0] = ctx->dev->alloc_ctx; 117 118 if (*nbuffers == 0) 119 *nbuffers = 1; 120 121 return 0; 122} 123 124static int g2d_buf_prepare(struct vb2_buffer *vb) 125{ 126 struct g2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 127 struct g2d_frame *f = get_frame(ctx, vb->vb2_queue->type); 128 129 if (IS_ERR(f)) 130 return PTR_ERR(f); 131 vb2_set_plane_payload(vb, 0, f->size); 132 return 0; 133} 134 135static void g2d_buf_queue(struct vb2_buffer *vb) 136{ 137 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 138 struct g2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 139 v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); 140} 141 142static struct vb2_ops g2d_qops = { 143 .queue_setup = g2d_queue_setup, 144 .buf_prepare = g2d_buf_prepare, 145 .buf_queue = g2d_buf_queue, 146}; 147 148static int queue_init(void *priv, struct vb2_queue *src_vq, 149 struct vb2_queue *dst_vq) 150{ 151 struct g2d_ctx *ctx = priv; 152 int ret; 153 154 src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 155 src_vq->io_modes = VB2_MMAP | VB2_USERPTR; 156 src_vq->drv_priv = ctx; 157 src_vq->ops = &g2d_qops; 158 src_vq->mem_ops = &vb2_dma_contig_memops; 159 src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 160 src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 161 src_vq->lock = &ctx->dev->mutex; 162 163 ret = vb2_queue_init(src_vq); 164 if (ret) 165 return ret; 166 167 dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 168 dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; 169 dst_vq->drv_priv = ctx; 170 dst_vq->ops = &g2d_qops; 171 dst_vq->mem_ops = &vb2_dma_contig_memops; 172 dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 173 dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 174 dst_vq->lock = &ctx->dev->mutex; 175 176 return vb2_queue_init(dst_vq); 177} 178 179static int g2d_s_ctrl(struct v4l2_ctrl *ctrl) 180{ 181 struct g2d_ctx *ctx = container_of(ctrl->handler, struct g2d_ctx, 182 ctrl_handler); 183 unsigned long flags; 184 185 spin_lock_irqsave(&ctx->dev->ctrl_lock, flags); 186 switch (ctrl->id) { 187 case V4L2_CID_COLORFX: 188 if (ctrl->val == V4L2_COLORFX_NEGATIVE) 189 ctx->rop = ROP4_INVERT; 190 else 191 ctx->rop = ROP4_COPY; 192 break; 193 194 case V4L2_CID_HFLIP: 195 ctx->flip = ctx->ctrl_hflip->val | (ctx->ctrl_vflip->val << 1); 196 break; 197 198 } 199 spin_unlock_irqrestore(&ctx->dev->ctrl_lock, flags); 200 return 0; 201} 202 203static const struct v4l2_ctrl_ops g2d_ctrl_ops = { 204 .s_ctrl = g2d_s_ctrl, 205}; 206 207static int g2d_setup_ctrls(struct g2d_ctx *ctx) 208{ 209 struct g2d_dev *dev = ctx->dev; 210 211 v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3); 212 213 ctx->ctrl_hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops, 214 V4L2_CID_HFLIP, 0, 1, 1, 0); 215 216 ctx->ctrl_vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops, 217 V4L2_CID_VFLIP, 0, 1, 1, 0); 218 219 v4l2_ctrl_new_std_menu( 220 &ctx->ctrl_handler, 221 &g2d_ctrl_ops, 222 V4L2_CID_COLORFX, 223 V4L2_COLORFX_NEGATIVE, 224 ~((1 << V4L2_COLORFX_NONE) | (1 << V4L2_COLORFX_NEGATIVE)), 225 V4L2_COLORFX_NONE); 226 227 if (ctx->ctrl_handler.error) { 228 int err = ctx->ctrl_handler.error; 229 v4l2_err(&dev->v4l2_dev, "g2d_setup_ctrls failed\n"); 230 v4l2_ctrl_handler_free(&ctx->ctrl_handler); 231 return err; 232 } 233 234 v4l2_ctrl_cluster(2, &ctx->ctrl_hflip); 235 236 return 0; 237} 238 239static int g2d_open(struct file *file) 240{ 241 struct g2d_dev *dev = video_drvdata(file); 242 struct g2d_ctx *ctx = NULL; 243 int ret = 0; 244 245 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 246 if (!ctx) 247 return -ENOMEM; 248 ctx->dev = dev; 249 /* Set default formats */ 250 ctx->in = def_frame; 251 ctx->out = def_frame; 252 253 if (mutex_lock_interruptible(&dev->mutex)) { 254 kfree(ctx); 255 return -ERESTARTSYS; 256 } 257 ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); 258 if (IS_ERR(ctx->fh.m2m_ctx)) { 259 ret = PTR_ERR(ctx->fh.m2m_ctx); 260 mutex_unlock(&dev->mutex); 261 kfree(ctx); 262 return ret; 263 } 264 v4l2_fh_init(&ctx->fh, video_devdata(file)); 265 file->private_data = &ctx->fh; 266 v4l2_fh_add(&ctx->fh); 267 268 g2d_setup_ctrls(ctx); 269 270 /* Write the default values to the ctx struct */ 271 v4l2_ctrl_handler_setup(&ctx->ctrl_handler); 272 273 ctx->fh.ctrl_handler = &ctx->ctrl_handler; 274 mutex_unlock(&dev->mutex); 275 276 v4l2_info(&dev->v4l2_dev, "instance opened\n"); 277 return 0; 278} 279 280static int g2d_release(struct file *file) 281{ 282 struct g2d_dev *dev = video_drvdata(file); 283 struct g2d_ctx *ctx = fh2ctx(file->private_data); 284 285 v4l2_ctrl_handler_free(&ctx->ctrl_handler); 286 v4l2_fh_del(&ctx->fh); 287 v4l2_fh_exit(&ctx->fh); 288 kfree(ctx); 289 v4l2_info(&dev->v4l2_dev, "instance closed\n"); 290 return 0; 291} 292 293 294static int vidioc_querycap(struct file *file, void *priv, 295 struct v4l2_capability *cap) 296{ 297 strncpy(cap->driver, G2D_NAME, sizeof(cap->driver) - 1); 298 strncpy(cap->card, G2D_NAME, sizeof(cap->card) - 1); 299 cap->bus_info[0] = 0; 300 cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; 301 cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; 302 return 0; 303} 304 305static int vidioc_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f) 306{ 307 struct g2d_fmt *fmt; 308 if (f->index >= NUM_FORMATS) 309 return -EINVAL; 310 fmt = &formats[f->index]; 311 f->pixelformat = fmt->fourcc; 312 strncpy(f->description, fmt->name, sizeof(f->description) - 1); 313 return 0; 314} 315 316static int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f) 317{ 318 struct g2d_ctx *ctx = prv; 319 struct vb2_queue *vq; 320 struct g2d_frame *frm; 321 322 vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 323 if (!vq) 324 return -EINVAL; 325 frm = get_frame(ctx, f->type); 326 if (IS_ERR(frm)) 327 return PTR_ERR(frm); 328 329 f->fmt.pix.width = frm->width; 330 f->fmt.pix.height = frm->height; 331 f->fmt.pix.field = V4L2_FIELD_NONE; 332 f->fmt.pix.pixelformat = frm->fmt->fourcc; 333 f->fmt.pix.bytesperline = (frm->width * frm->fmt->depth) >> 3; 334 f->fmt.pix.sizeimage = frm->size; 335 return 0; 336} 337 338static int vidioc_try_fmt(struct file *file, void *prv, struct v4l2_format *f) 339{ 340 struct g2d_fmt *fmt; 341 enum v4l2_field *field; 342 343 fmt = find_fmt(f); 344 if (!fmt) 345 return -EINVAL; 346 347 field = &f->fmt.pix.field; 348 if (*field == V4L2_FIELD_ANY) 349 *field = V4L2_FIELD_NONE; 350 else if (*field != V4L2_FIELD_NONE) 351 return -EINVAL; 352 353 if (f->fmt.pix.width > MAX_WIDTH) 354 f->fmt.pix.width = MAX_WIDTH; 355 if (f->fmt.pix.height > MAX_HEIGHT) 356 f->fmt.pix.height = MAX_HEIGHT; 357 358 if (f->fmt.pix.width < 1) 359 f->fmt.pix.width = 1; 360 if (f->fmt.pix.height < 1) 361 f->fmt.pix.height = 1; 362 363 f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; 364 f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; 365 return 0; 366} 367 368static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f) 369{ 370 struct g2d_ctx *ctx = prv; 371 struct g2d_dev *dev = ctx->dev; 372 struct vb2_queue *vq; 373 struct g2d_frame *frm; 374 struct g2d_fmt *fmt; 375 int ret = 0; 376 377 /* Adjust all values accordingly to the hardware capabilities 378 * and chosen format. */ 379 ret = vidioc_try_fmt(file, prv, f); 380 if (ret) 381 return ret; 382 vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 383 if (vb2_is_busy(vq)) { 384 v4l2_err(&dev->v4l2_dev, "queue (%d) bust\n", f->type); 385 return -EBUSY; 386 } 387 frm = get_frame(ctx, f->type); 388 if (IS_ERR(frm)) 389 return PTR_ERR(frm); 390 fmt = find_fmt(f); 391 if (!fmt) 392 return -EINVAL; 393 frm->width = f->fmt.pix.width; 394 frm->height = f->fmt.pix.height; 395 frm->size = f->fmt.pix.sizeimage; 396 /* Reset crop settings */ 397 frm->o_width = 0; 398 frm->o_height = 0; 399 frm->c_width = frm->width; 400 frm->c_height = frm->height; 401 frm->right = frm->width; 402 frm->bottom = frm->height; 403 frm->fmt = fmt; 404 frm->stride = f->fmt.pix.bytesperline; 405 return 0; 406} 407 408static int vidioc_cropcap(struct file *file, void *priv, 409 struct v4l2_cropcap *cr) 410{ 411 struct g2d_ctx *ctx = priv; 412 struct g2d_frame *f; 413 414 f = get_frame(ctx, cr->type); 415 if (IS_ERR(f)) 416 return PTR_ERR(f); 417 418 cr->bounds.left = 0; 419 cr->bounds.top = 0; 420 cr->bounds.width = f->width; 421 cr->bounds.height = f->height; 422 cr->defrect = cr->bounds; 423 return 0; 424} 425 426static int vidioc_g_crop(struct file *file, void *prv, struct v4l2_crop *cr) 427{ 428 struct g2d_ctx *ctx = prv; 429 struct g2d_frame *f; 430 431 f = get_frame(ctx, cr->type); 432 if (IS_ERR(f)) 433 return PTR_ERR(f); 434 435 cr->c.left = f->o_height; 436 cr->c.top = f->o_width; 437 cr->c.width = f->c_width; 438 cr->c.height = f->c_height; 439 return 0; 440} 441 442static int vidioc_try_crop(struct file *file, void *prv, const struct v4l2_crop *cr) 443{ 444 struct g2d_ctx *ctx = prv; 445 struct g2d_dev *dev = ctx->dev; 446 struct g2d_frame *f; 447 448 f = get_frame(ctx, cr->type); 449 if (IS_ERR(f)) 450 return PTR_ERR(f); 451 452 if (cr->c.top < 0 || cr->c.left < 0) { 453 v4l2_err(&dev->v4l2_dev, 454 "doesn't support negative values for top & left\n"); 455 return -EINVAL; 456 } 457 458 return 0; 459} 460 461static int vidioc_s_crop(struct file *file, void *prv, const struct v4l2_crop *cr) 462{ 463 struct g2d_ctx *ctx = prv; 464 struct g2d_frame *f; 465 int ret; 466 467 ret = vidioc_try_crop(file, prv, cr); 468 if (ret) 469 return ret; 470 f = get_frame(ctx, cr->type); 471 if (IS_ERR(f)) 472 return PTR_ERR(f); 473 474 f->c_width = cr->c.width; 475 f->c_height = cr->c.height; 476 f->o_width = cr->c.left; 477 f->o_height = cr->c.top; 478 f->bottom = f->o_height + f->c_height; 479 f->right = f->o_width + f->c_width; 480 return 0; 481} 482 483static void job_abort(void *prv) 484{ 485 struct g2d_ctx *ctx = prv; 486 struct g2d_dev *dev = ctx->dev; 487 488 if (dev->curr == NULL) /* No job currently running */ 489 return; 490 491 wait_event_timeout(dev->irq_queue, 492 dev->curr == NULL, 493 msecs_to_jiffies(G2D_TIMEOUT)); 494} 495 496static void device_run(void *prv) 497{ 498 struct g2d_ctx *ctx = prv; 499 struct g2d_dev *dev = ctx->dev; 500 struct vb2_buffer *src, *dst; 501 unsigned long flags; 502 u32 cmd = 0; 503 504 dev->curr = ctx; 505 506 src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 507 dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); 508 509 clk_enable(dev->gate); 510 g2d_reset(dev); 511 512 spin_lock_irqsave(&dev->ctrl_lock, flags); 513 514 g2d_set_src_size(dev, &ctx->in); 515 g2d_set_src_addr(dev, vb2_dma_contig_plane_dma_addr(src, 0)); 516 517 g2d_set_dst_size(dev, &ctx->out); 518 g2d_set_dst_addr(dev, vb2_dma_contig_plane_dma_addr(dst, 0)); 519 520 g2d_set_rop4(dev, ctx->rop); 521 g2d_set_flip(dev, ctx->flip); 522 523 if (ctx->in.c_width != ctx->out.c_width || 524 ctx->in.c_height != ctx->out.c_height) { 525 if (dev->variant->hw_rev == TYPE_G2D_3X) 526 cmd |= CMD_V3_ENABLE_STRETCH; 527 else 528 g2d_set_v41_stretch(dev, &ctx->in, &ctx->out); 529 } 530 531 g2d_set_cmd(dev, cmd); 532 g2d_start(dev); 533 534 spin_unlock_irqrestore(&dev->ctrl_lock, flags); 535} 536 537static irqreturn_t g2d_isr(int irq, void *prv) 538{ 539 struct g2d_dev *dev = prv; 540 struct g2d_ctx *ctx = dev->curr; 541 struct vb2_v4l2_buffer *src, *dst; 542 543 g2d_clear_int(dev); 544 clk_disable(dev->gate); 545 546 BUG_ON(ctx == NULL); 547 548 src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 549 dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 550 551 BUG_ON(src == NULL); 552 BUG_ON(dst == NULL); 553 554 dst->timecode = src->timecode; 555 dst->timestamp = src->timestamp; 556 dst->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; 557 dst->flags |= 558 src->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; 559 560 v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); 561 v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); 562 v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx); 563 564 dev->curr = NULL; 565 wake_up(&dev->irq_queue); 566 return IRQ_HANDLED; 567} 568 569static const struct v4l2_file_operations g2d_fops = { 570 .owner = THIS_MODULE, 571 .open = g2d_open, 572 .release = g2d_release, 573 .poll = v4l2_m2m_fop_poll, 574 .unlocked_ioctl = video_ioctl2, 575 .mmap = v4l2_m2m_fop_mmap, 576}; 577 578static const struct v4l2_ioctl_ops g2d_ioctl_ops = { 579 .vidioc_querycap = vidioc_querycap, 580 581 .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt, 582 .vidioc_g_fmt_vid_cap = vidioc_g_fmt, 583 .vidioc_try_fmt_vid_cap = vidioc_try_fmt, 584 .vidioc_s_fmt_vid_cap = vidioc_s_fmt, 585 586 .vidioc_enum_fmt_vid_out = vidioc_enum_fmt, 587 .vidioc_g_fmt_vid_out = vidioc_g_fmt, 588 .vidioc_try_fmt_vid_out = vidioc_try_fmt, 589 .vidioc_s_fmt_vid_out = vidioc_s_fmt, 590 591 .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, 592 .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, 593 .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, 594 .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, 595 596 .vidioc_streamon = v4l2_m2m_ioctl_streamon, 597 .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, 598 599 .vidioc_g_crop = vidioc_g_crop, 600 .vidioc_s_crop = vidioc_s_crop, 601 .vidioc_cropcap = vidioc_cropcap, 602}; 603 604static struct video_device g2d_videodev = { 605 .name = G2D_NAME, 606 .fops = &g2d_fops, 607 .ioctl_ops = &g2d_ioctl_ops, 608 .minor = -1, 609 .release = video_device_release, 610 .vfl_dir = VFL_DIR_M2M, 611}; 612 613static struct v4l2_m2m_ops g2d_m2m_ops = { 614 .device_run = device_run, 615 .job_abort = job_abort, 616}; 617 618static const struct of_device_id exynos_g2d_match[]; 619 620static int g2d_probe(struct platform_device *pdev) 621{ 622 struct g2d_dev *dev; 623 struct video_device *vfd; 624 struct resource *res; 625 const struct of_device_id *of_id; 626 int ret = 0; 627 628 dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 629 if (!dev) 630 return -ENOMEM; 631 632 spin_lock_init(&dev->ctrl_lock); 633 mutex_init(&dev->mutex); 634 atomic_set(&dev->num_inst, 0); 635 init_waitqueue_head(&dev->irq_queue); 636 637 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 638 639 dev->regs = devm_ioremap_resource(&pdev->dev, res); 640 if (IS_ERR(dev->regs)) 641 return PTR_ERR(dev->regs); 642 643 dev->clk = clk_get(&pdev->dev, "sclk_fimg2d"); 644 if (IS_ERR(dev->clk)) { 645 dev_err(&pdev->dev, "failed to get g2d clock\n"); 646 return -ENXIO; 647 } 648 649 ret = clk_prepare(dev->clk); 650 if (ret) { 651 dev_err(&pdev->dev, "failed to prepare g2d clock\n"); 652 goto put_clk; 653 } 654 655 dev->gate = clk_get(&pdev->dev, "fimg2d"); 656 if (IS_ERR(dev->gate)) { 657 dev_err(&pdev->dev, "failed to get g2d clock gate\n"); 658 ret = -ENXIO; 659 goto unprep_clk; 660 } 661 662 ret = clk_prepare(dev->gate); 663 if (ret) { 664 dev_err(&pdev->dev, "failed to prepare g2d clock gate\n"); 665 goto put_clk_gate; 666 } 667 668 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 669 if (!res) { 670 dev_err(&pdev->dev, "failed to find IRQ\n"); 671 ret = -ENXIO; 672 goto unprep_clk_gate; 673 } 674 675 dev->irq = res->start; 676 677 ret = devm_request_irq(&pdev->dev, dev->irq, g2d_isr, 678 0, pdev->name, dev); 679 if (ret) { 680 dev_err(&pdev->dev, "failed to install IRQ\n"); 681 goto put_clk_gate; 682 } 683 684 dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); 685 if (IS_ERR(dev->alloc_ctx)) { 686 ret = PTR_ERR(dev->alloc_ctx); 687 goto unprep_clk_gate; 688 } 689 690 ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); 691 if (ret) 692 goto alloc_ctx_cleanup; 693 vfd = video_device_alloc(); 694 if (!vfd) { 695 v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n"); 696 ret = -ENOMEM; 697 goto unreg_v4l2_dev; 698 } 699 *vfd = g2d_videodev; 700 vfd->lock = &dev->mutex; 701 vfd->v4l2_dev = &dev->v4l2_dev; 702 ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); 703 if (ret) { 704 v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); 705 goto rel_vdev; 706 } 707 video_set_drvdata(vfd, dev); 708 snprintf(vfd->name, sizeof(vfd->name), "%s", g2d_videodev.name); 709 dev->vfd = vfd; 710 v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n", 711 vfd->num); 712 platform_set_drvdata(pdev, dev); 713 dev->m2m_dev = v4l2_m2m_init(&g2d_m2m_ops); 714 if (IS_ERR(dev->m2m_dev)) { 715 v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); 716 ret = PTR_ERR(dev->m2m_dev); 717 goto unreg_video_dev; 718 } 719 720 def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; 721 722 if (!pdev->dev.of_node) { 723 dev->variant = g2d_get_drv_data(pdev); 724 } else { 725 of_id = of_match_node(exynos_g2d_match, pdev->dev.of_node); 726 if (!of_id) { 727 ret = -ENODEV; 728 goto unreg_video_dev; 729 } 730 dev->variant = (struct g2d_variant *)of_id->data; 731 } 732 733 return 0; 734 735unreg_video_dev: 736 video_unregister_device(dev->vfd); 737rel_vdev: 738 video_device_release(vfd); 739unreg_v4l2_dev: 740 v4l2_device_unregister(&dev->v4l2_dev); 741alloc_ctx_cleanup: 742 vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); 743unprep_clk_gate: 744 clk_unprepare(dev->gate); 745put_clk_gate: 746 clk_put(dev->gate); 747unprep_clk: 748 clk_unprepare(dev->clk); 749put_clk: 750 clk_put(dev->clk); 751 752 return ret; 753} 754 755static int g2d_remove(struct platform_device *pdev) 756{ 757 struct g2d_dev *dev = platform_get_drvdata(pdev); 758 759 v4l2_info(&dev->v4l2_dev, "Removing " G2D_NAME); 760 v4l2_m2m_release(dev->m2m_dev); 761 video_unregister_device(dev->vfd); 762 v4l2_device_unregister(&dev->v4l2_dev); 763 vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); 764 clk_unprepare(dev->gate); 765 clk_put(dev->gate); 766 clk_unprepare(dev->clk); 767 clk_put(dev->clk); 768 return 0; 769} 770 771static struct g2d_variant g2d_drvdata_v3x = { 772 .hw_rev = TYPE_G2D_3X, /* Revision 3.0 for S5PV210 and Exynos4210 */ 773}; 774 775static struct g2d_variant g2d_drvdata_v4x = { 776 .hw_rev = TYPE_G2D_4X, /* Revision 4.1 for Exynos4X12 and Exynos5 */ 777}; 778 779static const struct of_device_id exynos_g2d_match[] = { 780 { 781 .compatible = "samsung,s5pv210-g2d", 782 .data = &g2d_drvdata_v3x, 783 }, { 784 .compatible = "samsung,exynos4212-g2d", 785 .data = &g2d_drvdata_v4x, 786 }, 787 {}, 788}; 789MODULE_DEVICE_TABLE(of, exynos_g2d_match); 790 791static const struct platform_device_id g2d_driver_ids[] = { 792 { 793 .name = "s5p-g2d", 794 .driver_data = (unsigned long)&g2d_drvdata_v3x, 795 }, { 796 .name = "s5p-g2d-v4x", 797 .driver_data = (unsigned long)&g2d_drvdata_v4x, 798 }, 799 {}, 800}; 801MODULE_DEVICE_TABLE(platform, g2d_driver_ids); 802 803static struct platform_driver g2d_pdrv = { 804 .probe = g2d_probe, 805 .remove = g2d_remove, 806 .id_table = g2d_driver_ids, 807 .driver = { 808 .name = G2D_NAME, 809 .of_match_table = exynos_g2d_match, 810 }, 811}; 812 813module_platform_driver(g2d_pdrv); 814 815MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>"); 816MODULE_DESCRIPTION("S5P G2D 2d graphics accelerator driver"); 817MODULE_LICENSE("GPL"); 818