Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v4.13-rc3 234 lines 6.8 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_modes.h> 13#include <drm/tinydrm/tinydrm.h> 14 15struct tinydrm_connector { 16 struct drm_connector base; 17 const struct drm_display_mode *mode; 18}; 19 20static inline struct tinydrm_connector * 21to_tinydrm_connector(struct drm_connector *connector) 22{ 23 return container_of(connector, struct tinydrm_connector, base); 24} 25 26static int tinydrm_connector_get_modes(struct drm_connector *connector) 27{ 28 struct tinydrm_connector *tconn = to_tinydrm_connector(connector); 29 struct drm_display_mode *mode; 30 31 mode = drm_mode_duplicate(connector->dev, tconn->mode); 32 if (!mode) { 33 DRM_ERROR("Failed to duplicate mode\n"); 34 return 0; 35 } 36 37 if (mode->name[0] == '\0') 38 drm_mode_set_name(mode); 39 40 mode->type |= DRM_MODE_TYPE_PREFERRED; 41 drm_mode_probed_add(connector, mode); 42 43 if (mode->width_mm) { 44 connector->display_info.width_mm = mode->width_mm; 45 connector->display_info.height_mm = mode->height_mm; 46 } 47 48 return 1; 49} 50 51static const struct drm_connector_helper_funcs tinydrm_connector_hfuncs = { 52 .get_modes = tinydrm_connector_get_modes, 53 .best_encoder = drm_atomic_helper_best_encoder, 54}; 55 56static enum drm_connector_status 57tinydrm_connector_detect(struct drm_connector *connector, bool force) 58{ 59 if (drm_device_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 .dpms = drm_atomic_helper_connector_dpms, 75 .reset = drm_atomic_helper_connector_reset, 76 .detect = tinydrm_connector_detect, 77 .fill_modes = drm_helper_probe_single_connector_modes, 78 .destroy = tinydrm_connector_destroy, 79 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 80 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 81}; 82 83struct drm_connector * 84tinydrm_connector_create(struct drm_device *drm, 85 const struct drm_display_mode *mode, 86 int connector_type) 87{ 88 struct tinydrm_connector *tconn; 89 struct drm_connector *connector; 90 int ret; 91 92 tconn = kzalloc(sizeof(*tconn), GFP_KERNEL); 93 if (!tconn) 94 return ERR_PTR(-ENOMEM); 95 96 tconn->mode = mode; 97 connector = &tconn->base; 98 99 drm_connector_helper_add(connector, &tinydrm_connector_hfuncs); 100 ret = drm_connector_init(drm, connector, &tinydrm_connector_funcs, 101 connector_type); 102 if (ret) { 103 kfree(tconn); 104 return ERR_PTR(ret); 105 } 106 107 connector->status = connector_status_connected; 108 109 return connector; 110} 111 112/** 113 * tinydrm_display_pipe_update - Display pipe update helper 114 * @pipe: Simple display pipe 115 * @old_state: Old plane state 116 * 117 * This function does a full framebuffer flush if the plane framebuffer 118 * has changed. It also handles vblank events. Drivers can use this as their 119 * &drm_simple_display_pipe_funcs->update callback. 120 */ 121void tinydrm_display_pipe_update(struct drm_simple_display_pipe *pipe, 122 struct drm_plane_state *old_state) 123{ 124 struct tinydrm_device *tdev = pipe_to_tinydrm(pipe); 125 struct drm_framebuffer *fb = pipe->plane.state->fb; 126 struct drm_crtc *crtc = &tdev->pipe.crtc; 127 128 if (fb && (fb != old_state->fb)) { 129 pipe->plane.fb = fb; 130 if (fb->funcs->dirty) 131 fb->funcs->dirty(fb, NULL, 0, 0, NULL, 0); 132 } 133 134 if (crtc->state->event) { 135 spin_lock_irq(&crtc->dev->event_lock); 136 drm_crtc_send_vblank_event(crtc, crtc->state->event); 137 spin_unlock_irq(&crtc->dev->event_lock); 138 crtc->state->event = NULL; 139 } 140} 141EXPORT_SYMBOL(tinydrm_display_pipe_update); 142 143/** 144 * tinydrm_display_pipe_prepare_fb - Display pipe prepare_fb helper 145 * @pipe: Simple display pipe 146 * @plane_state: Plane state 147 * 148 * This function uses drm_fb_cma_prepare_fb() to check if the plane FB has an 149 * dma-buf attached, extracts the exclusive fence and attaches it to plane 150 * state for the atomic helper to wait on. Drivers can use this as their 151 * &drm_simple_display_pipe_funcs->prepare_fb callback. 152 */ 153int tinydrm_display_pipe_prepare_fb(struct drm_simple_display_pipe *pipe, 154 struct drm_plane_state *plane_state) 155{ 156 return drm_fb_cma_prepare_fb(&pipe->plane, plane_state); 157} 158EXPORT_SYMBOL(tinydrm_display_pipe_prepare_fb); 159 160static int tinydrm_rotate_mode(struct drm_display_mode *mode, 161 unsigned int rotation) 162{ 163 if (rotation == 0 || rotation == 180) { 164 return 0; 165 } else if (rotation == 90 || rotation == 270) { 166 swap(mode->hdisplay, mode->vdisplay); 167 swap(mode->hsync_start, mode->vsync_start); 168 swap(mode->hsync_end, mode->vsync_end); 169 swap(mode->htotal, mode->vtotal); 170 swap(mode->width_mm, mode->height_mm); 171 return 0; 172 } else { 173 return -EINVAL; 174 } 175} 176 177/** 178 * tinydrm_display_pipe_init - Initialize display pipe 179 * @tdev: tinydrm device 180 * @funcs: Display pipe functions 181 * @connector_type: Connector type 182 * @formats: Array of supported formats (DRM_FORMAT\_\*) 183 * @format_count: Number of elements in @formats 184 * @mode: Supported mode 185 * @rotation: Initial @mode rotation in degrees Counter Clock Wise 186 * 187 * This function sets up a &drm_simple_display_pipe with a &drm_connector that 188 * has one fixed &drm_display_mode which is rotated according to @rotation. 189 * 190 * Returns: 191 * Zero on success, negative error code on failure. 192 */ 193int 194tinydrm_display_pipe_init(struct tinydrm_device *tdev, 195 const struct drm_simple_display_pipe_funcs *funcs, 196 int connector_type, 197 const uint32_t *formats, 198 unsigned int format_count, 199 const struct drm_display_mode *mode, 200 unsigned int rotation) 201{ 202 struct drm_device *drm = tdev->drm; 203 struct drm_display_mode *mode_copy; 204 struct drm_connector *connector; 205 int ret; 206 207 mode_copy = devm_kmalloc(drm->dev, sizeof(*mode_copy), GFP_KERNEL); 208 if (!mode_copy) 209 return -ENOMEM; 210 211 *mode_copy = *mode; 212 ret = tinydrm_rotate_mode(mode_copy, rotation); 213 if (ret) { 214 DRM_ERROR("Illegal rotation value %u\n", rotation); 215 return -EINVAL; 216 } 217 218 drm->mode_config.min_width = mode_copy->hdisplay; 219 drm->mode_config.max_width = mode_copy->hdisplay; 220 drm->mode_config.min_height = mode_copy->vdisplay; 221 drm->mode_config.max_height = mode_copy->vdisplay; 222 223 connector = tinydrm_connector_create(drm, mode_copy, connector_type); 224 if (IS_ERR(connector)) 225 return PTR_ERR(connector); 226 227 ret = drm_simple_display_pipe_init(drm, &tdev->pipe, funcs, formats, 228 format_count, connector); 229 if (ret) 230 return ret; 231 232 return 0; 233} 234EXPORT_SYMBOL(tinydrm_display_pipe_init);