diff options
author | Gerd Hoffmann <kraxel@redhat.com> | 2013-12-17 21:04:46 +0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2013-12-23 05:02:39 +0400 |
commit | 0a6659bdc5e8221da99eebb176fd9591435e38de (patch) | |
tree | a2f60a9d5c160721ac7405e5ed6b99c2a7073c2d /drivers/gpu/drm/bochs/bochs_kms.c | |
parent | 859ae233cd0ee76b6143f948ba1cb6b0b4c342f8 (diff) | |
download | linux-0a6659bdc5e8221da99eebb176fd9591435e38de.tar.xz |
drm/bochs: new driver
DRM driver for (virtual) vga cards using the bochs dispi
interface, such as the qemu standard vga (qemu -vga std).
Don't bother supporting anything but 32bpp for now, even
though the virtual hardware is able to do that.
Known issue: mmap(/dev/fb0) doesn't work.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/bochs/bochs_kms.c')
-rw-r--r-- | drivers/gpu/drm/bochs/bochs_kms.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/drivers/gpu/drm/bochs/bochs_kms.c b/drivers/gpu/drm/bochs/bochs_kms.c new file mode 100644 index 000000000000..62ec7d4b3816 --- /dev/null +++ b/drivers/gpu/drm/bochs/bochs_kms.c @@ -0,0 +1,294 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "bochs.h" + +static int defx = 1024; +static int defy = 768; + +module_param(defx, int, 0444); +module_param(defy, int, 0444); +MODULE_PARM_DESC(defx, "default x resolution"); +MODULE_PARM_DESC(defy, "default y resolution"); + +/* ---------------------------------------------------------------------- */ + +static void bochs_crtc_load_lut(struct drm_crtc *crtc) +{ +} + +static void bochs_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + switch (mode) { + case DRM_MODE_DPMS_ON: + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + default: + return; + } +} + +static bool bochs_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static int bochs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + struct bochs_device *bochs = + container_of(crtc, struct bochs_device, crtc); + struct bochs_framebuffer *bochs_fb; + struct bochs_bo *bo; + u64 gpu_addr = 0; + int ret; + + if (old_fb) { + bochs_fb = to_bochs_framebuffer(old_fb); + bo = gem_to_bochs_bo(bochs_fb->obj); + ret = ttm_bo_reserve(&bo->bo, true, false, false, 0); + if (ret) { + DRM_ERROR("failed to reserve old_fb bo\n"); + } else { + bochs_bo_unpin(bo); + ttm_bo_unreserve(&bo->bo); + } + } + + if (WARN_ON(crtc->fb == NULL)) + return -EINVAL; + + bochs_fb = to_bochs_framebuffer(crtc->fb); + bo = gem_to_bochs_bo(bochs_fb->obj); + ret = ttm_bo_reserve(&bo->bo, true, false, false, 0); + if (ret) + return ret; + + ret = bochs_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr); + if (ret) { + ttm_bo_unreserve(&bo->bo); + return ret; + } + + ttm_bo_unreserve(&bo->bo); + bochs_hw_setbase(bochs, x, y, gpu_addr); + return 0; +} + +static int bochs_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, struct drm_framebuffer *old_fb) +{ + struct bochs_device *bochs = + container_of(crtc, struct bochs_device, crtc); + + bochs_hw_setmode(bochs, mode); + bochs_crtc_mode_set_base(crtc, x, y, old_fb); + return 0; +} + +static void bochs_crtc_prepare(struct drm_crtc *crtc) +{ +} + +static void bochs_crtc_commit(struct drm_crtc *crtc) +{ +} + +static void bochs_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, uint32_t start, uint32_t size) +{ +} + +/* These provide the minimum set of functions required to handle a CRTC */ +static const struct drm_crtc_funcs bochs_crtc_funcs = { + .gamma_set = bochs_crtc_gamma_set, + .set_config = drm_crtc_helper_set_config, + .destroy = drm_crtc_cleanup, +}; + +static const struct drm_crtc_helper_funcs bochs_helper_funcs = { + .dpms = bochs_crtc_dpms, + .mode_fixup = bochs_crtc_mode_fixup, + .mode_set = bochs_crtc_mode_set, + .mode_set_base = bochs_crtc_mode_set_base, + .prepare = bochs_crtc_prepare, + .commit = bochs_crtc_commit, + .load_lut = bochs_crtc_load_lut, +}; + +static void bochs_crtc_init(struct drm_device *dev) +{ + struct bochs_device *bochs = dev->dev_private; + struct drm_crtc *crtc = &bochs->crtc; + + drm_crtc_init(dev, crtc, &bochs_crtc_funcs); + drm_mode_crtc_set_gamma_size(crtc, 256); + drm_crtc_helper_add(crtc, &bochs_helper_funcs); +} + +static bool bochs_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void bochs_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ +} + +static void bochs_encoder_dpms(struct drm_encoder *encoder, int state) +{ +} + +static void bochs_encoder_prepare(struct drm_encoder *encoder) +{ +} + +static void bochs_encoder_commit(struct drm_encoder *encoder) +{ +} + +static const struct drm_encoder_helper_funcs bochs_encoder_helper_funcs = { + .dpms = bochs_encoder_dpms, + .mode_fixup = bochs_encoder_mode_fixup, + .mode_set = bochs_encoder_mode_set, + .prepare = bochs_encoder_prepare, + .commit = bochs_encoder_commit, +}; + +static const struct drm_encoder_funcs bochs_encoder_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static void bochs_encoder_init(struct drm_device *dev) +{ + struct bochs_device *bochs = dev->dev_private; + struct drm_encoder *encoder = &bochs->encoder; + + encoder->possible_crtcs = 0x1; + drm_encoder_init(dev, encoder, &bochs_encoder_encoder_funcs, + DRM_MODE_ENCODER_DAC); + drm_encoder_helper_add(encoder, &bochs_encoder_helper_funcs); +} + + +int bochs_connector_get_modes(struct drm_connector *connector) +{ + int count; + + count = drm_add_modes_noedid(connector, 8192, 8192); + drm_set_preferred_mode(connector, defx, defy); + return count; +} + +static int bochs_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct bochs_device *bochs = + container_of(connector, struct bochs_device, connector); + unsigned long size = mode->hdisplay * mode->vdisplay * 4; + + /* + * Make sure we can fit two framebuffers into video memory. + * This allows up to 1600x1200 with 16 MB (default size). + * If you want more try this: + * 'qemu -vga std -global VGA.vgamem_mb=32 $otherargs' + */ + if (size * 2 > bochs->fb_size) + return MODE_BAD; + + return MODE_OK; +} + +static struct drm_encoder * +bochs_connector_best_encoder(struct drm_connector *connector) +{ + int enc_id = connector->encoder_ids[0]; + struct drm_mode_object *obj; + struct drm_encoder *encoder; + + /* pick the encoder ids */ + if (enc_id) { + obj = drm_mode_object_find(connector->dev, enc_id, + DRM_MODE_OBJECT_ENCODER); + if (!obj) + return NULL; + encoder = obj_to_encoder(obj); + return encoder; + } + return NULL; +} + +static enum drm_connector_status bochs_connector_detect(struct drm_connector + *connector, bool force) +{ + return connector_status_connected; +} + +struct drm_connector_helper_funcs bochs_connector_connector_helper_funcs = { + .get_modes = bochs_connector_get_modes, + .mode_valid = bochs_connector_mode_valid, + .best_encoder = bochs_connector_best_encoder, +}; + +struct drm_connector_funcs bochs_connector_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = bochs_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, +}; + +static void bochs_connector_init(struct drm_device *dev) +{ + struct bochs_device *bochs = dev->dev_private; + struct drm_connector *connector = &bochs->connector; + + drm_connector_init(dev, connector, &bochs_connector_connector_funcs, + DRM_MODE_CONNECTOR_VIRTUAL); + drm_connector_helper_add(connector, + &bochs_connector_connector_helper_funcs); +} + + +int bochs_kms_init(struct bochs_device *bochs) +{ + drm_mode_config_init(bochs->dev); + bochs->mode_config_initialized = true; + + bochs->dev->mode_config.max_width = 8192; + bochs->dev->mode_config.max_height = 8192; + + bochs->dev->mode_config.fb_base = bochs->fb_base; + bochs->dev->mode_config.preferred_depth = 24; + bochs->dev->mode_config.prefer_shadow = 0; + + bochs->dev->mode_config.funcs = (void *)&bochs_mode_funcs; + + bochs_crtc_init(bochs->dev); + bochs_encoder_init(bochs->dev); + bochs_connector_init(bochs->dev); + drm_mode_connector_attach_encoder(&bochs->connector, + &bochs->encoder); + + return 0; +} + +void bochs_kms_fini(struct bochs_device *bochs) +{ + if (bochs->mode_config_initialized) { + drm_mode_config_cleanup(bochs->dev); + bochs->mode_config_initialized = false; + } +} |