brintos

brintos / linux-shallow public Read only

0
0
Text · 3.6 KiB · 7ecec7b Raw
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