Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.9.
  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/of_graph.h>
 15
 16struct lvds_encoder {
 17	struct drm_bridge bridge;
 18	struct drm_bridge *panel_bridge;
 19};
 20
 21static int lvds_encoder_attach(struct drm_bridge *bridge)
 22{
 23	struct lvds_encoder *lvds_encoder = container_of(bridge,
 24							 struct lvds_encoder,
 25							 bridge);
 26
 27	return drm_bridge_attach(bridge->encoder, lvds_encoder->panel_bridge,
 28				 bridge);
 29}
 30
 31static struct drm_bridge_funcs funcs = {
 32	.attach = lvds_encoder_attach,
 33};
 34
 35static int lvds_encoder_probe(struct platform_device *pdev)
 36{
 37	struct device_node *port;
 38	struct device_node *endpoint;
 39	struct device_node *panel_node;
 40	struct drm_panel *panel;
 41	struct lvds_encoder *lvds_encoder;
 42
 43	lvds_encoder = devm_kzalloc(&pdev->dev, sizeof(*lvds_encoder),
 44				    GFP_KERNEL);
 45	if (!lvds_encoder)
 46		return -ENOMEM;
 47
 48	/* Locate the panel DT node. */
 49	port = of_graph_get_port_by_id(pdev->dev.of_node, 1);
 50	if (!port) {
 51		dev_dbg(&pdev->dev, "port 1 not found\n");
 52		return -ENXIO;
 53	}
 54
 55	endpoint = of_get_child_by_name(port, "endpoint");
 56	of_node_put(port);
 57	if (!endpoint) {
 58		dev_dbg(&pdev->dev, "no endpoint for port 1\n");
 59		return -ENXIO;
 60	}
 61
 62	panel_node = of_graph_get_remote_port_parent(endpoint);
 63	of_node_put(endpoint);
 64	if (!panel_node) {
 65		dev_dbg(&pdev->dev, "no remote endpoint for port 1\n");
 66		return -ENXIO;
 67	}
 68
 69	panel = of_drm_find_panel(panel_node);
 70	of_node_put(panel_node);
 71	if (!panel) {
 72		dev_dbg(&pdev->dev, "panel not found, deferring probe\n");
 73		return -EPROBE_DEFER;
 74	}
 75
 76	lvds_encoder->panel_bridge =
 77		devm_drm_panel_bridge_add(&pdev->dev,
 78					  panel, DRM_MODE_CONNECTOR_LVDS);
 79	if (IS_ERR(lvds_encoder->panel_bridge))
 80		return PTR_ERR(lvds_encoder->panel_bridge);
 81
 82	/* The panel_bridge bridge is attached to the panel's of_node,
 83	 * but we need a bridge attached to our of_node for our user
 84	 * to look up.
 85	 */
 86	lvds_encoder->bridge.of_node = pdev->dev.of_node;
 87	lvds_encoder->bridge.funcs = &funcs;
 88	drm_bridge_add(&lvds_encoder->bridge);
 89
 90	platform_set_drvdata(pdev, lvds_encoder);
 91
 92	return 0;
 93}
 94
 95static int lvds_encoder_remove(struct platform_device *pdev)
 96{
 97	struct lvds_encoder *lvds_encoder = platform_get_drvdata(pdev);
 98
 99	drm_bridge_remove(&lvds_encoder->bridge);
100
101	return 0;
102}
103
104static const struct of_device_id lvds_encoder_match[] = {
105	{ .compatible = "lvds-encoder" },
106	{ .compatible = "thine,thc63lvdm83d" },
107	{},
108};
109MODULE_DEVICE_TABLE(of, lvds_encoder_match);
110
111static struct platform_driver lvds_encoder_driver = {
112	.probe	= lvds_encoder_probe,
113	.remove	= lvds_encoder_remove,
114	.driver		= {
115		.name		= "lvds-encoder",
116		.of_match_table	= lvds_encoder_match,
117	},
118};
119module_platform_driver(lvds_encoder_driver);
120
121MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
122MODULE_DESCRIPTION("Transparent parallel to LVDS encoder");
123MODULE_LICENSE("GPL");