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