blob: 050e21b3385ac0c8cb67503a609646f424e60c3f [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{
Lloyd Atkinson6a5359d2017-06-21 10:18:08 -040072 struct sde_hw_blk_reg_map *c;
Narendra Muppalla1b0b3352015-09-29 10:16:51 -070073 int cfg;
74
Lloyd Atkinson6a5359d2017-06-21 10:18:08 -040075 if (!pp || !te)
76 return -EINVAL;
77 c = &pp->hw;
78
Narendra Muppalla1b0b3352015-09-29 10:16:51 -070079 cfg = BIT(19); /*VSYNC_COUNTER_EN */
80 if (te->hw_vsync_mode)
81 cfg |= BIT(20);
82
83 cfg |= te->vsync_count;
84
85 SDE_REG_WRITE(c, PP_SYNC_CONFIG_VSYNC, cfg);
86 SDE_REG_WRITE(c, PP_SYNC_CONFIG_HEIGHT, te->sync_cfg_height);
87 SDE_REG_WRITE(c, PP_VSYNC_INIT_VAL, te->vsync_init_val);
88 SDE_REG_WRITE(c, PP_RD_PTR_IRQ, te->rd_ptr_irq);
89 SDE_REG_WRITE(c, PP_START_POS, te->start_pos);
90 SDE_REG_WRITE(c, PP_SYNC_THRESH,
91 ((te->sync_threshold_continue << 16) |
92 te->sync_threshold_start));
93 SDE_REG_WRITE(c, PP_SYNC_WRCOUNT,
94 (te->start_pos + te->sync_threshold_start + 1));
95
96 return 0;
97}
98
Lloyd Atkinson45da92f2017-02-28 14:34:51 -050099static int sde_hw_pp_setup_autorefresh_config(struct sde_hw_pingpong *pp,
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700100 struct sde_hw_autorefresh *cfg)
101{
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500102 struct sde_hw_blk_reg_map *c;
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700103 u32 refresh_cfg;
104
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500105 if (!pp || !cfg)
106 return -EINVAL;
107 c = &pp->hw;
108
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700109 if (cfg->enable)
110 refresh_cfg = BIT(31) | cfg->frame_count;
111 else
112 refresh_cfg = 0;
113
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500114 SDE_REG_WRITE(c, PP_AUTOREFRESH_CONFIG, refresh_cfg);
115 SDE_EVT32(pp->idx - PINGPONG_0, refresh_cfg);
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700116
117 return 0;
118}
119
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500120static int sde_hw_pp_get_autorefresh_config(struct sde_hw_pingpong *pp,
121 struct sde_hw_autorefresh *cfg)
122{
123 struct sde_hw_blk_reg_map *c;
124 u32 val;
125
126 if (!pp || !cfg)
127 return -EINVAL;
128
129 c = &pp->hw;
130 val = SDE_REG_READ(c, PP_AUTOREFRESH_CONFIG);
131 cfg->enable = (val & BIT(31)) >> 31;
132 cfg->frame_count = val & 0xffff;
133
134 return 0;
135}
136
137static int sde_hw_pp_poll_timeout_wr_ptr(struct sde_hw_pingpong *pp,
138 u32 timeout_us)
139{
140 struct sde_hw_blk_reg_map *c;
141 u32 val;
142 int rc;
143
144 if (!pp)
145 return -EINVAL;
146
147 c = &pp->hw;
148 rc = readl_poll_timeout(c->base_off + c->blk_off + PP_LINE_COUNT,
149 val, (val & 0xffff) >= 1, 10, timeout_us);
150
151 return rc;
152}
153
154static void sde_hw_pp_dsc_enable(struct sde_hw_pingpong *pp)
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700155{
Lloyd Atkinson6a5359d2017-06-21 10:18:08 -0400156 struct sde_hw_blk_reg_map *c;
157
158 if (!pp)
159 return;
160 c = &pp->hw;
Jeykumar Sankaran5c2f0702017-03-09 18:03:15 -0800161
162 SDE_REG_WRITE(c, PP_DSC_MODE, 1);
163}
164
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500165static void sde_hw_pp_dsc_disable(struct sde_hw_pingpong *pp)
Jeykumar Sankaran5c2f0702017-03-09 18:03:15 -0800166{
Lloyd Atkinson6a5359d2017-06-21 10:18:08 -0400167 struct sde_hw_blk_reg_map *c;
Ingrid Gallardo2a2befb2017-08-07 15:02:51 -0700168 u32 data;
Lloyd Atkinson6a5359d2017-06-21 10:18:08 -0400169
170 if (!pp)
171 return;
172 c = &pp->hw;
Jeykumar Sankaran5c2f0702017-03-09 18:03:15 -0800173
Ingrid Gallardo2a2befb2017-08-07 15:02:51 -0700174 data = SDE_REG_READ(c, PP_DCE_DATA_OUT_SWAP);
175 data &= ~BIT(18); /* disable endian flip */
176 SDE_REG_WRITE(c, PP_DCE_DATA_OUT_SWAP, data);
177
Jeykumar Sankaran5c2f0702017-03-09 18:03:15 -0800178 SDE_REG_WRITE(c, PP_DSC_MODE, 0);
179}
180
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500181static int sde_hw_pp_setup_dsc(struct sde_hw_pingpong *pp)
Jeykumar Sankaran5c2f0702017-03-09 18:03:15 -0800182{
Lloyd Atkinson6a5359d2017-06-21 10:18:08 -0400183 struct sde_hw_blk_reg_map *c;
Jeykumar Sankaran5c2f0702017-03-09 18:03:15 -0800184 int data;
185
Lloyd Atkinson6a5359d2017-06-21 10:18:08 -0400186 if (!pp)
187 return -EINVAL;
188 c = &pp->hw;
189
190 data = SDE_REG_READ(c, PP_DCE_DATA_OUT_SWAP);
Jeykumar Sankaran5c2f0702017-03-09 18:03:15 -0800191 data |= BIT(18); /* endian flip */
Lloyd Atkinson6a5359d2017-06-21 10:18:08 -0400192 SDE_REG_WRITE(c, PP_DCE_DATA_OUT_SWAP, data);
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700193 return 0;
194}
Jeykumar Sankaran5c2f0702017-03-09 18:03:15 -0800195
Ping Li8430ee12017-02-24 14:14:44 -0800196static int sde_hw_pp_setup_dither_v1(struct sde_hw_pingpong *pp,
197 void *cfg, size_t len)
198{
199 struct sde_hw_blk_reg_map *c;
200 struct drm_msm_dither *dither = (struct drm_msm_dither *)cfg;
201 u32 base = 0, offset = 0, data = 0, i = 0;
202
203 if (!pp)
204 return -EINVAL;
205
206 c = &pp->hw;
207 base = pp->caps->sblk->dither.base;
208 if (!dither) {
209 /* dither property disable case */
210 SDE_REG_WRITE(c, base, 0);
211 return 0;
212 }
213
214 if (len != sizeof(struct drm_msm_dither)) {
215 DRM_ERROR("input len %zu, expected len %zu\n", len,
216 sizeof(struct drm_msm_dither));
217 return -EINVAL;
218 }
219
220 if (dither->c0_bitdepth >= DITHER_DEPTH_MAP_INDEX ||
221 dither->c1_bitdepth >= DITHER_DEPTH_MAP_INDEX ||
222 dither->c2_bitdepth >= DITHER_DEPTH_MAP_INDEX ||
223 dither->c3_bitdepth >= DITHER_DEPTH_MAP_INDEX)
224 return -EINVAL;
225
226 offset += 4;
227 data = dither_depth_map[dither->c0_bitdepth] & REG_MASK(2);
228 data |= (dither_depth_map[dither->c1_bitdepth] & REG_MASK(2)) << 2;
229 data |= (dither_depth_map[dither->c2_bitdepth] & REG_MASK(2)) << 4;
230 data |= (dither_depth_map[dither->c3_bitdepth] & REG_MASK(2)) << 6;
231 data |= (dither->temporal_en) ? (1 << 8) : 0;
232 SDE_REG_WRITE(c, base + offset, data);
233
234 for (i = 0; i < DITHER_MATRIX_SZ - 3; i += 4) {
235 offset += 4;
236 data = (dither->matrix[i] & REG_MASK(4)) |
237 ((dither->matrix[i + 1] & REG_MASK(4)) << 4) |
238 ((dither->matrix[i + 2] & REG_MASK(4)) << 8) |
239 ((dither->matrix[i + 3] & REG_MASK(4)) << 12);
240 SDE_REG_WRITE(c, base + offset, data);
241 }
242 SDE_REG_WRITE(c, base, 1);
243
244 return 0;
245}
246
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500247static int sde_hw_pp_enable_te(struct sde_hw_pingpong *pp, bool enable)
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700248{
Lloyd Atkinson6a5359d2017-06-21 10:18:08 -0400249 struct sde_hw_blk_reg_map *c;
250
251 if (!pp)
252 return -EINVAL;
253 c = &pp->hw;
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700254
255 SDE_REG_WRITE(c, PP_TEAR_CHECK_EN, enable);
256 return 0;
257}
258
Dhaval Patelac651032017-08-11 14:06:19 -0700259static int sde_hw_pp_connect_external_te(struct sde_hw_pingpong *pp,
260 bool enable_external_te)
261{
262 struct sde_hw_blk_reg_map *c = &pp->hw;
263 u32 cfg;
264 int orig;
265
266 if (!pp)
267 return -EINVAL;
268
269 c = &pp->hw;
270 cfg = SDE_REG_READ(c, PP_SYNC_CONFIG_VSYNC);
271 orig = (bool)(cfg & BIT(20));
272 if (enable_external_te)
273 cfg |= BIT(20);
274 else
275 cfg &= ~BIT(20);
276 SDE_REG_WRITE(c, PP_SYNC_CONFIG_VSYNC, cfg);
277 SDE_EVT32(pp->idx - PINGPONG_0, cfg);
278
279 return orig;
280}
281
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500282static int sde_hw_pp_get_vsync_info(struct sde_hw_pingpong *pp,
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700283 struct sde_hw_pp_vsync_info *info)
284{
Lloyd Atkinson6a5359d2017-06-21 10:18:08 -0400285 struct sde_hw_blk_reg_map *c;
Lloyd Atkinson3127a092016-05-30 13:46:55 -0400286 u32 val;
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700287
Lloyd Atkinson6a5359d2017-06-21 10:18:08 -0400288 if (!pp || !info)
289 return -EINVAL;
290 c = &pp->hw;
291
Lloyd Atkinson3127a092016-05-30 13:46:55 -0400292 val = SDE_REG_READ(c, PP_VSYNC_INIT_VAL);
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500293 info->rd_ptr_init_val = val & 0xffff;
Lloyd Atkinson3127a092016-05-30 13:46:55 -0400294
295 val = SDE_REG_READ(c, PP_INT_COUNT_VAL);
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500296 info->rd_ptr_frame_count = (val & 0xffff0000) >> 16;
297 info->rd_ptr_line_count = val & 0xffff;
298
299 val = SDE_REG_READ(c, PP_LINE_COUNT);
300 info->wr_ptr_line_count = val & 0xffff;
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700301
302 return 0;
303}
304
Benjamin Chan9cd866d2017-08-15 14:56:34 -0400305static u32 sde_hw_pp_get_line_count(struct sde_hw_pingpong *pp)
306{
307 struct sde_hw_blk_reg_map *c = &pp->hw;
308 u32 height, init;
309 u32 line = 0xFFFF;
310
311 if (!pp)
312 return 0;
313 c = &pp->hw;
314
315 init = SDE_REG_READ(c, PP_VSYNC_INIT_VAL) & 0xFFFF;
316 height = SDE_REG_READ(c, PP_SYNC_CONFIG_HEIGHT) & 0xFFFF;
317
318 if (height < init)
319 goto line_count_exit;
320
321 line = SDE_REG_READ(c, PP_INT_COUNT_VAL) & 0xFFFF;
322
323 if (line < init)
324 line += (0xFFFF - init);
325 else
326 line -= init;
327
328line_count_exit:
329 return line;
330}
331
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700332static void _setup_pingpong_ops(struct sde_hw_pingpong_ops *ops,
Ping Li8430ee12017-02-24 14:14:44 -0800333 const struct sde_pingpong_cfg *hw_cap)
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700334{
Ping Li8430ee12017-02-24 14:14:44 -0800335 u32 version = 0;
336
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700337 ops->setup_tearcheck = sde_hw_pp_setup_te_config;
338 ops->enable_tearcheck = sde_hw_pp_enable_te;
Dhaval Patelac651032017-08-11 14:06:19 -0700339 ops->connect_external_te = sde_hw_pp_connect_external_te;
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700340 ops->get_vsync_info = sde_hw_pp_get_vsync_info;
341 ops->setup_autorefresh = sde_hw_pp_setup_autorefresh_config;
Jeykumar Sankaran5c2f0702017-03-09 18:03:15 -0800342 ops->setup_dsc = sde_hw_pp_setup_dsc;
343 ops->enable_dsc = sde_hw_pp_dsc_enable;
344 ops->disable_dsc = sde_hw_pp_dsc_disable;
Lloyd Atkinson45da92f2017-02-28 14:34:51 -0500345 ops->get_autorefresh = sde_hw_pp_get_autorefresh_config;
346 ops->poll_timeout_wr_ptr = sde_hw_pp_poll_timeout_wr_ptr;
Benjamin Chan9cd866d2017-08-15 14:56:34 -0400347 ops->get_line_count = sde_hw_pp_get_line_count;
Ping Li8430ee12017-02-24 14:14:44 -0800348
349 version = SDE_COLOR_PROCESS_MAJOR(hw_cap->sblk->dither.version);
350 switch (version) {
351 case 1:
352 ops->setup_dither = sde_hw_pp_setup_dither_v1;
353 break;
354 default:
355 ops->setup_dither = NULL;
356 break;
357 }
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700358};
359
Shashank Babu Chinta Venkata5d641d42017-09-29 12:16:28 -0700360#define MDP_PP_DSC_OFFSET(index) (0x71000 + (0x800 * index) + 0x0a0)
361#define MDP_PP_AUTOREFRESH_OFFSET(index) (0x71000 + (0x800 * index) + 0x030)
362
363int sde_get_pp_dsc_for_cont_splash(void __iomem *mmio,
364 int max_dsc_cnt, u8 *dsc_ids)
365{
366 int index;
367 int value, dsc_cnt = 0;
368
369 if (!mmio || !dsc_ids) {
370 SDE_ERROR("invalid input parameters\n");
371 return 0;
372 }
373
374 SDE_DEBUG("max_dsc_cnt = %d\n", max_dsc_cnt);
375 for (index = 0; index < max_dsc_cnt; index++) {
376 value = readl_relaxed(mmio
377 + MDP_PP_DSC_OFFSET(index));
378 SDE_DEBUG("DSC[%d]=0x%x\n",
379 index, value);
380 SDE_DEBUG("dsc_cnt = %d\n", dsc_cnt);
381 if (value) {
382 dsc_ids[dsc_cnt] = index + DSC_0;
383 dsc_cnt++;
384 }
385 value = readl_relaxed(mmio
386 + MDP_PP_AUTOREFRESH_OFFSET(index));
387 SDE_DEBUG("AUTOREFRESH[%d]=0x%x\n",
388 index, value);
389 if (value) {
390 SDE_DEBUG("Disabling autoreferesh\n");
391 writel_relaxed(0x0, mmio
392 + MDP_PP_AUTOREFRESH_OFFSET(index));
Chandan Uddaraju9bb109a2017-10-29 18:08:51 -0700393 /*
394 * Wait for one frame update so that auto refresh
395 * disable is through
396 */
397 usleep_range(16000, 20000);
Shashank Babu Chinta Venkata5d641d42017-09-29 12:16:28 -0700398 }
399 }
400 return dsc_cnt;
401}
402
Lloyd Atkinson652e59b2017-05-03 11:20:30 -0400403static struct sde_hw_blk_ops sde_hw_ops = {
404 .start = NULL,
405 .stop = NULL,
406};
407
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700408struct sde_hw_pingpong *sde_hw_pingpong_init(enum sde_pingpong idx,
409 void __iomem *addr,
410 struct sde_mdss_cfg *m)
411{
412 struct sde_hw_pingpong *c;
413 struct sde_pingpong_cfg *cfg;
Lloyd Atkinson652e59b2017-05-03 11:20:30 -0400414 int rc;
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700415
416 c = kzalloc(sizeof(*c), GFP_KERNEL);
417 if (!c)
418 return ERR_PTR(-ENOMEM);
419
420 cfg = _pingpong_offset(idx, m, addr, &c->hw);
421 if (IS_ERR_OR_NULL(cfg)) {
422 kfree(c);
423 return ERR_PTR(-EINVAL);
424 }
425
426 c->idx = idx;
Lloyd Atkinson4a752fc2017-05-19 16:09:17 -0400427 c->caps = cfg;
Ping Li8430ee12017-02-24 14:14:44 -0800428 _setup_pingpong_ops(&c->ops, c->caps);
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700429
Lloyd Atkinson652e59b2017-05-03 11:20:30 -0400430 rc = sde_hw_blk_init(&c->base, SDE_HW_BLK_PINGPONG, idx, &sde_hw_ops);
431 if (rc) {
432 SDE_ERROR("failed to init hw blk %d\n", rc);
433 goto blk_init_error;
434 }
435
Lloyd Atkinson113aefd2016-10-23 13:15:18 -0400436 sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
437 c->hw.blk_off + c->hw.length, c->hw.xin_id);
438
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700439 return c;
Lloyd Atkinson652e59b2017-05-03 11:20:30 -0400440
441blk_init_error:
442 kzfree(c);
443
444 return ERR_PTR(rc);
Narendra Muppalla1b0b3352015-09-29 10:16:51 -0700445}
446
Lloyd Atkinson3127a092016-05-30 13:46:55 -0400447void sde_hw_pingpong_destroy(struct sde_hw_pingpong *pp)
448{
Lloyd Atkinson652e59b2017-05-03 11:20:30 -0400449 if (pp)
450 sde_hw_blk_destroy(&pp->base);
Lloyd Atkinson3127a092016-05-30 13:46:55 -0400451 kfree(pp);
452}