diff options
Diffstat (limited to 'drivers/gpu/drm/mgag200/mgag200_mode.c')
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_mode.c | 726 |
1 files changed, 219 insertions, 507 deletions
diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index 225cca2ed60e..bbab2549243a 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -11,24 +11,19 @@ #include <linux/delay.h> #include <linux/iosys-map.h> +#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> -#include <drm/drm_atomic_state_helper.h> -#include <drm/drm_crtc_helper.h> #include <drm/drm_damage_helper.h> #include <drm/drm_format_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem_atomic_helper.h> #include <drm/drm_gem_framebuffer_helper.h> -#include <drm/drm_plane_helper.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h> #include "mgag200_drv.h" -#define MGAG200_LUT_SIZE 256 - /* * This file contains setup code for the CRTC. */ @@ -132,95 +127,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 @@ -267,86 +173,10 @@ static void mgag200_set_startadd(struct mga_device *mdev, WREG_ECRT(0x00, crtcext0); } -static void mgag200_set_dac_regs(struct mga_device *mdev) -{ - size_t i; - u8 dacvalue[] = { - /* 0x00: */ 0, 0, 0, 0, 0, 0, 0x00, 0, - /* 0x08: */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x10: */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x18: */ 0x00, 0, 0xC9, 0xFF, 0xBF, 0x20, 0x1F, 0x20, - /* 0x20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x28: */ 0x00, 0x00, 0x00, 0x00, 0, 0, 0, 0x40, - /* 0x30: */ 0x00, 0xB0, 0x00, 0xC2, 0x34, 0x14, 0x02, 0x83, - /* 0x38: */ 0x00, 0x93, 0x00, 0x77, 0x00, 0x00, 0x00, 0x3A, - /* 0x40: */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 0x48: */ 0, 0, 0, 0, 0, 0, 0, 0 - }; - - switch (mdev->type) { - case G200_PCI: - case G200_AGP: - dacvalue[MGA1064_SYS_PLL_M] = 0x04; - dacvalue[MGA1064_SYS_PLL_N] = 0x2D; - dacvalue[MGA1064_SYS_PLL_P] = 0x19; - break; - case G200_SE_A: - case G200_SE_B: - dacvalue[MGA1064_VREF_CTL] = 0x03; - dacvalue[MGA1064_PIX_CLK_CTL] = MGA1064_PIX_CLK_CTL_SEL_PLL; - dacvalue[MGA1064_MISC_CTL] = MGA1064_MISC_CTL_DAC_EN | - MGA1064_MISC_CTL_VGA8 | - MGA1064_MISC_CTL_DAC_RAM_CS; - break; - case G200_WB: - case G200_EW3: - dacvalue[MGA1064_VREF_CTL] = 0x07; - break; - case G200_EV: - dacvalue[MGA1064_PIX_CLK_CTL] = MGA1064_PIX_CLK_CTL_SEL_PLL; - dacvalue[MGA1064_MISC_CTL] = MGA1064_MISC_CTL_VGA8 | - MGA1064_MISC_CTL_DAC_RAM_CS; - break; - case G200_EH: - case G200_EH3: - dacvalue[MGA1064_MISC_CTL] = MGA1064_MISC_CTL_VGA8 | - MGA1064_MISC_CTL_DAC_RAM_CS; - break; - case G200_ER: - break; - } - - for (i = 0; i < ARRAY_SIZE(dacvalue); i++) { - if ((i <= 0x17) || - (i == 0x1b) || - (i == 0x1c) || - ((i >= 0x1f) && (i <= 0x29)) || - ((i >= 0x30) && (i <= 0x37))) - continue; - if (IS_G200_SE(mdev) && - ((i == 0x2c) || (i == 0x2d) || (i == 0x2e))) - continue; - if ((mdev->type == G200_EV || - mdev->type == G200_WB || - mdev->type == G200_EH || - mdev->type == G200_EW3 || - mdev->type == G200_EH3) && - (i >= 0x44) && (i <= 0x4e)) - continue; - - WREG_DAC(i, dacvalue[i]); - } - - if (mdev->type == G200_ER) - WREG_DAC(0x90, 0); -} - -static void mgag200_init_regs(struct mga_device *mdev) +void mgag200_init_registers(struct mga_device *mdev) { u8 crtc11, misc; - mgag200_set_dac_regs(mdev); - WREG_SEQ(2, 0x0f); WREG_SEQ(3, 0x00); WREG_SEQ(4, 0x0e); @@ -364,19 +194,12 @@ static void mgag200_init_regs(struct mga_device *mdev) MGAREG_CRTC11_VINTCLR); WREG_CRT(0x11, crtc11); - if (mdev->type == G200_ER) - WREG_ECRT(0x24, 0x5); - - if (mdev->type == G200_EW3) - WREG_ECRT(0x34, 0x5); - misc = RREG8(MGA_MISC_IN); misc |= MGAREG_MISC_IOADSEL; WREG8(MGA_MISC_OUT, misc); } -static void mgag200_set_mode_regs(struct mga_device *mdev, - const struct drm_display_mode *mode) +void mgag200_set_mode_regs(struct mga_device *mdev, const struct drm_display_mode *mode) { const struct mgag200_device_info *info = mdev->info; unsigned int hdisplay, hsyncstart, hsyncend, htotal; @@ -500,11 +323,9 @@ static void mgag200_set_offset(struct mga_device *mdev, WREG_ECRT(0x00, crtcext0); } -static void mgag200_set_format_regs(struct mga_device *mdev, - const struct drm_framebuffer *fb) +void mgag200_set_format_regs(struct mga_device *mdev, const struct drm_format_info *format) { struct drm_device *dev = &mdev->base; - const struct drm_format_info *format = fb->format; unsigned int bpp, bppshift, scale; u8 crtcext3, xmulctrl; @@ -565,76 +386,9 @@ static void mgag200_set_format_regs(struct mga_device *mdev, WREG_ECRT(3, crtcext3); } -static void mgag200_g200er_reset_tagfifo(struct mga_device *mdev) +void mgag200_enable_display(struct mga_device *mdev) { - static uint32_t RESET_FLAG = 0x00200000; /* undocumented magic value */ - u32 memctl; - - memctl = RREG32(MGAREG_MEMCTL); - - memctl |= RESET_FLAG; - WREG32(MGAREG_MEMCTL, memctl); - - udelay(1000); - - memctl &= ~RESET_FLAG; - WREG32(MGAREG_MEMCTL, memctl); -} - -static void mgag200_g200se_set_hiprilvl(struct mga_device *mdev, - const struct drm_display_mode *mode, - const struct drm_framebuffer *fb) -{ - struct mgag200_g200se_device *g200se = to_mgag200_g200se_device(&mdev->base); - unsigned int hiprilvl; - u8 crtcext6; - - if (g200se->unique_rev_id >= 0x04) { - hiprilvl = 0; - } else if (g200se->unique_rev_id >= 0x02) { - unsigned int bpp; - unsigned long mb; - - if (fb->format->cpp[0] * 8 > 16) - bpp = 32; - else if (fb->format->cpp[0] * 8 > 8) - bpp = 16; - else - bpp = 8; - - mb = (mode->clock * bpp) / 1000; - if (mb > 3100) - hiprilvl = 0; - else if (mb > 2600) - hiprilvl = 1; - else if (mb > 1900) - hiprilvl = 2; - else if (mb > 1160) - hiprilvl = 3; - else if (mb > 440) - hiprilvl = 4; - else - hiprilvl = 5; - - } else if (g200se->unique_rev_id >= 0x01) { - hiprilvl = 3; - } else { - hiprilvl = 4; - } - - crtcext6 = hiprilvl; /* implicitly sets maxhipri to 0 */ - - WREG_ECRT(0x06, crtcext6); -} - -static void mgag200_g200ev_set_hiprilvl(struct mga_device *mdev) -{ - WREG_ECRT(0x06, 0x00); -} - -static void mgag200_enable_display(struct mga_device *mdev) -{ - u8 seq0, seq1, crtcext1; + u8 seq0, crtcext1; RREG_SEQ(0x00, seq0); seq0 |= MGAREG_SEQ0_SYNCRST | @@ -648,12 +402,6 @@ static void mgag200_enable_display(struct mga_device *mdev) mga_wait_vsync(mdev); mga_wait_busy(mdev); - RREG_SEQ(0x01, seq1); - seq1 &= ~MGAREG_SEQ1_SCROFF; - WREG_SEQ(0x01, seq1); - - msleep(20); - RREG_ECRT(0x01, crtcext1); crtcext1 &= ~MGAREG_CRTCEXT1_VSYNCOFF; crtcext1 &= ~MGAREG_CRTCEXT1_HSYNCOFF; @@ -662,7 +410,7 @@ static void mgag200_enable_display(struct mga_device *mdev) static void mgag200_disable_display(struct mga_device *mdev) { - u8 seq0, seq1, crtcext1; + u8 seq0, crtcext1; RREG_SEQ(0x00, seq0); seq0 &= ~MGAREG_SEQ0_SYNCRST; @@ -675,59 +423,127 @@ static void mgag200_disable_display(struct mga_device *mdev) mga_wait_vsync(mdev); mga_wait_busy(mdev); - RREG_SEQ(0x01, seq1); - seq1 |= MGAREG_SEQ1_SCROFF; - WREG_SEQ(0x01, seq1); - - msleep(20); - RREG_ECRT(0x01, crtcext1); crtcext1 |= MGAREG_CRTCEXT1_VSYNCOFF | MGAREG_CRTCEXT1_HSYNCOFF; WREG_ECRT(0x01, crtcext1); } +static void mgag200_handle_damage(struct mga_device *mdev, const struct iosys_map *vmap, + struct drm_framebuffer *fb, struct drm_rect *clip) +{ + struct iosys_map dst = IOSYS_MAP_INIT_VADDR_IOMEM(mdev->vram); + + iosys_map_incr(&dst, drm_fb_clip_offset(fb->pitches[0], fb->format, clip)); + drm_fb_memcpy(&dst, fb->pitches, vmap, fb, clip); +} + /* - * Connector + * Primary plane */ -static int mgag200_vga_connector_helper_get_modes(struct drm_connector *connector) +const uint32_t mgag200_primary_plane_formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGB888, +}; + +const size_t mgag200_primary_plane_formats_size = ARRAY_SIZE(mgag200_primary_plane_formats); + +const uint64_t mgag200_primary_plane_fmtmods[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +int mgag200_primary_plane_helper_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *new_state) { - struct mga_device *mdev = to_mga_device(connector->dev); + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(new_state, plane); + struct drm_framebuffer *new_fb = new_plane_state->fb; + struct drm_framebuffer *fb = NULL; + struct drm_crtc *new_crtc = new_plane_state->crtc; + struct drm_crtc_state *new_crtc_state = NULL; + struct mgag200_crtc_state *new_mgag200_crtc_state; int ret; - /* - * Protect access to I/O registers from concurrent modesetting - * by acquiring the I/O-register lock. - */ - mutex_lock(&mdev->rmmio_lock); - ret = drm_connector_helper_get_modes_from_ddc(connector); - mutex_unlock(&mdev->rmmio_lock); + if (new_crtc) + new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_crtc); - return ret; + ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, true); + if (ret) + return ret; + else if (!new_plane_state->visible) + return 0; + + if (plane->state) + fb = plane->state->fb; + + if (!fb || (fb->format != new_fb->format)) + new_crtc_state->mode_changed = true; /* update PLL settings */ + + new_mgag200_crtc_state = to_mgag200_crtc_state(new_crtc_state); + new_mgag200_crtc_state->format = new_fb->format; + + return 0; } -static const struct drm_connector_helper_funcs mga_vga_connector_helper_funcs = { - .get_modes = mgag200_vga_connector_helper_get_modes, -}; +void mgag200_primary_plane_helper_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *old_state) +{ + struct drm_device *dev = plane->dev; + struct mga_device *mdev = to_mga_device(dev); + struct drm_plane_state *plane_state = plane->state; + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(old_state, plane); + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); + struct drm_framebuffer *fb = plane_state->fb; + struct drm_atomic_helper_damage_iter iter; + struct drm_rect damage; + u8 seq1; -static const struct drm_connector_funcs mga_vga_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, -}; + if (!fb) + return; + + drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); + drm_atomic_for_each_plane_damage(&iter, &damage) { + mgag200_handle_damage(mdev, shadow_plane_state->data, fb, &damage); + } + + /* Always scanout image at VRAM offset 0 */ + mgag200_set_startadd(mdev, (u32)0); + mgag200_set_offset(mdev, fb); + + if (!old_plane_state->crtc && plane_state->crtc) { // enabling + RREG_SEQ(0x01, seq1); + seq1 &= ~MGAREG_SEQ1_SCROFF; + WREG_SEQ(0x01, seq1); + msleep(20); + } +} + +void mgag200_primary_plane_helper_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *old_state) +{ + struct drm_device *dev = plane->dev; + struct mga_device *mdev = to_mga_device(dev); + u8 seq1; + + RREG_SEQ(0x01, seq1); + seq1 |= MGAREG_SEQ1_SCROFF; + WREG_SEQ(0x01, seq1); + msleep(20); +} /* - * Simple Display Pipe + * CRTC */ -static enum drm_mode_status -mgag200_simple_display_pipe_mode_valid(struct drm_simple_display_pipe *pipe, - const struct drm_display_mode *mode) +enum drm_mode_status mgag200_crtc_helper_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) { - struct mga_device *mdev = to_mga_device(pipe->crtc.dev); + struct mga_device *mdev = to_mga_device(crtc->dev); const struct mgag200_device_info *info = mdev->info; /* @@ -754,167 +570,112 @@ mgag200_simple_display_pipe_mode_valid(struct drm_simple_display_pipe *pipe, return MODE_OK; } -static void -mgag200_handle_damage(struct mga_device *mdev, struct drm_framebuffer *fb, - struct drm_rect *clip, const struct iosys_map *map) -{ - void __iomem *dst = mdev->vram; - void *vmap = map->vaddr; /* TODO: Use mapping abstraction properly */ - - dst += drm_fb_clip_offset(fb->pitches[0], fb->format, clip); - drm_fb_memcpy_toio(dst, fb->pitches[0], vmap, fb, clip); -} - -static void -mgag200_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe, - struct drm_crtc_state *crtc_state, - struct drm_plane_state *plane_state) +int mgag200_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state) { - struct drm_crtc *crtc = &pipe->crtc; struct drm_device *dev = crtc->dev; struct mga_device *mdev = to_mga_device(dev); - struct mgag200_pll *pixpll = &mdev->pixpll; - struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; - struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state); - struct drm_framebuffer *fb = plane_state->fb; - struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); - struct drm_rect fullscreen = { - .x1 = 0, - .x2 = fb->width, - .y1 = 0, - .y2 = fb->height, - }; - - /* - * Concurrent operations could possibly trigger a call to - * drm_connector_helper_funcs.get_modes by trying to read the - * display modes. Protect access to I/O registers by acquiring - * the I/O-register lock. - */ - mutex_lock(&mdev->rmmio_lock); - - if (mdev->type == G200_WB || mdev->type == G200_EW3) - mgag200_g200wb_hold_bmc(mdev); - - mgag200_set_format_regs(mdev, fb); - mgag200_set_mode_regs(mdev, adjusted_mode); - - pixpll->funcs->update(pixpll, &mgag200_crtc_state->pixpllc); - - if (mdev->type == G200_ER) - mgag200_g200er_reset_tagfifo(mdev); - - if (IS_G200_SE(mdev)) - mgag200_g200se_set_hiprilvl(mdev, adjusted_mode, fb); - else if (mdev->type == G200_EV) - mgag200_g200ev_set_hiprilvl(mdev); - - if (mdev->type == G200_WB || mdev->type == G200_EW3) - mgag200_g200wb_release_bmc(mdev); + const struct mgag200_device_funcs *funcs = mdev->funcs; + struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc); + struct drm_property_blob *new_gamma_lut = new_crtc_state->gamma_lut; + int ret; - if (crtc_state->gamma_lut) - mgag200_crtc_set_gamma(mdev, fb->format, crtc_state->gamma_lut->data); - else - mgag200_crtc_set_gamma_linear(mdev, fb->format); + ret = drm_atomic_helper_check_crtc_state(new_crtc_state, false); + if (ret) + return ret; - mgag200_enable_display(mdev); + if (!new_crtc_state->enable) + return 0; - mgag200_handle_damage(mdev, fb, &fullscreen, &shadow_plane_state->data[0]); + if (new_crtc_state->mode_changed) { + if (funcs->pixpllc_atomic_check) { + ret = funcs->pixpllc_atomic_check(crtc, new_state); + if (ret) + return ret; + } + } - /* Always scanout image at VRAM offset 0 */ - mgag200_set_startadd(mdev, (u32)0); - mgag200_set_offset(mdev, fb); + if (new_crtc_state->color_mgmt_changed && new_gamma_lut) { + if (new_gamma_lut->length != MGAG200_LUT_SIZE * sizeof(struct drm_color_lut)) { + drm_dbg(dev, "Wrong size for gamma_lut %zu\n", new_gamma_lut->length); + return -EINVAL; + } + } - mutex_unlock(&mdev->rmmio_lock); + return drm_atomic_add_affected_planes(new_state, crtc); } -static void -mgag200_simple_display_pipe_disable(struct drm_simple_display_pipe *pipe) +void mgag200_crtc_helper_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_state *old_state) { - struct drm_crtc *crtc = &pipe->crtc; - struct mga_device *mdev = to_mga_device(crtc->dev); + struct drm_crtc_state *crtc_state = crtc->state; + struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state); + struct drm_device *dev = crtc->dev; + struct mga_device *mdev = to_mga_device(dev); - mgag200_disable_display(mdev); + if (crtc_state->enable && crtc_state->color_mgmt_changed) { + const struct drm_format_info *format = mgag200_crtc_state->format; + + if (crtc_state->gamma_lut) + mgag200_crtc_set_gamma(mdev, format, crtc_state->gamma_lut->data); + else + mgag200_crtc_set_gamma_linear(mdev, format); + } } -static int -mgag200_simple_display_pipe_check(struct drm_simple_display_pipe *pipe, - struct drm_plane_state *plane_state, - struct drm_crtc_state *crtc_state) +void mgag200_crtc_helper_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *old_state) { - struct drm_plane *plane = plane_state->plane; - struct drm_device *dev = plane->dev; + struct drm_device *dev = crtc->dev; struct mga_device *mdev = to_mga_device(dev); - struct mgag200_pll *pixpll = &mdev->pixpll; + 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); - struct drm_framebuffer *new_fb = plane_state->fb; - struct drm_framebuffer *fb = NULL; - int ret; + const struct drm_format_info *format = mgag200_crtc_state->format; - if (!new_fb) - return 0; + if (funcs->disable_vidrst) + funcs->disable_vidrst(mdev); - if (plane->state) - fb = plane->state->fb; + mgag200_set_format_regs(mdev, format); + mgag200_set_mode_regs(mdev, adjusted_mode); - if (!fb || (fb->format != new_fb->format)) - crtc_state->mode_changed = true; /* update PLL settings */ + if (funcs->pixpllc_atomic_update) + funcs->pixpllc_atomic_update(crtc, old_state); - if (crtc_state->mode_changed) { - ret = pixpll->funcs->compute(pixpll, crtc_state->mode.clock, - &mgag200_crtc_state->pixpllc); - if (ret) - return ret; - } + mgag200_enable_display(mdev); - if (crtc_state->color_mgmt_changed && crtc_state->gamma_lut) { - if (crtc_state->gamma_lut->length != - MGAG200_LUT_SIZE * sizeof(struct drm_color_lut)) { - drm_err(dev, "Wrong size for gamma_lut %zu\n", - crtc_state->gamma_lut->length); - return -EINVAL; - } - } - return 0; + if (funcs->enable_vidrst) + funcs->enable_vidrst(mdev); } -static void -mgag200_simple_display_pipe_update(struct drm_simple_display_pipe *pipe, - struct drm_plane_state *old_state) +void mgag200_crtc_helper_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_state *old_state) { - struct drm_plane *plane = &pipe->plane; - struct drm_crtc *crtc = &pipe->crtc; - struct drm_device *dev = plane->dev; - struct mga_device *mdev = to_mga_device(dev); - struct drm_plane_state *state = plane->state; - struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state); - struct drm_framebuffer *fb = state->fb; - struct drm_rect damage; - struct drm_atomic_helper_damage_iter iter; + struct mga_device *mdev = to_mga_device(crtc->dev); + const struct mgag200_device_funcs *funcs = mdev->funcs; - if (!fb) - return; + if (funcs->disable_vidrst) + funcs->disable_vidrst(mdev); - mutex_lock(&mdev->rmmio_lock); + mgag200_disable_display(mdev); - if (crtc->state->color_mgmt_changed && crtc->state->gamma_lut) - mgag200_crtc_set_gamma(mdev, fb->format, crtc->state->gamma_lut->data); + if (funcs->enable_vidrst) + funcs->enable_vidrst(mdev); +} - drm_atomic_helper_damage_iter_init(&iter, old_state, state); - drm_atomic_for_each_plane_damage(&iter, &damage) { - mgag200_handle_damage(mdev, fb, &damage, &shadow_plane_state->data[0]); - } - /* Always scanout image at VRAM offset 0 */ - mgag200_set_startadd(mdev, (u32)0); - mgag200_set_offset(mdev, fb); +void mgag200_crtc_reset(struct drm_crtc *crtc) +{ + struct mgag200_crtc_state *mgag200_crtc_state; - mutex_unlock(&mdev->rmmio_lock); + if (crtc->state) + crtc->funcs->atomic_destroy_state(crtc, crtc->state); + + mgag200_crtc_state = kzalloc(sizeof(*mgag200_crtc_state), GFP_KERNEL); + if (mgag200_crtc_state) + __drm_atomic_helper_crtc_reset(crtc, &mgag200_crtc_state->base); + else + __drm_atomic_helper_crtc_reset(crtc, NULL); } -static struct drm_crtc_state * -mgag200_simple_display_pipe_duplicate_crtc_state(struct drm_simple_display_pipe *pipe) +struct drm_crtc_state *mgag200_crtc_atomic_duplicate_state(struct drm_crtc *crtc) { - struct drm_crtc *crtc = &pipe->crtc; struct drm_crtc_state *crtc_state = crtc->state; struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state); struct mgag200_crtc_state *new_mgag200_crtc_state; @@ -927,14 +688,14 @@ mgag200_simple_display_pipe_duplicate_crtc_state(struct drm_simple_display_pipe return NULL; __drm_atomic_helper_crtc_duplicate_state(crtc, &new_mgag200_crtc_state->base); + new_mgag200_crtc_state->format = mgag200_crtc_state->format; memcpy(&new_mgag200_crtc_state->pixpllc, &mgag200_crtc_state->pixpllc, sizeof(new_mgag200_crtc_state->pixpllc)); return &new_mgag200_crtc_state->base; } -static void mgag200_simple_display_pipe_destroy_crtc_state(struct drm_simple_display_pipe *pipe, - struct drm_crtc_state *crtc_state) +void mgag200_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state) { struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state); @@ -942,50 +703,49 @@ static void mgag200_simple_display_pipe_destroy_crtc_state(struct drm_simple_dis kfree(mgag200_crtc_state); } -static void mgag200_simple_display_pipe_reset_crtc(struct drm_simple_display_pipe *pipe) +/* + * Connector + */ + +int mgag200_vga_connector_helper_get_modes(struct drm_connector *connector) { - struct drm_crtc *crtc = &pipe->crtc; - struct mgag200_crtc_state *mgag200_crtc_state; + struct mga_device *mdev = to_mga_device(connector->dev); + int ret; - if (crtc->state) { - mgag200_simple_display_pipe_destroy_crtc_state(pipe, crtc->state); - crtc->state = NULL; /* must be set to NULL here */ - } + /* + * Protect access to I/O registers from concurrent modesetting + * by acquiring the I/O-register lock. + */ + mutex_lock(&mdev->rmmio_lock); + ret = drm_connector_helper_get_modes_from_ddc(connector); + mutex_unlock(&mdev->rmmio_lock); - mgag200_crtc_state = kzalloc(sizeof(*mgag200_crtc_state), GFP_KERNEL); - if (!mgag200_crtc_state) - return; - __drm_atomic_helper_crtc_reset(crtc, &mgag200_crtc_state->base); + return ret; } -static const struct drm_simple_display_pipe_funcs -mgag200_simple_display_pipe_funcs = { - .mode_valid = mgag200_simple_display_pipe_mode_valid, - .enable = mgag200_simple_display_pipe_enable, - .disable = mgag200_simple_display_pipe_disable, - .check = mgag200_simple_display_pipe_check, - .update = mgag200_simple_display_pipe_update, - .reset_crtc = mgag200_simple_display_pipe_reset_crtc, - .duplicate_crtc_state = mgag200_simple_display_pipe_duplicate_crtc_state, - .destroy_crtc_state = mgag200_simple_display_pipe_destroy_crtc_state, - DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS, -}; - -static const uint32_t mgag200_simple_display_pipe_formats[] = { - DRM_FORMAT_XRGB8888, - DRM_FORMAT_RGB565, - DRM_FORMAT_RGB888, -}; - -static const uint64_t mgag200_simple_display_pipe_fmtmods[] = { - DRM_FORMAT_MOD_LINEAR, - DRM_FORMAT_MOD_INVALID -}; - /* * Mode config */ +static void mgag200_mode_config_helper_atomic_commit_tail(struct drm_atomic_state *state) +{ + struct mga_device *mdev = to_mga_device(state->dev); + + /* + * Concurrent operations could possibly trigger a call to + * drm_connector_helper_funcs.get_modes by trying to read the + * display modes. Protect access to I/O registers by acquiring + * the I/O-register lock. + */ + mutex_lock(&mdev->rmmio_lock); + drm_atomic_helper_commit_tail(state); + mutex_unlock(&mdev->rmmio_lock); +} + +static const struct drm_mode_config_helper_funcs mgag200_mode_config_helper_funcs = { + .atomic_commit_tail = mgag200_mode_config_helper_atomic_commit_tail, +}; + /* Calculates a mode's required memory bandwidth (in KiB/sec). */ static uint32_t mgag200_calculate_mode_bandwidth(const struct drm_display_mode *mode, unsigned int bits_per_pixel) @@ -1048,23 +808,16 @@ static const struct drm_mode_config_funcs mgag200_mode_config_funcs = { .atomic_commit = drm_atomic_helper_commit, }; -int mgag200_modeset_init(struct mga_device *mdev, resource_size_t vram_available) +int mgag200_mode_config_init(struct mga_device *mdev, resource_size_t vram_available) { struct drm_device *dev = &mdev->base; - struct mga_i2c_chan *i2c = &mdev->i2c; - struct drm_connector *connector = &mdev->connector; - struct drm_simple_display_pipe *pipe = &mdev->display_pipe; - size_t format_count = ARRAY_SIZE(mgag200_simple_display_pipe_formats); int ret; - mgag200_init_regs(mdev); - mdev->vram_available = vram_available; ret = drmm_mode_config_init(dev); if (ret) { - drm_err(dev, "drmm_mode_config_init() failed, error %d\n", - ret); + drm_err(dev, "drmm_mode_config_init() failed: %d\n", ret); return ret; } @@ -1073,48 +826,7 @@ int mgag200_modeset_init(struct mga_device *mdev, resource_size_t vram_available dev->mode_config.preferred_depth = 24; dev->mode_config.fb_base = mdev->vram_res->start; dev->mode_config.funcs = &mgag200_mode_config_funcs; - - ret = mgag200_i2c_init(mdev, i2c); - if (ret) { - drm_err(dev, "failed to add DDC bus: %d\n", ret); - return ret; - } - - ret = drm_connector_init_with_ddc(dev, connector, - &mga_vga_connector_funcs, - DRM_MODE_CONNECTOR_VGA, - &i2c->adapter); - if (ret) { - drm_err(dev, "drm_connector_init_with_ddc() failed: %d\n", ret); - return ret; - } - drm_connector_helper_add(connector, &mga_vga_connector_helper_funcs); - - ret = mgag200_pixpll_init(&mdev->pixpll, mdev); - if (ret) - return ret; - - ret = drm_simple_display_pipe_init(dev, pipe, - &mgag200_simple_display_pipe_funcs, - mgag200_simple_display_pipe_formats, - format_count, - mgag200_simple_display_pipe_fmtmods, - connector); - if (ret) { - drm_err(dev, - "drm_simple_display_pipe_init() failed, error %d\n", - ret); - return ret; - } - - drm_plane_enable_fb_damage_clips(&pipe->plane); - - /* FIXME: legacy gamma tables, but atomic gamma doesn't work without */ - drm_mode_crtc_set_gamma_size(&pipe->crtc, MGAG200_LUT_SIZE); - - drm_crtc_enable_color_mgmt(&pipe->crtc, 0, false, MGAG200_LUT_SIZE); - - drm_mode_config_reset(dev); + dev->mode_config.helper_private = &mgag200_mode_config_helper_funcs; return 0; } |