linux/drivers/staging/most/aim-v4l2/video.c
<<
>>
Prefs
   1/*
   2 * V4L2 AIM - V4L2 Application Interface Module for MostCore
   3 *
   4 * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG
   5 *
   6 * This program is distributed in the hope that it will be useful,
   7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
   8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   9 * GNU General Public License for more details.
  10 *
  11 * This file is licensed under GPLv2.
  12 */
  13
  14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  15
  16#include <linux/module.h>
  17#include <linux/slab.h>
  18#include <linux/init.h>
  19#include <linux/device.h>
  20#include <linux/suspend.h>
  21#include <linux/videodev2.h>
  22#include <linux/mutex.h>
  23#include <media/v4l2-common.h>
  24#include <media/v4l2-ioctl.h>
  25#include <media/v4l2-event.h>
  26#include <media/v4l2-device.h>
  27#include <media/v4l2-ctrls.h>
  28#include <media/v4l2-fh.h>
  29
  30#include "mostcore.h"
  31
  32#define V4L2_AIM_MAX_INPUT  1
  33
  34static struct most_aim aim_info;
  35
  36struct most_video_dev {
  37        struct most_interface *iface;
  38        int ch_idx;
  39        struct list_head list;
  40        bool mute;
  41
  42        struct list_head pending_mbos;
  43        spinlock_t list_lock;
  44
  45        struct v4l2_device v4l2_dev;
  46        atomic_t access_ref;
  47        struct video_device *vdev;
  48        unsigned int ctrl_input;
  49
  50        struct mutex lock;
  51
  52        wait_queue_head_t wait_data;
  53};
  54
  55struct aim_fh {
  56        /* must be the first field of this struct! */
  57        struct v4l2_fh fh;
  58        struct most_video_dev *mdev;
  59        u32 offs;
  60};
  61
  62static struct list_head video_devices = LIST_HEAD_INIT(video_devices);
  63static struct spinlock list_lock;
  64
  65static inline bool data_ready(struct most_video_dev *mdev)
  66{
  67        return !list_empty(&mdev->pending_mbos);
  68}
  69
  70static inline struct mbo *get_top_mbo(struct most_video_dev *mdev)
  71{
  72        return list_first_entry(&mdev->pending_mbos, struct mbo, list);
  73}
  74
  75static int aim_vdev_open(struct file *filp)
  76{
  77        int ret;
  78        struct video_device *vdev = video_devdata(filp);
  79        struct most_video_dev *mdev = video_drvdata(filp);
  80        struct aim_fh *fh;
  81
  82        v4l2_info(&mdev->v4l2_dev, "aim_vdev_open()\n");
  83
  84        switch (vdev->vfl_type) {
  85        case VFL_TYPE_GRABBER:
  86                break;
  87        default:
  88                return -EINVAL;
  89        }
  90
  91        fh = kzalloc(sizeof(*fh), GFP_KERNEL);
  92        if (!fh)
  93                return -ENOMEM;
  94
  95        if (!atomic_inc_and_test(&mdev->access_ref)) {
  96                v4l2_err(&mdev->v4l2_dev, "too many clients\n");
  97                ret = -EBUSY;
  98                goto err_dec;
  99        }
 100
 101        fh->mdev = mdev;
 102        v4l2_fh_init(&fh->fh, vdev);
 103        filp->private_data = fh;
 104
 105        v4l2_fh_add(&fh->fh);
 106
 107        ret = most_start_channel(mdev->iface, mdev->ch_idx, &aim_info);
 108        if (ret) {
 109                v4l2_err(&mdev->v4l2_dev, "most_start_channel() failed\n");
 110                goto err_rm;
 111        }
 112
 113        return 0;
 114
 115err_rm:
 116        v4l2_fh_del(&fh->fh);
 117        v4l2_fh_exit(&fh->fh);
 118
 119err_dec:
 120        atomic_dec(&mdev->access_ref);
 121        kfree(fh);
 122        return ret;
 123}
 124
 125static int aim_vdev_close(struct file *filp)
 126{
 127        struct aim_fh *fh = filp->private_data;
 128        struct most_video_dev *mdev = fh->mdev;
 129        struct mbo *mbo, *tmp;
 130
 131        v4l2_info(&mdev->v4l2_dev, "aim_vdev_close()\n");
 132
 133        /*
 134         * We need to put MBOs back before we call most_stop_channel()
 135         * to deallocate MBOs.
 136         * From the other hand mostcore still calling rx_completion()
 137         * to deliver MBOs until most_stop_channel() is called.
 138         * Use mute to work around this issue.
 139         * This must be implemented in core.
 140         */
 141
 142        spin_lock_irq(&mdev->list_lock);
 143        mdev->mute = true;
 144        list_for_each_entry_safe(mbo, tmp, &mdev->pending_mbos, list) {
 145                list_del(&mbo->list);
 146                spin_unlock_irq(&mdev->list_lock);
 147                most_put_mbo(mbo);
 148                spin_lock_irq(&mdev->list_lock);
 149        }
 150        spin_unlock_irq(&mdev->list_lock);
 151        most_stop_channel(mdev->iface, mdev->ch_idx, &aim_info);
 152        mdev->mute = false;
 153
 154        v4l2_fh_del(&fh->fh);
 155        v4l2_fh_exit(&fh->fh);
 156
 157        atomic_dec(&mdev->access_ref);
 158        kfree(fh);
 159        return 0;
 160}
 161
 162static ssize_t aim_vdev_read(struct file *filp, char __user *buf,
 163                             size_t count, loff_t *pos)
 164{
 165        struct aim_fh *fh = filp->private_data;
 166        struct most_video_dev *mdev = fh->mdev;
 167        int ret = 0;
 168
 169        if (*pos)
 170                return -ESPIPE;
 171
 172        if (!mdev)
 173                return -ENODEV;
 174
 175        /* wait for the first buffer */
 176        if (!(filp->f_flags & O_NONBLOCK)) {
 177                if (wait_event_interruptible(mdev->wait_data, data_ready(mdev)))
 178                        return -ERESTARTSYS;
 179        }
 180
 181        if (!data_ready(mdev))
 182                return -EAGAIN;
 183
 184        while (count > 0 && data_ready(mdev)) {
 185                struct mbo *const mbo = get_top_mbo(mdev);
 186                int const rem = mbo->processed_length - fh->offs;
 187                int const cnt = rem < count ? rem : count;
 188
 189                if (copy_to_user(buf, mbo->virt_address + fh->offs, cnt)) {
 190                        v4l2_err(&mdev->v4l2_dev, "read: copy_to_user failed\n");
 191                        if (!ret)
 192                                ret = -EFAULT;
 193                        return ret;
 194                }
 195
 196                fh->offs += cnt;
 197                count -= cnt;
 198                buf += cnt;
 199                ret += cnt;
 200
 201                if (cnt >= rem) {
 202                        fh->offs = 0;
 203                        spin_lock_irq(&mdev->list_lock);
 204                        list_del(&mbo->list);
 205                        spin_unlock_irq(&mdev->list_lock);
 206                        most_put_mbo(mbo);
 207                }
 208        }
 209        return ret;
 210}
 211
 212static unsigned int aim_vdev_poll(struct file *filp, poll_table *wait)
 213{
 214        struct aim_fh *fh = filp->private_data;
 215        struct most_video_dev *mdev = fh->mdev;
 216        unsigned int mask = 0;
 217
 218        /* only wait if no data is available */
 219        if (!data_ready(mdev))
 220                poll_wait(filp, &mdev->wait_data, wait);
 221        if (data_ready(mdev))
 222                mask |= POLLIN | POLLRDNORM;
 223
 224        return mask;
 225}
 226
 227static void aim_set_format_struct(struct v4l2_format *f)
 228{
 229        f->fmt.pix.width = 8;
 230        f->fmt.pix.height = 8;
 231        f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
 232        f->fmt.pix.bytesperline = 0;
 233        f->fmt.pix.sizeimage = 188 * 2;
 234        f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
 235        f->fmt.pix.field = V4L2_FIELD_NONE;
 236        f->fmt.pix.priv = 0;
 237}
 238
 239static int aim_set_format(struct most_video_dev *mdev, unsigned int cmd,
 240                          struct v4l2_format *format)
 241{
 242        if (format->fmt.pix.pixelformat != V4L2_PIX_FMT_MPEG)
 243                return -EINVAL;
 244
 245        if (cmd == VIDIOC_TRY_FMT)
 246                return 0;
 247
 248        aim_set_format_struct(format);
 249
 250        return 0;
 251}
 252
 253static int vidioc_querycap(struct file *file, void *priv,
 254                           struct v4l2_capability *cap)
 255{
 256        struct aim_fh *fh = priv;
 257        struct most_video_dev *mdev = fh->mdev;
 258
 259        v4l2_info(&mdev->v4l2_dev, "vidioc_querycap()\n");
 260
 261        strlcpy(cap->driver, "v4l2_most_aim", sizeof(cap->driver));
 262        strlcpy(cap->card, "MOST", sizeof(cap->card));
 263        snprintf(cap->bus_info, sizeof(cap->bus_info),
 264                 "%s", mdev->iface->description);
 265
 266        cap->capabilities =
 267                V4L2_CAP_READWRITE |
 268                V4L2_CAP_TUNER |
 269                V4L2_CAP_VIDEO_CAPTURE;
 270        return 0;
 271}
 272
 273static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
 274                                   struct v4l2_fmtdesc *f)
 275{
 276        struct aim_fh *fh = priv;
 277        struct most_video_dev *mdev = fh->mdev;
 278
 279        v4l2_info(&mdev->v4l2_dev, "vidioc_enum_fmt_vid_cap() %d\n", f->index);
 280
 281        if (f->index)
 282                return -EINVAL;
 283
 284        strcpy(f->description, "MPEG");
 285        f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 286        f->flags = V4L2_FMT_FLAG_COMPRESSED;
 287        f->pixelformat = V4L2_PIX_FMT_MPEG;
 288
 289        return 0;
 290}
 291
 292static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
 293                                struct v4l2_format *f)
 294{
 295        struct aim_fh *fh = priv;
 296        struct most_video_dev *mdev = fh->mdev;
 297
 298        v4l2_info(&mdev->v4l2_dev, "vidioc_g_fmt_vid_cap()\n");
 299
 300        aim_set_format_struct(f);
 301        return 0;
 302}
 303
 304static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
 305                                  struct v4l2_format *f)
 306{
 307        struct aim_fh *fh = priv;
 308        struct most_video_dev *mdev = fh->mdev;
 309
 310        return aim_set_format(mdev, VIDIOC_TRY_FMT, f);
 311}
 312
 313static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
 314                                struct v4l2_format *f)
 315{
 316        struct aim_fh *fh = priv;
 317        struct most_video_dev *mdev = fh->mdev;
 318
 319        return aim_set_format(mdev, VIDIOC_S_FMT, f);
 320}
 321
 322static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
 323{
 324        struct aim_fh *fh = priv;
 325        struct most_video_dev *mdev = fh->mdev;
 326
 327        v4l2_info(&mdev->v4l2_dev, "vidioc_g_std()\n");
 328
 329        *norm = V4L2_STD_UNKNOWN;
 330        return 0;
 331}
 332
 333static int vidioc_enum_input(struct file *file, void *priv,
 334                             struct v4l2_input *input)
 335{
 336        struct aim_fh *fh = priv;
 337        struct most_video_dev *mdev = fh->mdev;
 338
 339        if (input->index >= V4L2_AIM_MAX_INPUT)
 340                return -EINVAL;
 341
 342        strcpy(input->name, "MOST Video");
 343        input->type |= V4L2_INPUT_TYPE_CAMERA;
 344        input->audioset = 0;
 345
 346        input->std = mdev->vdev->tvnorms;
 347
 348        return 0;
 349}
 350
 351static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
 352{
 353        struct aim_fh *fh = priv;
 354        struct most_video_dev *mdev = fh->mdev;
 355        *i = mdev->ctrl_input;
 356        return 0;
 357}
 358
 359static int vidioc_s_input(struct file *file, void *priv, unsigned int index)
 360{
 361        struct aim_fh *fh = priv;
 362        struct most_video_dev *mdev = fh->mdev;
 363
 364        v4l2_info(&mdev->v4l2_dev, "vidioc_s_input(%d)\n", index);
 365
 366        if (index >= V4L2_AIM_MAX_INPUT)
 367                return -EINVAL;
 368        mdev->ctrl_input = index;
 369        return 0;
 370}
 371
 372static const struct v4l2_file_operations aim_fops = {
 373        .owner      = THIS_MODULE,
 374        .open       = aim_vdev_open,
 375        .release    = aim_vdev_close,
 376        .read       = aim_vdev_read,
 377        .poll       = aim_vdev_poll,
 378        .unlocked_ioctl = video_ioctl2,
 379};
 380
 381static const struct v4l2_ioctl_ops video_ioctl_ops = {
 382        .vidioc_querycap            = vidioc_querycap,
 383        .vidioc_enum_fmt_vid_cap    = vidioc_enum_fmt_vid_cap,
 384        .vidioc_g_fmt_vid_cap       = vidioc_g_fmt_vid_cap,
 385        .vidioc_try_fmt_vid_cap     = vidioc_try_fmt_vid_cap,
 386        .vidioc_s_fmt_vid_cap       = vidioc_s_fmt_vid_cap,
 387        .vidioc_g_std               = vidioc_g_std,
 388        .vidioc_enum_input          = vidioc_enum_input,
 389        .vidioc_g_input             = vidioc_g_input,
 390        .vidioc_s_input             = vidioc_s_input,
 391};
 392
 393static const struct video_device aim_videodev_template = {
 394        .fops = &aim_fops,
 395        .release = video_device_release,
 396        .ioctl_ops = &video_ioctl_ops,
 397        .tvnorms = V4L2_STD_UNKNOWN,
 398};
 399
 400/**************************************************************************/
 401
 402static struct most_video_dev *get_aim_dev(
 403        struct most_interface *iface, int channel_idx)
 404{
 405        struct most_video_dev *mdev;
 406        unsigned long flags;
 407
 408        spin_lock_irqsave(&list_lock, flags);
 409        list_for_each_entry(mdev, &video_devices, list) {
 410                if (mdev->iface == iface && mdev->ch_idx == channel_idx) {
 411                        spin_unlock_irqrestore(&list_lock, flags);
 412                        return mdev;
 413                }
 414        }
 415        spin_unlock_irqrestore(&list_lock, flags);
 416        return NULL;
 417}
 418
 419static int aim_rx_data(struct mbo *mbo)
 420{
 421        unsigned long flags;
 422        struct most_video_dev *mdev =
 423                get_aim_dev(mbo->ifp, mbo->hdm_channel_id);
 424
 425        if (!mdev)
 426                return -EIO;
 427
 428        spin_lock_irqsave(&mdev->list_lock, flags);
 429        if (unlikely(mdev->mute)) {
 430                spin_unlock_irqrestore(&mdev->list_lock, flags);
 431                return -EIO;
 432        }
 433
 434        list_add_tail(&mbo->list, &mdev->pending_mbos);
 435        spin_unlock_irqrestore(&mdev->list_lock, flags);
 436        wake_up_interruptible(&mdev->wait_data);
 437        return 0;
 438}
 439
 440static int aim_register_videodev(struct most_video_dev *mdev)
 441{
 442        int ret;
 443
 444        v4l2_info(&mdev->v4l2_dev, "aim_register_videodev()\n");
 445
 446        init_waitqueue_head(&mdev->wait_data);
 447
 448        /* allocate and fill v4l2 video struct */
 449        mdev->vdev = video_device_alloc();
 450        if (!mdev->vdev)
 451                return -ENOMEM;
 452
 453        /* Fill the video capture device struct */
 454        *mdev->vdev = aim_videodev_template;
 455        mdev->vdev->v4l2_dev = &mdev->v4l2_dev;
 456        mdev->vdev->lock = &mdev->lock;
 457        snprintf(mdev->vdev->name, sizeof(mdev->vdev->name), "MOST: %s",
 458                 mdev->v4l2_dev.name);
 459
 460        /* Register the v4l2 device */
 461        video_set_drvdata(mdev->vdev, mdev);
 462        ret = video_register_device(mdev->vdev, VFL_TYPE_GRABBER, -1);
 463        if (ret) {
 464                v4l2_err(&mdev->v4l2_dev, "video_register_device failed (%d)\n",
 465                         ret);
 466                video_device_release(mdev->vdev);
 467        }
 468
 469        return ret;
 470}
 471
 472static void aim_unregister_videodev(struct most_video_dev *mdev)
 473{
 474        v4l2_info(&mdev->v4l2_dev, "aim_unregister_videodev()\n");
 475
 476        video_unregister_device(mdev->vdev);
 477}
 478
 479static void aim_v4l2_dev_release(struct v4l2_device *v4l2_dev)
 480{
 481        struct most_video_dev *mdev =
 482                container_of(v4l2_dev, struct most_video_dev, v4l2_dev);
 483
 484        v4l2_device_unregister(v4l2_dev);
 485        kfree(mdev);
 486}
 487
 488static int aim_probe_channel(struct most_interface *iface, int channel_idx,
 489                             struct most_channel_config *ccfg,
 490                             struct kobject *parent, char *name)
 491{
 492        int ret;
 493        struct most_video_dev *mdev = get_aim_dev(iface, channel_idx);
 494
 495        pr_info("aim_probe_channel(%s)\n", name);
 496
 497        if (mdev) {
 498                pr_err("channel already linked\n");
 499                return -EEXIST;
 500        }
 501
 502        if (ccfg->direction != MOST_CH_RX) {
 503                pr_err("wrong direction, expect rx\n");
 504                return -EINVAL;
 505        }
 506
 507        if (ccfg->data_type != MOST_CH_SYNC &&
 508            ccfg->data_type != MOST_CH_ISOC) {
 509                pr_err("wrong channel type, expect sync or isoc\n");
 510                return -EINVAL;
 511        }
 512
 513        mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
 514        if (!mdev)
 515                return -ENOMEM;
 516
 517        mutex_init(&mdev->lock);
 518        atomic_set(&mdev->access_ref, -1);
 519        spin_lock_init(&mdev->list_lock);
 520        INIT_LIST_HEAD(&mdev->pending_mbos);
 521        mdev->iface = iface;
 522        mdev->ch_idx = channel_idx;
 523        mdev->v4l2_dev.release = aim_v4l2_dev_release;
 524
 525        /* Create the v4l2_device */
 526        strlcpy(mdev->v4l2_dev.name, name, sizeof(mdev->v4l2_dev.name));
 527        ret = v4l2_device_register(NULL, &mdev->v4l2_dev);
 528        if (ret) {
 529                pr_err("v4l2_device_register() failed\n");
 530                kfree(mdev);
 531                return ret;
 532        }
 533
 534        ret = aim_register_videodev(mdev);
 535        if (ret)
 536                goto err_unreg;
 537
 538        spin_lock_irq(&list_lock);
 539        list_add(&mdev->list, &video_devices);
 540        spin_unlock_irq(&list_lock);
 541        v4l2_info(&mdev->v4l2_dev, "aim_probe_channel() done\n");
 542        return 0;
 543
 544err_unreg:
 545        v4l2_device_disconnect(&mdev->v4l2_dev);
 546        v4l2_device_put(&mdev->v4l2_dev);
 547        return ret;
 548}
 549
 550static int aim_disconnect_channel(struct most_interface *iface,
 551                                  int channel_idx)
 552{
 553        struct most_video_dev *mdev = get_aim_dev(iface, channel_idx);
 554
 555        if (!mdev) {
 556                pr_err("no such channel is linked\n");
 557                return -ENOENT;
 558        }
 559
 560        v4l2_info(&mdev->v4l2_dev, "aim_disconnect_channel()\n");
 561
 562        spin_lock_irq(&list_lock);
 563        list_del(&mdev->list);
 564        spin_unlock_irq(&list_lock);
 565
 566        aim_unregister_videodev(mdev);
 567        v4l2_device_disconnect(&mdev->v4l2_dev);
 568        v4l2_device_put(&mdev->v4l2_dev);
 569        return 0;
 570}
 571
 572static struct most_aim aim_info = {
 573        .name = "v4l",
 574        .probe_channel = aim_probe_channel,
 575        .disconnect_channel = aim_disconnect_channel,
 576        .rx_completion = aim_rx_data,
 577};
 578
 579static int __init aim_init(void)
 580{
 581        spin_lock_init(&list_lock);
 582        return most_register_aim(&aim_info);
 583}
 584
 585static void __exit aim_exit(void)
 586{
 587        struct most_video_dev *mdev, *tmp;
 588
 589        /*
 590         * As the mostcore currently doesn't call disconnect_channel()
 591         * for linked channels while we call most_deregister_aim()
 592         * we simulate this call here.
 593         * This must be fixed in core.
 594         */
 595        spin_lock_irq(&list_lock);
 596        list_for_each_entry_safe(mdev, tmp, &video_devices, list) {
 597                list_del(&mdev->list);
 598                spin_unlock_irq(&list_lock);
 599
 600                aim_unregister_videodev(mdev);
 601                v4l2_device_disconnect(&mdev->v4l2_dev);
 602                v4l2_device_put(&mdev->v4l2_dev);
 603                spin_lock_irq(&list_lock);
 604        }
 605        spin_unlock_irq(&list_lock);
 606
 607        most_deregister_aim(&aim_info);
 608        BUG_ON(!list_empty(&video_devices));
 609}
 610
 611module_init(aim_init);
 612module_exit(aim_exit);
 613
 614MODULE_DESCRIPTION("V4L2 Application Interface Module for MostCore");
 615MODULE_AUTHOR("Andrey Shvetsov <andrey.shvetsov@k2l.de>");
 616MODULE_LICENSE("GPL");
 617