1022 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * vsp1_drv.c -- R-Car VSP1 Driver4 *5 * Copyright (C) 2013-2015 Renesas Electronics Corporation6 *7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)8 */9 10#include <linux/clk.h>11#include <linux/delay.h>12#include <linux/device.h>13#include <linux/interrupt.h>14#include <linux/module.h>15#include <linux/of.h>16#include <linux/platform_device.h>17#include <linux/pm_runtime.h>18#include <linux/reset.h>19#include <linux/videodev2.h>20 21#include <media/rcar-fcp.h>22#include <media/v4l2-subdev.h>23 24#include "vsp1.h"25#include "vsp1_brx.h"26#include "vsp1_clu.h"27#include "vsp1_dl.h"28#include "vsp1_drm.h"29#include "vsp1_hgo.h"30#include "vsp1_hgt.h"31#include "vsp1_hsit.h"32#include "vsp1_lif.h"33#include "vsp1_lut.h"34#include "vsp1_pipe.h"35#include "vsp1_rwpf.h"36#include "vsp1_sru.h"37#include "vsp1_uds.h"38#include "vsp1_uif.h"39#include "vsp1_video.h"40 41/* -----------------------------------------------------------------------------42 * Interrupt Handling43 */44 45static irqreturn_t vsp1_irq_handler(int irq, void *data)46{47 u32 mask = VI6_WPF_IRQ_STA_DFE | VI6_WPF_IRQ_STA_FRE |48 VI6_WPF_IRQ_STA_UND;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_WPF_IRQ_STA_UND) && wpf->entity.pipe) {64 wpf->entity.pipe->underrun_count++;65 66 dev_warn_ratelimited(vsp1->dev,67 "Underrun occurred at WPF%u (total underruns %u)\n",68 i, wpf->entity.pipe->underrun_count);69 }70 71 if (status & VI6_WPF_IRQ_STA_DFE) {72 vsp1_pipeline_frame_end(wpf->entity.pipe);73 ret = IRQ_HANDLED;74 }75 }76 77 return ret;78}79 80/* -----------------------------------------------------------------------------81 * Entities82 */83 84/*85 * vsp1_create_sink_links - Create links from all sources to the given sink86 *87 * This function creates media links from all valid sources to the given sink88 * pad. Links that would be invalid according to the VSP1 hardware capabilities89 * are skipped. Those include all links90 *91 * - from a UDS to a UDS (UDS entities can't be chained)92 * - from an entity to itself (no loops are allowed)93 *94 * Furthermore, the BRS can't be connected to histogram generators, but no95 * special check is currently needed as all VSP instances that include a BRS96 * have no histogram generator.97 */98static int vsp1_create_sink_links(struct vsp1_device *vsp1,99 struct vsp1_entity *sink)100{101 struct media_entity *entity = &sink->subdev.entity;102 struct vsp1_entity *source;103 unsigned int pad;104 int ret;105 106 list_for_each_entry(source, &vsp1->entities, list_dev) {107 u32 flags;108 109 if (source->type == sink->type)110 continue;111 112 if (source->type == VSP1_ENTITY_HGO ||113 source->type == VSP1_ENTITY_HGT ||114 source->type == VSP1_ENTITY_LIF ||115 source->type == VSP1_ENTITY_WPF)116 continue;117 118 flags = source->type == VSP1_ENTITY_RPF &&119 sink->type == VSP1_ENTITY_WPF &&120 source->index == sink->index121 ? MEDIA_LNK_FL_ENABLED : 0;122 123 for (pad = 0; pad < entity->num_pads; ++pad) {124 if (!(entity->pads[pad].flags & MEDIA_PAD_FL_SINK))125 continue;126 127 ret = media_create_pad_link(&source->subdev.entity,128 source->source_pad,129 entity, pad, flags);130 if (ret < 0)131 return ret;132 133 if (flags & MEDIA_LNK_FL_ENABLED)134 source->sink = sink;135 }136 }137 138 return 0;139}140 141static int vsp1_uapi_create_links(struct vsp1_device *vsp1)142{143 struct vsp1_entity *entity;144 unsigned int i;145 int ret;146 147 list_for_each_entry(entity, &vsp1->entities, list_dev) {148 if (entity->type == VSP1_ENTITY_LIF ||149 entity->type == VSP1_ENTITY_RPF)150 continue;151 152 ret = vsp1_create_sink_links(vsp1, entity);153 if (ret < 0)154 return ret;155 }156 157 if (vsp1->hgo) {158 ret = media_create_pad_link(&vsp1->hgo->histo.entity.subdev.entity,159 HISTO_PAD_SOURCE,160 &vsp1->hgo->histo.video.entity, 0,161 MEDIA_LNK_FL_ENABLED |162 MEDIA_LNK_FL_IMMUTABLE);163 if (ret < 0)164 return ret;165 }166 167 if (vsp1->hgt) {168 ret = media_create_pad_link(&vsp1->hgt->histo.entity.subdev.entity,169 HISTO_PAD_SOURCE,170 &vsp1->hgt->histo.video.entity, 0,171 MEDIA_LNK_FL_ENABLED |172 MEDIA_LNK_FL_IMMUTABLE);173 if (ret < 0)174 return ret;175 }176 177 for (i = 0; i < vsp1->info->lif_count; ++i) {178 if (!vsp1->lif[i])179 continue;180 181 ret = media_create_pad_link(&vsp1->wpf[i]->entity.subdev.entity,182 RWPF_PAD_SOURCE,183 &vsp1->lif[i]->entity.subdev.entity,184 LIF_PAD_SINK, 0);185 if (ret < 0)186 return ret;187 }188 189 for (i = 0; i < vsp1->info->rpf_count; ++i) {190 struct vsp1_rwpf *rpf = vsp1->rpf[i];191 192 ret = media_create_pad_link(&rpf->video->video.entity, 0,193 &rpf->entity.subdev.entity,194 RWPF_PAD_SINK,195 MEDIA_LNK_FL_ENABLED |196 MEDIA_LNK_FL_IMMUTABLE);197 if (ret < 0)198 return ret;199 }200 201 for (i = 0; i < vsp1->info->wpf_count; ++i) {202 /*203 * Connect the video device to the WPF. All connections are204 * immutable.205 */206 struct vsp1_rwpf *wpf = vsp1->wpf[i];207 208 ret = media_create_pad_link(&wpf->entity.subdev.entity,209 RWPF_PAD_SOURCE,210 &wpf->video->video.entity, 0,211 MEDIA_LNK_FL_IMMUTABLE |212 MEDIA_LNK_FL_ENABLED);213 if (ret < 0)214 return ret;215 }216 217 return 0;218}219 220static void vsp1_destroy_entities(struct vsp1_device *vsp1)221{222 struct vsp1_entity *entity, *_entity;223 struct vsp1_video *video, *_video;224 225 list_for_each_entry_safe(entity, _entity, &vsp1->entities, list_dev) {226 list_del(&entity->list_dev);227 vsp1_entity_destroy(entity);228 }229 230 list_for_each_entry_safe(video, _video, &vsp1->videos, list) {231 list_del(&video->list);232 vsp1_video_cleanup(video);233 }234 235 v4l2_device_unregister(&vsp1->v4l2_dev);236 if (vsp1->info->uapi)237 media_device_unregister(&vsp1->media_dev);238 media_device_cleanup(&vsp1->media_dev);239 240 if (!vsp1->info->uapi)241 vsp1_drm_cleanup(vsp1);242}243 244static int vsp1_create_entities(struct vsp1_device *vsp1)245{246 struct media_device *mdev = &vsp1->media_dev;247 struct v4l2_device *vdev = &vsp1->v4l2_dev;248 struct vsp1_entity *entity;249 unsigned int i;250 int ret;251 252 mdev->dev = vsp1->dev;253 mdev->hw_revision = vsp1->version;254 strscpy(mdev->model, vsp1->info->model, sizeof(mdev->model));255 media_device_init(mdev);256 257 vsp1->media_ops.link_setup = vsp1_entity_link_setup;258 /*259 * Don't perform link validation when the userspace API is disabled as260 * the pipeline is configured internally by the driver in that case, and261 * its configuration can thus be trusted.262 */263 if (vsp1->info->uapi)264 vsp1->media_ops.link_validate = v4l2_subdev_link_validate;265 266 vdev->mdev = mdev;267 ret = v4l2_device_register(vsp1->dev, vdev);268 if (ret < 0) {269 dev_err(vsp1->dev, "V4L2 device registration failed (%d)\n",270 ret);271 goto done;272 }273 274 /* Instantiate all the entities. */275 if (vsp1_feature(vsp1, VSP1_HAS_BRS)) {276 vsp1->brs = vsp1_brx_create(vsp1, VSP1_ENTITY_BRS);277 if (IS_ERR(vsp1->brs)) {278 ret = PTR_ERR(vsp1->brs);279 goto done;280 }281 282 list_add_tail(&vsp1->brs->entity.list_dev, &vsp1->entities);283 }284 285 if (vsp1_feature(vsp1, VSP1_HAS_BRU)) {286 vsp1->bru = vsp1_brx_create(vsp1, VSP1_ENTITY_BRU);287 if (IS_ERR(vsp1->bru)) {288 ret = PTR_ERR(vsp1->bru);289 goto done;290 }291 292 list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities);293 }294 295 if (vsp1_feature(vsp1, VSP1_HAS_CLU)) {296 vsp1->clu = vsp1_clu_create(vsp1);297 if (IS_ERR(vsp1->clu)) {298 ret = PTR_ERR(vsp1->clu);299 goto done;300 }301 302 list_add_tail(&vsp1->clu->entity.list_dev, &vsp1->entities);303 }304 305 vsp1->hsi = vsp1_hsit_create(vsp1, true);306 if (IS_ERR(vsp1->hsi)) {307 ret = PTR_ERR(vsp1->hsi);308 goto done;309 }310 311 list_add_tail(&vsp1->hsi->entity.list_dev, &vsp1->entities);312 313 vsp1->hst = vsp1_hsit_create(vsp1, false);314 if (IS_ERR(vsp1->hst)) {315 ret = PTR_ERR(vsp1->hst);316 goto done;317 }318 319 list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities);320 321 if (vsp1_feature(vsp1, VSP1_HAS_HGO) && vsp1->info->uapi) {322 vsp1->hgo = vsp1_hgo_create(vsp1);323 if (IS_ERR(vsp1->hgo)) {324 ret = PTR_ERR(vsp1->hgo);325 goto done;326 }327 328 list_add_tail(&vsp1->hgo->histo.entity.list_dev,329 &vsp1->entities);330 }331 332 if (vsp1_feature(vsp1, VSP1_HAS_HGT) && vsp1->info->uapi) {333 vsp1->hgt = vsp1_hgt_create(vsp1);334 if (IS_ERR(vsp1->hgt)) {335 ret = PTR_ERR(vsp1->hgt);336 goto done;337 }338 339 list_add_tail(&vsp1->hgt->histo.entity.list_dev,340 &vsp1->entities);341 }342 343 /*344 * The LIFs are only supported when used in conjunction with the DU, in345 * which case the userspace API is disabled. If the userspace API is346 * enabled skip the LIFs, even when present.347 */348 if (!vsp1->info->uapi) {349 for (i = 0; i < vsp1->info->lif_count; ++i) {350 struct vsp1_lif *lif;351 352 lif = vsp1_lif_create(vsp1, i);353 if (IS_ERR(lif)) {354 ret = PTR_ERR(lif);355 goto done;356 }357 358 vsp1->lif[i] = lif;359 list_add_tail(&lif->entity.list_dev, &vsp1->entities);360 }361 }362 363 if (vsp1_feature(vsp1, VSP1_HAS_LUT)) {364 vsp1->lut = vsp1_lut_create(vsp1);365 if (IS_ERR(vsp1->lut)) {366 ret = PTR_ERR(vsp1->lut);367 goto done;368 }369 370 list_add_tail(&vsp1->lut->entity.list_dev, &vsp1->entities);371 }372 373 for (i = 0; i < vsp1->info->rpf_count; ++i) {374 struct vsp1_rwpf *rpf;375 376 rpf = vsp1_rpf_create(vsp1, i);377 if (IS_ERR(rpf)) {378 ret = PTR_ERR(rpf);379 goto done;380 }381 382 vsp1->rpf[i] = rpf;383 list_add_tail(&rpf->entity.list_dev, &vsp1->entities);384 385 if (vsp1->info->uapi) {386 struct vsp1_video *video = vsp1_video_create(vsp1, rpf);387 388 if (IS_ERR(video)) {389 ret = PTR_ERR(video);390 goto done;391 }392 393 list_add_tail(&video->list, &vsp1->videos);394 }395 }396 397 if (vsp1_feature(vsp1, VSP1_HAS_SRU)) {398 vsp1->sru = vsp1_sru_create(vsp1);399 if (IS_ERR(vsp1->sru)) {400 ret = PTR_ERR(vsp1->sru);401 goto done;402 }403 404 list_add_tail(&vsp1->sru->entity.list_dev, &vsp1->entities);405 }406 407 for (i = 0; i < vsp1->info->uds_count; ++i) {408 struct vsp1_uds *uds;409 410 uds = vsp1_uds_create(vsp1, i);411 if (IS_ERR(uds)) {412 ret = PTR_ERR(uds);413 goto done;414 }415 416 vsp1->uds[i] = uds;417 list_add_tail(&uds->entity.list_dev, &vsp1->entities);418 }419 420 for (i = 0; i < vsp1->info->uif_count; ++i) {421 struct vsp1_uif *uif;422 423 uif = vsp1_uif_create(vsp1, i);424 if (IS_ERR(uif)) {425 ret = PTR_ERR(uif);426 goto done;427 }428 429 vsp1->uif[i] = uif;430 list_add_tail(&uif->entity.list_dev, &vsp1->entities);431 }432 433 for (i = 0; i < vsp1->info->wpf_count; ++i) {434 struct vsp1_rwpf *wpf;435 436 wpf = vsp1_wpf_create(vsp1, i);437 if (IS_ERR(wpf)) {438 ret = PTR_ERR(wpf);439 goto done;440 }441 442 vsp1->wpf[i] = wpf;443 list_add_tail(&wpf->entity.list_dev, &vsp1->entities);444 445 if (vsp1->info->uapi) {446 struct vsp1_video *video = vsp1_video_create(vsp1, wpf);447 448 if (IS_ERR(video)) {449 ret = PTR_ERR(video);450 goto done;451 }452 453 list_add_tail(&video->list, &vsp1->videos);454 }455 }456 457 /* Register all subdevs. */458 list_for_each_entry(entity, &vsp1->entities, list_dev) {459 ret = v4l2_device_register_subdev(&vsp1->v4l2_dev,460 &entity->subdev);461 if (ret < 0)462 goto done;463 }464 465 /*466 * Create links and register subdev nodes if the userspace API is467 * enabled or initialize the DRM pipeline otherwise.468 */469 if (vsp1->info->uapi) {470 ret = vsp1_uapi_create_links(vsp1);471 if (ret < 0)472 goto done;473 474 ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev);475 if (ret < 0)476 goto done;477 478 ret = media_device_register(mdev);479 } else {480 ret = vsp1_drm_init(vsp1);481 }482 483done:484 if (ret < 0)485 vsp1_destroy_entities(vsp1);486 487 return ret;488}489 490int vsp1_reset_wpf(struct vsp1_device *vsp1, unsigned int index)491{492 unsigned int timeout;493 u32 status;494 495 status = vsp1_read(vsp1, VI6_STATUS);496 if (!(status & VI6_STATUS_SYS_ACT(index)))497 return 0;498 499 vsp1_write(vsp1, VI6_SRESET, VI6_SRESET_SRTS(index));500 for (timeout = 10; timeout > 0; --timeout) {501 status = vsp1_read(vsp1, VI6_STATUS);502 if (!(status & VI6_STATUS_SYS_ACT(index)))503 break;504 505 usleep_range(1000, 2000);506 }507 508 if (!timeout) {509 dev_err(vsp1->dev, "failed to reset wpf.%u\n", index);510 return -ETIMEDOUT;511 }512 513 return 0;514}515 516static int vsp1_device_init(struct vsp1_device *vsp1)517{518 unsigned int i;519 int ret;520 521 /* Reset any channel that might be running. */522 for (i = 0; i < vsp1->info->wpf_count; ++i) {523 ret = vsp1_reset_wpf(vsp1, i);524 if (ret < 0)525 return ret;526 }527 528 vsp1_write(vsp1, VI6_CLK_DCSWT, (8 << VI6_CLK_DCSWT_CSTPW_SHIFT) |529 (8 << VI6_CLK_DCSWT_CSTRW_SHIFT));530 531 for (i = 0; i < vsp1->info->rpf_count; ++i)532 vsp1_write(vsp1, VI6_DPR_RPF_ROUTE(i), VI6_DPR_NODE_UNUSED);533 534 for (i = 0; i < vsp1->info->uds_count; ++i)535 vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED);536 537 for (i = 0; i < vsp1->info->uif_count; ++i)538 vsp1_write(vsp1, VI6_DPR_UIF_ROUTE(i), VI6_DPR_NODE_UNUSED);539 540 vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED);541 vsp1_write(vsp1, VI6_DPR_LUT_ROUTE, VI6_DPR_NODE_UNUSED);542 vsp1_write(vsp1, VI6_DPR_CLU_ROUTE, VI6_DPR_NODE_UNUSED);543 vsp1_write(vsp1, VI6_DPR_HST_ROUTE, VI6_DPR_NODE_UNUSED);544 vsp1_write(vsp1, VI6_DPR_HSI_ROUTE, VI6_DPR_NODE_UNUSED);545 vsp1_write(vsp1, VI6_DPR_BRU_ROUTE, VI6_DPR_NODE_UNUSED);546 547 if (vsp1_feature(vsp1, VSP1_HAS_BRS))548 vsp1_write(vsp1, VI6_DPR_ILV_BRS_ROUTE, VI6_DPR_NODE_UNUSED);549 550 vsp1_write(vsp1, VI6_DPR_HGO_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |551 (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));552 vsp1_write(vsp1, VI6_DPR_HGT_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |553 (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));554 555 vsp1_dlm_setup(vsp1);556 557 return 0;558}559 560static void vsp1_mask_all_interrupts(struct vsp1_device *vsp1)561{562 unsigned int i;563 564 for (i = 0; i < vsp1->info->lif_count; ++i)565 vsp1_write(vsp1, VI6_DISP_IRQ_ENB(i), 0);566 for (i = 0; i < vsp1->info->wpf_count; ++i)567 vsp1_write(vsp1, VI6_WPF_IRQ_ENB(i), 0);568}569 570/*571 * vsp1_device_get - Acquire the VSP1 device572 *573 * Make sure the device is not suspended and initialize it if needed.574 *575 * Return 0 on success or a negative error code otherwise.576 */577int vsp1_device_get(struct vsp1_device *vsp1)578{579 return pm_runtime_resume_and_get(vsp1->dev);580}581 582/*583 * vsp1_device_put - Release the VSP1 device584 *585 * Decrement the VSP1 reference count and cleanup the device if the last586 * reference is released.587 */588void vsp1_device_put(struct vsp1_device *vsp1)589{590 pm_runtime_put_sync(vsp1->dev);591}592 593/* -----------------------------------------------------------------------------594 * Power Management595 */596 597static int __maybe_unused vsp1_pm_suspend(struct device *dev)598{599 struct vsp1_device *vsp1 = dev_get_drvdata(dev);600 601 /*602 * When used as part of a display pipeline, the VSP is stopped and603 * restarted explicitly by the DU.604 */605 if (!vsp1->drm)606 vsp1_video_suspend(vsp1);607 608 pm_runtime_force_suspend(vsp1->dev);609 610 return 0;611}612 613static int __maybe_unused vsp1_pm_resume(struct device *dev)614{615 struct vsp1_device *vsp1 = dev_get_drvdata(dev);616 617 pm_runtime_force_resume(vsp1->dev);618 619 /*620 * When used as part of a display pipeline, the VSP is stopped and621 * restarted explicitly by the DU.622 */623 if (!vsp1->drm)624 vsp1_video_resume(vsp1);625 626 return 0;627}628 629static int __maybe_unused vsp1_pm_runtime_suspend(struct device *dev)630{631 struct vsp1_device *vsp1 = dev_get_drvdata(dev);632 633 rcar_fcp_disable(vsp1->fcp);634 reset_control_assert(vsp1->rstc);635 636 return 0;637}638 639static int __maybe_unused vsp1_pm_runtime_resume(struct device *dev)640{641 struct vsp1_device *vsp1 = dev_get_drvdata(dev);642 int ret;643 644 ret = reset_control_deassert(vsp1->rstc);645 if (ret < 0)646 return ret;647 648 if (vsp1->info) {649 /*650 * On R-Car Gen2 and RZ/G1, vsp1 register access after deassert651 * can cause lock-up. It is a special case and needs some delay652 * to avoid this lock-up.653 */654 if (vsp1->info->gen == 2)655 udelay(1);656 657 ret = vsp1_device_init(vsp1);658 if (ret < 0)659 goto done;660 }661 662 ret = rcar_fcp_enable(vsp1->fcp);663 664done:665 if (ret < 0)666 reset_control_assert(vsp1->rstc);667 668 return ret;669}670 671static const struct dev_pm_ops vsp1_pm_ops = {672 SET_SYSTEM_SLEEP_PM_OPS(vsp1_pm_suspend, vsp1_pm_resume)673 SET_RUNTIME_PM_OPS(vsp1_pm_runtime_suspend, vsp1_pm_runtime_resume, NULL)674};675 676/* -----------------------------------------------------------------------------677 * Platform Driver678 */679 680static const struct vsp1_device_info vsp1_device_infos[] = {681 {682 .version = VI6_IP_VERSION_MODEL_VSPS_H2,683 .model = "VSP1-S",684 .gen = 2,685 .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_HGO686 | VSP1_HAS_HGT | VSP1_HAS_LUT | VSP1_HAS_SRU687 | VSP1_HAS_WPF_VFLIP,688 .rpf_count = 5,689 .uds_count = 3,690 .wpf_count = 4,691 .num_bru_inputs = 4,692 .uapi = true,693 }, {694 .version = VI6_IP_VERSION_MODEL_VSPR_H2,695 .model = "VSP1-R",696 .gen = 2,697 .features = VSP1_HAS_BRU | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,698 .rpf_count = 5,699 .uds_count = 3,700 .wpf_count = 4,701 .num_bru_inputs = 4,702 .uapi = true,703 }, {704 .version = VI6_IP_VERSION_MODEL_VSPD_GEN2,705 .model = "VSP1-D",706 .gen = 2,707 .features = VSP1_HAS_BRU | VSP1_HAS_HGO | VSP1_HAS_LUT,708 .lif_count = 1,709 .rpf_count = 4,710 .uds_count = 1,711 .wpf_count = 1,712 .num_bru_inputs = 4,713 .uapi = true,714 }, {715 .version = VI6_IP_VERSION_MODEL_VSPS_M2,716 .model = "VSP1-S",717 .gen = 2,718 .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_HGO719 | VSP1_HAS_HGT | VSP1_HAS_LUT | VSP1_HAS_SRU720 | VSP1_HAS_WPF_VFLIP,721 .rpf_count = 5,722 .uds_count = 1,723 .wpf_count = 4,724 .num_bru_inputs = 4,725 .uapi = true,726 }, {727 .version = VI6_IP_VERSION_MODEL_VSPS_V2H,728 .model = "VSP1V-S",729 .gen = 2,730 .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT731 | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,732 .rpf_count = 4,733 .uds_count = 1,734 .wpf_count = 4,735 .num_bru_inputs = 4,736 .uapi = true,737 }, {738 .version = VI6_IP_VERSION_MODEL_VSPD_V2H,739 .model = "VSP1V-D",740 .gen = 2,741 .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT,742 .lif_count = 1,743 .rpf_count = 4,744 .uds_count = 1,745 .wpf_count = 1,746 .num_bru_inputs = 4,747 .uapi = true,748 }, {749 .version = VI6_IP_VERSION_MODEL_VSPI_GEN3,750 .model = "VSP2-I",751 .gen = 3,752 .features = VSP1_HAS_CLU | VSP1_HAS_HGO | VSP1_HAS_HGT753 | VSP1_HAS_LUT | VSP1_HAS_SRU | VSP1_HAS_WPF_HFLIP754 | VSP1_HAS_WPF_VFLIP,755 .rpf_count = 1,756 .uds_count = 1,757 .wpf_count = 1,758 .uapi = true,759 }, {760 .version = VI6_IP_VERSION_MODEL_VSPBD_GEN3,761 .model = "VSP2-BD",762 .gen = 3,763 .features = VSP1_HAS_BRU | VSP1_HAS_WPF_VFLIP,764 .rpf_count = 5,765 .wpf_count = 1,766 .num_bru_inputs = 5,767 .uapi = true,768 }, {769 .version = VI6_IP_VERSION_MODEL_VSPBC_GEN3,770 .model = "VSP2-BC",771 .gen = 3,772 .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_HGO773 | VSP1_HAS_LUT | VSP1_HAS_WPF_VFLIP,774 .rpf_count = 5,775 .wpf_count = 1,776 .num_bru_inputs = 5,777 .uapi = true,778 }, {779 .version = VI6_IP_VERSION_MODEL_VSPBS_GEN3,780 .model = "VSP2-BS",781 .gen = 3,782 .features = VSP1_HAS_BRS | VSP1_HAS_WPF_VFLIP,783 .rpf_count = 2,784 .wpf_count = 1,785 .uapi = true,786 }, {787 .version = VI6_IP_VERSION_MODEL_VSPD_GEN3,788 .model = "VSP2-D",789 .gen = 3,790 .features = VSP1_HAS_BRU | VSP1_HAS_WPF_VFLIP | VSP1_HAS_EXT_DL,791 .lif_count = 1,792 .rpf_count = 5,793 .uif_count = 1,794 .wpf_count = 2,795 .num_bru_inputs = 5,796 }, {797 .version = VI6_IP_VERSION_MODEL_VSPD_V3,798 .model = "VSP2-D",799 .soc = VI6_IP_VERSION_SOC_V3H,800 .gen = 3,801 .features = VSP1_HAS_BRS | VSP1_HAS_BRU,802 .lif_count = 1,803 .rpf_count = 5,804 .uif_count = 1,805 .wpf_count = 1,806 .num_bru_inputs = 5,807 }, {808 .version = VI6_IP_VERSION_MODEL_VSPD_V3,809 .model = "VSP2-D",810 .soc = VI6_IP_VERSION_SOC_V3M,811 .gen = 3,812 .features = VSP1_HAS_BRS | VSP1_HAS_BRU | VSP1_HAS_NON_ZERO_LBA,813 .lif_count = 1,814 .rpf_count = 5,815 .uif_count = 1,816 .wpf_count = 1,817 .num_bru_inputs = 5,818 }, {819 .version = VI6_IP_VERSION_MODEL_VSPDL_GEN3,820 .model = "VSP2-DL",821 .gen = 3,822 .features = VSP1_HAS_BRS | VSP1_HAS_BRU | VSP1_HAS_EXT_DL,823 .lif_count = 2,824 .rpf_count = 5,825 .uif_count = 2,826 .wpf_count = 2,827 .num_bru_inputs = 5,828 }, {829 .version = VI6_IP_VERSION_MODEL_VSPD_GEN4,830 .model = "VSP2-D",831 .gen = 4,832 .features = VSP1_HAS_BRU | VSP1_HAS_EXT_DL,833 .lif_count = 1,834 .rpf_count = 5,835 .uif_count = 2,836 .wpf_count = 1,837 .num_bru_inputs = 5,838 },839};840 841static const struct vsp1_device_info rzg2l_vsp2_device_info = {842 .version = VI6_IP_VERSION_MODEL_VSPD_RZG2L,843 .model = "VSP2-D",844 .soc = VI6_IP_VERSION_SOC_RZG2L,845 .gen = 3,846 .features = VSP1_HAS_BRS | VSP1_HAS_WPF_VFLIP | VSP1_HAS_EXT_DL847 | VSP1_HAS_NON_ZERO_LBA,848 .lif_count = 1,849 .rpf_count = 2,850 .wpf_count = 1,851};852 853static const struct vsp1_device_info *vsp1_lookup_info(struct vsp1_device *vsp1)854{855 const struct vsp1_device_info *info;856 unsigned int i;857 u32 model;858 u32 soc;859 860 /*861 * Try the info stored in match data first for devices that don't have862 * a version register.863 */864 info = of_device_get_match_data(vsp1->dev);865 if (info) {866 vsp1->version = VI6_IP_VERSION_VSP_SW | info->version | info->soc;867 return info;868 }869 870 vsp1->version = vsp1_read(vsp1, VI6_IP_VERSION);871 model = vsp1->version & VI6_IP_VERSION_MODEL_MASK;872 soc = vsp1->version & VI6_IP_VERSION_SOC_MASK;873 874 for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) {875 info = &vsp1_device_infos[i];876 877 if (model == info->version && (!info->soc || soc == info->soc))878 return info;879 }880 881 dev_err(vsp1->dev, "unsupported IP version 0x%08x\n", vsp1->version);882 883 return NULL;884}885 886static int vsp1_probe(struct platform_device *pdev)887{888 struct vsp1_device *vsp1;889 struct device_node *fcp_node;890 int ret;891 int irq;892 893 vsp1 = devm_kzalloc(&pdev->dev, sizeof(*vsp1), GFP_KERNEL);894 if (vsp1 == NULL)895 return -ENOMEM;896 897 vsp1->dev = &pdev->dev;898 INIT_LIST_HEAD(&vsp1->entities);899 INIT_LIST_HEAD(&vsp1->videos);900 901 platform_set_drvdata(pdev, vsp1);902 903 /* I/O and IRQ resources (clock managed by the clock PM domain). */904 vsp1->mmio = devm_platform_ioremap_resource(pdev, 0);905 if (IS_ERR(vsp1->mmio))906 return PTR_ERR(vsp1->mmio);907 908 irq = platform_get_irq(pdev, 0);909 if (irq < 0)910 return irq;911 912 vsp1->rstc = devm_reset_control_get_shared(&pdev->dev, NULL);913 if (IS_ERR(vsp1->rstc))914 return dev_err_probe(&pdev->dev, PTR_ERR(vsp1->rstc),915 "failed to get reset control\n");916 917 /* FCP (optional). */918 fcp_node = of_parse_phandle(pdev->dev.of_node, "renesas,fcp", 0);919 if (fcp_node) {920 vsp1->fcp = rcar_fcp_get(fcp_node);921 of_node_put(fcp_node);922 if (IS_ERR(vsp1->fcp)) {923 dev_dbg(&pdev->dev, "FCP not found (%ld)\n",924 PTR_ERR(vsp1->fcp));925 return PTR_ERR(vsp1->fcp);926 }927 928 /*929 * When the FCP is present, it handles all bus master accesses930 * for the VSP and must thus be used in place of the VSP device931 * to map DMA buffers.932 */933 vsp1->bus_master = rcar_fcp_get_device(vsp1->fcp);934 } else {935 vsp1->bus_master = vsp1->dev;936 }937 938 /* Configure device parameters based on the version register. */939 pm_runtime_enable(&pdev->dev);940 941 ret = vsp1_device_get(vsp1);942 if (ret < 0)943 goto done;944 945 vsp1->info = vsp1_lookup_info(vsp1);946 if (!vsp1->info) {947 vsp1_device_put(vsp1);948 ret = -ENXIO;949 goto done;950 }951 952 dev_dbg(&pdev->dev, "IP version 0x%08x\n", vsp1->version);953 954 /*955 * Previous use of the hardware (e.g. by the bootloader) could leave956 * some interrupts enabled and pending.957 *958 * TODO: Investigate if this shouldn't be better handled by using the959 * device reset provided by the CPG.960 */961 vsp1_mask_all_interrupts(vsp1);962 963 vsp1_device_put(vsp1);964 965 ret = devm_request_irq(&pdev->dev, irq, vsp1_irq_handler,966 IRQF_SHARED, dev_name(&pdev->dev), vsp1);967 if (ret < 0) {968 dev_err(&pdev->dev, "failed to request IRQ\n");969 goto done;970 }971 972 /* Instantiate entities. */973 ret = vsp1_create_entities(vsp1);974 if (ret < 0) {975 dev_err(&pdev->dev, "failed to create entities\n");976 goto done;977 }978 979done:980 if (ret) {981 pm_runtime_disable(&pdev->dev);982 rcar_fcp_put(vsp1->fcp);983 }984 985 return ret;986}987 988static void vsp1_remove(struct platform_device *pdev)989{990 struct vsp1_device *vsp1 = platform_get_drvdata(pdev);991 992 vsp1_destroy_entities(vsp1);993 rcar_fcp_put(vsp1->fcp);994 995 pm_runtime_disable(&pdev->dev);996}997 998static const struct of_device_id vsp1_of_match[] = {999 { .compatible = "renesas,vsp1" },1000 { .compatible = "renesas,vsp2" },1001 { .compatible = "renesas,r9a07g044-vsp2", .data = &rzg2l_vsp2_device_info },1002 { },1003};1004MODULE_DEVICE_TABLE(of, vsp1_of_match);1005 1006static struct platform_driver vsp1_platform_driver = {1007 .probe = vsp1_probe,1008 .remove_new = vsp1_remove,1009 .driver = {1010 .name = "vsp1",1011 .pm = &vsp1_pm_ops,1012 .of_match_table = vsp1_of_match,1013 },1014};1015 1016module_platform_driver(vsp1_platform_driver);1017 1018MODULE_ALIAS("vsp1");1019MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");1020MODULE_DESCRIPTION("Renesas VSP1 Driver");1021MODULE_LICENSE("GPL");1022