162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2012 Russell King 462306a36Sopenharmony_ci * Rewritten from the dovefb driver, and Armada510 manuals. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/clk.h> 862306a36Sopenharmony_ci#include <linux/component.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/of_device.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <drm/drm_atomic.h> 1462306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 1562306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 1662306a36Sopenharmony_ci#include <drm/drm_vblank.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "armada_crtc.h" 1962306a36Sopenharmony_ci#include "armada_drm.h" 2062306a36Sopenharmony_ci#include "armada_fb.h" 2162306a36Sopenharmony_ci#include "armada_gem.h" 2262306a36Sopenharmony_ci#include "armada_hw.h" 2362306a36Sopenharmony_ci#include "armada_plane.h" 2462306a36Sopenharmony_ci#include "armada_trace.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* 2762306a36Sopenharmony_ci * A note about interlacing. Let's consider HDMI 1920x1080i. 2862306a36Sopenharmony_ci * The timing parameters we have from X are: 2962306a36Sopenharmony_ci * Hact HsyA HsyI Htot Vact VsyA VsyI Vtot 3062306a36Sopenharmony_ci * 1920 2448 2492 2640 1080 1084 1094 1125 3162306a36Sopenharmony_ci * Which get translated to: 3262306a36Sopenharmony_ci * Hact HsyA HsyI Htot Vact VsyA VsyI Vtot 3362306a36Sopenharmony_ci * 1920 2448 2492 2640 540 542 547 562 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * This is how it is defined by CEA-861-D - line and pixel numbers are 3662306a36Sopenharmony_ci * referenced to the rising edge of VSYNC and HSYNC. Total clocks per 3762306a36Sopenharmony_ci * line: 2640. The odd frame, the first active line is at line 21, and 3862306a36Sopenharmony_ci * the even frame, the first active line is 584. 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * LN: 560 561 562 563 567 568 569 4162306a36Sopenharmony_ci * DE: ~~~|____________________________//__________________________ 4262306a36Sopenharmony_ci * HSYNC: ____|~|_____|~|_____|~|_____|~|_//__|~|_____|~|_____|~|_____ 4362306a36Sopenharmony_ci * VSYNC: _________________________|~~~~~~//~~~~~~~~~~~~~~~|__________ 4462306a36Sopenharmony_ci * 22 blanking lines. VSYNC at 1320 (referenced to the HSYNC rising edge). 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * LN: 1123 1124 1125 1 5 6 7 4762306a36Sopenharmony_ci * DE: ~~~|____________________________//__________________________ 4862306a36Sopenharmony_ci * HSYNC: ____|~|_____|~|_____|~|_____|~|_//__|~|_____|~|_____|~|_____ 4962306a36Sopenharmony_ci * VSYNC: ____________________|~~~~~~~~~~~//~~~~~~~~~~|_______________ 5062306a36Sopenharmony_ci * 23 blanking lines 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * The Armada LCD Controller line and pixel numbers are, like X timings, 5362306a36Sopenharmony_ci * referenced to the top left of the active frame. 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * So, translating these to our LCD controller: 5662306a36Sopenharmony_ci * Odd frame, 563 total lines, VSYNC at line 543-548, pixel 1128. 5762306a36Sopenharmony_ci * Even frame, 562 total lines, VSYNC at line 542-547, pixel 2448. 5862306a36Sopenharmony_ci * Note: Vsync front porch remains constant! 5962306a36Sopenharmony_ci * 6062306a36Sopenharmony_ci * if (odd_frame) { 6162306a36Sopenharmony_ci * vtotal = mode->crtc_vtotal + 1; 6262306a36Sopenharmony_ci * vbackporch = mode->crtc_vsync_start - mode->crtc_vdisplay + 1; 6362306a36Sopenharmony_ci * vhorizpos = mode->crtc_hsync_start - mode->crtc_htotal / 2 6462306a36Sopenharmony_ci * } else { 6562306a36Sopenharmony_ci * vtotal = mode->crtc_vtotal; 6662306a36Sopenharmony_ci * vbackporch = mode->crtc_vsync_start - mode->crtc_vdisplay; 6762306a36Sopenharmony_ci * vhorizpos = mode->crtc_hsync_start; 6862306a36Sopenharmony_ci * } 6962306a36Sopenharmony_ci * vfrontporch = mode->crtc_vtotal - mode->crtc_vsync_end; 7062306a36Sopenharmony_ci * 7162306a36Sopenharmony_ci * So, we need to reprogram these registers on each vsync event: 7262306a36Sopenharmony_ci * LCD_SPU_V_PORCH, LCD_SPU_ADV_REG, LCD_SPUT_V_H_TOTAL 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * Note: we do not use the frame done interrupts because these appear 7562306a36Sopenharmony_ci * to happen too early, and lead to jitter on the display (presumably 7662306a36Sopenharmony_ci * they occur at the end of the last active line, before the vsync back 7762306a36Sopenharmony_ci * porch, which we're reprogramming.) 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_civoid 8162306a36Sopenharmony_ciarmada_drm_crtc_update_regs(struct armada_crtc *dcrtc, struct armada_regs *regs) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci while (regs->offset != ~0) { 8462306a36Sopenharmony_ci void __iomem *reg = dcrtc->base + regs->offset; 8562306a36Sopenharmony_ci uint32_t val; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci val = regs->mask; 8862306a36Sopenharmony_ci if (val != 0) 8962306a36Sopenharmony_ci val &= readl_relaxed(reg); 9062306a36Sopenharmony_ci writel_relaxed(val | regs->val, reg); 9162306a36Sopenharmony_ci ++regs; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic void armada_drm_crtc_update(struct armada_crtc *dcrtc, bool enable) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci uint32_t dumb_ctrl; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci dumb_ctrl = dcrtc->cfg_dumb_ctrl; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (enable) 10262306a36Sopenharmony_ci dumb_ctrl |= CFG_DUMB_ENA; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* 10562306a36Sopenharmony_ci * When the dumb interface isn't in DUMB24_RGB888_0 mode, it might 10662306a36Sopenharmony_ci * be using SPI or GPIO. If we set this to DUMB_BLANK, we will 10762306a36Sopenharmony_ci * force LCD_D[23:0] to output blank color, overriding the GPIO or 10862306a36Sopenharmony_ci * SPI usage. So leave it as-is unless in DUMB24_RGB888_0 mode. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci if (!enable && (dumb_ctrl & DUMB_MASK) == DUMB24_RGB888_0) { 11162306a36Sopenharmony_ci dumb_ctrl &= ~DUMB_MASK; 11262306a36Sopenharmony_ci dumb_ctrl |= DUMB_BLANK; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci armada_updatel(dumb_ctrl, 11662306a36Sopenharmony_ci ~(CFG_INV_CSYNC | CFG_INV_HSYNC | CFG_INV_VSYNC), 11762306a36Sopenharmony_ci dcrtc->base + LCD_SPU_DUMB_CTRL); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void armada_drm_crtc_queue_state_event(struct drm_crtc *crtc) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 12362306a36Sopenharmony_ci struct drm_pending_vblank_event *event; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* If we have an event, we need vblank events enabled */ 12662306a36Sopenharmony_ci event = xchg(&crtc->state->event, NULL); 12762306a36Sopenharmony_ci if (event) { 12862306a36Sopenharmony_ci WARN_ON(drm_crtc_vblank_get(crtc) != 0); 12962306a36Sopenharmony_ci dcrtc->event = event; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void armada_drm_update_gamma(struct drm_crtc *crtc) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct drm_property_blob *blob = crtc->state->gamma_lut; 13662306a36Sopenharmony_ci void __iomem *base = drm_to_armada_crtc(crtc)->base; 13762306a36Sopenharmony_ci int i; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (blob) { 14062306a36Sopenharmony_ci struct drm_color_lut *lut = blob->data; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci armada_updatel(CFG_CSB_256x8, CFG_CSB_256x8 | CFG_PDWN256x8, 14362306a36Sopenharmony_ci base + LCD_SPU_SRAM_PARA1); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci for (i = 0; i < 256; i++) { 14662306a36Sopenharmony_ci writel_relaxed(drm_color_lut_extract(lut[i].red, 8), 14762306a36Sopenharmony_ci base + LCD_SPU_SRAM_WRDAT); 14862306a36Sopenharmony_ci writel_relaxed(i | SRAM_WRITE | SRAM_GAMMA_YR, 14962306a36Sopenharmony_ci base + LCD_SPU_SRAM_CTRL); 15062306a36Sopenharmony_ci readl_relaxed(base + LCD_SPU_HWC_OVSA_HPXL_VLN); 15162306a36Sopenharmony_ci writel_relaxed(drm_color_lut_extract(lut[i].green, 8), 15262306a36Sopenharmony_ci base + LCD_SPU_SRAM_WRDAT); 15362306a36Sopenharmony_ci writel_relaxed(i | SRAM_WRITE | SRAM_GAMMA_UG, 15462306a36Sopenharmony_ci base + LCD_SPU_SRAM_CTRL); 15562306a36Sopenharmony_ci readl_relaxed(base + LCD_SPU_HWC_OVSA_HPXL_VLN); 15662306a36Sopenharmony_ci writel_relaxed(drm_color_lut_extract(lut[i].blue, 8), 15762306a36Sopenharmony_ci base + LCD_SPU_SRAM_WRDAT); 15862306a36Sopenharmony_ci writel_relaxed(i | SRAM_WRITE | SRAM_GAMMA_VB, 15962306a36Sopenharmony_ci base + LCD_SPU_SRAM_CTRL); 16062306a36Sopenharmony_ci readl_relaxed(base + LCD_SPU_HWC_OVSA_HPXL_VLN); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci armada_updatel(CFG_GAMMA_ENA, CFG_GAMMA_ENA, 16362306a36Sopenharmony_ci base + LCD_SPU_DMA_CTRL0); 16462306a36Sopenharmony_ci } else { 16562306a36Sopenharmony_ci armada_updatel(0, CFG_GAMMA_ENA, base + LCD_SPU_DMA_CTRL0); 16662306a36Sopenharmony_ci armada_updatel(CFG_PDWN256x8, CFG_CSB_256x8 | CFG_PDWN256x8, 16762306a36Sopenharmony_ci base + LCD_SPU_SRAM_PARA1); 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic enum drm_mode_status armada_drm_crtc_mode_valid(struct drm_crtc *crtc, 17262306a36Sopenharmony_ci const struct drm_display_mode *mode) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (mode->vscan > 1) 17762306a36Sopenharmony_ci return MODE_NO_VSCAN; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_DBLSCAN) 18062306a36Sopenharmony_ci return MODE_NO_DBLESCAN; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_HSKEW) 18362306a36Sopenharmony_ci return MODE_H_ILLEGAL; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* We can't do interlaced modes if we don't have the SPU_ADV_REG */ 18662306a36Sopenharmony_ci if (!dcrtc->variant->has_spu_adv_reg && 18762306a36Sopenharmony_ci mode->flags & DRM_MODE_FLAG_INTERLACE) 18862306a36Sopenharmony_ci return MODE_NO_INTERLACE; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (mode->flags & (DRM_MODE_FLAG_BCAST | DRM_MODE_FLAG_PIXMUX | 19162306a36Sopenharmony_ci DRM_MODE_FLAG_CLKDIV2)) 19262306a36Sopenharmony_ci return MODE_BAD; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return MODE_OK; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/* The mode_config.mutex will be held for this call */ 19862306a36Sopenharmony_cistatic bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc, 19962306a36Sopenharmony_ci const struct drm_display_mode *mode, struct drm_display_mode *adj) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 20262306a36Sopenharmony_ci int ret; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* 20562306a36Sopenharmony_ci * Set CRTC modesetting parameters for the adjusted mode. This is 20662306a36Sopenharmony_ci * applied after the connectors, bridges, and encoders have fixed up 20762306a36Sopenharmony_ci * this mode, as described above drm_atomic_helper_check_modeset(). 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci drm_mode_set_crtcinfo(adj, CRTC_INTERLACE_HALVE_V); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* 21262306a36Sopenharmony_ci * Validate the adjusted mode in case an encoder/bridge has set 21362306a36Sopenharmony_ci * something we don't support. 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ci if (armada_drm_crtc_mode_valid(crtc, adj) != MODE_OK) 21662306a36Sopenharmony_ci return false; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* Check whether the display mode is possible */ 21962306a36Sopenharmony_ci ret = dcrtc->variant->compute_clock(dcrtc, adj, NULL); 22062306a36Sopenharmony_ci if (ret) 22162306a36Sopenharmony_ci return false; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci return true; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/* These are locked by dev->vbl_lock */ 22762306a36Sopenharmony_cistatic void armada_drm_crtc_disable_irq(struct armada_crtc *dcrtc, u32 mask) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci if (dcrtc->irq_ena & mask) { 23062306a36Sopenharmony_ci dcrtc->irq_ena &= ~mask; 23162306a36Sopenharmony_ci writel(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA); 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic void armada_drm_crtc_enable_irq(struct armada_crtc *dcrtc, u32 mask) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci if ((dcrtc->irq_ena & mask) != mask) { 23862306a36Sopenharmony_ci dcrtc->irq_ena |= mask; 23962306a36Sopenharmony_ci writel(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA); 24062306a36Sopenharmony_ci if (readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR) & mask) 24162306a36Sopenharmony_ci writel(0, dcrtc->base + LCD_SPU_IRQ_ISR); 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct drm_pending_vblank_event *event; 24862306a36Sopenharmony_ci void __iomem *base = dcrtc->base; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (stat & DMA_FF_UNDERFLOW) 25162306a36Sopenharmony_ci DRM_ERROR("video underflow on crtc %u\n", dcrtc->num); 25262306a36Sopenharmony_ci if (stat & GRA_FF_UNDERFLOW) 25362306a36Sopenharmony_ci DRM_ERROR("graphics underflow on crtc %u\n", dcrtc->num); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (stat & VSYNC_IRQ) 25662306a36Sopenharmony_ci drm_crtc_handle_vblank(&dcrtc->crtc); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci spin_lock(&dcrtc->irq_lock); 25962306a36Sopenharmony_ci if (stat & GRA_FRAME_IRQ && dcrtc->interlaced) { 26062306a36Sopenharmony_ci int i = stat & GRA_FRAME_IRQ0 ? 0 : 1; 26162306a36Sopenharmony_ci uint32_t val; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci writel_relaxed(dcrtc->v[i].spu_v_porch, base + LCD_SPU_V_PORCH); 26462306a36Sopenharmony_ci writel_relaxed(dcrtc->v[i].spu_v_h_total, 26562306a36Sopenharmony_ci base + LCD_SPUT_V_H_TOTAL); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci val = readl_relaxed(base + LCD_SPU_ADV_REG); 26862306a36Sopenharmony_ci val &= ~(ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | ADV_VSYNCOFFEN); 26962306a36Sopenharmony_ci val |= dcrtc->v[i].spu_adv_reg; 27062306a36Sopenharmony_ci writel_relaxed(val, base + LCD_SPU_ADV_REG); 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (stat & dcrtc->irq_ena & DUMB_FRAMEDONE) { 27462306a36Sopenharmony_ci if (dcrtc->update_pending) { 27562306a36Sopenharmony_ci armada_drm_crtc_update_regs(dcrtc, dcrtc->regs); 27662306a36Sopenharmony_ci dcrtc->update_pending = false; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci if (dcrtc->cursor_update) { 27962306a36Sopenharmony_ci writel_relaxed(dcrtc->cursor_hw_pos, 28062306a36Sopenharmony_ci base + LCD_SPU_HWC_OVSA_HPXL_VLN); 28162306a36Sopenharmony_ci writel_relaxed(dcrtc->cursor_hw_sz, 28262306a36Sopenharmony_ci base + LCD_SPU_HWC_HPXL_VLN); 28362306a36Sopenharmony_ci armada_updatel(CFG_HWC_ENA, 28462306a36Sopenharmony_ci CFG_HWC_ENA | CFG_HWC_1BITMOD | 28562306a36Sopenharmony_ci CFG_HWC_1BITENA, 28662306a36Sopenharmony_ci base + LCD_SPU_DMA_CTRL0); 28762306a36Sopenharmony_ci dcrtc->cursor_update = false; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA); 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci spin_unlock(&dcrtc->irq_lock); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (stat & VSYNC_IRQ && !dcrtc->update_pending) { 29462306a36Sopenharmony_ci event = xchg(&dcrtc->event, NULL); 29562306a36Sopenharmony_ci if (event) { 29662306a36Sopenharmony_ci spin_lock(&dcrtc->crtc.dev->event_lock); 29762306a36Sopenharmony_ci drm_crtc_send_vblank_event(&dcrtc->crtc, event); 29862306a36Sopenharmony_ci spin_unlock(&dcrtc->crtc.dev->event_lock); 29962306a36Sopenharmony_ci drm_crtc_vblank_put(&dcrtc->crtc); 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic irqreturn_t armada_drm_irq(int irq, void *arg) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct armada_crtc *dcrtc = arg; 30762306a36Sopenharmony_ci u32 v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* 31062306a36Sopenharmony_ci * Reading the ISR appears to clear bits provided CLEAN_SPU_IRQ_ISR 31162306a36Sopenharmony_ci * is set. Writing has some other effect to acknowledge the IRQ - 31262306a36Sopenharmony_ci * without this, we only get a single IRQ. 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_ci writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci trace_armada_drm_irq(&dcrtc->crtc, stat); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* Mask out those interrupts we haven't enabled */ 31962306a36Sopenharmony_ci v = stat & dcrtc->irq_ena; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (v & (VSYNC_IRQ|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) { 32262306a36Sopenharmony_ci armada_drm_crtc_irq(dcrtc, stat); 32362306a36Sopenharmony_ci return IRQ_HANDLED; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci return IRQ_NONE; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci/* The mode_config.mutex will be held for this call */ 32962306a36Sopenharmony_cistatic void armada_drm_crtc_mode_set_nofb(struct drm_crtc *crtc) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct drm_display_mode *adj = &crtc->state->adjusted_mode; 33262306a36Sopenharmony_ci struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 33362306a36Sopenharmony_ci struct armada_regs regs[17]; 33462306a36Sopenharmony_ci uint32_t lm, rm, tm, bm, val, sclk; 33562306a36Sopenharmony_ci unsigned long flags; 33662306a36Sopenharmony_ci unsigned i; 33762306a36Sopenharmony_ci bool interlaced = !!(adj->flags & DRM_MODE_FLAG_INTERLACE); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci i = 0; 34062306a36Sopenharmony_ci rm = adj->crtc_hsync_start - adj->crtc_hdisplay; 34162306a36Sopenharmony_ci lm = adj->crtc_htotal - adj->crtc_hsync_end; 34262306a36Sopenharmony_ci bm = adj->crtc_vsync_start - adj->crtc_vdisplay; 34362306a36Sopenharmony_ci tm = adj->crtc_vtotal - adj->crtc_vsync_end; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci DRM_DEBUG_KMS("[CRTC:%d:%s] mode " DRM_MODE_FMT "\n", 34662306a36Sopenharmony_ci crtc->base.id, crtc->name, DRM_MODE_ARG(adj)); 34762306a36Sopenharmony_ci DRM_DEBUG_KMS("lm %d rm %d tm %d bm %d\n", lm, rm, tm, bm); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* Now compute the divider for real */ 35062306a36Sopenharmony_ci dcrtc->variant->compute_clock(dcrtc, adj, &sclk); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci armada_reg_queue_set(regs, i, sclk, LCD_CFG_SCLK_DIV); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci spin_lock_irqsave(&dcrtc->irq_lock, flags); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci dcrtc->interlaced = interlaced; 35762306a36Sopenharmony_ci /* Even interlaced/progressive frame */ 35862306a36Sopenharmony_ci dcrtc->v[1].spu_v_h_total = adj->crtc_vtotal << 16 | 35962306a36Sopenharmony_ci adj->crtc_htotal; 36062306a36Sopenharmony_ci dcrtc->v[1].spu_v_porch = tm << 16 | bm; 36162306a36Sopenharmony_ci val = adj->crtc_hsync_start; 36262306a36Sopenharmony_ci dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (interlaced) { 36562306a36Sopenharmony_ci /* Odd interlaced frame */ 36662306a36Sopenharmony_ci val -= adj->crtc_htotal / 2; 36762306a36Sopenharmony_ci dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN; 36862306a36Sopenharmony_ci dcrtc->v[0].spu_v_h_total = dcrtc->v[1].spu_v_h_total + 36962306a36Sopenharmony_ci (1 << 16); 37062306a36Sopenharmony_ci dcrtc->v[0].spu_v_porch = dcrtc->v[1].spu_v_porch + 1; 37162306a36Sopenharmony_ci } else { 37262306a36Sopenharmony_ci dcrtc->v[0] = dcrtc->v[1]; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci val = adj->crtc_vdisplay << 16 | adj->crtc_hdisplay; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci armada_reg_queue_set(regs, i, val, LCD_SPU_V_H_ACTIVE); 37862306a36Sopenharmony_ci armada_reg_queue_set(regs, i, (lm << 16) | rm, LCD_SPU_H_PORCH); 37962306a36Sopenharmony_ci armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_porch, LCD_SPU_V_PORCH); 38062306a36Sopenharmony_ci armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total, 38162306a36Sopenharmony_ci LCD_SPUT_V_H_TOTAL); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (dcrtc->variant->has_spu_adv_reg) 38462306a36Sopenharmony_ci armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg, 38562306a36Sopenharmony_ci ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | 38662306a36Sopenharmony_ci ADV_VSYNCOFFEN, LCD_SPU_ADV_REG); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci val = adj->flags & DRM_MODE_FLAG_NVSYNC ? CFG_VSYNC_INV : 0; 38962306a36Sopenharmony_ci armada_reg_queue_mod(regs, i, val, CFG_VSYNC_INV, LCD_SPU_DMA_CTRL1); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* 39262306a36Sopenharmony_ci * The documentation doesn't indicate what the normal state of 39362306a36Sopenharmony_ci * the sync signals are. Sebastian Hesselbart kindly probed 39462306a36Sopenharmony_ci * these signals on his board to determine their state. 39562306a36Sopenharmony_ci * 39662306a36Sopenharmony_ci * The non-inverted state of the sync signals is active high. 39762306a36Sopenharmony_ci * Setting these bits makes the appropriate signal active low. 39862306a36Sopenharmony_ci */ 39962306a36Sopenharmony_ci val = 0; 40062306a36Sopenharmony_ci if (adj->flags & DRM_MODE_FLAG_NCSYNC) 40162306a36Sopenharmony_ci val |= CFG_INV_CSYNC; 40262306a36Sopenharmony_ci if (adj->flags & DRM_MODE_FLAG_NHSYNC) 40362306a36Sopenharmony_ci val |= CFG_INV_HSYNC; 40462306a36Sopenharmony_ci if (adj->flags & DRM_MODE_FLAG_NVSYNC) 40562306a36Sopenharmony_ci val |= CFG_INV_VSYNC; 40662306a36Sopenharmony_ci armada_reg_queue_mod(regs, i, val, CFG_INV_CSYNC | CFG_INV_HSYNC | 40762306a36Sopenharmony_ci CFG_INV_VSYNC, LCD_SPU_DUMB_CTRL); 40862306a36Sopenharmony_ci armada_reg_queue_end(regs, i); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci armada_drm_crtc_update_regs(dcrtc, regs); 41162306a36Sopenharmony_ci spin_unlock_irqrestore(&dcrtc->irq_lock, flags); 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic int armada_drm_crtc_atomic_check(struct drm_crtc *crtc, 41562306a36Sopenharmony_ci struct drm_atomic_state *state) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, 41862306a36Sopenharmony_ci crtc); 41962306a36Sopenharmony_ci DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (crtc_state->gamma_lut && drm_color_lut_size(crtc_state->gamma_lut) != 256) 42262306a36Sopenharmony_ci return -EINVAL; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (crtc_state->color_mgmt_changed) 42562306a36Sopenharmony_ci crtc_state->planes_changed = true; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci return 0; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic void armada_drm_crtc_atomic_begin(struct drm_crtc *crtc, 43162306a36Sopenharmony_ci struct drm_atomic_state *state) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, 43462306a36Sopenharmony_ci crtc); 43562306a36Sopenharmony_ci struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (crtc_state->color_mgmt_changed) 44062306a36Sopenharmony_ci armada_drm_update_gamma(crtc); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci dcrtc->regs_idx = 0; 44362306a36Sopenharmony_ci dcrtc->regs = dcrtc->atomic_regs; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic void armada_drm_crtc_atomic_flush(struct drm_crtc *crtc, 44762306a36Sopenharmony_ci struct drm_atomic_state *state) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, 45062306a36Sopenharmony_ci crtc); 45162306a36Sopenharmony_ci struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci armada_reg_queue_end(dcrtc->regs, dcrtc->regs_idx); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* 45862306a36Sopenharmony_ci * If we aren't doing a full modeset, then we need to queue 45962306a36Sopenharmony_ci * the event here. 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_ci if (!drm_atomic_crtc_needs_modeset(crtc_state)) { 46262306a36Sopenharmony_ci dcrtc->update_pending = true; 46362306a36Sopenharmony_ci armada_drm_crtc_queue_state_event(crtc); 46462306a36Sopenharmony_ci spin_lock_irq(&dcrtc->irq_lock); 46562306a36Sopenharmony_ci armada_drm_crtc_enable_irq(dcrtc, DUMB_FRAMEDONE_ENA); 46662306a36Sopenharmony_ci spin_unlock_irq(&dcrtc->irq_lock); 46762306a36Sopenharmony_ci } else { 46862306a36Sopenharmony_ci spin_lock_irq(&dcrtc->irq_lock); 46962306a36Sopenharmony_ci armada_drm_crtc_update_regs(dcrtc, dcrtc->regs); 47062306a36Sopenharmony_ci spin_unlock_irq(&dcrtc->irq_lock); 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic void armada_drm_crtc_atomic_disable(struct drm_crtc *crtc, 47562306a36Sopenharmony_ci struct drm_atomic_state *state) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, 47862306a36Sopenharmony_ci crtc); 47962306a36Sopenharmony_ci struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 48062306a36Sopenharmony_ci struct drm_pending_vblank_event *event; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (old_state->adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE) 48562306a36Sopenharmony_ci drm_crtc_vblank_put(crtc); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci drm_crtc_vblank_off(crtc); 48862306a36Sopenharmony_ci armada_drm_crtc_update(dcrtc, false); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (!crtc->state->active) { 49162306a36Sopenharmony_ci /* 49262306a36Sopenharmony_ci * This modeset will be leaving the CRTC disabled, so 49362306a36Sopenharmony_ci * call the backend to disable upstream clocks etc. 49462306a36Sopenharmony_ci */ 49562306a36Sopenharmony_ci if (dcrtc->variant->disable) 49662306a36Sopenharmony_ci dcrtc->variant->disable(dcrtc); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* 49962306a36Sopenharmony_ci * We will not receive any further vblank events. 50062306a36Sopenharmony_ci * Send the flip_done event manually. 50162306a36Sopenharmony_ci */ 50262306a36Sopenharmony_ci event = crtc->state->event; 50362306a36Sopenharmony_ci crtc->state->event = NULL; 50462306a36Sopenharmony_ci if (event) { 50562306a36Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 50662306a36Sopenharmony_ci drm_crtc_send_vblank_event(crtc, event); 50762306a36Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cistatic void armada_drm_crtc_atomic_enable(struct drm_crtc *crtc, 51362306a36Sopenharmony_ci struct drm_atomic_state *state) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, 51662306a36Sopenharmony_ci crtc); 51762306a36Sopenharmony_ci struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (!old_state->active) { 52262306a36Sopenharmony_ci /* 52362306a36Sopenharmony_ci * This modeset is enabling the CRTC after it having 52462306a36Sopenharmony_ci * been disabled. Reverse the call to ->disable in 52562306a36Sopenharmony_ci * the atomic_disable(). 52662306a36Sopenharmony_ci */ 52762306a36Sopenharmony_ci if (dcrtc->variant->enable) 52862306a36Sopenharmony_ci dcrtc->variant->enable(dcrtc, &crtc->state->adjusted_mode); 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci armada_drm_crtc_update(dcrtc, true); 53162306a36Sopenharmony_ci drm_crtc_vblank_on(crtc); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (crtc->state->adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE) 53462306a36Sopenharmony_ci WARN_ON(drm_crtc_vblank_get(crtc)); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci armada_drm_crtc_queue_state_event(crtc); 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = { 54062306a36Sopenharmony_ci .mode_valid = armada_drm_crtc_mode_valid, 54162306a36Sopenharmony_ci .mode_fixup = armada_drm_crtc_mode_fixup, 54262306a36Sopenharmony_ci .mode_set_nofb = armada_drm_crtc_mode_set_nofb, 54362306a36Sopenharmony_ci .atomic_check = armada_drm_crtc_atomic_check, 54462306a36Sopenharmony_ci .atomic_begin = armada_drm_crtc_atomic_begin, 54562306a36Sopenharmony_ci .atomic_flush = armada_drm_crtc_atomic_flush, 54662306a36Sopenharmony_ci .atomic_disable = armada_drm_crtc_atomic_disable, 54762306a36Sopenharmony_ci .atomic_enable = armada_drm_crtc_atomic_enable, 54862306a36Sopenharmony_ci}; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic void armada_load_cursor_argb(void __iomem *base, uint32_t *pix, 55162306a36Sopenharmony_ci unsigned stride, unsigned width, unsigned height) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci uint32_t addr; 55462306a36Sopenharmony_ci unsigned y; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci addr = SRAM_HWC32_RAM1; 55762306a36Sopenharmony_ci for (y = 0; y < height; y++) { 55862306a36Sopenharmony_ci uint32_t *p = &pix[y * stride]; 55962306a36Sopenharmony_ci unsigned x; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci for (x = 0; x < width; x++, p++) { 56262306a36Sopenharmony_ci uint32_t val = *p; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* 56562306a36Sopenharmony_ci * In "ARGB888" (HWC32) mode, writing to the SRAM 56662306a36Sopenharmony_ci * requires these bits to contain: 56762306a36Sopenharmony_ci * 31:24 = alpha 23:16 = blue 15:8 = green 7:0 = red 56862306a36Sopenharmony_ci * So, it's actually ABGR8888. This is independent 56962306a36Sopenharmony_ci * of the SWAPRB bits in DMA control register 0. 57062306a36Sopenharmony_ci */ 57162306a36Sopenharmony_ci val = (val & 0xff00ff00) | 57262306a36Sopenharmony_ci (val & 0x000000ff) << 16 | 57362306a36Sopenharmony_ci (val & 0x00ff0000) >> 16; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci writel_relaxed(val, 57662306a36Sopenharmony_ci base + LCD_SPU_SRAM_WRDAT); 57762306a36Sopenharmony_ci writel_relaxed(addr | SRAM_WRITE, 57862306a36Sopenharmony_ci base + LCD_SPU_SRAM_CTRL); 57962306a36Sopenharmony_ci readl_relaxed(base + LCD_SPU_HWC_OVSA_HPXL_VLN); 58062306a36Sopenharmony_ci addr += 1; 58162306a36Sopenharmony_ci if ((addr & 0x00ff) == 0) 58262306a36Sopenharmony_ci addr += 0xf00; 58362306a36Sopenharmony_ci if ((addr & 0x30ff) == 0) 58462306a36Sopenharmony_ci addr = SRAM_HWC32_RAM2; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic void armada_drm_crtc_cursor_tran(void __iomem *base) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci unsigned addr; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci for (addr = 0; addr < 256; addr++) { 59462306a36Sopenharmony_ci /* write the default value */ 59562306a36Sopenharmony_ci writel_relaxed(0x55555555, base + LCD_SPU_SRAM_WRDAT); 59662306a36Sopenharmony_ci writel_relaxed(addr | SRAM_WRITE | SRAM_HWC32_TRAN, 59762306a36Sopenharmony_ci base + LCD_SPU_SRAM_CTRL); 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic int armada_drm_crtc_cursor_update(struct armada_crtc *dcrtc, bool reload) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci uint32_t xoff, xscr, w = dcrtc->cursor_w, s; 60462306a36Sopenharmony_ci uint32_t yoff, yscr, h = dcrtc->cursor_h; 60562306a36Sopenharmony_ci uint32_t para1; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci /* 60862306a36Sopenharmony_ci * Calculate the visible width and height of the cursor, 60962306a36Sopenharmony_ci * screen position, and the position in the cursor bitmap. 61062306a36Sopenharmony_ci */ 61162306a36Sopenharmony_ci if (dcrtc->cursor_x < 0) { 61262306a36Sopenharmony_ci xoff = -dcrtc->cursor_x; 61362306a36Sopenharmony_ci xscr = 0; 61462306a36Sopenharmony_ci w -= min(xoff, w); 61562306a36Sopenharmony_ci } else if (dcrtc->cursor_x + w > dcrtc->crtc.mode.hdisplay) { 61662306a36Sopenharmony_ci xoff = 0; 61762306a36Sopenharmony_ci xscr = dcrtc->cursor_x; 61862306a36Sopenharmony_ci w = max_t(int, dcrtc->crtc.mode.hdisplay - dcrtc->cursor_x, 0); 61962306a36Sopenharmony_ci } else { 62062306a36Sopenharmony_ci xoff = 0; 62162306a36Sopenharmony_ci xscr = dcrtc->cursor_x; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (dcrtc->cursor_y < 0) { 62562306a36Sopenharmony_ci yoff = -dcrtc->cursor_y; 62662306a36Sopenharmony_ci yscr = 0; 62762306a36Sopenharmony_ci h -= min(yoff, h); 62862306a36Sopenharmony_ci } else if (dcrtc->cursor_y + h > dcrtc->crtc.mode.vdisplay) { 62962306a36Sopenharmony_ci yoff = 0; 63062306a36Sopenharmony_ci yscr = dcrtc->cursor_y; 63162306a36Sopenharmony_ci h = max_t(int, dcrtc->crtc.mode.vdisplay - dcrtc->cursor_y, 0); 63262306a36Sopenharmony_ci } else { 63362306a36Sopenharmony_ci yoff = 0; 63462306a36Sopenharmony_ci yscr = dcrtc->cursor_y; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci /* On interlaced modes, the vertical cursor size must be halved */ 63862306a36Sopenharmony_ci s = dcrtc->cursor_w; 63962306a36Sopenharmony_ci if (dcrtc->interlaced) { 64062306a36Sopenharmony_ci s *= 2; 64162306a36Sopenharmony_ci yscr /= 2; 64262306a36Sopenharmony_ci h /= 2; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (!dcrtc->cursor_obj || !h || !w) { 64662306a36Sopenharmony_ci spin_lock_irq(&dcrtc->irq_lock); 64762306a36Sopenharmony_ci dcrtc->cursor_update = false; 64862306a36Sopenharmony_ci armada_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0); 64962306a36Sopenharmony_ci spin_unlock_irq(&dcrtc->irq_lock); 65062306a36Sopenharmony_ci return 0; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci spin_lock_irq(&dcrtc->irq_lock); 65462306a36Sopenharmony_ci para1 = readl_relaxed(dcrtc->base + LCD_SPU_SRAM_PARA1); 65562306a36Sopenharmony_ci armada_updatel(CFG_CSB_256x32, CFG_CSB_256x32 | CFG_PDWN256x32, 65662306a36Sopenharmony_ci dcrtc->base + LCD_SPU_SRAM_PARA1); 65762306a36Sopenharmony_ci spin_unlock_irq(&dcrtc->irq_lock); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* 66062306a36Sopenharmony_ci * Initialize the transparency if the SRAM was powered down. 66162306a36Sopenharmony_ci * We must also reload the cursor data as well. 66262306a36Sopenharmony_ci */ 66362306a36Sopenharmony_ci if (!(para1 & CFG_CSB_256x32)) { 66462306a36Sopenharmony_ci armada_drm_crtc_cursor_tran(dcrtc->base); 66562306a36Sopenharmony_ci reload = true; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (dcrtc->cursor_hw_sz != (h << 16 | w)) { 66962306a36Sopenharmony_ci spin_lock_irq(&dcrtc->irq_lock); 67062306a36Sopenharmony_ci dcrtc->cursor_update = false; 67162306a36Sopenharmony_ci armada_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0); 67262306a36Sopenharmony_ci spin_unlock_irq(&dcrtc->irq_lock); 67362306a36Sopenharmony_ci reload = true; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci if (reload) { 67662306a36Sopenharmony_ci struct armada_gem_object *obj = dcrtc->cursor_obj; 67762306a36Sopenharmony_ci uint32_t *pix; 67862306a36Sopenharmony_ci /* Set the top-left corner of the cursor image */ 67962306a36Sopenharmony_ci pix = obj->addr; 68062306a36Sopenharmony_ci pix += yoff * s + xoff; 68162306a36Sopenharmony_ci armada_load_cursor_argb(dcrtc->base, pix, s, w, h); 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci /* Reload the cursor position, size and enable in the IRQ handler */ 68562306a36Sopenharmony_ci spin_lock_irq(&dcrtc->irq_lock); 68662306a36Sopenharmony_ci dcrtc->cursor_hw_pos = yscr << 16 | xscr; 68762306a36Sopenharmony_ci dcrtc->cursor_hw_sz = h << 16 | w; 68862306a36Sopenharmony_ci dcrtc->cursor_update = true; 68962306a36Sopenharmony_ci armada_drm_crtc_enable_irq(dcrtc, DUMB_FRAMEDONE_ENA); 69062306a36Sopenharmony_ci spin_unlock_irq(&dcrtc->irq_lock); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci return 0; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic void cursor_update(void *data) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci armada_drm_crtc_cursor_update(data, true); 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cistatic int armada_drm_crtc_cursor_set(struct drm_crtc *crtc, 70162306a36Sopenharmony_ci struct drm_file *file, uint32_t handle, uint32_t w, uint32_t h) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 70462306a36Sopenharmony_ci struct armada_gem_object *obj = NULL; 70562306a36Sopenharmony_ci int ret; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci /* If no cursor support, replicate drm's return value */ 70862306a36Sopenharmony_ci if (!dcrtc->variant->has_spu_adv_reg) 70962306a36Sopenharmony_ci return -ENXIO; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (handle && w > 0 && h > 0) { 71262306a36Sopenharmony_ci /* maximum size is 64x32 or 32x64 */ 71362306a36Sopenharmony_ci if (w > 64 || h > 64 || (w > 32 && h > 32)) 71462306a36Sopenharmony_ci return -ENOMEM; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci obj = armada_gem_object_lookup(file, handle); 71762306a36Sopenharmony_ci if (!obj) 71862306a36Sopenharmony_ci return -ENOENT; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* Must be a kernel-mapped object */ 72162306a36Sopenharmony_ci if (!obj->addr) { 72262306a36Sopenharmony_ci drm_gem_object_put(&obj->obj); 72362306a36Sopenharmony_ci return -EINVAL; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (obj->obj.size < w * h * 4) { 72762306a36Sopenharmony_ci DRM_ERROR("buffer is too small\n"); 72862306a36Sopenharmony_ci drm_gem_object_put(&obj->obj); 72962306a36Sopenharmony_ci return -ENOMEM; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci if (dcrtc->cursor_obj) { 73462306a36Sopenharmony_ci dcrtc->cursor_obj->update = NULL; 73562306a36Sopenharmony_ci dcrtc->cursor_obj->update_data = NULL; 73662306a36Sopenharmony_ci drm_gem_object_put(&dcrtc->cursor_obj->obj); 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci dcrtc->cursor_obj = obj; 73962306a36Sopenharmony_ci dcrtc->cursor_w = w; 74062306a36Sopenharmony_ci dcrtc->cursor_h = h; 74162306a36Sopenharmony_ci ret = armada_drm_crtc_cursor_update(dcrtc, true); 74262306a36Sopenharmony_ci if (obj) { 74362306a36Sopenharmony_ci obj->update_data = dcrtc; 74462306a36Sopenharmony_ci obj->update = cursor_update; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci return ret; 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_cistatic int armada_drm_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 75362306a36Sopenharmony_ci int ret; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci /* If no cursor support, replicate drm's return value */ 75662306a36Sopenharmony_ci if (!dcrtc->variant->has_spu_adv_reg) 75762306a36Sopenharmony_ci return -EFAULT; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci dcrtc->cursor_x = x; 76062306a36Sopenharmony_ci dcrtc->cursor_y = y; 76162306a36Sopenharmony_ci ret = armada_drm_crtc_cursor_update(dcrtc, false); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci return ret; 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_cistatic void armada_drm_crtc_destroy(struct drm_crtc *crtc) 76762306a36Sopenharmony_ci{ 76862306a36Sopenharmony_ci struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 76962306a36Sopenharmony_ci struct armada_private *priv = drm_to_armada_dev(crtc->dev); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (dcrtc->cursor_obj) 77262306a36Sopenharmony_ci drm_gem_object_put(&dcrtc->cursor_obj->obj); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci priv->dcrtc[dcrtc->num] = NULL; 77562306a36Sopenharmony_ci drm_crtc_cleanup(&dcrtc->crtc); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (dcrtc->variant->disable) 77862306a36Sopenharmony_ci dcrtc->variant->disable(dcrtc); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ENA); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci of_node_put(dcrtc->crtc.port); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci kfree(dcrtc); 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic int armada_drm_crtc_late_register(struct drm_crtc *crtc) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_DEBUG_FS)) 79062306a36Sopenharmony_ci armada_drm_crtc_debugfs_init(drm_to_armada_crtc(crtc)); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci return 0; 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci/* These are called under the vbl_lock. */ 79662306a36Sopenharmony_cistatic int armada_drm_crtc_enable_vblank(struct drm_crtc *crtc) 79762306a36Sopenharmony_ci{ 79862306a36Sopenharmony_ci struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 79962306a36Sopenharmony_ci unsigned long flags; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci spin_lock_irqsave(&dcrtc->irq_lock, flags); 80262306a36Sopenharmony_ci armada_drm_crtc_enable_irq(dcrtc, VSYNC_IRQ_ENA); 80362306a36Sopenharmony_ci spin_unlock_irqrestore(&dcrtc->irq_lock, flags); 80462306a36Sopenharmony_ci return 0; 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cistatic void armada_drm_crtc_disable_vblank(struct drm_crtc *crtc) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 81062306a36Sopenharmony_ci unsigned long flags; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci spin_lock_irqsave(&dcrtc->irq_lock, flags); 81362306a36Sopenharmony_ci armada_drm_crtc_disable_irq(dcrtc, VSYNC_IRQ_ENA); 81462306a36Sopenharmony_ci spin_unlock_irqrestore(&dcrtc->irq_lock, flags); 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cistatic const struct drm_crtc_funcs armada_crtc_funcs = { 81862306a36Sopenharmony_ci .reset = drm_atomic_helper_crtc_reset, 81962306a36Sopenharmony_ci .cursor_set = armada_drm_crtc_cursor_set, 82062306a36Sopenharmony_ci .cursor_move = armada_drm_crtc_cursor_move, 82162306a36Sopenharmony_ci .destroy = armada_drm_crtc_destroy, 82262306a36Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 82362306a36Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 82462306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 82562306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 82662306a36Sopenharmony_ci .late_register = armada_drm_crtc_late_register, 82762306a36Sopenharmony_ci .enable_vblank = armada_drm_crtc_enable_vblank, 82862306a36Sopenharmony_ci .disable_vblank = armada_drm_crtc_disable_vblank, 82962306a36Sopenharmony_ci}; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ciint armada_crtc_select_clock(struct armada_crtc *dcrtc, 83262306a36Sopenharmony_ci struct armada_clk_result *res, 83362306a36Sopenharmony_ci const struct armada_clocking_params *params, 83462306a36Sopenharmony_ci struct clk *clks[], size_t num_clks, 83562306a36Sopenharmony_ci unsigned long desired_khz) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci unsigned long desired_hz = desired_khz * 1000; 83862306a36Sopenharmony_ci unsigned long desired_clk_hz; // requested clk input 83962306a36Sopenharmony_ci unsigned long real_clk_hz; // actual clk input 84062306a36Sopenharmony_ci unsigned long real_hz; // actual pixel clk 84162306a36Sopenharmony_ci unsigned long permillage; 84262306a36Sopenharmony_ci struct clk *clk; 84362306a36Sopenharmony_ci u32 div; 84462306a36Sopenharmony_ci int i; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci DRM_DEBUG_KMS("[CRTC:%u:%s] desired clock=%luHz\n", 84762306a36Sopenharmony_ci dcrtc->crtc.base.id, dcrtc->crtc.name, desired_hz); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci for (i = 0; i < num_clks; i++) { 85062306a36Sopenharmony_ci clk = clks[i]; 85162306a36Sopenharmony_ci if (!clk) 85262306a36Sopenharmony_ci continue; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci if (params->settable & BIT(i)) { 85562306a36Sopenharmony_ci real_clk_hz = clk_round_rate(clk, desired_hz); 85662306a36Sopenharmony_ci desired_clk_hz = desired_hz; 85762306a36Sopenharmony_ci } else { 85862306a36Sopenharmony_ci real_clk_hz = clk_get_rate(clk); 85962306a36Sopenharmony_ci desired_clk_hz = real_clk_hz; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci /* If the clock can do exactly the desired rate, we're done */ 86362306a36Sopenharmony_ci if (real_clk_hz == desired_hz) { 86462306a36Sopenharmony_ci real_hz = real_clk_hz; 86562306a36Sopenharmony_ci div = 1; 86662306a36Sopenharmony_ci goto found; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci /* Calculate the divider - if invalid, we can't do this rate */ 87062306a36Sopenharmony_ci div = DIV_ROUND_CLOSEST(real_clk_hz, desired_hz); 87162306a36Sopenharmony_ci if (div == 0 || div > params->div_max) 87262306a36Sopenharmony_ci continue; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci /* Calculate the actual rate - HDMI requires -0.6%..+0.5% */ 87562306a36Sopenharmony_ci real_hz = DIV_ROUND_CLOSEST(real_clk_hz, div); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci DRM_DEBUG_KMS("[CRTC:%u:%s] clk=%u %luHz div=%u real=%luHz\n", 87862306a36Sopenharmony_ci dcrtc->crtc.base.id, dcrtc->crtc.name, 87962306a36Sopenharmony_ci i, real_clk_hz, div, real_hz); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci /* Avoid repeated division */ 88262306a36Sopenharmony_ci if (real_hz < desired_hz) { 88362306a36Sopenharmony_ci permillage = real_hz / desired_khz; 88462306a36Sopenharmony_ci if (permillage < params->permillage_min) 88562306a36Sopenharmony_ci continue; 88662306a36Sopenharmony_ci } else { 88762306a36Sopenharmony_ci permillage = DIV_ROUND_UP(real_hz, desired_khz); 88862306a36Sopenharmony_ci if (permillage > params->permillage_max) 88962306a36Sopenharmony_ci continue; 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci goto found; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci return -ERANGE; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_cifound: 89762306a36Sopenharmony_ci DRM_DEBUG_KMS("[CRTC:%u:%s] selected clk=%u %luHz div=%u real=%luHz\n", 89862306a36Sopenharmony_ci dcrtc->crtc.base.id, dcrtc->crtc.name, 89962306a36Sopenharmony_ci i, real_clk_hz, div, real_hz); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci res->desired_clk_hz = desired_clk_hz; 90262306a36Sopenharmony_ci res->clk = clk; 90362306a36Sopenharmony_ci res->div = div; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci return i; 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_cistatic int armada_drm_crtc_create(struct drm_device *drm, struct device *dev, 90962306a36Sopenharmony_ci struct resource *res, int irq, const struct armada_variant *variant, 91062306a36Sopenharmony_ci struct device_node *port) 91162306a36Sopenharmony_ci{ 91262306a36Sopenharmony_ci struct armada_private *priv = drm_to_armada_dev(drm); 91362306a36Sopenharmony_ci struct armada_crtc *dcrtc; 91462306a36Sopenharmony_ci struct drm_plane *primary; 91562306a36Sopenharmony_ci void __iomem *base; 91662306a36Sopenharmony_ci int ret; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci base = devm_ioremap_resource(dev, res); 91962306a36Sopenharmony_ci if (IS_ERR(base)) 92062306a36Sopenharmony_ci return PTR_ERR(base); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci dcrtc = kzalloc(sizeof(*dcrtc), GFP_KERNEL); 92362306a36Sopenharmony_ci if (!dcrtc) { 92462306a36Sopenharmony_ci DRM_ERROR("failed to allocate Armada crtc\n"); 92562306a36Sopenharmony_ci return -ENOMEM; 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci if (dev != drm->dev) 92962306a36Sopenharmony_ci dev_set_drvdata(dev, dcrtc); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci dcrtc->variant = variant; 93262306a36Sopenharmony_ci dcrtc->base = base; 93362306a36Sopenharmony_ci dcrtc->num = drm->mode_config.num_crtc; 93462306a36Sopenharmony_ci dcrtc->cfg_dumb_ctrl = DUMB24_RGB888_0; 93562306a36Sopenharmony_ci dcrtc->spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24; 93662306a36Sopenharmony_ci spin_lock_init(&dcrtc->irq_lock); 93762306a36Sopenharmony_ci dcrtc->irq_ena = CLEAN_SPU_IRQ_ISR; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci /* Initialize some registers which we don't otherwise set */ 94062306a36Sopenharmony_ci writel_relaxed(0x00000001, dcrtc->base + LCD_CFG_SCLK_DIV); 94162306a36Sopenharmony_ci writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_BLANKCOLOR); 94262306a36Sopenharmony_ci writel_relaxed(dcrtc->spu_iopad_ctrl, 94362306a36Sopenharmony_ci dcrtc->base + LCD_SPU_IOPAD_CONTROL); 94462306a36Sopenharmony_ci writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_SRAM_PARA0); 94562306a36Sopenharmony_ci writel_relaxed(CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 | 94662306a36Sopenharmony_ci CFG_PDWN32x32 | CFG_PDWN16x66 | CFG_PDWN32x66 | 94762306a36Sopenharmony_ci CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1); 94862306a36Sopenharmony_ci writel_relaxed(0x2032ff81, dcrtc->base + LCD_SPU_DMA_CTRL1); 94962306a36Sopenharmony_ci writel_relaxed(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA); 95062306a36Sopenharmony_ci readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR); 95162306a36Sopenharmony_ci writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci ret = devm_request_irq(dev, irq, armada_drm_irq, 0, "armada_drm_crtc", 95462306a36Sopenharmony_ci dcrtc); 95562306a36Sopenharmony_ci if (ret < 0) 95662306a36Sopenharmony_ci goto err_crtc; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci if (dcrtc->variant->init) { 95962306a36Sopenharmony_ci ret = dcrtc->variant->init(dcrtc, dev); 96062306a36Sopenharmony_ci if (ret) 96162306a36Sopenharmony_ci goto err_crtc; 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci /* Ensure AXI pipeline is enabled */ 96562306a36Sopenharmony_ci armada_updatel(CFG_ARBFAST_ENA, 0, dcrtc->base + LCD_SPU_DMA_CTRL0); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci priv->dcrtc[dcrtc->num] = dcrtc; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci dcrtc->crtc.port = port; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci primary = kzalloc(sizeof(*primary), GFP_KERNEL); 97262306a36Sopenharmony_ci if (!primary) { 97362306a36Sopenharmony_ci ret = -ENOMEM; 97462306a36Sopenharmony_ci goto err_crtc; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci ret = armada_drm_primary_plane_init(drm, primary); 97862306a36Sopenharmony_ci if (ret) { 97962306a36Sopenharmony_ci kfree(primary); 98062306a36Sopenharmony_ci goto err_crtc; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci ret = drm_crtc_init_with_planes(drm, &dcrtc->crtc, primary, NULL, 98462306a36Sopenharmony_ci &armada_crtc_funcs, NULL); 98562306a36Sopenharmony_ci if (ret) 98662306a36Sopenharmony_ci goto err_crtc_init; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci ret = drm_mode_crtc_set_gamma_size(&dcrtc->crtc, 256); 99162306a36Sopenharmony_ci if (ret) 99262306a36Sopenharmony_ci return ret; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci drm_crtc_enable_color_mgmt(&dcrtc->crtc, 0, false, 256); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci return armada_overlay_plane_create(drm, 1 << dcrtc->num); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cierr_crtc_init: 99962306a36Sopenharmony_ci primary->funcs->destroy(primary); 100062306a36Sopenharmony_cierr_crtc: 100162306a36Sopenharmony_ci kfree(dcrtc); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci return ret; 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic int 100762306a36Sopenharmony_ciarmada_lcd_bind(struct device *dev, struct device *master, void *data) 100862306a36Sopenharmony_ci{ 100962306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 101062306a36Sopenharmony_ci struct drm_device *drm = data; 101162306a36Sopenharmony_ci struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 101262306a36Sopenharmony_ci int irq = platform_get_irq(pdev, 0); 101362306a36Sopenharmony_ci const struct armada_variant *variant; 101462306a36Sopenharmony_ci struct device_node *port = NULL; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci if (irq < 0) 101762306a36Sopenharmony_ci return irq; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci if (!dev->of_node) { 102062306a36Sopenharmony_ci const struct platform_device_id *id; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci id = platform_get_device_id(pdev); 102362306a36Sopenharmony_ci if (!id) 102462306a36Sopenharmony_ci return -ENXIO; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci variant = (const struct armada_variant *)id->driver_data; 102762306a36Sopenharmony_ci } else { 102862306a36Sopenharmony_ci const struct of_device_id *match; 102962306a36Sopenharmony_ci struct device_node *np, *parent = dev->of_node; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci match = of_match_device(dev->driver->of_match_table, dev); 103262306a36Sopenharmony_ci if (!match) 103362306a36Sopenharmony_ci return -ENXIO; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci np = of_get_child_by_name(parent, "ports"); 103662306a36Sopenharmony_ci if (np) 103762306a36Sopenharmony_ci parent = np; 103862306a36Sopenharmony_ci port = of_get_child_by_name(parent, "port"); 103962306a36Sopenharmony_ci of_node_put(np); 104062306a36Sopenharmony_ci if (!port) { 104162306a36Sopenharmony_ci dev_err(dev, "no port node found in %pOF\n", parent); 104262306a36Sopenharmony_ci return -ENXIO; 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci variant = match->data; 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci return armada_drm_crtc_create(drm, dev, res, irq, variant, port); 104962306a36Sopenharmony_ci} 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_cistatic void 105262306a36Sopenharmony_ciarmada_lcd_unbind(struct device *dev, struct device *master, void *data) 105362306a36Sopenharmony_ci{ 105462306a36Sopenharmony_ci struct armada_crtc *dcrtc = dev_get_drvdata(dev); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci armada_drm_crtc_destroy(&dcrtc->crtc); 105762306a36Sopenharmony_ci} 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic const struct component_ops armada_lcd_ops = { 106062306a36Sopenharmony_ci .bind = armada_lcd_bind, 106162306a36Sopenharmony_ci .unbind = armada_lcd_unbind, 106262306a36Sopenharmony_ci}; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_cistatic int armada_lcd_probe(struct platform_device *pdev) 106562306a36Sopenharmony_ci{ 106662306a36Sopenharmony_ci return component_add(&pdev->dev, &armada_lcd_ops); 106762306a36Sopenharmony_ci} 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_cistatic int armada_lcd_remove(struct platform_device *pdev) 107062306a36Sopenharmony_ci{ 107162306a36Sopenharmony_ci component_del(&pdev->dev, &armada_lcd_ops); 107262306a36Sopenharmony_ci return 0; 107362306a36Sopenharmony_ci} 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_cistatic const struct of_device_id armada_lcd_of_match[] = { 107662306a36Sopenharmony_ci { 107762306a36Sopenharmony_ci .compatible = "marvell,dove-lcd", 107862306a36Sopenharmony_ci .data = &armada510_ops, 107962306a36Sopenharmony_ci }, 108062306a36Sopenharmony_ci {} 108162306a36Sopenharmony_ci}; 108262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, armada_lcd_of_match); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_cistatic const struct platform_device_id armada_lcd_platform_ids[] = { 108562306a36Sopenharmony_ci { 108662306a36Sopenharmony_ci .name = "armada-lcd", 108762306a36Sopenharmony_ci .driver_data = (unsigned long)&armada510_ops, 108862306a36Sopenharmony_ci }, { 108962306a36Sopenharmony_ci .name = "armada-510-lcd", 109062306a36Sopenharmony_ci .driver_data = (unsigned long)&armada510_ops, 109162306a36Sopenharmony_ci }, 109262306a36Sopenharmony_ci { }, 109362306a36Sopenharmony_ci}; 109462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, armada_lcd_platform_ids); 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_cistruct platform_driver armada_lcd_platform_driver = { 109762306a36Sopenharmony_ci .probe = armada_lcd_probe, 109862306a36Sopenharmony_ci .remove = armada_lcd_remove, 109962306a36Sopenharmony_ci .driver = { 110062306a36Sopenharmony_ci .name = "armada-lcd", 110162306a36Sopenharmony_ci .owner = THIS_MODULE, 110262306a36Sopenharmony_ci .of_match_table = armada_lcd_of_match, 110362306a36Sopenharmony_ci }, 110462306a36Sopenharmony_ci .id_table = armada_lcd_platform_ids, 110562306a36Sopenharmony_ci}; 1106