1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * THC63LVD1024 LVDS to parallel data DRM bridge driver. 4 * 5 * Copyright (C) 2018 Jacopo Mondi <jacopo+renesas@jmondi.org> 6 */ 7 8 #include <linux/gpio/consumer.h> 9 #include <linux/module.h> 10 #include <linux/of.h> 11 #include <linux/of_graph.h> 12 #include <linux/platform_device.h> 13 #include <linux/regulator/consumer.h> 14 #include <linux/slab.h> 15 16 #include <drm/drm_bridge.h> 17 #include <drm/drm_panel.h> 18 19 enum thc63_ports { 20 THC63_LVDS_IN0, 21 THC63_LVDS_IN1, 22 THC63_RGB_OUT0, 23 THC63_RGB_OUT1, 24 }; 25 26 struct thc63_dev { 27 struct device *dev; 28 29 struct regulator *vcc; 30 31 struct gpio_desc *pdwn; 32 struct gpio_desc *oe; 33 34 struct drm_bridge bridge; 35 struct drm_bridge *next; 36 }; 37 38 static inline struct thc63_dev *to_thc63(struct drm_bridge *bridge) 39 { 40 return container_of(bridge, struct thc63_dev, bridge); 41 } 42 43 static int thc63_attach(struct drm_bridge *bridge) 44 { 45 struct thc63_dev *thc63 = to_thc63(bridge); 46 47 return drm_bridge_attach(bridge->encoder, thc63->next, bridge); 48 } 49 50 static enum drm_mode_status thc63_mode_valid(struct drm_bridge *bridge, 51 const struct drm_display_mode *mode) 52 { 53 /* 54 * The THC63LVD1024 clock frequency range is 8 to 135 MHz in single-in 55 * mode. Note that the limits are different in dual-in, single-out mode, 56 * and will need to be adjusted accordingly. 57 */ 58 if (mode->clock < 8000) 59 return MODE_CLOCK_LOW; 60 61 if (mode->clock > 135000) 62 return MODE_CLOCK_HIGH; 63 64 return MODE_OK; 65 } 66 67 static void thc63_enable(struct drm_bridge *bridge) 68 { 69 struct thc63_dev *thc63 = to_thc63(bridge); 70 int ret; 71 72 ret = regulator_enable(thc63->vcc); 73 if (ret) { 74 dev_err(thc63->dev, 75 "Failed to enable regulator \"vcc\": %d\n", ret); 76 return; 77 } 78 79 gpiod_set_value(thc63->pdwn, 0); 80 gpiod_set_value(thc63->oe, 1); 81 } 82 83 static void thc63_disable(struct drm_bridge *bridge) 84 { 85 struct thc63_dev *thc63 = to_thc63(bridge); 86 int ret; 87 88 gpiod_set_value(thc63->oe, 0); 89 gpiod_set_value(thc63->pdwn, 1); 90 91 ret = regulator_disable(thc63->vcc); 92 if (ret) 93 dev_err(thc63->dev, 94 "Failed to disable regulator \"vcc\": %d\n", ret); 95 } 96 97 static const struct drm_bridge_funcs thc63_bridge_func = { 98 .attach = thc63_attach, 99 .mode_valid = thc63_mode_valid, 100 .enable = thc63_enable, 101 .disable = thc63_disable, 102 }; 103 104 static int thc63_parse_dt(struct thc63_dev *thc63) 105 { 106 struct device_node *thc63_out; 107 struct device_node *remote; 108 109 thc63_out = of_graph_get_endpoint_by_regs(thc63->dev->of_node, 110 THC63_RGB_OUT0, -1); 111 if (!thc63_out) { 112 dev_err(thc63->dev, "Missing endpoint in port@%u\n", 113 THC63_RGB_OUT0); 114 return -ENODEV; 115 } 116 117 remote = of_graph_get_remote_port_parent(thc63_out); 118 of_node_put(thc63_out); 119 if (!remote) { 120 dev_err(thc63->dev, "Endpoint in port@%u unconnected\n", 121 THC63_RGB_OUT0); 122 return -ENODEV; 123 } 124 125 if (!of_device_is_available(remote)) { 126 dev_err(thc63->dev, "port@%u remote endpoint is disabled\n", 127 THC63_RGB_OUT0); 128 of_node_put(remote); 129 return -ENODEV; 130 } 131 132 thc63->next = of_drm_find_bridge(remote); 133 of_node_put(remote); 134 if (!thc63->next) 135 return -EPROBE_DEFER; 136 137 return 0; 138 } 139 140 static int thc63_gpio_init(struct thc63_dev *thc63) 141 { 142 thc63->oe = devm_gpiod_get_optional(thc63->dev, "oe", GPIOD_OUT_LOW); 143 if (IS_ERR(thc63->oe)) { 144 dev_err(thc63->dev, "Unable to get \"oe-gpios\": %ld\n", 145 PTR_ERR(thc63->oe)); 146 return PTR_ERR(thc63->oe); 147 } 148 149 thc63->pdwn = devm_gpiod_get_optional(thc63->dev, "powerdown", 150 GPIOD_OUT_HIGH); 151 if (IS_ERR(thc63->pdwn)) { 152 dev_err(thc63->dev, "Unable to get \"powerdown-gpios\": %ld\n", 153 PTR_ERR(thc63->pdwn)); 154 return PTR_ERR(thc63->pdwn); 155 } 156 157 return 0; 158 } 159 160 static int thc63_probe(struct platform_device *pdev) 161 { 162 struct thc63_dev *thc63; 163 int ret; 164 165 thc63 = devm_kzalloc(&pdev->dev, sizeof(*thc63), GFP_KERNEL); 166 if (!thc63) 167 return -ENOMEM; 168 169 thc63->dev = &pdev->dev; 170 platform_set_drvdata(pdev, thc63); 171 172 thc63->vcc = devm_regulator_get_optional(thc63->dev, "vcc"); 173 if (IS_ERR(thc63->vcc)) { 174 if (PTR_ERR(thc63->vcc) == -EPROBE_DEFER) 175 return -EPROBE_DEFER; 176 177 dev_err(thc63->dev, "Unable to get \"vcc\" supply: %ld\n", 178 PTR_ERR(thc63->vcc)); 179 return PTR_ERR(thc63->vcc); 180 } 181 182 ret = thc63_gpio_init(thc63); 183 if (ret) 184 return ret; 185 186 ret = thc63_parse_dt(thc63); 187 if (ret) 188 return ret; 189 190 thc63->bridge.driver_private = thc63; 191 thc63->bridge.of_node = pdev->dev.of_node; 192 thc63->bridge.funcs = &thc63_bridge_func; 193 194 drm_bridge_add(&thc63->bridge); 195 196 return 0; 197 } 198 199 static int thc63_remove(struct platform_device *pdev) 200 { 201 struct thc63_dev *thc63 = platform_get_drvdata(pdev); 202 203 drm_bridge_remove(&thc63->bridge); 204 205 return 0; 206 } 207 208 static const struct of_device_id thc63_match[] = { 209 { .compatible = "thine,thc63lvd1024", }, 210 { }, 211 }; 212 MODULE_DEVICE_TABLE(of, thc63_match); 213 214 static struct platform_driver thc63_driver = { 215 .probe = thc63_probe, 216 .remove = thc63_remove, 217 .driver = { 218 .name = "thc63lvd1024", 219 .of_match_table = thc63_match, 220 }, 221 }; 222 module_platform_driver(thc63_driver); 223 224 MODULE_AUTHOR("Jacopo Mondi <jacopo@jmondi.org>"); 225 MODULE_DESCRIPTION("Thine THC63LVD1024 LVDS decoder DRM bridge driver"); 226 MODULE_LICENSE("GPL v2"); 227