]>
Commit | Line | Data |
---|---|---|
51dacf20 CP |
1 | /* |
2 | * ARC PGU DRM driver. | |
3 | * | |
4 | * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com) | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | */ | |
16 | ||
17 | #include <drm/drm_atomic_helper.h> | |
fe1f664a | 18 | #include <drm/drm_device.h> |
51dacf20 CP |
19 | #include <drm/drm_fb_cma_helper.h> |
20 | #include <drm/drm_gem_cma_helper.h> | |
fe1f664a | 21 | #include <drm/drm_vblank.h> |
51dacf20 | 22 | #include <drm/drm_plane_helper.h> |
fcd70cd3 | 23 | #include <drm/drm_probe_helper.h> |
51dacf20 CP |
24 | #include <linux/clk.h> |
25 | #include <linux/platform_data/simplefb.h> | |
26 | ||
27 | #include "arcpgu.h" | |
28 | #include "arcpgu_regs.h" | |
29 | ||
30 | #define ENCODE_PGU_XY(x, y) ((((x) - 1) << 16) | ((y) - 1)) | |
31 | ||
32 | static struct simplefb_format supported_formats[] = { | |
33 | { "r5g6b5", 16, {11, 5}, {5, 6}, {0, 5}, {0, 0}, DRM_FORMAT_RGB565 }, | |
34 | { "r8g8b8", 24, {16, 8}, {8, 8}, {0, 8}, {0, 0}, DRM_FORMAT_RGB888 }, | |
35 | }; | |
36 | ||
37 | static void arc_pgu_set_pxl_fmt(struct drm_crtc *crtc) | |
38 | { | |
39 | struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc); | |
81852b6a | 40 | const struct drm_framebuffer *fb = crtc->primary->state->fb; |
438b74a5 | 41 | uint32_t pixel_format = fb->format->format; |
51dacf20 CP |
42 | struct simplefb_format *format = NULL; |
43 | int i; | |
44 | ||
45 | for (i = 0; i < ARRAY_SIZE(supported_formats); i++) { | |
46 | if (supported_formats[i].fourcc == pixel_format) | |
47 | format = &supported_formats[i]; | |
48 | } | |
49 | ||
50 | if (WARN_ON(!format)) | |
51 | return; | |
52 | ||
53 | if (format->fourcc == DRM_FORMAT_RGB888) | |
54 | arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, | |
55 | arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) | | |
56 | ARCPGU_MODE_RGB888_MASK); | |
57 | ||
58 | } | |
59 | ||
60 | static const struct drm_crtc_funcs arc_pgu_crtc_funcs = { | |
61 | .destroy = drm_crtc_cleanup, | |
62 | .set_config = drm_atomic_helper_set_config, | |
63 | .page_flip = drm_atomic_helper_page_flip, | |
64 | .reset = drm_atomic_helper_crtc_reset, | |
65 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, | |
66 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, | |
67 | }; | |
68 | ||
2bf5ccc2 | 69 | static enum drm_mode_status arc_pgu_crtc_mode_valid(struct drm_crtc *crtc, |
70 | const struct drm_display_mode *mode) | |
2b3d860e JA |
71 | { |
72 | struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc); | |
73 | long rate, clk_rate = mode->clock * 1000; | |
22d0be2a | 74 | long diff = clk_rate / 200; /* +-0.5% allowed by HDMI spec */ |
2b3d860e JA |
75 | |
76 | rate = clk_round_rate(arcpgu->clk, clk_rate); | |
22d0be2a JA |
77 | if ((max(rate, clk_rate) - min(rate, clk_rate) < diff) && (rate > 0)) |
78 | return MODE_OK; | |
2b3d860e | 79 | |
22d0be2a | 80 | return MODE_NOCLOCK; |
2b3d860e JA |
81 | } |
82 | ||
51dacf20 CP |
83 | static void arc_pgu_crtc_mode_set_nofb(struct drm_crtc *crtc) |
84 | { | |
85 | struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc); | |
86 | struct drm_display_mode *m = &crtc->state->adjusted_mode; | |
87 | u32 val; | |
88 | ||
89 | arc_pgu_write(arcpgu, ARCPGU_REG_FMT, | |
90 | ENCODE_PGU_XY(m->crtc_htotal, m->crtc_vtotal)); | |
91 | ||
92 | arc_pgu_write(arcpgu, ARCPGU_REG_HSYNC, | |
93 | ENCODE_PGU_XY(m->crtc_hsync_start - m->crtc_hdisplay, | |
94 | m->crtc_hsync_end - m->crtc_hdisplay)); | |
95 | ||
96 | arc_pgu_write(arcpgu, ARCPGU_REG_VSYNC, | |
97 | ENCODE_PGU_XY(m->crtc_vsync_start - m->crtc_vdisplay, | |
98 | m->crtc_vsync_end - m->crtc_vdisplay)); | |
99 | ||
100 | arc_pgu_write(arcpgu, ARCPGU_REG_ACTIVE, | |
101 | ENCODE_PGU_XY(m->crtc_hblank_end - m->crtc_hblank_start, | |
102 | m->crtc_vblank_end - m->crtc_vblank_start)); | |
103 | ||
104 | val = arc_pgu_read(arcpgu, ARCPGU_REG_CTRL); | |
105 | ||
106 | if (m->flags & DRM_MODE_FLAG_PVSYNC) | |
107 | val |= ARCPGU_CTRL_VS_POL_MASK << ARCPGU_CTRL_VS_POL_OFST; | |
108 | else | |
109 | val &= ~(ARCPGU_CTRL_VS_POL_MASK << ARCPGU_CTRL_VS_POL_OFST); | |
110 | ||
111 | if (m->flags & DRM_MODE_FLAG_PHSYNC) | |
112 | val |= ARCPGU_CTRL_HS_POL_MASK << ARCPGU_CTRL_HS_POL_OFST; | |
113 | else | |
114 | val &= ~(ARCPGU_CTRL_HS_POL_MASK << ARCPGU_CTRL_HS_POL_OFST); | |
115 | ||
116 | arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, val); | |
117 | arc_pgu_write(arcpgu, ARCPGU_REG_STRIDE, 0); | |
118 | arc_pgu_write(arcpgu, ARCPGU_REG_START_SET, 1); | |
119 | ||
120 | arc_pgu_set_pxl_fmt(crtc); | |
121 | ||
122 | clk_set_rate(arcpgu->clk, m->crtc_clock * 1000); | |
123 | } | |
124 | ||
0b20a0f8 LP |
125 | static void arc_pgu_crtc_atomic_enable(struct drm_crtc *crtc, |
126 | struct drm_crtc_state *old_state) | |
51dacf20 CP |
127 | { |
128 | struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc); | |
129 | ||
130 | clk_prepare_enable(arcpgu->clk); | |
131 | arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, | |
132 | arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) | | |
133 | ARCPGU_CTRL_ENABLE_MASK); | |
134 | } | |
135 | ||
64581714 LP |
136 | static void arc_pgu_crtc_atomic_disable(struct drm_crtc *crtc, |
137 | struct drm_crtc_state *old_state) | |
51dacf20 CP |
138 | { |
139 | struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc); | |
140 | ||
51dacf20 CP |
141 | clk_disable_unprepare(arcpgu->clk); |
142 | arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, | |
143 | arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) & | |
144 | ~ARCPGU_CTRL_ENABLE_MASK); | |
145 | } | |
146 | ||
51dacf20 CP |
147 | static void arc_pgu_crtc_atomic_begin(struct drm_crtc *crtc, |
148 | struct drm_crtc_state *state) | |
149 | { | |
84d9a4a2 | 150 | struct drm_pending_vblank_event *event = crtc->state->event; |
51dacf20 | 151 | |
84d9a4a2 | 152 | if (event) { |
51dacf20 | 153 | crtc->state->event = NULL; |
51dacf20 | 154 | |
84d9a4a2 DV |
155 | spin_lock_irq(&crtc->dev->event_lock); |
156 | drm_crtc_send_vblank_event(crtc, event); | |
157 | spin_unlock_irq(&crtc->dev->event_lock); | |
51dacf20 CP |
158 | } |
159 | } | |
160 | ||
161 | static const struct drm_crtc_helper_funcs arc_pgu_crtc_helper_funcs = { | |
2b3d860e | 162 | .mode_valid = arc_pgu_crtc_mode_valid, |
51dacf20 | 163 | .mode_set_nofb = arc_pgu_crtc_mode_set_nofb, |
51dacf20 | 164 | .atomic_begin = arc_pgu_crtc_atomic_begin, |
0b20a0f8 | 165 | .atomic_enable = arc_pgu_crtc_atomic_enable, |
64581714 | 166 | .atomic_disable = arc_pgu_crtc_atomic_disable, |
51dacf20 CP |
167 | }; |
168 | ||
169 | static void arc_pgu_plane_atomic_update(struct drm_plane *plane, | |
170 | struct drm_plane_state *state) | |
171 | { | |
172 | struct arcpgu_drm_private *arcpgu; | |
173 | struct drm_gem_cma_object *gem; | |
174 | ||
175 | if (!plane->state->crtc || !plane->state->fb) | |
176 | return; | |
177 | ||
178 | arcpgu = crtc_to_arcpgu_priv(plane->state->crtc); | |
179 | gem = drm_fb_cma_get_gem_obj(plane->state->fb, 0); | |
180 | arc_pgu_write(arcpgu, ARCPGU_REG_BUF0_ADDR, gem->paddr); | |
181 | } | |
182 | ||
183 | static const struct drm_plane_helper_funcs arc_pgu_plane_helper_funcs = { | |
51dacf20 CP |
184 | .atomic_update = arc_pgu_plane_atomic_update, |
185 | }; | |
186 | ||
187 | static void arc_pgu_plane_destroy(struct drm_plane *plane) | |
188 | { | |
51dacf20 CP |
189 | drm_plane_cleanup(plane); |
190 | } | |
191 | ||
192 | static const struct drm_plane_funcs arc_pgu_plane_funcs = { | |
193 | .update_plane = drm_atomic_helper_update_plane, | |
194 | .disable_plane = drm_atomic_helper_disable_plane, | |
195 | .destroy = arc_pgu_plane_destroy, | |
196 | .reset = drm_atomic_helper_plane_reset, | |
197 | .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, | |
198 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, | |
199 | }; | |
200 | ||
201 | static struct drm_plane *arc_pgu_plane_init(struct drm_device *drm) | |
202 | { | |
203 | struct arcpgu_drm_private *arcpgu = drm->dev_private; | |
204 | struct drm_plane *plane = NULL; | |
205 | u32 formats[ARRAY_SIZE(supported_formats)], i; | |
206 | int ret; | |
207 | ||
208 | plane = devm_kzalloc(drm->dev, sizeof(*plane), GFP_KERNEL); | |
209 | if (!plane) | |
210 | return ERR_PTR(-ENOMEM); | |
211 | ||
212 | for (i = 0; i < ARRAY_SIZE(supported_formats); i++) | |
213 | formats[i] = supported_formats[i].fourcc; | |
214 | ||
215 | ret = drm_universal_plane_init(drm, plane, 0xff, &arc_pgu_plane_funcs, | |
216 | formats, ARRAY_SIZE(formats), | |
e6fc3b68 | 217 | NULL, |
51dacf20 CP |
218 | DRM_PLANE_TYPE_PRIMARY, NULL); |
219 | if (ret) | |
220 | return ERR_PTR(ret); | |
221 | ||
222 | drm_plane_helper_add(plane, &arc_pgu_plane_helper_funcs); | |
223 | arcpgu->plane = plane; | |
224 | ||
225 | return plane; | |
226 | } | |
227 | ||
228 | int arc_pgu_setup_crtc(struct drm_device *drm) | |
229 | { | |
230 | struct arcpgu_drm_private *arcpgu = drm->dev_private; | |
231 | struct drm_plane *primary; | |
232 | int ret; | |
233 | ||
234 | primary = arc_pgu_plane_init(drm); | |
235 | if (IS_ERR(primary)) | |
236 | return PTR_ERR(primary); | |
237 | ||
238 | ret = drm_crtc_init_with_planes(drm, &arcpgu->crtc, primary, NULL, | |
239 | &arc_pgu_crtc_funcs, NULL); | |
240 | if (ret) { | |
241 | arc_pgu_plane_destroy(primary); | |
242 | return ret; | |
243 | } | |
244 | ||
245 | drm_crtc_helper_add(&arcpgu->crtc, &arc_pgu_crtc_helper_funcs); | |
246 | return 0; | |
247 | } |