Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v4.20 207 lines 5.9 kB view raw
1/* 2 * Copyright (C) 2016 Noralf Trønnes 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 */ 9 10#include <drm/drm_atomic_helper.h> 11#include <drm/drm_crtc_helper.h> 12#include <drm/drm_gem_framebuffer_helper.h> 13#include <drm/drm_modes.h> 14#include <drm/tinydrm/tinydrm.h> 15 16struct tinydrm_connector { 17 struct drm_connector base; 18 struct drm_display_mode mode; 19}; 20 21static inline struct tinydrm_connector * 22to_tinydrm_connector(struct drm_connector *connector) 23{ 24 return container_of(connector, struct tinydrm_connector, base); 25} 26 27static int tinydrm_connector_get_modes(struct drm_connector *connector) 28{ 29 struct tinydrm_connector *tconn = to_tinydrm_connector(connector); 30 struct drm_display_mode *mode; 31 32 mode = drm_mode_duplicate(connector->dev, &tconn->mode); 33 if (!mode) { 34 DRM_ERROR("Failed to duplicate mode\n"); 35 return 0; 36 } 37 38 if (mode->name[0] == '\0') 39 drm_mode_set_name(mode); 40 41 mode->type |= DRM_MODE_TYPE_PREFERRED; 42 drm_mode_probed_add(connector, mode); 43 44 if (mode->width_mm) { 45 connector->display_info.width_mm = mode->width_mm; 46 connector->display_info.height_mm = mode->height_mm; 47 } 48 49 return 1; 50} 51 52static const struct drm_connector_helper_funcs tinydrm_connector_hfuncs = { 53 .get_modes = tinydrm_connector_get_modes, 54}; 55 56static enum drm_connector_status 57tinydrm_connector_detect(struct drm_connector *connector, bool force) 58{ 59 if (drm_dev_is_unplugged(connector->dev)) 60 return connector_status_disconnected; 61 62 return connector->status; 63} 64 65static void tinydrm_connector_destroy(struct drm_connector *connector) 66{ 67 struct tinydrm_connector *tconn = to_tinydrm_connector(connector); 68 69 drm_connector_cleanup(connector); 70 kfree(tconn); 71} 72 73static const struct drm_connector_funcs tinydrm_connector_funcs = { 74 .reset = drm_atomic_helper_connector_reset, 75 .detect = tinydrm_connector_detect, 76 .fill_modes = drm_helper_probe_single_connector_modes, 77 .destroy = tinydrm_connector_destroy, 78 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 79 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 80}; 81 82struct drm_connector * 83tinydrm_connector_create(struct drm_device *drm, 84 const struct drm_display_mode *mode, 85 int connector_type) 86{ 87 struct tinydrm_connector *tconn; 88 struct drm_connector *connector; 89 int ret; 90 91 tconn = kzalloc(sizeof(*tconn), GFP_KERNEL); 92 if (!tconn) 93 return ERR_PTR(-ENOMEM); 94 95 drm_mode_copy(&tconn->mode, mode); 96 connector = &tconn->base; 97 98 drm_connector_helper_add(connector, &tinydrm_connector_hfuncs); 99 ret = drm_connector_init(drm, connector, &tinydrm_connector_funcs, 100 connector_type); 101 if (ret) { 102 kfree(tconn); 103 return ERR_PTR(ret); 104 } 105 106 connector->status = connector_status_connected; 107 108 return connector; 109} 110 111/** 112 * tinydrm_display_pipe_update - Display pipe update helper 113 * @pipe: Simple display pipe 114 * @old_state: Old plane state 115 * 116 * This function does a full framebuffer flush if the plane framebuffer 117 * has changed. It also handles vblank events. Drivers can use this as their 118 * &drm_simple_display_pipe_funcs->update callback. 119 */ 120void tinydrm_display_pipe_update(struct drm_simple_display_pipe *pipe, 121 struct drm_plane_state *old_state) 122{ 123 struct tinydrm_device *tdev = pipe_to_tinydrm(pipe); 124 struct drm_framebuffer *fb = pipe->plane.state->fb; 125 struct drm_crtc *crtc = &tdev->pipe.crtc; 126 127 if (fb && (fb != old_state->fb)) { 128 if (tdev->fb_dirty) 129 tdev->fb_dirty(fb, NULL, 0, 0, NULL, 0); 130 } 131 132 if (crtc->state->event) { 133 spin_lock_irq(&crtc->dev->event_lock); 134 drm_crtc_send_vblank_event(crtc, crtc->state->event); 135 spin_unlock_irq(&crtc->dev->event_lock); 136 crtc->state->event = NULL; 137 } 138} 139EXPORT_SYMBOL(tinydrm_display_pipe_update); 140 141static int tinydrm_rotate_mode(struct drm_display_mode *mode, 142 unsigned int rotation) 143{ 144 if (rotation == 0 || rotation == 180) { 145 return 0; 146 } else if (rotation == 90 || rotation == 270) { 147 swap(mode->hdisplay, mode->vdisplay); 148 swap(mode->hsync_start, mode->vsync_start); 149 swap(mode->hsync_end, mode->vsync_end); 150 swap(mode->htotal, mode->vtotal); 151 swap(mode->width_mm, mode->height_mm); 152 return 0; 153 } else { 154 return -EINVAL; 155 } 156} 157 158/** 159 * tinydrm_display_pipe_init - Initialize display pipe 160 * @tdev: tinydrm device 161 * @funcs: Display pipe functions 162 * @connector_type: Connector type 163 * @formats: Array of supported formats (DRM_FORMAT\_\*) 164 * @format_count: Number of elements in @formats 165 * @mode: Supported mode 166 * @rotation: Initial @mode rotation in degrees Counter Clock Wise 167 * 168 * This function sets up a &drm_simple_display_pipe with a &drm_connector that 169 * has one fixed &drm_display_mode which is rotated according to @rotation. 170 * 171 * Returns: 172 * Zero on success, negative error code on failure. 173 */ 174int 175tinydrm_display_pipe_init(struct tinydrm_device *tdev, 176 const struct drm_simple_display_pipe_funcs *funcs, 177 int connector_type, 178 const uint32_t *formats, 179 unsigned int format_count, 180 const struct drm_display_mode *mode, 181 unsigned int rotation) 182{ 183 struct drm_device *drm = tdev->drm; 184 struct drm_display_mode mode_copy; 185 struct drm_connector *connector; 186 int ret; 187 188 drm_mode_copy(&mode_copy, mode); 189 ret = tinydrm_rotate_mode(&mode_copy, rotation); 190 if (ret) { 191 DRM_ERROR("Illegal rotation value %u\n", rotation); 192 return -EINVAL; 193 } 194 195 drm->mode_config.min_width = mode_copy.hdisplay; 196 drm->mode_config.max_width = mode_copy.hdisplay; 197 drm->mode_config.min_height = mode_copy.vdisplay; 198 drm->mode_config.max_height = mode_copy.vdisplay; 199 200 connector = tinydrm_connector_create(drm, &mode_copy, connector_type); 201 if (IS_ERR(connector)) 202 return PTR_ERR(connector); 203 204 return drm_simple_display_pipe_init(drm, &tdev->pipe, funcs, formats, 205 format_count, NULL, connector); 206} 207EXPORT_SYMBOL(tinydrm_display_pipe_init);