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 12#include <drm/drm_bridge.h> 13#include <drm/drm_crtc.h> 14#include <drm/drm_modeset_helper_vtables.h> 15#include <drm/drm_panel.h> 16#include <drm/drm_simple_kms_helper.h> 17 18#include "rcar_du_drv.h" 19#include "rcar_du_encoder.h" 20#include "rcar_du_kms.h" 21#include "rcar_lvds.h" 22 23/* ----------------------------------------------------------------------------- 24 * Encoder 25 */ 26 27static unsigned int rcar_du_encoder_count_ports(struct device_node *node) 28{ 29 struct device_node *ports; 30 struct device_node *port; 31 unsigned int num_ports = 0; 32 33 ports = of_get_child_by_name(node, "ports"); 34 if (!ports) 35 ports = of_node_get(node); 36 37 for_each_child_of_node(ports, port) { 38 if (of_node_name_eq(port, "port")) 39 num_ports++; 40 } 41 42 of_node_put(ports); 43 44 return num_ports; 45} 46 47int rcar_du_encoder_init(struct rcar_du_device *rcdu, 48 enum rcar_du_output output, 49 struct device_node *enc_node) 50{ 51 struct rcar_du_encoder *renc; 52 struct drm_encoder *encoder; 53 struct drm_bridge *bridge; 54 int ret; 55 56 renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL); 57 if (renc == NULL) 58 return -ENOMEM; 59 60 renc->output = output; 61 encoder = rcar_encoder_to_drm_encoder(renc); 62 63 dev_dbg(rcdu->dev, "initializing encoder %pOF for output %u\n", 64 enc_node, output); 65 66 /* 67 * Locate the DRM bridge from the DT node. For the DPAD outputs, if the 68 * DT node has a single port, assume that it describes a panel and 69 * create a panel bridge. 70 */ 71 if ((output == RCAR_DU_OUTPUT_DPAD0 || 72 output == RCAR_DU_OUTPUT_DPAD1) && 73 rcar_du_encoder_count_ports(enc_node) == 1) { 74 struct drm_panel *panel = of_drm_find_panel(enc_node); 75 76 if (IS_ERR(panel)) { 77 ret = PTR_ERR(panel); 78 goto done; 79 } 80 81 bridge = devm_drm_panel_bridge_add_typed(rcdu->dev, panel, 82 DRM_MODE_CONNECTOR_DPI); 83 if (IS_ERR(bridge)) { 84 ret = PTR_ERR(bridge); 85 goto done; 86 } 87 } else { 88 bridge = of_drm_find_bridge(enc_node); 89 if (!bridge) { 90 ret = -EPROBE_DEFER; 91 goto done; 92 } 93 94 if (output == RCAR_DU_OUTPUT_LVDS0 || 95 output == RCAR_DU_OUTPUT_LVDS1) 96 rcdu->lvds[output - RCAR_DU_OUTPUT_LVDS0] = bridge; 97 } 98 99 /* 100 * On Gen3 skip the LVDS1 output if the LVDS1 encoder is used as a 101 * companion for LVDS0 in dual-link mode. 102 */ 103 if (rcdu->info->gen >= 3 && output == RCAR_DU_OUTPUT_LVDS1) { 104 if (rcar_lvds_dual_link(bridge)) { 105 ret = -ENOLINK; 106 goto done; 107 } 108 } 109 110 ret = drm_simple_encoder_init(rcdu->ddev, encoder, 111 DRM_MODE_ENCODER_NONE); 112 if (ret < 0) 113 goto done; 114 115 /* 116 * Attach the bridge to the encoder. The bridge will create the 117 * connector. 118 */ 119 ret = drm_bridge_attach(encoder, bridge, NULL, 0); 120 if (ret) { 121 drm_encoder_cleanup(encoder); 122 return ret; 123 } 124 125done: 126 if (ret < 0) { 127 if (encoder->name) 128 encoder->funcs->destroy(encoder); 129 devm_kfree(rcdu->dev, renc); 130 } 131 132 return ret; 133} 134