| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Copyright (C) 2019-2022 Bootlin
- * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
- */
- #include <linux/of.h>
- #include <linux/of_graph.h>
- #include <linux/types.h>
- #include <linux/workqueue.h>
- #include <drm/drm_atomic_helper.h>
- #include <drm/drm_crtc.h>
- #include <drm/drm_drv.h>
- #include <drm/drm_gem_dma_helper.h>
- #include <drm/drm_print.h>
- #include <drm/drm_vblank.h>
- #include "logicvc_crtc.h"
- #include "logicvc_drm.h"
- #include "logicvc_interface.h"
- #include "logicvc_layer.h"
- #include "logicvc_regs.h"
- #define logicvc_crtc(c) \
- container_of(c, struct logicvc_crtc, drm_crtc)
- static enum drm_mode_status
- logicvc_crtc_mode_valid(struct drm_crtc *drm_crtc,
- const struct drm_display_mode *mode)
- {
- if (mode->flags & DRM_MODE_FLAG_INTERLACE)
- return -EINVAL;
- return 0;
- }
- static void logicvc_crtc_atomic_begin(struct drm_crtc *drm_crtc,
- struct drm_atomic_state *state)
- {
- struct logicvc_crtc *crtc = logicvc_crtc(drm_crtc);
- struct drm_crtc_state *old_state =
- drm_atomic_get_old_crtc_state(state, drm_crtc);
- struct drm_device *drm_dev = drm_crtc->dev;
- unsigned long flags;
- /*
- * We need to grab the pending event here if vblank was already enabled
- * since we won't get a call to atomic_enable to grab it.
- */
- if (drm_crtc->state->event && old_state->active) {
- spin_lock_irqsave(&drm_dev->event_lock, flags);
- WARN_ON(drm_crtc_vblank_get(drm_crtc) != 0);
- crtc->event = drm_crtc->state->event;
- drm_crtc->state->event = NULL;
- spin_unlock_irqrestore(&drm_dev->event_lock, flags);
- }
- }
- static void logicvc_crtc_atomic_enable(struct drm_crtc *drm_crtc,
- struct drm_atomic_state *state)
- {
- struct logicvc_crtc *crtc = logicvc_crtc(drm_crtc);
- struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);
- struct drm_crtc_state *old_state =
- drm_atomic_get_old_crtc_state(state, drm_crtc);
- struct drm_crtc_state *new_state =
- drm_atomic_get_new_crtc_state(state, drm_crtc);
- struct drm_display_mode *mode = &new_state->adjusted_mode;
- struct drm_device *drm_dev = drm_crtc->dev;
- unsigned int hact, hfp, hsl, hbp;
- unsigned int vact, vfp, vsl, vbp;
- unsigned long flags;
- u32 ctrl;
- /* Timings */
- hact = mode->hdisplay;
- hfp = mode->hsync_start - mode->hdisplay;
- hsl = mode->hsync_end - mode->hsync_start;
- hbp = mode->htotal - mode->hsync_end;
- vact = mode->vdisplay;
- vfp = mode->vsync_start - mode->vdisplay;
- vsl = mode->vsync_end - mode->vsync_start;
- vbp = mode->vtotal - mode->vsync_end;
- regmap_write(logicvc->regmap, LOGICVC_HSYNC_FRONT_PORCH_REG, hfp - 1);
- regmap_write(logicvc->regmap, LOGICVC_HSYNC_REG, hsl - 1);
- regmap_write(logicvc->regmap, LOGICVC_HSYNC_BACK_PORCH_REG, hbp - 1);
- regmap_write(logicvc->regmap, LOGICVC_HRES_REG, hact - 1);
- regmap_write(logicvc->regmap, LOGICVC_VSYNC_FRONT_PORCH_REG, vfp - 1);
- regmap_write(logicvc->regmap, LOGICVC_VSYNC_REG, vsl - 1);
- regmap_write(logicvc->regmap, LOGICVC_VSYNC_BACK_PORCH_REG, vbp - 1);
- regmap_write(logicvc->regmap, LOGICVC_VRES_REG, vact - 1);
- /* Signals */
- ctrl = LOGICVC_CTRL_HSYNC_ENABLE | LOGICVC_CTRL_VSYNC_ENABLE |
- LOGICVC_CTRL_DE_ENABLE;
- if (mode->flags & DRM_MODE_FLAG_NHSYNC)
- ctrl |= LOGICVC_CTRL_HSYNC_INVERT;
- if (mode->flags & DRM_MODE_FLAG_NVSYNC)
- ctrl |= LOGICVC_CTRL_VSYNC_INVERT;
- if (logicvc->interface) {
- struct drm_connector *connector =
- &logicvc->interface->drm_connector;
- struct drm_display_info *display_info =
- &connector->display_info;
- if (display_info->bus_flags & DRM_BUS_FLAG_DE_LOW)
- ctrl |= LOGICVC_CTRL_DE_INVERT;
- if (display_info->bus_flags &
- DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
- ctrl |= LOGICVC_CTRL_CLOCK_INVERT;
- }
- regmap_update_bits(logicvc->regmap, LOGICVC_CTRL_REG,
- LOGICVC_CTRL_HSYNC_ENABLE |
- LOGICVC_CTRL_HSYNC_INVERT |
- LOGICVC_CTRL_VSYNC_ENABLE |
- LOGICVC_CTRL_VSYNC_INVERT |
- LOGICVC_CTRL_DE_ENABLE |
- LOGICVC_CTRL_DE_INVERT |
- LOGICVC_CTRL_PIXEL_INVERT |
- LOGICVC_CTRL_CLOCK_INVERT, ctrl);
- /* Generate internal state reset. */
- regmap_write(logicvc->regmap, LOGICVC_DTYPE_REG, 0);
- drm_crtc_vblank_on(drm_crtc);
- /* Register our event after vblank is enabled. */
- if (drm_crtc->state->event && !old_state->active) {
- spin_lock_irqsave(&drm_dev->event_lock, flags);
- WARN_ON(drm_crtc_vblank_get(drm_crtc) != 0);
- crtc->event = drm_crtc->state->event;
- drm_crtc->state->event = NULL;
- spin_unlock_irqrestore(&drm_dev->event_lock, flags);
- }
- }
- static void logicvc_crtc_atomic_disable(struct drm_crtc *drm_crtc,
- struct drm_atomic_state *state)
- {
- struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);
- struct drm_device *drm_dev = drm_crtc->dev;
- drm_crtc_vblank_off(drm_crtc);
- /* Disable and clear CRTC bits. */
- regmap_update_bits(logicvc->regmap, LOGICVC_CTRL_REG,
- LOGICVC_CTRL_HSYNC_ENABLE |
- LOGICVC_CTRL_HSYNC_INVERT |
- LOGICVC_CTRL_VSYNC_ENABLE |
- LOGICVC_CTRL_VSYNC_INVERT |
- LOGICVC_CTRL_DE_ENABLE |
- LOGICVC_CTRL_DE_INVERT |
- LOGICVC_CTRL_PIXEL_INVERT |
- LOGICVC_CTRL_CLOCK_INVERT, 0);
- /* Generate internal state reset. */
- regmap_write(logicvc->regmap, LOGICVC_DTYPE_REG, 0);
- /* Consume any leftover event since vblank is now disabled. */
- if (drm_crtc->state->event && !drm_crtc->state->active) {
- spin_lock_irq(&drm_dev->event_lock);
- drm_crtc_send_vblank_event(drm_crtc, drm_crtc->state->event);
- drm_crtc->state->event = NULL;
- spin_unlock_irq(&drm_dev->event_lock);
- }
- }
- static const struct drm_crtc_helper_funcs logicvc_crtc_helper_funcs = {
- .mode_valid = logicvc_crtc_mode_valid,
- .atomic_begin = logicvc_crtc_atomic_begin,
- .atomic_enable = logicvc_crtc_atomic_enable,
- .atomic_disable = logicvc_crtc_atomic_disable,
- };
- static int logicvc_crtc_enable_vblank(struct drm_crtc *drm_crtc)
- {
- struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);
- /* Clear any pending V_SYNC interrupt. */
- regmap_write_bits(logicvc->regmap, LOGICVC_INT_STAT_REG,
- LOGICVC_INT_STAT_V_SYNC, LOGICVC_INT_STAT_V_SYNC);
- /* Unmask V_SYNC interrupt. */
- regmap_write_bits(logicvc->regmap, LOGICVC_INT_MASK_REG,
- LOGICVC_INT_MASK_V_SYNC, 0);
- return 0;
- }
- static void logicvc_crtc_disable_vblank(struct drm_crtc *drm_crtc)
- {
- struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);
- /* Mask V_SYNC interrupt. */
- regmap_write_bits(logicvc->regmap, LOGICVC_INT_MASK_REG,
- LOGICVC_INT_MASK_V_SYNC, LOGICVC_INT_MASK_V_SYNC);
- }
- static const struct drm_crtc_funcs logicvc_crtc_funcs = {
- .reset = drm_atomic_helper_crtc_reset,
- .destroy = drm_crtc_cleanup,
- .set_config = drm_atomic_helper_set_config,
- .page_flip = drm_atomic_helper_page_flip,
- .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
- .enable_vblank = logicvc_crtc_enable_vblank,
- .disable_vblank = logicvc_crtc_disable_vblank,
- };
- void logicvc_crtc_vblank_handler(struct logicvc_drm *logicvc)
- {
- struct drm_device *drm_dev = &logicvc->drm_dev;
- struct logicvc_crtc *crtc = logicvc->crtc;
- unsigned long flags;
- if (!crtc)
- return;
- drm_crtc_handle_vblank(&crtc->drm_crtc);
- if (crtc->event) {
- spin_lock_irqsave(&drm_dev->event_lock, flags);
- drm_crtc_send_vblank_event(&crtc->drm_crtc, crtc->event);
- drm_crtc_vblank_put(&crtc->drm_crtc);
- crtc->event = NULL;
- spin_unlock_irqrestore(&drm_dev->event_lock, flags);
- }
- }
- int logicvc_crtc_init(struct logicvc_drm *logicvc)
- {
- struct drm_device *drm_dev = &logicvc->drm_dev;
- struct device *dev = drm_dev->dev;
- struct device_node *of_node = dev->of_node;
- struct logicvc_crtc *crtc;
- struct logicvc_layer *layer_primary;
- int ret;
- crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL);
- if (!crtc)
- return -ENOMEM;
- layer_primary = logicvc_layer_get_primary(logicvc);
- if (!layer_primary) {
- drm_err(drm_dev, "Failed to get primary layer\n");
- return -EINVAL;
- }
- ret = drm_crtc_init_with_planes(drm_dev, &crtc->drm_crtc,
- &layer_primary->drm_plane, NULL,
- &logicvc_crtc_funcs, NULL);
- if (ret) {
- drm_err(drm_dev, "Failed to initialize CRTC\n");
- return ret;
- }
- drm_crtc_helper_add(&crtc->drm_crtc, &logicvc_crtc_helper_funcs);
- crtc->drm_crtc.port = of_graph_get_port_by_id(of_node, 1);
- logicvc->crtc = crtc;
- return 0;
- }
|