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                   ((((unsigned long)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        struct v4l2_mbus_framefmt *format;
 123
 124        switch (which) {
 125        case V4L2_SUBDEV_FORMAT_TRY:
 126                format = v4l2_subdev_get_try_format(&xsw->xvip.subdev,
 127                                                    cfg, pad);
 128                break;
 129        case V4L2_SUBDEV_FORMAT_ACTIVE:
 130                format = &xsw->formats[pad];
 131                break;
 132        default:
 133                format = NULL;
 134                break;
 135        }
 136
 137        return format;
 138}
 139
 140static int xsw_get_format(struct v4l2_subdev *subdev,
 141                          struct v4l2_subdev_pad_config *cfg,
 142                          struct v4l2_subdev_format *fmt)
 143{
 144        struct xswitch_device *xsw = to_xsw(subdev);
 145        int pad = fmt->pad;
 146        struct v4l2_mbus_framefmt *format;
 147
 148        if (pad >= xsw->nsinks) {
 149                pad = xsw->routing[pad - xsw->nsinks];
 150                if (pad < 0) {
 151                        memset(&fmt->format, 0, sizeof(fmt->format));
 152                        return 0;
 153                }
 154        }
 155
 156        format = xsw_get_pad_format(xsw, cfg, pad, fmt->which);
 157        if (!format)
 158                return -EINVAL;
 159
 160        fmt->format = *format;
 161
 162        return 0;
 163}
 164
 165static int xsw_set_format(struct v4l2_subdev *subdev,
 166                          struct v4l2_subdev_pad_config *cfg,
 167                          struct v4l2_subdev_format *fmt)
 168{
 169        struct xswitch_device *xsw = to_xsw(subdev);
 170        struct v4l2_mbus_framefmt *format;
 171
 172        /* The source pad format is always identical to the sink pad format and
 173         * can't be modified.
 174         */
 175        if (fmt->pad >= xsw->nsinks)
 176                return xsw_get_format(subdev, cfg, fmt);
 177
 178        format = xsw_get_pad_format(xsw, cfg, fmt->pad, fmt->which);
 179        if (!format)
 180                return -EINVAL;
 181
 182        format->code = fmt->format.code;
 183        format->width = clamp_t(unsigned int, fmt->format.width,
 184                                XVIP_MIN_WIDTH, XVIP_MAX_WIDTH);
 185        format->height = clamp_t(unsigned int, fmt->format.height,
 186                                 XVIP_MIN_HEIGHT, XVIP_MAX_HEIGHT);
 187        format->field = V4L2_FIELD_NONE;
 188        format->colorspace = V4L2_COLORSPACE_SRGB;
 189
 190        fmt->format = *format;
 191
 192        return 0;
 193}
 194
 195static int xsw_get_routing(struct v4l2_subdev *subdev,
 196                           struct v4l2_subdev_routing *route)
 197{
 198        struct xswitch_device *xsw = to_xsw(subdev);
 199        unsigned int i;
 200
 201        mutex_lock(&subdev->entity.graph_obj.mdev->graph_mutex);
 202
 203        for (i = 0; i < min(xsw->nsources, route->num_routes); ++i) {
 204                route->routes[i].sink = xsw->routing[i];
 205                route->routes[i].source = i;
 206        }
 207
 208        route->num_routes = xsw->nsources;
 209
 210        mutex_unlock(&subdev->entity.graph_obj.mdev->graph_mutex);
 211
 212        return 0;
 213}
 214
 215static int xsw_set_routing(struct v4l2_subdev *subdev,
 216                           struct v4l2_subdev_routing *route)
 217{
 218        struct xswitch_device *xsw = to_xsw(subdev);
 219        unsigned int i;
 220        int ret = 0;
 221
 222        mutex_lock(&subdev->entity.graph_obj.mdev->graph_mutex);
 223
 224        if (subdev->entity.stream_count) {
 225                ret = -EBUSY;
 226                goto done;
 227        }
 228
 229        for (i = 0; i < xsw->nsources; ++i)
 230                xsw->routing[i] = -1;
 231
 232        for (i = 0; i < route->num_routes; ++i)
 233                xsw->routing[route->routes[i].source - xsw->nsinks] =
 234                        route->routes[i].sink;
 235
 236done:
 237        mutex_unlock(&subdev->entity.graph_obj.mdev->graph_mutex);
 238        return ret;
 239}
 240
 241/* -----------------------------------------------------------------------------
 242 * V4L2 Subdevice Operations
 243 */
 244
 245/**
 246 * xsw_init_formats - Initialize formats on all pads
 247 * @subdev: tpgper V4L2 subdevice
 248 * @fh: V4L2 subdev file handle
 249 *
 250 * Initialize all pad formats with default values. If fh is not NULL, try
 251 * formats are initialized on the file handle. Otherwise active formats are
 252 * initialized on the device.
 253 *
 254 * The function sets the format on pad 0 only. In two pads mode, this is the
 255 * sink pad and the set format handler will propagate the format to the source
 256 * pad. In one pad mode this is the source pad.
 257 */
 258static void xsw_init_formats(struct v4l2_subdev *subdev,
 259                              struct v4l2_subdev_fh *fh)
 260{
 261        struct xswitch_device *xsw = to_xsw(subdev);
 262        struct v4l2_subdev_format format;
 263        unsigned int i;
 264
 265        for (i = 0; i < xsw->nsinks; ++i) {
 266                memset(&format, 0, sizeof(format));
 267
 268                format.pad = 0;
 269                format.which = fh ? V4L2_SUBDEV_FORMAT_TRY
 270                             : V4L2_SUBDEV_FORMAT_ACTIVE;
 271                format.format.width = 1920;
 272                format.format.height = 1080;
 273
 274                xsw_set_format(subdev, fh ? fh->pad : NULL, &format);
 275        }
 276}
 277
 278static int xsw_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
 279{
 280        xsw_init_formats(subdev, fh);
 281
 282        return 0;
 283}
 284
 285static int xsw_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
 286{
 287        return 0;
 288}
 289
 290static struct v4l2_subdev_video_ops xsw_video_ops = {
 291        .s_stream = xsw_s_stream,
 292};
 293
 294static struct v4l2_subdev_pad_ops xsw_pad_ops = {
 295        .enum_mbus_code = xvip_enum_mbus_code,
 296        .enum_frame_size = xvip_enum_frame_size,
 297        .get_fmt = xsw_get_format,
 298        .set_fmt = xsw_set_format,
 299        .get_routing = xsw_get_routing,
 300        .set_routing = xsw_set_routing,
 301};
 302
 303static struct v4l2_subdev_ops xsw_ops = {
 304        .video = &xsw_video_ops,
 305        .pad = &xsw_pad_ops,
 306};
 307
 308static const struct v4l2_subdev_internal_ops xsw_internal_ops = {
 309        .open = xsw_open,
 310        .close = xsw_close,
 311};
 312
 313/* -----------------------------------------------------------------------------
 314 * Media Operations
 315 */
 316
 317static bool xsw_has_route(struct media_entity *entity, unsigned int pad0,
 318                          unsigned int pad1)
 319{
 320        struct xswitch_device *xsw = container_of(entity, struct xswitch_device,
 321                                                  xvip.subdev.entity);
 322        unsigned int sink0, sink1;
 323
 324        /* Two sinks are never connected together. */
 325        if (pad0 < xsw->nsinks && pad1 < xsw->nsinks)
 326                return false;
 327
 328        sink0 = pad0 < xsw->nsinks ? pad0 : xsw->routing[pad0 - xsw->nsinks];
 329        sink1 = pad1 < xsw->nsinks ? pad1 : xsw->routing[pad1 - xsw->nsinks];
 330
 331        return sink0 == sink1;
 332}
 333
 334static const struct media_entity_operations xsw_media_ops = {
 335        .link_validate = v4l2_subdev_link_validate,
 336        .has_route = xsw_has_route,
 337};
 338
 339/* -----------------------------------------------------------------------------
 340 * Platform Device Driver
 341 */
 342
 343static int xsw_parse_of(struct xswitch_device *xsw)
 344{
 345        struct device_node *node = xsw->xvip.dev->of_node;
 346        int ret;
 347
 348        ret = of_property_read_u32(node, "#xlnx,inputs", &xsw->nsinks);
 349        if (ret < 0) {
 350                dev_err(xsw->xvip.dev, "missing or invalid #xlnx,%s property\n",
 351                        "inputs");
 352                return ret;
 353        }
 354
 355        ret = of_property_read_u32(node, "#xlnx,outputs", &xsw->nsources);
 356        if (ret < 0) {
 357                dev_err(xsw->xvip.dev, "missing or invalid #xlnx,%s property\n",
 358                        "outputs");
 359                return ret;
 360        }
 361
 362        return 0;
 363}
 364
 365static int xsw_probe(struct platform_device *pdev)
 366{
 367        struct v4l2_subdev *subdev;
 368        struct xswitch_device *xsw;
 369        unsigned int npads;
 370        unsigned int i;
 371        int ret;
 372
 373        xsw = devm_kzalloc(&pdev->dev, sizeof(*xsw), GFP_KERNEL);
 374        if (!xsw)
 375                return -ENOMEM;
 376
 377        xsw->xvip.dev = &pdev->dev;
 378
 379        ret = xsw_parse_of(xsw);
 380        if (ret < 0)
 381                return ret;
 382
 383        ret = xvip_init_resources(&xsw->xvip);
 384        if (ret < 0)
 385                return ret;
 386
 387        /* Initialize V4L2 subdevice and media entity. Pad numbers depend on the
 388         * number of pads.
 389         */
 390        npads = xsw->nsinks + xsw->nsources;
 391        xsw->pads = devm_kzalloc(&pdev->dev, npads * sizeof(*xsw->pads),
 392                                 GFP_KERNEL);
 393        if (!xsw->pads)
 394                goto error_resources;
 395
 396        for (i = 0; i < xsw->nsinks; ++i)
 397                xsw->pads[i].flags = MEDIA_PAD_FL_SINK;
 398        for (; i < npads; ++i)
 399                xsw->pads[i].flags = MEDIA_PAD_FL_SOURCE;
 400
 401        xsw->formats = devm_kzalloc(&pdev->dev,
 402                                    xsw->nsinks * sizeof(*xsw->formats),
 403                                    GFP_KERNEL);
 404        if (!xsw->formats)
 405                goto error_resources;
 406
 407        for (i = 0; i < xsw->nsources; ++i)
 408                xsw->routing[i] = i < xsw->nsinks ? i : -1;
 409
 410        subdev = &xsw->xvip.subdev;
 411        v4l2_subdev_init(subdev, &xsw_ops);
 412        subdev->dev = &pdev->dev;
 413        subdev->internal_ops = &xsw_internal_ops;
 414        strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
 415        v4l2_set_subdevdata(subdev, xsw);
 416        subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
 417        subdev->entity.ops = &xsw_media_ops;
 418
 419        xsw_init_formats(subdev, NULL);
 420
 421        ret = media_entity_pads_init(&subdev->entity, npads, xsw->pads);
 422        if (ret < 0)
 423                goto error;
 424
 425        platform_set_drvdata(pdev, xsw);
 426
 427        xvip_print_version(&xsw->xvip);
 428
 429        ret = v4l2_async_register_subdev(subdev);
 430        if (ret < 0) {
 431                dev_err(&pdev->dev, "failed to register subdev\n");
 432                goto error;
 433        }
 434
 435        return 0;
 436
 437error:
 438        media_entity_cleanup(&subdev->entity);
 439error_resources:
 440        xvip_cleanup_resources(&xsw->xvip);
 441        return ret;
 442}
 443
 444static int xsw_remove(struct platform_device *pdev)
 445{
 446        struct xswitch_device *xsw = platform_get_drvdata(pdev);
 447        struct v4l2_subdev *subdev = &xsw->xvip.subdev;
 448
 449        v4l2_async_unregister_subdev(subdev);
 450        media_entity_cleanup(&subdev->entity);
 451
 452        xvip_cleanup_resources(&xsw->xvip);
 453
 454        return 0;
 455}
 456
 457static const struct of_device_id xsw_of_id_table[] = {
 458        { .compatible = "xlnx,v-switch-1.0" },
 459        { }
 460};
 461MODULE_DEVICE_TABLE(of, xsw_of_id_table);
 462
 463static struct platform_driver xsw_driver = {
 464        .driver = {
 465                .name           = "xilinx-switch",
 466                .of_match_table = xsw_of_id_table,
 467        },
 468        .probe                  = xsw_probe,
 469        .remove                 = xsw_remove,
 470};
 471
 472module_platform_driver(xsw_driver);
 473
 474MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
 475MODULE_DESCRIPTION("Xilinx Video Switch Driver");
 476MODULE_LICENSE("GPL v2");
 477