xref: /linux/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
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
330af86604SManikandan Muralidharan  * @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;
380af86604SManikandan Muralidharan 	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 }
46aca63b76SBoris Brezillon 
47aca63b76SBoris Brezillon /**
48c2edc1feSLee Jones  * struct atmel_hlcdc_crtc - Atmel HLCDC CRTC structure
491a396789SBoris Brezillon  *
501a396789SBoris Brezillon  * @base: base DRM CRTC structure
51c2edc1feSLee Jones  * @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);
661a396789SBoris Brezillon }
671a396789SBoris Brezillon 
atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc * c)682389fc13SBoris Brezillon static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)
691a396789SBoris Brezillon {
701a396789SBoris Brezillon 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
711a396789SBoris Brezillon 	struct regmap *regmap = crtc->dc->hlcdc->regmap;
722389fc13SBoris Brezillon 	struct drm_display_mode *adj = &c->state->adjusted_mode;
73d01cb045SMiquel Raynal 	struct drm_encoder *encoder = NULL, *en_iter;
74d01cb045SMiquel Raynal 	struct drm_connector *connector = NULL;
75aca63b76SBoris Brezillon 	struct atmel_hlcdc_crtc_state *state;
76d01cb045SMiquel Raynal 	struct drm_device *ddev = c->dev;
77d01cb045SMiquel Raynal 	struct drm_connector_list_iter iter;
781a396789SBoris Brezillon 	unsigned long mode_rate;
791a396789SBoris Brezillon 	struct videomode vm;
801a396789SBoris Brezillon 	unsigned long prate;
81a6eca2abSClaudiu Beznea 	unsigned int mask = ATMEL_HLCDC_CLKDIV_MASK | ATMEL_HLCDC_CLKPOL;
82a6eca2abSClaudiu Beznea 	unsigned int cfg = 0;
832c1fb9d8SClaudiu Beznea 	int div, ret;
842c1fb9d8SClaudiu Beznea 
85d01cb045SMiquel Raynal 	/* get encoder from crtc */
86d01cb045SMiquel Raynal 	drm_for_each_encoder(en_iter, ddev) {
87d01cb045SMiquel Raynal 		if (en_iter->crtc == c) {
88d01cb045SMiquel Raynal 			encoder = en_iter;
89d01cb045SMiquel Raynal 			break;
90d01cb045SMiquel Raynal 		}
91d01cb045SMiquel Raynal 	}
92d01cb045SMiquel Raynal 
93d01cb045SMiquel Raynal 	if (encoder) {
94d01cb045SMiquel Raynal 		/* Get the connector from encoder */
95d01cb045SMiquel Raynal 		drm_connector_list_iter_begin(ddev, &iter);
96d01cb045SMiquel Raynal 		drm_for_each_connector_iter(connector, &iter)
97d01cb045SMiquel Raynal 			if (connector->encoder == encoder)
98d01cb045SMiquel Raynal 				break;
99d01cb045SMiquel Raynal 		drm_connector_list_iter_end(&iter);
100d01cb045SMiquel Raynal 	}
101d01cb045SMiquel Raynal 
1022c1fb9d8SClaudiu Beznea 	ret = clk_prepare_enable(crtc->dc->hlcdc->sys_clk);
1032c1fb9d8SClaudiu Beznea 	if (ret)
1042c1fb9d8SClaudiu Beznea 		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) |
1241a396789SBoris Brezillon 		     ((adj->crtc_vdisplay - 1) << 16));
1251a396789SBoris Brezillon 
12607acf4baSClaudiu 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) {
12907acf4baSClaudiu Beznea 		prate *= 2;
130a6eca2abSClaudiu Beznea 		cfg |= ATMEL_HLCDC_CLKSEL;
131a6eca2abSClaudiu Beznea 		mask |= ATMEL_HLCDC_CLKSEL;
132a6eca2abSClaudiu Beznea 	}
1331a396789SBoris Brezillon 
1341a396789SBoris Brezillon 	div = DIV_ROUND_UP(prate, mode_rate);
135319711f9SPeter Rosin 	if (div < 2) {
1361a396789SBoris Brezillon 		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);
142319711f9SPeter Rosin 		if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK)
143319711f9SPeter Rosin 			div = ATMEL_HLCDC_CLKDIV_MASK;
1449946a3a9SPeter Rosin 	} else {
1459946a3a9SPeter Rosin 		int div_low = prate / mode_rate;
1469946a3a9SPeter Rosin 
1479946a3a9SPeter Rosin 		if (div_low >= 2 &&
14851a19d15SPeter Rosin 		    (10 * (prate / div_low - mode_rate) <
14951a19d15SPeter 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.
1549946a3a9SPeter Rosin 			 */
1559946a3a9SPeter Rosin 			div = div_low;
156319711f9SPeter Rosin 	}
1571a396789SBoris Brezillon 
1581a396789SBoris Brezillon 	cfg |= ATMEL_HLCDC_CLKDIV(div);
1591a396789SBoris Brezillon 
160d01cb045SMiquel Raynal 	if (connector &&
161d01cb045SMiquel Raynal 	    connector->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
162d01cb045SMiquel Raynal 		cfg |= ATMEL_HLCDC_CLKPOL;
163d01cb045SMiquel Raynal 
164a6eca2abSClaudiu Beznea 	regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0), mask, cfg);
1651a396789SBoris Brezillon 
166e1dc68a4SClaudiu Beznea 	state = drm_crtc_state_to_atmel_hlcdc_crtc_state(c->state);
167e1dc68a4SClaudiu Beznea 	cfg = state->output_mode << 8;
1681a396789SBoris Brezillon 
169d498771bSManikandan Muralidharan 	if (!crtc->dc->desc->is_xlcdc) {
1702389fc13SBoris Brezillon 		if (adj->flags & DRM_MODE_FLAG_NVSYNC)
1711a396789SBoris Brezillon 			cfg |= ATMEL_HLCDC_VSPOL;
1721a396789SBoris Brezillon 
1732389fc13SBoris Brezillon 		if (adj->flags & DRM_MODE_FLAG_NHSYNC)
1741a396789SBoris Brezillon 			cfg |= ATMEL_HLCDC_HSPOL;
1750af86604SManikandan Muralidharan 	} else {
1760af86604SManikandan Muralidharan 		cfg |= state->dpi << 11;
177d498771bSManikandan Muralidharan 	}
1781a396789SBoris Brezillon 
1791a396789SBoris Brezillon 	regmap_update_bits(regmap, ATMEL_HLCDC_CFG(5),
1801a396789SBoris Brezillon 			   ATMEL_HLCDC_HSPOL | ATMEL_HLCDC_VSPOL |
1811a396789SBoris Brezillon 			   ATMEL_HLCDC_VSPDLYS | ATMEL_HLCDC_VSPDLYE |
1821a396789SBoris Brezillon 			   ATMEL_HLCDC_DISPPOL | ATMEL_HLCDC_DISPDLY |
1831a396789SBoris Brezillon 			   ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO |
1840af86604SManikandan Muralidharan 			   ATMEL_HLCDC_GUARDTIME_MASK |
1850af86604SManikandan Muralidharan 			   (crtc->dc->desc->is_xlcdc ? ATMEL_XLCDC_MODE_MASK |
1860af86604SManikandan Muralidharan 			   ATMEL_XLCDC_DPI : ATMEL_HLCDC_MODE_MASK),
1871a396789SBoris Brezillon 			   cfg);
1882c1fb9d8SClaudiu Beznea 
1892c1fb9d8SClaudiu Beznea 	clk_disable_unprepare(crtc->dc->hlcdc->sys_clk);
1901a396789SBoris Brezillon }
1911a396789SBoris Brezillon 
192a57bf53eSJose Abreu static enum drm_mode_status
atmel_hlcdc_crtc_mode_valid(struct drm_crtc * c,const struct drm_display_mode * mode)193a57bf53eSJose Abreu atmel_hlcdc_crtc_mode_valid(struct drm_crtc *c,
194a57bf53eSJose Abreu 			    const struct drm_display_mode *mode)
1955ac44c8bSBoris Brezillon {
1965ac44c8bSBoris Brezillon 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
1975ac44c8bSBoris Brezillon 
198a57bf53eSJose Abreu 	return atmel_hlcdc_dc_mode_valid(crtc->dc, mode);
1995ac44c8bSBoris Brezillon }
2005ac44c8bSBoris Brezillon 
atmel_hlcdc_crtc_atomic_disable(struct drm_crtc * c,struct drm_atomic_state * state)20164581714SLaurent Pinchart static void atmel_hlcdc_crtc_atomic_disable(struct drm_crtc *c,
202351f950dSMaxime Ripard 					    struct drm_atomic_state *state)
2031a396789SBoris 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;
2081a396789SBoris Brezillon 
2092389fc13SBoris Brezillon 	drm_crtc_vblank_off(c);
2101a396789SBoris Brezillon 
2112389fc13SBoris Brezillon 	pm_runtime_get_sync(dev->dev);
2122389fc13SBoris Brezillon 
213d498771bSManikandan Muralidharan 	if (crtc->dc->desc->is_xlcdc) {
214d498771bSManikandan Muralidharan 		regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_XLCDC_CM);
215d498771bSManikandan Muralidharan 		if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
216d498771bSManikandan Muralidharan 					     !(status & ATMEL_XLCDC_CM),
217d498771bSManikandan Muralidharan 					     10, 1000))
218d498771bSManikandan Muralidharan 			dev_warn(dev->dev, "Atmel LCDC status register CMSTS timeout\n");
219d498771bSManikandan Muralidharan 
220d498771bSManikandan Muralidharan 		regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_XLCDC_SD);
221d498771bSManikandan Muralidharan 		if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
222d498771bSManikandan Muralidharan 					     status & ATMEL_XLCDC_SD,
223d498771bSManikandan Muralidharan 					     10, 1000))
224d498771bSManikandan Muralidharan 			dev_warn(dev->dev, "Atmel LCDC status register SDSTS timeout\n");
225d498771bSManikandan Muralidharan 	}
226d498771bSManikandan Muralidharan 
2272389fc13SBoris Brezillon 	regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP);
228f5a5f04bSManikandan Muralidharan 	if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
229f5a5f04bSManikandan Muralidharan 				     !(status & ATMEL_HLCDC_DISP),
230f5a5f04bSManikandan Muralidharan 				    10, 1000))
231f5a5f04bSManikandan Muralidharan 		dev_warn(dev->dev, "Atmel LCDC status register DISPSTS timeout\n");
2322389fc13SBoris Brezillon 
2332389fc13SBoris Brezillon 	regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC);
234f5a5f04bSManikandan Muralidharan 	if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
235f5a5f04bSManikandan Muralidharan 				     !(status & ATMEL_HLCDC_SYNC),
236f5a5f04bSManikandan Muralidharan 				    10, 1000))
237f5a5f04bSManikandan Muralidharan 		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);
240f5a5f04bSManikandan Muralidharan 	if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
241f5a5f04bSManikandan Muralidharan 				     !(status & ATMEL_HLCDC_PIXEL_CLK),
242f5a5f04bSManikandan Muralidharan 				    10, 1000))
243f5a5f04bSManikandan Muralidharan 		dev_warn(dev->dev, "Atmel LCDC status register CLKSTS timeout\n");
2442389fc13SBoris Brezillon 
2452389fc13SBoris Brezillon 	clk_disable_unprepare(crtc->dc->hlcdc->sys_clk);
24616e6004eSSylvain Rochet 	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);
2511a396789SBoris Brezillon }
2522389fc13SBoris Brezillon 
atmel_hlcdc_crtc_atomic_enable(struct drm_crtc * c,struct drm_atomic_state * state)2530b20a0f8SLaurent Pinchart static void atmel_hlcdc_crtc_atomic_enable(struct drm_crtc *c,
254351f950dSMaxime Ripard 					   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 
2612389fc13SBoris Brezillon 	pm_runtime_get_sync(dev->dev);
2622389fc13SBoris Brezillon 
2632389fc13SBoris Brezillon 	pm_runtime_forbid(dev->dev);
2642389fc13SBoris Brezillon 
26516e6004eSSylvain Rochet 	pinctrl_pm_select_default_state(dev->dev);
2662389fc13SBoris Brezillon 	clk_prepare_enable(crtc->dc->hlcdc->sys_clk);
2672389fc13SBoris Brezillon 
2682389fc13SBoris Brezillon 	regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PIXEL_CLK);
269f5a5f04bSManikandan Muralidharan 	if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
270f5a5f04bSManikandan Muralidharan 				     status & ATMEL_HLCDC_PIXEL_CLK,
271f5a5f04bSManikandan Muralidharan 				     10, 1000))
272f5a5f04bSManikandan Muralidharan 		dev_warn(dev->dev, "Atmel LCDC status register CLKSTS timeout\n");
2732389fc13SBoris Brezillon 
2742389fc13SBoris Brezillon 	regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC);
275f5a5f04bSManikandan Muralidharan 	if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
276f5a5f04bSManikandan Muralidharan 				     status & ATMEL_HLCDC_SYNC,
277f5a5f04bSManikandan Muralidharan 				     10, 1000))
278f5a5f04bSManikandan Muralidharan 		dev_warn(dev->dev, "Atmel LCDC status register LCDSTS timeout\n");
2792389fc13SBoris Brezillon 
2802389fc13SBoris Brezillon 	regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP);
281f5a5f04bSManikandan Muralidharan 	if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
282f5a5f04bSManikandan Muralidharan 				     status & ATMEL_HLCDC_DISP,
283f5a5f04bSManikandan Muralidharan 				     10, 1000))
284f5a5f04bSManikandan Muralidharan 		dev_warn(dev->dev, "Atmel LCDC status register DISPSTS timeout\n");
285f5a5f04bSManikandan Muralidharan 
286d498771bSManikandan Muralidharan 	if (crtc->dc->desc->is_xlcdc) {
287d498771bSManikandan Muralidharan 		regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_XLCDC_CM);
288d498771bSManikandan Muralidharan 		if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
289d498771bSManikandan Muralidharan 					     status & ATMEL_XLCDC_CM,
290d498771bSManikandan Muralidharan 					     10, 1000))
291d498771bSManikandan Muralidharan 			dev_warn(dev->dev, "Atmel LCDC status register CMSTS timeout\n");
292d498771bSManikandan Muralidharan 
293d498771bSManikandan Muralidharan 		regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_XLCDC_SD);
294d498771bSManikandan Muralidharan 		if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
295d498771bSManikandan Muralidharan 					     !(status & ATMEL_XLCDC_SD),
296d498771bSManikandan Muralidharan 					     10, 1000))
297d498771bSManikandan Muralidharan 			dev_warn(dev->dev, "Atmel LCDC status register SDSTS timeout\n");
298d498771bSManikandan Muralidharan 	}
2992389fc13SBoris Brezillon 
3002389fc13SBoris Brezillon 	pm_runtime_put_sync(dev->dev);
3012389fc13SBoris Brezillon 
302f026eb6eSSylvain Rochet }
303f026eb6eSSylvain Rochet 
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)
308*d0e78f53SManikandan Muralidharan #define ATMEL_HLCDC_DPI_RGB565C1_OUTPUT		BIT(4)
309*d0e78f53SManikandan Muralidharan #define ATMEL_HLCDC_DPI_RGB565C2_OUTPUT		BIT(5)
310*d0e78f53SManikandan Muralidharan #define ATMEL_HLCDC_DPI_RGB565C3_OUTPUT		BIT(6)
311*d0e78f53SManikandan Muralidharan #define ATMEL_HLCDC_DPI_RGB666C1_OUTPUT		BIT(7)
312*d0e78f53SManikandan Muralidharan #define ATMEL_HLCDC_DPI_RGB666C2_OUTPUT		BIT(8)
313*d0e78f53SManikandan Muralidharan #define ATMEL_HLCDC_DPI_RGB888_OUTPUT		BIT(9)
314aca63b76SBoris Brezillon #define ATMEL_HLCDC_OUTPUT_MODE_MASK		GENMASK(3, 0)
315*d0e78f53SManikandan Muralidharan #define ATMEL_XLCDC_OUTPUT_MODE_MASK		GENMASK(9, 0)
316*d0e78f53SManikandan Muralidharan 
atmel_xlcdc_connector_output_dsi(struct drm_encoder * encoder,struct drm_display_info * info)317*d0e78f53SManikandan Muralidharan static int atmel_xlcdc_connector_output_dsi(struct drm_encoder *encoder,
318*d0e78f53SManikandan Muralidharan 					    struct drm_display_info *info)
319*d0e78f53SManikandan Muralidharan {
320*d0e78f53SManikandan Muralidharan 	int j;
321*d0e78f53SManikandan Muralidharan 	unsigned int supported_fmts = 0;
322*d0e78f53SManikandan Muralidharan 
323*d0e78f53SManikandan Muralidharan 	switch (atmel_hlcdc_encoder_get_bus_fmt(encoder)) {
324*d0e78f53SManikandan Muralidharan 	case 0:
325*d0e78f53SManikandan Muralidharan 		break;
326*d0e78f53SManikandan Muralidharan 	case MEDIA_BUS_FMT_RGB565_1X16:
327*d0e78f53SManikandan Muralidharan 		return ATMEL_HLCDC_DPI_RGB565C1_OUTPUT;
328*d0e78f53SManikandan Muralidharan 	case MEDIA_BUS_FMT_RGB666_1X18:
329*d0e78f53SManikandan Muralidharan 		return ATMEL_HLCDC_DPI_RGB666C1_OUTPUT;
330*d0e78f53SManikandan Muralidharan 	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
331*d0e78f53SManikandan Muralidharan 		return ATMEL_HLCDC_DPI_RGB666C2_OUTPUT;
332*d0e78f53SManikandan Muralidharan 	case MEDIA_BUS_FMT_RGB888_1X24:
333*d0e78f53SManikandan Muralidharan 		return ATMEL_HLCDC_DPI_RGB888_OUTPUT;
334*d0e78f53SManikandan Muralidharan 	default:
335*d0e78f53SManikandan Muralidharan 		return -EINVAL;
336*d0e78f53SManikandan Muralidharan 	}
337*d0e78f53SManikandan Muralidharan 
338*d0e78f53SManikandan Muralidharan 	for (j = 0; j < info->num_bus_formats; j++) {
339*d0e78f53SManikandan Muralidharan 		switch (info->bus_formats[j]) {
340*d0e78f53SManikandan Muralidharan 		case MEDIA_BUS_FMT_RGB565_1X16:
341*d0e78f53SManikandan Muralidharan 			supported_fmts |= ATMEL_HLCDC_DPI_RGB565C1_OUTPUT;
342*d0e78f53SManikandan Muralidharan 			break;
343*d0e78f53SManikandan Muralidharan 		case MEDIA_BUS_FMT_RGB666_1X18:
344*d0e78f53SManikandan Muralidharan 			supported_fmts |= ATMEL_HLCDC_DPI_RGB666C1_OUTPUT;
345*d0e78f53SManikandan Muralidharan 			break;
346*d0e78f53SManikandan Muralidharan 		case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
347*d0e78f53SManikandan Muralidharan 			supported_fmts |= ATMEL_HLCDC_DPI_RGB666C2_OUTPUT;
348*d0e78f53SManikandan Muralidharan 			break;
349*d0e78f53SManikandan Muralidharan 		case MEDIA_BUS_FMT_RGB888_1X24:
350*d0e78f53SManikandan Muralidharan 			supported_fmts |= ATMEL_HLCDC_DPI_RGB888_OUTPUT;
351*d0e78f53SManikandan Muralidharan 			break;
352*d0e78f53SManikandan Muralidharan 		default:
353*d0e78f53SManikandan Muralidharan 			break;
354*d0e78f53SManikandan Muralidharan 		}
355*d0e78f53SManikandan Muralidharan 	}
356*d0e78f53SManikandan Muralidharan 	return supported_fmts;
357*d0e78f53SManikandan Muralidharan }
358aca63b76SBoris Brezillon 
atmel_hlcdc_connector_output_mode(struct drm_connector_state * state)359b6e075c3SPeter Rosin static int atmel_hlcdc_connector_output_mode(struct drm_connector_state *state)
360aca63b76SBoris Brezillon {
361b6e075c3SPeter Rosin 	struct drm_connector *connector = state->connector;
362aca63b76SBoris Brezillon 	struct drm_display_info *info = &connector->display_info;
363b6e075c3SPeter Rosin 	struct drm_encoder *encoder;
364aca63b76SBoris Brezillon 	unsigned int supported_fmts = 0;
365aca63b76SBoris Brezillon 	int j;
366aca63b76SBoris Brezillon 
367b6e075c3SPeter Rosin 	encoder = state->best_encoder;
368b6e075c3SPeter Rosin 	if (!encoder)
369b6e075c3SPeter Rosin 		encoder = connector->encoder;
370*d0e78f53SManikandan Muralidharan 	/*
371*d0e78f53SManikandan Muralidharan 	 * atmel-hlcdc to support DSI formats with DSI video pipeline
372*d0e78f53SManikandan Muralidharan 	 * when DRM_MODE_ENCODER_DSI type is set by
373*d0e78f53SManikandan Muralidharan 	 * connector driver component.
374*d0e78f53SManikandan Muralidharan 	 */
375*d0e78f53SManikandan Muralidharan 	if (encoder->encoder_type == DRM_MODE_ENCODER_DSI)
376*d0e78f53SManikandan Muralidharan 		return atmel_xlcdc_connector_output_dsi(encoder, info);
377b6e075c3SPeter Rosin 
378b6e075c3SPeter Rosin 	switch (atmel_hlcdc_encoder_get_bus_fmt(encoder)) {
379b6e075c3SPeter Rosin 	case 0:
380b6e075c3SPeter Rosin 		break;
381b6e075c3SPeter Rosin 	case MEDIA_BUS_FMT_RGB444_1X12:
382b6e075c3SPeter Rosin 		return ATMEL_HLCDC_RGB444_OUTPUT;
383b6e075c3SPeter Rosin 	case MEDIA_BUS_FMT_RGB565_1X16:
384b6e075c3SPeter Rosin 		return ATMEL_HLCDC_RGB565_OUTPUT;
385b6e075c3SPeter Rosin 	case MEDIA_BUS_FMT_RGB666_1X18:
386b6e075c3SPeter Rosin 		return ATMEL_HLCDC_RGB666_OUTPUT;
387b6e075c3SPeter Rosin 	case MEDIA_BUS_FMT_RGB888_1X24:
388b6e075c3SPeter Rosin 		return ATMEL_HLCDC_RGB888_OUTPUT;
389b6e075c3SPeter Rosin 	default:
390b6e075c3SPeter Rosin 		return -EINVAL;
391b6e075c3SPeter Rosin 	}
392aca63b76SBoris Brezillon 
393aca63b76SBoris Brezillon 	for (j = 0; j < info->num_bus_formats; j++) {
394aca63b76SBoris Brezillon 		switch (info->bus_formats[j]) {
395aca63b76SBoris Brezillon 		case MEDIA_BUS_FMT_RGB444_1X12:
396aca63b76SBoris Brezillon 			supported_fmts |= ATMEL_HLCDC_RGB444_OUTPUT;
397aca63b76SBoris Brezillon 			break;
398aca63b76SBoris Brezillon 		case MEDIA_BUS_FMT_RGB565_1X16:
399aca63b76SBoris Brezillon 			supported_fmts |= ATMEL_HLCDC_RGB565_OUTPUT;
400aca63b76SBoris Brezillon 			break;
401aca63b76SBoris Brezillon 		case MEDIA_BUS_FMT_RGB666_1X18:
402aca63b76SBoris Brezillon 			supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT;
403aca63b76SBoris Brezillon 			break;
404aca63b76SBoris Brezillon 		case MEDIA_BUS_FMT_RGB888_1X24:
405aca63b76SBoris Brezillon 			supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT;
406aca63b76SBoris Brezillon 			break;
407aca63b76SBoris Brezillon 		default:
408aca63b76SBoris Brezillon 			break;
409aca63b76SBoris Brezillon 		}
410aca63b76SBoris Brezillon 	}
411aca63b76SBoris Brezillon 
412b6e075c3SPeter Rosin 	return supported_fmts;
413b6e075c3SPeter Rosin }
414b6e075c3SPeter Rosin 
atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state * state)415b6e075c3SPeter Rosin static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state)
416b6e075c3SPeter Rosin {
417*d0e78f53SManikandan Muralidharan 	unsigned int output_fmts;
418b6e075c3SPeter Rosin 	struct atmel_hlcdc_crtc_state *hstate;
419b6e075c3SPeter Rosin 	struct drm_connector_state *cstate;
420b6e075c3SPeter Rosin 	struct drm_connector *connector;
421b6e075c3SPeter Rosin 	struct atmel_hlcdc_crtc *crtc;
422b6e075c3SPeter Rosin 	int i;
423b6e075c3SPeter Rosin 
424b6e075c3SPeter Rosin 	crtc = drm_crtc_to_atmel_hlcdc_crtc(state->crtc);
425*d0e78f53SManikandan Muralidharan 	output_fmts = crtc->dc->desc->is_xlcdc ? ATMEL_XLCDC_OUTPUT_MODE_MASK :
426*d0e78f53SManikandan Muralidharan 		      ATMEL_HLCDC_OUTPUT_MODE_MASK;
427b6e075c3SPeter Rosin 
428b6e075c3SPeter Rosin 	for_each_new_connector_in_state(state->state, connector, cstate, i) {
429b6e075c3SPeter Rosin 		unsigned int supported_fmts = 0;
430b6e075c3SPeter Rosin 
431b6e075c3SPeter Rosin 		if (!cstate->crtc)
432b6e075c3SPeter Rosin 			continue;
433b6e075c3SPeter Rosin 
434b6e075c3SPeter Rosin 		supported_fmts = atmel_hlcdc_connector_output_mode(cstate);
435b6e075c3SPeter Rosin 
436aca63b76SBoris Brezillon 		if (crtc->dc->desc->conflicting_output_formats)
437aca63b76SBoris Brezillon 			output_fmts &= supported_fmts;
438aca63b76SBoris Brezillon 		else
439aca63b76SBoris Brezillon 			output_fmts |= supported_fmts;
440aca63b76SBoris Brezillon 	}
441aca63b76SBoris Brezillon 
442aca63b76SBoris Brezillon 	if (!output_fmts)
443aca63b76SBoris Brezillon 		return -EINVAL;
444aca63b76SBoris Brezillon 
445aca63b76SBoris Brezillon 	hstate = drm_crtc_state_to_atmel_hlcdc_crtc_state(state);
446aca63b76SBoris Brezillon 	hstate->output_mode = fls(output_fmts) - 1;
4470af86604SManikandan Muralidharan 	if (crtc->dc->desc->is_xlcdc) {
4480af86604SManikandan Muralidharan 		/* check if MIPI DPI bit needs to be set */
4490af86604SManikandan Muralidharan 		if (fls(output_fmts) > 3) {
4500af86604SManikandan Muralidharan 			hstate->output_mode -= 4;
4510af86604SManikandan Muralidharan 			hstate->dpi = 1;
4520af86604SManikandan Muralidharan 		} else {
4530af86604SManikandan Muralidharan 			hstate->dpi = 0;
4540af86604SManikandan Muralidharan 		}
4550af86604SManikandan Muralidharan 	}
456aca63b76SBoris Brezillon 	return 0;
457aca63b76SBoris Brezillon }
458aca63b76SBoris Brezillon 
atmel_hlcdc_crtc_atomic_check(struct drm_crtc * c,struct drm_atomic_state * state)4592389fc13SBoris Brezillon static int atmel_hlcdc_crtc_atomic_check(struct drm_crtc *c,
46029b77ad7SMaxime Ripard 					 struct drm_atomic_state *state)
4612389fc13SBoris Brezillon {
46229b77ad7SMaxime Ripard 	struct drm_crtc_state *s = drm_atomic_get_new_crtc_state(state, c);
463aca63b76SBoris Brezillon 	int ret;
4642389fc13SBoris Brezillon 
465aca63b76SBoris Brezillon 	ret = atmel_hlcdc_crtc_select_output_mode(s);
466aca63b76SBoris Brezillon 	if (ret)
467aca63b76SBoris Brezillon 		return ret;
468aca63b76SBoris Brezillon 
469ebab87abSBoris Brezillon 	ret = atmel_hlcdc_plane_prepare_disc_area(s);
470ebab87abSBoris Brezillon 	if (ret)
471ebab87abSBoris Brezillon 		return ret;
472ebab87abSBoris Brezillon 
473ebab87abSBoris Brezillon 	return atmel_hlcdc_plane_prepare_ahb_routing(s);
4742389fc13SBoris Brezillon }
4752389fc13SBoris Brezillon 
atmel_hlcdc_crtc_atomic_begin(struct drm_crtc * c,struct drm_atomic_state * state)476613d2b27SMaarten Lankhorst static void atmel_hlcdc_crtc_atomic_begin(struct drm_crtc *c,
477f6ebe9f9SMaxime Ripard 					  struct drm_atomic_state *state)
4782389fc13SBoris Brezillon {
479e484028bSDan Sneddon 	drm_crtc_vblank_on(c);
480e484028bSDan Sneddon }
481e484028bSDan Sneddon 
atmel_hlcdc_crtc_atomic_flush(struct drm_crtc * c,struct drm_atomic_state * state)482e484028bSDan Sneddon static void atmel_hlcdc_crtc_atomic_flush(struct drm_crtc *c,
483e484028bSDan Sneddon 					  struct drm_atomic_state *state)
484e484028bSDan Sneddon {
4852389fc13SBoris Brezillon 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
486e484028bSDan Sneddon 	unsigned long flags;
487e484028bSDan Sneddon 
488e484028bSDan Sneddon 	spin_lock_irqsave(&c->dev->event_lock, flags);
4892389fc13SBoris Brezillon 
4902389fc13SBoris Brezillon 	if (c->state->event) {
4912389fc13SBoris Brezillon 		c->state->event->pipe = drm_crtc_index(c);
4922389fc13SBoris Brezillon 
4932389fc13SBoris Brezillon 		WARN_ON(drm_crtc_vblank_get(c) != 0);
4942389fc13SBoris Brezillon 
4952389fc13SBoris Brezillon 		crtc->event = c->state->event;
4962389fc13SBoris Brezillon 		c->state->event = NULL;
4972389fc13SBoris Brezillon 	}
498e484028bSDan Sneddon 	spin_unlock_irqrestore(&c->dev->event_lock, flags);
4991a396789SBoris Brezillon }
5001a396789SBoris Brezillon 
5011a396789SBoris Brezillon static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = {
502a57bf53eSJose Abreu 	.mode_valid = atmel_hlcdc_crtc_mode_valid,
5032389fc13SBoris Brezillon 	.mode_set_nofb = atmel_hlcdc_crtc_mode_set_nofb,
5042389fc13SBoris Brezillon 	.atomic_check = atmel_hlcdc_crtc_atomic_check,
5052389fc13SBoris Brezillon 	.atomic_begin = atmel_hlcdc_crtc_atomic_begin,
5062389fc13SBoris Brezillon 	.atomic_flush = atmel_hlcdc_crtc_atomic_flush,
5070b20a0f8SLaurent Pinchart 	.atomic_enable = atmel_hlcdc_crtc_atomic_enable,
50864581714SLaurent Pinchart 	.atomic_disable = atmel_hlcdc_crtc_atomic_disable,
5091a396789SBoris 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 {
5211a396789SBoris Brezillon 	struct drm_device *dev = crtc->base.dev;
5221a396789SBoris Brezillon 	unsigned long flags;
5231a396789SBoris Brezillon 
5241a396789SBoris Brezillon 	spin_lock_irqsave(&dev->event_lock, flags);
5251a396789SBoris Brezillon 	if (crtc->event) {
52681767317SGustavo Padovan 		drm_crtc_send_vblank_event(&crtc->base, crtc->event);
52723a25ed3SGustavo Padovan 		drm_crtc_vblank_put(&crtc->base);
5281a396789SBoris Brezillon 		crtc->event = NULL;
5291a396789SBoris Brezillon 	}
5301a396789SBoris Brezillon 	spin_unlock_irqrestore(&dev->event_lock, flags);
5311a396789SBoris Brezillon }
5321a396789SBoris Brezillon 
atmel_hlcdc_crtc_irq(struct drm_crtc * c)5331a396789SBoris Brezillon void atmel_hlcdc_crtc_irq(struct drm_crtc *c)
5341a396789SBoris Brezillon {
535548ebe1eSGustavo Padovan 	drm_crtc_handle_vblank(c);
5361a396789SBoris Brezillon 	atmel_hlcdc_crtc_finish_page_flip(drm_crtc_to_atmel_hlcdc_crtc(c));
5371a396789SBoris Brezillon }
5381a396789SBoris Brezillon 
atmel_hlcdc_crtc_reset(struct drm_crtc * crtc)5391ba7db07SThierry Reding static void atmel_hlcdc_crtc_reset(struct drm_crtc *crtc)
540aca63b76SBoris Brezillon {
541aca63b76SBoris Brezillon 	struct atmel_hlcdc_crtc_state *state;
542aca63b76SBoris Brezillon 
543aca63b76SBoris Brezillon 	if (crtc->state) {
544c2e4c994SBoris Brezillon 		__drm_atomic_helper_crtc_destroy_state(crtc->state);
545aca63b76SBoris Brezillon 		state = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state);
546aca63b76SBoris Brezillon 		kfree(state);
547c2e4c994SBoris Brezillon 		crtc->state = NULL;
548aca63b76SBoris Brezillon 	}
549aca63b76SBoris Brezillon 
550aca63b76SBoris Brezillon 	state = kzalloc(sizeof(*state), GFP_KERNEL);
55151f644b4SDaniel Vetter 	if (state)
55251f644b4SDaniel Vetter 		__drm_atomic_helper_crtc_reset(crtc, &state->base);
553aca63b76SBoris Brezillon }
554aca63b76SBoris Brezillon 
555aca63b76SBoris Brezillon static struct drm_crtc_state *
atmel_hlcdc_crtc_duplicate_state(struct drm_crtc * crtc)556aca63b76SBoris Brezillon atmel_hlcdc_crtc_duplicate_state(struct drm_crtc *crtc)
557aca63b76SBoris Brezillon {
558aca63b76SBoris Brezillon 	struct atmel_hlcdc_crtc_state *state, *cur;
5590af86604SManikandan Muralidharan 	struct atmel_hlcdc_crtc *c = drm_crtc_to_atmel_hlcdc_crtc(crtc);
560aca63b76SBoris Brezillon 
561aca63b76SBoris Brezillon 	if (WARN_ON(!crtc->state))
562aca63b76SBoris Brezillon 		return NULL;
563aca63b76SBoris Brezillon 
564aca63b76SBoris Brezillon 	state = kmalloc(sizeof(*state), GFP_KERNEL);
56558a2ab3aSDan Carpenter 	if (!state)
56658a2ab3aSDan Carpenter 		return NULL;
567aca63b76SBoris Brezillon 	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
568aca63b76SBoris Brezillon 
569aca63b76SBoris Brezillon 	cur = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state);
570aca63b76SBoris Brezillon 	state->output_mode = cur->output_mode;
5710af86604SManikandan Muralidharan 	if (c->dc->desc->is_xlcdc)
5720af86604SManikandan Muralidharan 		state->dpi = cur->dpi;
573aca63b76SBoris Brezillon 
574aca63b76SBoris Brezillon 	return &state->base;
575aca63b76SBoris Brezillon }
576aca63b76SBoris Brezillon 
atmel_hlcdc_crtc_destroy_state(struct drm_crtc * crtc,struct drm_crtc_state * s)577aca63b76SBoris Brezillon static void atmel_hlcdc_crtc_destroy_state(struct drm_crtc *crtc,
578aca63b76SBoris Brezillon 					   struct drm_crtc_state *s)
579aca63b76SBoris Brezillon {
580aca63b76SBoris Brezillon 	struct atmel_hlcdc_crtc_state *state;
581aca63b76SBoris Brezillon 
582aca63b76SBoris Brezillon 	state = drm_crtc_state_to_atmel_hlcdc_crtc_state(s);
583ec2dc6a0SDaniel Vetter 	__drm_atomic_helper_crtc_destroy_state(s);
584aca63b76SBoris Brezillon 	kfree(state);
585aca63b76SBoris Brezillon }
586aca63b76SBoris Brezillon 
atmel_hlcdc_crtc_enable_vblank(struct drm_crtc * c)58782308e27SShawn Guo static int atmel_hlcdc_crtc_enable_vblank(struct drm_crtc *c)
58882308e27SShawn Guo {
58982308e27SShawn Guo 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
59082308e27SShawn Guo 	struct regmap *regmap = crtc->dc->hlcdc->regmap;
59182308e27SShawn Guo 
59282308e27SShawn Guo 	/* Enable SOF (Start Of Frame) interrupt for vblank counting */
59382308e27SShawn Guo 	regmap_write(regmap, ATMEL_HLCDC_IER, ATMEL_HLCDC_SOF);
59482308e27SShawn Guo 
59582308e27SShawn Guo 	return 0;
59682308e27SShawn Guo }
59782308e27SShawn Guo 
atmel_hlcdc_crtc_disable_vblank(struct drm_crtc * c)59882308e27SShawn Guo static void atmel_hlcdc_crtc_disable_vblank(struct drm_crtc *c)
59982308e27SShawn Guo {
60082308e27SShawn Guo 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
60182308e27SShawn Guo 	struct regmap *regmap = crtc->dc->hlcdc->regmap;
60282308e27SShawn Guo 
60382308e27SShawn Guo 	regmap_write(regmap, ATMEL_HLCDC_IDR, ATMEL_HLCDC_SOF);
60482308e27SShawn Guo }
60582308e27SShawn Guo 
6061a396789SBoris Brezillon static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = {
6072389fc13SBoris Brezillon 	.page_flip = drm_atomic_helper_page_flip,
6082389fc13SBoris Brezillon 	.set_config = drm_atomic_helper_set_config,
6091a396789SBoris Brezillon 	.destroy = atmel_hlcdc_crtc_destroy,
610aca63b76SBoris Brezillon 	.reset = atmel_hlcdc_crtc_reset,
611aca63b76SBoris Brezillon 	.atomic_duplicate_state =  atmel_hlcdc_crtc_duplicate_state,
612aca63b76SBoris Brezillon 	.atomic_destroy_state = atmel_hlcdc_crtc_destroy_state,
61382308e27SShawn Guo 	.enable_vblank = atmel_hlcdc_crtc_enable_vblank,
61482308e27SShawn Guo 	.disable_vblank = atmel_hlcdc_crtc_disable_vblank,
6151a396789SBoris Brezillon };
6161a396789SBoris Brezillon 
atmel_hlcdc_crtc_create(struct drm_device * dev)6171a396789SBoris Brezillon int atmel_hlcdc_crtc_create(struct drm_device *dev)
6181a396789SBoris Brezillon {
6199a45d33cSBoris Brezillon 	struct atmel_hlcdc_plane *primary = NULL, *cursor = NULL;
6201a396789SBoris Brezillon 	struct atmel_hlcdc_dc *dc = dev->dev_private;
6211a396789SBoris Brezillon 	struct atmel_hlcdc_crtc *crtc;
6221a396789SBoris Brezillon 	int ret;
6231a396789SBoris Brezillon 	int i;
6241a396789SBoris Brezillon 
6251a396789SBoris Brezillon 	crtc = kzalloc(sizeof(*crtc), GFP_KERNEL);
6261a396789SBoris Brezillon 	if (!crtc)
6271a396789SBoris Brezillon 		return -ENOMEM;
6281a396789SBoris Brezillon 
6291a396789SBoris Brezillon 	crtc->dc = dc;
6301a396789SBoris Brezillon 
6319a45d33cSBoris Brezillon 	for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
6329a45d33cSBoris Brezillon 		if (!dc->layers[i])
6339a45d33cSBoris Brezillon 			continue;
6349a45d33cSBoris Brezillon 
6359a45d33cSBoris Brezillon 		switch (dc->layers[i]->desc->type) {
6369a45d33cSBoris Brezillon 		case ATMEL_HLCDC_BASE_LAYER:
6379a45d33cSBoris Brezillon 			primary = atmel_hlcdc_layer_to_plane(dc->layers[i]);
6389a45d33cSBoris Brezillon 			break;
6399a45d33cSBoris Brezillon 
6409a45d33cSBoris Brezillon 		case ATMEL_HLCDC_CURSOR_LAYER:
6419a45d33cSBoris Brezillon 			cursor = atmel_hlcdc_layer_to_plane(dc->layers[i]);
6429a45d33cSBoris Brezillon 			break;
6439a45d33cSBoris Brezillon 
6449a45d33cSBoris Brezillon 		default:
6459a45d33cSBoris Brezillon 			break;
6469a45d33cSBoris Brezillon 		}
6479a45d33cSBoris Brezillon 	}
6489a45d33cSBoris Brezillon 
6499a45d33cSBoris Brezillon 	ret = drm_crtc_init_with_planes(dev, &crtc->base, &primary->base,
6509a45d33cSBoris Brezillon 					&cursor->base, &atmel_hlcdc_crtc_funcs,
6519a45d33cSBoris Brezillon 					NULL);
6521a396789SBoris Brezillon 	if (ret < 0)
6531a396789SBoris Brezillon 		goto fail;
6541a396789SBoris Brezillon 
6551a396789SBoris Brezillon 	crtc->id = drm_crtc_index(&crtc->base);
6561a396789SBoris Brezillon 
6579a45d33cSBoris Brezillon 	for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
6589a45d33cSBoris Brezillon 		struct atmel_hlcdc_plane *overlay;
6591a396789SBoris Brezillon 
6609a45d33cSBoris Brezillon 		if (dc->layers[i] &&
6619a45d33cSBoris Brezillon 		    dc->layers[i]->desc->type == ATMEL_HLCDC_OVERLAY_LAYER) {
6629a45d33cSBoris Brezillon 			overlay = atmel_hlcdc_layer_to_plane(dc->layers[i]);
6639a45d33cSBoris Brezillon 			overlay->base.possible_crtcs = 1 << crtc->id;
6649a45d33cSBoris Brezillon 		}
6659a45d33cSBoris Brezillon 	}
6661a396789SBoris Brezillon 
6671a396789SBoris Brezillon 	drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs);
6681a396789SBoris Brezillon 
669364a7bf5SPeter Rosin 	drm_mode_crtc_set_gamma_size(&crtc->base, ATMEL_HLCDC_CLUT_SIZE);
670364a7bf5SPeter Rosin 	drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
671364a7bf5SPeter Rosin 				   ATMEL_HLCDC_CLUT_SIZE);
672364a7bf5SPeter Rosin 
6731a396789SBoris Brezillon 	dc->crtc = &crtc->base;
6741a396789SBoris Brezillon 
6751a396789SBoris Brezillon 	return 0;
6761a396789SBoris Brezillon 
6771a396789SBoris Brezillon fail:
6781a396789SBoris Brezillon 	atmel_hlcdc_crtc_destroy(&crtc->base);
6791a396789SBoris Brezillon 	return ret;
6801a396789SBoris Brezillon }
681