Linux Audio

Check our new training course

Linux kernel drivers training

May 6-19, 2025
Register
Loading...
Note: File does not exist in v6.13.7.
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 */
  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#include <drm/drm_vblank.h>
 11
 12#include "bochs.h"
 13
 14static int defx = 1024;
 15static int defy = 768;
 16
 17module_param(defx, int, 0444);
 18module_param(defy, int, 0444);
 19MODULE_PARM_DESC(defx, "default x resolution");
 20MODULE_PARM_DESC(defy, "default y resolution");
 21
 22/* ---------------------------------------------------------------------- */
 23
 24static const uint32_t bochs_formats[] = {
 25	DRM_FORMAT_XRGB8888,
 26	DRM_FORMAT_BGRX8888,
 27};
 28
 29static void bochs_plane_update(struct bochs_device *bochs,
 30			       struct drm_plane_state *state)
 31{
 32	struct drm_gem_vram_object *gbo;
 33
 34	if (!state->fb || !bochs->stride)
 35		return;
 36
 37	gbo = drm_gem_vram_of_gem(state->fb->obj[0]);
 38	bochs_hw_setbase(bochs,
 39			 state->crtc_x,
 40			 state->crtc_y,
 41			 state->fb->pitches[0],
 42			 state->fb->offsets[0] + gbo->bo.offset);
 43	bochs_hw_setformat(bochs, state->fb->format);
 44}
 45
 46static void bochs_pipe_enable(struct drm_simple_display_pipe *pipe,
 47			      struct drm_crtc_state *crtc_state,
 48			      struct drm_plane_state *plane_state)
 49{
 50	struct bochs_device *bochs = pipe->crtc.dev->dev_private;
 51
 52	bochs_hw_setmode(bochs, &crtc_state->mode);
 53	bochs_plane_update(bochs, plane_state);
 54}
 55
 56static void bochs_pipe_update(struct drm_simple_display_pipe *pipe,
 57			      struct drm_plane_state *old_state)
 58{
 59	struct bochs_device *bochs = pipe->crtc.dev->dev_private;
 60	struct drm_crtc *crtc = &pipe->crtc;
 61
 62	bochs_plane_update(bochs, pipe->plane.state);
 63
 64	if (crtc->state->event) {
 65		spin_lock_irq(&crtc->dev->event_lock);
 66		drm_crtc_send_vblank_event(crtc, crtc->state->event);
 67		crtc->state->event = NULL;
 68		spin_unlock_irq(&crtc->dev->event_lock);
 69	}
 70}
 71
 72static int bochs_pipe_prepare_fb(struct drm_simple_display_pipe *pipe,
 73				 struct drm_plane_state *new_state)
 74{
 75	struct drm_gem_vram_object *gbo;
 76
 77	if (!new_state->fb)
 78		return 0;
 79	gbo = drm_gem_vram_of_gem(new_state->fb->obj[0]);
 80	return drm_gem_vram_pin(gbo, DRM_GEM_VRAM_PL_FLAG_VRAM);
 81}
 82
 83static void bochs_pipe_cleanup_fb(struct drm_simple_display_pipe *pipe,
 84				  struct drm_plane_state *old_state)
 85{
 86	struct drm_gem_vram_object *gbo;
 87
 88	if (!old_state->fb)
 89		return;
 90	gbo = drm_gem_vram_of_gem(old_state->fb->obj[0]);
 91	drm_gem_vram_unpin(gbo);
 92}
 93
 94static const struct drm_simple_display_pipe_funcs bochs_pipe_funcs = {
 95	.enable	    = bochs_pipe_enable,
 96	.update	    = bochs_pipe_update,
 97	.prepare_fb = bochs_pipe_prepare_fb,
 98	.cleanup_fb = bochs_pipe_cleanup_fb,
 99};
100
101static int bochs_connector_get_modes(struct drm_connector *connector)
102{
103	struct bochs_device *bochs =
104		container_of(connector, struct bochs_device, connector);
105	int count = 0;
106
107	if (bochs->edid)
108		count = drm_add_edid_modes(connector, bochs->edid);
109
110	if (!count) {
111		count = drm_add_modes_noedid(connector, 8192, 8192);
112		drm_set_preferred_mode(connector, defx, defy);
113	}
114	return count;
115}
116
117static enum drm_mode_status bochs_connector_mode_valid(struct drm_connector *connector,
118				      struct drm_display_mode *mode)
119{
120	struct bochs_device *bochs =
121		container_of(connector, struct bochs_device, connector);
122	unsigned long size = mode->hdisplay * mode->vdisplay * 4;
123
124	/*
125	 * Make sure we can fit two framebuffers into video memory.
126	 * This allows up to 1600x1200 with 16 MB (default size).
127	 * If you want more try this:
128	 *     'qemu -vga std -global VGA.vgamem_mb=32 $otherargs'
129	 */
130	if (size * 2 > bochs->fb_size)
131		return MODE_BAD;
132
133	return MODE_OK;
134}
135
136static const struct drm_connector_helper_funcs bochs_connector_connector_helper_funcs = {
137	.get_modes = bochs_connector_get_modes,
138	.mode_valid = bochs_connector_mode_valid,
139};
140
141static const struct drm_connector_funcs bochs_connector_connector_funcs = {
142	.dpms = drm_helper_connector_dpms,
143	.fill_modes = drm_helper_probe_single_connector_modes,
144	.destroy = drm_connector_cleanup,
145	.reset = drm_atomic_helper_connector_reset,
146	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
147	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
148};
149
150static void bochs_connector_init(struct drm_device *dev)
151{
152	struct bochs_device *bochs = dev->dev_private;
153	struct drm_connector *connector = &bochs->connector;
154
155	drm_connector_init(dev, connector, &bochs_connector_connector_funcs,
156			   DRM_MODE_CONNECTOR_VIRTUAL);
157	drm_connector_helper_add(connector,
158				 &bochs_connector_connector_helper_funcs);
159	drm_connector_register(connector);
160
161	bochs_hw_load_edid(bochs);
162	if (bochs->edid) {
163		DRM_INFO("Found EDID data blob.\n");
164		drm_connector_attach_edid_property(connector);
165		drm_connector_update_edid_property(connector, bochs->edid);
166	}
167}
168
169static struct drm_framebuffer *
170bochs_gem_fb_create(struct drm_device *dev, struct drm_file *file,
171		    const struct drm_mode_fb_cmd2 *mode_cmd)
172{
173	if (mode_cmd->pixel_format != DRM_FORMAT_XRGB8888 &&
174	    mode_cmd->pixel_format != DRM_FORMAT_BGRX8888)
175		return ERR_PTR(-EINVAL);
176
177	return drm_gem_fb_create(dev, file, mode_cmd);
178}
179
180const struct drm_mode_config_funcs bochs_mode_funcs = {
181	.fb_create = bochs_gem_fb_create,
182	.atomic_check = drm_atomic_helper_check,
183	.atomic_commit = drm_atomic_helper_commit,
184};
185
186int bochs_kms_init(struct bochs_device *bochs)
187{
188	drm_mode_config_init(bochs->dev);
189
190	bochs->dev->mode_config.max_width = 8192;
191	bochs->dev->mode_config.max_height = 8192;
192
193	bochs->dev->mode_config.fb_base = bochs->fb_base;
194	bochs->dev->mode_config.preferred_depth = 24;
195	bochs->dev->mode_config.prefer_shadow = 0;
196	bochs->dev->mode_config.prefer_shadow_fbdev = 1;
197	bochs->dev->mode_config.quirk_addfb_prefer_host_byte_order = true;
198
199	bochs->dev->mode_config.funcs = &bochs_mode_funcs;
200
201	bochs_connector_init(bochs->dev);
202	drm_simple_display_pipe_init(bochs->dev,
203				     &bochs->pipe,
204				     &bochs_pipe_funcs,
205				     bochs_formats,
206				     ARRAY_SIZE(bochs_formats),
207				     NULL,
208				     &bochs->connector);
209
210	drm_mode_config_reset(bochs->dev);
211
212	return 0;
213}
214
215void bochs_kms_fini(struct bochs_device *bochs)
216{
217	drm_atomic_helper_shutdown(bochs->dev);
218	drm_mode_config_cleanup(bochs->dev);
219}