linux/drivers/media/platform/vsp1/vsp1_drv.c
<<
>>
Prefs
   1/*
   2 * vsp1_drv.c  --  R-Car VSP1 Driver
   3 *
   4 * Copyright (C) 2013-2015 Renesas Electronics Corporation
   5 *
   6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 */
  13
  14#include <linux/clk.h>
  15#include <linux/delay.h>
  16#include <linux/device.h>
  17#include <linux/interrupt.h>
  18#include <linux/module.h>
  19#include <linux/of.h>
  20#include <linux/of_device.h>
  21#include <linux/platform_device.h>
  22#include <linux/pm_runtime.h>
  23#include <linux/videodev2.h>
  24
  25#include <media/rcar-fcp.h>
  26#include <media/v4l2-subdev.h>
  27
  28#include "vsp1.h"
  29#include "vsp1_bru.h"
  30#include "vsp1_clu.h"
  31#include "vsp1_dl.h"
  32#include "vsp1_drm.h"
  33#include "vsp1_hsit.h"
  34#include "vsp1_lif.h"
  35#include "vsp1_lut.h"
  36#include "vsp1_pipe.h"
  37#include "vsp1_rwpf.h"
  38#include "vsp1_sru.h"
  39#include "vsp1_uds.h"
  40#include "vsp1_video.h"
  41
  42/* -----------------------------------------------------------------------------
  43 * Interrupt Handling
  44 */
  45
  46static irqreturn_t vsp1_irq_handler(int irq, void *data)
  47{
  48        u32 mask = VI6_WFP_IRQ_STA_DFE | VI6_WFP_IRQ_STA_FRE;
  49        struct vsp1_device *vsp1 = data;
  50        irqreturn_t ret = IRQ_NONE;
  51        unsigned int i;
  52        u32 status;
  53
  54        for (i = 0; i < vsp1->info->wpf_count; ++i) {
  55                struct vsp1_rwpf *wpf = vsp1->wpf[i];
  56
  57                if (wpf == NULL)
  58                        continue;
  59
  60                status = vsp1_read(vsp1, VI6_WPF_IRQ_STA(i));
  61                vsp1_write(vsp1, VI6_WPF_IRQ_STA(i), ~status & mask);
  62
  63                if (status & VI6_WFP_IRQ_STA_DFE) {
  64                        vsp1_pipeline_frame_end(wpf->pipe);
  65                        ret = IRQ_HANDLED;
  66                }
  67        }
  68
  69        status = vsp1_read(vsp1, VI6_DISP_IRQ_STA);
  70        vsp1_write(vsp1, VI6_DISP_IRQ_STA, ~status & VI6_DISP_IRQ_STA_DST);
  71
  72        if (status & VI6_DISP_IRQ_STA_DST) {
  73                vsp1_drm_display_start(vsp1);
  74                ret = IRQ_HANDLED;
  75        }
  76
  77        return ret;
  78}
  79
  80/* -----------------------------------------------------------------------------
  81 * Entities
  82 */
  83
  84/*
  85 * vsp1_create_sink_links - Create links from all sources to the given sink
  86 *
  87 * This function creates media links from all valid sources to the given sink
  88 * pad. Links that would be invalid according to the VSP1 hardware capabilities
  89 * are skipped. Those include all links
  90 *
  91 * - from a UDS to a UDS (UDS entities can't be chained)
  92 * - from an entity to itself (no loops are allowed)
  93 */
  94static int vsp1_create_sink_links(struct vsp1_device *vsp1,
  95                                  struct vsp1_entity *sink)
  96{
  97        struct media_entity *entity = &sink->subdev.entity;
  98        struct vsp1_entity *source;
  99        unsigned int pad;
 100        int ret;
 101
 102        list_for_each_entry(source, &vsp1->entities, list_dev) {
 103                u32 flags;
 104
 105                if (source->type == sink->type)
 106                        continue;
 107
 108                if (source->type == VSP1_ENTITY_LIF ||
 109                    source->type == VSP1_ENTITY_WPF)
 110                        continue;
 111
 112                flags = source->type == VSP1_ENTITY_RPF &&
 113                        sink->type == VSP1_ENTITY_WPF &&
 114                        source->index == sink->index
 115                      ? MEDIA_LNK_FL_ENABLED : 0;
 116
 117                for (pad = 0; pad < entity->num_pads; ++pad) {
 118                        if (!(entity->pads[pad].flags & MEDIA_PAD_FL_SINK))
 119                                continue;
 120
 121                        ret = media_create_pad_link(&source->subdev.entity,
 122                                                       source->source_pad,
 123                                                       entity, pad, flags);
 124                        if (ret < 0)
 125                                return ret;
 126
 127                        if (flags & MEDIA_LNK_FL_ENABLED)
 128                                source->sink = entity;
 129                }
 130        }
 131
 132        return 0;
 133}
 134
 135static int vsp1_uapi_create_links(struct vsp1_device *vsp1)
 136{
 137        struct vsp1_entity *entity;
 138        unsigned int i;
 139        int ret;
 140
 141        list_for_each_entry(entity, &vsp1->entities, list_dev) {
 142                if (entity->type == VSP1_ENTITY_LIF ||
 143                    entity->type == VSP1_ENTITY_RPF)
 144                        continue;
 145
 146                ret = vsp1_create_sink_links(vsp1, entity);
 147                if (ret < 0)
 148                        return ret;
 149        }
 150
 151        if (vsp1->lif) {
 152                ret = media_create_pad_link(&vsp1->wpf[0]->entity.subdev.entity,
 153                                            RWPF_PAD_SOURCE,
 154                                            &vsp1->lif->entity.subdev.entity,
 155                                            LIF_PAD_SINK, 0);
 156                if (ret < 0)
 157                        return ret;
 158        }
 159
 160        for (i = 0; i < vsp1->info->rpf_count; ++i) {
 161                struct vsp1_rwpf *rpf = vsp1->rpf[i];
 162
 163                ret = media_create_pad_link(&rpf->video->video.entity, 0,
 164                                            &rpf->entity.subdev.entity,
 165                                            RWPF_PAD_SINK,
 166                                            MEDIA_LNK_FL_ENABLED |
 167                                            MEDIA_LNK_FL_IMMUTABLE);
 168                if (ret < 0)
 169                        return ret;
 170        }
 171
 172        for (i = 0; i < vsp1->info->wpf_count; ++i) {
 173                /* Connect the video device to the WPF. All connections are
 174                 * immutable.
 175                 */
 176                struct vsp1_rwpf *wpf = vsp1->wpf[i];
 177
 178                ret = media_create_pad_link(&wpf->entity.subdev.entity,
 179                                            RWPF_PAD_SOURCE,
 180                                            &wpf->video->video.entity, 0,
 181                                            MEDIA_LNK_FL_IMMUTABLE |
 182                                            MEDIA_LNK_FL_ENABLED);
 183                if (ret < 0)
 184                        return ret;
 185        }
 186
 187        return 0;
 188}
 189
 190static void vsp1_destroy_entities(struct vsp1_device *vsp1)
 191{
 192        struct vsp1_entity *entity, *_entity;
 193        struct vsp1_video *video, *_video;
 194
 195        list_for_each_entry_safe(entity, _entity, &vsp1->entities, list_dev) {
 196                list_del(&entity->list_dev);
 197                vsp1_entity_destroy(entity);
 198        }
 199
 200        list_for_each_entry_safe(video, _video, &vsp1->videos, list) {
 201                list_del(&video->list);
 202                vsp1_video_cleanup(video);
 203        }
 204
 205        v4l2_device_unregister(&vsp1->v4l2_dev);
 206        if (vsp1->info->uapi)
 207                media_device_unregister(&vsp1->media_dev);
 208        media_device_cleanup(&vsp1->media_dev);
 209
 210        if (!vsp1->info->uapi)
 211                vsp1_drm_cleanup(vsp1);
 212}
 213
 214static int vsp1_create_entities(struct vsp1_device *vsp1)
 215{
 216        struct media_device *mdev = &vsp1->media_dev;
 217        struct v4l2_device *vdev = &vsp1->v4l2_dev;
 218        struct vsp1_entity *entity;
 219        unsigned int i;
 220        int ret;
 221
 222        mdev->dev = vsp1->dev;
 223        mdev->hw_revision = vsp1->version;
 224        strlcpy(mdev->model, vsp1->info->model, sizeof(mdev->model));
 225        snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s",
 226                 dev_name(mdev->dev));
 227        media_device_init(mdev);
 228
 229        vsp1->media_ops.link_setup = vsp1_entity_link_setup;
 230        /* Don't perform link validation when the userspace API is disabled as
 231         * the pipeline is configured internally by the driver in that case, and
 232         * its configuration can thus be trusted.
 233         */
 234        if (vsp1->info->uapi)
 235                vsp1->media_ops.link_validate = v4l2_subdev_link_validate;
 236
 237        vdev->mdev = mdev;
 238        ret = v4l2_device_register(vsp1->dev, vdev);
 239        if (ret < 0) {
 240                dev_err(vsp1->dev, "V4L2 device registration failed (%d)\n",
 241                        ret);
 242                goto done;
 243        }
 244
 245        /* Instantiate all the entities. */
 246        if (vsp1->info->features & VSP1_HAS_BRU) {
 247                vsp1->bru = vsp1_bru_create(vsp1);
 248                if (IS_ERR(vsp1->bru)) {
 249                        ret = PTR_ERR(vsp1->bru);
 250                        goto done;
 251                }
 252
 253                list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities);
 254        }
 255
 256        if (vsp1->info->features & VSP1_HAS_CLU) {
 257                vsp1->clu = vsp1_clu_create(vsp1);
 258                if (IS_ERR(vsp1->clu)) {
 259                        ret = PTR_ERR(vsp1->clu);
 260                        goto done;
 261                }
 262
 263                list_add_tail(&vsp1->clu->entity.list_dev, &vsp1->entities);
 264        }
 265
 266        vsp1->hsi = vsp1_hsit_create(vsp1, true);
 267        if (IS_ERR(vsp1->hsi)) {
 268                ret = PTR_ERR(vsp1->hsi);
 269                goto done;
 270        }
 271
 272        list_add_tail(&vsp1->hsi->entity.list_dev, &vsp1->entities);
 273
 274        vsp1->hst = vsp1_hsit_create(vsp1, false);
 275        if (IS_ERR(vsp1->hst)) {
 276                ret = PTR_ERR(vsp1->hst);
 277                goto done;
 278        }
 279
 280        list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities);
 281
 282        /* The LIF is only supported when used in conjunction with the DU, in
 283         * which case the userspace API is disabled. If the userspace API is
 284         * enabled skip the LIF, even when present.
 285         */
 286        if (vsp1->info->features & VSP1_HAS_LIF && !vsp1->info->uapi) {
 287                vsp1->lif = vsp1_lif_create(vsp1);
 288                if (IS_ERR(vsp1->lif)) {
 289                        ret = PTR_ERR(vsp1->lif);
 290                        goto done;
 291                }
 292
 293                list_add_tail(&vsp1->lif->entity.list_dev, &vsp1->entities);
 294        }
 295
 296        if (vsp1->info->features & VSP1_HAS_LUT) {
 297                vsp1->lut = vsp1_lut_create(vsp1);
 298                if (IS_ERR(vsp1->lut)) {
 299                        ret = PTR_ERR(vsp1->lut);
 300                        goto done;
 301                }
 302
 303                list_add_tail(&vsp1->lut->entity.list_dev, &vsp1->entities);
 304        }
 305
 306        for (i = 0; i < vsp1->info->rpf_count; ++i) {
 307                struct vsp1_rwpf *rpf;
 308
 309                rpf = vsp1_rpf_create(vsp1, i);
 310                if (IS_ERR(rpf)) {
 311                        ret = PTR_ERR(rpf);
 312                        goto done;
 313                }
 314
 315                vsp1->rpf[i] = rpf;
 316                list_add_tail(&rpf->entity.list_dev, &vsp1->entities);
 317
 318                if (vsp1->info->uapi) {
 319                        struct vsp1_video *video = vsp1_video_create(vsp1, rpf);
 320
 321                        if (IS_ERR(video)) {
 322                                ret = PTR_ERR(video);
 323                                goto done;
 324                        }
 325
 326                        list_add_tail(&video->list, &vsp1->videos);
 327                }
 328        }
 329
 330        if (vsp1->info->features & VSP1_HAS_SRU) {
 331                vsp1->sru = vsp1_sru_create(vsp1);
 332                if (IS_ERR(vsp1->sru)) {
 333                        ret = PTR_ERR(vsp1->sru);
 334                        goto done;
 335                }
 336
 337                list_add_tail(&vsp1->sru->entity.list_dev, &vsp1->entities);
 338        }
 339
 340        for (i = 0; i < vsp1->info->uds_count; ++i) {
 341                struct vsp1_uds *uds;
 342
 343                uds = vsp1_uds_create(vsp1, i);
 344                if (IS_ERR(uds)) {
 345                        ret = PTR_ERR(uds);
 346                        goto done;
 347                }
 348
 349                vsp1->uds[i] = uds;
 350                list_add_tail(&uds->entity.list_dev, &vsp1->entities);
 351        }
 352
 353        for (i = 0; i < vsp1->info->wpf_count; ++i) {
 354                struct vsp1_rwpf *wpf;
 355
 356                wpf = vsp1_wpf_create(vsp1, i);
 357                if (IS_ERR(wpf)) {
 358                        ret = PTR_ERR(wpf);
 359                        goto done;
 360                }
 361
 362                vsp1->wpf[i] = wpf;
 363                list_add_tail(&wpf->entity.list_dev, &vsp1->entities);
 364
 365                if (vsp1->info->uapi) {
 366                        struct vsp1_video *video = vsp1_video_create(vsp1, wpf);
 367
 368                        if (IS_ERR(video)) {
 369                                ret = PTR_ERR(video);
 370                                goto done;
 371                        }
 372
 373                        list_add_tail(&video->list, &vsp1->videos);
 374                        wpf->entity.sink = &video->video.entity;
 375                }
 376        }
 377
 378        /* Register all subdevs. */
 379        list_for_each_entry(entity, &vsp1->entities, list_dev) {
 380                ret = v4l2_device_register_subdev(&vsp1->v4l2_dev,
 381                                                  &entity->subdev);
 382                if (ret < 0)
 383                        goto done;
 384        }
 385
 386        /* Create links. */
 387        if (vsp1->info->uapi)
 388                ret = vsp1_uapi_create_links(vsp1);
 389        else
 390                ret = vsp1_drm_create_links(vsp1);
 391        if (ret < 0)
 392                goto done;
 393
 394        /* Register subdev nodes if the userspace API is enabled or initialize
 395         * the DRM pipeline otherwise.
 396         */
 397        if (vsp1->info->uapi) {
 398                ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev);
 399                if (ret < 0)
 400                        goto done;
 401
 402                ret = media_device_register(mdev);
 403        } else {
 404                ret = vsp1_drm_init(vsp1);
 405        }
 406
 407done:
 408        if (ret < 0)
 409                vsp1_destroy_entities(vsp1);
 410
 411        return ret;
 412}
 413
 414int vsp1_reset_wpf(struct vsp1_device *vsp1, unsigned int index)
 415{
 416        unsigned int timeout;
 417        u32 status;
 418
 419        status = vsp1_read(vsp1, VI6_STATUS);
 420        if (!(status & VI6_STATUS_SYS_ACT(index)))
 421                return 0;
 422
 423        vsp1_write(vsp1, VI6_SRESET, VI6_SRESET_SRTS(index));
 424        for (timeout = 10; timeout > 0; --timeout) {
 425                status = vsp1_read(vsp1, VI6_STATUS);
 426                if (!(status & VI6_STATUS_SYS_ACT(index)))
 427                        break;
 428
 429                usleep_range(1000, 2000);
 430        }
 431
 432        if (!timeout) {
 433                dev_err(vsp1->dev, "failed to reset wpf.%u\n", index);
 434                return -ETIMEDOUT;
 435        }
 436
 437        return 0;
 438}
 439
 440static int vsp1_device_init(struct vsp1_device *vsp1)
 441{
 442        unsigned int i;
 443        int ret;
 444
 445        /* Reset any channel that might be running. */
 446        for (i = 0; i < vsp1->info->wpf_count; ++i) {
 447                ret = vsp1_reset_wpf(vsp1, i);
 448                if (ret < 0)
 449                        return ret;
 450        }
 451
 452        vsp1_write(vsp1, VI6_CLK_DCSWT, (8 << VI6_CLK_DCSWT_CSTPW_SHIFT) |
 453                   (8 << VI6_CLK_DCSWT_CSTRW_SHIFT));
 454
 455        for (i = 0; i < vsp1->info->rpf_count; ++i)
 456                vsp1_write(vsp1, VI6_DPR_RPF_ROUTE(i), VI6_DPR_NODE_UNUSED);
 457
 458        for (i = 0; i < vsp1->info->uds_count; ++i)
 459                vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED);
 460
 461        vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED);
 462        vsp1_write(vsp1, VI6_DPR_LUT_ROUTE, VI6_DPR_NODE_UNUSED);
 463        vsp1_write(vsp1, VI6_DPR_CLU_ROUTE, VI6_DPR_NODE_UNUSED);
 464        vsp1_write(vsp1, VI6_DPR_HST_ROUTE, VI6_DPR_NODE_UNUSED);
 465        vsp1_write(vsp1, VI6_DPR_HSI_ROUTE, VI6_DPR_NODE_UNUSED);
 466        vsp1_write(vsp1, VI6_DPR_BRU_ROUTE, VI6_DPR_NODE_UNUSED);
 467
 468        vsp1_write(vsp1, VI6_DPR_HGO_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
 469                   (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));
 470        vsp1_write(vsp1, VI6_DPR_HGT_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
 471                   (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));
 472
 473        vsp1_dlm_setup(vsp1);
 474
 475        return 0;
 476}
 477
 478/*
 479 * vsp1_device_get - Acquire the VSP1 device
 480 *
 481 * Make sure the device is not suspended and initialize it if needed.
 482 *
 483 * Return 0 on success or a negative error code otherwise.
 484 */
 485int vsp1_device_get(struct vsp1_device *vsp1)
 486{
 487        int ret;
 488
 489        ret = pm_runtime_get_sync(vsp1->dev);
 490        return ret < 0 ? ret : 0;
 491}
 492
 493/*
 494 * vsp1_device_put - Release the VSP1 device
 495 *
 496 * Decrement the VSP1 reference count and cleanup the device if the last
 497 * reference is released.
 498 */
 499void vsp1_device_put(struct vsp1_device *vsp1)
 500{
 501        pm_runtime_put_sync(vsp1->dev);
 502}
 503
 504/* -----------------------------------------------------------------------------
 505 * Power Management
 506 */
 507
 508static int __maybe_unused vsp1_pm_suspend(struct device *dev)
 509{
 510        struct vsp1_device *vsp1 = dev_get_drvdata(dev);
 511
 512        vsp1_pipelines_suspend(vsp1);
 513        pm_runtime_force_suspend(vsp1->dev);
 514
 515        return 0;
 516}
 517
 518static int __maybe_unused vsp1_pm_resume(struct device *dev)
 519{
 520        struct vsp1_device *vsp1 = dev_get_drvdata(dev);
 521
 522        pm_runtime_force_resume(vsp1->dev);
 523        vsp1_pipelines_resume(vsp1);
 524
 525        return 0;
 526}
 527
 528static int __maybe_unused vsp1_pm_runtime_suspend(struct device *dev)
 529{
 530        struct vsp1_device *vsp1 = dev_get_drvdata(dev);
 531
 532        rcar_fcp_disable(vsp1->fcp);
 533
 534        return 0;
 535}
 536
 537static int __maybe_unused vsp1_pm_runtime_resume(struct device *dev)
 538{
 539        struct vsp1_device *vsp1 = dev_get_drvdata(dev);
 540        int ret;
 541
 542        if (vsp1->info) {
 543                ret = vsp1_device_init(vsp1);
 544                if (ret < 0)
 545                        return ret;
 546        }
 547
 548        return rcar_fcp_enable(vsp1->fcp);
 549}
 550
 551static const struct dev_pm_ops vsp1_pm_ops = {
 552        SET_SYSTEM_SLEEP_PM_OPS(vsp1_pm_suspend, vsp1_pm_resume)
 553        SET_RUNTIME_PM_OPS(vsp1_pm_runtime_suspend, vsp1_pm_runtime_resume, NULL)
 554};
 555
 556/* -----------------------------------------------------------------------------
 557 * Platform Driver
 558 */
 559
 560static const struct vsp1_device_info vsp1_device_infos[] = {
 561        {
 562                .version = VI6_IP_VERSION_MODEL_VSPS_H2,
 563                .model = "VSP1-S",
 564                .gen = 2,
 565                .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
 566                          | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
 567                .rpf_count = 5,
 568                .uds_count = 3,
 569                .wpf_count = 4,
 570                .num_bru_inputs = 4,
 571                .uapi = true,
 572        }, {
 573                .version = VI6_IP_VERSION_MODEL_VSPR_H2,
 574                .model = "VSP1-R",
 575                .gen = 2,
 576                .features = VSP1_HAS_BRU | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
 577                .rpf_count = 5,
 578                .uds_count = 3,
 579                .wpf_count = 4,
 580                .num_bru_inputs = 4,
 581                .uapi = true,
 582        }, {
 583                .version = VI6_IP_VERSION_MODEL_VSPD_GEN2,
 584                .model = "VSP1-D",
 585                .gen = 2,
 586                .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_LUT,
 587                .rpf_count = 4,
 588                .uds_count = 1,
 589                .wpf_count = 1,
 590                .num_bru_inputs = 4,
 591                .uapi = true,
 592        }, {
 593                .version = VI6_IP_VERSION_MODEL_VSPS_M2,
 594                .model = "VSP1-S",
 595                .gen = 2,
 596                .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
 597                          | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
 598                .rpf_count = 5,
 599                .uds_count = 1,
 600                .wpf_count = 4,
 601                .num_bru_inputs = 4,
 602                .uapi = true,
 603        }, {
 604                .version = VI6_IP_VERSION_MODEL_VSPS_V2H,
 605                .model = "VSP1V-S",
 606                .gen = 2,
 607                .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
 608                          | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
 609                .rpf_count = 4,
 610                .uds_count = 1,
 611                .wpf_count = 4,
 612                .num_bru_inputs = 4,
 613                .uapi = true,
 614        }, {
 615                .version = VI6_IP_VERSION_MODEL_VSPD_V2H,
 616                .model = "VSP1V-D",
 617                .gen = 2,
 618                .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
 619                          | VSP1_HAS_LIF,
 620                .rpf_count = 4,
 621                .uds_count = 1,
 622                .wpf_count = 1,
 623                .num_bru_inputs = 4,
 624                .uapi = true,
 625        }, {
 626                .version = VI6_IP_VERSION_MODEL_VSPI_GEN3,
 627                .model = "VSP2-I",
 628                .gen = 3,
 629                .features = VSP1_HAS_CLU | VSP1_HAS_LUT | VSP1_HAS_SRU
 630                          | VSP1_HAS_WPF_HFLIP | VSP1_HAS_WPF_VFLIP,
 631                .rpf_count = 1,
 632                .uds_count = 1,
 633                .wpf_count = 1,
 634                .uapi = true,
 635        }, {
 636                .version = VI6_IP_VERSION_MODEL_VSPBD_GEN3,
 637                .model = "VSP2-BD",
 638                .gen = 3,
 639                .features = VSP1_HAS_BRU | VSP1_HAS_WPF_VFLIP,
 640                .rpf_count = 5,
 641                .wpf_count = 1,
 642                .num_bru_inputs = 5,
 643                .uapi = true,
 644        }, {
 645                .version = VI6_IP_VERSION_MODEL_VSPBC_GEN3,
 646                .model = "VSP2-BC",
 647                .gen = 3,
 648                .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
 649                          | VSP1_HAS_WPF_VFLIP,
 650                .rpf_count = 5,
 651                .wpf_count = 1,
 652                .num_bru_inputs = 5,
 653                .uapi = true,
 654        }, {
 655                .version = VI6_IP_VERSION_MODEL_VSPD_GEN3,
 656                .model = "VSP2-D",
 657                .gen = 3,
 658                .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_WPF_VFLIP,
 659                .rpf_count = 5,
 660                .wpf_count = 2,
 661                .num_bru_inputs = 5,
 662        },
 663};
 664
 665static int vsp1_probe(struct platform_device *pdev)
 666{
 667        struct vsp1_device *vsp1;
 668        struct device_node *fcp_node;
 669        struct resource *irq;
 670        struct resource *io;
 671        unsigned int i;
 672        int ret;
 673
 674        vsp1 = devm_kzalloc(&pdev->dev, sizeof(*vsp1), GFP_KERNEL);
 675        if (vsp1 == NULL)
 676                return -ENOMEM;
 677
 678        vsp1->dev = &pdev->dev;
 679        INIT_LIST_HEAD(&vsp1->entities);
 680        INIT_LIST_HEAD(&vsp1->videos);
 681
 682        platform_set_drvdata(pdev, vsp1);
 683
 684        /* I/O and IRQ resources (clock managed by the clock PM domain) */
 685        io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 686        vsp1->mmio = devm_ioremap_resource(&pdev->dev, io);
 687        if (IS_ERR(vsp1->mmio))
 688                return PTR_ERR(vsp1->mmio);
 689
 690        irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 691        if (!irq) {
 692                dev_err(&pdev->dev, "missing IRQ\n");
 693                return -EINVAL;
 694        }
 695
 696        ret = devm_request_irq(&pdev->dev, irq->start, vsp1_irq_handler,
 697                              IRQF_SHARED, dev_name(&pdev->dev), vsp1);
 698        if (ret < 0) {
 699                dev_err(&pdev->dev, "failed to request IRQ\n");
 700                return ret;
 701        }
 702
 703        /* FCP (optional) */
 704        fcp_node = of_parse_phandle(pdev->dev.of_node, "renesas,fcp", 0);
 705        if (fcp_node) {
 706                vsp1->fcp = rcar_fcp_get(fcp_node);
 707                of_node_put(fcp_node);
 708                if (IS_ERR(vsp1->fcp)) {
 709                        dev_dbg(&pdev->dev, "FCP not found (%ld)\n",
 710                                PTR_ERR(vsp1->fcp));
 711                        return PTR_ERR(vsp1->fcp);
 712                }
 713        }
 714
 715        /* Configure device parameters based on the version register. */
 716        pm_runtime_enable(&pdev->dev);
 717
 718        ret = pm_runtime_get_sync(&pdev->dev);
 719        if (ret < 0)
 720                goto done;
 721
 722        vsp1->version = vsp1_read(vsp1, VI6_IP_VERSION);
 723        pm_runtime_put_sync(&pdev->dev);
 724
 725        for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) {
 726                if ((vsp1->version & VI6_IP_VERSION_MODEL_MASK) ==
 727                    vsp1_device_infos[i].version) {
 728                        vsp1->info = &vsp1_device_infos[i];
 729                        break;
 730                }
 731        }
 732
 733        if (!vsp1->info) {
 734                dev_err(&pdev->dev, "unsupported IP version 0x%08x\n",
 735                        vsp1->version);
 736                ret = -ENXIO;
 737                goto done;
 738        }
 739
 740        dev_dbg(&pdev->dev, "IP version 0x%08x\n", vsp1->version);
 741
 742        /* Instanciate entities */
 743        ret = vsp1_create_entities(vsp1);
 744        if (ret < 0) {
 745                dev_err(&pdev->dev, "failed to create entities\n");
 746                goto done;
 747        }
 748
 749done:
 750        if (ret)
 751                pm_runtime_disable(&pdev->dev);
 752
 753        return ret;
 754}
 755
 756static int vsp1_remove(struct platform_device *pdev)
 757{
 758        struct vsp1_device *vsp1 = platform_get_drvdata(pdev);
 759
 760        vsp1_destroy_entities(vsp1);
 761        rcar_fcp_put(vsp1->fcp);
 762
 763        pm_runtime_disable(&pdev->dev);
 764
 765        return 0;
 766}
 767
 768static const struct of_device_id vsp1_of_match[] = {
 769        { .compatible = "renesas,vsp1" },
 770        { .compatible = "renesas,vsp2" },
 771        { },
 772};
 773
 774static struct platform_driver vsp1_platform_driver = {
 775        .probe          = vsp1_probe,
 776        .remove         = vsp1_remove,
 777        .driver         = {
 778                .name   = "vsp1",
 779                .pm     = &vsp1_pm_ops,
 780                .of_match_table = vsp1_of_match,
 781        },
 782};
 783
 784module_platform_driver(vsp1_platform_driver);
 785
 786MODULE_ALIAS("vsp1");
 787MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
 788MODULE_DESCRIPTION("Renesas VSP1 Driver");
 789MODULE_LICENSE("GPL");
 790