1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
5 #include <linux/iopoll.h>
7 #include "dpu_hw_mdss.h"
9 #include "dpu_hw_catalog.h"
10 #include "dpu_hw_pingpong.h"
12 #include "dpu_trace.h"
14 #define PP_TEAR_CHECK_EN 0x000
15 #define PP_SYNC_CONFIG_VSYNC 0x004
16 #define PP_SYNC_CONFIG_HEIGHT 0x008
17 #define PP_SYNC_WRCOUNT 0x00C
18 #define PP_VSYNC_INIT_VAL 0x010
19 #define PP_INT_COUNT_VAL 0x014
20 #define PP_SYNC_THRESH 0x018
21 #define PP_START_POS 0x01C
22 #define PP_RD_PTR_IRQ 0x020
23 #define PP_WR_PTR_IRQ 0x024
24 #define PP_OUT_LINE_COUNT 0x028
25 #define PP_LINE_COUNT 0x02C
27 #define PP_FBC_MODE 0x034
28 #define PP_FBC_BUDGET_CTL 0x038
29 #define PP_FBC_LOSSY_MODE 0x03C
31 static const struct dpu_pingpong_cfg
*_pingpong_offset(enum dpu_pingpong pp
,
32 const struct dpu_mdss_cfg
*m
,
34 struct dpu_hw_blk_reg_map
*b
)
38 for (i
= 0; i
< m
->pingpong_count
; i
++) {
39 if (pp
== m
->pingpong
[i
].id
) {
41 b
->blk_off
= m
->pingpong
[i
].base
;
42 b
->length
= m
->pingpong
[i
].len
;
43 b
->hwversion
= m
->hwversion
;
44 b
->log_mask
= DPU_DBG_MASK_PINGPONG
;
45 return &m
->pingpong
[i
];
49 return ERR_PTR(-EINVAL
);
52 static int dpu_hw_pp_setup_te_config(struct dpu_hw_pingpong
*pp
,
53 struct dpu_hw_tear_check
*te
)
55 struct dpu_hw_blk_reg_map
*c
;
62 cfg
= BIT(19); /*VSYNC_COUNTER_EN */
63 if (te
->hw_vsync_mode
)
66 cfg
|= te
->vsync_count
;
68 DPU_REG_WRITE(c
, PP_SYNC_CONFIG_VSYNC
, cfg
);
69 DPU_REG_WRITE(c
, PP_SYNC_CONFIG_HEIGHT
, te
->sync_cfg_height
);
70 DPU_REG_WRITE(c
, PP_VSYNC_INIT_VAL
, te
->vsync_init_val
);
71 DPU_REG_WRITE(c
, PP_RD_PTR_IRQ
, te
->rd_ptr_irq
);
72 DPU_REG_WRITE(c
, PP_START_POS
, te
->start_pos
);
73 DPU_REG_WRITE(c
, PP_SYNC_THRESH
,
74 ((te
->sync_threshold_continue
<< 16) |
75 te
->sync_threshold_start
));
76 DPU_REG_WRITE(c
, PP_SYNC_WRCOUNT
,
77 (te
->start_pos
+ te
->sync_threshold_start
+ 1));
82 static int dpu_hw_pp_poll_timeout_wr_ptr(struct dpu_hw_pingpong
*pp
,
85 struct dpu_hw_blk_reg_map
*c
;
93 rc
= readl_poll_timeout(c
->base_off
+ c
->blk_off
+ PP_LINE_COUNT
,
94 val
, (val
& 0xffff) >= 1, 10, timeout_us
);
99 static int dpu_hw_pp_enable_te(struct dpu_hw_pingpong
*pp
, bool enable
)
101 struct dpu_hw_blk_reg_map
*c
;
107 DPU_REG_WRITE(c
, PP_TEAR_CHECK_EN
, enable
);
111 static int dpu_hw_pp_connect_external_te(struct dpu_hw_pingpong
*pp
,
112 bool enable_external_te
)
114 struct dpu_hw_blk_reg_map
*c
= &pp
->hw
;
122 cfg
= DPU_REG_READ(c
, PP_SYNC_CONFIG_VSYNC
);
123 orig
= (bool)(cfg
& BIT(20));
124 if (enable_external_te
)
128 DPU_REG_WRITE(c
, PP_SYNC_CONFIG_VSYNC
, cfg
);
129 trace_dpu_pp_connect_ext_te(pp
->idx
- PINGPONG_0
, cfg
);
134 static int dpu_hw_pp_get_vsync_info(struct dpu_hw_pingpong
*pp
,
135 struct dpu_hw_pp_vsync_info
*info
)
137 struct dpu_hw_blk_reg_map
*c
;
144 val
= DPU_REG_READ(c
, PP_VSYNC_INIT_VAL
);
145 info
->rd_ptr_init_val
= val
& 0xffff;
147 val
= DPU_REG_READ(c
, PP_INT_COUNT_VAL
);
148 info
->rd_ptr_frame_count
= (val
& 0xffff0000) >> 16;
149 info
->rd_ptr_line_count
= val
& 0xffff;
151 val
= DPU_REG_READ(c
, PP_LINE_COUNT
);
152 info
->wr_ptr_line_count
= val
& 0xffff;
157 static u32
dpu_hw_pp_get_line_count(struct dpu_hw_pingpong
*pp
)
159 struct dpu_hw_blk_reg_map
*c
= &pp
->hw
;
167 init
= DPU_REG_READ(c
, PP_VSYNC_INIT_VAL
) & 0xFFFF;
168 height
= DPU_REG_READ(c
, PP_SYNC_CONFIG_HEIGHT
) & 0xFFFF;
173 line
= DPU_REG_READ(c
, PP_INT_COUNT_VAL
) & 0xFFFF;
176 line
+= (0xFFFF - init
);
183 static void _setup_pingpong_ops(struct dpu_hw_pingpong_ops
*ops
,
184 const struct dpu_pingpong_cfg
*hw_cap
)
186 ops
->setup_tearcheck
= dpu_hw_pp_setup_te_config
;
187 ops
->enable_tearcheck
= dpu_hw_pp_enable_te
;
188 ops
->connect_external_te
= dpu_hw_pp_connect_external_te
;
189 ops
->get_vsync_info
= dpu_hw_pp_get_vsync_info
;
190 ops
->poll_timeout_wr_ptr
= dpu_hw_pp_poll_timeout_wr_ptr
;
191 ops
->get_line_count
= dpu_hw_pp_get_line_count
;
194 static struct dpu_hw_blk_ops dpu_hw_ops
;
196 struct dpu_hw_pingpong
*dpu_hw_pingpong_init(enum dpu_pingpong idx
,
198 const struct dpu_mdss_cfg
*m
)
200 struct dpu_hw_pingpong
*c
;
201 const struct dpu_pingpong_cfg
*cfg
;
203 c
= kzalloc(sizeof(*c
), GFP_KERNEL
);
205 return ERR_PTR(-ENOMEM
);
207 cfg
= _pingpong_offset(idx
, m
, addr
, &c
->hw
);
208 if (IS_ERR_OR_NULL(cfg
)) {
210 return ERR_PTR(-EINVAL
);
215 _setup_pingpong_ops(&c
->ops
, c
->caps
);
217 dpu_hw_blk_init(&c
->base
, DPU_HW_BLK_PINGPONG
, idx
, &dpu_hw_ops
);
222 void dpu_hw_pingpong_destroy(struct dpu_hw_pingpong
*pp
)
225 dpu_hw_blk_destroy(&pp
->base
);