Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Copyright (C) 2019-2022 Bootlin
0004  * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
0005  */
0006 
0007 #include <linux/of.h>
0008 #include <linux/of_graph.h>
0009 #include <linux/types.h>
0010 #include <linux/workqueue.h>
0011 
0012 #include <drm/drm_atomic_helper.h>
0013 #include <drm/drm_crtc.h>
0014 #include <drm/drm_drv.h>
0015 #include <drm/drm_gem_cma_helper.h>
0016 #include <drm/drm_print.h>
0017 #include <drm/drm_vblank.h>
0018 
0019 #include "logicvc_crtc.h"
0020 #include "logicvc_drm.h"
0021 #include "logicvc_interface.h"
0022 #include "logicvc_layer.h"
0023 #include "logicvc_regs.h"
0024 
0025 #define logicvc_crtc(c) \
0026     container_of(c, struct logicvc_crtc, drm_crtc)
0027 
0028 static enum drm_mode_status
0029 logicvc_crtc_mode_valid(struct drm_crtc *drm_crtc,
0030             const struct drm_display_mode *mode)
0031 {
0032     if (mode->flags & DRM_MODE_FLAG_INTERLACE)
0033         return -EINVAL;
0034 
0035     return 0;
0036 }
0037 
0038 static void logicvc_crtc_atomic_begin(struct drm_crtc *drm_crtc,
0039                       struct drm_atomic_state *state)
0040 {
0041     struct logicvc_crtc *crtc = logicvc_crtc(drm_crtc);
0042     struct drm_crtc_state *old_state =
0043         drm_atomic_get_old_crtc_state(state, drm_crtc);
0044     struct drm_device *drm_dev = drm_crtc->dev;
0045     unsigned long flags;
0046 
0047     /*
0048      * We need to grab the pending event here if vblank was already enabled
0049      * since we won't get a call to atomic_enable to grab it.
0050      */
0051     if (drm_crtc->state->event && old_state->active) {
0052         spin_lock_irqsave(&drm_dev->event_lock, flags);
0053         WARN_ON(drm_crtc_vblank_get(drm_crtc) != 0);
0054 
0055         crtc->event = drm_crtc->state->event;
0056         drm_crtc->state->event = NULL;
0057 
0058         spin_unlock_irqrestore(&drm_dev->event_lock, flags);
0059     }
0060 }
0061 
0062 static void logicvc_crtc_atomic_enable(struct drm_crtc *drm_crtc,
0063                        struct drm_atomic_state *state)
0064 {
0065     struct logicvc_crtc *crtc = logicvc_crtc(drm_crtc);
0066     struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);
0067     struct drm_crtc_state *old_state =
0068         drm_atomic_get_old_crtc_state(state, drm_crtc);
0069     struct drm_crtc_state *new_state =
0070         drm_atomic_get_new_crtc_state(state, drm_crtc);
0071     struct drm_display_mode *mode = &new_state->adjusted_mode;
0072 
0073     struct drm_device *drm_dev = drm_crtc->dev;
0074     unsigned int hact, hfp, hsl, hbp;
0075     unsigned int vact, vfp, vsl, vbp;
0076     unsigned long flags;
0077     u32 ctrl;
0078 
0079     /* Timings */
0080 
0081     hact = mode->hdisplay;
0082     hfp = mode->hsync_start - mode->hdisplay;
0083     hsl = mode->hsync_end - mode->hsync_start;
0084     hbp = mode->htotal - mode->hsync_end;
0085 
0086     vact = mode->vdisplay;
0087     vfp = mode->vsync_start - mode->vdisplay;
0088     vsl = mode->vsync_end - mode->vsync_start;
0089     vbp = mode->vtotal - mode->vsync_end;
0090 
0091     regmap_write(logicvc->regmap, LOGICVC_HSYNC_FRONT_PORCH_REG, hfp - 1);
0092     regmap_write(logicvc->regmap, LOGICVC_HSYNC_REG, hsl - 1);
0093     regmap_write(logicvc->regmap, LOGICVC_HSYNC_BACK_PORCH_REG, hbp - 1);
0094     regmap_write(logicvc->regmap, LOGICVC_HRES_REG, hact - 1);
0095 
0096     regmap_write(logicvc->regmap, LOGICVC_VSYNC_FRONT_PORCH_REG, vfp - 1);
0097     regmap_write(logicvc->regmap, LOGICVC_VSYNC_REG, vsl - 1);
0098     regmap_write(logicvc->regmap, LOGICVC_VSYNC_BACK_PORCH_REG, vbp - 1);
0099     regmap_write(logicvc->regmap, LOGICVC_VRES_REG, vact - 1);
0100 
0101     /* Signals */
0102 
0103     ctrl = LOGICVC_CTRL_HSYNC_ENABLE | LOGICVC_CTRL_VSYNC_ENABLE |
0104            LOGICVC_CTRL_DE_ENABLE;
0105 
0106     if (mode->flags & DRM_MODE_FLAG_NHSYNC)
0107         ctrl |= LOGICVC_CTRL_HSYNC_INVERT;
0108 
0109     if (mode->flags & DRM_MODE_FLAG_NVSYNC)
0110         ctrl |= LOGICVC_CTRL_VSYNC_INVERT;
0111 
0112     if (logicvc->interface) {
0113         struct drm_connector *connector =
0114             &logicvc->interface->drm_connector;
0115         struct drm_display_info *display_info =
0116             &connector->display_info;
0117 
0118         if (display_info->bus_flags & DRM_BUS_FLAG_DE_LOW)
0119             ctrl |= LOGICVC_CTRL_DE_INVERT;
0120 
0121         if (display_info->bus_flags &
0122             DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
0123             ctrl |= LOGICVC_CTRL_CLOCK_INVERT;
0124     }
0125 
0126     regmap_update_bits(logicvc->regmap, LOGICVC_CTRL_REG,
0127                LOGICVC_CTRL_HSYNC_ENABLE |
0128                LOGICVC_CTRL_HSYNC_INVERT |
0129                LOGICVC_CTRL_VSYNC_ENABLE |
0130                LOGICVC_CTRL_VSYNC_INVERT |
0131                LOGICVC_CTRL_DE_ENABLE |
0132                LOGICVC_CTRL_DE_INVERT |
0133                LOGICVC_CTRL_PIXEL_INVERT |
0134                LOGICVC_CTRL_CLOCK_INVERT, ctrl);
0135 
0136     /* Generate internal state reset. */
0137     regmap_write(logicvc->regmap, LOGICVC_DTYPE_REG, 0);
0138 
0139     drm_crtc_vblank_on(drm_crtc);
0140 
0141     /* Register our event after vblank is enabled. */
0142     if (drm_crtc->state->event && !old_state->active) {
0143         spin_lock_irqsave(&drm_dev->event_lock, flags);
0144         WARN_ON(drm_crtc_vblank_get(drm_crtc) != 0);
0145 
0146         crtc->event = drm_crtc->state->event;
0147         drm_crtc->state->event = NULL;
0148         spin_unlock_irqrestore(&drm_dev->event_lock, flags);
0149     }
0150 }
0151 
0152 static void logicvc_crtc_atomic_disable(struct drm_crtc *drm_crtc,
0153                     struct drm_atomic_state *state)
0154 {
0155     struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);
0156     struct drm_device *drm_dev = drm_crtc->dev;
0157 
0158     drm_crtc_vblank_off(drm_crtc);
0159 
0160     /* Disable and clear CRTC bits. */
0161     regmap_update_bits(logicvc->regmap, LOGICVC_CTRL_REG,
0162                LOGICVC_CTRL_HSYNC_ENABLE |
0163                LOGICVC_CTRL_HSYNC_INVERT |
0164                LOGICVC_CTRL_VSYNC_ENABLE |
0165                LOGICVC_CTRL_VSYNC_INVERT |
0166                LOGICVC_CTRL_DE_ENABLE |
0167                LOGICVC_CTRL_DE_INVERT |
0168                LOGICVC_CTRL_PIXEL_INVERT |
0169                LOGICVC_CTRL_CLOCK_INVERT, 0);
0170 
0171     /* Generate internal state reset. */
0172     regmap_write(logicvc->regmap, LOGICVC_DTYPE_REG, 0);
0173 
0174     /* Consume any leftover event since vblank is now disabled. */
0175     if (drm_crtc->state->event && !drm_crtc->state->active) {
0176         spin_lock_irq(&drm_dev->event_lock);
0177 
0178         drm_crtc_send_vblank_event(drm_crtc, drm_crtc->state->event);
0179         drm_crtc->state->event = NULL;
0180         spin_unlock_irq(&drm_dev->event_lock);
0181     }
0182 }
0183 
0184 static const struct drm_crtc_helper_funcs logicvc_crtc_helper_funcs = {
0185     .mode_valid     = logicvc_crtc_mode_valid,
0186     .atomic_begin       = logicvc_crtc_atomic_begin,
0187     .atomic_enable      = logicvc_crtc_atomic_enable,
0188     .atomic_disable     = logicvc_crtc_atomic_disable,
0189 };
0190 
0191 static int logicvc_crtc_enable_vblank(struct drm_crtc *drm_crtc)
0192 {
0193     struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);
0194 
0195     /* Clear any pending V_SYNC interrupt. */
0196     regmap_write_bits(logicvc->regmap, LOGICVC_INT_STAT_REG,
0197               LOGICVC_INT_STAT_V_SYNC, LOGICVC_INT_STAT_V_SYNC);
0198 
0199     /* Unmask V_SYNC interrupt. */
0200     regmap_write_bits(logicvc->regmap, LOGICVC_INT_MASK_REG,
0201               LOGICVC_INT_MASK_V_SYNC, 0);
0202 
0203     return 0;
0204 }
0205 
0206 static void logicvc_crtc_disable_vblank(struct drm_crtc *drm_crtc)
0207 {
0208     struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);
0209 
0210     /* Mask V_SYNC interrupt. */
0211     regmap_write_bits(logicvc->regmap, LOGICVC_INT_MASK_REG,
0212               LOGICVC_INT_MASK_V_SYNC, LOGICVC_INT_MASK_V_SYNC);
0213 }
0214 
0215 static const struct drm_crtc_funcs logicvc_crtc_funcs = {
0216     .reset          = drm_atomic_helper_crtc_reset,
0217     .destroy        = drm_crtc_cleanup,
0218     .set_config     = drm_atomic_helper_set_config,
0219     .page_flip      = drm_atomic_helper_page_flip,
0220     .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
0221     .atomic_destroy_state   = drm_atomic_helper_crtc_destroy_state,
0222     .enable_vblank      = logicvc_crtc_enable_vblank,
0223     .disable_vblank     = logicvc_crtc_disable_vblank,
0224 };
0225 
0226 void logicvc_crtc_vblank_handler(struct logicvc_drm *logicvc)
0227 {
0228     struct drm_device *drm_dev = &logicvc->drm_dev;
0229     struct logicvc_crtc *crtc = logicvc->crtc;
0230     unsigned long flags;
0231 
0232     if (!crtc)
0233         return;
0234 
0235     drm_crtc_handle_vblank(&crtc->drm_crtc);
0236 
0237     if (crtc->event) {
0238         spin_lock_irqsave(&drm_dev->event_lock, flags);
0239         drm_crtc_send_vblank_event(&crtc->drm_crtc, crtc->event);
0240         drm_crtc_vblank_put(&crtc->drm_crtc);
0241         crtc->event = NULL;
0242         spin_unlock_irqrestore(&drm_dev->event_lock, flags);
0243     }
0244 }
0245 
0246 int logicvc_crtc_init(struct logicvc_drm *logicvc)
0247 {
0248     struct drm_device *drm_dev = &logicvc->drm_dev;
0249     struct device *dev = drm_dev->dev;
0250     struct device_node *of_node = dev->of_node;
0251     struct logicvc_crtc *crtc;
0252     struct logicvc_layer *layer_primary;
0253     int ret;
0254 
0255     crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL);
0256     if (!crtc)
0257         return -ENOMEM;
0258 
0259     layer_primary = logicvc_layer_get_primary(logicvc);
0260     if (!layer_primary) {
0261         drm_err(drm_dev, "Failed to get primary layer\n");
0262         return -EINVAL;
0263     }
0264 
0265     ret = drm_crtc_init_with_planes(drm_dev, &crtc->drm_crtc,
0266                     &layer_primary->drm_plane, NULL,
0267                     &logicvc_crtc_funcs, NULL);
0268     if (ret) {
0269         drm_err(drm_dev, "Failed to initialize CRTC\n");
0270         return ret;
0271     }
0272 
0273     drm_crtc_helper_add(&crtc->drm_crtc, &logicvc_crtc_helper_funcs);
0274 
0275     crtc->drm_crtc.port = of_graph_get_port_by_id(of_node, 1);
0276 
0277     logicvc->crtc = crtc;
0278 
0279     return 0;
0280 }