aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_cdm.c
blob: e9cdc7934a49965cbd56ae0294528804ccf60396 (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
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2023, The Linux Foundation. All rights reserved.
 */

#include <linux/bitfield.h>

#include <drm/drm_managed.h>

#include "dpu_hw_mdss.h"
#include "dpu_hw_util.h"
#include "dpu_hw_catalog.h"
#include "dpu_hw_cdm.h"
#include "dpu_kms.h"

#define CDM_CSC_10_OPMODE                  0x000
#define CDM_CSC_10_BASE                    0x004

#define CDM_CDWN2_OP_MODE                  0x100
#define CDM_CDWN2_CLAMP_OUT                0x104
#define CDM_CDWN2_PARAMS_3D_0              0x108
#define CDM_CDWN2_PARAMS_3D_1              0x10C
#define CDM_CDWN2_COEFF_COSITE_H_0         0x110
#define CDM_CDWN2_COEFF_COSITE_H_1         0x114
#define CDM_CDWN2_COEFF_COSITE_H_2         0x118
#define CDM_CDWN2_COEFF_OFFSITE_H_0        0x11C
#define CDM_CDWN2_COEFF_OFFSITE_H_1        0x120
#define CDM_CDWN2_COEFF_OFFSITE_H_2        0x124
#define CDM_CDWN2_COEFF_COSITE_V           0x128
#define CDM_CDWN2_COEFF_OFFSITE_V          0x12C
#define CDM_CDWN2_OUT_SIZE                 0x130

#define CDM_HDMI_PACK_OP_MODE              0x200
#define CDM_CSC_10_MATRIX_COEFF_0          0x004

#define CDM_MUX                            0x224

/* CDM CDWN2 sub-block bit definitions */
#define CDM_CDWN2_OP_MODE_EN                  BIT(0)
#define CDM_CDWN2_OP_MODE_ENABLE_H            BIT(1)
#define CDM_CDWN2_OP_MODE_ENABLE_V            BIT(2)
#define CDM_CDWN2_OP_MODE_BITS_OUT_8BIT       BIT(7)
#define CDM_CDWN2_V_PIXEL_METHOD_MASK         GENMASK(6, 5)
#define CDM_CDWN2_H_PIXEL_METHOD_MASK         GENMASK(4, 3)

/* CDM CSC10 sub-block bit definitions */
#define CDM_CSC10_OP_MODE_EN               BIT(0)
#define CDM_CSC10_OP_MODE_SRC_FMT_YUV      BIT(1)
#define CDM_CSC10_OP_MODE_DST_FMT_YUV      BIT(2)

/* CDM HDMI pack sub-block bit definitions */
#define CDM_HDMI_PACK_OP_MODE_EN           BIT(0)

/*
 * Horizontal coefficients for cosite chroma downscale
 * s13 representation of coefficients
 */
static u32 cosite_h_coeff[] = {0x00000016, 0x000001cc, 0x0100009e};

/*
 * Horizontal coefficients for offsite chroma downscale
 */
static u32 offsite_h_coeff[] = {0x000b0005, 0x01db01eb, 0x00e40046};

/*
 * Vertical coefficients for cosite chroma downscale
 */
static u32 cosite_v_coeff[] = {0x00080004};
/*
 * Vertical coefficients for offsite chroma downscale
 */
static u32 offsite_v_coeff[] = {0x00060002};

static int dpu_hw_cdm_setup_cdwn(struct dpu_hw_cdm *ctx, struct dpu_hw_cdm_cfg *cfg)
{
	struct dpu_hw_blk_reg_map *c = &ctx->hw;
	u32 opmode;
	u32 out_size;

	switch (cfg->h_cdwn_type) {
	case CDM_CDWN_DISABLE:
		opmode = 0;
		break;
	case CDM_CDWN_PIXEL_DROP:
		opmode = CDM_CDWN2_OP_MODE_ENABLE_H |
				FIELD_PREP(CDM_CDWN2_H_PIXEL_METHOD_MASK,
					   CDM_CDWN2_METHOD_PIXEL_DROP);
		break;
	case CDM_CDWN_AVG:
		opmode = CDM_CDWN2_OP_MODE_ENABLE_H |
				FIELD_PREP(CDM_CDWN2_H_PIXEL_METHOD_MASK,
					   CDM_CDWN2_METHOD_AVG);
		break;
	case CDM_CDWN_COSITE:
		opmode = CDM_CDWN2_OP_MODE_ENABLE_H |
				FIELD_PREP(CDM_CDWN2_H_PIXEL_METHOD_MASK,
					   CDM_CDWN2_METHOD_COSITE);
		DPU_REG_WRITE(c, CDM_CDWN2_COEFF_COSITE_H_0,
			      cosite_h_coeff[0]);
		DPU_REG_WRITE(c, CDM_CDWN2_COEFF_COSITE_H_1,
			      cosite_h_coeff[1]);
		DPU_REG_WRITE(c, CDM_CDWN2_COEFF_COSITE_H_2,
			      cosite_h_coeff[2]);
		break;
	case CDM_CDWN_OFFSITE:
		opmode = CDM_CDWN2_OP_MODE_ENABLE_H |
				FIELD_PREP(CDM_CDWN2_H_PIXEL_METHOD_MASK, CDM_CDWN2_METHOD_OFFSITE);
		DPU_REG_WRITE(c, CDM_CDWN2_COEFF_OFFSITE_H_0,
			      offsite_h_coeff[0]);
		DPU_REG_WRITE(c, CDM_CDWN2_COEFF_OFFSITE_H_1,
			      offsite_h_coeff[1]);
		DPU_REG_WRITE(c, CDM_CDWN2_COEFF_OFFSITE_H_2,
			      offsite_h_coeff[2]);
		break;
	default:
		DPU_ERROR("%s invalid horz down sampling type\n", __func__);
		return -EINVAL;
	}

	switch (cfg->v_cdwn_type) {
	case CDM_CDWN_DISABLE:
		/* if its only Horizontal downsample, we dont need to do anything here */
		break;
	case CDM_CDWN_PIXEL_DROP:
		opmode |= CDM_CDWN2_OP_MODE_ENABLE_V |
				FIELD_PREP(CDM_CDWN2_V_PIXEL_METHOD_MASK,
					   CDM_CDWN2_METHOD_PIXEL_DROP);
		break;
	case CDM_CDWN_AVG:
		opmode |= CDM_CDWN2_OP_MODE_ENABLE_V |
				FIELD_PREP(CDM_CDWN2_V_PIXEL_METHOD_MASK,
					   CDM_CDWN2_METHOD_AVG);
		break;
	case CDM_CDWN_COSITE:
		opmode |= CDM_CDWN2_OP_MODE_ENABLE_V |
				FIELD_PREP(CDM_CDWN2_V_PIXEL_METHOD_MASK,
					   CDM_CDWN2_METHOD_COSITE);
		DPU_REG_WRITE(c,
			      CDM_CDWN2_COEFF_COSITE_V,
			      cosite_v_coeff[0]);
		break;
	case CDM_CDWN_OFFSITE:
		opmode |= CDM_CDWN2_OP_MODE_ENABLE_V |
				FIELD_PREP(CDM_CDWN2_V_PIXEL_METHOD_MASK,
					   CDM_CDWN2_METHOD_OFFSITE);
		DPU_REG_WRITE(c,
			      CDM_CDWN2_COEFF_OFFSITE_V,
			      offsite_v_coeff[0]);
		break;
	default:
		return -EINVAL;
	}

	if (cfg->output_bit_depth != CDM_CDWN_OUTPUT_10BIT)
		opmode |= CDM_CDWN2_OP_MODE_BITS_OUT_8BIT;

	if (cfg->v_cdwn_type || cfg->h_cdwn_type)
		opmode |= CDM_CDWN2_OP_MODE_EN; /* EN CDWN module */
	else
		opmode &= ~CDM_CDWN2_OP_MODE_EN;

	out_size = (cfg->output_width & 0xFFFF) | ((cfg->output_height & 0xFFFF) << 16);
	DPU_REG_WRITE(c, CDM_CDWN2_OUT_SIZE, out_size);
	DPU_REG_WRITE(c, CDM_CDWN2_OP_MODE, opmode);
	DPU_REG_WRITE(c, CDM_CDWN2_CLAMP_OUT, ((0x3FF << 16) | 0x0));

	return 0;
}

static int dpu_hw_cdm_enable(struct dpu_hw_cdm *ctx, struct dpu_hw_cdm_cfg *cdm)
{
	struct dpu_hw_blk_reg_map *c = &ctx->hw;
	const struct dpu_format *fmt;
	u32 opmode = 0;
	u32 csc = 0;

	if (!ctx || !cdm)
		return -EINVAL;

	fmt = cdm->output_fmt;

	if (!DPU_FORMAT_IS_YUV(fmt))
		return -EINVAL;

	dpu_hw_csc_setup(&ctx->hw, CDM_CSC_10_MATRIX_COEFF_0, cdm->csc_cfg, true);
	dpu_hw_cdm_setup_cdwn(ctx, cdm);

	if (cdm->output_type == CDM_CDWN_OUTPUT_HDMI) {
		if (fmt->chroma_sample != DPU_CHROMA_H1V2)
			return -EINVAL; /*unsupported format */
		opmode = CDM_HDMI_PACK_OP_MODE_EN;
		opmode |= (fmt->chroma_sample << 1);
	}

	csc |= CDM_CSC10_OP_MODE_DST_FMT_YUV;
	csc &= ~CDM_CSC10_OP_MODE_SRC_FMT_YUV;
	csc |= CDM_CSC10_OP_MODE_EN;

	if (ctx && ctx->ops.bind_pingpong_blk)
		ctx->ops.bind_pingpong_blk(ctx, cdm->pp_id);

	DPU_REG_WRITE(c, CDM_CSC_10_OPMODE, csc);
	DPU_REG_WRITE(c, CDM_HDMI_PACK_OP_MODE, opmode);
	return 0;
}

static void dpu_hw_cdm_bind_pingpong_blk(struct dpu_hw_cdm *ctx, const enum dpu_pingpong pp)
{
	struct dpu_hw_blk_reg_map *c;
	int mux_cfg;

	c = &ctx->hw;

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

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

	DPU_REG_WRITE(c, CDM_MUX, mux_cfg);
}

struct dpu_hw_cdm *dpu_hw_cdm_init(struct drm_device *dev,
				   const struct dpu_cdm_cfg *cfg, void __iomem *addr,
				   const struct dpu_mdss_version *mdss_rev)
{
	struct dpu_hw_cdm *c;

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

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

	/* Assign ops */
	c->idx = cfg->id;
	c->caps = cfg;

	c->ops.enable = dpu_hw_cdm_enable;
	if (mdss_rev->core_major_ver >= 5)
		c->ops.bind_pingpong_blk = dpu_hw_cdm_bind_pingpong_blk;

	return c;
}