brintos

brintos / linux-shallow public Read only

0
0
Text · 44.5 KiB · a1a2c84 Raw
1582 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2015 Free Electrons4 * Copyright (C) 2015 NextThing Co5 *6 * Maxime Ripard <maxime.ripard@free-electrons.com>7 */8 9#include <linux/component.h>10#include <linux/ioport.h>11#include <linux/media-bus-format.h>12#include <linux/module.h>13#include <linux/of.h>14#include <linux/of_platform.h>15#include <linux/platform_device.h>16#include <linux/regmap.h>17#include <linux/reset.h>18 19#include <drm/drm_atomic_helper.h>20#include <drm/drm_bridge.h>21#include <drm/drm_connector.h>22#include <drm/drm_crtc.h>23#include <drm/drm_encoder.h>24#include <drm/drm_modes.h>25#include <drm/drm_of.h>26#include <drm/drm_panel.h>27#include <drm/drm_print.h>28#include <drm/drm_probe_helper.h>29#include <drm/drm_vblank.h>30 31#include <uapi/drm/drm_mode.h>32 33#include "sun4i_crtc.h"34#include "sun4i_drv.h"35#include "sun4i_lvds.h"36#include "sun4i_rgb.h"37#include "sun4i_tcon.h"38#include "sun6i_mipi_dsi.h"39#include "sun4i_tcon_dclk.h"40#include "sun8i_tcon_top.h"41#include "sunxi_engine.h"42 43static struct drm_connector *sun4i_tcon_get_connector(const struct drm_encoder *encoder)44{45	struct drm_connector *connector;46	struct drm_connector_list_iter iter;47 48	drm_connector_list_iter_begin(encoder->dev, &iter);49	drm_for_each_connector_iter(connector, &iter)50		if (connector->encoder == encoder) {51			drm_connector_list_iter_end(&iter);52			return connector;53		}54	drm_connector_list_iter_end(&iter);55 56	return NULL;57}58 59static int sun4i_tcon_get_pixel_depth(const struct drm_encoder *encoder)60{61	struct drm_connector *connector;62	struct drm_display_info *info;63 64	connector = sun4i_tcon_get_connector(encoder);65	if (!connector)66		return -EINVAL;67 68	info = &connector->display_info;69	if (info->num_bus_formats != 1)70		return -EINVAL;71 72	switch (info->bus_formats[0]) {73	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:74		return 18;75 76	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:77	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:78		return 24;79	}80 81	return -EINVAL;82}83 84static void sun4i_tcon_channel_set_status(struct sun4i_tcon *tcon, int channel,85					  bool enabled)86{87	struct clk *clk;88 89	switch (channel) {90	case 0:91		WARN_ON(!tcon->quirks->has_channel_0);92		regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,93				   SUN4I_TCON0_CTL_TCON_ENABLE,94				   enabled ? SUN4I_TCON0_CTL_TCON_ENABLE : 0);95		clk = tcon->dclk;96		break;97	case 1:98		WARN_ON(!tcon->quirks->has_channel_1);99		regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,100				   SUN4I_TCON1_CTL_TCON_ENABLE,101				   enabled ? SUN4I_TCON1_CTL_TCON_ENABLE : 0);102		clk = tcon->sclk1;103		break;104	default:105		DRM_WARN("Unknown channel... doing nothing\n");106		return;107	}108 109	if (enabled) {110		clk_prepare_enable(clk);111		clk_rate_exclusive_get(clk);112	} else {113		clk_rate_exclusive_put(clk);114		clk_disable_unprepare(clk);115	}116}117 118static void sun4i_tcon_setup_lvds_phy(struct sun4i_tcon *tcon,119				      const struct drm_encoder *encoder)120{121	regmap_write(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,122		     SUN4I_TCON0_LVDS_ANA0_CK_EN |123		     SUN4I_TCON0_LVDS_ANA0_REG_V |124		     SUN4I_TCON0_LVDS_ANA0_REG_C |125		     SUN4I_TCON0_LVDS_ANA0_EN_MB |126		     SUN4I_TCON0_LVDS_ANA0_PD |127		     SUN4I_TCON0_LVDS_ANA0_DCHS);128 129	udelay(2); /* delay at least 1200 ns */130	regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA1_REG,131			   SUN4I_TCON0_LVDS_ANA1_INIT,132			   SUN4I_TCON0_LVDS_ANA1_INIT);133	udelay(1); /* delay at least 120 ns */134	regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA1_REG,135			   SUN4I_TCON0_LVDS_ANA1_UPDATE,136			   SUN4I_TCON0_LVDS_ANA1_UPDATE);137	regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,138			   SUN4I_TCON0_LVDS_ANA0_EN_MB,139			   SUN4I_TCON0_LVDS_ANA0_EN_MB);140}141 142static void sun6i_tcon_setup_lvds_phy(struct sun4i_tcon *tcon,143				      const struct drm_encoder *encoder)144{145	u8 val;146 147	regmap_write(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,148		     SUN6I_TCON0_LVDS_ANA0_C(2) |149		     SUN6I_TCON0_LVDS_ANA0_V(3) |150		     SUN6I_TCON0_LVDS_ANA0_PD(2) |151		     SUN6I_TCON0_LVDS_ANA0_EN_LDO);152	udelay(2);153 154	regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,155			   SUN6I_TCON0_LVDS_ANA0_EN_MB,156			   SUN6I_TCON0_LVDS_ANA0_EN_MB);157	udelay(2);158 159	regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,160			   SUN6I_TCON0_LVDS_ANA0_EN_DRVC,161			   SUN6I_TCON0_LVDS_ANA0_EN_DRVC);162 163	if (sun4i_tcon_get_pixel_depth(encoder) == 18)164		val = 7;165	else166		val = 0xf;167 168	regmap_write_bits(tcon->regs, SUN4I_TCON0_LVDS_ANA0_REG,169			  SUN6I_TCON0_LVDS_ANA0_EN_DRVD(0xf),170			  SUN6I_TCON0_LVDS_ANA0_EN_DRVD(val));171}172 173static void sun4i_tcon_lvds_set_status(struct sun4i_tcon *tcon,174				       const struct drm_encoder *encoder,175				       bool enabled)176{177	if (enabled) {178		regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_IF_REG,179				   SUN4I_TCON0_LVDS_IF_EN,180				   SUN4I_TCON0_LVDS_IF_EN);181		if (tcon->quirks->setup_lvds_phy)182			tcon->quirks->setup_lvds_phy(tcon, encoder);183	} else {184		regmap_update_bits(tcon->regs, SUN4I_TCON0_LVDS_IF_REG,185				   SUN4I_TCON0_LVDS_IF_EN, 0);186	}187}188 189void sun4i_tcon_set_status(struct sun4i_tcon *tcon,190			   const struct drm_encoder *encoder,191			   bool enabled)192{193	bool is_lvds = false;194	int channel;195 196	switch (encoder->encoder_type) {197	case DRM_MODE_ENCODER_LVDS:198		is_lvds = true;199		fallthrough;200	case DRM_MODE_ENCODER_DSI:201	case DRM_MODE_ENCODER_NONE:202		channel = 0;203		break;204	case DRM_MODE_ENCODER_TMDS:205	case DRM_MODE_ENCODER_TVDAC:206		channel = 1;207		break;208	default:209		DRM_DEBUG_DRIVER("Unknown encoder type, doing nothing...\n");210		return;211	}212 213	if (is_lvds && !enabled)214		sun4i_tcon_lvds_set_status(tcon, encoder, false);215 216	regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,217			   SUN4I_TCON_GCTL_TCON_ENABLE,218			   enabled ? SUN4I_TCON_GCTL_TCON_ENABLE : 0);219 220	if (is_lvds && enabled)221		sun4i_tcon_lvds_set_status(tcon, encoder, true);222 223	sun4i_tcon_channel_set_status(tcon, channel, enabled);224}225 226void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable)227{228	u32 mask, val = 0;229 230	DRM_DEBUG_DRIVER("%sabling VBLANK interrupt\n", enable ? "En" : "Dis");231 232	mask = SUN4I_TCON_GINT0_VBLANK_ENABLE(0) |233		SUN4I_TCON_GINT0_VBLANK_ENABLE(1) |234		SUN4I_TCON_GINT0_TCON0_TRI_FINISH_ENABLE;235 236	if (enable)237		val = mask;238 239	regmap_update_bits(tcon->regs, SUN4I_TCON_GINT0_REG, mask, val);240}241EXPORT_SYMBOL(sun4i_tcon_enable_vblank);242 243/*244 * This function is a helper for TCON output muxing. The TCON output245 * muxing control register in earlier SoCs (without the TCON TOP block)246 * are located in TCON0. This helper returns a pointer to TCON0's247 * sun4i_tcon structure, or NULL if not found.248 */249static struct sun4i_tcon *sun4i_get_tcon0(struct drm_device *drm)250{251	struct sun4i_drv *drv = drm->dev_private;252	struct sun4i_tcon *tcon;253 254	list_for_each_entry(tcon, &drv->tcon_list, list)255		if (tcon->id == 0)256			return tcon;257 258	dev_warn(drm->dev,259		 "TCON0 not found, display output muxing may not work\n");260 261	return NULL;262}263 264static void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel,265			       const struct drm_encoder *encoder)266{267	int ret = -ENOTSUPP;268 269	if (tcon->quirks->set_mux)270		ret = tcon->quirks->set_mux(tcon, encoder);271 272	DRM_DEBUG_DRIVER("Muxing encoder %s to CRTC %s: %d\n",273			 encoder->name, encoder->crtc->name, ret);274}275 276static int sun4i_tcon_get_clk_delay(const struct drm_display_mode *mode,277				    int channel)278{279	int delay = mode->vtotal - mode->vdisplay;280 281	if (mode->flags & DRM_MODE_FLAG_INTERLACE)282		delay /= 2;283 284	if (channel == 1)285		delay -= 2;286 287	delay = min(delay, 30);288 289	DRM_DEBUG_DRIVER("TCON %d clock delay %u\n", channel, delay);290 291	return delay;292}293 294static void sun4i_tcon0_mode_set_dithering(struct sun4i_tcon *tcon,295					   const struct drm_connector *connector)296{297	u32 bus_format = 0;298	u32 val = 0;299 300	/* XXX Would this ever happen? */301	if (!connector)302		return;303 304	/*305	 * FIXME: Undocumented bits306	 *307	 * The whole dithering process and these parameters are not308	 * explained in the vendor documents or BSP kernel code.309	 */310	regmap_write(tcon->regs, SUN4I_TCON0_FRM_SEED_PR_REG, 0x11111111);311	regmap_write(tcon->regs, SUN4I_TCON0_FRM_SEED_PG_REG, 0x11111111);312	regmap_write(tcon->regs, SUN4I_TCON0_FRM_SEED_PB_REG, 0x11111111);313	regmap_write(tcon->regs, SUN4I_TCON0_FRM_SEED_LR_REG, 0x11111111);314	regmap_write(tcon->regs, SUN4I_TCON0_FRM_SEED_LG_REG, 0x11111111);315	regmap_write(tcon->regs, SUN4I_TCON0_FRM_SEED_LB_REG, 0x11111111);316	regmap_write(tcon->regs, SUN4I_TCON0_FRM_TBL0_REG, 0x01010000);317	regmap_write(tcon->regs, SUN4I_TCON0_FRM_TBL1_REG, 0x15151111);318	regmap_write(tcon->regs, SUN4I_TCON0_FRM_TBL2_REG, 0x57575555);319	regmap_write(tcon->regs, SUN4I_TCON0_FRM_TBL3_REG, 0x7f7f7777);320 321	/* Do dithering if panel only supports 6 bits per color */322	if (connector->display_info.bpc == 6)323		val |= SUN4I_TCON0_FRM_CTL_EN;324 325	if (connector->display_info.num_bus_formats == 1)326		bus_format = connector->display_info.bus_formats[0];327 328	/* Check the connection format */329	switch (bus_format) {330	case MEDIA_BUS_FMT_RGB565_1X16:331		/* R and B components are only 5 bits deep */332		val |= SUN4I_TCON0_FRM_CTL_MODE_R;333		val |= SUN4I_TCON0_FRM_CTL_MODE_B;334		fallthrough;335	case MEDIA_BUS_FMT_RGB666_1X18:336	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:337		/* Fall through: enable dithering */338		val |= SUN4I_TCON0_FRM_CTL_EN;339		break;340	}341 342	/* Write dithering settings */343	regmap_write(tcon->regs, SUN4I_TCON_FRM_CTL_REG, val);344}345 346static void sun4i_tcon0_mode_set_cpu(struct sun4i_tcon *tcon,347				     const struct drm_encoder *encoder,348				     const struct drm_display_mode *mode)349{350	/* TODO support normal CPU interface modes */351	struct sun6i_dsi *dsi = encoder_to_sun6i_dsi(encoder);352	struct mipi_dsi_device *device = dsi->device;353	u8 bpp = mipi_dsi_pixel_format_to_bpp(device->format);354	u8 lanes = device->lanes;355	u32 block_space, start_delay;356	u32 tcon_div;357 358	/*359	 * dclk is required to run at 1/4 the DSI per-lane bit rate.360	 */361	tcon->dclk_min_div = SUN6I_DSI_TCON_DIV;362	tcon->dclk_max_div = SUN6I_DSI_TCON_DIV;363	clk_set_rate(tcon->dclk, mode->crtc_clock * 1000 * (bpp / lanes)364						  / SUN6I_DSI_TCON_DIV);365 366	/* Set the resolution */367	regmap_write(tcon->regs, SUN4I_TCON0_BASIC0_REG,368		     SUN4I_TCON0_BASIC0_X(mode->crtc_hdisplay) |369		     SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay));370 371	/* Set dithering if needed */372	sun4i_tcon0_mode_set_dithering(tcon, sun4i_tcon_get_connector(encoder));373 374	regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,375			   SUN4I_TCON0_CTL_IF_MASK,376			   SUN4I_TCON0_CTL_IF_8080);377 378	regmap_write(tcon->regs, SUN4I_TCON_ECC_FIFO_REG,379		     SUN4I_TCON_ECC_FIFO_EN);380 381	regmap_write(tcon->regs, SUN4I_TCON0_CPU_IF_REG,382		     SUN4I_TCON0_CPU_IF_MODE_DSI |383		     SUN4I_TCON0_CPU_IF_TRI_FIFO_FLUSH |384		     SUN4I_TCON0_CPU_IF_TRI_FIFO_EN |385		     SUN4I_TCON0_CPU_IF_TRI_EN);386 387	/*388	 * This looks suspicious, but it works...389	 *390	 * The datasheet says that this should be set higher than 20 *391	 * pixel cycle, but it's not clear what a pixel cycle is.392	 */393	regmap_read(tcon->regs, SUN4I_TCON0_DCLK_REG, &tcon_div);394	tcon_div &= GENMASK(6, 0);395	block_space = mode->htotal * bpp / (tcon_div * lanes);396	block_space -= mode->hdisplay + 40;397 398	regmap_write(tcon->regs, SUN4I_TCON0_CPU_TRI0_REG,399		     SUN4I_TCON0_CPU_TRI0_BLOCK_SPACE(block_space) |400		     SUN4I_TCON0_CPU_TRI0_BLOCK_SIZE(mode->hdisplay));401 402	regmap_write(tcon->regs, SUN4I_TCON0_CPU_TRI1_REG,403		     SUN4I_TCON0_CPU_TRI1_BLOCK_NUM(mode->vdisplay));404 405	start_delay = (mode->crtc_vtotal - mode->crtc_vdisplay - 10 - 1);406	start_delay = start_delay * mode->crtc_htotal * 149;407	start_delay = start_delay / (mode->crtc_clock / 1000) / 8;408	regmap_write(tcon->regs, SUN4I_TCON0_CPU_TRI2_REG,409		     SUN4I_TCON0_CPU_TRI2_TRANS_START_SET(10) |410		     SUN4I_TCON0_CPU_TRI2_START_DELAY(start_delay));411 412	/*413	 * The Allwinner BSP has a comment that the period should be414	 * the display clock * 15, but uses an hardcoded 3000...415	 */416	regmap_write(tcon->regs, SUN4I_TCON_SAFE_PERIOD_REG,417		     SUN4I_TCON_SAFE_PERIOD_NUM(3000) |418		     SUN4I_TCON_SAFE_PERIOD_MODE(3));419 420	/* Enable the output on the pins */421	regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG,422		     0xe0000000);423}424 425static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon,426				      const struct drm_encoder *encoder,427				      const struct drm_display_mode *mode)428{429	unsigned int bp;430	u8 clk_delay;431	u32 reg, val = 0;432 433	WARN_ON(!tcon->quirks->has_channel_0);434 435	tcon->dclk_min_div = 7;436	tcon->dclk_max_div = 7;437	clk_set_rate(tcon->dclk, mode->crtc_clock * 1000);438 439	/* Set the resolution */440	regmap_write(tcon->regs, SUN4I_TCON0_BASIC0_REG,441		     SUN4I_TCON0_BASIC0_X(mode->crtc_hdisplay) |442		     SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay));443 444	/* Set dithering if needed */445	sun4i_tcon0_mode_set_dithering(tcon, sun4i_tcon_get_connector(encoder));446 447	/* Adjust clock delay */448	clk_delay = sun4i_tcon_get_clk_delay(mode, 0);449	regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,450			   SUN4I_TCON0_CTL_CLK_DELAY_MASK,451			   SUN4I_TCON0_CTL_CLK_DELAY(clk_delay));452 453	/*454	 * This is called a backporch in the register documentation,455	 * but it really is the back porch + hsync456	 */457	bp = mode->crtc_htotal - mode->crtc_hsync_start;458	DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",459			 mode->crtc_htotal, bp);460 461	/* Set horizontal display timings */462	regmap_write(tcon->regs, SUN4I_TCON0_BASIC1_REG,463		     SUN4I_TCON0_BASIC1_H_TOTAL(mode->htotal) |464		     SUN4I_TCON0_BASIC1_H_BACKPORCH(bp));465 466	/*467	 * This is called a backporch in the register documentation,468	 * but it really is the back porch + hsync469	 */470	bp = mode->crtc_vtotal - mode->crtc_vsync_start;471	DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n",472			 mode->crtc_vtotal, bp);473 474	/* Set vertical display timings */475	regmap_write(tcon->regs, SUN4I_TCON0_BASIC2_REG,476		     SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal * 2) |477		     SUN4I_TCON0_BASIC2_V_BACKPORCH(bp));478 479	reg = SUN4I_TCON0_LVDS_IF_CLK_SEL_TCON0;480	if (sun4i_tcon_get_pixel_depth(encoder) == 24)481		reg |= SUN4I_TCON0_LVDS_IF_BITWIDTH_24BITS;482	else483		reg |= SUN4I_TCON0_LVDS_IF_BITWIDTH_18BITS;484 485	regmap_write(tcon->regs, SUN4I_TCON0_LVDS_IF_REG, reg);486 487	/* Setup the polarity of the various signals */488	if (!(mode->flags & DRM_MODE_FLAG_PHSYNC))489		val |= SUN4I_TCON0_IO_POL_HSYNC_POSITIVE;490 491	if (!(mode->flags & DRM_MODE_FLAG_PVSYNC))492		val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE;493 494	regmap_write(tcon->regs, SUN4I_TCON0_IO_POL_REG, val);495 496	/* Map output pins to channel 0 */497	regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,498			   SUN4I_TCON_GCTL_IOMAP_MASK,499			   SUN4I_TCON_GCTL_IOMAP_TCON0);500 501	/* Enable the output on the pins */502	regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, 0xe0000000);503}504 505static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon,506				     const struct drm_encoder *encoder,507				     const struct drm_display_mode *mode)508{509	struct drm_connector *connector = sun4i_tcon_get_connector(encoder);510	const struct drm_display_info *info = &connector->display_info;511	unsigned int bp, hsync, vsync;512	u8 clk_delay;513	u32 val = 0;514 515	WARN_ON(!tcon->quirks->has_channel_0);516 517	tcon->dclk_min_div = tcon->quirks->dclk_min_div;518	tcon->dclk_max_div = 127;519	clk_set_rate(tcon->dclk, mode->crtc_clock * 1000);520 521	/* Set the resolution */522	regmap_write(tcon->regs, SUN4I_TCON0_BASIC0_REG,523		     SUN4I_TCON0_BASIC0_X(mode->crtc_hdisplay) |524		     SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay));525 526	/* Set dithering if needed */527	sun4i_tcon0_mode_set_dithering(tcon, connector);528 529	/* Adjust clock delay */530	clk_delay = sun4i_tcon_get_clk_delay(mode, 0);531	regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,532			   SUN4I_TCON0_CTL_CLK_DELAY_MASK,533			   SUN4I_TCON0_CTL_CLK_DELAY(clk_delay));534 535	/*536	 * This is called a backporch in the register documentation,537	 * but it really is the back porch + hsync538	 */539	bp = mode->crtc_htotal - mode->crtc_hsync_start;540	DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",541			 mode->crtc_htotal, bp);542 543	/* Set horizontal display timings */544	regmap_write(tcon->regs, SUN4I_TCON0_BASIC1_REG,545		     SUN4I_TCON0_BASIC1_H_TOTAL(mode->crtc_htotal) |546		     SUN4I_TCON0_BASIC1_H_BACKPORCH(bp));547 548	/*549	 * This is called a backporch in the register documentation,550	 * but it really is the back porch + hsync551	 */552	bp = mode->crtc_vtotal - mode->crtc_vsync_start;553	DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n",554			 mode->crtc_vtotal, bp);555 556	/* Set vertical display timings */557	regmap_write(tcon->regs, SUN4I_TCON0_BASIC2_REG,558		     SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal * 2) |559		     SUN4I_TCON0_BASIC2_V_BACKPORCH(bp));560 561	/* Set Hsync and Vsync length */562	hsync = mode->crtc_hsync_end - mode->crtc_hsync_start;563	vsync = mode->crtc_vsync_end - mode->crtc_vsync_start;564	DRM_DEBUG_DRIVER("Setting HSYNC %d, VSYNC %d\n", hsync, vsync);565	regmap_write(tcon->regs, SUN4I_TCON0_BASIC3_REG,566		     SUN4I_TCON0_BASIC3_V_SYNC(vsync) |567		     SUN4I_TCON0_BASIC3_H_SYNC(hsync));568 569	/* Setup the polarity of the various signals */570	if (mode->flags & DRM_MODE_FLAG_PHSYNC)571		val |= SUN4I_TCON0_IO_POL_HSYNC_POSITIVE;572 573	if (mode->flags & DRM_MODE_FLAG_PVSYNC)574		val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE;575 576	if (info->bus_flags & DRM_BUS_FLAG_DE_LOW)577		val |= SUN4I_TCON0_IO_POL_DE_NEGATIVE;578 579	if (info->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)580		val |= SUN4I_TCON0_IO_POL_DCLK_DRIVE_NEGEDGE;581 582	regmap_update_bits(tcon->regs, SUN4I_TCON0_IO_POL_REG,583			   SUN4I_TCON0_IO_POL_HSYNC_POSITIVE |584			   SUN4I_TCON0_IO_POL_VSYNC_POSITIVE |585			   SUN4I_TCON0_IO_POL_DCLK_DRIVE_NEGEDGE |586			   SUN4I_TCON0_IO_POL_DE_NEGATIVE,587			   val);588 589	/* Map output pins to channel 0 */590	regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,591			   SUN4I_TCON_GCTL_IOMAP_MASK,592			   SUN4I_TCON_GCTL_IOMAP_TCON0);593 594	/* Enable the output on the pins */595	regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, 0);596}597 598static void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,599				 const struct drm_display_mode *mode)600{601	unsigned int bp, hsync, vsync, vtotal;602	u8 clk_delay;603	u32 val;604 605	WARN_ON(!tcon->quirks->has_channel_1);606 607	/* Configure the dot clock */608	clk_set_rate(tcon->sclk1, mode->crtc_clock * 1000);609 610	/* Adjust clock delay */611	clk_delay = sun4i_tcon_get_clk_delay(mode, 1);612	regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,613			   SUN4I_TCON1_CTL_CLK_DELAY_MASK,614			   SUN4I_TCON1_CTL_CLK_DELAY(clk_delay));615 616	/* Set interlaced mode */617	if (mode->flags & DRM_MODE_FLAG_INTERLACE)618		val = SUN4I_TCON1_CTL_INTERLACE_ENABLE;619	else620		val = 0;621	regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,622			   SUN4I_TCON1_CTL_INTERLACE_ENABLE,623			   val);624 625	/* Set the input resolution */626	regmap_write(tcon->regs, SUN4I_TCON1_BASIC0_REG,627		     SUN4I_TCON1_BASIC0_X(mode->crtc_hdisplay) |628		     SUN4I_TCON1_BASIC0_Y(mode->crtc_vdisplay));629 630	/* Set the upscaling resolution */631	regmap_write(tcon->regs, SUN4I_TCON1_BASIC1_REG,632		     SUN4I_TCON1_BASIC1_X(mode->crtc_hdisplay) |633		     SUN4I_TCON1_BASIC1_Y(mode->crtc_vdisplay));634 635	/* Set the output resolution */636	regmap_write(tcon->regs, SUN4I_TCON1_BASIC2_REG,637		     SUN4I_TCON1_BASIC2_X(mode->crtc_hdisplay) |638		     SUN4I_TCON1_BASIC2_Y(mode->crtc_vdisplay));639 640	/* Set horizontal display timings */641	bp = mode->crtc_htotal - mode->crtc_hsync_start;642	DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",643			 mode->htotal, bp);644	regmap_write(tcon->regs, SUN4I_TCON1_BASIC3_REG,645		     SUN4I_TCON1_BASIC3_H_TOTAL(mode->crtc_htotal) |646		     SUN4I_TCON1_BASIC3_H_BACKPORCH(bp));647 648	bp = mode->crtc_vtotal - mode->crtc_vsync_start;649	DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n",650			 mode->crtc_vtotal, bp);651 652	/*653	 * The vertical resolution needs to be doubled in all654	 * cases. We could use crtc_vtotal and always multiply by two,655	 * but that leads to a rounding error in interlace when vtotal656	 * is odd.657	 *658	 * This happens with TV's PAL for example, where vtotal will659	 * be 625, crtc_vtotal 312, and thus crtc_vtotal * 2 will be660	 * 624, which apparently confuses the hardware.661	 *662	 * To work around this, we will always use vtotal, and663	 * multiply by two only if we're not in interlace.664	 */665	vtotal = mode->vtotal;666	if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))667		vtotal = vtotal * 2;668 669	/* Set vertical display timings */670	regmap_write(tcon->regs, SUN4I_TCON1_BASIC4_REG,671		     SUN4I_TCON1_BASIC4_V_TOTAL(vtotal) |672		     SUN4I_TCON1_BASIC4_V_BACKPORCH(bp));673 674	/* Set Hsync and Vsync length */675	hsync = mode->crtc_hsync_end - mode->crtc_hsync_start;676	vsync = mode->crtc_vsync_end - mode->crtc_vsync_start;677	DRM_DEBUG_DRIVER("Setting HSYNC %d, VSYNC %d\n", hsync, vsync);678	regmap_write(tcon->regs, SUN4I_TCON1_BASIC5_REG,679		     SUN4I_TCON1_BASIC5_V_SYNC(vsync) |680		     SUN4I_TCON1_BASIC5_H_SYNC(hsync));681 682	/* Setup the polarity of multiple signals */683	if (tcon->quirks->polarity_in_ch0) {684		val = 0;685 686		if (mode->flags & DRM_MODE_FLAG_PHSYNC)687			val |= SUN4I_TCON0_IO_POL_HSYNC_POSITIVE;688 689		if (mode->flags & DRM_MODE_FLAG_PVSYNC)690			val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE;691 692		regmap_write(tcon->regs, SUN4I_TCON0_IO_POL_REG, val);693	} else {694		/* according to vendor driver, this bit must be always set */695		val = SUN4I_TCON1_IO_POL_UNKNOWN;696 697		if (mode->flags & DRM_MODE_FLAG_PHSYNC)698			val |= SUN4I_TCON1_IO_POL_HSYNC_POSITIVE;699 700		if (mode->flags & DRM_MODE_FLAG_PVSYNC)701			val |= SUN4I_TCON1_IO_POL_VSYNC_POSITIVE;702 703		regmap_write(tcon->regs, SUN4I_TCON1_IO_POL_REG, val);704	}705 706	/* Map output pins to channel 1 */707	regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,708			   SUN4I_TCON_GCTL_IOMAP_MASK,709			   SUN4I_TCON_GCTL_IOMAP_TCON1);710}711 712void sun4i_tcon_mode_set(struct sun4i_tcon *tcon,713			 const struct drm_encoder *encoder,714			 const struct drm_display_mode *mode)715{716	switch (encoder->encoder_type) {717	case DRM_MODE_ENCODER_DSI:718		/* DSI is tied to special case of CPU interface */719		sun4i_tcon0_mode_set_cpu(tcon, encoder, mode);720		break;721	case DRM_MODE_ENCODER_LVDS:722		sun4i_tcon0_mode_set_lvds(tcon, encoder, mode);723		break;724	case DRM_MODE_ENCODER_NONE:725		sun4i_tcon0_mode_set_rgb(tcon, encoder, mode);726		sun4i_tcon_set_mux(tcon, 0, encoder);727		break;728	case DRM_MODE_ENCODER_TVDAC:729	case DRM_MODE_ENCODER_TMDS:730		sun4i_tcon1_mode_set(tcon, mode);731		sun4i_tcon_set_mux(tcon, 1, encoder);732		break;733	default:734		DRM_DEBUG_DRIVER("Unknown encoder type, doing nothing...\n");735	}736}737EXPORT_SYMBOL(sun4i_tcon_mode_set);738 739static void sun4i_tcon_finish_page_flip(struct drm_device *dev,740					struct sun4i_crtc *scrtc)741{742	unsigned long flags;743 744	spin_lock_irqsave(&dev->event_lock, flags);745	if (scrtc->event) {746		drm_crtc_send_vblank_event(&scrtc->crtc, scrtc->event);747		drm_crtc_vblank_put(&scrtc->crtc);748		scrtc->event = NULL;749	}750	spin_unlock_irqrestore(&dev->event_lock, flags);751}752 753static irqreturn_t sun4i_tcon_handler(int irq, void *private)754{755	struct sun4i_tcon *tcon = private;756	struct drm_device *drm = tcon->drm;757	struct sun4i_crtc *scrtc = tcon->crtc;758	struct sunxi_engine *engine = scrtc->engine;759	unsigned int status;760 761	regmap_read(tcon->regs, SUN4I_TCON_GINT0_REG, &status);762 763	if (!(status & (SUN4I_TCON_GINT0_VBLANK_INT(0) |764			SUN4I_TCON_GINT0_VBLANK_INT(1) |765			SUN4I_TCON_GINT0_TCON0_TRI_FINISH_INT)))766		return IRQ_NONE;767 768	drm_crtc_handle_vblank(&scrtc->crtc);769	sun4i_tcon_finish_page_flip(drm, scrtc);770 771	/* Acknowledge the interrupt */772	regmap_update_bits(tcon->regs, SUN4I_TCON_GINT0_REG,773			   SUN4I_TCON_GINT0_VBLANK_INT(0) |774			   SUN4I_TCON_GINT0_VBLANK_INT(1) |775			   SUN4I_TCON_GINT0_TCON0_TRI_FINISH_INT,776			   0);777 778	if (engine->ops->vblank_quirk)779		engine->ops->vblank_quirk(engine);780 781	return IRQ_HANDLED;782}783 784static int sun4i_tcon_init_clocks(struct device *dev,785				  struct sun4i_tcon *tcon)786{787	tcon->clk = devm_clk_get_enabled(dev, "ahb");788	if (IS_ERR(tcon->clk)) {789		dev_err(dev, "Couldn't get the TCON bus clock\n");790		return PTR_ERR(tcon->clk);791	}792 793	if (tcon->quirks->has_channel_0) {794		tcon->sclk0 = devm_clk_get_enabled(dev, "tcon-ch0");795		if (IS_ERR(tcon->sclk0)) {796			dev_err(dev, "Couldn't get the TCON channel 0 clock\n");797			return PTR_ERR(tcon->sclk0);798		}799	}800 801	if (tcon->quirks->has_channel_1) {802		tcon->sclk1 = devm_clk_get(dev, "tcon-ch1");803		if (IS_ERR(tcon->sclk1)) {804			dev_err(dev, "Couldn't get the TCON channel 1 clock\n");805			return PTR_ERR(tcon->sclk1);806		}807	}808 809	return 0;810}811 812static int sun4i_tcon_init_irq(struct device *dev,813			       struct sun4i_tcon *tcon)814{815	struct platform_device *pdev = to_platform_device(dev);816	int irq, ret;817 818	irq = platform_get_irq(pdev, 0);819	if (irq < 0)820		return irq;821 822	ret = devm_request_irq(dev, irq, sun4i_tcon_handler, 0,823			       dev_name(dev), tcon);824	if (ret) {825		dev_err(dev, "Couldn't request the IRQ\n");826		return ret;827	}828 829	return 0;830}831 832static const struct regmap_config sun4i_tcon_regmap_config = {833	.reg_bits	= 32,834	.val_bits	= 32,835	.reg_stride	= 4,836	.max_register	= 0x800,837};838 839static int sun4i_tcon_init_regmap(struct device *dev,840				  struct sun4i_tcon *tcon)841{842	struct platform_device *pdev = to_platform_device(dev);843	void __iomem *regs;844 845	regs = devm_platform_ioremap_resource(pdev, 0);846	if (IS_ERR(regs))847		return PTR_ERR(regs);848 849	tcon->regs = devm_regmap_init_mmio(dev, regs,850					   &sun4i_tcon_regmap_config);851	if (IS_ERR(tcon->regs)) {852		dev_err(dev, "Couldn't create the TCON regmap\n");853		return PTR_ERR(tcon->regs);854	}855 856	/* Make sure the TCON is disabled and all IRQs are off */857	regmap_write(tcon->regs, SUN4I_TCON_GCTL_REG, 0);858	regmap_write(tcon->regs, SUN4I_TCON_GINT0_REG, 0);859	regmap_write(tcon->regs, SUN4I_TCON_GINT1_REG, 0);860 861	/* Disable IO lines and set them to tristate */862	regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, ~0);863	regmap_write(tcon->regs, SUN4I_TCON1_IO_TRI_REG, ~0);864 865	return 0;866}867 868/*869 * On SoCs with the old display pipeline design (Display Engine 1.0),870 * the TCON is always tied to just one backend. Hence we can traverse871 * the of_graph upwards to find the backend our tcon is connected to,872 * and take its ID as our own.873 *874 * We can either identify backends from their compatible strings, which875 * means maintaining a large list of them. Or, since the backend is876 * registered and binded before the TCON, we can just go through the877 * list of registered backends and compare the device node.878 *879 * As the structures now store engines instead of backends, here this880 * function in fact searches the corresponding engine, and the ID is881 * requested via the get_id function of the engine.882 */883static struct sunxi_engine *884sun4i_tcon_find_engine_traverse(struct sun4i_drv *drv,885				struct device_node *node,886				u32 port_id)887{888	struct device_node *port, *ep, *remote;889	struct sunxi_engine *engine = ERR_PTR(-EINVAL);890	u32 reg = 0;891 892	port = of_graph_get_port_by_id(node, port_id);893	if (!port)894		return ERR_PTR(-EINVAL);895 896	/*897	 * This only works if there is only one path from the TCON898	 * to any display engine. Otherwise the probe order of the899	 * TCONs and display engines is not guaranteed. They may900	 * either bind to the wrong one, or worse, bind to the same901	 * one if additional checks are not done.902	 *903	 * Bail out if there are multiple input connections.904	 */905	if (of_get_available_child_count(port) != 1)906		goto out_put_port;907 908	/* Get the first connection without specifying an ID */909	ep = of_get_next_available_child(port, NULL);910	if (!ep)911		goto out_put_port;912 913	remote = of_graph_get_remote_port_parent(ep);914	if (!remote)915		goto out_put_ep;916 917	/* does this node match any registered engines? */918	list_for_each_entry(engine, &drv->engine_list, list)919		if (remote == engine->node)920			goto out_put_remote;921 922	/*923	 * According to device tree binding input ports have even id924	 * number and output ports have odd id. Since component with925	 * more than one input and one output (TCON TOP) exits, correct926	 * remote input id has to be calculated by subtracting 1 from927	 * remote output id. If this for some reason can't be done, 0928	 * is used as input port id.929	 */930	of_node_put(port);931	port = of_graph_get_remote_port(ep);932	if (!of_property_read_u32(port, "reg", &reg) && reg > 0)933		reg -= 1;934 935	/* keep looking through upstream ports */936	engine = sun4i_tcon_find_engine_traverse(drv, remote, reg);937 938out_put_remote:939	of_node_put(remote);940out_put_ep:941	of_node_put(ep);942out_put_port:943	of_node_put(port);944 945	return engine;946}947 948/*949 * The device tree binding says that the remote endpoint ID of any950 * connection between components, up to and including the TCON, of951 * the display pipeline should be equal to the actual ID of the local952 * component. Thus we can look at any one of the input connections of953 * the TCONs, and use that connection's remote endpoint ID as our own.954 *955 * Since the user of this function already finds the input port,956 * the port is passed in directly without further checks.957 */958static int sun4i_tcon_of_get_id_from_port(struct device_node *port)959{960	struct device_node *ep;961	int ret = -EINVAL;962 963	/* try finding an upstream endpoint */964	for_each_available_child_of_node(port, ep) {965		struct device_node *remote;966		u32 reg;967 968		remote = of_graph_get_remote_endpoint(ep);969		if (!remote)970			continue;971 972		ret = of_property_read_u32(remote, "reg", &reg);973		if (ret)974			continue;975 976		ret = reg;977	}978 979	return ret;980}981 982/*983 * Once we know the TCON's id, we can look through the list of984 * engines to find a matching one. We assume all engines have985 * been probed and added to the list.986 */987static struct sunxi_engine *sun4i_tcon_get_engine_by_id(struct sun4i_drv *drv,988							int id)989{990	struct sunxi_engine *engine;991 992	list_for_each_entry(engine, &drv->engine_list, list)993		if (engine->id == id)994			return engine;995 996	return ERR_PTR(-EINVAL);997}998 999static bool sun4i_tcon_connected_to_tcon_top(struct device_node *node)1000{1001	struct device_node *remote;1002	bool ret = false;1003 1004	remote = of_graph_get_remote_node(node, 0, -1);1005	if (remote) {1006		ret = !!(IS_ENABLED(CONFIG_DRM_SUN8I_TCON_TOP) &&1007			 of_match_node(sun8i_tcon_top_of_table, remote));1008		of_node_put(remote);1009	}1010 1011	return ret;1012}1013 1014static int sun4i_tcon_get_index(struct sun4i_drv *drv)1015{1016	struct list_head *pos;1017	int size = 0;1018 1019	/*1020	 * Because TCON is added to the list at the end of the probe1021	 * (after this function is called), index of the current TCON1022	 * will be same as current TCON list size.1023	 */1024	list_for_each(pos, &drv->tcon_list)1025		++size;1026 1027	return size;1028}1029 1030/*1031 * On SoCs with the old display pipeline design (Display Engine 1.0),1032 * we assumed the TCON was always tied to just one backend. However1033 * this proved not to be the case. On the A31, the TCON can select1034 * either backend as its source. On the A20 (and likely on the A10),1035 * the backend can choose which TCON to output to.1036 *1037 * The device tree binding says that the remote endpoint ID of any1038 * connection between components, up to and including the TCON, of1039 * the display pipeline should be equal to the actual ID of the local1040 * component. Thus we should be able to look at any one of the input1041 * connections of the TCONs, and use that connection's remote endpoint1042 * ID as our own.1043 *1044 * However  the connections between the backend and TCON were assumed1045 * to be always singular, and their endpoit IDs were all incorrectly1046 * set to 0. This means for these old device trees, we cannot just look1047 * up the remote endpoint ID of a TCON input endpoint. TCON1 would be1048 * incorrectly identified as TCON0.1049 *1050 * This function first checks if the TCON node has 2 input endpoints.1051 * If so, then the device tree is a corrected version, and it will use1052 * sun4i_tcon_of_get_id() and sun4i_tcon_get_engine_by_id() from above1053 * to fetch the ID and engine directly. If not, then it is likely an1054 * old device trees, where the endpoint IDs were incorrect, but did not1055 * have endpoint connections between the backend and TCON across1056 * different display pipelines. It will fall back to the old method of1057 * traversing the  of_graph to try and find a matching engine by device1058 * node.1059 *1060 * In the case of single display pipeline device trees, either method1061 * works.1062 */1063static struct sunxi_engine *sun4i_tcon_find_engine(struct sun4i_drv *drv,1064						   struct device_node *node)1065{1066	struct device_node *port;1067	struct sunxi_engine *engine;1068 1069	port = of_graph_get_port_by_id(node, 0);1070	if (!port)1071		return ERR_PTR(-EINVAL);1072 1073	/*1074	 * Is this a corrected device tree with cross pipeline1075	 * connections between the backend and TCON?1076	 */1077	if (of_get_child_count(port) > 1) {1078		int id;1079 1080		/*1081		 * When pipeline has the same number of TCONs and engines which1082		 * are represented by frontends/backends (DE1) or mixers (DE2),1083		 * we match them by their respective IDs. However, if pipeline1084		 * contains TCON TOP, chances are that there are either more1085		 * TCONs than engines (R40) or TCONs with non-consecutive ids.1086		 * (H6). In that case it's easier just use TCON index in list1087		 * as an id. That means that on R40, any 2 TCONs can be enabled1088		 * in DT out of 4 (there are 2 mixers). Due to the design of1089		 * TCON TOP, remaining 2 TCONs can't be connected to anything1090		 * anyway.1091		 */1092		if (sun4i_tcon_connected_to_tcon_top(node))1093			id = sun4i_tcon_get_index(drv);1094		else1095			id = sun4i_tcon_of_get_id_from_port(port);1096 1097		/* Get our engine by matching our ID */1098		engine = sun4i_tcon_get_engine_by_id(drv, id);1099 1100		of_node_put(port);1101		return engine;1102	}1103 1104	/* Fallback to old method by traversing input endpoints */1105	of_node_put(port);1106	return sun4i_tcon_find_engine_traverse(drv, node, 0);1107}1108 1109static int sun4i_tcon_bind(struct device *dev, struct device *master,1110			   void *data)1111{1112	struct drm_device *drm = data;1113	struct sun4i_drv *drv = drm->dev_private;1114	struct sunxi_engine *engine;1115	struct device_node *remote;1116	struct sun4i_tcon *tcon;1117	struct reset_control *edp_rstc;1118	bool has_lvds_rst, has_lvds_alt, can_lvds;1119	int ret;1120 1121	engine = sun4i_tcon_find_engine(drv, dev->of_node);1122	if (IS_ERR(engine)) {1123		dev_err(dev, "Couldn't find matching engine\n");1124		return -EPROBE_DEFER;1125	}1126 1127	tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL);1128	if (!tcon)1129		return -ENOMEM;1130	dev_set_drvdata(dev, tcon);1131	tcon->drm = drm;1132	tcon->dev = dev;1133	tcon->id = engine->id;1134	tcon->quirks = of_device_get_match_data(dev);1135 1136	tcon->lcd_rst = devm_reset_control_get(dev, "lcd");1137	if (IS_ERR(tcon->lcd_rst)) {1138		dev_err(dev, "Couldn't get our reset line\n");1139		return PTR_ERR(tcon->lcd_rst);1140	}1141 1142	if (tcon->quirks->needs_edp_reset) {1143		edp_rstc = devm_reset_control_get_shared(dev, "edp");1144		if (IS_ERR(edp_rstc)) {1145			dev_err(dev, "Couldn't get edp reset line\n");1146			return PTR_ERR(edp_rstc);1147		}1148 1149		ret = reset_control_deassert(edp_rstc);1150		if (ret) {1151			dev_err(dev, "Couldn't deassert edp reset line\n");1152			return ret;1153		}1154	}1155 1156	/* Make sure our TCON is reset */1157	ret = reset_control_reset(tcon->lcd_rst);1158	if (ret) {1159		dev_err(dev, "Couldn't deassert our reset line\n");1160		return ret;1161	}1162 1163	if (tcon->quirks->supports_lvds) {1164		/*1165		 * This can only be made optional since we've had DT1166		 * nodes without the LVDS reset properties.1167		 *1168		 * If the property is missing, just disable LVDS, and1169		 * print a warning.1170		 */1171		tcon->lvds_rst = devm_reset_control_get_optional(dev, "lvds");1172		if (IS_ERR(tcon->lvds_rst)) {1173			dev_err(dev, "Couldn't get our reset line\n");1174			return PTR_ERR(tcon->lvds_rst);1175		} else if (tcon->lvds_rst) {1176			has_lvds_rst = true;1177			reset_control_reset(tcon->lvds_rst);1178		} else {1179			has_lvds_rst = false;1180		}1181 1182		/*1183		 * This can only be made optional since we've had DT1184		 * nodes without the LVDS reset properties.1185		 *1186		 * If the property is missing, just disable LVDS, and1187		 * print a warning.1188		 */1189		if (tcon->quirks->has_lvds_alt) {1190			tcon->lvds_pll = devm_clk_get(dev, "lvds-alt");1191			if (IS_ERR(tcon->lvds_pll)) {1192				if (PTR_ERR(tcon->lvds_pll) == -ENOENT) {1193					has_lvds_alt = false;1194				} else {1195					dev_err(dev, "Couldn't get the LVDS PLL\n");1196					return PTR_ERR(tcon->lvds_pll);1197				}1198			} else {1199				has_lvds_alt = true;1200			}1201		}1202 1203		if (!has_lvds_rst ||1204		    (tcon->quirks->has_lvds_alt && !has_lvds_alt)) {1205			dev_warn(dev, "Missing LVDS properties, Please upgrade your DT\n");1206			dev_warn(dev, "LVDS output disabled\n");1207			can_lvds = false;1208		} else {1209			can_lvds = true;1210		}1211	} else {1212		can_lvds = false;1213	}1214 1215	ret = sun4i_tcon_init_clocks(dev, tcon);1216	if (ret) {1217		dev_err(dev, "Couldn't init our TCON clocks\n");1218		goto err_assert_reset;1219	}1220 1221	ret = sun4i_tcon_init_regmap(dev, tcon);1222	if (ret) {1223		dev_err(dev, "Couldn't init our TCON regmap\n");1224		goto err_assert_reset;1225	}1226 1227	if (tcon->quirks->has_channel_0) {1228		ret = sun4i_dclk_create(dev, tcon);1229		if (ret) {1230			dev_err(dev, "Couldn't create our TCON dot clock\n");1231			goto err_assert_reset;1232		}1233	}1234 1235	ret = sun4i_tcon_init_irq(dev, tcon);1236	if (ret) {1237		dev_err(dev, "Couldn't init our TCON interrupts\n");1238		goto err_free_dclk;1239	}1240 1241	tcon->crtc = sun4i_crtc_init(drm, engine, tcon);1242	if (IS_ERR(tcon->crtc)) {1243		dev_err(dev, "Couldn't create our CRTC\n");1244		ret = PTR_ERR(tcon->crtc);1245		goto err_free_dclk;1246	}1247 1248	if (tcon->quirks->has_channel_0) {1249		/*1250		 * If we have an LVDS panel connected to the TCON, we should1251		 * just probe the LVDS connector. Otherwise, just probe RGB as1252		 * we used to.1253		 */1254		remote = of_graph_get_remote_node(dev->of_node, 1, 0);1255		if (of_device_is_compatible(remote, "panel-lvds"))1256			if (can_lvds)1257				ret = sun4i_lvds_init(drm, tcon);1258			else1259				ret = -EINVAL;1260		else1261			ret = sun4i_rgb_init(drm, tcon);1262		of_node_put(remote);1263 1264		if (ret < 0)1265			goto err_free_dclk;1266	}1267 1268	if (tcon->quirks->needs_de_be_mux) {1269		/*1270		 * We assume there is no dynamic muxing of backends1271		 * and TCONs, so we select the backend with same ID.1272		 *1273		 * While dynamic selection might be interesting, since1274		 * the CRTC is tied to the TCON, while the layers are1275		 * tied to the backends, this means, we will need to1276		 * switch between groups of layers. There might not be1277		 * a way to represent this constraint in DRM.1278		 */1279		regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,1280				   SUN4I_TCON0_CTL_SRC_SEL_MASK,1281				   tcon->id);1282		regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,1283				   SUN4I_TCON1_CTL_SRC_SEL_MASK,1284				   tcon->id);1285	}1286 1287	list_add_tail(&tcon->list, &drv->tcon_list);1288 1289	return 0;1290 1291err_free_dclk:1292	if (tcon->quirks->has_channel_0)1293		sun4i_dclk_free(tcon);1294err_assert_reset:1295	reset_control_assert(tcon->lcd_rst);1296	return ret;1297}1298 1299static void sun4i_tcon_unbind(struct device *dev, struct device *master,1300			      void *data)1301{1302	struct sun4i_tcon *tcon = dev_get_drvdata(dev);1303 1304	list_del(&tcon->list);1305	if (tcon->quirks->has_channel_0)1306		sun4i_dclk_free(tcon);1307}1308 1309static const struct component_ops sun4i_tcon_ops = {1310	.bind	= sun4i_tcon_bind,1311	.unbind	= sun4i_tcon_unbind,1312};1313 1314static int sun4i_tcon_probe(struct platform_device *pdev)1315{1316	struct device_node *node = pdev->dev.of_node;1317	const struct sun4i_tcon_quirks *quirks;1318	struct drm_bridge *bridge;1319	struct drm_panel *panel;1320	int ret;1321 1322	quirks = of_device_get_match_data(&pdev->dev);1323 1324	/* panels and bridges are present only on TCONs with channel 0 */1325	if (quirks->has_channel_0) {1326		ret = drm_of_find_panel_or_bridge(node, 1, 0, &panel, &bridge);1327		if (ret == -EPROBE_DEFER)1328			return ret;1329	}1330 1331	return component_add(&pdev->dev, &sun4i_tcon_ops);1332}1333 1334static void sun4i_tcon_remove(struct platform_device *pdev)1335{1336	component_del(&pdev->dev, &sun4i_tcon_ops);1337}1338 1339/* platform specific TCON muxing callbacks */1340static int sun4i_a10_tcon_set_mux(struct sun4i_tcon *tcon,1341				  const struct drm_encoder *encoder)1342{1343	struct sun4i_tcon *tcon0 = sun4i_get_tcon0(encoder->dev);1344	u32 shift;1345 1346	if (!tcon0)1347		return -EINVAL;1348 1349	switch (encoder->encoder_type) {1350	case DRM_MODE_ENCODER_TMDS:1351		/* HDMI */1352		shift = 8;1353		break;1354	default:1355		return -EINVAL;1356	}1357 1358	regmap_update_bits(tcon0->regs, SUN4I_TCON_MUX_CTRL_REG,1359			   0x3 << shift, tcon->id << shift);1360 1361	return 0;1362}1363 1364static int sun5i_a13_tcon_set_mux(struct sun4i_tcon *tcon,1365				  const struct drm_encoder *encoder)1366{1367	u32 val;1368 1369	if (encoder->encoder_type == DRM_MODE_ENCODER_TVDAC)1370		val = 1;1371	else1372		val = 0;1373 1374	/*1375	 * FIXME: Undocumented bits1376	 */1377	return regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, val);1378}1379 1380static int sun6i_tcon_set_mux(struct sun4i_tcon *tcon,1381			      const struct drm_encoder *encoder)1382{1383	struct sun4i_tcon *tcon0 = sun4i_get_tcon0(encoder->dev);1384	u32 shift;1385 1386	if (!tcon0)1387		return -EINVAL;1388 1389	switch (encoder->encoder_type) {1390	case DRM_MODE_ENCODER_TMDS:1391		/* HDMI */1392		shift = 8;1393		break;1394	default:1395		/* TODO A31 has MIPI DSI but A31s does not */1396		return -EINVAL;1397	}1398 1399	regmap_update_bits(tcon0->regs, SUN4I_TCON_MUX_CTRL_REG,1400			   0x3 << shift, tcon->id << shift);1401 1402	return 0;1403}1404 1405static int sun8i_r40_tcon_tv_set_mux(struct sun4i_tcon *tcon,1406				     const struct drm_encoder *encoder)1407{1408	struct device_node *port, *remote;1409	struct platform_device *pdev;1410	int id, ret;1411 1412	/* find TCON TOP platform device and TCON id */1413 1414	port = of_graph_get_port_by_id(tcon->dev->of_node, 0);1415	if (!port)1416		return -EINVAL;1417 1418	id = sun4i_tcon_of_get_id_from_port(port);1419	of_node_put(port);1420 1421	remote = of_graph_get_remote_node(tcon->dev->of_node, 0, -1);1422	if (!remote)1423		return -EINVAL;1424 1425	pdev = of_find_device_by_node(remote);1426	of_node_put(remote);1427	if (!pdev)1428		return -EINVAL;1429 1430	if (IS_ENABLED(CONFIG_DRM_SUN8I_TCON_TOP) &&1431	    encoder->encoder_type == DRM_MODE_ENCODER_TMDS) {1432		ret = sun8i_tcon_top_set_hdmi_src(&pdev->dev, id);1433		if (ret) {1434			put_device(&pdev->dev);1435			return ret;1436		}1437	}1438 1439	if (IS_ENABLED(CONFIG_DRM_SUN8I_TCON_TOP)) {1440		ret = sun8i_tcon_top_de_config(&pdev->dev, tcon->id, id);1441		if (ret) {1442			put_device(&pdev->dev);1443			return ret;1444		}1445	}1446 1447	return 0;1448}1449 1450static const struct sun4i_tcon_quirks sun4i_a10_quirks = {1451	.has_channel_0		= true,1452	.has_channel_1		= true,1453	.dclk_min_div		= 4,1454	.set_mux		= sun4i_a10_tcon_set_mux,1455};1456 1457static const struct sun4i_tcon_quirks sun5i_a13_quirks = {1458	.has_channel_0		= true,1459	.has_channel_1		= true,1460	.dclk_min_div		= 4,1461	.set_mux		= sun5i_a13_tcon_set_mux,1462};1463 1464static const struct sun4i_tcon_quirks sun6i_a31_quirks = {1465	.has_channel_0		= true,1466	.has_channel_1		= true,1467	.has_lvds_alt		= true,1468	.needs_de_be_mux	= true,1469	.dclk_min_div		= 1,1470	.set_mux		= sun6i_tcon_set_mux,1471};1472 1473static const struct sun4i_tcon_quirks sun6i_a31s_quirks = {1474	.has_channel_0		= true,1475	.has_channel_1		= true,1476	.needs_de_be_mux	= true,1477	.dclk_min_div		= 1,1478};1479 1480static const struct sun4i_tcon_quirks sun7i_a20_tcon0_quirks = {1481	.supports_lvds		= true,1482	.has_channel_0		= true,1483	.has_channel_1		= true,1484	.dclk_min_div		= 4,1485	/* Same display pipeline structure as A10 */1486	.set_mux		= sun4i_a10_tcon_set_mux,1487	.setup_lvds_phy		= sun4i_tcon_setup_lvds_phy,1488};1489 1490static const struct sun4i_tcon_quirks sun7i_a20_quirks = {1491	.has_channel_0		= true,1492	.has_channel_1		= true,1493	.dclk_min_div		= 4,1494	/* Same display pipeline structure as A10 */1495	.set_mux		= sun4i_a10_tcon_set_mux,1496};1497 1498static const struct sun4i_tcon_quirks sun8i_a33_quirks = {1499	.has_channel_0		= true,1500	.has_lvds_alt		= true,1501	.dclk_min_div		= 1,1502	.setup_lvds_phy		= sun6i_tcon_setup_lvds_phy,1503	.supports_lvds		= true,1504};1505 1506static const struct sun4i_tcon_quirks sun8i_a83t_lcd_quirks = {1507	.supports_lvds		= true,1508	.has_channel_0		= true,1509	.dclk_min_div		= 1,1510	.setup_lvds_phy		= sun6i_tcon_setup_lvds_phy,1511};1512 1513static const struct sun4i_tcon_quirks sun8i_a83t_tv_quirks = {1514	.has_channel_1		= true,1515};1516 1517static const struct sun4i_tcon_quirks sun8i_r40_tv_quirks = {1518	.has_channel_1		= true,1519	.polarity_in_ch0	= true,1520	.set_mux		= sun8i_r40_tcon_tv_set_mux,1521};1522 1523static const struct sun4i_tcon_quirks sun8i_v3s_quirks = {1524	.has_channel_0		= true,1525	.dclk_min_div		= 1,1526};1527 1528static const struct sun4i_tcon_quirks sun9i_a80_tcon_lcd_quirks = {1529	.has_channel_0		= true,1530	.needs_edp_reset	= true,1531	.dclk_min_div		= 1,1532};1533 1534static const struct sun4i_tcon_quirks sun9i_a80_tcon_tv_quirks = {1535	.has_channel_1	= true,1536	.needs_edp_reset = true,1537};1538 1539static const struct sun4i_tcon_quirks sun20i_d1_lcd_quirks = {1540	.has_channel_0		= true,1541	.dclk_min_div		= 1,1542	.set_mux		= sun8i_r40_tcon_tv_set_mux,1543};1544 1545/* sun4i_drv uses this list to check if a device node is a TCON */1546const struct of_device_id sun4i_tcon_of_table[] = {1547	{ .compatible = "allwinner,sun4i-a10-tcon", .data = &sun4i_a10_quirks },1548	{ .compatible = "allwinner,sun5i-a13-tcon", .data = &sun5i_a13_quirks },1549	{ .compatible = "allwinner,sun6i-a31-tcon", .data = &sun6i_a31_quirks },1550	{ .compatible = "allwinner,sun6i-a31s-tcon", .data = &sun6i_a31s_quirks },1551	{ .compatible = "allwinner,sun7i-a20-tcon", .data = &sun7i_a20_quirks },1552	{ .compatible = "allwinner,sun7i-a20-tcon0", .data = &sun7i_a20_tcon0_quirks },1553	{ .compatible = "allwinner,sun7i-a20-tcon1", .data = &sun7i_a20_quirks },1554	{ .compatible = "allwinner,sun8i-a23-tcon", .data = &sun8i_a33_quirks },1555	{ .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks },1556	{ .compatible = "allwinner,sun8i-a83t-tcon-lcd", .data = &sun8i_a83t_lcd_quirks },1557	{ .compatible = "allwinner,sun8i-a83t-tcon-tv", .data = &sun8i_a83t_tv_quirks },1558	{ .compatible = "allwinner,sun8i-r40-tcon-tv", .data = &sun8i_r40_tv_quirks },1559	{ .compatible = "allwinner,sun8i-v3s-tcon", .data = &sun8i_v3s_quirks },1560	{ .compatible = "allwinner,sun9i-a80-tcon-lcd", .data = &sun9i_a80_tcon_lcd_quirks },1561	{ .compatible = "allwinner,sun9i-a80-tcon-tv", .data = &sun9i_a80_tcon_tv_quirks },1562	{ .compatible = "allwinner,sun20i-d1-tcon-lcd", .data = &sun20i_d1_lcd_quirks },1563	{ .compatible = "allwinner,sun20i-d1-tcon-tv", .data = &sun8i_r40_tv_quirks },1564	{ }1565};1566MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table);1567EXPORT_SYMBOL(sun4i_tcon_of_table);1568 1569static struct platform_driver sun4i_tcon_platform_driver = {1570	.probe		= sun4i_tcon_probe,1571	.remove_new	= sun4i_tcon_remove,1572	.driver		= {1573		.name		= "sun4i-tcon",1574		.of_match_table	= sun4i_tcon_of_table,1575	},1576};1577module_platform_driver(sun4i_tcon_platform_driver);1578 1579MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");1580MODULE_DESCRIPTION("Allwinner A10 Timing Controller Driver");1581MODULE_LICENSE("GPL");1582