at v4.13 243 lines 6.1 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#include <drm/drm_panel.h> 20 21#include "rcar_du_drv.h" 22#include "rcar_du_encoder.h" 23#include "rcar_du_kms.h" 24#include "rcar_du_lvdscon.h" 25#include "rcar_du_lvdsenc.h" 26 27/* ----------------------------------------------------------------------------- 28 * Encoder 29 */ 30 31static void rcar_du_encoder_disable(struct drm_encoder *encoder) 32{ 33 struct rcar_du_encoder *renc = to_rcar_encoder(encoder); 34 35 if (renc->connector && renc->connector->panel) { 36 drm_panel_disable(renc->connector->panel); 37 drm_panel_unprepare(renc->connector->panel); 38 } 39 40 if (renc->lvds) 41 rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, false); 42} 43 44static void rcar_du_encoder_enable(struct drm_encoder *encoder) 45{ 46 struct rcar_du_encoder *renc = to_rcar_encoder(encoder); 47 48 if (renc->lvds) 49 rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, true); 50 51 if (renc->connector && renc->connector->panel) { 52 drm_panel_prepare(renc->connector->panel); 53 drm_panel_enable(renc->connector->panel); 54 } 55} 56 57static int rcar_du_encoder_atomic_check(struct drm_encoder *encoder, 58 struct drm_crtc_state *crtc_state, 59 struct drm_connector_state *conn_state) 60{ 61 struct rcar_du_encoder *renc = to_rcar_encoder(encoder); 62 struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; 63 const struct drm_display_mode *mode = &crtc_state->mode; 64 struct drm_connector *connector = conn_state->connector; 65 struct drm_device *dev = encoder->dev; 66 67 /* 68 * Only panel-related encoder types require validation here, everything 69 * else is handled by the bridge drivers. 70 */ 71 if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) { 72 const struct drm_display_mode *panel_mode; 73 74 if (list_empty(&connector->modes)) { 75 dev_dbg(dev->dev, "encoder: empty modes list\n"); 76 return -EINVAL; 77 } 78 79 panel_mode = list_first_entry(&connector->modes, 80 struct drm_display_mode, head); 81 82 /* We're not allowed to modify the resolution. */ 83 if (mode->hdisplay != panel_mode->hdisplay || 84 mode->vdisplay != panel_mode->vdisplay) 85 return -EINVAL; 86 87 /* 88 * The flat panel mode is fixed, just copy it to the adjusted 89 * mode. 90 */ 91 drm_mode_copy(adjusted_mode, panel_mode); 92 } 93 94 if (renc->lvds) 95 rcar_du_lvdsenc_atomic_check(renc->lvds, adjusted_mode); 96 97 return 0; 98} 99 100static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, 101 struct drm_crtc_state *crtc_state, 102 struct drm_connector_state *conn_state) 103{ 104 struct rcar_du_encoder *renc = to_rcar_encoder(encoder); 105 struct drm_display_info *info = &conn_state->connector->display_info; 106 enum rcar_lvds_mode mode; 107 108 rcar_du_crtc_route_output(crtc_state->crtc, renc->output); 109 110 if (!renc->lvds) { 111 /* 112 * The DU driver creates connectors only for the outputs of the 113 * internal LVDS encoders. 114 */ 115 renc->connector = NULL; 116 return; 117 } 118 119 renc->connector = to_rcar_connector(conn_state->connector); 120 121 if (!info->num_bus_formats || !info->bus_formats) { 122 dev_err(encoder->dev->dev, "no LVDS bus format reported\n"); 123 return; 124 } 125 126 switch (info->bus_formats[0]) { 127 case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: 128 case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: 129 mode = RCAR_LVDS_MODE_JEIDA; 130 break; 131 case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: 132 mode = RCAR_LVDS_MODE_VESA; 133 break; 134 default: 135 dev_err(encoder->dev->dev, 136 "unsupported LVDS bus format 0x%04x\n", 137 info->bus_formats[0]); 138 return; 139 } 140 141 if (info->bus_flags & DRM_BUS_FLAG_DATA_LSB_TO_MSB) 142 mode |= RCAR_LVDS_MODE_MIRROR; 143 144 rcar_du_lvdsenc_set_mode(renc->lvds, mode); 145} 146 147static const struct drm_encoder_helper_funcs encoder_helper_funcs = { 148 .atomic_mode_set = rcar_du_encoder_mode_set, 149 .disable = rcar_du_encoder_disable, 150 .enable = rcar_du_encoder_enable, 151 .atomic_check = rcar_du_encoder_atomic_check, 152}; 153 154static const struct drm_encoder_funcs encoder_funcs = { 155 .destroy = drm_encoder_cleanup, 156}; 157 158int rcar_du_encoder_init(struct rcar_du_device *rcdu, 159 enum rcar_du_output output, 160 struct device_node *enc_node, 161 struct device_node *con_node) 162{ 163 struct rcar_du_encoder *renc; 164 struct drm_encoder *encoder; 165 struct drm_bridge *bridge = NULL; 166 int ret; 167 168 renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL); 169 if (renc == NULL) 170 return -ENOMEM; 171 172 renc->output = output; 173 encoder = rcar_encoder_to_drm_encoder(renc); 174 175 switch (output) { 176 case RCAR_DU_OUTPUT_LVDS0: 177 renc->lvds = rcdu->lvds[0]; 178 break; 179 180 case RCAR_DU_OUTPUT_LVDS1: 181 renc->lvds = rcdu->lvds[1]; 182 break; 183 184 default: 185 break; 186 } 187 188 if (enc_node) { 189 dev_dbg(rcdu->dev, "initializing encoder %s for output %u\n", 190 of_node_full_name(enc_node), output); 191 192 /* Locate the DRM bridge from the encoder DT node. */ 193 bridge = of_drm_find_bridge(enc_node); 194 if (!bridge) { 195 ret = -EPROBE_DEFER; 196 goto done; 197 } 198 } else { 199 dev_dbg(rcdu->dev, 200 "initializing internal encoder for output %u\n", 201 output); 202 } 203 204 ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs, 205 DRM_MODE_ENCODER_NONE, NULL); 206 if (ret < 0) 207 goto done; 208 209 drm_encoder_helper_add(encoder, &encoder_helper_funcs); 210 211 if (bridge) { 212 /* 213 * Attach the bridge to the encoder. The bridge will create the 214 * connector. 215 */ 216 ret = drm_bridge_attach(encoder, bridge, NULL); 217 if (ret) { 218 drm_encoder_cleanup(encoder); 219 return ret; 220 } 221 } else { 222 /* There's no bridge, create the connector manually. */ 223 switch (output) { 224 case RCAR_DU_OUTPUT_LVDS0: 225 case RCAR_DU_OUTPUT_LVDS1: 226 ret = rcar_du_lvds_connector_init(rcdu, renc, con_node); 227 break; 228 229 default: 230 ret = -EINVAL; 231 break; 232 } 233 } 234 235done: 236 if (ret < 0) { 237 if (encoder->name) 238 encoder->funcs->destroy(encoder); 239 devm_kfree(rcdu->dev, renc); 240 } 241 242 return ret; 243}