at v4.15-rc6 233 lines 6.7 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 const 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 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 pipe->plane.fb = fb; 129 if (fb->funcs->dirty) 130 fb->funcs->dirty(fb, NULL, 0, 0, NULL, 0); 131 } 132 133 if (crtc->state->event) { 134 spin_lock_irq(&crtc->dev->event_lock); 135 drm_crtc_send_vblank_event(crtc, crtc->state->event); 136 spin_unlock_irq(&crtc->dev->event_lock); 137 crtc->state->event = NULL; 138 } 139} 140EXPORT_SYMBOL(tinydrm_display_pipe_update); 141 142/** 143 * tinydrm_display_pipe_prepare_fb - Display pipe prepare_fb helper 144 * @pipe: Simple display pipe 145 * @plane_state: Plane state 146 * 147 * This function uses drm_gem_fb_prepare_fb() to check if the plane FB has an 148 * dma-buf attached, extracts the exclusive fence and attaches it to plane 149 * state for the atomic helper to wait on. Drivers can use this as their 150 * &drm_simple_display_pipe_funcs->prepare_fb callback. 151 */ 152int tinydrm_display_pipe_prepare_fb(struct drm_simple_display_pipe *pipe, 153 struct drm_plane_state *plane_state) 154{ 155 return drm_gem_fb_prepare_fb(&pipe->plane, plane_state); 156} 157EXPORT_SYMBOL(tinydrm_display_pipe_prepare_fb); 158 159static int tinydrm_rotate_mode(struct drm_display_mode *mode, 160 unsigned int rotation) 161{ 162 if (rotation == 0 || rotation == 180) { 163 return 0; 164 } else if (rotation == 90 || rotation == 270) { 165 swap(mode->hdisplay, mode->vdisplay); 166 swap(mode->hsync_start, mode->vsync_start); 167 swap(mode->hsync_end, mode->vsync_end); 168 swap(mode->htotal, mode->vtotal); 169 swap(mode->width_mm, mode->height_mm); 170 return 0; 171 } else { 172 return -EINVAL; 173 } 174} 175 176/** 177 * tinydrm_display_pipe_init - Initialize display pipe 178 * @tdev: tinydrm device 179 * @funcs: Display pipe functions 180 * @connector_type: Connector type 181 * @formats: Array of supported formats (DRM_FORMAT\_\*) 182 * @format_count: Number of elements in @formats 183 * @mode: Supported mode 184 * @rotation: Initial @mode rotation in degrees Counter Clock Wise 185 * 186 * This function sets up a &drm_simple_display_pipe with a &drm_connector that 187 * has one fixed &drm_display_mode which is rotated according to @rotation. 188 * 189 * Returns: 190 * Zero on success, negative error code on failure. 191 */ 192int 193tinydrm_display_pipe_init(struct tinydrm_device *tdev, 194 const struct drm_simple_display_pipe_funcs *funcs, 195 int connector_type, 196 const uint32_t *formats, 197 unsigned int format_count, 198 const struct drm_display_mode *mode, 199 unsigned int rotation) 200{ 201 struct drm_device *drm = tdev->drm; 202 struct drm_display_mode *mode_copy; 203 struct drm_connector *connector; 204 int ret; 205 206 mode_copy = devm_kmalloc(drm->dev, sizeof(*mode_copy), GFP_KERNEL); 207 if (!mode_copy) 208 return -ENOMEM; 209 210 *mode_copy = *mode; 211 ret = tinydrm_rotate_mode(mode_copy, rotation); 212 if (ret) { 213 DRM_ERROR("Illegal rotation value %u\n", rotation); 214 return -EINVAL; 215 } 216 217 drm->mode_config.min_width = mode_copy->hdisplay; 218 drm->mode_config.max_width = mode_copy->hdisplay; 219 drm->mode_config.min_height = mode_copy->vdisplay; 220 drm->mode_config.max_height = mode_copy->vdisplay; 221 222 connector = tinydrm_connector_create(drm, mode_copy, connector_type); 223 if (IS_ERR(connector)) 224 return PTR_ERR(connector); 225 226 ret = drm_simple_display_pipe_init(drm, &tdev->pipe, funcs, formats, 227 format_count, NULL, connector); 228 if (ret) 229 return ret; 230 231 return 0; 232} 233EXPORT_SYMBOL(tinydrm_display_pipe_init);