xref: /linux/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c (revision a1c613ae4c322ddd58d5a8539dbfba2a0380a8c0)
1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21a396789SBoris Brezillon /*
31a396789SBoris Brezillon  * Copyright (C) 2014 Traphandler
41a396789SBoris Brezillon  * Copyright (C) 2014 Free Electrons
51a396789SBoris Brezillon  *
61a396789SBoris Brezillon  * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
71a396789SBoris Brezillon  * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
81a396789SBoris Brezillon  */
91a396789SBoris Brezillon 
101a396789SBoris Brezillon #include <linux/clk.h>
1172bd9ea3SVille Syrjälä #include <linux/media-bus-format.h>
1271866a56SSam Ravnborg #include <linux/mfd/atmel-hlcdc.h>
1371866a56SSam Ravnborg #include <linux/pinctrl/consumer.h>
141a396789SBoris Brezillon #include <linux/pm.h>
151a396789SBoris Brezillon #include <linux/pm_runtime.h>
161a396789SBoris Brezillon 
171a396789SBoris Brezillon #include <video/videomode.h>
181a396789SBoris Brezillon 
1971866a56SSam Ravnborg #include <drm/drm_atomic.h>
2071866a56SSam Ravnborg #include <drm/drm_atomic_helper.h>
2171866a56SSam Ravnborg #include <drm/drm_crtc.h>
2271866a56SSam Ravnborg #include <drm/drm_modeset_helper_vtables.h>
2371866a56SSam Ravnborg #include <drm/drm_probe_helper.h>
2471866a56SSam Ravnborg #include <drm/drm_vblank.h>
2571866a56SSam Ravnborg 
261a396789SBoris Brezillon #include "atmel_hlcdc_dc.h"
271a396789SBoris Brezillon 
281a396789SBoris Brezillon /**
29c2edc1feSLee Jones  * struct atmel_hlcdc_crtc_state - Atmel HLCDC CRTC state structure
30aca63b76SBoris Brezillon  *
31aca63b76SBoris Brezillon  * @base: base CRTC state
32aca63b76SBoris Brezillon  * @output_mode: RGBXXX output mode
33aca63b76SBoris Brezillon  * @dpi: output DPI mode
34aca63b76SBoris Brezillon  */
35aca63b76SBoris Brezillon struct atmel_hlcdc_crtc_state {
36aca63b76SBoris Brezillon 	struct drm_crtc_state base;
37aca63b76SBoris Brezillon 	unsigned int output_mode;
38aca63b76SBoris Brezillon 	u8 dpi;
39aca63b76SBoris Brezillon };
40aca63b76SBoris Brezillon 
41aca63b76SBoris Brezillon static inline struct atmel_hlcdc_crtc_state *
drm_crtc_state_to_atmel_hlcdc_crtc_state(struct drm_crtc_state * state)42aca63b76SBoris Brezillon drm_crtc_state_to_atmel_hlcdc_crtc_state(struct drm_crtc_state *state)
43aca63b76SBoris Brezillon {
44aca63b76SBoris Brezillon 	return container_of(state, struct atmel_hlcdc_crtc_state, base);
45aca63b76SBoris Brezillon }
46c2edc1feSLee Jones 
471a396789SBoris Brezillon /**
481a396789SBoris Brezillon  * struct atmel_hlcdc_crtc - Atmel HLCDC CRTC structure
49c2edc1feSLee Jones  *
501a396789SBoris Brezillon  * @base: base DRM CRTC structure
511a396789SBoris Brezillon  * @dc: pointer to the atmel_hlcdc structure provided by the MFD device
521a396789SBoris Brezillon  * @event: pointer to the current page flip event
531a396789SBoris Brezillon  * @id: CRTC id (returned by drm_crtc_index)
541a396789SBoris Brezillon  */
551a396789SBoris Brezillon struct atmel_hlcdc_crtc {
561a396789SBoris Brezillon 	struct drm_crtc base;
571a396789SBoris Brezillon 	struct atmel_hlcdc_dc *dc;
581a396789SBoris Brezillon 	struct drm_pending_vblank_event *event;
591a396789SBoris Brezillon 	int id;
601a396789SBoris Brezillon };
611a396789SBoris Brezillon 
621a396789SBoris Brezillon static inline struct atmel_hlcdc_crtc *
drm_crtc_to_atmel_hlcdc_crtc(struct drm_crtc * crtc)631a396789SBoris Brezillon drm_crtc_to_atmel_hlcdc_crtc(struct drm_crtc *crtc)
641a396789SBoris Brezillon {
651a396789SBoris Brezillon 	return container_of(crtc, struct atmel_hlcdc_crtc, base);
662389fc13SBoris Brezillon }
671a396789SBoris Brezillon 
atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc * c)681a396789SBoris Brezillon static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)
691a396789SBoris Brezillon {
702389fc13SBoris Brezillon 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
71*d01cb045SMiquel Raynal 	struct regmap *regmap = crtc->dc->hlcdc->regmap;
72*d01cb045SMiquel Raynal 	struct drm_display_mode *adj = &c->state->adjusted_mode;
73aca63b76SBoris Brezillon 	struct drm_encoder *encoder = NULL, *en_iter;
74*d01cb045SMiquel Raynal 	struct drm_connector *connector = NULL;
75*d01cb045SMiquel Raynal 	struct atmel_hlcdc_crtc_state *state;
761a396789SBoris Brezillon 	struct drm_device *ddev = c->dev;
771a396789SBoris Brezillon 	struct drm_connector_list_iter iter;
781a396789SBoris Brezillon 	unsigned long mode_rate;
79a6eca2abSClaudiu Beznea 	struct videomode vm;
80a6eca2abSClaudiu Beznea 	unsigned long prate;
812c1fb9d8SClaudiu Beznea 	unsigned int mask = ATMEL_HLCDC_CLKDIV_MASK | ATMEL_HLCDC_CLKPOL;
822c1fb9d8SClaudiu Beznea 	unsigned int cfg = 0;
83*d01cb045SMiquel Raynal 	int div, ret;
84*d01cb045SMiquel Raynal 
85*d01cb045SMiquel Raynal 	/* get encoder from crtc */
86*d01cb045SMiquel Raynal 	drm_for_each_encoder(en_iter, ddev) {
87*d01cb045SMiquel Raynal 		if (en_iter->crtc == c) {
88*d01cb045SMiquel Raynal 			encoder = en_iter;
89*d01cb045SMiquel Raynal 			break;
90*d01cb045SMiquel Raynal 		}
91*d01cb045SMiquel Raynal 	}
92*d01cb045SMiquel Raynal 
93*d01cb045SMiquel Raynal 	if (encoder) {
94*d01cb045SMiquel Raynal 		/* Get the connector from encoder */
95*d01cb045SMiquel Raynal 		drm_connector_list_iter_begin(ddev, &iter);
96*d01cb045SMiquel Raynal 		drm_for_each_connector_iter(connector, &iter)
97*d01cb045SMiquel Raynal 			if (connector->encoder == encoder)
98*d01cb045SMiquel Raynal 				break;
99*d01cb045SMiquel Raynal 		drm_connector_list_iter_end(&iter);
1002c1fb9d8SClaudiu Beznea 	}
1012c1fb9d8SClaudiu Beznea 
1022c1fb9d8SClaudiu Beznea 	ret = clk_prepare_enable(crtc->dc->hlcdc->sys_clk);
1031a396789SBoris Brezillon 	if (ret)
1041a396789SBoris Brezillon 		return;
1051a396789SBoris Brezillon 
1061a396789SBoris Brezillon 	vm.vfront_porch = adj->crtc_vsync_start - adj->crtc_vdisplay;
1071a396789SBoris Brezillon 	vm.vback_porch = adj->crtc_vtotal - adj->crtc_vsync_end;
1081a396789SBoris Brezillon 	vm.vsync_len = adj->crtc_vsync_end - adj->crtc_vsync_start;
1091a396789SBoris Brezillon 	vm.hfront_porch = adj->crtc_hsync_start - adj->crtc_hdisplay;
1101a396789SBoris Brezillon 	vm.hback_porch = adj->crtc_htotal - adj->crtc_hsync_end;
1111a396789SBoris Brezillon 	vm.hsync_len = adj->crtc_hsync_end - adj->crtc_hsync_start;
1121a396789SBoris Brezillon 
1131a396789SBoris Brezillon 	regmap_write(regmap, ATMEL_HLCDC_CFG(1),
1141a396789SBoris Brezillon 		     (vm.hsync_len - 1) | ((vm.vsync_len - 1) << 16));
1151a396789SBoris Brezillon 
1161a396789SBoris Brezillon 	regmap_write(regmap, ATMEL_HLCDC_CFG(2),
1171a396789SBoris Brezillon 		     (vm.vfront_porch - 1) | (vm.vback_porch << 16));
1181a396789SBoris Brezillon 
1191a396789SBoris Brezillon 	regmap_write(regmap, ATMEL_HLCDC_CFG(3),
1201a396789SBoris Brezillon 		     (vm.hfront_porch - 1) | ((vm.hback_porch - 1) << 16));
1211a396789SBoris Brezillon 
1221a396789SBoris Brezillon 	regmap_write(regmap, ATMEL_HLCDC_CFG(4),
1231a396789SBoris Brezillon 		     (adj->crtc_hdisplay - 1) |
12407acf4baSClaudiu Beznea 		     ((adj->crtc_vdisplay - 1) << 16));
12507acf4baSClaudiu Beznea 
126a6eca2abSClaudiu Beznea 	prate = clk_get_rate(crtc->dc->hlcdc->sys_clk);
12707acf4baSClaudiu Beznea 	mode_rate = adj->crtc_clock * 1000;
128a6eca2abSClaudiu Beznea 	if (!crtc->dc->desc->fixed_clksrc) {
129a6eca2abSClaudiu Beznea 		prate *= 2;
130a6eca2abSClaudiu Beznea 		cfg |= ATMEL_HLCDC_CLKSEL;
1311a396789SBoris Brezillon 		mask |= ATMEL_HLCDC_CLKSEL;
1321a396789SBoris Brezillon 	}
133319711f9SPeter Rosin 
1341a396789SBoris Brezillon 	div = DIV_ROUND_UP(prate, mode_rate);
135319711f9SPeter Rosin 	if (div < 2) {
136319711f9SPeter Rosin 		div = 2;
137319711f9SPeter Rosin 	} else if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK) {
138319711f9SPeter Rosin 		/* The divider ended up too big, try a lower base rate. */
139319711f9SPeter Rosin 		cfg &= ~ATMEL_HLCDC_CLKSEL;
140319711f9SPeter Rosin 		prate /= 2;
141319711f9SPeter Rosin 		div = DIV_ROUND_UP(prate, mode_rate);
1429946a3a9SPeter Rosin 		if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK)
1439946a3a9SPeter Rosin 			div = ATMEL_HLCDC_CLKDIV_MASK;
1449946a3a9SPeter Rosin 	} else {
1459946a3a9SPeter Rosin 		int div_low = prate / mode_rate;
14651a19d15SPeter Rosin 
14751a19d15SPeter Rosin 		if (div_low >= 2 &&
1489946a3a9SPeter Rosin 		    (10 * (prate / div_low - mode_rate) <
1499946a3a9SPeter Rosin 		     (mode_rate - prate / div)))
1509946a3a9SPeter Rosin 			/*
1519946a3a9SPeter Rosin 			 * At least 10 times better when using a higher
1529946a3a9SPeter Rosin 			 * frequency than requested, instead of a lower.
1539946a3a9SPeter Rosin 			 * So, go with that.
154319711f9SPeter Rosin 			 */
1551a396789SBoris Brezillon 			div = div_low;
1561a396789SBoris Brezillon 	}
1571a396789SBoris Brezillon 
158*d01cb045SMiquel Raynal 	cfg |= ATMEL_HLCDC_CLKDIV(div);
159*d01cb045SMiquel Raynal 
160*d01cb045SMiquel Raynal 	if (connector &&
161*d01cb045SMiquel Raynal 	    connector->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
162a6eca2abSClaudiu Beznea 		cfg |= ATMEL_HLCDC_CLKPOL;
1631a396789SBoris Brezillon 
164e1dc68a4SClaudiu Beznea 	regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0), mask, cfg);
165e1dc68a4SClaudiu Beznea 
1661a396789SBoris Brezillon 	state = drm_crtc_state_to_atmel_hlcdc_crtc_state(c->state);
1672389fc13SBoris Brezillon 	cfg = state->output_mode << 8;
1681a396789SBoris Brezillon 
1691a396789SBoris Brezillon 	if (!crtc->dc->desc->is_xlcdc) {
1702389fc13SBoris Brezillon 		if (adj->flags & DRM_MODE_FLAG_NVSYNC)
1711a396789SBoris Brezillon 			cfg |= ATMEL_HLCDC_VSPOL;
1721a396789SBoris Brezillon 
1731a396789SBoris Brezillon 		if (adj->flags & DRM_MODE_FLAG_NHSYNC)
1741a396789SBoris Brezillon 			cfg |= ATMEL_HLCDC_HSPOL;
1751a396789SBoris Brezillon 	} else {
1761a396789SBoris Brezillon 		cfg |= state->dpi << 11;
1771a396789SBoris Brezillon 	}
178aca63b76SBoris Brezillon 
1791a396789SBoris Brezillon 	regmap_update_bits(regmap, ATMEL_HLCDC_CFG(5),
1802c1fb9d8SClaudiu Beznea 			   ATMEL_HLCDC_HSPOL | ATMEL_HLCDC_VSPOL |
1812c1fb9d8SClaudiu Beznea 			   ATMEL_HLCDC_VSPDLYS | ATMEL_HLCDC_VSPDLYE |
1821a396789SBoris Brezillon 			   ATMEL_HLCDC_DISPPOL | ATMEL_HLCDC_DISPDLY |
1831a396789SBoris Brezillon 			   ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO |
184a57bf53eSJose Abreu 			   ATMEL_HLCDC_GUARDTIME_MASK |
185a57bf53eSJose Abreu 			   (crtc->dc->desc->is_xlcdc ? ATMEL_XLCDC_MODE_MASK |
186a57bf53eSJose Abreu 			   ATMEL_XLCDC_DPI : ATMEL_HLCDC_MODE_MASK),
1875ac44c8bSBoris Brezillon 			   cfg);
1885ac44c8bSBoris Brezillon 
1895ac44c8bSBoris Brezillon 	clk_disable_unprepare(crtc->dc->hlcdc->sys_clk);
190a57bf53eSJose Abreu }
1915ac44c8bSBoris Brezillon 
1925ac44c8bSBoris Brezillon static enum drm_mode_status
atmel_hlcdc_crtc_mode_valid(struct drm_crtc * c,const struct drm_display_mode * mode)19364581714SLaurent Pinchart atmel_hlcdc_crtc_mode_valid(struct drm_crtc *c,
194351f950dSMaxime Ripard 			    const struct drm_display_mode *mode)
1951a396789SBoris Brezillon {
1962389fc13SBoris Brezillon 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
1972389fc13SBoris Brezillon 
1982389fc13SBoris Brezillon 	return atmel_hlcdc_dc_mode_valid(crtc->dc, mode);
1992389fc13SBoris Brezillon }
2001a396789SBoris Brezillon 
atmel_hlcdc_crtc_atomic_disable(struct drm_crtc * c,struct drm_atomic_state * state)2012389fc13SBoris Brezillon static void atmel_hlcdc_crtc_atomic_disable(struct drm_crtc *c,
2021a396789SBoris Brezillon 					    struct drm_atomic_state *state)
2032389fc13SBoris Brezillon {
2042389fc13SBoris Brezillon 	struct drm_device *dev = c->dev;
2052389fc13SBoris Brezillon 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
2062389fc13SBoris Brezillon 	struct regmap *regmap = crtc->dc->hlcdc->regmap;
2072389fc13SBoris Brezillon 	unsigned int status;
2082389fc13SBoris Brezillon 
2092389fc13SBoris Brezillon 	drm_crtc_vblank_off(c);
2102389fc13SBoris Brezillon 
2112389fc13SBoris Brezillon 	pm_runtime_get_sync(dev->dev);
2122389fc13SBoris Brezillon 
2132389fc13SBoris Brezillon 	if (crtc->dc->desc->is_xlcdc) {
2142389fc13SBoris Brezillon 		regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_XLCDC_CM);
2152389fc13SBoris Brezillon 		if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
2162389fc13SBoris Brezillon 					     !(status & ATMEL_XLCDC_CM),
2172389fc13SBoris Brezillon 					     10, 1000))
2182389fc13SBoris Brezillon 			dev_warn(dev->dev, "Atmel LCDC status register CMSTS timeout\n");
2192389fc13SBoris Brezillon 
2202389fc13SBoris Brezillon 		regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_XLCDC_SD);
22116e6004eSSylvain Rochet 		if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
2222389fc13SBoris Brezillon 					     status & ATMEL_XLCDC_SD,
2232389fc13SBoris Brezillon 					     10, 1000))
2242389fc13SBoris Brezillon 			dev_warn(dev->dev, "Atmel LCDC status register SDSTS timeout\n");
2252389fc13SBoris Brezillon 	}
2261a396789SBoris Brezillon 
2272389fc13SBoris Brezillon 	regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP);
2280b20a0f8SLaurent Pinchart 	if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
229351f950dSMaxime Ripard 				     !(status & ATMEL_HLCDC_DISP),
2302389fc13SBoris Brezillon 				    10, 1000))
2312389fc13SBoris Brezillon 		dev_warn(dev->dev, "Atmel LCDC status register DISPSTS timeout\n");
2322389fc13SBoris Brezillon 
2332389fc13SBoris Brezillon 	regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC);
2342389fc13SBoris Brezillon 	if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
2352389fc13SBoris Brezillon 				     !(status & ATMEL_HLCDC_SYNC),
2362389fc13SBoris Brezillon 				    10, 1000))
2372389fc13SBoris Brezillon 		dev_warn(dev->dev, "Atmel LCDC status register LCDSTS timeout\n");
2382389fc13SBoris Brezillon 
2392389fc13SBoris Brezillon 	regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PIXEL_CLK);
24016e6004eSSylvain Rochet 	if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
2412389fc13SBoris Brezillon 				     !(status & ATMEL_HLCDC_PIXEL_CLK),
2422389fc13SBoris Brezillon 				    10, 1000))
2432389fc13SBoris Brezillon 		dev_warn(dev->dev, "Atmel LCDC status register CLKSTS timeout\n");
2442389fc13SBoris Brezillon 
2452389fc13SBoris Brezillon 	clk_disable_unprepare(crtc->dc->hlcdc->sys_clk);
2462389fc13SBoris Brezillon 	pinctrl_pm_select_sleep_state(dev->dev);
2472389fc13SBoris Brezillon 
2482389fc13SBoris Brezillon 	pm_runtime_allow(dev->dev);
2492389fc13SBoris Brezillon 
2502389fc13SBoris Brezillon 	pm_runtime_put_sync(dev->dev);
2512389fc13SBoris Brezillon }
2522389fc13SBoris Brezillon 
atmel_hlcdc_crtc_atomic_enable(struct drm_crtc * c,struct drm_atomic_state * state)2532389fc13SBoris Brezillon static void atmel_hlcdc_crtc_atomic_enable(struct drm_crtc *c,
2542389fc13SBoris Brezillon 					   struct drm_atomic_state *state)
2552389fc13SBoris Brezillon {
2562389fc13SBoris Brezillon 	struct drm_device *dev = c->dev;
2572389fc13SBoris Brezillon 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
2582389fc13SBoris Brezillon 	struct regmap *regmap = crtc->dc->hlcdc->regmap;
2592389fc13SBoris Brezillon 	unsigned int status;
2602389fc13SBoris Brezillon 
261f026eb6eSSylvain Rochet 	pm_runtime_get_sync(dev->dev);
262f026eb6eSSylvain Rochet 
263aca63b76SBoris Brezillon 	pm_runtime_forbid(dev->dev);
264aca63b76SBoris Brezillon 
265aca63b76SBoris Brezillon 	pinctrl_pm_select_default_state(dev->dev);
266aca63b76SBoris Brezillon 	clk_prepare_enable(crtc->dc->hlcdc->sys_clk);
267aca63b76SBoris Brezillon 
268aca63b76SBoris Brezillon 	regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PIXEL_CLK);
269b6e075c3SPeter Rosin 	if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
270aca63b76SBoris Brezillon 				     status & ATMEL_HLCDC_PIXEL_CLK,
271b6e075c3SPeter Rosin 				     10, 1000))
272aca63b76SBoris Brezillon 		dev_warn(dev->dev, "Atmel LCDC status register CLKSTS timeout\n");
273b6e075c3SPeter Rosin 
274aca63b76SBoris Brezillon 	regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC);
275aca63b76SBoris Brezillon 	if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
276aca63b76SBoris Brezillon 				     status & ATMEL_HLCDC_SYNC,
277b6e075c3SPeter Rosin 				     10, 1000))
278b6e075c3SPeter Rosin 		dev_warn(dev->dev, "Atmel LCDC status register LCDSTS timeout\n");
279b6e075c3SPeter Rosin 
280b6e075c3SPeter Rosin 	regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP);
281b6e075c3SPeter Rosin 	if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
282b6e075c3SPeter Rosin 				     status & ATMEL_HLCDC_DISP,
283b6e075c3SPeter Rosin 				     10, 1000))
284b6e075c3SPeter Rosin 		dev_warn(dev->dev, "Atmel LCDC status register DISPSTS timeout\n");
285b6e075c3SPeter Rosin 
286b6e075c3SPeter Rosin 	if (crtc->dc->desc->is_xlcdc) {
287b6e075c3SPeter Rosin 		regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_XLCDC_CM);
288b6e075c3SPeter Rosin 		if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
289b6e075c3SPeter Rosin 					     status & ATMEL_XLCDC_CM,
290b6e075c3SPeter Rosin 					     10, 1000))
291b6e075c3SPeter Rosin 			dev_warn(dev->dev, "Atmel LCDC status register CMSTS timeout\n");
292b6e075c3SPeter Rosin 
293b6e075c3SPeter Rosin 		regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_XLCDC_SD);
294b6e075c3SPeter Rosin 		if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
295aca63b76SBoris Brezillon 					     !(status & ATMEL_XLCDC_SD),
296aca63b76SBoris Brezillon 					     10, 1000))
297aca63b76SBoris Brezillon 			dev_warn(dev->dev, "Atmel LCDC status register SDSTS timeout\n");
298aca63b76SBoris Brezillon 	}
299aca63b76SBoris Brezillon 
300aca63b76SBoris Brezillon 	pm_runtime_put_sync(dev->dev);
301aca63b76SBoris Brezillon 
302aca63b76SBoris Brezillon }
303aca63b76SBoris Brezillon 
304aca63b76SBoris Brezillon #define ATMEL_HLCDC_RGB444_OUTPUT		BIT(0)
305aca63b76SBoris Brezillon #define ATMEL_HLCDC_RGB565_OUTPUT		BIT(1)
306aca63b76SBoris Brezillon #define ATMEL_HLCDC_RGB666_OUTPUT		BIT(2)
307aca63b76SBoris Brezillon #define ATMEL_HLCDC_RGB888_OUTPUT		BIT(3)
308aca63b76SBoris Brezillon #define ATMEL_HLCDC_DPI_RGB565C1_OUTPUT		BIT(4)
309aca63b76SBoris Brezillon #define ATMEL_HLCDC_DPI_RGB565C2_OUTPUT		BIT(5)
310aca63b76SBoris Brezillon #define ATMEL_HLCDC_DPI_RGB565C3_OUTPUT		BIT(6)
311aca63b76SBoris Brezillon #define ATMEL_HLCDC_DPI_RGB666C1_OUTPUT		BIT(7)
312aca63b76SBoris Brezillon #define ATMEL_HLCDC_DPI_RGB666C2_OUTPUT		BIT(8)
313aca63b76SBoris Brezillon #define ATMEL_HLCDC_DPI_RGB888_OUTPUT		BIT(9)
314aca63b76SBoris Brezillon #define ATMEL_HLCDC_OUTPUT_MODE_MASK		GENMASK(3, 0)
315b6e075c3SPeter Rosin #define ATMEL_XLCDC_OUTPUT_MODE_MASK		GENMASK(9, 0)
316b6e075c3SPeter Rosin 
atmel_xlcdc_connector_output_dsi(struct drm_encoder * encoder,struct drm_display_info * info)317b6e075c3SPeter Rosin static int atmel_xlcdc_connector_output_dsi(struct drm_encoder *encoder,
318b6e075c3SPeter Rosin 					    struct drm_display_info *info)
319b6e075c3SPeter Rosin {
320b6e075c3SPeter Rosin 	int j;
321b6e075c3SPeter Rosin 	unsigned int supported_fmts = 0;
322b6e075c3SPeter Rosin 
323b6e075c3SPeter Rosin 	switch (atmel_hlcdc_encoder_get_bus_fmt(encoder)) {
324b6e075c3SPeter Rosin 	case 0:
325b6e075c3SPeter Rosin 		break;
326b6e075c3SPeter Rosin 	case MEDIA_BUS_FMT_RGB565_1X16:
327b6e075c3SPeter Rosin 		return ATMEL_HLCDC_DPI_RGB565C1_OUTPUT;
328b6e075c3SPeter Rosin 	case MEDIA_BUS_FMT_RGB666_1X18:
329b6e075c3SPeter Rosin 		return ATMEL_HLCDC_DPI_RGB666C1_OUTPUT;
330b6e075c3SPeter Rosin 	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
331b6e075c3SPeter Rosin 		return ATMEL_HLCDC_DPI_RGB666C2_OUTPUT;
332b6e075c3SPeter Rosin 	case MEDIA_BUS_FMT_RGB888_1X24:
333b6e075c3SPeter Rosin 		return ATMEL_HLCDC_DPI_RGB888_OUTPUT;
334b6e075c3SPeter Rosin 	default:
335b6e075c3SPeter Rosin 		return -EINVAL;
336b6e075c3SPeter Rosin 	}
337aca63b76SBoris Brezillon 
338aca63b76SBoris Brezillon 	for (j = 0; j < info->num_bus_formats; j++) {
339aca63b76SBoris Brezillon 		switch (info->bus_formats[j]) {
340aca63b76SBoris Brezillon 		case MEDIA_BUS_FMT_RGB565_1X16:
341aca63b76SBoris Brezillon 			supported_fmts |= ATMEL_HLCDC_DPI_RGB565C1_OUTPUT;
342aca63b76SBoris Brezillon 			break;
343aca63b76SBoris Brezillon 		case MEDIA_BUS_FMT_RGB666_1X18:
344aca63b76SBoris Brezillon 			supported_fmts |= ATMEL_HLCDC_DPI_RGB666C1_OUTPUT;
345aca63b76SBoris Brezillon 			break;
346aca63b76SBoris Brezillon 		case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
347aca63b76SBoris Brezillon 			supported_fmts |= ATMEL_HLCDC_DPI_RGB666C2_OUTPUT;
348aca63b76SBoris Brezillon 			break;
349aca63b76SBoris Brezillon 		case MEDIA_BUS_FMT_RGB888_1X24:
350aca63b76SBoris Brezillon 			supported_fmts |= ATMEL_HLCDC_DPI_RGB888_OUTPUT;
351aca63b76SBoris Brezillon 			break;
3522389fc13SBoris Brezillon 		default:
35329b77ad7SMaxime Ripard 			break;
3542389fc13SBoris Brezillon 		}
35529b77ad7SMaxime Ripard 	}
356aca63b76SBoris Brezillon 	return supported_fmts;
3572389fc13SBoris Brezillon }
358aca63b76SBoris Brezillon 
atmel_hlcdc_connector_output_mode(struct drm_connector_state * state)359aca63b76SBoris Brezillon static int atmel_hlcdc_connector_output_mode(struct drm_connector_state *state)
360aca63b76SBoris Brezillon {
361aca63b76SBoris Brezillon 	struct drm_connector *connector = state->connector;
362ebab87abSBoris Brezillon 	struct drm_display_info *info = &connector->display_info;
363ebab87abSBoris Brezillon 	struct drm_encoder *encoder;
364ebab87abSBoris Brezillon 	unsigned int supported_fmts = 0;
365ebab87abSBoris Brezillon 	int j;
366ebab87abSBoris Brezillon 
3672389fc13SBoris Brezillon 	encoder = state->best_encoder;
3682389fc13SBoris Brezillon 	if (!encoder)
369613d2b27SMaarten Lankhorst 		encoder = connector->encoder;
370f6ebe9f9SMaxime Ripard 	/*
3712389fc13SBoris Brezillon 	 * atmel-hlcdc to support DSI formats with DSI video pipeline
372e484028bSDan Sneddon 	 * when DRM_MODE_ENCODER_DSI type is set by
373e484028bSDan Sneddon 	 * connector driver component.
374e484028bSDan Sneddon 	 */
375e484028bSDan Sneddon 	if (encoder->encoder_type == DRM_MODE_ENCODER_DSI)
376e484028bSDan Sneddon 		return atmel_xlcdc_connector_output_dsi(encoder, info);
377e484028bSDan Sneddon 
3782389fc13SBoris Brezillon 	switch (atmel_hlcdc_encoder_get_bus_fmt(encoder)) {
379e484028bSDan Sneddon 	case 0:
380e484028bSDan Sneddon 		break;
381e484028bSDan Sneddon 	case MEDIA_BUS_FMT_RGB444_1X12:
3822389fc13SBoris Brezillon 		return ATMEL_HLCDC_RGB444_OUTPUT;
3832389fc13SBoris Brezillon 	case MEDIA_BUS_FMT_RGB565_1X16:
3842389fc13SBoris Brezillon 		return ATMEL_HLCDC_RGB565_OUTPUT;
3852389fc13SBoris Brezillon 	case MEDIA_BUS_FMT_RGB666_1X18:
3862389fc13SBoris Brezillon 		return ATMEL_HLCDC_RGB666_OUTPUT;
3872389fc13SBoris Brezillon 	case MEDIA_BUS_FMT_RGB888_1X24:
3882389fc13SBoris Brezillon 		return ATMEL_HLCDC_RGB888_OUTPUT;
3892389fc13SBoris Brezillon 	default:
3902389fc13SBoris Brezillon 		return -EINVAL;
391e484028bSDan Sneddon 	}
3921a396789SBoris Brezillon 
3931a396789SBoris Brezillon 	for (j = 0; j < info->num_bus_formats; j++) {
3941a396789SBoris Brezillon 		switch (info->bus_formats[j]) {
395a57bf53eSJose Abreu 		case MEDIA_BUS_FMT_RGB444_1X12:
3962389fc13SBoris Brezillon 			supported_fmts |= ATMEL_HLCDC_RGB444_OUTPUT;
3972389fc13SBoris Brezillon 			break;
3982389fc13SBoris Brezillon 		case MEDIA_BUS_FMT_RGB565_1X16:
3992389fc13SBoris Brezillon 			supported_fmts |= ATMEL_HLCDC_RGB565_OUTPUT;
4000b20a0f8SLaurent Pinchart 			break;
40164581714SLaurent Pinchart 		case MEDIA_BUS_FMT_RGB666_1X18:
4021a396789SBoris Brezillon 			supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT;
4031a396789SBoris Brezillon 			break;
4041a396789SBoris Brezillon 		case MEDIA_BUS_FMT_RGB888_1X24:
4051a396789SBoris Brezillon 			supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT;
4061a396789SBoris Brezillon 			break;
4071a396789SBoris Brezillon 		default:
4081a396789SBoris Brezillon 			break;
4091a396789SBoris Brezillon 		}
4101a396789SBoris Brezillon 	}
4111a396789SBoris Brezillon 
4121a396789SBoris Brezillon 	return supported_fmts;
4131a396789SBoris Brezillon }
4141a396789SBoris Brezillon 
atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state * state)4151a396789SBoris Brezillon static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state)
4161a396789SBoris Brezillon {
4171a396789SBoris Brezillon 	unsigned int output_fmts;
4181a396789SBoris Brezillon 	struct atmel_hlcdc_crtc_state *hstate;
41981767317SGustavo Padovan 	struct drm_connector_state *cstate;
42023a25ed3SGustavo Padovan 	struct drm_connector *connector;
4211a396789SBoris Brezillon 	struct atmel_hlcdc_crtc *crtc;
4221a396789SBoris Brezillon 	int i;
4231a396789SBoris Brezillon 
4241a396789SBoris Brezillon 	crtc = drm_crtc_to_atmel_hlcdc_crtc(state->crtc);
4251a396789SBoris Brezillon 	output_fmts = crtc->dc->desc->is_xlcdc ? ATMEL_XLCDC_OUTPUT_MODE_MASK :
4261a396789SBoris Brezillon 		      ATMEL_HLCDC_OUTPUT_MODE_MASK;
4271a396789SBoris Brezillon 
428548ebe1eSGustavo Padovan 	for_each_new_connector_in_state(state->state, connector, cstate, i) {
4291a396789SBoris Brezillon 		unsigned int supported_fmts = 0;
4301a396789SBoris Brezillon 
4311a396789SBoris Brezillon 		if (!cstate->crtc)
4321ba7db07SThierry Reding 			continue;
433aca63b76SBoris Brezillon 
434aca63b76SBoris Brezillon 		supported_fmts = atmel_hlcdc_connector_output_mode(cstate);
435aca63b76SBoris Brezillon 
436aca63b76SBoris Brezillon 		if (crtc->dc->desc->conflicting_output_formats)
437c2e4c994SBoris Brezillon 			output_fmts &= supported_fmts;
438aca63b76SBoris Brezillon 		else
439aca63b76SBoris Brezillon 			output_fmts |= supported_fmts;
440c2e4c994SBoris Brezillon 	}
441aca63b76SBoris Brezillon 
442aca63b76SBoris Brezillon 	if (!output_fmts)
443aca63b76SBoris Brezillon 		return -EINVAL;
44451f644b4SDaniel Vetter 
44551f644b4SDaniel Vetter 	hstate = drm_crtc_state_to_atmel_hlcdc_crtc_state(state);
446aca63b76SBoris Brezillon 	hstate->output_mode = fls(output_fmts) - 1;
447aca63b76SBoris Brezillon 	if (crtc->dc->desc->is_xlcdc) {
448aca63b76SBoris Brezillon 		/* check if MIPI DPI bit needs to be set */
449aca63b76SBoris Brezillon 		if (fls(output_fmts) > 3) {
450aca63b76SBoris Brezillon 			hstate->output_mode -= 4;
451aca63b76SBoris Brezillon 			hstate->dpi = 1;
452aca63b76SBoris Brezillon 		} else {
453aca63b76SBoris Brezillon 			hstate->dpi = 0;
454aca63b76SBoris Brezillon 		}
455aca63b76SBoris Brezillon 	}
456aca63b76SBoris Brezillon 	return 0;
45758a2ab3aSDan Carpenter }
45858a2ab3aSDan Carpenter 
atmel_hlcdc_crtc_atomic_check(struct drm_crtc * c,struct drm_atomic_state * state)459aca63b76SBoris Brezillon static int atmel_hlcdc_crtc_atomic_check(struct drm_crtc *c,
460aca63b76SBoris Brezillon 					 struct drm_atomic_state *state)
461aca63b76SBoris Brezillon {
462aca63b76SBoris Brezillon 	struct drm_crtc_state *s = drm_atomic_get_new_crtc_state(state, c);
463aca63b76SBoris Brezillon 	int ret;
464aca63b76SBoris Brezillon 
465aca63b76SBoris Brezillon 	ret = atmel_hlcdc_crtc_select_output_mode(s);
466aca63b76SBoris Brezillon 	if (ret)
467aca63b76SBoris Brezillon 		return ret;
468aca63b76SBoris Brezillon 
469aca63b76SBoris Brezillon 	ret = atmel_hlcdc_plane_prepare_disc_area(s);
470aca63b76SBoris Brezillon 	if (ret)
471aca63b76SBoris Brezillon 		return ret;
472aca63b76SBoris Brezillon 
473ec2dc6a0SDaniel Vetter 	return atmel_hlcdc_plane_prepare_ahb_routing(s);
474aca63b76SBoris Brezillon }
475aca63b76SBoris Brezillon 
atmel_hlcdc_crtc_atomic_begin(struct drm_crtc * c,struct drm_atomic_state * state)476aca63b76SBoris Brezillon static void atmel_hlcdc_crtc_atomic_begin(struct drm_crtc *c,
47782308e27SShawn Guo 					  struct drm_atomic_state *state)
47882308e27SShawn Guo {
47982308e27SShawn Guo 	drm_crtc_vblank_on(c);
48082308e27SShawn Guo }
48182308e27SShawn Guo 
atmel_hlcdc_crtc_atomic_flush(struct drm_crtc * c,struct drm_atomic_state * state)48282308e27SShawn Guo static void atmel_hlcdc_crtc_atomic_flush(struct drm_crtc *c,
48382308e27SShawn Guo 					  struct drm_atomic_state *state)
48482308e27SShawn Guo {
48582308e27SShawn Guo 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
48682308e27SShawn Guo 	unsigned long flags;
48782308e27SShawn Guo 
48882308e27SShawn Guo 	spin_lock_irqsave(&c->dev->event_lock, flags);
48982308e27SShawn Guo 
49082308e27SShawn Guo 	if (c->state->event) {
49182308e27SShawn Guo 		c->state->event->pipe = drm_crtc_index(c);
49282308e27SShawn Guo 
49382308e27SShawn Guo 		WARN_ON(drm_crtc_vblank_get(c) != 0);
49482308e27SShawn Guo 
49582308e27SShawn Guo 		crtc->event = c->state->event;
4961a396789SBoris Brezillon 		c->state->event = NULL;
4972389fc13SBoris Brezillon 	}
4982389fc13SBoris Brezillon 	spin_unlock_irqrestore(&c->dev->event_lock, flags);
4991a396789SBoris Brezillon }
500aca63b76SBoris Brezillon 
501aca63b76SBoris Brezillon static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = {
502aca63b76SBoris Brezillon 	.mode_valid = atmel_hlcdc_crtc_mode_valid,
50382308e27SShawn Guo 	.mode_set_nofb = atmel_hlcdc_crtc_mode_set_nofb,
50482308e27SShawn Guo 	.atomic_check = atmel_hlcdc_crtc_atomic_check,
5051a396789SBoris Brezillon 	.atomic_begin = atmel_hlcdc_crtc_atomic_begin,
5061a396789SBoris Brezillon 	.atomic_flush = atmel_hlcdc_crtc_atomic_flush,
5071a396789SBoris Brezillon 	.atomic_enable = atmel_hlcdc_crtc_atomic_enable,
5081a396789SBoris Brezillon 	.atomic_disable = atmel_hlcdc_crtc_atomic_disable,
5099a45d33cSBoris Brezillon };
5101a396789SBoris Brezillon 
atmel_hlcdc_crtc_destroy(struct drm_crtc * c)5111a396789SBoris Brezillon static void atmel_hlcdc_crtc_destroy(struct drm_crtc *c)
5121a396789SBoris Brezillon {
5131a396789SBoris Brezillon 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
5141a396789SBoris Brezillon 
5151a396789SBoris Brezillon 	drm_crtc_cleanup(c);
5161a396789SBoris Brezillon 	kfree(crtc);
5171a396789SBoris Brezillon }
5181a396789SBoris Brezillon 
atmel_hlcdc_crtc_finish_page_flip(struct atmel_hlcdc_crtc * crtc)5191a396789SBoris Brezillon static void atmel_hlcdc_crtc_finish_page_flip(struct atmel_hlcdc_crtc *crtc)
5201a396789SBoris Brezillon {
5219a45d33cSBoris Brezillon 	struct drm_device *dev = crtc->base.dev;
5229a45d33cSBoris Brezillon 	unsigned long flags;
5239a45d33cSBoris Brezillon 
5249a45d33cSBoris Brezillon 	spin_lock_irqsave(&dev->event_lock, flags);
5259a45d33cSBoris Brezillon 	if (crtc->event) {
5269a45d33cSBoris Brezillon 		drm_crtc_send_vblank_event(&crtc->base, crtc->event);
5279a45d33cSBoris Brezillon 		drm_crtc_vblank_put(&crtc->base);
5289a45d33cSBoris Brezillon 		crtc->event = NULL;
5299a45d33cSBoris Brezillon 	}
5309a45d33cSBoris Brezillon 	spin_unlock_irqrestore(&dev->event_lock, flags);
5319a45d33cSBoris Brezillon }
5329a45d33cSBoris Brezillon 
atmel_hlcdc_crtc_irq(struct drm_crtc * c)5339a45d33cSBoris Brezillon void atmel_hlcdc_crtc_irq(struct drm_crtc *c)
5349a45d33cSBoris Brezillon {
5359a45d33cSBoris Brezillon 	drm_crtc_handle_vblank(c);
5369a45d33cSBoris Brezillon 	atmel_hlcdc_crtc_finish_page_flip(drm_crtc_to_atmel_hlcdc_crtc(c));
5379a45d33cSBoris Brezillon }
5389a45d33cSBoris Brezillon 
atmel_hlcdc_crtc_reset(struct drm_crtc * crtc)5399a45d33cSBoris Brezillon static void atmel_hlcdc_crtc_reset(struct drm_crtc *crtc)
5409a45d33cSBoris Brezillon {
5419a45d33cSBoris Brezillon 	struct atmel_hlcdc_crtc_state *state;
5421a396789SBoris Brezillon 
5431a396789SBoris Brezillon 	if (crtc->state) {
5441a396789SBoris Brezillon 		__drm_atomic_helper_crtc_destroy_state(crtc->state);
5451a396789SBoris Brezillon 		state = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state);
5461a396789SBoris Brezillon 		kfree(state);
5479a45d33cSBoris Brezillon 		crtc->state = NULL;
5489a45d33cSBoris Brezillon 	}
5491a396789SBoris Brezillon 
5509a45d33cSBoris Brezillon 	state = kzalloc(sizeof(*state), GFP_KERNEL);
5519a45d33cSBoris Brezillon 	if (state)
5529a45d33cSBoris Brezillon 		__drm_atomic_helper_crtc_reset(crtc, &state->base);
5539a45d33cSBoris Brezillon }
5549a45d33cSBoris Brezillon 
5559a45d33cSBoris Brezillon static struct drm_crtc_state *
atmel_hlcdc_crtc_duplicate_state(struct drm_crtc * crtc)5561a396789SBoris Brezillon atmel_hlcdc_crtc_duplicate_state(struct drm_crtc *crtc)
5571a396789SBoris Brezillon {
5581a396789SBoris Brezillon 	struct atmel_hlcdc_crtc_state *state, *cur;
559364a7bf5SPeter Rosin 	struct atmel_hlcdc_crtc *c = drm_crtc_to_atmel_hlcdc_crtc(crtc);
560364a7bf5SPeter Rosin 
561364a7bf5SPeter Rosin 	if (WARN_ON(!crtc->state))
562364a7bf5SPeter Rosin 		return NULL;
5631a396789SBoris Brezillon 
5641a396789SBoris Brezillon 	state = kmalloc(sizeof(*state), GFP_KERNEL);
5651a396789SBoris Brezillon 	if (!state)
5661a396789SBoris Brezillon 		return NULL;
5671a396789SBoris Brezillon 	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
5681a396789SBoris Brezillon 
5691a396789SBoris Brezillon 	cur = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state);
5701a396789SBoris Brezillon 	state->output_mode = cur->output_mode;
571 	if (c->dc->desc->is_xlcdc)
572 		state->dpi = cur->dpi;
573 
574 	return &state->base;
575 }
576 
atmel_hlcdc_crtc_destroy_state(struct drm_crtc * crtc,struct drm_crtc_state * s)577 static void atmel_hlcdc_crtc_destroy_state(struct drm_crtc *crtc,
578 					   struct drm_crtc_state *s)
579 {
580 	struct atmel_hlcdc_crtc_state *state;
581 
582 	state = drm_crtc_state_to_atmel_hlcdc_crtc_state(s);
583 	__drm_atomic_helper_crtc_destroy_state(s);
584 	kfree(state);
585 }
586 
atmel_hlcdc_crtc_enable_vblank(struct drm_crtc * c)587 static int atmel_hlcdc_crtc_enable_vblank(struct drm_crtc *c)
588 {
589 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
590 	struct regmap *regmap = crtc->dc->hlcdc->regmap;
591 
592 	/* Enable SOF (Start Of Frame) interrupt for vblank counting */
593 	regmap_write(regmap, ATMEL_HLCDC_IER, ATMEL_HLCDC_SOF);
594 
595 	return 0;
596 }
597 
atmel_hlcdc_crtc_disable_vblank(struct drm_crtc * c)598 static void atmel_hlcdc_crtc_disable_vblank(struct drm_crtc *c)
599 {
600 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
601 	struct regmap *regmap = crtc->dc->hlcdc->regmap;
602 
603 	regmap_write(regmap, ATMEL_HLCDC_IDR, ATMEL_HLCDC_SOF);
604 }
605 
606 static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = {
607 	.page_flip = drm_atomic_helper_page_flip,
608 	.set_config = drm_atomic_helper_set_config,
609 	.destroy = atmel_hlcdc_crtc_destroy,
610 	.reset = atmel_hlcdc_crtc_reset,
611 	.atomic_duplicate_state =  atmel_hlcdc_crtc_duplicate_state,
612 	.atomic_destroy_state = atmel_hlcdc_crtc_destroy_state,
613 	.enable_vblank = atmel_hlcdc_crtc_enable_vblank,
614 	.disable_vblank = atmel_hlcdc_crtc_disable_vblank,
615 };
616 
atmel_hlcdc_crtc_create(struct drm_device * dev)617 int atmel_hlcdc_crtc_create(struct drm_device *dev)
618 {
619 	struct atmel_hlcdc_plane *primary = NULL, *cursor = NULL;
620 	struct atmel_hlcdc_dc *dc = dev->dev_private;
621 	struct atmel_hlcdc_crtc *crtc;
622 	int ret;
623 	int i;
624 
625 	crtc = kzalloc(sizeof(*crtc), GFP_KERNEL);
626 	if (!crtc)
627 		return -ENOMEM;
628 
629 	crtc->dc = dc;
630 
631 	for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
632 		if (!dc->layers[i])
633 			continue;
634 
635 		switch (dc->layers[i]->desc->type) {
636 		case ATMEL_HLCDC_BASE_LAYER:
637 			primary = atmel_hlcdc_layer_to_plane(dc->layers[i]);
638 			break;
639 
640 		case ATMEL_HLCDC_CURSOR_LAYER:
641 			cursor = atmel_hlcdc_layer_to_plane(dc->layers[i]);
642 			break;
643 
644 		default:
645 			break;
646 		}
647 	}
648 
649 	ret = drm_crtc_init_with_planes(dev, &crtc->base, &primary->base,
650 					&cursor->base, &atmel_hlcdc_crtc_funcs,
651 					NULL);
652 	if (ret < 0)
653 		goto fail;
654 
655 	crtc->id = drm_crtc_index(&crtc->base);
656 
657 	for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
658 		struct atmel_hlcdc_plane *overlay;
659 
660 		if (dc->layers[i] &&
661 		    dc->layers[i]->desc->type == ATMEL_HLCDC_OVERLAY_LAYER) {
662 			overlay = atmel_hlcdc_layer_to_plane(dc->layers[i]);
663 			overlay->base.possible_crtcs = 1 << crtc->id;
664 		}
665 	}
666 
667 	drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs);
668 
669 	drm_mode_crtc_set_gamma_size(&crtc->base, ATMEL_HLCDC_CLUT_SIZE);
670 	drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
671 				   ATMEL_HLCDC_CLUT_SIZE);
672 
673 	dc->crtc = &crtc->base;
674 
675 	return 0;
676 
677 fail:
678 	atmel_hlcdc_crtc_destroy(&crtc->base);
679 	return ret;
680 }
681