162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2014 Traphandler 462306a36Sopenharmony_ci * Copyright (C) 2014 Free Electrons 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com> 762306a36Sopenharmony_ci * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/media-bus-format.h> 1262306a36Sopenharmony_ci#include <linux/mfd/atmel-hlcdc.h> 1362306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 1462306a36Sopenharmony_ci#include <linux/pm.h> 1562306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <video/videomode.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <drm/drm_atomic.h> 2062306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 2162306a36Sopenharmony_ci#include <drm/drm_crtc.h> 2262306a36Sopenharmony_ci#include <drm/drm_modeset_helper_vtables.h> 2362306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 2462306a36Sopenharmony_ci#include <drm/drm_vblank.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "atmel_hlcdc_dc.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/** 2962306a36Sopenharmony_ci * struct atmel_hlcdc_crtc_state - Atmel HLCDC CRTC state structure 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * @base: base CRTC state 3262306a36Sopenharmony_ci * @output_mode: RGBXXX output mode 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_cistruct atmel_hlcdc_crtc_state { 3562306a36Sopenharmony_ci struct drm_crtc_state base; 3662306a36Sopenharmony_ci unsigned int output_mode; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic inline struct atmel_hlcdc_crtc_state * 4062306a36Sopenharmony_cidrm_crtc_state_to_atmel_hlcdc_crtc_state(struct drm_crtc_state *state) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci return container_of(state, struct atmel_hlcdc_crtc_state, base); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/** 4662306a36Sopenharmony_ci * struct atmel_hlcdc_crtc - Atmel HLCDC CRTC structure 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * @base: base DRM CRTC structure 4962306a36Sopenharmony_ci * @dc: pointer to the atmel_hlcdc structure provided by the MFD device 5062306a36Sopenharmony_ci * @event: pointer to the current page flip event 5162306a36Sopenharmony_ci * @id: CRTC id (returned by drm_crtc_index) 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_cistruct atmel_hlcdc_crtc { 5462306a36Sopenharmony_ci struct drm_crtc base; 5562306a36Sopenharmony_ci struct atmel_hlcdc_dc *dc; 5662306a36Sopenharmony_ci struct drm_pending_vblank_event *event; 5762306a36Sopenharmony_ci int id; 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic inline struct atmel_hlcdc_crtc * 6162306a36Sopenharmony_cidrm_crtc_to_atmel_hlcdc_crtc(struct drm_crtc *crtc) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci return container_of(crtc, struct atmel_hlcdc_crtc, base); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 6962306a36Sopenharmony_ci struct regmap *regmap = crtc->dc->hlcdc->regmap; 7062306a36Sopenharmony_ci struct drm_display_mode *adj = &c->state->adjusted_mode; 7162306a36Sopenharmony_ci struct drm_encoder *encoder = NULL, *en_iter; 7262306a36Sopenharmony_ci struct drm_connector *connector = NULL; 7362306a36Sopenharmony_ci struct atmel_hlcdc_crtc_state *state; 7462306a36Sopenharmony_ci struct drm_device *ddev = c->dev; 7562306a36Sopenharmony_ci struct drm_connector_list_iter iter; 7662306a36Sopenharmony_ci unsigned long mode_rate; 7762306a36Sopenharmony_ci struct videomode vm; 7862306a36Sopenharmony_ci unsigned long prate; 7962306a36Sopenharmony_ci unsigned int mask = ATMEL_HLCDC_CLKDIV_MASK | ATMEL_HLCDC_CLKPOL; 8062306a36Sopenharmony_ci unsigned int cfg = 0; 8162306a36Sopenharmony_ci int div, ret; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* get encoder from crtc */ 8462306a36Sopenharmony_ci drm_for_each_encoder(en_iter, ddev) { 8562306a36Sopenharmony_ci if (en_iter->crtc == c) { 8662306a36Sopenharmony_ci encoder = en_iter; 8762306a36Sopenharmony_ci break; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (encoder) { 9262306a36Sopenharmony_ci /* Get the connector from encoder */ 9362306a36Sopenharmony_ci drm_connector_list_iter_begin(ddev, &iter); 9462306a36Sopenharmony_ci drm_for_each_connector_iter(connector, &iter) 9562306a36Sopenharmony_ci if (connector->encoder == encoder) 9662306a36Sopenharmony_ci break; 9762306a36Sopenharmony_ci drm_connector_list_iter_end(&iter); 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci ret = clk_prepare_enable(crtc->dc->hlcdc->sys_clk); 10162306a36Sopenharmony_ci if (ret) 10262306a36Sopenharmony_ci return; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci vm.vfront_porch = adj->crtc_vsync_start - adj->crtc_vdisplay; 10562306a36Sopenharmony_ci vm.vback_porch = adj->crtc_vtotal - adj->crtc_vsync_end; 10662306a36Sopenharmony_ci vm.vsync_len = adj->crtc_vsync_end - adj->crtc_vsync_start; 10762306a36Sopenharmony_ci vm.hfront_porch = adj->crtc_hsync_start - adj->crtc_hdisplay; 10862306a36Sopenharmony_ci vm.hback_porch = adj->crtc_htotal - adj->crtc_hsync_end; 10962306a36Sopenharmony_ci vm.hsync_len = adj->crtc_hsync_end - adj->crtc_hsync_start; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci regmap_write(regmap, ATMEL_HLCDC_CFG(1), 11262306a36Sopenharmony_ci (vm.hsync_len - 1) | ((vm.vsync_len - 1) << 16)); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci regmap_write(regmap, ATMEL_HLCDC_CFG(2), 11562306a36Sopenharmony_ci (vm.vfront_porch - 1) | (vm.vback_porch << 16)); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci regmap_write(regmap, ATMEL_HLCDC_CFG(3), 11862306a36Sopenharmony_ci (vm.hfront_porch - 1) | ((vm.hback_porch - 1) << 16)); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci regmap_write(regmap, ATMEL_HLCDC_CFG(4), 12162306a36Sopenharmony_ci (adj->crtc_hdisplay - 1) | 12262306a36Sopenharmony_ci ((adj->crtc_vdisplay - 1) << 16)); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci prate = clk_get_rate(crtc->dc->hlcdc->sys_clk); 12562306a36Sopenharmony_ci mode_rate = adj->crtc_clock * 1000; 12662306a36Sopenharmony_ci if (!crtc->dc->desc->fixed_clksrc) { 12762306a36Sopenharmony_ci prate *= 2; 12862306a36Sopenharmony_ci cfg |= ATMEL_HLCDC_CLKSEL; 12962306a36Sopenharmony_ci mask |= ATMEL_HLCDC_CLKSEL; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci div = DIV_ROUND_UP(prate, mode_rate); 13362306a36Sopenharmony_ci if (div < 2) { 13462306a36Sopenharmony_ci div = 2; 13562306a36Sopenharmony_ci } else if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK) { 13662306a36Sopenharmony_ci /* The divider ended up too big, try a lower base rate. */ 13762306a36Sopenharmony_ci cfg &= ~ATMEL_HLCDC_CLKSEL; 13862306a36Sopenharmony_ci prate /= 2; 13962306a36Sopenharmony_ci div = DIV_ROUND_UP(prate, mode_rate); 14062306a36Sopenharmony_ci if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK) 14162306a36Sopenharmony_ci div = ATMEL_HLCDC_CLKDIV_MASK; 14262306a36Sopenharmony_ci } else { 14362306a36Sopenharmony_ci int div_low = prate / mode_rate; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (div_low >= 2 && 14662306a36Sopenharmony_ci (10 * (prate / div_low - mode_rate) < 14762306a36Sopenharmony_ci (mode_rate - prate / div))) 14862306a36Sopenharmony_ci /* 14962306a36Sopenharmony_ci * At least 10 times better when using a higher 15062306a36Sopenharmony_ci * frequency than requested, instead of a lower. 15162306a36Sopenharmony_ci * So, go with that. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_ci div = div_low; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci cfg |= ATMEL_HLCDC_CLKDIV(div); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (connector && 15962306a36Sopenharmony_ci connector->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) 16062306a36Sopenharmony_ci cfg |= ATMEL_HLCDC_CLKPOL; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0), mask, cfg); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci state = drm_crtc_state_to_atmel_hlcdc_crtc_state(c->state); 16562306a36Sopenharmony_ci cfg = state->output_mode << 8; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (adj->flags & DRM_MODE_FLAG_NVSYNC) 16862306a36Sopenharmony_ci cfg |= ATMEL_HLCDC_VSPOL; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (adj->flags & DRM_MODE_FLAG_NHSYNC) 17162306a36Sopenharmony_ci cfg |= ATMEL_HLCDC_HSPOL; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci regmap_update_bits(regmap, ATMEL_HLCDC_CFG(5), 17462306a36Sopenharmony_ci ATMEL_HLCDC_HSPOL | ATMEL_HLCDC_VSPOL | 17562306a36Sopenharmony_ci ATMEL_HLCDC_VSPDLYS | ATMEL_HLCDC_VSPDLYE | 17662306a36Sopenharmony_ci ATMEL_HLCDC_DISPPOL | ATMEL_HLCDC_DISPDLY | 17762306a36Sopenharmony_ci ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO | 17862306a36Sopenharmony_ci ATMEL_HLCDC_GUARDTIME_MASK | ATMEL_HLCDC_MODE_MASK, 17962306a36Sopenharmony_ci cfg); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci clk_disable_unprepare(crtc->dc->hlcdc->sys_clk); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic enum drm_mode_status 18562306a36Sopenharmony_ciatmel_hlcdc_crtc_mode_valid(struct drm_crtc *c, 18662306a36Sopenharmony_ci const struct drm_display_mode *mode) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci return atmel_hlcdc_dc_mode_valid(crtc->dc, mode); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic void atmel_hlcdc_crtc_atomic_disable(struct drm_crtc *c, 19462306a36Sopenharmony_ci struct drm_atomic_state *state) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct drm_device *dev = c->dev; 19762306a36Sopenharmony_ci struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 19862306a36Sopenharmony_ci struct regmap *regmap = crtc->dc->hlcdc->regmap; 19962306a36Sopenharmony_ci unsigned int status; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci drm_crtc_vblank_off(c); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci pm_runtime_get_sync(dev->dev); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP); 20662306a36Sopenharmony_ci while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 20762306a36Sopenharmony_ci (status & ATMEL_HLCDC_DISP)) 20862306a36Sopenharmony_ci cpu_relax(); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC); 21162306a36Sopenharmony_ci while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 21262306a36Sopenharmony_ci (status & ATMEL_HLCDC_SYNC)) 21362306a36Sopenharmony_ci cpu_relax(); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PIXEL_CLK); 21662306a36Sopenharmony_ci while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 21762306a36Sopenharmony_ci (status & ATMEL_HLCDC_PIXEL_CLK)) 21862306a36Sopenharmony_ci cpu_relax(); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci clk_disable_unprepare(crtc->dc->hlcdc->sys_clk); 22162306a36Sopenharmony_ci pinctrl_pm_select_sleep_state(dev->dev); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci pm_runtime_allow(dev->dev); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci pm_runtime_put_sync(dev->dev); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic void atmel_hlcdc_crtc_atomic_enable(struct drm_crtc *c, 22962306a36Sopenharmony_ci struct drm_atomic_state *state) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct drm_device *dev = c->dev; 23262306a36Sopenharmony_ci struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 23362306a36Sopenharmony_ci struct regmap *regmap = crtc->dc->hlcdc->regmap; 23462306a36Sopenharmony_ci unsigned int status; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci pm_runtime_get_sync(dev->dev); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci pm_runtime_forbid(dev->dev); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci pinctrl_pm_select_default_state(dev->dev); 24162306a36Sopenharmony_ci clk_prepare_enable(crtc->dc->hlcdc->sys_clk); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PIXEL_CLK); 24462306a36Sopenharmony_ci while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 24562306a36Sopenharmony_ci !(status & ATMEL_HLCDC_PIXEL_CLK)) 24662306a36Sopenharmony_ci cpu_relax(); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC); 25062306a36Sopenharmony_ci while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 25162306a36Sopenharmony_ci !(status & ATMEL_HLCDC_SYNC)) 25262306a36Sopenharmony_ci cpu_relax(); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP); 25562306a36Sopenharmony_ci while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 25662306a36Sopenharmony_ci !(status & ATMEL_HLCDC_DISP)) 25762306a36Sopenharmony_ci cpu_relax(); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci pm_runtime_put_sync(dev->dev); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci#define ATMEL_HLCDC_RGB444_OUTPUT BIT(0) 26462306a36Sopenharmony_ci#define ATMEL_HLCDC_RGB565_OUTPUT BIT(1) 26562306a36Sopenharmony_ci#define ATMEL_HLCDC_RGB666_OUTPUT BIT(2) 26662306a36Sopenharmony_ci#define ATMEL_HLCDC_RGB888_OUTPUT BIT(3) 26762306a36Sopenharmony_ci#define ATMEL_HLCDC_OUTPUT_MODE_MASK GENMASK(3, 0) 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int atmel_hlcdc_connector_output_mode(struct drm_connector_state *state) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct drm_connector *connector = state->connector; 27262306a36Sopenharmony_ci struct drm_display_info *info = &connector->display_info; 27362306a36Sopenharmony_ci struct drm_encoder *encoder; 27462306a36Sopenharmony_ci unsigned int supported_fmts = 0; 27562306a36Sopenharmony_ci int j; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci encoder = state->best_encoder; 27862306a36Sopenharmony_ci if (!encoder) 27962306a36Sopenharmony_ci encoder = connector->encoder; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci switch (atmel_hlcdc_encoder_get_bus_fmt(encoder)) { 28262306a36Sopenharmony_ci case 0: 28362306a36Sopenharmony_ci break; 28462306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB444_1X12: 28562306a36Sopenharmony_ci return ATMEL_HLCDC_RGB444_OUTPUT; 28662306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB565_1X16: 28762306a36Sopenharmony_ci return ATMEL_HLCDC_RGB565_OUTPUT; 28862306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB666_1X18: 28962306a36Sopenharmony_ci return ATMEL_HLCDC_RGB666_OUTPUT; 29062306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB888_1X24: 29162306a36Sopenharmony_ci return ATMEL_HLCDC_RGB888_OUTPUT; 29262306a36Sopenharmony_ci default: 29362306a36Sopenharmony_ci return -EINVAL; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci for (j = 0; j < info->num_bus_formats; j++) { 29762306a36Sopenharmony_ci switch (info->bus_formats[j]) { 29862306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB444_1X12: 29962306a36Sopenharmony_ci supported_fmts |= ATMEL_HLCDC_RGB444_OUTPUT; 30062306a36Sopenharmony_ci break; 30162306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB565_1X16: 30262306a36Sopenharmony_ci supported_fmts |= ATMEL_HLCDC_RGB565_OUTPUT; 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB666_1X18: 30562306a36Sopenharmony_ci supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT; 30662306a36Sopenharmony_ci break; 30762306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB888_1X24: 30862306a36Sopenharmony_ci supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT; 30962306a36Sopenharmony_ci break; 31062306a36Sopenharmony_ci default: 31162306a36Sopenharmony_ci break; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return supported_fmts; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci unsigned int output_fmts = ATMEL_HLCDC_OUTPUT_MODE_MASK; 32162306a36Sopenharmony_ci struct atmel_hlcdc_crtc_state *hstate; 32262306a36Sopenharmony_ci struct drm_connector_state *cstate; 32362306a36Sopenharmony_ci struct drm_connector *connector; 32462306a36Sopenharmony_ci struct atmel_hlcdc_crtc *crtc; 32562306a36Sopenharmony_ci int i; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci crtc = drm_crtc_to_atmel_hlcdc_crtc(state->crtc); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci for_each_new_connector_in_state(state->state, connector, cstate, i) { 33062306a36Sopenharmony_ci unsigned int supported_fmts = 0; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (!cstate->crtc) 33362306a36Sopenharmony_ci continue; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci supported_fmts = atmel_hlcdc_connector_output_mode(cstate); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (crtc->dc->desc->conflicting_output_formats) 33862306a36Sopenharmony_ci output_fmts &= supported_fmts; 33962306a36Sopenharmony_ci else 34062306a36Sopenharmony_ci output_fmts |= supported_fmts; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (!output_fmts) 34462306a36Sopenharmony_ci return -EINVAL; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci hstate = drm_crtc_state_to_atmel_hlcdc_crtc_state(state); 34762306a36Sopenharmony_ci hstate->output_mode = fls(output_fmts) - 1; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic int atmel_hlcdc_crtc_atomic_check(struct drm_crtc *c, 35362306a36Sopenharmony_ci struct drm_atomic_state *state) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct drm_crtc_state *s = drm_atomic_get_new_crtc_state(state, c); 35662306a36Sopenharmony_ci int ret; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci ret = atmel_hlcdc_crtc_select_output_mode(s); 35962306a36Sopenharmony_ci if (ret) 36062306a36Sopenharmony_ci return ret; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci ret = atmel_hlcdc_plane_prepare_disc_area(s); 36362306a36Sopenharmony_ci if (ret) 36462306a36Sopenharmony_ci return ret; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return atmel_hlcdc_plane_prepare_ahb_routing(s); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic void atmel_hlcdc_crtc_atomic_begin(struct drm_crtc *c, 37062306a36Sopenharmony_ci struct drm_atomic_state *state) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci drm_crtc_vblank_on(c); 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic void atmel_hlcdc_crtc_atomic_flush(struct drm_crtc *c, 37662306a36Sopenharmony_ci struct drm_atomic_state *state) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 37962306a36Sopenharmony_ci unsigned long flags; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci spin_lock_irqsave(&c->dev->event_lock, flags); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (c->state->event) { 38462306a36Sopenharmony_ci c->state->event->pipe = drm_crtc_index(c); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci WARN_ON(drm_crtc_vblank_get(c) != 0); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci crtc->event = c->state->event; 38962306a36Sopenharmony_ci c->state->event = NULL; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci spin_unlock_irqrestore(&c->dev->event_lock, flags); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = { 39562306a36Sopenharmony_ci .mode_valid = atmel_hlcdc_crtc_mode_valid, 39662306a36Sopenharmony_ci .mode_set_nofb = atmel_hlcdc_crtc_mode_set_nofb, 39762306a36Sopenharmony_ci .atomic_check = atmel_hlcdc_crtc_atomic_check, 39862306a36Sopenharmony_ci .atomic_begin = atmel_hlcdc_crtc_atomic_begin, 39962306a36Sopenharmony_ci .atomic_flush = atmel_hlcdc_crtc_atomic_flush, 40062306a36Sopenharmony_ci .atomic_enable = atmel_hlcdc_crtc_atomic_enable, 40162306a36Sopenharmony_ci .atomic_disable = atmel_hlcdc_crtc_atomic_disable, 40262306a36Sopenharmony_ci}; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic void atmel_hlcdc_crtc_destroy(struct drm_crtc *c) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci drm_crtc_cleanup(c); 40962306a36Sopenharmony_ci kfree(crtc); 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic void atmel_hlcdc_crtc_finish_page_flip(struct atmel_hlcdc_crtc *crtc) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct drm_device *dev = crtc->base.dev; 41562306a36Sopenharmony_ci unsigned long flags; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci spin_lock_irqsave(&dev->event_lock, flags); 41862306a36Sopenharmony_ci if (crtc->event) { 41962306a36Sopenharmony_ci drm_crtc_send_vblank_event(&crtc->base, crtc->event); 42062306a36Sopenharmony_ci drm_crtc_vblank_put(&crtc->base); 42162306a36Sopenharmony_ci crtc->event = NULL; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->event_lock, flags); 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_civoid atmel_hlcdc_crtc_irq(struct drm_crtc *c) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci drm_crtc_handle_vblank(c); 42962306a36Sopenharmony_ci atmel_hlcdc_crtc_finish_page_flip(drm_crtc_to_atmel_hlcdc_crtc(c)); 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic void atmel_hlcdc_crtc_reset(struct drm_crtc *crtc) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct atmel_hlcdc_crtc_state *state; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (crtc->state) { 43762306a36Sopenharmony_ci __drm_atomic_helper_crtc_destroy_state(crtc->state); 43862306a36Sopenharmony_ci state = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state); 43962306a36Sopenharmony_ci kfree(state); 44062306a36Sopenharmony_ci crtc->state = NULL; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci state = kzalloc(sizeof(*state), GFP_KERNEL); 44462306a36Sopenharmony_ci if (state) 44562306a36Sopenharmony_ci __drm_atomic_helper_crtc_reset(crtc, &state->base); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic struct drm_crtc_state * 44962306a36Sopenharmony_ciatmel_hlcdc_crtc_duplicate_state(struct drm_crtc *crtc) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct atmel_hlcdc_crtc_state *state, *cur; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (WARN_ON(!crtc->state)) 45462306a36Sopenharmony_ci return NULL; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci state = kmalloc(sizeof(*state), GFP_KERNEL); 45762306a36Sopenharmony_ci if (!state) 45862306a36Sopenharmony_ci return NULL; 45962306a36Sopenharmony_ci __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci cur = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state); 46262306a36Sopenharmony_ci state->output_mode = cur->output_mode; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return &state->base; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic void atmel_hlcdc_crtc_destroy_state(struct drm_crtc *crtc, 46862306a36Sopenharmony_ci struct drm_crtc_state *s) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct atmel_hlcdc_crtc_state *state; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci state = drm_crtc_state_to_atmel_hlcdc_crtc_state(s); 47362306a36Sopenharmony_ci __drm_atomic_helper_crtc_destroy_state(s); 47462306a36Sopenharmony_ci kfree(state); 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic int atmel_hlcdc_crtc_enable_vblank(struct drm_crtc *c) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 48062306a36Sopenharmony_ci struct regmap *regmap = crtc->dc->hlcdc->regmap; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* Enable SOF (Start Of Frame) interrupt for vblank counting */ 48362306a36Sopenharmony_ci regmap_write(regmap, ATMEL_HLCDC_IER, ATMEL_HLCDC_SOF); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci return 0; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic void atmel_hlcdc_crtc_disable_vblank(struct drm_crtc *c) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 49162306a36Sopenharmony_ci struct regmap *regmap = crtc->dc->hlcdc->regmap; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci regmap_write(regmap, ATMEL_HLCDC_IDR, ATMEL_HLCDC_SOF); 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = { 49762306a36Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 49862306a36Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 49962306a36Sopenharmony_ci .destroy = atmel_hlcdc_crtc_destroy, 50062306a36Sopenharmony_ci .reset = atmel_hlcdc_crtc_reset, 50162306a36Sopenharmony_ci .atomic_duplicate_state = atmel_hlcdc_crtc_duplicate_state, 50262306a36Sopenharmony_ci .atomic_destroy_state = atmel_hlcdc_crtc_destroy_state, 50362306a36Sopenharmony_ci .enable_vblank = atmel_hlcdc_crtc_enable_vblank, 50462306a36Sopenharmony_ci .disable_vblank = atmel_hlcdc_crtc_disable_vblank, 50562306a36Sopenharmony_ci}; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ciint atmel_hlcdc_crtc_create(struct drm_device *dev) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci struct atmel_hlcdc_plane *primary = NULL, *cursor = NULL; 51062306a36Sopenharmony_ci struct atmel_hlcdc_dc *dc = dev->dev_private; 51162306a36Sopenharmony_ci struct atmel_hlcdc_crtc *crtc; 51262306a36Sopenharmony_ci int ret; 51362306a36Sopenharmony_ci int i; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci crtc = kzalloc(sizeof(*crtc), GFP_KERNEL); 51662306a36Sopenharmony_ci if (!crtc) 51762306a36Sopenharmony_ci return -ENOMEM; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci crtc->dc = dc; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 52262306a36Sopenharmony_ci if (!dc->layers[i]) 52362306a36Sopenharmony_ci continue; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci switch (dc->layers[i]->desc->type) { 52662306a36Sopenharmony_ci case ATMEL_HLCDC_BASE_LAYER: 52762306a36Sopenharmony_ci primary = atmel_hlcdc_layer_to_plane(dc->layers[i]); 52862306a36Sopenharmony_ci break; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci case ATMEL_HLCDC_CURSOR_LAYER: 53162306a36Sopenharmony_ci cursor = atmel_hlcdc_layer_to_plane(dc->layers[i]); 53262306a36Sopenharmony_ci break; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci default: 53562306a36Sopenharmony_ci break; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci ret = drm_crtc_init_with_planes(dev, &crtc->base, &primary->base, 54062306a36Sopenharmony_ci &cursor->base, &atmel_hlcdc_crtc_funcs, 54162306a36Sopenharmony_ci NULL); 54262306a36Sopenharmony_ci if (ret < 0) 54362306a36Sopenharmony_ci goto fail; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci crtc->id = drm_crtc_index(&crtc->base); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 54862306a36Sopenharmony_ci struct atmel_hlcdc_plane *overlay; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (dc->layers[i] && 55162306a36Sopenharmony_ci dc->layers[i]->desc->type == ATMEL_HLCDC_OVERLAY_LAYER) { 55262306a36Sopenharmony_ci overlay = atmel_hlcdc_layer_to_plane(dc->layers[i]); 55362306a36Sopenharmony_ci overlay->base.possible_crtcs = 1 << crtc->id; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci drm_mode_crtc_set_gamma_size(&crtc->base, ATMEL_HLCDC_CLUT_SIZE); 56062306a36Sopenharmony_ci drm_crtc_enable_color_mgmt(&crtc->base, 0, false, 56162306a36Sopenharmony_ci ATMEL_HLCDC_CLUT_SIZE); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci dc->crtc = &crtc->base; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci return 0; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cifail: 56862306a36Sopenharmony_ci atmel_hlcdc_crtc_destroy(&crtc->base); 56962306a36Sopenharmony_ci return ret; 57062306a36Sopenharmony_ci} 571