1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2014 Traphandler 4 * Copyright (C) 2014 Free Electrons 5 * Copyright (C) 2014 Atmel 6 * 7 * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com> 8 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 9 */ 10 11 #include <linux/of_graph.h> 12 13 #include <drm/drmP.h> 14 #include <drm/drm_of.h> 15 #include <drm/drm_bridge.h> 16 17 #include "atmel_hlcdc_dc.h" 18 19 struct atmel_hlcdc_rgb_output { 20 struct drm_encoder encoder; 21 int bus_fmt; 22 }; 23 24 static const struct drm_encoder_funcs atmel_hlcdc_panel_encoder_funcs = { 25 .destroy = drm_encoder_cleanup, 26 }; 27 28 static struct atmel_hlcdc_rgb_output * 29 atmel_hlcdc_encoder_to_rgb_output(struct drm_encoder *encoder) 30 { 31 return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder); 32 } 33 34 int atmel_hlcdc_encoder_get_bus_fmt(struct drm_encoder *encoder) 35 { 36 struct atmel_hlcdc_rgb_output *output; 37 38 output = atmel_hlcdc_encoder_to_rgb_output(encoder); 39 40 return output->bus_fmt; 41 } 42 43 static int atmel_hlcdc_of_bus_fmt(const struct device_node *ep) 44 { 45 u32 bus_width; 46 int ret; 47 48 ret = of_property_read_u32(ep, "bus-width", &bus_width); 49 if (ret == -EINVAL) 50 return 0; 51 if (ret) 52 return ret; 53 54 switch (bus_width) { 55 case 12: 56 return MEDIA_BUS_FMT_RGB444_1X12; 57 case 16: 58 return MEDIA_BUS_FMT_RGB565_1X16; 59 case 18: 60 return MEDIA_BUS_FMT_RGB666_1X18; 61 case 24: 62 return MEDIA_BUS_FMT_RGB888_1X24; 63 default: 64 return -EINVAL; 65 } 66 } 67 68 static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) 69 { 70 struct atmel_hlcdc_rgb_output *output; 71 struct device_node *ep; 72 struct drm_panel *panel; 73 struct drm_bridge *bridge; 74 int ret; 75 76 ep = of_graph_get_endpoint_by_regs(dev->dev->of_node, 0, endpoint); 77 if (!ep) 78 return -ENODEV; 79 80 ret = drm_of_find_panel_or_bridge(dev->dev->of_node, 0, endpoint, 81 &panel, &bridge); 82 if (ret) { 83 of_node_put(ep); 84 return ret; 85 } 86 87 output = devm_kzalloc(dev->dev, sizeof(*output), GFP_KERNEL); 88 if (!output) { 89 of_node_put(ep); 90 return -ENOMEM; 91 } 92 93 output->bus_fmt = atmel_hlcdc_of_bus_fmt(ep); 94 of_node_put(ep); 95 if (output->bus_fmt < 0) { 96 dev_err(dev->dev, "endpoint %d: invalid bus width\n", endpoint); 97 return -EINVAL; 98 } 99 100 ret = drm_encoder_init(dev, &output->encoder, 101 &atmel_hlcdc_panel_encoder_funcs, 102 DRM_MODE_ENCODER_NONE, NULL); 103 if (ret) 104 return ret; 105 106 output->encoder.possible_crtcs = 0x1; 107 108 if (panel) { 109 bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_Unknown); 110 if (IS_ERR(bridge)) 111 return PTR_ERR(bridge); 112 } 113 114 if (bridge) { 115 ret = drm_bridge_attach(&output->encoder, bridge, NULL); 116 if (!ret) 117 return 0; 118 119 if (panel) 120 drm_panel_bridge_remove(bridge); 121 } 122 123 drm_encoder_cleanup(&output->encoder); 124 125 return ret; 126 } 127 128 int atmel_hlcdc_create_outputs(struct drm_device *dev) 129 { 130 int endpoint, ret = 0; 131 int attached = 0; 132 133 /* 134 * Always scan the first few endpoints even if we get -ENODEV, 135 * but keep going after that as long as we keep getting hits. 136 */ 137 for (endpoint = 0; !ret || endpoint < 4; endpoint++) { 138 ret = atmel_hlcdc_attach_endpoint(dev, endpoint); 139 if (ret == -ENODEV) 140 continue; 141 if (ret) 142 break; 143 attached++; 144 } 145 146 /* At least one device was successfully attached.*/ 147 if (ret == -ENODEV && attached) 148 return 0; 149 150 return ret; 151 } 152