/*
* Copyright (C) 2012 Russell King
* Rewritten from the dovefb driver, and Armada510 manuals.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include "armada_crtc.h"
#include "armada_drm.h"
#include "armada_fb.h"
#include "armada_gem.h"
#include "armada_hw.h"
struct armada_frame_work {
struct drm_pending_vblank_event *event;
struct armada_regs regs[4];
struct drm_framebuffer *old_fb;
};
enum csc_mode {
CSC_AUTO = 0,
CSC_YUV_CCIR601 = 1,
CSC_YUV_CCIR709 = 2,
CSC_RGB_COMPUTER = 1,
CSC_RGB_STUDIO = 2,
};
/*
* A note about interlacing. Let's consider HDMI 1920x1080i.
* The timing parameters we have from X are:
* Hact HsyA HsyI Htot Vact VsyA VsyI Vtot
* 1920 2448 2492 2640 1080 1084 1094 1125
* Which get translated to:
* Hact HsyA HsyI Htot Vact VsyA VsyI Vtot
* 1920 2448 2492 2640 540 542 547 562
*
* This is how it is defined by CEA-861-D - line and pixel numbers are
* referenced to the rising edge of VSYNC and HSYNC. Total clocks per
* line: 2640. The odd frame, the first active line is at line 21, and
* the even frame, the first active line is 584.
*
* LN: 560 561 562 563 567 568 569
* DE: ~~~|____________________________//__________________________
* HSYNC: ____|~|_____|~|_____|~|_____|~|_//__|~|_____|~|_____|~|_____
* VSYNC: _________________________|~~~~~~//~~~~~~~~~~~~~~~|__________
* 22 blanking lines. VSYNC at 1320 (referenced to the HSYNC rising edge).
*
* LN: 1123 1124 1125 1 5 6 7
* DE: ~~~|____________________________//__________________________
* HSYNC: ____|~|_____|~|_____|~|_____|~|_//__|~|_____|~|_____|~|_____
* VSYNC: ____________________|~~~~~~~~~~~//~~~~~~~~~~|_______________
* 23 blanking lines
*
* The Armada LCD Controller line and pixel numbers are, like X timings,
* referenced to the top left of the active frame.
*
* So, translating these to our LCD controller:
* Odd frame, 563 total lines, VSYNC at line 543-548, pixel 1128.
* Even frame, 562 total lines, VSYNC at line 542-547, pixel 2448.
* Note: Vsync front porch remains constant!
*
* if (odd_frame) {
* vtotal = mode->crtc_vtotal + 1;
* vbackporch = mode->crtc_vsync_start - mode->crtc_vdisplay + 1;
* vhorizpos = mode->crtc_hsync_start - mode->crtc_htotal / 2
* } else {
* vtotal = mode->crtc_vtotal;
* vbackporch = mode->crtc_vsync_start - mode->crtc_vdisplay;
* vhorizpos = mode->crtc_hsync_start;
* }
* vfrontporch = mode->crtc_vtotal - mode->crtc_vsync_end;
*
* So, we need to reprogram these registers on each vsync event:
* LCD_SPU_V_PORCH, LCD_SPU_ADV_REG, LCD_SPUT_V_H_TOTAL
*
* Note: we do not use the frame done interrupts because these appear
* to happen too early, and lead to jitter on the display (presumably
* they occur at the end of the last active line, before the vsync back
* porch, which we're reprogramming.)
*/
void
armada_drm_crtc_update_regs(struct armada_crtc *dcrtc, struct armada_regs *regs)
{
while (regs->offset != ~0) {
void __iomem *reg = dcrtc->base + regs->offset;
uint32_t val;
val = regs->mask;
if (val != 0)
val &= readl_relaxed(reg);
writel_relaxed(val | regs->val, reg);
++regs;
}
}
#define dpms_blanked(dpms) ((dpms) != DRM_MODE_DPMS_ON)
static void armada_drm_crtc_update(struct armada_crtc *dcrtc)
{
uint32_t dumb_ctrl;
dumb_ctrl = dcrtc->cfg_dumb_ctrl;
if (!dpms_blanked(dcrtc->dpms))
dumb_ctrl |= CFG_DUMB_ENA;
/*
* When the dumb interface isn't in DUMB24_RGB888_0 mode, it might
* be using SPI or GPIO. If we set this to DUMB_BLANK, we will
* force LCD_D[23:0] to output blank color, overriding the GPIO or
* SPI usage. So leave it as-is unless in DUMB24_RGB888_0 mode.
*/
if (dpms_blanked(dcrtc->dpms) &&
(dumb_ctrl & DUMB_MASK) == DUMB24_RGB888_0) {
dumb_ctrl &= ~DUMB_MASK;
dumb_ctrl |= DUMB_BLANK;
}
/*
* The documentation doesn't indicate what the normal state of
* the sync signals are. Sebastian Hesselbart kindly probed
* these signals on his board to determine their state.
*
* The non-inverted state of the sync signals is active high.
* Setting these bits makes the appropriate signal active low.
*/
if (dcrtc->crtc.mode.flags & DRM_MODE_FLAG_NCSYNC)
dumb_ctrl |= CFG_INV_CSYNC;
if (dcrtc->crtc.mode.flags & DRM_MODE_FLAG_NHSYNC)
dumb_ctrl |= CFG_INV_HSYNC;
if (dcrtc->crtc.mode.flags & DRM_MODE_FLAG_NVSYNC)
dumb_ctrl |= CFG_INV_VSYNC;
if (dcrtc->dumb_ctrl != dumb_ctrl) {
dcrtc->dumb_ctrl = dumb_ctrl;
writel_relaxed(dumb_ctrl, dcrtc->base + LCD_SPU_DUMB_CTRL);
}
}
static unsigned armada_drm_crtc_calc_fb(struct drm_framebuffer *fb,
int x, int y, struct armada_regs *regs, bool interlaced)
{
struct armada_gem_object *obj = drm_fb_obj(fb);
unsigned pitch = fb->pitches[0];
unsigned offset = y * pitch + x * fb->bits_per_pixel / 8;
uint32_t addr_odd, addr_even;
unsigned i = 0;
DRM_DEBUG_DRIVER("pitch %u x %d y %d bpp %d\n",
pitch, x, y,