Files
linux/drivers/gpu/drm/mgag200/mgag200_bmc.c
Thomas Zimmermann 202fe72cce 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
2024-06-13 09:31:31 +02:00

207 lines
5.6 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
#include <linux/delay.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_managed.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
static struct mgag200_bmc_connector *to_mgag200_bmc_connector(struct drm_connector *connector)
{
return container_of(connector, struct mgag200_bmc_connector, base);
}
void mgag200_bmc_disable_vidrst(struct mga_device *mdev)
{
u8 tmp;
int iter_max;
/*
* 1 - The first step is to inform the BMC of an upcoming mode
* change. We are putting the misc<0> to output.
*/
WREG8(DAC_INDEX, MGA1064_GEN_IO_CTL);
tmp = RREG8(DAC_DATA);
tmp |= 0x10;
WREG_DAC(MGA1064_GEN_IO_CTL, tmp);
/* we are putting a 1 on the misc<0> line */
WREG8(DAC_INDEX, MGA1064_GEN_IO_DATA);
tmp = RREG8(DAC_DATA);
tmp |= 0x10;
WREG_DAC(MGA1064_GEN_IO_DATA, tmp);
/*
* 2- Second step to mask any further scan request. This is
* done by asserting the remfreqmsk bit (XSPAREREG<7>)
*/
WREG8(DAC_INDEX, MGA1064_SPAREREG);
tmp = RREG8(DAC_DATA);
tmp |= 0x80;
WREG_DAC(MGA1064_SPAREREG, tmp);
/*
* 3a- The third step is to verify if there is an active scan.
* We are waiting for a 0 on remhsyncsts <XSPAREREG<0>).
*/
iter_max = 300;
while (!(tmp & 0x1) && iter_max) {
WREG8(DAC_INDEX, MGA1064_SPAREREG);
tmp = RREG8(DAC_DATA);
udelay(1000);
iter_max--;
}
/*
* 3b- This step occurs only if the remove is actually
* scanning. We are waiting for the end of the frame which is
* a 1 on remvsyncsts (XSPAREREG<1>)
*/
if (iter_max) {
iter_max = 300;
while ((tmp & 0x2) && iter_max) {
WREG8(DAC_INDEX, MGA1064_SPAREREG);
tmp = RREG8(DAC_DATA);
udelay(1000);
iter_max--;
}
}
}
void mgag200_bmc_enable_vidrst(struct mga_device *mdev)
{
u8 tmp;
/* Ensure that the vrsten and hrsten are set */
WREG8(MGAREG_CRTCEXT_INDEX, 1);
tmp = RREG8(MGAREG_CRTCEXT_DATA);
WREG8(MGAREG_CRTCEXT_DATA, tmp | 0x88);
/* Assert rstlvl2 */
WREG8(DAC_INDEX, MGA1064_REMHEADCTL2);
tmp = RREG8(DAC_DATA);
tmp |= 0x8;
WREG8(DAC_DATA, tmp);
udelay(10);
/* Deassert rstlvl2 */
tmp &= ~0x08;
WREG8(DAC_INDEX, MGA1064_REMHEADCTL2);
WREG8(DAC_DATA, tmp);
/* Remove mask of scan request */
WREG8(DAC_INDEX, MGA1064_SPAREREG);
tmp = RREG8(DAC_DATA);
tmp &= ~0x80;
WREG8(DAC_DATA, tmp);
/* Put back a 0 on the misc<0> line */
WREG8(DAC_INDEX, MGA1064_GEN_IO_DATA);
tmp = RREG8(DAC_DATA);
tmp &= ~0x10;
WREG_DAC(MGA1064_GEN_IO_DATA, tmp);
}
static const struct drm_encoder_funcs mgag200_bmc_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
static int mgag200_bmc_connector_helper_detect_ctx(struct drm_connector *connector,
struct drm_modeset_acquire_ctx *ctx,
bool force)
{
struct mgag200_bmc_connector *bmc_connector = to_mgag200_bmc_connector(connector);
struct drm_connector *physical_connector = bmc_connector->physical_connector;
/*
* Most user-space compositors cannot handle more than one connected
* connector per CRTC. Hence, we only mark the BMC as connected if the
* physical connector is disconnected. If the physical connector's status
* is connected or unknown, the BMC remains disconnected. This has no
* effect on the output of the BMC.
*
* FIXME: Remove this logic once user-space compositors can handle more
* than one connector per CRTC. The BMC should always be connected.
*/
if (physical_connector && physical_connector->status == connector_status_disconnected)
return connector_status_connected;
return connector_status_disconnected;
}
static int mgag200_bmc_connector_helper_get_modes(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct mga_device *mdev = to_mga_device(dev);
const struct mgag200_device_info *minfo = mdev->info;
return drm_add_modes_noedid(connector, minfo->max_hdisplay, minfo->max_vdisplay);
}
static const struct drm_connector_helper_funcs mgag200_bmc_connector_helper_funcs = {
.get_modes = mgag200_bmc_connector_helper_get_modes,
.detect_ctx = mgag200_bmc_connector_helper_detect_ctx,
};
static const struct drm_connector_funcs mgag200_bmc_connector_funcs = {
.reset = drm_atomic_helper_connector_reset,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int mgag200_bmc_connector_init(struct drm_device *dev,
struct mgag200_bmc_connector *bmc_connector,
struct drm_connector *physical_connector)
{
struct drm_connector *connector = &bmc_connector->base;
int ret;
ret = drm_connector_init(dev, connector, &mgag200_bmc_connector_funcs,
DRM_MODE_CONNECTOR_VIRTUAL);
if (ret)
return ret;
drm_connector_helper_add(connector, &mgag200_bmc_connector_helper_funcs);
bmc_connector->physical_connector = physical_connector;
return 0;
}
int mgag200_bmc_output_init(struct mga_device *mdev, struct drm_connector *physical_connector)
{
struct drm_device *dev = &mdev->base;
struct drm_crtc *crtc = &mdev->crtc;
struct drm_encoder *encoder;
struct mgag200_bmc_connector *bmc_connector;
struct drm_connector *connector;
int ret;
encoder = &mdev->output.bmc.encoder;
ret = drm_encoder_init(dev, encoder, &mgag200_bmc_encoder_funcs,
DRM_MODE_ENCODER_VIRTUAL, NULL);
if (ret)
return ret;
encoder->possible_crtcs = drm_crtc_mask(crtc);
bmc_connector = &mdev->output.bmc.bmc_connector;
ret = mgag200_bmc_connector_init(dev, bmc_connector, physical_connector);
if (ret)
return ret;
connector = &bmc_connector->base;
ret = drm_connector_attach_encoder(connector, encoder);
if (ret)
return ret;
return 0;
}