summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc_1_2.c
blob: 24fe1d98eb86aed2a922b21967adaaee19161b6a (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
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
 * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved
 */

#include <drm/display/drm_dsc_helper.h>

#include "dpu_kms.h"
#include "dpu_hw_catalog.h"
#include "dpu_hwio.h"
#include "dpu_hw_mdss.h"
#include "dpu_hw_dsc.h"

#define DSC_CMN_MAIN_CNF           0x00

/* DPU_DSC_ENC register offsets */
#define ENC_DF_CTRL                0x00
#define ENC_GENERAL_STATUS         0x04
#define ENC_HSLICE_STATUS          0x08
#define ENC_OUT_STATUS             0x0C
#define ENC_INT_STAT               0x10
#define ENC_INT_CLR                0x14
#define ENC_INT_MASK               0x18
#define DSC_MAIN_CONF              0x30
#define DSC_PICTURE_SIZE           0x34
#define DSC_SLICE_SIZE             0x38
#define DSC_MISC_SIZE              0x3C
#define DSC_HRD_DELAYS             0x40
#define DSC_RC_SCALE               0x44
#define DSC_RC_SCALE_INC_DEC       0x48
#define DSC_RC_OFFSETS_1           0x4C
#define DSC_RC_OFFSETS_2           0x50
#define DSC_RC_OFFSETS_3           0x54
#define DSC_RC_OFFSETS_4           0x58
#define DSC_FLATNESS_QP            0x5C
#define DSC_RC_MODEL_SIZE          0x60
#define DSC_RC_CONFIG              0x64
#define DSC_RC_BUF_THRESH_0        0x68
#define DSC_RC_BUF_THRESH_1        0x6C
#define DSC_RC_BUF_THRESH_2        0x70
#define DSC_RC_BUF_THRESH_3        0x74
#define DSC_RC_MIN_QP_0            0x78
#define DSC_RC_MIN_QP_1            0x7C
#define DSC_RC_MIN_QP_2            0x80
#define DSC_RC_MAX_QP_0            0x84
#define DSC_RC_MAX_QP_1            0x88
#define DSC_RC_MAX_QP_2            0x8C
#define DSC_RC_RANGE_BPG_OFFSETS_0 0x90
#define DSC_RC_RANGE_BPG_OFFSETS_1 0x94
#define DSC_RC_RANGE_BPG_OFFSETS_2 0x98

/* DPU_DSC_CTL register offsets */
#define DSC_CTL                    0x00
#define DSC_CFG                    0x04
#define DSC_DATA_IN_SWAP           0x08
#define DSC_CLK_CTRL               0x0C

static int _dsc_calc_output_buf_max_addr(struct dpu_hw_dsc *hw_dsc, int num_softslice)
{
	int max_addr = 2400 / num_softslice;

	if (hw_dsc->caps->features & BIT(DPU_DSC_NATIVE_42x_EN))
		max_addr /= 2;

	return max_addr - 1;
};

static void dpu_hw_dsc_disable_1_2(struct dpu_hw_dsc *hw_dsc)
{
	struct dpu_hw_blk_reg_map *hw;
	const struct dpu_dsc_sub_blks *sblk;

	if (!hw_dsc)
		return;

	hw = &hw_dsc->hw;
	sblk = hw_dsc->caps->sblk;
	DPU_REG_WRITE(hw, sblk->ctl.base + DSC_CFG, 0);

	DPU_REG_WRITE(hw, sblk->enc.base + ENC_DF_CTRL, 0);
	DPU_REG_WRITE(hw, sblk->enc.base + DSC_MAIN_CONF, 0);
}

static void dpu_hw_dsc_config_1_2(struct dpu_hw_dsc *hw_dsc,
				  struct drm_dsc_config *dsc,
				  u32 mode,
				  u32 initial_lines)
{
	struct dpu_hw_blk_reg_map *hw;
	const struct dpu_dsc_sub_blks *sblk;
	u32 data = 0;
	u32 det_thresh_flatness;
	u32 num_active_slice_per_enc;
	u32 bpp;

	if (!hw_dsc || !dsc)
		return;

	hw = &hw_dsc->hw;

	sblk = hw_dsc->caps->sblk;

	if (mode & DSC_MODE_SPLIT_PANEL)
		data |= BIT(0);

	if (mode & DSC_MODE_MULTIPLEX)
		data |= BIT(1);

	num_active_slice_per_enc = dsc->slice_count;
	if (mode & DSC_MODE_MULTIPLEX)
		num_active_slice_per_enc = dsc->slice_count / 2;

	data |= (num_active_slice_per_enc & 0x3) << 7;

	DPU_REG_WRITE(hw, DSC_CMN_MAIN_CNF, data);

	data = (initial_lines & 0xff);

	if (mode & DSC_MODE_VIDEO)
		data |= BIT(9);

	data |= (_dsc_calc_output_buf_max_addr(hw_dsc, num_active_slice_per_enc) << 18);

	DPU_REG_WRITE(hw, sblk->enc.base + ENC_DF_CTRL, data);

	data = (dsc->dsc_version_minor & 0xf) << 28;
	if (dsc->dsc_version_minor == 0x2) {
		if (dsc->native_422)
			data |= BIT(22);
		if (dsc->native_420)
			data |= BIT(21);
	}

	bpp = dsc->bits_per_pixel;
	/* as per hw requirement bpp should be programmed
	 * twice the actual value in case of 420 or 422 encoding
	 */
	if (dsc->native_422 || dsc->native_420)
		bpp = 2 * bpp;

	data |= bpp << 10;

	if (dsc->block_pred_enable)
		data |= BIT(20);

	if (dsc->convert_rgb)
		data |= BIT(4);

	data |= (dsc->line_buf_depth & 0xf) << 6;
	data |= dsc->bits_per_component & 0xf;

	DPU_REG_WRITE(hw, sblk->enc.base + DSC_MAIN_CONF, data);

	data = (dsc->pic_width & 0xffff) |
		((dsc->pic_height & 0xffff) << 16);

	DPU_REG_WRITE(hw, sblk->enc.base + DSC_PICTURE_SIZE, data);

	data = (dsc->slice_width & 0xffff) |
		((dsc->slice_height & 0xffff) << 16);

	DPU_REG_WRITE(hw, sblk->enc.base + DSC_SLICE_SIZE, data);

	DPU_REG_WRITE(hw, sblk->enc.base + DSC_MISC_SIZE,
		      (dsc->slice_chunk_size) & 0xffff);

	data = (dsc->initial_xmit_delay & 0xffff) |
		((dsc->initial_dec_delay & 0xffff) << 16);

	DPU_REG_WRITE(hw, sblk->enc.base + DSC_HRD_DELAYS, data);

	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_SCALE,
		      dsc->initial_scale_value & 0x3f);

	data = (dsc->scale_increment_interval & 0xffff) |
		((dsc->scale_decrement_interval & 0x7ff) << 16);

	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_SCALE_INC_DEC, data);

	data = (dsc->first_line_bpg_offset & 0x1f) |
		((dsc->second_line_bpg_offset & 0x1f) << 5);

	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_OFFSETS_1, data);

	data = (dsc->nfl_bpg_offset & 0xffff) |
		((dsc->slice_bpg_offset & 0xffff) << 16);

	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_OFFSETS_2, data);

	data = (dsc->initial_offset & 0xffff) |
		((dsc->final_offset & 0xffff) << 16);

	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_OFFSETS_3, data);

	data = (dsc->nsl_bpg_offset & 0xffff) |
		((dsc->second_line_offset_adj & 0xffff) << 16);

	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_OFFSETS_4, data);

	det_thresh_flatness = drm_dsc_flatness_det_thresh(dsc);
	data = (dsc->flatness_min_qp & 0x1f) |
		((dsc->flatness_max_qp & 0x1f) << 5) |
		((det_thresh_flatness & 0xff) << 10);

	DPU_REG_WRITE(hw, sblk->enc.base + DSC_FLATNESS_QP, data);

	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_MODEL_SIZE,
		      (dsc->rc_model_size) & 0xffff);

	data = dsc->rc_edge_factor & 0xf;
	data |= (dsc->rc_quant_incr_limit0 & 0x1f) << 8;
	data |= (dsc->rc_quant_incr_limit1 & 0x1f) << 13;
	data |= (dsc->rc_tgt_offset_high & 0xf) << 20;
	data |= (dsc->rc_tgt_offset_low & 0xf) << 24;

	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_CONFIG, data);

	/* program the dsc wrapper */
	data = BIT(0); /* encoder enable */
	if (dsc->native_422)
		data |= BIT(8);
	else if (dsc->native_420)
		data |= BIT(9);
	if (!dsc->convert_rgb)
		data |= BIT(10);
	if (dsc->bits_per_component == 8)
		data |= BIT(11);
	if (mode & DSC_MODE_SPLIT_PANEL)
		data |= BIT(12);
	if (mode & DSC_MODE_MULTIPLEX)
		data |= BIT(13);
	if (!(mode & DSC_MODE_VIDEO))
		data |= BIT(17);

	DPU_REG_WRITE(hw, sblk->ctl.base + DSC_CFG, data);
}

static void dpu_hw_dsc_config_thresh_1_2(struct dpu_hw_dsc *hw_dsc,
					 struct drm_dsc_config *dsc)
{
	struct dpu_hw_blk_reg_map *hw;
	const struct dpu_dsc_sub_blks *sblk;
	struct drm_dsc_rc_range_parameters *rc;

	if (!hw_dsc || !dsc)
		return;

	hw = &hw_dsc->hw;

	sblk = hw_dsc->caps->sblk;

	rc = dsc->rc_range_params;

	/*
	 * With BUF_THRESH -- 14 in total
	 * each register contains 4 thresh values with the last register
	 * containing only 2 thresh values
	 */
	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_BUF_THRESH_0,
		      (dsc->rc_buf_thresh[0] << 0) |
		      (dsc->rc_buf_thresh[1] << 8) |
		      (dsc->rc_buf_thresh[2] << 16) |
		      (dsc->rc_buf_thresh[3] << 24));
	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_BUF_THRESH_1,
		      (dsc->rc_buf_thresh[4] << 0) |
		      (dsc->rc_buf_thresh[5] << 8) |
		      (dsc->rc_buf_thresh[6] << 16) |
		      (dsc->rc_buf_thresh[7] << 24));
	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_BUF_THRESH_2,
		      (dsc->rc_buf_thresh[8] << 0) |
		      (dsc->rc_buf_thresh[9] << 8) |
		      (dsc->rc_buf_thresh[10] << 16) |
		      (dsc->rc_buf_thresh[11] << 24));
	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_BUF_THRESH_3,
		      (dsc->rc_buf_thresh[12] << 0) |
		      (dsc->rc_buf_thresh[13] << 8));

	/*
	 * with min/max_QP -- 5 bits
	 * each register contains 5 min_qp or max_qp for total of 15
	 *
	 * With BPG_OFFSET -- 6 bits
	 * each register contains 5 BPG_offset for total of 15
	 */
	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_MIN_QP_0,
		      (rc[0].range_min_qp << 0) |
		      (rc[1].range_min_qp << 5) |
		      (rc[2].range_min_qp << 10) |
		      (rc[3].range_min_qp << 15) |
		      (rc[4].range_min_qp << 20));
	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_MAX_QP_0,
		      (rc[0].range_max_qp << 0) |
		      (rc[1].range_max_qp << 5) |
		      (rc[2].range_max_qp << 10) |
		      (rc[3].range_max_qp << 15) |
		      (rc[4].range_max_qp << 20));
	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_RANGE_BPG_OFFSETS_0,
		      (rc[0].range_bpg_offset << 0) |
		      (rc[1].range_bpg_offset << 6) |
		      (rc[2].range_bpg_offset << 12) |
		      (rc[3].range_bpg_offset << 18) |
		      (rc[4].range_bpg_offset << 24));

	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_MIN_QP_1,
		      (rc[5].range_min_qp << 0) |
		      (rc[6].range_min_qp << 5) |
		      (rc[7].range_min_qp << 10) |
		      (rc[8].range_min_qp << 15) |
		      (rc[9].range_min_qp << 20));
	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_MAX_QP_1,
		      (rc[5].range_max_qp << 0) |
		      (rc[6].range_max_qp << 5) |
		      (rc[7].range_max_qp << 10) |
		      (rc[8].range_max_qp << 15) |
		      (rc[9].range_max_qp << 20));
	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_RANGE_BPG_OFFSETS_1,
		      (rc[5].range_bpg_offset << 0) |
		      (rc[6].range_bpg_offset << 6) |
		      (rc[7].range_bpg_offset << 12) |
		      (rc[8].range_bpg_offset << 18) |
		      (rc[9].range_bpg_offset << 24));

	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_MIN_QP_2,
		      (rc[10].range_min_qp << 0) |
		      (rc[11].range_min_qp << 5) |
		      (rc[12].range_min_qp << 10) |
		      (rc[13].range_min_qp << 15) |
		      (rc[14].range_min_qp << 20));
	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_MAX_QP_2,
		      (rc[10].range_max_qp << 0) |
		      (rc[11].range_max_qp << 5) |
		      (rc[12].range_max_qp << 10) |
		      (rc[13].range_max_qp << 15) |
		      (rc[14].range_max_qp << 20));
	DPU_REG_WRITE(hw, sblk->enc.base + DSC_RC_RANGE_BPG_OFFSETS_2,
		      (rc[10].range_bpg_offset << 0) |
		      (rc[11].range_bpg_offset << 6) |
		      (rc[12].range_bpg_offset << 12) |
		      (rc[13].range_bpg_offset << 18) |
		      (rc[14].range_bpg_offset << 24));
}

static void dpu_hw_dsc_bind_pingpong_blk_1_2(struct dpu_hw_dsc *hw_dsc,
					     const enum dpu_pingpong pp)
{
	struct dpu_hw_blk_reg_map *hw;
	const struct dpu_dsc_sub_blks *sblk;
	int mux_cfg = 0xf; /* Disabled */

	hw = &hw_dsc->hw;

	sblk = hw_dsc->caps->sblk;

	if (pp)
		mux_cfg = (pp - PINGPONG_0) & 0x7;

	DPU_REG_WRITE(hw, sblk->ctl.base + DSC_CTL, mux_cfg);
}

static void _setup_dcs_ops_1_2(struct dpu_hw_dsc_ops *ops,
			       const unsigned long features)
{
	ops->dsc_disable = dpu_hw_dsc_disable_1_2;
	ops->dsc_config = dpu_hw_dsc_config_1_2;
	ops->dsc_config_thresh = dpu_hw_dsc_config_thresh_1_2;
	ops->dsc_bind_pingpong_blk = dpu_hw_dsc_bind_pingpong_blk_1_2;
}

struct dpu_hw_dsc *dpu_hw_dsc_init_1_2(const struct dpu_dsc_cfg *cfg,
				       void __iomem *addr)
{
	struct dpu_hw_dsc *c;

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

	c->hw.blk_addr = addr + cfg->base;
	c->hw.log_mask = DPU_DBG_MASK_DSC;

	c->idx = cfg->id;
	c->caps = cfg;
	_setup_dcs_ops_1_2(&c->ops, c->caps->features);

	return c;
}