// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/host1x.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_probe_helper.h>
#include "drm.h"
#include "dc.h"
#include "plane.h"
static const u32 tegra_shared_plane_formats[] = {
DRM_FORMAT_ARGB1555,
DRM_FORMAT_RGB565,
DRM_FORMAT_RGBA5551,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_ABGR8888,
/* new on Tegra114 */
DRM_FORMAT_ABGR4444,
DRM_FORMAT_ABGR1555,
DRM_FORMAT_BGRA5551,
DRM_FORMAT_XRGB1555,
DRM_FORMAT_RGBX5551,
DRM_FORMAT_XBGR1555,
DRM_FORMAT_BGRX5551,
DRM_FORMAT_BGR565,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_XBGR8888,
/* planar formats */
DRM_FORMAT_UYVY,
DRM_FORMAT_YUYV,
DRM_FORMAT_YUV420,
DRM_FORMAT_YUV422,
};
static const u64 tegra_shared_plane_modifiers[] = {
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0),
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1),
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2),
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3),
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4),
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5),
DRM_FORMAT_MOD_INVALID
};
static inline unsigned int tegra_plane_offset(struct tegra_plane *plane,
unsigned int offset)
{
if (offset >= 0x500 && offset <= 0x581) {
offset = 0x000 + (offset - 0x500);
return plane->offset + offset;
}
if (offset >= 0x700 && offset <= 0x73c) {
offset = 0x180 + (offset - 0x700);
return plane->offset + offset;
}
if (offset >= 0x800 && offset <= 0x83e) {
offset = 0x1c0 + (offset - 0x800);
return plane->offset + offset;
}
dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset);
return plane->offset + offset;
}
static inline u32 tegra_plane_readl(struct tegra_plane *plane,
unsigned int offset)
{
return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset));
}
static inline void tegra_plane_writel(struct tegra_plane *plane, u32 value,
unsigned int offset)
{
tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset));
}
static int tegra_windowgroup_enable(struct tegra_windowgroup *wgrp)
{
int err = 0;
mutex_lock(&wgrp->lock);
if (wgrp->usecount == 0) {
err = host1x_client_resume(wgrp->parent);
if (err < 0) {
dev_err(wgrp->parent->dev, "failed to resume: %d\n", err);
goto unlock;
}
reset_control_deassert(wgrp->rst);
}
wgrp->usecount++;
unlock:
mutex_unlock(&wgrp->lock);
return err;
}
static void tegra_windowgroup_disable(struct tegra_windowgroup *wgrp)
{
int err;
mutex_lock(&wgrp->lock);
if (wgrp->usecount == 1) {
err = reset_control_assert(wgrp->rst);
if (err < 0) {
pr_err("failed to assert reset for window group %u\n",
wgrp->index);
}
host1x_client_suspend(wgrp->parent);
}
wgrp->usecount--;
mutex_unlock(&wgrp->lock);
}
int tegra_display_hub_prepare(struct tegra_display_hub *hub)
{
unsigned int i;
/*
* XXX Enabling/disabling windowgroups needs to happen when the owner
* display controller is disabled. There's currently no good point at
* which this could be executed, so unconditionally enable all window
* groups for now.
*/
for (i = 0; i < hub->soc->num_wgrps; i++) {
struct tegra_windowgroup *wgrp = &hub->wgrps[i];
/* Skip orphaned window group whose parent DC is disabled */
if (wgrp->parent)
tegra_windowgroup_enable(wgrp);
}
return 0;
}
void tegra_display_hub_cleanup(struct