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_print.h> 19 #include <drm/drm_simple_kms_helper.h> 20 21 #include "atmel_hlcdc_dc.h" 22 23 struct atmel_hlcdc_rgb_output { 24 struct drm_encoder encoder; 25 int bus_fmt; 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_bridge *bridge; 73 struct atmel_hlcdc_dc *dc = dev->dev_private; 74 struct drm_crtc *crtc = dc->crtc; 75 int ret = 0; 76 77 bridge = devm_drm_of_get_bridge(dev->dev, dev->dev->of_node, 0, endpoint); 78 if (IS_ERR(bridge)) 79 return PTR_ERR(bridge); 80 81 output = drmm_simple_encoder_alloc(dev, struct atmel_hlcdc_rgb_output, 82 encoder, DRM_MODE_ENCODER_NONE); 83 if (IS_ERR(output)) 84 return PTR_ERR(output); 85 86 ep = of_graph_get_endpoint_by_regs(dev->dev->of_node, 0, endpoint); 87 if (!ep) 88 return -ENODEV; 89 90 output->bus_fmt = atmel_hlcdc_of_bus_fmt(ep); 91 of_node_put(ep); 92 if (output->bus_fmt < 0) { 93 drm_err(dev, "endpoint %d: invalid bus width\n", endpoint); 94 return -EINVAL; 95 } 96 97 98 output->encoder.possible_crtcs = drm_crtc_mask(crtc); 99 100 if (bridge) 101 ret = drm_bridge_attach(&output->encoder, bridge, NULL, 0); 102 103 return ret; 104 } 105 106 int atmel_hlcdc_create_outputs(struct drm_device *dev) 107 { 108 int endpoint, ret = 0; 109 int attached = 0; 110 111 /* 112 * Always scan the first few endpoints even if we get -ENODEV, 113 * but keep going after that as long as we keep getting hits. 114 */ 115 for (endpoint = 0; !ret || endpoint < 4; endpoint++) { 116 ret = atmel_hlcdc_attach_endpoint(dev, endpoint); 117 if (ret == -ENODEV) 118 continue; 119 if (ret) 120 break; 121 attached++; 122 } 123 124 /* At least one device was successfully attached.*/ 125 if (ret == -ENODEV && attached) 126 return 0; 127 128 return ret; 129 } 130