linux/drivers/media/platform/xilinx/xilinx-scaler.c
<<
>>
Prefs
   1/*
   2 * Xilinx Scaler
   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/fixp-arith.h>
  22#include <linux/module.h>
  23#include <linux/of.h>
  24#include <linux/platform_device.h>
  25#include <linux/slab.h>
  26
  27#include <media/v4l2-async.h>
  28#include <media/v4l2-subdev.h>
  29
  30#include "xilinx-vip.h"
  31
  32#define XSCALER_MIN_WIDTH                       32
  33#define XSCALER_MAX_WIDTH                       4096
  34#define XSCALER_MIN_HEIGHT                      32
  35#define XSCALER_MAX_HEIGHT                      4096
  36
  37#define XSCALER_HSF                             0x0100
  38#define XSCALER_VSF                             0x0104
  39#define XSCALER_SF_SHIFT                        20
  40#define XSCALER_SF_MASK                         0xffffff
  41#define XSCALER_SOURCE_SIZE                     0x0108
  42#define XSCALER_SIZE_HORZ_SHIFT                 0
  43#define XSCALER_SIZE_VERT_SHIFT                 16
  44#define XSCALER_SIZE_MASK                       0xfff
  45#define XSCALER_HAPERTURE                       0x010c
  46#define XSCALER_VAPERTURE                       0x0110
  47#define XSCALER_APERTURE_START_SHIFT            0
  48#define XSCALER_APERTURE_END_SHIFT              16
  49#define XSCALER_OUTPUT_SIZE                     0x0114
  50#define XSCALER_COEF_DATA_IN                    0x0134
  51#define XSCALER_COEF_DATA_IN_SHIFT              16
  52
  53/* Fixed point operations */
  54#define FRAC_N  8
  55
  56static inline s16 fixp_new(s16 a)
  57{
  58        return a << FRAC_N;
  59}
  60
  61static inline s16 fixp_mult(s16 a, s16 b)
  62{
  63        return ((s32)(a * b)) >> FRAC_N;
  64}
  65
  66/**
  67 * struct xscaler_device - Xilinx Scaler device structure
  68 * @xvip: Xilinx Video IP device
  69 * @pads: media pads
  70 * @formats: V4L2 media bus formats at the sink and source pads
  71 * @default_formats: default V4L2 media bus formats
  72 * @vip_format: Xilinx Video IP format
  73 * @crop: Active crop rectangle for the sink pad
  74 * @num_hori_taps: number of vertical taps
  75 * @num_vert_taps: number of vertical taps
  76 * @max_num_phases: maximum number of phases
  77 * @separate_yc_coef: separate coefficients for Luma(y) and Chroma(c)
  78 * @separate_hv_coef: separate coefficients for Horizontal(h) and Vertical(v)
  79 */
  80struct xscaler_device {
  81        struct xvip_device xvip;
  82
  83        struct media_pad pads[2];
  84
  85        struct v4l2_mbus_framefmt formats[2];
  86        struct v4l2_mbus_framefmt default_formats[2];
  87        const struct xvip_video_format *vip_format;
  88        struct v4l2_rect crop;
  89
  90        u32 num_hori_taps;
  91        u32 num_vert_taps;
  92        u32 max_num_phases;
  93        bool separate_yc_coef;
  94        bool separate_hv_coef;
  95};
  96
  97static inline struct xscaler_device *to_scaler(struct v4l2_subdev *subdev)
  98{
  99        return container_of(subdev, struct xscaler_device, xvip.subdev);
 100}
 101
 102/*
 103 * V4L2 Subdevice Video Operations
 104 */
 105
 106/**
 107 * lanczos - Lanczos 2D FIR kernel convolution
 108 * @x: phase
 109 * @a: Lanczos kernel size
 110 *
 111 * Return: the coefficient value in fixed point format.
 112 */
 113static s16 lanczos(s16 x, s16 a)
 114{
 115        s16 pi;
 116        s16 numerator;
 117        s16 denominator;
 118        s16 temp;
 119
 120        if (x < -a || x > a)
 121                return 0;
 122        else if (x == 0)
 123                return fixp_new(1);
 124
 125        /* a * sin(pi * x) * sin(pi * x / a) / (pi * pi * x * x) */
 126
 127        pi = (fixp_new(157) << FRAC_N) / fixp_new(50);
 128
 129        if (x < 0)
 130                x = -x;
 131
 132        /* sin(pi * x) */
 133        temp = fixp_mult(fixp_new(180), x);
 134        temp = fixp_sin16(temp >> FRAC_N);
 135
 136        /* a * sin(pi * x) */
 137        numerator = fixp_mult(temp , a);
 138
 139        /* sin(pi * x / a) */
 140        temp = (fixp_mult(fixp_new(180), x) << FRAC_N) / a;
 141        temp = fixp_sin16(temp >> FRAC_N);
 142
 143        /* a * sin(pi * x) * sin(pi * x / a) */
 144        numerator = fixp_mult(temp, numerator);
 145
 146        /* pi * pi * x * x */
 147        denominator = fixp_mult(pi, pi);
 148        temp = fixp_mult(x, x);
 149        denominator = fixp_mult(temp, denominator);
 150
 151        return (numerator << FRAC_N) / denominator;
 152}
 153
 154/**
 155 * xscaler_set_coefs - generate and program the coefficient table
 156 * @xscaler: scaler device
 157 * @taps: maximum coefficient tap index
 158 *
 159 * Generate the coefficient table using Lanczos resampling, and program
 160 * generated coefficients to the scaler. The generated coefficients are
 161 * supposed to work regardless of resolutions.
 162 *
 163 * Return: 0 if the coefficient table is programmed, and -ENOMEM if memory
 164 * allocation for the table fails.
 165 */
 166static int xscaler_set_coefs(struct xscaler_device *xscaler, s16 taps)
 167{
 168        s16 *coef;
 169        s16 dy;
 170        u32 coef_val;
 171        u16 phases = xscaler->max_num_phases;
 172        u16 i;
 173        u16 j;
 174
 175        coef = kcalloc(phases, sizeof(*coef), GFP_KERNEL);
 176        if (!coef)
 177                return -ENOMEM;
 178
 179        for (i = 0; i < phases; i++) {
 180                s16 sum = 0;
 181
 182                dy = ((fixp_new(i) << FRAC_N) / fixp_new(phases));
 183
 184                /* Generate Lanczos coefficients */
 185                for (j = 0; j < taps; j++) {
 186                        coef[j] = lanczos(fixp_new(j - (taps >> 1)) + dy,
 187                                          fixp_new(taps >> 1));
 188                        sum += coef[j];
 189                }
 190
 191                /* Program coefficients */
 192                for (j = 0; j < taps; j += 2) {
 193                        /* Normalize and multiply coefficients */
 194                        coef_val = (((coef[j] << FRAC_N) << (FRAC_N - 2)) /
 195                                    sum) & 0xffff;
 196                        if (j + 1 < taps)
 197                                coef_val |= ((((coef[j + 1] << FRAC_N) <<
 198                                              (FRAC_N - 2)) / sum) & 0xffff) <<
 199                                            16;
 200
 201                        xvip_write(&xscaler->xvip, XSCALER_COEF_DATA_IN,
 202                                   coef_val);
 203                }
 204        }
 205
 206        kfree(coef);
 207
 208        return 0;
 209}
 210
 211static void xscaler_set_aperture(struct xscaler_device *xscaler)
 212{
 213        u16 start;
 214        u16 end;
 215        u32 scale_factor;
 216
 217        xvip_disable_reg_update(&xscaler->xvip);
 218
 219        /* set horizontal aperture */
 220        start = xscaler->crop.left;
 221        end = start + xscaler->crop.width - 1;
 222        xvip_write(&xscaler->xvip, XSCALER_HAPERTURE,
 223                   (end << XSCALER_APERTURE_END_SHIFT) |
 224                   (start << XSCALER_APERTURE_START_SHIFT));
 225
 226        /* set vertical aperture */
 227        start = xscaler->crop.top;
 228        end = start + xscaler->crop.height - 1;
 229        xvip_write(&xscaler->xvip, XSCALER_VAPERTURE,
 230                   (end << XSCALER_APERTURE_END_SHIFT) |
 231                   (start << XSCALER_APERTURE_START_SHIFT));
 232
 233        /* set scaling factors */
 234        scale_factor = ((xscaler->crop.width << XSCALER_SF_SHIFT) /
 235                        xscaler->formats[XVIP_PAD_SOURCE].width) &
 236                       XSCALER_SF_MASK;
 237        xvip_write(&xscaler->xvip, XSCALER_HSF, scale_factor);
 238
 239        scale_factor = ((xscaler->crop.height << XSCALER_SF_SHIFT) /
 240                        xscaler->formats[XVIP_PAD_SOURCE].height) &
 241                       XSCALER_SF_MASK;
 242        xvip_write(&xscaler->xvip, XSCALER_VSF, scale_factor);
 243
 244        xvip_enable_reg_update(&xscaler->xvip);
 245}
 246
 247static int xscaler_s_stream(struct v4l2_subdev *subdev, int enable)
 248{
 249        struct xscaler_device *xscaler = to_scaler(subdev);
 250        u32 width;
 251        u32 height;
 252
 253        if (!enable) {
 254                xvip_stop(&xscaler->xvip);
 255                return 0;
 256        }
 257
 258        /* set input width / height */
 259        width = xscaler->formats[XVIP_PAD_SINK].width;
 260        height = xscaler->formats[XVIP_PAD_SINK].height;
 261        xvip_write(&xscaler->xvip, XSCALER_SOURCE_SIZE,
 262                   (height << XSCALER_SIZE_VERT_SHIFT) |
 263                   (width << XSCALER_SIZE_HORZ_SHIFT));
 264
 265        /* set output width / height */
 266        width = xscaler->formats[XVIP_PAD_SOURCE].width;
 267        height = xscaler->formats[XVIP_PAD_SOURCE].height;
 268        xvip_write(&xscaler->xvip, XSCALER_OUTPUT_SIZE,
 269                   (height << XSCALER_SIZE_VERT_SHIFT) |
 270                   (width << XSCALER_SIZE_HORZ_SHIFT));
 271
 272        /* set aperture */
 273        xscaler_set_aperture(xscaler);
 274
 275        xvip_start(&xscaler->xvip);
 276
 277        return 0;
 278}
 279
 280/*
 281 * V4L2 Subdevice Pad Operations
 282 */
 283
 284static int xscaler_enum_frame_size(struct v4l2_subdev *subdev,
 285                                   struct v4l2_subdev_pad_config *cfg,
 286                                   struct v4l2_subdev_frame_size_enum *fse)
 287{
 288        struct v4l2_mbus_framefmt *format;
 289
 290        format = v4l2_subdev_get_try_format(subdev, cfg, fse->pad);
 291
 292        if (fse->index || fse->code != format->code)
 293                return -EINVAL;
 294
 295        fse->min_width = XSCALER_MIN_WIDTH;
 296        fse->max_width = XSCALER_MAX_WIDTH;
 297        fse->min_height = XSCALER_MIN_HEIGHT;
 298        fse->max_height = XSCALER_MAX_HEIGHT;
 299
 300        return 0;
 301}
 302
 303static struct v4l2_mbus_framefmt *
 304__xscaler_get_pad_format(struct xscaler_device *xscaler,
 305                         struct v4l2_subdev_pad_config *cfg,
 306                         unsigned int pad, u32 which)
 307{
 308        switch (which) {
 309        case V4L2_SUBDEV_FORMAT_TRY:
 310                return v4l2_subdev_get_try_format(&xscaler->xvip.subdev, cfg,
 311                                                  pad);
 312        case V4L2_SUBDEV_FORMAT_ACTIVE:
 313                return &xscaler->formats[pad];
 314        default:
 315                return NULL;
 316        }
 317}
 318
 319static struct v4l2_rect *__xscaler_get_crop(struct xscaler_device *xscaler,
 320                                            struct v4l2_subdev_pad_config *cfg,
 321                                            u32 which)
 322{
 323        switch (which) {
 324        case V4L2_SUBDEV_FORMAT_TRY:
 325                return v4l2_subdev_get_try_crop(&xscaler->xvip.subdev, cfg,
 326                                                XVIP_PAD_SINK);
 327        case V4L2_SUBDEV_FORMAT_ACTIVE:
 328                return &xscaler->crop;
 329        default:
 330                return NULL;
 331        }
 332}
 333
 334static int xscaler_get_format(struct v4l2_subdev *subdev,
 335                              struct v4l2_subdev_pad_config *cfg,
 336                              struct v4l2_subdev_format *fmt)
 337{
 338        struct xscaler_device *xscaler = to_scaler(subdev);
 339
 340        fmt->format = *__xscaler_get_pad_format(xscaler, cfg, fmt->pad,
 341                                                fmt->which);
 342
 343        return 0;
 344}
 345
 346static void xscaler_try_crop(const struct v4l2_mbus_framefmt *sink,
 347                             struct v4l2_rect *crop)
 348{
 349
 350        crop->left = min_t(u32, crop->left, sink->width - XSCALER_MIN_WIDTH);
 351        crop->top = min_t(u32, crop->top, sink->height - XSCALER_MIN_HEIGHT);
 352        crop->width = clamp_t(u32, crop->width, XSCALER_MIN_WIDTH,
 353                              sink->width - crop->left);
 354        crop->height = clamp_t(u32, crop->height, XSCALER_MIN_HEIGHT,
 355                               sink->height - crop->top);
 356}
 357
 358static int xscaler_set_format(struct v4l2_subdev *subdev,
 359                              struct v4l2_subdev_pad_config *cfg,
 360                              struct v4l2_subdev_format *fmt)
 361{
 362        struct xscaler_device *xscaler = to_scaler(subdev);
 363        struct v4l2_mbus_framefmt *format;
 364        struct v4l2_rect *crop;
 365
 366        format = __xscaler_get_pad_format(xscaler, cfg, fmt->pad, fmt->which);
 367
 368        format->width = clamp_t(unsigned int, fmt->format.width,
 369                                  XSCALER_MIN_WIDTH, XSCALER_MAX_WIDTH);
 370        format->height = clamp_t(unsigned int, fmt->format.height,
 371                                   XSCALER_MIN_HEIGHT, XSCALER_MAX_HEIGHT);
 372
 373        fmt->format = *format;
 374
 375        if (fmt->pad == XVIP_PAD_SINK) {
 376                /* Set the crop rectangle to the full frame */
 377                crop = __xscaler_get_crop(xscaler, cfg, fmt->which);
 378                crop->left = 0;
 379                crop->top = 0;
 380                crop->width = fmt->format.width;
 381                crop->height = fmt->format.height;
 382        }
 383
 384        return 0;
 385}
 386
 387static int xscaler_get_selection(struct v4l2_subdev *subdev,
 388                                 struct v4l2_subdev_pad_config *cfg,
 389                                 struct v4l2_subdev_selection *sel)
 390{
 391        struct xscaler_device *xscaler = to_scaler(subdev);
 392        struct v4l2_mbus_framefmt *format;
 393
 394        if (sel->pad != XVIP_PAD_SINK)
 395                return -EINVAL;
 396
 397        switch (sel->target) {
 398        case V4L2_SEL_TGT_CROP_BOUNDS:
 399                format = __xscaler_get_pad_format(xscaler, cfg, XVIP_PAD_SINK,
 400                                                  sel->which);
 401                sel->r.left = 0;
 402                sel->r.top = 0;
 403                sel->r.width = format->width;
 404                sel->r.height = format->height;
 405                return 0;
 406        case V4L2_SEL_TGT_CROP:
 407                sel->r = *__xscaler_get_crop(xscaler, cfg, sel->which);
 408                return 0;
 409        default:
 410                return -EINVAL;
 411        }
 412}
 413
 414static int xscaler_set_selection(struct v4l2_subdev *subdev,
 415                                 struct v4l2_subdev_pad_config *cfg,
 416                                 struct v4l2_subdev_selection *sel)
 417{
 418        struct xscaler_device *xscaler = to_scaler(subdev);
 419        struct v4l2_mbus_framefmt *format;
 420
 421        if ((sel->target != V4L2_SEL_TGT_CROP) || (sel->pad != XVIP_PAD_SINK))
 422                return -EINVAL;
 423
 424        format = __xscaler_get_pad_format(xscaler, cfg, XVIP_PAD_SINK,
 425                                          sel->which);
 426        xscaler_try_crop(format, &sel->r);
 427        *__xscaler_get_crop(xscaler, cfg, sel->which) = sel->r;
 428
 429        return 0;
 430}
 431
 432/*
 433 * V4L2 Subdevice Operations
 434 */
 435
 436static int xscaler_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
 437{
 438        struct xscaler_device *xscaler = to_scaler(subdev);
 439        struct v4l2_mbus_framefmt *format;
 440
 441        /* Initialize with default formats */
 442        format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SINK);
 443        *format = xscaler->default_formats[XVIP_PAD_SINK];
 444
 445        format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SOURCE);
 446        *format = xscaler->default_formats[XVIP_PAD_SOURCE];
 447
 448        return 0;
 449}
 450
 451static int xscaler_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
 452{
 453        return 0;
 454}
 455
 456static struct v4l2_subdev_video_ops xscaler_video_ops = {
 457        .s_stream = xscaler_s_stream,
 458};
 459
 460static struct v4l2_subdev_pad_ops xscaler_pad_ops = {
 461        .enum_mbus_code         = xvip_enum_mbus_code,
 462        .enum_frame_size        = xscaler_enum_frame_size,
 463        .get_fmt                = xscaler_get_format,
 464        .set_fmt                = xscaler_set_format,
 465        .get_selection          = xscaler_get_selection,
 466        .set_selection          = xscaler_set_selection,
 467};
 468
 469static struct v4l2_subdev_ops xscaler_ops = {
 470        .video  = &xscaler_video_ops,
 471        .pad    = &xscaler_pad_ops,
 472};
 473
 474static const struct v4l2_subdev_internal_ops xscaler_internal_ops = {
 475        .open   = xscaler_open,
 476        .close  = xscaler_close,
 477};
 478
 479/*
 480 * Media Operations
 481 */
 482
 483static const struct media_entity_operations xscaler_media_ops = {
 484        .link_validate = v4l2_subdev_link_validate,
 485};
 486
 487/*
 488 * Power Management
 489 */
 490
 491static int __maybe_unused xscaler_pm_suspend(struct device *dev)
 492{
 493        struct xscaler_device *xscaler = dev_get_drvdata(dev);
 494
 495        xvip_suspend(&xscaler->xvip);
 496
 497        return 0;
 498}
 499
 500static int __maybe_unused xscaler_pm_resume(struct device *dev)
 501{
 502        struct xscaler_device *xscaler = dev_get_drvdata(dev);
 503
 504        xvip_resume(&xscaler->xvip);
 505
 506        return 0;
 507}
 508
 509/*
 510 * Platform Device Driver
 511 */
 512
 513static int xscaler_parse_of(struct xscaler_device *xscaler)
 514{
 515        struct device *dev = xscaler->xvip.dev;
 516        struct device_node *node = xscaler->xvip.dev->of_node;
 517        struct device_node *ports;
 518        struct device_node *port;
 519        int ret;
 520
 521        ports = of_get_child_by_name(node, "ports");
 522        if (ports == NULL)
 523                ports = node;
 524
 525        /* Get the format description for each pad */
 526        for_each_child_of_node(ports, port) {
 527                if (port->name && (of_node_cmp(port->name, "port") == 0)) {
 528                        const struct xvip_video_format *vip_format;
 529
 530                        vip_format = xvip_of_get_format(port);
 531                        if (IS_ERR(vip_format)) {
 532                                dev_err(dev, "invalid format in DT");
 533                                return PTR_ERR(vip_format);
 534                        }
 535
 536                        if (!xscaler->vip_format) {
 537                                xscaler->vip_format = vip_format;
 538                        } else if (xscaler->vip_format != vip_format) {
 539                                dev_err(dev, "in/out format mismatch in DT");
 540                                return -EINVAL;
 541                        }
 542                }
 543        }
 544
 545        ret = of_property_read_u32(node, "xlnx,num-hori-taps",
 546                                   &xscaler->num_hori_taps);
 547        if (ret < 0)
 548                return ret;
 549
 550        ret = of_property_read_u32(node, "xlnx,num-vert-taps",
 551                                   &xscaler->num_vert_taps);
 552        if (ret < 0)
 553                return ret;
 554
 555        ret = of_property_read_u32(node, "xlnx,max-num-phases",
 556                                   &xscaler->max_num_phases);
 557        if (ret < 0)
 558                return ret;
 559
 560        xscaler->separate_yc_coef =
 561                of_property_read_bool(node, "xlnx,separate-yc-coef");
 562
 563        xscaler->separate_hv_coef =
 564                of_property_read_bool(node, "xlnx,separate-hv-coef");
 565
 566        return 0;
 567}
 568
 569static int xscaler_probe(struct platform_device *pdev)
 570{
 571        struct xscaler_device *xscaler;
 572        struct v4l2_subdev *subdev;
 573        struct v4l2_mbus_framefmt *default_format;
 574        u32 size;
 575        int ret;
 576
 577        xscaler = devm_kzalloc(&pdev->dev, sizeof(*xscaler), GFP_KERNEL);
 578        if (!xscaler)
 579                return -ENOMEM;
 580
 581        xscaler->xvip.dev = &pdev->dev;
 582
 583        ret = xscaler_parse_of(xscaler);
 584        if (ret < 0)
 585                return ret;
 586
 587        ret = xvip_init_resources(&xscaler->xvip);
 588        if (ret < 0)
 589                return ret;
 590
 591        /* Reset and initialize the core */
 592        xvip_reset(&xscaler->xvip);
 593
 594        /* Initialize V4L2 subdevice and media entity */
 595        subdev = &xscaler->xvip.subdev;
 596        v4l2_subdev_init(subdev, &xscaler_ops);
 597        subdev->dev = &pdev->dev;
 598        subdev->internal_ops = &xscaler_internal_ops;
 599        strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
 600        v4l2_set_subdevdata(subdev, xscaler);
 601        subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
 602
 603        /* Initialize default and active formats */
 604        default_format = &xscaler->default_formats[XVIP_PAD_SINK];
 605        default_format->code = xscaler->vip_format->code;
 606        default_format->field = V4L2_FIELD_NONE;
 607        default_format->colorspace = V4L2_COLORSPACE_SRGB;
 608        size = xvip_read(&xscaler->xvip, XSCALER_SOURCE_SIZE);
 609        default_format->width = (size >> XSCALER_SIZE_HORZ_SHIFT) &
 610                                 XSCALER_SIZE_MASK;
 611        default_format->height = (size >> XSCALER_SIZE_VERT_SHIFT) &
 612                                 XSCALER_SIZE_MASK;
 613
 614        xscaler->formats[XVIP_PAD_SINK] = *default_format;
 615
 616        default_format = &xscaler->default_formats[XVIP_PAD_SOURCE];
 617        *default_format = xscaler->default_formats[XVIP_PAD_SINK];
 618        size = xvip_read(&xscaler->xvip, XSCALER_OUTPUT_SIZE);
 619        default_format->width = (size >> XSCALER_SIZE_HORZ_SHIFT) &
 620                                 XSCALER_SIZE_MASK;
 621        default_format->height = (size >> XSCALER_SIZE_VERT_SHIFT) &
 622                                 XSCALER_SIZE_MASK;
 623
 624        xscaler->formats[XVIP_PAD_SOURCE] = *default_format;
 625
 626        xscaler->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
 627        xscaler->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
 628        subdev->entity.ops = &xscaler_media_ops;
 629
 630        ret = media_entity_pads_init(&subdev->entity, 2, xscaler->pads);
 631        if (ret < 0)
 632                goto error;
 633
 634        platform_set_drvdata(pdev, xscaler);
 635
 636        xvip_print_version(&xscaler->xvip);
 637
 638        ret = xscaler_set_coefs(xscaler, (s16)xscaler->num_hori_taps);
 639        if (ret < 0)
 640                goto error;
 641
 642        if (xscaler->separate_hv_coef) {
 643                ret = xscaler_set_coefs(xscaler, (s16)xscaler->num_vert_taps);
 644                if (ret < 0)
 645                        goto error;
 646        }
 647
 648        if (xscaler->separate_yc_coef) {
 649                ret = xscaler_set_coefs(xscaler, (s16)xscaler->num_hori_taps);
 650                if (ret < 0)
 651                        goto error;
 652
 653                if (xscaler->separate_hv_coef) {
 654                        ret = xscaler_set_coefs(xscaler,
 655                                                (s16)xscaler->num_vert_taps);
 656                        if (ret < 0)
 657                                goto error;
 658                }
 659        }
 660
 661        ret = v4l2_async_register_subdev(subdev);
 662        if (ret < 0) {
 663                dev_err(&pdev->dev, "failed to register subdev\n");
 664                goto error;
 665        }
 666
 667        return 0;
 668
 669error:
 670        media_entity_cleanup(&subdev->entity);
 671        xvip_cleanup_resources(&xscaler->xvip);
 672        return ret;
 673}
 674
 675static int xscaler_remove(struct platform_device *pdev)
 676{
 677        struct xscaler_device *xscaler = platform_get_drvdata(pdev);
 678        struct v4l2_subdev *subdev = &xscaler->xvip.subdev;
 679
 680        v4l2_async_unregister_subdev(subdev);
 681        media_entity_cleanup(&subdev->entity);
 682
 683        xvip_cleanup_resources(&xscaler->xvip);
 684
 685        return 0;
 686}
 687
 688static SIMPLE_DEV_PM_OPS(xscaler_pm_ops, xscaler_pm_suspend, xscaler_pm_resume);
 689
 690static const struct of_device_id xscaler_of_id_table[] = {
 691        { .compatible = "xlnx,v-scaler-8.1" },
 692        { }
 693};
 694MODULE_DEVICE_TABLE(of, xscaler_of_id_table);
 695
 696static struct platform_driver xscaler_driver = {
 697        .driver                 = {
 698                .name           = "xilinx-scaler",
 699                .of_match_table = xscaler_of_id_table,
 700        },
 701        .probe                  = xscaler_probe,
 702        .remove                 = xscaler_remove,
 703};
 704
 705module_platform_driver(xscaler_driver);
 706
 707MODULE_DESCRIPTION("Xilinx Scaler Driver");
 708MODULE_LICENSE("GPL v2");
 709