1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright © 2018-2020 Intel Corporation |
4 | */ |
5 | |
6 | #include <linux/clk.h> |
7 | |
8 | #include <drm/drm_atomic.h> |
9 | #include <drm/drm_atomic_helper.h> |
10 | #include <drm/drm_crtc.h> |
11 | #include <drm/drm_print.h> |
12 | #include <drm/drm_vblank.h> |
13 | #include <drm/drm_modeset_helper_vtables.h> |
14 | |
15 | #include "kmb_drv.h" |
16 | #include "kmb_dsi.h" |
17 | #include "kmb_plane.h" |
18 | #include "kmb_regs.h" |
19 | |
20 | struct kmb_crtc_timing { |
21 | u32 vfront_porch; |
22 | u32 vback_porch; |
23 | u32 vsync_len; |
24 | u32 hfront_porch; |
25 | u32 hback_porch; |
26 | u32 hsync_len; |
27 | }; |
28 | |
29 | static int kmb_crtc_enable_vblank(struct drm_crtc *crtc) |
30 | { |
31 | struct drm_device *dev = crtc->dev; |
32 | struct kmb_drm_private *kmb = to_kmb(dev); |
33 | |
34 | /* Clear interrupt */ |
35 | kmb_write_lcd(dev_p: kmb, LCD_INT_CLEAR, LCD_INT_VERT_COMP); |
36 | /* Set which interval to generate vertical interrupt */ |
37 | kmb_write_lcd(dev_p: kmb, LCD_VSTATUS_COMPARE, |
38 | LCD_VSTATUS_COMPARE_VSYNC); |
39 | /* Enable vertical interrupt */ |
40 | kmb_set_bitmask_lcd(dev_p: kmb, LCD_INT_ENABLE, |
41 | LCD_INT_VERT_COMP); |
42 | return 0; |
43 | } |
44 | |
45 | static void kmb_crtc_disable_vblank(struct drm_crtc *crtc) |
46 | { |
47 | struct drm_device *dev = crtc->dev; |
48 | struct kmb_drm_private *kmb = to_kmb(dev); |
49 | |
50 | /* Clear interrupt */ |
51 | kmb_write_lcd(dev_p: kmb, LCD_INT_CLEAR, LCD_INT_VERT_COMP); |
52 | /* Disable vertical interrupt */ |
53 | kmb_clr_bitmask_lcd(dev_p: kmb, LCD_INT_ENABLE, |
54 | LCD_INT_VERT_COMP); |
55 | } |
56 | |
57 | static const struct drm_crtc_funcs kmb_crtc_funcs = { |
58 | .destroy = drm_crtc_cleanup, |
59 | .set_config = drm_atomic_helper_set_config, |
60 | .page_flip = drm_atomic_helper_page_flip, |
61 | .reset = drm_atomic_helper_crtc_reset, |
62 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, |
63 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, |
64 | .enable_vblank = kmb_crtc_enable_vblank, |
65 | .disable_vblank = kmb_crtc_disable_vblank, |
66 | }; |
67 | |
68 | static void kmb_crtc_set_mode(struct drm_crtc *crtc, |
69 | struct drm_atomic_state *old_state) |
70 | { |
71 | struct drm_device *dev = crtc->dev; |
72 | struct drm_display_mode *m = &crtc->state->adjusted_mode; |
73 | struct kmb_crtc_timing vm; |
74 | struct kmb_drm_private *kmb = to_kmb(dev); |
75 | unsigned int val = 0; |
76 | |
77 | /* Initialize mipi */ |
78 | kmb_dsi_mode_set(kmb_dsi: kmb->kmb_dsi, mode: m, sys_clk_mhz: kmb->sys_clk_mhz, old_state); |
79 | drm_info(dev, |
80 | "vfp= %d vbp= %d vsync_len=%d hfp=%d hbp=%d hsync_len=%d\n" , |
81 | m->crtc_vsync_start - m->crtc_vdisplay, |
82 | m->crtc_vtotal - m->crtc_vsync_end, |
83 | m->crtc_vsync_end - m->crtc_vsync_start, |
84 | m->crtc_hsync_start - m->crtc_hdisplay, |
85 | m->crtc_htotal - m->crtc_hsync_end, |
86 | m->crtc_hsync_end - m->crtc_hsync_start); |
87 | val = kmb_read_lcd(dev_p: kmb, LCD_INT_ENABLE); |
88 | kmb_clr_bitmask_lcd(dev_p: kmb, LCD_INT_ENABLE, mask: val); |
89 | kmb_set_bitmask_lcd(dev_p: kmb, LCD_INT_CLEAR, mask: ~0x0); |
90 | vm.vfront_porch = 2; |
91 | vm.vback_porch = 2; |
92 | vm.vsync_len = 8; |
93 | vm.hfront_porch = 0; |
94 | vm.hback_porch = 0; |
95 | vm.hsync_len = 28; |
96 | |
97 | drm_dbg(dev, "%s : %dactive height= %d vbp=%d vfp=%d vsync-w=%d h-active=%d h-bp=%d h-fp=%d hsync-l=%d" , |
98 | __func__, __LINE__, |
99 | m->crtc_vdisplay, vm.vback_porch, vm.vfront_porch, |
100 | vm.vsync_len, m->crtc_hdisplay, vm.hback_porch, |
101 | vm.hfront_porch, vm.hsync_len); |
102 | kmb_write_lcd(dev_p: kmb, LCD_V_ACTIVEHEIGHT, |
103 | value: m->crtc_vdisplay - 1); |
104 | kmb_write_lcd(dev_p: kmb, LCD_V_BACKPORCH, value: vm.vback_porch); |
105 | kmb_write_lcd(dev_p: kmb, LCD_V_FRONTPORCH, value: vm.vfront_porch); |
106 | kmb_write_lcd(dev_p: kmb, LCD_VSYNC_WIDTH, value: vm.vsync_len - 1); |
107 | kmb_write_lcd(dev_p: kmb, LCD_H_ACTIVEWIDTH, |
108 | value: m->crtc_hdisplay - 1); |
109 | kmb_write_lcd(dev_p: kmb, LCD_H_BACKPORCH, value: vm.hback_porch); |
110 | kmb_write_lcd(dev_p: kmb, LCD_H_FRONTPORCH, value: vm.hfront_porch); |
111 | kmb_write_lcd(dev_p: kmb, LCD_HSYNC_WIDTH, value: vm.hsync_len - 1); |
112 | /* This is hardcoded as 0 in the Myriadx code */ |
113 | kmb_write_lcd(dev_p: kmb, LCD_VSYNC_START, value: 0); |
114 | kmb_write_lcd(dev_p: kmb, LCD_VSYNC_END, value: 0); |
115 | /* Back ground color */ |
116 | kmb_write_lcd(dev_p: kmb, LCD_BG_COLOUR_LS, value: 0x4); |
117 | if (m->flags == DRM_MODE_FLAG_INTERLACE) { |
118 | kmb_write_lcd(dev_p: kmb, |
119 | LCD_VSYNC_WIDTH_EVEN, value: vm.vsync_len - 1); |
120 | kmb_write_lcd(dev_p: kmb, |
121 | LCD_V_BACKPORCH_EVEN, value: vm.vback_porch); |
122 | kmb_write_lcd(dev_p: kmb, |
123 | LCD_V_FRONTPORCH_EVEN, value: vm.vfront_porch); |
124 | kmb_write_lcd(dev_p: kmb, LCD_V_ACTIVEHEIGHT_EVEN, |
125 | value: m->crtc_vdisplay - 1); |
126 | /* This is hardcoded as 10 in the Myriadx code */ |
127 | kmb_write_lcd(dev_p: kmb, LCD_VSYNC_START_EVEN, value: 10); |
128 | kmb_write_lcd(dev_p: kmb, LCD_VSYNC_END_EVEN, value: 10); |
129 | } |
130 | kmb_write_lcd(dev_p: kmb, LCD_TIMING_GEN_TRIG, value: 1); |
131 | kmb_set_bitmask_lcd(dev_p: kmb, LCD_CONTROL, LCD_CTRL_ENABLE); |
132 | kmb_set_bitmask_lcd(dev_p: kmb, LCD_INT_ENABLE, mask: val); |
133 | } |
134 | |
135 | static void kmb_crtc_atomic_enable(struct drm_crtc *crtc, |
136 | struct drm_atomic_state *state) |
137 | { |
138 | struct kmb_drm_private *kmb = crtc_to_kmb_priv(x: crtc); |
139 | |
140 | clk_prepare_enable(clk: kmb->kmb_clk.clk_lcd); |
141 | kmb_crtc_set_mode(crtc, old_state: state); |
142 | drm_crtc_vblank_on(crtc); |
143 | } |
144 | |
145 | static void kmb_crtc_atomic_disable(struct drm_crtc *crtc, |
146 | struct drm_atomic_state *state) |
147 | { |
148 | struct kmb_drm_private *kmb = crtc_to_kmb_priv(x: crtc); |
149 | struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc); |
150 | |
151 | /* due to hw limitations, planes need to be off when crtc is off */ |
152 | drm_atomic_helper_disable_planes_on_crtc(old_crtc_state: old_state, atomic: false); |
153 | |
154 | drm_crtc_vblank_off(crtc); |
155 | clk_disable_unprepare(clk: kmb->kmb_clk.clk_lcd); |
156 | } |
157 | |
158 | static void kmb_crtc_atomic_begin(struct drm_crtc *crtc, |
159 | struct drm_atomic_state *state) |
160 | { |
161 | struct drm_device *dev = crtc->dev; |
162 | struct kmb_drm_private *kmb = to_kmb(dev); |
163 | |
164 | kmb_clr_bitmask_lcd(dev_p: kmb, LCD_INT_ENABLE, |
165 | LCD_INT_VERT_COMP); |
166 | } |
167 | |
168 | static void kmb_crtc_atomic_flush(struct drm_crtc *crtc, |
169 | struct drm_atomic_state *state) |
170 | { |
171 | struct drm_device *dev = crtc->dev; |
172 | struct kmb_drm_private *kmb = to_kmb(dev); |
173 | |
174 | kmb_set_bitmask_lcd(dev_p: kmb, LCD_INT_ENABLE, |
175 | LCD_INT_VERT_COMP); |
176 | |
177 | spin_lock_irq(lock: &crtc->dev->event_lock); |
178 | if (crtc->state->event) { |
179 | if (drm_crtc_vblank_get(crtc) == 0) |
180 | drm_crtc_arm_vblank_event(crtc, e: crtc->state->event); |
181 | else |
182 | drm_crtc_send_vblank_event(crtc, e: crtc->state->event); |
183 | } |
184 | crtc->state->event = NULL; |
185 | spin_unlock_irq(lock: &crtc->dev->event_lock); |
186 | } |
187 | |
188 | static enum drm_mode_status |
189 | kmb_crtc_mode_valid(struct drm_crtc *crtc, |
190 | const struct drm_display_mode *mode) |
191 | { |
192 | int refresh; |
193 | struct drm_device *dev = crtc->dev; |
194 | int vfp = mode->vsync_start - mode->vdisplay; |
195 | |
196 | if (mode->vdisplay < KMB_CRTC_MAX_HEIGHT) { |
197 | drm_dbg(dev, "height = %d less than %d" , |
198 | mode->vdisplay, KMB_CRTC_MAX_HEIGHT); |
199 | return MODE_BAD_VVALUE; |
200 | } |
201 | if (mode->hdisplay < KMB_CRTC_MAX_WIDTH) { |
202 | drm_dbg(dev, "width = %d less than %d" , |
203 | mode->hdisplay, KMB_CRTC_MAX_WIDTH); |
204 | return MODE_BAD_HVALUE; |
205 | } |
206 | refresh = drm_mode_vrefresh(mode); |
207 | if (refresh < KMB_MIN_VREFRESH || refresh > KMB_MAX_VREFRESH) { |
208 | drm_dbg(dev, "refresh = %d less than %d or greater than %d" , |
209 | refresh, KMB_MIN_VREFRESH, KMB_MAX_VREFRESH); |
210 | return MODE_BAD; |
211 | } |
212 | |
213 | if (vfp < KMB_CRTC_MIN_VFP) { |
214 | drm_dbg(dev, "vfp = %d less than %d" , vfp, KMB_CRTC_MIN_VFP); |
215 | return MODE_BAD; |
216 | } |
217 | |
218 | return MODE_OK; |
219 | } |
220 | |
221 | static const struct drm_crtc_helper_funcs kmb_crtc_helper_funcs = { |
222 | .atomic_begin = kmb_crtc_atomic_begin, |
223 | .atomic_enable = kmb_crtc_atomic_enable, |
224 | .atomic_disable = kmb_crtc_atomic_disable, |
225 | .atomic_flush = kmb_crtc_atomic_flush, |
226 | .mode_valid = kmb_crtc_mode_valid, |
227 | }; |
228 | |
229 | int kmb_setup_crtc(struct drm_device *drm) |
230 | { |
231 | struct kmb_drm_private *kmb = to_kmb(dev: drm); |
232 | struct kmb_plane *primary; |
233 | int ret; |
234 | |
235 | primary = kmb_plane_init(drm); |
236 | if (IS_ERR(ptr: primary)) |
237 | return PTR_ERR(ptr: primary); |
238 | |
239 | ret = drm_crtc_init_with_planes(dev: drm, crtc: &kmb->crtc, primary: &primary->base_plane, |
240 | NULL, funcs: &kmb_crtc_funcs, NULL); |
241 | if (ret) { |
242 | kmb_plane_destroy(plane: &primary->base_plane); |
243 | return ret; |
244 | } |
245 | |
246 | drm_crtc_helper_add(crtc: &kmb->crtc, funcs: &kmb_crtc_helper_funcs); |
247 | return 0; |
248 | } |
249 | |