Armada DRM updates:

- Fix interlace support.
 - use __drm_atomic_helper_plane_reset in overlay reset.
 - since the overlay and video planes use essentially the same format
   registers, precompute their values while validating.
 - fix a long-standing deficiency with overlay planes and interlace modes
 - calculate plane starting address at atomic_check stage rather than
   when we're programming the registers.
 - add gamma support.
 - ensure mode adjustments made by other components are properly handled
   in the driver and applied to the CRTC-programmed mode.
 - add and use register definitions for the "REG4F" register.
 - use drm_atomic_helper_shutdown() when tearing down to ensure that the
   hardware is properly shutdown.
 - add CRTC-level mode validation to ensure that we don't allow a mode
   that the CRTC-level hardware can not support.
 - improve the clocking selection for Armada 510 support.
 - move CRTC debugfs files into the crtc-specific directory, using the
   DRM helper to create these files.
 - patch from Lubomir Rintel to replace a simple framebuffer.
 - use the OF graph walker rather than open-coding this.
 - eliminate a useless check for the availability of the remote's parent
   which isn't required.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIVAwUAXRsdkfTnkBvkraxkAQKl/w//ai5bPyto6CwwkhNfmfFV53Y7fuii8Pbv
 aSuzziNGRjMJd7bQLb618QM7wcof7FzlEfPlHveZhQr0Pg8/FFza9C8Hk+Lwin1h
 bJzUWbyShsAVK6ZxHTxHVA94Ww4GNR9UayPQYn+R31BzDHv4maTJYwrz6TvhIPiW
 yKLhQDsq9X8/MjRyiK7pbYqzVPAP+VEt6Kw7CI/Xyvq3iNZWPqgvSRLwsEze04Jk
 CGqJE3+1NmgdOhOhEuCLWwSpk0U798AP5/39wpyGqHA1bmfWVK+kaVSakeUXYqe0
 MmGCC/N79ibVCS74l09Bc7u6iGJiuoIg5K+Jis8LvxxWnKKfKiC0MXSmpRynwX6Y
 9PIHwqXK2f7cSzTjxHdSgA8W/HKFs9Sn0nIm2trjVoqbVIaTOddK1Ej3dcL2eqD6
 3EtdDhhsXFNMFkR6FdXKyhxSccVyt/4u9xeE7kTB0oo+n7bfE9YDNXGWpMn+9Tvp
 oSoSYG4flRusar/6KfItSItNE5Pg4T6syFnY1P0dxYyUTr3KP9MPLMXcBKFIsk1Z
 B+5jzvkpw5VkegYJKp7qYKs5CLjYINk7T0x4JLysE/2lb4LfBnMyDfEiFjILCCVU
 zaXo4TwiaThExblbKWoxuN7fnGr4Soxfv9tJng3iY7DVoPe817N6b9A7BRn7NjWJ
 7CvgY9uIXoA=
 =+Fy+
 -----END PGP SIGNATURE-----

Merge tag 'for-airlie-armada' of git://git.armlinux.org.uk/~rmk/linux-arm into drm-next

Armada DRM updates:
- Fix interlace support.
- use __drm_atomic_helper_plane_reset in overlay reset.
- since the overlay and video planes use essentially the same format
  registers, precompute their values while validating.
- fix a long-standing deficiency with overlay planes and interlace modes
- calculate plane starting address at atomic_check stage rather than
  when we're programming the registers.
- add gamma support.
- ensure mode adjustments made by other components are properly handled
  in the driver and applied to the CRTC-programmed mode.
- add and use register definitions for the "REG4F" register.
- use drm_atomic_helper_shutdown() when tearing down to ensure that the
  hardware is properly shutdown.
- add CRTC-level mode validation to ensure that we don't allow a mode
  that the CRTC-level hardware can not support.
- improve the clocking selection for Armada 510 support.
- move CRTC debugfs files into the crtc-specific directory, using the
  DRM helper to create these files.
- patch from Lubomir Rintel to replace a simple framebuffer.
- use the OF graph walker rather than open-coding this.
- eliminate a useless check for the availability of the remote's parent
  which isn't required.

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Russell King <rmk@armlinux.org.uk>
Link: https://patchwork.freedesktop.org/patch/msgid/20190702091313.GA23442@rmk-PC.armlinux.org.uk
This commit is contained in:
Dave Airlie 2019-07-04 14:08:07 +10:00
commit f07b56e7d0
10 changed files with 518 additions and 208 deletions

View File

@ -14,18 +14,62 @@
#include "armada_drm.h"
#include "armada_hw.h"
struct armada510_variant_data {
struct clk *clks[4];
struct clk *sel_clk;
};
static int armada510_crtc_init(struct armada_crtc *dcrtc, struct device *dev)
{
struct armada510_variant_data *v;
struct clk *clk;
int idx;
clk = devm_clk_get(dev, "ext_ref_clk1");
if (IS_ERR(clk))
return PTR_ERR(clk) == -ENOENT ? -EPROBE_DEFER : PTR_ERR(clk);
v = devm_kzalloc(dev, sizeof(*v), GFP_KERNEL);
if (!v)
return -ENOMEM;
dcrtc->extclk[0] = clk;
dcrtc->variant_data = v;
/* Lower the watermark so to eliminate jitter at higher bandwidths */
armada_updatel(0x20, (1 << 11) | 0xff, dcrtc->base + LCD_CFG_RDREG4F);
if (dev->of_node) {
struct property *prop;
const char *s;
of_property_for_each_string(dev->of_node, "clock-names", prop,
s) {
if (!strcmp(s, "ext_ref_clk0"))
idx = 0;
else if (!strcmp(s, "ext_ref_clk1"))
idx = 1;
else if (!strcmp(s, "plldivider"))
idx = 2;
else if (!strcmp(s, "axibus"))
idx = 3;
else
continue;
clk = devm_clk_get(dev, s);
if (IS_ERR(clk))
return PTR_ERR(clk) == -ENOENT ? -EPROBE_DEFER :
PTR_ERR(clk);
v->clks[idx] = clk;
}
} else {
clk = devm_clk_get(dev, "ext_ref_clk1");
if (IS_ERR(clk))
return PTR_ERR(clk) == -ENOENT ? -EPROBE_DEFER :
PTR_ERR(clk);
v->clks[1] = clk;
}
/*
* Lower the watermark so to eliminate jitter at higher bandwidths.
* Disable SRAM read wait state to avoid system hang with external
* clock.
*/
armada_updatel(CFG_DMA_WM(0x20), CFG_SRAM_WAIT | CFG_DMA_WM_MASK,
dcrtc->base + LCD_CFG_RDREG4F);
/* Initialise SPU register */
writel_relaxed(ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND,
@ -34,65 +78,77 @@ static int armada510_crtc_init(struct armada_crtc *dcrtc, struct device *dev)
return 0;
}
static const u32 armada510_clk_sels[] = {
SCLK_510_EXTCLK0,
SCLK_510_EXTCLK1,
SCLK_510_PLL,
SCLK_510_AXI,
};
static const struct armada_clocking_params armada510_clocking = {
/* HDMI requires -0.6%..+0.5% */
.permillage_min = 994,
.permillage_max = 1005,
.settable = BIT(0) | BIT(1),
.div_max = SCLK_510_INT_DIV_MASK,
};
/*
* Armada510 specific SCLK register selection.
* This gets called with sclk = NULL to test whether the mode is
* supportable, and again with sclk != NULL to set the clocks up for
* that. The former can return an error, but the latter is expected
* not to.
*
* We currently are pretty rudimentary here, always selecting
* EXT_REF_CLK_1 for LCD0 and erroring LCD1. This needs improvement!
*/
static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc,
const struct drm_display_mode *mode, uint32_t *sclk)
{
struct clk *clk = dcrtc->extclk[0];
int ret;
struct armada510_variant_data *v = dcrtc->variant_data;
unsigned long desired_khz = mode->crtc_clock;
struct armada_clk_result res;
int ret, idx;
if (dcrtc->num == 1)
return -EINVAL;
idx = armada_crtc_select_clock(dcrtc, &res, &armada510_clocking,
v->clks, ARRAY_SIZE(v->clks),
desired_khz);
if (idx < 0)
return idx;
if (IS_ERR(clk))
return PTR_ERR(clk);
if (dcrtc->clk != clk) {
ret = clk_prepare_enable(clk);
if (ret)
return ret;
dcrtc->clk = clk;
}
ret = clk_prepare_enable(res.clk);
if (ret)
return ret;
if (sclk) {
uint32_t rate, ref, div;
clk_set_rate(res.clk, res.desired_clk_hz);
rate = mode->clock * 1000;
ref = clk_round_rate(clk, rate);
div = DIV_ROUND_UP(ref, rate);
if (div < 1)
div = 1;
*sclk = res.div | armada510_clk_sels[idx];
clk_set_rate(clk, ref);
*sclk = div | SCLK_510_EXTCLK1;
/* We are now using this clock */
v->sel_clk = res.clk;
swap(dcrtc->clk, res.clk);
}
clk_disable_unprepare(res.clk);
return 0;
}
static void armada510_crtc_disable(struct armada_crtc *dcrtc)
{
if (!IS_ERR(dcrtc->clk)) {
if (dcrtc->clk) {
clk_disable_unprepare(dcrtc->clk);
dcrtc->clk = ERR_PTR(-EINVAL);
dcrtc->clk = NULL;
}
}
static void armada510_crtc_enable(struct armada_crtc *dcrtc,
const struct drm_display_mode *mode)
{
if (IS_ERR(dcrtc->clk)) {
dcrtc->clk = dcrtc->extclk[0];
WARN_ON(clk_prepare_enable(dcrtc->clk));
struct armada510_variant_data *v = dcrtc->variant_data;
if (!dcrtc->clk && v->sel_clk) {
if (!WARN_ON(clk_prepare_enable(v->sel_clk)))
dcrtc->clk = v->sel_clk;
}
}

View File

@ -130,6 +130,70 @@ static void armada_drm_crtc_queue_state_event(struct drm_crtc *crtc)
}
}
static void armada_drm_update_gamma(struct drm_crtc *crtc)
{
struct drm_property_blob *blob = crtc->state->gamma_lut;
void __iomem *base = drm_to_armada_crtc(crtc)->base;
int i;
if (blob) {
struct drm_color_lut *lut = blob->data;
armada_updatel(CFG_CSB_256x8, CFG_CSB_256x8 | CFG_PDWN256x8,
base + LCD_SPU_SRAM_PARA1);
for (i = 0; i < 256; i++) {
writel_relaxed(drm_color_lut_extract(lut[i].red, 8),
base + LCD_SPU_SRAM_WRDAT);
writel_relaxed(i | SRAM_WRITE | SRAM_GAMMA_YR,
base + LCD_SPU_SRAM_CTRL);
readl_relaxed(base + LCD_SPU_HWC_OVSA_HPXL_VLN);
writel_relaxed(drm_color_lut_extract(lut[i].green, 8),
base + LCD_SPU_SRAM_WRDAT);
writel_relaxed(i | SRAM_WRITE | SRAM_GAMMA_UG,
base + LCD_SPU_SRAM_CTRL);
readl_relaxed(base + LCD_SPU_HWC_OVSA_HPXL_VLN);
writel_relaxed(drm_color_lut_extract(lut[i].blue, 8),
base + LCD_SPU_SRAM_WRDAT);
writel_relaxed(i | SRAM_WRITE | SRAM_GAMMA_VB,
base + LCD_SPU_SRAM_CTRL);
readl_relaxed(base + LCD_SPU_HWC_OVSA_HPXL_VLN);
}
armada_updatel(CFG_GAMMA_ENA, CFG_GAMMA_ENA,
base + LCD_SPU_DMA_CTRL0);
} else {
armada_updatel(0, CFG_GAMMA_ENA, base + LCD_SPU_DMA_CTRL0);
armada_updatel(CFG_PDWN256x8, CFG_CSB_256x8 | CFG_PDWN256x8,
base + LCD_SPU_SRAM_PARA1);
}
}
static enum drm_mode_status armada_drm_crtc_mode_valid(struct drm_crtc *crtc,
const struct drm_display_mode *mode)
{
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
if (mode->vscan > 1)
return MODE_NO_VSCAN;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
if (mode->flags & DRM_MODE_FLAG_HSKEW)
return MODE_H_ILLEGAL;
/* We can't do interlaced modes if we don't have the SPU_ADV_REG */
if (!dcrtc->variant->has_spu_adv_reg &&
mode->flags & DRM_MODE_FLAG_INTERLACE)
return MODE_NO_INTERLACE;
if (mode->flags & (DRM_MODE_FLAG_BCAST | DRM_MODE_FLAG_PIXMUX |
DRM_MODE_FLAG_CLKDIV2))
return MODE_BAD;
return MODE_OK;
}
/* The mode_config.mutex will be held for this call */
static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode, struct drm_display_mode *adj)
@ -137,9 +201,18 @@ static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc,
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
int ret;
/* We can't do interlaced modes if we don't have the SPU_ADV_REG */
if (!dcrtc->variant->has_spu_adv_reg &&
adj->flags & DRM_MODE_FLAG_INTERLACE)
/*
* Set CRTC modesetting parameters for the adjusted mode. This is
* applied after the connectors, bridges, and encoders have fixed up
* this mode, as described above drm_atomic_helper_check_modeset().
*/
drm_mode_set_crtcinfo(adj, CRTC_INTERLACE_HALVE_V);
/*
* Validate the adjusted mode in case an encoder/bridge has set
* something we don't support.
*/
if (armada_drm_crtc_mode_valid(crtc, adj) != MODE_OK)
return false;
/* Check whether the display mode is possible */
@ -278,16 +351,9 @@ static void armada_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
armada_reg_queue_set(regs, i, sclk, LCD_CFG_SCLK_DIV);
if (interlaced ^ dcrtc->interlaced) {
if (adj->flags & DRM_MODE_FLAG_INTERLACE)
drm_crtc_vblank_get(&dcrtc->crtc);
else
drm_crtc_vblank_put(&dcrtc->crtc);
dcrtc->interlaced = interlaced;
}
spin_lock_irqsave(&dcrtc->irq_lock, flags);
dcrtc->interlaced = interlaced;
/* Even interlaced/progressive frame */
dcrtc->v[1].spu_v_h_total = adj->crtc_vtotal << 16 |
adj->crtc_htotal;
@ -345,6 +411,20 @@ static void armada_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
}
static int armada_drm_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
if (state->gamma_lut && drm_color_lut_size(state->gamma_lut) != 256)
return -EINVAL;
if (state->color_mgmt_changed)
state->planes_changed = true;
return 0;
}
static void armada_drm_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state)
{
@ -352,6 +432,9 @@ static void armada_drm_crtc_atomic_begin(struct drm_crtc *crtc,
DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
if (crtc->state->color_mgmt_changed)
armada_drm_update_gamma(crtc);
dcrtc->regs_idx = 0;
dcrtc->regs = dcrtc->atomic_regs;
}
@ -390,6 +473,9 @@ static void armada_drm_crtc_atomic_disable(struct drm_crtc *crtc,
DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
if (old_state->adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE)
drm_crtc_vblank_put(crtc);
drm_crtc_vblank_off(crtc);
armada_drm_crtc_update(dcrtc, false);
@ -434,12 +520,17 @@ static void armada_drm_crtc_atomic_enable(struct drm_crtc *crtc,
armada_drm_crtc_update(dcrtc, true);
drm_crtc_vblank_on(crtc);
if (crtc->state->adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE)
WARN_ON(drm_crtc_vblank_get(crtc));
armada_drm_crtc_queue_state_event(crtc);
}
static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = {
.mode_valid = armada_drm_crtc_mode_valid,
.mode_fixup = armada_drm_crtc_mode_fixup,
.mode_set_nofb = armada_drm_crtc_mode_set_nofb,
.atomic_check = armada_drm_crtc_atomic_check,
.atomic_begin = armada_drm_crtc_atomic_begin,
.atomic_flush = armada_drm_crtc_atomic_flush,
.atomic_disable = armada_drm_crtc_atomic_disable,
@ -460,6 +551,13 @@ static void armada_load_cursor_argb(void __iomem *base, uint32_t *pix,
for (x = 0; x < width; x++, p++) {
uint32_t val = *p;
/*
* In "ARGB888" (HWC32) mode, writing to the SRAM
* requires these bits to contain:
* 31:24 = alpha 23:16 = blue 15:8 = green 7:0 = red
* So, it's actually ABGR8888. This is independent
* of the SWAPRB bits in DMA control register 0.
*/
val = (val & 0xff00ff00) |
(val & 0x000000ff) << 16 |
(val & 0x00ff0000) >> 16;
@ -676,6 +774,14 @@ static void armada_drm_crtc_destroy(struct drm_crtc *crtc)
kfree(dcrtc);
}
static int armada_drm_crtc_late_register(struct drm_crtc *crtc)
{
if (IS_ENABLED(CONFIG_DEBUG_FS))
armada_drm_crtc_debugfs_init(drm_to_armada_crtc(crtc));
return 0;
}
/* These are called under the vbl_lock. */
static int armada_drm_crtc_enable_vblank(struct drm_crtc *crtc)
{
@ -703,14 +809,93 @@ static const struct drm_crtc_funcs armada_crtc_funcs = {
.cursor_set = armada_drm_crtc_cursor_set,
.cursor_move = armada_drm_crtc_cursor_move,
.destroy = armada_drm_crtc_destroy,
.gamma_set = drm_atomic_helper_legacy_gamma_set,
.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,
.late_register = armada_drm_crtc_late_register,
.enable_vblank = armada_drm_crtc_enable_vblank,
.disable_vblank = armada_drm_crtc_disable_vblank,
};
int armada_crtc_select_clock(struct armada_crtc *dcrtc,
struct armada_clk_result *res,
const struct armada_clocking_params *params,
struct clk *clks[], size_t num_clks,
unsigned long desired_khz)
{
unsigned long desired_hz = desired_khz * 1000;
unsigned long desired_clk_hz; // requested clk input
unsigned long real_clk_hz; // actual clk input
unsigned long real_hz; // actual pixel clk
unsigned long permillage;
struct clk *clk;
u32 div;
int i;
DRM_DEBUG_KMS("[CRTC:%u:%s] desired clock=%luHz\n",
dcrtc->crtc.base.id, dcrtc->crtc.name, desired_hz);
for (i = 0; i < num_clks; i++) {
clk = clks[i];
if (!clk)
continue;
if (params->settable & BIT(i)) {
real_clk_hz = clk_round_rate(clk, desired_hz);
desired_clk_hz = desired_hz;
} else {
real_clk_hz = clk_get_rate(clk);
desired_clk_hz = real_clk_hz;
}
/* If the clock can do exactly the desired rate, we're done */
if (real_clk_hz == desired_hz) {
real_hz = real_clk_hz;
div = 1;
goto found;
}
/* Calculate the divider - if invalid, we can't do this rate */
div = DIV_ROUND_CLOSEST(real_clk_hz, desired_hz);
if (div == 0 || div > params->div_max)
continue;
/* Calculate the actual rate - HDMI requires -0.6%..+0.5% */
real_hz = DIV_ROUND_CLOSEST(real_clk_hz, div);
DRM_DEBUG_KMS("[CRTC:%u:%s] clk=%u %luHz div=%u real=%luHz\n",
dcrtc->crtc.base.id, dcrtc->crtc.name,
i, real_clk_hz, div, real_hz);
/* Avoid repeated division */
if (real_hz < desired_hz) {
permillage = real_hz / desired_khz;
if (permillage < params->permillage_min)
continue;
} else {
permillage = DIV_ROUND_UP(real_hz, desired_khz);
if (permillage > params->permillage_max)
continue;
}
goto found;
}
return -ERANGE;
found:
DRM_DEBUG_KMS("[CRTC:%u:%s] selected clk=%u %luHz div=%u real=%luHz\n",
dcrtc->crtc.base.id, dcrtc->crtc.name,
i, real_clk_hz, div, real_hz);
res->desired_clk_hz = desired_clk_hz;
res->clk = clk;
res->div = div;
return i;
}
static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
struct resource *res, int irq, const struct armada_variant *variant,
struct device_node *port)
@ -737,7 +922,6 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
dcrtc->variant = variant;
dcrtc->base = base;
dcrtc->num = drm->mode_config.num_crtc;
dcrtc->clk = ERR_PTR(-EINVAL);
dcrtc->cfg_dumb_ctrl = DUMB24_RGB888_0;
dcrtc->spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24;
spin_lock_init(&dcrtc->irq_lock);
@ -794,6 +978,12 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs);
ret = drm_mode_crtc_set_gamma_size(&dcrtc->crtc, 256);
if (ret)
return ret;
drm_crtc_enable_color_mgmt(&dcrtc->crtc, 0, false, 256);
return armada_overlay_plane_create(drm, 1 << dcrtc->num);
err_crtc_init:

View File

@ -39,10 +39,10 @@ struct armada_variant;
struct armada_crtc {
struct drm_crtc crtc;
const struct armada_variant *variant;
void *variant_data;
unsigned num;
void __iomem *base;
struct clk *clk;
struct clk *extclk[2];
struct {
uint32_t spu_v_h_total;
uint32_t spu_v_porch;
@ -75,6 +75,25 @@ struct armada_crtc {
void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *);
struct armada_clocking_params {
unsigned long permillage_min;
unsigned long permillage_max;
u32 settable;
u32 div_max;
};
struct armada_clk_result {
unsigned long desired_clk_hz;
struct clk *clk;
u32 div;
};
int armada_crtc_select_clock(struct armada_crtc *dcrtc,
struct armada_clk_result *res,
const struct armada_clocking_params *params,
struct clk *clks[], size_t num_clks,
unsigned long desired_khz);
extern struct platform_driver armada_lcd_platform_driver;
#endif

View File

@ -28,50 +28,33 @@ static int armada_debugfs_gem_linear_show(struct seq_file *m, void *data)
return 0;
}
static int armada_debugfs_reg_show(struct seq_file *m, void *data)
static int armada_debugfs_crtc_reg_show(struct seq_file *m, void *data)
{
struct drm_device *dev = m->private;
struct armada_private *priv = dev->dev_private;
int n, i;
struct armada_crtc *dcrtc = m->private;
int i;
if (priv) {
for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) {
struct armada_crtc *dcrtc = priv->dcrtc[n];
if (!dcrtc)
continue;
for (i = 0x84; i <= 0x1c4; i += 4) {
uint32_t v = readl_relaxed(dcrtc->base + i);
seq_printf(m, "%u: 0x%04x: 0x%08x\n", n, i, v);
}
}
for (i = 0x84; i <= 0x1c4; i += 4) {
u32 v = readl_relaxed(dcrtc->base + i);
seq_printf(m, "0x%04x: 0x%08x\n", i, v);
}
return 0;
}
static int armada_debugfs_reg_r_open(struct inode *inode, struct file *file)
static int armada_debugfs_crtc_reg_open(struct inode *inode, struct file *file)
{
return single_open(file, armada_debugfs_reg_show, inode->i_private);
return single_open(file, armada_debugfs_crtc_reg_show,
inode->i_private);
}
static const struct file_operations fops_reg_r = {
.owner = THIS_MODULE,
.open = armada_debugfs_reg_r_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int armada_debugfs_write(struct file *file, const char __user *ptr,
size_t len, loff_t *off)
static int armada_debugfs_crtc_reg_write(struct file *file,
const char __user *ptr, size_t len, loff_t *off)
{
struct drm_device *dev = file->private_data;
struct armada_private *priv = dev->dev_private;
struct armada_crtc *dcrtc = priv->dcrtc[0];
char buf[32], *p;
uint32_t reg, val;
struct armada_crtc *dcrtc;
unsigned long reg, mask, val;
char buf[32];
int ret;
u32 v;
if (*off != 0)
return 0;
@ -84,24 +67,35 @@ static int armada_debugfs_write(struct file *file, const char __user *ptr,
return ret;
buf[len] = '\0';
reg = simple_strtoul(buf, &p, 16);
if (!isspace(*p))
if (sscanf(buf, "%lx %lx %lx", &reg, &mask, &val) != 3)
return -EINVAL;
val = simple_strtoul(p + 1, NULL, 16);
if (reg < 0x84 || reg > 0x1c4 || reg & 3)
return -ERANGE;
if (reg >= 0x84 && reg <= 0x1c4)
writel(val, dcrtc->base + reg);
dcrtc = ((struct seq_file *)file->private_data)->private;
v = readl(dcrtc->base + reg);
v &= ~mask;
v |= val & mask;
writel(v, dcrtc->base + reg);
return len;
}
static const struct file_operations fops_reg_w = {
static const struct file_operations armada_debugfs_crtc_reg_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.write = armada_debugfs_write,
.llseek = noop_llseek,
.open = armada_debugfs_crtc_reg_open,
.read = seq_read,
.write = armada_debugfs_crtc_reg_write,
.llseek = seq_lseek,
.release = single_release,
};
void armada_drm_crtc_debugfs_init(struct armada_crtc *dcrtc)
{
debugfs_create_file("armada-regs", 0600, dcrtc->crtc.debugfs_entry,
dcrtc, &armada_debugfs_crtc_reg_fops);
}
static struct drm_info_list armada_debugfs_list[] = {
{ "gem_linear", armada_debugfs_gem_linear_show, 0 },
};
@ -109,24 +103,8 @@ static struct drm_info_list armada_debugfs_list[] = {
int armada_drm_debugfs_init(struct drm_minor *minor)
{
struct dentry *de;
int ret;
ret = drm_debugfs_create_files(armada_debugfs_list,
ARMADA_DEBUGFS_ENTRIES,
minor->debugfs_root, minor);
if (ret)
return ret;
de = debugfs_create_file("reg", S_IFREG | S_IRUSR,
minor->debugfs_root, minor->dev, &fops_reg_r);
if (!de)
return -ENOMEM;
de = debugfs_create_file("reg_wr", S_IFREG | S_IWUSR,
minor->debugfs_root, minor->dev, &fops_reg_w);
if (!de)
return -ENOMEM;
drm_debugfs_create_files(armada_debugfs_list, ARMADA_DEBUGFS_ENTRIES,
minor->debugfs_root, minor);
return 0;
}

View File

@ -78,6 +78,7 @@ void armada_fbdev_fini(struct drm_device *);
int armada_overlay_plane_create(struct drm_device *, unsigned long);
void armada_drm_crtc_debugfs_init(struct armada_crtc *dcrtc);
int armada_drm_debugfs_init(struct drm_minor *);
#endif

View File

@ -100,6 +100,17 @@ static int armada_drm_bind(struct device *dev)
return ret;
}
/* Remove early framebuffers */
ret = drm_fb_helper_remove_conflicting_framebuffers(NULL,
"armada-drm-fb",
false);
if (ret) {
dev_err(dev, "[" DRM_NAME ":%s] can't kick out simple-fb: %d\n",
__func__, ret);
kfree(priv);
return ret;
}
priv->drm.dev_private = priv;
dev_set_drvdata(dev, &priv->drm);
@ -171,6 +182,8 @@ static void armada_drm_unbind(struct device *dev)
drm_dev_unregister(&priv->drm);
drm_atomic_helper_shutdown(&priv->drm);
component_unbind_all(dev, &priv->drm);
drm_mode_config_cleanup(&priv->drm);
@ -191,23 +204,15 @@ static int compare_dev_name(struct device *dev, void *data)
}
static void armada_add_endpoints(struct device *dev,
struct component_match **match, struct device_node *port)
struct component_match **match, struct device_node *dev_node)
{
struct device_node *ep, *remote;
for_each_child_of_node(port, ep) {
for_each_endpoint_of_node(dev_node, ep) {
remote = of_graph_get_remote_port_parent(ep);
if (!remote || !of_device_is_available(remote)) {
of_node_put(remote);
continue;
} else if (!of_device_is_available(remote->parent)) {
dev_warn(dev, "parent device of %pOF is not available\n",
remote);
of_node_put(remote);
continue;
}
drm_of_component_match_add(dev, match, compare_of, remote);
if (remote && of_device_is_available(remote))
drm_of_component_match_add(dev, match, compare_of,
remote);
of_node_put(remote);
}
}
@ -229,7 +234,6 @@ static int armada_drm_probe(struct platform_device *pdev)
if (dev->platform_data) {
char **devices = dev->platform_data;
struct device_node *port;
struct device *d;
int i;
@ -245,10 +249,8 @@ static int armada_drm_probe(struct platform_device *pdev)
for (i = 0; devices[i]; i++) {
d = bus_find_device_by_name(&platform_bus_type, NULL,
devices[i]);
if (d && d->of_node) {
for_each_child_of_node(d->of_node, port)
armada_add_endpoints(dev, &match, port);
}
if (d && d->of_node)
armada_add_endpoints(dev, &match, d->of_node);
put_device(d);
}
}

View File

@ -88,6 +88,16 @@ enum {
ADV_VSYNC_H_OFF = 0xfff << 0,
};
/* LCD_CFG_RDREG4F - Armada 510 only */
enum {
CFG_SRAM_WAIT = BIT(11),
CFG_SMPN_FASTTX = BIT(10),
CFG_DMA_ARB = BIT(9),
CFG_DMA_WM_EN = BIT(8),
CFG_DMA_WM_MASK = 0xff,
#define CFG_DMA_WM(x) ((x) & CFG_DMA_WM_MASK)
};
enum {
CFG_565 = 0,
CFG_1555 = 1,
@ -169,6 +179,10 @@ enum {
SRAM_READ = 0 << 14,
SRAM_WRITE = 2 << 14,
SRAM_INIT = 3 << 14,
SRAM_GAMMA_YR = 0x0 << 8,
SRAM_GAMMA_UG = 0x1 << 8,
SRAM_GAMMA_VB = 0x2 << 8,
SRAM_PALETTE = 0x3 << 8,
SRAM_HWC32_RAM1 = 0xc << 8,
SRAM_HWC32_RAM2 = 0xd << 8,
SRAM_HWC32_RAMR = SRAM_HWC32_RAM1,
@ -316,19 +330,4 @@ enum {
PWRDN_IRQ_LEVEL = 1 << 0,
};
static inline u32 armada_rect_hw_fp(struct drm_rect *r)
{
return (drm_rect_height(r) & 0xffff0000) | drm_rect_width(r) >> 16;
}
static inline u32 armada_rect_hw(struct drm_rect *r)
{
return drm_rect_height(r) << 16 | (drm_rect_width(r) & 0x0000ffff);
}
static inline u32 armada_rect_yx(struct drm_rect *r)
{
return (r)->y1 << 16 | ((r)->x1 & 0x0000ffff);
}
#endif

View File

@ -27,7 +27,7 @@
#define DEFAULT_ENCODING DRM_COLOR_YCBCR_BT601
struct armada_overlay_state {
struct drm_plane_state base;
struct armada_plane_state base;
u32 colorkey_yr;
u32 colorkey_ug;
u32 colorkey_vb;
@ -38,7 +38,7 @@ struct armada_overlay_state {
u16 saturation;
};
#define drm_to_overlay_state(s) \
container_of(s, struct armada_overlay_state, base)
container_of(s, struct armada_overlay_state, base.base)
static inline u32 armada_spu_contrast(struct drm_plane_state *state)
{
@ -94,41 +94,39 @@ static void armada_drm_overlay_plane_atomic_update(struct drm_plane *plane,
armada_reg_queue_mod(regs, idx,
0, CFG_PDWN16x66 | CFG_PDWN32x66,
LCD_SPU_SRAM_PARA1);
val = armada_rect_hw_fp(&state->src);
if (armada_rect_hw_fp(&old_state->src) != val)
val = armada_src_hw(state);
if (armada_src_hw(old_state) != val)
armada_reg_queue_set(regs, idx, val, LCD_SPU_DMA_HPXL_VLN);
val = armada_rect_yx(&state->dst);
if (armada_rect_yx(&old_state->dst) != val)
val = armada_dst_yx(state);
if (armada_dst_yx(old_state) != val)
armada_reg_queue_set(regs, idx, val, LCD_SPU_DMA_OVSA_HPXL_VLN);
val = armada_rect_hw(&state->dst);
if (armada_rect_hw(&old_state->dst) != val)
val = armada_dst_hw(state);
if (armada_dst_hw(old_state) != val)
armada_reg_queue_set(regs, idx, val, LCD_SPU_DZM_HPXL_VLN);
/* FIXME: overlay on an interlaced display */
if (old_state->src.x1 != state->src.x1 ||
old_state->src.y1 != state->src.y1 ||
old_state->fb != state->fb) {
old_state->fb != state->fb ||
state->crtc->state->mode_changed) {
const struct drm_format_info *format;
u16 src_x, pitches[3];
u32 addrs[2][3];
u16 src_x;
armada_drm_plane_calc(state, addrs, pitches, false);
armada_reg_queue_set(regs, idx, addrs[0][0],
armada_reg_queue_set(regs, idx, armada_addr(state, 0, 0),
LCD_SPU_DMA_START_ADDR_Y0);
armada_reg_queue_set(regs, idx, addrs[0][1],
armada_reg_queue_set(regs, idx, armada_addr(state, 0, 1),
LCD_SPU_DMA_START_ADDR_U0);
armada_reg_queue_set(regs, idx, addrs[0][2],
armada_reg_queue_set(regs, idx, armada_addr(state, 0, 2),
LCD_SPU_DMA_START_ADDR_V0);
armada_reg_queue_set(regs, idx, addrs[1][0],
armada_reg_queue_set(regs, idx, armada_addr(state, 1, 0),
LCD_SPU_DMA_START_ADDR_Y1);
armada_reg_queue_set(regs, idx, addrs[1][1],
armada_reg_queue_set(regs, idx, armada_addr(state, 1, 1),
LCD_SPU_DMA_START_ADDR_U1);
armada_reg_queue_set(regs, idx, addrs[1][2],
armada_reg_queue_set(regs, idx, armada_addr(state, 1, 2),
LCD_SPU_DMA_START_ADDR_V1);
val = pitches[0] << 16 | pitches[0];
val = armada_pitch(state, 0) << 16 | armada_pitch(state, 0);
armada_reg_queue_set(regs, idx, val, LCD_SPU_DMA_PITCH_YC);
val = pitches[1] << 16 | pitches[2];
val = armada_pitch(state, 1) << 16 | armada_pitch(state, 2);
armada_reg_queue_set(regs, idx, val, LCD_SPU_DMA_PITCH_UV);
cfg = CFG_DMA_FMT(drm_fb_to_armada_fb(state->fb)->fmt) |
@ -146,6 +144,8 @@ static void armada_drm_overlay_plane_atomic_update(struct drm_plane *plane,
src_x = state->src.x1 >> 16;
if (format->num_planes == 1 && src_x & (format->hsub - 1))
cfg ^= CFG_DMA_MOD(CFG_SWAPUV);
if (to_armada_plane_state(state)->interlace)
cfg |= CFG_DMA_FTOGGLE;
cfg_mask = CFG_CBSH_ENA | CFG_DMAFORMAT |
CFG_DMA_MOD(CFG_SWAPRB | CFG_SWAPUV |
CFG_SWAPYU | CFG_YUV2RGB) |
@ -307,13 +307,10 @@ static void armada_overlay_reset(struct drm_plane *plane)
if (plane->state)
__drm_atomic_helper_plane_destroy_state(plane->state);
kfree(plane->state);
plane->state = NULL;
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (state) {
state->base.plane = plane;
state->base.color_encoding = DEFAULT_ENCODING;
state->base.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
state->base.rotation = DRM_MODE_ROTATE_0;
state->colorkey_yr = 0xfefefe00;
state->colorkey_ug = 0x01010100;
state->colorkey_vb = 0x01010100;
@ -323,8 +320,10 @@ static void armada_overlay_reset(struct drm_plane *plane)
state->brightness = DEFAULT_BRIGHTNESS;
state->contrast = DEFAULT_CONTRAST;
state->saturation = DEFAULT_SATURATION;
__drm_atomic_helper_plane_reset(plane, &state->base.base);
state->base.base.color_encoding = DEFAULT_ENCODING;
state->base.base.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
}
plane->state = &state->base;
}
struct drm_plane_state *
@ -337,8 +336,9 @@ armada_overlay_duplicate_state(struct drm_plane *plane)
state = kmemdup(plane->state, sizeof(*state), GFP_KERNEL);
if (state)
__drm_atomic_helper_plane_duplicate_state(plane, &state->base);
return &state->base;
__drm_atomic_helper_plane_duplicate_state(plane,
&state->base.base);
return &state->base.base;
}
static int armada_overlay_set_property(struct drm_plane *plane,

View File

@ -79,23 +79,6 @@ void armada_drm_plane_calc(struct drm_plane_state *state, u32 addrs[2][3],
}
}
static unsigned armada_drm_crtc_calc_fb(struct drm_plane_state *state,
struct armada_regs *regs, bool interlaced)
{
u16 pitches[3];
u32 addrs[2][3];
unsigned i = 0;
armada_drm_plane_calc(state, addrs, pitches, interlaced);
/* write offset, base, and pitch */
armada_reg_queue_set(regs, i, addrs[0][0], LCD_CFG_GRA_START_ADDR0);
armada_reg_queue_set(regs, i, addrs[1][0], LCD_CFG_GRA_START_ADDR1);
armada_reg_queue_mod(regs, i, pitches[0], 0xffff, LCD_CFG_GRA_PITCH);
return i;
}
int armada_drm_plane_prepare_fb(struct drm_plane *plane,
struct drm_plane_state *state)
{
@ -126,20 +109,50 @@ void armada_drm_plane_cleanup_fb(struct drm_plane *plane,
int armada_drm_plane_atomic_check(struct drm_plane *plane,
struct drm_plane_state *state)
{
if (state->fb && !WARN_ON(!state->crtc)) {
struct drm_crtc *crtc = state->crtc;
struct drm_crtc_state *crtc_state;
struct armada_plane_state *st = to_armada_plane_state(state);
struct drm_crtc *crtc = state->crtc;
struct drm_crtc_state *crtc_state;
bool interlace;
int ret;
if (state->state)
crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
else
crtc_state = crtc->state;
return drm_atomic_helper_check_plane_state(state, crtc_state,
0, INT_MAX,
true, false);
} else {
if (!state->fb || WARN_ON(!state->crtc)) {
state->visible = false;
return 0;
}
if (state->state)
crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
else
crtc_state = crtc->state;
ret = drm_atomic_helper_check_plane_state(state, crtc_state, 0,
INT_MAX, true, false);
if (ret)
return ret;
interlace = crtc_state->adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE;
if (interlace) {
if ((state->dst.y1 | state->dst.y2) & 1)
return -EINVAL;
st->src_hw = drm_rect_height(&state->src) >> 17;
st->dst_yx = state->dst.y1 >> 1;
st->dst_hw = drm_rect_height(&state->dst) >> 1;
} else {
st->src_hw = drm_rect_height(&state->src) >> 16;
st->dst_yx = state->dst.y1;
st->dst_hw = drm_rect_height(&state->dst);
}
st->src_hw <<= 16;
st->src_hw |= drm_rect_width(&state->src) >> 16;
st->dst_yx <<= 16;
st->dst_yx |= state->dst.x1 & 0x0000ffff;
st->dst_hw <<= 16;
st->dst_hw |= drm_rect_width(&state->dst) & 0x0000ffff;
armada_drm_plane_calc(state, st->addrs, st->pitches, interlace);
st->interlace = interlace;
return 0;
}
@ -173,21 +186,25 @@ static void armada_drm_primary_plane_atomic_update(struct drm_plane *plane,
val |= CFG_PDWN256x24;
armada_reg_queue_mod(regs, idx, 0, val, LCD_SPU_SRAM_PARA1);
}
val = armada_rect_hw_fp(&state->src);
if (armada_rect_hw_fp(&old_state->src) != val)
val = armada_src_hw(state);
if (armada_src_hw(old_state) != val)
armada_reg_queue_set(regs, idx, val, LCD_SPU_GRA_HPXL_VLN);
val = armada_rect_yx(&state->dst);
if (armada_rect_yx(&old_state->dst) != val)
val = armada_dst_yx(state);
if (armada_dst_yx(old_state) != val)
armada_reg_queue_set(regs, idx, val, LCD_SPU_GRA_OVSA_HPXL_VLN);
val = armada_rect_hw(&state->dst);
if (armada_rect_hw(&old_state->dst) != val)
val = armada_dst_hw(state);
if (armada_dst_hw(old_state) != val)
armada_reg_queue_set(regs, idx, val, LCD_SPU_GZM_HPXL_VLN);
if (old_state->src.x1 != state->src.x1 ||
old_state->src.y1 != state->src.y1 ||
old_state->fb != state->fb ||
state->crtc->state->mode_changed) {
idx += armada_drm_crtc_calc_fb(state, regs + idx,
dcrtc->interlaced);
armada_reg_queue_set(regs, idx, armada_addr(state, 0, 0),
LCD_CFG_GRA_START_ADDR0);
armada_reg_queue_set(regs, idx, armada_addr(state, 1, 0),
LCD_CFG_GRA_START_ADDR1);
armada_reg_queue_mod(regs, idx, armada_pitch(state, 0), 0xffff,
LCD_CFG_GRA_PITCH);
}
if (old_state->fb != state->fb ||
state->crtc->state->mode_changed) {
@ -197,7 +214,7 @@ static void armada_drm_primary_plane_atomic_update(struct drm_plane *plane,
cfg |= CFG_PALETTE_ENA;
if (state->visible)
cfg |= CFG_GRA_ENA;
if (dcrtc->interlaced)
if (to_armada_plane_state(state)->interlace)
cfg |= CFG_GRA_FTOGGLE;
cfg_mask = CFG_GRAFORMAT |
CFG_GRA_MOD(CFG_SWAPRB | CFG_SWAPUV |
@ -248,7 +265,7 @@ static void armada_drm_primary_plane_atomic_disable(struct drm_plane *plane,
/* Disable plane and power down most RAMs and FIFOs */
armada_reg_queue_mod(regs, idx, 0, CFG_GRA_ENA, LCD_SPU_DMA_CTRL0);
armada_reg_queue_mod(regs, idx, CFG_PDWN256x32 | CFG_PDWN256x24 |
CFG_PDWN256x8 | CFG_PDWN32x32 | CFG_PDWN64x66,
CFG_PDWN32x32 | CFG_PDWN64x66,
0, LCD_SPU_SRAM_PARA1);
dcrtc->regs_idx += idx;
@ -262,12 +279,37 @@ static const struct drm_plane_helper_funcs armada_primary_plane_helper_funcs = {
.atomic_disable = armada_drm_primary_plane_atomic_disable,
};
void armada_plane_reset(struct drm_plane *plane)
{
struct armada_plane_state *st;
if (plane->state)
__drm_atomic_helper_plane_destroy_state(plane->state);
kfree(plane->state);
st = kzalloc(sizeof(*st), GFP_KERNEL);
if (st)
__drm_atomic_helper_plane_reset(plane, &st->base);
}
struct drm_plane_state *armada_plane_duplicate_state(struct drm_plane *plane)
{
struct armada_plane_state *st;
if (WARN_ON(!plane->state))
return NULL;
st = kmemdup(plane->state, sizeof(*st), GFP_KERNEL);
if (st)
__drm_atomic_helper_plane_duplicate_state(plane, &st->base);
return &st->base;
}
static const struct drm_plane_funcs armada_primary_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_primary_helper_destroy,
.reset = drm_atomic_helper_plane_reset,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.reset = armada_plane_reset,
.atomic_duplicate_state = armada_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};

View File

@ -1,6 +1,24 @@
#ifndef ARMADA_PLANE_H
#define ARMADA_PLANE_H
struct armada_plane_state {
struct drm_plane_state base;
u32 src_hw;
u32 dst_yx;
u32 dst_hw;
u32 addrs[2][3];
u16 pitches[3];
bool interlace;
};
#define to_armada_plane_state(st) \
container_of(st, struct armada_plane_state, base)
#define armada_src_hw(state) to_armada_plane_state(state)->src_hw
#define armada_dst_yx(state) to_armada_plane_state(state)->dst_yx
#define armada_dst_hw(state) to_armada_plane_state(state)->dst_hw
#define armada_addr(state, f, p) to_armada_plane_state(state)->addrs[f][p]
#define armada_pitch(state, n) to_armada_plane_state(state)->pitches[n]
void armada_drm_plane_calc(struct drm_plane_state *state, u32 addrs[2][3],
u16 pitches[3], bool interlaced);
int armada_drm_plane_prepare_fb(struct drm_plane *plane,
@ -9,6 +27,11 @@ void armada_drm_plane_cleanup_fb(struct drm_plane *plane,
struct drm_plane_state *old_state);
int armada_drm_plane_atomic_check(struct drm_plane *plane,
struct drm_plane_state *state);
void armada_plane_reset(struct drm_plane *plane);
struct drm_plane_state *armada_plane_duplicate_state(struct drm_plane *plane);
void armada_plane_destroy_state(struct drm_plane *plane,
struct drm_plane_state *state);
int armada_drm_primary_plane_init(struct drm_device *drm,
struct drm_plane *primary);