root/drivers/gpu/drm/bridge/thc63lvd1024.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. to_thc63
  2. thc63_attach
  3. thc63_mode_valid
  4. thc63_enable
  5. thc63_disable
  6. thc63_parse_dt
  7. thc63_gpio_init
  8. thc63_probe
  9. thc63_remove

   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         struct drm_bridge_timings timings;
  38 };
  39 
  40 static inline struct thc63_dev *to_thc63(struct drm_bridge *bridge)
  41 {
  42         return container_of(bridge, struct thc63_dev, bridge);
  43 }
  44 
  45 static int thc63_attach(struct drm_bridge *bridge)
  46 {
  47         struct thc63_dev *thc63 = to_thc63(bridge);
  48 
  49         return drm_bridge_attach(bridge->encoder, thc63->next, bridge);
  50 }
  51 
  52 static enum drm_mode_status thc63_mode_valid(struct drm_bridge *bridge,
  53                                         const struct drm_display_mode *mode)
  54 {
  55         struct thc63_dev *thc63 = to_thc63(bridge);
  56         unsigned int min_freq;
  57         unsigned int max_freq;
  58 
  59         /*
  60          * The THC63LVD1024 pixel rate range is 8 to 135 MHz in all modes but
  61          * dual-in, single-out where it is 40 to 150 MHz. As dual-in, dual-out
  62          * isn't supported by the driver yet, simply derive the limits from the
  63          * input mode.
  64          */
  65         if (thc63->timings.dual_link) {
  66                 min_freq = 40000;
  67                 max_freq = 150000;
  68         } else {
  69                 min_freq = 8000;
  70                 max_freq = 135000;
  71         }
  72 
  73         if (mode->clock < min_freq)
  74                 return MODE_CLOCK_LOW;
  75 
  76         if (mode->clock > max_freq)
  77                 return MODE_CLOCK_HIGH;
  78 
  79         return MODE_OK;
  80 }
  81 
  82 static void thc63_enable(struct drm_bridge *bridge)
  83 {
  84         struct thc63_dev *thc63 = to_thc63(bridge);
  85         int ret;
  86 
  87         ret = regulator_enable(thc63->vcc);
  88         if (ret) {
  89                 dev_err(thc63->dev,
  90                         "Failed to enable regulator \"vcc\": %d\n", ret);
  91                 return;
  92         }
  93 
  94         gpiod_set_value(thc63->pdwn, 0);
  95         gpiod_set_value(thc63->oe, 1);
  96 }
  97 
  98 static void thc63_disable(struct drm_bridge *bridge)
  99 {
 100         struct thc63_dev *thc63 = to_thc63(bridge);
 101         int ret;
 102 
 103         gpiod_set_value(thc63->oe, 0);
 104         gpiod_set_value(thc63->pdwn, 1);
 105 
 106         ret = regulator_disable(thc63->vcc);
 107         if (ret)
 108                 dev_err(thc63->dev,
 109                         "Failed to disable regulator \"vcc\": %d\n", ret);
 110 }
 111 
 112 static const struct drm_bridge_funcs thc63_bridge_func = {
 113         .attach = thc63_attach,
 114         .mode_valid = thc63_mode_valid,
 115         .enable = thc63_enable,
 116         .disable = thc63_disable,
 117 };
 118 
 119 static int thc63_parse_dt(struct thc63_dev *thc63)
 120 {
 121         struct device_node *endpoint;
 122         struct device_node *remote;
 123 
 124         endpoint = of_graph_get_endpoint_by_regs(thc63->dev->of_node,
 125                                                  THC63_RGB_OUT0, -1);
 126         if (!endpoint) {
 127                 dev_err(thc63->dev, "Missing endpoint in port@%u\n",
 128                         THC63_RGB_OUT0);
 129                 return -ENODEV;
 130         }
 131 
 132         remote = of_graph_get_remote_port_parent(endpoint);
 133         of_node_put(endpoint);
 134         if (!remote) {
 135                 dev_err(thc63->dev, "Endpoint in port@%u unconnected\n",
 136                         THC63_RGB_OUT0);
 137                 return -ENODEV;
 138         }
 139 
 140         if (!of_device_is_available(remote)) {
 141                 dev_err(thc63->dev, "port@%u remote endpoint is disabled\n",
 142                         THC63_RGB_OUT0);
 143                 of_node_put(remote);
 144                 return -ENODEV;
 145         }
 146 
 147         thc63->next = of_drm_find_bridge(remote);
 148         of_node_put(remote);
 149         if (!thc63->next)
 150                 return -EPROBE_DEFER;
 151 
 152         endpoint = of_graph_get_endpoint_by_regs(thc63->dev->of_node,
 153                                                  THC63_LVDS_IN1, -1);
 154         if (endpoint) {
 155                 remote = of_graph_get_remote_port_parent(endpoint);
 156                 of_node_put(endpoint);
 157 
 158                 if (remote) {
 159                         if (of_device_is_available(remote))
 160                                 thc63->timings.dual_link = true;
 161                         of_node_put(remote);
 162                 }
 163         }
 164 
 165         dev_dbg(thc63->dev, "operating in %s-link mode\n",
 166                 thc63->timings.dual_link ? "dual" : "single");
 167 
 168         return 0;
 169 }
 170 
 171 static int thc63_gpio_init(struct thc63_dev *thc63)
 172 {
 173         thc63->oe = devm_gpiod_get_optional(thc63->dev, "oe", GPIOD_OUT_LOW);
 174         if (IS_ERR(thc63->oe)) {
 175                 dev_err(thc63->dev, "Unable to get \"oe-gpios\": %ld\n",
 176                         PTR_ERR(thc63->oe));
 177                 return PTR_ERR(thc63->oe);
 178         }
 179 
 180         thc63->pdwn = devm_gpiod_get_optional(thc63->dev, "powerdown",
 181                                               GPIOD_OUT_HIGH);
 182         if (IS_ERR(thc63->pdwn)) {
 183                 dev_err(thc63->dev, "Unable to get \"powerdown-gpios\": %ld\n",
 184                         PTR_ERR(thc63->pdwn));
 185                 return PTR_ERR(thc63->pdwn);
 186         }
 187 
 188         return 0;
 189 }
 190 
 191 static int thc63_probe(struct platform_device *pdev)
 192 {
 193         struct thc63_dev *thc63;
 194         int ret;
 195 
 196         thc63 = devm_kzalloc(&pdev->dev, sizeof(*thc63), GFP_KERNEL);
 197         if (!thc63)
 198                 return -ENOMEM;
 199 
 200         thc63->dev = &pdev->dev;
 201         platform_set_drvdata(pdev, thc63);
 202 
 203         thc63->vcc = devm_regulator_get_optional(thc63->dev, "vcc");
 204         if (IS_ERR(thc63->vcc)) {
 205                 if (PTR_ERR(thc63->vcc) == -EPROBE_DEFER)
 206                         return -EPROBE_DEFER;
 207 
 208                 dev_err(thc63->dev, "Unable to get \"vcc\" supply: %ld\n",
 209                         PTR_ERR(thc63->vcc));
 210                 return PTR_ERR(thc63->vcc);
 211         }
 212 
 213         ret = thc63_gpio_init(thc63);
 214         if (ret)
 215                 return ret;
 216 
 217         ret = thc63_parse_dt(thc63);
 218         if (ret)
 219                 return ret;
 220 
 221         thc63->bridge.driver_private = thc63;
 222         thc63->bridge.of_node = pdev->dev.of_node;
 223         thc63->bridge.funcs = &thc63_bridge_func;
 224         thc63->bridge.timings = &thc63->timings;
 225 
 226         drm_bridge_add(&thc63->bridge);
 227 
 228         return 0;
 229 }
 230 
 231 static int thc63_remove(struct platform_device *pdev)
 232 {
 233         struct thc63_dev *thc63 = platform_get_drvdata(pdev);
 234 
 235         drm_bridge_remove(&thc63->bridge);
 236 
 237         return 0;
 238 }
 239 
 240 static const struct of_device_id thc63_match[] = {
 241         { .compatible = "thine,thc63lvd1024", },
 242         { },
 243 };
 244 MODULE_DEVICE_TABLE(of, thc63_match);
 245 
 246 static struct platform_driver thc63_driver = {
 247         .probe  = thc63_probe,
 248         .remove = thc63_remove,
 249         .driver = {
 250                 .name           = "thc63lvd1024",
 251                 .of_match_table = thc63_match,
 252         },
 253 };
 254 module_platform_driver(thc63_driver);
 255 
 256 MODULE_AUTHOR("Jacopo Mondi <jacopo@jmondi.org>");
 257 MODULE_DESCRIPTION("Thine THC63LVD1024 LVDS decoder DRM bridge driver");
 258 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */