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 #define PP_DITHER_EN 0x000
32 #define PP_DITHER_BITDEPTH 0x004
33 #define PP_DITHER_MATRIX 0x008
35 #define DITHER_DEPTH_MAP_INDEX 9
37 static u32 dither_depth_map
[DITHER_DEPTH_MAP_INDEX
] = {
38 0, 0, 0, 0, 0, 0, 0, 1, 2
41 static const struct dpu_pingpong_cfg
*_pingpong_offset(enum dpu_pingpong pp
,
42 const struct dpu_mdss_cfg
*m
,
44 struct dpu_hw_blk_reg_map
*b
)
48 for (i
= 0; i
< m
->pingpong_count
; i
++) {
49 if (pp
== m
->pingpong
[i
].id
) {
51 b
->blk_off
= m
->pingpong
[i
].base
;
52 b
->length
= m
->pingpong
[i
].len
;
53 b
->hwversion
= m
->hwversion
;
54 b
->log_mask
= DPU_DBG_MASK_PINGPONG
;
55 return &m
->pingpong
[i
];
59 return ERR_PTR(-EINVAL
);
62 static void dpu_hw_pp_setup_dither(struct dpu_hw_pingpong
*pp
,
63 struct dpu_hw_dither_cfg
*cfg
)
65 struct dpu_hw_blk_reg_map
*c
;
66 u32 i
, base
, data
= 0;
69 base
= pp
->caps
->sblk
->dither
.base
;
71 DPU_REG_WRITE(c
, base
+ PP_DITHER_EN
, 0);
75 data
= dither_depth_map
[cfg
->c0_bitdepth
] & REG_MASK(2);
76 data
|= (dither_depth_map
[cfg
->c1_bitdepth
] & REG_MASK(2)) << 2;
77 data
|= (dither_depth_map
[cfg
->c2_bitdepth
] & REG_MASK(2)) << 4;
78 data
|= (dither_depth_map
[cfg
->c3_bitdepth
] & REG_MASK(2)) << 6;
79 data
|= (cfg
->temporal_en
) ? (1 << 8) : 0;
81 DPU_REG_WRITE(c
, base
+ PP_DITHER_BITDEPTH
, data
);
83 for (i
= 0; i
< DITHER_MATRIX_SZ
- 3; i
+= 4) {
84 data
= (cfg
->matrix
[i
] & REG_MASK(4)) |
85 ((cfg
->matrix
[i
+ 1] & REG_MASK(4)) << 4) |
86 ((cfg
->matrix
[i
+ 2] & REG_MASK(4)) << 8) |
87 ((cfg
->matrix
[i
+ 3] & REG_MASK(4)) << 12);
88 DPU_REG_WRITE(c
, base
+ PP_DITHER_MATRIX
+ i
, data
);
90 DPU_REG_WRITE(c
, base
+ PP_DITHER_EN
, 1);
93 static int dpu_hw_pp_setup_te_config(struct dpu_hw_pingpong
*pp
,
94 struct dpu_hw_tear_check
*te
)
96 struct dpu_hw_blk_reg_map
*c
;
103 cfg
= BIT(19); /*VSYNC_COUNTER_EN */
104 if (te
->hw_vsync_mode
)
107 cfg
|= te
->vsync_count
;
109 DPU_REG_WRITE(c
, PP_SYNC_CONFIG_VSYNC
, cfg
);
110 DPU_REG_WRITE(c
, PP_SYNC_CONFIG_HEIGHT
, te
->sync_cfg_height
);
111 DPU_REG_WRITE(c
, PP_VSYNC_INIT_VAL
, te
->vsync_init_val
);
112 DPU_REG_WRITE(c
, PP_RD_PTR_IRQ
, te
->rd_ptr_irq
);
113 DPU_REG_WRITE(c
, PP_START_POS
, te
->start_pos
);
114 DPU_REG_WRITE(c
, PP_SYNC_THRESH
,
115 ((te
->sync_threshold_continue
<< 16) |
116 te
->sync_threshold_start
));
117 DPU_REG_WRITE(c
, PP_SYNC_WRCOUNT
,
118 (te
->start_pos
+ te
->sync_threshold_start
+ 1));
123 static int dpu_hw_pp_poll_timeout_wr_ptr(struct dpu_hw_pingpong
*pp
,
126 struct dpu_hw_blk_reg_map
*c
;
134 rc
= readl_poll_timeout(c
->base_off
+ c
->blk_off
+ PP_LINE_COUNT
,
135 val
, (val
& 0xffff) >= 1, 10, timeout_us
);
140 static int dpu_hw_pp_enable_te(struct dpu_hw_pingpong
*pp
, bool enable
)
142 struct dpu_hw_blk_reg_map
*c
;
148 DPU_REG_WRITE(c
, PP_TEAR_CHECK_EN
, enable
);
152 static int dpu_hw_pp_connect_external_te(struct dpu_hw_pingpong
*pp
,
153 bool enable_external_te
)
155 struct dpu_hw_blk_reg_map
*c
= &pp
->hw
;
163 cfg
= DPU_REG_READ(c
, PP_SYNC_CONFIG_VSYNC
);
164 orig
= (bool)(cfg
& BIT(20));
165 if (enable_external_te
)
169 DPU_REG_WRITE(c
, PP_SYNC_CONFIG_VSYNC
, cfg
);
170 trace_dpu_pp_connect_ext_te(pp
->idx
- PINGPONG_0
, cfg
);
175 static int dpu_hw_pp_get_vsync_info(struct dpu_hw_pingpong
*pp
,
176 struct dpu_hw_pp_vsync_info
*info
)
178 struct dpu_hw_blk_reg_map
*c
;
185 val
= DPU_REG_READ(c
, PP_VSYNC_INIT_VAL
);
186 info
->rd_ptr_init_val
= val
& 0xffff;
188 val
= DPU_REG_READ(c
, PP_INT_COUNT_VAL
);
189 info
->rd_ptr_frame_count
= (val
& 0xffff0000) >> 16;
190 info
->rd_ptr_line_count
= val
& 0xffff;
192 val
= DPU_REG_READ(c
, PP_LINE_COUNT
);
193 info
->wr_ptr_line_count
= val
& 0xffff;
198 static u32
dpu_hw_pp_get_line_count(struct dpu_hw_pingpong
*pp
)
200 struct dpu_hw_blk_reg_map
*c
= &pp
->hw
;
208 init
= DPU_REG_READ(c
, PP_VSYNC_INIT_VAL
) & 0xFFFF;
209 height
= DPU_REG_READ(c
, PP_SYNC_CONFIG_HEIGHT
) & 0xFFFF;
214 line
= DPU_REG_READ(c
, PP_INT_COUNT_VAL
) & 0xFFFF;
217 line
+= (0xFFFF - init
);
224 static void _setup_pingpong_ops(struct dpu_hw_pingpong
*c
,
225 unsigned long features
)
227 c
->ops
.setup_tearcheck
= dpu_hw_pp_setup_te_config
;
228 c
->ops
.enable_tearcheck
= dpu_hw_pp_enable_te
;
229 c
->ops
.connect_external_te
= dpu_hw_pp_connect_external_te
;
230 c
->ops
.get_vsync_info
= dpu_hw_pp_get_vsync_info
;
231 c
->ops
.poll_timeout_wr_ptr
= dpu_hw_pp_poll_timeout_wr_ptr
;
232 c
->ops
.get_line_count
= dpu_hw_pp_get_line_count
;
234 if (test_bit(DPU_PINGPONG_DITHER
, &features
))
235 c
->ops
.setup_dither
= dpu_hw_pp_setup_dither
;
238 static struct dpu_hw_blk_ops dpu_hw_ops
;
240 struct dpu_hw_pingpong
*dpu_hw_pingpong_init(enum dpu_pingpong idx
,
242 const struct dpu_mdss_cfg
*m
)
244 struct dpu_hw_pingpong
*c
;
245 const struct dpu_pingpong_cfg
*cfg
;
247 c
= kzalloc(sizeof(*c
), GFP_KERNEL
);
249 return ERR_PTR(-ENOMEM
);
251 cfg
= _pingpong_offset(idx
, m
, addr
, &c
->hw
);
252 if (IS_ERR_OR_NULL(cfg
)) {
254 return ERR_PTR(-EINVAL
);
259 _setup_pingpong_ops(c
, c
->caps
->features
);
261 dpu_hw_blk_init(&c
->base
, DPU_HW_BLK_PINGPONG
, idx
, &dpu_hw_ops
);
266 void dpu_hw_pingpong_destroy(struct dpu_hw_pingpong
*pp
)
269 dpu_hw_blk_destroy(&pp
->base
);