]>
Commit | Line | Data |
---|---|---|
97fb5e8d | 1 | // SPDX-License-Identifier: GPL-2.0-only |
25fdd593 | 2 | /* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. |
25fdd593 JS |
3 | */ |
4 | ||
5 | #include "dpu_hwio.h" | |
6 | #include "dpu_hw_catalog.h" | |
7 | #include "dpu_hw_intf.h" | |
25fdd593 JS |
8 | #include "dpu_kms.h" |
9 | ||
10 | #define INTF_TIMING_ENGINE_EN 0x000 | |
11 | #define INTF_CONFIG 0x004 | |
12 | #define INTF_HSYNC_CTL 0x008 | |
13 | #define INTF_VSYNC_PERIOD_F0 0x00C | |
14 | #define INTF_VSYNC_PERIOD_F1 0x010 | |
15 | #define INTF_VSYNC_PULSE_WIDTH_F0 0x014 | |
16 | #define INTF_VSYNC_PULSE_WIDTH_F1 0x018 | |
17 | #define INTF_DISPLAY_V_START_F0 0x01C | |
18 | #define INTF_DISPLAY_V_START_F1 0x020 | |
19 | #define INTF_DISPLAY_V_END_F0 0x024 | |
20 | #define INTF_DISPLAY_V_END_F1 0x028 | |
21 | #define INTF_ACTIVE_V_START_F0 0x02C | |
22 | #define INTF_ACTIVE_V_START_F1 0x030 | |
23 | #define INTF_ACTIVE_V_END_F0 0x034 | |
24 | #define INTF_ACTIVE_V_END_F1 0x038 | |
25 | #define INTF_DISPLAY_HCTL 0x03C | |
26 | #define INTF_ACTIVE_HCTL 0x040 | |
27 | #define INTF_BORDER_COLOR 0x044 | |
28 | #define INTF_UNDERFLOW_COLOR 0x048 | |
29 | #define INTF_HSYNC_SKEW 0x04C | |
30 | #define INTF_POLARITY_CTL 0x050 | |
31 | #define INTF_TEST_CTL 0x054 | |
32 | #define INTF_TP_COLOR0 0x058 | |
33 | #define INTF_TP_COLOR1 0x05C | |
34 | #define INTF_FRAME_LINE_COUNT_EN 0x0A8 | |
35 | #define INTF_FRAME_COUNT 0x0AC | |
36 | #define INTF_LINE_COUNT 0x0B0 | |
37 | ||
38 | #define INTF_DEFLICKER_CONFIG 0x0F0 | |
39 | #define INTF_DEFLICKER_STRNG_COEFF 0x0F4 | |
40 | #define INTF_DEFLICKER_WEAK_COEFF 0x0F8 | |
41 | ||
42 | #define INTF_DSI_CMD_MODE_TRIGGER_EN 0x084 | |
43 | #define INTF_PANEL_FORMAT 0x090 | |
44 | #define INTF_TPG_ENABLE 0x100 | |
45 | #define INTF_TPG_MAIN_CONTROL 0x104 | |
46 | #define INTF_TPG_VIDEO_CONFIG 0x108 | |
47 | #define INTF_TPG_COMPONENT_LIMITS 0x10C | |
48 | #define INTF_TPG_RECTANGLE 0x110 | |
49 | #define INTF_TPG_INITIAL_VALUE 0x114 | |
50 | #define INTF_TPG_BLK_WHITE_PATTERN_FRAMES 0x118 | |
51 | #define INTF_TPG_RGB_MAPPING 0x11C | |
52 | #define INTF_PROG_FETCH_START 0x170 | |
53 | #define INTF_PROG_ROT_START 0x174 | |
54 | ||
55 | #define INTF_FRAME_LINE_COUNT_EN 0x0A8 | |
56 | #define INTF_FRAME_COUNT 0x0AC | |
57 | #define INTF_LINE_COUNT 0x0B0 | |
58 | ||
25fdd593 JS |
59 | static struct dpu_intf_cfg *_intf_offset(enum dpu_intf intf, |
60 | struct dpu_mdss_cfg *m, | |
61 | void __iomem *addr, | |
62 | struct dpu_hw_blk_reg_map *b) | |
63 | { | |
64 | int i; | |
65 | ||
66 | for (i = 0; i < m->intf_count; i++) { | |
67 | if ((intf == m->intf[i].id) && | |
68 | (m->intf[i].type != INTF_NONE)) { | |
69 | b->base_off = addr; | |
70 | b->blk_off = m->intf[i].base; | |
71 | b->length = m->intf[i].len; | |
72 | b->hwversion = m->hwversion; | |
73 | b->log_mask = DPU_DBG_MASK_INTF; | |
74 | return &m->intf[i]; | |
75 | } | |
76 | } | |
77 | ||
78 | return ERR_PTR(-EINVAL); | |
79 | } | |
80 | ||
81 | static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx, | |
82 | const struct intf_timing_params *p, | |
83 | const struct dpu_format *fmt) | |
84 | { | |
85 | struct dpu_hw_blk_reg_map *c = &ctx->hw; | |
86 | u32 hsync_period, vsync_period; | |
87 | u32 display_v_start, display_v_end; | |
88 | u32 hsync_start_x, hsync_end_x; | |
89 | u32 active_h_start, active_h_end; | |
90 | u32 active_v_start, active_v_end; | |
91 | u32 active_hctl, display_hctl, hsync_ctl; | |
92 | u32 polarity_ctl, den_polarity, hsync_polarity, vsync_polarity; | |
93 | u32 panel_format; | |
94 | u32 intf_cfg; | |
95 | ||
96 | /* read interface_cfg */ | |
97 | intf_cfg = DPU_REG_READ(c, INTF_CONFIG); | |
98 | hsync_period = p->hsync_pulse_width + p->h_back_porch + p->width + | |
99 | p->h_front_porch; | |
100 | vsync_period = p->vsync_pulse_width + p->v_back_porch + p->height + | |
101 | p->v_front_porch; | |
102 | ||
103 | display_v_start = ((p->vsync_pulse_width + p->v_back_porch) * | |
104 | hsync_period) + p->hsync_skew; | |
105 | display_v_end = ((vsync_period - p->v_front_porch) * hsync_period) + | |
106 | p->hsync_skew - 1; | |
107 | ||
108 | if (ctx->cap->type == INTF_EDP || ctx->cap->type == INTF_DP) { | |
109 | display_v_start += p->hsync_pulse_width + p->h_back_porch; | |
110 | display_v_end -= p->h_front_porch; | |
111 | } | |
112 | ||
113 | hsync_start_x = p->h_back_porch + p->hsync_pulse_width; | |
114 | hsync_end_x = hsync_period - p->h_front_porch - 1; | |
115 | ||
116 | if (p->width != p->xres) { | |
117 | active_h_start = hsync_start_x; | |
118 | active_h_end = active_h_start + p->xres - 1; | |
119 | } else { | |
120 | active_h_start = 0; | |
121 | active_h_end = 0; | |
122 | } | |
123 | ||
124 | if (p->height != p->yres) { | |
125 | active_v_start = display_v_start; | |
126 | active_v_end = active_v_start + (p->yres * hsync_period) - 1; | |
127 | } else { | |
128 | active_v_start = 0; | |
129 | active_v_end = 0; | |
130 | } | |
131 | ||
132 | if (active_h_end) { | |
133 | active_hctl = (active_h_end << 16) | active_h_start; | |
134 | intf_cfg |= BIT(29); /* ACTIVE_H_ENABLE */ | |
135 | } else { | |
136 | active_hctl = 0; | |
137 | } | |
138 | ||
139 | if (active_v_end) | |
140 | intf_cfg |= BIT(30); /* ACTIVE_V_ENABLE */ | |
141 | ||
142 | hsync_ctl = (hsync_period << 16) | p->hsync_pulse_width; | |
143 | display_hctl = (hsync_end_x << 16) | hsync_start_x; | |
144 | ||
145 | den_polarity = 0; | |
146 | if (ctx->cap->type == INTF_HDMI) { | |
147 | hsync_polarity = p->yres >= 720 ? 0 : 1; | |
148 | vsync_polarity = p->yres >= 720 ? 0 : 1; | |
149 | } else { | |
150 | hsync_polarity = 0; | |
151 | vsync_polarity = 0; | |
152 | } | |
153 | polarity_ctl = (den_polarity << 2) | /* DEN Polarity */ | |
154 | (vsync_polarity << 1) | /* VSYNC Polarity */ | |
155 | (hsync_polarity << 0); /* HSYNC Polarity */ | |
156 | ||
157 | if (!DPU_FORMAT_IS_YUV(fmt)) | |
158 | panel_format = (fmt->bits[C0_G_Y] | | |
159 | (fmt->bits[C1_B_Cb] << 2) | | |
160 | (fmt->bits[C2_R_Cr] << 4) | | |
161 | (0x21 << 8)); | |
162 | else | |
163 | /* Interface treats all the pixel data in RGB888 format */ | |
164 | panel_format = (COLOR_8BIT | | |
165 | (COLOR_8BIT << 2) | | |
166 | (COLOR_8BIT << 4) | | |
167 | (0x21 << 8)); | |
168 | ||
169 | DPU_REG_WRITE(c, INTF_HSYNC_CTL, hsync_ctl); | |
170 | DPU_REG_WRITE(c, INTF_VSYNC_PERIOD_F0, vsync_period * hsync_period); | |
171 | DPU_REG_WRITE(c, INTF_VSYNC_PULSE_WIDTH_F0, | |
172 | p->vsync_pulse_width * hsync_period); | |
173 | DPU_REG_WRITE(c, INTF_DISPLAY_HCTL, display_hctl); | |
174 | DPU_REG_WRITE(c, INTF_DISPLAY_V_START_F0, display_v_start); | |
175 | DPU_REG_WRITE(c, INTF_DISPLAY_V_END_F0, display_v_end); | |
176 | DPU_REG_WRITE(c, INTF_ACTIVE_HCTL, active_hctl); | |
177 | DPU_REG_WRITE(c, INTF_ACTIVE_V_START_F0, active_v_start); | |
178 | DPU_REG_WRITE(c, INTF_ACTIVE_V_END_F0, active_v_end); | |
179 | DPU_REG_WRITE(c, INTF_BORDER_COLOR, p->border_clr); | |
180 | DPU_REG_WRITE(c, INTF_UNDERFLOW_COLOR, p->underflow_clr); | |
181 | DPU_REG_WRITE(c, INTF_HSYNC_SKEW, p->hsync_skew); | |
182 | DPU_REG_WRITE(c, INTF_POLARITY_CTL, polarity_ctl); | |
183 | DPU_REG_WRITE(c, INTF_FRAME_LINE_COUNT_EN, 0x3); | |
184 | DPU_REG_WRITE(c, INTF_CONFIG, intf_cfg); | |
185 | DPU_REG_WRITE(c, INTF_PANEL_FORMAT, panel_format); | |
186 | } | |
187 | ||
188 | static void dpu_hw_intf_enable_timing_engine( | |
189 | struct dpu_hw_intf *intf, | |
190 | u8 enable) | |
191 | { | |
192 | struct dpu_hw_blk_reg_map *c = &intf->hw; | |
193 | /* Note: Display interface select is handled in top block hw layer */ | |
194 | DPU_REG_WRITE(c, INTF_TIMING_ENGINE_EN, enable != 0); | |
195 | } | |
196 | ||
197 | static void dpu_hw_intf_setup_prg_fetch( | |
198 | struct dpu_hw_intf *intf, | |
199 | const struct intf_prog_fetch *fetch) | |
200 | { | |
201 | struct dpu_hw_blk_reg_map *c = &intf->hw; | |
202 | int fetch_enable; | |
203 | ||
204 | /* | |
205 | * Fetch should always be outside the active lines. If the fetching | |
206 | * is programmed within active region, hardware behavior is unknown. | |
207 | */ | |
208 | ||
209 | fetch_enable = DPU_REG_READ(c, INTF_CONFIG); | |
210 | if (fetch->enable) { | |
211 | fetch_enable |= BIT(31); | |
212 | DPU_REG_WRITE(c, INTF_PROG_FETCH_START, | |
213 | fetch->fetch_start); | |
214 | } else { | |
215 | fetch_enable &= ~BIT(31); | |
216 | } | |
217 | ||
218 | DPU_REG_WRITE(c, INTF_CONFIG, fetch_enable); | |
219 | } | |
220 | ||
221 | static void dpu_hw_intf_get_status( | |
222 | struct dpu_hw_intf *intf, | |
223 | struct intf_status *s) | |
224 | { | |
225 | struct dpu_hw_blk_reg_map *c = &intf->hw; | |
226 | ||
227 | s->is_en = DPU_REG_READ(c, INTF_TIMING_ENGINE_EN); | |
228 | if (s->is_en) { | |
229 | s->frame_count = DPU_REG_READ(c, INTF_FRAME_COUNT); | |
230 | s->line_count = DPU_REG_READ(c, INTF_LINE_COUNT); | |
231 | } else { | |
232 | s->line_count = 0; | |
233 | s->frame_count = 0; | |
234 | } | |
235 | } | |
236 | ||
25fdd593 JS |
237 | static u32 dpu_hw_intf_get_line_count(struct dpu_hw_intf *intf) |
238 | { | |
239 | struct dpu_hw_blk_reg_map *c; | |
240 | ||
241 | if (!intf) | |
242 | return 0; | |
243 | ||
244 | c = &intf->hw; | |
245 | ||
246 | return DPU_REG_READ(c, INTF_LINE_COUNT); | |
247 | } | |
248 | ||
249 | static void _setup_intf_ops(struct dpu_hw_intf_ops *ops, | |
250 | unsigned long cap) | |
251 | { | |
252 | ops->setup_timing_gen = dpu_hw_intf_setup_timing_engine; | |
253 | ops->setup_prg_fetch = dpu_hw_intf_setup_prg_fetch; | |
254 | ops->get_status = dpu_hw_intf_get_status; | |
255 | ops->enable_timing = dpu_hw_intf_enable_timing_engine; | |
25fdd593 JS |
256 | ops->get_line_count = dpu_hw_intf_get_line_count; |
257 | } | |
258 | ||
53edf462 | 259 | static struct dpu_hw_blk_ops dpu_hw_ops; |
25fdd593 JS |
260 | |
261 | struct dpu_hw_intf *dpu_hw_intf_init(enum dpu_intf idx, | |
262 | void __iomem *addr, | |
263 | struct dpu_mdss_cfg *m) | |
264 | { | |
265 | struct dpu_hw_intf *c; | |
266 | struct dpu_intf_cfg *cfg; | |
25fdd593 JS |
267 | |
268 | c = kzalloc(sizeof(*c), GFP_KERNEL); | |
269 | if (!c) | |
270 | return ERR_PTR(-ENOMEM); | |
271 | ||
272 | cfg = _intf_offset(idx, m, addr, &c->hw); | |
273 | if (IS_ERR_OR_NULL(cfg)) { | |
274 | kfree(c); | |
275 | pr_err("failed to create dpu_hw_intf %d\n", idx); | |
276 | return ERR_PTR(-EINVAL); | |
277 | } | |
278 | ||
279 | /* | |
280 | * Assign ops | |
281 | */ | |
282 | c->idx = idx; | |
283 | c->cap = cfg; | |
284 | c->mdss = m; | |
285 | _setup_intf_ops(&c->ops, c->cap->features); | |
286 | ||
53edf462 | 287 | dpu_hw_blk_init(&c->base, DPU_HW_BLK_INTF, idx, &dpu_hw_ops); |
25fdd593 JS |
288 | |
289 | return c; | |
25fdd593 JS |
290 | } |
291 | ||
292 | void dpu_hw_intf_destroy(struct dpu_hw_intf *intf) | |
293 | { | |
294 | if (intf) | |
295 | dpu_hw_blk_destroy(&intf->base); | |
296 | kfree(intf); | |
297 | } | |
298 |