drm/mgag200: Provide per-device callbacks for BMC synchronization

Move the BMC-related code into its own file and wire it up with device
callbacks.

While programming a new display mode, G200EW3 and G200WB have to de-
synchronize with the BMC. Synchronization is done via VIDRST pins
and controlled via VRSTEN and HRSTEN bits. Move the BMC code behind
a serviceable interface and call it from the CRTC's enable and
disable functions.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Jocelyn Falempe <jfalempe@redhat.com>
Tested-by: Jocelyn Falempe <jfalempe@redhat.com>
Acked-by: Sam Ravnborg <sam@ravnborg.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20220728124103.30159-11-tzimmermann@suse.de
This commit is contained in:
Thomas Zimmermann 2022-07-28 14:40:59 +02:00
parent f639f74a78
commit 8aeeb3144f
6 changed files with 129 additions and 97 deletions

View File

@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
mgag200-y := \
mgag200_bmc.o \
mgag200_drv.o \
mgag200_g200.o \
mgag200_g200eh.o \

View File

@ -0,0 +1,99 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/delay.h>
#include "mgag200_drv.h"
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);
}

View File

@ -263,6 +263,17 @@ struct mgag200_device_info {
}
struct mgag200_device_funcs {
/*
* Disables an external reset source (i.e., BMC) before programming
* a new display mode.
*/
void (*disable_vidrst)(struct mga_device *mdev);
/*
* Enables an external reset source (i.e., BMC) after programming
* a new display mode.
*/
void (*enable_vidrst)(struct mga_device *mdev);
};
struct mga_device {
@ -354,6 +365,10 @@ resource_size_t mgag200_device_probe_vram(struct mga_device *mdev);
void mgag200_init_registers(struct mga_device *mdev);
int mgag200_modeset_init(struct mga_device *mdev, resource_size_t vram_fb_available);
/* mgag200_bmc.c */
void mgag200_bmc_disable_vidrst(struct mga_device *mdev);
void mgag200_bmc_enable_vidrst(struct mga_device *mdev);
/* mgag200_i2c.c */
int mgag200_i2c_init(struct mga_device *mdev, struct mga_i2c_chan *i2c);

View File

@ -21,6 +21,8 @@ static const struct mgag200_device_info mgag200_g200ew3_device_info =
MGAG200_DEVICE_INFO_INIT(2048, 2048, 0, true, 0, 1, false);
static const struct mgag200_device_funcs mgag200_g200ew3_device_funcs = {
.disable_vidrst = mgag200_bmc_disable_vidrst,
.enable_vidrst = mgag200_bmc_enable_vidrst,
};
static resource_size_t mgag200_g200ew3_device_probe_vram(struct mga_device *mdev)

View File

@ -36,6 +36,8 @@ static const struct mgag200_device_info mgag200_g200wb_device_info =
MGAG200_DEVICE_INFO_INIT(1280, 1024, 31877, true, 0, 1, false);
static const struct mgag200_device_funcs mgag200_g200wb_device_funcs = {
.disable_vidrst = mgag200_bmc_disable_vidrst,
.enable_vidrst = mgag200_bmc_enable_vidrst,
};
struct mga_device *mgag200_g200wb_device_create(struct pci_dev *pdev, const struct drm_driver *drv,

View File

@ -131,95 +131,6 @@ static inline void mga_wait_busy(struct mga_device *mdev)
} while ((status & 0x01) && time_before(jiffies, timeout));
}
static void mgag200_g200wb_hold_bmc(struct mga_device *mdev)
{
u8 tmp;
int iter_max;
/* 1- The first step is to warn 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 and further scan request
* This will be 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 verifu if there is an active scan
* We are searching 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--;
}
}
}
static void mgag200_g200wb_release_bmc(struct mga_device *mdev)
{
u8 tmp;
/* 1- The first step is to ensure that the vrsten and hrsten are set */
WREG8(MGAREG_CRTCEXT_INDEX, 1);
tmp = RREG8(MGAREG_CRTCEXT_DATA);
WREG8(MGAREG_CRTCEXT_DATA, tmp | 0x88);
/* 2- second step is to assert the rstlvl2 */
WREG8(DAC_INDEX, MGA1064_REMHEADCTL2);
tmp = RREG8(DAC_DATA);
tmp |= 0x8;
WREG8(DAC_DATA, tmp);
/* wait 10 us */
udelay(10);
/* 3- deassert rstlvl2 */
tmp &= ~0x08;
WREG8(DAC_INDEX, MGA1064_REMHEADCTL2);
WREG8(DAC_DATA, tmp);
/* 4- remove mask of scan request */
WREG8(DAC_INDEX, MGA1064_SPAREREG);
tmp = RREG8(DAC_DATA);
tmp &= ~0x80;
WREG8(DAC_DATA, tmp);
/* 5- 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);
}
/*
* This is how the framebuffer base address is stored in g200 cards:
* * Assume @offset is the gpu_addr variable of the framebuffer object
@ -802,14 +713,15 @@ static void mgag200_crtc_helper_atomic_enable(struct drm_crtc *crtc,
{
struct drm_device *dev = crtc->dev;
struct mga_device *mdev = to_mga_device(dev);
const struct mgag200_device_funcs *funcs = mdev->funcs;
struct drm_crtc_state *crtc_state = crtc->state;
struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
const struct drm_format_info *format = mgag200_crtc_state->format;
struct mgag200_pll *pixpll = &mdev->pixpll;
if (mdev->type == G200_WB || mdev->type == G200_EW3)
mgag200_g200wb_hold_bmc(mdev);
if (funcs->disable_vidrst)
funcs->disable_vidrst(mdev);
mgag200_set_format_regs(mdev, format);
mgag200_set_mode_regs(mdev, adjusted_mode);
@ -826,22 +738,23 @@ static void mgag200_crtc_helper_atomic_enable(struct drm_crtc *crtc,
mgag200_enable_display(mdev);
if (mdev->type == G200_WB || mdev->type == G200_EW3)
mgag200_g200wb_release_bmc(mdev);
if (funcs->enable_vidrst)
funcs->enable_vidrst(mdev);
}
static void mgag200_crtc_helper_atomic_disable(struct drm_crtc *crtc,
struct drm_atomic_state *old_state)
{
struct mga_device *mdev = to_mga_device(crtc->dev);
const struct mgag200_device_funcs *funcs = mdev->funcs;
if (mdev->type == G200_WB || mdev->type == G200_EW3)
mgag200_g200wb_hold_bmc(mdev);
if (funcs->disable_vidrst)
funcs->disable_vidrst(mdev);
mgag200_disable_display(mdev);
if (mdev->type == G200_WB || mdev->type == G200_EW3)
mgag200_g200wb_release_bmc(mdev);
if (funcs->enable_vidrst)
funcs->enable_vidrst(mdev);
}
static const struct drm_crtc_helper_funcs mgag200_crtc_helper_funcs = {