linux/drivers/media/platform/xilinx/xilinx-switch.c
<<
>>
Prefs
   1/*
   2 * Xilinx Video Switch
   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/device.h>
  16#include <linux/module.h>
  17#include <linux/of.h>
  18#include <linux/platform_device.h>
  19
  20#include <media/media-device.h>
  21#include <media/v4l2-async.h>
  22#include <media/v4l2-subdev.h>
  23
  24#include "xilinx-vip.h"
  25
  26#define XSW_CORE_CH_CTRL                        0x0100
  27#define XSW_CORE_CH_CTRL_FORCE                  (1 << 3)
  28
  29#define XSW_SWITCH_STATUS                       0x0104
  30
  31/**
  32 * struct xswitch_device - Xilinx Video Switch device structure
  33 * @xvip: Xilinx Video IP device
  34 * @pads: media pads
  35 * @nsinks: number of sink pads (2 to 8)
  36 * @nsources: number of source pads (1 to 8)
  37 * @routing: sink pad connected to each source pad (-1 if none)
  38 * @formats: active V4L2 media bus formats on sink pads
  39 */
  40struct xswitch_device {
  41        struct xvip_device xvip;
  42
  43        struct media_pad *pads;
  44        unsigned int nsinks;
  45        unsigned int nsources;
  46
  47        int routing[8];
  48
  49        struct v4l2_mbus_framefmt *formats;
  50};
  51
  52static inline struct xswitch_device *to_xsw(struct v4l2_subdev *subdev)
  53{
  54        return container_of(subdev, struct xswitch_device, xvip.subdev);
  55}
  56
  57/* -----------------------------------------------------------------------------
  58 * V4L2 Subdevice Video Operations
  59 */
  60
  61static int xsw_s_stream(struct v4l2_subdev *subdev, int enable)
  62{
  63        struct xswitch_device *xsw = to_xsw(subdev);
  64        unsigned int unused_input;
  65        unsigned int i;
  66        u32 routing;
  67
  68        if (!enable) {
  69                xvip_stop(&xsw->xvip);
  70                return 0;
  71        }
  72
  73        /*
  74         * All outputs must be routed to an input. When less than 8 inputs are
  75         * synthesized we can use input 7 for that purpose. Otherwise find an
  76         * unused input to connect to unused outputs.
  77         */
  78        if (xsw->nsinks == 8) {
  79                u32 mask;
  80
  81                for (i = 0, mask = 0xff; i < xsw->nsources; ++i) {
  82                        if (xsw->routing[i] != -1)
  83                                mask &= ~BIT(xsw->routing[i]);
  84                }
  85
  86                /*
  87                 * If all inputs are used all outputs are also used. We don't
  88                 * need an unused input in that case, use a zero value.
  89                 */
  90                unused_input = mask ? ffs(mask) - 1 : 0;
  91        } else {
  92                unused_input = 7;
  93        }
  94
  95        /* Configure routing. */
  96        for (i = 0, routing = 0; i < xsw->nsources; ++i) {
  97                unsigned int route;
  98
  99                route = xsw->routing[i] == -1 ?  unused_input : xsw->routing[i];
 100                routing |= (XSW_CORE_CH_CTRL_FORCE | route)
 101                        << (i * 4);
 102        }
 103
 104        xvip_write(&xsw->xvip, XSW_CORE_CH_CTRL, routing);
 105
 106        xvip_write(&xsw->xvip, XVIP_CTRL_CONTROL,
 107                   (((1 << xsw->nsources) - 1) << 4) |
 108                   XVIP_CTRL_CONTROL_SW_ENABLE);
 109
 110        return 0;
 111}
 112
 113/* -----------------------------------------------------------------------------
 114 * V4L2 Subdevice Pad Operations
 115 */
 116
 117static struct v4l2_mbus_framefmt *
 118xsw_get_pad_format(struct xswitch_device *xsw,
 119                   struct v4l2_subdev_pad_config *cfg,
 120                   unsigned int pad, u32 which)
 121{
 122        switch (which) {
 123        case V4L2_SUBDEV_FORMAT_TRY:
 124                return v4l2_subdev_get_try_format(&xsw->xvip.subdev, cfg, pad);
 125        case V4L2_SUBDEV_FORMAT_ACTIVE:
 126                return &xsw->formats[pad];
 127        default:
 128                return NULL;
 129        }
 130}
 131
 132static int xsw_get_format(struct v4l2_subdev *subdev,
 133                          struct v4l2_subdev_pad_config *cfg,
 134                          struct v4l2_subdev_format *fmt)
 135{
 136        struct xswitch_device *xsw = to_xsw(subdev);
 137        int pad = fmt->pad;
 138
 139        if (pad >= xsw->nsinks) {
 140                pad = xsw->routing[pad - xsw->nsinks];
 141                if (pad < 0) {
 142                        memset(&fmt->format, 0, sizeof(fmt->format));
 143                        return 0;
 144                }
 145        }
 146
 147        fmt->format = *xsw_get_pad_format(xsw, cfg, pad, fmt->which);
 148
 149        return 0;
 150}
 151
 152static int xsw_set_format(struct v4l2_subdev *subdev,
 153                          struct v4l2_subdev_pad_config *cfg,
 154                          struct v4l2_subdev_format *fmt)
 155{
 156        struct xswitch_device *xsw = to_xsw(subdev);
 157        struct v4l2_mbus_framefmt *format;
 158
 159        /* The source pad format is always identical to the sink pad format and
 160         * can't be modified.
 161         */
 162        if (fmt->pad >= xsw->nsinks)
 163                return xsw_get_format(subdev, cfg, fmt);
 164
 165        format = xsw_get_pad_format(xsw, cfg, fmt->pad, fmt->which);
 166
 167        format->code = fmt->format.code;
 168        format->width = clamp_t(unsigned int, fmt->format.width,
 169                                XVIP_MIN_WIDTH, XVIP_MAX_WIDTH);
 170        format->height = clamp_t(unsigned int, fmt->format.height,
 171                                 XVIP_MIN_HEIGHT, XVIP_MAX_HEIGHT);
 172        format->field = V4L2_FIELD_NONE;
 173        format->colorspace = V4L2_COLORSPACE_SRGB;
 174
 175        fmt->format = *format;
 176
 177        return 0;
 178}
 179
 180static int xsw_get_routing(struct v4l2_subdev *subdev,
 181                           struct v4l2_subdev_routing *route)
 182{
 183        struct xswitch_device *xsw = to_xsw(subdev);
 184        unsigned int i;
 185
 186        mutex_lock(&subdev->entity.graph_obj.mdev->graph_mutex);
 187
 188        for (i = 0; i < min(xsw->nsources, route->num_routes); ++i) {
 189                route->routes[i].sink = xsw->routing[i];
 190                route->routes[i].source = i;
 191        }
 192
 193        route->num_routes = xsw->nsources;
 194
 195        mutex_unlock(&subdev->entity.graph_obj.mdev->graph_mutex);
 196
 197        return 0;
 198}
 199
 200static int xsw_set_routing(struct v4l2_subdev *subdev,
 201                           struct v4l2_subdev_routing *route)
 202{
 203        struct xswitch_device *xsw = to_xsw(subdev);
 204        unsigned int i;
 205        int ret = 0;
 206
 207        mutex_lock(&subdev->entity.graph_obj.mdev->graph_mutex);
 208
 209        if (subdev->entity.stream_count) {
 210                ret = -EBUSY;
 211                goto done;
 212        }
 213
 214        for (i = 0; i < xsw->nsources; ++i)
 215                xsw->routing[i] = -1;
 216
 217        for (i = 0; i < route->num_routes; ++i)
 218                xsw->routing[route->routes[i].source - xsw->nsinks] =
 219                        route->routes[i].sink;
 220
 221done:
 222        mutex_unlock(&subdev->entity.graph_obj.mdev->graph_mutex);
 223        return ret;
 224}
 225
 226/* -----------------------------------------------------------------------------
 227 * V4L2 Subdevice Operations
 228 */
 229
 230/**
 231 * xsw_init_formats - Initialize formats on all pads
 232 * @subdev: tpgper V4L2 subdevice
 233 * @fh: V4L2 subdev file handle
 234 *
 235 * Initialize all pad formats with default values. If fh is not NULL, try
 236 * formats are initialized on the file handle. Otherwise active formats are
 237 * initialized on the device.
 238 *
 239 * The function sets the format on pad 0 only. In two pads mode, this is the
 240 * sink pad and the set format handler will propagate the format to the source
 241 * pad. In one pad mode this is the source pad.
 242 */
 243static void xsw_init_formats(struct v4l2_subdev *subdev,
 244                              struct v4l2_subdev_fh *fh)
 245{
 246        struct xswitch_device *xsw = to_xsw(subdev);
 247        struct v4l2_subdev_format format;
 248        unsigned int i;
 249
 250        for (i = 0; i < xsw->nsinks; ++i) {
 251                memset(&format, 0, sizeof(format));
 252
 253                format.pad = 0;
 254                format.which = fh ? V4L2_SUBDEV_FORMAT_TRY
 255                             : V4L2_SUBDEV_FORMAT_ACTIVE;
 256                format.format.width = 1920;
 257                format.format.height = 1080;
 258
 259                xsw_set_format(subdev, fh ? fh->pad : NULL, &format);
 260        }
 261}
 262
 263static int xsw_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
 264{
 265        xsw_init_formats(subdev, fh);
 266
 267        return 0;
 268}
 269
 270static int xsw_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
 271{
 272        return 0;
 273}
 274
 275static struct v4l2_subdev_video_ops xsw_video_ops = {
 276        .s_stream = xsw_s_stream,
 277};
 278
 279static struct v4l2_subdev_pad_ops xsw_pad_ops = {
 280        .enum_mbus_code = xvip_enum_mbus_code,
 281        .enum_frame_size = xvip_enum_frame_size,
 282        .get_fmt = xsw_get_format,
 283        .set_fmt = xsw_set_format,
 284        .get_routing = xsw_get_routing,
 285        .set_routing = xsw_set_routing,
 286};
 287
 288static struct v4l2_subdev_ops xsw_ops = {
 289        .video = &xsw_video_ops,
 290        .pad = &xsw_pad_ops,
 291};
 292
 293static const struct v4l2_subdev_internal_ops xsw_internal_ops = {
 294        .open = xsw_open,
 295        .close = xsw_close,
 296};
 297
 298/* -----------------------------------------------------------------------------
 299 * Media Operations
 300 */
 301
 302static bool xsw_has_route(struct media_entity *entity, unsigned int pad0,
 303                          unsigned int pad1)
 304{
 305        struct xswitch_device *xsw = container_of(entity, struct xswitch_device,
 306                                                  xvip.subdev.entity);
 307        unsigned int sink0, sink1;
 308
 309        /* Two sinks are never connected together. */
 310        if (pad0 < xsw->nsinks && pad1 < xsw->nsinks)
 311                return false;
 312
 313        sink0 = pad0 < xsw->nsinks ? pad0 : xsw->routing[pad0 - xsw->nsinks];
 314        sink1 = pad1 < xsw->nsinks ? pad1 : xsw->routing[pad1 - xsw->nsinks];
 315
 316        return sink0 == sink1;
 317}
 318
 319static const struct media_entity_operations xsw_media_ops = {
 320        .link_validate = v4l2_subdev_link_validate,
 321        .has_route = xsw_has_route,
 322};
 323
 324/* -----------------------------------------------------------------------------
 325 * Platform Device Driver
 326 */
 327
 328static int xsw_parse_of(struct xswitch_device *xsw)
 329{
 330        struct device_node *node = xsw->xvip.dev->of_node;
 331        int ret;
 332
 333        ret = of_property_read_u32(node, "#xlnx,inputs", &xsw->nsinks);
 334        if (ret < 0) {
 335                dev_err(xsw->xvip.dev, "missing or invalid #xlnx,%s property\n",
 336                        "inputs");
 337                return ret;
 338        }
 339
 340        ret = of_property_read_u32(node, "#xlnx,outputs", &xsw->nsources);
 341        if (ret < 0) {
 342                dev_err(xsw->xvip.dev, "missing or invalid #xlnx,%s property\n",
 343                        "outputs");
 344                return ret;
 345        }
 346
 347        return 0;
 348}
 349
 350static int xsw_probe(struct platform_device *pdev)
 351{
 352        struct v4l2_subdev *subdev;
 353        struct xswitch_device *xsw;
 354        unsigned int npads;
 355        unsigned int i;
 356        int ret;
 357
 358        xsw = devm_kzalloc(&pdev->dev, sizeof(*xsw), GFP_KERNEL);
 359        if (!xsw)
 360                return -ENOMEM;
 361
 362        xsw->xvip.dev = &pdev->dev;
 363
 364        ret = xsw_parse_of(xsw);
 365        if (ret < 0)
 366                return ret;
 367
 368        ret = xvip_init_resources(&xsw->xvip);
 369        if (ret < 0)
 370                return ret;
 371
 372        /* Initialize V4L2 subdevice and media entity. Pad numbers depend on the
 373         * number of pads.
 374         */
 375        npads = xsw->nsinks + xsw->nsources;
 376        xsw->pads = devm_kzalloc(&pdev->dev, npads * sizeof(*xsw->pads),
 377                                 GFP_KERNEL);
 378        if (!xsw->pads)
 379                goto error;
 380
 381        for (i = 0; i < xsw->nsinks; ++i)
 382                xsw->pads[i].flags = MEDIA_PAD_FL_SINK;
 383        for (; i < npads; ++i)
 384                xsw->pads[i].flags = MEDIA_PAD_FL_SOURCE;
 385
 386        xsw->formats = devm_kzalloc(&pdev->dev,
 387                                    xsw->nsinks * sizeof(*xsw->formats),
 388                                    GFP_KERNEL);
 389        if (!xsw->formats)
 390                goto error;
 391
 392        for (i = 0; i < xsw->nsources; ++i)
 393                xsw->routing[i] = i < xsw->nsinks ? i : -1;
 394
 395        subdev = &xsw->xvip.subdev;
 396        v4l2_subdev_init(subdev, &xsw_ops);
 397        subdev->dev = &pdev->dev;
 398        subdev->internal_ops = &xsw_internal_ops;
 399        strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
 400        v4l2_set_subdevdata(subdev, xsw);
 401        subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
 402        subdev->entity.ops = &xsw_media_ops;
 403
 404        xsw_init_formats(subdev, NULL);
 405
 406        ret = media_entity_pads_init(&subdev->entity, npads, xsw->pads);
 407        if (ret < 0)
 408                goto error;
 409
 410        platform_set_drvdata(pdev, xsw);
 411
 412        xvip_print_version(&xsw->xvip);
 413
 414        ret = v4l2_async_register_subdev(subdev);
 415        if (ret < 0) {
 416                dev_err(&pdev->dev, "failed to register subdev\n");
 417                goto error;
 418        }
 419
 420        return 0;
 421
 422error:
 423        media_entity_cleanup(&subdev->entity);
 424        xvip_cleanup_resources(&xsw->xvip);
 425        return ret;
 426}
 427
 428static int xsw_remove(struct platform_device *pdev)
 429{
 430        struct xswitch_device *xsw = platform_get_drvdata(pdev);
 431        struct v4l2_subdev *subdev = &xsw->xvip.subdev;
 432
 433        v4l2_async_unregister_subdev(subdev);
 434        media_entity_cleanup(&subdev->entity);
 435
 436        xvip_cleanup_resources(&xsw->xvip);
 437
 438        return 0;
 439}
 440
 441static const struct of_device_id xsw_of_id_table[] = {
 442        { .compatible = "xlnx,v-switch-1.0" },
 443        { }
 444};
 445MODULE_DEVICE_TABLE(of, xsw_of_id_table);
 446
 447static struct platform_driver xsw_driver = {
 448        .driver = {
 449                .name           = "xilinx-switch",
 450                .of_match_table = xsw_of_id_table,
 451        },
 452        .probe                  = xsw_probe,
 453        .remove                 = xsw_remove,
 454};
 455
 456module_platform_driver(xsw_driver);
 457
 458MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
 459MODULE_DESCRIPTION("Xilinx Video Switch Driver");
 460MODULE_LICENSE("GPL v2");
 461