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