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