1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2019 Renesas Electronics Corporation 4 * Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com> 5 */ 6 7 #include <linux/gpio/consumer.h> 8 #include <linux/module.h> 9 #include <linux/of.h> 10 #include <linux/of_device.h> 11 #include <linux/of_graph.h> 12 #include <linux/platform_device.h> 13 14 #include <drm/drm_bridge.h> 15 #include <drm/drm_panel.h> 16 17 struct lvds_codec { 18 struct drm_bridge bridge; 19 struct drm_bridge *panel_bridge; 20 struct gpio_desc *powerdown_gpio; 21 u32 connector_type; 22 }; 23 24 static int lvds_codec_attach(struct drm_bridge *bridge) 25 { 26 struct lvds_codec *lvds_codec = container_of(bridge, 27 struct lvds_codec, bridge); 28 29 return drm_bridge_attach(bridge->encoder, lvds_codec->panel_bridge, 30 bridge); 31 } 32 33 static void lvds_codec_enable(struct drm_bridge *bridge) 34 { 35 struct lvds_codec *lvds_codec = container_of(bridge, 36 struct lvds_codec, bridge); 37 38 if (lvds_codec->powerdown_gpio) 39 gpiod_set_value_cansleep(lvds_codec->powerdown_gpio, 0); 40 } 41 42 static void lvds_codec_disable(struct drm_bridge *bridge) 43 { 44 struct lvds_codec *lvds_codec = container_of(bridge, 45 struct lvds_codec, bridge); 46 47 if (lvds_codec->powerdown_gpio) 48 gpiod_set_value_cansleep(lvds_codec->powerdown_gpio, 1); 49 } 50 51 static struct drm_bridge_funcs funcs = { 52 .attach = lvds_codec_attach, 53 .enable = lvds_codec_enable, 54 .disable = lvds_codec_disable, 55 }; 56 57 static int lvds_codec_probe(struct platform_device *pdev) 58 { 59 struct device *dev = &pdev->dev; 60 struct device_node *panel_node; 61 struct drm_panel *panel; 62 struct lvds_codec *lvds_codec; 63 64 lvds_codec = devm_kzalloc(dev, sizeof(*lvds_codec), GFP_KERNEL); 65 if (!lvds_codec) 66 return -ENOMEM; 67 68 lvds_codec->connector_type = (uintptr_t)of_device_get_match_data(dev); 69 lvds_codec->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown", 70 GPIOD_OUT_HIGH); 71 if (IS_ERR(lvds_codec->powerdown_gpio)) { 72 int err = PTR_ERR(lvds_codec->powerdown_gpio); 73 74 if (err != -EPROBE_DEFER) 75 dev_err(dev, "powerdown GPIO failure: %d\n", err); 76 return err; 77 } 78 79 /* Locate the panel DT node. */ 80 panel_node = of_graph_get_remote_node(dev->of_node, 1, 0); 81 if (!panel_node) { 82 dev_dbg(dev, "panel DT node not found\n"); 83 return -ENXIO; 84 } 85 86 panel = of_drm_find_panel(panel_node); 87 of_node_put(panel_node); 88 if (IS_ERR(panel)) { 89 dev_dbg(dev, "panel not found, deferring probe\n"); 90 return PTR_ERR(panel); 91 } 92 93 lvds_codec->panel_bridge = 94 devm_drm_panel_bridge_add_typed(dev, panel, 95 lvds_codec->connector_type); 96 if (IS_ERR(lvds_codec->panel_bridge)) 97 return PTR_ERR(lvds_codec->panel_bridge); 98 99 /* 100 * The panel_bridge bridge is attached to the panel's of_node, 101 * but we need a bridge attached to our of_node for our user 102 * to look up. 103 */ 104 lvds_codec->bridge.of_node = dev->of_node; 105 lvds_codec->bridge.funcs = &funcs; 106 drm_bridge_add(&lvds_codec->bridge); 107 108 platform_set_drvdata(pdev, lvds_codec); 109 110 return 0; 111 } 112 113 static int lvds_codec_remove(struct platform_device *pdev) 114 { 115 struct lvds_codec *lvds_codec = platform_get_drvdata(pdev); 116 117 drm_bridge_remove(&lvds_codec->bridge); 118 119 return 0; 120 } 121 122 static const struct of_device_id lvds_codec_match[] = { 123 { 124 .compatible = "lvds-decoder", 125 .data = (void *)DRM_MODE_CONNECTOR_DPI, 126 }, 127 { 128 .compatible = "lvds-encoder", 129 .data = (void *)DRM_MODE_CONNECTOR_LVDS, 130 }, 131 { 132 .compatible = "thine,thc63lvdm83d", 133 .data = (void *)DRM_MODE_CONNECTOR_LVDS, 134 }, 135 {}, 136 }; 137 MODULE_DEVICE_TABLE(of, lvds_codec_match); 138 139 static struct platform_driver lvds_codec_driver = { 140 .probe = lvds_codec_probe, 141 .remove = lvds_codec_remove, 142 .driver = { 143 .name = "lvds-codec", 144 .of_match_table = lvds_codec_match, 145 }, 146 }; 147 module_platform_driver(lvds_codec_driver); 148 149 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); 150 MODULE_DESCRIPTION("LVDS encoders and decoders"); 151 MODULE_LICENSE("GPL"); 152