blob: f82224a9c90f563c24183b9e2c6667472d4cd3b3 [file] [log] [blame]
/*
* Copyright (C) 2016 Freescale Semiconductor, Inc.
* Copyright 2017-2018 NXP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include <linux/io.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <video/dpu.h>
#include "dpu-prv.h"
static unsigned int safety_stream_cf_color = 0x0;
module_param(safety_stream_cf_color, uint, 0444);
MODULE_PARM_DESC(safety_stream_cf_color,
"Safety stream constframe color in hex(0xRRGGBBAA) [default=0x00000000]");
#define FRAMEDIMENSIONS 0xC
#define WIDTH(w) (((w) - 1) & 0x3FFF)
#define HEIGHT(h) ((((h) - 1) & 0x3FFF) << 16)
#define CONSTANTCOLOR 0x10
#define RED(r) (((r) & 0xFF) << 24)
#define GREEN(g) (((g) & 0xFF) << 16)
#define BLUE(b) (((b) & 0xFF) << 8)
#define ALPHA(a) ((a) & 0xFF)
#define CONTROLTRIGGER 0x14
#define START 0x18
#define STATUS 0x1C
static const shadow_load_req_t cf_shdlreqs[] = {
SHLDREQID_CONSTFRAME0, SHLDREQID_CONSTFRAME1,
SHLDREQID_CONSTFRAME4, SHLDREQID_CONSTFRAME5,
};
struct dpu_constframe {
void __iomem *pec_base;
void __iomem *base;
struct mutex mutex;
int id;
bool inuse;
struct dpu_soc *dpu;
shadow_load_req_t shdlreq;
};
static inline u32 dpu_cf_read(struct dpu_constframe *cf, unsigned int offset)
{
return readl(cf->base + offset);
}
static inline void dpu_cf_write(struct dpu_constframe *cf, u32 value,
unsigned int offset)
{
writel(value, cf->base + offset);
}
void constframe_shden(struct dpu_constframe *cf, bool enable)
{
u32 val;
val = enable ? SHDEN : 0;
mutex_lock(&cf->mutex);
dpu_cf_write(cf, val, STATICCONTROL);
mutex_unlock(&cf->mutex);
}
EXPORT_SYMBOL_GPL(constframe_shden);
void constframe_framedimensions(struct dpu_constframe *cf, unsigned int w,
unsigned int h)
{
u32 val;
val = WIDTH(w) | HEIGHT(h);
mutex_lock(&cf->mutex);
dpu_cf_write(cf, val, FRAMEDIMENSIONS);
mutex_unlock(&cf->mutex);
}
EXPORT_SYMBOL_GPL(constframe_framedimensions);
void constframe_framedimensions_copy_prim(struct dpu_constframe *cf)
{
struct dpu_constframe *prim_cf = NULL;
unsigned int prim_id;
int i;
u32 val;
if (cf->id != 0 && cf->id != 1) {
dev_warn(cf->dpu->dev, "ConstFrame%d is not a secondary one\n",
cf->id);
return;
}
prim_id = cf->id + 4;
for (i = 0; i < ARRAY_SIZE(cf_ids); i++)
if (cf_ids[i] == prim_id)
prim_cf = cf->dpu->cf_priv[i];
if (!prim_cf) {
dev_warn(cf->dpu->dev, "cannot find ConstFrame%d's primary peer\n",
cf->id);
return;
}
mutex_lock(&cf->mutex);
val = dpu_cf_read(prim_cf, FRAMEDIMENSIONS);
dpu_cf_write(cf, val, FRAMEDIMENSIONS);
mutex_unlock(&cf->mutex);
}
EXPORT_SYMBOL_GPL(constframe_framedimensions_copy_prim);
void constframe_constantcolor(struct dpu_constframe *cf, unsigned int r,
unsigned int g, unsigned int b, unsigned int a)
{
u32 val;
val = RED(r) | GREEN(g) | BLUE(b) | ALPHA(a);
mutex_lock(&cf->mutex);
dpu_cf_write(cf, val, CONSTANTCOLOR);
mutex_unlock(&cf->mutex);
}
EXPORT_SYMBOL_GPL(constframe_constantcolor);
void constframe_controltrigger(struct dpu_constframe *cf, bool trigger)
{
u32 val;
val = trigger ? SHDTOKGEN : 0;
mutex_lock(&cf->mutex);
dpu_cf_write(cf, val, CONTROLTRIGGER);
mutex_unlock(&cf->mutex);
}
EXPORT_SYMBOL_GPL(constframe_controltrigger);
shadow_load_req_t constframe_to_shdldreq_t(struct dpu_constframe *cf)
{
shadow_load_req_t t = 0;
switch (cf->id) {
case 0:
t = SHLDREQID_CONSTFRAME0;
break;
case 1:
t = SHLDREQID_CONSTFRAME1;
break;
case 4:
t = SHLDREQID_CONSTFRAME4;
break;
case 5:
t = SHLDREQID_CONSTFRAME5;
break;
}
return t;
}
EXPORT_SYMBOL_GPL(constframe_to_shdldreq_t);
struct dpu_constframe *dpu_cf_get(struct dpu_soc *dpu, int id)
{
struct dpu_constframe *cf;
int i;
for (i = 0; i < ARRAY_SIZE(cf_ids); i++)
if (cf_ids[i] == id)
break;
if (i == ARRAY_SIZE(cf_ids))
return ERR_PTR(-EINVAL);
cf = dpu->cf_priv[i];
mutex_lock(&cf->mutex);
if (cf->inuse) {
mutex_unlock(&cf->mutex);
return ERR_PTR(-EBUSY);
}
cf->inuse = true;
mutex_unlock(&cf->mutex);
return cf;
}
EXPORT_SYMBOL_GPL(dpu_cf_get);
void dpu_cf_put(struct dpu_constframe *cf)
{
mutex_lock(&cf->mutex);
cf->inuse = false;
mutex_unlock(&cf->mutex);
}
EXPORT_SYMBOL_GPL(dpu_cf_put);
struct dpu_constframe *dpu_aux_cf_peek(struct dpu_constframe *cf)
{
unsigned int aux_id = cf->id ^ 1;
int i;
for (i = 0; i < ARRAY_SIZE(cf_ids); i++)
if (cf_ids[i] == aux_id)
return cf->dpu->cf_priv[i];
return NULL;
}
EXPORT_SYMBOL_GPL(dpu_aux_cf_peek);
void _dpu_cf_init(struct dpu_soc *dpu, unsigned int id)
{
struct dpu_constframe *cf;
int i;
for (i = 0; i < ARRAY_SIZE(cf_ids); i++)
if (cf_ids[i] == id)
break;
if (WARN_ON(i == ARRAY_SIZE(cf_ids)))
return;
cf = dpu->cf_priv[i];
constframe_shden(cf, true);
if (id == 4 || id == 5) {
mutex_lock(&cf->mutex);
dpu_cf_write(cf, safety_stream_cf_color, CONSTANTCOLOR);
mutex_unlock(&cf->mutex);
}
}
int dpu_cf_init(struct dpu_soc *dpu, unsigned int id,
unsigned long pec_base, unsigned long base)
{
struct dpu_constframe *cf;
int i;
cf = devm_kzalloc(dpu->dev, sizeof(*cf), GFP_KERNEL);
if (!cf)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(cf_ids); i++)
if (cf_ids[i] == id)
break;
if (i == ARRAY_SIZE(cf_ids))
return -EINVAL;
dpu->cf_priv[i] = cf;
cf->pec_base = devm_ioremap(dpu->dev, pec_base, SZ_16);
if (!cf->pec_base)
return -ENOMEM;
cf->base = devm_ioremap(dpu->dev, base, SZ_32);
if (!cf->base)
return -ENOMEM;
cf->dpu = dpu;
cf->id = id;
cf->shdlreq = cf_shdlreqs[i];
mutex_init(&cf->mutex);
_dpu_cf_init(dpu, id);
return 0;
}