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 /* The internal LVDS encoder has a clock frequency operating range of 93 * 30MHz to 150MHz. Clamp the clock accordingly. 94 */ 95 if (renc->lvds) 96 adjusted_mode->clock = clamp(adjusted_mode->clock, 97 30000, 150000); 98 99 return 0; 100} 101 102static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, 103 struct drm_display_mode *mode, 104 struct drm_display_mode *adjusted_mode) 105{ 106 struct rcar_du_encoder *renc = to_rcar_encoder(encoder); 107 108 rcar_du_crtc_route_output(encoder->crtc, renc->output); 109} 110 111static const struct drm_encoder_helper_funcs encoder_helper_funcs = { 112 .mode_set = rcar_du_encoder_mode_set, 113 .disable = rcar_du_encoder_disable, 114 .enable = rcar_du_encoder_enable, 115 .atomic_check = rcar_du_encoder_atomic_check, 116}; 117 118static const struct drm_encoder_funcs encoder_funcs = { 119 .destroy = drm_encoder_cleanup, 120}; 121 122int rcar_du_encoder_init(struct rcar_du_device *rcdu, 123 enum rcar_du_encoder_type type, 124 enum rcar_du_output output, 125 struct device_node *enc_node, 126 struct device_node *con_node) 127{ 128 struct rcar_du_encoder *renc; 129 struct drm_encoder *encoder; 130 unsigned int encoder_type; 131 int ret; 132 133 renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL); 134 if (renc == NULL) 135 return -ENOMEM; 136 137 renc->output = output; 138 encoder = rcar_encoder_to_drm_encoder(renc); 139 140 switch (output) { 141 case RCAR_DU_OUTPUT_LVDS0: 142 renc->lvds = rcdu->lvds[0]; 143 break; 144 145 case RCAR_DU_OUTPUT_LVDS1: 146 renc->lvds = rcdu->lvds[1]; 147 break; 148 149 default: 150 break; 151 } 152 153 switch (type) { 154 case RCAR_DU_ENCODER_VGA: 155 encoder_type = DRM_MODE_ENCODER_DAC; 156 break; 157 case RCAR_DU_ENCODER_LVDS: 158 encoder_type = DRM_MODE_ENCODER_LVDS; 159 break; 160 case RCAR_DU_ENCODER_HDMI: 161 encoder_type = DRM_MODE_ENCODER_TMDS; 162 break; 163 case RCAR_DU_ENCODER_NONE: 164 default: 165 /* No external encoder, use the internal encoder type. */ 166 encoder_type = rcdu->info->routes[output].encoder_type; 167 break; 168 } 169 170 if (type == RCAR_DU_ENCODER_HDMI) { 171 ret = rcar_du_hdmienc_init(rcdu, renc, enc_node); 172 if (ret < 0) 173 goto done; 174 } else { 175 ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs, 176 encoder_type); 177 if (ret < 0) 178 goto done; 179 180 drm_encoder_helper_add(encoder, &encoder_helper_funcs); 181 } 182 183 switch (encoder_type) { 184 case DRM_MODE_ENCODER_LVDS: 185 ret = rcar_du_lvds_connector_init(rcdu, renc, con_node); 186 break; 187 188 case DRM_MODE_ENCODER_DAC: 189 ret = rcar_du_vga_connector_init(rcdu, renc); 190 break; 191 192 case DRM_MODE_ENCODER_TMDS: 193 ret = rcar_du_hdmi_connector_init(rcdu, renc); 194 break; 195 196 default: 197 ret = -EINVAL; 198 break; 199 } 200 201done: 202 if (ret < 0) { 203 if (encoder->name) 204 encoder->funcs->destroy(encoder); 205 devm_kfree(rcdu->dev, renc); 206 } 207 208 return ret; 209} 210