281 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Copyright (C) 2019-2022 Bootlin4 * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>5 */6 7#include <linux/of.h>8#include <linux/of_graph.h>9#include <linux/types.h>10#include <linux/workqueue.h>11 12#include <drm/drm_atomic_helper.h>13#include <drm/drm_crtc.h>14#include <drm/drm_drv.h>15#include <drm/drm_gem_dma_helper.h>16#include <drm/drm_print.h>17#include <drm/drm_vblank.h>18 19#include "logicvc_crtc.h"20#include "logicvc_drm.h"21#include "logicvc_interface.h"22#include "logicvc_layer.h"23#include "logicvc_regs.h"24 25#define logicvc_crtc(c) \26 container_of(c, struct logicvc_crtc, drm_crtc)27 28static enum drm_mode_status29logicvc_crtc_mode_valid(struct drm_crtc *drm_crtc,30 const struct drm_display_mode *mode)31{32 if (mode->flags & DRM_MODE_FLAG_INTERLACE)33 return -EINVAL;34 35 return 0;36}37 38static void logicvc_crtc_atomic_begin(struct drm_crtc *drm_crtc,39 struct drm_atomic_state *state)40{41 struct logicvc_crtc *crtc = logicvc_crtc(drm_crtc);42 struct drm_crtc_state *old_state =43 drm_atomic_get_old_crtc_state(state, drm_crtc);44 struct drm_device *drm_dev = drm_crtc->dev;45 unsigned long flags;46 47 /*48 * We need to grab the pending event here if vblank was already enabled49 * since we won't get a call to atomic_enable to grab it.50 */51 if (drm_crtc->state->event && old_state->active) {52 spin_lock_irqsave(&drm_dev->event_lock, flags);53 WARN_ON(drm_crtc_vblank_get(drm_crtc) != 0);54 55 crtc->event = drm_crtc->state->event;56 drm_crtc->state->event = NULL;57 58 spin_unlock_irqrestore(&drm_dev->event_lock, flags);59 }60}61 62static void logicvc_crtc_atomic_enable(struct drm_crtc *drm_crtc,63 struct drm_atomic_state *state)64{65 struct logicvc_crtc *crtc = logicvc_crtc(drm_crtc);66 struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);67 struct drm_crtc_state *old_state =68 drm_atomic_get_old_crtc_state(state, drm_crtc);69 struct drm_crtc_state *new_state =70 drm_atomic_get_new_crtc_state(state, drm_crtc);71 struct drm_display_mode *mode = &new_state->adjusted_mode;72 73 struct drm_device *drm_dev = drm_crtc->dev;74 unsigned int hact, hfp, hsl, hbp;75 unsigned int vact, vfp, vsl, vbp;76 unsigned long flags;77 u32 ctrl;78 79 /* Timings */80 81 hact = mode->hdisplay;82 hfp = mode->hsync_start - mode->hdisplay;83 hsl = mode->hsync_end - mode->hsync_start;84 hbp = mode->htotal - mode->hsync_end;85 86 vact = mode->vdisplay;87 vfp = mode->vsync_start - mode->vdisplay;88 vsl = mode->vsync_end - mode->vsync_start;89 vbp = mode->vtotal - mode->vsync_end;90 91 regmap_write(logicvc->regmap, LOGICVC_HSYNC_FRONT_PORCH_REG, hfp - 1);92 regmap_write(logicvc->regmap, LOGICVC_HSYNC_REG, hsl - 1);93 regmap_write(logicvc->regmap, LOGICVC_HSYNC_BACK_PORCH_REG, hbp - 1);94 regmap_write(logicvc->regmap, LOGICVC_HRES_REG, hact - 1);95 96 regmap_write(logicvc->regmap, LOGICVC_VSYNC_FRONT_PORCH_REG, vfp - 1);97 regmap_write(logicvc->regmap, LOGICVC_VSYNC_REG, vsl - 1);98 regmap_write(logicvc->regmap, LOGICVC_VSYNC_BACK_PORCH_REG, vbp - 1);99 regmap_write(logicvc->regmap, LOGICVC_VRES_REG, vact - 1);100 101 /* Signals */102 103 ctrl = LOGICVC_CTRL_HSYNC_ENABLE | LOGICVC_CTRL_VSYNC_ENABLE |104 LOGICVC_CTRL_DE_ENABLE;105 106 if (mode->flags & DRM_MODE_FLAG_NHSYNC)107 ctrl |= LOGICVC_CTRL_HSYNC_INVERT;108 109 if (mode->flags & DRM_MODE_FLAG_NVSYNC)110 ctrl |= LOGICVC_CTRL_VSYNC_INVERT;111 112 if (logicvc->interface) {113 struct drm_connector *connector =114 &logicvc->interface->drm_connector;115 struct drm_display_info *display_info =116 &connector->display_info;117 118 if (display_info->bus_flags & DRM_BUS_FLAG_DE_LOW)119 ctrl |= LOGICVC_CTRL_DE_INVERT;120 121 if (display_info->bus_flags &122 DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)123 ctrl |= LOGICVC_CTRL_CLOCK_INVERT;124 }125 126 regmap_update_bits(logicvc->regmap, LOGICVC_CTRL_REG,127 LOGICVC_CTRL_HSYNC_ENABLE |128 LOGICVC_CTRL_HSYNC_INVERT |129 LOGICVC_CTRL_VSYNC_ENABLE |130 LOGICVC_CTRL_VSYNC_INVERT |131 LOGICVC_CTRL_DE_ENABLE |132 LOGICVC_CTRL_DE_INVERT |133 LOGICVC_CTRL_PIXEL_INVERT |134 LOGICVC_CTRL_CLOCK_INVERT, ctrl);135 136 /* Generate internal state reset. */137 regmap_write(logicvc->regmap, LOGICVC_DTYPE_REG, 0);138 139 drm_crtc_vblank_on(drm_crtc);140 141 /* Register our event after vblank is enabled. */142 if (drm_crtc->state->event && !old_state->active) {143 spin_lock_irqsave(&drm_dev->event_lock, flags);144 WARN_ON(drm_crtc_vblank_get(drm_crtc) != 0);145 146 crtc->event = drm_crtc->state->event;147 drm_crtc->state->event = NULL;148 spin_unlock_irqrestore(&drm_dev->event_lock, flags);149 }150}151 152static void logicvc_crtc_atomic_disable(struct drm_crtc *drm_crtc,153 struct drm_atomic_state *state)154{155 struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);156 struct drm_device *drm_dev = drm_crtc->dev;157 158 drm_crtc_vblank_off(drm_crtc);159 160 /* Disable and clear CRTC bits. */161 regmap_update_bits(logicvc->regmap, LOGICVC_CTRL_REG,162 LOGICVC_CTRL_HSYNC_ENABLE |163 LOGICVC_CTRL_HSYNC_INVERT |164 LOGICVC_CTRL_VSYNC_ENABLE |165 LOGICVC_CTRL_VSYNC_INVERT |166 LOGICVC_CTRL_DE_ENABLE |167 LOGICVC_CTRL_DE_INVERT |168 LOGICVC_CTRL_PIXEL_INVERT |169 LOGICVC_CTRL_CLOCK_INVERT, 0);170 171 /* Generate internal state reset. */172 regmap_write(logicvc->regmap, LOGICVC_DTYPE_REG, 0);173 174 /* Consume any leftover event since vblank is now disabled. */175 if (drm_crtc->state->event && !drm_crtc->state->active) {176 spin_lock_irq(&drm_dev->event_lock);177 178 drm_crtc_send_vblank_event(drm_crtc, drm_crtc->state->event);179 drm_crtc->state->event = NULL;180 spin_unlock_irq(&drm_dev->event_lock);181 }182}183 184static const struct drm_crtc_helper_funcs logicvc_crtc_helper_funcs = {185 .mode_valid = logicvc_crtc_mode_valid,186 .atomic_begin = logicvc_crtc_atomic_begin,187 .atomic_enable = logicvc_crtc_atomic_enable,188 .atomic_disable = logicvc_crtc_atomic_disable,189};190 191static int logicvc_crtc_enable_vblank(struct drm_crtc *drm_crtc)192{193 struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);194 195 /* Clear any pending V_SYNC interrupt. */196 regmap_write_bits(logicvc->regmap, LOGICVC_INT_STAT_REG,197 LOGICVC_INT_STAT_V_SYNC, LOGICVC_INT_STAT_V_SYNC);198 199 /* Unmask V_SYNC interrupt. */200 regmap_write_bits(logicvc->regmap, LOGICVC_INT_MASK_REG,201 LOGICVC_INT_MASK_V_SYNC, 0);202 203 return 0;204}205 206static void logicvc_crtc_disable_vblank(struct drm_crtc *drm_crtc)207{208 struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);209 210 /* Mask V_SYNC interrupt. */211 regmap_write_bits(logicvc->regmap, LOGICVC_INT_MASK_REG,212 LOGICVC_INT_MASK_V_SYNC, LOGICVC_INT_MASK_V_SYNC);213}214 215static const struct drm_crtc_funcs logicvc_crtc_funcs = {216 .reset = drm_atomic_helper_crtc_reset,217 .destroy = drm_crtc_cleanup,218 .set_config = drm_atomic_helper_set_config,219 .page_flip = drm_atomic_helper_page_flip,220 .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,221 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,222 .enable_vblank = logicvc_crtc_enable_vblank,223 .disable_vblank = logicvc_crtc_disable_vblank,224};225 226void logicvc_crtc_vblank_handler(struct logicvc_drm *logicvc)227{228 struct drm_device *drm_dev = &logicvc->drm_dev;229 struct logicvc_crtc *crtc = logicvc->crtc;230 unsigned long flags;231 232 if (!crtc)233 return;234 235 drm_crtc_handle_vblank(&crtc->drm_crtc);236 237 if (crtc->event) {238 spin_lock_irqsave(&drm_dev->event_lock, flags);239 drm_crtc_send_vblank_event(&crtc->drm_crtc, crtc->event);240 drm_crtc_vblank_put(&crtc->drm_crtc);241 crtc->event = NULL;242 spin_unlock_irqrestore(&drm_dev->event_lock, flags);243 }244}245 246int logicvc_crtc_init(struct logicvc_drm *logicvc)247{248 struct drm_device *drm_dev = &logicvc->drm_dev;249 struct device *dev = drm_dev->dev;250 struct device_node *of_node = dev->of_node;251 struct logicvc_crtc *crtc;252 struct logicvc_layer *layer_primary;253 int ret;254 255 crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL);256 if (!crtc)257 return -ENOMEM;258 259 layer_primary = logicvc_layer_get_primary(logicvc);260 if (!layer_primary) {261 drm_err(drm_dev, "Failed to get primary layer\n");262 return -EINVAL;263 }264 265 ret = drm_crtc_init_with_planes(drm_dev, &crtc->drm_crtc,266 &layer_primary->drm_plane, NULL,267 &logicvc_crtc_funcs, NULL);268 if (ret) {269 drm_err(drm_dev, "Failed to initialize CRTC\n");270 return ret;271 }272 273 drm_crtc_helper_add(&crtc->drm_crtc, &logicvc_crtc_helper_funcs);274 275 crtc->drm_crtc.port = of_graph_get_port_by_id(of_node, 1);276 277 logicvc->crtc = crtc;278 279 return 0;280}281