Loading...
Note: File does not exist in v6.13.7.
1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * rcar_du_encoder.c -- R-Car Display Unit Encoder
4 *
5 * Copyright (C) 2013-2014 Renesas Electronics Corporation
6 *
7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
8 */
9
10#include <linux/export.h>
11#include <linux/slab.h>
12
13#include <drm/drm_bridge.h>
14#include <drm/drm_crtc.h>
15#include <drm/drm_managed.h>
16#include <drm/drm_modeset_helper_vtables.h>
17#include <drm/drm_panel.h>
18
19#include "rcar_du_drv.h"
20#include "rcar_du_encoder.h"
21#include "rcar_du_kms.h"
22#include "rcar_lvds.h"
23
24/* -----------------------------------------------------------------------------
25 * Encoder
26 */
27
28static unsigned int rcar_du_encoder_count_ports(struct device_node *node)
29{
30 struct device_node *ports;
31 struct device_node *port;
32 unsigned int num_ports = 0;
33
34 ports = of_get_child_by_name(node, "ports");
35 if (!ports)
36 ports = of_node_get(node);
37
38 for_each_child_of_node(ports, port) {
39 if (of_node_name_eq(port, "port"))
40 num_ports++;
41 }
42
43 of_node_put(ports);
44
45 return num_ports;
46}
47
48static const struct drm_encoder_funcs rcar_du_encoder_funcs = {
49};
50
51int rcar_du_encoder_init(struct rcar_du_device *rcdu,
52 enum rcar_du_output output,
53 struct device_node *enc_node)
54{
55 struct rcar_du_encoder *renc;
56 struct drm_bridge *bridge;
57
58 /*
59 * Locate the DRM bridge from the DT node. For the DPAD outputs, if the
60 * DT node has a single port, assume that it describes a panel and
61 * create a panel bridge.
62 */
63 if ((output == RCAR_DU_OUTPUT_DPAD0 ||
64 output == RCAR_DU_OUTPUT_DPAD1) &&
65 rcar_du_encoder_count_ports(enc_node) == 1) {
66 struct drm_panel *panel = of_drm_find_panel(enc_node);
67
68 if (IS_ERR(panel))
69 return PTR_ERR(panel);
70
71 bridge = devm_drm_panel_bridge_add_typed(rcdu->dev, panel,
72 DRM_MODE_CONNECTOR_DPI);
73 if (IS_ERR(bridge))
74 return PTR_ERR(bridge);
75 } else {
76 bridge = of_drm_find_bridge(enc_node);
77 if (!bridge)
78 return -EPROBE_DEFER;
79
80 if (output == RCAR_DU_OUTPUT_LVDS0 ||
81 output == RCAR_DU_OUTPUT_LVDS1)
82 rcdu->lvds[output - RCAR_DU_OUTPUT_LVDS0] = bridge;
83 }
84
85 /*
86 * Create and initialize the encoder. On Gen3 skip the LVDS1 output if
87 * the LVDS1 encoder is used as a companion for LVDS0 in dual-link
88 * mode.
89 */
90 if (rcdu->info->gen >= 3 && output == RCAR_DU_OUTPUT_LVDS1) {
91 if (rcar_lvds_dual_link(bridge))
92 return -ENOLINK;
93 }
94
95 dev_dbg(rcdu->dev, "initializing encoder %pOF for output %u\n",
96 enc_node, output);
97
98 renc = drmm_encoder_alloc(&rcdu->ddev, struct rcar_du_encoder, base,
99 &rcar_du_encoder_funcs, DRM_MODE_ENCODER_NONE,
100 NULL);
101 if (!renc)
102 return -ENOMEM;
103
104 renc->output = output;
105
106 /*
107 * Attach the bridge to the encoder. The bridge will create the
108 * connector.
109 */
110 return drm_bridge_attach(&renc->base, bridge, NULL, 0);
111}