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