.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | | - * This program is free software; you can redistribute it and/or modify |
---|
3 | | - * it under the terms of the GNU General Public License as published by |
---|
4 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
5 | | - * (at your option) any later version. |
---|
6 | 3 | */ |
---|
7 | 4 | |
---|
| 5 | +#include <linux/moduleparam.h> |
---|
| 6 | + |
---|
| 7 | +#include <drm/drm_atomic_helper.h> |
---|
| 8 | +#include <drm/drm_gem_framebuffer_helper.h> |
---|
| 9 | +#include <drm/drm_probe_helper.h> |
---|
| 10 | + |
---|
8 | 11 | #include "bochs.h" |
---|
9 | | -#include <drm/drm_plane_helper.h> |
---|
10 | 12 | |
---|
11 | 13 | static int defx = 1024; |
---|
12 | 14 | static int defy = 768; |
---|
.. | .. |
---|
18 | 20 | |
---|
19 | 21 | /* ---------------------------------------------------------------------- */ |
---|
20 | 22 | |
---|
21 | | -static void bochs_crtc_dpms(struct drm_crtc *crtc, int mode) |
---|
| 23 | +static const uint32_t bochs_formats[] = { |
---|
| 24 | + DRM_FORMAT_XRGB8888, |
---|
| 25 | + DRM_FORMAT_BGRX8888, |
---|
| 26 | +}; |
---|
| 27 | + |
---|
| 28 | +static void bochs_plane_update(struct bochs_device *bochs, |
---|
| 29 | + struct drm_plane_state *state) |
---|
22 | 30 | { |
---|
23 | | - switch (mode) { |
---|
24 | | - case DRM_MODE_DPMS_ON: |
---|
25 | | - case DRM_MODE_DPMS_STANDBY: |
---|
26 | | - case DRM_MODE_DPMS_SUSPEND: |
---|
27 | | - case DRM_MODE_DPMS_OFF: |
---|
28 | | - default: |
---|
| 31 | + struct drm_gem_vram_object *gbo; |
---|
| 32 | + s64 gpu_addr; |
---|
| 33 | + |
---|
| 34 | + if (!state->fb || !bochs->stride) |
---|
29 | 35 | return; |
---|
30 | | - } |
---|
| 36 | + |
---|
| 37 | + gbo = drm_gem_vram_of_gem(state->fb->obj[0]); |
---|
| 38 | + gpu_addr = drm_gem_vram_offset(gbo); |
---|
| 39 | + if (WARN_ON_ONCE(gpu_addr < 0)) |
---|
| 40 | + return; /* Bug: we didn't pin the BO to VRAM in prepare_fb. */ |
---|
| 41 | + |
---|
| 42 | + bochs_hw_setbase(bochs, |
---|
| 43 | + state->crtc_x, |
---|
| 44 | + state->crtc_y, |
---|
| 45 | + state->fb->pitches[0], |
---|
| 46 | + state->fb->offsets[0] + gpu_addr); |
---|
| 47 | + bochs_hw_setformat(bochs, state->fb->format); |
---|
31 | 48 | } |
---|
32 | 49 | |
---|
33 | | -static int bochs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, |
---|
34 | | - struct drm_framebuffer *old_fb) |
---|
| 50 | +static void bochs_pipe_enable(struct drm_simple_display_pipe *pipe, |
---|
| 51 | + struct drm_crtc_state *crtc_state, |
---|
| 52 | + struct drm_plane_state *plane_state) |
---|
35 | 53 | { |
---|
36 | | - struct bochs_device *bochs = |
---|
37 | | - container_of(crtc, struct bochs_device, crtc); |
---|
38 | | - struct bochs_framebuffer *bochs_fb; |
---|
39 | | - struct bochs_bo *bo; |
---|
40 | | - u64 gpu_addr = 0; |
---|
41 | | - int ret; |
---|
| 54 | + struct bochs_device *bochs = pipe->crtc.dev->dev_private; |
---|
42 | 55 | |
---|
43 | | - if (old_fb) { |
---|
44 | | - bochs_fb = to_bochs_framebuffer(old_fb); |
---|
45 | | - bo = gem_to_bochs_bo(bochs_fb->obj); |
---|
46 | | - ret = ttm_bo_reserve(&bo->bo, true, false, NULL); |
---|
47 | | - if (ret) { |
---|
48 | | - DRM_ERROR("failed to reserve old_fb bo\n"); |
---|
49 | | - } else { |
---|
50 | | - bochs_bo_unpin(bo); |
---|
51 | | - ttm_bo_unreserve(&bo->bo); |
---|
52 | | - } |
---|
53 | | - } |
---|
54 | | - |
---|
55 | | - if (WARN_ON(crtc->primary->fb == NULL)) |
---|
56 | | - return -EINVAL; |
---|
57 | | - |
---|
58 | | - bochs_fb = to_bochs_framebuffer(crtc->primary->fb); |
---|
59 | | - bo = gem_to_bochs_bo(bochs_fb->obj); |
---|
60 | | - ret = ttm_bo_reserve(&bo->bo, true, false, NULL); |
---|
61 | | - if (ret) |
---|
62 | | - return ret; |
---|
63 | | - |
---|
64 | | - ret = bochs_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr); |
---|
65 | | - if (ret) { |
---|
66 | | - ttm_bo_unreserve(&bo->bo); |
---|
67 | | - return ret; |
---|
68 | | - } |
---|
69 | | - |
---|
70 | | - ttm_bo_unreserve(&bo->bo); |
---|
71 | | - bochs_hw_setbase(bochs, x, y, gpu_addr); |
---|
72 | | - return 0; |
---|
| 56 | + bochs_hw_setmode(bochs, &crtc_state->mode); |
---|
| 57 | + bochs_plane_update(bochs, plane_state); |
---|
73 | 58 | } |
---|
74 | 59 | |
---|
75 | | -static int bochs_crtc_mode_set(struct drm_crtc *crtc, |
---|
76 | | - struct drm_display_mode *mode, |
---|
77 | | - struct drm_display_mode *adjusted_mode, |
---|
78 | | - int x, int y, struct drm_framebuffer *old_fb) |
---|
| 60 | +static void bochs_pipe_update(struct drm_simple_display_pipe *pipe, |
---|
| 61 | + struct drm_plane_state *old_state) |
---|
79 | 62 | { |
---|
80 | | - struct bochs_device *bochs = |
---|
81 | | - container_of(crtc, struct bochs_device, crtc); |
---|
| 63 | + struct bochs_device *bochs = pipe->crtc.dev->dev_private; |
---|
82 | 64 | |
---|
83 | | - bochs_hw_setmode(bochs, mode); |
---|
84 | | - bochs_crtc_mode_set_base(crtc, x, y, old_fb); |
---|
85 | | - return 0; |
---|
| 65 | + bochs_plane_update(bochs, pipe->plane.state); |
---|
86 | 66 | } |
---|
87 | 67 | |
---|
88 | | -static void bochs_crtc_prepare(struct drm_crtc *crtc) |
---|
89 | | -{ |
---|
90 | | -} |
---|
91 | | - |
---|
92 | | -static void bochs_crtc_commit(struct drm_crtc *crtc) |
---|
93 | | -{ |
---|
94 | | -} |
---|
95 | | - |
---|
96 | | -static int bochs_crtc_page_flip(struct drm_crtc *crtc, |
---|
97 | | - struct drm_framebuffer *fb, |
---|
98 | | - struct drm_pending_vblank_event *event, |
---|
99 | | - uint32_t page_flip_flags, |
---|
100 | | - struct drm_modeset_acquire_ctx *ctx) |
---|
101 | | -{ |
---|
102 | | - struct bochs_device *bochs = |
---|
103 | | - container_of(crtc, struct bochs_device, crtc); |
---|
104 | | - struct drm_framebuffer *old_fb = crtc->primary->fb; |
---|
105 | | - unsigned long irqflags; |
---|
106 | | - |
---|
107 | | - crtc->primary->fb = fb; |
---|
108 | | - bochs_crtc_mode_set_base(crtc, 0, 0, old_fb); |
---|
109 | | - if (event) { |
---|
110 | | - spin_lock_irqsave(&bochs->dev->event_lock, irqflags); |
---|
111 | | - drm_crtc_send_vblank_event(crtc, event); |
---|
112 | | - spin_unlock_irqrestore(&bochs->dev->event_lock, irqflags); |
---|
113 | | - } |
---|
114 | | - return 0; |
---|
115 | | -} |
---|
116 | | - |
---|
117 | | -/* These provide the minimum set of functions required to handle a CRTC */ |
---|
118 | | -static const struct drm_crtc_funcs bochs_crtc_funcs = { |
---|
119 | | - .set_config = drm_crtc_helper_set_config, |
---|
120 | | - .destroy = drm_crtc_cleanup, |
---|
121 | | - .page_flip = bochs_crtc_page_flip, |
---|
| 68 | +static const struct drm_simple_display_pipe_funcs bochs_pipe_funcs = { |
---|
| 69 | + .enable = bochs_pipe_enable, |
---|
| 70 | + .update = bochs_pipe_update, |
---|
| 71 | + .prepare_fb = drm_gem_vram_simple_display_pipe_prepare_fb, |
---|
| 72 | + .cleanup_fb = drm_gem_vram_simple_display_pipe_cleanup_fb, |
---|
122 | 73 | }; |
---|
123 | | - |
---|
124 | | -static const struct drm_crtc_helper_funcs bochs_helper_funcs = { |
---|
125 | | - .dpms = bochs_crtc_dpms, |
---|
126 | | - .mode_set = bochs_crtc_mode_set, |
---|
127 | | - .mode_set_base = bochs_crtc_mode_set_base, |
---|
128 | | - .prepare = bochs_crtc_prepare, |
---|
129 | | - .commit = bochs_crtc_commit, |
---|
130 | | -}; |
---|
131 | | - |
---|
132 | | -static void bochs_crtc_init(struct drm_device *dev) |
---|
133 | | -{ |
---|
134 | | - struct bochs_device *bochs = dev->dev_private; |
---|
135 | | - struct drm_crtc *crtc = &bochs->crtc; |
---|
136 | | - |
---|
137 | | - drm_crtc_init(dev, crtc, &bochs_crtc_funcs); |
---|
138 | | - drm_crtc_helper_add(crtc, &bochs_helper_funcs); |
---|
139 | | -} |
---|
140 | | - |
---|
141 | | -static void bochs_encoder_mode_set(struct drm_encoder *encoder, |
---|
142 | | - struct drm_display_mode *mode, |
---|
143 | | - struct drm_display_mode *adjusted_mode) |
---|
144 | | -{ |
---|
145 | | -} |
---|
146 | | - |
---|
147 | | -static void bochs_encoder_dpms(struct drm_encoder *encoder, int state) |
---|
148 | | -{ |
---|
149 | | -} |
---|
150 | | - |
---|
151 | | -static void bochs_encoder_prepare(struct drm_encoder *encoder) |
---|
152 | | -{ |
---|
153 | | -} |
---|
154 | | - |
---|
155 | | -static void bochs_encoder_commit(struct drm_encoder *encoder) |
---|
156 | | -{ |
---|
157 | | -} |
---|
158 | | - |
---|
159 | | -static const struct drm_encoder_helper_funcs bochs_encoder_helper_funcs = { |
---|
160 | | - .dpms = bochs_encoder_dpms, |
---|
161 | | - .mode_set = bochs_encoder_mode_set, |
---|
162 | | - .prepare = bochs_encoder_prepare, |
---|
163 | | - .commit = bochs_encoder_commit, |
---|
164 | | -}; |
---|
165 | | - |
---|
166 | | -static const struct drm_encoder_funcs bochs_encoder_encoder_funcs = { |
---|
167 | | - .destroy = drm_encoder_cleanup, |
---|
168 | | -}; |
---|
169 | | - |
---|
170 | | -static void bochs_encoder_init(struct drm_device *dev) |
---|
171 | | -{ |
---|
172 | | - struct bochs_device *bochs = dev->dev_private; |
---|
173 | | - struct drm_encoder *encoder = &bochs->encoder; |
---|
174 | | - |
---|
175 | | - encoder->possible_crtcs = 0x1; |
---|
176 | | - drm_encoder_init(dev, encoder, &bochs_encoder_encoder_funcs, |
---|
177 | | - DRM_MODE_ENCODER_DAC, NULL); |
---|
178 | | - drm_encoder_helper_add(encoder, &bochs_encoder_helper_funcs); |
---|
179 | | -} |
---|
180 | | - |
---|
181 | 74 | |
---|
182 | 75 | static int bochs_connector_get_modes(struct drm_connector *connector) |
---|
183 | 76 | { |
---|
184 | | - int count; |
---|
185 | | - |
---|
186 | | - count = drm_add_modes_noedid(connector, 8192, 8192); |
---|
187 | | - drm_set_preferred_mode(connector, defx, defy); |
---|
188 | | - return count; |
---|
189 | | -} |
---|
190 | | - |
---|
191 | | -static enum drm_mode_status bochs_connector_mode_valid(struct drm_connector *connector, |
---|
192 | | - struct drm_display_mode *mode) |
---|
193 | | -{ |
---|
194 | 77 | struct bochs_device *bochs = |
---|
195 | 78 | container_of(connector, struct bochs_device, connector); |
---|
196 | | - unsigned long size = mode->hdisplay * mode->vdisplay * 4; |
---|
| 79 | + int count = 0; |
---|
197 | 80 | |
---|
198 | | - /* |
---|
199 | | - * Make sure we can fit two framebuffers into video memory. |
---|
200 | | - * This allows up to 1600x1200 with 16 MB (default size). |
---|
201 | | - * If you want more try this: |
---|
202 | | - * 'qemu -vga std -global VGA.vgamem_mb=32 $otherargs' |
---|
203 | | - */ |
---|
204 | | - if (size * 2 > bochs->fb_size) |
---|
205 | | - return MODE_BAD; |
---|
| 81 | + if (bochs->edid) |
---|
| 82 | + count = drm_add_edid_modes(connector, bochs->edid); |
---|
206 | 83 | |
---|
207 | | - return MODE_OK; |
---|
208 | | -} |
---|
209 | | - |
---|
210 | | -static struct drm_encoder * |
---|
211 | | -bochs_connector_best_encoder(struct drm_connector *connector) |
---|
212 | | -{ |
---|
213 | | - int enc_id = connector->encoder_ids[0]; |
---|
214 | | - /* pick the encoder ids */ |
---|
215 | | - if (enc_id) |
---|
216 | | - return drm_encoder_find(connector->dev, NULL, enc_id); |
---|
217 | | - return NULL; |
---|
| 84 | + if (!count) { |
---|
| 85 | + count = drm_add_modes_noedid(connector, 8192, 8192); |
---|
| 86 | + drm_set_preferred_mode(connector, defx, defy); |
---|
| 87 | + } |
---|
| 88 | + return count; |
---|
218 | 89 | } |
---|
219 | 90 | |
---|
220 | 91 | static const struct drm_connector_helper_funcs bochs_connector_connector_helper_funcs = { |
---|
221 | 92 | .get_modes = bochs_connector_get_modes, |
---|
222 | | - .mode_valid = bochs_connector_mode_valid, |
---|
223 | | - .best_encoder = bochs_connector_best_encoder, |
---|
224 | 93 | }; |
---|
225 | 94 | |
---|
226 | 95 | static const struct drm_connector_funcs bochs_connector_connector_funcs = { |
---|
227 | | - .dpms = drm_helper_connector_dpms, |
---|
228 | 96 | .fill_modes = drm_helper_probe_single_connector_modes, |
---|
229 | 97 | .destroy = drm_connector_cleanup, |
---|
| 98 | + .reset = drm_atomic_helper_connector_reset, |
---|
| 99 | + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, |
---|
| 100 | + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, |
---|
230 | 101 | }; |
---|
231 | 102 | |
---|
232 | 103 | static void bochs_connector_init(struct drm_device *dev) |
---|
.. | .. |
---|
238 | 109 | DRM_MODE_CONNECTOR_VIRTUAL); |
---|
239 | 110 | drm_connector_helper_add(connector, |
---|
240 | 111 | &bochs_connector_connector_helper_funcs); |
---|
241 | | - drm_connector_register(connector); |
---|
| 112 | + |
---|
| 113 | + bochs_hw_load_edid(bochs); |
---|
| 114 | + if (bochs->edid) { |
---|
| 115 | + DRM_INFO("Found EDID data blob.\n"); |
---|
| 116 | + drm_connector_attach_edid_property(connector); |
---|
| 117 | + drm_connector_update_edid_property(connector, bochs->edid); |
---|
| 118 | + } |
---|
242 | 119 | } |
---|
243 | 120 | |
---|
| 121 | +static struct drm_framebuffer * |
---|
| 122 | +bochs_gem_fb_create(struct drm_device *dev, struct drm_file *file, |
---|
| 123 | + const struct drm_mode_fb_cmd2 *mode_cmd) |
---|
| 124 | +{ |
---|
| 125 | + if (mode_cmd->pixel_format != DRM_FORMAT_XRGB8888 && |
---|
| 126 | + mode_cmd->pixel_format != DRM_FORMAT_BGRX8888) |
---|
| 127 | + return ERR_PTR(-EINVAL); |
---|
| 128 | + |
---|
| 129 | + return drm_gem_fb_create(dev, file, mode_cmd); |
---|
| 130 | +} |
---|
| 131 | + |
---|
| 132 | +const struct drm_mode_config_funcs bochs_mode_funcs = { |
---|
| 133 | + .fb_create = bochs_gem_fb_create, |
---|
| 134 | + .mode_valid = drm_vram_helper_mode_valid, |
---|
| 135 | + .atomic_check = drm_atomic_helper_check, |
---|
| 136 | + .atomic_commit = drm_atomic_helper_commit, |
---|
| 137 | +}; |
---|
244 | 138 | |
---|
245 | 139 | int bochs_kms_init(struct bochs_device *bochs) |
---|
246 | 140 | { |
---|
247 | | - drm_mode_config_init(bochs->dev); |
---|
248 | | - bochs->mode_config_initialized = true; |
---|
| 141 | + int ret; |
---|
| 142 | + |
---|
| 143 | + ret = drmm_mode_config_init(bochs->dev); |
---|
| 144 | + if (ret) |
---|
| 145 | + return ret; |
---|
249 | 146 | |
---|
250 | 147 | bochs->dev->mode_config.max_width = 8192; |
---|
251 | 148 | bochs->dev->mode_config.max_height = 8192; |
---|
.. | .. |
---|
253 | 150 | bochs->dev->mode_config.fb_base = bochs->fb_base; |
---|
254 | 151 | bochs->dev->mode_config.preferred_depth = 24; |
---|
255 | 152 | bochs->dev->mode_config.prefer_shadow = 0; |
---|
| 153 | + bochs->dev->mode_config.prefer_shadow_fbdev = 1; |
---|
| 154 | + bochs->dev->mode_config.fbdev_use_iomem = true; |
---|
| 155 | + bochs->dev->mode_config.quirk_addfb_prefer_host_byte_order = true; |
---|
256 | 156 | |
---|
257 | 157 | bochs->dev->mode_config.funcs = &bochs_mode_funcs; |
---|
258 | 158 | |
---|
259 | | - bochs_crtc_init(bochs->dev); |
---|
260 | | - bochs_encoder_init(bochs->dev); |
---|
261 | 159 | bochs_connector_init(bochs->dev); |
---|
262 | | - drm_connector_attach_encoder(&bochs->connector, |
---|
263 | | - &bochs->encoder); |
---|
| 160 | + drm_simple_display_pipe_init(bochs->dev, |
---|
| 161 | + &bochs->pipe, |
---|
| 162 | + &bochs_pipe_funcs, |
---|
| 163 | + bochs_formats, |
---|
| 164 | + ARRAY_SIZE(bochs_formats), |
---|
| 165 | + NULL, |
---|
| 166 | + &bochs->connector); |
---|
| 167 | + |
---|
| 168 | + drm_mode_config_reset(bochs->dev); |
---|
264 | 169 | |
---|
265 | 170 | return 0; |
---|
266 | | -} |
---|
267 | | - |
---|
268 | | -void bochs_kms_fini(struct bochs_device *bochs) |
---|
269 | | -{ |
---|
270 | | - if (bochs->mode_config_initialized) { |
---|
271 | | - drm_mode_config_cleanup(bochs->dev); |
---|
272 | | - bochs->mode_config_initialized = false; |
---|
273 | | - } |
---|
274 | 171 | } |
---|