1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
3  */
4 
5 #include <linux/iopoll.h>
6 
7 #include "dpu_hw_mdss.h"
8 #include "dpu_hwio.h"
9 #include "dpu_hw_catalog.h"
10 #include "dpu_hw_pingpong.h"
11 #include "dpu_kms.h"
12 #include "dpu_trace.h"
13 
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
26 
27 #define PP_FBC_MODE                     0x034
28 #define PP_FBC_BUDGET_CTL               0x038
29 #define PP_FBC_LOSSY_MODE               0x03C
30 
31 static const struct dpu_pingpong_cfg *_pingpong_offset(enum dpu_pingpong pp,
32 		const struct dpu_mdss_cfg *m,
33 		void __iomem *addr,
34 		struct dpu_hw_blk_reg_map *b)
35 {
36 	int i;
37 
38 	for (i = 0; i < m->pingpong_count; i++) {
39 		if (pp == m->pingpong[i].id) {
40 			b->base_off = addr;
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];
46 		}
47 	}
48 
49 	return ERR_PTR(-EINVAL);
50 }
51 
52 static int dpu_hw_pp_setup_te_config(struct dpu_hw_pingpong *pp,
53 		struct dpu_hw_tear_check *te)
54 {
55 	struct dpu_hw_blk_reg_map *c;
56 	int cfg;
57 
58 	if (!pp || !te)
59 		return -EINVAL;
60 	c = &pp->hw;
61 
62 	cfg = BIT(19); /*VSYNC_COUNTER_EN */
63 	if (te->hw_vsync_mode)
64 		cfg |= BIT(20);
65 
66 	cfg |= te->vsync_count;
67 
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));
78 
79 	return 0;
80 }
81 
82 static int dpu_hw_pp_poll_timeout_wr_ptr(struct dpu_hw_pingpong *pp,
83 		u32 timeout_us)
84 {
85 	struct dpu_hw_blk_reg_map *c;
86 	u32 val;
87 	int rc;
88 
89 	if (!pp)
90 		return -EINVAL;
91 
92 	c = &pp->hw;
93 	rc = readl_poll_timeout(c->base_off + c->blk_off + PP_LINE_COUNT,
94 			val, (val & 0xffff) >= 1, 10, timeout_us);
95 
96 	return rc;
97 }
98 
99 static int dpu_hw_pp_enable_te(struct dpu_hw_pingpong *pp, bool enable)
100 {
101 	struct dpu_hw_blk_reg_map *c;
102 
103 	if (!pp)
104 		return -EINVAL;
105 	c = &pp->hw;
106 
107 	DPU_REG_WRITE(c, PP_TEAR_CHECK_EN, enable);
108 	return 0;
109 }
110 
111 static int dpu_hw_pp_connect_external_te(struct dpu_hw_pingpong *pp,
112 		bool enable_external_te)
113 {
114 	struct dpu_hw_blk_reg_map *c = &pp->hw;
115 	u32 cfg;
116 	int orig;
117 
118 	if (!pp)
119 		return -EINVAL;
120 
121 	c = &pp->hw;
122 	cfg = DPU_REG_READ(c, PP_SYNC_CONFIG_VSYNC);
123 	orig = (bool)(cfg & BIT(20));
124 	if (enable_external_te)
125 		cfg |= BIT(20);
126 	else
127 		cfg &= ~BIT(20);
128 	DPU_REG_WRITE(c, PP_SYNC_CONFIG_VSYNC, cfg);
129 	trace_dpu_pp_connect_ext_te(pp->idx - PINGPONG_0, cfg);
130 
131 	return orig;
132 }
133 
134 static int dpu_hw_pp_get_vsync_info(struct dpu_hw_pingpong *pp,
135 		struct dpu_hw_pp_vsync_info *info)
136 {
137 	struct dpu_hw_blk_reg_map *c;
138 	u32 val;
139 
140 	if (!pp || !info)
141 		return -EINVAL;
142 	c = &pp->hw;
143 
144 	val = DPU_REG_READ(c, PP_VSYNC_INIT_VAL);
145 	info->rd_ptr_init_val = val & 0xffff;
146 
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;
150 
151 	val = DPU_REG_READ(c, PP_LINE_COUNT);
152 	info->wr_ptr_line_count = val & 0xffff;
153 
154 	return 0;
155 }
156 
157 static u32 dpu_hw_pp_get_line_count(struct dpu_hw_pingpong *pp)
158 {
159 	struct dpu_hw_blk_reg_map *c = &pp->hw;
160 	u32 height, init;
161 	u32 line = 0xFFFF;
162 
163 	if (!pp)
164 		return 0;
165 	c = &pp->hw;
166 
167 	init = DPU_REG_READ(c, PP_VSYNC_INIT_VAL) & 0xFFFF;
168 	height = DPU_REG_READ(c, PP_SYNC_CONFIG_HEIGHT) & 0xFFFF;
169 
170 	if (height < init)
171 		return line;
172 
173 	line = DPU_REG_READ(c, PP_INT_COUNT_VAL) & 0xFFFF;
174 
175 	if (line < init)
176 		line += (0xFFFF - init);
177 	else
178 		line -= init;
179 
180 	return line;
181 }
182 
183 static void _setup_pingpong_ops(struct dpu_hw_pingpong_ops *ops,
184 	const struct dpu_pingpong_cfg *hw_cap)
185 {
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;
192 };
193 
194 static struct dpu_hw_blk_ops dpu_hw_ops;
195 
196 struct dpu_hw_pingpong *dpu_hw_pingpong_init(enum dpu_pingpong idx,
197 		void __iomem *addr,
198 		const struct dpu_mdss_cfg *m)
199 {
200 	struct dpu_hw_pingpong *c;
201 	const struct dpu_pingpong_cfg *cfg;
202 
203 	c = kzalloc(sizeof(*c), GFP_KERNEL);
204 	if (!c)
205 		return ERR_PTR(-ENOMEM);
206 
207 	cfg = _pingpong_offset(idx, m, addr, &c->hw);
208 	if (IS_ERR_OR_NULL(cfg)) {
209 		kfree(c);
210 		return ERR_PTR(-EINVAL);
211 	}
212 
213 	c->idx = idx;
214 	c->caps = cfg;
215 	_setup_pingpong_ops(&c->ops, c->caps);
216 
217 	dpu_hw_blk_init(&c->base, DPU_HW_BLK_PINGPONG, idx, &dpu_hw_ops);
218 
219 	return c;
220 }
221 
222 void dpu_hw_pingpong_destroy(struct dpu_hw_pingpong *pp)
223 {
224 	if (pp)
225 		dpu_hw_blk_destroy(&pp->base);
226 	kfree(pp);
227 }
228