brintos

brintos / linux-shallow public Read only

0
0
Text · 10.2 KiB · 1e8ec50 Raw
373 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2012 Avionic Design GmbH4 * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.5 */6 7#include <linux/clk.h>8#include <linux/of.h>9 10#include <drm/drm_atomic_helper.h>11#include <drm/drm_bridge_connector.h>12#include <drm/drm_simple_kms_helper.h>13 14#include "drm.h"15#include "dc.h"16 17struct tegra_rgb {18	struct tegra_output output;19	struct tegra_dc *dc;20 21	struct clk *pll_d_out0;22	struct clk *pll_d2_out0;23	struct clk *clk_parent;24	struct clk *clk;25};26 27static inline struct tegra_rgb *to_rgb(struct tegra_output *output)28{29	return container_of(output, struct tegra_rgb, output);30}31 32struct reg_entry {33	unsigned long offset;34	unsigned long value;35};36 37static const struct reg_entry rgb_enable[] = {38	{ DC_COM_PIN_OUTPUT_ENABLE(0),   0x00000000 },39	{ DC_COM_PIN_OUTPUT_ENABLE(1),   0x00000000 },40	{ DC_COM_PIN_OUTPUT_ENABLE(2),   0x00000000 },41	{ DC_COM_PIN_OUTPUT_ENABLE(3),   0x00000000 },42	{ DC_COM_PIN_OUTPUT_POLARITY(0), 0x00000000 },43	{ DC_COM_PIN_OUTPUT_POLARITY(1), 0x01000000 },44	{ DC_COM_PIN_OUTPUT_POLARITY(2), 0x00000000 },45	{ DC_COM_PIN_OUTPUT_POLARITY(3), 0x00000000 },46	{ DC_COM_PIN_OUTPUT_DATA(0),     0x00000000 },47	{ DC_COM_PIN_OUTPUT_DATA(1),     0x00000000 },48	{ DC_COM_PIN_OUTPUT_DATA(2),     0x00000000 },49	{ DC_COM_PIN_OUTPUT_DATA(3),     0x00000000 },50	{ DC_COM_PIN_OUTPUT_SELECT(0),   0x00000000 },51	{ DC_COM_PIN_OUTPUT_SELECT(1),   0x00000000 },52	{ DC_COM_PIN_OUTPUT_SELECT(2),   0x00000000 },53	{ DC_COM_PIN_OUTPUT_SELECT(3),   0x00000000 },54	{ DC_COM_PIN_OUTPUT_SELECT(4),   0x00210222 },55	{ DC_COM_PIN_OUTPUT_SELECT(5),   0x00002200 },56	{ DC_COM_PIN_OUTPUT_SELECT(6),   0x00020000 },57};58 59static const struct reg_entry rgb_disable[] = {60	{ DC_COM_PIN_OUTPUT_SELECT(6),   0x00000000 },61	{ DC_COM_PIN_OUTPUT_SELECT(5),   0x00000000 },62	{ DC_COM_PIN_OUTPUT_SELECT(4),   0x00000000 },63	{ DC_COM_PIN_OUTPUT_SELECT(3),   0x00000000 },64	{ DC_COM_PIN_OUTPUT_SELECT(2),   0x00000000 },65	{ DC_COM_PIN_OUTPUT_SELECT(1),   0x00000000 },66	{ DC_COM_PIN_OUTPUT_SELECT(0),   0x00000000 },67	{ DC_COM_PIN_OUTPUT_DATA(3),     0xaaaaaaaa },68	{ DC_COM_PIN_OUTPUT_DATA(2),     0xaaaaaaaa },69	{ DC_COM_PIN_OUTPUT_DATA(1),     0xaaaaaaaa },70	{ DC_COM_PIN_OUTPUT_DATA(0),     0xaaaaaaaa },71	{ DC_COM_PIN_OUTPUT_POLARITY(3), 0x00000000 },72	{ DC_COM_PIN_OUTPUT_POLARITY(2), 0x00000000 },73	{ DC_COM_PIN_OUTPUT_POLARITY(1), 0x00000000 },74	{ DC_COM_PIN_OUTPUT_POLARITY(0), 0x00000000 },75	{ DC_COM_PIN_OUTPUT_ENABLE(3),   0x55555555 },76	{ DC_COM_PIN_OUTPUT_ENABLE(2),   0x55555555 },77	{ DC_COM_PIN_OUTPUT_ENABLE(1),   0x55150005 },78	{ DC_COM_PIN_OUTPUT_ENABLE(0),   0x55555555 },79};80 81static void tegra_dc_write_regs(struct tegra_dc *dc,82				const struct reg_entry *table,83				unsigned int num)84{85	unsigned int i;86 87	for (i = 0; i < num; i++)88		tegra_dc_writel(dc, table[i].value, table[i].offset);89}90 91static void tegra_rgb_encoder_disable(struct drm_encoder *encoder)92{93	struct tegra_output *output = encoder_to_output(encoder);94	struct tegra_rgb *rgb = to_rgb(output);95 96	tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable));97	tegra_dc_commit(rgb->dc);98}99 100static void tegra_rgb_encoder_enable(struct drm_encoder *encoder)101{102	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;103	struct tegra_output *output = encoder_to_output(encoder);104	struct tegra_rgb *rgb = to_rgb(output);105	u32 value;106 107	tegra_dc_write_regs(rgb->dc, rgb_enable, ARRAY_SIZE(rgb_enable));108 109	value = DE_SELECT_ACTIVE | DE_CONTROL_NORMAL;110	tegra_dc_writel(rgb->dc, value, DC_DISP_DATA_ENABLE_OPTIONS);111 112	/* configure H- and V-sync signal polarities */113	value = tegra_dc_readl(rgb->dc, DC_COM_PIN_OUTPUT_POLARITY(1));114 115	if (mode->flags & DRM_MODE_FLAG_NHSYNC)116		value |= LHS_OUTPUT_POLARITY_LOW;117	else118		value &= ~LHS_OUTPUT_POLARITY_LOW;119 120	if (mode->flags & DRM_MODE_FLAG_NVSYNC)121		value |= LVS_OUTPUT_POLARITY_LOW;122	else123		value &= ~LVS_OUTPUT_POLARITY_LOW;124 125	tegra_dc_writel(rgb->dc, value, DC_COM_PIN_OUTPUT_POLARITY(1));126 127	/* XXX: parameterize? */128	value = DISP_DATA_FORMAT_DF1P1C | DISP_ALIGNMENT_MSB |129		DISP_ORDER_RED_BLUE;130	tegra_dc_writel(rgb->dc, value, DC_DISP_DISP_INTERFACE_CONTROL);131 132	tegra_dc_commit(rgb->dc);133}134 135static bool tegra_rgb_pll_rate_change_allowed(struct tegra_rgb *rgb)136{137	if (!rgb->pll_d2_out0)138		return false;139 140	if (!clk_is_match(rgb->clk_parent, rgb->pll_d_out0) &&141	    !clk_is_match(rgb->clk_parent, rgb->pll_d2_out0))142		return false;143 144	return true;145}146 147static int148tegra_rgb_encoder_atomic_check(struct drm_encoder *encoder,149			       struct drm_crtc_state *crtc_state,150			       struct drm_connector_state *conn_state)151{152	struct tegra_output *output = encoder_to_output(encoder);153	struct tegra_dc *dc = to_tegra_dc(conn_state->crtc);154	unsigned long pclk = crtc_state->mode.clock * 1000;155	struct tegra_rgb *rgb = to_rgb(output);156	unsigned int div;157	int err;158 159	/*160	 * We may not want to change the frequency of the parent clock, since161	 * it may be a parent for other peripherals. This is due to the fact162	 * that on Tegra20 there's only a single clock dedicated to display163	 * (pll_d_out0), whereas later generations have a second one that can164	 * be used to independently drive a second output (pll_d2_out0).165	 *166	 * As a way to support multiple outputs on Tegra20 as well, pll_p is167	 * typically used as the parent clock for the display controllers.168	 * But this comes at a cost: pll_p is the parent of several other169	 * peripherals, so its frequency shouldn't change out of the blue.170	 *171	 * The best we can do at this point is to use the shift clock divider172	 * and hope that the desired frequency can be matched (or at least173	 * matched sufficiently close that the panel will still work).174	 */175	if (tegra_rgb_pll_rate_change_allowed(rgb)) {176		/*177		 * Set display controller clock to x2 of PCLK in order to178		 * produce higher resolution pulse positions.179		 */180		div = 2;181		pclk *= 2;182	} else {183		div = ((clk_get_rate(rgb->clk) * 2) / pclk) - 2;184		pclk = 0;185	}186 187	err = tegra_dc_state_setup_clock(dc, crtc_state, rgb->clk_parent,188					 pclk, div);189	if (err < 0) {190		dev_err(output->dev, "failed to setup CRTC state: %d\n", err);191		return err;192	}193 194	return err;195}196 197static const struct drm_encoder_helper_funcs tegra_rgb_encoder_helper_funcs = {198	.disable = tegra_rgb_encoder_disable,199	.enable = tegra_rgb_encoder_enable,200	.atomic_check = tegra_rgb_encoder_atomic_check,201};202 203int tegra_dc_rgb_probe(struct tegra_dc *dc)204{205	struct device_node *np;206	struct tegra_rgb *rgb;207	int err;208 209	np = of_get_child_by_name(dc->dev->of_node, "rgb");210	if (!np || !of_device_is_available(np))211		return -ENODEV;212 213	rgb = devm_kzalloc(dc->dev, sizeof(*rgb), GFP_KERNEL);214	if (!rgb)215		return -ENOMEM;216 217	rgb->output.dev = dc->dev;218	rgb->output.of_node = np;219	rgb->dc = dc;220 221	err = tegra_output_probe(&rgb->output);222	if (err < 0)223		return err;224 225	rgb->clk = devm_clk_get(dc->dev, NULL);226	if (IS_ERR(rgb->clk)) {227		dev_err(dc->dev, "failed to get clock\n");228		err = PTR_ERR(rgb->clk);229		goto remove;230	}231 232	rgb->clk_parent = devm_clk_get(dc->dev, "parent");233	if (IS_ERR(rgb->clk_parent)) {234		dev_err(dc->dev, "failed to get parent clock\n");235		err = PTR_ERR(rgb->clk_parent);236		goto remove;237	}238 239	err = clk_set_parent(rgb->clk, rgb->clk_parent);240	if (err < 0) {241		dev_err(dc->dev, "failed to set parent clock: %d\n", err);242		goto remove;243	}244 245	rgb->pll_d_out0 = clk_get_sys(NULL, "pll_d_out0");246	if (IS_ERR(rgb->pll_d_out0)) {247		err = PTR_ERR(rgb->pll_d_out0);248		dev_err(dc->dev, "failed to get pll_d_out0: %d\n", err);249		goto remove;250	}251 252	if (dc->soc->has_pll_d2_out0) {253		rgb->pll_d2_out0 = clk_get_sys(NULL, "pll_d2_out0");254		if (IS_ERR(rgb->pll_d2_out0)) {255			err = PTR_ERR(rgb->pll_d2_out0);256			dev_err(dc->dev, "failed to get pll_d2_out0: %d\n", err);257			goto put_pll;258		}259	}260 261	dc->rgb = &rgb->output;262 263	return 0;264 265put_pll:266	clk_put(rgb->pll_d_out0);267remove:268	tegra_output_remove(&rgb->output);269	return err;270}271 272void tegra_dc_rgb_remove(struct tegra_dc *dc)273{274	struct tegra_rgb *rgb;275 276	if (!dc->rgb)277		return;278 279	rgb = to_rgb(dc->rgb);280	clk_put(rgb->pll_d2_out0);281	clk_put(rgb->pll_d_out0);282 283	tegra_output_remove(dc->rgb);284	dc->rgb = NULL;285}286 287int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)288{289	struct tegra_output *output = dc->rgb;290	struct drm_connector *connector;291	int err;292 293	if (!dc->rgb)294		return -ENODEV;295 296	drm_simple_encoder_init(drm, &output->encoder, DRM_MODE_ENCODER_LVDS);297	drm_encoder_helper_add(&output->encoder,298			       &tegra_rgb_encoder_helper_funcs);299 300	/*301	 * Wrap directly-connected panel into DRM bridge in order to let302	 * DRM core to handle panel for us.303	 */304	if (output->panel) {305		output->bridge = devm_drm_panel_bridge_add(output->dev,306							   output->panel);307		if (IS_ERR(output->bridge)) {308			dev_err(output->dev,309				"failed to wrap panel into bridge: %pe\n",310				output->bridge);311			return PTR_ERR(output->bridge);312		}313 314		output->panel = NULL;315	}316 317	/*318	 * Tegra devices that have LVDS panel utilize LVDS encoder bridge319	 * for converting up to 28 LCD LVTTL lanes into 5/4 LVDS lanes that320	 * go to display panel's receiver.321	 *322	 * Encoder usually have a power-down control which needs to be enabled323	 * in order to transmit data to the panel.  Historically devices that324	 * use an older device-tree version didn't model the bridge, assuming325	 * that encoder is turned ON by default, while today's DRM allows us326	 * to model LVDS encoder properly.327	 *328	 * Newer device-trees utilize LVDS encoder bridge, which provides329	 * us with a connector and handles the display panel.330	 *331	 * For older device-trees we wrapped panel into the panel-bridge.332	 */333	if (output->bridge) {334		err = drm_bridge_attach(&output->encoder, output->bridge,335					NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);336		if (err)337			return err;338 339		connector = drm_bridge_connector_init(drm, &output->encoder);340		if (IS_ERR(connector)) {341			dev_err(output->dev,342				"failed to initialize bridge connector: %pe\n",343				connector);344			return PTR_ERR(connector);345		}346 347		drm_connector_attach_encoder(connector, &output->encoder);348	}349 350	err = tegra_output_init(drm, output);351	if (err < 0) {352		dev_err(output->dev, "failed to initialize output: %d\n", err);353		return err;354	}355 356	/*357	 * Other outputs can be attached to either display controller. The RGB358	 * outputs are an exception and work only with their parent display359	 * controller.360	 */361	output->encoder.possible_crtcs = drm_crtc_mask(&dc->base);362 363	return 0;364}365 366int tegra_dc_rgb_exit(struct tegra_dc *dc)367{368	if (dc->rgb)369		tegra_output_exit(dc->rgb);370 371	return 0;372}373