138 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * R-Car Display Unit Encoder4 *5 * Copyright (C) 2013-2014 Renesas Electronics Corporation6 *7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)8 */9 10#include <linux/export.h>11#include <linux/of.h>12 13#include <drm/drm_bridge.h>14#include <drm/drm_bridge_connector.h>15#include <drm/drm_panel.h>16 17#include "rcar_du_drv.h"18#include "rcar_du_encoder.h"19#include "rcar_lvds.h"20 21/* -----------------------------------------------------------------------------22 * Encoder23 */24 25static unsigned int rcar_du_encoder_count_ports(struct device_node *node)26{27 struct device_node *ports;28 struct device_node *port;29 unsigned int num_ports = 0;30 31 ports = of_get_child_by_name(node, "ports");32 if (!ports)33 ports = of_node_get(node);34 35 for_each_child_of_node(ports, port) {36 if (of_node_name_eq(port, "port"))37 num_ports++;38 }39 40 of_node_put(ports);41 42 return num_ports;43}44 45static const struct drm_encoder_funcs rcar_du_encoder_funcs = {46};47 48int rcar_du_encoder_init(struct rcar_du_device *rcdu,49 enum rcar_du_output output,50 struct device_node *enc_node)51{52 struct rcar_du_encoder *renc;53 struct drm_connector *connector;54 struct drm_bridge *bridge;55 int ret;56 57 /*58 * Locate the DRM bridge from the DT node. For the DPAD outputs, if the59 * DT node has a single port, assume that it describes a panel and60 * create a panel bridge.61 */62 if ((output == RCAR_DU_OUTPUT_DPAD0 ||63 output == RCAR_DU_OUTPUT_DPAD1) &&64 rcar_du_encoder_count_ports(enc_node) == 1) {65 struct drm_panel *panel = of_drm_find_panel(enc_node);66 67 if (IS_ERR(panel))68 return PTR_ERR(panel);69 70 bridge = devm_drm_panel_bridge_add_typed(rcdu->dev, panel,71 DRM_MODE_CONNECTOR_DPI);72 if (IS_ERR(bridge))73 return PTR_ERR(bridge);74 } else {75 bridge = of_drm_find_bridge(enc_node);76 if (!bridge)77 return -EPROBE_DEFER;78 79 if (output == RCAR_DU_OUTPUT_LVDS0 ||80 output == RCAR_DU_OUTPUT_LVDS1)81 rcdu->lvds[output - RCAR_DU_OUTPUT_LVDS0] = bridge;82 83 if (output == RCAR_DU_OUTPUT_DSI0 ||84 output == RCAR_DU_OUTPUT_DSI1)85 rcdu->dsi[output - RCAR_DU_OUTPUT_DSI0] = bridge;86 }87 88 /*89 * Create and initialize the encoder. On Gen3, skip the LVDS1 output if90 * the LVDS1 encoder is used as a companion for LVDS0 in dual-link91 * mode, or any LVDS output if it isn't connected. The latter may happen92 * on D3 or E3 as the LVDS encoders are needed to provide the pixel93 * clock to the DU, even when the LVDS outputs are not used.94 */95 if (rcdu->info->gen >= 3) {96 if (output == RCAR_DU_OUTPUT_LVDS1 &&97 rcar_lvds_dual_link(bridge))98 return -ENOLINK;99 100 if ((output == RCAR_DU_OUTPUT_LVDS0 ||101 output == RCAR_DU_OUTPUT_LVDS1) &&102 !rcar_lvds_is_connected(bridge))103 return -ENOLINK;104 }105 106 dev_dbg(rcdu->dev, "initializing encoder %pOF for output %s\n",107 enc_node, rcar_du_output_name(output));108 109 renc = drmm_encoder_alloc(&rcdu->ddev, struct rcar_du_encoder, base,110 &rcar_du_encoder_funcs, DRM_MODE_ENCODER_NONE,111 NULL);112 if (IS_ERR(renc))113 return PTR_ERR(renc);114 115 renc->output = output;116 117 /* Attach the bridge to the encoder. */118 ret = drm_bridge_attach(&renc->base, bridge, NULL,119 DRM_BRIDGE_ATTACH_NO_CONNECTOR);120 if (ret) {121 dev_err(rcdu->dev,122 "failed to attach bridge %pOF for output %s (%d)\n",123 bridge->of_node, rcar_du_output_name(output), ret);124 return ret;125 }126 127 /* Create the connector for the chain of bridges. */128 connector = drm_bridge_connector_init(&rcdu->ddev, &renc->base);129 if (IS_ERR(connector)) {130 dev_err(rcdu->dev,131 "failed to created connector for output %s (%ld)\n",132 rcar_du_output_name(output), PTR_ERR(connector));133 return PTR_ERR(connector);134 }135 136 return drm_connector_attach_encoder(connector, &renc->base);137}138