linux/drivers/media/platform/xilinx/xilinx-rgb2yuv.c
<<
>>
Prefs
   1/*
   2 * Xilinx RGB to YUV Convertor
   3 *
   4 * Copyright (C) 2013-2015 Ideas on Board
   5 * Copyright (C) 2013-2015 Xilinx, Inc.
   6 *
   7 * Contacts: Hyun Kwon <hyun.kwon@xilinx.com>
   8 *           Laurent Pinchart <laurent.pinchart@ideasonboard.com>
   9 *
  10 * This software is licensed under the terms of the GNU General Public
  11 * License version 2, as published by the Free Software Foundation, and
  12 * may be copied, distributed, and modified under those terms.
  13 *
  14 * This program is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 * GNU General Public License for more details.
  18 */
  19
  20#include <linux/device.h>
  21#include <linux/module.h>
  22#include <linux/of.h>
  23#include <linux/platform_device.h>
  24#include <linux/xilinx-v4l2-controls.h>
  25
  26#include <media/v4l2-async.h>
  27#include <media/v4l2-ctrls.h>
  28#include <media/v4l2-subdev.h>
  29
  30#include "xilinx-vip.h"
  31
  32#define XRGB2YUV_YMAX                                   0x100
  33#define XRGB2YUV_YMIN                                   0x104
  34#define XRGB2YUV_CBMAX                                  0x108
  35#define XRGB2YUV_CBMIN                                  0x10c
  36#define XRGB2YUV_CRMAX                                  0x110
  37#define XRGB2YUV_CRMIN                                  0x114
  38#define XRGB2YUV_YOFFSET                                0x118
  39#define XRGB2YUV_CBOFFSET                               0x11c
  40#define XRGB2YUV_CROFFSET                               0x120
  41#define XRGB2YUV_ACOEF                                  0x124
  42#define XRGB2YUV_BCOEF                                  0x128
  43#define XRGB2YUV_CCOEF                                  0x12c
  44#define XRGB2YUV_DCOEF                                  0x130
  45
  46/**
  47 * struct xrgb2yuv_device - Xilinx RGB2YUV device structure
  48 * @xvip: Xilinx Video IP device
  49 * @pads: media pads
  50 * @formats: V4L2 media bus formats at the sink and source pads
  51 * @default_formats: default V4L2 media bus formats
  52 * @vip_formats: Xilinx Video IP formats
  53 * @ctrl_handler: control handler
  54 */
  55struct xrgb2yuv_device {
  56        struct xvip_device xvip;
  57
  58        struct media_pad pads[2];
  59
  60        struct v4l2_mbus_framefmt formats[2];
  61        struct v4l2_mbus_framefmt default_formats[2];
  62        const struct xvip_video_format *vip_formats[2];
  63
  64        struct v4l2_ctrl_handler ctrl_handler;
  65};
  66
  67static inline struct xrgb2yuv_device *to_rgb2yuv(struct v4l2_subdev *subdev)
  68{
  69        return container_of(subdev, struct xrgb2yuv_device, xvip.subdev);
  70}
  71
  72/*
  73 * V4L2 Subdevice Video Operations
  74 */
  75
  76static int xrgb2yuv_s_stream(struct v4l2_subdev *subdev, int enable)
  77{
  78        struct xrgb2yuv_device *xrgb2yuv = to_rgb2yuv(subdev);
  79
  80        if (!enable) {
  81                xvip_stop(&xrgb2yuv->xvip);
  82                return 0;
  83        }
  84
  85        xvip_set_frame_size(&xrgb2yuv->xvip, &xrgb2yuv->formats[XVIP_PAD_SINK]);
  86
  87        xvip_start(&xrgb2yuv->xvip);
  88
  89        return 0;
  90}
  91
  92/*
  93 * V4L2 Subdevice Pad Operations
  94 */
  95
  96static struct v4l2_mbus_framefmt *
  97__xrgb2yuv_get_pad_format(struct xrgb2yuv_device *xrgb2yuv,
  98                          struct v4l2_subdev_pad_config *cfg,
  99                          unsigned int pad, u32 which)
 100{
 101        switch (which) {
 102        case V4L2_SUBDEV_FORMAT_TRY:
 103                return v4l2_subdev_get_try_format(&xrgb2yuv->xvip.subdev, cfg,
 104                                                  pad);
 105        case V4L2_SUBDEV_FORMAT_ACTIVE:
 106                return &xrgb2yuv->formats[pad];
 107        default:
 108                return NULL;
 109        }
 110}
 111
 112static int xrgb2yuv_get_format(struct v4l2_subdev *subdev,
 113                               struct v4l2_subdev_pad_config *cfg,
 114                               struct v4l2_subdev_format *fmt)
 115{
 116        struct xrgb2yuv_device *xrgb2yuv = to_rgb2yuv(subdev);
 117
 118        fmt->format = *__xrgb2yuv_get_pad_format(xrgb2yuv, cfg, fmt->pad,
 119                                                 fmt->which);
 120
 121        return 0;
 122}
 123
 124static int xrgb2yuv_set_format(struct v4l2_subdev *subdev,
 125                               struct v4l2_subdev_pad_config *cfg,
 126                               struct v4l2_subdev_format *fmt)
 127{
 128        struct xrgb2yuv_device *xrgb2yuv = to_rgb2yuv(subdev);
 129        struct v4l2_mbus_framefmt *format;
 130
 131        format = __xrgb2yuv_get_pad_format(xrgb2yuv, cfg, fmt->pad, fmt->which);
 132
 133        if (fmt->pad == XVIP_PAD_SOURCE) {
 134                fmt->format = *format;
 135                return 0;
 136        }
 137
 138        xvip_set_format_size(format, fmt);
 139
 140        fmt->format = *format;
 141
 142        /* Propagate the format to the source pad. */
 143        format = __xrgb2yuv_get_pad_format(xrgb2yuv, cfg, XVIP_PAD_SOURCE,
 144                                             fmt->which);
 145
 146        xvip_set_format_size(format, fmt);
 147
 148        return 0;
 149}
 150
 151/*
 152 * V4L2 Subdevice Operations
 153 */
 154
 155static int xrgb2yuv_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
 156{
 157        struct xrgb2yuv_device *xrgb2yuv = to_rgb2yuv(subdev);
 158        struct v4l2_mbus_framefmt *format;
 159
 160        /* Initialize with default formats */
 161        format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SINK);
 162        *format = xrgb2yuv->default_formats[XVIP_PAD_SINK];
 163
 164        format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SOURCE);
 165        *format = xrgb2yuv->default_formats[XVIP_PAD_SOURCE];
 166
 167        return 0;
 168}
 169
 170static int xrgb2yuv_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
 171{
 172        return 0;
 173}
 174
 175static int xrgb2yuv_s_ctrl(struct v4l2_ctrl *ctrl)
 176{
 177        struct xrgb2yuv_device *xrgb2yuv =
 178                container_of(ctrl->handler, struct xrgb2yuv_device,
 179                             ctrl_handler);
 180
 181        switch (ctrl->id) {
 182        case V4L2_CID_XILINX_RGB2YUV_YMAX:
 183                xvip_write(&xrgb2yuv->xvip, XRGB2YUV_YMAX, ctrl->val);
 184                return 0;
 185        case V4L2_CID_XILINX_RGB2YUV_YMIN:
 186                xvip_write(&xrgb2yuv->xvip, XRGB2YUV_YMIN, ctrl->val);
 187                return 0;
 188        case V4L2_CID_XILINX_RGB2YUV_CBMAX:
 189                xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CBMAX, ctrl->val);
 190                return 0;
 191        case V4L2_CID_XILINX_RGB2YUV_CBMIN:
 192                xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CBMIN, ctrl->val);
 193                return 0;
 194        case V4L2_CID_XILINX_RGB2YUV_CRMAX:
 195                xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CRMAX, ctrl->val);
 196                return 0;
 197        case V4L2_CID_XILINX_RGB2YUV_CRMIN:
 198                xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CRMIN, ctrl->val);
 199                return 0;
 200        case V4L2_CID_XILINX_RGB2YUV_YOFFSET:
 201                xvip_write(&xrgb2yuv->xvip, XRGB2YUV_YOFFSET, ctrl->val);
 202                return 0;
 203        case V4L2_CID_XILINX_RGB2YUV_CBOFFSET:
 204                xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CBOFFSET, ctrl->val);
 205                return 0;
 206        case V4L2_CID_XILINX_RGB2YUV_CROFFSET:
 207                xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CROFFSET, ctrl->val);
 208                return 0;
 209        case V4L2_CID_XILINX_RGB2YUV_ACOEF:
 210                xvip_write(&xrgb2yuv->xvip, XRGB2YUV_ACOEF, ctrl->val);
 211                return 0;
 212        case V4L2_CID_XILINX_RGB2YUV_BCOEF:
 213                xvip_write(&xrgb2yuv->xvip, XRGB2YUV_BCOEF, ctrl->val);
 214                return 0;
 215        case V4L2_CID_XILINX_RGB2YUV_CCOEF:
 216                xvip_write(&xrgb2yuv->xvip, XRGB2YUV_CCOEF, ctrl->val);
 217                return 0;
 218        case V4L2_CID_XILINX_RGB2YUV_DCOEF:
 219                xvip_write(&xrgb2yuv->xvip, XRGB2YUV_DCOEF, ctrl->val);
 220                return 0;
 221        }
 222
 223        return -EINVAL;
 224}
 225
 226static const struct v4l2_ctrl_ops xrgb2yuv_ctrl_ops = {
 227        .s_ctrl = xrgb2yuv_s_ctrl,
 228};
 229
 230static struct v4l2_subdev_video_ops xrgb2yuv_video_ops = {
 231        .s_stream = xrgb2yuv_s_stream,
 232};
 233
 234static struct v4l2_subdev_pad_ops xrgb2yuv_pad_ops = {
 235        .enum_mbus_code         = xvip_enum_mbus_code,
 236        .enum_frame_size        = xvip_enum_frame_size,
 237        .get_fmt                = xrgb2yuv_get_format,
 238        .set_fmt                = xrgb2yuv_set_format,
 239};
 240
 241static struct v4l2_subdev_ops xrgb2yuv_ops = {
 242        .video  = &xrgb2yuv_video_ops,
 243        .pad    = &xrgb2yuv_pad_ops,
 244};
 245
 246static const struct v4l2_subdev_internal_ops xrgb2yuv_internal_ops = {
 247        .open   = xrgb2yuv_open,
 248        .close  = xrgb2yuv_close,
 249};
 250
 251/*
 252 * Control Configs
 253 */
 254
 255static struct v4l2_ctrl_config xrgb2yuv_ctrls[] = {
 256        {
 257                .ops    = &xrgb2yuv_ctrl_ops,
 258                .id     = V4L2_CID_XILINX_RGB2YUV_YMAX,
 259                .name   = "RGB to YUV: Maximum Y value",
 260                .type   = V4L2_CTRL_TYPE_INTEGER,
 261                .min    = 0,
 262                .max    = (1 << 16) - 1,
 263                .step   = 1,
 264        }, {
 265                .ops    = &xrgb2yuv_ctrl_ops,
 266                .id     = V4L2_CID_XILINX_RGB2YUV_YMIN,
 267                .name   = "RGB to YUV: Minimum Y value",
 268                .type   = V4L2_CTRL_TYPE_INTEGER,
 269                .min    = 0,
 270                .max    = (1 << 16) - 1,
 271                .step   = 1,
 272        }, {
 273                .ops    = &xrgb2yuv_ctrl_ops,
 274                .id     = V4L2_CID_XILINX_RGB2YUV_CBMAX,
 275                .name   = "RGB to YUV: Maximum Cb value",
 276                .type   = V4L2_CTRL_TYPE_INTEGER,
 277                .min    = 0,
 278                .max    = (1 << 16) - 1,
 279                .step   = 1,
 280        }, {
 281                .ops    = &xrgb2yuv_ctrl_ops,
 282                .id     = V4L2_CID_XILINX_RGB2YUV_CBMIN,
 283                .name   = "RGB to YUV: Minimum Cb value",
 284                .type   = V4L2_CTRL_TYPE_INTEGER,
 285                .min    = 0,
 286                .max    = (1 << 16) - 1,
 287                .step   = 1,
 288        }, {
 289                .ops    = &xrgb2yuv_ctrl_ops,
 290                .id     = V4L2_CID_XILINX_RGB2YUV_CRMAX,
 291                .name   = "RGB to YUV: Maximum Cr value",
 292                .type   = V4L2_CTRL_TYPE_INTEGER,
 293                .min    = 0,
 294                .max    = (1 << 16) - 1,
 295                .step   = 1,
 296        }, {
 297                .ops    = &xrgb2yuv_ctrl_ops,
 298                .id     = V4L2_CID_XILINX_RGB2YUV_CRMIN,
 299                .name   = "RGB to YUV: Minimum Cr value",
 300                .type   = V4L2_CTRL_TYPE_INTEGER,
 301                .min    = 0,
 302                .max    = (1 << 16) - 1,
 303                .step   = 1,
 304        }, {
 305                .ops    = &xrgb2yuv_ctrl_ops,
 306                .id     = V4L2_CID_XILINX_RGB2YUV_YOFFSET,
 307                .name   = "RGB to YUV: Luma offset",
 308                .type   = V4L2_CTRL_TYPE_INTEGER,
 309                .min    = 0,
 310                .max    = (1 << 17) - 1,
 311                .step   = 1,
 312        }, {
 313                .ops    = &xrgb2yuv_ctrl_ops,
 314                .id     = V4L2_CID_XILINX_RGB2YUV_CBOFFSET,
 315                .name   = "RGB to YUV: Chroma Cb offset",
 316                .type   = V4L2_CTRL_TYPE_INTEGER,
 317                .min    = 0,
 318                .max    = (1 << 17) - 1,
 319                .step   = 1,
 320        }, {
 321                .ops    = &xrgb2yuv_ctrl_ops,
 322                .id     = V4L2_CID_XILINX_RGB2YUV_CROFFSET,
 323                .name   = "RGB to YUV: Chroma Cr offset",
 324                .type   = V4L2_CTRL_TYPE_INTEGER,
 325                .min    = 0,
 326                .max    = (1 << 17) - 1,
 327                .step   = 1,
 328        }, {
 329                .ops    = &xrgb2yuv_ctrl_ops,
 330                .id     = V4L2_CID_XILINX_RGB2YUV_ACOEF,
 331                .name   = "RGB to YUV: CA coefficient",
 332                .type   = V4L2_CTRL_TYPE_INTEGER,
 333                .min    = -((1 << 17) - 1),
 334                .max    = (1 << 17) - 1,
 335                .step   = 1,
 336        }, {
 337                .ops    = &xrgb2yuv_ctrl_ops,
 338                .id     = V4L2_CID_XILINX_RGB2YUV_BCOEF,
 339                .name   = "RGB to YUV: CB coefficient",
 340                .type   = V4L2_CTRL_TYPE_INTEGER,
 341                .min    = -((1 << 17) - 1),
 342                .max    = (1 << 17) - 1,
 343                .step   = 1,
 344        }, {
 345                .ops    = &xrgb2yuv_ctrl_ops,
 346                .id     = V4L2_CID_XILINX_RGB2YUV_CCOEF,
 347                .name   = "RGB to YUV: CC coefficient",
 348                .type   = V4L2_CTRL_TYPE_INTEGER,
 349                .min    = -((1 << 17) - 1),
 350                .max    = (1 << 17) - 1,
 351                .step   = 1,
 352        }, {
 353                .ops    = &xrgb2yuv_ctrl_ops,
 354                .id     = V4L2_CID_XILINX_RGB2YUV_DCOEF,
 355                .name   = "RGB to YUV: CD coefficient",
 356                .type   = V4L2_CTRL_TYPE_INTEGER,
 357                .min    = -((1 << 17) - 1),
 358                .max    = (1 << 17) - 1,
 359                .step   = 1,
 360        },
 361};
 362
 363/*
 364 * Media Operations
 365 */
 366
 367static const struct media_entity_operations xrgb2yuv_media_ops = {
 368        .link_validate = v4l2_subdev_link_validate,
 369};
 370
 371/*
 372 * Power Management
 373 */
 374
 375static int __maybe_unused xrgb2yuv_pm_suspend(struct device *dev)
 376{
 377        struct xrgb2yuv_device *xrgb2yuv = dev_get_drvdata(dev);
 378
 379        xvip_suspend(&xrgb2yuv->xvip);
 380
 381        return 0;
 382}
 383
 384static int __maybe_unused xrgb2yuv_pm_resume(struct device *dev)
 385{
 386        struct xrgb2yuv_device *xrgb2yuv = dev_get_drvdata(dev);
 387
 388        xvip_resume(&xrgb2yuv->xvip);
 389
 390        return 0;
 391}
 392
 393/*
 394 * Platform Device Driver
 395 */
 396
 397static int xrgb2yuv_parse_of(struct xrgb2yuv_device *xrgb2yuv)
 398{
 399        struct device *dev = xrgb2yuv->xvip.dev;
 400        struct device_node *node = xrgb2yuv->xvip.dev->of_node;
 401        struct device_node *ports;
 402        struct device_node *port;
 403        u32 port_id;
 404        int ret;
 405
 406        ports = of_get_child_by_name(node, "ports");
 407        if (ports == NULL)
 408                ports = node;
 409
 410        /* Get the format description for each pad */
 411        for_each_child_of_node(ports, port) {
 412                if (port->name && (of_node_cmp(port->name, "port") == 0)) {
 413                        const struct xvip_video_format *vip_format;
 414
 415                        vip_format = xvip_of_get_format(port);
 416                        if (IS_ERR(vip_format)) {
 417                                dev_err(dev, "invalid format in DT");
 418                                return PTR_ERR(vip_format);
 419                        }
 420
 421                        ret = of_property_read_u32(port, "reg", &port_id);
 422                        if (ret < 0) {
 423                                dev_err(dev, "no reg in DT");
 424                                return ret;
 425                        }
 426
 427                        if (port_id != 0 && port_id != 1) {
 428                                dev_err(dev, "invalid reg in DT");
 429                                return -EINVAL;
 430                        }
 431
 432                        xrgb2yuv->vip_formats[port_id] = vip_format;
 433                }
 434        }
 435
 436        return 0;
 437}
 438
 439static int xrgb2yuv_probe(struct platform_device *pdev)
 440{
 441        struct xrgb2yuv_device *xrgb2yuv;
 442        struct v4l2_subdev *subdev;
 443        struct v4l2_mbus_framefmt *default_format;
 444        unsigned int i;
 445        int ret;
 446
 447        xrgb2yuv = devm_kzalloc(&pdev->dev, sizeof(*xrgb2yuv), GFP_KERNEL);
 448        if (!xrgb2yuv)
 449                return -ENOMEM;
 450
 451        xrgb2yuv->xvip.dev = &pdev->dev;
 452
 453        ret = xrgb2yuv_parse_of(xrgb2yuv);
 454        if (ret < 0)
 455                return ret;
 456
 457        ret = xvip_init_resources(&xrgb2yuv->xvip);
 458        if (ret < 0)
 459                return ret;
 460
 461        /* Reset and initialize the core */
 462        xvip_reset(&xrgb2yuv->xvip);
 463
 464        /* Initialize V4L2 subdevice and media entity */
 465        subdev = &xrgb2yuv->xvip.subdev;
 466        v4l2_subdev_init(subdev, &xrgb2yuv_ops);
 467        subdev->dev = &pdev->dev;
 468        subdev->internal_ops = &xrgb2yuv_internal_ops;
 469        strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
 470        v4l2_set_subdevdata(subdev, xrgb2yuv);
 471        subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
 472
 473        /* Initialize default and active formats */
 474        default_format = &xrgb2yuv->default_formats[XVIP_PAD_SINK];
 475        default_format->code = xrgb2yuv->vip_formats[XVIP_PAD_SINK]->code;
 476        default_format->field = V4L2_FIELD_NONE;
 477        default_format->colorspace = V4L2_COLORSPACE_SRGB;
 478        xvip_get_frame_size(&xrgb2yuv->xvip, default_format);
 479
 480        xrgb2yuv->formats[XVIP_PAD_SINK] = *default_format;
 481
 482        default_format = &xrgb2yuv->default_formats[XVIP_PAD_SOURCE];
 483        *default_format = xrgb2yuv->default_formats[XVIP_PAD_SINK];
 484        default_format->code = xrgb2yuv->vip_formats[XVIP_PAD_SOURCE]->code;
 485
 486        xrgb2yuv->formats[XVIP_PAD_SOURCE] = *default_format;
 487
 488        xrgb2yuv->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
 489        xrgb2yuv->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
 490        subdev->entity.ops = &xrgb2yuv_media_ops;
 491        ret = media_entity_pads_init(&subdev->entity, 2, xrgb2yuv->pads);
 492        if (ret < 0)
 493                goto error;
 494
 495        v4l2_ctrl_handler_init(&xrgb2yuv->ctrl_handler, 13);
 496
 497        for (i = 0; i < ARRAY_SIZE(xrgb2yuv_ctrls); i++) {
 498                xrgb2yuv_ctrls[i].def = xvip_read(&xrgb2yuv->xvip,
 499                                                  XRGB2YUV_YMAX + i * 4);
 500                v4l2_ctrl_new_custom(&xrgb2yuv->ctrl_handler,
 501                                     &xrgb2yuv_ctrls[i], NULL);
 502        }
 503
 504        if (xrgb2yuv->ctrl_handler.error) {
 505                dev_err(&pdev->dev, "failed to add controls\n");
 506                ret = xrgb2yuv->ctrl_handler.error;
 507                goto error;
 508        }
 509        subdev->ctrl_handler = &xrgb2yuv->ctrl_handler;
 510
 511        platform_set_drvdata(pdev, xrgb2yuv);
 512
 513        xvip_print_version(&xrgb2yuv->xvip);
 514
 515        ret = v4l2_async_register_subdev(subdev);
 516        if (ret < 0) {
 517                dev_err(&pdev->dev, "failed to register subdev\n");
 518                goto error;
 519        }
 520
 521        return 0;
 522
 523error:
 524        v4l2_ctrl_handler_free(&xrgb2yuv->ctrl_handler);
 525        media_entity_cleanup(&subdev->entity);
 526        xvip_cleanup_resources(&xrgb2yuv->xvip);
 527        return ret;
 528}
 529
 530static int xrgb2yuv_remove(struct platform_device *pdev)
 531{
 532        struct xrgb2yuv_device *xrgb2yuv = platform_get_drvdata(pdev);
 533        struct v4l2_subdev *subdev = &xrgb2yuv->xvip.subdev;
 534
 535        v4l2_async_unregister_subdev(subdev);
 536        v4l2_ctrl_handler_free(&xrgb2yuv->ctrl_handler);
 537        media_entity_cleanup(&subdev->entity);
 538
 539        xvip_cleanup_resources(&xrgb2yuv->xvip);
 540
 541        return 0;
 542}
 543
 544static SIMPLE_DEV_PM_OPS(xrgb2yuv_pm_ops, xrgb2yuv_pm_suspend,
 545                         xrgb2yuv_pm_resume);
 546
 547static const struct of_device_id xrgb2yuv_of_id_table[] = {
 548        { .compatible = "xlnx,v-rgb2yuv-7.1" },
 549        { }
 550};
 551MODULE_DEVICE_TABLE(of, xrgb2yuv_of_id_table);
 552
 553static struct platform_driver xrgb2yuv_driver = {
 554        .driver                 = {
 555                .name           = "xilinx-rgb2yuv",
 556                .pm             = &xrgb2yuv_pm_ops,
 557                .of_match_table = xrgb2yuv_of_id_table,
 558        },
 559        .probe                  = xrgb2yuv_probe,
 560        .remove                 = xrgb2yuv_remove,
 561};
 562
 563module_platform_driver(xrgb2yuv_driver);
 564
 565MODULE_DESCRIPTION("Xilinx RGB to YUV Converter Driver");
 566MODULE_LICENSE("GPL v2");
 567