linux/drivers/media/platform/xilinx/xilinx-cresample.c
<<
>>
Prefs
   1/*
   2 * Xilinx Chroma Resampler
   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 XCRESAMPLE_ENCODING                     0x100
  33#define XCRESAMPLE_ENCODING_FIELD               (1 << 7)
  34#define XCRESAMPLE_ENCODING_CHROMA              (1 << 8)
  35
  36/**
  37 * struct xcresample_device - Xilinx CRESAMPLE device structure
  38 * @xvip: Xilinx Video IP device
  39 * @pads: media pads
  40 * @formats: V4L2 media bus formats at the sink and source pads
  41 * @default_formats: default V4L2 media bus formats
  42 * @vip_formats: Xilinx Video IP formats
  43 * @ctrl_handler: control handler
  44 */
  45struct xcresample_device {
  46        struct xvip_device xvip;
  47
  48        struct media_pad pads[2];
  49
  50        struct v4l2_mbus_framefmt formats[2];
  51        struct v4l2_mbus_framefmt default_formats[2];
  52        const struct xvip_video_format *vip_formats[2];
  53
  54        struct v4l2_ctrl_handler ctrl_handler;
  55};
  56
  57static inline struct xcresample_device *to_cresample(struct v4l2_subdev *subdev)
  58{
  59        return container_of(subdev, struct xcresample_device, xvip.subdev);
  60}
  61
  62/*
  63 * V4L2 Subdevice Video Operations
  64 */
  65
  66static int xcresample_s_stream(struct v4l2_subdev *subdev, int enable)
  67{
  68        struct xcresample_device *xcresample = to_cresample(subdev);
  69
  70        if (!enable) {
  71                xvip_stop(&xcresample->xvip);
  72                return 0;
  73        }
  74
  75        xvip_set_frame_size(&xcresample->xvip,
  76                            &xcresample->formats[XVIP_PAD_SINK]);
  77
  78        xvip_start(&xcresample->xvip);
  79
  80        return 0;
  81}
  82
  83/*
  84 * V4L2 Subdevice Pad Operations
  85 */
  86
  87static struct v4l2_mbus_framefmt *
  88__xcresample_get_pad_format(struct xcresample_device *xcresample,
  89                            struct v4l2_subdev_pad_config *cfg,
  90                            unsigned int pad, u32 which)
  91{
  92        switch (which) {
  93        case V4L2_SUBDEV_FORMAT_TRY:
  94                return v4l2_subdev_get_try_format(&xcresample->xvip.subdev, cfg,
  95                                                  pad);
  96        case V4L2_SUBDEV_FORMAT_ACTIVE:
  97                return &xcresample->formats[pad];
  98        default:
  99                return NULL;
 100        }
 101}
 102
 103static int xcresample_get_format(struct v4l2_subdev *subdev,
 104                                 struct v4l2_subdev_pad_config *cfg,
 105                                 struct v4l2_subdev_format *fmt)
 106{
 107        struct xcresample_device *xcresample = to_cresample(subdev);
 108
 109        fmt->format = *__xcresample_get_pad_format(xcresample, cfg, fmt->pad,
 110                                                   fmt->which);
 111
 112        return 0;
 113}
 114
 115static int xcresample_set_format(struct v4l2_subdev *subdev,
 116                                 struct v4l2_subdev_pad_config *cfg,
 117                                 struct v4l2_subdev_format *fmt)
 118{
 119        struct xcresample_device *xcresample = to_cresample(subdev);
 120        struct v4l2_mbus_framefmt *format;
 121
 122        format = __xcresample_get_pad_format(xcresample, cfg, fmt->pad,
 123                                             fmt->which);
 124
 125        if (fmt->pad == XVIP_PAD_SOURCE) {
 126                fmt->format = *format;
 127                return 0;
 128        }
 129
 130        xvip_set_format_size(format, fmt);
 131
 132        fmt->format = *format;
 133
 134        /* Propagate the format to the source pad. */
 135        format = __xcresample_get_pad_format(xcresample, cfg, XVIP_PAD_SOURCE,
 136                                             fmt->which);
 137
 138        xvip_set_format_size(format, fmt);
 139
 140        return 0;
 141}
 142
 143/*
 144 * V4L2 Subdevice Operations
 145 */
 146
 147static int xcresample_open(struct v4l2_subdev *subdev,
 148                           struct v4l2_subdev_fh *fh)
 149{
 150        struct xcresample_device *xcresample = to_cresample(subdev);
 151        struct v4l2_mbus_framefmt *format;
 152
 153        /* Initialize with default formats */
 154        format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SINK);
 155        *format = xcresample->default_formats[XVIP_PAD_SINK];
 156
 157        format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SOURCE);
 158        *format = xcresample->default_formats[XVIP_PAD_SOURCE];
 159
 160        return 0;
 161}
 162
 163static int xcresample_close(struct v4l2_subdev *subdev,
 164                            struct v4l2_subdev_fh *fh)
 165{
 166        return 0;
 167}
 168
 169static int xcresample_s_ctrl(struct v4l2_ctrl *ctrl)
 170{
 171        struct xcresample_device *xcresample =
 172                container_of(ctrl->handler, struct xcresample_device,
 173                             ctrl_handler);
 174        switch (ctrl->id) {
 175        case V4L2_CID_XILINX_CRESAMPLE_FIELD_PARITY:
 176                xvip_clr_or_set(&xcresample->xvip, XCRESAMPLE_ENCODING,
 177                                XCRESAMPLE_ENCODING_FIELD, ctrl->val);
 178                return 0;
 179        case V4L2_CID_XILINX_CRESAMPLE_CHROMA_PARITY:
 180                xvip_clr_or_set(&xcresample->xvip, XCRESAMPLE_ENCODING,
 181                                XCRESAMPLE_ENCODING_CHROMA, ctrl->val);
 182                return 0;
 183        }
 184
 185        return -EINVAL;
 186
 187}
 188
 189static const struct v4l2_ctrl_ops xcresample_ctrl_ops = {
 190        .s_ctrl = xcresample_s_ctrl,
 191};
 192
 193static struct v4l2_subdev_video_ops xcresample_video_ops = {
 194        .s_stream = xcresample_s_stream,
 195};
 196
 197static struct v4l2_subdev_pad_ops xcresample_pad_ops = {
 198        .enum_mbus_code         = xvip_enum_mbus_code,
 199        .enum_frame_size        = xvip_enum_frame_size,
 200        .get_fmt                = xcresample_get_format,
 201        .set_fmt                = xcresample_set_format,
 202};
 203
 204static struct v4l2_subdev_ops xcresample_ops = {
 205        .video  = &xcresample_video_ops,
 206        .pad    = &xcresample_pad_ops,
 207};
 208
 209static const struct v4l2_subdev_internal_ops xcresample_internal_ops = {
 210        .open   = xcresample_open,
 211        .close  = xcresample_close,
 212};
 213
 214/*
 215 * Control Configs
 216 */
 217
 218static const char *const xcresample_parity_string[] = {
 219        "Even",
 220        "Odd",
 221};
 222
 223static struct v4l2_ctrl_config xcresample_field = {
 224        .ops    = &xcresample_ctrl_ops,
 225        .id     = V4L2_CID_XILINX_CRESAMPLE_FIELD_PARITY,
 226        .name   = "Chroma Resampler: Encoding Field Parity",
 227        .type   = V4L2_CTRL_TYPE_MENU,
 228        .min    = 0,
 229        .max    = 1,
 230        .qmenu  = xcresample_parity_string,
 231};
 232
 233static struct v4l2_ctrl_config xcresample_chroma = {
 234        .ops    = &xcresample_ctrl_ops,
 235        .id     = V4L2_CID_XILINX_CRESAMPLE_CHROMA_PARITY,
 236        .name   = "Chroma Resampler: Encoding Chroma Parity",
 237        .type   = V4L2_CTRL_TYPE_MENU,
 238        .min    = 0,
 239        .max    = 1,
 240        .qmenu  = xcresample_parity_string,
 241};
 242
 243/*
 244 * Media Operations
 245 */
 246
 247static const struct media_entity_operations xcresample_media_ops = {
 248        .link_validate = v4l2_subdev_link_validate,
 249};
 250
 251/*
 252 * Power Management
 253 */
 254
 255static int __maybe_unused xcresample_pm_suspend(struct device *dev)
 256{
 257        struct xcresample_device *xcresample = dev_get_drvdata(dev);
 258
 259        xvip_suspend(&xcresample->xvip);
 260
 261        return 0;
 262}
 263
 264static int __maybe_unused xcresample_pm_resume(struct device *dev)
 265{
 266        struct xcresample_device *xcresample = dev_get_drvdata(dev);
 267
 268        xvip_resume(&xcresample->xvip);
 269
 270        return 0;
 271}
 272
 273/*
 274 * Platform Device Driver
 275 */
 276
 277static int xcresample_parse_of(struct xcresample_device *xcresample)
 278{
 279        struct device *dev = xcresample->xvip.dev;
 280        struct device_node *node = xcresample->xvip.dev->of_node;
 281        struct device_node *ports;
 282        struct device_node *port;
 283        u32 port_id;
 284        int ret;
 285
 286        ports = of_get_child_by_name(node, "ports");
 287        if (ports == NULL)
 288                ports = node;
 289
 290        /* Get the format description for each pad */
 291        for_each_child_of_node(ports, port) {
 292                if (port->name && (of_node_cmp(port->name, "port") == 0)) {
 293                        const struct xvip_video_format *vip_format;
 294
 295                        vip_format = xvip_of_get_format(port);
 296                        if (IS_ERR(vip_format)) {
 297                                dev_err(dev, "invalid format in DT");
 298                                return PTR_ERR(vip_format);
 299                        }
 300
 301                        ret = of_property_read_u32(port, "reg", &port_id);
 302                        if (ret < 0) {
 303                                dev_err(dev, "no reg in DT");
 304                                return ret;
 305                        }
 306
 307                        if (port_id != 0 && port_id != 1) {
 308                                dev_err(dev, "invalid reg in DT");
 309                                return -EINVAL;
 310                        }
 311
 312                        xcresample->vip_formats[port_id] = vip_format;
 313                }
 314        }
 315
 316        return 0;
 317}
 318
 319static int xcresample_probe(struct platform_device *pdev)
 320{
 321        struct xcresample_device *xcresample;
 322        struct v4l2_subdev *subdev;
 323        struct v4l2_mbus_framefmt *default_format;
 324        int ret;
 325
 326        xcresample = devm_kzalloc(&pdev->dev, sizeof(*xcresample), GFP_KERNEL);
 327        if (!xcresample)
 328                return -ENOMEM;
 329
 330        xcresample->xvip.dev = &pdev->dev;
 331
 332        ret = xcresample_parse_of(xcresample);
 333        if (ret < 0)
 334                return ret;
 335
 336        ret = xvip_init_resources(&xcresample->xvip);
 337        if (ret < 0)
 338                return ret;
 339
 340        /* Reset and initialize the core */
 341        xvip_reset(&xcresample->xvip);
 342
 343        /* Initialize V4L2 subdevice and media entity */
 344        subdev = &xcresample->xvip.subdev;
 345        v4l2_subdev_init(subdev, &xcresample_ops);
 346        subdev->dev = &pdev->dev;
 347        subdev->internal_ops = &xcresample_internal_ops;
 348        strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
 349        v4l2_set_subdevdata(subdev, xcresample);
 350        subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
 351
 352        /* Initialize default and active formats */
 353        default_format = &xcresample->default_formats[XVIP_PAD_SINK];
 354        default_format->code = xcresample->vip_formats[XVIP_PAD_SINK]->code;
 355        default_format->field = V4L2_FIELD_NONE;
 356        default_format->colorspace = V4L2_COLORSPACE_SRGB;
 357        xvip_get_frame_size(&xcresample->xvip, default_format);
 358
 359        xcresample->formats[XVIP_PAD_SINK] = *default_format;
 360
 361        default_format = &xcresample->default_formats[XVIP_PAD_SOURCE];
 362        *default_format = xcresample->default_formats[XVIP_PAD_SINK];
 363        default_format->code = xcresample->vip_formats[XVIP_PAD_SOURCE]->code;
 364
 365        xcresample->formats[XVIP_PAD_SOURCE] = *default_format;
 366
 367        xcresample->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
 368        xcresample->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
 369        subdev->entity.ops = &xcresample_media_ops;
 370        ret = media_entity_pads_init(&subdev->entity, 2, xcresample->pads);
 371        if (ret < 0)
 372                goto error;
 373
 374        v4l2_ctrl_handler_init(&xcresample->ctrl_handler, 2);
 375        xcresample_field.def =
 376                (xvip_read(&xcresample->xvip, XCRESAMPLE_ENCODING) &
 377                 XCRESAMPLE_ENCODING_FIELD) ? 1 : 0;
 378        v4l2_ctrl_new_custom(&xcresample->ctrl_handler, &xcresample_field,
 379                             NULL);
 380        xcresample_chroma.def =
 381                (xvip_read(&xcresample->xvip, XCRESAMPLE_ENCODING) &
 382                 XCRESAMPLE_ENCODING_CHROMA) ? 1 : 0;
 383        v4l2_ctrl_new_custom(&xcresample->ctrl_handler, &xcresample_chroma,
 384                             NULL);
 385        if (xcresample->ctrl_handler.error) {
 386                dev_err(&pdev->dev, "failed to add controls\n");
 387                ret = xcresample->ctrl_handler.error;
 388                goto error;
 389        }
 390        subdev->ctrl_handler = &xcresample->ctrl_handler;
 391
 392        platform_set_drvdata(pdev, xcresample);
 393
 394        xvip_print_version(&xcresample->xvip);
 395
 396        ret = v4l2_async_register_subdev(subdev);
 397        if (ret < 0) {
 398                dev_err(&pdev->dev, "failed to register subdev\n");
 399                goto error;
 400        }
 401
 402        return 0;
 403
 404error:
 405        v4l2_ctrl_handler_free(&xcresample->ctrl_handler);
 406        media_entity_cleanup(&subdev->entity);
 407        xvip_cleanup_resources(&xcresample->xvip);
 408        return ret;
 409}
 410
 411static int xcresample_remove(struct platform_device *pdev)
 412{
 413        struct xcresample_device *xcresample = platform_get_drvdata(pdev);
 414        struct v4l2_subdev *subdev = &xcresample->xvip.subdev;
 415
 416        v4l2_async_unregister_subdev(subdev);
 417        v4l2_ctrl_handler_free(&xcresample->ctrl_handler);
 418        media_entity_cleanup(&subdev->entity);
 419
 420        xvip_cleanup_resources(&xcresample->xvip);
 421
 422        return 0;
 423}
 424
 425static SIMPLE_DEV_PM_OPS(xcresample_pm_ops, xcresample_pm_suspend,
 426                         xcresample_pm_resume);
 427
 428static const struct of_device_id xcresample_of_id_table[] = {
 429        { .compatible = "xlnx,v-cresample-4.0" },
 430        { }
 431};
 432MODULE_DEVICE_TABLE(of, xcresample_of_id_table);
 433
 434static struct platform_driver xcresample_driver = {
 435        .driver                 = {
 436                .name           = "xilinx-cresample",
 437                .pm             = &xcresample_pm_ops,
 438                .of_match_table = xcresample_of_id_table,
 439        },
 440        .probe                  = xcresample_probe,
 441        .remove                 = xcresample_remove,
 442};
 443
 444module_platform_driver(xcresample_driver);
 445
 446MODULE_DESCRIPTION("Xilinx Chroma Resampler Driver");
 447MODULE_LICENSE("GPL v2");
 448