linux/drivers/media/platform/xilinx/xilinx-remapper.c
<<
>>
Prefs
   1/*
   2 * Xilinx Video Remapper
   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 program is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License version 2 as
  12 * published by the Free Software Foundation.
  13 */
  14
  15#include <linux/clk.h>
  16#include <linux/module.h>
  17#include <linux/of.h>
  18#include <linux/platform_device.h>
  19#include <linux/slab.h>
  20
  21#include <media/v4l2-async.h>
  22#include <media/v4l2-subdev.h>
  23
  24#include "xilinx-vip.h"
  25
  26#define XREMAP_MIN_WIDTH                        1
  27#define XREMAP_DEF_WIDTH                        1920
  28#define XREMAP_MAX_WIDTH                        65535
  29#define XREMAP_MIN_HEIGHT                       1
  30#define XREMAP_DEF_HEIGHT                       1080
  31#define XREMAP_MAX_HEIGHT                       65535
  32
  33#define XREMAP_PAD_SINK                         0
  34#define XREMAP_PAD_SOURCE                       1
  35
  36/**
  37 * struct xremap_mapping_output - Output format description
  38 * @code: media bus pixel core after remapping
  39 * @num_components: number of pixel components after remapping
  40 * @component_maps: configuration array corresponding to this output
  41 */
  42struct xremap_mapping_output {
  43        u32 code;
  44        unsigned int num_components;
  45        unsigned int component_maps[4];
  46};
  47
  48/**
  49 * struct xremap_mapping - Input-output remapping description
  50 * @code: media bus pixel code before remapping
  51 * @width: video bus width in bits
  52 * @num_components: number of pixel components before remapping
  53 * @outputs: array of possible output formats
  54 */
  55struct xremap_mapping {
  56        u32 code;
  57        unsigned int width;
  58        unsigned int num_components;
  59        const struct xremap_mapping_output *outputs;
  60};
  61
  62/**
  63 * struct xremap_device - Xilinx Test Pattern Generator device structure
  64 * @xvip: Xilinx Video IP device
  65 * @pads: media pads
  66 * @formats: V4L2 media bus formats at the sink and source pads
  67 * @config: device configuration parsed from its DT node
  68 * @config.width: video bus width in bits
  69 * @config.num_s_components: number of pixel components at the input
  70 * @config.num_m_components: number of pixel components at the output
  71 * @config.component_maps: component remapping configuration
  72 * @default_mapping: Default mapping compatible with the configuration
  73 * @default_output: Default output format for the default mapping
  74 */
  75struct xremap_device {
  76        struct xvip_device xvip;
  77        struct media_pad pads[2];
  78        struct v4l2_mbus_framefmt formats[2];
  79
  80        struct {
  81                unsigned int width;
  82                unsigned int num_s_components;
  83                unsigned int num_m_components;
  84                unsigned int component_maps[4];
  85        } config;
  86
  87        const struct xremap_mapping *default_mapping;
  88        const struct xremap_mapping_output *default_output;
  89};
  90
  91static inline struct xremap_device *to_remap(struct v4l2_subdev *subdev)
  92{
  93        return container_of(subdev, struct xremap_device, xvip.subdev);
  94}
  95
  96/* -----------------------------------------------------------------------------
  97 * Mappings
  98 */
  99
 100static const struct xremap_mapping xremap_mappings[] = {
 101        {
 102                .code = MEDIA_BUS_FMT_RBG888_1X24,
 103                .width = 8,
 104                .num_components = 3,
 105                .outputs = (const struct xremap_mapping_output[]) {
 106                        { MEDIA_BUS_FMT_RGB888_1X32_PADHI, 4, { 1, 0, 2, 4 } },
 107                        { },
 108                },
 109        },
 110};
 111
 112static const struct xremap_mapping_output *
 113xremap_match_mapping(struct xremap_device *xremap,
 114                     const struct xremap_mapping *mapping)
 115{
 116        const struct xremap_mapping_output *output;
 117
 118        if (mapping->width != xremap->config.width ||
 119            mapping->num_components != xremap->config.num_s_components)
 120                return NULL;
 121
 122        for (output = mapping->outputs; output->code; ++output) {
 123                unsigned int i;
 124
 125                if (output->num_components != xremap->config.num_m_components)
 126                        continue;
 127
 128                for (i = 0; i < output->num_components; ++i) {
 129                        if (output->component_maps[i] !=
 130                            xremap->config.component_maps[i])
 131                                break;
 132                }
 133
 134                if (i == output->num_components)
 135                        return output;
 136        }
 137
 138        return NULL;
 139}
 140
 141/* -----------------------------------------------------------------------------
 142 * V4L2 Subdevice Pad Operations
 143 */
 144
 145static int xremap_enum_mbus_code(struct v4l2_subdev *subdev,
 146                                 struct v4l2_subdev_pad_config *cfg,
 147                                 struct v4l2_subdev_mbus_code_enum *code)
 148{
 149        struct xremap_device *xremap = to_remap(subdev);
 150        struct v4l2_mbus_framefmt *format;
 151
 152        if (code->pad == XREMAP_PAD_SINK) {
 153                const struct xremap_mapping *mapping = NULL;
 154                unsigned int index = code->index + 1;
 155                unsigned int i;
 156
 157                /* Iterate through the mappings and skip the ones that don't
 158                 * match the remapper configuration until we reach the requested
 159                 * index.
 160                 */
 161                for (i = 0; i < ARRAY_SIZE(xremap_mappings) && index; ++i) {
 162                        mapping = &xremap_mappings[i];
 163
 164                        if (xremap_match_mapping(xremap, mapping))
 165                                index--;
 166                }
 167
 168                /* If the index was larger than the number of supported mappings
 169                 * return -EINVAL.
 170                 */
 171                if (index > 0)
 172                        return -EINVAL;
 173
 174                code->code = mapping->code;
 175        } else {
 176                if (code->index)
 177                        return -EINVAL;
 178
 179                format = v4l2_subdev_get_try_format(subdev, cfg, code->pad);
 180                code->code = format->code;
 181        }
 182
 183        return 0;
 184}
 185
 186static int xremap_enum_frame_size(struct v4l2_subdev *subdev,
 187                                  struct v4l2_subdev_pad_config *cfg,
 188                                  struct v4l2_subdev_frame_size_enum *fse)
 189{
 190        struct v4l2_mbus_framefmt *format;
 191
 192        format = v4l2_subdev_get_try_format(subdev, cfg, fse->pad);
 193
 194        if (fse->index || fse->code != format->code)
 195                return -EINVAL;
 196
 197        if (fse->pad == XREMAP_PAD_SINK) {
 198                /* The remapper doesn't restrict the size on the sink pad. */
 199                fse->min_width = XREMAP_MIN_WIDTH;
 200                fse->max_width = XREMAP_MAX_WIDTH;
 201                fse->min_height = XREMAP_MIN_HEIGHT;
 202                fse->max_height = XREMAP_MAX_HEIGHT;
 203        } else {
 204                /* The size on the source pad are fixed and always identical to
 205                 * the size on the sink pad.
 206                 */
 207                fse->min_width = format->width;
 208                fse->max_width = format->width;
 209                fse->min_height = format->height;
 210                fse->max_height = format->height;
 211        }
 212
 213        return 0;
 214}
 215
 216static struct v4l2_mbus_framefmt *
 217xremap_get_pad_format(struct xremap_device *xremap,
 218                      struct v4l2_subdev_pad_config *cfg,
 219                      unsigned int pad, u32 which)
 220{
 221        switch (which) {
 222        case V4L2_SUBDEV_FORMAT_TRY:
 223                return v4l2_subdev_get_try_format(&xremap->xvip.subdev, cfg,
 224                                                  pad);
 225        case V4L2_SUBDEV_FORMAT_ACTIVE:
 226                return &xremap->formats[pad];
 227        default:
 228                return NULL;
 229        }
 230}
 231
 232static int xremap_get_format(struct v4l2_subdev *subdev,
 233                             struct v4l2_subdev_pad_config *cfg,
 234                             struct v4l2_subdev_format *fmt)
 235{
 236        struct xremap_device *xremap = to_remap(subdev);
 237
 238        fmt->format = *xremap_get_pad_format(xremap, cfg, fmt->pad, fmt->which);
 239
 240        return 0;
 241}
 242
 243static int xremap_set_format(struct v4l2_subdev *subdev,
 244                             struct v4l2_subdev_pad_config *cfg,
 245                             struct v4l2_subdev_format *fmt)
 246{
 247        struct xremap_device *xremap = to_remap(subdev);
 248        const struct xremap_mapping_output *output;
 249        const struct xremap_mapping *mapping;
 250        struct v4l2_mbus_framefmt *format;
 251        unsigned int i;
 252
 253        format = xremap_get_pad_format(xremap, cfg, fmt->pad, fmt->which);
 254
 255        if (fmt->pad == XREMAP_PAD_SOURCE) {
 256                fmt->format = *format;
 257                return 0;
 258        }
 259
 260        /* Find the mapping. If the requested format has no mapping, use the
 261         * default.
 262         */
 263        for (i = 0; i < ARRAY_SIZE(xremap_mappings); ++i) {
 264                mapping = &xremap_mappings[i];
 265                if (mapping->code != fmt->format.code)
 266                        continue;
 267
 268                output = xremap_match_mapping(xremap, mapping);
 269                if (output)
 270                        break;
 271        }
 272
 273        if (!output) {
 274                mapping = xremap->default_mapping;
 275                output = xremap->default_output;
 276        }
 277
 278        format->code = mapping->code;
 279        format->width = clamp_t(unsigned int, fmt->format.width,
 280                                XREMAP_MIN_WIDTH, XREMAP_MAX_WIDTH);
 281        format->height = clamp_t(unsigned int, fmt->format.height,
 282                                 XREMAP_MIN_HEIGHT, XREMAP_MAX_HEIGHT);
 283        format->field = V4L2_FIELD_NONE;
 284        format->colorspace = V4L2_COLORSPACE_SRGB;
 285
 286        fmt->format = *format;
 287
 288        /* Propagate the format to the source pad. */
 289        format = xremap_get_pad_format(xremap, cfg, XREMAP_PAD_SOURCE,
 290                                       fmt->which);
 291        *format = fmt->format;
 292        format->code = output->code;
 293
 294        return 0;
 295}
 296
 297/* -----------------------------------------------------------------------------
 298 * V4L2 Subdevice Operations
 299 */
 300
 301/*
 302 * xremap_init_formats - Initialize formats on all pads
 303 * @subdev: remapper V4L2 subdevice
 304 * @fh: V4L2 subdev file handle
 305 *
 306 * Initialize all pad formats with default values. If fh is not NULL, try
 307 * formats are initialized on the file handle. Otherwise active formats are
 308 * initialized on the device.
 309 */
 310static void xremap_init_formats(struct v4l2_subdev *subdev,
 311                                struct v4l2_subdev_fh *fh)
 312{
 313        struct xremap_device *xremap = to_remap(subdev);
 314        struct v4l2_subdev_format format;
 315
 316        memset(&format, 0, sizeof(format));
 317
 318        format.pad = XREMAP_PAD_SINK;
 319        format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
 320        format.format.code = xremap->default_mapping->code;
 321        format.format.width = XREMAP_DEF_WIDTH;
 322        format.format.height = XREMAP_DEF_HEIGHT;
 323
 324        xremap_set_format(subdev, fh ? fh->pad : NULL, &format);
 325}
 326
 327static int xremap_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
 328{
 329        xremap_init_formats(subdev, fh);
 330
 331        return 0;
 332}
 333
 334static int xremap_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
 335{
 336        return 0;
 337}
 338
 339static struct v4l2_subdev_core_ops xremap_core_ops = {
 340};
 341
 342static struct v4l2_subdev_video_ops xremap_video_ops = {
 343};
 344
 345static struct v4l2_subdev_pad_ops xremap_pad_ops = {
 346        .enum_mbus_code = xremap_enum_mbus_code,
 347        .enum_frame_size = xremap_enum_frame_size,
 348        .get_fmt = xremap_get_format,
 349        .set_fmt = xremap_set_format,
 350};
 351
 352static struct v4l2_subdev_ops xremap_ops = {
 353        .core   = &xremap_core_ops,
 354        .video  = &xremap_video_ops,
 355        .pad    = &xremap_pad_ops,
 356};
 357
 358static const struct v4l2_subdev_internal_ops xremap_internal_ops = {
 359        .open = xremap_open,
 360        .close = xremap_close,
 361};
 362
 363/* -----------------------------------------------------------------------------
 364 * Media Operations
 365 */
 366
 367static const struct media_entity_operations xremap_media_ops = {
 368        .link_validate = v4l2_subdev_link_validate,
 369};
 370
 371/* -----------------------------------------------------------------------------
 372 * Platform Device Driver
 373 */
 374
 375static int xremap_parse_of(struct xremap_device *xremap)
 376{
 377        struct device_node *node = xremap->xvip.dev->of_node;
 378        unsigned int i;
 379        int ret;
 380
 381        /* Parse the DT properties. */
 382        ret = of_property_read_u32(node, "xlnx,video-width",
 383                                   &xremap->config.width);
 384        if (ret < 0) {
 385                dev_dbg(xremap->xvip.dev, "unable to parse %s property\n",
 386                        "xlnx,video-width");
 387                return -EINVAL;
 388        }
 389
 390        ret = of_property_read_u32(node, "#xlnx,s-components",
 391                                   &xremap->config.num_s_components);
 392        if (ret < 0) {
 393                dev_dbg(xremap->xvip.dev, "unable to parse %s property\n",
 394                        "#xlnx,s-components");
 395                return -EINVAL;
 396        }
 397
 398        ret = of_property_read_u32(node, "#xlnx,m-components",
 399                                   &xremap->config.num_m_components);
 400        if (ret < 0) {
 401                dev_dbg(xremap->xvip.dev, "unable to parse %s property\n",
 402                        "#xlnx,m-components");
 403                return -EINVAL;
 404        }
 405
 406        ret = of_property_read_u32_array(node, "xlnx,component-maps",
 407                                         xremap->config.component_maps,
 408                                         xremap->config.num_m_components);
 409        if (ret < 0) {
 410                dev_dbg(xremap->xvip.dev, "unable to parse %s property\n",
 411                        "xlnx,component-maps");
 412                return -EINVAL;
 413        }
 414
 415        /* Validate the parsed values. */
 416        if (xremap->config.num_s_components > 4 ||
 417            xremap->config.num_m_components > 4) {
 418                dev_dbg(xremap->xvip.dev,
 419                        "invalid number of components (s %u m %u)\n",
 420                        xremap->config.num_s_components,
 421                        xremap->config.num_m_components);
 422                return -EINVAL;
 423        }
 424
 425        for (i = 0; i < xremap->config.num_m_components; ++i) {
 426                if (xremap->config.component_maps[i] > 4) {
 427                        dev_dbg(xremap->xvip.dev, "invalid map %u @%u\n",
 428                                xremap->config.component_maps[i], i);
 429                        return -EINVAL;
 430                }
 431        }
 432
 433        /* Find the first mapping that matches the remapper configuration and
 434         * store it as the default mapping.
 435         */
 436        for (i = 0; i < ARRAY_SIZE(xremap_mappings); ++i) {
 437                const struct xremap_mapping_output *output;
 438                const struct xremap_mapping *mapping;
 439
 440                mapping = &xremap_mappings[i];
 441                output = xremap_match_mapping(xremap, mapping);
 442
 443                if (output) {
 444                        xremap->default_mapping = mapping;
 445                        xremap->default_output = output;
 446                        return 0;
 447                }
 448        }
 449
 450        dev_err(xremap->xvip.dev,
 451                "No format compatible with device configuration\n");
 452
 453        return -EINVAL;
 454}
 455
 456static int xremap_probe(struct platform_device *pdev)
 457{
 458        struct xremap_device *xremap;
 459        struct v4l2_subdev *subdev;
 460        int ret;
 461
 462        xremap = devm_kzalloc(&pdev->dev, sizeof(*xremap), GFP_KERNEL);
 463        if (!xremap)
 464                return -ENOMEM;
 465
 466        xremap->xvip.dev = &pdev->dev;
 467
 468        ret = xremap_parse_of(xremap);
 469        if (ret < 0)
 470                return ret;
 471
 472        xremap->xvip.clk = devm_clk_get(xremap->xvip.dev, NULL);
 473        if (IS_ERR(xremap->xvip.clk))
 474                return PTR_ERR(xremap->xvip.clk);
 475
 476        clk_prepare_enable(xremap->xvip.clk);
 477
 478        /* Initialize V4L2 subdevice and media entity */
 479        subdev = &xremap->xvip.subdev;
 480        v4l2_subdev_init(subdev, &xremap_ops);
 481        subdev->dev = &pdev->dev;
 482        subdev->internal_ops = &xremap_internal_ops;
 483        strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
 484        v4l2_set_subdevdata(subdev, xremap);
 485        subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
 486
 487        xremap_init_formats(subdev, NULL);
 488
 489        xremap->pads[XREMAP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
 490        xremap->pads[XREMAP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
 491        subdev->entity.ops = &xremap_media_ops;
 492        ret = media_entity_pads_init(&subdev->entity, 2, xremap->pads);
 493        if (ret < 0)
 494                goto error;
 495
 496        platform_set_drvdata(pdev, xremap);
 497
 498        ret = v4l2_async_register_subdev(subdev);
 499        if (ret < 0) {
 500                dev_err(&pdev->dev, "failed to register subdev\n");
 501                goto error;
 502        }
 503
 504        dev_info(&pdev->dev, "device registered\n");
 505
 506        return 0;
 507
 508error:
 509        media_entity_cleanup(&subdev->entity);
 510        clk_disable_unprepare(xremap->xvip.clk);
 511        return ret;
 512}
 513
 514static int xremap_remove(struct platform_device *pdev)
 515{
 516        struct xremap_device *xremap = platform_get_drvdata(pdev);
 517        struct v4l2_subdev *subdev = &xremap->xvip.subdev;
 518
 519        v4l2_async_unregister_subdev(subdev);
 520        media_entity_cleanup(&subdev->entity);
 521
 522        clk_disable_unprepare(xremap->xvip.clk);
 523
 524        return 0;
 525}
 526
 527static const struct of_device_id xremap_of_id_table[] = {
 528        { .compatible = "xlnx,v-remapper" },
 529        { }
 530};
 531MODULE_DEVICE_TABLE(of, xremap_of_id_table);
 532
 533static struct platform_driver xremap_driver = {
 534        .driver = {
 535                .name = "xilinx-remapper",
 536                .of_match_table = xremap_of_id_table,
 537        },
 538        .probe = xremap_probe,
 539        .remove = xremap_remove,
 540};
 541
 542module_platform_driver(xremap_driver);
 543
 544MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
 545MODULE_DESCRIPTION("Xilinx Video Remapper Driver");
 546MODULE_LICENSE("GPL v2");
 547