linux/drivers/media/platform/xilinx/xilinx-cfa.c
<<
>>
Prefs
   1/*
   2 * Xilinx Color Filter Array
   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
  25#include <media/v4l2-async.h>
  26#include <media/v4l2-subdev.h>
  27
  28#include "xilinx-vip.h"
  29
  30#define XCFA_BAYER_PHASE        0x100
  31#define XCFA_BAYER_PHASE_RGGB   0
  32#define XCFA_BAYER_PHASE_GRBG   1
  33#define XCFA_BAYER_PHASE_GBRG   2
  34#define XCFA_BAYER_PHASE_BGGR   3
  35
  36/**
  37 * struct xcfa_device - Xilinx CFA device structure
  38 * @xvip: Xilinx Video IP device
  39 * @pads: media pads
  40 * @formats: V4L2 media bus formats
  41 * @default_formats: default V4L2 media bus formats
  42 * @vip_formats: Xilinx Video IP formats
  43 */
  44struct xcfa_device {
  45        struct xvip_device xvip;
  46
  47        struct media_pad pads[2];
  48
  49        struct v4l2_mbus_framefmt formats[2];
  50        struct v4l2_mbus_framefmt default_formats[2];
  51        const struct xvip_video_format *vip_formats[2];
  52};
  53
  54static inline struct xcfa_device *to_cfa(struct v4l2_subdev *subdev)
  55{
  56        return container_of(subdev, struct xcfa_device, xvip.subdev);
  57}
  58
  59/*
  60 * V4L2 Subdevice Video Operations
  61 */
  62
  63static int xcfa_get_bayer_phase(const unsigned int code)
  64{
  65        switch (code) {
  66        case MEDIA_BUS_FMT_SRGGB8_1X8:
  67                return XCFA_BAYER_PHASE_RGGB;
  68        case MEDIA_BUS_FMT_SGRBG8_1X8:
  69                return XCFA_BAYER_PHASE_GRBG;
  70        case MEDIA_BUS_FMT_SGBRG8_1X8:
  71                return XCFA_BAYER_PHASE_GBRG;
  72        case MEDIA_BUS_FMT_SBGGR8_1X8:
  73                return XCFA_BAYER_PHASE_BGGR;
  74        }
  75
  76        return -EINVAL;
  77}
  78
  79static int xcfa_s_stream(struct v4l2_subdev *subdev, int enable)
  80{
  81        struct xcfa_device *xcfa = to_cfa(subdev);
  82        const unsigned int code = xcfa->formats[XVIP_PAD_SINK].code;
  83        u32 bayer_phase;
  84
  85        if (!enable) {
  86                xvip_stop(&xcfa->xvip);
  87                return 0;
  88        }
  89
  90        /* This always returns the valid bayer phase value */
  91        bayer_phase = xcfa_get_bayer_phase(code);
  92
  93        xvip_write(&xcfa->xvip, XCFA_BAYER_PHASE, bayer_phase);
  94
  95        xvip_set_frame_size(&xcfa->xvip, &xcfa->formats[XVIP_PAD_SINK]);
  96
  97        xvip_start(&xcfa->xvip);
  98
  99        return 0;
 100}
 101
 102/*
 103 * V4L2 Subdevice Pad Operations
 104 */
 105
 106static struct v4l2_mbus_framefmt *
 107__xcfa_get_pad_format(struct xcfa_device *xcfa,
 108                      struct v4l2_subdev_pad_config *cfg,
 109                      unsigned int pad, u32 which)
 110{
 111        switch (which) {
 112        case V4L2_SUBDEV_FORMAT_TRY:
 113                return v4l2_subdev_get_try_format(&xcfa->xvip.subdev, cfg, pad);
 114        case V4L2_SUBDEV_FORMAT_ACTIVE:
 115                return &xcfa->formats[pad];
 116        default:
 117                return NULL;
 118        }
 119}
 120
 121static int xcfa_get_format(struct v4l2_subdev *subdev,
 122                           struct v4l2_subdev_pad_config *cfg,
 123                           struct v4l2_subdev_format *fmt)
 124{
 125        struct xcfa_device *xcfa = to_cfa(subdev);
 126
 127        fmt->format = *__xcfa_get_pad_format(xcfa, cfg, fmt->pad, fmt->which);
 128
 129        return 0;
 130}
 131
 132static int xcfa_set_format(struct v4l2_subdev *subdev,
 133                           struct v4l2_subdev_pad_config *cfg,
 134                           struct v4l2_subdev_format *fmt)
 135{
 136        struct xcfa_device *xcfa = to_cfa(subdev);
 137        struct v4l2_mbus_framefmt *format;
 138        int bayer_phase;
 139
 140        format = __xcfa_get_pad_format(xcfa, cfg, fmt->pad, fmt->which);
 141
 142        if (fmt->pad == XVIP_PAD_SOURCE) {
 143                fmt->format = *format;
 144                return 0;
 145        }
 146
 147        bayer_phase = xcfa_get_bayer_phase(fmt->format.code);
 148        if (bayer_phase >= 0) {
 149                xcfa->vip_formats[XVIP_PAD_SINK] =
 150                        xvip_get_format_by_code(fmt->format.code);
 151                format->code = fmt->format.code;
 152        }
 153
 154        xvip_set_format_size(format, fmt);
 155
 156        fmt->format = *format;
 157
 158        /* Propagate the format to the source pad */
 159        format = __xcfa_get_pad_format(xcfa, cfg, XVIP_PAD_SOURCE, fmt->which);
 160
 161        xvip_set_format_size(format, fmt);
 162
 163        return 0;
 164}
 165
 166/*
 167 * V4L2 Subdevice Operations
 168 */
 169
 170static int xcfa_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
 171{
 172        struct xcfa_device *xcfa = to_cfa(subdev);
 173        struct v4l2_mbus_framefmt *format;
 174
 175        /* Initialize with default formats */
 176        format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SINK);
 177        *format = xcfa->default_formats[XVIP_PAD_SINK];
 178
 179        format = v4l2_subdev_get_try_format(subdev, fh->pad, XVIP_PAD_SOURCE);
 180        *format = xcfa->default_formats[XVIP_PAD_SOURCE];
 181
 182        return 0;
 183}
 184
 185static int xcfa_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
 186{
 187        return 0;
 188}
 189
 190static struct v4l2_subdev_video_ops xcfa_video_ops = {
 191        .s_stream = xcfa_s_stream,
 192};
 193
 194static struct v4l2_subdev_pad_ops xcfa_pad_ops = {
 195        .enum_mbus_code         = xvip_enum_mbus_code,
 196        .enum_frame_size        = xvip_enum_frame_size,
 197        .get_fmt                = xcfa_get_format,
 198        .set_fmt                = xcfa_set_format,
 199};
 200
 201static struct v4l2_subdev_ops xcfa_ops = {
 202        .video  = &xcfa_video_ops,
 203        .pad    = &xcfa_pad_ops,
 204};
 205
 206static const struct v4l2_subdev_internal_ops xcfa_internal_ops = {
 207        .open   = xcfa_open,
 208        .close  = xcfa_close,
 209};
 210
 211/*
 212 * Media Operations
 213 */
 214
 215static const struct media_entity_operations xcfa_media_ops = {
 216        .link_validate = v4l2_subdev_link_validate,
 217};
 218
 219/*
 220 * Power Management
 221 */
 222
 223static int __maybe_unused xcfa_pm_suspend(struct device *dev)
 224{
 225        struct xcfa_device *xcfa = dev_get_drvdata(dev);
 226
 227        xvip_suspend(&xcfa->xvip);
 228
 229        return 0;
 230}
 231
 232static int __maybe_unused xcfa_pm_resume(struct device *dev)
 233{
 234        struct xcfa_device *xcfa = dev_get_drvdata(dev);
 235
 236        xvip_resume(&xcfa->xvip);
 237
 238        return 0;
 239}
 240
 241/*
 242 * Platform Device Driver
 243 */
 244
 245static int xcfa_parse_of(struct xcfa_device *xcfa)
 246{
 247        struct device *dev = xcfa->xvip.dev;
 248        struct device_node *node = xcfa->xvip.dev->of_node;
 249        struct device_node *ports;
 250        struct device_node *port;
 251        u32 port_id;
 252        int ret;
 253
 254        ports = of_get_child_by_name(node, "ports");
 255        if (ports == NULL)
 256                ports = node;
 257
 258        /* Get the format description for each pad */
 259        for_each_child_of_node(ports, port) {
 260                if (port->name && (of_node_cmp(port->name, "port") == 0)) {
 261                        const struct xvip_video_format *vip_format;
 262
 263                        vip_format = xvip_of_get_format(port);
 264                        if (IS_ERR(vip_format)) {
 265                                dev_err(dev, "invalid format in DT");
 266                                return PTR_ERR(vip_format);
 267                        }
 268
 269                        ret = of_property_read_u32(port, "reg", &port_id);
 270                        if (ret < 0) {
 271                                dev_err(dev, "no reg in DT");
 272                                return ret;
 273                        }
 274
 275                        if (port_id != 0 && port_id != 1) {
 276                                dev_err(dev, "invalid reg in DT");
 277                                return -EINVAL;
 278                        }
 279
 280                        xcfa->vip_formats[port_id] = vip_format;
 281                }
 282        }
 283
 284        return 0;
 285}
 286
 287static int xcfa_probe(struct platform_device *pdev)
 288{
 289        struct xcfa_device *xcfa;
 290        struct v4l2_subdev *subdev;
 291        struct v4l2_mbus_framefmt *default_format;
 292        int ret;
 293
 294        xcfa = devm_kzalloc(&pdev->dev, sizeof(*xcfa), GFP_KERNEL);
 295        if (!xcfa)
 296                return -ENOMEM;
 297
 298        xcfa->xvip.dev = &pdev->dev;
 299
 300        ret = xcfa_parse_of(xcfa);
 301        if (ret < 0)
 302                return ret;
 303
 304        ret = xvip_init_resources(&xcfa->xvip);
 305        if (ret < 0)
 306                return ret;
 307
 308        /* Reset and initialize the core */
 309        xvip_reset(&xcfa->xvip);
 310
 311        /* Initialize V4L2 subdevice and media entity */
 312        subdev = &xcfa->xvip.subdev;
 313        v4l2_subdev_init(subdev, &xcfa_ops);
 314        subdev->dev = &pdev->dev;
 315        subdev->internal_ops = &xcfa_internal_ops;
 316        strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
 317        v4l2_set_subdevdata(subdev, xcfa);
 318        subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
 319
 320        /* Initialize default and active formats */
 321        default_format = &xcfa->default_formats[XVIP_PAD_SINK];
 322        default_format->code = xcfa->vip_formats[XVIP_PAD_SINK]->code;
 323        default_format->field = V4L2_FIELD_NONE;
 324        default_format->colorspace = V4L2_COLORSPACE_SRGB;
 325        xvip_get_frame_size(&xcfa->xvip, default_format);
 326
 327        xcfa->formats[XVIP_PAD_SINK] = *default_format;
 328
 329        default_format = &xcfa->default_formats[XVIP_PAD_SOURCE];
 330        *default_format = xcfa->default_formats[XVIP_PAD_SINK];
 331        default_format->code = xcfa->vip_formats[XVIP_PAD_SOURCE]->code;
 332
 333        xcfa->formats[XVIP_PAD_SOURCE] = *default_format;
 334
 335        xcfa->pads[XVIP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
 336        xcfa->pads[XVIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
 337        subdev->entity.ops = &xcfa_media_ops;
 338        ret = media_entity_pads_init(&subdev->entity, 2, xcfa->pads);
 339        if (ret < 0)
 340                goto error;
 341
 342        platform_set_drvdata(pdev, xcfa);
 343
 344        xvip_print_version(&xcfa->xvip);
 345
 346        ret = v4l2_async_register_subdev(subdev);
 347        if (ret < 0) {
 348                dev_err(&pdev->dev, "failed to register subdev\n");
 349                goto error;
 350        }
 351
 352        return 0;
 353
 354error:
 355        media_entity_cleanup(&subdev->entity);
 356        xvip_cleanup_resources(&xcfa->xvip);
 357        return ret;
 358}
 359
 360static int xcfa_remove(struct platform_device *pdev)
 361{
 362        struct xcfa_device *xcfa = platform_get_drvdata(pdev);
 363        struct v4l2_subdev *subdev = &xcfa->xvip.subdev;
 364
 365        v4l2_async_unregister_subdev(subdev);
 366        media_entity_cleanup(&subdev->entity);
 367
 368        xvip_cleanup_resources(&xcfa->xvip);
 369
 370        return 0;
 371}
 372
 373static SIMPLE_DEV_PM_OPS(xcfa_pm_ops, xcfa_pm_suspend, xcfa_pm_resume);
 374
 375static const struct of_device_id xcfa_of_id_table[] = {
 376        { .compatible = "xlnx,v-cfa-7.0" },
 377        { }
 378};
 379MODULE_DEVICE_TABLE(of, xcfa_of_id_table);
 380
 381static struct platform_driver xcfa_driver = {
 382        .driver                 = {
 383                .name           = "xilinx-cfa",
 384                .pm             = &xcfa_pm_ops,
 385                .of_match_table = xcfa_of_id_table,
 386        },
 387        .probe                  = xcfa_probe,
 388        .remove                 = xcfa_remove,
 389};
 390
 391module_platform_driver(xcfa_driver);
 392
 393MODULE_DESCRIPTION("Xilinx Color Filter Array Driver");
 394MODULE_LICENSE("GPL v2");
 395