Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.1-rc1 156 lines 4.1 kB view raw
1/* 2 * Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com> 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of 7 * the License, or (at your option) any later version. 8 */ 9 10#include <drm/drmP.h> 11#include <drm/drm_bridge.h> 12#include <drm/drm_panel.h> 13 14#include <linux/gpio/consumer.h> 15#include <linux/of_graph.h> 16 17struct lvds_encoder { 18 struct drm_bridge bridge; 19 struct drm_bridge *panel_bridge; 20 struct gpio_desc *powerdown_gpio; 21}; 22 23static int lvds_encoder_attach(struct drm_bridge *bridge) 24{ 25 struct lvds_encoder *lvds_encoder = container_of(bridge, 26 struct lvds_encoder, 27 bridge); 28 29 return drm_bridge_attach(bridge->encoder, lvds_encoder->panel_bridge, 30 bridge); 31} 32 33static void lvds_encoder_enable(struct drm_bridge *bridge) 34{ 35 struct lvds_encoder *lvds_encoder = container_of(bridge, 36 struct lvds_encoder, 37 bridge); 38 39 if (lvds_encoder->powerdown_gpio) 40 gpiod_set_value_cansleep(lvds_encoder->powerdown_gpio, 0); 41} 42 43static void lvds_encoder_disable(struct drm_bridge *bridge) 44{ 45 struct lvds_encoder *lvds_encoder = container_of(bridge, 46 struct lvds_encoder, 47 bridge); 48 49 if (lvds_encoder->powerdown_gpio) 50 gpiod_set_value_cansleep(lvds_encoder->powerdown_gpio, 1); 51} 52 53static struct drm_bridge_funcs funcs = { 54 .attach = lvds_encoder_attach, 55 .enable = lvds_encoder_enable, 56 .disable = lvds_encoder_disable, 57}; 58 59static int lvds_encoder_probe(struct platform_device *pdev) 60{ 61 struct device *dev = &pdev->dev; 62 struct device_node *port; 63 struct device_node *endpoint; 64 struct device_node *panel_node; 65 struct drm_panel *panel; 66 struct lvds_encoder *lvds_encoder; 67 68 lvds_encoder = devm_kzalloc(dev, sizeof(*lvds_encoder), GFP_KERNEL); 69 if (!lvds_encoder) 70 return -ENOMEM; 71 72 lvds_encoder->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown", 73 GPIOD_OUT_HIGH); 74 if (IS_ERR(lvds_encoder->powerdown_gpio)) { 75 int err = PTR_ERR(lvds_encoder->powerdown_gpio); 76 77 if (err != -EPROBE_DEFER) 78 dev_err(dev, "powerdown GPIO failure: %d\n", err); 79 return err; 80 } 81 82 /* Locate the panel DT node. */ 83 port = of_graph_get_port_by_id(dev->of_node, 1); 84 if (!port) { 85 dev_dbg(dev, "port 1 not found\n"); 86 return -ENXIO; 87 } 88 89 endpoint = of_get_child_by_name(port, "endpoint"); 90 of_node_put(port); 91 if (!endpoint) { 92 dev_dbg(dev, "no endpoint for port 1\n"); 93 return -ENXIO; 94 } 95 96 panel_node = of_graph_get_remote_port_parent(endpoint); 97 of_node_put(endpoint); 98 if (!panel_node) { 99 dev_dbg(dev, "no remote endpoint for port 1\n"); 100 return -ENXIO; 101 } 102 103 panel = of_drm_find_panel(panel_node); 104 of_node_put(panel_node); 105 if (IS_ERR(panel)) { 106 dev_dbg(dev, "panel not found, deferring probe\n"); 107 return PTR_ERR(panel); 108 } 109 110 lvds_encoder->panel_bridge = 111 devm_drm_panel_bridge_add(dev, panel, DRM_MODE_CONNECTOR_LVDS); 112 if (IS_ERR(lvds_encoder->panel_bridge)) 113 return PTR_ERR(lvds_encoder->panel_bridge); 114 115 /* The panel_bridge bridge is attached to the panel's of_node, 116 * but we need a bridge attached to our of_node for our user 117 * to look up. 118 */ 119 lvds_encoder->bridge.of_node = dev->of_node; 120 lvds_encoder->bridge.funcs = &funcs; 121 drm_bridge_add(&lvds_encoder->bridge); 122 123 platform_set_drvdata(pdev, lvds_encoder); 124 125 return 0; 126} 127 128static int lvds_encoder_remove(struct platform_device *pdev) 129{ 130 struct lvds_encoder *lvds_encoder = platform_get_drvdata(pdev); 131 132 drm_bridge_remove(&lvds_encoder->bridge); 133 134 return 0; 135} 136 137static const struct of_device_id lvds_encoder_match[] = { 138 { .compatible = "lvds-encoder" }, 139 { .compatible = "thine,thc63lvdm83d" }, 140 {}, 141}; 142MODULE_DEVICE_TABLE(of, lvds_encoder_match); 143 144static struct platform_driver lvds_encoder_driver = { 145 .probe = lvds_encoder_probe, 146 .remove = lvds_encoder_remove, 147 .driver = { 148 .name = "lvds-encoder", 149 .of_match_table = lvds_encoder_match, 150 }, 151}; 152module_platform_driver(lvds_encoder_driver); 153 154MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); 155MODULE_DESCRIPTION("Transparent parallel to LVDS encoder"); 156MODULE_LICENSE("GPL");