2019-06-04 16:11:33 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2012-11-16 05:28:22 +08:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2012 Avionic Design GmbH
|
|
|
|
* Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/clk.h>
|
2013-09-24 22:32:47 +08:00
|
|
|
#include <linux/debugfs.h>
|
2019-08-04 17:41:30 +08:00
|
|
|
#include <linux/delay.h>
|
2014-06-27 03:41:53 +08:00
|
|
|
#include <linux/iommu.h>
|
2019-08-04 17:41:30 +08:00
|
|
|
#include <linux/module.h>
|
2017-08-21 22:35:17 +08:00
|
|
|
#include <linux/of_device.h>
|
2015-08-03 19:20:49 +08:00
|
|
|
#include <linux/pm_runtime.h>
|
2013-11-07 07:20:54 +08:00
|
|
|
#include <linux/reset.h>
|
2012-11-16 05:28:22 +08:00
|
|
|
|
2014-07-07 21:32:53 +08:00
|
|
|
#include <soc/tegra/pmc.h>
|
|
|
|
|
2019-08-04 17:41:30 +08:00
|
|
|
#include <drm/drm_atomic.h>
|
|
|
|
#include <drm/drm_atomic_helper.h>
|
|
|
|
#include <drm/drm_debugfs.h>
|
|
|
|
#include <drm/drm_fourcc.h>
|
|
|
|
#include <drm/drm_plane_helper.h>
|
|
|
|
#include <drm/drm_vblank.h>
|
|
|
|
|
2013-03-22 22:34:08 +08:00
|
|
|
#include "dc.h"
|
|
|
|
#include "drm.h"
|
|
|
|
#include "gem.h"
|
2017-08-30 23:42:54 +08:00
|
|
|
#include "hub.h"
|
2017-11-10 22:27:25 +08:00
|
|
|
#include "plane.h"
|
2012-11-16 05:28:22 +08:00
|
|
|
|
2019-03-01 20:56:24 +08:00
|
|
|
static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
|
|
|
|
struct drm_crtc_state *state);
|
|
|
|
|
2015-07-29 03:27:05 +08:00
|
|
|
static void tegra_dc_stats_reset(struct tegra_dc_stats *stats)
|
|
|
|
{
|
|
|
|
stats->frames = 0;
|
|
|
|
stats->vblank = 0;
|
|
|
|
stats->underflow = 0;
|
|
|
|
stats->overflow = 0;
|
|
|
|
}
|
|
|
|
|
2017-12-14 20:37:53 +08:00
|
|
|
/* Reads the active copy of a register. */
|
2014-12-08 23:03:53 +08:00
|
|
|
static u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset)
|
|
|
|
{
|
|
|
|
u32 value;
|
|
|
|
|
|
|
|
tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
|
|
|
|
value = tegra_dc_readl(dc, offset);
|
|
|
|
tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS);
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2017-12-14 20:37:53 +08:00
|
|
|
static inline unsigned int tegra_plane_offset(struct tegra_plane *plane,
|
|
|
|
unsigned int offset)
|
|
|
|
{
|
|
|
|
if (offset >= 0x500 && offset <= 0x638) {
|
|
|
|
offset = 0x000 + (offset - 0x500);
|
|
|
|
return plane->offset + offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset >= 0x700 && offset <= 0x719) {
|
|
|
|
offset = 0x180 + (offset - 0x700);
|
|
|
|
return plane->offset + offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset >= 0x800 && offset <= 0x839) {
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2017-10-13 01:12:57 +08:00
|
|
|
bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev)
|
|
|
|
{
|
|
|
|
struct device_node *np = dc->dev->of_node;
|
|
|
|
struct of_phandle_iterator it;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0)
|
|
|
|
if (it.node == dev->of_node)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-12-08 22:50:04 +08:00
|
|
|
/*
|
|
|
|
* Double-buffered registers have two copies: ASSEMBLY and ACTIVE. When the
|
|
|
|
* *_ACT_REQ bits are set the ASSEMBLY copy is latched into the ACTIVE copy.
|
|
|
|
* Latching happens mmediately if the display controller is in STOP mode or
|
|
|
|
* on the next frame boundary otherwise.
|
|
|
|
*
|
|
|
|
* Triple-buffered registers have three copies: ASSEMBLY, ARM and ACTIVE. The
|
|
|
|
* ASSEMBLY copy is latched into the ARM copy immediately after *_UPDATE bits
|
|
|
|
* are written. When the *_ACT_REQ bits are written, the ARM copy is latched
|
|
|
|
* into the ACTIVE copy, either immediately if the display controller is in
|
|
|
|
* STOP mode, or at the next frame boundary otherwise.
|
|
|
|
*/
|
2014-11-22 00:33:33 +08:00
|
|
|
void tegra_dc_commit(struct tegra_dc *dc)
|
2014-10-21 19:41:46 +08:00
|
|
|
{
|
|
|
|
tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
|
|
|
|
tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
|
|
|
|
}
|
|
|
|
|
2014-03-14 16:54:58 +08:00
|
|
|
static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v,
|
|
|
|
unsigned int bpp)
|
|
|
|
{
|
|
|
|
fixed20_12 outf = dfixed_init(out);
|
|
|
|
fixed20_12 inf = dfixed_init(in);
|
|
|
|
u32 dda_inc;
|
|
|
|
int max;
|
|
|
|
|
|
|
|
if (v)
|
|
|
|
max = 15;
|
|
|
|
else {
|
|
|
|
switch (bpp) {
|
|
|
|
case 2:
|
|
|
|
max = 8;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
WARN_ON_ONCE(1);
|
2020-08-24 06:36:59 +08:00
|
|
|
fallthrough;
|
2014-03-14 16:54:58 +08:00
|
|
|
case 4:
|
|
|
|
max = 4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1));
|
|
|
|
inf.full -= dfixed_const(1);
|
|
|
|
|
|
|
|
dda_inc = dfixed_div(inf, outf);
|
|
|
|
dda_inc = min_t(u32, dda_inc, dfixed_const(max));
|
|
|
|
|
|
|
|
return dda_inc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline u32 compute_initial_dda(unsigned int in)
|
|
|
|
{
|
|
|
|
fixed20_12 inf = dfixed_init(in);
|
|
|
|
return dfixed_frac(inf);
|
|
|
|
}
|
|
|
|
|
2017-12-14 20:46:20 +08:00
|
|
|
static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane)
|
|
|
|
{
|
2017-12-20 16:39:14 +08:00
|
|
|
u32 background[3] = {
|
|
|
|
BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
|
|
|
|
BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
|
|
|
|
BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
|
|
|
|
};
|
|
|
|
u32 foreground = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255) |
|
|
|
|
BLEND_COLOR_KEY_NONE;
|
|
|
|
u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255);
|
|
|
|
struct tegra_plane_state *state;
|
2018-05-04 22:39:59 +08:00
|
|
|
u32 blending[2];
|
2017-12-20 16:39:14 +08:00
|
|
|
unsigned int i;
|
|
|
|
|
2018-05-04 22:39:59 +08:00
|
|
|
/* disable blending for non-overlapping case */
|
|
|
|
tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY);
|
|
|
|
tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN);
|
|
|
|
|
2017-12-20 16:39:14 +08:00
|
|
|
state = to_tegra_plane_state(plane->base.state);
|
|
|
|
|
2018-05-04 22:39:59 +08:00
|
|
|
if (state->opaque) {
|
|
|
|
/*
|
|
|
|
* Since custom fix-weight blending isn't utilized and weight
|
|
|
|
* of top window is set to max, we can enforce dependent
|
|
|
|
* blending which in this case results in transparent bottom
|
|
|
|
* window if top window is opaque and if top window enables
|
|
|
|
* alpha blending, then bottom window is getting alpha value
|
|
|
|
* of 1 minus the sum of alpha components of the overlapping
|
|
|
|
* plane.
|
|
|
|
*/
|
|
|
|
background[0] |= BLEND_CONTROL_DEPENDENT;
|
|
|
|
background[1] |= BLEND_CONTROL_DEPENDENT;
|
2017-12-20 16:39:14 +08:00
|
|
|
|
2018-05-04 22:39:59 +08:00
|
|
|
/*
|
|
|
|
* The region where three windows overlap is the intersection
|
|
|
|
* of the two regions where two windows overlap. It contributes
|
|
|
|
* to the area if all of the windows on top of it have an alpha
|
|
|
|
* component.
|
|
|
|
*/
|
|
|
|
switch (state->base.normalized_zpos) {
|
|
|
|
case 0:
|
|
|
|
if (state->blending[0].alpha &&
|
|
|
|
state->blending[1].alpha)
|
|
|
|
background[2] |= BLEND_CONTROL_DEPENDENT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
background[2] |= BLEND_CONTROL_DEPENDENT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Enable alpha blending if pixel format has an alpha
|
|
|
|
* component.
|
|
|
|
*/
|
2017-12-20 16:39:14 +08:00
|
|
|
foreground |= BLEND_CONTROL_ALPHA;
|
|
|
|
|
2018-05-04 22:39:59 +08:00
|
|
|
/*
|
|
|
|
* If any of the windows on top of this window is opaque, it
|
|
|
|
* will completely conceal this window within that area. If
|
|
|
|
* top window has an alpha component, it is blended over the
|
|
|
|
* bottom window.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
if (state->blending[i].alpha &&
|
|
|
|
state->blending[i].top)
|
|
|
|
background[i] |= BLEND_CONTROL_DEPENDENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (state->base.normalized_zpos) {
|
|
|
|
case 0:
|
|
|
|
if (state->blending[0].alpha &&
|
|
|
|
state->blending[1].alpha)
|
|
|
|
background[2] |= BLEND_CONTROL_DEPENDENT;
|
|
|
|
break;
|
2017-12-14 20:46:20 +08:00
|
|
|
|
2018-05-04 22:39:59 +08:00
|
|
|
case 1:
|
|
|
|
/*
|
|
|
|
* When both middle and topmost windows have an alpha,
|
|
|
|
* these windows a mixed together and then the result
|
|
|
|
* is blended over the bottom window.
|
|
|
|
*/
|
|
|
|
if (state->blending[0].alpha &&
|
|
|
|
state->blending[0].top)
|
|
|
|
background[2] |= BLEND_CONTROL_ALPHA;
|
|
|
|
|
|
|
|
if (state->blending[1].alpha &&
|
|
|
|
state->blending[1].top)
|
|
|
|
background[2] |= BLEND_CONTROL_ALPHA;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (state->base.normalized_zpos) {
|
2017-12-14 20:46:20 +08:00
|
|
|
case 0:
|
2017-12-20 16:39:14 +08:00
|
|
|
tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X);
|
|
|
|
tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y);
|
|
|
|
tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
|
2017-12-14 20:46:20 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
2018-05-04 22:39:59 +08:00
|
|
|
/*
|
|
|
|
* If window B / C is topmost, then X / Y registers are
|
|
|
|
* matching the order of blending[...] state indices,
|
|
|
|
* otherwise a swap is required.
|
|
|
|
*/
|
|
|
|
if (!state->blending[0].top && state->blending[1].top) {
|
|
|
|
blending[0] = foreground;
|
|
|
|
blending[1] = background[1];
|
|
|
|
} else {
|
|
|
|
blending[0] = background[0];
|
|
|
|
blending[1] = foreground;
|
|
|
|
}
|
|
|
|
|
|
|
|
tegra_plane_writel(plane, blending[0], DC_WIN_BLEND_2WIN_X);
|
|
|
|
tegra_plane_writel(plane, blending[1], DC_WIN_BLEND_2WIN_Y);
|
2017-12-20 16:39:14 +08:00
|
|
|
tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
|
2017-12-14 20:46:20 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
2017-12-20 16:39:14 +08:00
|
|
|
tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X);
|
|
|
|
tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_Y);
|
|
|
|
tegra_plane_writel(plane, foreground, DC_WIN_BLEND_3WIN_XY);
|
2017-12-14 20:46:20 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tegra_plane_setup_blending(struct tegra_plane *plane,
|
|
|
|
const struct tegra_dc_window *window)
|
|
|
|
{
|
|
|
|
u32 value;
|
|
|
|
|
|
|
|
value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
|
|
|
|
BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
|
|
|
|
BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
|
|
|
|
tegra_plane_writel(plane, value, DC_WIN_BLEND_MATCH_SELECT);
|
|
|
|
|
|
|
|
value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
|
|
|
|
BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
|
|
|
|
BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
|
|
|
|
tegra_plane_writel(plane, value, DC_WIN_BLEND_NOMATCH_SELECT);
|
|
|
|
|
|
|
|
value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - window->zpos);
|
|
|
|
tegra_plane_writel(plane, value, DC_WIN_BLEND_LAYER_CONTROL);
|
|
|
|
}
|
|
|
|
|
2018-05-04 22:39:58 +08:00
|
|
|
static bool
|
|
|
|
tegra_plane_use_horizontal_filtering(struct tegra_plane *plane,
|
|
|
|
const struct tegra_dc_window *window)
|
|
|
|
{
|
|
|
|
struct tegra_dc *dc = plane->dc;
|
|
|
|
|
|
|
|
if (window->src.w == window->dst.w)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (plane->index == 0 && dc->soc->has_win_a_without_filters)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
tegra_plane_use_vertical_filtering(struct tegra_plane *plane,
|
|
|
|
const struct tegra_dc_window *window)
|
|
|
|
{
|
|
|
|
struct tegra_dc *dc = plane->dc;
|
|
|
|
|
|
|
|
if (window->src.h == window->dst.h)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (plane->index == 0 && dc->soc->has_win_a_without_filters)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (plane->index == 2 && dc->soc->has_win_c_without_vert_filter)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-12-14 20:37:53 +08:00
|
|
|
static void tegra_dc_setup_window(struct tegra_plane *plane,
|
2014-11-24 23:27:13 +08:00
|
|
|
const struct tegra_dc_window *window)
|
2014-03-14 16:54:58 +08:00
|
|
|
{
|
|
|
|
unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp;
|
2017-12-14 20:37:53 +08:00
|
|
|
struct tegra_dc *dc = plane->dc;
|
2014-03-14 16:54:58 +08:00
|
|
|
bool yuv, planar;
|
2017-12-14 20:37:53 +08:00
|
|
|
u32 value;
|
2014-03-14 16:54:58 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* For YUV planar modes, the number of bytes per pixel takes into
|
|
|
|
* account only the luma component and therefore is 1.
|
|
|
|
*/
|
2021-05-28 02:15:46 +08:00
|
|
|
yuv = tegra_plane_format_is_yuv(window->format, &planar, NULL);
|
2014-03-14 16:54:58 +08:00
|
|
|
if (!yuv)
|
|
|
|
bpp = window->bits_per_pixel / 8;
|
|
|
|
else
|
|
|
|
bpp = planar ? 1 : 2;
|
|
|
|
|
2017-12-14 20:37:53 +08:00
|
|
|
tegra_plane_writel(plane, window->format, DC_WIN_COLOR_DEPTH);
|
|
|
|
tegra_plane_writel(plane, window->swap, DC_WIN_BYTE_SWAP);
|
2014-03-14 16:54:58 +08:00
|
|
|
|
|
|
|
value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x);
|
2017-12-14 20:37:53 +08:00
|
|
|
tegra_plane_writel(plane, value, DC_WIN_POSITION);
|
2014-03-14 16:54:58 +08:00
|
|
|
|
|
|
|
value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w);
|
2017-12-14 20:37:53 +08:00
|
|
|
tegra_plane_writel(plane, value, DC_WIN_SIZE);
|
2014-03-14 16:54:58 +08:00
|
|
|
|
|
|
|
h_offset = window->src.x * bpp;
|
|
|
|
v_offset = window->src.y;
|
|
|
|
h_size = window->src.w * bpp;
|
|
|
|
v_size = window->src.h;
|
|
|
|
|
2020-06-18 07:40:39 +08:00
|
|
|
if (window->reflect_x)
|
|
|
|
h_offset += (window->src.w - 1) * bpp;
|
|
|
|
|
|
|
|
if (window->reflect_y)
|
|
|
|
v_offset += window->src.h - 1;
|
|
|
|
|
2014-03-14 16:54:58 +08:00
|
|
|
value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size);
|
2017-12-14 20:37:53 +08:00
|
|
|
tegra_plane_writel(plane, value, DC_WIN_PRESCALED_SIZE);
|
2014-03-14 16:54:58 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* For DDA computations the number of bytes per pixel for YUV planar
|
|
|
|
* modes needs to take into account all Y, U and V components.
|
|
|
|
*/
|
|
|
|
if (yuv && planar)
|
|
|
|
bpp = 2;
|
|
|
|
|
|
|
|
h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp);
|
|
|
|
v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp);
|
|
|
|
|
|
|
|
value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda);
|
2017-12-14 20:37:53 +08:00
|
|
|
tegra_plane_writel(plane, value, DC_WIN_DDA_INC);
|
2014-03-14 16:54:58 +08:00
|
|
|
|
|
|
|
h_dda = compute_initial_dda(window->src.x);
|
|
|
|
v_dda = compute_initial_dda(window->src.y);
|
|
|
|
|
2017-12-14 20:37:53 +08:00
|
|
|
tegra_plane_writel(plane, h_dda, DC_WIN_H_INITIAL_DDA);
|
|
|
|
tegra_plane_writel(plane, v_dda, DC_WIN_V_INITIAL_DDA);
|
2014-03-14 16:54:58 +08:00
|
|
|
|
2017-12-14 20:37:53 +08:00
|
|
|
tegra_plane_writel(plane, 0, DC_WIN_UV_BUF_STRIDE);
|
|
|
|
tegra_plane_writel(plane, 0, DC_WIN_BUF_STRIDE);
|
2014-03-14 16:54:58 +08:00
|
|
|
|
2017-12-14 20:37:53 +08:00
|
|
|
tegra_plane_writel(plane, window->base[0], DC_WINBUF_START_ADDR);
|
2014-03-14 16:54:58 +08:00
|
|
|
|
|
|
|
if (yuv && planar) {
|
2017-12-14 20:37:53 +08:00
|
|
|
tegra_plane_writel(plane, window->base[1], DC_WINBUF_START_ADDR_U);
|
|
|
|
tegra_plane_writel(plane, window->base[2], DC_WINBUF_START_ADDR_V);
|
2014-03-14 16:54:58 +08:00
|
|
|
value = window->stride[1] << 16 | window->stride[0];
|
2017-12-14 20:37:53 +08:00
|
|
|
tegra_plane_writel(plane, value, DC_WIN_LINE_STRIDE);
|
2014-03-14 16:54:58 +08:00
|
|
|
} else {
|
2017-12-14 20:37:53 +08:00
|
|
|
tegra_plane_writel(plane, window->stride[0], DC_WIN_LINE_STRIDE);
|
2014-03-14 16:54:58 +08:00
|
|
|
}
|
|
|
|
|
2017-12-14 20:37:53 +08:00
|
|
|
tegra_plane_writel(plane, h_offset, DC_WINBUF_ADDR_H_OFFSET);
|
|
|
|
tegra_plane_writel(plane, v_offset, DC_WINBUF_ADDR_V_OFFSET);
|
2014-03-14 16:54:58 +08:00
|
|
|
|
2014-06-03 20:48:12 +08:00
|
|
|
if (dc->soc->supports_block_linear) {
|
|
|
|
unsigned long height = window->tiling.value;
|
|
|
|
|
|
|
|
switch (window->tiling.mode) {
|
|
|
|
case TEGRA_BO_TILING_MODE_PITCH:
|
|
|
|
value = DC_WINBUF_SURFACE_KIND_PITCH;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TEGRA_BO_TILING_MODE_TILED:
|
|
|
|
value = DC_WINBUF_SURFACE_KIND_TILED;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TEGRA_BO_TILING_MODE_BLOCK:
|
|
|
|
value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) |
|
|
|
|
DC_WINBUF_SURFACE_KIND_BLOCK;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-12-14 20:37:53 +08:00
|
|
|
tegra_plane_writel(plane, value, DC_WINBUF_SURFACE_KIND);
|
2014-03-14 16:54:58 +08:00
|
|
|
} else {
|
2014-06-03 20:48:12 +08:00
|
|
|
switch (window->tiling.mode) {
|
|
|
|
case TEGRA_BO_TILING_MODE_PITCH:
|
|
|
|
value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
|
|
|
|
DC_WIN_BUFFER_ADDR_MODE_LINEAR;
|
|
|
|
break;
|
2014-03-14 16:54:58 +08:00
|
|
|
|
2014-06-03 20:48:12 +08:00
|
|
|
case TEGRA_BO_TILING_MODE_TILED:
|
|
|
|
value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
|
|
|
|
DC_WIN_BUFFER_ADDR_MODE_TILE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TEGRA_BO_TILING_MODE_BLOCK:
|
2014-11-24 23:27:13 +08:00
|
|
|
/*
|
|
|
|
* No need to handle this here because ->atomic_check
|
|
|
|
* will already have filtered it out.
|
|
|
|
*/
|
|
|
|
break;
|
2014-06-03 20:48:12 +08:00
|
|
|
}
|
|
|
|
|
2017-12-14 20:37:53 +08:00
|
|
|
tegra_plane_writel(plane, value, DC_WIN_BUFFER_ADDR_MODE);
|
2014-06-03 20:48:12 +08:00
|
|
|
}
|
2014-03-14 16:54:58 +08:00
|
|
|
|
|
|
|
value = WIN_ENABLE;
|
|
|
|
|
|
|
|
if (yuv) {
|
|
|
|
/* setup default colorspace conversion coefficients */
|
2017-12-14 20:37:53 +08:00
|
|
|
tegra_plane_writel(plane, 0x00f0, DC_WIN_CSC_YOF);
|
|
|
|
tegra_plane_writel(plane, 0x012a, DC_WIN_CSC_KYRGB);
|
|
|
|
tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KUR);
|
|
|
|
tegra_plane_writel(plane, 0x0198, DC_WIN_CSC_KVR);
|
|
|
|
tegra_plane_writel(plane, 0x039b, DC_WIN_CSC_KUG);
|
|
|
|
tegra_plane_writel(plane, 0x032f, DC_WIN_CSC_KVG);
|
|
|
|
tegra_plane_writel(plane, 0x0204, DC_WIN_CSC_KUB);
|
|
|
|
tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KVB);
|
2014-03-14 16:54:58 +08:00
|
|
|
|
|
|
|
value |= CSC_ENABLE;
|
|
|
|
} else if (window->bits_per_pixel < 24) {
|
|
|
|
value |= COLOR_EXPAND;
|
|
|
|
}
|
|
|
|
|
2020-06-18 07:40:39 +08:00
|
|
|
if (window->reflect_x)
|
|
|
|
value |= H_DIRECTION;
|
|
|
|
|
2020-06-18 07:40:38 +08:00
|
|
|
if (window->reflect_y)
|
2014-03-14 16:54:58 +08:00
|
|
|
value |= V_DIRECTION;
|
|
|
|
|
2018-05-04 22:39:58 +08:00
|
|
|
if (tegra_plane_use_horizontal_filtering(plane, window)) {
|
|
|
|
/*
|
|
|
|
* Enable horizontal 6-tap filter and set filtering
|
|
|
|
* coefficients to the default values defined in TRM.
|
|
|
|
*/
|
|
|
|
tegra_plane_writel(plane, 0x00008000, DC_WIN_H_FILTER_P(0));
|
|
|
|
tegra_plane_writel(plane, 0x3e087ce1, DC_WIN_H_FILTER_P(1));
|
|
|
|
tegra_plane_writel(plane, 0x3b117ac1, DC_WIN_H_FILTER_P(2));
|
|
|
|
tegra_plane_writel(plane, 0x591b73aa, DC_WIN_H_FILTER_P(3));
|
|
|
|
tegra_plane_writel(plane, 0x57256d9a, DC_WIN_H_FILTER_P(4));
|
|
|
|
tegra_plane_writel(plane, 0x552f668b, DC_WIN_H_FILTER_P(5));
|
|
|
|
tegra_plane_writel(plane, 0x73385e8b, DC_WIN_H_FILTER_P(6));
|
|
|
|
tegra_plane_writel(plane, 0x72435583, DC_WIN_H_FILTER_P(7));
|
|
|
|
tegra_plane_writel(plane, 0x714c4c8b, DC_WIN_H_FILTER_P(8));
|
|
|
|
tegra_plane_writel(plane, 0x70554393, DC_WIN_H_FILTER_P(9));
|
|
|
|
tegra_plane_writel(plane, 0x715e389b, DC_WIN_H_FILTER_P(10));
|
|
|
|
tegra_plane_writel(plane, 0x71662faa, DC_WIN_H_FILTER_P(11));
|
|
|
|
tegra_plane_writel(plane, 0x536d25ba, DC_WIN_H_FILTER_P(12));
|
|
|
|
tegra_plane_writel(plane, 0x55731bca, DC_WIN_H_FILTER_P(13));
|
|
|
|
tegra_plane_writel(plane, 0x387a11d9, DC_WIN_H_FILTER_P(14));
|
|
|
|
tegra_plane_writel(plane, 0x3c7c08f1, DC_WIN_H_FILTER_P(15));
|
|
|
|
|
|
|
|
value |= H_FILTER;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tegra_plane_use_vertical_filtering(plane, window)) {
|
|
|
|
unsigned int i, k;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Enable vertical 2-tap filter and set filtering
|
|
|
|
* coefficients to the default values defined in TRM.
|
|
|
|
*/
|
|
|
|
for (i = 0, k = 128; i < 16; i++, k -= 8)
|
|
|
|
tegra_plane_writel(plane, k, DC_WIN_V_FILTER_P(i));
|
|
|
|
|
|
|
|
value |= V_FILTER;
|
|
|
|
}
|
|
|
|
|
2017-12-14 20:37:53 +08:00
|
|
|
tegra_plane_writel(plane, value, DC_WIN_WIN_OPTIONS);
|
2014-03-14 16:54:58 +08:00
|
|
|
|
2018-05-04 22:40:00 +08:00
|
|
|
if (dc->soc->has_legacy_blending)
|
2017-12-14 20:46:20 +08:00
|
|
|
tegra_plane_setup_blending_legacy(plane);
|
2018-05-04 22:40:00 +08:00
|
|
|
else
|
|
|
|
tegra_plane_setup_blending(plane, window);
|
2014-10-21 19:51:53 +08:00
|
|
|
}
|
|
|
|
|
2017-11-14 23:07:40 +08:00
|
|
|
static const u32 tegra20_primary_formats[] = {
|
|
|
|
DRM_FORMAT_ARGB4444,
|
|
|
|
DRM_FORMAT_ARGB1555,
|
|
|
|
DRM_FORMAT_RGB565,
|
|
|
|
DRM_FORMAT_RGBA5551,
|
2017-10-12 23:30:55 +08:00
|
|
|
DRM_FORMAT_ABGR8888,
|
|
|
|
DRM_FORMAT_ARGB8888,
|
2017-12-20 16:39:14 +08:00
|
|
|
/* non-native formats */
|
|
|
|
DRM_FORMAT_XRGB1555,
|
|
|
|
DRM_FORMAT_RGBX5551,
|
|
|
|
DRM_FORMAT_XBGR8888,
|
|
|
|
DRM_FORMAT_XRGB8888,
|
2017-11-14 23:07:40 +08:00
|
|
|
};
|
|
|
|
|
2018-03-15 23:44:04 +08:00
|
|
|
static const u64 tegra20_modifiers[] = {
|
|
|
|
DRM_FORMAT_MOD_LINEAR,
|
|
|
|
DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED,
|
|
|
|
DRM_FORMAT_MOD_INVALID
|
|
|
|
};
|
|
|
|
|
2017-11-14 23:07:40 +08:00
|
|
|
static const u32 tegra114_primary_formats[] = {
|
|
|
|
DRM_FORMAT_ARGB4444,
|
|
|
|
DRM_FORMAT_ARGB1555,
|
|
|
|
DRM_FORMAT_RGB565,
|
|
|
|
DRM_FORMAT_RGBA5551,
|
|
|
|
DRM_FORMAT_ABGR8888,
|
|
|
|
DRM_FORMAT_ARGB8888,
|
|
|
|
/* 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_BGRA8888,
|
|
|
|
DRM_FORMAT_RGBA8888,
|
|
|
|
DRM_FORMAT_XRGB8888,
|
|
|
|
DRM_FORMAT_XBGR8888,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const u32 tegra124_primary_formats[] = {
|
|
|
|
DRM_FORMAT_ARGB4444,
|
|
|
|
DRM_FORMAT_ARGB1555,
|
2014-10-21 19:51:53 +08:00
|
|
|
DRM_FORMAT_RGB565,
|
2017-11-14 23:07:40 +08:00
|
|
|
DRM_FORMAT_RGBA5551,
|
|
|
|
DRM_FORMAT_ABGR8888,
|
|
|
|
DRM_FORMAT_ARGB8888,
|
|
|
|
/* 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_BGRA8888,
|
|
|
|
DRM_FORMAT_RGBA8888,
|
|
|
|
DRM_FORMAT_XRGB8888,
|
|
|
|
DRM_FORMAT_XBGR8888,
|
|
|
|
/* new on Tegra124 */
|
|
|
|
DRM_FORMAT_RGBX8888,
|
|
|
|
DRM_FORMAT_BGRX8888,
|
2014-10-21 19:51:53 +08:00
|
|
|
};
|
|
|
|
|
2018-03-15 23:44:04 +08:00
|
|
|
static const u64 tegra124_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
|
|
|
|
};
|
|
|
|
|
2014-11-24 23:27:13 +08:00
|
|
|
static int tegra_plane_atomic_check(struct drm_plane *plane,
|
drm/atomic: Pass the full state to planes atomic_check
The current atomic helpers have either their object state being passed as
an argument or the full atomic state.
The former is the pattern that was done at first, before switching to the
latter for new hooks or when it was needed.
Let's convert all the remaining helpers to provide a consistent
interface, starting with the planes atomic_check.
The conversion was done using the coccinelle script below plus some
manual changes for vmwgfx, built tested on all the drivers.
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
int (*atomic_check)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_check = func,
...,
};
@@
struct drm_plane_helper_funcs *FUNCS;
identifier f;
identifier dev;
identifier plane, plane_state, state;
@@
f(struct drm_device *dev, struct drm_atomic_state *state)
{
<+...
- FUNCS->atomic_check(plane, plane_state)
+ FUNCS->atomic_check(plane, state)
...+>
}
@ ignores_new_state @
identifier plane_atomic_func.func;
identifier plane, new_plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *new_plane_state)
{
... when != new_plane_state
}
@ adds_new_state depends on plane_atomic_func && !ignores_new_state @
identifier plane_atomic_func.func;
identifier plane, new_plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *new_plane_state)
{
+ struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
...
}
@ depends on plane_atomic_func @
identifier plane_atomic_func.func;
identifier plane, new_plane_state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *new_plane_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_new_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_new_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-4-maxime@cerno.tech
2021-02-19 20:00:24 +08:00
|
|
|
struct drm_atomic_state *state)
|
2014-11-24 23:27:13 +08:00
|
|
|
{
|
drm/atomic: Pass the full state to planes atomic_check
The current atomic helpers have either their object state being passed as
an argument or the full atomic state.
The former is the pattern that was done at first, before switching to the
latter for new hooks or when it was needed.
Let's convert all the remaining helpers to provide a consistent
interface, starting with the planes atomic_check.
The conversion was done using the coccinelle script below plus some
manual changes for vmwgfx, built tested on all the drivers.
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
int (*atomic_check)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_check = func,
...,
};
@@
struct drm_plane_helper_funcs *FUNCS;
identifier f;
identifier dev;
identifier plane, plane_state, state;
@@
f(struct drm_device *dev, struct drm_atomic_state *state)
{
<+...
- FUNCS->atomic_check(plane, plane_state)
+ FUNCS->atomic_check(plane, state)
...+>
}
@ ignores_new_state @
identifier plane_atomic_func.func;
identifier plane, new_plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *new_plane_state)
{
... when != new_plane_state
}
@ adds_new_state depends on plane_atomic_func && !ignores_new_state @
identifier plane_atomic_func.func;
identifier plane, new_plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *new_plane_state)
{
+ struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
...
}
@ depends on plane_atomic_func @
identifier plane_atomic_func.func;
identifier plane, new_plane_state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *new_plane_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_new_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_new_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-4-maxime@cerno.tech
2021-02-19 20:00:24 +08:00
|
|
|
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
|
|
|
|
plane);
|
2021-02-19 20:00:22 +08:00
|
|
|
struct tegra_plane_state *plane_state = to_tegra_plane_state(new_plane_state);
|
2020-06-18 07:40:39 +08:00
|
|
|
unsigned int supported_rotation = DRM_MODE_ROTATE_0 |
|
|
|
|
DRM_MODE_REFLECT_X |
|
|
|
|
DRM_MODE_REFLECT_Y;
|
2021-02-19 20:00:22 +08:00
|
|
|
unsigned int rotation = new_plane_state->rotation;
|
2014-11-28 20:14:55 +08:00
|
|
|
struct tegra_bo_tiling *tiling = &plane_state->tiling;
|
2014-11-26 19:28:39 +08:00
|
|
|
struct tegra_plane *tegra = to_tegra_plane(plane);
|
2021-02-19 20:00:22 +08:00
|
|
|
struct tegra_dc *dc = to_tegra_dc(new_plane_state->crtc);
|
2014-11-24 23:27:13 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
/* no need for further checks if the plane is being disabled */
|
2021-02-19 20:00:22 +08:00
|
|
|
if (!new_plane_state->crtc)
|
2014-11-24 23:27:13 +08:00
|
|
|
return 0;
|
|
|
|
|
2021-02-19 20:00:22 +08:00
|
|
|
err = tegra_plane_format(new_plane_state->fb->format->format,
|
2018-05-04 22:39:59 +08:00
|
|
|
&plane_state->format,
|
2017-11-10 22:27:25 +08:00
|
|
|
&plane_state->swap);
|
2014-11-24 23:27:13 +08:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
2017-12-20 16:39:14 +08:00
|
|
|
/*
|
|
|
|
* Tegra20 and Tegra30 are special cases here because they support
|
|
|
|
* only variants of specific formats with an alpha component, but not
|
|
|
|
* the corresponding opaque formats. However, the opaque formats can
|
|
|
|
* be emulated by disabling alpha blending for the plane.
|
|
|
|
*/
|
2018-05-04 22:40:00 +08:00
|
|
|
if (dc->soc->has_legacy_blending) {
|
2018-05-04 22:39:59 +08:00
|
|
|
err = tegra_plane_setup_legacy_state(tegra, plane_state);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2017-12-20 16:39:14 +08:00
|
|
|
}
|
|
|
|
|
2021-02-19 20:00:22 +08:00
|
|
|
err = tegra_fb_get_tiling(new_plane_state->fb, tiling);
|
2014-11-28 20:14:55 +08:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK &&
|
2014-11-24 23:27:13 +08:00
|
|
|
!dc->soc->supports_block_linear) {
|
|
|
|
DRM_ERROR("hardware doesn't support block linear mode\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2020-06-18 07:40:39 +08:00
|
|
|
/*
|
|
|
|
* Older userspace used custom BO flag in order to specify the Y
|
|
|
|
* reflection, while modern userspace uses the generic DRM rotation
|
|
|
|
* property in order to achieve the same result. The legacy BO flag
|
|
|
|
* duplicates the DRM rotation property when both are set.
|
|
|
|
*/
|
2021-02-19 20:00:22 +08:00
|
|
|
if (tegra_fb_is_bottom_up(new_plane_state->fb))
|
2020-06-18 07:40:39 +08:00
|
|
|
rotation |= DRM_MODE_REFLECT_Y;
|
|
|
|
|
|
|
|
rotation = drm_rotation_simplify(rotation, supported_rotation);
|
|
|
|
|
|
|
|
if (rotation & DRM_MODE_REFLECT_X)
|
|
|
|
plane_state->reflect_x = true;
|
|
|
|
else
|
|
|
|
plane_state->reflect_x = false;
|
2018-03-20 00:20:46 +08:00
|
|
|
|
|
|
|
if (rotation & DRM_MODE_REFLECT_Y)
|
2020-06-18 07:40:38 +08:00
|
|
|
plane_state->reflect_y = true;
|
2018-03-20 00:20:46 +08:00
|
|
|
else
|
2020-06-18 07:40:38 +08:00
|
|
|
plane_state->reflect_y = false;
|
2018-03-20 00:20:46 +08:00
|
|
|
|
2014-11-24 23:27:13 +08:00
|
|
|
/*
|
|
|
|
* Tegra doesn't support different strides for U and V planes so we
|
|
|
|
* error out if the user tries to display a framebuffer with such a
|
|
|
|
* configuration.
|
|
|
|
*/
|
2021-02-19 20:00:22 +08:00
|
|
|
if (new_plane_state->fb->format->num_planes > 2) {
|
|
|
|
if (new_plane_state->fb->pitches[2] != new_plane_state->fb->pitches[1]) {
|
2014-11-24 23:27:13 +08:00
|
|
|
DRM_ERROR("unsupported UV-plane configuration\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-19 20:00:22 +08:00
|
|
|
err = tegra_plane_state_add(tegra, new_plane_state);
|
2014-11-26 19:28:39 +08:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
2014-11-24 23:27:13 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-08-30 23:34:10 +08:00
|
|
|
static void tegra_plane_atomic_disable(struct drm_plane *plane,
|
drm/atomic: Pass the full state to planes atomic disable and update
The current atomic helpers have either their object state being passed as
an argument or the full atomic state.
The former is the pattern that was done at first, before switching to the
latter for new hooks or when it was needed.
Let's convert the remaining helpers to provide a consistent interface,
this time with the planes atomic_update and atomic_disable.
The conversion was done using the coccinelle script below, built tested on
all the drivers.
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
void (*atomic_update)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
void (*atomic_disable)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
)
@@
struct drm_plane_helper_funcs *FUNCS;
identifier f;
identifier crtc_state;
identifier plane, plane_state, state;
expression e;
@@
f(struct drm_crtc_state *crtc_state)
{
...
struct drm_atomic_state *state = e;
<+...
(
- FUNCS->atomic_disable(plane, plane_state)
+ FUNCS->atomic_disable(plane, state)
|
- FUNCS->atomic_update(plane, plane_state)
+ FUNCS->atomic_update(plane, state)
)
...+>
}
@@
identifier plane_atomic_func.func;
identifier plane;
symbol state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *state)
+ struct drm_plane_state *old_plane_state)
{
<...
- state
+ old_plane_state
...>
}
@ ignores_old_state @
identifier plane_atomic_func.func;
identifier plane, old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
... when != old_state
}
@ adds_old_state depends on plane_atomic_func && !ignores_old_state @
identifier plane_atomic_func.func;
identifier plane, plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *plane_state)
{
+ struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane);
...
}
@ depends on plane_atomic_func @
identifier plane_atomic_func.func;
identifier plane, plane_state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *plane_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_old_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_old_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
@@
identifier plane_atomic_func.func;
identifier plane, state;
identifier plane_state;
@@
func(struct drm_plane *plane, struct drm_atomic_state *state) {
...
struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane);
<+...
- plane_state->state
+ state
...+>
}
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-9-maxime@cerno.tech
2021-02-19 20:00:29 +08:00
|
|
|
struct drm_atomic_state *state)
|
2017-06-15 07:18:31 +08:00
|
|
|
{
|
drm/atomic: Pass the full state to planes atomic disable and update
The current atomic helpers have either their object state being passed as
an argument or the full atomic state.
The former is the pattern that was done at first, before switching to the
latter for new hooks or when it was needed.
Let's convert the remaining helpers to provide a consistent interface,
this time with the planes atomic_update and atomic_disable.
The conversion was done using the coccinelle script below, built tested on
all the drivers.
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
void (*atomic_update)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
void (*atomic_disable)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
)
@@
struct drm_plane_helper_funcs *FUNCS;
identifier f;
identifier crtc_state;
identifier plane, plane_state, state;
expression e;
@@
f(struct drm_crtc_state *crtc_state)
{
...
struct drm_atomic_state *state = e;
<+...
(
- FUNCS->atomic_disable(plane, plane_state)
+ FUNCS->atomic_disable(plane, state)
|
- FUNCS->atomic_update(plane, plane_state)
+ FUNCS->atomic_update(plane, state)
)
...+>
}
@@
identifier plane_atomic_func.func;
identifier plane;
symbol state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *state)
+ struct drm_plane_state *old_plane_state)
{
<...
- state
+ old_plane_state
...>
}
@ ignores_old_state @
identifier plane_atomic_func.func;
identifier plane, old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
... when != old_state
}
@ adds_old_state depends on plane_atomic_func && !ignores_old_state @
identifier plane_atomic_func.func;
identifier plane, plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *plane_state)
{
+ struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane);
...
}
@ depends on plane_atomic_func @
identifier plane_atomic_func.func;
identifier plane, plane_state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *plane_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_old_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_old_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
@@
identifier plane_atomic_func.func;
identifier plane, state;
identifier plane_state;
@@
func(struct drm_plane *plane, struct drm_atomic_state *state) {
...
struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane);
<+...
- plane_state->state
+ state
...+>
}
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-9-maxime@cerno.tech
2021-02-19 20:00:29 +08:00
|
|
|
struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
|
|
|
|
plane);
|
2017-08-30 23:34:10 +08:00
|
|
|
struct tegra_plane *p = to_tegra_plane(plane);
|
2017-06-15 07:18:31 +08:00
|
|
|
u32 value;
|
|
|
|
|
2017-08-30 23:34:10 +08:00
|
|
|
/* rien ne va plus */
|
|
|
|
if (!old_state || !old_state->crtc)
|
|
|
|
return;
|
|
|
|
|
2017-12-14 20:37:53 +08:00
|
|
|
value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS);
|
2017-06-15 07:18:31 +08:00
|
|
|
value &= ~WIN_ENABLE;
|
2017-12-14 20:37:53 +08:00
|
|
|
tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
|
2017-06-15 07:18:31 +08:00
|
|
|
}
|
|
|
|
|
2014-11-24 23:27:13 +08:00
|
|
|
static void tegra_plane_atomic_update(struct drm_plane *plane,
|
drm/atomic: Pass the full state to planes atomic disable and update
The current atomic helpers have either their object state being passed as
an argument or the full atomic state.
The former is the pattern that was done at first, before switching to the
latter for new hooks or when it was needed.
Let's convert the remaining helpers to provide a consistent interface,
this time with the planes atomic_update and atomic_disable.
The conversion was done using the coccinelle script below, built tested on
all the drivers.
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
void (*atomic_update)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
void (*atomic_disable)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
)
@@
struct drm_plane_helper_funcs *FUNCS;
identifier f;
identifier crtc_state;
identifier plane, plane_state, state;
expression e;
@@
f(struct drm_crtc_state *crtc_state)
{
...
struct drm_atomic_state *state = e;
<+...
(
- FUNCS->atomic_disable(plane, plane_state)
+ FUNCS->atomic_disable(plane, state)
|
- FUNCS->atomic_update(plane, plane_state)
+ FUNCS->atomic_update(plane, state)
)
...+>
}
@@
identifier plane_atomic_func.func;
identifier plane;
symbol state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *state)
+ struct drm_plane_state *old_plane_state)
{
<...
- state
+ old_plane_state
...>
}
@ ignores_old_state @
identifier plane_atomic_func.func;
identifier plane, old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
... when != old_state
}
@ adds_old_state depends on plane_atomic_func && !ignores_old_state @
identifier plane_atomic_func.func;
identifier plane, plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *plane_state)
{
+ struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane);
...
}
@ depends on plane_atomic_func @
identifier plane_atomic_func.func;
identifier plane, plane_state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *plane_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_old_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_old_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
@@
identifier plane_atomic_func.func;
identifier plane, state;
identifier plane_state;
@@
func(struct drm_plane *plane, struct drm_atomic_state *state) {
...
struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane);
<+...
- plane_state->state
+ state
...+>
}
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-9-maxime@cerno.tech
2021-02-19 20:00:29 +08:00
|
|
|
struct drm_atomic_state *state)
|
2014-11-24 23:27:13 +08:00
|
|
|
{
|
drm: Use state helper instead of the plane state pointer
Many drivers reference the plane->state pointer in order to get the
current plane state in their atomic_update or atomic_disable hooks,
which would be the new plane state in the global atomic state since
_swap_state happened when those hooks are run.
Use the drm_atomic_get_new_plane_state helper to get that state to make it
more obvious.
This was made using the coccinelle script below:
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ adds_new_state @
identifier plane_atomic_func.func;
identifier plane, state;
identifier new_state;
@@
func(struct drm_plane *plane, struct drm_atomic_state *state)
{
...
- struct drm_plane_state *new_state = plane->state;
+ struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
...
}
@ include depends on adds_new_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_new_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://lore.kernel.org/r/20210219120032.260676-1-maxime@cerno.tech
2021-02-19 20:00:30 +08:00
|
|
|
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
|
|
|
|
plane);
|
drm: Rename plane->state variables in atomic update and disable
Some drivers are storing the plane->state pointer in atomic_update and
atomic_disable in a variable simply called state, while the state passed
as an argument is called old_state.
In order to ease subsequent reworks and to avoid confusing or
inconsistent names, let's rename those variables to new_state.
This was done using the following coccinelle script, plus some manual
changes for mtk and tegra.
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
symbol old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_state = plane->state;
...
}
@ depends on moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ new_state
...>
}
@ moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
symbol oldstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *oldstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *newstate = plane->state;
...
}
@ depends on moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ newstate
...>
}
@ moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
symbol old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_pstate = plane->state;
...
}
@ depends on moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
<...
- state
+ new_pstate
...>
}
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-8-maxime@cerno.tech
2021-02-19 20:00:28 +08:00
|
|
|
struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state);
|
drm: Store new plane state in a variable for atomic_update and disable
In order to store the new plane state in a subsequent helper, let's move
the plane->state dereferences into a variable.
This was done using the following coccinelle script, plus some hand
changes for vmwgfx:
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ has_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
symbol old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
+ struct drm_plane_state *new_state = plane->state;
<+...
- plane->state
+ new_state
...+>
}
@ has_new_state_state @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *state)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_state @
identifier plane_atomic_func.func;
identifier plane;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *state)
{
+ struct drm_plane_state *new_plane_state = plane->state;
<+...
- plane->state
+ new_plane_state
...+>
}
@ has_new_state_old_s @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol old_s;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_s)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_old_s @
identifier plane_atomic_func.func;
identifier plane;
symbol old_s;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_s)
{
+ struct drm_plane_state *new_s = plane->state;
<+...
- plane->state
+ new_s
...+>
}
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://lore.kernel.org/r/20210219120032.260676-1-maxime@cerno.tech
2021-02-19 20:00:27 +08:00
|
|
|
struct drm_framebuffer *fb = new_state->fb;
|
2014-10-21 19:51:53 +08:00
|
|
|
struct tegra_plane *p = to_tegra_plane(plane);
|
|
|
|
struct tegra_dc_window window;
|
2014-11-24 23:27:13 +08:00
|
|
|
unsigned int i;
|
2014-10-21 19:51:53 +08:00
|
|
|
|
2014-11-24 23:27:13 +08:00
|
|
|
/* rien ne va plus */
|
drm: Store new plane state in a variable for atomic_update and disable
In order to store the new plane state in a subsequent helper, let's move
the plane->state dereferences into a variable.
This was done using the following coccinelle script, plus some hand
changes for vmwgfx:
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ has_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
symbol old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
+ struct drm_plane_state *new_state = plane->state;
<+...
- plane->state
+ new_state
...+>
}
@ has_new_state_state @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *state)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_state @
identifier plane_atomic_func.func;
identifier plane;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *state)
{
+ struct drm_plane_state *new_plane_state = plane->state;
<+...
- plane->state
+ new_plane_state
...+>
}
@ has_new_state_old_s @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol old_s;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_s)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_old_s @
identifier plane_atomic_func.func;
identifier plane;
symbol old_s;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_s)
{
+ struct drm_plane_state *new_s = plane->state;
<+...
- plane->state
+ new_s
...+>
}
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://lore.kernel.org/r/20210219120032.260676-1-maxime@cerno.tech
2021-02-19 20:00:27 +08:00
|
|
|
if (!new_state->crtc || !new_state->fb)
|
2014-11-24 23:27:13 +08:00
|
|
|
return;
|
|
|
|
|
drm: Store new plane state in a variable for atomic_update and disable
In order to store the new plane state in a subsequent helper, let's move
the plane->state dereferences into a variable.
This was done using the following coccinelle script, plus some hand
changes for vmwgfx:
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ has_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
symbol old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
+ struct drm_plane_state *new_state = plane->state;
<+...
- plane->state
+ new_state
...+>
}
@ has_new_state_state @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *state)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_state @
identifier plane_atomic_func.func;
identifier plane;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *state)
{
+ struct drm_plane_state *new_plane_state = plane->state;
<+...
- plane->state
+ new_plane_state
...+>
}
@ has_new_state_old_s @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol old_s;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_s)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_old_s @
identifier plane_atomic_func.func;
identifier plane;
symbol old_s;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_s)
{
+ struct drm_plane_state *new_s = plane->state;
<+...
- plane->state
+ new_s
...+>
}
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://lore.kernel.org/r/20210219120032.260676-1-maxime@cerno.tech
2021-02-19 20:00:27 +08:00
|
|
|
if (!new_state->visible)
|
drm/atomic: Pass the full state to planes atomic disable and update
The current atomic helpers have either their object state being passed as
an argument or the full atomic state.
The former is the pattern that was done at first, before switching to the
latter for new hooks or when it was needed.
Let's convert the remaining helpers to provide a consistent interface,
this time with the planes atomic_update and atomic_disable.
The conversion was done using the coccinelle script below, built tested on
all the drivers.
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
void (*atomic_update)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
void (*atomic_disable)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
)
@@
struct drm_plane_helper_funcs *FUNCS;
identifier f;
identifier crtc_state;
identifier plane, plane_state, state;
expression e;
@@
f(struct drm_crtc_state *crtc_state)
{
...
struct drm_atomic_state *state = e;
<+...
(
- FUNCS->atomic_disable(plane, plane_state)
+ FUNCS->atomic_disable(plane, state)
|
- FUNCS->atomic_update(plane, plane_state)
+ FUNCS->atomic_update(plane, state)
)
...+>
}
@@
identifier plane_atomic_func.func;
identifier plane;
symbol state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *state)
+ struct drm_plane_state *old_plane_state)
{
<...
- state
+ old_plane_state
...>
}
@ ignores_old_state @
identifier plane_atomic_func.func;
identifier plane, old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
... when != old_state
}
@ adds_old_state depends on plane_atomic_func && !ignores_old_state @
identifier plane_atomic_func.func;
identifier plane, plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *plane_state)
{
+ struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane);
...
}
@ depends on plane_atomic_func @
identifier plane_atomic_func.func;
identifier plane, plane_state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *plane_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_old_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_old_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
@@
identifier plane_atomic_func.func;
identifier plane, state;
identifier plane_state;
@@
func(struct drm_plane *plane, struct drm_atomic_state *state) {
...
struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane);
<+...
- plane_state->state
+ state
...+>
}
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-9-maxime@cerno.tech
2021-02-19 20:00:29 +08:00
|
|
|
return tegra_plane_atomic_disable(plane, state);
|
2017-06-15 07:18:31 +08:00
|
|
|
|
2014-10-21 19:51:53 +08:00
|
|
|
memset(&window, 0, sizeof(window));
|
drm: Store new plane state in a variable for atomic_update and disable
In order to store the new plane state in a subsequent helper, let's move
the plane->state dereferences into a variable.
This was done using the following coccinelle script, plus some hand
changes for vmwgfx:
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ has_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
symbol old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
+ struct drm_plane_state *new_state = plane->state;
<+...
- plane->state
+ new_state
...+>
}
@ has_new_state_state @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *state)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_state @
identifier plane_atomic_func.func;
identifier plane;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *state)
{
+ struct drm_plane_state *new_plane_state = plane->state;
<+...
- plane->state
+ new_plane_state
...+>
}
@ has_new_state_old_s @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol old_s;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_s)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_old_s @
identifier plane_atomic_func.func;
identifier plane;
symbol old_s;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_s)
{
+ struct drm_plane_state *new_s = plane->state;
<+...
- plane->state
+ new_s
...+>
}
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://lore.kernel.org/r/20210219120032.260676-1-maxime@cerno.tech
2021-02-19 20:00:27 +08:00
|
|
|
window.src.x = new_state->src.x1 >> 16;
|
|
|
|
window.src.y = new_state->src.y1 >> 16;
|
|
|
|
window.src.w = drm_rect_width(&new_state->src) >> 16;
|
|
|
|
window.src.h = drm_rect_height(&new_state->src) >> 16;
|
|
|
|
window.dst.x = new_state->dst.x1;
|
|
|
|
window.dst.y = new_state->dst.y1;
|
|
|
|
window.dst.w = drm_rect_width(&new_state->dst);
|
|
|
|
window.dst.h = drm_rect_height(&new_state->dst);
|
2016-12-15 05:32:20 +08:00
|
|
|
window.bits_per_pixel = fb->format->cpp[0] * 8;
|
drm: Rename plane->state variables in atomic update and disable
Some drivers are storing the plane->state pointer in atomic_update and
atomic_disable in a variable simply called state, while the state passed
as an argument is called old_state.
In order to ease subsequent reworks and to avoid confusing or
inconsistent names, let's rename those variables to new_state.
This was done using the following coccinelle script, plus some manual
changes for mtk and tegra.
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
symbol old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_state = plane->state;
...
}
@ depends on moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ new_state
...>
}
@ moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
symbol oldstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *oldstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *newstate = plane->state;
...
}
@ depends on moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ newstate
...>
}
@ moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
symbol old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_pstate = plane->state;
...
}
@ depends on moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
<...
- state
+ new_pstate
...>
}
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-8-maxime@cerno.tech
2021-02-19 20:00:28 +08:00
|
|
|
window.reflect_x = tegra_plane_state->reflect_x;
|
|
|
|
window.reflect_y = tegra_plane_state->reflect_y;
|
2014-10-21 19:51:53 +08:00
|
|
|
|
2014-11-28 20:14:55 +08:00
|
|
|
/* copy from state */
|
drm: Store new plane state in a variable for atomic_update and disable
In order to store the new plane state in a subsequent helper, let's move
the plane->state dereferences into a variable.
This was done using the following coccinelle script, plus some hand
changes for vmwgfx:
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ has_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
symbol old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
+ struct drm_plane_state *new_state = plane->state;
<+...
- plane->state
+ new_state
...+>
}
@ has_new_state_state @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *state)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_state @
identifier plane_atomic_func.func;
identifier plane;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *state)
{
+ struct drm_plane_state *new_plane_state = plane->state;
<+...
- plane->state
+ new_plane_state
...+>
}
@ has_new_state_old_s @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol old_s;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_s)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_old_s @
identifier plane_atomic_func.func;
identifier plane;
symbol old_s;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_s)
{
+ struct drm_plane_state *new_s = plane->state;
<+...
- plane->state
+ new_s
...+>
}
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://lore.kernel.org/r/20210219120032.260676-1-maxime@cerno.tech
2021-02-19 20:00:27 +08:00
|
|
|
window.zpos = new_state->normalized_zpos;
|
drm: Rename plane->state variables in atomic update and disable
Some drivers are storing the plane->state pointer in atomic_update and
atomic_disable in a variable simply called state, while the state passed
as an argument is called old_state.
In order to ease subsequent reworks and to avoid confusing or
inconsistent names, let's rename those variables to new_state.
This was done using the following coccinelle script, plus some manual
changes for mtk and tegra.
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
symbol old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_state = plane->state;
...
}
@ depends on moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ new_state
...>
}
@ moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
symbol oldstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *oldstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *newstate = plane->state;
...
}
@ depends on moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ newstate
...>
}
@ moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
symbol old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_pstate = plane->state;
...
}
@ depends on moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
<...
- state
+ new_pstate
...>
}
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-8-maxime@cerno.tech
2021-02-19 20:00:28 +08:00
|
|
|
window.tiling = tegra_plane_state->tiling;
|
|
|
|
window.format = tegra_plane_state->format;
|
|
|
|
window.swap = tegra_plane_state->swap;
|
2014-10-21 19:51:53 +08:00
|
|
|
|
2016-12-15 05:30:22 +08:00
|
|
|
for (i = 0; i < fb->format->num_planes; i++) {
|
drm: Rename plane->state variables in atomic update and disable
Some drivers are storing the plane->state pointer in atomic_update and
atomic_disable in a variable simply called state, while the state passed
as an argument is called old_state.
In order to ease subsequent reworks and to avoid confusing or
inconsistent names, let's rename those variables to new_state.
This was done using the following coccinelle script, plus some manual
changes for mtk and tegra.
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
symbol old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_state = plane->state;
...
}
@ depends on moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ new_state
...>
}
@ moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
symbol oldstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *oldstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *newstate = plane->state;
...
}
@ depends on moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ newstate
...>
}
@ moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
symbol old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_pstate = plane->state;
...
}
@ depends on moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
<...
- state
+ new_pstate
...>
}
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-8-maxime@cerno.tech
2021-02-19 20:00:28 +08:00
|
|
|
window.base[i] = tegra_plane_state->iova[i] + fb->offsets[i];
|
2016-08-21 16:57:58 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Tegra uses a shared stride for UV planes. Framebuffers are
|
|
|
|
* already checked for this in the tegra_plane_atomic_check()
|
|
|
|
* function, so it's safe to ignore the V-plane pitch here.
|
|
|
|
*/
|
|
|
|
if (i < 2)
|
|
|
|
window.stride[i] = fb->pitches[i];
|
2014-11-24 23:27:13 +08:00
|
|
|
}
|
2014-03-14 16:54:58 +08:00
|
|
|
|
2017-12-14 20:37:53 +08:00
|
|
|
tegra_dc_setup_window(p, &window);
|
2014-03-14 16:54:58 +08:00
|
|
|
}
|
|
|
|
|
2017-08-30 23:34:10 +08:00
|
|
|
static const struct drm_plane_helper_funcs tegra_plane_helper_funcs = {
|
2019-10-28 20:37:17 +08:00
|
|
|
.prepare_fb = tegra_plane_prepare_fb,
|
|
|
|
.cleanup_fb = tegra_plane_cleanup_fb,
|
2014-11-24 23:27:13 +08:00
|
|
|
.atomic_check = tegra_plane_atomic_check,
|
|
|
|
.atomic_disable = tegra_plane_atomic_disable,
|
2017-08-30 23:34:10 +08:00
|
|
|
.atomic_update = tegra_plane_atomic_update,
|
2014-10-21 19:51:53 +08:00
|
|
|
};
|
|
|
|
|
2018-01-08 23:11:00 +08:00
|
|
|
static unsigned long tegra_plane_get_possible_crtcs(struct drm_device *drm)
|
2014-10-21 19:51:53 +08:00
|
|
|
{
|
2014-12-17 01:04:08 +08:00
|
|
|
/*
|
|
|
|
* Ideally this would use drm_crtc_mask(), but that would require the
|
|
|
|
* CRTC to already be in the mode_config's list of CRTCs. However, it
|
|
|
|
* will only be added to that list in the drm_crtc_init_with_planes()
|
|
|
|
* (in tegra_dc_init()), which in turn requires registration of these
|
|
|
|
* planes. So we have ourselves a nice little chicken and egg problem
|
|
|
|
* here.
|
|
|
|
*
|
|
|
|
* We work around this by manually creating the mask from the number
|
|
|
|
* of CRTCs that have been registered, and should therefore always be
|
|
|
|
* the same as drm_crtc_index() after registration.
|
|
|
|
*/
|
2018-01-08 23:11:00 +08:00
|
|
|
return 1 << drm->mode_config.num_crtc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,
|
|
|
|
struct tegra_dc *dc)
|
|
|
|
{
|
|
|
|
unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
|
2017-08-30 23:42:54 +08:00
|
|
|
enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY;
|
2014-10-21 19:51:53 +08:00
|
|
|
struct tegra_plane *plane;
|
|
|
|
unsigned int num_formats;
|
2018-03-15 23:44:04 +08:00
|
|
|
const u64 *modifiers;
|
2014-10-21 19:51:53 +08:00
|
|
|
const u32 *formats;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
plane = kzalloc(sizeof(*plane), GFP_KERNEL);
|
|
|
|
if (!plane)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
2017-12-14 20:37:53 +08:00
|
|
|
/* Always use window A as primary window */
|
|
|
|
plane->offset = 0xa00;
|
2017-11-13 18:08:13 +08:00
|
|
|
plane->index = 0;
|
2017-12-14 20:37:53 +08:00
|
|
|
plane->dc = dc;
|
|
|
|
|
|
|
|
num_formats = dc->soc->num_primary_formats;
|
|
|
|
formats = dc->soc->primary_formats;
|
2018-03-15 23:44:04 +08:00
|
|
|
modifiers = dc->soc->modifiers;
|
2017-11-13 18:08:13 +08:00
|
|
|
|
2014-12-17 01:04:08 +08:00
|
|
|
err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
|
2017-08-31 00:04:12 +08:00
|
|
|
&tegra_plane_funcs, formats,
|
2018-03-15 23:44:04 +08:00
|
|
|
num_formats, modifiers, type, NULL);
|
2014-10-21 19:51:53 +08:00
|
|
|
if (err < 0) {
|
|
|
|
kfree(plane);
|
|
|
|
return ERR_PTR(err);
|
|
|
|
}
|
|
|
|
|
2017-08-30 23:34:10 +08:00
|
|
|
drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
|
2018-05-04 22:39:59 +08:00
|
|
|
drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
|
2017-12-14 20:46:20 +08:00
|
|
|
|
2018-03-20 00:20:46 +08:00
|
|
|
err = drm_plane_create_rotation_property(&plane->base,
|
|
|
|
DRM_MODE_ROTATE_0,
|
|
|
|
DRM_MODE_ROTATE_0 |
|
2020-06-18 07:40:40 +08:00
|
|
|
DRM_MODE_ROTATE_180 |
|
2020-06-18 07:40:39 +08:00
|
|
|
DRM_MODE_REFLECT_X |
|
2018-03-20 00:20:46 +08:00
|
|
|
DRM_MODE_REFLECT_Y);
|
|
|
|
if (err < 0)
|
|
|
|
dev_err(dc->dev, "failed to create rotation property: %d\n",
|
|
|
|
err);
|
|
|
|
|
2014-10-21 19:51:53 +08:00
|
|
|
return &plane->base;
|
|
|
|
}
|
|
|
|
|
2021-03-26 22:51:34 +08:00
|
|
|
static const u32 tegra_legacy_cursor_plane_formats[] = {
|
2014-10-21 19:51:53 +08:00
|
|
|
DRM_FORMAT_RGBA8888,
|
|
|
|
};
|
|
|
|
|
2021-03-26 22:51:34 +08:00
|
|
|
static const u32 tegra_cursor_plane_formats[] = {
|
|
|
|
DRM_FORMAT_ARGB8888,
|
|
|
|
};
|
|
|
|
|
2014-11-24 23:27:13 +08:00
|
|
|
static int tegra_cursor_atomic_check(struct drm_plane *plane,
|
drm/atomic: Pass the full state to planes atomic_check
The current atomic helpers have either their object state being passed as
an argument or the full atomic state.
The former is the pattern that was done at first, before switching to the
latter for new hooks or when it was needed.
Let's convert all the remaining helpers to provide a consistent
interface, starting with the planes atomic_check.
The conversion was done using the coccinelle script below plus some
manual changes for vmwgfx, built tested on all the drivers.
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
int (*atomic_check)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_check = func,
...,
};
@@
struct drm_plane_helper_funcs *FUNCS;
identifier f;
identifier dev;
identifier plane, plane_state, state;
@@
f(struct drm_device *dev, struct drm_atomic_state *state)
{
<+...
- FUNCS->atomic_check(plane, plane_state)
+ FUNCS->atomic_check(plane, state)
...+>
}
@ ignores_new_state @
identifier plane_atomic_func.func;
identifier plane, new_plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *new_plane_state)
{
... when != new_plane_state
}
@ adds_new_state depends on plane_atomic_func && !ignores_new_state @
identifier plane_atomic_func.func;
identifier plane, new_plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *new_plane_state)
{
+ struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
...
}
@ depends on plane_atomic_func @
identifier plane_atomic_func.func;
identifier plane, new_plane_state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *new_plane_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_new_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_new_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-4-maxime@cerno.tech
2021-02-19 20:00:24 +08:00
|
|
|
struct drm_atomic_state *state)
|
2014-10-21 19:51:53 +08:00
|
|
|
{
|
drm/atomic: Pass the full state to planes atomic_check
The current atomic helpers have either their object state being passed as
an argument or the full atomic state.
The former is the pattern that was done at first, before switching to the
latter for new hooks or when it was needed.
Let's convert all the remaining helpers to provide a consistent
interface, starting with the planes atomic_check.
The conversion was done using the coccinelle script below plus some
manual changes for vmwgfx, built tested on all the drivers.
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
int (*atomic_check)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_check = func,
...,
};
@@
struct drm_plane_helper_funcs *FUNCS;
identifier f;
identifier dev;
identifier plane, plane_state, state;
@@
f(struct drm_device *dev, struct drm_atomic_state *state)
{
<+...
- FUNCS->atomic_check(plane, plane_state)
+ FUNCS->atomic_check(plane, state)
...+>
}
@ ignores_new_state @
identifier plane_atomic_func.func;
identifier plane, new_plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *new_plane_state)
{
... when != new_plane_state
}
@ adds_new_state depends on plane_atomic_func && !ignores_new_state @
identifier plane_atomic_func.func;
identifier plane, new_plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *new_plane_state)
{
+ struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
...
}
@ depends on plane_atomic_func @
identifier plane_atomic_func.func;
identifier plane, new_plane_state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *new_plane_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_new_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_new_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-4-maxime@cerno.tech
2021-02-19 20:00:24 +08:00
|
|
|
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
|
|
|
|
plane);
|
2014-11-26 19:28:39 +08:00
|
|
|
struct tegra_plane *tegra = to_tegra_plane(plane);
|
|
|
|
int err;
|
|
|
|
|
2014-11-24 23:27:13 +08:00
|
|
|
/* no need for further checks if the plane is being disabled */
|
2021-02-19 20:00:22 +08:00
|
|
|
if (!new_plane_state->crtc)
|
2014-11-24 23:27:13 +08:00
|
|
|
return 0;
|
2014-10-21 19:51:53 +08:00
|
|
|
|
|
|
|
/* scaling not supported for cursor */
|
2021-02-19 20:00:22 +08:00
|
|
|
if ((new_plane_state->src_w >> 16 != new_plane_state->crtc_w) ||
|
|
|
|
(new_plane_state->src_h >> 16 != new_plane_state->crtc_h))
|
2014-10-21 19:51:53 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* only square cursors supported */
|
2021-02-19 20:00:22 +08:00
|
|
|
if (new_plane_state->src_w != new_plane_state->src_h)
|
2014-11-24 23:27:13 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2021-02-19 20:00:22 +08:00
|
|
|
if (new_plane_state->crtc_w != 32 && new_plane_state->crtc_w != 64 &&
|
|
|
|
new_plane_state->crtc_w != 128 && new_plane_state->crtc_w != 256)
|
2014-10-21 19:51:53 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2021-02-19 20:00:22 +08:00
|
|
|
err = tegra_plane_state_add(tegra, new_plane_state);
|
2014-11-26 19:28:39 +08:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
2014-11-24 23:27:13 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tegra_cursor_atomic_update(struct drm_plane *plane,
|
drm/atomic: Pass the full state to planes atomic disable and update
The current atomic helpers have either their object state being passed as
an argument or the full atomic state.
The former is the pattern that was done at first, before switching to the
latter for new hooks or when it was needed.
Let's convert the remaining helpers to provide a consistent interface,
this time with the planes atomic_update and atomic_disable.
The conversion was done using the coccinelle script below, built tested on
all the drivers.
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
void (*atomic_update)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
void (*atomic_disable)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
)
@@
struct drm_plane_helper_funcs *FUNCS;
identifier f;
identifier crtc_state;
identifier plane, plane_state, state;
expression e;
@@
f(struct drm_crtc_state *crtc_state)
{
...
struct drm_atomic_state *state = e;
<+...
(
- FUNCS->atomic_disable(plane, plane_state)
+ FUNCS->atomic_disable(plane, state)
|
- FUNCS->atomic_update(plane, plane_state)
+ FUNCS->atomic_update(plane, state)
)
...+>
}
@@
identifier plane_atomic_func.func;
identifier plane;
symbol state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *state)
+ struct drm_plane_state *old_plane_state)
{
<...
- state
+ old_plane_state
...>
}
@ ignores_old_state @
identifier plane_atomic_func.func;
identifier plane, old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
... when != old_state
}
@ adds_old_state depends on plane_atomic_func && !ignores_old_state @
identifier plane_atomic_func.func;
identifier plane, plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *plane_state)
{
+ struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane);
...
}
@ depends on plane_atomic_func @
identifier plane_atomic_func.func;
identifier plane, plane_state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *plane_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_old_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_old_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
@@
identifier plane_atomic_func.func;
identifier plane, state;
identifier plane_state;
@@
func(struct drm_plane *plane, struct drm_atomic_state *state) {
...
struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane);
<+...
- plane_state->state
+ state
...+>
}
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-9-maxime@cerno.tech
2021-02-19 20:00:29 +08:00
|
|
|
struct drm_atomic_state *state)
|
2014-11-24 23:27:13 +08:00
|
|
|
{
|
drm: Use state helper instead of the plane state pointer
Many drivers reference the plane->state pointer in order to get the
current plane state in their atomic_update or atomic_disable hooks,
which would be the new plane state in the global atomic state since
_swap_state happened when those hooks are run.
Use the drm_atomic_get_new_plane_state helper to get that state to make it
more obvious.
This was made using the coccinelle script below:
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ adds_new_state @
identifier plane_atomic_func.func;
identifier plane, state;
identifier new_state;
@@
func(struct drm_plane *plane, struct drm_atomic_state *state)
{
...
- struct drm_plane_state *new_state = plane->state;
+ struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
...
}
@ include depends on adds_new_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_new_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://lore.kernel.org/r/20210219120032.260676-1-maxime@cerno.tech
2021-02-19 20:00:30 +08:00
|
|
|
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
|
|
|
|
plane);
|
drm: Rename plane->state variables in atomic update and disable
Some drivers are storing the plane->state pointer in atomic_update and
atomic_disable in a variable simply called state, while the state passed
as an argument is called old_state.
In order to ease subsequent reworks and to avoid confusing or
inconsistent names, let's rename those variables to new_state.
This was done using the following coccinelle script, plus some manual
changes for mtk and tegra.
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
symbol old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_state = plane->state;
...
}
@ depends on moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ new_state
...>
}
@ moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
symbol oldstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *oldstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *newstate = plane->state;
...
}
@ depends on moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ newstate
...>
}
@ moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
symbol old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_pstate = plane->state;
...
}
@ depends on moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
<...
- state
+ new_pstate
...>
}
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-8-maxime@cerno.tech
2021-02-19 20:00:28 +08:00
|
|
|
struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state);
|
drm: Store new plane state in a variable for atomic_update and disable
In order to store the new plane state in a subsequent helper, let's move
the plane->state dereferences into a variable.
This was done using the following coccinelle script, plus some hand
changes for vmwgfx:
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ has_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
symbol old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
+ struct drm_plane_state *new_state = plane->state;
<+...
- plane->state
+ new_state
...+>
}
@ has_new_state_state @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *state)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_state @
identifier plane_atomic_func.func;
identifier plane;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *state)
{
+ struct drm_plane_state *new_plane_state = plane->state;
<+...
- plane->state
+ new_plane_state
...+>
}
@ has_new_state_old_s @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol old_s;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_s)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_old_s @
identifier plane_atomic_func.func;
identifier plane;
symbol old_s;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_s)
{
+ struct drm_plane_state *new_s = plane->state;
<+...
- plane->state
+ new_s
...+>
}
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://lore.kernel.org/r/20210219120032.260676-1-maxime@cerno.tech
2021-02-19 20:00:27 +08:00
|
|
|
struct tegra_dc *dc = to_tegra_dc(new_state->crtc);
|
2021-03-26 22:51:34 +08:00
|
|
|
struct tegra_drm *tegra = plane->dev->dev_private;
|
|
|
|
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
|
|
|
|
u64 dma_mask = *dc->dev->dma_mask;
|
|
|
|
#endif
|
|
|
|
unsigned int x, y;
|
|
|
|
u32 value = 0;
|
2014-11-24 23:27:13 +08:00
|
|
|
|
|
|
|
/* rien ne va plus */
|
drm: Store new plane state in a variable for atomic_update and disable
In order to store the new plane state in a subsequent helper, let's move
the plane->state dereferences into a variable.
This was done using the following coccinelle script, plus some hand
changes for vmwgfx:
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ has_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
symbol old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
+ struct drm_plane_state *new_state = plane->state;
<+...
- plane->state
+ new_state
...+>
}
@ has_new_state_state @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *state)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_state @
identifier plane_atomic_func.func;
identifier plane;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *state)
{
+ struct drm_plane_state *new_plane_state = plane->state;
<+...
- plane->state
+ new_plane_state
...+>
}
@ has_new_state_old_s @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol old_s;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_s)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_old_s @
identifier plane_atomic_func.func;
identifier plane;
symbol old_s;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_s)
{
+ struct drm_plane_state *new_s = plane->state;
<+...
- plane->state
+ new_s
...+>
}
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://lore.kernel.org/r/20210219120032.260676-1-maxime@cerno.tech
2021-02-19 20:00:27 +08:00
|
|
|
if (!new_state->crtc || !new_state->fb)
|
2014-11-24 23:27:13 +08:00
|
|
|
return;
|
|
|
|
|
2021-03-26 22:51:34 +08:00
|
|
|
/*
|
|
|
|
* Legacy display supports hardware clipping of the cursor, but
|
|
|
|
* nvdisplay relies on software to clip the cursor to the screen.
|
|
|
|
*/
|
|
|
|
if (!dc->soc->has_nvdisplay)
|
|
|
|
value |= CURSOR_CLIP_DISPLAY;
|
|
|
|
|
drm: Store new plane state in a variable for atomic_update and disable
In order to store the new plane state in a subsequent helper, let's move
the plane->state dereferences into a variable.
This was done using the following coccinelle script, plus some hand
changes for vmwgfx:
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ has_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
symbol old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
+ struct drm_plane_state *new_state = plane->state;
<+...
- plane->state
+ new_state
...+>
}
@ has_new_state_state @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *state)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_state @
identifier plane_atomic_func.func;
identifier plane;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *state)
{
+ struct drm_plane_state *new_plane_state = plane->state;
<+...
- plane->state
+ new_plane_state
...+>
}
@ has_new_state_old_s @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol old_s;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_s)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_old_s @
identifier plane_atomic_func.func;
identifier plane;
symbol old_s;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_s)
{
+ struct drm_plane_state *new_s = plane->state;
<+...
- plane->state
+ new_s
...+>
}
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://lore.kernel.org/r/20210219120032.260676-1-maxime@cerno.tech
2021-02-19 20:00:27 +08:00
|
|
|
switch (new_state->crtc_w) {
|
2014-10-21 19:51:53 +08:00
|
|
|
case 32:
|
|
|
|
value |= CURSOR_SIZE_32x32;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 64:
|
|
|
|
value |= CURSOR_SIZE_64x64;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 128:
|
|
|
|
value |= CURSOR_SIZE_128x128;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 256:
|
|
|
|
value |= CURSOR_SIZE_256x256;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2019-12-04 00:19:09 +08:00
|
|
|
WARN(1, "cursor size %ux%u not supported\n",
|
drm: Store new plane state in a variable for atomic_update and disable
In order to store the new plane state in a subsequent helper, let's move
the plane->state dereferences into a variable.
This was done using the following coccinelle script, plus some hand
changes for vmwgfx:
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ has_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
symbol old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
+ struct drm_plane_state *new_state = plane->state;
<+...
- plane->state
+ new_state
...+>
}
@ has_new_state_state @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *state)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_state @
identifier plane_atomic_func.func;
identifier plane;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *state)
{
+ struct drm_plane_state *new_plane_state = plane->state;
<+...
- plane->state
+ new_plane_state
...+>
}
@ has_new_state_old_s @
identifier plane_atomic_func.func;
identifier plane;
identifier new_state;
symbol old_s;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_s)
{
...
struct drm_plane_state *new_state = plane->state;
...
}
@ depends on !has_new_state_old_s @
identifier plane_atomic_func.func;
identifier plane;
symbol old_s;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_s)
{
+ struct drm_plane_state *new_s = plane->state;
<+...
- plane->state
+ new_s
...+>
}
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://lore.kernel.org/r/20210219120032.260676-1-maxime@cerno.tech
2021-02-19 20:00:27 +08:00
|
|
|
new_state->crtc_w, new_state->crtc_h);
|
2014-11-24 23:27:13 +08:00
|
|
|
return;
|
2014-10-21 19:51:53 +08:00
|
|
|
}
|
|
|
|
|
drm: Rename plane->state variables in atomic update and disable
Some drivers are storing the plane->state pointer in atomic_update and
atomic_disable in a variable simply called state, while the state passed
as an argument is called old_state.
In order to ease subsequent reworks and to avoid confusing or
inconsistent names, let's rename those variables to new_state.
This was done using the following coccinelle script, plus some manual
changes for mtk and tegra.
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
symbol old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_state = plane->state;
...
}
@ depends on moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ new_state
...>
}
@ moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
symbol oldstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *oldstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *newstate = plane->state;
...
}
@ depends on moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ newstate
...>
}
@ moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
symbol old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_pstate = plane->state;
...
}
@ depends on moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
<...
- state
+ new_pstate
...>
}
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-8-maxime@cerno.tech
2021-02-19 20:00:28 +08:00
|
|
|
value |= (tegra_plane_state->iova[0] >> 10) & 0x3fffff;
|
2014-10-21 19:51:53 +08:00
|
|
|
tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR);
|
|
|
|
|
|
|
|
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
|
2021-03-26 22:51:34 +08:00
|
|
|
value = (tegra_plane_state->iova[0] >> 32) & (dma_mask >> 32);
|
2014-10-21 19:51:53 +08:00
|
|
|
tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* enable cursor and set blend mode */
|
|
|
|
value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
|
|
|
|
value |= CURSOR_ENABLE;
|
|
|
|
tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
|
|
|
|
|
|
|
|
value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL);
|
|
|
|
value &= ~CURSOR_DST_BLEND_MASK;
|
|
|
|
value &= ~CURSOR_SRC_BLEND_MASK;
|
2021-03-26 22:51:34 +08:00
|
|
|
|
|
|
|
if (dc->soc->has_nvdisplay)
|
|
|
|
value &= ~CURSOR_COMPOSITION_MODE_XOR;
|
|
|
|
else
|
|
|
|
value |= CURSOR_MODE_NORMAL;
|
|
|
|
|
2014-10-21 19:51:53 +08:00
|
|
|
value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC;
|
|
|
|
value |= CURSOR_SRC_BLEND_K1_TIMES_SRC;
|
|
|
|
value |= CURSOR_ALPHA;
|
|
|
|
tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);
|
|
|
|
|
2021-03-26 22:51:34 +08:00
|
|
|
/* nvdisplay relies on software for clipping */
|
|
|
|
if (dc->soc->has_nvdisplay) {
|
|
|
|
struct drm_rect src;
|
|
|
|
|
|
|
|
x = new_state->dst.x1;
|
|
|
|
y = new_state->dst.y1;
|
|
|
|
|
|
|
|
drm_rect_fp_to_int(&src, &new_state->src);
|
|
|
|
|
|
|
|
value = (src.y1 & tegra->vmask) << 16 | (src.x1 & tegra->hmask);
|
|
|
|
tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_POINT_IN_CURSOR);
|
|
|
|
|
|
|
|
value = (drm_rect_height(&src) & tegra->vmask) << 16 |
|
|
|
|
(drm_rect_width(&src) & tegra->hmask);
|
|
|
|
tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_SIZE_IN_CURSOR);
|
|
|
|
} else {
|
|
|
|
x = new_state->crtc_x;
|
|
|
|
y = new_state->crtc_y;
|
|
|
|
}
|
|
|
|
|
2014-10-21 19:51:53 +08:00
|
|
|
/* position the cursor */
|
2021-03-26 22:51:34 +08:00
|
|
|
value = ((y & tegra->vmask) << 16) | (x & tegra->hmask);
|
2014-10-21 19:51:53 +08:00
|
|
|
tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
|
|
|
|
}
|
|
|
|
|
2014-11-24 23:27:13 +08:00
|
|
|
static void tegra_cursor_atomic_disable(struct drm_plane *plane,
|
drm/atomic: Pass the full state to planes atomic disable and update
The current atomic helpers have either their object state being passed as
an argument or the full atomic state.
The former is the pattern that was done at first, before switching to the
latter for new hooks or when it was needed.
Let's convert the remaining helpers to provide a consistent interface,
this time with the planes atomic_update and atomic_disable.
The conversion was done using the coccinelle script below, built tested on
all the drivers.
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
void (*atomic_update)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
void (*atomic_disable)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
)
@@
struct drm_plane_helper_funcs *FUNCS;
identifier f;
identifier crtc_state;
identifier plane, plane_state, state;
expression e;
@@
f(struct drm_crtc_state *crtc_state)
{
...
struct drm_atomic_state *state = e;
<+...
(
- FUNCS->atomic_disable(plane, plane_state)
+ FUNCS->atomic_disable(plane, state)
|
- FUNCS->atomic_update(plane, plane_state)
+ FUNCS->atomic_update(plane, state)
)
...+>
}
@@
identifier plane_atomic_func.func;
identifier plane;
symbol state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *state)
+ struct drm_plane_state *old_plane_state)
{
<...
- state
+ old_plane_state
...>
}
@ ignores_old_state @
identifier plane_atomic_func.func;
identifier plane, old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
... when != old_state
}
@ adds_old_state depends on plane_atomic_func && !ignores_old_state @
identifier plane_atomic_func.func;
identifier plane, plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *plane_state)
{
+ struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane);
...
}
@ depends on plane_atomic_func @
identifier plane_atomic_func.func;
identifier plane, plane_state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *plane_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_old_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_old_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
@@
identifier plane_atomic_func.func;
identifier plane, state;
identifier plane_state;
@@
func(struct drm_plane *plane, struct drm_atomic_state *state) {
...
struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane);
<+...
- plane_state->state
+ state
...+>
}
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-9-maxime@cerno.tech
2021-02-19 20:00:29 +08:00
|
|
|
struct drm_atomic_state *state)
|
2014-10-21 19:51:53 +08:00
|
|
|
{
|
drm/atomic: Pass the full state to planes atomic disable and update
The current atomic helpers have either their object state being passed as
an argument or the full atomic state.
The former is the pattern that was done at first, before switching to the
latter for new hooks or when it was needed.
Let's convert the remaining helpers to provide a consistent interface,
this time with the planes atomic_update and atomic_disable.
The conversion was done using the coccinelle script below, built tested on
all the drivers.
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
void (*atomic_update)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
void (*atomic_disable)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
)
@@
struct drm_plane_helper_funcs *FUNCS;
identifier f;
identifier crtc_state;
identifier plane, plane_state, state;
expression e;
@@
f(struct drm_crtc_state *crtc_state)
{
...
struct drm_atomic_state *state = e;
<+...
(
- FUNCS->atomic_disable(plane, plane_state)
+ FUNCS->atomic_disable(plane, state)
|
- FUNCS->atomic_update(plane, plane_state)
+ FUNCS->atomic_update(plane, state)
)
...+>
}
@@
identifier plane_atomic_func.func;
identifier plane;
symbol state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *state)
+ struct drm_plane_state *old_plane_state)
{
<...
- state
+ old_plane_state
...>
}
@ ignores_old_state @
identifier plane_atomic_func.func;
identifier plane, old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
... when != old_state
}
@ adds_old_state depends on plane_atomic_func && !ignores_old_state @
identifier plane_atomic_func.func;
identifier plane, plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *plane_state)
{
+ struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane);
...
}
@ depends on plane_atomic_func @
identifier plane_atomic_func.func;
identifier plane, plane_state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *plane_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_old_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_old_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
@@
identifier plane_atomic_func.func;
identifier plane, state;
identifier plane_state;
@@
func(struct drm_plane *plane, struct drm_atomic_state *state) {
...
struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane);
<+...
- plane_state->state
+ state
...+>
}
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-9-maxime@cerno.tech
2021-02-19 20:00:29 +08:00
|
|
|
struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
|
|
|
|
plane);
|
2014-11-24 23:27:13 +08:00
|
|
|
struct tegra_dc *dc;
|
2014-10-21 19:51:53 +08:00
|
|
|
u32 value;
|
|
|
|
|
2014-11-24 23:27:13 +08:00
|
|
|
/* rien ne va plus */
|
|
|
|
if (!old_state || !old_state->crtc)
|
|
|
|
return;
|
|
|
|
|
|
|
|
dc = to_tegra_dc(old_state->crtc);
|
2014-10-21 19:51:53 +08:00
|
|
|
|
|
|
|
value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
|
|
|
|
value &= ~CURSOR_ENABLE;
|
|
|
|
tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
|
|
|
|
}
|
|
|
|
|
2014-11-24 23:27:13 +08:00
|
|
|
static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = {
|
2019-10-28 20:37:17 +08:00
|
|
|
.prepare_fb = tegra_plane_prepare_fb,
|
|
|
|
.cleanup_fb = tegra_plane_cleanup_fb,
|
2014-11-24 23:27:13 +08:00
|
|
|
.atomic_check = tegra_cursor_atomic_check,
|
|
|
|
.atomic_update = tegra_cursor_atomic_update,
|
|
|
|
.atomic_disable = tegra_cursor_atomic_disable,
|
2014-10-21 19:51:53 +08:00
|
|
|
};
|
|
|
|
|
2021-04-13 17:49:01 +08:00
|
|
|
static const uint64_t linear_modifiers[] = {
|
|
|
|
DRM_FORMAT_MOD_LINEAR,
|
|
|
|
DRM_FORMAT_MOD_INVALID
|
|
|
|
};
|
|
|
|
|
2014-10-21 19:51:53 +08:00
|
|
|
static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
|
|
|
|
struct tegra_dc *dc)
|
|
|
|
{
|
2018-01-08 23:11:00 +08:00
|
|
|
unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
|
2014-10-21 19:51:53 +08:00
|
|
|
struct tegra_plane *plane;
|
|
|
|
unsigned int num_formats;
|
|
|
|
const u32 *formats;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
plane = kzalloc(sizeof(*plane), GFP_KERNEL);
|
|
|
|
if (!plane)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
2014-11-26 19:28:39 +08:00
|
|
|
/*
|
2015-07-21 22:42:30 +08:00
|
|
|
* This index is kind of fake. The cursor isn't a regular plane, but
|
|
|
|
* its update and activation request bits in DC_CMD_STATE_CONTROL do
|
|
|
|
* use the same programming. Setting this fake index here allows the
|
|
|
|
* code in tegra_add_plane_state() to do the right thing without the
|
|
|
|
* need to special-casing the cursor plane.
|
2014-11-26 19:28:39 +08:00
|
|
|
*/
|
|
|
|
plane->index = 6;
|
2017-12-14 20:37:53 +08:00
|
|
|
plane->dc = dc;
|
2014-11-26 19:28:39 +08:00
|
|
|
|
2021-03-26 22:51:34 +08:00
|
|
|
if (!dc->soc->has_nvdisplay) {
|
|
|
|
num_formats = ARRAY_SIZE(tegra_legacy_cursor_plane_formats);
|
|
|
|
formats = tegra_legacy_cursor_plane_formats;
|
|
|
|
} else {
|
|
|
|
num_formats = ARRAY_SIZE(tegra_cursor_plane_formats);
|
|
|
|
formats = tegra_cursor_plane_formats;
|
|
|
|
}
|
2014-10-21 19:51:53 +08:00
|
|
|
|
2018-01-08 23:11:00 +08:00
|
|
|
err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
|
2017-08-31 00:04:12 +08:00
|
|
|
&tegra_plane_funcs, formats,
|
2021-04-13 17:49:01 +08:00
|
|
|
num_formats, linear_modifiers,
|
2017-07-24 11:46:38 +08:00
|
|
|
DRM_PLANE_TYPE_CURSOR, NULL);
|
2014-10-21 19:51:53 +08:00
|
|
|
if (err < 0) {
|
|
|
|
kfree(plane);
|
|
|
|
return ERR_PTR(err);
|
|
|
|
}
|
|
|
|
|
2014-11-24 23:27:13 +08:00
|
|
|
drm_plane_helper_add(&plane->base, &tegra_cursor_plane_helper_funcs);
|
2020-06-10 19:30:59 +08:00
|
|
|
drm_plane_create_zpos_immutable_property(&plane->base, 255);
|
2012-11-05 04:47:13 +08:00
|
|
|
|
2014-11-24 23:27:13 +08:00
|
|
|
return &plane->base;
|
2012-11-05 04:47:13 +08:00
|
|
|
}
|
|
|
|
|
2017-11-14 23:07:40 +08:00
|
|
|
static const u32 tegra20_overlay_formats[] = {
|
|
|
|
DRM_FORMAT_ARGB4444,
|
|
|
|
DRM_FORMAT_ARGB1555,
|
|
|
|
DRM_FORMAT_RGB565,
|
|
|
|
DRM_FORMAT_RGBA5551,
|
|
|
|
DRM_FORMAT_ABGR8888,
|
|
|
|
DRM_FORMAT_ARGB8888,
|
2017-12-20 16:39:14 +08:00
|
|
|
/* non-native formats */
|
|
|
|
DRM_FORMAT_XRGB1555,
|
|
|
|
DRM_FORMAT_RGBX5551,
|
|
|
|
DRM_FORMAT_XBGR8888,
|
|
|
|
DRM_FORMAT_XRGB8888,
|
2017-11-14 23:07:40 +08:00
|
|
|
/* planar formats */
|
|
|
|
DRM_FORMAT_UYVY,
|
|
|
|
DRM_FORMAT_YUYV,
|
|
|
|
DRM_FORMAT_YUV420,
|
|
|
|
DRM_FORMAT_YUV422,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const u32 tegra114_overlay_formats[] = {
|
|
|
|
DRM_FORMAT_ARGB4444,
|
|
|
|
DRM_FORMAT_ARGB1555,
|
|
|
|
DRM_FORMAT_RGB565,
|
|
|
|
DRM_FORMAT_RGBA5551,
|
2017-10-12 23:30:55 +08:00
|
|
|
DRM_FORMAT_ABGR8888,
|
|
|
|
DRM_FORMAT_ARGB8888,
|
2017-11-14 23:07:40 +08:00
|
|
|
/* 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_BGRA8888,
|
|
|
|
DRM_FORMAT_RGBA8888,
|
|
|
|
DRM_FORMAT_XRGB8888,
|
|
|
|
DRM_FORMAT_XBGR8888,
|
|
|
|
/* planar formats */
|
|
|
|
DRM_FORMAT_UYVY,
|
|
|
|
DRM_FORMAT_YUYV,
|
|
|
|
DRM_FORMAT_YUV420,
|
|
|
|
DRM_FORMAT_YUV422,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const u32 tegra124_overlay_formats[] = {
|
|
|
|
DRM_FORMAT_ARGB4444,
|
|
|
|
DRM_FORMAT_ARGB1555,
|
2013-03-22 22:37:30 +08:00
|
|
|
DRM_FORMAT_RGB565,
|
2017-11-14 23:07:40 +08:00
|
|
|
DRM_FORMAT_RGBA5551,
|
|
|
|
DRM_FORMAT_ABGR8888,
|
|
|
|
DRM_FORMAT_ARGB8888,
|
|
|
|
/* 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_BGRA8888,
|
|
|
|
DRM_FORMAT_RGBA8888,
|
|
|
|
DRM_FORMAT_XRGB8888,
|
|
|
|
DRM_FORMAT_XBGR8888,
|
|
|
|
/* new on Tegra124 */
|
|
|
|
DRM_FORMAT_RGBX8888,
|
|
|
|
DRM_FORMAT_BGRX8888,
|
|
|
|
/* planar formats */
|
2012-11-05 04:47:13 +08:00
|
|
|
DRM_FORMAT_UYVY,
|
2014-01-30 03:31:17 +08:00
|
|
|
DRM_FORMAT_YUYV,
|
2012-11-05 04:47:13 +08:00
|
|
|
DRM_FORMAT_YUV420,
|
|
|
|
DRM_FORMAT_YUV422,
|
|
|
|
};
|
|
|
|
|
2014-10-21 19:51:53 +08:00
|
|
|
static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
|
|
|
|
struct tegra_dc *dc,
|
2018-03-15 09:00:25 +08:00
|
|
|
unsigned int index,
|
|
|
|
bool cursor)
|
2012-11-05 04:47:13 +08:00
|
|
|
{
|
2018-01-08 23:11:00 +08:00
|
|
|
unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
|
2014-10-21 19:51:53 +08:00
|
|
|
struct tegra_plane *plane;
|
|
|
|
unsigned int num_formats;
|
2018-03-15 09:00:25 +08:00
|
|
|
enum drm_plane_type type;
|
2014-10-21 19:51:53 +08:00
|
|
|
const u32 *formats;
|
|
|
|
int err;
|
2012-11-05 04:47:13 +08:00
|
|
|
|
2014-10-21 19:51:53 +08:00
|
|
|
plane = kzalloc(sizeof(*plane), GFP_KERNEL);
|
|
|
|
if (!plane)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
2012-11-05 04:47:13 +08:00
|
|
|
|
2017-12-14 20:37:53 +08:00
|
|
|
plane->offset = 0xa00 + 0x200 * index;
|
2014-10-21 19:51:53 +08:00
|
|
|
plane->index = index;
|
2017-12-14 20:37:53 +08:00
|
|
|
plane->dc = dc;
|
2012-11-05 04:47:13 +08:00
|
|
|
|
2017-11-14 23:07:40 +08:00
|
|
|
num_formats = dc->soc->num_overlay_formats;
|
|
|
|
formats = dc->soc->overlay_formats;
|
2012-11-05 04:47:13 +08:00
|
|
|
|
2018-03-15 09:00:25 +08:00
|
|
|
if (!cursor)
|
|
|
|
type = DRM_PLANE_TYPE_OVERLAY;
|
|
|
|
else
|
|
|
|
type = DRM_PLANE_TYPE_CURSOR;
|
|
|
|
|
2018-01-08 23:11:00 +08:00
|
|
|
err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
|
2017-08-31 00:04:12 +08:00
|
|
|
&tegra_plane_funcs, formats,
|
2021-04-13 17:49:01 +08:00
|
|
|
num_formats, linear_modifiers,
|
|
|
|
type, NULL);
|
2014-10-21 19:51:53 +08:00
|
|
|
if (err < 0) {
|
|
|
|
kfree(plane);
|
|
|
|
return ERR_PTR(err);
|
|
|
|
}
|
|
|
|
|
2017-08-30 23:34:10 +08:00
|
|
|
drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
|
2018-05-04 22:39:59 +08:00
|
|
|
drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
|
2017-12-14 20:46:20 +08:00
|
|
|
|
2018-03-20 00:20:46 +08:00
|
|
|
err = drm_plane_create_rotation_property(&plane->base,
|
|
|
|
DRM_MODE_ROTATE_0,
|
|
|
|
DRM_MODE_ROTATE_0 |
|
2020-06-18 07:40:40 +08:00
|
|
|
DRM_MODE_ROTATE_180 |
|
2020-06-18 07:40:39 +08:00
|
|
|
DRM_MODE_REFLECT_X |
|
2018-03-20 00:20:46 +08:00
|
|
|
DRM_MODE_REFLECT_Y);
|
|
|
|
if (err < 0)
|
|
|
|
dev_err(dc->dev, "failed to create rotation property: %d\n",
|
|
|
|
err);
|
|
|
|
|
2014-10-21 19:51:53 +08:00
|
|
|
return &plane->base;
|
|
|
|
}
|
|
|
|
|
2017-08-30 23:42:54 +08:00
|
|
|
static struct drm_plane *tegra_dc_add_shared_planes(struct drm_device *drm,
|
|
|
|
struct tegra_dc *dc)
|
|
|
|
{
|
|
|
|
struct drm_plane *plane, *primary = NULL;
|
|
|
|
unsigned int i, j;
|
|
|
|
|
|
|
|
for (i = 0; i < dc->soc->num_wgrps; i++) {
|
|
|
|
const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i];
|
|
|
|
|
|
|
|
if (wgrp->dc == dc->pipe) {
|
|
|
|
for (j = 0; j < wgrp->num_windows; j++) {
|
|
|
|
unsigned int index = wgrp->windows[j];
|
|
|
|
|
|
|
|
plane = tegra_shared_plane_create(drm, dc,
|
|
|
|
wgrp->index,
|
|
|
|
index);
|
|
|
|
if (IS_ERR(plane))
|
|
|
|
return plane;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Choose the first shared plane owned by this
|
|
|
|
* head as the primary plane.
|
|
|
|
*/
|
|
|
|
if (!primary) {
|
|
|
|
plane->type = DRM_PLANE_TYPE_PRIMARY;
|
|
|
|
primary = plane;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return primary;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct drm_plane *tegra_dc_add_planes(struct drm_device *drm,
|
|
|
|
struct tegra_dc *dc)
|
2014-10-21 19:51:53 +08:00
|
|
|
{
|
2018-01-08 23:16:06 +08:00
|
|
|
struct drm_plane *planes[2], *primary;
|
2018-03-15 09:00:25 +08:00
|
|
|
unsigned int planes_num;
|
2014-10-21 19:51:53 +08:00
|
|
|
unsigned int i;
|
2018-01-08 23:16:06 +08:00
|
|
|
int err;
|
2014-10-21 19:51:53 +08:00
|
|
|
|
2017-08-30 23:42:54 +08:00
|
|
|
primary = tegra_primary_plane_create(drm, dc);
|
|
|
|
if (IS_ERR(primary))
|
|
|
|
return primary;
|
|
|
|
|
2018-03-15 09:00:25 +08:00
|
|
|
if (dc->soc->supports_cursor)
|
|
|
|
planes_num = 2;
|
|
|
|
else
|
|
|
|
planes_num = 1;
|
|
|
|
|
|
|
|
for (i = 0; i < planes_num; i++) {
|
|
|
|
planes[i] = tegra_dc_overlay_plane_create(drm, dc, 1 + i,
|
|
|
|
false);
|
2018-01-08 23:16:06 +08:00
|
|
|
if (IS_ERR(planes[i])) {
|
|
|
|
err = PTR_ERR(planes[i]);
|
|
|
|
|
|
|
|
while (i--)
|
|
|
|
tegra_plane_funcs.destroy(planes[i]);
|
|
|
|
|
|
|
|
tegra_plane_funcs.destroy(primary);
|
|
|
|
return ERR_PTR(err);
|
2017-08-30 23:42:54 +08:00
|
|
|
}
|
2012-11-05 04:47:13 +08:00
|
|
|
}
|
|
|
|
|
2017-08-30 23:42:54 +08:00
|
|
|
return primary;
|
2012-11-05 04:47:13 +08:00
|
|
|
}
|
|
|
|
|
2013-10-14 20:06:02 +08:00
|
|
|
static void tegra_dc_destroy(struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
drm_crtc_cleanup(crtc);
|
|
|
|
}
|
|
|
|
|
2014-12-08 23:14:45 +08:00
|
|
|
static void tegra_crtc_reset(struct drm_crtc *crtc)
|
|
|
|
{
|
2019-03-01 20:56:24 +08:00
|
|
|
struct tegra_dc_state *state = kzalloc(sizeof(*state), GFP_KERNEL);
|
2014-12-08 23:14:45 +08:00
|
|
|
|
2015-01-28 22:01:22 +08:00
|
|
|
if (crtc->state)
|
2019-03-01 20:56:24 +08:00
|
|
|
tegra_crtc_atomic_destroy_state(crtc, crtc->state);
|
2015-07-02 23:04:06 +08:00
|
|
|
|
2019-03-01 20:56:24 +08:00
|
|
|
__drm_atomic_helper_crtc_reset(crtc, &state->base);
|
2014-12-08 23:14:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct drm_crtc_state *
|
|
|
|
tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
struct tegra_dc_state *state = to_dc_state(crtc->state);
|
|
|
|
struct tegra_dc_state *copy;
|
|
|
|
|
2015-01-28 22:01:22 +08:00
|
|
|
copy = kmalloc(sizeof(*copy), GFP_KERNEL);
|
2014-12-08 23:14:45 +08:00
|
|
|
if (!copy)
|
|
|
|
return NULL;
|
|
|
|
|
2015-01-28 22:01:22 +08:00
|
|
|
__drm_atomic_helper_crtc_duplicate_state(crtc, ©->base);
|
|
|
|
copy->clk = state->clk;
|
|
|
|
copy->pclk = state->pclk;
|
|
|
|
copy->div = state->div;
|
|
|
|
copy->planes = state->planes;
|
2014-12-08 23:14:45 +08:00
|
|
|
|
|
|
|
return ©->base;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
|
|
|
|
struct drm_crtc_state *state)
|
|
|
|
{
|
2016-05-09 22:34:09 +08:00
|
|
|
__drm_atomic_helper_crtc_destroy_state(state);
|
2014-12-08 23:14:45 +08:00
|
|
|
kfree(state);
|
|
|
|
}
|
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name }
|
2017-11-08 20:32:05 +08:00
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
static const struct debugfs_reg32 tegra_dc_regs[] = {
|
|
|
|
DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT),
|
|
|
|
DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL),
|
|
|
|
DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_ERROR),
|
|
|
|
DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT),
|
|
|
|
DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL),
|
|
|
|
DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_ERROR),
|
|
|
|
DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT),
|
|
|
|
DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL),
|
|
|
|
DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_ERROR),
|
|
|
|
DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT),
|
|
|
|
DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL),
|
|
|
|
DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_ERROR),
|
|
|
|
DEBUGFS_REG32(DC_CMD_CONT_SYNCPT_VSYNC),
|
|
|
|
DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND_OPTION0),
|
|
|
|
DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND),
|
|
|
|
DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE),
|
|
|
|
DEBUGFS_REG32(DC_CMD_DISPLAY_POWER_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_CMD_INT_STATUS),
|
|
|
|
DEBUGFS_REG32(DC_CMD_INT_MASK),
|
|
|
|
DEBUGFS_REG32(DC_CMD_INT_ENABLE),
|
|
|
|
DEBUGFS_REG32(DC_CMD_INT_TYPE),
|
|
|
|
DEBUGFS_REG32(DC_CMD_INT_POLARITY),
|
|
|
|
DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE1),
|
|
|
|
DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE2),
|
|
|
|
DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE3),
|
|
|
|
DEBUGFS_REG32(DC_CMD_STATE_ACCESS),
|
|
|
|
DEBUGFS_REG32(DC_CMD_STATE_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_CMD_DISPLAY_WINDOW_HEADER),
|
|
|
|
DEBUGFS_REG32(DC_CMD_REG_ACT_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_COM_CRC_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_COM_CRC_CHECKSUM),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(0)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(1)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(2)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(3)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(0)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(1)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(2)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(3)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(0)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(1)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(2)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(3)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(0)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(1)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(2)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(3)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(0)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(1)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(0)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(1)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(2)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(3)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(4)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(5)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(6)),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_MISC_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_PM0_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_PM0_DUTY_CYCLE),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_PM1_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_COM_PIN_PM1_DUTY_CYCLE),
|
|
|
|
DEBUGFS_REG32(DC_COM_SPI_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_COM_SPI_START_BYTE),
|
|
|
|
DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_AB),
|
|
|
|
DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_CD),
|
|
|
|
DEBUGFS_REG32(DC_COM_HSPI_CS_DC),
|
|
|
|
DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_A),
|
|
|
|
DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_B),
|
|
|
|
DEBUGFS_REG32(DC_COM_GPIO_CTRL),
|
|
|
|
DEBUGFS_REG32(DC_COM_GPIO_DEBOUNCE_COUNTER),
|
|
|
|
DEBUGFS_REG32(DC_COM_CRC_CHECKSUM_LATCHED),
|
|
|
|
DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS0),
|
|
|
|
DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS1),
|
|
|
|
DEBUGFS_REG32(DC_DISP_DISP_WIN_OPTIONS),
|
|
|
|
DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY),
|
|
|
|
DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER),
|
|
|
|
DEBUGFS_REG32(DC_DISP_DISP_TIMING_OPTIONS),
|
|
|
|
DEBUGFS_REG32(DC_DISP_REF_TO_SYNC),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SYNC_WIDTH),
|
|
|
|
DEBUGFS_REG32(DC_DISP_BACK_PORCH),
|
|
|
|
DEBUGFS_REG32(DC_DISP_ACTIVE),
|
|
|
|
DEBUGFS_REG32(DC_DISP_FRONT_PORCH),
|
|
|
|
DEBUGFS_REG32(DC_DISP_H_PULSE0_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_A),
|
|
|
|
DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_B),
|
|
|
|
DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_C),
|
|
|
|
DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_D),
|
|
|
|
DEBUGFS_REG32(DC_DISP_H_PULSE1_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_A),
|
|
|
|
DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_B),
|
|
|
|
DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_C),
|
|
|
|
DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_D),
|
|
|
|
DEBUGFS_REG32(DC_DISP_H_PULSE2_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_A),
|
|
|
|
DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_B),
|
|
|
|
DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_C),
|
|
|
|
DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_D),
|
|
|
|
DEBUGFS_REG32(DC_DISP_V_PULSE0_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_A),
|
|
|
|
DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_B),
|
|
|
|
DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_C),
|
|
|
|
DEBUGFS_REG32(DC_DISP_V_PULSE1_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_A),
|
|
|
|
DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_B),
|
|
|
|
DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_C),
|
|
|
|
DEBUGFS_REG32(DC_DISP_V_PULSE2_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_V_PULSE2_POSITION_A),
|
|
|
|
DEBUGFS_REG32(DC_DISP_V_PULSE3_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_V_PULSE3_POSITION_A),
|
|
|
|
DEBUGFS_REG32(DC_DISP_M0_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_M1_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_DI_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_PP_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_PP_SELECT_A),
|
|
|
|
DEBUGFS_REG32(DC_DISP_PP_SELECT_B),
|
|
|
|
DEBUGFS_REG32(DC_DISP_PP_SELECT_C),
|
|
|
|
DEBUGFS_REG32(DC_DISP_PP_SELECT_D),
|
|
|
|
DEBUGFS_REG32(DC_DISP_DISP_CLOCK_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_DISP_INTERFACE_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_DISP_COLOR_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SHIFT_CLOCK_OPTIONS),
|
|
|
|
DEBUGFS_REG32(DC_DISP_DATA_ENABLE_OPTIONS),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SERIAL_INTERFACE_OPTIONS),
|
|
|
|
DEBUGFS_REG32(DC_DISP_LCD_SPI_OPTIONS),
|
|
|
|
DEBUGFS_REG32(DC_DISP_BORDER_COLOR),
|
|
|
|
DEBUGFS_REG32(DC_DISP_COLOR_KEY0_LOWER),
|
|
|
|
DEBUGFS_REG32(DC_DISP_COLOR_KEY0_UPPER),
|
|
|
|
DEBUGFS_REG32(DC_DISP_COLOR_KEY1_LOWER),
|
|
|
|
DEBUGFS_REG32(DC_DISP_COLOR_KEY1_UPPER),
|
|
|
|
DEBUGFS_REG32(DC_DISP_CURSOR_FOREGROUND),
|
|
|
|
DEBUGFS_REG32(DC_DISP_CURSOR_BACKGROUND),
|
|
|
|
DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR),
|
|
|
|
DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_NS),
|
|
|
|
DEBUGFS_REG32(DC_DISP_CURSOR_POSITION),
|
|
|
|
DEBUGFS_REG32(DC_DISP_CURSOR_POSITION_NS),
|
|
|
|
DEBUGFS_REG32(DC_DISP_INIT_SEQ_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_A),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_B),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_C),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_D),
|
|
|
|
DEBUGFS_REG32(DC_DISP_DC_MCCIF_FIFOCTRL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0A_HYST),
|
|
|
|
DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0B_HYST),
|
|
|
|
DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1A_HYST),
|
|
|
|
DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1B_HYST),
|
|
|
|
DEBUGFS_REG32(DC_DISP_DAC_CRT_CTRL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_DISP_MISC_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_CSC_COEFF),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_LUT(0)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_LUT(1)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_LUT(2)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_LUT(3)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_LUT(4)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_LUT(5)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_LUT(6)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_LUT(7)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_LUT(8)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_FLICKER_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_DC_PIXEL_COUNT),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(0)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(1)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(2)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(3)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(4)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(5)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(6)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(7)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_BL_TF(0)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_BL_TF(1)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_BL_TF(2)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_BL_TF(3)),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_BL_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_HW_K_VALUES),
|
|
|
|
DEBUGFS_REG32(DC_DISP_SD_MAN_K_VALUES),
|
|
|
|
DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_HI),
|
|
|
|
DEBUGFS_REG32(DC_DISP_BLEND_CURSOR_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_WIN_WIN_OPTIONS),
|
|
|
|
DEBUGFS_REG32(DC_WIN_BYTE_SWAP),
|
|
|
|
DEBUGFS_REG32(DC_WIN_BUFFER_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_WIN_COLOR_DEPTH),
|
|
|
|
DEBUGFS_REG32(DC_WIN_POSITION),
|
|
|
|
DEBUGFS_REG32(DC_WIN_SIZE),
|
|
|
|
DEBUGFS_REG32(DC_WIN_PRESCALED_SIZE),
|
|
|
|
DEBUGFS_REG32(DC_WIN_H_INITIAL_DDA),
|
|
|
|
DEBUGFS_REG32(DC_WIN_V_INITIAL_DDA),
|
|
|
|
DEBUGFS_REG32(DC_WIN_DDA_INC),
|
|
|
|
DEBUGFS_REG32(DC_WIN_LINE_STRIDE),
|
|
|
|
DEBUGFS_REG32(DC_WIN_BUF_STRIDE),
|
|
|
|
DEBUGFS_REG32(DC_WIN_UV_BUF_STRIDE),
|
|
|
|
DEBUGFS_REG32(DC_WIN_BUFFER_ADDR_MODE),
|
|
|
|
DEBUGFS_REG32(DC_WIN_DV_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_WIN_BLEND_NOKEY),
|
|
|
|
DEBUGFS_REG32(DC_WIN_BLEND_1WIN),
|
|
|
|
DEBUGFS_REG32(DC_WIN_BLEND_2WIN_X),
|
|
|
|
DEBUGFS_REG32(DC_WIN_BLEND_2WIN_Y),
|
|
|
|
DEBUGFS_REG32(DC_WIN_BLEND_3WIN_XY),
|
|
|
|
DEBUGFS_REG32(DC_WIN_HP_FETCH_CONTROL),
|
|
|
|
DEBUGFS_REG32(DC_WINBUF_START_ADDR),
|
|
|
|
DEBUGFS_REG32(DC_WINBUF_START_ADDR_NS),
|
|
|
|
DEBUGFS_REG32(DC_WINBUF_START_ADDR_U),
|
|
|
|
DEBUGFS_REG32(DC_WINBUF_START_ADDR_U_NS),
|
|
|
|
DEBUGFS_REG32(DC_WINBUF_START_ADDR_V),
|
|
|
|
DEBUGFS_REG32(DC_WINBUF_START_ADDR_V_NS),
|
|
|
|
DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET),
|
|
|
|
DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET_NS),
|
|
|
|
DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET),
|
|
|
|
DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET_NS),
|
|
|
|
DEBUGFS_REG32(DC_WINBUF_UFLOW_STATUS),
|
|
|
|
DEBUGFS_REG32(DC_WINBUF_AD_UFLOW_STATUS),
|
|
|
|
DEBUGFS_REG32(DC_WINBUF_BD_UFLOW_STATUS),
|
|
|
|
DEBUGFS_REG32(DC_WINBUF_CD_UFLOW_STATUS),
|
|
|
|
};
|
2017-11-08 20:32:05 +08:00
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
static int tegra_dc_show_regs(struct seq_file *s, void *data)
|
2017-11-08 20:32:05 +08:00
|
|
|
{
|
2017-11-08 20:40:54 +08:00
|
|
|
struct drm_info_node *node = s->private;
|
|
|
|
struct tegra_dc *dc = node->info_ent->data;
|
|
|
|
unsigned int i;
|
|
|
|
int err = 0;
|
2017-11-08 20:32:05 +08:00
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
drm_modeset_lock(&dc->base.mutex, NULL);
|
2017-11-08 20:32:05 +08:00
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
if (!dc->base.state->active) {
|
|
|
|
err = -EBUSY;
|
|
|
|
goto unlock;
|
|
|
|
}
|
2017-11-08 20:32:05 +08:00
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
for (i = 0; i < ARRAY_SIZE(tegra_dc_regs); i++) {
|
|
|
|
unsigned int offset = tegra_dc_regs[i].offset;
|
2017-11-08 20:32:05 +08:00
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
seq_printf(s, "%-40s %#05x %08x\n", tegra_dc_regs[i].name,
|
|
|
|
offset, tegra_dc_readl(dc, offset));
|
|
|
|
}
|
|
|
|
|
|
|
|
unlock:
|
|
|
|
drm_modeset_unlock(&dc->base.mutex);
|
|
|
|
return err;
|
2017-11-08 20:32:05 +08:00
|
|
|
}
|
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
static int tegra_dc_show_crc(struct seq_file *s, void *data)
|
2017-11-08 20:32:05 +08:00
|
|
|
{
|
2017-11-08 20:40:54 +08:00
|
|
|
struct drm_info_node *node = s->private;
|
|
|
|
struct tegra_dc *dc = node->info_ent->data;
|
|
|
|
int err = 0;
|
|
|
|
u32 value;
|
|
|
|
|
|
|
|
drm_modeset_lock(&dc->base.mutex, NULL);
|
|
|
|
|
|
|
|
if (!dc->base.state->active) {
|
|
|
|
err = -EBUSY;
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
value = DC_COM_CRC_CONTROL_ACTIVE_DATA | DC_COM_CRC_CONTROL_ENABLE;
|
|
|
|
tegra_dc_writel(dc, value, DC_COM_CRC_CONTROL);
|
|
|
|
tegra_dc_commit(dc);
|
|
|
|
|
|
|
|
drm_crtc_wait_one_vblank(&dc->base);
|
|
|
|
drm_crtc_wait_one_vblank(&dc->base);
|
|
|
|
|
|
|
|
value = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM);
|
|
|
|
seq_printf(s, "%08x\n", value);
|
|
|
|
|
|
|
|
tegra_dc_writel(dc, 0, DC_COM_CRC_CONTROL);
|
|
|
|
|
|
|
|
unlock:
|
|
|
|
drm_modeset_unlock(&dc->base.mutex);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tegra_dc_show_stats(struct seq_file *s, void *data)
|
|
|
|
{
|
|
|
|
struct drm_info_node *node = s->private;
|
|
|
|
struct tegra_dc *dc = node->info_ent->data;
|
|
|
|
|
|
|
|
seq_printf(s, "frames: %lu\n", dc->stats.frames);
|
|
|
|
seq_printf(s, "vblank: %lu\n", dc->stats.vblank);
|
|
|
|
seq_printf(s, "underflow: %lu\n", dc->stats.underflow);
|
|
|
|
seq_printf(s, "overflow: %lu\n", dc->stats.overflow);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct drm_info_list debugfs_files[] = {
|
|
|
|
{ "regs", tegra_dc_show_regs, 0, NULL },
|
|
|
|
{ "crc", tegra_dc_show_crc, 0, NULL },
|
|
|
|
{ "stats", tegra_dc_show_stats, 0, NULL },
|
|
|
|
};
|
|
|
|
|
|
|
|
static int tegra_dc_late_register(struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
unsigned int i, count = ARRAY_SIZE(debugfs_files);
|
|
|
|
struct drm_minor *minor = crtc->dev->primary;
|
2017-12-18 21:44:46 +08:00
|
|
|
struct dentry *root;
|
2017-11-08 20:40:54 +08:00
|
|
|
struct tegra_dc *dc = to_tegra_dc(crtc);
|
|
|
|
|
2017-12-18 21:44:46 +08:00
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
|
|
root = crtc->debugfs_entry;
|
|
|
|
#else
|
|
|
|
root = NULL;
|
|
|
|
#endif
|
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!dc->debugfs_files)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
dc->debugfs_files[i].data = dc;
|
|
|
|
|
2020-03-10 21:31:05 +08:00
|
|
|
drm_debugfs_create_files(dc->debugfs_files, count, root, minor);
|
2017-11-08 20:40:54 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tegra_dc_early_unregister(struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
unsigned int count = ARRAY_SIZE(debugfs_files);
|
|
|
|
struct drm_minor *minor = crtc->dev->primary;
|
|
|
|
struct tegra_dc *dc = to_tegra_dc(crtc);
|
|
|
|
|
|
|
|
drm_debugfs_remove_files(dc->debugfs_files, count, minor);
|
|
|
|
kfree(dc->debugfs_files);
|
|
|
|
dc->debugfs_files = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
struct tegra_dc *dc = to_tegra_dc(crtc);
|
|
|
|
|
2017-08-30 23:42:54 +08:00
|
|
|
/* XXX vblank syncpoints don't work with nvdisplay yet */
|
|
|
|
if (dc->syncpt && !dc->soc->has_nvdisplay)
|
2017-11-08 20:40:54 +08:00
|
|
|
return host1x_syncpt_read(dc->syncpt);
|
|
|
|
|
|
|
|
/* fallback to software emulated VBLANK counter */
|
2018-02-03 13:12:58 +08:00
|
|
|
return (u32)drm_crtc_vblank_count(&dc->base);
|
2017-11-08 20:40:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int tegra_dc_enable_vblank(struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
struct tegra_dc *dc = to_tegra_dc(crtc);
|
2017-12-14 20:50:19 +08:00
|
|
|
u32 value;
|
2017-11-08 20:40:54 +08:00
|
|
|
|
|
|
|
value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
|
|
|
|
value |= VBLANK_INT;
|
|
|
|
tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tegra_dc_disable_vblank(struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
struct tegra_dc *dc = to_tegra_dc(crtc);
|
2017-12-14 20:50:19 +08:00
|
|
|
u32 value;
|
2017-11-08 20:32:05 +08:00
|
|
|
|
|
|
|
value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
|
|
|
|
value &= ~VBLANK_INT;
|
|
|
|
tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
|
|
|
|
}
|
|
|
|
|
2012-11-16 05:28:22 +08:00
|
|
|
static const struct drm_crtc_funcs tegra_crtc_funcs = {
|
2014-11-25 00:41:23 +08:00
|
|
|
.page_flip = drm_atomic_helper_page_flip,
|
2014-11-25 00:08:20 +08:00
|
|
|
.set_config = drm_atomic_helper_set_config,
|
2013-10-14 20:06:02 +08:00
|
|
|
.destroy = tegra_dc_destroy,
|
2014-12-08 23:14:45 +08:00
|
|
|
.reset = tegra_crtc_reset,
|
|
|
|
.atomic_duplicate_state = tegra_crtc_atomic_duplicate_state,
|
|
|
|
.atomic_destroy_state = tegra_crtc_atomic_destroy_state,
|
2017-11-08 20:40:54 +08:00
|
|
|
.late_register = tegra_dc_late_register,
|
|
|
|
.early_unregister = tegra_dc_early_unregister,
|
2017-02-07 17:16:32 +08:00
|
|
|
.get_vblank_counter = tegra_dc_get_vblank_counter,
|
|
|
|
.enable_vblank = tegra_dc_enable_vblank,
|
|
|
|
.disable_vblank = tegra_dc_disable_vblank,
|
2012-11-16 05:28:22 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static int tegra_dc_set_timings(struct tegra_dc *dc,
|
|
|
|
struct drm_display_mode *mode)
|
|
|
|
{
|
2014-04-16 15:22:38 +08:00
|
|
|
unsigned int h_ref_to_sync = 1;
|
|
|
|
unsigned int v_ref_to_sync = 1;
|
2012-11-16 05:28:22 +08:00
|
|
|
unsigned long value;
|
|
|
|
|
2017-08-30 23:42:54 +08:00
|
|
|
if (!dc->soc->has_nvdisplay) {
|
|
|
|
tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS);
|
2012-11-16 05:28:22 +08:00
|
|
|
|
2017-08-30 23:42:54 +08:00
|
|
|
value = (v_ref_to_sync << 16) | h_ref_to_sync;
|
|
|
|
tegra_dc_writel(dc, value, DC_DISP_REF_TO_SYNC);
|
|
|
|
}
|
2012-11-16 05:28:22 +08:00
|
|
|
|
|
|
|
value = ((mode->vsync_end - mode->vsync_start) << 16) |
|
|
|
|
((mode->hsync_end - mode->hsync_start) << 0);
|
|
|
|
tegra_dc_writel(dc, value, DC_DISP_SYNC_WIDTH);
|
|
|
|
|
|
|
|
value = ((mode->vtotal - mode->vsync_end) << 16) |
|
|
|
|
((mode->htotal - mode->hsync_end) << 0);
|
2012-12-20 05:38:52 +08:00
|
|
|
tegra_dc_writel(dc, value, DC_DISP_BACK_PORCH);
|
|
|
|
|
|
|
|
value = ((mode->vsync_start - mode->vdisplay) << 16) |
|
|
|
|
((mode->hsync_start - mode->hdisplay) << 0);
|
2012-11-16 05:28:22 +08:00
|
|
|
tegra_dc_writel(dc, value, DC_DISP_FRONT_PORCH);
|
|
|
|
|
|
|
|
value = (mode->vdisplay << 16) | mode->hdisplay;
|
|
|
|
tegra_dc_writel(dc, value, DC_DISP_ACTIVE);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-28 22:25:54 +08:00
|
|
|
/**
|
|
|
|
* tegra_dc_state_setup_clock - check clock settings and store them in atomic
|
|
|
|
* state
|
|
|
|
* @dc: display controller
|
|
|
|
* @crtc_state: CRTC atomic state
|
|
|
|
* @clk: parent clock for display controller
|
|
|
|
* @pclk: pixel clock
|
|
|
|
* @div: shift clock divider
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* 0 on success or a negative error-code on failure.
|
|
|
|
*/
|
2014-12-08 23:14:45 +08:00
|
|
|
int tegra_dc_state_setup_clock(struct tegra_dc *dc,
|
|
|
|
struct drm_crtc_state *crtc_state,
|
|
|
|
struct clk *clk, unsigned long pclk,
|
|
|
|
unsigned int div)
|
|
|
|
{
|
|
|
|
struct tegra_dc_state *state = to_dc_state(crtc_state);
|
|
|
|
|
2015-01-22 15:48:25 +08:00
|
|
|
if (!clk_has_parent(dc->clk, clk))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2014-12-08 23:14:45 +08:00
|
|
|
state->clk = clk;
|
|
|
|
state->pclk = pclk;
|
|
|
|
state->div = div;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-12-19 22:09:16 +08:00
|
|
|
static void tegra_dc_commit_state(struct tegra_dc *dc,
|
|
|
|
struct tegra_dc_state *state)
|
|
|
|
{
|
|
|
|
u32 value;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = clk_set_parent(dc->clk, state->clk);
|
|
|
|
if (err < 0)
|
|
|
|
dev_err(dc->dev, "failed to set parent clock: %d\n", err);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Outputs may not want to change the parent clock rate. This is only
|
|
|
|
* relevant to Tegra20 where only a single display PLL is available.
|
|
|
|
* Since that PLL would typically be used for HDMI, an internal LVDS
|
|
|
|
* panel would need to be driven by some other clock such as PLL_P
|
|
|
|
* which is shared with other peripherals. Changing the clock rate
|
|
|
|
* should therefore be avoided.
|
|
|
|
*/
|
|
|
|
if (state->pclk > 0) {
|
|
|
|
err = clk_set_rate(state->clk, state->pclk);
|
|
|
|
if (err < 0)
|
|
|
|
dev_err(dc->dev,
|
|
|
|
"failed to set clock rate to %lu Hz\n",
|
|
|
|
state->pclk);
|
2021-03-02 21:15:06 +08:00
|
|
|
|
|
|
|
err = clk_set_rate(dc->clk, state->pclk);
|
|
|
|
if (err < 0)
|
|
|
|
dev_err(dc->dev, "failed to set clock %pC to %lu Hz: %d\n",
|
|
|
|
dc->clk, state->pclk, err);
|
2014-12-19 22:09:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk),
|
|
|
|
state->div);
|
|
|
|
DRM_DEBUG_KMS("pclk: %lu\n", state->pclk);
|
|
|
|
|
2017-08-30 23:42:54 +08:00
|
|
|
if (!dc->soc->has_nvdisplay) {
|
|
|
|
value = SHIFT_CLK_DIVIDER(state->div) | PIXEL_CLK_DIVIDER_PCD1;
|
|
|
|
tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
|
|
|
|
}
|
2014-12-19 22:09:16 +08:00
|
|
|
}
|
|
|
|
|
2015-08-03 19:16:26 +08:00
|
|
|
static void tegra_dc_stop(struct tegra_dc *dc)
|
|
|
|
{
|
|
|
|
u32 value;
|
|
|
|
|
|
|
|
/* stop the display controller */
|
|
|
|
value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
|
|
|
|
value &= ~DISP_CTRL_MODE_MASK;
|
|
|
|
tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
|
|
|
|
|
|
|
|
tegra_dc_commit(dc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool tegra_dc_idle(struct tegra_dc *dc)
|
|
|
|
{
|
|
|
|
u32 value;
|
|
|
|
|
|
|
|
value = tegra_dc_readl_active(dc, DC_CMD_DISPLAY_COMMAND);
|
|
|
|
|
|
|
|
return (value & DISP_CTRL_MODE_MASK) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout)
|
|
|
|
{
|
|
|
|
timeout = jiffies + msecs_to_jiffies(timeout);
|
|
|
|
|
|
|
|
while (time_before(jiffies, timeout)) {
|
|
|
|
if (tegra_dc_idle(dc))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
usleep_range(1000, 2000);
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_dbg(dc->dev, "timeout waiting for DC to become idle\n");
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
2017-06-30 17:36:45 +08:00
|
|
|
static void tegra_crtc_atomic_disable(struct drm_crtc *crtc,
|
drm/atomic: Pass the full state to CRTC atomic enable/disable
If the CRTC driver ever needs to access the full DRM state, it can't do so
at atomic_enable / atomic_disable time since drm_atomic_helper_swap_state
will have cleared the pointer from the struct drm_crtc_state to the struct
drm_atomic_state before calling those hooks.
In order to allow that, let's pass the full DRM state to atomic_enable and
atomic_disable. The conversion was done using the coccinelle script below,
built tested on all the drivers and actually tested on vc4.
virtual report
@@
struct drm_crtc_helper_funcs *FUNCS;
identifier dev, state;
identifier crtc, crtc_state;
@@
disable_outputs(struct drm_device *dev, struct drm_atomic_state *state)
{
<...
- FUNCS->atomic_disable(crtc, crtc_state);
+ FUNCS->atomic_disable(crtc, state);
...>
}
@@
struct drm_crtc_helper_funcs *FUNCS;
identifier dev, state;
identifier crtc, crtc_state;
@@
drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, struct drm_atomic_state *state)
{
<...
- FUNCS->atomic_enable(crtc, crtc_state);
+ FUNCS->atomic_enable(crtc, state);
...>
}
@@
identifier crtc, old_state;
@@
struct drm_crtc_helper_funcs {
...
- void (*atomic_enable)(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+ void (*atomic_enable)(struct drm_crtc *crtc, struct drm_atomic_state *state);
...
- void (*atomic_disable)(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+ void (*atomic_disable)(struct drm_crtc *crtc, struct drm_atomic_state *state);
...
}
@ crtc_atomic_func @
identifier helpers;
identifier func;
@@
(
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_enable = func,
...,
};
|
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
)
@ ignores_old_state @
identifier crtc_atomic_func.func;
identifier crtc, old_state;
@@
void func(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
... when != old_state
}
@ adds_old_state depends on crtc_atomic_func && !ignores_old_state @
identifier crtc_atomic_func.func;
identifier crtc, old_state;
@@
void func(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
{
+ struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc);
...
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
expression E;
type T;
@@
void func(...)
{
...
- T state = E;
+ T crtc_state = E;
<+...
- state
+ crtc_state
...+>
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
type T;
@@
void func(...)
{
...
- T state;
+ T crtc_state;
<+...
- state
+ crtc_state
...+>
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
identifier old_state;
identifier crtc;
@@
void func(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_old_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_old_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/845aa10ef171fc0ea060495efef142a0c13f7870.1602161031.git-series.maxime@cerno.tech
2020-10-08 20:44:08 +08:00
|
|
|
struct drm_atomic_state *state)
|
2017-11-08 20:40:54 +08:00
|
|
|
{
|
|
|
|
struct tegra_dc *dc = to_tegra_dc(crtc);
|
|
|
|
u32 value;
|
2019-12-02 22:29:03 +08:00
|
|
|
int err;
|
2015-08-03 19:16:26 +08:00
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
if (!tegra_dc_idle(dc)) {
|
|
|
|
tegra_dc_stop(dc);
|
2015-08-03 19:16:26 +08:00
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
/*
|
|
|
|
* Ignore the return value, there isn't anything useful to do
|
|
|
|
* in case this fails.
|
|
|
|
*/
|
|
|
|
tegra_dc_wait_idle(dc, 100);
|
2015-08-03 19:16:26 +08:00
|
|
|
}
|
2012-11-16 05:28:22 +08:00
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
/*
|
|
|
|
* This should really be part of the RGB encoder driver, but clearing
|
|
|
|
* these bits has the side-effect of stopping the display controller.
|
|
|
|
* When that happens no VBLANK interrupts will be raised. At the same
|
|
|
|
* time the encoder is disabled before the display controller, so the
|
|
|
|
* above code is always going to timeout waiting for the controller
|
|
|
|
* to go idle.
|
|
|
|
*
|
|
|
|
* Given the close coupling between the RGB encoder and the display
|
|
|
|
* controller doing it here is still kind of okay. None of the other
|
|
|
|
* encoder drivers require these bits to be cleared.
|
|
|
|
*
|
|
|
|
* XXX: Perhaps given that the display controller is switched off at
|
|
|
|
* this point anyway maybe clearing these bits isn't even useful for
|
|
|
|
* the RGB encoder?
|
|
|
|
*/
|
|
|
|
if (dc->rgb) {
|
|
|
|
value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
|
|
|
|
value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
|
|
|
|
PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
|
|
|
|
tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
|
2017-11-10 19:13:22 +08:00
|
|
|
}
|
2012-11-16 05:28:22 +08:00
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
tegra_dc_stats_reset(&dc->stats);
|
|
|
|
drm_crtc_vblank_off(crtc);
|
|
|
|
|
2017-10-12 23:40:46 +08:00
|
|
|
spin_lock_irq(&crtc->dev->event_lock);
|
|
|
|
|
|
|
|
if (crtc->state->event) {
|
|
|
|
drm_crtc_send_vblank_event(crtc, crtc->state->event);
|
|
|
|
crtc->state->event = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_irq(&crtc->dev->event_lock);
|
|
|
|
|
2019-12-02 22:29:03 +08:00
|
|
|
err = host1x_client_suspend(&dc->client);
|
|
|
|
if (err < 0)
|
|
|
|
dev_err(dc->dev, "failed to suspend: %d\n", err);
|
2012-11-16 05:28:22 +08:00
|
|
|
}
|
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
|
drm/atomic: Pass the full state to CRTC atomic enable/disable
If the CRTC driver ever needs to access the full DRM state, it can't do so
at atomic_enable / atomic_disable time since drm_atomic_helper_swap_state
will have cleared the pointer from the struct drm_crtc_state to the struct
drm_atomic_state before calling those hooks.
In order to allow that, let's pass the full DRM state to atomic_enable and
atomic_disable. The conversion was done using the coccinelle script below,
built tested on all the drivers and actually tested on vc4.
virtual report
@@
struct drm_crtc_helper_funcs *FUNCS;
identifier dev, state;
identifier crtc, crtc_state;
@@
disable_outputs(struct drm_device *dev, struct drm_atomic_state *state)
{
<...
- FUNCS->atomic_disable(crtc, crtc_state);
+ FUNCS->atomic_disable(crtc, state);
...>
}
@@
struct drm_crtc_helper_funcs *FUNCS;
identifier dev, state;
identifier crtc, crtc_state;
@@
drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, struct drm_atomic_state *state)
{
<...
- FUNCS->atomic_enable(crtc, crtc_state);
+ FUNCS->atomic_enable(crtc, state);
...>
}
@@
identifier crtc, old_state;
@@
struct drm_crtc_helper_funcs {
...
- void (*atomic_enable)(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+ void (*atomic_enable)(struct drm_crtc *crtc, struct drm_atomic_state *state);
...
- void (*atomic_disable)(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+ void (*atomic_disable)(struct drm_crtc *crtc, struct drm_atomic_state *state);
...
}
@ crtc_atomic_func @
identifier helpers;
identifier func;
@@
(
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_enable = func,
...,
};
|
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
)
@ ignores_old_state @
identifier crtc_atomic_func.func;
identifier crtc, old_state;
@@
void func(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
... when != old_state
}
@ adds_old_state depends on crtc_atomic_func && !ignores_old_state @
identifier crtc_atomic_func.func;
identifier crtc, old_state;
@@
void func(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
{
+ struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc);
...
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
expression E;
type T;
@@
void func(...)
{
...
- T state = E;
+ T crtc_state = E;
<+...
- state
+ crtc_state
...+>
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
type T;
@@
void func(...)
{
...
- T state;
+ T crtc_state;
<+...
- state
+ crtc_state
...+>
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
identifier old_state;
identifier crtc;
@@
void func(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_old_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_old_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/845aa10ef171fc0ea060495efef142a0c13f7870.1602161031.git-series.maxime@cerno.tech
2020-10-08 20:44:08 +08:00
|
|
|
struct drm_atomic_state *state)
|
2015-04-01 20:59:40 +08:00
|
|
|
{
|
2017-11-08 20:40:54 +08:00
|
|
|
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
|
drm/atomic: Pass the full state to CRTC atomic enable/disable
If the CRTC driver ever needs to access the full DRM state, it can't do so
at atomic_enable / atomic_disable time since drm_atomic_helper_swap_state
will have cleared the pointer from the struct drm_crtc_state to the struct
drm_atomic_state before calling those hooks.
In order to allow that, let's pass the full DRM state to atomic_enable and
atomic_disable. The conversion was done using the coccinelle script below,
built tested on all the drivers and actually tested on vc4.
virtual report
@@
struct drm_crtc_helper_funcs *FUNCS;
identifier dev, state;
identifier crtc, crtc_state;
@@
disable_outputs(struct drm_device *dev, struct drm_atomic_state *state)
{
<...
- FUNCS->atomic_disable(crtc, crtc_state);
+ FUNCS->atomic_disable(crtc, state);
...>
}
@@
struct drm_crtc_helper_funcs *FUNCS;
identifier dev, state;
identifier crtc, crtc_state;
@@
drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, struct drm_atomic_state *state)
{
<...
- FUNCS->atomic_enable(crtc, crtc_state);
+ FUNCS->atomic_enable(crtc, state);
...>
}
@@
identifier crtc, old_state;
@@
struct drm_crtc_helper_funcs {
...
- void (*atomic_enable)(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+ void (*atomic_enable)(struct drm_crtc *crtc, struct drm_atomic_state *state);
...
- void (*atomic_disable)(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+ void (*atomic_disable)(struct drm_crtc *crtc, struct drm_atomic_state *state);
...
}
@ crtc_atomic_func @
identifier helpers;
identifier func;
@@
(
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_enable = func,
...,
};
|
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
)
@ ignores_old_state @
identifier crtc_atomic_func.func;
identifier crtc, old_state;
@@
void func(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
... when != old_state
}
@ adds_old_state depends on crtc_atomic_func && !ignores_old_state @
identifier crtc_atomic_func.func;
identifier crtc, old_state;
@@
void func(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
{
+ struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc);
...
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
expression E;
type T;
@@
void func(...)
{
...
- T state = E;
+ T crtc_state = E;
<+...
- state
+ crtc_state
...+>
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
type T;
@@
void func(...)
{
...
- T state;
+ T crtc_state;
<+...
- state
+ crtc_state
...+>
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
identifier old_state;
identifier crtc;
@@
void func(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_old_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_old_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/845aa10ef171fc0ea060495efef142a0c13f7870.1602161031.git-series.maxime@cerno.tech
2020-10-08 20:44:08 +08:00
|
|
|
struct tegra_dc_state *crtc_state = to_dc_state(crtc->state);
|
2017-11-08 20:40:54 +08:00
|
|
|
struct tegra_dc *dc = to_tegra_dc(crtc);
|
2015-04-01 20:59:40 +08:00
|
|
|
u32 value;
|
2019-12-02 22:29:03 +08:00
|
|
|
int err;
|
2015-04-01 20:59:40 +08:00
|
|
|
|
2019-12-02 22:29:03 +08:00
|
|
|
err = host1x_client_resume(&dc->client);
|
|
|
|
if (err < 0) {
|
|
|
|
dev_err(dc->dev, "failed to resume: %d\n", err);
|
|
|
|
return;
|
|
|
|
}
|
2015-08-03 19:16:26 +08:00
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
/* initialize display controller */
|
|
|
|
if (dc->syncpt) {
|
2017-08-30 23:42:54 +08:00
|
|
|
u32 syncpt = host1x_syncpt_id(dc->syncpt), enable;
|
|
|
|
|
|
|
|
if (dc->soc->has_nvdisplay)
|
|
|
|
enable = 1 << 31;
|
|
|
|
else
|
|
|
|
enable = 1 << 8;
|
2017-11-08 20:40:54 +08:00
|
|
|
|
|
|
|
value = SYNCPT_CNTRL_NO_STALL;
|
|
|
|
tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
|
|
|
|
|
2017-08-30 23:42:54 +08:00
|
|
|
value = enable | syncpt;
|
2017-11-08 20:40:54 +08:00
|
|
|
tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC);
|
2015-08-03 19:16:26 +08:00
|
|
|
}
|
|
|
|
|
2017-08-30 23:42:54 +08:00
|
|
|
if (dc->soc->has_nvdisplay) {
|
|
|
|
value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT |
|
|
|
|
DSC_OBUF_UF_INT;
|
|
|
|
tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
|
2015-04-01 20:59:40 +08:00
|
|
|
|
2017-08-30 23:42:54 +08:00
|
|
|
value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT |
|
|
|
|
DSC_OBUF_UF_INT | SD3_BUCKET_WALK_DONE_INT |
|
|
|
|
HEAD_UF_INT | MSF_INT | REG_TMOUT_INT |
|
|
|
|
REGION_CRC_INT | V_PULSE2_INT | V_PULSE3_INT |
|
|
|
|
VBLANK_INT | FRAME_END_INT;
|
|
|
|
tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
|
2015-04-01 20:59:40 +08:00
|
|
|
|
2017-08-30 23:42:54 +08:00
|
|
|
value = SD3_BUCKET_WALK_DONE_INT | HEAD_UF_INT | VBLANK_INT |
|
|
|
|
FRAME_END_INT;
|
|
|
|
tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
|
2015-04-01 20:59:40 +08:00
|
|
|
|
2017-08-30 23:42:54 +08:00
|
|
|
value = HEAD_UF_INT | REG_TMOUT_INT | FRAME_END_INT;
|
|
|
|
tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
|
2017-11-08 20:40:54 +08:00
|
|
|
|
2017-08-30 23:42:54 +08:00
|
|
|
tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
|
|
|
|
} else {
|
|
|
|
value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
|
|
|
|
WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
|
|
|
|
tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
|
|
|
|
|
|
|
|
value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
|
|
|
|
WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
|
|
|
|
tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
|
|
|
|
|
|
|
|
/* initialize timer */
|
|
|
|
value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) |
|
|
|
|
WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20);
|
|
|
|
tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY);
|
|
|
|
|
|
|
|
value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) |
|
|
|
|
WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1);
|
|
|
|
tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER);
|
|
|
|
|
|
|
|
value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
|
|
|
|
WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
|
|
|
|
tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
|
|
|
|
|
|
|
|
value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
|
|
|
|
WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
|
|
|
|
tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
|
|
|
|
}
|
2017-11-08 20:40:54 +08:00
|
|
|
|
2017-11-13 18:20:48 +08:00
|
|
|
if (dc->soc->supports_background_color)
|
|
|
|
tegra_dc_writel(dc, 0, DC_DISP_BLEND_BACKGROUND_COLOR);
|
|
|
|
else
|
2017-11-08 20:40:54 +08:00
|
|
|
tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR);
|
|
|
|
|
|
|
|
/* apply PLL and pixel clock changes */
|
drm/atomic: Pass the full state to CRTC atomic enable/disable
If the CRTC driver ever needs to access the full DRM state, it can't do so
at atomic_enable / atomic_disable time since drm_atomic_helper_swap_state
will have cleared the pointer from the struct drm_crtc_state to the struct
drm_atomic_state before calling those hooks.
In order to allow that, let's pass the full DRM state to atomic_enable and
atomic_disable. The conversion was done using the coccinelle script below,
built tested on all the drivers and actually tested on vc4.
virtual report
@@
struct drm_crtc_helper_funcs *FUNCS;
identifier dev, state;
identifier crtc, crtc_state;
@@
disable_outputs(struct drm_device *dev, struct drm_atomic_state *state)
{
<...
- FUNCS->atomic_disable(crtc, crtc_state);
+ FUNCS->atomic_disable(crtc, state);
...>
}
@@
struct drm_crtc_helper_funcs *FUNCS;
identifier dev, state;
identifier crtc, crtc_state;
@@
drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, struct drm_atomic_state *state)
{
<...
- FUNCS->atomic_enable(crtc, crtc_state);
+ FUNCS->atomic_enable(crtc, state);
...>
}
@@
identifier crtc, old_state;
@@
struct drm_crtc_helper_funcs {
...
- void (*atomic_enable)(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+ void (*atomic_enable)(struct drm_crtc *crtc, struct drm_atomic_state *state);
...
- void (*atomic_disable)(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+ void (*atomic_disable)(struct drm_crtc *crtc, struct drm_atomic_state *state);
...
}
@ crtc_atomic_func @
identifier helpers;
identifier func;
@@
(
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_enable = func,
...,
};
|
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
)
@ ignores_old_state @
identifier crtc_atomic_func.func;
identifier crtc, old_state;
@@
void func(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
... when != old_state
}
@ adds_old_state depends on crtc_atomic_func && !ignores_old_state @
identifier crtc_atomic_func.func;
identifier crtc, old_state;
@@
void func(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
{
+ struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc);
...
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
expression E;
type T;
@@
void func(...)
{
...
- T state = E;
+ T crtc_state = E;
<+...
- state
+ crtc_state
...+>
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
type T;
@@
void func(...)
{
...
- T state;
+ T crtc_state;
<+...
- state
+ crtc_state
...+>
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
identifier old_state;
identifier crtc;
@@
void func(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_old_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_old_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/845aa10ef171fc0ea060495efef142a0c13f7870.1602161031.git-series.maxime@cerno.tech
2020-10-08 20:44:08 +08:00
|
|
|
tegra_dc_commit_state(dc, crtc_state);
|
2017-11-08 20:40:54 +08:00
|
|
|
|
|
|
|
/* program display mode */
|
|
|
|
tegra_dc_set_timings(dc, mode);
|
|
|
|
|
|
|
|
/* interlacing isn't supported yet, so disable it */
|
|
|
|
if (dc->soc->supports_interlacing) {
|
|
|
|
value = tegra_dc_readl(dc, DC_DISP_INTERLACE_CONTROL);
|
|
|
|
value &= ~INTERLACE_ENABLE;
|
|
|
|
tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL);
|
|
|
|
}
|
|
|
|
|
|
|
|
value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
|
|
|
|
value &= ~DISP_CTRL_MODE_MASK;
|
|
|
|
value |= DISP_CTRL_MODE_C_DISPLAY;
|
|
|
|
tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
|
|
|
|
|
2017-08-30 23:42:54 +08:00
|
|
|
if (!dc->soc->has_nvdisplay) {
|
|
|
|
value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
|
|
|
|
value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
|
|
|
|
PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
|
|
|
|
tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* enable underflow reporting and display red for missing pixels */
|
|
|
|
if (dc->soc->has_nvdisplay) {
|
|
|
|
value = UNDERFLOW_MODE_RED | UNDERFLOW_REPORT_ENABLE;
|
|
|
|
tegra_dc_writel(dc, value, DC_COM_RG_UNDERFLOW);
|
|
|
|
}
|
2017-11-08 20:40:54 +08:00
|
|
|
|
|
|
|
tegra_dc_commit(dc);
|
|
|
|
|
|
|
|
drm_crtc_vblank_on(crtc);
|
2015-04-01 20:59:40 +08:00
|
|
|
}
|
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
static void tegra_crtc_atomic_begin(struct drm_crtc *crtc,
|
drm/atomic: Pass the full state to CRTC atomic begin and flush
The current atomic helpers have either their object state being passed as
an argument or the full atomic state.
The former is the pattern that was done at first, before switching to the
latter for new hooks or when it was needed.
Let's start convert all the remaining helpers to provide a consistent
interface, starting with the CRTC's atomic_begin and atomic_flush.
The conversion was done using the coccinelle script below, built tested on
all the drivers and actually tested on vc4.
virtual report
@@
struct drm_crtc_helper_funcs *FUNCS;
identifier old_crtc_state, old_state;
identifier crtc;
identifier f;
@@
f(struct drm_crtc_state *old_crtc_state)
{
...
struct drm_atomic_state *old_state = old_crtc_state->state;
<...
- FUNCS->atomic_begin(crtc, old_crtc_state);
+ FUNCS->atomic_begin(crtc, old_state);
...>
}
@@
struct drm_crtc_helper_funcs *FUNCS;
identifier old_crtc_state, old_state;
identifier crtc;
identifier f;
@@
f(struct drm_crtc_state *old_crtc_state)
{
...
struct drm_atomic_state *old_state = old_crtc_state->state;
<...
- FUNCS->atomic_flush(crtc, old_crtc_state);
+ FUNCS->atomic_flush(crtc, old_state);
...>
}
@@
struct drm_crtc_helper_funcs *FUNCS;
struct drm_crtc *crtc;
struct drm_crtc_state *crtc_state;
identifier dev, state;
identifier f;
@@
f(struct drm_device *dev, struct drm_atomic_state *state, ...)
{
<...
- FUNCS->atomic_begin(crtc, crtc_state);
+ FUNCS->atomic_begin(crtc, state);
...>
}
@@
struct drm_crtc_helper_funcs *FUNCS;
struct drm_crtc *crtc;
struct drm_crtc_state *crtc_state;
identifier dev, state;
identifier f;
@@
f(struct drm_device *dev, struct drm_atomic_state *state, ...)
{
<...
- FUNCS->atomic_flush(crtc, crtc_state);
+ FUNCS->atomic_flush(crtc, state);
...>
}
@@
identifier crtc, old_state;
@@
struct drm_crtc_helper_funcs {
...
- void (*atomic_begin)(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+ void (*atomic_begin)(struct drm_crtc *crtc, struct drm_atomic_state *state);
...
- void (*atomic_flush)(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+ void (*atomic_flush)(struct drm_crtc *crtc, struct drm_atomic_state *state);
...
}
@ crtc_atomic_func @
identifier helpers;
identifier func;
@@
(
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_begin = func,
...,
};
|
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_flush = func,
...,
};
)
@ ignores_old_state @
identifier crtc_atomic_func.func;
identifier crtc, old_state;
@@
void func(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
... when != old_state
}
@ adds_old_state depends on crtc_atomic_func && !ignores_old_state @
identifier crtc_atomic_func.func;
identifier crtc, old_state;
@@
void func(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
{
+ struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc);
...
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
expression E;
type T;
@@
void func(...)
{
...
- T state = E;
+ T crtc_state = E;
<+...
- state
+ crtc_state
...+>
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
type T;
@@
void func(...)
{
...
- T state;
+ T crtc_state;
<+...
- state
+ crtc_state
...+>
}
@@
identifier old_state;
identifier crtc;
@@
void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
)
{
+ struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc);
...
}
@@
identifier old_state;
identifier crtc;
@@
void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
);
@@
identifier old_state;
identifier crtc;
@@
void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
)
{
...
}
@@
identifier old_state;
identifier crtc;
@@
void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
);
@@
identifier old_state;
identifier crtc;
@@
void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
)
{
...
}
@@
identifier old_state;
identifier crtc;
@@
void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
);
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
identifier old_state;
identifier crtc;
@@
void func(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_old_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_old_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20201028123222.1732139-2-maxime@cerno.tech
2020-10-28 20:32:22 +08:00
|
|
|
struct drm_atomic_state *state)
|
2017-11-08 20:40:54 +08:00
|
|
|
{
|
2017-10-12 23:40:46 +08:00
|
|
|
unsigned long flags;
|
2015-07-29 03:27:05 +08:00
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
if (crtc->state->event) {
|
2017-10-12 23:40:46 +08:00
|
|
|
spin_lock_irqsave(&crtc->dev->event_lock, flags);
|
|
|
|
|
|
|
|
if (drm_crtc_vblank_get(crtc) != 0)
|
|
|
|
drm_crtc_send_vblank_event(crtc, crtc->state->event);
|
|
|
|
else
|
|
|
|
drm_crtc_arm_vblank_event(crtc, crtc->state->event);
|
2017-11-08 20:40:54 +08:00
|
|
|
|
2017-10-12 23:40:46 +08:00
|
|
|
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
2017-11-08 20:40:54 +08:00
|
|
|
|
|
|
|
crtc->state->event = NULL;
|
|
|
|
}
|
2012-11-16 05:28:22 +08:00
|
|
|
}
|
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
static void tegra_crtc_atomic_flush(struct drm_crtc *crtc,
|
drm/atomic: Pass the full state to CRTC atomic begin and flush
The current atomic helpers have either their object state being passed as
an argument or the full atomic state.
The former is the pattern that was done at first, before switching to the
latter for new hooks or when it was needed.
Let's start convert all the remaining helpers to provide a consistent
interface, starting with the CRTC's atomic_begin and atomic_flush.
The conversion was done using the coccinelle script below, built tested on
all the drivers and actually tested on vc4.
virtual report
@@
struct drm_crtc_helper_funcs *FUNCS;
identifier old_crtc_state, old_state;
identifier crtc;
identifier f;
@@
f(struct drm_crtc_state *old_crtc_state)
{
...
struct drm_atomic_state *old_state = old_crtc_state->state;
<...
- FUNCS->atomic_begin(crtc, old_crtc_state);
+ FUNCS->atomic_begin(crtc, old_state);
...>
}
@@
struct drm_crtc_helper_funcs *FUNCS;
identifier old_crtc_state, old_state;
identifier crtc;
identifier f;
@@
f(struct drm_crtc_state *old_crtc_state)
{
...
struct drm_atomic_state *old_state = old_crtc_state->state;
<...
- FUNCS->atomic_flush(crtc, old_crtc_state);
+ FUNCS->atomic_flush(crtc, old_state);
...>
}
@@
struct drm_crtc_helper_funcs *FUNCS;
struct drm_crtc *crtc;
struct drm_crtc_state *crtc_state;
identifier dev, state;
identifier f;
@@
f(struct drm_device *dev, struct drm_atomic_state *state, ...)
{
<...
- FUNCS->atomic_begin(crtc, crtc_state);
+ FUNCS->atomic_begin(crtc, state);
...>
}
@@
struct drm_crtc_helper_funcs *FUNCS;
struct drm_crtc *crtc;
struct drm_crtc_state *crtc_state;
identifier dev, state;
identifier f;
@@
f(struct drm_device *dev, struct drm_atomic_state *state, ...)
{
<...
- FUNCS->atomic_flush(crtc, crtc_state);
+ FUNCS->atomic_flush(crtc, state);
...>
}
@@
identifier crtc, old_state;
@@
struct drm_crtc_helper_funcs {
...
- void (*atomic_begin)(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+ void (*atomic_begin)(struct drm_crtc *crtc, struct drm_atomic_state *state);
...
- void (*atomic_flush)(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+ void (*atomic_flush)(struct drm_crtc *crtc, struct drm_atomic_state *state);
...
}
@ crtc_atomic_func @
identifier helpers;
identifier func;
@@
(
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_begin = func,
...,
};
|
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_flush = func,
...,
};
)
@ ignores_old_state @
identifier crtc_atomic_func.func;
identifier crtc, old_state;
@@
void func(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
... when != old_state
}
@ adds_old_state depends on crtc_atomic_func && !ignores_old_state @
identifier crtc_atomic_func.func;
identifier crtc, old_state;
@@
void func(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
{
+ struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc);
...
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
expression E;
type T;
@@
void func(...)
{
...
- T state = E;
+ T crtc_state = E;
<+...
- state
+ crtc_state
...+>
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
type T;
@@
void func(...)
{
...
- T state;
+ T crtc_state;
<+...
- state
+ crtc_state
...+>
}
@@
identifier old_state;
identifier crtc;
@@
void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
)
{
+ struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc);
...
}
@@
identifier old_state;
identifier crtc;
@@
void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
);
@@
identifier old_state;
identifier crtc;
@@
void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
)
{
...
}
@@
identifier old_state;
identifier crtc;
@@
void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
);
@@
identifier old_state;
identifier crtc;
@@
void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
)
{
...
}
@@
identifier old_state;
identifier crtc;
@@
void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
);
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
identifier old_state;
identifier crtc;
@@
void func(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_old_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_old_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20201028123222.1732139-2-maxime@cerno.tech
2020-10-28 20:32:22 +08:00
|
|
|
struct drm_atomic_state *state)
|
2017-11-08 20:40:54 +08:00
|
|
|
{
|
drm: Use state helper instead of CRTC state pointer
Many drivers reference the crtc->pointer in order to get the current CRTC
state in their atomic_begin or atomic_flush hooks, which would be the new
CRTC state in the global atomic state since _swap_state happened when those
hooks are run.
Use the drm_atomic_get_new_crtc_state helper to get that state to make it
more obvious.
This was made using the coccinelle script below:
@ crtc_atomic_func @
identifier helpers;
identifier func;
@@
(
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_begin = func,
...,
};
|
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_flush = func,
...,
};
)
@@
identifier crtc_atomic_func.func;
identifier crtc, state;
symbol crtc_state;
expression e;
@@
func(struct drm_crtc *crtc, struct drm_atomic_state *state) {
...
- struct tegra_dc_state *crtc_state = e;
+ struct tegra_dc_state *dc_state = e;
<+...
- crtc_state
+ dc_state
...+>
}
@@
identifier crtc_atomic_func.func;
identifier crtc, state;
symbol crtc_state;
expression e;
@@
func(struct drm_crtc *crtc, struct drm_atomic_state *state) {
...
- struct mtk_crtc_state *crtc_state = e;
+ struct mtk_crtc_state *mtk_crtc_state = e;
<+...
- crtc_state
+ mtk_crtc_state
...+>
}
@ replaces_new_state @
identifier crtc_atomic_func.func;
identifier crtc, state, crtc_state;
@@
func(struct drm_crtc *crtc, struct drm_atomic_state *state) {
...
- struct drm_crtc_state *crtc_state = crtc->state;
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
...
}
@@
identifier crtc_atomic_func.func;
identifier crtc, state, crtc_state;
@@
func(struct drm_crtc *crtc, struct drm_atomic_state *state) {
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
...
- crtc->state
+ crtc_state
...
}
@ adds_new_state @
identifier crtc_atomic_func.func;
identifier crtc, state;
@@
func(struct drm_crtc *crtc, struct drm_atomic_state *state) {
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
...
- crtc->state
+ crtc_state
...
}
@ include depends on adds_new_state || replaces_new_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && (adds_new_state || replaces_new_state) @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Suggested-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Cc: "James (Qian) Wang" <james.qian.wang@arm.com>
Cc: Liviu Dudau <liviu.dudau@arm.com>
Cc: Mihail Atanassov <mihail.atanassov@arm.com>
Cc: Brian Starkey <brian.starkey@arm.com>
Cc: Russell King <linux@armlinux.org.uk>
Cc: Paul Cercueil <paul@crapouillou.net>
Cc: Chun-Kuang Hu <chunkuang.hu@kernel.org>
Cc: Philipp Zabel <p.zabel@pengutronix.de>
Cc: Sandy Huang <hjc@rock-chips.com>
Cc: "Heiko Stübner" <heiko@sntech.de>
Cc: Thierry Reding <thierry.reding@gmail.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201105164518.392891-1-maxime@cerno.tech
2020-11-06 00:45:18 +08:00
|
|
|
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
|
|
|
|
crtc);
|
|
|
|
struct tegra_dc_state *dc_state = to_dc_state(crtc_state);
|
2017-11-08 20:40:54 +08:00
|
|
|
struct tegra_dc *dc = to_tegra_dc(crtc);
|
2017-08-30 23:42:54 +08:00
|
|
|
u32 value;
|
|
|
|
|
drm: Use state helper instead of CRTC state pointer
Many drivers reference the crtc->pointer in order to get the current CRTC
state in their atomic_begin or atomic_flush hooks, which would be the new
CRTC state in the global atomic state since _swap_state happened when those
hooks are run.
Use the drm_atomic_get_new_crtc_state helper to get that state to make it
more obvious.
This was made using the coccinelle script below:
@ crtc_atomic_func @
identifier helpers;
identifier func;
@@
(
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_begin = func,
...,
};
|
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_flush = func,
...,
};
)
@@
identifier crtc_atomic_func.func;
identifier crtc, state;
symbol crtc_state;
expression e;
@@
func(struct drm_crtc *crtc, struct drm_atomic_state *state) {
...
- struct tegra_dc_state *crtc_state = e;
+ struct tegra_dc_state *dc_state = e;
<+...
- crtc_state
+ dc_state
...+>
}
@@
identifier crtc_atomic_func.func;
identifier crtc, state;
symbol crtc_state;
expression e;
@@
func(struct drm_crtc *crtc, struct drm_atomic_state *state) {
...
- struct mtk_crtc_state *crtc_state = e;
+ struct mtk_crtc_state *mtk_crtc_state = e;
<+...
- crtc_state
+ mtk_crtc_state
...+>
}
@ replaces_new_state @
identifier crtc_atomic_func.func;
identifier crtc, state, crtc_state;
@@
func(struct drm_crtc *crtc, struct drm_atomic_state *state) {
...
- struct drm_crtc_state *crtc_state = crtc->state;
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
...
}
@@
identifier crtc_atomic_func.func;
identifier crtc, state, crtc_state;
@@
func(struct drm_crtc *crtc, struct drm_atomic_state *state) {
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
...
- crtc->state
+ crtc_state
...
}
@ adds_new_state @
identifier crtc_atomic_func.func;
identifier crtc, state;
@@
func(struct drm_crtc *crtc, struct drm_atomic_state *state) {
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
...
- crtc->state
+ crtc_state
...
}
@ include depends on adds_new_state || replaces_new_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && (adds_new_state || replaces_new_state) @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Suggested-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Cc: "James (Qian) Wang" <james.qian.wang@arm.com>
Cc: Liviu Dudau <liviu.dudau@arm.com>
Cc: Mihail Atanassov <mihail.atanassov@arm.com>
Cc: Brian Starkey <brian.starkey@arm.com>
Cc: Russell King <linux@armlinux.org.uk>
Cc: Paul Cercueil <paul@crapouillou.net>
Cc: Chun-Kuang Hu <chunkuang.hu@kernel.org>
Cc: Philipp Zabel <p.zabel@pengutronix.de>
Cc: Sandy Huang <hjc@rock-chips.com>
Cc: "Heiko Stübner" <heiko@sntech.de>
Cc: Thierry Reding <thierry.reding@gmail.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201105164518.392891-1-maxime@cerno.tech
2020-11-06 00:45:18 +08:00
|
|
|
value = dc_state->planes << 8 | GENERAL_UPDATE;
|
2017-08-30 23:42:54 +08:00
|
|
|
tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
|
|
|
|
value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
|
2017-11-08 20:40:54 +08:00
|
|
|
|
drm: Use state helper instead of CRTC state pointer
Many drivers reference the crtc->pointer in order to get the current CRTC
state in their atomic_begin or atomic_flush hooks, which would be the new
CRTC state in the global atomic state since _swap_state happened when those
hooks are run.
Use the drm_atomic_get_new_crtc_state helper to get that state to make it
more obvious.
This was made using the coccinelle script below:
@ crtc_atomic_func @
identifier helpers;
identifier func;
@@
(
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_begin = func,
...,
};
|
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_flush = func,
...,
};
)
@@
identifier crtc_atomic_func.func;
identifier crtc, state;
symbol crtc_state;
expression e;
@@
func(struct drm_crtc *crtc, struct drm_atomic_state *state) {
...
- struct tegra_dc_state *crtc_state = e;
+ struct tegra_dc_state *dc_state = e;
<+...
- crtc_state
+ dc_state
...+>
}
@@
identifier crtc_atomic_func.func;
identifier crtc, state;
symbol crtc_state;
expression e;
@@
func(struct drm_crtc *crtc, struct drm_atomic_state *state) {
...
- struct mtk_crtc_state *crtc_state = e;
+ struct mtk_crtc_state *mtk_crtc_state = e;
<+...
- crtc_state
+ mtk_crtc_state
...+>
}
@ replaces_new_state @
identifier crtc_atomic_func.func;
identifier crtc, state, crtc_state;
@@
func(struct drm_crtc *crtc, struct drm_atomic_state *state) {
...
- struct drm_crtc_state *crtc_state = crtc->state;
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
...
}
@@
identifier crtc_atomic_func.func;
identifier crtc, state, crtc_state;
@@
func(struct drm_crtc *crtc, struct drm_atomic_state *state) {
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
...
- crtc->state
+ crtc_state
...
}
@ adds_new_state @
identifier crtc_atomic_func.func;
identifier crtc, state;
@@
func(struct drm_crtc *crtc, struct drm_atomic_state *state) {
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
...
- crtc->state
+ crtc_state
...
}
@ include depends on adds_new_state || replaces_new_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && (adds_new_state || replaces_new_state) @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Suggested-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Cc: "James (Qian) Wang" <james.qian.wang@arm.com>
Cc: Liviu Dudau <liviu.dudau@arm.com>
Cc: Mihail Atanassov <mihail.atanassov@arm.com>
Cc: Brian Starkey <brian.starkey@arm.com>
Cc: Russell King <linux@armlinux.org.uk>
Cc: Paul Cercueil <paul@crapouillou.net>
Cc: Chun-Kuang Hu <chunkuang.hu@kernel.org>
Cc: Philipp Zabel <p.zabel@pengutronix.de>
Cc: Sandy Huang <hjc@rock-chips.com>
Cc: "Heiko Stübner" <heiko@sntech.de>
Cc: Thierry Reding <thierry.reding@gmail.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20201105164518.392891-1-maxime@cerno.tech
2020-11-06 00:45:18 +08:00
|
|
|
value = dc_state->planes | GENERAL_ACT_REQ;
|
2017-08-30 23:42:54 +08:00
|
|
|
tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
|
|
|
|
value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
|
2017-11-08 20:40:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
|
|
|
|
.atomic_begin = tegra_crtc_atomic_begin,
|
|
|
|
.atomic_flush = tegra_crtc_atomic_flush,
|
|
|
|
.atomic_enable = tegra_crtc_atomic_enable,
|
|
|
|
.atomic_disable = tegra_crtc_atomic_disable,
|
2012-11-16 05:28:22 +08:00
|
|
|
};
|
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
static irqreturn_t tegra_dc_irq(int irq, void *data)
|
2012-11-16 05:28:22 +08:00
|
|
|
{
|
2017-11-08 20:40:54 +08:00
|
|
|
struct tegra_dc *dc = data;
|
|
|
|
unsigned long status;
|
2012-11-16 05:28:22 +08:00
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
|
|
|
|
tegra_dc_writel(dc, status, DC_CMD_INT_STATUS);
|
|
|
|
|
|
|
|
if (status & FRAME_END_INT) {
|
|
|
|
/*
|
|
|
|
dev_dbg(dc->dev, "%s(): frame end\n", __func__);
|
|
|
|
*/
|
|
|
|
dc->stats.frames++;
|
|
|
|
}
|
2012-11-16 05:28:22 +08:00
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
if (status & VBLANK_INT) {
|
|
|
|
/*
|
|
|
|
dev_dbg(dc->dev, "%s(): vertical blank\n", __func__);
|
|
|
|
*/
|
|
|
|
drm_crtc_handle_vblank(&dc->base);
|
|
|
|
dc->stats.vblank++;
|
|
|
|
}
|
2012-11-16 05:28:22 +08:00
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) {
|
|
|
|
/*
|
|
|
|
dev_dbg(dc->dev, "%s(): underflow\n", __func__);
|
|
|
|
*/
|
|
|
|
dc->stats.underflow++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status & (WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT)) {
|
|
|
|
/*
|
|
|
|
dev_dbg(dc->dev, "%s(): overflow\n", __func__);
|
|
|
|
*/
|
|
|
|
dc->stats.overflow++;
|
|
|
|
}
|
|
|
|
|
2017-08-30 23:42:54 +08:00
|
|
|
if (status & HEAD_UF_INT) {
|
|
|
|
dev_dbg_ratelimited(dc->dev, "%s(): head underflow\n", __func__);
|
|
|
|
dc->stats.underflow++;
|
|
|
|
}
|
|
|
|
|
2017-11-08 20:40:54 +08:00
|
|
|
return IRQ_HANDLED;
|
2012-11-16 05:28:22 +08:00
|
|
|
}
|
|
|
|
|
2018-11-30 00:24:35 +08:00
|
|
|
static bool tegra_dc_has_window_groups(struct tegra_dc *dc)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
if (!dc->soc->wgrps)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
for (i = 0; i < dc->soc->num_wgrps; i++) {
|
|
|
|
const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i];
|
|
|
|
|
|
|
|
if (wgrp->dc == dc->pipe && wgrp->num_windows > 0)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-03-26 22:51:38 +08:00
|
|
|
static int tegra_dc_early_init(struct host1x_client *client)
|
|
|
|
{
|
|
|
|
struct drm_device *drm = dev_get_drvdata(client->host);
|
|
|
|
struct tegra_drm *tegra = drm->dev_private;
|
|
|
|
|
|
|
|
tegra->num_crtcs++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-09-24 21:35:40 +08:00
|
|
|
static int tegra_dc_init(struct host1x_client *client)
|
2012-11-16 05:28:22 +08:00
|
|
|
{
|
2019-12-02 17:51:58 +08:00
|
|
|
struct drm_device *drm = dev_get_drvdata(client->host);
|
2015-08-24 20:47:10 +08:00
|
|
|
unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED;
|
2013-10-14 20:43:22 +08:00
|
|
|
struct tegra_dc *dc = host1x_client_to_dc(client);
|
2014-07-11 14:29:14 +08:00
|
|
|
struct tegra_drm *tegra = drm->dev_private;
|
2014-10-21 19:51:53 +08:00
|
|
|
struct drm_plane *primary = NULL;
|
|
|
|
struct drm_plane *cursor = NULL;
|
2012-11-16 05:28:22 +08:00
|
|
|
int err;
|
|
|
|
|
2021-03-29 21:38:34 +08:00
|
|
|
/*
|
|
|
|
* DC has been reset by now, so VBLANK syncpoint can be released
|
|
|
|
* for general use.
|
|
|
|
*/
|
|
|
|
host1x_syncpt_release_vblank_reservation(client, 26 + dc->pipe);
|
|
|
|
|
2018-09-21 18:27:42 +08:00
|
|
|
/*
|
|
|
|
* XXX do not register DCs with no window groups because we cannot
|
|
|
|
* assign a primary plane to them, which in turn will cause KMS to
|
|
|
|
* crash.
|
|
|
|
*/
|
2018-11-30 00:24:35 +08:00
|
|
|
if (!tegra_dc_has_window_groups(dc))
|
|
|
|
return 0;
|
2018-09-21 18:27:42 +08:00
|
|
|
|
2019-12-02 22:29:03 +08:00
|
|
|
/*
|
|
|
|
* Set the display hub as the host1x client parent for the display
|
|
|
|
* controller. This is needed for the runtime reference counting that
|
|
|
|
* ensures the display hub is always powered when any of the display
|
|
|
|
* controllers are.
|
|
|
|
*/
|
|
|
|
if (dc->soc->has_nvdisplay)
|
|
|
|
client->parent = &tegra->hub->client;
|
|
|
|
|
2017-08-30 18:48:31 +08:00
|
|
|
dc->syncpt = host1x_syncpt_request(client, flags);
|
2015-08-24 20:47:10 +08:00
|
|
|
if (!dc->syncpt)
|
|
|
|
dev_warn(dc->dev, "failed to allocate syncpoint\n");
|
|
|
|
|
2019-10-28 20:37:08 +08:00
|
|
|
err = host1x_client_iommu_attach(client);
|
2019-12-04 00:19:12 +08:00
|
|
|
if (err < 0 && err != -ENODEV) {
|
2018-05-04 21:02:24 +08:00
|
|
|
dev_err(client->dev, "failed to attach to domain: %d\n", err);
|
|
|
|
return err;
|
2014-06-27 03:41:53 +08:00
|
|
|
}
|
|
|
|
|
2017-08-30 23:42:54 +08:00
|
|
|
if (dc->soc->wgrps)
|
|
|
|
primary = tegra_dc_add_shared_planes(drm, dc);
|
|
|
|
else
|
|
|
|
primary = tegra_dc_add_planes(drm, dc);
|
|
|
|
|
2014-10-21 19:51:53 +08:00
|
|
|
if (IS_ERR(primary)) {
|
|
|
|
err = PTR_ERR(primary);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dc->soc->supports_cursor) {
|
|
|
|
cursor = tegra_dc_cursor_plane_create(drm, dc);
|
|
|
|
if (IS_ERR(cursor)) {
|
|
|
|
err = PTR_ERR(cursor);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2018-03-15 09:00:25 +08:00
|
|
|
} else {
|
|
|
|
/* dedicate one overlay to mouse cursor */
|
|
|
|
cursor = tegra_dc_overlay_plane_create(drm, dc, 2, true);
|
|
|
|
if (IS_ERR(cursor)) {
|
|
|
|
err = PTR_ERR(cursor);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2014-10-21 19:51:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
err = drm_crtc_init_with_planes(drm, &dc->base, primary, cursor,
|
drm: Pass 'name' to drm_crtc_init_with_planes()
Done with coccinelle for the most part. However, it thinks '...' is
part of the semantic patch, so I put an 'int DOTDOTDOT' placeholder
in its place and got rid of it with sed afterwards.
I didn't convert drm_crtc_init() since passing the varargs through
would mean either cpp macros or va_list, and I figured we don't
care about these legacy functions enough to warrant the extra pain.
@@
identifier dev, crtc, primary, cursor, funcs;
@@
int drm_crtc_init_with_planes(struct drm_device *dev,
struct drm_crtc *crtc,
struct drm_plane *primary, struct drm_plane *cursor,
const struct drm_crtc_funcs *funcs
+ ,const char *name, int DOTDOTDOT
)
{ ... }
@@
identifier dev, crtc, primary, cursor, funcs;
@@
int drm_crtc_init_with_planes(struct drm_device *dev,
struct drm_crtc *crtc,
struct drm_plane *primary, struct drm_plane *cursor,
const struct drm_crtc_funcs *funcs
+ ,const char *name, int DOTDOTDOT
);
@@
expression E1, E2, E3, E4, E5;
@@
drm_crtc_init_with_planes(E1, E2, E3, E4, E5
+ ,NULL
)
v2: Split crtc and plane changes apart
Pass NULL for no-name instead of ""
Leave drm_crtc_init() alone
v3: Add ', or NULL...' to @name kernel doc (Jani)
Annotate the function with __printf() attribute (Jani)
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/1449670771-2751-1-git-send-email-ville.syrjala@linux.intel.com
2015-12-09 22:19:31 +08:00
|
|
|
&tegra_crtc_funcs, NULL);
|
2014-10-21 19:51:53 +08:00
|
|
|
if (err < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2012-11-16 05:28:22 +08:00
|
|
|
drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs);
|
|
|
|
|
2014-07-11 14:29:14 +08:00
|
|
|
/*
|
|
|
|
* Keep track of the minimum pitch alignment across all display
|
|
|
|
* controllers.
|
|
|
|
*/
|
|
|
|
if (dc->soc->pitch_align > tegra->pitch_align)
|
|
|
|
tegra->pitch_align = dc->soc->pitch_align;
|
|
|
|
|
2021-03-26 22:51:33 +08:00
|
|
|
/* track maximum resolution */
|
|
|
|
if (dc->soc->has_nvdisplay)
|
|
|
|
drm->mode_config.max_width = drm->mode_config.max_height = 16384;
|
|
|
|
else
|
|
|
|
drm->mode_config.max_width = drm->mode_config.max_height = 4096;
|
|
|
|
|
2014-05-22 15:57:15 +08:00
|
|
|
err = tegra_dc_rgb_init(drm, dc);
|
2012-11-16 05:28:22 +08:00
|
|
|
if (err < 0 && err != -ENODEV) {
|
|
|
|
dev_err(dc->dev, "failed to initialize RGB output: %d\n", err);
|
2014-10-21 19:51:53 +08:00
|
|
|
goto cleanup;
|
2012-11-16 05:28:22 +08:00
|
|
|
}
|
|
|
|
|
2012-11-28 18:45:47 +08:00
|
|
|
err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0,
|
2012-11-16 05:28:22 +08:00
|
|
|
dev_name(dc->dev), dc);
|
|
|
|
if (err < 0) {
|
|
|
|
dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq,
|
|
|
|
err);
|
2014-10-21 19:51:53 +08:00
|
|
|
goto cleanup;
|
2012-11-16 05:28:22 +08:00
|
|
|
}
|
|
|
|
|
2019-09-09 20:25:45 +08:00
|
|
|
/*
|
|
|
|
* Inherit the DMA parameters (such as maximum segment size) from the
|
2019-12-02 17:51:58 +08:00
|
|
|
* parent host1x device.
|
2019-09-09 20:25:45 +08:00
|
|
|
*/
|
2019-12-02 17:51:58 +08:00
|
|
|
client->dev->dma_parms = client->host->dma_parms;
|
2019-09-09 20:25:45 +08:00
|
|
|
|
2012-11-16 05:28:22 +08:00
|
|
|
return 0;
|
2014-10-21 19:51:53 +08:00
|
|
|
|
|
|
|
cleanup:
|
2017-08-30 23:42:54 +08:00
|
|
|
if (!IS_ERR_OR_NULL(cursor))
|
2014-10-21 19:51:53 +08:00
|
|
|
drm_plane_cleanup(cursor);
|
|
|
|
|
2017-08-30 23:42:54 +08:00
|
|
|
if (!IS_ERR(primary))
|
2014-10-21 19:51:53 +08:00
|
|
|
drm_plane_cleanup(primary);
|
|
|
|
|
2019-02-08 21:35:13 +08:00
|
|
|
host1x_client_iommu_detach(client);
|
2021-03-29 21:38:32 +08:00
|
|
|
host1x_syncpt_put(dc->syncpt);
|
2018-05-04 21:00:54 +08:00
|
|
|
|
2014-10-21 19:51:53 +08:00
|
|
|
return err;
|
2012-11-16 05:28:22 +08:00
|
|
|
}
|
|
|
|
|
2013-09-24 21:35:40 +08:00
|
|
|
static int tegra_dc_exit(struct host1x_client *client)
|
2012-11-16 05:28:22 +08:00
|
|
|
{
|
2013-10-14 20:43:22 +08:00
|
|
|
struct tegra_dc *dc = host1x_client_to_dc(client);
|
2012-11-16 05:28:22 +08:00
|
|
|
int err;
|
|
|
|
|
2018-11-30 00:24:35 +08:00
|
|
|
if (!tegra_dc_has_window_groups(dc))
|
|
|
|
return 0;
|
|
|
|
|
2019-09-09 20:25:45 +08:00
|
|
|
/* avoid a dangling pointer just in case this disappears */
|
|
|
|
client->dev->dma_parms = NULL;
|
|
|
|
|
2012-11-16 05:28:22 +08:00
|
|
|
devm_free_irq(dc->dev, dc->irq, dc);
|
|
|
|
|
|
|
|
err = tegra_dc_rgb_exit(dc);
|
|
|
|
if (err) {
|
|
|
|
dev_err(dc->dev, "failed to shutdown RGB output: %d\n", err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2019-02-08 21:35:13 +08:00
|
|
|
host1x_client_iommu_detach(client);
|
2021-03-29 21:38:32 +08:00
|
|
|
host1x_syncpt_put(dc->syncpt);
|
2015-08-24 20:47:10 +08:00
|
|
|
|
2012-11-16 05:28:22 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-03-26 22:51:38 +08:00
|
|
|
static int tegra_dc_late_exit(struct host1x_client *client)
|
|
|
|
{
|
|
|
|
struct drm_device *drm = dev_get_drvdata(client->host);
|
|
|
|
struct tegra_drm *tegra = drm->dev_private;
|
|
|
|
|
|
|
|
tegra->num_crtcs--;
|
2015-08-24 20:47:10 +08:00
|
|
|
|
2012-11-16 05:28:22 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-12-02 22:29:03 +08:00
|
|
|
static int tegra_dc_runtime_suspend(struct host1x_client *client)
|
|
|
|
{
|
|
|
|
struct tegra_dc *dc = host1x_client_to_dc(client);
|
|
|
|
struct device *dev = client->dev;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = reset_control_assert(dc->rst);
|
|
|
|
if (err < 0) {
|
|
|
|
dev_err(dev, "failed to assert reset: %d\n", err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dc->soc->has_powergate)
|
|
|
|
tegra_powergate_power_off(dc->powergate);
|
|
|
|
|
|
|
|
clk_disable_unprepare(dc->clk);
|
|
|
|
pm_runtime_put_sync(dev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tegra_dc_runtime_resume(struct host1x_client *client)
|
|
|
|
{
|
|
|
|
struct tegra_dc *dc = host1x_client_to_dc(client);
|
|
|
|
struct device *dev = client->dev;
|
|
|
|
int err;
|
|
|
|
|
2020-12-01 20:56:31 +08:00
|
|
|
err = pm_runtime_resume_and_get(dev);
|
2019-12-02 22:29:03 +08:00
|
|
|
if (err < 0) {
|
|
|
|
dev_err(dev, "failed to get runtime PM: %d\n", err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dc->soc->has_powergate) {
|
|
|
|
err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk,
|
|
|
|
dc->rst);
|
|
|
|
if (err < 0) {
|
|
|
|
dev_err(dev, "failed to power partition: %d\n", err);
|
|
|
|
goto put_rpm;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
err = clk_prepare_enable(dc->clk);
|
|
|
|
if (err < 0) {
|
|
|
|
dev_err(dev, "failed to enable clock: %d\n", err);
|
|
|
|
goto put_rpm;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = reset_control_deassert(dc->rst);
|
|
|
|
if (err < 0) {
|
|
|
|
dev_err(dev, "failed to deassert reset: %d\n", err);
|
|
|
|
goto disable_clk;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
disable_clk:
|
|
|
|
clk_disable_unprepare(dc->clk);
|
|
|
|
put_rpm:
|
|
|
|
pm_runtime_put_sync(dev);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2012-11-16 05:28:22 +08:00
|
|
|
static const struct host1x_client_ops dc_client_ops = {
|
2021-03-26 22:51:38 +08:00
|
|
|
.early_init = tegra_dc_early_init,
|
2013-09-24 21:35:40 +08:00
|
|
|
.init = tegra_dc_init,
|
|
|
|
.exit = tegra_dc_exit,
|
2021-03-26 22:51:38 +08:00
|
|
|
.late_exit = tegra_dc_late_exit,
|
2019-12-02 22:29:03 +08:00
|
|
|
.suspend = tegra_dc_runtime_suspend,
|
|
|
|
.resume = tegra_dc_runtime_resume,
|
2012-11-16 05:28:22 +08:00
|
|
|
};
|
|
|
|
|
2013-12-12 18:03:59 +08:00
|
|
|
static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
|
2017-11-13 18:20:48 +08:00
|
|
|
.supports_background_color = false,
|
2013-12-12 18:03:59 +08:00
|
|
|
.supports_interlacing = false,
|
2013-12-20 20:58:33 +08:00
|
|
|
.supports_cursor = false,
|
2014-06-03 20:48:12 +08:00
|
|
|
.supports_block_linear = false,
|
2021-03-26 22:51:39 +08:00
|
|
|
.supports_sector_layout = false,
|
2018-05-04 22:40:00 +08:00
|
|
|
.has_legacy_blending = true,
|
2014-07-11 14:29:14 +08:00
|
|
|
.pitch_align = 8,
|
2014-07-07 21:32:53 +08:00
|
|
|
.has_powergate = false,
|
2017-12-20 23:46:10 +08:00
|
|
|
.coupled_pm = true,
|
2017-08-30 23:42:54 +08:00
|
|
|
.has_nvdisplay = false,
|
2017-11-14 23:07:40 +08:00
|
|
|
.num_primary_formats = ARRAY_SIZE(tegra20_primary_formats),
|
|
|
|
.primary_formats = tegra20_primary_formats,
|
|
|
|
.num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats),
|
|
|
|
.overlay_formats = tegra20_overlay_formats,
|
2018-03-15 23:44:04 +08:00
|
|
|
.modifiers = tegra20_modifiers,
|
2018-05-04 22:39:58 +08:00
|
|
|
.has_win_a_without_filters = true,
|
|
|
|
.has_win_c_without_vert_filter = true,
|
2013-12-12 18:03:59 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
|
2017-11-13 18:20:48 +08:00
|
|
|
.supports_background_color = false,
|
2013-12-12 18:03:59 +08:00
|
|
|
.supports_interlacing = false,
|
2013-12-20 20:58:33 +08:00
|
|
|
.supports_cursor = false,
|
2014-06-03 20:48:12 +08:00
|
|
|
.supports_block_linear = false,
|
2021-03-26 22:51:39 +08:00
|
|
|
.supports_sector_layout = false,
|
2018-05-04 22:40:00 +08:00
|
|
|
.has_legacy_blending = true,
|
2014-07-11 14:29:14 +08:00
|
|
|
.pitch_align = 8,
|
2014-07-07 21:32:53 +08:00
|
|
|
.has_powergate = false,
|
2017-12-20 23:46:10 +08:00
|
|
|
.coupled_pm = false,
|
2017-08-30 23:42:54 +08:00
|
|
|
.has_nvdisplay = false,
|
2017-11-14 23:07:40 +08:00
|
|
|
.num_primary_formats = ARRAY_SIZE(tegra20_primary_formats),
|
|
|
|
.primary_formats = tegra20_primary_formats,
|
|
|
|
.num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats),
|
|
|
|
.overlay_formats = tegra20_overlay_formats,
|
2018-03-15 23:44:04 +08:00
|
|
|
.modifiers = tegra20_modifiers,
|
2018-05-04 22:39:58 +08:00
|
|
|
.has_win_a_without_filters = false,
|
|
|
|
.has_win_c_without_vert_filter = false,
|
2014-07-11 14:29:14 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
|
2017-11-13 18:20:48 +08:00
|
|
|
.supports_background_color = false,
|
2014-07-11 14:29:14 +08:00
|
|
|
.supports_interlacing = false,
|
|
|
|
.supports_cursor = false,
|
|
|
|
.supports_block_linear = false,
|
2021-03-26 22:51:39 +08:00
|
|
|
.supports_sector_layout = false,
|
2018-05-04 22:40:00 +08:00
|
|
|
.has_legacy_blending = true,
|
2014-07-11 14:29:14 +08:00
|
|
|
.pitch_align = 64,
|
2014-07-07 21:32:53 +08:00
|
|
|
.has_powergate = true,
|
2017-12-20 23:46:10 +08:00
|
|
|
.coupled_pm = false,
|
2017-08-30 23:42:54 +08:00
|
|
|
.has_nvdisplay = false,
|
2017-11-14 23:07:40 +08:00
|
|
|
.num_primary_formats = ARRAY_SIZE(tegra114_primary_formats),
|
|
|
|
.primary_formats = tegra114_primary_formats,
|
|
|
|
.num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats),
|
|
|
|
.overlay_formats = tegra114_overlay_formats,
|
2018-03-15 23:44:04 +08:00
|
|
|
.modifiers = tegra20_modifiers,
|
2018-05-04 22:39:58 +08:00
|
|
|
.has_win_a_without_filters = false,
|
|
|
|
.has_win_c_without_vert_filter = false,
|
2013-12-12 18:03:59 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
|
2017-11-13 18:20:48 +08:00
|
|
|
.supports_background_color = true,
|
2013-12-12 18:03:59 +08:00
|
|
|
.supports_interlacing = true,
|
2013-12-20 20:58:33 +08:00
|
|
|
.supports_cursor = true,
|
2014-06-03 20:48:12 +08:00
|
|
|
.supports_block_linear = true,
|
2021-03-26 22:51:39 +08:00
|
|
|
.supports_sector_layout = false,
|
2018-05-04 22:40:00 +08:00
|
|
|
.has_legacy_blending = false,
|
2014-07-11 14:29:14 +08:00
|
|
|
.pitch_align = 64,
|
2014-07-07 21:32:53 +08:00
|
|
|
.has_powergate = true,
|
2017-12-20 23:46:10 +08:00
|
|
|
.coupled_pm = false,
|
2017-08-30 23:42:54 +08:00
|
|
|
.has_nvdisplay = false,
|
2017-11-14 23:07:40 +08:00
|
|
|
.num_primary_formats = ARRAY_SIZE(tegra124_primary_formats),
|
2018-03-20 17:11:44 +08:00
|
|
|
.primary_formats = tegra124_primary_formats,
|
2017-11-14 23:07:40 +08:00
|
|
|
.num_overlay_formats = ARRAY_SIZE(tegra124_overlay_formats),
|
2018-03-20 17:11:44 +08:00
|
|
|
.overlay_formats = tegra124_overlay_formats,
|
2018-03-15 23:44:04 +08:00
|
|
|
.modifiers = tegra124_modifiers,
|
2018-05-04 22:39:58 +08:00
|
|
|
.has_win_a_without_filters = false,
|
|
|
|
.has_win_c_without_vert_filter = false,
|
2013-12-12 18:03:59 +08:00
|
|
|
};
|
|
|
|
|
2015-03-27 17:31:58 +08:00
|
|
|
static const struct tegra_dc_soc_info tegra210_dc_soc_info = {
|
2017-11-13 18:20:48 +08:00
|
|
|
.supports_background_color = true,
|
2015-03-27 17:31:58 +08:00
|
|
|
.supports_interlacing = true,
|
|
|
|
.supports_cursor = true,
|
|
|
|
.supports_block_linear = true,
|
2021-03-26 22:51:39 +08:00
|
|
|
.supports_sector_layout = false,
|
2018-05-04 22:40:00 +08:00
|
|
|
.has_legacy_blending = false,
|
2015-03-27 17:31:58 +08:00
|
|
|
.pitch_align = 64,
|
|
|
|
.has_powergate = true,
|
2017-12-20 23:46:10 +08:00
|
|
|
.coupled_pm = false,
|
2017-08-30 23:42:54 +08:00
|
|
|
.has_nvdisplay = false,
|
2017-11-14 23:07:40 +08:00
|
|
|
.num_primary_formats = ARRAY_SIZE(tegra114_primary_formats),
|
|
|
|
.primary_formats = tegra114_primary_formats,
|
|
|
|
.num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats),
|
|
|
|
.overlay_formats = tegra114_overlay_formats,
|
2018-03-15 23:44:04 +08:00
|
|
|
.modifiers = tegra124_modifiers,
|
2018-05-04 22:39:58 +08:00
|
|
|
.has_win_a_without_filters = false,
|
|
|
|
.has_win_c_without_vert_filter = false,
|
2017-08-30 23:42:54 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = {
|
|
|
|
{
|
|
|
|
.index = 0,
|
|
|
|
.dc = 0,
|
|
|
|
.windows = (const unsigned int[]) { 0 },
|
|
|
|
.num_windows = 1,
|
|
|
|
}, {
|
|
|
|
.index = 1,
|
|
|
|
.dc = 1,
|
|
|
|
.windows = (const unsigned int[]) { 1 },
|
|
|
|
.num_windows = 1,
|
|
|
|
}, {
|
|
|
|
.index = 2,
|
|
|
|
.dc = 1,
|
|
|
|
.windows = (const unsigned int[]) { 2 },
|
|
|
|
.num_windows = 1,
|
|
|
|
}, {
|
|
|
|
.index = 3,
|
|
|
|
.dc = 2,
|
|
|
|
.windows = (const unsigned int[]) { 3 },
|
|
|
|
.num_windows = 1,
|
|
|
|
}, {
|
|
|
|
.index = 4,
|
|
|
|
.dc = 2,
|
|
|
|
.windows = (const unsigned int[]) { 4 },
|
|
|
|
.num_windows = 1,
|
|
|
|
}, {
|
|
|
|
.index = 5,
|
|
|
|
.dc = 2,
|
|
|
|
.windows = (const unsigned int[]) { 5 },
|
|
|
|
.num_windows = 1,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct tegra_dc_soc_info tegra186_dc_soc_info = {
|
|
|
|
.supports_background_color = true,
|
|
|
|
.supports_interlacing = true,
|
|
|
|
.supports_cursor = true,
|
|
|
|
.supports_block_linear = true,
|
2021-03-26 22:51:39 +08:00
|
|
|
.supports_sector_layout = false,
|
2018-05-04 22:40:00 +08:00
|
|
|
.has_legacy_blending = false,
|
2017-08-30 23:42:54 +08:00
|
|
|
.pitch_align = 64,
|
|
|
|
.has_powergate = false,
|
2017-12-20 23:46:10 +08:00
|
|
|
.coupled_pm = false,
|
2017-08-30 23:42:54 +08:00
|
|
|
.has_nvdisplay = true,
|
|
|
|
.wgrps = tegra186_dc_wgrps,
|
|
|
|
.num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps),
|
2015-03-27 17:31:58 +08:00
|
|
|
};
|
|
|
|
|
2018-09-21 18:27:44 +08:00
|
|
|
static const struct tegra_windowgroup_soc tegra194_dc_wgrps[] = {
|
|
|
|
{
|
|
|
|
.index = 0,
|
|
|
|
.dc = 0,
|
|
|
|
.windows = (const unsigned int[]) { 0 },
|
|
|
|
.num_windows = 1,
|
|
|
|
}, {
|
|
|
|
.index = 1,
|
|
|
|
.dc = 1,
|
|
|
|
.windows = (const unsigned int[]) { 1 },
|
|
|
|
.num_windows = 1,
|
|
|
|
}, {
|
|
|
|
.index = 2,
|
|
|
|
.dc = 1,
|
|
|
|
.windows = (const unsigned int[]) { 2 },
|
|
|
|
.num_windows = 1,
|
|
|
|
}, {
|
|
|
|
.index = 3,
|
|
|
|
.dc = 2,
|
|
|
|
.windows = (const unsigned int[]) { 3 },
|
|
|
|
.num_windows = 1,
|
|
|
|
}, {
|
|
|
|
.index = 4,
|
|
|
|
.dc = 2,
|
|
|
|
.windows = (const unsigned int[]) { 4 },
|
|
|
|
.num_windows = 1,
|
|
|
|
}, {
|
|
|
|
.index = 5,
|
|
|
|
.dc = 2,
|
|
|
|
.windows = (const unsigned int[]) { 5 },
|
|
|
|
.num_windows = 1,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct tegra_dc_soc_info tegra194_dc_soc_info = {
|
|
|
|
.supports_background_color = true,
|
|
|
|
.supports_interlacing = true,
|
|
|
|
.supports_cursor = true,
|
|
|
|
.supports_block_linear = true,
|
2021-03-26 22:51:39 +08:00
|
|
|
.supports_sector_layout = true,
|
2018-09-21 18:27:44 +08:00
|
|
|
.has_legacy_blending = false,
|
|
|
|
.pitch_align = 64,
|
|
|
|
.has_powergate = false,
|
|
|
|
.coupled_pm = false,
|
|
|
|
.has_nvdisplay = true,
|
|
|
|
.wgrps = tegra194_dc_wgrps,
|
|
|
|
.num_wgrps = ARRAY_SIZE(tegra194_dc_wgrps),
|
|
|
|
};
|
|
|
|
|
2013-12-12 18:03:59 +08:00
|
|
|
static const struct of_device_id tegra_dc_of_match[] = {
|
|
|
|
{
|
2018-09-21 18:27:44 +08:00
|
|
|
.compatible = "nvidia,tegra194-dc",
|
|
|
|
.data = &tegra194_dc_soc_info,
|
|
|
|
}, {
|
2017-08-30 23:42:54 +08:00
|
|
|
.compatible = "nvidia,tegra186-dc",
|
|
|
|
.data = &tegra186_dc_soc_info,
|
|
|
|
}, {
|
2015-03-27 17:31:58 +08:00
|
|
|
.compatible = "nvidia,tegra210-dc",
|
|
|
|
.data = &tegra210_dc_soc_info,
|
|
|
|
}, {
|
2013-12-12 18:03:59 +08:00
|
|
|
.compatible = "nvidia,tegra124-dc",
|
|
|
|
.data = &tegra124_dc_soc_info,
|
2014-07-07 21:32:53 +08:00
|
|
|
}, {
|
|
|
|
.compatible = "nvidia,tegra114-dc",
|
|
|
|
.data = &tegra114_dc_soc_info,
|
2013-12-12 18:03:59 +08:00
|
|
|
}, {
|
|
|
|
.compatible = "nvidia,tegra30-dc",
|
|
|
|
.data = &tegra30_dc_soc_info,
|
|
|
|
}, {
|
|
|
|
.compatible = "nvidia,tegra20-dc",
|
|
|
|
.data = &tegra20_dc_soc_info,
|
|
|
|
}, {
|
|
|
|
/* sentinel */
|
|
|
|
}
|
|
|
|
};
|
2014-06-19 06:21:55 +08:00
|
|
|
MODULE_DEVICE_TABLE(of, tegra_dc_of_match);
|
2013-12-12 18:03:59 +08:00
|
|
|
|
2014-01-10 00:08:36 +08:00
|
|
|
static int tegra_dc_parse_dt(struct tegra_dc *dc)
|
|
|
|
{
|
|
|
|
struct device_node *np;
|
|
|
|
u32 value = 0;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = of_property_read_u32(dc->dev->of_node, "nvidia,head", &value);
|
|
|
|
if (err < 0) {
|
|
|
|
dev_err(dc->dev, "missing \"nvidia,head\" property\n");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the nvidia,head property isn't present, try to find the
|
|
|
|
* correct head number by looking up the position of this
|
|
|
|
* display controller's node within the device tree. Assuming
|
|
|
|
* that the nodes are ordered properly in the DTS file and
|
|
|
|
* that the translation into a flattened device tree blob
|
|
|
|
* preserves that ordering this will actually yield the right
|
|
|
|
* head number.
|
|
|
|
*
|
|
|
|
* If those assumptions don't hold, this will still work for
|
|
|
|
* cases where only a single display controller is used.
|
|
|
|
*/
|
|
|
|
for_each_matching_node(np, tegra_dc_of_match) {
|
2015-10-24 22:42:31 +08:00
|
|
|
if (np == dc->dev->of_node) {
|
|
|
|
of_node_put(np);
|
2014-01-10 00:08:36 +08:00
|
|
|
break;
|
2015-10-24 22:42:31 +08:00
|
|
|
}
|
2014-01-10 00:08:36 +08:00
|
|
|
|
|
|
|
value++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dc->pipe = value;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-06-15 01:54:00 +08:00
|
|
|
static int tegra_dc_match_by_pipe(struct device *dev, const void *data)
|
2017-12-20 23:46:10 +08:00
|
|
|
{
|
|
|
|
struct tegra_dc *dc = dev_get_drvdata(dev);
|
2019-06-15 01:54:00 +08:00
|
|
|
unsigned int pipe = (unsigned long)(void *)data;
|
2017-12-20 23:46:10 +08:00
|
|
|
|
|
|
|
return dc->pipe == pipe;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tegra_dc_couple(struct tegra_dc *dc)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* On Tegra20, DC1 requires DC0 to be taken out of reset in order to
|
|
|
|
* be enabled, otherwise CPU hangs on writing to CMD_DISPLAY_COMMAND /
|
|
|
|
* POWER_CONTROL registers during CRTC enabling.
|
|
|
|
*/
|
|
|
|
if (dc->soc->coupled_pm && dc->pipe == 1) {
|
2021-03-19 15:06:37 +08:00
|
|
|
struct device *companion;
|
|
|
|
struct tegra_dc *parent;
|
2017-12-20 23:46:10 +08:00
|
|
|
|
2021-03-19 15:06:37 +08:00
|
|
|
companion = driver_find_device(dc->dev->driver, NULL, (const void *)0,
|
|
|
|
tegra_dc_match_by_pipe);
|
|
|
|
if (!companion)
|
2017-12-20 23:46:10 +08:00
|
|
|
return -EPROBE_DEFER;
|
|
|
|
|
2021-03-19 15:06:37 +08:00
|
|
|
parent = dev_get_drvdata(companion);
|
|
|
|
dc->client.parent = &parent->client;
|
2017-12-20 23:46:10 +08:00
|
|
|
|
2021-03-19 15:06:37 +08:00
|
|
|
dev_dbg(dc->dev, "coupled to %s\n", dev_name(companion));
|
2017-12-20 23:46:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-16 05:28:22 +08:00
|
|
|
static int tegra_dc_probe(struct platform_device *pdev)
|
|
|
|
{
|
2021-03-26 22:51:32 +08:00
|
|
|
u64 dma_mask = dma_get_mask(pdev->dev.parent);
|
2012-11-16 05:28:22 +08:00
|
|
|
struct tegra_dc *dc;
|
|
|
|
int err;
|
|
|
|
|
2021-03-26 22:51:32 +08:00
|
|
|
err = dma_coerce_mask_and_coherent(&pdev->dev, dma_mask);
|
|
|
|
if (err < 0) {
|
|
|
|
dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2012-11-16 05:28:22 +08:00
|
|
|
dc = devm_kzalloc(&pdev->dev, sizeof(*dc), GFP_KERNEL);
|
|
|
|
if (!dc)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2017-08-21 22:35:17 +08:00
|
|
|
dc->soc = of_device_get_match_data(&pdev->dev);
|
2013-12-12 18:03:59 +08:00
|
|
|
|
2012-11-16 05:28:22 +08:00
|
|
|
INIT_LIST_HEAD(&dc->list);
|
|
|
|
dc->dev = &pdev->dev;
|
|
|
|
|
2014-01-10 00:08:36 +08:00
|
|
|
err = tegra_dc_parse_dt(dc);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
2017-12-20 23:46:10 +08:00
|
|
|
err = tegra_dc_couple(dc);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
2012-11-16 05:28:22 +08:00
|
|
|
dc->clk = devm_clk_get(&pdev->dev, NULL);
|
|
|
|
if (IS_ERR(dc->clk)) {
|
|
|
|
dev_err(&pdev->dev, "failed to get clock\n");
|
|
|
|
return PTR_ERR(dc->clk);
|
|
|
|
}
|
|
|
|
|
2013-11-07 07:20:54 +08:00
|
|
|
dc->rst = devm_reset_control_get(&pdev->dev, "dc");
|
|
|
|
if (IS_ERR(dc->rst)) {
|
|
|
|
dev_err(&pdev->dev, "failed to get reset\n");
|
|
|
|
return PTR_ERR(dc->rst);
|
|
|
|
}
|
|
|
|
|
2017-08-30 23:41:00 +08:00
|
|
|
/* assert reset and disable clock */
|
2017-12-20 23:46:10 +08:00
|
|
|
err = clk_prepare_enable(dc->clk);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2017-08-30 23:41:00 +08:00
|
|
|
|
2017-12-20 23:46:10 +08:00
|
|
|
usleep_range(2000, 4000);
|
2017-08-30 23:41:00 +08:00
|
|
|
|
2017-12-20 23:46:10 +08:00
|
|
|
err = reset_control_assert(dc->rst);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2017-08-30 23:41:00 +08:00
|
|
|
|
2017-12-20 23:46:10 +08:00
|
|
|
usleep_range(2000, 4000);
|
2017-08-30 23:41:00 +08:00
|
|
|
|
2017-12-20 23:46:10 +08:00
|
|
|
clk_disable_unprepare(dc->clk);
|
2015-08-03 19:20:49 +08:00
|
|
|
|
2014-07-07 21:32:53 +08:00
|
|
|
if (dc->soc->has_powergate) {
|
|
|
|
if (dc->pipe == 0)
|
|
|
|
dc->powergate = TEGRA_POWERGATE_DIS;
|
|
|
|
else
|
|
|
|
dc->powergate = TEGRA_POWERGATE_DISB;
|
|
|
|
|
2015-08-03 19:20:49 +08:00
|
|
|
tegra_powergate_power_off(dc->powergate);
|
2014-07-07 21:32:53 +08:00
|
|
|
}
|
2012-11-16 05:28:22 +08:00
|
|
|
|
2020-03-09 06:38:07 +08:00
|
|
|
dc->regs = devm_platform_ioremap_resource(pdev, 0);
|
2013-01-21 18:09:02 +08:00
|
|
|
if (IS_ERR(dc->regs))
|
|
|
|
return PTR_ERR(dc->regs);
|
2012-11-16 05:28:22 +08:00
|
|
|
|
|
|
|
dc->irq = platform_get_irq(pdev, 0);
|
2020-04-16 22:43:17 +08:00
|
|
|
if (dc->irq < 0)
|
2012-11-16 05:28:22 +08:00
|
|
|
return -ENXIO;
|
|
|
|
|
|
|
|
err = tegra_dc_rgb_probe(dc);
|
|
|
|
if (err < 0 && err != -ENODEV) {
|
2020-03-09 06:38:09 +08:00
|
|
|
const char *level = KERN_ERR;
|
|
|
|
|
|
|
|
if (err == -EPROBE_DEFER)
|
|
|
|
level = KERN_DEBUG;
|
|
|
|
|
|
|
|
dev_printk(level, dc->dev, "failed to probe RGB output: %d\n",
|
|
|
|
err);
|
2012-11-16 05:28:22 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2015-08-03 19:20:49 +08:00
|
|
|
platform_set_drvdata(pdev, dc);
|
|
|
|
pm_runtime_enable(&pdev->dev);
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&dc->client.list);
|
|
|
|
dc->client.ops = &dc_client_ops;
|
|
|
|
dc->client.dev = &pdev->dev;
|
|
|
|
|
2013-10-14 20:43:22 +08:00
|
|
|
err = host1x_client_register(&dc->client);
|
2012-11-16 05:28:22 +08:00
|
|
|
if (err < 0) {
|
|
|
|
dev_err(&pdev->dev, "failed to register host1x client: %d\n",
|
|
|
|
err);
|
2020-03-09 06:38:08 +08:00
|
|
|
goto disable_pm;
|
2012-11-16 05:28:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2020-03-09 06:38:08 +08:00
|
|
|
|
|
|
|
disable_pm:
|
|
|
|
pm_runtime_disable(&pdev->dev);
|
|
|
|
tegra_dc_rgb_remove(dc);
|
|
|
|
|
|
|
|
return err;
|
2012-11-16 05:28:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int tegra_dc_remove(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct tegra_dc *dc = platform_get_drvdata(pdev);
|
|
|
|
int err;
|
|
|
|
|
2013-10-14 20:43:22 +08:00
|
|
|
err = host1x_client_unregister(&dc->client);
|
2012-11-16 05:28:22 +08:00
|
|
|
if (err < 0) {
|
|
|
|
dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
|
|
|
|
err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-10-14 20:26:42 +08:00
|
|
|
err = tegra_dc_rgb_remove(dc);
|
|
|
|
if (err < 0) {
|
|
|
|
dev_err(&pdev->dev, "failed to remove RGB output: %d\n", err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2015-08-03 19:20:49 +08:00
|
|
|
pm_runtime_disable(&pdev->dev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-16 05:28:22 +08:00
|
|
|
struct platform_driver tegra_dc_driver = {
|
|
|
|
.driver = {
|
|
|
|
.name = "tegra-dc",
|
|
|
|
.of_match_table = tegra_dc_of_match,
|
|
|
|
},
|
|
|
|
.probe = tegra_dc_probe,
|
|
|
|
.remove = tegra_dc_remove,
|
|
|
|
};
|