at v5.2 179 lines 5.0 kB view raw
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (C) 2016 Noralf Trønnes 4 */ 5 6#include <drm/drm_atomic_helper.h> 7#include <drm/drm_drv.h> 8#include <drm/drm_gem_framebuffer_helper.h> 9#include <drm/drm_modes.h> 10#include <drm/drm_probe_helper.h> 11#include <drm/drm_print.h> 12#include <drm/drm_simple_kms_helper.h> 13 14struct tinydrm_connector { 15 struct drm_connector base; 16 struct drm_display_mode mode; 17}; 18 19static inline struct tinydrm_connector * 20to_tinydrm_connector(struct drm_connector *connector) 21{ 22 return container_of(connector, struct tinydrm_connector, base); 23} 24 25static int tinydrm_connector_get_modes(struct drm_connector *connector) 26{ 27 struct tinydrm_connector *tconn = to_tinydrm_connector(connector); 28 struct drm_display_mode *mode; 29 30 mode = drm_mode_duplicate(connector->dev, &tconn->mode); 31 if (!mode) { 32 DRM_ERROR("Failed to duplicate mode\n"); 33 return 0; 34 } 35 36 if (mode->name[0] == '\0') 37 drm_mode_set_name(mode); 38 39 mode->type |= DRM_MODE_TYPE_PREFERRED; 40 drm_mode_probed_add(connector, mode); 41 42 if (mode->width_mm) { 43 connector->display_info.width_mm = mode->width_mm; 44 connector->display_info.height_mm = mode->height_mm; 45 } 46 47 return 1; 48} 49 50static const struct drm_connector_helper_funcs tinydrm_connector_hfuncs = { 51 .get_modes = tinydrm_connector_get_modes, 52}; 53 54static enum drm_connector_status 55tinydrm_connector_detect(struct drm_connector *connector, bool force) 56{ 57 if (drm_dev_is_unplugged(connector->dev)) 58 return connector_status_disconnected; 59 60 return connector->status; 61} 62 63static void tinydrm_connector_destroy(struct drm_connector *connector) 64{ 65 struct tinydrm_connector *tconn = to_tinydrm_connector(connector); 66 67 drm_connector_cleanup(connector); 68 kfree(tconn); 69} 70 71static const struct drm_connector_funcs tinydrm_connector_funcs = { 72 .reset = drm_atomic_helper_connector_reset, 73 .detect = tinydrm_connector_detect, 74 .fill_modes = drm_helper_probe_single_connector_modes, 75 .destroy = tinydrm_connector_destroy, 76 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 77 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 78}; 79 80struct drm_connector * 81tinydrm_connector_create(struct drm_device *drm, 82 const struct drm_display_mode *mode, 83 int connector_type) 84{ 85 struct tinydrm_connector *tconn; 86 struct drm_connector *connector; 87 int ret; 88 89 tconn = kzalloc(sizeof(*tconn), GFP_KERNEL); 90 if (!tconn) 91 return ERR_PTR(-ENOMEM); 92 93 drm_mode_copy(&tconn->mode, mode); 94 connector = &tconn->base; 95 96 drm_connector_helper_add(connector, &tinydrm_connector_hfuncs); 97 ret = drm_connector_init(drm, connector, &tinydrm_connector_funcs, 98 connector_type); 99 if (ret) { 100 kfree(tconn); 101 return ERR_PTR(ret); 102 } 103 104 connector->status = connector_status_connected; 105 106 return connector; 107} 108 109static int tinydrm_rotate_mode(struct drm_display_mode *mode, 110 unsigned int rotation) 111{ 112 if (rotation == 0 || rotation == 180) { 113 return 0; 114 } else if (rotation == 90 || rotation == 270) { 115 swap(mode->hdisplay, mode->vdisplay); 116 swap(mode->hsync_start, mode->vsync_start); 117 swap(mode->hsync_end, mode->vsync_end); 118 swap(mode->htotal, mode->vtotal); 119 swap(mode->width_mm, mode->height_mm); 120 return 0; 121 } else { 122 return -EINVAL; 123 } 124} 125 126/** 127 * tinydrm_display_pipe_init - Initialize display pipe 128 * @drm: DRM device 129 * @pipe: Display pipe 130 * @funcs: Display pipe functions 131 * @connector_type: Connector type 132 * @formats: Array of supported formats (DRM_FORMAT\_\*) 133 * @format_count: Number of elements in @formats 134 * @mode: Supported mode 135 * @rotation: Initial @mode rotation in degrees Counter Clock Wise 136 * 137 * This function sets up a &drm_simple_display_pipe with a &drm_connector that 138 * has one fixed &drm_display_mode which is rotated according to @rotation. 139 * 140 * Returns: 141 * Zero on success, negative error code on failure. 142 */ 143int tinydrm_display_pipe_init(struct drm_device *drm, 144 struct drm_simple_display_pipe *pipe, 145 const struct drm_simple_display_pipe_funcs *funcs, 146 int connector_type, 147 const uint32_t *formats, 148 unsigned int format_count, 149 const struct drm_display_mode *mode, 150 unsigned int rotation) 151{ 152 struct drm_display_mode mode_copy; 153 struct drm_connector *connector; 154 int ret; 155 static const uint64_t modifiers[] = { 156 DRM_FORMAT_MOD_LINEAR, 157 DRM_FORMAT_MOD_INVALID 158 }; 159 160 drm_mode_copy(&mode_copy, mode); 161 ret = tinydrm_rotate_mode(&mode_copy, rotation); 162 if (ret) { 163 DRM_ERROR("Illegal rotation value %u\n", rotation); 164 return -EINVAL; 165 } 166 167 drm->mode_config.min_width = mode_copy.hdisplay; 168 drm->mode_config.max_width = mode_copy.hdisplay; 169 drm->mode_config.min_height = mode_copy.vdisplay; 170 drm->mode_config.max_height = mode_copy.vdisplay; 171 172 connector = tinydrm_connector_create(drm, &mode_copy, connector_type); 173 if (IS_ERR(connector)) 174 return PTR_ERR(connector); 175 176 return drm_simple_display_pipe_init(drm, pipe, funcs, formats, 177 format_count, modifiers, connector); 178} 179EXPORT_SYMBOL(tinydrm_display_pipe_init);