Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v4.7 205 lines 5.2 kB view raw
1/* 2 * rcar_du_encoder.c -- R-Car Display Unit Encoder 3 * 4 * Copyright (C) 2013-2014 Renesas Electronics Corporation 5 * 6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 */ 13 14#include <linux/export.h> 15 16#include <drm/drmP.h> 17#include <drm/drm_crtc.h> 18#include <drm/drm_crtc_helper.h> 19 20#include "rcar_du_drv.h" 21#include "rcar_du_encoder.h" 22#include "rcar_du_hdmicon.h" 23#include "rcar_du_hdmienc.h" 24#include "rcar_du_kms.h" 25#include "rcar_du_lvdscon.h" 26#include "rcar_du_lvdsenc.h" 27#include "rcar_du_vgacon.h" 28 29/* ----------------------------------------------------------------------------- 30 * Common connector functions 31 */ 32 33struct drm_encoder * 34rcar_du_connector_best_encoder(struct drm_connector *connector) 35{ 36 struct rcar_du_connector *rcon = to_rcar_connector(connector); 37 38 return rcar_encoder_to_drm_encoder(rcon->encoder); 39} 40 41/* ----------------------------------------------------------------------------- 42 * Encoder 43 */ 44 45static void rcar_du_encoder_disable(struct drm_encoder *encoder) 46{ 47 struct rcar_du_encoder *renc = to_rcar_encoder(encoder); 48 49 if (renc->lvds) 50 rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, false); 51} 52 53static void rcar_du_encoder_enable(struct drm_encoder *encoder) 54{ 55 struct rcar_du_encoder *renc = to_rcar_encoder(encoder); 56 57 if (renc->lvds) 58 rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, true); 59} 60 61static int rcar_du_encoder_atomic_check(struct drm_encoder *encoder, 62 struct drm_crtc_state *crtc_state, 63 struct drm_connector_state *conn_state) 64{ 65 struct rcar_du_encoder *renc = to_rcar_encoder(encoder); 66 struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; 67 const struct drm_display_mode *mode = &crtc_state->mode; 68 const struct drm_display_mode *panel_mode; 69 struct drm_connector *connector = conn_state->connector; 70 struct drm_device *dev = encoder->dev; 71 72 /* DAC encoders have currently no restriction on the mode. */ 73 if (encoder->encoder_type == DRM_MODE_ENCODER_DAC) 74 return 0; 75 76 if (list_empty(&connector->modes)) { 77 dev_dbg(dev->dev, "encoder: empty modes list\n"); 78 return -EINVAL; 79 } 80 81 panel_mode = list_first_entry(&connector->modes, 82 struct drm_display_mode, head); 83 84 /* We're not allowed to modify the resolution. */ 85 if (mode->hdisplay != panel_mode->hdisplay || 86 mode->vdisplay != panel_mode->vdisplay) 87 return -EINVAL; 88 89 /* The flat panel mode is fixed, just copy it to the adjusted mode. */ 90 drm_mode_copy(adjusted_mode, panel_mode); 91 92 if (renc->lvds) 93 rcar_du_lvdsenc_atomic_check(renc->lvds, adjusted_mode); 94 95 return 0; 96} 97 98static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, 99 struct drm_display_mode *mode, 100 struct drm_display_mode *adjusted_mode) 101{ 102 struct rcar_du_encoder *renc = to_rcar_encoder(encoder); 103 104 rcar_du_crtc_route_output(encoder->crtc, renc->output); 105} 106 107static const struct drm_encoder_helper_funcs encoder_helper_funcs = { 108 .mode_set = rcar_du_encoder_mode_set, 109 .disable = rcar_du_encoder_disable, 110 .enable = rcar_du_encoder_enable, 111 .atomic_check = rcar_du_encoder_atomic_check, 112}; 113 114static const struct drm_encoder_funcs encoder_funcs = { 115 .destroy = drm_encoder_cleanup, 116}; 117 118int rcar_du_encoder_init(struct rcar_du_device *rcdu, 119 enum rcar_du_encoder_type type, 120 enum rcar_du_output output, 121 struct device_node *enc_node, 122 struct device_node *con_node) 123{ 124 struct rcar_du_encoder *renc; 125 struct drm_encoder *encoder; 126 unsigned int encoder_type; 127 int ret; 128 129 renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL); 130 if (renc == NULL) 131 return -ENOMEM; 132 133 renc->output = output; 134 encoder = rcar_encoder_to_drm_encoder(renc); 135 136 switch (output) { 137 case RCAR_DU_OUTPUT_LVDS0: 138 renc->lvds = rcdu->lvds[0]; 139 break; 140 141 case RCAR_DU_OUTPUT_LVDS1: 142 renc->lvds = rcdu->lvds[1]; 143 break; 144 145 default: 146 break; 147 } 148 149 switch (type) { 150 case RCAR_DU_ENCODER_VGA: 151 encoder_type = DRM_MODE_ENCODER_DAC; 152 break; 153 case RCAR_DU_ENCODER_LVDS: 154 encoder_type = DRM_MODE_ENCODER_LVDS; 155 break; 156 case RCAR_DU_ENCODER_HDMI: 157 encoder_type = DRM_MODE_ENCODER_TMDS; 158 break; 159 case RCAR_DU_ENCODER_NONE: 160 default: 161 /* No external encoder, use the internal encoder type. */ 162 encoder_type = rcdu->info->routes[output].encoder_type; 163 break; 164 } 165 166 if (type == RCAR_DU_ENCODER_HDMI) { 167 ret = rcar_du_hdmienc_init(rcdu, renc, enc_node); 168 if (ret < 0) 169 goto done; 170 } else { 171 ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs, 172 encoder_type, NULL); 173 if (ret < 0) 174 goto done; 175 176 drm_encoder_helper_add(encoder, &encoder_helper_funcs); 177 } 178 179 switch (encoder_type) { 180 case DRM_MODE_ENCODER_LVDS: 181 ret = rcar_du_lvds_connector_init(rcdu, renc, con_node); 182 break; 183 184 case DRM_MODE_ENCODER_DAC: 185 ret = rcar_du_vga_connector_init(rcdu, renc); 186 break; 187 188 case DRM_MODE_ENCODER_TMDS: 189 ret = rcar_du_hdmi_connector_init(rcdu, renc); 190 break; 191 192 default: 193 ret = -EINVAL; 194 break; 195 } 196 197done: 198 if (ret < 0) { 199 if (encoder->name) 200 encoder->funcs->destroy(encoder); 201 devm_kfree(rcdu->dev, renc); 202 } 203 204 return ret; 205}