Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/mgag200: Add BMC output

The BMC output can be viewed via the BMC's web interface or a
similar client. Represent it as virtual encoder and connector.
It's attached to the same CRTC as the VGA connector.

The connector's status depends on the physical connector's status.
The BMC is only connected if the physical connector is not. This
is necessary to support userspace clients that can only handle a
single output per CRTC.

The BMC is a server feature. Add a BMC output for all server chips,
but not the desktop models.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Jocelyn Falempe <jfalempe@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240610141141.29527-3-tzimmermann@suse.de

+145
+107
drivers/gpu/drm/mgag200/mgag200_bmc.c
··· 2 2 3 3 #include <linux/delay.h> 4 4 5 + #include <drm/drm_atomic_helper.h> 6 + #include <drm/drm_edid.h> 7 + #include <drm/drm_managed.h> 8 + #include <drm/drm_probe_helper.h> 9 + 5 10 #include "mgag200_drv.h" 11 + 12 + static struct mgag200_bmc_connector *to_mgag200_bmc_connector(struct drm_connector *connector) 13 + { 14 + return container_of(connector, struct mgag200_bmc_connector, base); 15 + } 6 16 7 17 void mgag200_bmc_disable_vidrst(struct mga_device *mdev) 8 18 { ··· 106 96 tmp = RREG8(DAC_DATA); 107 97 tmp &= ~0x10; 108 98 WREG_DAC(MGA1064_GEN_IO_DATA, tmp); 99 + } 100 + 101 + static const struct drm_encoder_funcs mgag200_bmc_encoder_funcs = { 102 + .destroy = drm_encoder_cleanup, 103 + }; 104 + 105 + static int mgag200_bmc_connector_helper_detect_ctx(struct drm_connector *connector, 106 + struct drm_modeset_acquire_ctx *ctx, 107 + bool force) 108 + { 109 + struct mgag200_bmc_connector *bmc_connector = to_mgag200_bmc_connector(connector); 110 + struct drm_connector *physical_connector = bmc_connector->physical_connector; 111 + 112 + /* 113 + * Most user-space compositors cannot handle more than one connected 114 + * connector per CRTC. Hence, we only mark the BMC as connected if the 115 + * physical connector is disconnected. If the physical connector's status 116 + * is connected or unknown, the BMC remains disconnected. This has no 117 + * effect on the output of the BMC. 118 + * 119 + * FIXME: Remove this logic once user-space compositors can handle more 120 + * than one connector per CRTC. The BMC should always be connected. 121 + */ 122 + 123 + if (physical_connector && physical_connector->status == connector_status_disconnected) 124 + return connector_status_connected; 125 + 126 + return connector_status_disconnected; 127 + } 128 + 129 + static int mgag200_bmc_connector_helper_get_modes(struct drm_connector *connector) 130 + { 131 + struct drm_device *dev = connector->dev; 132 + struct mga_device *mdev = to_mga_device(dev); 133 + const struct mgag200_device_info *minfo = mdev->info; 134 + 135 + return drm_add_modes_noedid(connector, minfo->max_hdisplay, minfo->max_vdisplay); 136 + } 137 + 138 + static const struct drm_connector_helper_funcs mgag200_bmc_connector_helper_funcs = { 139 + .get_modes = mgag200_bmc_connector_helper_get_modes, 140 + .detect_ctx = mgag200_bmc_connector_helper_detect_ctx, 141 + }; 142 + 143 + static const struct drm_connector_funcs mgag200_bmc_connector_funcs = { 144 + .reset = drm_atomic_helper_connector_reset, 145 + .fill_modes = drm_helper_probe_single_connector_modes, 146 + .destroy = drm_connector_cleanup, 147 + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 148 + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 149 + }; 150 + 151 + static int mgag200_bmc_connector_init(struct drm_device *dev, 152 + struct mgag200_bmc_connector *bmc_connector, 153 + struct drm_connector *physical_connector) 154 + { 155 + struct drm_connector *connector = &bmc_connector->base; 156 + int ret; 157 + 158 + ret = drm_connector_init(dev, connector, &mgag200_bmc_connector_funcs, 159 + DRM_MODE_CONNECTOR_VIRTUAL); 160 + if (ret) 161 + return ret; 162 + drm_connector_helper_add(connector, &mgag200_bmc_connector_helper_funcs); 163 + 164 + bmc_connector->physical_connector = physical_connector; 165 + 166 + return 0; 167 + } 168 + 169 + int mgag200_bmc_output_init(struct mga_device *mdev, struct drm_connector *physical_connector) 170 + { 171 + struct drm_device *dev = &mdev->base; 172 + struct drm_crtc *crtc = &mdev->crtc; 173 + struct drm_encoder *encoder; 174 + struct mgag200_bmc_connector *bmc_connector; 175 + struct drm_connector *connector; 176 + int ret; 177 + 178 + encoder = &mdev->output.bmc.encoder; 179 + ret = drm_encoder_init(dev, encoder, &mgag200_bmc_encoder_funcs, 180 + DRM_MODE_ENCODER_VIRTUAL, NULL); 181 + if (ret) 182 + return ret; 183 + encoder->possible_crtcs = drm_crtc_mask(crtc); 184 + 185 + bmc_connector = &mdev->output.bmc.bmc_connector; 186 + ret = mgag200_bmc_connector_init(dev, bmc_connector, physical_connector); 187 + if (ret) 188 + return ret; 189 + connector = &bmc_connector->base; 190 + 191 + ret = drm_connector_attach_encoder(connector, encoder); 192 + if (ret) 193 + return ret; 194 + 195 + return 0; 109 196 }
+10
drivers/gpu/drm/mgag200/mgag200_drv.h
··· 186 186 return container_of(base, struct mgag200_crtc_state, base); 187 187 } 188 188 189 + struct mgag200_bmc_connector { 190 + struct drm_connector base; 191 + struct drm_connector *physical_connector; 192 + }; 193 + 189 194 enum mga_type { 190 195 G200_PCI, 191 196 G200_AGP, ··· 293 288 struct drm_encoder encoder; 294 289 struct drm_connector connector; 295 290 } vga; 291 + struct { 292 + struct drm_encoder encoder; 293 + struct mgag200_bmc_connector bmc_connector; 294 + } bmc; 296 295 } output; 297 296 }; 298 297 ··· 442 433 /* mgag200_bmc.c */ 443 434 void mgag200_bmc_disable_vidrst(struct mga_device *mdev); 444 435 void mgag200_bmc_enable_vidrst(struct mga_device *mdev); 436 + int mgag200_bmc_output_init(struct mga_device *mdev, struct drm_connector *physical_connector); 445 437 446 438 #endif /* __MGAG200_DRV_H__ */
+4
drivers/gpu/drm/mgag200/mgag200_g200eh.c
··· 218 218 if (ret) 219 219 return ret; 220 220 221 + ret = mgag200_bmc_output_init(mdev, &mdev->output.vga.connector); 222 + if (ret) 223 + return ret; 224 + 221 225 return 0; 222 226 } 223 227
+4
drivers/gpu/drm/mgag200/mgag200_g200eh3.c
··· 122 122 if (ret) 123 123 return ret; 124 124 125 + ret = mgag200_bmc_output_init(mdev, &mdev->output.vga.connector); 126 + if (ret) 127 + return ret; 128 + 125 129 return 0; 126 130 } 127 131
+4
drivers/gpu/drm/mgag200/mgag200_g200er.c
··· 261 261 if (ret) 262 262 return ret; 263 263 264 + ret = mgag200_bmc_output_init(mdev, &mdev->output.vga.connector); 265 + if (ret) 266 + return ret; 267 + 264 268 return 0; 265 269 } 266 270
+4
drivers/gpu/drm/mgag200/mgag200_g200ev.c
··· 262 262 if (ret) 263 263 return ret; 264 264 265 + ret = mgag200_bmc_output_init(mdev, &mdev->output.vga.connector); 266 + if (ret) 267 + return ret; 268 + 265 269 return 0; 266 270 } 267 271
+4
drivers/gpu/drm/mgag200/mgag200_g200ew3.c
··· 131 131 if (ret) 132 132 return ret; 133 133 134 + ret = mgag200_bmc_output_init(mdev, &mdev->output.vga.connector); 135 + if (ret) 136 + return ret; 137 + 134 138 return 0; 135 139 } 136 140
+4
drivers/gpu/drm/mgag200/mgag200_g200se.c
··· 393 393 if (ret) 394 394 return ret; 395 395 396 + ret = mgag200_bmc_output_init(mdev, &mdev->output.vga.connector); 397 + if (ret) 398 + return ret; 399 + 396 400 return 0; 397 401 } 398 402
+4
drivers/gpu/drm/mgag200/mgag200_g200wb.c
··· 265 265 if (ret) 266 266 return ret; 267 267 268 + ret = mgag200_bmc_output_init(mdev, &mdev->output.vga.connector); 269 + if (ret) 270 + return ret; 271 + 268 272 return 0; 269 273 } 270 274