1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved
4 */
5
6 #include <drm/drm_managed.h>
7
8 #include "dpu_hw_mdss.h"
9 #include "dpu_hwio.h"
10 #include "dpu_hw_catalog.h"
11 #include "dpu_hw_wb.h"
12 #include "dpu_formats.h"
13 #include "dpu_kms.h"
14
15 #define WB_DST_FORMAT 0x000
16 #define WB_DST_OP_MODE 0x004
17 #define WB_DST_PACK_PATTERN 0x008
18 #define WB_DST0_ADDR 0x00C
19 #define WB_DST1_ADDR 0x010
20 #define WB_DST2_ADDR 0x014
21 #define WB_DST3_ADDR 0x018
22 #define WB_DST_YSTRIDE0 0x01C
23 #define WB_DST_YSTRIDE1 0x020
24 #define WB_DST_YSTRIDE1 0x020
25 #define WB_DST_DITHER_BITDEPTH 0x024
26 #define WB_DST_MATRIX_ROW0 0x030
27 #define WB_DST_MATRIX_ROW1 0x034
28 #define WB_DST_MATRIX_ROW2 0x038
29 #define WB_DST_MATRIX_ROW3 0x03C
30 #define WB_DST_WRITE_CONFIG 0x048
31 #define WB_ROTATION_DNSCALER 0x050
32 #define WB_ROTATOR_PIPE_DOWNSCALER 0x054
33 #define WB_N16_INIT_PHASE_X_C03 0x060
34 #define WB_N16_INIT_PHASE_X_C12 0x064
35 #define WB_N16_INIT_PHASE_Y_C03 0x068
36 #define WB_N16_INIT_PHASE_Y_C12 0x06C
37 #define WB_OUT_SIZE 0x074
38 #define WB_ALPHA_X_VALUE 0x078
39 #define WB_DANGER_LUT 0x084
40 #define WB_SAFE_LUT 0x088
41 #define WB_QOS_CTRL 0x090
42 #define WB_CREQ_LUT_0 0x098
43 #define WB_CREQ_LUT_1 0x09C
44 #define WB_UBWC_STATIC_CTRL 0x144
45 #define WB_MUX 0x150
46 #define WB_CROP_CTRL 0x154
47 #define WB_CROP_OFFSET 0x158
48 #define WB_CLK_CTRL 0x178
49 #define WB_CSC_BASE 0x260
50 #define WB_DST_ADDR_SW_STATUS 0x2B0
51 #define WB_CDP_CNTL 0x2B4
52 #define WB_OUT_IMAGE_SIZE 0x2C0
53 #define WB_OUT_XY 0x2C4
54
dpu_hw_wb_setup_outaddress(struct dpu_hw_wb * ctx,struct dpu_hw_wb_cfg * data)55 static void dpu_hw_wb_setup_outaddress(struct dpu_hw_wb *ctx,
56 struct dpu_hw_wb_cfg *data)
57 {
58 struct dpu_hw_blk_reg_map *c = &ctx->hw;
59
60 DPU_REG_WRITE(c, WB_DST0_ADDR, data->dest.plane_addr[0]);
61 DPU_REG_WRITE(c, WB_DST1_ADDR, data->dest.plane_addr[1]);
62 DPU_REG_WRITE(c, WB_DST2_ADDR, data->dest.plane_addr[2]);
63 DPU_REG_WRITE(c, WB_DST3_ADDR, data->dest.plane_addr[3]);
64 }
65
dpu_hw_wb_setup_format(struct dpu_hw_wb * ctx,struct dpu_hw_wb_cfg * data)66 static void dpu_hw_wb_setup_format(struct dpu_hw_wb *ctx,
67 struct dpu_hw_wb_cfg *data)
68 {
69 struct dpu_hw_blk_reg_map *c = &ctx->hw;
70 const struct msm_format *fmt = data->dest.format;
71 u32 dst_format, pattern, ystride0, ystride1, outsize, chroma_samp;
72 u32 write_config = 0;
73 u32 opmode = 0;
74 u32 dst_addr_sw = 0;
75
76 chroma_samp = fmt->chroma_sample;
77
78 dst_format = (chroma_samp << 23) |
79 (fmt->fetch_type << 19) |
80 (fmt->bpc_a << 6) |
81 (fmt->bpc_r_cr << 4) |
82 (fmt->bpc_b_cb << 2) |
83 (fmt->bpc_g_y << 0);
84
85 if (fmt->bpc_a || fmt->alpha_enable) {
86 dst_format |= BIT(8); /* DSTC3_EN */
87 if (!fmt->alpha_enable ||
88 !(ctx->caps->features & BIT(DPU_WB_PIPE_ALPHA)))
89 dst_format |= BIT(14); /* DST_ALPHA_X */
90 }
91
92 if (MSM_FORMAT_IS_YUV(fmt))
93 dst_format |= BIT(15);
94
95 pattern = (fmt->element[3] << 24) |
96 (fmt->element[2] << 16) |
97 (fmt->element[1] << 8) |
98 (fmt->element[0] << 0);
99
100 dst_format |= ((fmt->flags & MSM_FORMAT_FLAG_UNPACK_ALIGN_MSB ? 1 : 0) << 18) |
101 ((fmt->flags & MSM_FORMAT_FLAG_UNPACK_TIGHT ? 1 : 0) << 17) |
102 ((fmt->unpack_count - 1) << 12) |
103 ((fmt->bpp - 1) << 9);
104
105 ystride0 = data->dest.plane_pitch[0] |
106 (data->dest.plane_pitch[1] << 16);
107 ystride1 = data->dest.plane_pitch[2] |
108 (data->dest.plane_pitch[3] << 16);
109
110 if (drm_rect_height(&data->roi) && drm_rect_width(&data->roi))
111 outsize = (drm_rect_height(&data->roi) << 16) | drm_rect_width(&data->roi);
112 else
113 outsize = (data->dest.height << 16) | data->dest.width;
114
115 DPU_REG_WRITE(c, WB_ALPHA_X_VALUE, 0xFF);
116 DPU_REG_WRITE(c, WB_DST_FORMAT, dst_format);
117 DPU_REG_WRITE(c, WB_DST_OP_MODE, opmode);
118 DPU_REG_WRITE(c, WB_DST_PACK_PATTERN, pattern);
119 DPU_REG_WRITE(c, WB_DST_YSTRIDE0, ystride0);
120 DPU_REG_WRITE(c, WB_DST_YSTRIDE1, ystride1);
121 DPU_REG_WRITE(c, WB_OUT_SIZE, outsize);
122 DPU_REG_WRITE(c, WB_DST_WRITE_CONFIG, write_config);
123 DPU_REG_WRITE(c, WB_DST_ADDR_SW_STATUS, dst_addr_sw);
124 }
125
dpu_hw_wb_roi(struct dpu_hw_wb * ctx,struct dpu_hw_wb_cfg * wb)126 static void dpu_hw_wb_roi(struct dpu_hw_wb *ctx, struct dpu_hw_wb_cfg *wb)
127 {
128 struct dpu_hw_blk_reg_map *c = &ctx->hw;
129 u32 image_size, out_size, out_xy;
130
131 image_size = (wb->dest.height << 16) | wb->dest.width;
132 out_xy = 0;
133 out_size = (drm_rect_height(&wb->roi) << 16) | drm_rect_width(&wb->roi);
134
135 DPU_REG_WRITE(c, WB_OUT_IMAGE_SIZE, image_size);
136 DPU_REG_WRITE(c, WB_OUT_XY, out_xy);
137 DPU_REG_WRITE(c, WB_OUT_SIZE, out_size);
138 }
139
dpu_hw_wb_setup_qos_lut(struct dpu_hw_wb * ctx,struct dpu_hw_qos_cfg * cfg)140 static void dpu_hw_wb_setup_qos_lut(struct dpu_hw_wb *ctx,
141 struct dpu_hw_qos_cfg *cfg)
142 {
143 if (!ctx || !cfg)
144 return;
145
146 _dpu_hw_setup_qos_lut(&ctx->hw, WB_DANGER_LUT,
147 test_bit(DPU_WB_QOS_8LVL, &ctx->caps->features),
148 cfg);
149 }
150
dpu_hw_wb_setup_cdp(struct dpu_hw_wb * ctx,const struct msm_format * fmt,bool enable)151 static void dpu_hw_wb_setup_cdp(struct dpu_hw_wb *ctx,
152 const struct msm_format *fmt,
153 bool enable)
154 {
155 if (!ctx)
156 return;
157
158 dpu_setup_cdp(&ctx->hw, WB_CDP_CNTL, fmt, enable);
159 }
160
dpu_hw_wb_bind_pingpong_blk(struct dpu_hw_wb * ctx,const enum dpu_pingpong pp)161 static void dpu_hw_wb_bind_pingpong_blk(
162 struct dpu_hw_wb *ctx,
163 const enum dpu_pingpong pp)
164 {
165 struct dpu_hw_blk_reg_map *c;
166 int mux_cfg;
167
168 if (!ctx)
169 return;
170
171 c = &ctx->hw;
172
173 mux_cfg = DPU_REG_READ(c, WB_MUX);
174 mux_cfg &= ~0xf;
175
176 if (pp)
177 mux_cfg |= (pp - PINGPONG_0) & 0x7;
178 else
179 mux_cfg |= 0xf;
180
181 DPU_REG_WRITE(c, WB_MUX, mux_cfg);
182 }
183
dpu_hw_wb_setup_clk_force_ctrl(struct dpu_hw_wb * ctx,bool enable)184 static bool dpu_hw_wb_setup_clk_force_ctrl(struct dpu_hw_wb *ctx, bool enable)
185 {
186 static const struct dpu_clk_ctrl_reg wb_clk_ctrl = {
187 .reg_off = WB_CLK_CTRL,
188 .bit_off = 0
189 };
190
191 return dpu_hw_clk_force_ctrl(&ctx->hw, &wb_clk_ctrl, enable);
192 }
193
_setup_wb_ops(struct dpu_hw_wb_ops * ops,unsigned long features,const struct dpu_mdss_version * mdss_rev)194 static void _setup_wb_ops(struct dpu_hw_wb_ops *ops,
195 unsigned long features, const struct dpu_mdss_version *mdss_rev)
196 {
197 ops->setup_outaddress = dpu_hw_wb_setup_outaddress;
198 ops->setup_outformat = dpu_hw_wb_setup_format;
199
200 if (test_bit(DPU_WB_XY_ROI_OFFSET, &features))
201 ops->setup_roi = dpu_hw_wb_roi;
202
203 if (test_bit(DPU_WB_QOS, &features))
204 ops->setup_qos_lut = dpu_hw_wb_setup_qos_lut;
205
206 if (test_bit(DPU_WB_CDP, &features))
207 ops->setup_cdp = dpu_hw_wb_setup_cdp;
208
209 if (test_bit(DPU_WB_INPUT_CTRL, &features))
210 ops->bind_pingpong_blk = dpu_hw_wb_bind_pingpong_blk;
211
212 if (mdss_rev->core_major_ver >= 9)
213 ops->setup_clk_force_ctrl = dpu_hw_wb_setup_clk_force_ctrl;
214 }
215
dpu_hw_wb_init(struct drm_device * dev,const struct dpu_wb_cfg * cfg,void __iomem * addr,const struct dpu_mdss_version * mdss_rev)216 struct dpu_hw_wb *dpu_hw_wb_init(struct drm_device *dev,
217 const struct dpu_wb_cfg *cfg,
218 void __iomem *addr,
219 const struct dpu_mdss_version *mdss_rev)
220 {
221 struct dpu_hw_wb *c;
222
223 if (!addr)
224 return ERR_PTR(-EINVAL);
225
226 c = drmm_kzalloc(dev, sizeof(*c), GFP_KERNEL);
227 if (!c)
228 return ERR_PTR(-ENOMEM);
229
230 c->hw.blk_addr = addr + cfg->base;
231 c->hw.log_mask = DPU_DBG_MASK_WB;
232
233 /* Assign ops */
234 c->idx = cfg->id;
235 c->caps = cfg;
236 _setup_wb_ops(&c->ops, c->caps->features, mdss_rev);
237
238 return c;
239 }
240