1 /* 2 * Copyright (C) 2014 Traphandler 3 * Copyright (C) 2014 Free Electrons 4 * Copyright (C) 2014 Atmel 5 * 6 * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com> 7 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU General Public License version 2 as published by 11 * the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 16 * more details. 17 * 18 * You should have received a copy of the GNU General Public License along with 19 * this program. If not, see <http://www.gnu.org/licenses/>. 20 */ 21 22 #include <linux/of_graph.h> 23 24 #include <drm/drmP.h> 25 #include <drm/drm_of.h> 26 #include <drm/drm_bridge.h> 27 28 #include "atmel_hlcdc_dc.h" 29 30 struct atmel_hlcdc_rgb_output { 31 struct drm_encoder encoder; 32 int bus_fmt; 33 }; 34 35 static const struct drm_encoder_funcs atmel_hlcdc_panel_encoder_funcs = { 36 .destroy = drm_encoder_cleanup, 37 }; 38 39 static struct atmel_hlcdc_rgb_output * 40 atmel_hlcdc_encoder_to_rgb_output(struct drm_encoder *encoder) 41 { 42 return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder); 43 } 44 45 int atmel_hlcdc_encoder_get_bus_fmt(struct drm_encoder *encoder) 46 { 47 struct atmel_hlcdc_rgb_output *output; 48 49 output = atmel_hlcdc_encoder_to_rgb_output(encoder); 50 51 return output->bus_fmt; 52 } 53 54 static int atmel_hlcdc_of_bus_fmt(const struct device_node *ep) 55 { 56 u32 bus_width; 57 int ret; 58 59 ret = of_property_read_u32(ep, "bus-width", &bus_width); 60 if (ret == -EINVAL) 61 return 0; 62 if (ret) 63 return ret; 64 65 switch (bus_width) { 66 case 12: 67 return MEDIA_BUS_FMT_RGB444_1X12; 68 case 16: 69 return MEDIA_BUS_FMT_RGB565_1X16; 70 case 18: 71 return MEDIA_BUS_FMT_RGB666_1X18; 72 case 24: 73 return MEDIA_BUS_FMT_RGB888_1X24; 74 default: 75 return -EINVAL; 76 } 77 } 78 79 static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) 80 { 81 struct atmel_hlcdc_rgb_output *output; 82 struct device_node *ep; 83 struct drm_panel *panel; 84 struct drm_bridge *bridge; 85 int ret; 86 87 ep = of_graph_get_endpoint_by_regs(dev->dev->of_node, 0, endpoint); 88 if (!ep) 89 return -ENODEV; 90 91 ret = drm_of_find_panel_or_bridge(dev->dev->of_node, 0, endpoint, 92 &panel, &bridge); 93 if (ret) { 94 of_node_put(ep); 95 return ret; 96 } 97 98 output = devm_kzalloc(dev->dev, sizeof(*output), GFP_KERNEL); 99 if (!output) { 100 of_node_put(ep); 101 return -ENOMEM; 102 } 103 104 output->bus_fmt = atmel_hlcdc_of_bus_fmt(ep); 105 of_node_put(ep); 106 if (output->bus_fmt < 0) { 107 dev_err(dev->dev, "endpoint %d: invalid bus width\n", endpoint); 108 return -EINVAL; 109 } 110 111 ret = drm_encoder_init(dev, &output->encoder, 112 &atmel_hlcdc_panel_encoder_funcs, 113 DRM_MODE_ENCODER_NONE, NULL); 114 if (ret) 115 return ret; 116 117 output->encoder.possible_crtcs = 0x1; 118 119 if (panel) { 120 bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_Unknown); 121 if (IS_ERR(bridge)) 122 return PTR_ERR(bridge); 123 } 124 125 if (bridge) { 126 ret = drm_bridge_attach(&output->encoder, bridge, NULL); 127 if (!ret) 128 return 0; 129 130 if (panel) 131 drm_panel_bridge_remove(bridge); 132 } 133 134 drm_encoder_cleanup(&output->encoder); 135 136 return ret; 137 } 138 139 int atmel_hlcdc_create_outputs(struct drm_device *dev) 140 { 141 int endpoint, ret = 0; 142 int attached = 0; 143 144 /* 145 * Always scan the first few endpoints even if we get -ENODEV, 146 * but keep going after that as long as we keep getting hits. 147 */ 148 for (endpoint = 0; !ret || endpoint < 4; endpoint++) { 149 ret = atmel_hlcdc_attach_endpoint(dev, endpoint); 150 if (ret == -ENODEV) 151 continue; 152 if (ret) 153 break; 154 attached++; 155 } 156 157 /* At least one device was successfully attached.*/ 158 if (ret == -ENODEV && attached) 159 return 0; 160 161 return ret; 162 } 163