1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
6 #include "dpu_hw_catalog.h"
7 #include "dpu_hw_top.h"
10 #define SSPP_SPARE 0x28
11 #define UBWC_STATIC 0x144
13 #define FLD_SPLIT_DISPLAY_CMD BIT(1)
14 #define FLD_SMART_PANEL_FREE_RUN BIT(2)
15 #define FLD_INTF_1_SW_TRG_MUX BIT(4)
16 #define FLD_INTF_2_SW_TRG_MUX BIT(8)
17 #define FLD_TE_LINE_INTER_WATERLEVEL_MASK 0xFFFF
19 #define DANGER_STATUS 0x360
20 #define SAFE_STATUS 0x364
22 #define TE_LINE_INTERVAL 0x3F4
24 #define TRAFFIC_SHAPER_EN BIT(31)
25 #define TRAFFIC_SHAPER_RD_CLIENT(num) (0x030 + (num * 4))
26 #define TRAFFIC_SHAPER_WR_CLIENT(num) (0x060 + (num * 4))
27 #define TRAFFIC_SHAPER_FIXPOINT_FACTOR 4
29 #define MDP_WD_TIMER_0_CTL 0x380
30 #define MDP_WD_TIMER_0_CTL2 0x384
31 #define MDP_WD_TIMER_0_LOAD_VALUE 0x388
32 #define MDP_WD_TIMER_1_CTL 0x390
33 #define MDP_WD_TIMER_1_CTL2 0x394
34 #define MDP_WD_TIMER_1_LOAD_VALUE 0x398
35 #define MDP_WD_TIMER_2_CTL 0x420
36 #define MDP_WD_TIMER_2_CTL2 0x424
37 #define MDP_WD_TIMER_2_LOAD_VALUE 0x428
38 #define MDP_WD_TIMER_3_CTL 0x430
39 #define MDP_WD_TIMER_3_CTL2 0x434
40 #define MDP_WD_TIMER_3_LOAD_VALUE 0x438
41 #define MDP_WD_TIMER_4_CTL 0x440
42 #define MDP_WD_TIMER_4_CTL2 0x444
43 #define MDP_WD_TIMER_4_LOAD_VALUE 0x448
45 #define MDP_TICK_COUNT 16
46 #define XO_CLK_RATE 19200
47 #define MS_TICKS_IN_SEC 1000
49 #define CALCULATE_WD_LOAD_VALUE(fps) \
50 ((uint32_t)((MS_TICKS_IN_SEC * XO_CLK_RATE)/(MDP_TICK_COUNT * fps)))
54 static void dpu_hw_setup_split_pipe(struct dpu_hw_mdp
*mdp
,
55 struct split_pipe_cfg
*cfg
)
57 struct dpu_hw_blk_reg_map
*c
;
67 if (cfg
->mode
== INTF_MODE_CMD
) {
68 lower_pipe
= FLD_SPLIT_DISPLAY_CMD
;
69 /* interface controlling sw trigger */
70 if (cfg
->intf
== INTF_2
)
71 lower_pipe
|= FLD_INTF_1_SW_TRG_MUX
;
73 lower_pipe
|= FLD_INTF_2_SW_TRG_MUX
;
74 upper_pipe
= lower_pipe
;
76 if (cfg
->intf
== INTF_2
) {
77 lower_pipe
= FLD_INTF_1_SW_TRG_MUX
;
78 upper_pipe
= FLD_INTF_2_SW_TRG_MUX
;
80 lower_pipe
= FLD_INTF_2_SW_TRG_MUX
;
81 upper_pipe
= FLD_INTF_1_SW_TRG_MUX
;
86 DPU_REG_WRITE(c
, SSPP_SPARE
, cfg
->split_flush_en
? 0x1 : 0x0);
87 DPU_REG_WRITE(c
, SPLIT_DISPLAY_LOWER_PIPE_CTRL
, lower_pipe
);
88 DPU_REG_WRITE(c
, SPLIT_DISPLAY_UPPER_PIPE_CTRL
, upper_pipe
);
89 DPU_REG_WRITE(c
, SPLIT_DISPLAY_EN
, cfg
->en
& 0x1);
92 static bool dpu_hw_setup_clk_force_ctrl(struct dpu_hw_mdp
*mdp
,
93 enum dpu_clk_ctrl_type clk_ctrl
, bool enable
)
95 struct dpu_hw_blk_reg_map
*c
;
105 if (clk_ctrl
<= DPU_CLK_CTRL_NONE
|| clk_ctrl
>= DPU_CLK_CTRL_MAX
)
108 reg_off
= mdp
->caps
->clk_ctrls
[clk_ctrl
].reg_off
;
109 bit_off
= mdp
->caps
->clk_ctrls
[clk_ctrl
].bit_off
;
111 reg_val
= DPU_REG_READ(c
, reg_off
);
114 new_val
= reg_val
| BIT(bit_off
);
116 new_val
= reg_val
& ~BIT(bit_off
);
118 DPU_REG_WRITE(c
, reg_off
, new_val
);
120 clk_forced_on
= !(reg_val
& BIT(bit_off
));
122 return clk_forced_on
;
126 static void dpu_hw_get_danger_status(struct dpu_hw_mdp
*mdp
,
127 struct dpu_danger_safe_status
*status
)
129 struct dpu_hw_blk_reg_map
*c
;
137 value
= DPU_REG_READ(c
, DANGER_STATUS
);
138 status
->mdp
= (value
>> 0) & 0x3;
139 status
->sspp
[SSPP_VIG0
] = (value
>> 4) & 0x3;
140 status
->sspp
[SSPP_VIG1
] = (value
>> 6) & 0x3;
141 status
->sspp
[SSPP_VIG2
] = (value
>> 8) & 0x3;
142 status
->sspp
[SSPP_VIG3
] = (value
>> 10) & 0x3;
143 status
->sspp
[SSPP_RGB0
] = (value
>> 12) & 0x3;
144 status
->sspp
[SSPP_RGB1
] = (value
>> 14) & 0x3;
145 status
->sspp
[SSPP_RGB2
] = (value
>> 16) & 0x3;
146 status
->sspp
[SSPP_RGB3
] = (value
>> 18) & 0x3;
147 status
->sspp
[SSPP_DMA0
] = (value
>> 20) & 0x3;
148 status
->sspp
[SSPP_DMA1
] = (value
>> 22) & 0x3;
149 status
->sspp
[SSPP_DMA2
] = (value
>> 28) & 0x3;
150 status
->sspp
[SSPP_DMA3
] = (value
>> 30) & 0x3;
151 status
->sspp
[SSPP_CURSOR0
] = (value
>> 24) & 0x3;
152 status
->sspp
[SSPP_CURSOR1
] = (value
>> 26) & 0x3;
155 static void dpu_hw_setup_vsync_source(struct dpu_hw_mdp
*mdp
,
156 struct dpu_vsync_source_cfg
*cfg
)
158 struct dpu_hw_blk_reg_map
*c
;
159 u32 reg
, wd_load_value
, wd_ctl
, wd_ctl2
, i
;
160 static const u32 pp_offset
[PINGPONG_MAX
] = {0xC, 0x8, 0x4, 0x13, 0x18};
162 if (!mdp
|| !cfg
|| (cfg
->pp_count
> ARRAY_SIZE(cfg
->ppnumber
)))
166 reg
= DPU_REG_READ(c
, MDP_VSYNC_SEL
);
167 for (i
= 0; i
< cfg
->pp_count
; i
++) {
168 int pp_idx
= cfg
->ppnumber
[i
] - PINGPONG_0
;
170 if (pp_idx
>= ARRAY_SIZE(pp_offset
))
173 reg
&= ~(0xf << pp_offset
[pp_idx
]);
174 reg
|= (cfg
->vsync_source
& 0xf) << pp_offset
[pp_idx
];
176 DPU_REG_WRITE(c
, MDP_VSYNC_SEL
, reg
);
178 if (cfg
->vsync_source
>= DPU_VSYNC_SOURCE_WD_TIMER_4
&&
179 cfg
->vsync_source
<= DPU_VSYNC_SOURCE_WD_TIMER_0
) {
180 switch (cfg
->vsync_source
) {
181 case DPU_VSYNC_SOURCE_WD_TIMER_4
:
182 wd_load_value
= MDP_WD_TIMER_4_LOAD_VALUE
;
183 wd_ctl
= MDP_WD_TIMER_4_CTL
;
184 wd_ctl2
= MDP_WD_TIMER_4_CTL2
;
186 case DPU_VSYNC_SOURCE_WD_TIMER_3
:
187 wd_load_value
= MDP_WD_TIMER_3_LOAD_VALUE
;
188 wd_ctl
= MDP_WD_TIMER_3_CTL
;
189 wd_ctl2
= MDP_WD_TIMER_3_CTL2
;
191 case DPU_VSYNC_SOURCE_WD_TIMER_2
:
192 wd_load_value
= MDP_WD_TIMER_2_LOAD_VALUE
;
193 wd_ctl
= MDP_WD_TIMER_2_CTL
;
194 wd_ctl2
= MDP_WD_TIMER_2_CTL2
;
196 case DPU_VSYNC_SOURCE_WD_TIMER_1
:
197 wd_load_value
= MDP_WD_TIMER_1_LOAD_VALUE
;
198 wd_ctl
= MDP_WD_TIMER_1_CTL
;
199 wd_ctl2
= MDP_WD_TIMER_1_CTL2
;
201 case DPU_VSYNC_SOURCE_WD_TIMER_0
:
203 wd_load_value
= MDP_WD_TIMER_0_LOAD_VALUE
;
204 wd_ctl
= MDP_WD_TIMER_0_CTL
;
205 wd_ctl2
= MDP_WD_TIMER_0_CTL2
;
209 DPU_REG_WRITE(c
, wd_load_value
,
210 CALCULATE_WD_LOAD_VALUE(cfg
->frame_rate
));
212 DPU_REG_WRITE(c
, wd_ctl
, BIT(0)); /* clear timer */
213 reg
= DPU_REG_READ(c
, wd_ctl2
);
214 reg
|= BIT(8); /* enable heartbeat timer */
215 reg
|= BIT(0); /* enable WD timer */
216 DPU_REG_WRITE(c
, wd_ctl2
, reg
);
218 /* make sure that timers are enabled/disabled for vsync state */
223 static void dpu_hw_get_safe_status(struct dpu_hw_mdp
*mdp
,
224 struct dpu_danger_safe_status
*status
)
226 struct dpu_hw_blk_reg_map
*c
;
234 value
= DPU_REG_READ(c
, SAFE_STATUS
);
235 status
->mdp
= (value
>> 0) & 0x1;
236 status
->sspp
[SSPP_VIG0
] = (value
>> 4) & 0x1;
237 status
->sspp
[SSPP_VIG1
] = (value
>> 6) & 0x1;
238 status
->sspp
[SSPP_VIG2
] = (value
>> 8) & 0x1;
239 status
->sspp
[SSPP_VIG3
] = (value
>> 10) & 0x1;
240 status
->sspp
[SSPP_RGB0
] = (value
>> 12) & 0x1;
241 status
->sspp
[SSPP_RGB1
] = (value
>> 14) & 0x1;
242 status
->sspp
[SSPP_RGB2
] = (value
>> 16) & 0x1;
243 status
->sspp
[SSPP_RGB3
] = (value
>> 18) & 0x1;
244 status
->sspp
[SSPP_DMA0
] = (value
>> 20) & 0x1;
245 status
->sspp
[SSPP_DMA1
] = (value
>> 22) & 0x1;
246 status
->sspp
[SSPP_DMA2
] = (value
>> 28) & 0x1;
247 status
->sspp
[SSPP_DMA3
] = (value
>> 30) & 0x1;
248 status
->sspp
[SSPP_CURSOR0
] = (value
>> 24) & 0x1;
249 status
->sspp
[SSPP_CURSOR1
] = (value
>> 26) & 0x1;
252 static void dpu_hw_reset_ubwc(struct dpu_hw_mdp
*mdp
, struct dpu_mdss_cfg
*m
)
254 struct dpu_hw_blk_reg_map c
;
259 if (!IS_UBWC_20_SUPPORTED(m
->caps
->ubwc_version
))
262 /* force blk offset to zero to access beginning of register region */
265 DPU_REG_WRITE(&c
, UBWC_STATIC
, m
->mdp
[0].ubwc_static
);
268 static void dpu_hw_intf_audio_select(struct dpu_hw_mdp
*mdp
)
270 struct dpu_hw_blk_reg_map
*c
;
277 DPU_REG_WRITE(c
, HDMI_DP_CORE_SELECT
, 0x1);
280 static void _setup_mdp_ops(struct dpu_hw_mdp_ops
*ops
,
283 ops
->setup_split_pipe
= dpu_hw_setup_split_pipe
;
284 ops
->setup_clk_force_ctrl
= dpu_hw_setup_clk_force_ctrl
;
285 ops
->get_danger_status
= dpu_hw_get_danger_status
;
286 ops
->setup_vsync_source
= dpu_hw_setup_vsync_source
;
287 ops
->get_safe_status
= dpu_hw_get_safe_status
;
288 ops
->reset_ubwc
= dpu_hw_reset_ubwc
;
289 ops
->intf_audio_select
= dpu_hw_intf_audio_select
;
292 static const struct dpu_mdp_cfg
*_top_offset(enum dpu_mdp mdp
,
293 const struct dpu_mdss_cfg
*m
,
295 struct dpu_hw_blk_reg_map
*b
)
299 if (!m
|| !addr
|| !b
)
300 return ERR_PTR(-EINVAL
);
302 for (i
= 0; i
< m
->mdp_count
; i
++) {
303 if (mdp
== m
->mdp
[i
].id
) {
305 b
->blk_off
= m
->mdp
[i
].base
;
306 b
->length
= m
->mdp
[i
].len
;
307 b
->hwversion
= m
->hwversion
;
308 b
->log_mask
= DPU_DBG_MASK_TOP
;
313 return ERR_PTR(-EINVAL
);
316 static struct dpu_hw_blk_ops dpu_hw_ops
;
318 struct dpu_hw_mdp
*dpu_hw_mdptop_init(enum dpu_mdp idx
,
320 const struct dpu_mdss_cfg
*m
)
322 struct dpu_hw_mdp
*mdp
;
323 const struct dpu_mdp_cfg
*cfg
;
326 return ERR_PTR(-EINVAL
);
328 mdp
= kzalloc(sizeof(*mdp
), GFP_KERNEL
);
330 return ERR_PTR(-ENOMEM
);
332 cfg
= _top_offset(idx
, m
, addr
, &mdp
->hw
);
333 if (IS_ERR_OR_NULL(cfg
)) {
335 return ERR_PTR(-EINVAL
);
343 _setup_mdp_ops(&mdp
->ops
, mdp
->caps
->features
);
345 dpu_hw_blk_init(&mdp
->base
, DPU_HW_BLK_TOP
, idx
, &dpu_hw_ops
);
350 void dpu_hw_mdp_destroy(struct dpu_hw_mdp
*mdp
)
353 dpu_hw_blk_destroy(&mdp
->base
);