summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_wb.c
blob: 63dc2ee446d47bce57429bc317d52ea3edf8b65e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
// SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved
  */

#include "dpu_hw_mdss.h"
#include "dpu_hwio.h"
#include "dpu_hw_catalog.h"
#include "dpu_hw_wb.h"
#include "dpu_formats.h"
#include "dpu_kms.h"

#define WB_DST_FORMAT                         0x000
#define WB_DST_OP_MODE                        0x004
#define WB_DST_PACK_PATTERN                   0x008
#define WB_DST0_ADDR                          0x00C
#define WB_DST1_ADDR                          0x010
#define WB_DST2_ADDR                          0x014
#define WB_DST3_ADDR                          0x018
#define WB_DST_YSTRIDE0                       0x01C
#define WB_DST_YSTRIDE1                       0x020
#define WB_DST_YSTRIDE1                       0x020
#define WB_DST_DITHER_BITDEPTH                0x024
#define WB_DST_MATRIX_ROW0                    0x030
#define WB_DST_MATRIX_ROW1                    0x034
#define WB_DST_MATRIX_ROW2                    0x038
#define WB_DST_MATRIX_ROW3                    0x03C
#define WB_DST_WRITE_CONFIG                   0x048
#define WB_ROTATION_DNSCALER                  0x050
#define WB_ROTATOR_PIPE_DOWNSCALER            0x054
#define WB_N16_INIT_PHASE_X_C03               0x060
#define WB_N16_INIT_PHASE_X_C12               0x064
#define WB_N16_INIT_PHASE_Y_C03               0x068
#define WB_N16_INIT_PHASE_Y_C12               0x06C
#define WB_OUT_SIZE                           0x074
#define WB_ALPHA_X_VALUE                      0x078
#define WB_DANGER_LUT                         0x084
#define WB_SAFE_LUT                           0x088
#define WB_QOS_CTRL                           0x090
#define WB_CREQ_LUT_0                         0x098
#define WB_CREQ_LUT_1                         0x09C
#define WB_UBWC_STATIC_CTRL                   0x144
#define WB_MUX                                0x150
#define WB_CROP_CTRL                          0x154
#define WB_CROP_OFFSET                        0x158
#define WB_CSC_BASE                           0x260
#define WB_DST_ADDR_SW_STATUS                 0x2B0
#define WB_CDP_CNTL                           0x2B4
#define WB_OUT_IMAGE_SIZE                     0x2C0
#define WB_OUT_XY                             0x2C4

/* WB_QOS_CTRL */
#define WB_QOS_CTRL_DANGER_SAFE_EN            BIT(0)

static const struct dpu_wb_cfg *_wb_offset(enum dpu_wb wb,
		const struct dpu_mdss_cfg *m, void __iomem *addr,
		struct dpu_hw_blk_reg_map *b)
{
	int i;

	for (i = 0; i < m->wb_count; i++) {
		if (wb == m->wb[i].id) {
			b->blk_addr = addr + m->wb[i].base;
			b->log_mask = DPU_DBG_MASK_WB;
			return &m->wb[i];
		}
	}
	return ERR_PTR(-EINVAL);
}

static void dpu_hw_wb_setup_outaddress(struct dpu_hw_wb *ctx,
		struct dpu_hw_wb_cfg *data)
{
	struct dpu_hw_blk_reg_map *c = &ctx->hw;

	DPU_REG_WRITE(c, WB_DST0_ADDR, data->dest.plane_addr[0]);
	DPU_REG_WRITE(c, WB_DST1_ADDR, data->dest.plane_addr[1]);
	DPU_REG_WRITE(c, WB_DST2_ADDR, data->dest.plane_addr[2]);
	DPU_REG_WRITE(c, WB_DST3_ADDR, data->dest.plane_addr[3]);
}

static void dpu_hw_wb_setup_format(struct dpu_hw_wb *ctx,
		struct dpu_hw_wb_cfg *data)
{
	struct dpu_hw_blk_reg_map *c = &ctx->hw;
	const struct dpu_format *fmt = data->dest.format;
	u32 dst_format, pattern, ystride0, ystride1, outsize, chroma_samp;
	u32 write_config = 0;
	u32 opmode = 0;
	u32 dst_addr_sw = 0;

	chroma_samp = fmt->chroma_sample;

	dst_format = (chroma_samp << 23) |
		(fmt->fetch_planes << 19) |
		(fmt->bits[C3_ALPHA] << 6) |
		(fmt->bits[C2_R_Cr] << 4) |
		(fmt->bits[C1_B_Cb] << 2) |
		(fmt->bits[C0_G_Y] << 0);

	if (fmt->bits[C3_ALPHA] || fmt->alpha_enable) {
		dst_format |= BIT(8); /* DSTC3_EN */
		if (!fmt->alpha_enable ||
			!(ctx->caps->features & BIT(DPU_WB_PIPE_ALPHA)))
			dst_format |= BIT(14); /* DST_ALPHA_X */
	}

	if (DPU_FORMAT_IS_YUV(fmt))
		dst_format |= BIT(15);

	pattern = (fmt->element[3] << 24) |
		(fmt->element[2] << 16) |
		(fmt->element[1] << 8)  |
		(fmt->element[0] << 0);

	dst_format |= (fmt->unpack_align_msb << 18) |
		(fmt->unpack_tight << 17) |
		((fmt->unpack_count - 1) << 12) |
		((fmt->bpp - 1) << 9);

	ystride0 = data->dest.plane_pitch[0] |
		(data->dest.plane_pitch[1] << 16);
	ystride1 = data->dest.plane_pitch[2] |
	(data->dest.plane_pitch[3] << 16);

	if (drm_rect_height(&data->roi) && drm_rect_width(&data->roi))
		outsize = (drm_rect_height(&data->roi) << 16) | drm_rect_width(&data->roi);
	else
		outsize = (data->dest.height << 16) | data->dest.width;

	DPU_REG_WRITE(c, WB_ALPHA_X_VALUE, 0xFF);
	DPU_REG_WRITE(c, WB_DST_FORMAT, dst_format);
	DPU_REG_WRITE(c, WB_DST_OP_MODE, opmode);
	DPU_REG_WRITE(c, WB_DST_PACK_PATTERN, pattern);
	DPU_REG_WRITE(c, WB_DST_YSTRIDE0, ystride0);
	DPU_REG_WRITE(c, WB_DST_YSTRIDE1, ystride1);
	DPU_REG_WRITE(c, WB_OUT_SIZE, outsize);
	DPU_REG_WRITE(c, WB_DST_WRITE_CONFIG, write_config);
	DPU_REG_WRITE(c, WB_DST_ADDR_SW_STATUS, dst_addr_sw);
}

static void dpu_hw_wb_roi(struct dpu_hw_wb *ctx, struct dpu_hw_wb_cfg *wb)
{
	struct dpu_hw_blk_reg_map *c = &ctx->hw;
	u32 image_size, out_size, out_xy;

	image_size = (wb->dest.height << 16) | wb->dest.width;
	out_xy = 0;
	out_size = (drm_rect_height(&wb->roi) << 16) | drm_rect_width(&wb->roi);

	DPU_REG_WRITE(c, WB_OUT_IMAGE_SIZE, image_size);
	DPU_REG_WRITE(c, WB_OUT_XY, out_xy);
	DPU_REG_WRITE(c, WB_OUT_SIZE, out_size);
}

static void dpu_hw_wb_setup_qos_lut(struct dpu_hw_wb *ctx,
		struct dpu_hw_wb_qos_cfg *cfg)
{
	struct dpu_hw_blk_reg_map *c = &ctx->hw;
	u32 qos_ctrl = 0;

	if (!ctx || !cfg)
		return;

	DPU_REG_WRITE(c, WB_DANGER_LUT, cfg->danger_lut);
	DPU_REG_WRITE(c, WB_SAFE_LUT, cfg->safe_lut);

	/*
	 * for chipsets not using DPU_WB_QOS_8LVL but still using DPU
	 * driver such as msm8998, the reset value of WB_CREQ_LUT is
	 * sufficient for writeback to work. SW doesn't need to explicitly
	 * program a value.
	 */
	if (ctx->caps && test_bit(DPU_WB_QOS_8LVL, &ctx->caps->features)) {
		DPU_REG_WRITE(c, WB_CREQ_LUT_0, cfg->creq_lut);
		DPU_REG_WRITE(c, WB_CREQ_LUT_1, cfg->creq_lut >> 32);
	}

	if (cfg->danger_safe_en)
		qos_ctrl |= WB_QOS_CTRL_DANGER_SAFE_EN;

	DPU_REG_WRITE(c, WB_QOS_CTRL, qos_ctrl);
}

static void dpu_hw_wb_setup_cdp(struct dpu_hw_wb *ctx,
		struct dpu_hw_cdp_cfg *cfg)
{
	struct dpu_hw_blk_reg_map *c;
	u32 cdp_cntl = 0;

	if (!ctx || !cfg)
		return;

	c = &ctx->hw;

	if (cfg->enable)
		cdp_cntl |= BIT(0);
	if (cfg->ubwc_meta_enable)
		cdp_cntl |= BIT(1);
	if (cfg->preload_ahead == DPU_WB_CDP_PRELOAD_AHEAD_64)
		cdp_cntl |= BIT(3);

	DPU_REG_WRITE(c, WB_CDP_CNTL, cdp_cntl);
}

static void dpu_hw_wb_bind_pingpong_blk(
		struct dpu_hw_wb *ctx,
		bool enable, const enum dpu_pingpong pp)
{
	struct dpu_hw_blk_reg_map *c;
	int mux_cfg;

	if (!ctx)
		return;

	c = &ctx->hw;

	mux_cfg = DPU_REG_READ(c, WB_MUX);
	mux_cfg &= ~0xf;

	if (enable)
		mux_cfg |= (pp - PINGPONG_0) & 0x7;
	else
		mux_cfg |= 0xf;

	DPU_REG_WRITE(c, WB_MUX, mux_cfg);
}

static void _setup_wb_ops(struct dpu_hw_wb_ops *ops,
		unsigned long features)
{
	ops->setup_outaddress = dpu_hw_wb_setup_outaddress;
	ops->setup_outformat = dpu_hw_wb_setup_format;

	if (test_bit(DPU_WB_XY_ROI_OFFSET, &features))
		ops->setup_roi = dpu_hw_wb_roi;

	if (test_bit(DPU_WB_QOS, &features))
		ops->setup_qos_lut = dpu_hw_wb_setup_qos_lut;

	if (test_bit(DPU_WB_CDP, &features))
		ops->setup_cdp = dpu_hw_wb_setup_cdp;

	if (test_bit(DPU_WB_INPUT_CTRL, &features))
		ops->bind_pingpong_blk = dpu_hw_wb_bind_pingpong_blk;
}

struct dpu_hw_wb *dpu_hw_wb_init(enum dpu_wb idx,
		void __iomem *addr, const struct dpu_mdss_cfg *m)
{
	struct dpu_hw_wb *c;
	const struct dpu_wb_cfg *cfg;

	if (!addr || !m)
		return ERR_PTR(-EINVAL);

	c = kzalloc(sizeof(*c), GFP_KERNEL);
	if (!c)
		return ERR_PTR(-ENOMEM);

	cfg = _wb_offset(idx, m, addr, &c->hw);
	if (IS_ERR(cfg)) {
		WARN(1, "Unable to find wb idx=%d\n", idx);
		kfree(c);
		return ERR_PTR(-EINVAL);
	}

	/* Assign ops */
	c->mdp = &m->mdp[0];
	c->idx = idx;
	c->caps = cfg;
	_setup_wb_ops(&c->ops, c->caps->features);

	return c;
}

void dpu_hw_wb_destroy(struct dpu_hw_wb *hw_wb)
{
	kfree(hw_wb);
}