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
12 #define FLD_SPLIT_DISPLAY_CMD BIT(1)
13 #define FLD_SMART_PANEL_FREE_RUN BIT(2)
14 #define FLD_INTF_1_SW_TRG_MUX BIT(4)
15 #define FLD_INTF_2_SW_TRG_MUX BIT(8)
16 #define FLD_TE_LINE_INTER_WATERLEVEL_MASK 0xFFFF
18 #define DANGER_STATUS 0x360
19 #define SAFE_STATUS 0x364
21 #define TE_LINE_INTERVAL 0x3F4
23 #define TRAFFIC_SHAPER_EN BIT(31)
24 #define TRAFFIC_SHAPER_RD_CLIENT(num) (0x030 + (num * 4))
25 #define TRAFFIC_SHAPER_WR_CLIENT(num) (0x060 + (num * 4))
26 #define TRAFFIC_SHAPER_FIXPOINT_FACTOR 4
28 #define MDP_WD_TIMER_0_CTL 0x380
29 #define MDP_WD_TIMER_0_CTL2 0x384
30 #define MDP_WD_TIMER_0_LOAD_VALUE 0x388
31 #define MDP_WD_TIMER_1_CTL 0x390
32 #define MDP_WD_TIMER_1_CTL2 0x394
33 #define MDP_WD_TIMER_1_LOAD_VALUE 0x398
34 #define MDP_WD_TIMER_2_CTL 0x420
35 #define MDP_WD_TIMER_2_CTL2 0x424
36 #define MDP_WD_TIMER_2_LOAD_VALUE 0x428
37 #define MDP_WD_TIMER_3_CTL 0x430
38 #define MDP_WD_TIMER_3_CTL2 0x434
39 #define MDP_WD_TIMER_3_LOAD_VALUE 0x438
40 #define MDP_WD_TIMER_4_CTL 0x440
41 #define MDP_WD_TIMER_4_CTL2 0x444
42 #define MDP_WD_TIMER_4_LOAD_VALUE 0x448
44 #define MDP_TICK_COUNT 16
45 #define XO_CLK_RATE 19200
46 #define MS_TICKS_IN_SEC 1000
48 #define CALCULATE_WD_LOAD_VALUE(fps) \
49 ((uint32_t)((MS_TICKS_IN_SEC * XO_CLK_RATE)/(MDP_TICK_COUNT * fps)))
53 static void dpu_hw_setup_split_pipe(struct dpu_hw_mdp
*mdp
,
54 struct split_pipe_cfg
*cfg
)
56 struct dpu_hw_blk_reg_map
*c
;
66 if (cfg
->mode
== INTF_MODE_CMD
) {
67 lower_pipe
= FLD_SPLIT_DISPLAY_CMD
;
68 /* interface controlling sw trigger */
69 if (cfg
->intf
== INTF_2
)
70 lower_pipe
|= FLD_INTF_1_SW_TRG_MUX
;
72 lower_pipe
|= FLD_INTF_2_SW_TRG_MUX
;
73 upper_pipe
= lower_pipe
;
75 if (cfg
->intf
== INTF_2
) {
76 lower_pipe
= FLD_INTF_1_SW_TRG_MUX
;
77 upper_pipe
= FLD_INTF_2_SW_TRG_MUX
;
79 lower_pipe
= FLD_INTF_2_SW_TRG_MUX
;
80 upper_pipe
= FLD_INTF_1_SW_TRG_MUX
;
85 DPU_REG_WRITE(c
, SSPP_SPARE
, cfg
->split_flush_en
? 0x1 : 0x0);
86 DPU_REG_WRITE(c
, SPLIT_DISPLAY_LOWER_PIPE_CTRL
, lower_pipe
);
87 DPU_REG_WRITE(c
, SPLIT_DISPLAY_UPPER_PIPE_CTRL
, upper_pipe
);
88 DPU_REG_WRITE(c
, SPLIT_DISPLAY_EN
, cfg
->en
& 0x1);
91 static bool dpu_hw_setup_clk_force_ctrl(struct dpu_hw_mdp
*mdp
,
92 enum dpu_clk_ctrl_type clk_ctrl
, bool enable
)
94 struct dpu_hw_blk_reg_map
*c
;
104 if (clk_ctrl
<= DPU_CLK_CTRL_NONE
|| clk_ctrl
>= DPU_CLK_CTRL_MAX
)
107 reg_off
= mdp
->caps
->clk_ctrls
[clk_ctrl
].reg_off
;
108 bit_off
= mdp
->caps
->clk_ctrls
[clk_ctrl
].bit_off
;
110 reg_val
= DPU_REG_READ(c
, reg_off
);
113 new_val
= reg_val
| BIT(bit_off
);
115 new_val
= reg_val
& ~BIT(bit_off
);
117 DPU_REG_WRITE(c
, reg_off
, new_val
);
119 clk_forced_on
= !(reg_val
& BIT(bit_off
));
121 return clk_forced_on
;
125 static void dpu_hw_get_danger_status(struct dpu_hw_mdp
*mdp
,
126 struct dpu_danger_safe_status
*status
)
128 struct dpu_hw_blk_reg_map
*c
;
136 value
= DPU_REG_READ(c
, DANGER_STATUS
);
137 status
->mdp
= (value
>> 0) & 0x3;
138 status
->sspp
[SSPP_VIG0
] = (value
>> 4) & 0x3;
139 status
->sspp
[SSPP_VIG1
] = (value
>> 6) & 0x3;
140 status
->sspp
[SSPP_VIG2
] = (value
>> 8) & 0x3;
141 status
->sspp
[SSPP_VIG3
] = (value
>> 10) & 0x3;
142 status
->sspp
[SSPP_RGB0
] = (value
>> 12) & 0x3;
143 status
->sspp
[SSPP_RGB1
] = (value
>> 14) & 0x3;
144 status
->sspp
[SSPP_RGB2
] = (value
>> 16) & 0x3;
145 status
->sspp
[SSPP_RGB3
] = (value
>> 18) & 0x3;
146 status
->sspp
[SSPP_DMA0
] = (value
>> 20) & 0x3;
147 status
->sspp
[SSPP_DMA1
] = (value
>> 22) & 0x3;
148 status
->sspp
[SSPP_DMA2
] = (value
>> 28) & 0x3;
149 status
->sspp
[SSPP_DMA3
] = (value
>> 30) & 0x3;
150 status
->sspp
[SSPP_CURSOR0
] = (value
>> 24) & 0x3;
151 status
->sspp
[SSPP_CURSOR1
] = (value
>> 26) & 0x3;
154 static void dpu_hw_setup_vsync_source(struct dpu_hw_mdp
*mdp
,
155 struct dpu_vsync_source_cfg
*cfg
)
157 struct dpu_hw_blk_reg_map
*c
;
158 u32 reg
, wd_load_value
, wd_ctl
, wd_ctl2
, i
;
159 static const u32 pp_offset
[PINGPONG_MAX
] = {0xC, 0x8, 0x4, 0x13, 0x18};
161 if (!mdp
|| !cfg
|| (cfg
->pp_count
> ARRAY_SIZE(cfg
->ppnumber
)))
165 reg
= DPU_REG_READ(c
, MDP_VSYNC_SEL
);
166 for (i
= 0; i
< cfg
->pp_count
; i
++) {
167 int pp_idx
= cfg
->ppnumber
[i
] - PINGPONG_0
;
169 if (pp_idx
>= ARRAY_SIZE(pp_offset
))
172 reg
&= ~(0xf << pp_offset
[pp_idx
]);
173 reg
|= (cfg
->vsync_source
& 0xf) << pp_offset
[pp_idx
];
175 DPU_REG_WRITE(c
, MDP_VSYNC_SEL
, reg
);
177 if (cfg
->vsync_source
>= DPU_VSYNC_SOURCE_WD_TIMER_4
&&
178 cfg
->vsync_source
<= DPU_VSYNC_SOURCE_WD_TIMER_0
) {
179 switch (cfg
->vsync_source
) {
180 case DPU_VSYNC_SOURCE_WD_TIMER_4
:
181 wd_load_value
= MDP_WD_TIMER_4_LOAD_VALUE
;
182 wd_ctl
= MDP_WD_TIMER_4_CTL
;
183 wd_ctl2
= MDP_WD_TIMER_4_CTL2
;
185 case DPU_VSYNC_SOURCE_WD_TIMER_3
:
186 wd_load_value
= MDP_WD_TIMER_3_LOAD_VALUE
;
187 wd_ctl
= MDP_WD_TIMER_3_CTL
;
188 wd_ctl2
= MDP_WD_TIMER_3_CTL2
;
190 case DPU_VSYNC_SOURCE_WD_TIMER_2
:
191 wd_load_value
= MDP_WD_TIMER_2_LOAD_VALUE
;
192 wd_ctl
= MDP_WD_TIMER_2_CTL
;
193 wd_ctl2
= MDP_WD_TIMER_2_CTL2
;
195 case DPU_VSYNC_SOURCE_WD_TIMER_1
:
196 wd_load_value
= MDP_WD_TIMER_1_LOAD_VALUE
;
197 wd_ctl
= MDP_WD_TIMER_1_CTL
;
198 wd_ctl2
= MDP_WD_TIMER_1_CTL2
;
200 case DPU_VSYNC_SOURCE_WD_TIMER_0
:
202 wd_load_value
= MDP_WD_TIMER_0_LOAD_VALUE
;
203 wd_ctl
= MDP_WD_TIMER_0_CTL
;
204 wd_ctl2
= MDP_WD_TIMER_0_CTL2
;
208 DPU_REG_WRITE(c
, wd_load_value
,
209 CALCULATE_WD_LOAD_VALUE(cfg
->frame_rate
));
211 DPU_REG_WRITE(c
, wd_ctl
, BIT(0)); /* clear timer */
212 reg
= DPU_REG_READ(c
, wd_ctl2
);
213 reg
|= BIT(8); /* enable heartbeat timer */
214 reg
|= BIT(0); /* enable WD timer */
215 DPU_REG_WRITE(c
, wd_ctl2
, reg
);
217 /* make sure that timers are enabled/disabled for vsync state */
222 static void dpu_hw_get_safe_status(struct dpu_hw_mdp
*mdp
,
223 struct dpu_danger_safe_status
*status
)
225 struct dpu_hw_blk_reg_map
*c
;
233 value
= DPU_REG_READ(c
, SAFE_STATUS
);
234 status
->mdp
= (value
>> 0) & 0x1;
235 status
->sspp
[SSPP_VIG0
] = (value
>> 4) & 0x1;
236 status
->sspp
[SSPP_VIG1
] = (value
>> 6) & 0x1;
237 status
->sspp
[SSPP_VIG2
] = (value
>> 8) & 0x1;
238 status
->sspp
[SSPP_VIG3
] = (value
>> 10) & 0x1;
239 status
->sspp
[SSPP_RGB0
] = (value
>> 12) & 0x1;
240 status
->sspp
[SSPP_RGB1
] = (value
>> 14) & 0x1;
241 status
->sspp
[SSPP_RGB2
] = (value
>> 16) & 0x1;
242 status
->sspp
[SSPP_RGB3
] = (value
>> 18) & 0x1;
243 status
->sspp
[SSPP_DMA0
] = (value
>> 20) & 0x1;
244 status
->sspp
[SSPP_DMA1
] = (value
>> 22) & 0x1;
245 status
->sspp
[SSPP_DMA2
] = (value
>> 28) & 0x1;
246 status
->sspp
[SSPP_DMA3
] = (value
>> 30) & 0x1;
247 status
->sspp
[SSPP_CURSOR0
] = (value
>> 24) & 0x1;
248 status
->sspp
[SSPP_CURSOR1
] = (value
>> 26) & 0x1;
251 static void dpu_hw_intf_audio_select(struct dpu_hw_mdp
*mdp
)
253 struct dpu_hw_blk_reg_map
*c
;
260 DPU_REG_WRITE(c
, HDMI_DP_CORE_SELECT
, 0x1);
263 static void _setup_mdp_ops(struct dpu_hw_mdp_ops
*ops
,
266 ops
->setup_split_pipe
= dpu_hw_setup_split_pipe
;
267 ops
->setup_clk_force_ctrl
= dpu_hw_setup_clk_force_ctrl
;
268 ops
->get_danger_status
= dpu_hw_get_danger_status
;
269 ops
->setup_vsync_source
= dpu_hw_setup_vsync_source
;
270 ops
->get_safe_status
= dpu_hw_get_safe_status
;
271 ops
->intf_audio_select
= dpu_hw_intf_audio_select
;
274 static const struct dpu_mdp_cfg
*_top_offset(enum dpu_mdp mdp
,
275 const struct dpu_mdss_cfg
*m
,
277 struct dpu_hw_blk_reg_map
*b
)
281 if (!m
|| !addr
|| !b
)
282 return ERR_PTR(-EINVAL
);
284 for (i
= 0; i
< m
->mdp_count
; i
++) {
285 if (mdp
== m
->mdp
[i
].id
) {
287 b
->blk_off
= m
->mdp
[i
].base
;
288 b
->length
= m
->mdp
[i
].len
;
289 b
->hwversion
= m
->hwversion
;
290 b
->log_mask
= DPU_DBG_MASK_TOP
;
295 return ERR_PTR(-EINVAL
);
298 static struct dpu_hw_blk_ops dpu_hw_ops
;
300 struct dpu_hw_mdp
*dpu_hw_mdptop_init(enum dpu_mdp idx
,
302 const struct dpu_mdss_cfg
*m
)
304 struct dpu_hw_mdp
*mdp
;
305 const struct dpu_mdp_cfg
*cfg
;
308 return ERR_PTR(-EINVAL
);
310 mdp
= kzalloc(sizeof(*mdp
), GFP_KERNEL
);
312 return ERR_PTR(-ENOMEM
);
314 cfg
= _top_offset(idx
, m
, addr
, &mdp
->hw
);
315 if (IS_ERR_OR_NULL(cfg
)) {
317 return ERR_PTR(-EINVAL
);
325 _setup_mdp_ops(&mdp
->ops
, mdp
->caps
->features
);
327 dpu_hw_blk_init(&mdp
->base
, DPU_HW_BLK_TOP
, idx
, &dpu_hw_ops
);
332 void dpu_hw_mdp_destroy(struct dpu_hw_mdp
*mdp
)
335 dpu_hw_blk_destroy(&mdp
->base
);