1 /* 2 * Copyright 2015 Freescale Semiconductor, Inc. 3 * 4 * Freescale DCU drm device driver 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 */ 11 12 #include <linux/backlight.h> 13 #include <linux/of_graph.h> 14 15 #include <drm/drmP.h> 16 #include <drm/drm_atomic_helper.h> 17 #include <drm/drm_crtc_helper.h> 18 #include <drm/drm_panel.h> 19 20 #include "fsl_dcu_drm_drv.h" 21 #include "fsl_tcon.h" 22 23 static int 24 fsl_dcu_drm_encoder_atomic_check(struct drm_encoder *encoder, 25 struct drm_crtc_state *crtc_state, 26 struct drm_connector_state *conn_state) 27 { 28 return 0; 29 } 30 31 static void fsl_dcu_drm_encoder_disable(struct drm_encoder *encoder) 32 { 33 struct drm_device *dev = encoder->dev; 34 struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; 35 36 if (fsl_dev->tcon) 37 fsl_tcon_bypass_disable(fsl_dev->tcon); 38 } 39 40 static void fsl_dcu_drm_encoder_enable(struct drm_encoder *encoder) 41 { 42 struct drm_device *dev = encoder->dev; 43 struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; 44 45 if (fsl_dev->tcon) 46 fsl_tcon_bypass_enable(fsl_dev->tcon); 47 } 48 49 static const struct drm_encoder_helper_funcs encoder_helper_funcs = { 50 .atomic_check = fsl_dcu_drm_encoder_atomic_check, 51 .disable = fsl_dcu_drm_encoder_disable, 52 .enable = fsl_dcu_drm_encoder_enable, 53 }; 54 55 static void fsl_dcu_drm_encoder_destroy(struct drm_encoder *encoder) 56 { 57 drm_encoder_cleanup(encoder); 58 } 59 60 static const struct drm_encoder_funcs encoder_funcs = { 61 .destroy = fsl_dcu_drm_encoder_destroy, 62 }; 63 64 int fsl_dcu_drm_encoder_create(struct fsl_dcu_drm_device *fsl_dev, 65 struct drm_crtc *crtc) 66 { 67 struct drm_encoder *encoder = &fsl_dev->encoder; 68 int ret; 69 70 encoder->possible_crtcs = 1; 71 ret = drm_encoder_init(fsl_dev->drm, encoder, &encoder_funcs, 72 DRM_MODE_ENCODER_LVDS, NULL); 73 if (ret < 0) 74 return ret; 75 76 drm_encoder_helper_add(encoder, &encoder_helper_funcs); 77 78 return 0; 79 } 80 81 static void fsl_dcu_drm_connector_destroy(struct drm_connector *connector) 82 { 83 struct fsl_dcu_drm_connector *fsl_con = to_fsl_dcu_connector(connector); 84 85 drm_connector_unregister(connector); 86 drm_panel_detach(fsl_con->panel); 87 drm_connector_cleanup(connector); 88 } 89 90 static enum drm_connector_status 91 fsl_dcu_drm_connector_detect(struct drm_connector *connector, bool force) 92 { 93 return connector_status_connected; 94 } 95 96 static const struct drm_connector_funcs fsl_dcu_drm_connector_funcs = { 97 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 98 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 99 .destroy = fsl_dcu_drm_connector_destroy, 100 .detect = fsl_dcu_drm_connector_detect, 101 .dpms = drm_atomic_helper_connector_dpms, 102 .fill_modes = drm_helper_probe_single_connector_modes, 103 .reset = drm_atomic_helper_connector_reset, 104 }; 105 106 static int fsl_dcu_drm_connector_get_modes(struct drm_connector *connector) 107 { 108 struct fsl_dcu_drm_connector *fsl_connector; 109 int (*get_modes)(struct drm_panel *panel); 110 int num_modes = 0; 111 112 fsl_connector = to_fsl_dcu_connector(connector); 113 if (fsl_connector->panel && fsl_connector->panel->funcs && 114 fsl_connector->panel->funcs->get_modes) { 115 get_modes = fsl_connector->panel->funcs->get_modes; 116 num_modes = get_modes(fsl_connector->panel); 117 } 118 119 return num_modes; 120 } 121 122 static int fsl_dcu_drm_connector_mode_valid(struct drm_connector *connector, 123 struct drm_display_mode *mode) 124 { 125 if (mode->hdisplay & 0xf) 126 return MODE_ERROR; 127 128 return MODE_OK; 129 } 130 131 static const struct drm_connector_helper_funcs connector_helper_funcs = { 132 .get_modes = fsl_dcu_drm_connector_get_modes, 133 .mode_valid = fsl_dcu_drm_connector_mode_valid, 134 }; 135 136 static int fsl_dcu_attach_panel(struct fsl_dcu_drm_device *fsl_dev, 137 struct drm_panel *panel) 138 { 139 struct drm_encoder *encoder = &fsl_dev->encoder; 140 struct drm_connector *connector = &fsl_dev->connector.base; 141 struct drm_mode_config *mode_config = &fsl_dev->drm->mode_config; 142 int ret; 143 144 fsl_dev->connector.encoder = encoder; 145 146 ret = drm_connector_init(fsl_dev->drm, connector, 147 &fsl_dcu_drm_connector_funcs, 148 DRM_MODE_CONNECTOR_LVDS); 149 if (ret < 0) 150 return ret; 151 152 drm_connector_helper_add(connector, &connector_helper_funcs); 153 ret = drm_connector_register(connector); 154 if (ret < 0) 155 goto err_cleanup; 156 157 ret = drm_mode_connector_attach_encoder(connector, encoder); 158 if (ret < 0) 159 goto err_sysfs; 160 161 drm_object_property_set_value(&connector->base, 162 mode_config->dpms_property, 163 DRM_MODE_DPMS_OFF); 164 165 ret = drm_panel_attach(panel, connector); 166 if (ret) { 167 dev_err(fsl_dev->dev, "failed to attach panel\n"); 168 goto err_sysfs; 169 } 170 171 return 0; 172 173 err_sysfs: 174 drm_connector_unregister(connector); 175 err_cleanup: 176 drm_connector_cleanup(connector); 177 return ret; 178 } 179 180 static int fsl_dcu_attach_endpoint(struct fsl_dcu_drm_device *fsl_dev, 181 const struct of_endpoint *ep) 182 { 183 struct drm_bridge *bridge; 184 struct device_node *np; 185 186 np = of_graph_get_remote_port_parent(ep->local_node); 187 188 fsl_dev->connector.panel = of_drm_find_panel(np); 189 if (fsl_dev->connector.panel) { 190 of_node_put(np); 191 return fsl_dcu_attach_panel(fsl_dev, fsl_dev->connector.panel); 192 } 193 194 bridge = of_drm_find_bridge(np); 195 of_node_put(np); 196 if (!bridge) 197 return -ENODEV; 198 199 fsl_dev->encoder.bridge = bridge; 200 bridge->encoder = &fsl_dev->encoder; 201 202 return drm_bridge_attach(fsl_dev->drm, bridge); 203 } 204 205 int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev) 206 { 207 struct of_endpoint ep; 208 struct device_node *ep_node, *panel_node; 209 int ret; 210 211 /* This is for backward compatibility */ 212 panel_node = of_parse_phandle(fsl_dev->np, "fsl,panel", 0); 213 if (panel_node) { 214 fsl_dev->connector.panel = of_drm_find_panel(panel_node); 215 of_node_put(panel_node); 216 if (!fsl_dev->connector.panel) 217 return -EPROBE_DEFER; 218 return fsl_dcu_attach_panel(fsl_dev, fsl_dev->connector.panel); 219 } 220 221 ep_node = of_graph_get_next_endpoint(fsl_dev->np, NULL); 222 if (!ep_node) 223 return -ENODEV; 224 225 ret = of_graph_parse_endpoint(ep_node, &ep); 226 of_node_put(ep_node); 227 if (ret) 228 return -ENODEV; 229 230 return fsl_dcu_attach_endpoint(fsl_dev, &ep); 231 } 232