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/version.h> 16#include <linux/timer.h> 17#include <linux/sched.h> 18#include <linux/slab.h> 19#include <linux/clk.h> 20#include <linux/interrupt.h> 21#include <linux/of.h> 22 23#include <linux/platform_device.h> 24#include <media/v4l2-mem2mem.h> 25#include <media/v4l2-device.h> 26#include <media/v4l2-ioctl.h> 27#include <media/videobuf2-core.h> 28#include <media/videobuf2-dma-contig.h> 29 30#include "g2d.h" 31#include "g2d-regs.h" 32 33#define fh2ctx(__fh) container_of(__fh, struct g2d_ctx, fh) 34 35static struct g2d_fmt formats[] = { 36 { 37 .name = "XRGB_8888", 38 .fourcc = V4L2_PIX_FMT_RGB32, 39 .depth = 32, 40 .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_8888), 41 }, 42 { 43 .name = "RGB_565", 44 .fourcc = V4L2_PIX_FMT_RGB565X, 45 .depth = 16, 46 .hw = COLOR_MODE(ORDER_XRGB, MODE_RGB_565), 47 }, 48 { 49 .name = "XRGB_1555", 50 .fourcc = V4L2_PIX_FMT_RGB555X, 51 .depth = 16, 52 .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_1555), 53 }, 54 { 55 .name = "XRGB_4444", 56 .fourcc = V4L2_PIX_FMT_RGB444, 57 .depth = 16, 58 .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_4444), 59 }, 60 { 61 .name = "PACKED_RGB_888", 62 .fourcc = V4L2_PIX_FMT_RGB24, 63 .depth = 24, 64 .hw = COLOR_MODE(ORDER_XRGB, MODE_PACKED_RGB_888), 65 }, 66}; 67#define NUM_FORMATS ARRAY_SIZE(formats) 68 69static struct g2d_frame def_frame = { 70 .width = DEFAULT_WIDTH, 71 .height = DEFAULT_HEIGHT, 72 .c_width = DEFAULT_WIDTH, 73 .c_height = DEFAULT_HEIGHT, 74 .o_width = 0, 75 .o_height = 0, 76 .fmt = &formats[0], 77 .right = DEFAULT_WIDTH, 78 .bottom = DEFAULT_HEIGHT, 79}; 80 81static struct g2d_fmt *find_fmt(struct v4l2_format *f) 82{ 83 unsigned int i; 84 for (i = 0; i < NUM_FORMATS; i++) { 85 if (formats[i].fourcc == f->fmt.pix.pixelformat) 86 return &formats[i]; 87 } 88 return NULL; 89} 90 91 92static struct g2d_frame *get_frame(struct g2d_ctx *ctx, 93 enum v4l2_buf_type type) 94{ 95 switch (type) { 96 case V4L2_BUF_TYPE_VIDEO_OUTPUT: 97 return &ctx->in; 98 case V4L2_BUF_TYPE_VIDEO_CAPTURE: 99 return &ctx->out; 100 default: 101 return ERR_PTR(-EINVAL); 102 } 103} 104 105static int g2d_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, 106 unsigned int *nbuffers, unsigned int *nplanes, 107 unsigned int sizes[], void *alloc_ctxs[]) 108{ 109 struct g2d_ctx *ctx = vb2_get_drv_priv(vq); 110 struct g2d_frame *f = get_frame(ctx, vq->type); 111 112 if (IS_ERR(f)) 113 return PTR_ERR(f); 114 115 sizes[0] = f->size; 116 *nplanes = 1; 117 alloc_ctxs[0] = ctx->dev->alloc_ctx; 118 119 if (*nbuffers == 0) 120 *nbuffers = 1; 121 122 return 0; 123} 124 125static int g2d_buf_prepare(struct vb2_buffer *vb) 126{ 127 struct g2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 128 struct g2d_frame *f = get_frame(ctx, vb->vb2_queue->type); 129 130 if (IS_ERR(f)) 131 return PTR_ERR(f); 132 vb2_set_plane_payload(vb, 0, f->size); 133 return 0; 134} 135 136static void g2d_buf_queue(struct vb2_buffer *vb) 137{ 138 struct g2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 139 v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); 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->version = KERNEL_VERSION(1, 0, 0); 301 /* 302 * This is only a mem-to-mem video device. The capture and output 303 * device capability flags are left only for backward compatibility 304 * and are scheduled for removal. 305 */ 306 cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | 307 V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; 308 return 0; 309} 310 311static int vidioc_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f) 312{ 313 struct g2d_fmt *fmt; 314 if (f->index >= NUM_FORMATS) 315 return -EINVAL; 316 fmt = &formats[f->index]; 317 f->pixelformat = fmt->fourcc; 318 strncpy(f->description, fmt->name, sizeof(f->description) - 1); 319 return 0; 320} 321 322static int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f) 323{ 324 struct g2d_ctx *ctx = prv; 325 struct vb2_queue *vq; 326 struct g2d_frame *frm; 327 328 vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 329 if (!vq) 330 return -EINVAL; 331 frm = get_frame(ctx, f->type); 332 if (IS_ERR(frm)) 333 return PTR_ERR(frm); 334 335 f->fmt.pix.width = frm->width; 336 f->fmt.pix.height = frm->height; 337 f->fmt.pix.field = V4L2_FIELD_NONE; 338 f->fmt.pix.pixelformat = frm->fmt->fourcc; 339 f->fmt.pix.bytesperline = (frm->width * frm->fmt->depth) >> 3; 340 f->fmt.pix.sizeimage = frm->size; 341 return 0; 342} 343 344static int vidioc_try_fmt(struct file *file, void *prv, struct v4l2_format *f) 345{ 346 struct g2d_fmt *fmt; 347 enum v4l2_field *field; 348 349 fmt = find_fmt(f); 350 if (!fmt) 351 return -EINVAL; 352 353 field = &f->fmt.pix.field; 354 if (*field == V4L2_FIELD_ANY) 355 *field = V4L2_FIELD_NONE; 356 else if (*field != V4L2_FIELD_NONE) 357 return -EINVAL; 358 359 if (f->fmt.pix.width > MAX_WIDTH) 360 f->fmt.pix.width = MAX_WIDTH; 361 if (f->fmt.pix.height > MAX_HEIGHT) 362 f->fmt.pix.height = MAX_HEIGHT; 363 364 if (f->fmt.pix.width < 1) 365 f->fmt.pix.width = 1; 366 if (f->fmt.pix.height < 1) 367 f->fmt.pix.height = 1; 368 369 f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; 370 f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; 371 return 0; 372} 373 374static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f) 375{ 376 struct g2d_ctx *ctx = prv; 377 struct g2d_dev *dev = ctx->dev; 378 struct vb2_queue *vq; 379 struct g2d_frame *frm; 380 struct g2d_fmt *fmt; 381 int ret = 0; 382 383 /* Adjust all values accordingly to the hardware capabilities 384 * and chosen format. */ 385 ret = vidioc_try_fmt(file, prv, f); 386 if (ret) 387 return ret; 388 vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 389 if (vb2_is_busy(vq)) { 390 v4l2_err(&dev->v4l2_dev, "queue (%d) bust\n", f->type); 391 return -EBUSY; 392 } 393 frm = get_frame(ctx, f->type); 394 if (IS_ERR(frm)) 395 return PTR_ERR(frm); 396 fmt = find_fmt(f); 397 if (!fmt) 398 return -EINVAL; 399 frm->width = f->fmt.pix.width; 400 frm->height = f->fmt.pix.height; 401 frm->size = f->fmt.pix.sizeimage; 402 /* Reset crop settings */ 403 frm->o_width = 0; 404 frm->o_height = 0; 405 frm->c_width = frm->width; 406 frm->c_height = frm->height; 407 frm->right = frm->width; 408 frm->bottom = frm->height; 409 frm->fmt = fmt; 410 frm->stride = f->fmt.pix.bytesperline; 411 return 0; 412} 413 414static int vidioc_cropcap(struct file *file, void *priv, 415 struct v4l2_cropcap *cr) 416{ 417 struct g2d_ctx *ctx = priv; 418 struct g2d_frame *f; 419 420 f = get_frame(ctx, cr->type); 421 if (IS_ERR(f)) 422 return PTR_ERR(f); 423 424 cr->bounds.left = 0; 425 cr->bounds.top = 0; 426 cr->bounds.width = f->width; 427 cr->bounds.height = f->height; 428 cr->defrect = cr->bounds; 429 return 0; 430} 431 432static int vidioc_g_crop(struct file *file, void *prv, struct v4l2_crop *cr) 433{ 434 struct g2d_ctx *ctx = prv; 435 struct g2d_frame *f; 436 437 f = get_frame(ctx, cr->type); 438 if (IS_ERR(f)) 439 return PTR_ERR(f); 440 441 cr->c.left = f->o_height; 442 cr->c.top = f->o_width; 443 cr->c.width = f->c_width; 444 cr->c.height = f->c_height; 445 return 0; 446} 447 448static int vidioc_try_crop(struct file *file, void *prv, const struct v4l2_crop *cr) 449{ 450 struct g2d_ctx *ctx = prv; 451 struct g2d_dev *dev = ctx->dev; 452 struct g2d_frame *f; 453 454 f = get_frame(ctx, cr->type); 455 if (IS_ERR(f)) 456 return PTR_ERR(f); 457 458 if (cr->c.top < 0 || cr->c.left < 0) { 459 v4l2_err(&dev->v4l2_dev, 460 "doesn't support negative values for top & left\n"); 461 return -EINVAL; 462 } 463 464 return 0; 465} 466 467static int vidioc_s_crop(struct file *file, void *prv, const struct v4l2_crop *cr) 468{ 469 struct g2d_ctx *ctx = prv; 470 struct g2d_frame *f; 471 int ret; 472 473 ret = vidioc_try_crop(file, prv, cr); 474 if (ret) 475 return ret; 476 f = get_frame(ctx, cr->type); 477 if (IS_ERR(f)) 478 return PTR_ERR(f); 479 480 f->c_width = cr->c.width; 481 f->c_height = cr->c.height; 482 f->o_width = cr->c.left; 483 f->o_height = cr->c.top; 484 f->bottom = f->o_height + f->c_height; 485 f->right = f->o_width + f->c_width; 486 return 0; 487} 488 489static void job_abort(void *prv) 490{ 491 struct g2d_ctx *ctx = prv; 492 struct g2d_dev *dev = ctx->dev; 493 494 if (dev->curr == NULL) /* No job currently running */ 495 return; 496 497 wait_event_timeout(dev->irq_queue, 498 dev->curr == NULL, 499 msecs_to_jiffies(G2D_TIMEOUT)); 500} 501 502static void device_run(void *prv) 503{ 504 struct g2d_ctx *ctx = prv; 505 struct g2d_dev *dev = ctx->dev; 506 struct vb2_buffer *src, *dst; 507 unsigned long flags; 508 u32 cmd = 0; 509 510 dev->curr = ctx; 511 512 src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 513 dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); 514 515 clk_enable(dev->gate); 516 g2d_reset(dev); 517 518 spin_lock_irqsave(&dev->ctrl_lock, flags); 519 520 g2d_set_src_size(dev, &ctx->in); 521 g2d_set_src_addr(dev, vb2_dma_contig_plane_dma_addr(src, 0)); 522 523 g2d_set_dst_size(dev, &ctx->out); 524 g2d_set_dst_addr(dev, vb2_dma_contig_plane_dma_addr(dst, 0)); 525 526 g2d_set_rop4(dev, ctx->rop); 527 g2d_set_flip(dev, ctx->flip); 528 529 if (ctx->in.c_width != ctx->out.c_width || 530 ctx->in.c_height != ctx->out.c_height) { 531 if (dev->variant->hw_rev == TYPE_G2D_3X) 532 cmd |= CMD_V3_ENABLE_STRETCH; 533 else 534 g2d_set_v41_stretch(dev, &ctx->in, &ctx->out); 535 } 536 537 g2d_set_cmd(dev, cmd); 538 g2d_start(dev); 539 540 spin_unlock_irqrestore(&dev->ctrl_lock, flags); 541} 542 543static irqreturn_t g2d_isr(int irq, void *prv) 544{ 545 struct g2d_dev *dev = prv; 546 struct g2d_ctx *ctx = dev->curr; 547 struct vb2_buffer *src, *dst; 548 549 g2d_clear_int(dev); 550 clk_disable(dev->gate); 551 552 BUG_ON(ctx == NULL); 553 554 src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 555 dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 556 557 BUG_ON(src == NULL); 558 BUG_ON(dst == NULL); 559 560 dst->v4l2_buf.timecode = src->v4l2_buf.timecode; 561 dst->v4l2_buf.timestamp = src->v4l2_buf.timestamp; 562 dst->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; 563 dst->v4l2_buf.flags |= 564 src->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; 565 566 v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); 567 v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); 568 v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx); 569 570 dev->curr = NULL; 571 wake_up(&dev->irq_queue); 572 return IRQ_HANDLED; 573} 574 575static const struct v4l2_file_operations g2d_fops = { 576 .owner = THIS_MODULE, 577 .open = g2d_open, 578 .release = g2d_release, 579 .poll = v4l2_m2m_fop_poll, 580 .unlocked_ioctl = video_ioctl2, 581 .mmap = v4l2_m2m_fop_mmap, 582}; 583 584static const struct v4l2_ioctl_ops g2d_ioctl_ops = { 585 .vidioc_querycap = vidioc_querycap, 586 587 .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt, 588 .vidioc_g_fmt_vid_cap = vidioc_g_fmt, 589 .vidioc_try_fmt_vid_cap = vidioc_try_fmt, 590 .vidioc_s_fmt_vid_cap = vidioc_s_fmt, 591 592 .vidioc_enum_fmt_vid_out = vidioc_enum_fmt, 593 .vidioc_g_fmt_vid_out = vidioc_g_fmt, 594 .vidioc_try_fmt_vid_out = vidioc_try_fmt, 595 .vidioc_s_fmt_vid_out = vidioc_s_fmt, 596 597 .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, 598 .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, 599 .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, 600 .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, 601 602 .vidioc_streamon = v4l2_m2m_ioctl_streamon, 603 .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, 604 605 .vidioc_g_crop = vidioc_g_crop, 606 .vidioc_s_crop = vidioc_s_crop, 607 .vidioc_cropcap = vidioc_cropcap, 608}; 609 610static struct video_device g2d_videodev = { 611 .name = G2D_NAME, 612 .fops = &g2d_fops, 613 .ioctl_ops = &g2d_ioctl_ops, 614 .minor = -1, 615 .release = video_device_release, 616 .vfl_dir = VFL_DIR_M2M, 617}; 618 619static struct v4l2_m2m_ops g2d_m2m_ops = { 620 .device_run = device_run, 621 .job_abort = job_abort, 622}; 623 624static const struct of_device_id exynos_g2d_match[]; 625 626static int g2d_probe(struct platform_device *pdev) 627{ 628 struct g2d_dev *dev; 629 struct video_device *vfd; 630 struct resource *res; 631 const struct of_device_id *of_id; 632 int ret = 0; 633 634 dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 635 if (!dev) 636 return -ENOMEM; 637 638 spin_lock_init(&dev->ctrl_lock); 639 mutex_init(&dev->mutex); 640 atomic_set(&dev->num_inst, 0); 641 init_waitqueue_head(&dev->irq_queue); 642 643 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 644 645 dev->regs = devm_ioremap_resource(&pdev->dev, res); 646 if (IS_ERR(dev->regs)) 647 return PTR_ERR(dev->regs); 648 649 dev->clk = clk_get(&pdev->dev, "sclk_fimg2d"); 650 if (IS_ERR(dev->clk)) { 651 dev_err(&pdev->dev, "failed to get g2d clock\n"); 652 return -ENXIO; 653 } 654 655 ret = clk_prepare(dev->clk); 656 if (ret) { 657 dev_err(&pdev->dev, "failed to prepare g2d clock\n"); 658 goto put_clk; 659 } 660 661 dev->gate = clk_get(&pdev->dev, "fimg2d"); 662 if (IS_ERR(dev->gate)) { 663 dev_err(&pdev->dev, "failed to get g2d clock gate\n"); 664 ret = -ENXIO; 665 goto unprep_clk; 666 } 667 668 ret = clk_prepare(dev->gate); 669 if (ret) { 670 dev_err(&pdev->dev, "failed to prepare g2d clock gate\n"); 671 goto put_clk_gate; 672 } 673 674 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 675 if (!res) { 676 dev_err(&pdev->dev, "failed to find IRQ\n"); 677 ret = -ENXIO; 678 goto unprep_clk_gate; 679 } 680 681 dev->irq = res->start; 682 683 ret = devm_request_irq(&pdev->dev, dev->irq, g2d_isr, 684 0, pdev->name, dev); 685 if (ret) { 686 dev_err(&pdev->dev, "failed to install IRQ\n"); 687 goto put_clk_gate; 688 } 689 690 dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); 691 if (IS_ERR(dev->alloc_ctx)) { 692 ret = PTR_ERR(dev->alloc_ctx); 693 goto unprep_clk_gate; 694 } 695 696 ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); 697 if (ret) 698 goto alloc_ctx_cleanup; 699 vfd = video_device_alloc(); 700 if (!vfd) { 701 v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n"); 702 ret = -ENOMEM; 703 goto unreg_v4l2_dev; 704 } 705 *vfd = g2d_videodev; 706 vfd->lock = &dev->mutex; 707 vfd->v4l2_dev = &dev->v4l2_dev; 708 ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); 709 if (ret) { 710 v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); 711 goto rel_vdev; 712 } 713 video_set_drvdata(vfd, dev); 714 snprintf(vfd->name, sizeof(vfd->name), "%s", g2d_videodev.name); 715 dev->vfd = vfd; 716 v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n", 717 vfd->num); 718 platform_set_drvdata(pdev, dev); 719 dev->m2m_dev = v4l2_m2m_init(&g2d_m2m_ops); 720 if (IS_ERR(dev->m2m_dev)) { 721 v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); 722 ret = PTR_ERR(dev->m2m_dev); 723 goto unreg_video_dev; 724 } 725 726 def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; 727 728 if (!pdev->dev.of_node) { 729 dev->variant = g2d_get_drv_data(pdev); 730 } else { 731 of_id = of_match_node(exynos_g2d_match, pdev->dev.of_node); 732 if (!of_id) { 733 ret = -ENODEV; 734 goto unreg_video_dev; 735 } 736 dev->variant = (struct g2d_variant *)of_id->data; 737 } 738 739 return 0; 740 741unreg_video_dev: 742 video_unregister_device(dev->vfd); 743rel_vdev: 744 video_device_release(vfd); 745unreg_v4l2_dev: 746 v4l2_device_unregister(&dev->v4l2_dev); 747alloc_ctx_cleanup: 748 vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); 749unprep_clk_gate: 750 clk_unprepare(dev->gate); 751put_clk_gate: 752 clk_put(dev->gate); 753unprep_clk: 754 clk_unprepare(dev->clk); 755put_clk: 756 clk_put(dev->clk); 757 758 return ret; 759} 760 761static int g2d_remove(struct platform_device *pdev) 762{ 763 struct g2d_dev *dev = platform_get_drvdata(pdev); 764 765 v4l2_info(&dev->v4l2_dev, "Removing " G2D_NAME); 766 v4l2_m2m_release(dev->m2m_dev); 767 video_unregister_device(dev->vfd); 768 v4l2_device_unregister(&dev->v4l2_dev); 769 vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); 770 clk_unprepare(dev->gate); 771 clk_put(dev->gate); 772 clk_unprepare(dev->clk); 773 clk_put(dev->clk); 774 return 0; 775} 776 777static struct g2d_variant g2d_drvdata_v3x = { 778 .hw_rev = TYPE_G2D_3X, /* Revision 3.0 for S5PV210 and Exynos4210 */ 779}; 780 781static struct g2d_variant g2d_drvdata_v4x = { 782 .hw_rev = TYPE_G2D_4X, /* Revision 4.1 for Exynos4X12 and Exynos5 */ 783}; 784 785static const struct of_device_id exynos_g2d_match[] = { 786 { 787 .compatible = "samsung,s5pv210-g2d", 788 .data = &g2d_drvdata_v3x, 789 }, { 790 .compatible = "samsung,exynos4212-g2d", 791 .data = &g2d_drvdata_v4x, 792 }, 793 {}, 794}; 795MODULE_DEVICE_TABLE(of, exynos_g2d_match); 796 797static struct platform_device_id g2d_driver_ids[] = { 798 { 799 .name = "s5p-g2d", 800 .driver_data = (unsigned long)&g2d_drvdata_v3x, 801 }, { 802 .name = "s5p-g2d-v4x", 803 .driver_data = (unsigned long)&g2d_drvdata_v4x, 804 }, 805 {}, 806}; 807MODULE_DEVICE_TABLE(platform, g2d_driver_ids); 808 809static struct platform_driver g2d_pdrv = { 810 .probe = g2d_probe, 811 .remove = g2d_remove, 812 .id_table = g2d_driver_ids, 813 .driver = { 814 .name = G2D_NAME, 815 .owner = THIS_MODULE, 816 .of_match_table = exynos_g2d_match, 817 }, 818}; 819 820module_platform_driver(g2d_pdrv); 821 822MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>"); 823MODULE_DESCRIPTION("S5P G2D 2d graphics accelerator driver"); 824MODULE_LICENSE("GPL"); 825