blob: e844bc0e2a67b3156ba1298b299dd7b3e567ab6b [file] [log] [blame]
Lloyd Atkinson77158732016-10-23 13:02:00 -04001/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
Narendra Muppalla1b0b3352015-09-29 10:16:51 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
Lloyd Atkinson45da92f2017-02-28 14:34:51 -050013#include <linux/iopoll.h>
14
Narendra Muppalla1b0b3352015-09-29 10:16:51 -070015#include "sde_hw_mdss.h"
16#include "sde_hwio.h"
17#include "sde_hw_catalog.h"
18#include "sde_hw_pingpong.h"
Lloyd Atkinson113aefd2016-10-23 13:15:18 -040019#include "sde_dbg.h"
Lloyd Atkinson652e59b2017-05-03 11:20:30 -040020#include "sde_kms.h"
Narendra Muppalla1b0b3352015-09-29 10:16:51 -070021
22#define PP_TEAR_CHECK_EN 0x000
23#define PP_SYNC_CONFIG_VSYNC 0x004
24#define PP_SYNC_CONFIG_HEIGHT 0x008
25#define PP_SYNC_WRCOUNT 0x00C
26#define PP_VSYNC_INIT_VAL 0x010
27#define PP_INT_COUNT_VAL 0x014
28#define PP_SYNC_THRESH 0x018
29#define PP_START_POS 0x01C
30#define PP_RD_PTR_IRQ 0x020
31#define PP_WR_PTR_IRQ 0x024
32#define PP_OUT_LINE_COUNT 0x028
33#define PP_LINE_COUNT 0x02C
34#define PP_AUTOREFRESH_CONFIG 0x030
35
36#define PP_FBC_MODE 0x034
37#define PP_FBC_BUDGET_CTL 0x038
38#define PP_FBC_LOSSY_MODE 0x03C
39#define PP_DSC_MODE 0x0a0
40#define PP_DCE_DATA_IN_SWAP 0x0ac
41#define PP_DCE_DATA_OUT_SWAP 0x0c8
42
Ping Li8430ee12017-02-24 14:14:44 -080043#define DITHER_DEPTH_MAP_INDEX 9
44static u32 dither_depth_map[DITHER_DEPTH_MAP_INDEX] = {
45 0, 0, 0, 0, 0, 1, 2, 3, 3
46};
47
Narendra Muppalla1b0b3352015-09-29 10:16:51 -070048static struct sde_pingpong_cfg *_pingpong_offset(enum sde_pingpong pp,
49 struct sde_mdss_cfg *m,
50 void __iomem *addr,
51 struct sde_hw_blk_reg_map *b)
52{
53 int i;
54
55 for (i = 0; i < m->pingpong_count; i++) {
56 if (pp == m->pingpong[i].id) {
57 b->base_off = addr;
58 b->blk_off = m->pingpong[i].base;
Lloyd Atkinson77158732016-10-23 13:02:00 -040059 b->length = m->pingpong[i].len;
Narendra Muppalla1b0b3352015-09-29 10:16:51 -070060 b->hwversion = m->hwversion;
Clarence Ip4ce59322016-06-26 22:27:51 -040061 b->log_mask = SDE_DBG_MASK_PINGPONG;
Narendra Muppalla1b0b3352015-09-29 10:16:51 -070062 return &m->pingpong[i];
63 }
64 }
65
66 return ERR_PTR(-EINVAL);
67}
68
69static int sde_hw_pp_setup_te_config(struct sde_hw_pingpong *pp,
70 struct sde_hw_tear_check *te)
71{
72 struct sde_hw_blk_reg_map *c = &pp->hw;
73 int cfg;
74
75 cfg = BIT(19); /*VSYNC_COUNTER_EN */
76 if (te->hw_vsync_mode)
77 cfg |= BIT(20);
78
79 cfg |= te->vsync_count;
80
81 SDE_REG_WRITE(c, PP_SYNC_CONFIG_VSYNC, cfg);
82 SDE_REG_WRITE(c, PP_SYNC_CONFIG_HEIGHT, te->sync_cfg_height);
83 SDE_REG_WRITE(c, PP_VSYNC_INIT_VAL, te->vsync_init_val);
84 SDE_REG_WRITE(c, PP_RD_PTR_IRQ, te->rd_ptr_irq);
85 SDE_REG_WRITE(c, PP_START_POS, te->start_pos);
86 SDE_REG_WRITE(c, PP_SYNC_THRESH,
87 ((te->sync_threshold_continue << 16) |
88 te->sync_threshold_start));
89 SDE_REG_WRITE(c, PP_SYNC_WRCOUNT,
90 (te->start_pos + te->sync_threshold_start + 1));
91
92 return 0;
93}
94
Lloyd Atkinson45da92f2017-02-28 14:34:51 -050095static int sde_hw_pp_setup_autorefresh_config(struct sde_hw_pingpong *pp,
Narendra Muppalla1b0b3352015-09-29 10:16:51 -070096 struct sde_hw_autorefresh *cfg)
97{
Lloyd Atkinson45da92f2017-02-28 14:34:51 -050098 struct sde_hw_blk_reg_map *c;
Narendra Muppalla1b0b3352015-09-29 10:16:51 -070099 u32 refresh_cfg;
100
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500101 if (!pp || !cfg)
102 return -EINVAL;
103 c = &pp->hw;
104
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700105 if (cfg->enable)
106 refresh_cfg = BIT(31) | cfg->frame_count;
107 else
108 refresh_cfg = 0;
109
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500110 SDE_REG_WRITE(c, PP_AUTOREFRESH_CONFIG, refresh_cfg);
111 SDE_EVT32(pp->idx - PINGPONG_0, refresh_cfg);
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700112
113 return 0;
114}
115
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500116static int sde_hw_pp_get_autorefresh_config(struct sde_hw_pingpong *pp,
117 struct sde_hw_autorefresh *cfg)
118{
119 struct sde_hw_blk_reg_map *c;
120 u32 val;
121
122 if (!pp || !cfg)
123 return -EINVAL;
124
125 c = &pp->hw;
126 val = SDE_REG_READ(c, PP_AUTOREFRESH_CONFIG);
127 cfg->enable = (val & BIT(31)) >> 31;
128 cfg->frame_count = val & 0xffff;
129
130 return 0;
131}
132
133static int sde_hw_pp_poll_timeout_wr_ptr(struct sde_hw_pingpong *pp,
134 u32 timeout_us)
135{
136 struct sde_hw_blk_reg_map *c;
137 u32 val;
138 int rc;
139
140 if (!pp)
141 return -EINVAL;
142
143 c = &pp->hw;
144 rc = readl_poll_timeout(c->base_off + c->blk_off + PP_LINE_COUNT,
145 val, (val & 0xffff) >= 1, 10, timeout_us);
146
147 return rc;
148}
149
150static void sde_hw_pp_dsc_enable(struct sde_hw_pingpong *pp)
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700151{
Jeykumar Sankaran5c2f0702017-03-09 18:03:15 -0800152 struct sde_hw_blk_reg_map *c = &pp->hw;
153
154 SDE_REG_WRITE(c, PP_DSC_MODE, 1);
155}
156
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500157static void sde_hw_pp_dsc_disable(struct sde_hw_pingpong *pp)
Jeykumar Sankaran5c2f0702017-03-09 18:03:15 -0800158{
159 struct sde_hw_blk_reg_map *c = &pp->hw;
160
161 SDE_REG_WRITE(c, PP_DSC_MODE, 0);
162}
163
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500164static int sde_hw_pp_setup_dsc(struct sde_hw_pingpong *pp)
Jeykumar Sankaran5c2f0702017-03-09 18:03:15 -0800165{
166 struct sde_hw_blk_reg_map *pp_c = &pp->hw;
167 int data;
168
169 data = SDE_REG_READ(pp_c, PP_DCE_DATA_OUT_SWAP);
170 data |= BIT(18); /* endian flip */
171 SDE_REG_WRITE(pp_c, PP_DCE_DATA_OUT_SWAP, data);
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700172 return 0;
173}
Jeykumar Sankaran5c2f0702017-03-09 18:03:15 -0800174
Ping Li8430ee12017-02-24 14:14:44 -0800175static int sde_hw_pp_setup_dither_v1(struct sde_hw_pingpong *pp,
176 void *cfg, size_t len)
177{
178 struct sde_hw_blk_reg_map *c;
179 struct drm_msm_dither *dither = (struct drm_msm_dither *)cfg;
180 u32 base = 0, offset = 0, data = 0, i = 0;
181
182 if (!pp)
183 return -EINVAL;
184
185 c = &pp->hw;
186 base = pp->caps->sblk->dither.base;
187 if (!dither) {
188 /* dither property disable case */
189 SDE_REG_WRITE(c, base, 0);
190 return 0;
191 }
192
193 if (len != sizeof(struct drm_msm_dither)) {
194 DRM_ERROR("input len %zu, expected len %zu\n", len,
195 sizeof(struct drm_msm_dither));
196 return -EINVAL;
197 }
198
199 if (dither->c0_bitdepth >= DITHER_DEPTH_MAP_INDEX ||
200 dither->c1_bitdepth >= DITHER_DEPTH_MAP_INDEX ||
201 dither->c2_bitdepth >= DITHER_DEPTH_MAP_INDEX ||
202 dither->c3_bitdepth >= DITHER_DEPTH_MAP_INDEX)
203 return -EINVAL;
204
205 offset += 4;
206 data = dither_depth_map[dither->c0_bitdepth] & REG_MASK(2);
207 data |= (dither_depth_map[dither->c1_bitdepth] & REG_MASK(2)) << 2;
208 data |= (dither_depth_map[dither->c2_bitdepth] & REG_MASK(2)) << 4;
209 data |= (dither_depth_map[dither->c3_bitdepth] & REG_MASK(2)) << 6;
210 data |= (dither->temporal_en) ? (1 << 8) : 0;
211 SDE_REG_WRITE(c, base + offset, data);
212
213 for (i = 0; i < DITHER_MATRIX_SZ - 3; i += 4) {
214 offset += 4;
215 data = (dither->matrix[i] & REG_MASK(4)) |
216 ((dither->matrix[i + 1] & REG_MASK(4)) << 4) |
217 ((dither->matrix[i + 2] & REG_MASK(4)) << 8) |
218 ((dither->matrix[i + 3] & REG_MASK(4)) << 12);
219 SDE_REG_WRITE(c, base + offset, data);
220 }
221 SDE_REG_WRITE(c, base, 1);
222
223 return 0;
224}
225
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500226static int sde_hw_pp_enable_te(struct sde_hw_pingpong *pp, bool enable)
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700227{
228 struct sde_hw_blk_reg_map *c = &pp->hw;
229
230 SDE_REG_WRITE(c, PP_TEAR_CHECK_EN, enable);
231 return 0;
232}
233
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500234static int sde_hw_pp_get_vsync_info(struct sde_hw_pingpong *pp,
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700235 struct sde_hw_pp_vsync_info *info)
236{
237 struct sde_hw_blk_reg_map *c = &pp->hw;
Lloyd Atkinson3127a092016-05-30 13:46:55 -0400238 u32 val;
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700239
Lloyd Atkinson3127a092016-05-30 13:46:55 -0400240 val = SDE_REG_READ(c, PP_VSYNC_INIT_VAL);
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500241 info->rd_ptr_init_val = val & 0xffff;
Lloyd Atkinson3127a092016-05-30 13:46:55 -0400242
243 val = SDE_REG_READ(c, PP_INT_COUNT_VAL);
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500244 info->rd_ptr_frame_count = (val & 0xffff0000) >> 16;
245 info->rd_ptr_line_count = val & 0xffff;
246
247 val = SDE_REG_READ(c, PP_LINE_COUNT);
248 info->wr_ptr_line_count = val & 0xffff;
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700249
250 return 0;
251}
252
253static void _setup_pingpong_ops(struct sde_hw_pingpong_ops *ops,
Ping Li8430ee12017-02-24 14:14:44 -0800254 const struct sde_pingpong_cfg *hw_cap)
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700255{
Ping Li8430ee12017-02-24 14:14:44 -0800256 u32 version = 0;
257
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700258 ops->setup_tearcheck = sde_hw_pp_setup_te_config;
259 ops->enable_tearcheck = sde_hw_pp_enable_te;
260 ops->get_vsync_info = sde_hw_pp_get_vsync_info;
261 ops->setup_autorefresh = sde_hw_pp_setup_autorefresh_config;
Jeykumar Sankaran5c2f0702017-03-09 18:03:15 -0800262 ops->setup_dsc = sde_hw_pp_setup_dsc;
263 ops->enable_dsc = sde_hw_pp_dsc_enable;
264 ops->disable_dsc = sde_hw_pp_dsc_disable;
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500265 ops->get_autorefresh = sde_hw_pp_get_autorefresh_config;
266 ops->poll_timeout_wr_ptr = sde_hw_pp_poll_timeout_wr_ptr;
Ping Li8430ee12017-02-24 14:14:44 -0800267
268 version = SDE_COLOR_PROCESS_MAJOR(hw_cap->sblk->dither.version);
269 switch (version) {
270 case 1:
271 ops->setup_dither = sde_hw_pp_setup_dither_v1;
272 break;
273 default:
274 ops->setup_dither = NULL;
275 break;
276 }
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700277};
278
Lloyd Atkinson652e59b2017-05-03 11:20:30 -0400279static struct sde_hw_blk_ops sde_hw_ops = {
280 .start = NULL,
281 .stop = NULL,
282};
283
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700284struct sde_hw_pingpong *sde_hw_pingpong_init(enum sde_pingpong idx,
285 void __iomem *addr,
286 struct sde_mdss_cfg *m)
287{
288 struct sde_hw_pingpong *c;
289 struct sde_pingpong_cfg *cfg;
Lloyd Atkinson652e59b2017-05-03 11:20:30 -0400290 int rc;
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700291
292 c = kzalloc(sizeof(*c), GFP_KERNEL);
293 if (!c)
294 return ERR_PTR(-ENOMEM);
295
296 cfg = _pingpong_offset(idx, m, addr, &c->hw);
297 if (IS_ERR_OR_NULL(cfg)) {
298 kfree(c);
299 return ERR_PTR(-EINVAL);
300 }
301
302 c->idx = idx;
Lloyd Atkinson4a752fc2017-05-19 16:09:17 -0400303 c->caps = cfg;
Ping Li8430ee12017-02-24 14:14:44 -0800304 _setup_pingpong_ops(&c->ops, c->caps);
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700305
Lloyd Atkinson652e59b2017-05-03 11:20:30 -0400306 rc = sde_hw_blk_init(&c->base, SDE_HW_BLK_PINGPONG, idx, &sde_hw_ops);
307 if (rc) {
308 SDE_ERROR("failed to init hw blk %d\n", rc);
309 goto blk_init_error;
310 }
311
Lloyd Atkinson113aefd2016-10-23 13:15:18 -0400312 sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
313 c->hw.blk_off + c->hw.length, c->hw.xin_id);
314
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700315 return c;
Lloyd Atkinson652e59b2017-05-03 11:20:30 -0400316
317blk_init_error:
318 kzfree(c);
319
320 return ERR_PTR(rc);
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700321}
322
Lloyd Atkinson3127a092016-05-30 13:46:55 -0400323void sde_hw_pingpong_destroy(struct sde_hw_pingpong *pp)
324{
Lloyd Atkinson652e59b2017-05-03 11:20:30 -0400325 if (pp)
326 sde_hw_blk_destroy(&pp->base);
Lloyd Atkinson3127a092016-05-30 13:46:55 -0400327 kfree(pp);
328}