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