xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (C) 2014 Traphandler
4*4882a593Smuzhiyun  * Copyright (C) 2014 Free Electrons
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
7*4882a593Smuzhiyun  * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include <linux/clk.h>
11*4882a593Smuzhiyun #include <linux/mfd/atmel-hlcdc.h>
12*4882a593Smuzhiyun #include <linux/pinctrl/consumer.h>
13*4882a593Smuzhiyun #include <linux/pm.h>
14*4882a593Smuzhiyun #include <linux/pm_runtime.h>
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun #include <video/videomode.h>
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include <drm/drm_atomic.h>
19*4882a593Smuzhiyun #include <drm/drm_atomic_helper.h>
20*4882a593Smuzhiyun #include <drm/drm_crtc.h>
21*4882a593Smuzhiyun #include <drm/drm_modeset_helper_vtables.h>
22*4882a593Smuzhiyun #include <drm/drm_probe_helper.h>
23*4882a593Smuzhiyun #include <drm/drm_vblank.h>
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun #include "atmel_hlcdc_dc.h"
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun /**
28*4882a593Smuzhiyun  * Atmel HLCDC CRTC state structure
29*4882a593Smuzhiyun  *
30*4882a593Smuzhiyun  * @base: base CRTC state
31*4882a593Smuzhiyun  * @output_mode: RGBXXX output mode
32*4882a593Smuzhiyun  */
33*4882a593Smuzhiyun struct atmel_hlcdc_crtc_state {
34*4882a593Smuzhiyun 	struct drm_crtc_state base;
35*4882a593Smuzhiyun 	unsigned int output_mode;
36*4882a593Smuzhiyun };
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun static inline struct atmel_hlcdc_crtc_state *
drm_crtc_state_to_atmel_hlcdc_crtc_state(struct drm_crtc_state * state)39*4882a593Smuzhiyun drm_crtc_state_to_atmel_hlcdc_crtc_state(struct drm_crtc_state *state)
40*4882a593Smuzhiyun {
41*4882a593Smuzhiyun 	return container_of(state, struct atmel_hlcdc_crtc_state, base);
42*4882a593Smuzhiyun }
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun /**
45*4882a593Smuzhiyun  * Atmel HLCDC CRTC structure
46*4882a593Smuzhiyun  *
47*4882a593Smuzhiyun  * @base: base DRM CRTC structure
48*4882a593Smuzhiyun  * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
49*4882a593Smuzhiyun  * @event: pointer to the current page flip event
50*4882a593Smuzhiyun  * @id: CRTC id (returned by drm_crtc_index)
51*4882a593Smuzhiyun  */
52*4882a593Smuzhiyun struct atmel_hlcdc_crtc {
53*4882a593Smuzhiyun 	struct drm_crtc base;
54*4882a593Smuzhiyun 	struct atmel_hlcdc_dc *dc;
55*4882a593Smuzhiyun 	struct drm_pending_vblank_event *event;
56*4882a593Smuzhiyun 	int id;
57*4882a593Smuzhiyun };
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun static inline struct atmel_hlcdc_crtc *
drm_crtc_to_atmel_hlcdc_crtc(struct drm_crtc * crtc)60*4882a593Smuzhiyun drm_crtc_to_atmel_hlcdc_crtc(struct drm_crtc *crtc)
61*4882a593Smuzhiyun {
62*4882a593Smuzhiyun 	return container_of(crtc, struct atmel_hlcdc_crtc, base);
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun 
atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc * c)65*4882a593Smuzhiyun static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)
66*4882a593Smuzhiyun {
67*4882a593Smuzhiyun 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
68*4882a593Smuzhiyun 	struct regmap *regmap = crtc->dc->hlcdc->regmap;
69*4882a593Smuzhiyun 	struct drm_display_mode *adj = &c->state->adjusted_mode;
70*4882a593Smuzhiyun 	struct atmel_hlcdc_crtc_state *state;
71*4882a593Smuzhiyun 	unsigned long mode_rate;
72*4882a593Smuzhiyun 	struct videomode vm;
73*4882a593Smuzhiyun 	unsigned long prate;
74*4882a593Smuzhiyun 	unsigned int mask = ATMEL_HLCDC_CLKDIV_MASK | ATMEL_HLCDC_CLKPOL;
75*4882a593Smuzhiyun 	unsigned int cfg = 0;
76*4882a593Smuzhiyun 	int div, ret;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	ret = clk_prepare_enable(crtc->dc->hlcdc->sys_clk);
79*4882a593Smuzhiyun 	if (ret)
80*4882a593Smuzhiyun 		return;
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 	vm.vfront_porch = adj->crtc_vsync_start - adj->crtc_vdisplay;
83*4882a593Smuzhiyun 	vm.vback_porch = adj->crtc_vtotal - adj->crtc_vsync_end;
84*4882a593Smuzhiyun 	vm.vsync_len = adj->crtc_vsync_end - adj->crtc_vsync_start;
85*4882a593Smuzhiyun 	vm.hfront_porch = adj->crtc_hsync_start - adj->crtc_hdisplay;
86*4882a593Smuzhiyun 	vm.hback_porch = adj->crtc_htotal - adj->crtc_hsync_end;
87*4882a593Smuzhiyun 	vm.hsync_len = adj->crtc_hsync_end - adj->crtc_hsync_start;
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	regmap_write(regmap, ATMEL_HLCDC_CFG(1),
90*4882a593Smuzhiyun 		     (vm.hsync_len - 1) | ((vm.vsync_len - 1) << 16));
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	regmap_write(regmap, ATMEL_HLCDC_CFG(2),
93*4882a593Smuzhiyun 		     (vm.vfront_porch - 1) | (vm.vback_porch << 16));
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	regmap_write(regmap, ATMEL_HLCDC_CFG(3),
96*4882a593Smuzhiyun 		     (vm.hfront_porch - 1) | ((vm.hback_porch - 1) << 16));
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	regmap_write(regmap, ATMEL_HLCDC_CFG(4),
99*4882a593Smuzhiyun 		     (adj->crtc_hdisplay - 1) |
100*4882a593Smuzhiyun 		     ((adj->crtc_vdisplay - 1) << 16));
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 	prate = clk_get_rate(crtc->dc->hlcdc->sys_clk);
103*4882a593Smuzhiyun 	mode_rate = adj->crtc_clock * 1000;
104*4882a593Smuzhiyun 	if (!crtc->dc->desc->fixed_clksrc) {
105*4882a593Smuzhiyun 		prate *= 2;
106*4882a593Smuzhiyun 		cfg |= ATMEL_HLCDC_CLKSEL;
107*4882a593Smuzhiyun 		mask |= ATMEL_HLCDC_CLKSEL;
108*4882a593Smuzhiyun 	}
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 	div = DIV_ROUND_UP(prate, mode_rate);
111*4882a593Smuzhiyun 	if (div < 2) {
112*4882a593Smuzhiyun 		div = 2;
113*4882a593Smuzhiyun 	} else if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK) {
114*4882a593Smuzhiyun 		/* The divider ended up too big, try a lower base rate. */
115*4882a593Smuzhiyun 		cfg &= ~ATMEL_HLCDC_CLKSEL;
116*4882a593Smuzhiyun 		prate /= 2;
117*4882a593Smuzhiyun 		div = DIV_ROUND_UP(prate, mode_rate);
118*4882a593Smuzhiyun 		if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK)
119*4882a593Smuzhiyun 			div = ATMEL_HLCDC_CLKDIV_MASK;
120*4882a593Smuzhiyun 	} else {
121*4882a593Smuzhiyun 		int div_low = prate / mode_rate;
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 		if (div_low >= 2 &&
124*4882a593Smuzhiyun 		    (10 * (prate / div_low - mode_rate) <
125*4882a593Smuzhiyun 		     (mode_rate - prate / div)))
126*4882a593Smuzhiyun 			/*
127*4882a593Smuzhiyun 			 * At least 10 times better when using a higher
128*4882a593Smuzhiyun 			 * frequency than requested, instead of a lower.
129*4882a593Smuzhiyun 			 * So, go with that.
130*4882a593Smuzhiyun 			 */
131*4882a593Smuzhiyun 			div = div_low;
132*4882a593Smuzhiyun 	}
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	cfg |= ATMEL_HLCDC_CLKDIV(div);
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun 	regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0), mask, cfg);
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	state = drm_crtc_state_to_atmel_hlcdc_crtc_state(c->state);
139*4882a593Smuzhiyun 	cfg = state->output_mode << 8;
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	if (adj->flags & DRM_MODE_FLAG_NVSYNC)
142*4882a593Smuzhiyun 		cfg |= ATMEL_HLCDC_VSPOL;
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun 	if (adj->flags & DRM_MODE_FLAG_NHSYNC)
145*4882a593Smuzhiyun 		cfg |= ATMEL_HLCDC_HSPOL;
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 	regmap_update_bits(regmap, ATMEL_HLCDC_CFG(5),
148*4882a593Smuzhiyun 			   ATMEL_HLCDC_HSPOL | ATMEL_HLCDC_VSPOL |
149*4882a593Smuzhiyun 			   ATMEL_HLCDC_VSPDLYS | ATMEL_HLCDC_VSPDLYE |
150*4882a593Smuzhiyun 			   ATMEL_HLCDC_DISPPOL | ATMEL_HLCDC_DISPDLY |
151*4882a593Smuzhiyun 			   ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO |
152*4882a593Smuzhiyun 			   ATMEL_HLCDC_GUARDTIME_MASK | ATMEL_HLCDC_MODE_MASK,
153*4882a593Smuzhiyun 			   cfg);
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	clk_disable_unprepare(crtc->dc->hlcdc->sys_clk);
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun static enum drm_mode_status
atmel_hlcdc_crtc_mode_valid(struct drm_crtc * c,const struct drm_display_mode * mode)159*4882a593Smuzhiyun atmel_hlcdc_crtc_mode_valid(struct drm_crtc *c,
160*4882a593Smuzhiyun 			    const struct drm_display_mode *mode)
161*4882a593Smuzhiyun {
162*4882a593Smuzhiyun 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	return atmel_hlcdc_dc_mode_valid(crtc->dc, mode);
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun 
atmel_hlcdc_crtc_atomic_disable(struct drm_crtc * c,struct drm_crtc_state * old_state)167*4882a593Smuzhiyun static void atmel_hlcdc_crtc_atomic_disable(struct drm_crtc *c,
168*4882a593Smuzhiyun 					    struct drm_crtc_state *old_state)
169*4882a593Smuzhiyun {
170*4882a593Smuzhiyun 	struct drm_device *dev = c->dev;
171*4882a593Smuzhiyun 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
172*4882a593Smuzhiyun 	struct regmap *regmap = crtc->dc->hlcdc->regmap;
173*4882a593Smuzhiyun 	unsigned int status;
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	drm_crtc_vblank_off(c);
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	pm_runtime_get_sync(dev->dev);
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP);
180*4882a593Smuzhiyun 	while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
181*4882a593Smuzhiyun 	       (status & ATMEL_HLCDC_DISP))
182*4882a593Smuzhiyun 		cpu_relax();
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC);
185*4882a593Smuzhiyun 	while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
186*4882a593Smuzhiyun 	       (status & ATMEL_HLCDC_SYNC))
187*4882a593Smuzhiyun 		cpu_relax();
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PIXEL_CLK);
190*4882a593Smuzhiyun 	while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
191*4882a593Smuzhiyun 	       (status & ATMEL_HLCDC_PIXEL_CLK))
192*4882a593Smuzhiyun 		cpu_relax();
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	clk_disable_unprepare(crtc->dc->hlcdc->sys_clk);
195*4882a593Smuzhiyun 	pinctrl_pm_select_sleep_state(dev->dev);
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	pm_runtime_allow(dev->dev);
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	pm_runtime_put_sync(dev->dev);
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun 
atmel_hlcdc_crtc_atomic_enable(struct drm_crtc * c,struct drm_crtc_state * old_state)202*4882a593Smuzhiyun static void atmel_hlcdc_crtc_atomic_enable(struct drm_crtc *c,
203*4882a593Smuzhiyun 					   struct drm_crtc_state *old_state)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun 	struct drm_device *dev = c->dev;
206*4882a593Smuzhiyun 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
207*4882a593Smuzhiyun 	struct regmap *regmap = crtc->dc->hlcdc->regmap;
208*4882a593Smuzhiyun 	unsigned int status;
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	pm_runtime_get_sync(dev->dev);
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	pm_runtime_forbid(dev->dev);
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	pinctrl_pm_select_default_state(dev->dev);
215*4882a593Smuzhiyun 	clk_prepare_enable(crtc->dc->hlcdc->sys_clk);
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PIXEL_CLK);
218*4882a593Smuzhiyun 	while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
219*4882a593Smuzhiyun 	       !(status & ATMEL_HLCDC_PIXEL_CLK))
220*4882a593Smuzhiyun 		cpu_relax();
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC);
224*4882a593Smuzhiyun 	while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
225*4882a593Smuzhiyun 	       !(status & ATMEL_HLCDC_SYNC))
226*4882a593Smuzhiyun 		cpu_relax();
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP);
229*4882a593Smuzhiyun 	while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
230*4882a593Smuzhiyun 	       !(status & ATMEL_HLCDC_DISP))
231*4882a593Smuzhiyun 		cpu_relax();
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	pm_runtime_put_sync(dev->dev);
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	drm_crtc_vblank_on(c);
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun #define ATMEL_HLCDC_RGB444_OUTPUT	BIT(0)
239*4882a593Smuzhiyun #define ATMEL_HLCDC_RGB565_OUTPUT	BIT(1)
240*4882a593Smuzhiyun #define ATMEL_HLCDC_RGB666_OUTPUT	BIT(2)
241*4882a593Smuzhiyun #define ATMEL_HLCDC_RGB888_OUTPUT	BIT(3)
242*4882a593Smuzhiyun #define ATMEL_HLCDC_OUTPUT_MODE_MASK	GENMASK(3, 0)
243*4882a593Smuzhiyun 
atmel_hlcdc_connector_output_mode(struct drm_connector_state * state)244*4882a593Smuzhiyun static int atmel_hlcdc_connector_output_mode(struct drm_connector_state *state)
245*4882a593Smuzhiyun {
246*4882a593Smuzhiyun 	struct drm_connector *connector = state->connector;
247*4882a593Smuzhiyun 	struct drm_display_info *info = &connector->display_info;
248*4882a593Smuzhiyun 	struct drm_encoder *encoder;
249*4882a593Smuzhiyun 	unsigned int supported_fmts = 0;
250*4882a593Smuzhiyun 	int j;
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	encoder = state->best_encoder;
253*4882a593Smuzhiyun 	if (!encoder)
254*4882a593Smuzhiyun 		encoder = connector->encoder;
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 	switch (atmel_hlcdc_encoder_get_bus_fmt(encoder)) {
257*4882a593Smuzhiyun 	case 0:
258*4882a593Smuzhiyun 		break;
259*4882a593Smuzhiyun 	case MEDIA_BUS_FMT_RGB444_1X12:
260*4882a593Smuzhiyun 		return ATMEL_HLCDC_RGB444_OUTPUT;
261*4882a593Smuzhiyun 	case MEDIA_BUS_FMT_RGB565_1X16:
262*4882a593Smuzhiyun 		return ATMEL_HLCDC_RGB565_OUTPUT;
263*4882a593Smuzhiyun 	case MEDIA_BUS_FMT_RGB666_1X18:
264*4882a593Smuzhiyun 		return ATMEL_HLCDC_RGB666_OUTPUT;
265*4882a593Smuzhiyun 	case MEDIA_BUS_FMT_RGB888_1X24:
266*4882a593Smuzhiyun 		return ATMEL_HLCDC_RGB888_OUTPUT;
267*4882a593Smuzhiyun 	default:
268*4882a593Smuzhiyun 		return -EINVAL;
269*4882a593Smuzhiyun 	}
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	for (j = 0; j < info->num_bus_formats; j++) {
272*4882a593Smuzhiyun 		switch (info->bus_formats[j]) {
273*4882a593Smuzhiyun 		case MEDIA_BUS_FMT_RGB444_1X12:
274*4882a593Smuzhiyun 			supported_fmts |= ATMEL_HLCDC_RGB444_OUTPUT;
275*4882a593Smuzhiyun 			break;
276*4882a593Smuzhiyun 		case MEDIA_BUS_FMT_RGB565_1X16:
277*4882a593Smuzhiyun 			supported_fmts |= ATMEL_HLCDC_RGB565_OUTPUT;
278*4882a593Smuzhiyun 			break;
279*4882a593Smuzhiyun 		case MEDIA_BUS_FMT_RGB666_1X18:
280*4882a593Smuzhiyun 			supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT;
281*4882a593Smuzhiyun 			break;
282*4882a593Smuzhiyun 		case MEDIA_BUS_FMT_RGB888_1X24:
283*4882a593Smuzhiyun 			supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT;
284*4882a593Smuzhiyun 			break;
285*4882a593Smuzhiyun 		default:
286*4882a593Smuzhiyun 			break;
287*4882a593Smuzhiyun 		}
288*4882a593Smuzhiyun 	}
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 	return supported_fmts;
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun 
atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state * state)293*4882a593Smuzhiyun static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state)
294*4882a593Smuzhiyun {
295*4882a593Smuzhiyun 	unsigned int output_fmts = ATMEL_HLCDC_OUTPUT_MODE_MASK;
296*4882a593Smuzhiyun 	struct atmel_hlcdc_crtc_state *hstate;
297*4882a593Smuzhiyun 	struct drm_connector_state *cstate;
298*4882a593Smuzhiyun 	struct drm_connector *connector;
299*4882a593Smuzhiyun 	struct atmel_hlcdc_crtc *crtc;
300*4882a593Smuzhiyun 	int i;
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 	crtc = drm_crtc_to_atmel_hlcdc_crtc(state->crtc);
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	for_each_new_connector_in_state(state->state, connector, cstate, i) {
305*4882a593Smuzhiyun 		unsigned int supported_fmts = 0;
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 		if (!cstate->crtc)
308*4882a593Smuzhiyun 			continue;
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 		supported_fmts = atmel_hlcdc_connector_output_mode(cstate);
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 		if (crtc->dc->desc->conflicting_output_formats)
313*4882a593Smuzhiyun 			output_fmts &= supported_fmts;
314*4882a593Smuzhiyun 		else
315*4882a593Smuzhiyun 			output_fmts |= supported_fmts;
316*4882a593Smuzhiyun 	}
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	if (!output_fmts)
319*4882a593Smuzhiyun 		return -EINVAL;
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun 	hstate = drm_crtc_state_to_atmel_hlcdc_crtc_state(state);
322*4882a593Smuzhiyun 	hstate->output_mode = fls(output_fmts) - 1;
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 	return 0;
325*4882a593Smuzhiyun }
326*4882a593Smuzhiyun 
atmel_hlcdc_crtc_atomic_check(struct drm_crtc * c,struct drm_crtc_state * s)327*4882a593Smuzhiyun static int atmel_hlcdc_crtc_atomic_check(struct drm_crtc *c,
328*4882a593Smuzhiyun 					 struct drm_crtc_state *s)
329*4882a593Smuzhiyun {
330*4882a593Smuzhiyun 	int ret;
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 	ret = atmel_hlcdc_crtc_select_output_mode(s);
333*4882a593Smuzhiyun 	if (ret)
334*4882a593Smuzhiyun 		return ret;
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	ret = atmel_hlcdc_plane_prepare_disc_area(s);
337*4882a593Smuzhiyun 	if (ret)
338*4882a593Smuzhiyun 		return ret;
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 	return atmel_hlcdc_plane_prepare_ahb_routing(s);
341*4882a593Smuzhiyun }
342*4882a593Smuzhiyun 
atmel_hlcdc_crtc_atomic_begin(struct drm_crtc * c,struct drm_crtc_state * old_s)343*4882a593Smuzhiyun static void atmel_hlcdc_crtc_atomic_begin(struct drm_crtc *c,
344*4882a593Smuzhiyun 					  struct drm_crtc_state *old_s)
345*4882a593Smuzhiyun {
346*4882a593Smuzhiyun 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	if (c->state->event) {
349*4882a593Smuzhiyun 		c->state->event->pipe = drm_crtc_index(c);
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun 		WARN_ON(drm_crtc_vblank_get(c) != 0);
352*4882a593Smuzhiyun 
353*4882a593Smuzhiyun 		crtc->event = c->state->event;
354*4882a593Smuzhiyun 		c->state->event = NULL;
355*4882a593Smuzhiyun 	}
356*4882a593Smuzhiyun }
357*4882a593Smuzhiyun 
atmel_hlcdc_crtc_atomic_flush(struct drm_crtc * crtc,struct drm_crtc_state * old_s)358*4882a593Smuzhiyun static void atmel_hlcdc_crtc_atomic_flush(struct drm_crtc *crtc,
359*4882a593Smuzhiyun 					  struct drm_crtc_state *old_s)
360*4882a593Smuzhiyun {
361*4882a593Smuzhiyun 	/* TODO: write common plane control register if available */
362*4882a593Smuzhiyun }
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = {
365*4882a593Smuzhiyun 	.mode_valid = atmel_hlcdc_crtc_mode_valid,
366*4882a593Smuzhiyun 	.mode_set_nofb = atmel_hlcdc_crtc_mode_set_nofb,
367*4882a593Smuzhiyun 	.atomic_check = atmel_hlcdc_crtc_atomic_check,
368*4882a593Smuzhiyun 	.atomic_begin = atmel_hlcdc_crtc_atomic_begin,
369*4882a593Smuzhiyun 	.atomic_flush = atmel_hlcdc_crtc_atomic_flush,
370*4882a593Smuzhiyun 	.atomic_enable = atmel_hlcdc_crtc_atomic_enable,
371*4882a593Smuzhiyun 	.atomic_disable = atmel_hlcdc_crtc_atomic_disable,
372*4882a593Smuzhiyun };
373*4882a593Smuzhiyun 
atmel_hlcdc_crtc_destroy(struct drm_crtc * c)374*4882a593Smuzhiyun static void atmel_hlcdc_crtc_destroy(struct drm_crtc *c)
375*4882a593Smuzhiyun {
376*4882a593Smuzhiyun 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun 	drm_crtc_cleanup(c);
379*4882a593Smuzhiyun 	kfree(crtc);
380*4882a593Smuzhiyun }
381*4882a593Smuzhiyun 
atmel_hlcdc_crtc_finish_page_flip(struct atmel_hlcdc_crtc * crtc)382*4882a593Smuzhiyun static void atmel_hlcdc_crtc_finish_page_flip(struct atmel_hlcdc_crtc *crtc)
383*4882a593Smuzhiyun {
384*4882a593Smuzhiyun 	struct drm_device *dev = crtc->base.dev;
385*4882a593Smuzhiyun 	unsigned long flags;
386*4882a593Smuzhiyun 
387*4882a593Smuzhiyun 	spin_lock_irqsave(&dev->event_lock, flags);
388*4882a593Smuzhiyun 	if (crtc->event) {
389*4882a593Smuzhiyun 		drm_crtc_send_vblank_event(&crtc->base, crtc->event);
390*4882a593Smuzhiyun 		drm_crtc_vblank_put(&crtc->base);
391*4882a593Smuzhiyun 		crtc->event = NULL;
392*4882a593Smuzhiyun 	}
393*4882a593Smuzhiyun 	spin_unlock_irqrestore(&dev->event_lock, flags);
394*4882a593Smuzhiyun }
395*4882a593Smuzhiyun 
atmel_hlcdc_crtc_irq(struct drm_crtc * c)396*4882a593Smuzhiyun void atmel_hlcdc_crtc_irq(struct drm_crtc *c)
397*4882a593Smuzhiyun {
398*4882a593Smuzhiyun 	drm_crtc_handle_vblank(c);
399*4882a593Smuzhiyun 	atmel_hlcdc_crtc_finish_page_flip(drm_crtc_to_atmel_hlcdc_crtc(c));
400*4882a593Smuzhiyun }
401*4882a593Smuzhiyun 
atmel_hlcdc_crtc_reset(struct drm_crtc * crtc)402*4882a593Smuzhiyun static void atmel_hlcdc_crtc_reset(struct drm_crtc *crtc)
403*4882a593Smuzhiyun {
404*4882a593Smuzhiyun 	struct atmel_hlcdc_crtc_state *state;
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun 	if (crtc->state) {
407*4882a593Smuzhiyun 		__drm_atomic_helper_crtc_destroy_state(crtc->state);
408*4882a593Smuzhiyun 		state = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state);
409*4882a593Smuzhiyun 		kfree(state);
410*4882a593Smuzhiyun 		crtc->state = NULL;
411*4882a593Smuzhiyun 	}
412*4882a593Smuzhiyun 
413*4882a593Smuzhiyun 	state = kzalloc(sizeof(*state), GFP_KERNEL);
414*4882a593Smuzhiyun 	if (state)
415*4882a593Smuzhiyun 		__drm_atomic_helper_crtc_reset(crtc, &state->base);
416*4882a593Smuzhiyun }
417*4882a593Smuzhiyun 
418*4882a593Smuzhiyun static struct drm_crtc_state *
atmel_hlcdc_crtc_duplicate_state(struct drm_crtc * crtc)419*4882a593Smuzhiyun atmel_hlcdc_crtc_duplicate_state(struct drm_crtc *crtc)
420*4882a593Smuzhiyun {
421*4882a593Smuzhiyun 	struct atmel_hlcdc_crtc_state *state, *cur;
422*4882a593Smuzhiyun 
423*4882a593Smuzhiyun 	if (WARN_ON(!crtc->state))
424*4882a593Smuzhiyun 		return NULL;
425*4882a593Smuzhiyun 
426*4882a593Smuzhiyun 	state = kmalloc(sizeof(*state), GFP_KERNEL);
427*4882a593Smuzhiyun 	if (!state)
428*4882a593Smuzhiyun 		return NULL;
429*4882a593Smuzhiyun 	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun 	cur = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state);
432*4882a593Smuzhiyun 	state->output_mode = cur->output_mode;
433*4882a593Smuzhiyun 
434*4882a593Smuzhiyun 	return &state->base;
435*4882a593Smuzhiyun }
436*4882a593Smuzhiyun 
atmel_hlcdc_crtc_destroy_state(struct drm_crtc * crtc,struct drm_crtc_state * s)437*4882a593Smuzhiyun static void atmel_hlcdc_crtc_destroy_state(struct drm_crtc *crtc,
438*4882a593Smuzhiyun 					   struct drm_crtc_state *s)
439*4882a593Smuzhiyun {
440*4882a593Smuzhiyun 	struct atmel_hlcdc_crtc_state *state;
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun 	state = drm_crtc_state_to_atmel_hlcdc_crtc_state(s);
443*4882a593Smuzhiyun 	__drm_atomic_helper_crtc_destroy_state(s);
444*4882a593Smuzhiyun 	kfree(state);
445*4882a593Smuzhiyun }
446*4882a593Smuzhiyun 
atmel_hlcdc_crtc_enable_vblank(struct drm_crtc * c)447*4882a593Smuzhiyun static int atmel_hlcdc_crtc_enable_vblank(struct drm_crtc *c)
448*4882a593Smuzhiyun {
449*4882a593Smuzhiyun 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
450*4882a593Smuzhiyun 	struct regmap *regmap = crtc->dc->hlcdc->regmap;
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 	/* Enable SOF (Start Of Frame) interrupt for vblank counting */
453*4882a593Smuzhiyun 	regmap_write(regmap, ATMEL_HLCDC_IER, ATMEL_HLCDC_SOF);
454*4882a593Smuzhiyun 
455*4882a593Smuzhiyun 	return 0;
456*4882a593Smuzhiyun }
457*4882a593Smuzhiyun 
atmel_hlcdc_crtc_disable_vblank(struct drm_crtc * c)458*4882a593Smuzhiyun static void atmel_hlcdc_crtc_disable_vblank(struct drm_crtc *c)
459*4882a593Smuzhiyun {
460*4882a593Smuzhiyun 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
461*4882a593Smuzhiyun 	struct regmap *regmap = crtc->dc->hlcdc->regmap;
462*4882a593Smuzhiyun 
463*4882a593Smuzhiyun 	regmap_write(regmap, ATMEL_HLCDC_IDR, ATMEL_HLCDC_SOF);
464*4882a593Smuzhiyun }
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = {
467*4882a593Smuzhiyun 	.page_flip = drm_atomic_helper_page_flip,
468*4882a593Smuzhiyun 	.set_config = drm_atomic_helper_set_config,
469*4882a593Smuzhiyun 	.destroy = atmel_hlcdc_crtc_destroy,
470*4882a593Smuzhiyun 	.reset = atmel_hlcdc_crtc_reset,
471*4882a593Smuzhiyun 	.atomic_duplicate_state =  atmel_hlcdc_crtc_duplicate_state,
472*4882a593Smuzhiyun 	.atomic_destroy_state = atmel_hlcdc_crtc_destroy_state,
473*4882a593Smuzhiyun 	.enable_vblank = atmel_hlcdc_crtc_enable_vblank,
474*4882a593Smuzhiyun 	.disable_vblank = atmel_hlcdc_crtc_disable_vblank,
475*4882a593Smuzhiyun 	.gamma_set = drm_atomic_helper_legacy_gamma_set,
476*4882a593Smuzhiyun };
477*4882a593Smuzhiyun 
atmel_hlcdc_crtc_create(struct drm_device * dev)478*4882a593Smuzhiyun int atmel_hlcdc_crtc_create(struct drm_device *dev)
479*4882a593Smuzhiyun {
480*4882a593Smuzhiyun 	struct atmel_hlcdc_plane *primary = NULL, *cursor = NULL;
481*4882a593Smuzhiyun 	struct atmel_hlcdc_dc *dc = dev->dev_private;
482*4882a593Smuzhiyun 	struct atmel_hlcdc_crtc *crtc;
483*4882a593Smuzhiyun 	int ret;
484*4882a593Smuzhiyun 	int i;
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun 	crtc = kzalloc(sizeof(*crtc), GFP_KERNEL);
487*4882a593Smuzhiyun 	if (!crtc)
488*4882a593Smuzhiyun 		return -ENOMEM;
489*4882a593Smuzhiyun 
490*4882a593Smuzhiyun 	crtc->dc = dc;
491*4882a593Smuzhiyun 
492*4882a593Smuzhiyun 	for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
493*4882a593Smuzhiyun 		if (!dc->layers[i])
494*4882a593Smuzhiyun 			continue;
495*4882a593Smuzhiyun 
496*4882a593Smuzhiyun 		switch (dc->layers[i]->desc->type) {
497*4882a593Smuzhiyun 		case ATMEL_HLCDC_BASE_LAYER:
498*4882a593Smuzhiyun 			primary = atmel_hlcdc_layer_to_plane(dc->layers[i]);
499*4882a593Smuzhiyun 			break;
500*4882a593Smuzhiyun 
501*4882a593Smuzhiyun 		case ATMEL_HLCDC_CURSOR_LAYER:
502*4882a593Smuzhiyun 			cursor = atmel_hlcdc_layer_to_plane(dc->layers[i]);
503*4882a593Smuzhiyun 			break;
504*4882a593Smuzhiyun 
505*4882a593Smuzhiyun 		default:
506*4882a593Smuzhiyun 			break;
507*4882a593Smuzhiyun 		}
508*4882a593Smuzhiyun 	}
509*4882a593Smuzhiyun 
510*4882a593Smuzhiyun 	ret = drm_crtc_init_with_planes(dev, &crtc->base, &primary->base,
511*4882a593Smuzhiyun 					&cursor->base, &atmel_hlcdc_crtc_funcs,
512*4882a593Smuzhiyun 					NULL);
513*4882a593Smuzhiyun 	if (ret < 0)
514*4882a593Smuzhiyun 		goto fail;
515*4882a593Smuzhiyun 
516*4882a593Smuzhiyun 	crtc->id = drm_crtc_index(&crtc->base);
517*4882a593Smuzhiyun 
518*4882a593Smuzhiyun 	for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
519*4882a593Smuzhiyun 		struct atmel_hlcdc_plane *overlay;
520*4882a593Smuzhiyun 
521*4882a593Smuzhiyun 		if (dc->layers[i] &&
522*4882a593Smuzhiyun 		    dc->layers[i]->desc->type == ATMEL_HLCDC_OVERLAY_LAYER) {
523*4882a593Smuzhiyun 			overlay = atmel_hlcdc_layer_to_plane(dc->layers[i]);
524*4882a593Smuzhiyun 			overlay->base.possible_crtcs = 1 << crtc->id;
525*4882a593Smuzhiyun 		}
526*4882a593Smuzhiyun 	}
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs);
529*4882a593Smuzhiyun 
530*4882a593Smuzhiyun 	drm_mode_crtc_set_gamma_size(&crtc->base, ATMEL_HLCDC_CLUT_SIZE);
531*4882a593Smuzhiyun 	drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
532*4882a593Smuzhiyun 				   ATMEL_HLCDC_CLUT_SIZE);
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 	dc->crtc = &crtc->base;
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 	return 0;
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun fail:
539*4882a593Smuzhiyun 	atmel_hlcdc_crtc_destroy(&crtc->base);
540*4882a593Smuzhiyun 	return ret;
541*4882a593Smuzhiyun }
542