treewide: remove redundant IS_ERR() before error code check
[linux/fpc-iii.git] / drivers / gpu / drm / msm / disp / dpu1 / dpu_hw_pingpong.c
blobd110a40f0e7308c99814399a08b1523d13687038
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
3 */
5 #include <linux/iopoll.h>
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"
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,
33 void __iomem *addr,
34 struct dpu_hw_blk_reg_map *b)
36 int i;
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];
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;
56 int cfg;
58 if (!pp || !te)
59 return -EINVAL;
60 c = &pp->hw;
62 cfg = BIT(19); /*VSYNC_COUNTER_EN */
63 if (te->hw_vsync_mode)
64 cfg |= BIT(20);
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));
79 return 0;
82 static int dpu_hw_pp_poll_timeout_wr_ptr(struct dpu_hw_pingpong *pp,
83 u32 timeout_us)
85 struct dpu_hw_blk_reg_map *c;
86 u32 val;
87 int rc;
89 if (!pp)
90 return -EINVAL;
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);
96 return rc;
99 static int dpu_hw_pp_enable_te(struct dpu_hw_pingpong *pp, bool enable)
101 struct dpu_hw_blk_reg_map *c;
103 if (!pp)
104 return -EINVAL;
105 c = &pp->hw;
107 DPU_REG_WRITE(c, PP_TEAR_CHECK_EN, enable);
108 return 0;
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;
115 u32 cfg;
116 int orig;
118 if (!pp)
119 return -EINVAL;
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);
131 return orig;
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;
138 u32 val;
140 if (!pp || !info)
141 return -EINVAL;
142 c = &pp->hw;
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;
154 return 0;
157 static u32 dpu_hw_pp_get_line_count(struct dpu_hw_pingpong *pp)
159 struct dpu_hw_blk_reg_map *c = &pp->hw;
160 u32 height, init;
161 u32 line = 0xFFFF;
163 if (!pp)
164 return 0;
165 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;
170 if (height < init)
171 return line;
173 line = DPU_REG_READ(c, PP_INT_COUNT_VAL) & 0xFFFF;
175 if (line < init)
176 line += (0xFFFF - init);
177 else
178 line -= init;
180 return line;
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,
197 void __iomem *addr,
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);
204 if (!c)
205 return ERR_PTR(-ENOMEM);
207 cfg = _pingpong_offset(idx, m, addr, &c->hw);
208 if (IS_ERR_OR_NULL(cfg)) {
209 kfree(c);
210 return ERR_PTR(-EINVAL);
213 c->idx = idx;
214 c->caps = cfg;
215 _setup_pingpong_ops(&c->ops, c->caps);
217 dpu_hw_blk_init(&c->base, DPU_HW_BLK_PINGPONG, idx, &dpu_hw_ops);
219 return c;
222 void dpu_hw_pingpong_destroy(struct dpu_hw_pingpong *pp)
224 if (pp)
225 dpu_hw_blk_destroy(&pp->base);
226 kfree(pp);