linux/drivers/gpu/drm/bochs/bochs_kms.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 */
   4
   5#include <linux/moduleparam.h>
   6
   7#include <drm/drm_atomic_helper.h>
   8#include <drm/drm_gem_framebuffer_helper.h>
   9#include <drm/drm_probe_helper.h>
  10#include <drm/drm_vblank.h>
  11
  12#include "bochs.h"
  13
  14static int defx = 1024;
  15static int defy = 768;
  16
  17module_param(defx, int, 0444);
  18module_param(defy, int, 0444);
  19MODULE_PARM_DESC(defx, "default x resolution");
  20MODULE_PARM_DESC(defy, "default y resolution");
  21
  22/* ---------------------------------------------------------------------- */
  23
  24static const uint32_t bochs_formats[] = {
  25        DRM_FORMAT_XRGB8888,
  26        DRM_FORMAT_BGRX8888,
  27};
  28
  29static void bochs_plane_update(struct bochs_device *bochs,
  30                               struct drm_plane_state *state)
  31{
  32        struct drm_gem_vram_object *gbo;
  33
  34        if (!state->fb || !bochs->stride)
  35                return;
  36
  37        gbo = drm_gem_vram_of_gem(state->fb->obj[0]);
  38        bochs_hw_setbase(bochs,
  39                         state->crtc_x,
  40                         state->crtc_y,
  41                         state->fb->pitches[0],
  42                         state->fb->offsets[0] + gbo->bo.offset);
  43        bochs_hw_setformat(bochs, state->fb->format);
  44}
  45
  46static void bochs_pipe_enable(struct drm_simple_display_pipe *pipe,
  47                              struct drm_crtc_state *crtc_state,
  48                              struct drm_plane_state *plane_state)
  49{
  50        struct bochs_device *bochs = pipe->crtc.dev->dev_private;
  51
  52        bochs_hw_setmode(bochs, &crtc_state->mode);
  53        bochs_plane_update(bochs, plane_state);
  54}
  55
  56static void bochs_pipe_update(struct drm_simple_display_pipe *pipe,
  57                              struct drm_plane_state *old_state)
  58{
  59        struct bochs_device *bochs = pipe->crtc.dev->dev_private;
  60        struct drm_crtc *crtc = &pipe->crtc;
  61
  62        bochs_plane_update(bochs, pipe->plane.state);
  63
  64        if (crtc->state->event) {
  65                spin_lock_irq(&crtc->dev->event_lock);
  66                drm_crtc_send_vblank_event(crtc, crtc->state->event);
  67                crtc->state->event = NULL;
  68                spin_unlock_irq(&crtc->dev->event_lock);
  69        }
  70}
  71
  72static const struct drm_simple_display_pipe_funcs bochs_pipe_funcs = {
  73        .enable     = bochs_pipe_enable,
  74        .update     = bochs_pipe_update,
  75        .prepare_fb = drm_gem_vram_simple_display_pipe_prepare_fb,
  76        .cleanup_fb = drm_gem_vram_simple_display_pipe_cleanup_fb,
  77};
  78
  79static int bochs_connector_get_modes(struct drm_connector *connector)
  80{
  81        struct bochs_device *bochs =
  82                container_of(connector, struct bochs_device, connector);
  83        int count = 0;
  84
  85        if (bochs->edid)
  86                count = drm_add_edid_modes(connector, bochs->edid);
  87
  88        if (!count) {
  89                count = drm_add_modes_noedid(connector, 8192, 8192);
  90                drm_set_preferred_mode(connector, defx, defy);
  91        }
  92        return count;
  93}
  94
  95static enum drm_mode_status bochs_connector_mode_valid(struct drm_connector *connector,
  96                                      struct drm_display_mode *mode)
  97{
  98        struct bochs_device *bochs =
  99                container_of(connector, struct bochs_device, connector);
 100        unsigned long size = mode->hdisplay * mode->vdisplay * 4;
 101
 102        /*
 103         * Make sure we can fit two framebuffers into video memory.
 104         * This allows up to 1600x1200 with 16 MB (default size).
 105         * If you want more try this:
 106         *     'qemu -vga std -global VGA.vgamem_mb=32 $otherargs'
 107         */
 108        if (size * 2 > bochs->fb_size)
 109                return MODE_BAD;
 110
 111        return MODE_OK;
 112}
 113
 114static const struct drm_connector_helper_funcs bochs_connector_connector_helper_funcs = {
 115        .get_modes = bochs_connector_get_modes,
 116        .mode_valid = bochs_connector_mode_valid,
 117};
 118
 119static const struct drm_connector_funcs bochs_connector_connector_funcs = {
 120        .dpms = drm_helper_connector_dpms,
 121        .fill_modes = drm_helper_probe_single_connector_modes,
 122        .destroy = drm_connector_cleanup,
 123        .reset = drm_atomic_helper_connector_reset,
 124        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 125        .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 126};
 127
 128static void bochs_connector_init(struct drm_device *dev)
 129{
 130        struct bochs_device *bochs = dev->dev_private;
 131        struct drm_connector *connector = &bochs->connector;
 132
 133        drm_connector_init(dev, connector, &bochs_connector_connector_funcs,
 134                           DRM_MODE_CONNECTOR_VIRTUAL);
 135        drm_connector_helper_add(connector,
 136                                 &bochs_connector_connector_helper_funcs);
 137        drm_connector_register(connector);
 138
 139        bochs_hw_load_edid(bochs);
 140        if (bochs->edid) {
 141                DRM_INFO("Found EDID data blob.\n");
 142                drm_connector_attach_edid_property(connector);
 143                drm_connector_update_edid_property(connector, bochs->edid);
 144        }
 145}
 146
 147static struct drm_framebuffer *
 148bochs_gem_fb_create(struct drm_device *dev, struct drm_file *file,
 149                    const struct drm_mode_fb_cmd2 *mode_cmd)
 150{
 151        if (mode_cmd->pixel_format != DRM_FORMAT_XRGB8888 &&
 152            mode_cmd->pixel_format != DRM_FORMAT_BGRX8888)
 153                return ERR_PTR(-EINVAL);
 154
 155        return drm_gem_fb_create(dev, file, mode_cmd);
 156}
 157
 158const struct drm_mode_config_funcs bochs_mode_funcs = {
 159        .fb_create = bochs_gem_fb_create,
 160        .atomic_check = drm_atomic_helper_check,
 161        .atomic_commit = drm_atomic_helper_commit,
 162};
 163
 164int bochs_kms_init(struct bochs_device *bochs)
 165{
 166        drm_mode_config_init(bochs->dev);
 167
 168        bochs->dev->mode_config.max_width = 8192;
 169        bochs->dev->mode_config.max_height = 8192;
 170
 171        bochs->dev->mode_config.fb_base = bochs->fb_base;
 172        bochs->dev->mode_config.preferred_depth = 24;
 173        bochs->dev->mode_config.prefer_shadow = 0;
 174        bochs->dev->mode_config.prefer_shadow_fbdev = 1;
 175        bochs->dev->mode_config.quirk_addfb_prefer_host_byte_order = true;
 176
 177        bochs->dev->mode_config.funcs = &bochs_mode_funcs;
 178
 179        bochs_connector_init(bochs->dev);
 180        drm_simple_display_pipe_init(bochs->dev,
 181                                     &bochs->pipe,
 182                                     &bochs_pipe_funcs,
 183                                     bochs_formats,
 184                                     ARRAY_SIZE(bochs_formats),
 185                                     NULL,
 186                                     &bochs->connector);
 187
 188        drm_mode_config_reset(bochs->dev);
 189
 190        return 0;
 191}
 192
 193void bochs_kms_fini(struct bochs_device *bochs)
 194{
 195        drm_atomic_helper_shutdown(bochs->dev);
 196        drm_mode_config_cleanup(bochs->dev);
 197}
 198