1/* 2 * Copyright (C) 2012 Russell King 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 version 2 as 6 * published by the Free Software Foundation. 7 */ 8#include <drm/drmP.h> 9#include <drm/drm_crtc_helper.h> 10#include <drm/drm_edid.h> 11#include <drm/drm_encoder_slave.h> 12#include "armada_output.h" 13#include "armada_drm.h" 14 15struct armada_connector { 16 struct drm_connector conn; 17 const struct armada_output_type *type; 18}; 19 20#define drm_to_armada_conn(c) container_of(c, struct armada_connector, conn) 21 22struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn) 23{ 24 struct drm_encoder *enc = conn->encoder; 25 26 return enc ? enc : drm_encoder_find(conn->dev, conn->encoder_ids[0]); 27} 28 29static enum drm_connector_status armada_drm_connector_detect( 30 struct drm_connector *conn, bool force) 31{ 32 struct armada_connector *dconn = drm_to_armada_conn(conn); 33 enum drm_connector_status status = connector_status_disconnected; 34 35 if (dconn->type->detect) { 36 status = dconn->type->detect(conn, force); 37 } else { 38 struct drm_encoder *enc = armada_drm_connector_encoder(conn); 39 40 if (enc) 41 status = encoder_helper_funcs(enc)->detect(enc, conn); 42 } 43 44 return status; 45} 46 47static void armada_drm_connector_destroy(struct drm_connector *conn) 48{ 49 struct armada_connector *dconn = drm_to_armada_conn(conn); 50 51 drm_connector_unregister(conn); 52 drm_connector_cleanup(conn); 53 kfree(dconn); 54} 55 56static int armada_drm_connector_set_property(struct drm_connector *conn, 57 struct drm_property *property, uint64_t value) 58{ 59 struct armada_connector *dconn = drm_to_armada_conn(conn); 60 61 if (!dconn->type->set_property) 62 return -EINVAL; 63 64 return dconn->type->set_property(conn, property, value); 65} 66 67static const struct drm_connector_funcs armada_drm_conn_funcs = { 68 .dpms = drm_helper_connector_dpms, 69 .fill_modes = drm_helper_probe_single_connector_modes, 70 .detect = armada_drm_connector_detect, 71 .destroy = armada_drm_connector_destroy, 72 .set_property = armada_drm_connector_set_property, 73}; 74 75void armada_drm_encoder_prepare(struct drm_encoder *encoder) 76{ 77 encoder_helper_funcs(encoder)->dpms(encoder, DRM_MODE_DPMS_OFF); 78} 79 80void armada_drm_encoder_commit(struct drm_encoder *encoder) 81{ 82 encoder_helper_funcs(encoder)->dpms(encoder, DRM_MODE_DPMS_ON); 83} 84 85bool armada_drm_encoder_mode_fixup(struct drm_encoder *encoder, 86 const struct drm_display_mode *mode, struct drm_display_mode *adjusted) 87{ 88 return true; 89} 90 91/* Shouldn't this be a generic helper function? */ 92int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn, 93 struct drm_display_mode *mode) 94{ 95 struct drm_encoder *encoder = armada_drm_connector_encoder(conn); 96 int valid = MODE_BAD; 97 98 if (encoder) { 99 struct drm_encoder_slave *slave = to_encoder_slave(encoder); 100 101 valid = slave->slave_funcs->mode_valid(encoder, mode); 102 } 103 return valid; 104} 105 106int armada_drm_slave_encoder_set_property(struct drm_connector *conn, 107 struct drm_property *property, uint64_t value) 108{ 109 struct drm_encoder *encoder = armada_drm_connector_encoder(conn); 110 int rc = -EINVAL; 111 112 if (encoder) { 113 struct drm_encoder_slave *slave = to_encoder_slave(encoder); 114 115 rc = slave->slave_funcs->set_property(encoder, conn, property, 116 value); 117 } 118 return rc; 119} 120 121int armada_output_create(struct drm_device *dev, 122 const struct armada_output_type *type, const void *data) 123{ 124 struct armada_connector *dconn; 125 int ret; 126 127 dconn = kzalloc(sizeof(*dconn), GFP_KERNEL); 128 if (!dconn) 129 return -ENOMEM; 130 131 dconn->type = type; 132 133 ret = drm_connector_init(dev, &dconn->conn, &armada_drm_conn_funcs, 134 type->connector_type); 135 if (ret) { 136 DRM_ERROR("unable to init connector\n"); 137 goto err_destroy_dconn; 138 } 139 140 ret = type->create(&dconn->conn, data); 141 if (ret) 142 goto err_conn; 143 144 ret = drm_connector_register(&dconn->conn); 145 if (ret) 146 goto err_sysfs; 147 148 return 0; 149 150 err_sysfs: 151 if (dconn->conn.encoder) 152 dconn->conn.encoder->funcs->destroy(dconn->conn.encoder); 153 err_conn: 154 drm_connector_cleanup(&dconn->conn); 155 err_destroy_dconn: 156 kfree(dconn); 157 return ret; 158} 159